1
0
Fork 0

Merge branch 'master' into electric-engine

This commit is contained in:
Vit Hanousek 2020-04-11 09:15:21 +02:00
commit 30528a9d6c
75 changed files with 1268 additions and 1145 deletions

View file

@ -23,11 +23,7 @@ add_library(hidapi STATIC
) )
if(WIN32) if(WIN32)
find_library(SETUP_API_LIB Setupapi) target_link_libraries(hidapi PUBLIC SetupApi)
if (NOT SETUP_API_LIB)
message(FATAL_ERROR "Failed to find Setupapi.lib")
endif()
target_link_libraries(hidapi ${SETUP_API_LIB})
elseif(APPLE) elseif(APPLE)
find_library(IOKIT_FRAMEWORK IOKit) find_library(IOKIT_FRAMEWORK IOKit)
target_link_libraries(hidapi ${IOKIT_FRAMEWORK}) target_link_libraries(hidapi ${IOKIT_FRAMEWORK})

View file

@ -76,7 +76,7 @@ include(GNUInstallDirs)
# System detection/default settings # System detection/default settings
include( DetectDistro ) include( DetectDistro )
include( DetectBrowser ) include( DetectBrowser )
include( ExportDebugSymbols )
set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually d on windows") set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually d on windows")
set(CMAKE_RELEASE_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows") set(CMAKE_RELEASE_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
set(CMAKE_RELWITHDEBINFO_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows") set(CMAKE_RELWITHDEBINFO_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
@ -123,11 +123,7 @@ IF(APPLE)
find_library(COCOA_LIBRARY Cocoa) find_library(COCOA_LIBRARY Cocoa)
list(APPEND PLATFORM_LIBS ${COCOA_LIBRARY} ${CORESERVICES_LIBRARY}) list(APPEND PLATFORM_LIBS ${COCOA_LIBRARY} ${CORESERVICES_LIBRARY})
elseif(WIN32) elseif(WIN32)
find_library(SETUP_API_LIB Setupapi)
if (SETUP_API_LIB)
set(EVENT_INPUT_DEFAULT 1) set(EVENT_INPUT_DEFAULT 1)
endif()
list(APPEND PLATFORM_LIBS "Shlwapi.lib") list(APPEND PLATFORM_LIBS "Shlwapi.lib")
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION "bin") set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION "bin")
@ -264,16 +260,9 @@ if(EVENT_INPUT)
set(EVENT_INPUT_LIBRARIES ${UDEV_LIBRARIES}) set(EVENT_INPUT_LIBRARIES ${UDEV_LIBRARIES})
message(STATUS "event-based input enabled. Using ${UDEV_LIBRARIES}") message(STATUS "event-based input enabled. Using ${UDEV_LIBRARIES}")
endif() endif()
else()
# Windows
if (NOT SETUP_API_LIB)
message(WARNING "Setupapi.lib not found, HID/event input is disabled")
set(ENABLE_HID_INPUT 0)
set(EVENT_INPUT 0)
else() else()
add_definitions(-DWITH_EVENTINPUT) add_definitions(-DWITH_EVENTINPUT)
endif() endif()
endif()
if (ENABLE_HID_INPUT) if (ENABLE_HID_INPUT)
message(STATUS "Enabling HID-API input") message(STATUS "Enabling HID-API input")
@ -282,7 +271,15 @@ if(EVENT_INPUT)
endif(EVENT_INPUT) endif(EVENT_INPUT)
if (ENABLE_SWIFT) if (ENABLE_SWIFT)
# DBUS # DBUS
# our local FindDBus.cmake file uses pkg-config on non-Windows
# we want to ensure our local prefixes are searched, so set this
set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH 1)
# unfortunately CMAKE_INSTALL_PREFIX is not searched, so add that manually
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX})
find_package(DBus) find_package(DBus)
#libevent #libevent
@ -317,6 +314,13 @@ find_package(OpenSceneGraph 3.2.0 REQUIRED
osgGA osgGA
) )
find_package(sentry QUIET)
if (TARGET sentry::sentry)
message(STATUS "Sentry.io crash reporting enabled")
set(HAVE_SENTRY 1)
endif()
if (MSVC) if (MSVC)
find_package(CrashRpt) find_package(CrashRpt)
if (CRASHRPT_FOUND) if (CRASHRPT_FOUND)
@ -415,10 +419,7 @@ endif()
if(ENABLE_RTI) if(ENABLE_RTI)
message(STATUS "RTI: ENABLED") message(STATUS "RTI: ENABLED")
find_package(RTI)
if(RTI_FOUND)
set(FG_HAVE_HLA 1) set(FG_HAVE_HLA 1)
endif(RTI_FOUND)
else() else()
message(STATUS "RTI: DISABLED") message(STATUS "RTI: DISABLED")
endif(ENABLE_RTI) endif(ENABLE_RTI)

View file

@ -48,6 +48,9 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
set (CMAKE_LIBRARY_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/SimGear/lib ) set (CMAKE_LIBRARY_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/SimGear/lib )
set (CMAKE_INCLUDE_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/SimGear/include) set (CMAKE_INCLUDE_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/SimGear/include)
# ensure 3rdparty/lib/cmake is searched
list(APPEND CMAKE_PREFIX_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR})
if(NOT BOOST_INCLUDEDIR) if(NOT BOOST_INCLUDEDIR)
# if this variable was not set by the user, set it to 3rdparty root's # if this variable was not set by the user, set it to 3rdparty root's
# parent dir, which is the normal location for people using our # parent dir, which is the normal location for people using our

View file

@ -0,0 +1,22 @@
# placehodler target for other ones to depend upon
add_custom_target(
debug_symbols
)
function(export_debug_symbols target)
if (APPLE)
add_custom_target(${target}.dSYM
COMMENT "Generating dSYM files for ${target}"
COMMAND dsymutil --out=${target}.dSYM $<TARGET_FILE:${target}>
DEPENDS $<TARGET_FILE:${target}>
)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${target}.dSYM DESTINATION symbols OPTIONAL)
add_dependencies(debug_symbols ${target}.dSYM)
endif()
endfunction()

View file

@ -7,7 +7,7 @@ function(setup_fgfs_bundle target)
# in our local CMakeModules dir # in our local CMakeModules dir
set_target_properties(${target} PROPERTIES set_target_properties(${target} PROPERTIES
MACOSX_BUNDLE_INFO_PLIST FlightGearBundleInfo.plist.in MACOSX_BUNDLE_INFO_PLIST FlightGearBundleInfo.plist.in
MACOSX_BUNDLE_GUI_IDENTIFIER "org.flightgear.FlightGear" MACOSX_BUNDLE_GUI_IDENTIFIER "org.flightgear.mac-nightly"
MACOSX_BUNDLE_SHORT_VERSION_STRING ${FLIGHTGEAR_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${FLIGHTGEAR_VERSION}
MACOSX_BUNDLE_LONG_VERSION_STRING "FlightGear ${FLIGHTGEAR_VERSION} Nightly" MACOSX_BUNDLE_LONG_VERSION_STRING "FlightGear ${FLIGHTGEAR_VERSION} Nightly"
MACOSX_BUNDLE_BUNDLE_VERSION ${FLIGHTGEAR_VERSION} MACOSX_BUNDLE_BUNDLE_VERSION ${FLIGHTGEAR_VERSION}

View file

@ -7,7 +7,7 @@ function(setup_fgfs_libraries target)
#message(STATUS "SG libs ${SIMGEAR_LIBRARIES}") #message(STATUS "SG libs ${SIMGEAR_LIBRARIES}")
if(RTI_FOUND) if(RTI_FOUND)
set(HLA_LIBRARIES ${RTI_LIBRARIES}) set(HLA_LIBRARIES ${RTI_LDFLAGS})
else() else()
set(HLA_LIBRARIES "") set(HLA_LIBRARIES "")
endif() endif()

View file

@ -20,15 +20,12 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H #include <config.h>
# include <config.h>
#endif
#include <string.h> #include <string.h>
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <boost/foreach.hpp>
#include <string> #include <string>
#include <osg/ref_ptr> #include <osg/ref_ptr>
@ -67,11 +64,9 @@ public:
} }
~FGAIModelData() ~FGAIModelData() = default;
{
}
virtual FGAIModelData* clone() const { return new FGAIModelData(); } FGAIModelData* clone() const override { return new FGAIModelData(); }
/** osg callback, thread-safe */ /** osg callback, thread-safe */
void modelLoaded(const std::string& path, SGPropertyNode *prop, osg::Node *n) void modelLoaded(const std::string& path, SGPropertyNode *prop, osg::Node *n)

View file

@ -72,7 +72,7 @@ void FGAICarrier::readFromScenario(SGPropertyNode* scFileNode) {
_flolsPosOffset(2) = - flols->getDoubleValue("z-offset-m", 0); _flolsPosOffset(2) = - flols->getDoubleValue("z-offset-m", 0);
_flolsHeadingOffsetDeg = flols->getDoubleValue("heading-offset-deg", 0.0); _flolsHeadingOffsetDeg = flols->getDoubleValue("heading-offset-deg", 0.0);
_flolsApproachAngle = flols->getDoubleValue("glidepath-angle-deg", 3.0); _flolsApproachAngle = flols->getDoubleValue("glidepath-angle-deg", 3.5);
} else } else
_flolsPosOffset = SGVec3d::zeros(); _flolsPosOffset = SGVec3d::zeros();
@ -686,6 +686,9 @@ void FGAICarrier::extractCarriersFromScenario(SGPropertyNode_ptr xmlNode, SGProp
carrierNode->setDoubleValue("longitude", c->getDoubleValue("longitude")); carrierNode->setDoubleValue("longitude", c->getDoubleValue("longitude"));
carrierNode->setDoubleValue("latitude", c->getDoubleValue("latitude")); carrierNode->setDoubleValue("latitude", c->getDoubleValue("latitude"));
// A description of the carrier is also available from the entry. Primarily for use by the launcher
carrierNode->setStringValue("description", c->getStringValue("description"));
// the find code above just looks for anything called a name (so alias // the find code above just looks for anything called a name (so alias
// are possible, for example) // are possible, for example)
if (!name.empty()) carrierNode->addChild("name")->setStringValue(name); if (!name.empty()) carrierNode->addChild("name")->setStringValue(name);

View file

@ -234,6 +234,13 @@ FGTaxiNodeRef FGGroundNetwork::findNearestNodeOffRunway(const SGGeod& aGeod, FGR
[runwayLine, cartPos, marginMSqr] (const FGTaxiNodeRef& a) [runwayLine, cartPos, marginMSqr] (const FGTaxiNodeRef& a)
{ {
if (a->getIsOnRunway()) return false; if (a->getIsOnRunway()) return false;
// exclude parking positions from consideration. This helps to
// exclude airports whose ground nets only list parking positions,
// since these typically produce bad results. See discussion in
// https://sourceforge.net/p/flightgear/codetickets/2110/
if (a->type() == FGPositioned::PARKING) return false;
return (distSqr(runwayLine, a->cart()) >= marginMSqr); return (distSqr(runwayLine, a->cart()) >= marginMSqr);
}); });

View file

@ -153,7 +153,7 @@ public:
_nd(nd) _nd(nd)
{} {}
virtual void valueChanged (SGPropertyNode * prop) void valueChanged (SGPropertyNode * prop) override
{ {
_nd->invalidatePositionedCache(); _nd->invalidatePositionedCache();
} }
@ -168,7 +168,7 @@ public:
_nd(nd) _nd(nd)
{} {}
virtual void valueChanged (SGPropertyNode * prop) void valueChanged (SGPropertyNode * prop) override
{ {
_nd->forceUpdate(); _nd->forceUpdate();
} }
@ -256,7 +256,7 @@ public:
// record instances for limiting by count // record instances for limiting by count
int instanceCount; int instanceCount;
private: private:
SymbolDef* definition; SymbolDef* definition = nullptr;
std::unique_ptr<SGCondition> enable; std::unique_ptr<SGCondition> enable;
string_set required_states; string_set required_states;
@ -459,7 +459,7 @@ NavDisplay::NavDisplay(SGPropertyNode *node) :
SGPropertyNode* symbol; SGPropertyNode* symbol;
map<string, SymbolDef*> definitionDict; map<string, SymbolDef*> definitionDict;
for (int i = 0; (symbol = symbolsNode->getChild("symbol", i)) != NULL; ++i) { for (int i = 0; (symbol = symbolsNode->getChild("symbol", i)) != nullptr; ++i) {
SymbolDef* def = new SymbolDef; SymbolDef* def = new SymbolDef;
if (!def->initFromNode(symbol, this)) { if (!def->initFromNode(symbol, this)) {
delete def; delete def;
@ -539,7 +539,7 @@ NavDisplay::init ()
} }
// no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect // no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect
_symbolTexture = SGLoadTexture2D(tpath, NULL, false, false); _symbolTexture = SGLoadTexture2D(tpath, nullptr, false, false);
_odg = new FGODGauge; _odg = new FGODGauge;
_odg->setSize(_Instrument->getIntValue("texture-size", 512)); _odg->setSize(_Instrument->getIntValue("texture-size", 512));
@ -958,7 +958,7 @@ public:
double minRunwayLengthFt; double minRunwayLengthFt;
virtual bool pass(FGPositioned* aPos) const bool pass(FGPositioned* aPos) const override
{ {
if (aPos->type() == FGPositioned::FIX) { if (aPos->type() == FGPositioned::FIX) {
string ident(aPos->ident()); string ident(aPos->ident());
@ -979,11 +979,13 @@ public:
return _owner->isPositionedShown(aPos); return _owner->isPositionedShown(aPos);
} }
virtual FGPositioned::Type minType() const { FGPositioned::Type minType() const override
{
return FGPositioned::AIRPORT; return FGPositioned::AIRPORT;
} }
virtual FGPositioned::Type maxType() const { FGPositioned::Type maxType() const override
{
return FGPositioned::OBSTACLE; return FGPositioned::OBSTACLE;
} }
@ -1105,7 +1107,7 @@ FGNavRecord* NavDisplay::processNavRadio(const SGPropertyNode_ptr& radio)
FGNavRecord* nav = FGNavList::findByFreq(mhz, _pos, FGNavList::navFilter()); FGNavRecord* nav = FGNavList::findByFreq(mhz, _pos, FGNavList::navFilter());
if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) { if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) {
// station was not found // station was not found
return NULL; return nullptr;
} }
@ -1367,11 +1369,11 @@ void NavDisplay::computeAIStates(const SGPropertyNode* ai, string_set& states)
SymbolInstance* NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars) SymbolInstance* NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars)
{ {
if (isProjectedClipped(proj)) { if (isProjectedClipped(proj)) {
return NULL; return nullptr;
} }
if ((def->limitCount > 0) && (def->instanceCount >= def->limitCount)) { if ((def->limitCount > 0) && (def->instanceCount >= def->limitCount)) {
return NULL; return nullptr;
} }
++def->instanceCount; ++def->instanceCount;
@ -1416,28 +1418,28 @@ void NavDisplay::addTestSymbols()
double dummy; double dummy;
SGGeodesy::direct(_pos, 45.0, 20.0 * SG_NM_TO_METER, a1, dummy); SGGeodesy::direct(_pos, 45.0, 20.0 * SG_NM_TO_METER, a1, dummy);
addTestSymbol("airport", "", a1, 0.0, NULL); addTestSymbol("airport", "", a1, 0.0, nullptr);
SGGeodesy::direct(_pos, 95.0, 40.0 * SG_NM_TO_METER, a1, dummy); SGGeodesy::direct(_pos, 95.0, 40.0 * SG_NM_TO_METER, a1, dummy);
addTestSymbol("vor", "", a1, 0.0, NULL); addTestSymbol("vor", "", a1, 0.0, nullptr);
SGGeodesy::direct(_pos, 120, 80.0 * SG_NM_TO_METER, a1, dummy); SGGeodesy::direct(_pos, 120, 80.0 * SG_NM_TO_METER, a1, dummy);
addTestSymbol("airport", "destination", a1, 0.0, NULL); addTestSymbol("airport", "destination", a1, 0.0, nullptr);
SGGeodesy::direct(_pos, 80.0, 20.0 * SG_NM_TO_METER, a1, dummy); SGGeodesy::direct(_pos, 80.0, 20.0 * SG_NM_TO_METER, a1, dummy);
addTestSymbol("fix", "", a1, 0.0, NULL); addTestSymbol("fix", "", a1, 0.0, nullptr);
SGGeodesy::direct(_pos, 140.0, 20.0 * SG_NM_TO_METER, a1, dummy); SGGeodesy::direct(_pos, 140.0, 20.0 * SG_NM_TO_METER, a1, dummy);
addTestSymbol("fix", "", a1, 0.0, NULL); addTestSymbol("fix", "", a1, 0.0, nullptr);
SGGeodesy::direct(_pos, 110.0, 10.0 * SG_NM_TO_METER, a1, dummy); SGGeodesy::direct(_pos, 110.0, 10.0 * SG_NM_TO_METER, a1, dummy);
addTestSymbol("fix", "", a1, 0.0, NULL); addTestSymbol("fix", "", a1, 0.0, nullptr);
SGGeodesy::direct(_pos, 110.0, 5.0 * SG_NM_TO_METER, a1, dummy); SGGeodesy::direct(_pos, 110.0, 5.0 * SG_NM_TO_METER, a1, dummy);
addTestSymbol("fix", "", a1, 0.0, NULL); addTestSymbol("fix", "", a1, 0.0, nullptr);
} }
void NavDisplay::addRule(SymbolRule* r) void NavDisplay::addRule(SymbolRule* r)

View file

@ -184,6 +184,9 @@ MetarProperties::MetarProperties( SGPropertyNode_ptr rootNode ) :
_tiedProperties.Tie("decoded", this, &MetarProperties::get_decoded ); _tiedProperties.Tie("decoded", this, &MetarProperties::get_decoded );
_tiedProperties.Tie("cavok", &_cavok ); _tiedProperties.Tie("cavok", &_cavok );
_tiedProperties.Tie("description", this, &MetarProperties::get_description ); _tiedProperties.Tie("description", this, &MetarProperties::get_description );
// mark proeprties as listener-safe, we invoke valueChanged explicitly
_tiedProperties.setAttribute(SGPropertyNode::LISTENER_SAFE, true);
} }
MetarProperties::~MetarProperties() MetarProperties::~MetarProperties()
@ -422,6 +425,7 @@ void MetarProperties::setMetar( SGSharedPtr<FGMetar> m )
_hour = m->getHour(); _hour = m->getHour();
_minute = m->getMinute(); _minute = m->getMinute();
_cavok = m->getCAVOK(); _cavok = m->getCAVOK();
_tiedProperties.fireValueChanged();
_metarValidNode->setBoolValue(true); _metarValidNode->setBoolValue(true);
_description = m->getDescription(-1); _description = m->getDescription(-1);
} }

View file

@ -204,9 +204,9 @@ void FGFDM::init()
fgSetDouble(buf, CM2GALS * _airplane.getTankCapacity(i)/density); fgSetDouble(buf, CM2GALS * _airplane.getTankCapacity(i)/density);
} }
// This has a nasty habit of being false at startup. That's not if (_yasimN->getBoolValue("respect-external-gear-state") == false) {
// good.
fgSetBool("/controls/gear/gear-down", true); fgSetBool("/controls/gear/gear-down", true);
}
_airplane.getModel()->setTurbulence(_turb); _airplane.getModel()->setTurbulence(_turb);
} }

View file

