Merge branch 'master' into electric-engine
This commit is contained in:
commit
30528a9d6c
75 changed files with 1268 additions and 1145 deletions
6
3rdparty/hidapi/CMakeLists.txt
vendored
6
3rdparty/hidapi/CMakeLists.txt
vendored
|
@ -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})
|
||||||
|
|
|
@ -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")
|
||||||
|
@ -283,6 +272,14 @@ 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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
22
CMakeModules/ExportDebugSymbols.cmake
Normal file
22
CMakeModules/ExportDebugSymbols.cmake
Normal 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()
|
|
@ -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}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -98,6 +98,7 @@ protected:
|
||||||
static SGGeod unproject(const QPointF &xy, const SGGeod ¢er);
|
static SGGeod unproject(const QPointF &xy, const SGGeod ¢er);
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -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
148
src/GUI/CarrierDiagram.cxx
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
80
src/GUI/CarrierDiagram.hxx
Normal file
80
src/GUI/CarrierDiagram.hxx
Normal 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
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)");
|
||||||
|
|
62
src/GUI/FavouriteAircraftData.cxx
Normal file
62
src/GUI/FavouriteAircraftData.cxx
Normal 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);
|
||||||
|
}
|
31
src/GUI/FavouriteAircraftData.hxx
Normal file
31
src/GUI/FavouriteAircraftData.hxx
Normal 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
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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();
|
||||||
|
|
113
src/GUI/assets/aircraft-carrier-icon.svg
Normal file
113
src/GUI/assets/aircraft-carrier-icon.svg
Normal 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 |
BIN
src/GUI/assets/icons8-airport-50.png
Normal file
BIN
src/GUI/assets/icons8-airport-50.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -70,3 +70,5 @@
|
||||||
#cmakedefine ENABLE_COMPOSITOR
|
#cmakedefine ENABLE_COMPOSITOR
|
||||||
|
|
||||||
#cmakedefine ENABLE_SWIFT
|
#cmakedefine ENABLE_SWIFT
|
||||||
|
|
||||||
|
#cmakedefine HAVE_SENTRY
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 },
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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():
|
||||||
|
|
|
@ -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 },
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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")) {
|
||||||
|
|
|
@ -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
0
utils/fgcom/positions.hxx
Normal file → Executable 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
|
||||||
|
|
|
@ -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(
|
|
||||||
${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
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
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})
|
install(TARGETS fgpanel RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
else(WITH_FGPANEL)
|
||||||
|
message(WARNING "FGPanel : DISABLED")
|
||||||
|
endif(WITH_FGPANEL)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 ();
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 ();
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
========
|
========
|
||||||
|
|
||||||
|
|
|
@ -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"; }
|
||||||
|
|
|
@ -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 "")
|
||||||
|
|
Loading…
Reference in a new issue