@ -196,8 +196,10 @@ void YASim::init()
// Are we at ground level? If so, lift the plane up so the gear // Are we at ground level? If so, lift the plane up so the gear
// clear the ground. // clear the ground.
bool respect_external_gear_state = fgGetBool("/fdm/yasim/respect-external-gear-state");
double runway_altitude = get_Runway_altitude(); double runway_altitude = get_Runway_altitude();
if(get_Altitude() - runway_altitude < 50) { if(get_Altitude() - runway_altitude < 50) {
bool gear_state = fgGetBool("/controls/gear/gear-down");
fgSetBool("/controls/gear/gear-down", false); fgSetBool("/controls/gear/gear-down", false);
float minGearZ = 1e18; float minGearZ = 1e18;
for(int i=0; i<airplane->numGear(); i++) { for(int i=0; i<airplane->numGear(); i++) {
@ -209,9 +211,9 @@ void YASim::init()
} }
_set_Altitude(runway_altitude - minGearZ*M2FT); _set_Altitude(runway_altitude - minGearZ*M2FT);
// ground start-up: gear down // ground start-up: gear down
fgSetBool("/controls/gear/gear-down", true); fgSetBool("/controls/gear/gear-down", respect_external_gear_state ? gear_state : true);
} }
else else if (! respect_external_gear_state)
{ {
// airborne start-up: gear up // airborne start-up: gear up
fgSetBool("/controls/gear/gear-down", false); fgSetBool("/controls/gear/gear-down", false);

View file

@ -38,6 +38,7 @@
#include <Include/version.h> #include <Include/version.h>
#include "QmlAircraftInfo.hxx" #include "QmlAircraftInfo.hxx"
#include "FavouriteAircraftData.hxx"
using namespace simgear::pkg; using namespace simgear::pkg;
@ -125,7 +126,7 @@ private:
return QModelIndex(); return QModelIndex();
} }
int offset = std::distance(m_model->m_packages.begin(), it); const int offset = static_cast<int>(std::distance(m_model->m_packages.begin(), it));
return m_model->index(offset + m_model->m_cachedLocalAircraftCount); return m_model->index(offset + m_model->m_cachedLocalAircraftCount);
} }
@ -138,6 +139,8 @@ void PackageDelegate::catalogRefreshed(CatalogRef aCatalog, StatusCode aReason)
// nothing to do // nothing to do
} else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) { } else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) {
m_model->refreshPackages(); m_model->refreshPackages();
} else if (aReason == FAIL_VERSION) {
// silent about this
} else { } else {
qWarning() << "failed refresh of" qWarning() << "failed refresh of"
<< QString::fromStdString(aCatalog->url()) << ":" << aReason << endl; << QString::fromStdString(aCatalog->url()) << ":" << aReason << endl;
@ -158,8 +161,6 @@ AircraftItemModel::AircraftItemModel(QObject* pr) :
this, &AircraftItemModel::onScanAddedItems); this, &AircraftItemModel::onScanAddedItems);
connect(cache, &LocalAircraftCache::cleared, connect(cache, &LocalAircraftCache::cleared,
this, &AircraftItemModel::onLocalCacheCleared); this, &AircraftItemModel::onLocalCacheCleared);
loadFavourites();
} }
AircraftItemModel::~AircraftItemModel() AircraftItemModel::~AircraftItemModel()
@ -244,7 +245,7 @@ QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
if (role == AircraftIsFavouriteRole) { if (role == AircraftIsFavouriteRole) {
// recursive call here, hope that's okay // recursive call here, hope that's okay
const auto uri = data(index, AircraftURIRole).toUrl(); const auto uri = data(index, AircraftURIRole).toUrl();
return m_favourites.contains(uri); return FavouriteAircraftData::instance()->isFavourite(uri);
} }
if (row >= m_cachedLocalAircraftCount) { if (row >= m_cachedLocalAircraftCount) {
@ -338,7 +339,7 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta
QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const DelegateState& state, int role) const QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const DelegateState& state, int role) const
{ {
if (role >= AircraftVariantDescriptionRole) { if (role >= AircraftVariantDescriptionRole) {
int variantIndex = role - AircraftVariantDescriptionRole; const unsigned int variantIndex = static_cast<unsigned int>(role - AircraftVariantDescriptionRole);
QString desc = QString::fromStdString(item->nameForVariant(variantIndex)); QString desc = QString::fromStdString(item->nameForVariant(variantIndex));
if (desc.isEmpty()) { if (desc.isEmpty()) {
desc = tr("Missing description for: %1").arg(QString::fromStdString(item->id())); desc = tr("Missing description for: %1").arg(QString::fromStdString(item->id()));
@ -431,18 +432,12 @@ bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value,
emit dataChanged(index, index); emit dataChanged(index, index);
return true; return true;
} else if (role == AircraftIsFavouriteRole) { } else if (role == AircraftIsFavouriteRole) {
bool f = value.toBool();
const auto uri = data(index, AircraftURIRole).toUrl(); const auto uri = data(index, AircraftURIRole).toUrl();
const auto cur = m_favourites.contains(uri); bool changed = FavouriteAircraftData::instance()->setFavourite(uri, value.toBool());
if (f && !cur) { if (changed) {
m_favourites.append(uri);
} else if (!f && cur) {
m_favourites.removeOne(uri);
}
saveFavourites();
emit dataChanged(index, index); emit dataChanged(index, index);
} }
}
return false; return false;
} }
@ -493,9 +488,10 @@ QModelIndex AircraftItemModel::indexOfAircraftURI(QUrl uri) const
PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString()); PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString());
if (pkg) { if (pkg) {
for (size_t i=0; i < m_packages.size(); ++i) { const auto numPackages = m_packages.size();
if (m_packages[i] == pkg) { for (unsigned int i=0; i < numPackages; ++i) {
return index(rowOffset + i); if (m_packages.at(i) == pkg) {
return index(static_cast<int>(rowOffset + i));
} }
} // of linear package scan } // of linear package scan
} }
@ -663,21 +659,3 @@ bool AircraftItemModel::isIndexRunnable(const QModelIndex& index) const
return !ex->isDownloading(); return !ex->isDownloading();
} }
void AircraftItemModel::loadFavourites()
{
m_favourites.clear();
QSettings settings;
Q_FOREACH(auto v, settings.value("favourite-aircraft").toList()) {
m_favourites.append(v.toUrl());
}
}
void AircraftItemModel::saveFavourites()
{
QVariantList favs;
Q_FOREACH(auto u, m_favourites) {
favs.append(u);
}
QSettings settings;
settings.setValue("favourite-aircraft", favs);
}

View file

@ -146,9 +146,6 @@ private:
void installSucceeded(QModelIndex index); void installSucceeded(QModelIndex index);
void installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason); void installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason);
void loadFavourites();
void saveFavourites();
private: private:
PackageDelegate* m_delegate = nullptr; PackageDelegate* m_delegate = nullptr;
@ -156,8 +153,6 @@ private:
simgear::pkg::RootRef m_packageRoot; simgear::pkg::RootRef m_packageRoot;
simgear::pkg::PackageList m_packages; simgear::pkg::PackageList m_packages;
QVector<QUrl> m_favourites;
int m_cachedLocalAircraftCount = 0; int m_cachedLocalAircraftCount = 0;
}; };

View file

@ -4,6 +4,8 @@
#include <QDebug> #include <QDebug>
#include "AircraftModel.hxx" #include "AircraftModel.hxx"
#include "FavouriteAircraftData.hxx"
#include <simgear/package/Package.hxx> #include <simgear/package/Package.hxx>
AircraftProxyModel::AircraftProxyModel(QObject *pr, QAbstractItemModel * source) : AircraftProxyModel::AircraftProxyModel(QObject *pr, QAbstractItemModel * source) :
@ -121,6 +123,13 @@ void AircraftProxyModel::setShowFavourites(bool e)
return; return;
m_onlyShowFavourites = e; m_onlyShowFavourites = e;
if (e) {
setDynamicSortFilter(false);
connect(FavouriteAircraftData::instance(), &FavouriteAircraftData::changed,
[this]() {
this->invalidate();
});
}
invalidate(); invalidate();
} }

View file

@ -68,7 +68,7 @@ protected:
private: private:
bool filterAircraft(const QModelIndex& sourceIndex) const; bool filterAircraft(const QModelIndex& sourceIndex) const;
bool m_ratingsFilter = true; bool m_ratingsFilter = false;
bool m_onlyShowInstalled = false; bool m_onlyShowInstalled = false;
bool m_onlyShowWithUpdate = false; bool m_onlyShowWithUpdate = false;
bool m_onlyShowFavourites = false; bool m_onlyShowFavourites = false;

View file

@ -155,6 +155,25 @@ void BaseDiagram::paintAirplaneIcon(QPainter* painter, const SGGeod& geod, int h
painter->restore(); painter->restore();
} }
void BaseDiagram::paintCarrierIcon(QPainter* painter, const SGGeod& geod, int headingDeg)
{
QPointF pos = project(geod);
QPixmap pix(":/svg/aircraft-carrier");
pos = m_viewportTransform.map(pos);
painter->save();
painter->setWorldTransform(m_baseDeviceTransform);
painter->translate(pos.x(), pos.y());
painter->rotate(headingDeg);
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
QRect carrierIconRect = pix.rect();
carrierIconRect.moveCenter(QPoint(0,0));
painter->drawPixmap(carrierIconRect, pix);
painter->restore();
}
void BaseDiagram::paintPolygonData(QPainter* painter) void BaseDiagram::paintPolygonData(QPainter* painter)
{ {
QTransform invT = m_viewportTransform.inverted(); QTransform invT = m_viewportTransform.inverted();

View file

@ -98,6 +98,7 @@ protected:
static SGGeod unproject(const QPointF &xy, const SGGeod &center); static SGGeod unproject(const QPointF &xy, const SGGeod &center);
void paintAirplaneIcon(QPainter *painter, const SGGeod &geod, int headingDeg); void paintAirplaneIcon(QPainter *painter, const SGGeod &geod, int headingDeg);
void paintCarrierIcon(QPainter *painter, const SGGeod &geod, int headingDeg);
void paintAirways(QPainter* painter, const FGPositionedList& navs); void paintAirways(QPainter* painter, const FGPositionedList& navs);
QPointF projectedPosition(PositionedID pid) const; QPointF projectedPosition(PositionedID pid) const;

View file

@ -84,6 +84,8 @@ if (HAVE_QT)
BaseDiagram.hxx BaseDiagram.hxx
AirportDiagram.cxx AirportDiagram.cxx
AirportDiagram.hxx AirportDiagram.hxx
CarrierDiagram.cxx
CarrierDiagram.hxx
NavaidDiagram.cxx NavaidDiagram.cxx
NavaidDiagram.hxx NavaidDiagram.hxx
SetupRootDialog.cxx SetupRootDialog.cxx
@ -128,6 +130,8 @@ if (HAVE_QT)
PathListModel.hxx PathListModel.hxx
CarriersLocationModel.cxx CarriersLocationModel.cxx
CarriersLocationModel.hxx CarriersLocationModel.hxx
FavouriteAircraftData.cxx
FavouriteAircraftData.hxx
${uic_sources} ${uic_sources}
${qrc_sources} ${qrc_sources}
${qml_sources}) ${qml_sources})

148
src/GUI/CarrierDiagram.cxx Normal file
View file

@ -0,0 +1,148 @@
// CarrierDiagram.cxx - part of GUI launcher using Qt5
//
// Written by Stuart Buchanan, started April 2020.
//
// Copyright (C) 2022 Stuart Buchanan <stuart13@gmail.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "CarrierDiagram.hxx"
#include <limits>
#include <QPainter>
#include <QDebug>
#include <QVector2D>
#include <QMouseEvent>
#include <Navaids/NavDataCache.hxx>
CarrierDiagram::CarrierDiagram(QQuickItem* pr) :
BaseDiagram(pr)
{
}
void CarrierDiagram::setGeod(QmlGeod geod)
{
setGeod(geod.geod());
}
QmlGeod CarrierDiagram::geod() const
{
return QmlGeod(m_geod);
}
void CarrierDiagram::setGeod(const SGGeod &geod)
{
m_Carrier.clear();
m_geod = geod;
m_projectionCenter = m_geod;
recomputeBounds(true);
emit locationChanged();
}
void CarrierDiagram::setOffsetEnabled(bool offset)
{
if (m_offsetEnabled == offset)
return;
m_offsetEnabled = offset;
recomputeBounds(true);
emit offsetChanged();
}
void CarrierDiagram::setAbeam(bool abeam)
{
if (m_abeam == abeam)
return;
m_abeam = abeam;
recomputeBounds(true);
emit offsetChanged();
}
void CarrierDiagram::setOffsetDistance(QuantityValue distanceNm)
{
if (distanceNm == m_offsetDistance)
return;
m_offsetDistance = distanceNm;
update();
emit offsetChanged();
}
void CarrierDiagram::paintContents(QPainter *painter)
{
QPointF base = project(m_geod);
SGGeod carrierPos = m_geod;
SGGeod aircraftPos = m_geod;
float aircraft_heading = 0;
if (m_offsetEnabled) {
// We don't actually know the eventual orientation of the carrier,
// so we place the aircraft relative to the aircraft carrier icon,
// which has the angled flight deck at 80 degrees (e.g. just N or E)
//
// There are two case:
// - On finals, which will be at -100 degrees, aircraft heading 80
// - Abeam on a left hand circuit, so offset 0 degrees,
// aircraft heading downwind -90 (note that this is parallel to the
// _carrier_, not the angled flightdeck)
float offset_heading = -100;
aircraft_heading = 80;
if (m_abeam) {
offset_heading = 0;
aircraft_heading = -90;
}
double d = m_offsetDistance.convertToUnit(Units::Kilometers).value * 1000;
SGGeod offsetGeod = SGGeodesy::direct(m_geod, offset_heading, d);
QPointF offset = project(offsetGeod);
QPen pen(Qt::green);
pen.setCosmetic(true);
painter->setPen(pen);
painter->drawLine(base, offset);
aircraftPos = offsetGeod;
} else {
// We're at a parking position or on the catapults, so simply rotate to
// match the carrier heading - E
aircraft_heading = 90;
}
paintCarrierIcon(painter, carrierPos, 0.0);
paintAirplaneIcon(painter, aircraftPos, aircraft_heading);
}
void CarrierDiagram::doComputeBounds()
{
extendBounds(project(m_geod));
// project four points around the base location at 20nm to give some
// coverage
for (int i=0; i<4; ++i) {
SGGeod pt = SGGeodesy::direct(m_geod, i * 90, SG_NM_TO_METER * 20.0);
extendBounds(project(pt));
}
if (m_offsetEnabled) {
double d = m_offsetDistance.convertToUnit(Units::Kilometers).value * 1000;
float offset_heading = m_abeam ? 0 : -100; // See above for explanation
SGGeod offsetPos = SGGeodesy::direct(m_geod, offset_heading, d);
extendBounds(project(offsetPos));
}
}

View file

@ -0,0 +1,80 @@
// CarrierDiagram.hxx - part of GUI launcher using Qt5
//
// Written by Stuart Buchanan, started April 2020.
//
// Copyright (C) 2022 Stuart Buchanan <stuart13@gmail.com>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef GUI_CARRIER_DIAGRAM_HXX
#define GUI_CARRIER_DIAGRAM_HXX
#include "BaseDiagram.hxx"
#include "QmlPositioned.hxx"
#include "UnitsModel.hxx"
#include <Navaids/navrecord.hxx>
#include <simgear/math/sg_geodesy.hxx>
class CarrierDiagram : public BaseDiagram
{
Q_OBJECT
Q_PROPERTY(QmlGeod geod READ geod WRITE setGeod NOTIFY locationChanged)
Q_PROPERTY(bool offsetEnabled READ isOffsetEnabled WRITE setOffsetEnabled NOTIFY offsetChanged)
Q_PROPERTY(bool abeam READ isAbeam WRITE setAbeam NOTIFY offsetChanged)
Q_PROPERTY(QuantityValue offsetDistance READ offsetDistance WRITE setOffsetDistance NOTIFY offsetChanged)
public:
CarrierDiagram(QQuickItem* pr = nullptr);
//void setCarrier(qlonglong nav);
//qlonglong navaid() const;
void setGeod(QmlGeod geod);
QmlGeod geod() const;
void setGeod(const SGGeod& geod);
bool isOffsetEnabled() const
{ return m_offsetEnabled; }
bool isAbeam() const
{ return m_abeam; }
void setOffsetEnabled(bool offset);
void setAbeam(bool abeam);
void setOffsetDistance(QuantityValue distance);
QuantityValue offsetDistance() const
{ return m_offsetDistance; }
signals:
void locationChanged();
void offsetChanged();
protected:
void paintContents(QPainter *) override;
void doComputeBounds() override;
private:
FGPositionedRef m_Carrier;
SGGeod m_geod;
bool m_offsetEnabled = false;
bool m_abeam = false;
QuantityValue m_offsetDistance;
};
#endif // of GUI_CARRIER_DIAGRAM_HXX

View file

@ -12,7 +12,7 @@ CarriersLocationModel::CarriersLocationModel(QObject *parent)
SGPropertyNode_ptr localRoot(new SGPropertyNode); SGPropertyNode_ptr localRoot(new SGPropertyNode);
FGAIManager::registerScenarios(localRoot); FGAIManager::registerScenarios(localRoot);
// this code encodes some scenario structre, sorry // this code encodes some scenario structure, sorry
for (auto s : localRoot->getNode("sim/ai/scenarios")->getChildren("scenario")) { for (auto s : localRoot->getNode("sim/ai/scenarios")->getChildren("scenario")) {
const std::string scenarioId = s->getStringValue("id"); const std::string scenarioId = s->getStringValue("id");
for (auto c : s->getChildren("carrier")) { for (auto c : s->getChildren("carrier")) {
@ -26,6 +26,7 @@ void CarriersLocationModel::processCarrier(const string &scenario, SGPropertyNod
const auto name = QString::fromStdString(carrierNode->getStringValue("name")); const auto name = QString::fromStdString(carrierNode->getStringValue("name"));
const auto pennant = QString::fromStdString(carrierNode->getStringValue("pennant-number")); const auto pennant = QString::fromStdString(carrierNode->getStringValue("pennant-number"));
const auto tacan = QString::fromStdString(carrierNode->getStringValue("TACAN-channel-ID")); const auto tacan = QString::fromStdString(carrierNode->getStringValue("TACAN-channel-ID"));
const auto desc = QString::fromStdString(carrierNode->getStringValue("description"));
SGGeod geod = SGGeod::fromDeg(carrierNode->getDoubleValue("longitude"), SGGeod geod = SGGeod::fromDeg(carrierNode->getDoubleValue("longitude"),
carrierNode->getDoubleValue("latitude")); carrierNode->getDoubleValue("latitude"));
@ -38,6 +39,7 @@ void CarriersLocationModel::processCarrier(const string &scenario, SGPropertyNod
QString::fromStdString(scenario), QString::fromStdString(scenario),
pennant, pennant,
name, name,
desc,
geod, geod,
tacan, tacan,
parkings parkings
@ -65,6 +67,8 @@ QVariant CarriersLocationModel::data(const QModelIndex &index, int role) const
case NameRole: return c.mName; case NameRole: return c.mName;
// case GeodRole: return QVariant::fromValue(c.mInitialLocation); // case GeodRole: return QVariant::fromValue(c.mInitialLocation);
case IdentRole: return c.mCallsign; case IdentRole: return c.mCallsign;
case DescriptionRole: return c.mDescription;
case TypeRole: return "Carrier";
case IconRole: return QPixmap(":/svg/aircraft-carrier"); case IconRole: return QPixmap(":/svg/aircraft-carrier");
default: default:
break; break;
@ -83,6 +87,7 @@ QHash<int, QByteArray> CarriersLocationModel::roleNames() const
result[NameRole] = "name"; result[NameRole] = "name";
result[IconRole] = "icon"; result[IconRole] = "icon";
result[TypeRole] = "type"; result[TypeRole] = "type";
result[DescriptionRole] = "description";
result[NavFrequencyRole] = "frequency"; result[NavFrequencyRole] = "frequency";
return result; return result;
} }

View file

@ -30,7 +30,8 @@ public:
NameRole = Qt::UserRole + 4, NameRole = Qt::UserRole + 4,
IconRole = Qt::UserRole + 5, IconRole = Qt::UserRole + 5,
TypeRole = Qt::UserRole + 6, TypeRole = Qt::UserRole + 6,
NavFrequencyRole = Qt::UserRole + 7 NavFrequencyRole = Qt::UserRole + 7,
DescriptionRole = Qt::UserRole + 8
}; };
int indexOf(const QString name) const; int indexOf(const QString name) const;
@ -46,6 +47,7 @@ private:
QString mScenario; // scenario ID for loading QString mScenario; // scenario ID for loading
QString mCallsign; // pennant-number QString mCallsign; // pennant-number
QString mName; QString mName;
QString mDescription;
SGGeod mInitialLocation; SGGeod mInitialLocation;
// icon? // icon?
QString mTACAN; QString mTACAN;

View file

@ -111,7 +111,7 @@ QVariant CatalogListModel::data(const QModelIndex& index, int role) const
desc = tr("The catalog data was not found on the server at the expected location (URL)"); desc = tr("The catalog data was not found on the server at the expected location (URL)");
break; break;
case Delegate::FAIL_VERSION: case Delegate::FAIL_VERSION:
desc = tr("The catalog is not comaptible with the version of FlightGear"); desc = tr("The catalog is not compatible with the version of FlightGear");
break; break;
case Delegate::FAIL_HTTP_FORBIDDEN: case Delegate::FAIL_HTTP_FORBIDDEN:
desc = tr("The catalog server is blocking access from some reason (forbidden)"); desc = tr("The catalog server is blocking access from some reason (forbidden)");

View file

@ -0,0 +1,62 @@
#include "FavouriteAircraftData.hxx"
#include <QSettings>
#include <memory>
static std::unique_ptr<FavouriteAircraftData> static_instance;
FavouriteAircraftData *FavouriteAircraftData::instance()
{
if (!static_instance) {
static_instance.reset(new FavouriteAircraftData);
}
return static_instance.get();
}
bool FavouriteAircraftData::isFavourite(QUrl u) const
{
return m_favourites.contains(u);
}
bool FavouriteAircraftData::setFavourite(QUrl u, bool b)
{
const auto cur = m_favourites.contains(u);
if (b == cur)
return false;
if (b && !cur) {
m_favourites.append(u);
} else if (!b && cur) {
m_favourites.removeOne(u);
}
emit changed(u);
saveFavourites();
return true;
}
FavouriteAircraftData::FavouriteAircraftData()
{
loadFavourites();
}
void FavouriteAircraftData::loadFavourites()
{
m_favourites.clear();
QSettings settings;
Q_FOREACH(auto v, settings.value("favourite-aircraft").toList()) {
m_favourites.append(v.toUrl());
}
}
void FavouriteAircraftData::saveFavourites()
{
QVariantList favs;
Q_FOREACH(auto u, m_favourites) {
favs.append(u);
}
QSettings settings;
settings.setValue("favourite-aircraft", favs);
}

View file

@ -0,0 +1,31 @@
#ifndef FAVOURITEAIRCRAFTDATA_HXX
#define FAVOURITEAIRCRAFTDATA_HXX
#include <QObject>
#include <QUrl>
#include <QVector>
class FavouriteAircraftData : public QObject
{
Q_OBJECT
public:
static FavouriteAircraftData* instance();
bool isFavourite(QUrl u) const;
bool setFavourite(QUrl u, bool b);
signals:
void changed(QUrl u);
private:
FavouriteAircraftData();
void loadFavourites();
void saveFavourites();
QVector<QUrl> m_favourites;
};
#endif // FAVOURITEAIRCRAFTDATA_HXX

View file

@ -45,6 +45,7 @@
#include "QmlPositioned.hxx" #include "QmlPositioned.hxx"
#include "PixmapImageItem.hxx" #include "PixmapImageItem.hxx"
#include "AirportDiagram.hxx" #include "AirportDiagram.hxx"
#include "CarrierDiagram.hxx"
#include "NavaidDiagram.hxx" #include "NavaidDiagram.hxx"
#include "RouteDiagram.hxx" #include "RouteDiagram.hxx"
#include "QmlRadioButtonHelper.hxx" #include "QmlRadioButtonHelper.hxx"
@ -168,6 +169,7 @@ void LauncherController::initQML()
qmlRegisterType<PixmapImageItem>("FlightGear", 1, 0, "PixmapImage"); qmlRegisterType<PixmapImageItem>("FlightGear", 1, 0, "PixmapImage");
qmlRegisterType<AirportDiagram>("FlightGear", 1, 0, "AirportDiagram"); qmlRegisterType<AirportDiagram>("FlightGear", 1, 0, "AirportDiagram");
qmlRegisterType<CarrierDiagram>("FlightGear", 1, 0, "CarrierDiagram");
qmlRegisterType<NavaidDiagram>("FlightGear", 1, 0, "NavaidDiagram"); qmlRegisterType<NavaidDiagram>("FlightGear", 1, 0, "NavaidDiagram");
qmlRegisterType<RouteDiagram>("FlightGear", 1, 0, "RouteDiagram"); qmlRegisterType<RouteDiagram>("FlightGear", 1, 0, "RouteDiagram");
qmlRegisterType<QmlRadioButtonGroup>("FlightGear", 1, 0, "RadioButtonGroup"); qmlRegisterType<QmlRadioButtonGroup>("FlightGear", 1, 0, "RadioButtonGroup");
@ -389,6 +391,10 @@ QString LauncherController::selectAircraftStateAutomatically()
} }
} }
if (m_location->isCarrier() && m_location->isAirborneLocation() && m_selectedAircraftInfo->hasState("carrier-approach")) {
return "carrier-approach";
}
if (m_location->isAirborneLocation() && m_selectedAircraftInfo->hasState("approach")) { if (m_location->isAirborneLocation() && m_selectedAircraftInfo->hasState("approach")) {
return "approach"; return "approach";
} }
@ -400,9 +406,13 @@ QString LauncherController::selectAircraftStateAutomatically()
if (m_selectedAircraftInfo->hasState("parking")) { if (m_selectedAircraftInfo->hasState("parking")) {
return "parking"; return "parking";
} }
} else { }
// also try 'engines-running'?
if (m_selectedAircraftInfo->hasState("take-off")) if (m_location->isCarrier() && m_selectedAircraftInfo->hasState("carrier-take-off")) {
return "carrier-take-off";
}
if (m_selectedAircraftInfo->hasState("take-off")) {
return "take-off"; return "take-off";
} }

View file

@ -39,9 +39,6 @@
static quint32 CACHE_VERSION = 12; static quint32 CACHE_VERSION = 12;
const int STANDARD_THUMBNAIL_HEIGHT = 128;
//const int STANDARD_THUMBNAIL_WIDTH = 172;
AircraftItem::AircraftItem() AircraftItem::AircraftItem()
{ {
} }

View file

@ -31,6 +31,7 @@
#include <simgear/structure/exception.hxx> #include <simgear/structure/exception.hxx>
#include "AirportDiagram.hxx" #include "AirportDiagram.hxx"
#include "CarrierDiagram.hxx"
#include "NavaidDiagram.hxx" #include "NavaidDiagram.hxx"
#include "LaunchConfig.hxx" #include "LaunchConfig.hxx"
#include "DefaultAircraftLocator.hxx" #include "DefaultAircraftLocator.hxx"
@ -226,6 +227,7 @@ void LocationController::clearLocation()
{ {
m_locationIsLatLon = false; m_locationIsLatLon = false;
m_locationIsCarrier = false; m_locationIsCarrier = false;
m_abeam = false;
m_location.clear(); m_location.clear();
m_carrierName.clear(); m_carrierName.clear();
m_airportLocation.clear(); m_airportLocation.clear();
@ -507,6 +509,12 @@ void LocationController::setUseCarrierFLOLS(bool useCarrierFLOLS)
emit configChanged(); emit configChanged();
} }
void LocationController::setAbeam(bool abeam)
{
m_abeam = abeam;
emit configChanged();
}
void LocationController::restoreLocation(QVariantMap l) void LocationController::restoreLocation(QVariantMap l)
{ {
clearLocation(); clearLocation();
@ -533,6 +541,7 @@ void LocationController::restoreLocation(QVariantMap l)
setCarrierLocation(l.value("carrier").toString()); setCarrierLocation(l.value("carrier").toString());
if (l.contains("carrier-flols")) { if (l.contains("carrier-flols")) {
setUseCarrierFLOLS(l.value("carrier-flols").toBool()); setUseCarrierFLOLS(l.value("carrier-flols").toBool());
setAbeam(l.value("abeam").toBool());
// overwrite value form above, intentionally // overwrite value form above, intentionally
m_offsetDistance = l.value("location-carrier-flols-distance", QVariant::fromValue(m_defaultOffsetDistance)).value<QuantityValue>(); m_offsetDistance = l.value("location-carrier-flols-distance", QVariant::fromValue(m_defaultOffsetDistance)).value<QuantityValue>();
} else if (l.contains("carrier-parking")) { } else if (l.contains("carrier-parking")) {
@ -576,6 +585,7 @@ void LocationController::restoreLocation(QVariantMap l)
} }
m_onFinal = l.value("location-on-final").toBool(); m_onFinal = l.value("location-on-final").toBool();
setAbeam(l.value("abeam").toBool());
m_offsetDistance = l.value("location-apt-final-distance", QVariant::fromValue(m_defaultOffsetDistance)).value<QuantityValue>(); m_offsetDistance = l.value("location-apt-final-distance", QVariant::fromValue(m_defaultOffsetDistance)).value<QuantityValue>();
} // of location is an airport } // of location is an airport
} catch (const sg_exception&) { } catch (const sg_exception&) {
@ -619,6 +629,7 @@ QVariantMap LocationController::saveLocation() const
if (m_useCarrierFLOLS) { if (m_useCarrierFLOLS) {
locationSet.insert("carrier-flols", true); locationSet.insert("carrier-flols", true);
locationSet.insert("location-carrier-flols-distance", QVariant::fromValue(m_offsetDistance)); locationSet.insert("location-carrier-flols-distance", QVariant::fromValue(m_offsetDistance));
locationSet.insert("abeam", m_abeam);
} else if (!m_carrierParking.isEmpty()) { } else if (!m_carrierParking.isEmpty()) {
locationSet.insert("carrier-parking", m_carrierParking); locationSet.insert("carrier-parking", m_carrierParking);
} }
@ -628,6 +639,7 @@ QVariantMap LocationController::saveLocation() const
if (m_airportLocation) { if (m_airportLocation) {
locationSet.insert("location-on-final", m_onFinal); locationSet.insert("location-on-final", m_onFinal);
locationSet.insert("location-apt-final-distance", QVariant::fromValue(m_offsetDistance)); locationSet.insert("location-apt-final-distance", QVariant::fromValue(m_offsetDistance));
locationSet.insert("abeam", m_abeam);
if (m_useActiveRunway) { if (m_useActiveRunway) {
locationSet.insert("location-apt-runway", "ACTIVE"); locationSet.insert("location-apt-runway", "ACTIVE");
} else if (m_useAvailableParking) { } else if (m_useAvailableParking) {
@ -675,7 +687,7 @@ void LocationController::setLocationProperties()
"runway-requested" << "navaid-id" << "offset-azimuth-deg" << "runway-requested" << "navaid-id" << "offset-azimuth-deg" <<
"offset-distance-nm" << "glideslope-deg" << "offset-distance-nm" << "glideslope-deg" <<
"speed-set" << "on-ground" << "airspeed-kt" << "speed-set" << "on-ground" << "airspeed-kt" <<
"airport-id" << "runway" << "parkpos" << "carrier"; "airport-id" << "runway" << "parkpos" << "carrier" << "abeam";
Q_FOREACH(QString s, props) { Q_FOREACH(QString s, props) {
SGPropertyNode* c = presets->getChild(s.toStdString()); SGPropertyNode* c = presets->getChild(s.toStdString());
@ -709,7 +721,8 @@ void LocationController::setLocationProperties()
// treat the FLOLS as a runway, for the purposes of communication with position-init // treat the FLOLS as a runway, for the purposes of communication with position-init
fgSetString("/sim/presets/runway", "FLOLS"); fgSetString("/sim/presets/runway", "FLOLS");
fgSetDouble("/sim/presets/offset-distance-nm", m_offsetDistance.convertToUnit(Units::NauticalMiles).value); fgSetDouble("/sim/presets/offset-distance-nm", m_offsetDistance.convertToUnit(Units::NauticalMiles).value);
fgSetBool("/sim/presets/abeam", m_abeam);
applyAltitude();
applyAirspeed(); applyAirspeed();
} else if (!m_carrierParking.isEmpty()) { } else if (!m_carrierParking.isEmpty()) {
fgSetString("/sim/presets/parkpos", m_carrierParking.toStdString()); fgSetString("/sim/presets/parkpos", m_carrierParking.toStdString());
@ -730,6 +743,7 @@ void LocationController::setLocationProperties()
fgSetString("/sim/presets/airport-id", m_airportLocation->ident()); fgSetString("/sim/presets/airport-id", m_airportLocation->ident());
fgSetBool("/sim/presets/on-ground", true); fgSetBool("/sim/presets/on-ground", true);
fgSetBool("/sim/presets/airport-requested", true); fgSetBool("/sim/presets/airport-requested", true);
fgSetBool("/sim/presets/abeam", m_abeam);
const bool onRunway = (m_detailLocation && (m_detailLocation->type() == FGPositioned::RUNWAY)); const bool onRunway = (m_detailLocation && (m_detailLocation->type() == FGPositioned::RUNWAY));
const bool atParking = (m_detailLocation && (m_detailLocation->type() == FGPositioned::PARKING)); const bool atParking = (m_detailLocation && (m_detailLocation->type() == FGPositioned::PARKING));
@ -847,7 +861,7 @@ void LocationController::applyAltitude()
switch (m_altitude.unit) { switch (m_altitude.unit) {
default: default:
qWarning() << Q_FUNC_INFO << "unsupported altitdue unit"; qWarning() << Q_FUNC_INFO << "unsupported altitude unit";
break; break;
case Units::FeetMSL: case Units::FeetMSL:
m_config->setArg("altitude", QString::number(m_altitude.value)); m_config->setArg("altitude", QString::number(m_altitude.value));
@ -915,6 +929,9 @@ void LocationController::onCollectConfig()
m_config->setArg("runway", QStringLiteral("FLOLS")); m_config->setArg("runway", QStringLiteral("FLOLS"));
const double offsetNm = m_offsetDistance.convertToUnit(Units::NauticalMiles).value; const double offsetNm = m_offsetDistance.convertToUnit(Units::NauticalMiles).value;
m_config->setArg("offset-distance", QString::number(offsetNm)); m_config->setArg("offset-distance", QString::number(offsetNm));
if (m_abeam) m_config->setArg("carrier-abeam", QStringLiteral("true"));
applyAltitude();
applyAirspeed(); applyAirspeed();
} }
@ -1029,6 +1046,8 @@ QString compassPointFromHeading(int heading)
QString LocationController::description() const QString LocationController::description() const
{ {
const double offsetNm = m_offsetDistance.convertToUnit(Units::NauticalMiles).value;
if (!m_location) { if (!m_location) {
if (m_locationIsLatLon) { if (m_locationIsLatLon) {
const auto s = simgear::strutils::formatGeodAsString(m_geodLocation, const auto s = simgear::strutils::formatGeodAsString(m_geodLocation,
@ -1039,7 +1058,15 @@ QString LocationController::description() const
if (m_locationIsCarrier) { if (m_locationIsCarrier) {
QString pennant = m_carriersModel->pennantForIndex(m_carriersModel->indexOf(m_carrierName)); QString pennant = m_carriersModel->pennantForIndex(m_carriersModel->indexOf(m_carrierName));
return tr("on carrier %1 (%2)").arg(m_carrierName).arg(pennant); QString locationToCarrier;
if (m_abeam) {
locationToCarrier = tr("%1nm abeam").arg(offsetNm);
} else if (m_useCarrierFLOLS) {
locationToCarrier = tr("on %1nm final to").arg(offsetNm);
} else {
locationToCarrier = tr("on deck at %1 on").arg(m_carrierParking);
}
return tr("%1 carrier %2 (%3)").arg(locationToCarrier).arg(m_carrierName).arg(pennant);
} }
return tr("No location selected"); return tr("No location selected");
@ -1049,7 +1076,6 @@ QString LocationController::description() const
name = QString::fromStdString(m_location->name()); name = QString::fromStdString(m_location->name());
name = fixNavaidName(name); name = fixNavaidName(name);
const double offsetNm = m_offsetDistance.convertToUnit(Units::NauticalMiles).value;
if (m_airportLocation) { if (m_airportLocation) {
const bool onRunway = (m_detailLocation && (m_detailLocation->type() == FGPositioned::RUNWAY)); const bool onRunway = (m_detailLocation && (m_detailLocation->type() == FGPositioned::RUNWAY));

View file

@ -75,6 +75,7 @@ class LocationController : public QObject
Q_PROPERTY(QStringList carrierParkings READ carrierParkings NOTIFY baseLocationChanged) Q_PROPERTY(QStringList carrierParkings READ carrierParkings NOTIFY baseLocationChanged)
Q_PROPERTY(bool useCarrierFLOLS READ useCarrierFLOLS WRITE setUseCarrierFLOLS NOTIFY configChanged) Q_PROPERTY(bool useCarrierFLOLS READ useCarrierFLOLS WRITE setUseCarrierFLOLS NOTIFY configChanged)
Q_PROPERTY(QString carrierParking READ carrierParking WRITE setCarrierParking NOTIFY configChanged) Q_PROPERTY(QString carrierParking READ carrierParking WRITE setCarrierParking NOTIFY configChanged)
Q_PROPERTY(bool abeam READ abeam WRITE setAbeam NOTIFY configChanged)
// allow collecting the location properties to be disabled, if the // allow collecting the location properties to be disabled, if the
@ -189,6 +190,11 @@ public:
return m_useCarrierFLOLS; return m_useCarrierFLOLS;
} }
bool abeam() const
{
return m_abeam;
}
public slots: public slots:
void setOffsetRadial(QuantityValue offsetRadial); void setOffsetRadial(QuantityValue offsetRadial);
@ -204,6 +210,8 @@ public slots:
void setUseCarrierFLOLS(bool useCarrierFLOLS); void setUseCarrierFLOLS(bool useCarrierFLOLS);
void setAbeam(bool abeam);
Q_SIGNALS: Q_SIGNALS:
void descriptionChanged(); void descriptionChanged();
void offsetChanged(); void offsetChanged();
@ -261,6 +269,7 @@ private:
bool m_speedEnabled = false; bool m_speedEnabled = false;
bool m_altitudeEnabled = false; bool m_altitudeEnabled = false;
bool m_skipFromArgs = false; bool m_skipFromArgs = false;
bool m_abeam;
bool m_useCarrierFLOLS = false; bool m_useCarrierFLOLS = false;
QString m_carrierParking; QString m_carrierParking;

View file

@ -43,7 +43,10 @@ QVariant ModelDataExtractor::data() const
return m_rawModel.property(uIndex).toVariant(); return m_rawModel.property(uIndex).toVariant();
} }
if (!m_rawModel.isUndefined() && !m_rawModel.isNull()) {
qWarning() << "Unable to convert model data:" << m_rawModel.toString(); qWarning() << "Unable to convert model data:" << m_rawModel.toString();
}
return {}; return {};
} }

View file

@ -14,6 +14,7 @@
#include <Main/globals.hxx> #include <Main/globals.hxx>
#include "LocalAircraftCache.hxx" #include "LocalAircraftCache.hxx"
#include "FavouriteAircraftData.hxx"
using namespace simgear::pkg; using namespace simgear::pkg;
@ -36,6 +37,8 @@ public:
} }
protected: protected:
void finishInstall(InstallRef aInstall, StatusCode aReason) override;
void catalogRefreshed(CatalogRef, StatusCode) override void catalogRefreshed(CatalogRef, StatusCode) override
{ {
} }
@ -55,19 +58,9 @@ protected:
} }
} }
void finishInstall(InstallRef aInstall, StatusCode aReason) override
{
Q_UNUSED(aReason);
if (aInstall->package() == p->packageRef()) {
p->_cachedProps.reset();
p->checkForStates();
p->infoChanged();
}
}
void installStatusChanged(InstallRef aInstall, StatusCode aReason) override void installStatusChanged(InstallRef aInstall, StatusCode aReason) override
{ {
Q_UNUSED(aReason); Q_UNUSED(aReason)
if (aInstall->package() == p->packageRef()) { if (aInstall->package() == p->packageRef()) {
p->downloadChanged(); p->downloadChanged();
} }
@ -85,6 +78,16 @@ private:
QmlAircraftInfo* p; QmlAircraftInfo* p;
}; };
void QmlAircraftInfo::Delegate::finishInstall(InstallRef aInstall, StatusCode aReason)
{
Q_UNUSED(aReason)
if (aInstall->package() == p->packageRef()) {
p->_cachedProps.reset();
p->checkForStates();
p->infoChanged();
}
}
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@ -141,7 +144,7 @@ QString humanNameFromStateTag(const std::string& tag)
{ {
if (tag == "approach") return QObject::tr("On approach"); if (tag == "approach") return QObject::tr("On approach");
if ((tag == "take-off") || (tag == "takeoff")) if ((tag == "take-off") || (tag == "takeoff"))
return QObject::tr("Ready for Take-off"); return QObject::tr("Ready for take-off");
if ((tag == "parked") || (tag == "parking") || (tag == "cold-and-dark")) if ((tag == "parked") || (tag == "parking") || (tag == "cold-and-dark"))
return QObject::tr("Parked, cold & dark"); return QObject::tr("Parked, cold & dark");
if (tag == "auto") if (tag == "auto")
@ -150,6 +153,10 @@ QString humanNameFromStateTag(const std::string& tag)
return QObject::tr("Cruise"); return QObject::tr("Cruise");
if (tag == "taxi") if (tag == "taxi")
return QObject::tr("Ready to taxi"); return QObject::tr("Ready to taxi");
if (tag == "carrier-approach")
return QObject::tr("On approach to a carrier");
if (tag == "carrier-take-off")
return QObject::tr("Ready for catapult launch");
qWarning() << Q_FUNC_INFO << "add translation / string for" << QString::fromStdString(tag); qWarning() << Q_FUNC_INFO << "add translation / string for" << QString::fromStdString(tag);
// no mapping, let's use the tag directly // no mapping, let's use the tag directly
@ -299,6 +306,8 @@ QmlAircraftInfo::QmlAircraftInfo(QObject *parent)
, _delegate(new Delegate(this)) , _delegate(new Delegate(this))
{ {
qmlRegisterUncreatableType<StatesModel>("FlightGear.Launcher", 1, 0, "StatesModel", "no"); qmlRegisterUncreatableType<StatesModel>("FlightGear.Launcher", 1, 0, "StatesModel", "no");
connect(FavouriteAircraftData::instance(), &FavouriteAircraftData::changed,
this, &QmlAircraftInfo::onFavouriteChanged);
} }
QmlAircraftInfo::~QmlAircraftInfo() QmlAircraftInfo::~QmlAircraftInfo()
@ -441,7 +450,7 @@ QVariantList QmlAircraftInfo::previews() const
for (auto p : previews) { for (auto p : previews) {
SGPath localPreviewPath = ex->path() / p.path; SGPath localPreviewPath = ex->path() / p.path;
if (!localPreviewPath.exists()) { if (!localPreviewPath.exists()) {
qWarning() << "missing local preview" << QString::fromStdString(localPreviewPath.utf8Str()); // this happens when the aircraft is being installed, for example
continue; continue;
} }
result.append(QUrl::fromLocalFile(QString::fromStdString(localPreviewPath.utf8Str()))); result.append(QUrl::fromLocalFile(QString::fromStdString(localPreviewPath.utf8Str())));
@ -667,6 +676,7 @@ void QmlAircraftInfo::setUri(QUrl u)
emit uriChanged(); emit uriChanged();
emit infoChanged(); emit infoChanged();
emit downloadChanged(); emit downloadChanged();
emit favouriteChanged();
} }
void QmlAircraftInfo::setVariant(quint32 variant) void QmlAircraftInfo::setVariant(quint32 variant)
@ -691,6 +701,19 @@ void QmlAircraftInfo::setVariant(quint32 variant)
emit variantChanged(_variant); emit variantChanged(_variant);
} }
void QmlAircraftInfo::setFavourite(bool favourite)
{
FavouriteAircraftData::instance()->setFavourite(uri(), favourite);
}
void QmlAircraftInfo::onFavouriteChanged(QUrl u)
{
if (u != uri())
return;
emit favouriteChanged();
}
QVariant QmlAircraftInfo::packageAircraftStatus(simgear::pkg::PackageRef p) QVariant QmlAircraftInfo::packageAircraftStatus(simgear::pkg::PackageRef p)
{ {
if (p->hasTag("needs-maintenance")) { if (p->hasTag("needs-maintenance")) {
@ -876,5 +899,9 @@ bool QmlAircraftInfo::hasTag(QString tag) const
return false; return false;
} }
#include "QmlAircraftInfo.moc" bool QmlAircraftInfo::favourite() const
{
return FavouriteAircraftData::instance()->isFavourite(uri());
}
#include "QmlAircraftInfo.moc"

View file

@ -59,6 +59,7 @@ class QmlAircraftInfo : public QObject
Q_PROPERTY(bool hasStates READ hasStates NOTIFY infoChanged) Q_PROPERTY(bool hasStates READ hasStates NOTIFY infoChanged)
Q_PROPERTY(StatesModel* statesModel READ statesModel NOTIFY infoChanged) Q_PROPERTY(StatesModel* statesModel READ statesModel NOTIFY infoChanged)
Q_PROPERTY(bool favourite READ favourite WRITE setFavourite NOTIFY favouriteChanged)
public: public:
explicit QmlAircraftInfo(QObject *parent = nullptr); explicit QmlAircraftInfo(QObject *parent = nullptr);
virtual ~QmlAircraftInfo(); virtual ~QmlAircraftInfo();
@ -127,11 +128,14 @@ public:
Q_INVOKABLE bool isAltitudeBelowLimits(QuantityValue speed) const; Q_INVOKABLE bool isAltitudeBelowLimits(QuantityValue speed) const;
Q_INVOKABLE bool hasTag(QString tag) const; Q_INVOKABLE bool hasTag(QString tag) const;
bool favourite() const;
signals: signals:
void uriChanged(); void uriChanged();
void infoChanged(); void infoChanged();
void downloadChanged(); void downloadChanged();
void variantChanged(quint32 variant); void variantChanged(quint32 variant);
void favouriteChanged();
public slots: public slots:
@ -139,6 +143,10 @@ public slots:
void setVariant(quint32 variant); void setVariant(quint32 variant);
void setFavourite(bool favourite);
private slots:
void onFavouriteChanged(QUrl u);
private: private:
AircraftItemPtr resolveItem() const; AircraftItemPtr resolveItem() const;
void checkForStates(); void checkForStates();

View file

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="87.149193"
height="37.111004"
viewBox="0 0 461.16447 196.37906"
version="1.1"
id="svg8"
inkscape:export-filename="/home/stuart/bitmap.png"
inkscape:export-xdpi="2.8"
inkscape:export-ydpi="2.8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="aircraft-carrier-icon.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4"
inkscape:cx="82.435073"
inkscape:cy="-17.019439"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="2493"
inkscape:window-height="1385"
inkscape:window-x="67"
inkscape:window-y="27"
inkscape:window-maximized="1"
fit-margin-top="5"
fit-margin-left="5"
fit-margin-right="5"
fit-margin-bottom="5"
units="px"
scale-x="10" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
style="display:inline"
transform="translate(319.16852,-98.581295)">
<path
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#009bff;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -290.00765,177.53052 7.30519,71.36096 55.25252,16.03618 215.076885,1.06907 30.407428,-31.5378 94.930487,-12.2944 -1.4833,-45.43582 -94.559656,-10.69078 -31.890713,-38.48681 -219.155941,2.13815 -8.15808,34.74504 z"
id="Outline"
inkscape:connector-curvature="0"
inkscape:label="#path826"
sodipodi:nodetypes="cccccccccccc" />
<path
style="fill:#009bff;fill-opacity:1;fill-rule:evenodd;stroke:#009bff;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -82.210854,265.48522 0.200595,-46.99016 h -24.786801 l -0.73387,13.23009 -59.39478,-0.54281 -0.6013,33.61805 z"
id="Island"
inkscape:connector-curvature="0"
inkscape:label="#path828"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#009aff;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -286.7815,192.23034 4.44986,40.62497"
id="path832"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#009aff;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -14.968809,127.55112 -271.812691,64.67922 2.97333,48.86685 300.730074,-75.05926"
id="LandingMarks"
inkscape:connector-curvature="0"
inkscape:label="#path834"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#009aff;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M -47.355536,198.09657 100.08338,193.60644"
id="Cat1"
inkscape:connector-curvature="0"
inkscape:label="#path836" />
<path
style="fill:none;fill-rule:evenodd;stroke:#009aff;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M -44.09832,226.88876 102.04272,209.30242"
id="Cat2"
inkscape:connector-curvature="0"
inkscape:label="#path836"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#009aff;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:29.99999994, 4.99999999;stroke-dashoffset:0;stroke-opacity:1"
d="M -273.57231,212.56627 -18.148692,149.13964"
id="Centerline"
inkscape:connector-curvature="0"
inkscape:label="#path853"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -155,9 +155,20 @@ Rectangle {
visible: aircraft.previews.length > 0 visible: aircraft.previews.length > 0
} }
Row {
height: ratingGrid.height
width: parent.width
spacing: Style.strutSize
FavouriteToggleButton {
checked: aircraft.favourite
onToggle: {
aircraft.favourite = on;
}
}
Grid { Grid {
id: ratingGrid id: ratingGrid
anchors.left: parent.left
visible: aircraft.ratings !== undefined visible: aircraft.ratings !== undefined
@ -208,6 +219,7 @@ Rectangle {
value: aircraft.ratings[3] value: aircraft.ratings[3]
} }
} }
} // of rating grid
} }
StyledText { StyledText {

View file

@ -109,6 +109,10 @@ Item {
return "%1 - %2 (%3 MHz)".arg(model.ident).arg(model.name).arg(freq); return "%1 - %2 (%3 MHz)".arg(model.ident).arg(model.name).arg(freq);
} }
if (model.type === "Carrier") {
return "%1 - %2\n%3".arg(model.ident).arg(model.name).arg(model.description);
}
// general case // general case
return "%1 - %2".arg(model.ident).arg(model.name); return "%1 - %2".arg(model.ident).arg(model.name);
} }
@ -225,7 +229,13 @@ Item {
icon: "qrc:///svg/icon-carrier" icon: "qrc:///svg/icon-carrier"
onClicked: { onClicked: {
root.showCarriers = true; root.showCarriers = ! root.showCarriers;
if (root.showCarriers) {
this.icon = "qrc:///svg/icon-airport"
} else {
this.icon = "qrc:///svg/icon-carrier"
}
} }
} }

View file

@ -6,14 +6,13 @@ import "."
Item { Item {
property alias geod: diagram.geod property alias geod: diagram.geod
NavaidDiagram { CarrierDiagram {
id: diagram id: diagram
anchors.fill: parent anchors.fill: parent
offsetEnabled: _location.offsetEnabled offsetEnabled: _location.useCarrierFLOLS
offsetBearing: _location.offsetRadial
offsetDistance: _location.offsetDistance offsetDistance: _location.offsetDistance
heading: _location.heading abeam: _location.abeam
} }
Component.onCompleted: { Component.onCompleted: {
@ -22,7 +21,9 @@ Item {
function syncUIFromController() function syncUIFromController()
{ {
if (_location.useCarrierFLOLS) { if (_location.abeam) {
abeamRadio.select()
} else if (_location.useCarrierFLOLS) {
flolsRadio.select() flolsRadio.select()
} else { } else {
parkingRadio.select(); parkingRadio.select();
@ -46,7 +47,7 @@ Item {
height: selectionGrid.height + Style.margin * 2 height: selectionGrid.height + Style.margin * 2
// set opacity here only, so we don't make the whole summary pannel translucent // set opacity here only, so we don't make the whole summary panel translucent
Rectangle { Rectangle {
id: background id: background
anchors.fill: parent anchors.fill: parent
@ -76,7 +77,7 @@ Item {
font.pixelSize: Style.headingFontPixelSize font.pixelSize: Style.headingFontPixelSize
} }
// on FLOLS offset // on final approach
Row { Row {
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: Style.margin anchors.leftMargin: Style.margin
@ -89,9 +90,12 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
group: radioGroup group: radioGroup
onClicked: { onClicked: {
if (selected) _location.useCarrierFLOLS = selected if (selected) {
_location.useCarrierFLOLS = selected;
_location.abeam = false;
} }
selected: _location.useCarrierFLOLS }
selected: _location.useCarrierFLOLS && (!_location.abeam)
} }
StyledText { StyledText {
@ -101,6 +105,47 @@ Item {
} }
}
// Abeam the FLOLS
Row {
anchors.left: parent.left
anchors.leftMargin: Style.margin
anchors.right: parent.right
anchors.rightMargin: Style.margin
spacing: Style.margin
RadioButton {
id: abeamRadio
anchors.verticalCenter: parent.verticalCenter
group: radioGroup
onClicked: {
if (selected) _location.abeam = _location.useCarrierFLOLS = true
}
selected: _location.abeam
}
StyledText {
text: qsTr("Abeam carrier at 180 degrees")
anchors.verticalCenter: parent.verticalCenter
enabled: abeamRadio.selected
}
}
// Offset selection
readonly property bool offsetEnabled: (flolsRadio.selected || abeamRadio.selected)
Row {
anchors.left: parent.left
anchors.leftMargin: Style.margin
anchors.right: parent.right
anchors.rightMargin: Style.margin
spacing: Style.margin
Item {
height: 1; width: Style.strutSize
}
NumericalEdit { NumericalEdit {
id: offsetNmEdit id: offsetNmEdit
quantity: _location.offsetDistance quantity: _location.offsetDistance
@ -109,13 +154,13 @@ Item {
unitsMode: Units.Distance unitsMode: Units.Distance
live: true live: true
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
enabled: flolsRadio.selected enabled: selectionGrid.offsetEnabled
} }
StyledText { StyledText {
text: qsTr(" from the FLOLS (aka the ball)") text: qsTr(" from the FLOLS (aka the ball)")
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
enabled: flolsRadio.selected enabled: selectionGrid.offsetEnabled
} }
Item { Item {
@ -124,7 +169,7 @@ Item {
ToggleSwitch { ToggleSwitch {
id: airspeedToggle id: airspeedToggle
enabled: flolsRadio.selected enabled: selectionGrid.offsetEnabled
checked: _location.speedEnabled checked: _location.speedEnabled
onCheckedChanged: _location.speedEnabled = checked; onCheckedChanged: _location.speedEnabled = checked;
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -134,12 +179,23 @@ Item {
id: airspeedSpinbox id: airspeedSpinbox
label: qsTr("Airspeed:") label: qsTr("Airspeed:")
unitsMode: Units.SpeedWithoutMach unitsMode: Units.SpeedWithoutMach
enabled: _location.speedEnabled && flolsRadio.selected enabled: _location.speedEnabled && selectionGrid.offsetEnabled
quantity: _location.airspeed quantity: _location.airspeed
onCommit: _location.airspeed = newValue onCommit: _location.airspeed = newValue
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
} // of FLOLS row
Item {
height: 1; width: Style.strutSize
}
LocationAltitudeRow
{
enabled: selectionGrid.offsetEnabled
width: parent.width
}
} // of Offset selection
// parking row // parking row
Row { Row {
@ -159,10 +215,11 @@ Item {
onClicked: { onClicked: {
if (selected) parkingChoice.setLocation(); if (selected) parkingChoice.setLocation();
} }
selected : (! _location.abeam) && (! _location.useCarrierFLOLS)
} }
StyledText { StyledText {
text: qsTr("Parking") text: qsTr("On deck")
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
enabled: parkingRadio.selected enabled: parkingRadio.selected
} }

View file

@ -90,8 +90,9 @@ Item {
var defaultValue = ("defaultValue" in root) ? root.defaultValue : undefined; var defaultValue = ("defaultValue" in root) ? root.defaultValue : undefined;
var rawValue = _config.getValueForKey("", root.setting, defaultValue); var rawValue = _config.getValueForKey("", root.setting, defaultValue);
// console.warn("restoring state for " + root.setting + ", got raw value " + rawValue + " with type " + typeof(rawValue)) // console.warn("restoring state for " + root.setting + ", got raw value " + rawValue + " with type " + typeof(rawValue))
if (rawValue !== undefined) { if (rawValue !== defaultValue) {
setValue(rawValue); setValue(rawValue);
} }
} }
@ -99,6 +100,6 @@ Item {
function setValue(newValue) function setValue(newValue)
{ {
// hook method so controls can override // hook method so controls can override
this.value = newValue root.value = newValue
} }
} }

View file

@ -155,6 +155,7 @@
<file alias="icon-list-view">assets/icons8-menu.svg</file> <file alias="icon-list-view">assets/icons8-menu.svg</file>
<file alias="icon-hide">assets/icons8-hide-50.png</file> <file alias="icon-hide">assets/icons8-hide-50.png</file>
<file alias="icon-carrier">assets/icons8-cargo-ship-50.png</file> <file alias="icon-carrier">assets/icons8-cargo-ship-50.png</file>
<file alias="aircraft-carrier">assets/aircraft-carrier-icon.png</file> <file alias="icon-airport">assets/icons8-airport-50.png</file>
<file alias="aircraft-carrier">assets/aircraft-carrier-icon.svg</file>
</qresource> </qresource>
</RCC> </RCC>

View file

@ -70,3 +70,5 @@
#cmakedefine ENABLE_COMPOSITOR #cmakedefine ENABLE_COMPOSITOR
#cmakedefine ENABLE_SWIFT #cmakedefine ENABLE_SWIFT
#cmakedefine HAVE_SENTRY

View file

@ -1,8 +1,7 @@
include(FlightGearComponent) include(FlightGearComponent)
IF(APPLE) IF(APPLE)
set(EVENT_INPUT_SOURCES FGMacOSXEventInput.cxx) # no Mac implemention, use HID
set(EVENT_INPUT_HEADERS FGMacOSXEventInput.hxx)
elseif(WIN32) elseif(WIN32)
# no Win32 specific implementation, at least for now # no Win32 specific implementation, at least for now
else() else()

View file

@ -176,12 +176,12 @@ void FGAxisEvent::fire( FGEventData & eventData )
// We need a copy of the FGEventData struct to set the new value and to avoid side effects // We need a copy of the FGEventData struct to set the new value and to avoid side effects
FGEventData ed = eventData; FGEventData ed = eventData;
if( fabs(ed.value) < deadband )
ed.value = 0.0;
if( minRange != maxRange ) if( minRange != maxRange )
ed.value = 2.0*(eventData.value-minRange)/(maxRange-minRange)-1.0; ed.value = 2.0*(eventData.value-minRange)/(maxRange-minRange)-1.0;
if( fabs(ed.value) < deadband )
ed.value = 0.0;
if (interpolater) { if (interpolater) {
if ((ed.value < 0.0) && mirrorInterpolater) { if ((ed.value < 0.0) && mirrorInterpolater) {
// mirror the positive interpolation for negative values // mirror the positive interpolation for negative values

View file

@ -312,12 +312,16 @@ private:
std::string _hidPath; std::string _hidPath;
hid_device* _device = nullptr; hid_device* _device = nullptr;
bool _haveNumberedReports = false; bool _haveNumberedReports = false;
bool _debugRaw = false;
/// set if we parsed the device description our XML /// set if we parsed the device description our XML
/// instead of from the USB data. Useful on Windows where the data /// instead of from the USB data. Useful on Windows where the data
/// is inaccessible, or devices with broken descriptors /// is inaccessible, or devices with broken descriptors
bool _haveLocalDescriptor = false; bool _haveLocalDescriptor = false;
/// allow specifying the descriptor as hex bytes in XML
std::vector<uint8_t>_rawXMLDescriptor;
// all sets which will be send on the next update() call. // all sets which will be send on the next update() call.
std::set<Report*> _dirtyReports; std::set<Report*> _dirtyReports;
}; };
@ -384,6 +388,17 @@ void FGHIDDevice::Configure(SGPropertyNode_ptr node)
defineReport(report); defineReport(report);
} }
} }
if (node->hasChild("hid-raw-descriptor")) {
_rawXMLDescriptor = simgear::strutils::decodeHex(node->getStringValue("hid-raw-descriptor"));
if (debugEvents) {
SG_LOG(SG_INPUT, SG_INFO, GetUniqueName() << " will configure using XML-defined raw HID descriptor");
}
}
if (node->getBoolValue("hid-debug-raw")) {
_debugRaw = true;
}
} }
bool FGHIDDevice::Open() bool FGHIDDevice::Open()
@ -395,6 +410,19 @@ bool FGHIDDevice::Open()
return false; return false;
} }
#if !defined(SG_WINDOWS)
if (_rawXMLDescriptor.empty()) {
_rawXMLDescriptor.resize(2048);
int descriptorSize = hid_get_descriptor(_device, _rawXMLDescriptor.data(), _rawXMLDescriptor.size());
if (descriptorSize <= 0) {
SG_LOG(SG_INPUT, SG_WARN, "HID: " << GetUniqueName() << " failed to read HID descriptor");
return false;
}
_rawXMLDescriptor.resize(descriptorSize);
}
#endif
if (!_haveLocalDescriptor) { if (!_haveLocalDescriptor) {
bool ok = parseUSBHIDDescriptor(); bool ok = parseUSBHIDDescriptor();
if (!ok) if (!ok)
@ -422,26 +450,22 @@ bool FGHIDDevice::Open()
bool FGHIDDevice::parseUSBHIDDescriptor() bool FGHIDDevice::parseUSBHIDDescriptor()
{ {
#if defined(SG_WINDOWS) #if defined(SG_WINDOWS)
if (_rawXMLDescriptor.empty()) {
SG_LOG(SG_INPUT, SG_ALERT, GetUniqueName() << ": on Windows, there is no way to extract the UDB-HID report descriptor. " SG_LOG(SG_INPUT, SG_ALERT, GetUniqueName() << ": on Windows, there is no way to extract the UDB-HID report descriptor. "
<< "\nPlease supply the report descriptor in the device XML configuration."); << "\nPlease supply the report descriptor in the device XML configuration.");
return false; SG_LOG(SG_INPUT, SG_ALERT, "See this page:<> for information on extracting the report descriptor on Windows");
#endif
unsigned char reportDescriptor[1024];
int descriptorSize = hid_get_descriptor(_device, reportDescriptor, 1024);
if (descriptorSize <= 0) {
SG_LOG(SG_INPUT, SG_WARN, "HID: " << GetUniqueName() << " failed to read HID descriptor");
return false; return false;
} }
#endif
if (debugEvents) { if (_debugRaw) {
SG_LOG(SG_INPUT, SG_INFO, "\nHID: descriptor for:" << GetUniqueName()); SG_LOG(SG_INPUT, SG_INFO, "\nHID: descriptor for:" << GetUniqueName());
{ {
std::ostringstream byteString; std::ostringstream byteString;
for (int i=0; i<descriptorSize; ++i) { for (auto i=0; i<_rawXMLDescriptor.size(); ++i) {
byteString << hexTable[reportDescriptor[i] >> 4]; byteString << hexTable[_rawXMLDescriptor[i] >> 4];
byteString << hexTable[reportDescriptor[i] & 0x0f]; byteString << hexTable[_rawXMLDescriptor[i] & 0x0f];
byteString << " "; byteString << " ";
} }
SG_LOG(SG_INPUT, SG_INFO, "\tbytes: " << byteString.str()); SG_LOG(SG_INPUT, SG_INFO, "\tbytes: " << byteString.str());
@ -449,7 +473,7 @@ bool FGHIDDevice::parseUSBHIDDescriptor()
} }
hid_item* rootItem = nullptr; hid_item* rootItem = nullptr;
hid_parse_reportdesc(reportDescriptor, descriptorSize, &rootItem); hid_parse_reportdesc(_rawXMLDescriptor.data(), _rawXMLDescriptor.size(), &rootItem);
if (debugEvents) { if (debugEvents) {
SG_LOG(SG_INPUT, SG_INFO, "\nHID: scan for:" << GetUniqueName()); SG_LOG(SG_INPUT, SG_INFO, "\nHID: scan for:" << GetUniqueName());
} }
@ -645,6 +669,18 @@ void FGHIDDevice::sendReport(Report* report) const
} }
reportLength /= 8; reportLength /= 8;
if (_debugRaw) {
std::ostringstream byteString;
for (size_t i=0; i<reportLength; ++i) {
byteString << hexTable[reportBytes[i] >> 4];
byteString << hexTable[reportBytes[i] & 0x0f];
byteString << " ";
}
SG_LOG(SG_INPUT, SG_INFO, "sending bytes: " << byteString.str());
}
// send the data, based on the report type // send the data, based on the report type
if (report->type == HID::ReportType::Feature) { if (report->type == HID::ReportType::Feature) {
hid_send_feature_report(_device, reportBytes, reportLength + 1); hid_send_feature_report(_device, reportBytes, reportLength + 1);
@ -663,7 +699,7 @@ void FGHIDDevice::processInputReport(Report* report, unsigned char* data,
size_t length, size_t length,
double dt, int keyModifiers) double dt, int keyModifiers)
{ {
if (debugEvents) { if (_debugRaw) {
SG_LOG(SG_INPUT, SG_INFO, GetName() << " FGHIDDeivce received input report:" << (int) report->number << ", len=" << length); SG_LOG(SG_INPUT, SG_INFO, GetName() << " FGHIDDeivce received input report:" << (int) report->number << ", len=" << length);
{ {
std::ostringstream byteString; std::ostringstream byteString;
@ -698,7 +734,7 @@ void FGHIDDevice::processInputReport(Report* report, unsigned char* data,
if (!item->event) if (!item->event)
continue; continue;
if (debugEvents) { if (_debugRaw) {
SG_LOG(SG_INPUT, SG_INFO, "\titem:" << item->name << " = " << value); SG_LOG(SG_INPUT, SG_INFO, "\titem:" << item->name << " = " << value);
} }
@ -713,7 +749,7 @@ void FGHIDDevice::SendFeatureReport(unsigned int reportId, const std::string& da
return; return;
} }
if (debugEvents) { if (_debugRaw) {
SG_LOG(SG_INPUT, SG_INFO, GetName() << ": FGHIDDevice: Sending feature report:" << (int) reportId << ", len=" << data.size()); SG_LOG(SG_INPUT, SG_INFO, GetName() << ": FGHIDDevice: Sending feature report:" << (int) reportId << ", len=" << data.size());
{ {
std::ostringstream byteString; std::ostringstream byteString;

View file

@ -1,650 +0,0 @@
// FGMacOSXEventInput.cxx -- handle event driven input devices for Mac OS X
//
// Written by Tatsuhiro Nishioka, started Aug. 2009.
//
// Copyright (C) 2009 Tasuhiro Nishioka, tat <dot> fgmacosx <at> gmail <dot> com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include "FGMacOSXEventInput.hxx"
#include <simgear/sg_inlines.h>
#include <cstdlib>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/hid/IOHIDLib.h>
#include <IOKit/HID/IOHIDKeys.h>
#include <simgear/structure/exception.hxx>
static std::string nameForUsage(uint32_t usagePage, uint32_t usage)
{
if (usagePage == kHIDPage_GenericDesktop) {
switch (usage) {
case kHIDUsage_GD_Joystick: return "joystick";
case kHIDUsage_GD_Wheel: return "wheel";
case kHIDUsage_GD_Dial: return "dial";
case kHIDUsage_GD_Hatswitch: return "hat";
case kHIDUsage_GD_Slider: return "slider";
case kHIDUsage_GD_Rx: return "x-rotate";
case kHIDUsage_GD_Ry: return "y-rotate";
case kHIDUsage_GD_Rz: return "z-rotate";
case kHIDUsage_GD_X: return "x-translate";
case kHIDUsage_GD_Y: return "y-translate";
case kHIDUsage_GD_Z: return "z-translate";
default:
SG_LOG(SG_INPUT, SG_WARN, "Unhandled HID generic desktop usage:" << usage);
}
} else if (usagePage == kHIDPage_Simulation) {
switch (usage) {
default:
SG_LOG(SG_INPUT, SG_WARN, "Unhandled HID simulation usage:" << usage);
}
} else if (usagePage == kHIDPage_Consumer) {
switch (usage) {
default:
SG_LOG(SG_INPUT, SG_WARN, "Unhandled HID consumer usage:" << usage);
}
} else if (usagePage == kHIDPage_AlphanumericDisplay) {
switch (usage) {
case kHIDUsage_AD_AlphanumericDisplay: return "alphanumeric";
case kHIDUsage_AD_CharacterReport: return "character-report";
case kHIDUsage_AD_DisplayData: return "display-data";
case 0x46: return "display-brightness";
default:
SG_LOG(SG_INPUT, SG_WARN, "Unhandled HID alphanumeric usage:" << usage);
}
} else if (usagePage == kHIDPage_LEDs) {
switch (usage) {
case kHIDUsage_LED_GenericIndicator: return "led-misc";
case kHIDUsage_LED_Pause: return "led-pause";
default:
SG_LOG(SG_INPUT, SG_WARN, "Unhandled HID LED usage:" << usage);
}
} else if (usagePage == kHIDPage_Button) {
std::stringstream os;
os << "button-" << usage;
return os.str();
} else if (usagePage >= kHIDPage_VendorDefinedStart) {
return "vendor";
} else {
SG_LOG(SG_INPUT, SG_WARN, "Unhandled HID usage page:" << usagePage);
}
return "unknown";
}
class FGMacOSXEventInputPrivate
{
public:
IOHIDManagerRef hidManager = nullptr;
FGMacOSXEventInput* p = nullptr;
double currentDt = 0.0;
int currentModifiers = 0;
void matchedDevice(IOHIDDeviceRef device);
void removedDevice(IOHIDDeviceRef device);
void iterateDevices(CFSetRef matchingSet);
std::string getDeviceStringProperty(IOHIDDeviceRef device, CFStringRef hidProp);
bool getDeviceIntProperty(IOHIDDeviceRef device, CFStringRef hidProp, int& value);
};
static void deviceMatchingCallback(
void * inContext, // context from IOHIDManagerRegisterDeviceMatchingCallback
IOReturn inResult, // the result of the matching operation
void * inSender, // the IOHIDManagerRef for the new device
IOHIDDeviceRef inIOHIDDeviceRef // the new HID device
)
{
// printf("%s(context: %p, result: %p, sender: %p, device: %p).\n",
// __PRETTY_FUNCTION__, inContext, (void *) inResult, inSender, (void*) inIOHIDDeviceRef);
FGMacOSXEventInputPrivate* p = static_cast<FGMacOSXEventInputPrivate*>(inContext);
p->matchedDevice(inIOHIDDeviceRef);
} // Handle_DeviceMatchingCallback
static void deviceRemovalCallback(
void * inContext, // context from IOHIDManagerRegisterDeviceMatchingCallback
IOReturn inResult, // the result of the matching operation
void * inSender, // the IOHIDManagerRef for the new device
IOHIDDeviceRef inIOHIDDeviceRef // the new HID device
)
{
// printf("%s(context: %p, result: %p, sender: %p, device: %p).\n",
// __PRETTY_FUNCTION__, inContext, (void *) inResult, inSender, (void*) inIOHIDDeviceRef);
FGMacOSXEventInputPrivate* p = static_cast<FGMacOSXEventInputPrivate*>(inContext);
p->removedDevice(inIOHIDDeviceRef);
} // Handle_DeviceMatchingCallback
//
// FGMacOSXInputDevice implementation
//
//
// FGMacOSXInputDevice
// Mac OS X specific FGInputDevice
//
class FGMacOSXInputDevice : public FGInputDevice {
public:
FGMacOSXInputDevice(IOHIDDeviceRef hidRef,
FGMacOSXEventInputPrivate* subsys);
virtual ~FGMacOSXInputDevice();
bool Open() override;
void Close() override;
virtual void update(double dt);
virtual const char *TranslateEventName(FGEventData &eventData);
virtual void Send( const char * eventName, double value );
virtual void SendFeatureReport(unsigned int reportId, const std::string& data);
virtual void AddHandledEvent( FGInputEvent_ptr handledEvent );
void drainQueue();
void handleValue(IOHIDValueRef value);
private:
void buildElementNameDictionary();
std::string nameForHIDElement(IOHIDElementRef element) const;
IOHIDDeviceRef _hid = nullptr;
IOHIDQueueRef _queue = nullptr;
FGMacOSXEventInputPrivate* _subsystem;
typedef std::map<std::string, IOHIDElementRef> NameElementDict;
NameElementDict namedElements;
};
#if 0
static void valueAvailableCallback(void * inContext, // context from IOHIDQueueRegisterValueAvailableCallback
IOReturn inResult, // the inResult
void * inSender // IOHIDQueueRef of the queue
) {
FGMacOSXInputDevice* dev = static_cast<FGMacOSXInputDevice*>(inContext);
dev->drainQueue();
} // Handle_ValueAvailableCallback
#endif
//
// FGMacOSXEventInput implementation
//
FGMacOSXEventInput::FGMacOSXEventInput() :
FGEventInput(),
d(new FGMacOSXEventInputPrivate)
{
d->p = this; // store back pointer to outer object on pimpl
SG_LOG(SG_INPUT, SG_DEBUG, "FGMacOSXEventInput created");
}
FGMacOSXEventInput::~FGMacOSXEventInput()
{
}
void FGMacOSXEventInput::init()
{
// everything is deffered until postinit since we need Nasal
}
void FGMacOSXEventInput::postinit()
{
d->hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
// set the HID device matching dictionary
IOHIDManagerSetDeviceMatching( d->hidManager, NULL /* all devices */);
IOHIDManagerRegisterDeviceMatchingCallback(d->hidManager, deviceMatchingCallback, d.get());
IOHIDManagerRegisterDeviceRemovalCallback(d->hidManager, deviceRemovalCallback, d.get());
IOHIDManagerOpen(d->hidManager, kIOHIDOptionsTypeNone);
IOHIDManagerScheduleWithRunLoop(d->hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
void FGMacOSXEventInput::shutdown()
{
FGEventInput::shutdown();
if (d->hidManager) {
IOHIDManagerClose(d->hidManager, kIOHIDOptionsTypeNone);
IOHIDManagerUnscheduleFromRunLoop(d->hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
CFRelease(d->hidManager);
}
}
//
// read all elements in each input device
//
void FGMacOSXEventInput::update(double dt)
{
d->currentDt = dt;
d->currentModifiers = fgGetKeyModifiers();
FGEventInput::update(dt);
}
void FGMacOSXEventInputPrivate::matchedDevice(IOHIDDeviceRef device)
{
std::string productName = getDeviceStringProperty(device, CFSTR(kIOHIDProductKey));
std::string manufacturer = getDeviceStringProperty(device, CFSTR(kIOHIDManufacturerKey));
std::string serial = getDeviceStringProperty(device, CFSTR(kIOHIDSerialNumberKey));
// filter out keyboard and mouse devices : this is especially important for
// Catalina TCC hardening to avoid secuirty alerts
int usagePage, usage;
getDeviceIntProperty(device, CFSTR(kIOHIDPrimaryUsagePageKey), usagePage);
getDeviceIntProperty(device, CFSTR(kIOHIDPrimaryUsageKey), usage);
if (usagePage == kHIDPage_GenericDesktop) {
if ((usage == kHIDUsage_GD_Keyboard) || (usage == kHIDUsage_GD_Mouse)) {
SG_LOG(SG_INPUT, SG_INFO, "MacOSX-EventInput: skipping device:" << productName << "( from " << manufacturer << ") becuase it is a keyboard or mouse");
return;
}
}
SG_LOG(SG_INPUT, SG_DEBUG, "MacOSX-EventInput: matched device:" << productName << "( from " << manufacturer << ")");
// allocate a Mac input device, and add to the base class to see if we have
// a config
FGMacOSXInputDevice* macInputDevice = new FGMacOSXInputDevice(device, this);
macInputDevice->SetName(manufacturer + " " + productName);
macInputDevice->SetSerialNumber(serial);
p->AddDevice(macInputDevice);
}
void FGMacOSXEventInputPrivate::removedDevice(IOHIDDeviceRef device)
{
std::string productName = getDeviceStringProperty(device, CFSTR(kIOHIDProductKey));
std::string manufacturer = getDeviceStringProperty(device, CFSTR(kIOHIDManufacturerKey));
// see if we have an entry for the device
}
std::string FGMacOSXEventInputPrivate::getDeviceStringProperty(IOHIDDeviceRef device, CFStringRef hidProp)
{
CFStringRef prop = (CFStringRef) IOHIDDeviceGetProperty(device, hidProp);
if ((prop == nullptr)|| (CFGetTypeID(prop) != CFStringGetTypeID())) {
return std::string();
}
size_t len = CFStringGetLength(prop);
if (len == 0) {
return std::string();
}
char* buffer = static_cast<char*>(malloc(len + 1)); // + 1 for the null byte
Boolean ok = CFStringGetCString(prop, buffer, len + 1, kCFStringEncodingUTF8);
if (!ok) {
SG_LOG(SG_INPUT, SG_WARN, "string conversion failed");
}
std::string result(buffer, len);
free(buffer);
return result;
}
bool FGMacOSXEventInputPrivate::getDeviceIntProperty(IOHIDDeviceRef device, CFStringRef hidProp, int& value)
{
CFTypeRef prop = IOHIDDeviceGetProperty(device, hidProp);
if (CFGetTypeID(prop) != CFNumberGetTypeID()) {
return false;
}
int32_t v;
Boolean result = CFNumberGetValue((CFNumberRef) prop, kCFNumberSInt32Type, &v);
value = v;
return result;
}
void FGMacOSXEventInputPrivate::iterateDevices(CFSetRef matchingSet)
{
size_t numDevices = CFSetGetCount(matchingSet);
IOHIDDeviceRef* devs = static_cast<IOHIDDeviceRef*>(::malloc(numDevices * sizeof(IOHIDDeviceRef)));
CFSetGetValues(matchingSet, (const void **) devs);
for (size_t i=0; i < numDevices; ++i) {
matchedDevice(devs[i]);
}
free(devs);
}
FGMacOSXInputDevice::FGMacOSXInputDevice(IOHIDDeviceRef hidRef,
FGMacOSXEventInputPrivate* subsys)
{
_hid = hidRef;
CFRetain(_hid);
_subsystem = subsys;
}
FGMacOSXInputDevice::~FGMacOSXInputDevice()
{
if (_queue) {
CFRelease(_queue);
}
CFRelease(_hid);
}
bool FGMacOSXInputDevice::Open()
{
IOReturn result = IOHIDDeviceOpen(_hid, kIOHIDOptionsTypeNone);
if (result != kIOReturnSuccess) {
return false;
}
IOHIDDeviceScheduleWithRunLoop(_hid, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
// IOHIDQueueRegisterValueAvailableCallback(_queue, valueAvailableCallback, this);
CFIndex maxDepth = 128;
_queue = IOHIDQueueCreate(kCFAllocatorDefault, _hid, maxDepth, kIOHIDOptionsTypeNone);
IOHIDQueueScheduleWithRunLoop(_queue, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
IOHIDQueueStart(_queue);
return true;
}
void FGMacOSXInputDevice::buildElementNameDictionary()
{
// copy all elements from the device
CFArrayRef elements = IOHIDDeviceCopyMatchingElements(_hid, NULL,
kIOHIDOptionsTypeNone);
CFIndex count = CFArrayGetCount(elements);
typedef std::map<std::string, unsigned int> NameCountMap;
NameCountMap nameCounts;
std::set<IOHIDElementCookie> seenCookies;
for (int i = 0; i < count; ++i) {
IOHIDElementRef element = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i);
IOHIDElementCookie cookie = IOHIDElementGetCookie(element);
if (seenCookies.find(cookie) != seenCookies.end()) {
// skip duplicate match of this element;
continue;
}
seenCookies.insert(cookie);
// bool isWrapping = IOHIDElementIsWrapping(element);
IOHIDElementType ty = IOHIDElementGetType(element);
if (ty == kIOHIDElementTypeCollection)
{
continue;
}
uint32_t page = IOHIDElementGetUsagePage(element);
uint32_t usage = IOHIDElementGetUsage(element);
bool isRelative = IOHIDElementIsRelative(element);
// compute the name for the element
std::string name = nameForUsage(page, usage);
if (isRelative) {
name = "rel-" + name; // prefix relative elements
}
NameCountMap::iterator it = nameCounts.find(name);
unsigned int nameCount;
std::string finalName = name;
if (it == nameCounts.end()) {
nameCounts[name] = nameCount = 1;
} else {
// check if we have a collison but different element types, eg
// input & feature. In which case, prefix the feature one since
// we assume it's the input one which is more interesting.
IOHIDElementRef other = namedElements[name];
IOHIDElementType otherTy = IOHIDElementGetType(other);
if (otherTy != ty) {
// types mismatch
if (otherTy == kIOHIDElementTypeFeature) {
namedElements[name] = element;
element = other;
finalName = "feature-" + name;
} else if (ty == kIOHIDElementTypeFeature) {
finalName = "feature-" + name;
}
nameCount = 1;
} else {
// duplicate element, append ordinal suffix
std::stringstream os;
os << name << "-" << it->second;
finalName = os.str();
nameCount = ++(it->second);
}
}
CFRetain(element);
namedElements[finalName] = element;
if (nameCount == 2) {
// we have more than one entry for this name, so ensures
// the first item is availabe with the -0 suffix
std::stringstream os;
os << name << "-0";
namedElements[os.str()] = namedElements[name];
CFRetain(namedElements[os.str()]);
}
}
// HID debugging code
#if 0
NameElementDict::const_iterator it;
for (it = namedElements.begin(); it != namedElements.end(); ++it) {
int report = IOHIDElementGetReportID(it->second);
int reportCount = IOHIDElementGetReportCount(it->second);
int reportSize = IOHIDElementGetReportSize(it->second);
CFTypeRef sizeProp = IOHIDElementGetProperty(it->second, CFSTR(kIOHIDElementDuplicateIndexKey));
if (sizeProp) {
CFShow(sizeProp);
}
bool isArray = IOHIDElementIsArray(it->second);
if (isArray) {
SG_LOG(SG_INPUT, SG_INFO, "YYYYYYYYYYYYYYYYYY");
}
SG_LOG(SG_INPUT, SG_INFO, "\t" << it->first << " report ID " << report << " /count=" << reportCount
<< " ;sz=" << reportSize);
}
#endif
CFRelease(elements);
}
void FGMacOSXInputDevice::Close()
{
// leaking these otherwise we get a crash shutting down the HID-manager
// object. Don't understand why that should be the case
#if 0
for (auto it : namedElements) {
CFRelease(it.second);
}
#endif
namedElements.clear();
IOHIDQueueStop(_queue);
IOHIDQueueUnscheduleFromRunLoop(_queue, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
IOHIDDeviceUnscheduleFromRunLoop(_hid, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
IOHIDDeviceClose(_hid, kIOHIDOptionsTypeNone);
}
void FGMacOSXInputDevice::AddHandledEvent( FGInputEvent_ptr handledEvent )
{
SG_LOG(SG_INPUT, SG_DEBUG, "adding event:" << handledEvent->GetName());
if (namedElements.empty()) {
buildElementNameDictionary();
}
NameElementDict::iterator it = namedElements.find(handledEvent->GetName());
if (it == namedElements.end()) {
SG_LOG(SG_INPUT, SG_WARN, "device does not have any element with name:" << handledEvent->GetName());
return;
}
IOHIDQueueAddElement(_queue, it->second);
FGInputDevice::AddHandledEvent(handledEvent);
}
void FGMacOSXInputDevice::update(double dt)
{
SG_UNUSED(dt);
drainQueue();
FGInputDevice::update(dt);
}
void FGMacOSXInputDevice::drainQueue()
{
do {
IOHIDValueRef valueRef = IOHIDQueueCopyNextValueWithTimeout( _queue, 0. );
if ( !valueRef ) break;
handleValue(valueRef);
CFRelease( valueRef );
} while ( 1 ) ;
}
void FGMacOSXInputDevice::handleValue(IOHIDValueRef value)
{
IOHIDElementRef element = IOHIDValueGetElement(value);
CFIndex val;
// need report count to know if we have to handle this specially
int reportCount = IOHIDElementGetReportCount(element);
if (reportCount > 1) {
// for a repeated element, we need to read the final value of
// the data bytes (even though this will be the lowest numbered name
// for this element.
int bitSize = IOHIDElementGetReportSize(element);
const uint8_t* bytes= IOHIDValueGetBytePtr(value);
size_t byteLen = IOHIDValueGetLength(value);
uint8_t finalByte = bytes[byteLen - 1];
if (bitSize == 8) {
val = (int8_t) finalByte; // force sign extension
} else if (bitSize == 4) {
int8_t b = finalByte >> 4; // high nibble
if (b & 0x08) {
// manually sign extend
b |= 0xf0; // set all bits except the low nibble
}
val = b;
} else {
throw sg_io_exception("Unhandled bit size in MacoSXHID");
}
} else {
val = IOHIDValueGetIntegerValue(value);
}
// supress spurious 0-valued relative events
std::string name = nameForHIDElement(element);
if ((name.find("rel-") == 0) && (val == 0)) {
return;
}
FGMacOSXEventData eventData(name, val,
_subsystem->currentDt,
_subsystem->currentModifiers);
HandleEvent(eventData);
}
std::string FGMacOSXInputDevice::nameForHIDElement(IOHIDElementRef element) const
{
NameElementDict::const_iterator it;
for (it = namedElements.begin(); it != namedElements.end(); ++it) {
if (it->second == element) {
return it->first;
}
}
throw sg_exception("Unknown HID element");
}
const char *FGMacOSXInputDevice::TranslateEventName(FGEventData &eventData)
{
FGMacOSXEventData &macEvent = (FGMacOSXEventData &)eventData;
return macEvent.name.c_str();
}
//
// Outputs value to an writable element (like LEDElement)
//
void FGMacOSXInputDevice::Send(const char *eventName, double value)
{
NameElementDict::const_iterator it = namedElements.find(eventName);
if (it == namedElements.end()) {
SG_LOG(SG_INPUT, SG_WARN, "FGMacOSXInputDevice::Send: unknown element:" << eventName);
return;
}
CFIndex cfVal = value;
uint64_t timestamp = 0;
IOHIDValueRef valueRef = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault,
it->second, timestamp, cfVal);
IOHIDDeviceSetValue(_hid, it->second, valueRef);
CFRelease(valueRef);
}
void FGMacOSXInputDevice::SendFeatureReport(unsigned int reportId, const std::string& data)
{
string d(data);
if (reportId > 0) {
// prefix with report number
d.insert(d.begin(), static_cast<char>(reportId));
}
size_t len = d.size();
const uint8_t* bytes = (const uint8_t*) d.data();
std::stringstream ss;
for (int i=0; i<len; ++i) {
ss << static_cast<int>(bytes[i]);
ss << ":";
}
SG_LOG(SG_INPUT, SG_INFO, "report " << reportId << " sending " << ss.str());
IOReturn res = IOHIDDeviceSetReport(_hid,
kIOHIDReportTypeFeature,
reportId, /* Report ID*/
bytes, len);
if (res != kIOReturnSuccess) {
SG_LOG(SG_INPUT, SG_WARN, "failed to send feature report" << reportId);
}
}

View file

@ -1,67 +0,0 @@
// FGMacOSXEventInput.hxx -- handle event driven input devices for Mac OS X
//
// Written by Tatsuhiro Nishioka, started Aug. 2009.
//
// Copyright (C) 2009 Tasuhiro Nishioka, tat <dot> fgmacosx <at> gmail <dot> com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef __FGMACOSXEVENTINPUT_HXX_
#define __FGMACOSXEVENTINPUT_HXX_
#include <string>
#include <vector>
#include <map>
#include <memory>
#include "FGEventInput.hxx"
class FGMacOSXEventInputPrivate;
struct FGMacOSXEventData : public FGEventData {
FGMacOSXEventData(std::string name, double value, double dt, int modifiers) :
FGEventData(value, dt, modifiers), name(name) {}
std::string name;
};
//
// Mac OS X specific FGEventInput
//
class FGMacOSXEventInput : public FGEventInput
{
public:
FGMacOSXEventInput();
virtual ~FGMacOSXEventInput();
// Subsystem API.
void init() override;
void postinit() override;
void shutdown() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "input-event"; }
private:
friend class FGMacOSXEventInputPrivate;
std::unique_ptr<FGMacOSXEventInputPrivate> d;
};
#endif

View file

@ -561,7 +561,7 @@ void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindo
SGSceneryPicks pickList = globals->get_renderer()->pick(windowPos); SGSceneryPicks pickList = globals->get_renderer()->pick(windowPos);
if( updown != MOUSE_BUTTON_DOWN ) if( updown == MOUSE_BUTTON_UP )
{ {
// Execute the mouse up event in any case, may be we should // Execute the mouse up event in any case, may be we should
// stop processing here? // stop processing here?
@ -572,7 +572,7 @@ void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindo
{ {
SGPickCallbackPtr& cb = callbacks.front(); SGPickCallbackPtr& cb = callbacks.front();
const SGSceneryPick* pick = getPick(pickList, cb); const SGSceneryPick* pick = getPick(pickList, cb);
cb->buttonReleased(ea->getModKeyMask(), *ea, pick ? &pick->info : 0); cb->buttonReleased(ea->getModKeyMask(), *ea, pick ? &pick->info : nullptr);
callbacks.pop_front(); callbacks.pop_front();
} }

View file

@ -38,10 +38,9 @@
#ifdef WITH_EVENTINPUT #ifdef WITH_EVENTINPUT
#if defined ( SG_MAC ) #if defined ( SG_MAC )
#include "FGMacOSXEventInput.hxx" // we use HID
#define INPUTEVENT_CLASS FGMacOSXEventInput
#elif defined(SG_WINDOWS) #elif defined(SG_WINDOWS)
// we use HID
#else #else
#include "FGLinuxEventInput.hxx" #include "FGLinuxEventInput.hxx"
#define INPUTEVENT_CLASS FGLinuxEventInput #define INPUTEVENT_CLASS FGLinuxEventInput

View file

@ -101,6 +101,7 @@ endif()
# Set up the target links. # Set up the target links.
setup_fgfs_libraries(fgfs) setup_fgfs_libraries(fgfs)
export_debug_symbols(fgfs)
if (APPLE) if (APPLE)
install(TARGETS fgfs BUNDLE DESTINATION .) install(TARGETS fgfs BUNDLE DESTINATION .)
@ -108,6 +109,21 @@ else()
install(TARGETS fgfs RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS fgfs RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif() endif()
if (TARGET sentry::sentry)
# disabling this for now until Sentry-native fixes their CMake export :)
# target_link_libraries(fgfs sentry::sentry)
endif()
if (TARGET sentry_crashpad::handler)
if (APPLE)
# install inside the bundle
install(FILES $<TARGET_FILE:sentry_crashpad::handler> DESTINATION fgfs.app/Contents/MacOS OPTIONAL)
else()
# install in the bin-dir, next to the application binary
install(FILES $<TARGET_FILE:sentry_crashpad::handler> DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
endif()
endif()
if(ENABLE_METAR) if(ENABLE_METAR)
add_executable(metar metar_main.cxx) add_executable(metar metar_main.cxx)
target_link_libraries(metar target_link_libraries(metar

View file

@ -390,6 +390,26 @@ private:
SG_LOG(SG_AIRCRAFT, SG_DEV_ALERT, "Aircraft does not specify a minimum FG version: please add one at /sim/minimum-fg-version"); SG_LOG(SG_AIRCRAFT, SG_DEV_ALERT, "Aircraft does not specify a minimum FG version: please add one at /sim/minimum-fg-version");
} }
auto compatNodes = globals->get_props()->getNode("/sim")->getChildren("compatible-fg-version");
if (!compatNodes.empty()) {
bool showCompatWarning = true;
// if we have at least one compatibility node, then it needs to match
for (const auto& cn : compatNodes) {
const auto v = cn->getStringValue();
if (simgear::strutils::compareVersionToWildcard(FLIGHTGEAR_VERSION, v)) {
showCompatWarning = false;
break;
}
}
if (showCompatWarning) {
flightgear::modalMessageBox("Aircraft not compatible with this version",
"The selected aircraft has not been checked for compatability with this version of FlightGear (" FLIGHTGEAR_VERSION "). "
"Some aircraft features might not work, or might be displayed incorrectly.");
}
}
return true; return true;
} }
@ -628,7 +648,7 @@ int fgInitConfig ( int argc, char **argv, bool reinit )
#endif #endif
// Read global defaults from $FG_ROOT/defaults // Read global defaults from $FG_ROOT/defaults
SG_LOG(SG_GENERAL, SG_INFO, "Reading global defaults"); SG_LOG(SG_GENERAL, SG_DEBUG, "Reading global defaults");
SGPath defaultsXML = globals->get_fg_root() / "defaults.xml"; SGPath defaultsXML = globals->get_fg_root() / "defaults.xml";
if (!defaultsXML.exists()) { if (!defaultsXML.exists()) {
flightgear::fatalMessageBoxThenExit( flightgear::fatalMessageBoxThenExit(

View file

@ -216,11 +216,12 @@ FGIO::parse_port_config( const string_list& tokens )
"(one argument expected: --hla-local=<federationname>" ); "(one argument expected: --hla-local=<federationname>" );
return NULL; return NULL;
} }
tokens.insert(tokens.begin(), ""); std::vector<std::string> HLA_tokens (tokens);
tokens.insert(tokens.begin(), "60"); HLA_tokens.insert(HLA_tokens.begin(), "");
tokens.insert(tokens.begin(), "bi"); HLA_tokens.insert(HLA_tokens.begin(), "60");
tokens.push_back("fg-local.xml"); HLA_tokens.insert(HLA_tokens.begin(), "bi");
return new FGHLA(tokens); HLA_tokens.push_back("fg-local.xml");
return new FGHLA(HLA_tokens);
} }
#endif #endif
else { else {

View file

@ -326,28 +326,6 @@ getGMTString ()
return buf; return buf;
} }
/**
* Return the current heading in degrees.
*/
static double
getHeadingMag ()
{
double magheading = fgGetDouble("/orientation/heading-deg") -
fgGetDouble("/environment/magnetic-variation-deg");
return SGMiscd::normalizePeriodic(0, 360, magheading );
}
/**
* Return the current track in degrees.
*/
static double
getTrackMag ()
{
double magtrack = fgGetDouble("/orientation/track-deg") -
fgGetDouble("/environment/magnetic-variation-deg");
return SGMiscd::normalizePeriodic(0, 360, magtrack );
}
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Tie the properties. // Tie the properties.
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
@ -439,9 +417,11 @@ FGProperties::bind ()
_tiedProperties.Tie<const char*>("/position/latitude-string", getLatitudeString); _tiedProperties.Tie<const char*>("/position/latitude-string", getLatitudeString);
_tiedProperties.Tie<const char*>("/position/longitude-string", getLongitudeString); _tiedProperties.Tie<const char*>("/position/longitude-string", getLongitudeString);
// Orientation _headingMagnetic = fgGetNode("/orientation/heading-magnetic-deg", true);
_tiedProperties.Tie<double>("/orientation/heading-magnetic-deg", getHeadingMag); _trackMagnetic = fgGetNode("/orientation/track-magnetic-deg", true);
_tiedProperties.Tie<double>("/orientation/track-magnetic-deg", getTrackMag); _magVar = fgGetNode("/environment/magnetic-variation-deg", true);
_trueHeading = fgGetNode("/orientation/heading-deg", true);
_trueTrack = fgGetNode("/orientation/track-deg", true);
} }
void void
@ -481,6 +461,10 @@ FGProperties::update (double dt)
_rmin->setIntValue(r->tm_min); _rmin->setIntValue(r->tm_min);
_rsec->setIntValue(r->tm_sec); _rsec->setIntValue(r->tm_sec);
_rwday->setIntValue(r->tm_wday); _rwday->setIntValue(r->tm_wday);
const double magvar = _magVar->getDoubleValue();
_headingMagnetic->setDoubleValue(_trueHeading->getDoubleValue() - magvar);
_trackMagnetic->setDoubleValue(_trueTrack->getDoubleValue() - magvar);
} }

View file

@ -43,6 +43,10 @@ private:
SGPropertyNode_ptr _offset; SGPropertyNode_ptr _offset;
SGPropertyNode_ptr _uyear, _umonth, _uday, _uhour, _umin, _usec, _uwday, _udsec; SGPropertyNode_ptr _uyear, _umonth, _uday, _uhour, _umin, _usec, _uwday, _udsec;
SGPropertyNode_ptr _ryear, _rmonth, _rday, _rhour, _rmin, _rsec, _rwday, _rdsec; SGPropertyNode_ptr _ryear, _rmonth, _rday, _rhour, _rmin, _rsec, _rwday, _rdsec;
SGPropertyNode_ptr _headingMagnetic, _trackMagnetic;
SGPropertyNode_ptr _magVar;
SGPropertyNode_ptr _trueHeading, _trueTrack;
}; };
@ -736,7 +740,7 @@ fgTie (const char * name, V (*getter)(), void (*setter)(V) = 0,
{ {
if (!globals->get_props()->tie(name, SGRawValueFunctions<V>(getter, setter), if (!globals->get_props()->tie(name, SGRawValueFunctions<V>(getter, setter),
useDefault)) useDefault))
SG_LOG(SG_GENERAL, SG_WARN, SG_LOG(SG_GENERAL, SG_DEV_WARN,
"Failed to tie property " << name << " to functions"); "Failed to tie property " << name << " to functions");
} }
@ -769,7 +773,7 @@ fgTie (const char * name, int index, V (*getter)(int),
getter, getter,
setter), setter),
useDefault)) useDefault))
SG_LOG(SG_GENERAL, SG_WARN, SG_LOG(SG_GENERAL, SG_DEV_WARN,
"Failed to tie property " << name << " to indexed functions"); "Failed to tie property " << name << " to indexed functions");
} }
@ -801,7 +805,7 @@ fgTie (const char * name, T * obj, V (T::*getter)() const,
if (!globals->get_props()->tie(name, if (!globals->get_props()->tie(name,
SGRawValueMethods<T,V>(*obj, getter, setter), SGRawValueMethods<T,V>(*obj, getter, setter),
useDefault)) useDefault))
SG_LOG(SG_GENERAL, SG_WARN, SG_LOG(SG_GENERAL, SG_DEV_WARN,
"Failed to tie property " << name << " to object methods"); "Failed to tie property " << name << " to object methods");
} }
@ -837,7 +841,7 @@ fgTie (const char * name, T * obj, int index,
getter, getter,
setter), setter),
useDefault)) useDefault))
SG_LOG(SG_GENERAL, SG_WARN, SG_LOG(SG_GENERAL, SG_DEV_WARN,
"Failed to tie property " << name << " to indexed object methods"); "Failed to tie property " << name << " to indexed object methods");
} }

View file

@ -21,9 +21,7 @@
// $Id$ // $Id$
#ifdef HAVE_CONFIG_H #include <config.h>
# include <config.h>
#endif
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <simgear/structure/exception.hxx> #include <simgear/structure/exception.hxx>
@ -1680,6 +1678,7 @@ struct OptionDesc {
{"ndb", true, OPTION_FUNC, "", false, "", fgOptNDB }, {"ndb", true, OPTION_FUNC, "", false, "", fgOptNDB },
{"ndb-frequency", true, OPTION_DOUBLE, "/sim/presets/ndb-freq", false, "", fgOptVOR }, {"ndb-frequency", true, OPTION_DOUBLE, "/sim/presets/ndb-freq", false, "", fgOptVOR },
{"carrier", true, OPTION_FUNC, "", false, "", fgOptCarrier }, {"carrier", true, OPTION_FUNC, "", false, "", fgOptCarrier },
{"carrier-abeam", true, OPTION_BOOL, "/sim/presets/carrier-abeam", true, "", 0 },
{"parkpos", true, OPTION_FUNC, "", false, "", fgOptParkpos }, {"parkpos", true, OPTION_FUNC, "", false, "", fgOptParkpos },
{"fix", true, OPTION_FUNC, "", false, "", fgOptFIX }, {"fix", true, OPTION_FUNC, "", false, "", fgOptFIX },
{"offset-distance", true, OPTION_DOUBLE, "/sim/presets/offset-distance-nm", false, "", 0 }, {"offset-distance", true, OPTION_DOUBLE, "/sim/presets/offset-distance-nm", false, "", 0 },
@ -1719,6 +1718,7 @@ struct OptionDesc {
{"notrim", false, OPTION_BOOL, "/sim/presets/trim", false, "", 0 }, {"notrim", false, OPTION_BOOL, "/sim/presets/trim", false, "", 0 },
{"on-ground", false, OPTION_BOOL, "/sim/presets/onground", true, "", 0 }, {"on-ground", false, OPTION_BOOL, "/sim/presets/onground", true, "", 0 },
{"in-air", false, OPTION_BOOL, "/sim/presets/onground", false, "", 0 }, {"in-air", false, OPTION_BOOL, "/sim/presets/onground", false, "", 0 },
{"disable-hold-short", false, OPTION_BOOL, "/sim/presets/mp-hold-short-override", true, "", 0 },
{"fog-disable", false, OPTION_STRING, "/sim/rendering/fog", false, "disabled", 0 }, {"fog-disable", false, OPTION_STRING, "/sim/rendering/fog", false, "disabled", 0 },
{"fog-fastest", false, OPTION_STRING, "/sim/rendering/fog", false, "fastest", 0 }, {"fog-fastest", false, OPTION_STRING, "/sim/rendering/fog", false, "fastest", 0 },
{"fog-nicest", false, OPTION_STRING, "/sim/rendering/fog", false, "nicest", 0 }, {"fog-nicest", false, OPTION_STRING, "/sim/rendering/fog", false, "nicest", 0 },

View file

@ -180,7 +180,9 @@ std::tuple<SGGeod, double> runwayStartPos(FGRunwayRef runway)
double startOffset = fgGetDouble("/sim/airport/runways/start-offset-m", 5.0); double startOffset = fgGetDouble("/sim/airport/runways/start-offset-m", 5.0);
SGGeod pos = runway->pointOnCenterline(startOffset); SGGeod pos = runway->pointOnCenterline(startOffset);
if (FGIO::isMultiplayerRequested() && (fabs(offsetNm) <0.1)) { const bool overrideHoldShort = fgGetBool("/sim/presets/mp-hold-short-override", false);
if (!overrideHoldShort && FGIO::isMultiplayerRequested() && (fabs(offsetNm) <0.1)) {
SG_LOG( SG_GENERAL, SG_WARN, "Requested to start on " << runway->airport()->ident() << "/" << SG_LOG( SG_GENERAL, SG_WARN, "Requested to start on " << runway->airport()->ident() << "/" <<
runway->ident() << ", MP is enabled so computing hold short position to avoid runway incursion"); runway->ident() << ", MP is enabled so computing hold short position to avoid runway incursion");
@ -503,7 +505,7 @@ static InitPosResult setFinalPosFromCarrier( const string& carrier, const string
return Failure; return Failure;
} }
static InitPosResult setFinalPosFromCarrierFLOLS(const string& carrier) static InitPosResult setFinalPosFromCarrierFLOLS(const string& carrier, bool abeam)
{ {
SGSharedPtr<FGAICarrier> carrierRef = FGAICarrier::findCarrierByNameOrPennant(carrier); SGSharedPtr<FGAICarrier> carrierRef = FGAICarrier::findCarrierByNameOrPennant(carrier);
if (!carrierRef) { if (!carrierRef) {
@ -529,16 +531,29 @@ static InitPosResult setFinalPosFromCarrierFLOLS(const string& carrier)
double gs = SGMiscd::deg2rad(carrierRef->getFLOLFSGlidepathAngleDeg()); double gs = SGMiscd::deg2rad(carrierRef->getFLOLFSGlidepathAngleDeg());
const double od = fgGetDouble("/sim/presets/offset-distance-nm"); const double od = fgGetDouble("/sim/presets/offset-distance-nm");
// start position, but with altitude not set // start position
SGGeod startPos = SGGeodesy::direct(flolsPosition, headingToFLOLS + 180, od * SG_NM_TO_METER); SGGeod startPos;
if (abeam) {
// If we're starting from the abeam position, we are opposite the FLOLS, downwind on a left hand circuit
startPos = SGGeodesy::direct(flolsPosition, headingToFLOLS - 90, od * SG_NM_TO_METER);
} else {
startPos = SGGeodesy::direct(flolsPosition, headingToFLOLS + 180, od * SG_NM_TO_METER);
}
double alt = fgGetDouble("/sim/presets/altitude-ft");
if (alt < 0.0f) {
// No altitude set, so base on glideslope
const double offsetFt = od * SG_NM_TO_METER * SG_METER_TO_FEET; const double offsetFt = od * SG_NM_TO_METER * SG_METER_TO_FEET;
startPos.setElevationFt(fabs(offsetFt*tan(gs)) + flolsElevationFt); startPos.setElevationFt(fabs(offsetFt*tan(gs)) + flolsElevationFt);
} else {
startPos.setElevationFt(alt);
}
fgSetDouble("/sim/presets/longitude-deg", startPos.getLongitudeDeg()); fgSetDouble("/sim/presets/longitude-deg", startPos.getLongitudeDeg());
fgSetDouble("/sim/presets/latitude-deg", startPos.getLatitudeDeg()); fgSetDouble("/sim/presets/latitude-deg", startPos.getLatitudeDeg());
fgSetDouble("/sim/presets/altitude-ft", startPos.getElevationFt()); fgSetDouble("/sim/presets/altitude-ft", startPos.getElevationFt());
fgSetDouble("/sim/presets/heading-deg", headingToFLOLS); fgSetDouble("/sim/presets/heading-deg", abeam ? (headingToFLOLS - 180) : headingToFLOLS);
fgSetDouble("/position/longitude-deg", startPos.getLongitudeDeg()); fgSetDouble("/position/longitude-deg", startPos.getLongitudeDeg());
fgSetDouble("/position/latitude-deg", startPos.getLatitudeDeg()); fgSetDouble("/position/latitude-deg", startPos.getLatitudeDeg());
fgSetDouble("/position/altitude-ft", startPos.getElevationFt()); fgSetDouble("/position/altitude-ft", startPos.getElevationFt());
@ -820,13 +835,14 @@ void finalizePosition()
std::string runway = fgGetString("/sim/presets/runway"); std::string runway = fgGetString("/sim/presets/runway");
std::string apt = fgGetString("/sim/presets/airport-id"); std::string apt = fgGetString("/sim/presets/airport-id");
const bool rwy_req = fgGetBool("/sim/presets/runway-requested"); const bool rwy_req = fgGetBool("/sim/presets/runway-requested");
const bool abeam = fgGetBool("/sim/presets/carrier-abeam");
if (!carrier.empty()) if (!carrier.empty())
{ {
const bool atFLOLS = rwy_req && (runway == "FLOLS"); const bool atFLOLS = rwy_req && (runway == "FLOLS" || parkpos == "FLOLS");
InitPosResult carrierResult; InitPosResult carrierResult;
if (atFLOLS) { if (atFLOLS) {
carrierResult = setFinalPosFromCarrierFLOLS(carrier); carrierResult = setFinalPosFromCarrierFLOLS(carrier, abeam);
} else { } else {
carrierResult = setFinalPosFromCarrier(carrier, parkpos); carrierResult = setFinalPosFromCarrier(carrier, parkpos);
} }

View file

@ -2449,9 +2449,9 @@ FGMultiplayMgr::addMultiplayer(const std::string& callsign,
} }
} }
/* Copy values from our local <set>/sim/view[]/config/* into global // Copy values from our local <set>/sim/view[]/config/* into global
/ai/models/multiplayer/set/sim/view[]/config/ so that we have view offsets // /ai/models/multiplayer/set/sim/view[]/config/ so that we have view offsets
available for this multiplayer aircraft. */ // available for this multiplayer aircraft.
SGPropertyNode* global_set = mp->_getProps()->addChild("set"); SGPropertyNode* global_set = mp->_getProps()->addChild("set");
SGPropertyNode* global_sim = global_set->addChild("sim"); SGPropertyNode* global_sim = global_set->addChild("sim");
double sim_chase_distance_m = -25; double sim_chase_distance_m = -25;

View file

@ -62,7 +62,12 @@ public:
_manager(manager), _manager(manager),
_useVBOsProp(fgGetNode("/sim/rendering/use-vbos", true)), _useVBOsProp(fgGetNode("/sim/rendering/use-vbos", true)),
_enableCacheProp(fgGetNode("/sim/tile-cache/enable", true)), _enableCacheProp(fgGetNode("/sim/tile-cache/enable", true)),
_pagedLODMaximumProp(fgGetNode("/sim/rendering/max-paged-lod", true)) _pagedLODMaximumProp(fgGetNode("/sim/rendering/max-paged-lod", true)),
_lodDetailed(fgGetNode("/sim/rendering/static-lod/detailed", true)),
_lodRoughDelta(fgGetNode("/sim/rendering/static-lod/rough-delta", true)),
_lodBareDelta(fgGetNode("/sim/rendering/static-lod/bare-delta", true)),
_lodRough(fgGetNode("/sim/rendering/static-lod/rough", true)),
_lodBare(fgGetNode("/sim/rendering/static-lod/bare", true))
{ {
_useVBOsProp->addChangeListener(this, true); _useVBOsProp->addChangeListener(this, true);
@ -78,6 +83,9 @@ public:
_pagedLODMaximumProp->setIntValue(current); _pagedLODMaximumProp->setIntValue(current);
} }
_pagedLODMaximumProp->addChangeListener(this, true); _pagedLODMaximumProp->addChangeListener(this, true);
_lodDetailed->addChangeListener(this, true);
_lodBareDelta->addChangeListener(this, true);
_lodRoughDelta->addChangeListener(this, true);
} }
~TileManagerListener() ~TileManagerListener()
@ -85,6 +93,9 @@ public:
_useVBOsProp->removeChangeListener(this); _useVBOsProp->removeChangeListener(this);
_enableCacheProp->removeChangeListener(this); _enableCacheProp->removeChangeListener(this);
_pagedLODMaximumProp->removeChangeListener(this); _pagedLODMaximumProp->removeChangeListener(this);
_lodDetailed->removeChangeListener(this);
_lodBareDelta->removeChangeListener(this);
_lodRoughDelta->removeChangeListener(this);
} }
virtual void valueChanged(SGPropertyNode* prop) virtual void valueChanged(SGPropertyNode* prop)
@ -99,6 +110,13 @@ public:
int v = prop->getIntValue(); int v = prop->getIntValue();
osg::ref_ptr<osgViewer::Viewer> viewer(globals->get_renderer()->getViewer()); osg::ref_ptr<osgViewer::Viewer> viewer(globals->get_renderer()->getViewer());
viewer->getDatabasePager()->setTargetMaximumNumberOfPageLOD(v); viewer->getDatabasePager()->setTargetMaximumNumberOfPageLOD(v);
} else if (prop == _lodDetailed || prop == _lodBareDelta || prop == _lodRoughDelta) {
// compatibility with earlier versions; set the static lod ranges appropriately as otherwise (bad) self managed
// LOD on scenery with range animations doesn't work.
// see also /sim/rendering/enable-range-lod-animations - which is false by default in > 2019.2 which also fixes
// the scenery but in a more efficient way.
_lodBare->setDoubleValue(_lodDetailed->getDoubleValue() + _lodRoughDelta->getDoubleValue());
_lodRough->setDoubleValue(_lodBare->getDoubleValue() + _lodBareDelta->getDoubleValue());
} }
} }
@ -106,7 +124,13 @@ private:
FGTileMgr* _manager; FGTileMgr* _manager;
SGPropertyNode_ptr _useVBOsProp, SGPropertyNode_ptr _useVBOsProp,
_enableCacheProp, _enableCacheProp,
_pagedLODMaximumProp; _pagedLODMaximumProp,
_lodDetailed,
_lodRoughDelta,
_lodBareDelta,
_lodRough,
_lodBare
;
}; };
FGTileMgr::FGTileMgr(): FGTileMgr::FGTileMgr():

View file

@ -21,6 +21,7 @@
#include "config.h" #include "config.h"
#include <cstring> #include <cstring>
#include <algorithm>
#include "NasalPositioned.hxx" #include "NasalPositioned.hxx"
@ -2446,7 +2447,8 @@ private:
class NasalFPDelegateFactory : public FlightPlan::DelegateFactory class NasalFPDelegateFactory : public FlightPlan::DelegateFactory
{ {
public: public:
NasalFPDelegateFactory(naRef code) NasalFPDelegateFactory(naRef code, const std::string& id) :
_id(id)
{ {
_nasal = globals->get_subsystem<FGNasalSys>(); _nasal = globals->get_subsystem<FGNasalSys>();
_func = code; _func = code;
@ -2474,7 +2476,11 @@ public:
naFreeContext(ctx); naFreeContext(ctx);
return result; return result;
} }
const std::string& id() const
{ return _id; }
private: private:
const std::string _id;
FGNasalSys* _nasal; FGNasalSys* _nasal;
naRef _func; naRef _func;
int _gcSaveKey; int _gcSaveKey;
@ -2499,12 +2505,47 @@ static naRef f_registerFPDelegate(naContext c, naRef me, int argc, naRef* args)
if ((argc < 1) || !naIsFunc(args[0])) { if ((argc < 1) || !naIsFunc(args[0])) {
naRuntimeError(c, "non-function argument to registerFlightPlanDelegate"); naRuntimeError(c, "non-function argument to registerFlightPlanDelegate");
} }
NasalFPDelegateFactory* factory = new NasalFPDelegateFactory(args[0]);
const std::string delegateId = (argc > 1) ? naStr_data(args[1]) : std::string{};
if (!delegateId.empty()) {
auto it = std::find_if(static_nasalDelegateFactories.begin(), static_nasalDelegateFactories.end(),
[delegateId](NasalFPDelegateFactory* delegate) {
return delegate->id() == delegateId;
});
if (it != static_nasalDelegateFactories.end()) {
naRuntimeError(c, "duplicate delegate ID at registerFlightPlanDelegate: %s", delegateId.c_str());
}
}
NasalFPDelegateFactory* factory = new NasalFPDelegateFactory(args[0], delegateId);
FlightPlan::registerDelegateFactory(factory); FlightPlan::registerDelegateFactory(factory);
static_nasalDelegateFactories.push_back(factory); static_nasalDelegateFactories.push_back(factory);
return naNil(); return naNil();
} }
static naRef f_unregisterFPDelegate(naContext c, naRef me, int argc, naRef* args)
{
if ((argc < 1) || !naIsString(args[0])) {
naRuntimeError(c, "non-string argument to unregisterFlightPlanDelegate");
}
const std::string delegateId = naStr_data(args[0]);
auto it = std::find_if(static_nasalDelegateFactories.begin(), static_nasalDelegateFactories.end(),
[delegateId](NasalFPDelegateFactory* delegate) {
return delegate->id() == delegateId;
});
if (it == static_nasalDelegateFactories.end()) {
SG_LOG(SG_NASAL, SG_DEV_WARN, "f_unregisterFPDelegate: no de;egate with ID:" << delegateId);
return naNil();
}
FlightPlan::unregisterDelegateFactory(*it);
static_nasalDelegateFactories.erase(it);
return naNil();
}
static WayptRef wayptFromArg(naRef arg) static WayptRef wayptFromArg(naRef arg)
{ {
WayptRef r = wayptGhost(arg); WayptRef r = wayptGhost(arg);
@ -3295,6 +3336,7 @@ static struct { const char* name; naCFunction func; } funcs[] = {
{ "flightplan", f_flightplan }, { "flightplan", f_flightplan },
{ "createFlightplan", f_createFlightplan }, { "createFlightplan", f_createFlightplan },
{ "registerFlightPlanDelegate", f_registerFPDelegate }, { "registerFlightPlanDelegate", f_registerFPDelegate },
{ "unregisterFlightPlanDelegate", f_unregisterFPDelegate},
{ "createWP", f_createWP }, { "createWP", f_createWP },
{ "createWPFrom", f_createWPFrom }, { "createWPFrom", f_createWPFrom },
{ "createViaTo", f_createViaTo }, { "createViaTo", f_createViaTo },

View file

@ -955,6 +955,17 @@ naRef FGNasalSys::cmdArgGhost()
return propNodeGhost(_cmdArg); return propNodeGhost(_cmdArg);
} }
void FGNasalSys::initLogLevelConstants()
{
hashset(_globals, "LOG_BULK", naNum(SG_BULK));
hashset(_globals, "LOG_WARN", naNum(SG_WARN));
hashset(_globals, "LOG_DEBUG", naNum(SG_DEBUG));
hashset(_globals, "LOG_INFO", naNum(SG_INFO));
hashset(_globals, "LOG_ALERT", naNum(SG_ALERT));
hashset(_globals, "DEV_WARN", naNum(SG_DEV_WARN));
hashset(_globals, "DEV_ALERT", naNum(SG_DEV_ALERT));
}
void FGNasalSys::setCmdArg(SGPropertyNode* aNode) void FGNasalSys::setCmdArg(SGPropertyNode* aNode)
{ {
_cmdArg = aNode; _cmdArg = aNode;
@ -983,6 +994,8 @@ void FGNasalSys::init()
hashset(_globals, "thread", naInit_thread(_context)); hashset(_globals, "thread", naInit_thread(_context));
hashset(_globals, "utf8", naInit_utf8(_context)); hashset(_globals, "utf8", naInit_utf8(_context));
initLogLevelConstants();
// Add our custom extension functions: // Add our custom extension functions:
for(i=0; funcs[i].name; i++) for(i=0; funcs[i].name; i++)
hashset(_globals, funcs[i].name, hashset(_globals, funcs[i].name,

View file

@ -161,6 +161,18 @@ public:
simgear::BufferedLogCallback* log() const simgear::BufferedLogCallback* log() const
{ return _log.get(); } { return _log.get(); }
private:
void initLogLevelConstants();
void loadPropertyScripts();
void loadPropertyScripts(SGPropertyNode* n);
void loadScriptDirectory(simgear::Dir nasalDir);
void addModule(std::string moduleName, simgear::PathList scripts);
static void logError(naContext);
naRef parse(naContext ctx, const char* filename, const char* buf, int len,
std::string& errors);
naRef genPropsModule();
private: private:
//friend class FGNasalScript; //friend class FGNasalScript;
friend class FGNasalListener; friend class FGNasalListener;
@ -180,15 +192,6 @@ private:
static int _listenerId; static int _listenerId;
void loadPropertyScripts();
void loadPropertyScripts(SGPropertyNode* n);
void loadScriptDirectory(simgear::Dir nasalDir);
void addModule(std::string moduleName, simgear::PathList scripts);
static void logError(naContext);
naRef parse(naContext ctx, const char* filename, const char* buf, int len,
std::string& errors);
naRef genPropsModule();
bool _inited; bool _inited;
naContext _context; naContext _context;
naRef _globals, naRef _globals,

View file

@ -505,7 +505,7 @@ void FGTrafficManager::shutdown()
bool FGTrafficManager::doDataSync() bool FGTrafficManager::doDataSync()
{ {
simgear::SGTerraSync* terraSync = static_cast<simgear::SGTerraSync*>(globals->get_subsystem("terrasync")); auto terraSync = globals->get_subsystem<simgear::SGTerraSync>();
bool doDataSync = fgGetBool("/sim/terrasync/ai-data-enabled"); bool doDataSync = fgGetBool("/sim/terrasync/ai-data-enabled");
if (doDataSync && terraSync) { if (doDataSync && terraSync) {
if (!trafficSyncRequested) { if (!trafficSyncRequested) {

View file

@ -171,7 +171,7 @@ View* View::createFromProperties(SGPropertyNode_ptr config, int view_index)
double fov_deg = config->getDoubleValue("default-field-of-view-deg"); double fov_deg = config->getDoubleValue("default-field-of-view-deg");
double near_m = config->getDoubleValue("ground-level-nearplane-m"); double near_m = config->getDoubleValue("ground-level-nearplane-m");
View* v = 0; View* v = nullptr;
// supporting two types "lookat" = 1 and "lookfrom" = 0 // supporting two types "lookat" = 1 and "lookfrom" = 0
const char *type = config->getParent()->getStringValue("type"); const char *type = config->getParent()->getStringValue("type");
if (!strcmp(type, "lookat")) { if (!strcmp(type, "lookat")) {

View file

@ -21,7 +21,7 @@ target_link_libraries(fgai
SimGearCore SimGearScene SimGearCore SimGearScene
${OPENSCENEGRAPH_LIBRARIES} ${OPENSCENEGRAPH_LIBRARIES}
${OPENGL_LIBRARIES} ${OPENGL_LIBRARIES}
${RTI_LIBRARIES} ${RTI_LDFLAGS}
) )
install(TARGETS fgai RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS fgai RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

0
utils/fgcom/positions.hxx Normal file → Executable file
View file

View file

@ -16,7 +16,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// //
#ifdef _WIN32 #ifdef _WIN32
# include <direct.h> // for getcwd() # include <direct.h> // for getcwd()
#else // !_WIN32 #else // !_WIN32

View file

@ -1,13 +1,26 @@
find_package(PNG) find_package(PNG QUIET)
find_package(OpenGL) find_package(OpenGL QUIET)
find_package(GLEW) find_package(Freetype QUIET)
find_package(Freetype)
if ((NOT PNG_FOUND) OR (NOT OPENGL_FOUND) OR (NOT GLEW_FOUND) OR (NOT FREETYPE_FOUND)) find_package(GLUT QUIET)
find_package(GLEW QUIET)
if(NOT ${GLUT_FOUND})
message(WARNING "GLUT NOT found, can't build FGPanel")
set(WITH_FGPANEL 0)
return()
endif()
if(NOT ${GLEW_FOUND})
message(WARNING "GLEW NOT found, can't build FGPanel")
set(WITH_FGPANEL 0)
return()
endif()
if((NOT PNG_FOUND) OR (NOT OPENGL_FOUND) OR (NOT FREETYPE_FOUND))
message(WARNING "FGPanel enabled, but some dependencies are missing") message(WARNING "FGPanel enabled, but some dependencies are missing")
message(STATUS "libPNG: ${PNG_FOUND}") message(STATUS "libPNG: ${PNG_FOUND}")
message(STATUS "OpenGL: ${OPENGL_FOUND}") message(STATUS "OpenGL: ${OPENGL_FOUND}")
message(STATUS "GLEW: ${GLEW_FOUND}")
message(STATUS "Freetype: ${FREETYPE_FOUND}") message(STATUS "Freetype: ${FREETYPE_FOUND}")
return() return()
endif() endif()
@ -18,21 +31,7 @@ find_path(BCMHOST_INCLUDE_DIR
NO_DEFAULT_PATH NO_DEFAULT_PATH
) )
include_directories( set(TARGET_SOURCES
${FREETYPE_INCLUDE_DIRS}
${PNG_INCLUDE_DIR}
)
if(${BCMHOST_INCLUDE_DIR} STREQUAL "BCMHOST_INCLUDE_DIR-NOTFOUND")
else()
# CMAKE > 3.1 : target_sources(fgpanel
set(TARGET_SOURCES
GLES_utils.cxx
GLES_utils.hxx
)
endif()
add_executable(fgpanel
main.cxx main.cxx
ApplicationProperties.hxx ApplicationProperties.hxx
ApplicationProperties.cxx ApplicationProperties.cxx
@ -74,45 +73,71 @@ add_executable(fgpanel
panel_io.hxx panel_io.hxx
GL_utils.cxx GL_utils.cxx
GL_utils.hxx GL_utils.hxx
${TARGET_SOURCES}
) )
add_executable(fgpanel ${TARGET_SOURCES})
target_link_libraries(fgpanel target_link_libraries(fgpanel
SimGearCore SimGearCore
${PNG_LIBRARIES} ${PNG_LIBRARIES}
${FREETYPE_LIBRARIES} ${FREETYPE_LIBRARIES}
)
if(${BCMHOST_INCLUDE_DIR} STREQUAL "BCMHOST_INCLUDE_DIR-NOTFOUND")
find_package(GLUT REQUIRED)
if(GLUT_FOUND)
message(STATUS "found GLUT inc ${GLUT_INCLUDE_DIR}, lib ${GLUT_LIBRARIES} ")
if (MSVC)
add_definitions( -DFREEGLUT_LIB_PRAGMAS=0 )
endif ()
target_link_libraries(fgpanel
${OPENGL_LIBRARIES} ${OPENGL_LIBRARIES}
${GLUT_LIBRARIES} ${GLUT_LIBRARIES}
GLEW::GLEW ${GLEW_LIBRARIES}
) )
else(GLUT_FOUND)
message(STATUS "glut NOT found, can't build fgpanel") target_include_directories(fgpanel PUBLIC
endif(GLUT_FOUND) ${FREETYPE_INCLUDE_DIRS}
else() ${PNG_INCLUDE_DIR}
${GLEW_INCLUDE_DIRS}
)
if(MSVC)
target_compile_definitions(fgpanel PUBLIC
-DFREEGLUT_LIB_PRAGMAS=0
)
endif(MSVC)
if(BCMHOST_INCLUDE_DIR)
message(STATUS "found Raspberry Pi") message(STATUS "found Raspberry Pi")
target_link_libraries(fgpanel add_executable(fgpanel-egl ${TARGET_SOURCES}
-lGLESv2 -lEGL -lm -lbcm_host -L/opt/vc/lib GLES_utils.cxx
GLES_utils.hxx
) )
target_include_directories(fgpanel-egl PUBLIC
include_directories( ${FREETYPE_INCLUDE_DIRS}
${PNG_INCLUDE_DIR}
${BCMHOST_INCLUDE_DIR} ${BCMHOST_INCLUDE_DIR}
${BCMHOST_INCLUDE_DIR}/interface/vcos/pthreads ${BCMHOST_INCLUDE_DIR}/interface/vcos/pthreads
${BCMHOST_INCLUDE_DIR}/interface/vmcs_host/linux ${BCMHOST_INCLUDE_DIR}/interface/vmcs_host/linux
) )
add_definitions(-D_GLES2 -D_RPI) target_link_libraries(fgpanel-egl
endif() SimGearCore
${PNG_LIBRARIES}
${FREETYPE_LIBRARIES}
-lbrcmGLESv2 -lbrcmEGL -lm -lbcm_host -L/opt/vc/lib
)
install(TARGETS fgpanel RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) target_compile_definitions(fgpanel-egl PUBLIC
-D_GLES2 -D_RPI
)
target_compile_definitions(fgpanel PUBLIC
-D_RPI
)
message(STATUS "FGPanel (Raspberry Pi) : ENABLED")
install(TARGETS fgpanel-egl RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
else(BCMHOST_INCLUDE_DIR)
message(STATUS "FGPanel (Raspberry Pi) : DISABLED")
endif(BCMHOST_INCLUDE_DIR)
if(WITH_FGPANEL)
message(STATUS "FGPanel : ENABLED")
install(TARGETS fgpanel RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
else(WITH_FGPANEL)
message(WARNING "FGPanel : DISABLED")
endif(WITH_FGPANEL)

View file

@ -89,12 +89,13 @@ void
FGPanel::init () { FGPanel::init () {
// Textured Layer Shaders // Textured Layer Shaders
const char V_Textured_Layer_Shader_Str[] = const char V_Textured_Layer_Shader_Str[] =
#ifdef _GLES2 #ifdef _RPI
"#version 100 \n"
"attribute vec4 a_position; \n" "attribute vec4 a_position; \n"
"attribute vec2 a_tex_coord; \n" "attribute vec2 a_tex_coord; \n"
"varying vec2 v_tex_coord; \n" "varying vec2 v_tex_coord; \n"
#else #else
"#version 330 \n" "#version 130 \n"
"in vec4 a_position; \n" "in vec4 a_position; \n"
"in vec2 a_tex_coord; \n" "in vec2 a_tex_coord; \n"
"out vec2 v_tex_coord; \n" "out vec2 v_tex_coord; \n"
@ -106,13 +107,13 @@ FGPanel::init () {
"} \n"; "} \n";
const char F_Textured_Layer_Shader_Str[] = const char F_Textured_Layer_Shader_Str[] =
#ifdef _GLES2 #ifdef _RPI
"#version 100 \n"
"precision mediump float; \n" "precision mediump float; \n"
"varying vec2 v_tex_coord; \n" "varying vec2 v_tex_coord; \n"
#else #else
"#version 330 \n" "#version 130 \n"
"in vec2 v_tex_coord; \n" "in vec2 v_tex_coord; \n"
"out vec4 gl_FragColor; \n"
#endif #endif
"uniform sampler2D u_texture; \n" "uniform sampler2D u_texture; \n"
"void main () { \n" "void main () { \n"

View file

@ -51,8 +51,7 @@ using namespace std;
* redraw themselves when necessary, and will pass mouse clicks on to * redraw themselves when necessary, and will pass mouse clicks on to
* the appropriate instruments for processing. * the appropriate instruments for processing.
*/ */
class FGPanel : public SGSubsystem class FGPanel : public SGSubsystem {
{
public: public:
FGPanel (const SGPropertyNode_ptr root); FGPanel (const SGPropertyNode_ptr root);
virtual ~FGPanel (); virtual ~FGPanel ();

View file

@ -36,7 +36,6 @@
#include <GLUT/glut.h> #include <GLUT/glut.h>
#elif defined (_GLES2) #elif defined (_GLES2)
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
#include "GLES_utils.hxx"
#else #else
#include <GL/glew.h> // Must be included before <GL/gl.h> #include <GL/glew.h> // Must be included before <GL/gl.h>
#include <GL/gl.h> #include <GL/gl.h>

View file

@ -28,8 +28,7 @@ class PropertySetter;
typedef vector<PropertySetter*> PropertySetterVector; typedef vector<PropertySetter*> PropertySetterVector;
class FGPanelProtocol : public SGSubsystem class FGPanelProtocol : public SGSubsystem {
{
public: public:
FGPanelProtocol (SGPropertyNode_ptr a_Root); FGPanelProtocol (SGPropertyNode_ptr a_Root);
virtual ~FGPanelProtocol (); virtual ~FGPanelProtocol ();

View file

@ -35,26 +35,32 @@ GLint FGTextLayer::Text_Layer_Color_Loc (0);
bool bool
FGTextLayer::Init () { FGTextLayer::Init () {
const char V_Text_Layer_Shader_Str[] = const char V_Text_Layer_Shader_Str[] =
#ifdef _GLES2 #ifdef _RPI
"#version 100 \n"
"attribute vec4 a_position; \n" "attribute vec4 a_position; \n"
"attribute vec2 a_tex_coord; \n" "attribute vec2 a_tex_coord; \n"
"varying vec2 v_tex_coord; \n"
#else #else
"#version 330 \n" "#version 130 \n"
"in vec4 a_position; \n" "in vec4 a_position; \n"
"in vec2 a_tex_coord; \n" "in vec2 a_tex_coord; \n"
"out vec2 v_tex_coord; \n"
#endif #endif
"uniform mat4 u_mvp_matrix; \n" "uniform mat4 u_mvp_matrix; \n"
"varying vec2 v_tex_coord; \n"
"void main () { \n" "void main () { \n"
" gl_Position = u_mvp_matrix * a_position; \n" " gl_Position = u_mvp_matrix * a_position; \n"
" v_tex_coord = a_tex_coord; \n" " v_tex_coord = a_tex_coord; \n"
"} \n"; "} \n";
const char F_Text_Layer_Shader_Str[] = const char F_Text_Layer_Shader_Str[] =
#ifdef _GLES2 #ifdef _RPI
"#version 100 \n"
"precision mediump float; \n" "precision mediump float; \n"
#endif
"varying vec2 v_tex_coord; \n" "varying vec2 v_tex_coord; \n"
#else
"#version 130 \n"
"in vec2 v_tex_coord; \n"
#endif
"uniform sampler2D u_texture; \n" "uniform sampler2D u_texture; \n"
"uniform vec4 u_color; \n" "uniform vec4 u_color; \n"
"void main () { \n" "void main () { \n"

View file

@ -15,10 +15,10 @@ INSTALLATION
The source code of FGPanel can easily be adapted to other embedded devices supporting OpenGL ES 2.0. The source code of FGPanel can easily be adapted to other embedded devices supporting OpenGL ES 2.0.
3. Build FGPanel only (don't build all components on the Raspberry Pi as this will take ages!!!): 3. Build FGPanel only (don't build all components on the Raspberry Pi as this will take ages!!!):
make -- fgpanel make -- fgpanel-egl fgpanel
USAGE USAGE (fgpanel-egl)
===== ===================
1. Increase the amount of GPU memory by editing the /boot/config.txt file. 1. Increase the amount of GPU memory by editing the /boot/config.txt file.
Add the following line (this is for Raspberry Pi model 1B with 512 Mb of memory): Add the following line (this is for Raspberry Pi model 1B with 512 Mb of memory):
@ -30,10 +30,21 @@ USAGE
3. Stop X server. FGPanel runs in the console (Linux framebuffer). 3. Stop X server. FGPanel runs in the console (Linux framebuffer).
4. Start FGPanel as usual but as ROOT or use the 'sudo' command (see README): 4. Start FGPanel as usual but as ROOT or use the 'sudo' command (see README):
sudo -- utils/fgpanel/fgpanel --fg-root=/path/to/fg/data --panel=Aircraft/MyAircraft/Panels/MyPanel.xml sudo -- utils/fgpanel/fgpanel-egl --fg-root=/path/to/fg/data --panel=Aircraft/MyAircraft/Panels/MyPanel.xml
ROOT privileges are required to access the GPU of the Raspberry Pi. ROOT privileges are required to access the GPU of the Raspberry Pi.
USAGE (fgpanel)
===================
1. Activate the Full KMS driver on the Raspberry Pi using the raspi-config program:
Advanced Options -> GL Driver -> Full KMS
2. Reboot. The Full KMS driver should be activated.
3. When X server is running, start FGPanel as usual (see README):
utils/fgpanel/fgpanel --fg-root=/path/to/fg/data --panel=Aircraft/MyAircraft/Panels/MyPanel.xml
FEATURES FEATURES
======== ========

View file

@ -31,8 +31,7 @@
#include "FGPanel.hxx" #include "FGPanel.hxx"
class FGReadablePanel : public FGPanel class FGReadablePanel : public FGPanel {
{
public: public:
// Subsystem identification. // Subsystem identification.
static const char* staticSubsystemClassId() { return "readable-panel"; } static const char* staticSubsystemClassId() { return "readable-panel"; }

View file

@ -35,7 +35,7 @@ if(RTI_FOUND)
HLAWindowDrawable.cxx HLAWindowDrawable.cxx
HLAWindowDrawableClass.cxx HLAWindowDrawableClass.cxx
) )
set(FGVIEWER_RTI_LIBRARIES ${RTI_LIBRARIES}) set(FGVIEWER_RTI_LIBRARIES ${RTI_LDFLAGS})
else() else()
set(FGVIEWER_RTI_LIBRARIES "") set(FGVIEWER_RTI_LIBRARIES "")
set(FGVIEWER_RTI_SOURCES "") set(FGVIEWER_RTI_SOURCES "")