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)
|
||||
find_library(SETUP_API_LIB Setupapi)
|
||||
if (NOT SETUP_API_LIB)
|
||||
message(FATAL_ERROR "Failed to find Setupapi.lib")
|
||||
endif()
|
||||
target_link_libraries(hidapi ${SETUP_API_LIB})
|
||||
target_link_libraries(hidapi PUBLIC SetupApi)
|
||||
elseif(APPLE)
|
||||
find_library(IOKIT_FRAMEWORK IOKit)
|
||||
target_link_libraries(hidapi ${IOKIT_FRAMEWORK})
|
||||
|
|
|
@ -76,7 +76,7 @@ include(GNUInstallDirs)
|
|||
# System detection/default settings
|
||||
include( DetectDistro )
|
||||
include( DetectBrowser )
|
||||
|
||||
include( ExportDebugSymbols )
|
||||
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_RELWITHDEBINFO_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
||||
|
@ -123,11 +123,7 @@ IF(APPLE)
|
|||
find_library(COCOA_LIBRARY Cocoa)
|
||||
list(APPEND PLATFORM_LIBS ${COCOA_LIBRARY} ${CORESERVICES_LIBRARY})
|
||||
elseif(WIN32)
|
||||
find_library(SETUP_API_LIB Setupapi)
|
||||
if (SETUP_API_LIB)
|
||||
set(EVENT_INPUT_DEFAULT 1)
|
||||
endif()
|
||||
|
||||
set(EVENT_INPUT_DEFAULT 1)
|
||||
list(APPEND PLATFORM_LIBS "Shlwapi.lib")
|
||||
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION "bin")
|
||||
|
@ -265,14 +261,7 @@ if(EVENT_INPUT)
|
|||
message(STATUS "event-based input enabled. Using ${UDEV_LIBRARIES}")
|
||||
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()
|
||||
add_definitions(-DWITH_EVENTINPUT)
|
||||
endif()
|
||||
add_definitions(-DWITH_EVENTINPUT)
|
||||
endif()
|
||||
|
||||
if (ENABLE_HID_INPUT)
|
||||
|
@ -282,7 +271,15 @@ if(EVENT_INPUT)
|
|||
endif(EVENT_INPUT)
|
||||
|
||||
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)
|
||||
|
||||
#libevent
|
||||
|
@ -317,6 +314,13 @@ find_package(OpenSceneGraph 3.2.0 REQUIRED
|
|||
osgGA
|
||||
)
|
||||
|
||||
find_package(sentry QUIET)
|
||||
|
||||
if (TARGET sentry::sentry)
|
||||
message(STATUS "Sentry.io crash reporting enabled")
|
||||
set(HAVE_SENTRY 1)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
find_package(CrashRpt)
|
||||
if (CRASHRPT_FOUND)
|
||||
|
@ -415,10 +419,7 @@ endif()
|
|||
|
||||
if(ENABLE_RTI)
|
||||
message(STATUS "RTI: ENABLED")
|
||||
find_package(RTI)
|
||||
if(RTI_FOUND)
|
||||
set(FG_HAVE_HLA 1)
|
||||
endif(RTI_FOUND)
|
||||
set(FG_HAVE_HLA 1)
|
||||
else()
|
||||
message(STATUS "RTI: DISABLED")
|
||||
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_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 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
|
||||
|
|
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
|
||||
set_target_properties(${target} PROPERTIES
|
||||
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_LONG_VERSION_STRING "FlightGear ${FLIGHTGEAR_VERSION} Nightly"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION ${FLIGHTGEAR_VERSION}
|
||||
|
|
|
@ -7,7 +7,7 @@ function(setup_fgfs_libraries target)
|
|||
#message(STATUS "SG libs ${SIMGEAR_LIBRARIES}")
|
||||
|
||||
if(RTI_FOUND)
|
||||
set(HLA_LIBRARIES ${RTI_LIBRARIES})
|
||||
set(HLA_LIBRARIES ${RTI_LDFLAGS})
|
||||
else()
|
||||
set(HLA_LIBRARIES "")
|
||||
endif()
|
||||
|
|
|
@ -20,15 +20,12 @@
|
|||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <config.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <string>
|
||||
|
||||
#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 */
|
||||
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);
|
||||
|
||||
_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
|
||||
_flolsPosOffset = SGVec3d::zeros();
|
||||
|
||||
|
@ -621,7 +621,7 @@ std::pair<bool, SGGeod> FGAICarrier::initialPositionForCarrier(const std::string
|
|||
// this is actually a three-layer search (we want the scenario with the
|
||||
// carrier with the correct penanant or name. Sometimes an XPath for
|
||||
// properties would be quite handy :)
|
||||
|
||||
|
||||
for (auto s : fgGetNode("/sim/ai/scenarios")->getChildren("scenario")) {
|
||||
auto carriers = s->getChildren("carrier");
|
||||
auto it = std::find_if(carriers.begin(), carriers.end(),
|
||||
|
@ -636,16 +636,16 @@ std::pair<bool, SGGeod> FGAICarrier::initialPositionForCarrier(const std::string
|
|||
if (it == carriers.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// mark the scenario for loading (which will happen in post-init of the AIManager)
|
||||
fgGetNode("/sim/ai/")->addChild("scenario")->setStringValue(s->getStringValue("id"));
|
||||
|
||||
|
||||
// read out the initial-position
|
||||
SGGeod geod = SGGeod::fromDeg((*it)->getDoubleValue("longitude"),
|
||||
(*it)->getDoubleValue("latitude"));
|
||||
return std::make_pair(true, geod);
|
||||
} // of scenarios iteration
|
||||
|
||||
|
||||
return std::make_pair(false, SGGeod());
|
||||
}
|
||||
|
||||
|
@ -655,7 +655,7 @@ SGSharedPtr<FGAICarrier> FGAICarrier::findCarrierByNameOrPennant(const std::stri
|
|||
if (!aiManager) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
for (const auto& aiObject : aiManager->get_ai_list()) {
|
||||
if (aiObject->isa(FGAIBase::otCarrier)) {
|
||||
SGSharedPtr<FGAICarrier> c = static_cast<FGAICarrier*>(aiObject.get());
|
||||
|
@ -664,7 +664,7 @@ SGSharedPtr<FGAICarrier> FGAICarrier::findCarrierByNameOrPennant(const std::stri
|
|||
}
|
||||
}
|
||||
} // of all objects iteration
|
||||
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -673,19 +673,22 @@ void FGAICarrier::extractCarriersFromScenario(SGPropertyNode_ptr xmlNode, SGProp
|
|||
for (auto c : xmlNode->getChildren("entry")) {
|
||||
if (c->getStringValue("type") != std::string("carrier"))
|
||||
continue;
|
||||
|
||||
|
||||
const std::string name = c->getStringValue("name");
|
||||
const std::string pennant = c->getStringValue("pennant-number");
|
||||
if (name.empty() && pennant.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
SGPropertyNode_ptr carrierNode = scenario->addChild("carrier");
|
||||
|
||||
// extract the initial position from the scenario
|
||||
carrierNode->setDoubleValue("longitude", c->getDoubleValue("longitude"));
|
||||
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
|
||||
// are possible, for example)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
});
|
||||
|
||||
|
|
|
@ -153,7 +153,7 @@ public:
|
|||
_nd(nd)
|
||||
{}
|
||||
|
||||
virtual void valueChanged (SGPropertyNode * prop)
|
||||
void valueChanged (SGPropertyNode * prop) override
|
||||
{
|
||||
_nd->invalidatePositionedCache();
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ public:
|
|||
_nd(nd)
|
||||
{}
|
||||
|
||||
virtual void valueChanged (SGPropertyNode * prop)
|
||||
void valueChanged (SGPropertyNode * prop) override
|
||||
{
|
||||
_nd->forceUpdate();
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ public:
|
|||
// record instances for limiting by count
|
||||
int instanceCount;
|
||||
private:
|
||||
SymbolDef* definition;
|
||||
SymbolDef* definition = nullptr;
|
||||
|
||||
std::unique_ptr<SGCondition> enable;
|
||||
string_set required_states;
|
||||
|
@ -459,7 +459,7 @@ NavDisplay::NavDisplay(SGPropertyNode *node) :
|
|||
SGPropertyNode* symbol;
|
||||
|
||||
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;
|
||||
if (!def->initFromNode(symbol, this)) {
|
||||
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
|
||||
_symbolTexture = SGLoadTexture2D(tpath, NULL, false, false);
|
||||
_symbolTexture = SGLoadTexture2D(tpath, nullptr, false, false);
|
||||
|
||||
_odg = new FGODGauge;
|
||||
_odg->setSize(_Instrument->getIntValue("texture-size", 512));
|
||||
|
@ -958,7 +958,7 @@ public:
|
|||
|
||||
double minRunwayLengthFt;
|
||||
|
||||
virtual bool pass(FGPositioned* aPos) const
|
||||
bool pass(FGPositioned* aPos) const override
|
||||
{
|
||||
if (aPos->type() == FGPositioned::FIX) {
|
||||
string ident(aPos->ident());
|
||||
|
@ -979,11 +979,13 @@ public:
|
|||
return _owner->isPositionedShown(aPos);
|
||||
}
|
||||
|
||||
virtual FGPositioned::Type minType() const {
|
||||
FGPositioned::Type minType() const override
|
||||
{
|
||||
return FGPositioned::AIRPORT;
|
||||
}
|
||||
|
||||
virtual FGPositioned::Type maxType() const {
|
||||
FGPositioned::Type maxType() const override
|
||||
{
|
||||
return FGPositioned::OBSTACLE;
|
||||
}
|
||||
|
||||
|
@ -1105,7 +1107,7 @@ FGNavRecord* NavDisplay::processNavRadio(const SGPropertyNode_ptr& radio)
|
|||
FGNavRecord* nav = FGNavList::findByFreq(mhz, _pos, FGNavList::navFilter());
|
||||
if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) {
|
||||
// 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)
|
||||
{
|
||||
if (isProjectedClipped(proj)) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((def->limitCount > 0) && (def->instanceCount >= def->limitCount)) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
++def->instanceCount;
|
||||
|
@ -1416,28 +1418,28 @@ void NavDisplay::addTestSymbols()
|
|||
double 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);
|
||||
|
||||
addTestSymbol("vor", "", a1, 0.0, NULL);
|
||||
addTestSymbol("vor", "", a1, 0.0, nullptr);
|
||||
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
addTestSymbol("fix", "", a1, 0.0, NULL);
|
||||
addTestSymbol("fix", "", a1, 0.0, nullptr);
|
||||
}
|
||||
|
||||
void NavDisplay::addRule(SymbolRule* r)
|
||||
|
|
|
@ -184,6 +184,9 @@ MetarProperties::MetarProperties( SGPropertyNode_ptr rootNode ) :
|
|||
_tiedProperties.Tie("decoded", this, &MetarProperties::get_decoded );
|
||||
_tiedProperties.Tie("cavok", &_cavok );
|
||||
_tiedProperties.Tie("description", this, &MetarProperties::get_description );
|
||||
|
||||
// mark proeprties as listener-safe, we invoke valueChanged explicitly
|
||||
_tiedProperties.setAttribute(SGPropertyNode::LISTENER_SAFE, true);
|
||||
}
|
||||
|
||||
MetarProperties::~MetarProperties()
|
||||
|
@ -422,6 +425,7 @@ void MetarProperties::setMetar( SGSharedPtr<FGMetar> m )
|
|||
_hour = m->getHour();
|
||||
_minute = m->getMinute();
|
||||
_cavok = m->getCAVOK();
|
||||
_tiedProperties.fireValueChanged();
|
||||
_metarValidNode->setBoolValue(true);
|
||||
_description = m->getDescription(-1);
|
||||
}
|
||||
|
|
|
@ -204,9 +204,9 @@ void FGFDM::init()
|
|||
fgSetDouble(buf, CM2GALS * _airplane.getTankCapacity(i)/density);
|
||||
}
|
||||
|
||||
// This has a nasty habit of being false at startup. That's not
|
||||
// good.
|
||||
fgSetBool("/controls/gear/gear-down", true);
|
||||
if (_yasimN->getBoolValue("respect-external-gear-state") == false) {
|
||||
fgSetBool("/controls/gear/gear-down", true);
|
||||
}
|
||||
|
||||
_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
|
||||
// clear the ground.
|
||||
bool respect_external_gear_state = fgGetBool("/fdm/yasim/respect-external-gear-state");
|
||||
double runway_altitude = get_Runway_altitude();
|
||||
if(get_Altitude() - runway_altitude < 50) {
|
||||
bool gear_state = fgGetBool("/controls/gear/gear-down");
|
||||
fgSetBool("/controls/gear/gear-down", false);
|
||||
float minGearZ = 1e18;
|
||||
for(int i=0; i<airplane->numGear(); i++) {
|
||||
|
@ -209,9 +211,9 @@ void YASim::init()
|
|||
}
|
||||
_set_Altitude(runway_altitude - minGearZ*M2FT);
|
||||
// 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
|
||||
fgSetBool("/controls/gear/gear-down", false);
|
||||
|
@ -300,7 +302,7 @@ void YASim::copyToYASim(bool copyState)
|
|||
atmo.setDensity(dens);
|
||||
atmo.setTemperature(temp);
|
||||
atmo.setPressure(pressure);
|
||||
|
||||
|
||||
// Convert and set:
|
||||
Model* model = _fdm->getAirplane()->getModel();
|
||||
yasim::State s;
|
||||
|
@ -380,9 +382,9 @@ void YASim::copyToYASim(bool copyState)
|
|||
// These are set below:
|
||||
// _set_Accels_Local
|
||||
// _set_Accels_Body
|
||||
// _set_Accels_CG_Body
|
||||
// _set_Accels_CG_Body
|
||||
// _set_Accels_Pilot_Body
|
||||
// _set_Accels_CG_Body_N
|
||||
// _set_Accels_CG_Body_N
|
||||
// _set_Velocities_Local
|
||||
// _set_Velocities_Ground
|
||||
// _set_Velocities_Body
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <Include/version.h>
|
||||
|
||||
#include "QmlAircraftInfo.hxx"
|
||||
#include "FavouriteAircraftData.hxx"
|
||||
|
||||
using namespace simgear::pkg;
|
||||
|
||||
|
@ -125,7 +126,7 @@ private:
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -138,6 +139,8 @@ void PackageDelegate::catalogRefreshed(CatalogRef aCatalog, StatusCode aReason)
|
|||
// nothing to do
|
||||
} else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) {
|
||||
m_model->refreshPackages();
|
||||
} else if (aReason == FAIL_VERSION) {
|
||||
// silent about this
|
||||
} else {
|
||||
qWarning() << "failed refresh of"
|
||||
<< QString::fromStdString(aCatalog->url()) << ":" << aReason << endl;
|
||||
|
@ -158,8 +161,6 @@ AircraftItemModel::AircraftItemModel(QObject* pr) :
|
|||
this, &AircraftItemModel::onScanAddedItems);
|
||||
connect(cache, &LocalAircraftCache::cleared,
|
||||
this, &AircraftItemModel::onLocalCacheCleared);
|
||||
|
||||
loadFavourites();
|
||||
}
|
||||
|
||||
AircraftItemModel::~AircraftItemModel()
|
||||
|
@ -244,7 +245,7 @@ QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
|
|||
if (role == AircraftIsFavouriteRole) {
|
||||
// recursive call here, hope that's okay
|
||||
const auto uri = data(index, AircraftURIRole).toUrl();
|
||||
return m_favourites.contains(uri);
|
||||
return FavouriteAircraftData::instance()->isFavourite(uri);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if (role >= AircraftVariantDescriptionRole) {
|
||||
int variantIndex = role - AircraftVariantDescriptionRole;
|
||||
const unsigned int variantIndex = static_cast<unsigned int>(role - AircraftVariantDescriptionRole);
|
||||
QString desc = QString::fromStdString(item->nameForVariant(variantIndex));
|
||||
if (desc.isEmpty()) {
|
||||
desc = tr("Missing description for: %1").arg(QString::fromStdString(item->id()));
|
||||
|
@ -431,17 +432,11 @@ bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value,
|
|||
emit dataChanged(index, index);
|
||||
return true;
|
||||
} else if (role == AircraftIsFavouriteRole) {
|
||||
bool f = value.toBool();
|
||||
const auto uri = data(index, AircraftURIRole).toUrl();
|
||||
const auto cur = m_favourites.contains(uri);
|
||||
if (f && !cur) {
|
||||
m_favourites.append(uri);
|
||||
} else if (!f && cur) {
|
||||
m_favourites.removeOne(uri);
|
||||
bool changed = FavouriteAircraftData::instance()->setFavourite(uri, value.toBool());
|
||||
if (changed) {
|
||||
emit dataChanged(index, index);
|
||||
}
|
||||
|
||||
saveFavourites();
|
||||
emit dataChanged(index, index);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -493,9 +488,10 @@ QModelIndex AircraftItemModel::indexOfAircraftURI(QUrl uri) const
|
|||
|
||||
PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString());
|
||||
if (pkg) {
|
||||
for (size_t i=0; i < m_packages.size(); ++i) {
|
||||
if (m_packages[i] == pkg) {
|
||||
return index(rowOffset + i);
|
||||
const auto numPackages = m_packages.size();
|
||||
for (unsigned int i=0; i < numPackages; ++i) {
|
||||
if (m_packages.at(i) == pkg) {
|
||||
return index(static_cast<int>(rowOffset + i));
|
||||
}
|
||||
} // of linear package scan
|
||||
}
|
||||
|
@ -542,7 +538,7 @@ void AircraftItemModel::selectVariantForAircraftURI(QUrl uri)
|
|||
if (pkg) {
|
||||
for (size_t i=0; i < m_packages.size(); ++i) {
|
||||
if (m_packages[i] == pkg) {
|
||||
modelIndex = index(rowOffset + static_cast<int>(i));
|
||||
modelIndex = index(rowOffset + static_cast<int>(i));
|
||||
variantIndex = pkg->indexOfVariant(ident.toStdString());
|
||||
break;
|
||||
}
|
||||
|
@ -663,21 +659,3 @@ bool AircraftItemModel::isIndexRunnable(const QModelIndex& index) const
|
|||
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 installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason);
|
||||
|
||||
void loadFavourites();
|
||||
void saveFavourites();
|
||||
|
||||
private:
|
||||
PackageDelegate* m_delegate = nullptr;
|
||||
|
||||
|
@ -156,8 +153,6 @@ private:
|
|||
|
||||
simgear::pkg::RootRef m_packageRoot;
|
||||
simgear::pkg::PackageList m_packages;
|
||||
|
||||
QVector<QUrl> m_favourites;
|
||||
int m_cachedLocalAircraftCount = 0;
|
||||
|
||||
};
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <QDebug>
|
||||
|
||||
#include "AircraftModel.hxx"
|
||||
#include "FavouriteAircraftData.hxx"
|
||||
|
||||
#include <simgear/package/Package.hxx>
|
||||
|
||||
AircraftProxyModel::AircraftProxyModel(QObject *pr, QAbstractItemModel * source) :
|
||||
|
@ -121,6 +123,13 @@ void AircraftProxyModel::setShowFavourites(bool e)
|
|||
return;
|
||||
|
||||
m_onlyShowFavourites = e;
|
||||
if (e) {
|
||||
setDynamicSortFilter(false);
|
||||
connect(FavouriteAircraftData::instance(), &FavouriteAircraftData::changed,
|
||||
[this]() {
|
||||
this->invalidate();
|
||||
});
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ protected:
|
|||
private:
|
||||
bool filterAircraft(const QModelIndex& sourceIndex) const;
|
||||
|
||||
bool m_ratingsFilter = true;
|
||||
bool m_ratingsFilter = false;
|
||||
bool m_onlyShowInstalled = false;
|
||||
bool m_onlyShowWithUpdate = false;
|
||||
bool m_onlyShowFavourites = false;
|
||||
|
|
|
@ -155,6 +155,25 @@ void BaseDiagram::paintAirplaneIcon(QPainter* painter, const SGGeod& geod, int h
|
|||
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)
|
||||
{
|
||||
QTransform invT = m_viewportTransform.inverted();
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
void setAircraftType(LauncherController::AircraftType type);
|
||||
|
||||
QRect rect() const;
|
||||
|
||||
|
||||
Q_INVOKABLE void resetZoom();
|
||||
protected:
|
||||
void paint(QPainter* p) override;
|
||||
|
@ -78,7 +78,7 @@ protected:
|
|||
void extendBounds(const QPointF& p, double radiusM = 1.0);
|
||||
QPointF project(const SGGeod& geod) const;
|
||||
QTransform transform() const;
|
||||
|
||||
|
||||
void clearIgnoredNavaids();
|
||||
void addIgnoredNavaid(FGPositionedRef pos);
|
||||
|
||||
|
@ -98,6 +98,7 @@ protected:
|
|||
static SGGeod unproject(const QPointF &xy, const SGGeod ¢er);
|
||||
|
||||
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);
|
||||
|
||||
QPointF projectedPosition(PositionedID pid) const;
|
||||
|
|
|
@ -84,6 +84,8 @@ if (HAVE_QT)
|
|||
BaseDiagram.hxx
|
||||
AirportDiagram.cxx
|
||||
AirportDiagram.hxx
|
||||
CarrierDiagram.cxx
|
||||
CarrierDiagram.hxx
|
||||
NavaidDiagram.cxx
|
||||
NavaidDiagram.hxx
|
||||
SetupRootDialog.cxx
|
||||
|
@ -128,6 +130,8 @@ if (HAVE_QT)
|
|||
PathListModel.hxx
|
||||
CarriersLocationModel.cxx
|
||||
CarriersLocationModel.hxx
|
||||
FavouriteAircraftData.cxx
|
||||
FavouriteAircraftData.hxx
|
||||
${uic_sources}
|
||||
${qrc_sources}
|
||||
${qml_sources})
|
||||
|
@ -137,7 +141,7 @@ if (HAVE_QT)
|
|||
target_include_directories(fglauncher PRIVATE ${PROJECT_BINARY_DIR}/src/GUI)
|
||||
|
||||
add_library(fgqmlui QQuickDrawable.cxx
|
||||
QQuickDrawable.hxx
|
||||
QQuickDrawable.hxx
|
||||
QtQuickFGCanvasItem.cxx
|
||||
QtQuickFGCanvasItem.hxx
|
||||
PropertyItemModel.cxx
|
||||
|
|
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);
|
||||
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")) {
|
||||
const std::string scenarioId = s->getStringValue("id");
|
||||
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 pennant = QString::fromStdString(carrierNode->getStringValue("pennant-number"));
|
||||
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"),
|
||||
carrierNode->getDoubleValue("latitude"));
|
||||
|
||||
|
@ -38,6 +39,7 @@ void CarriersLocationModel::processCarrier(const string &scenario, SGPropertyNod
|
|||
QString::fromStdString(scenario),
|
||||
pennant,
|
||||
name,
|
||||
desc,
|
||||
geod,
|
||||
tacan,
|
||||
parkings
|
||||
|
@ -65,6 +67,8 @@ QVariant CarriersLocationModel::data(const QModelIndex &index, int role) const
|
|||
case NameRole: return c.mName;
|
||||
// case GeodRole: return QVariant::fromValue(c.mInitialLocation);
|
||||
case IdentRole: return c.mCallsign;
|
||||
case DescriptionRole: return c.mDescription;
|
||||
case TypeRole: return "Carrier";
|
||||
case IconRole: return QPixmap(":/svg/aircraft-carrier");
|
||||
default:
|
||||
break;
|
||||
|
@ -83,6 +87,7 @@ QHash<int, QByteArray> CarriersLocationModel::roleNames() const
|
|||
result[NameRole] = "name";
|
||||
result[IconRole] = "icon";
|
||||
result[TypeRole] = "type";
|
||||
result[DescriptionRole] = "description";
|
||||
result[NavFrequencyRole] = "frequency";
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@ public:
|
|||
NameRole = Qt::UserRole + 4,
|
||||
IconRole = Qt::UserRole + 5,
|
||||
TypeRole = Qt::UserRole + 6,
|
||||
NavFrequencyRole = Qt::UserRole + 7
|
||||
NavFrequencyRole = Qt::UserRole + 7,
|
||||
DescriptionRole = Qt::UserRole + 8
|
||||
};
|
||||
|
||||
int indexOf(const QString name) const;
|
||||
|
@ -46,6 +47,7 @@ private:
|
|||
QString mScenario; // scenario ID for loading
|
||||
QString mCallsign; // pennant-number
|
||||
QString mName;
|
||||
QString mDescription;
|
||||
SGGeod mInitialLocation;
|
||||
// icon?
|
||||
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)");
|
||||
break;
|
||||
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;
|
||||
case Delegate::FAIL_HTTP_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 "PixmapImageItem.hxx"
|
||||
#include "AirportDiagram.hxx"
|
||||
#include "CarrierDiagram.hxx"
|
||||
#include "NavaidDiagram.hxx"
|
||||
#include "RouteDiagram.hxx"
|
||||
#include "QmlRadioButtonHelper.hxx"
|
||||
|
@ -168,6 +169,7 @@ void LauncherController::initQML()
|
|||
|
||||
qmlRegisterType<PixmapImageItem>("FlightGear", 1, 0, "PixmapImage");
|
||||
qmlRegisterType<AirportDiagram>("FlightGear", 1, 0, "AirportDiagram");
|
||||
qmlRegisterType<CarrierDiagram>("FlightGear", 1, 0, "CarrierDiagram");
|
||||
qmlRegisterType<NavaidDiagram>("FlightGear", 1, 0, "NavaidDiagram");
|
||||
qmlRegisterType<RouteDiagram>("FlightGear", 1, 0, "RouteDiagram");
|
||||
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")) {
|
||||
return "approach";
|
||||
}
|
||||
|
@ -400,10 +406,14 @@ QString LauncherController::selectAircraftStateAutomatically()
|
|||
if (m_selectedAircraftInfo->hasState("parking")) {
|
||||
return "parking";
|
||||
}
|
||||
} else {
|
||||
// also try 'engines-running'?
|
||||
if (m_selectedAircraftInfo->hasState("take-off"))
|
||||
return "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 {}; // failed to compute, give up
|
||||
|
|
|
@ -39,9 +39,6 @@
|
|||
|
||||
static quint32 CACHE_VERSION = 12;
|
||||
|
||||
const int STANDARD_THUMBNAIL_HEIGHT = 128;
|
||||
//const int STANDARD_THUMBNAIL_WIDTH = 172;
|
||||
|
||||
AircraftItem::AircraftItem()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include "AirportDiagram.hxx"
|
||||
#include "CarrierDiagram.hxx"
|
||||
#include "NavaidDiagram.hxx"
|
||||
#include "LaunchConfig.hxx"
|
||||
#include "DefaultAircraftLocator.hxx"
|
||||
|
@ -226,6 +227,7 @@ void LocationController::clearLocation()
|
|||
{
|
||||
m_locationIsLatLon = false;
|
||||
m_locationIsCarrier = false;
|
||||
m_abeam = false;
|
||||
m_location.clear();
|
||||
m_carrierName.clear();
|
||||
m_airportLocation.clear();
|
||||
|
@ -365,8 +367,8 @@ void LocationController::showHistoryInSearchModel()
|
|||
const std::string tutorialICAO = "PHTO"; // C172P tutorial aiurport
|
||||
|
||||
// remove them from the recent locations
|
||||
auto it = std::remove_if(locs.begin(), locs.end(),
|
||||
[defaultICAO, tutorialICAO](FGPositionedRef pos)
|
||||
auto it = std::remove_if(locs.begin(), locs.end(),
|
||||
[defaultICAO, tutorialICAO](FGPositionedRef pos)
|
||||
{
|
||||
return (pos->ident() == defaultICAO) || (pos->ident() == tutorialICAO);
|
||||
});
|
||||
|
@ -507,6 +509,12 @@ void LocationController::setUseCarrierFLOLS(bool useCarrierFLOLS)
|
|||
emit configChanged();
|
||||
}
|
||||
|
||||
void LocationController::setAbeam(bool abeam)
|
||||
{
|
||||
m_abeam = abeam;
|
||||
emit configChanged();
|
||||
}
|
||||
|
||||
void LocationController::restoreLocation(QVariantMap l)
|
||||
{
|
||||
clearLocation();
|
||||
|
@ -533,6 +541,7 @@ void LocationController::restoreLocation(QVariantMap l)
|
|||
setCarrierLocation(l.value("carrier").toString());
|
||||
if (l.contains("carrier-flols")) {
|
||||
setUseCarrierFLOLS(l.value("carrier-flols").toBool());
|
||||
setAbeam(l.value("abeam").toBool());
|
||||
// overwrite value form above, intentionally
|
||||
m_offsetDistance = l.value("location-carrier-flols-distance", QVariant::fromValue(m_defaultOffsetDistance)).value<QuantityValue>();
|
||||
} else if (l.contains("carrier-parking")) {
|
||||
|
@ -576,6 +585,7 @@ void LocationController::restoreLocation(QVariantMap l)
|
|||
}
|
||||
|
||||
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>();
|
||||
} // of location is an airport
|
||||
} catch (const sg_exception&) {
|
||||
|
@ -593,7 +603,7 @@ bool LocationController::shouldStartPaused() const
|
|||
if (m_useCarrierFLOLS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (!m_location) {
|
||||
return false; // defaults to on-ground at the default airport
|
||||
}
|
||||
|
@ -619,6 +629,7 @@ QVariantMap LocationController::saveLocation() const
|
|||
if (m_useCarrierFLOLS) {
|
||||
locationSet.insert("carrier-flols", true);
|
||||
locationSet.insert("location-carrier-flols-distance", QVariant::fromValue(m_offsetDistance));
|
||||
locationSet.insert("abeam", m_abeam);
|
||||
} else if (!m_carrierParking.isEmpty()) {
|
||||
locationSet.insert("carrier-parking", m_carrierParking);
|
||||
}
|
||||
|
@ -628,6 +639,7 @@ QVariantMap LocationController::saveLocation() const
|
|||
if (m_airportLocation) {
|
||||
locationSet.insert("location-on-final", m_onFinal);
|
||||
locationSet.insert("location-apt-final-distance", QVariant::fromValue(m_offsetDistance));
|
||||
locationSet.insert("abeam", m_abeam);
|
||||
if (m_useActiveRunway) {
|
||||
locationSet.insert("location-apt-runway", "ACTIVE");
|
||||
} else if (m_useAvailableParking) {
|
||||
|
@ -675,7 +687,7 @@ void LocationController::setLocationProperties()
|
|||
"runway-requested" << "navaid-id" << "offset-azimuth-deg" <<
|
||||
"offset-distance-nm" << "glideslope-deg" <<
|
||||
"speed-set" << "on-ground" << "airspeed-kt" <<
|
||||
"airport-id" << "runway" << "parkpos" << "carrier";
|
||||
"airport-id" << "runway" << "parkpos" << "carrier" << "abeam";
|
||||
|
||||
Q_FOREACH(QString s, props) {
|
||||
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
|
||||
fgSetString("/sim/presets/runway", "FLOLS");
|
||||
fgSetDouble("/sim/presets/offset-distance-nm", m_offsetDistance.convertToUnit(Units::NauticalMiles).value);
|
||||
|
||||
fgSetBool("/sim/presets/abeam", m_abeam);
|
||||
applyAltitude();
|
||||
applyAirspeed();
|
||||
} else if (!m_carrierParking.isEmpty()) {
|
||||
fgSetString("/sim/presets/parkpos", m_carrierParking.toStdString());
|
||||
|
@ -730,6 +743,7 @@ void LocationController::setLocationProperties()
|
|||
fgSetString("/sim/presets/airport-id", m_airportLocation->ident());
|
||||
fgSetBool("/sim/presets/on-ground", 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 atParking = (m_detailLocation && (m_detailLocation->type() == FGPositioned::PARKING));
|
||||
|
@ -792,11 +806,11 @@ void LocationController::setLocationProperties()
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// set disambiguation property
|
||||
globals->get_props()->setIntValue("/sim/presets/navaid-id",
|
||||
static_cast<int>(m_location->guid()));
|
||||
|
||||
|
||||
applyPositionOffset();
|
||||
applyAltitude();
|
||||
applyAirspeed();
|
||||
|
@ -847,7 +861,7 @@ void LocationController::applyAltitude()
|
|||
|
||||
switch (m_altitude.unit) {
|
||||
default:
|
||||
qWarning() << Q_FUNC_INFO << "unsupported altitdue unit";
|
||||
qWarning() << Q_FUNC_INFO << "unsupported altitude unit";
|
||||
break;
|
||||
case Units::FeetMSL:
|
||||
m_config->setArg("altitude", QString::number(m_altitude.value));
|
||||
|
@ -915,6 +929,9 @@ void LocationController::onCollectConfig()
|
|||
m_config->setArg("runway", QStringLiteral("FLOLS"));
|
||||
const double offsetNm = m_offsetDistance.convertToUnit(Units::NauticalMiles).value;
|
||||
m_config->setArg("offset-distance", QString::number(offsetNm));
|
||||
|
||||
if (m_abeam) m_config->setArg("carrier-abeam", QStringLiteral("true"));
|
||||
applyAltitude();
|
||||
applyAirspeed();
|
||||
}
|
||||
|
||||
|
@ -1029,6 +1046,8 @@ QString compassPointFromHeading(int heading)
|
|||
|
||||
QString LocationController::description() const
|
||||
{
|
||||
const double offsetNm = m_offsetDistance.convertToUnit(Units::NauticalMiles).value;
|
||||
|
||||
if (!m_location) {
|
||||
if (m_locationIsLatLon) {
|
||||
const auto s = simgear::strutils::formatGeodAsString(m_geodLocation,
|
||||
|
@ -1039,7 +1058,15 @@ QString LocationController::description() const
|
|||
|
||||
if (m_locationIsCarrier) {
|
||||
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");
|
||||
|
@ -1049,7 +1076,6 @@ QString LocationController::description() const
|
|||
name = QString::fromStdString(m_location->name());
|
||||
|
||||
name = fixNavaidName(name);
|
||||
const double offsetNm = m_offsetDistance.convertToUnit(Units::NauticalMiles).value;
|
||||
|
||||
if (m_airportLocation) {
|
||||
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(bool useCarrierFLOLS READ useCarrierFLOLS WRITE setUseCarrierFLOLS 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
|
||||
|
@ -189,6 +190,11 @@ public:
|
|||
return m_useCarrierFLOLS;
|
||||
}
|
||||
|
||||
bool abeam() const
|
||||
{
|
||||
return m_abeam;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void setOffsetRadial(QuantityValue offsetRadial);
|
||||
|
||||
|
@ -204,6 +210,8 @@ public slots:
|
|||
|
||||
void setUseCarrierFLOLS(bool useCarrierFLOLS);
|
||||
|
||||
void setAbeam(bool abeam);
|
||||
|
||||
Q_SIGNALS:
|
||||
void descriptionChanged();
|
||||
void offsetChanged();
|
||||
|
@ -261,6 +269,7 @@ private:
|
|||
bool m_speedEnabled = false;
|
||||
bool m_altitudeEnabled = false;
|
||||
bool m_skipFromArgs = false;
|
||||
bool m_abeam;
|
||||
|
||||
bool m_useCarrierFLOLS = false;
|
||||
QString m_carrierParking;
|
||||
|
|
|
@ -43,7 +43,10 @@ QVariant ModelDataExtractor::data() const
|
|||
return m_rawModel.property(uIndex).toVariant();
|
||||
}
|
||||
|
||||
qWarning() << "Unable to convert model data:" << m_rawModel.toString();
|
||||
if (!m_rawModel.isUndefined() && !m_rawModel.isNull()) {
|
||||
qWarning() << "Unable to convert model data:" << m_rawModel.toString();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <Main/globals.hxx>
|
||||
|
||||
#include "LocalAircraftCache.hxx"
|
||||
#include "FavouriteAircraftData.hxx"
|
||||
|
||||
using namespace simgear::pkg;
|
||||
|
||||
|
@ -36,6 +37,8 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
void finishInstall(InstallRef aInstall, StatusCode aReason) 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
|
||||
{
|
||||
Q_UNUSED(aReason);
|
||||
Q_UNUSED(aReason)
|
||||
if (aInstall->package() == p->packageRef()) {
|
||||
p->downloadChanged();
|
||||
}
|
||||
|
@ -85,6 +78,16 @@ private:
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
@ -109,7 +112,7 @@ static AircraftStateVec readAircraftStates(const SGPath& setXMLPath)
|
|||
// malformed include or XML, just bail
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
if (!root->getNode("sim/state")) {
|
||||
return {};
|
||||
}
|
||||
|
@ -141,7 +144,7 @@ QString humanNameFromStateTag(const std::string& tag)
|
|||
{
|
||||
if (tag == "approach") return QObject::tr("On approach");
|
||||
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"))
|
||||
return QObject::tr("Parked, cold & dark");
|
||||
if (tag == "auto")
|
||||
|
@ -150,6 +153,10 @@ QString humanNameFromStateTag(const std::string& tag)
|
|||
return QObject::tr("Cruise");
|
||||
if (tag == "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);
|
||||
// no mapping, let's use the tag directly
|
||||
|
@ -299,6 +306,8 @@ QmlAircraftInfo::QmlAircraftInfo(QObject *parent)
|
|||
, _delegate(new Delegate(this))
|
||||
{
|
||||
qmlRegisterUncreatableType<StatesModel>("FlightGear.Launcher", 1, 0, "StatesModel", "no");
|
||||
connect(FavouriteAircraftData::instance(), &FavouriteAircraftData::changed,
|
||||
this, &QmlAircraftInfo::onFavouriteChanged);
|
||||
}
|
||||
|
||||
QmlAircraftInfo::~QmlAircraftInfo()
|
||||
|
@ -441,7 +450,7 @@ QVariantList QmlAircraftInfo::previews() const
|
|||
for (auto p : previews) {
|
||||
SGPath localPreviewPath = ex->path() / p.path;
|
||||
if (!localPreviewPath.exists()) {
|
||||
qWarning() << "missing local preview" << QString::fromStdString(localPreviewPath.utf8Str());
|
||||
// this happens when the aircraft is being installed, for example
|
||||
continue;
|
||||
}
|
||||
result.append(QUrl::fromLocalFile(QString::fromStdString(localPreviewPath.utf8Str())));
|
||||
|
@ -667,6 +676,7 @@ void QmlAircraftInfo::setUri(QUrl u)
|
|||
emit uriChanged();
|
||||
emit infoChanged();
|
||||
emit downloadChanged();
|
||||
emit favouriteChanged();
|
||||
}
|
||||
|
||||
void QmlAircraftInfo::setVariant(quint32 variant)
|
||||
|
@ -691,6 +701,19 @@ void QmlAircraftInfo::setVariant(quint32 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)
|
||||
{
|
||||
if (p->hasTag("needs-maintenance")) {
|
||||
|
@ -876,5 +899,9 @@ bool QmlAircraftInfo::hasTag(QString tag) const
|
|||
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(StatesModel* statesModel READ statesModel NOTIFY infoChanged)
|
||||
|
||||
Q_PROPERTY(bool favourite READ favourite WRITE setFavourite NOTIFY favouriteChanged)
|
||||
public:
|
||||
explicit QmlAircraftInfo(QObject *parent = nullptr);
|
||||
virtual ~QmlAircraftInfo();
|
||||
|
@ -127,11 +128,14 @@ public:
|
|||
Q_INVOKABLE bool isAltitudeBelowLimits(QuantityValue speed) const;
|
||||
|
||||
Q_INVOKABLE bool hasTag(QString tag) const;
|
||||
bool favourite() const;
|
||||
|
||||
signals:
|
||||
void uriChanged();
|
||||
void infoChanged();
|
||||
void downloadChanged();
|
||||
void variantChanged(quint32 variant);
|
||||
void favouriteChanged();
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -139,6 +143,10 @@ public slots:
|
|||
|
||||
void setVariant(quint32 variant);
|
||||
|
||||
void setFavourite(bool favourite);
|
||||
|
||||
private slots:
|
||||
void onFavouriteChanged(QUrl u);
|
||||
private:
|
||||
AircraftItemPtr resolveItem() const;
|
||||
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,59 +155,71 @@ Rectangle {
|
|||
visible: aircraft.previews.length > 0
|
||||
}
|
||||
|
||||
Grid {
|
||||
id: ratingGrid
|
||||
anchors.left: parent.left
|
||||
Row {
|
||||
height: ratingGrid.height
|
||||
width: parent.width
|
||||
spacing: Style.strutSize
|
||||
|
||||
visible: aircraft.ratings !== undefined
|
||||
|
||||
rows: 2
|
||||
columns: 3
|
||||
rowSpacing: Style.margin
|
||||
columnSpacing: Style.margin
|
||||
|
||||
StyledText {
|
||||
id: ratingsLabel
|
||||
text: qsTr("Ratings:")
|
||||
}
|
||||
|
||||
|
||||
AircraftRating {
|
||||
title: qsTr("Flight model")
|
||||
Binding on value {
|
||||
when: aircraft.ratings !== undefined
|
||||
value: aircraft.ratings[0]
|
||||
FavouriteToggleButton {
|
||||
checked: aircraft.favourite
|
||||
onToggle: {
|
||||
aircraft.favourite = on;
|
||||
}
|
||||
}
|
||||
|
||||
AircraftRating {
|
||||
title: qsTr("Systems")
|
||||
Binding on value {
|
||||
when: aircraft.ratings !== undefined
|
||||
value: aircraft.ratings[1]
|
||||
}
|
||||
}
|
||||
Grid {
|
||||
id: ratingGrid
|
||||
|
||||
Item {
|
||||
width: ratingsLabel.width
|
||||
height: 1
|
||||
} // placeholder
|
||||
visible: aircraft.ratings !== undefined
|
||||
|
||||
AircraftRating {
|
||||
title: qsTr("Cockpit")
|
||||
Binding on value {
|
||||
when: aircraft.ratings !== undefined
|
||||
value: aircraft.ratings[2]
|
||||
}
|
||||
}
|
||||
rows: 2
|
||||
columns: 3
|
||||
rowSpacing: Style.margin
|
||||
columnSpacing: Style.margin
|
||||
|
||||
AircraftRating {
|
||||
title: qsTr("Exterior")
|
||||
Binding on value {
|
||||
when: aircraft.ratings !== undefined
|
||||
value: aircraft.ratings[3]
|
||||
StyledText {
|
||||
id: ratingsLabel
|
||||
text: qsTr("Ratings:")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AircraftRating {
|
||||
title: qsTr("Flight model")
|
||||
Binding on value {
|
||||
when: aircraft.ratings !== undefined
|
||||
value: aircraft.ratings[0]
|
||||
}
|
||||
}
|
||||
|
||||
AircraftRating {
|
||||
title: qsTr("Systems")
|
||||
Binding on value {
|
||||
when: aircraft.ratings !== undefined
|
||||
value: aircraft.ratings[1]
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: ratingsLabel.width
|
||||
height: 1
|
||||
} // placeholder
|
||||
|
||||
AircraftRating {
|
||||
title: qsTr("Cockpit")
|
||||
Binding on value {
|
||||
when: aircraft.ratings !== undefined
|
||||
value: aircraft.ratings[2]
|
||||
}
|
||||
}
|
||||
|
||||
AircraftRating {
|
||||
title: qsTr("Exterior")
|
||||
Binding on value {
|
||||
when: aircraft.ratings !== undefined
|
||||
value: aircraft.ratings[3]
|
||||
}
|
||||
}
|
||||
} // of rating grid
|
||||
}
|
||||
|
||||
StyledText {
|
||||
|
|
|
@ -109,6 +109,10 @@ Item {
|
|||
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
|
||||
return "%1 - %2".arg(model.ident).arg(model.name);
|
||||
}
|
||||
|
@ -225,7 +229,13 @@ Item {
|
|||
icon: "qrc:///svg/icon-carrier"
|
||||
|
||||
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 {
|
||||
property alias geod: diagram.geod
|
||||
|
||||
NavaidDiagram {
|
||||
CarrierDiagram {
|
||||
id: diagram
|
||||
anchors.fill: parent
|
||||
|
||||
offsetEnabled: _location.offsetEnabled
|
||||
offsetBearing: _location.offsetRadial
|
||||
offsetEnabled: _location.useCarrierFLOLS
|
||||
offsetDistance: _location.offsetDistance
|
||||
heading: _location.heading
|
||||
abeam: _location.abeam
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
|
@ -22,7 +21,9 @@ Item {
|
|||
|
||||
function syncUIFromController()
|
||||
{
|
||||
if (_location.useCarrierFLOLS) {
|
||||
if (_location.abeam) {
|
||||
abeamRadio.select()
|
||||
} else if (_location.useCarrierFLOLS) {
|
||||
flolsRadio.select()
|
||||
} else {
|
||||
parkingRadio.select();
|
||||
|
@ -46,7 +47,7 @@ Item {
|
|||
|
||||
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 {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
|
@ -76,7 +77,7 @@ Item {
|
|||
font.pixelSize: Style.headingFontPixelSize
|
||||
}
|
||||
|
||||
// on FLOLS offset
|
||||
// on final approach
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.margin
|
||||
|
@ -89,9 +90,12 @@ Item {
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
group: radioGroup
|
||||
onClicked: {
|
||||
if (selected) _location.useCarrierFLOLS = selected
|
||||
if (selected) {
|
||||
_location.useCarrierFLOLS = selected;
|
||||
_location.abeam = false;
|
||||
}
|
||||
}
|
||||
selected: _location.useCarrierFLOLS
|
||||
selected: _location.useCarrierFLOLS && (!_location.abeam)
|
||||
}
|
||||
|
||||
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 {
|
||||
id: offsetNmEdit
|
||||
quantity: _location.offsetDistance
|
||||
|
@ -109,13 +154,13 @@ Item {
|
|||
unitsMode: Units.Distance
|
||||
live: true
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
enabled: flolsRadio.selected
|
||||
enabled: selectionGrid.offsetEnabled
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: qsTr(" from the FLOLS (aka the ball)")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
enabled: flolsRadio.selected
|
||||
enabled: selectionGrid.offsetEnabled
|
||||
}
|
||||
|
||||
Item {
|
||||
|
@ -124,7 +169,7 @@ Item {
|
|||
|
||||
ToggleSwitch {
|
||||
id: airspeedToggle
|
||||
enabled: flolsRadio.selected
|
||||
enabled: selectionGrid.offsetEnabled
|
||||
checked: _location.speedEnabled
|
||||
onCheckedChanged: _location.speedEnabled = checked;
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
@ -134,12 +179,23 @@ Item {
|
|||
id: airspeedSpinbox
|
||||
label: qsTr("Airspeed:")
|
||||
unitsMode: Units.SpeedWithoutMach
|
||||
enabled: _location.speedEnabled && flolsRadio.selected
|
||||
enabled: _location.speedEnabled && selectionGrid.offsetEnabled
|
||||
quantity: _location.airspeed
|
||||
onCommit: _location.airspeed = newValue
|
||||
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
|
||||
Row {
|
||||
|
@ -159,10 +215,11 @@ Item {
|
|||
onClicked: {
|
||||
if (selected) parkingChoice.setLocation();
|
||||
}
|
||||
selected : (! _location.abeam) && (! _location.useCarrierFLOLS)
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: qsTr("Parking")
|
||||
text: qsTr("On deck")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
enabled: parkingRadio.selected
|
||||
}
|
||||
|
|
|
@ -90,8 +90,9 @@ Item {
|
|||
|
||||
var defaultValue = ("defaultValue" in root) ? root.defaultValue : undefined;
|
||||
var rawValue = _config.getValueForKey("", root.setting, defaultValue);
|
||||
// console.warn("restoring state for " + root.setting + ", got raw value " + rawValue + " with type " + typeof(rawValue))
|
||||
if (rawValue !== undefined) {
|
||||
|
||||
// console.warn("restoring state for " + root.setting + ", got raw value " + rawValue + " with type " + typeof(rawValue))
|
||||
if (rawValue !== defaultValue) {
|
||||
setValue(rawValue);
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +100,6 @@ Item {
|
|||
function setValue(newValue)
|
||||
{
|
||||
// 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-hide">assets/icons8-hide-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>
|
||||
</RCC>
|
||||
|
|
|
@ -70,3 +70,5 @@
|
|||
#cmakedefine ENABLE_COMPOSITOR
|
||||
|
||||
#cmakedefine ENABLE_SWIFT
|
||||
|
||||
#cmakedefine HAVE_SENTRY
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
include(FlightGearComponent)
|
||||
|
||||
IF(APPLE)
|
||||
set(EVENT_INPUT_SOURCES FGMacOSXEventInput.cxx)
|
||||
set(EVENT_INPUT_HEADERS FGMacOSXEventInput.hxx)
|
||||
# no Mac implemention, use HID
|
||||
elseif(WIN32)
|
||||
# no Win32 specific implementation, at least for now
|
||||
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
|
||||
FGEventData ed = eventData;
|
||||
|
||||
if( fabs(ed.value) < deadband )
|
||||
ed.value = 0.0;
|
||||
|
||||
if( minRange != maxRange )
|
||||
ed.value = 2.0*(eventData.value-minRange)/(maxRange-minRange)-1.0;
|
||||
|
||||
if( fabs(ed.value) < deadband )
|
||||
ed.value = 0.0;
|
||||
|
||||
if (interpolater) {
|
||||
if ((ed.value < 0.0) && mirrorInterpolater) {
|
||||
// mirror the positive interpolation for negative values
|
||||
|
|
|
@ -312,12 +312,16 @@ private:
|
|||
std::string _hidPath;
|
||||
hid_device* _device = nullptr;
|
||||
bool _haveNumberedReports = false;
|
||||
bool _debugRaw = false;
|
||||
|
||||
/// set if we parsed the device description our XML
|
||||
/// instead of from the USB data. Useful on Windows where the data
|
||||
/// is inaccessible, or devices with broken descriptors
|
||||
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.
|
||||
std::set<Report*> _dirtyReports;
|
||||
};
|
||||
|
@ -384,6 +388,17 @@ void FGHIDDevice::Configure(SGPropertyNode_ptr node)
|
|||
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()
|
||||
|
@ -395,6 +410,19 @@ bool FGHIDDevice::Open()
|
|||
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) {
|
||||
bool ok = parseUSBHIDDescriptor();
|
||||
if (!ok)
|
||||
|
@ -422,26 +450,22 @@ bool FGHIDDevice::Open()
|
|||
bool FGHIDDevice::parseUSBHIDDescriptor()
|
||||
{
|
||||
#if defined(SG_WINDOWS)
|
||||
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.");
|
||||
return false;
|
||||
#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");
|
||||
if (_rawXMLDescriptor.empty()) {
|
||||
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.");
|
||||
SG_LOG(SG_INPUT, SG_ALERT, "See this page:<> for information on extracting the report descriptor on Windows");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (debugEvents) {
|
||||
if (_debugRaw) {
|
||||
SG_LOG(SG_INPUT, SG_INFO, "\nHID: descriptor for:" << GetUniqueName());
|
||||
{
|
||||
std::ostringstream byteString;
|
||||
|
||||
for (int i=0; i<descriptorSize; ++i) {
|
||||
byteString << hexTable[reportDescriptor[i] >> 4];
|
||||
byteString << hexTable[reportDescriptor[i] & 0x0f];
|
||||
for (auto i=0; i<_rawXMLDescriptor.size(); ++i) {
|
||||
byteString << hexTable[_rawXMLDescriptor[i] >> 4];
|
||||
byteString << hexTable[_rawXMLDescriptor[i] & 0x0f];
|
||||
byteString << " ";
|
||||
}
|
||||
SG_LOG(SG_INPUT, SG_INFO, "\tbytes: " << byteString.str());
|
||||
|
@ -449,7 +473,7 @@ bool FGHIDDevice::parseUSBHIDDescriptor()
|
|||
}
|
||||
|
||||
hid_item* rootItem = nullptr;
|
||||
hid_parse_reportdesc(reportDescriptor, descriptorSize, &rootItem);
|
||||
hid_parse_reportdesc(_rawXMLDescriptor.data(), _rawXMLDescriptor.size(), &rootItem);
|
||||
if (debugEvents) {
|
||||
SG_LOG(SG_INPUT, SG_INFO, "\nHID: scan for:" << GetUniqueName());
|
||||
}
|
||||
|
@ -645,6 +669,18 @@ void FGHIDDevice::sendReport(Report* report) const
|
|||
}
|
||||
|
||||
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
|
||||
if (report->type == HID::ReportType::Feature) {
|
||||
hid_send_feature_report(_device, reportBytes, reportLength + 1);
|
||||
|
@ -663,7 +699,7 @@ void FGHIDDevice::processInputReport(Report* report, unsigned char* data,
|
|||
size_t length,
|
||||
double dt, int keyModifiers)
|
||||
{
|
||||
if (debugEvents) {
|
||||
if (_debugRaw) {
|
||||
SG_LOG(SG_INPUT, SG_INFO, GetName() << " FGHIDDeivce received input report:" << (int) report->number << ", len=" << length);
|
||||
{
|
||||
std::ostringstream byteString;
|
||||
|
@ -698,7 +734,7 @@ void FGHIDDevice::processInputReport(Report* report, unsigned char* data,
|
|||
if (!item->event)
|
||||
continue;
|
||||
|
||||
if (debugEvents) {
|
||||
if (_debugRaw) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (debugEvents) {
|
||||
if (_debugRaw) {
|
||||
SG_LOG(SG_INPUT, SG_INFO, GetName() << ": FGHIDDevice: Sending feature report:" << (int) reportId << ", len=" << data.size());
|
||||
{
|
||||
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);
|
||||
|
||||
if( updown != MOUSE_BUTTON_DOWN )
|
||||
if( updown == MOUSE_BUTTON_UP )
|
||||
{
|
||||
// Execute the mouse up event in any case, may be we should
|
||||
// stop processing here?
|
||||
|
@ -572,7 +572,7 @@ void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindo
|
|||
{
|
||||
SGPickCallbackPtr& cb = callbacks.front();
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -38,10 +38,9 @@
|
|||
|
||||
#ifdef WITH_EVENTINPUT
|
||||
#if defined ( SG_MAC )
|
||||
#include "FGMacOSXEventInput.hxx"
|
||||
#define INPUTEVENT_CLASS FGMacOSXEventInput
|
||||
// we use HID
|
||||
#elif defined(SG_WINDOWS)
|
||||
|
||||
// we use HID
|
||||
#else
|
||||
#include "FGLinuxEventInput.hxx"
|
||||
#define INPUTEVENT_CLASS FGLinuxEventInput
|
||||
|
|
|
@ -101,6 +101,7 @@ endif()
|
|||
|
||||
# Set up the target links.
|
||||
setup_fgfs_libraries(fgfs)
|
||||
export_debug_symbols(fgfs)
|
||||
|
||||
if (APPLE)
|
||||
install(TARGETS fgfs BUNDLE DESTINATION .)
|
||||
|
@ -108,6 +109,21 @@ else()
|
|||
install(TARGETS fgfs RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
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)
|
||||
add_executable(metar metar_main.cxx)
|
||||
target_link_libraries(metar
|
||||
|
|
|
@ -389,7 +389,27 @@ private:
|
|||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -628,7 +648,7 @@ int fgInitConfig ( int argc, char **argv, bool reinit )
|
|||
#endif
|
||||
|
||||
// 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";
|
||||
if (!defaultsXML.exists()) {
|
||||
flightgear::fatalMessageBoxThenExit(
|
||||
|
|
|
@ -216,11 +216,12 @@ FGIO::parse_port_config( const string_list& tokens )
|
|||
"(one argument expected: --hla-local=<federationname>" );
|
||||
return NULL;
|
||||
}
|
||||
tokens.insert(tokens.begin(), "");
|
||||
tokens.insert(tokens.begin(), "60");
|
||||
tokens.insert(tokens.begin(), "bi");
|
||||
tokens.push_back("fg-local.xml");
|
||||
return new FGHLA(tokens);
|
||||
std::vector<std::string> HLA_tokens (tokens);
|
||||
HLA_tokens.insert(HLA_tokens.begin(), "");
|
||||
HLA_tokens.insert(HLA_tokens.begin(), "60");
|
||||
HLA_tokens.insert(HLA_tokens.begin(), "bi");
|
||||
HLA_tokens.push_back("fg-local.xml");
|
||||
return new FGHLA(HLA_tokens);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
|
|
|
@ -326,28 +326,6 @@ getGMTString ()
|
|||
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.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
@ -439,9 +417,11 @@ FGProperties::bind ()
|
|||
_tiedProperties.Tie<const char*>("/position/latitude-string", getLatitudeString);
|
||||
_tiedProperties.Tie<const char*>("/position/longitude-string", getLongitudeString);
|
||||
|
||||
// Orientation
|
||||
_tiedProperties.Tie<double>("/orientation/heading-magnetic-deg", getHeadingMag);
|
||||
_tiedProperties.Tie<double>("/orientation/track-magnetic-deg", getTrackMag);
|
||||
_headingMagnetic = fgGetNode("/orientation/heading-magnetic-deg", true);
|
||||
_trackMagnetic = fgGetNode("/orientation/track-magnetic-deg", true);
|
||||
_magVar = fgGetNode("/environment/magnetic-variation-deg", true);
|
||||
_trueHeading = fgGetNode("/orientation/heading-deg", true);
|
||||
_trueTrack = fgGetNode("/orientation/track-deg", true);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -481,6 +461,10 @@ FGProperties::update (double dt)
|
|||
_rmin->setIntValue(r->tm_min);
|
||||
_rsec->setIntValue(r->tm_sec);
|
||||
_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 _uyear, _umonth, _uday, _uhour, _umin, _usec, _uwday, _udsec;
|
||||
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),
|
||||
useDefault))
|
||||
SG_LOG(SG_GENERAL, SG_WARN,
|
||||
SG_LOG(SG_GENERAL, SG_DEV_WARN,
|
||||
"Failed to tie property " << name << " to functions");
|
||||
}
|
||||
|
||||
|
@ -769,7 +773,7 @@ fgTie (const char * name, int index, V (*getter)(int),
|
|||
getter,
|
||||
setter),
|
||||
useDefault))
|
||||
SG_LOG(SG_GENERAL, SG_WARN,
|
||||
SG_LOG(SG_GENERAL, SG_DEV_WARN,
|
||||
"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,
|
||||
SGRawValueMethods<T,V>(*obj, getter, setter),
|
||||
useDefault))
|
||||
SG_LOG(SG_GENERAL, SG_WARN,
|
||||
SG_LOG(SG_GENERAL, SG_DEV_WARN,
|
||||
"Failed to tie property " << name << " to object methods");
|
||||
}
|
||||
|
||||
|
@ -837,7 +841,7 @@ fgTie (const char * name, T * obj, int index,
|
|||
getter,
|
||||
setter),
|
||||
useDefault))
|
||||
SG_LOG(SG_GENERAL, SG_WARN,
|
||||
SG_LOG(SG_GENERAL, SG_DEV_WARN,
|
||||
"Failed to tie property " << name << " to indexed object methods");
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,7 @@
|
|||
// $Id$
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <config.h>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
@ -165,7 +163,7 @@ void fgSetDefaults ()
|
|||
fgSetDouble("/sim/presets/offset-distance-nm", 0.0);
|
||||
|
||||
fgSetBool("/sim/presets/runway-requested", false);
|
||||
|
||||
|
||||
fgSetBool("/sim/presets/onground", true);
|
||||
fgSetBool("/sim/presets/trim", false);
|
||||
|
||||
|
@ -1421,22 +1419,22 @@ fgOptScenario( const char *arg )
|
|||
// make absolute
|
||||
path = simgear::Dir::current().path() / arg;
|
||||
}
|
||||
|
||||
|
||||
// create description node
|
||||
auto n = FGAIManager::registerScenarioFile(globals->get_props(), path);
|
||||
if (!n) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "failed to read scenario file at:" << path);
|
||||
return FG_OPTIONS_ERROR;
|
||||
}
|
||||
|
||||
|
||||
// also set the /sim/ai/scenario entry so we load it on startup
|
||||
name = path.file_base();
|
||||
}
|
||||
|
||||
|
||||
// add the 'load it' node
|
||||
SGPropertyNode_ptr ai_node = fgGetNode( "/sim/ai", true );
|
||||
ai_node->addChild("scenario")->setStringValue(name);
|
||||
|
||||
|
||||
return FG_OPTIONS_OK;
|
||||
}
|
||||
|
||||
|
@ -1680,6 +1678,7 @@ struct OptionDesc {
|
|||
{"ndb", true, OPTION_FUNC, "", false, "", fgOptNDB },
|
||||
{"ndb-frequency", true, OPTION_DOUBLE, "/sim/presets/ndb-freq", false, "", fgOptVOR },
|
||||
{"carrier", true, OPTION_FUNC, "", false, "", fgOptCarrier },
|
||||
{"carrier-abeam", true, OPTION_BOOL, "/sim/presets/carrier-abeam", true, "", 0 },
|
||||
{"parkpos", true, OPTION_FUNC, "", false, "", fgOptParkpos },
|
||||
{"fix", true, OPTION_FUNC, "", false, "", fgOptFIX },
|
||||
{"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 },
|
||||
{"on-ground", false, OPTION_BOOL, "/sim/presets/onground", true, "", 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-fastest", false, OPTION_STRING, "/sim/rendering/fog", false, "fastest", 0 },
|
||||
{"fog-nicest", false, OPTION_STRING, "/sim/rendering/fog", false, "nicest", 0 },
|
||||
|
|
|
@ -52,8 +52,8 @@ using std::string;
|
|||
|
||||
namespace flightgear
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
enum InitPosResult {
|
||||
ExactPosition,
|
||||
VicinityPosition,
|
||||
|
@ -180,7 +180,9 @@ std::tuple<SGGeod, double> runwayStartPos(FGRunwayRef runway)
|
|||
double startOffset = fgGetDouble("/sim/airport/runways/start-offset-m", 5.0);
|
||||
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() << "/" <<
|
||||
runway->ident() << ", MP is enabled so computing hold short position to avoid runway incursion");
|
||||
|
||||
|
@ -417,7 +419,7 @@ static InitPosResult setInitialPosFromCarrier( const string& carrier )
|
|||
SG_LOG( SG_GENERAL, SG_DEBUG, "Initial carrier pos = " << initialPos.second );
|
||||
return VicinityPosition;
|
||||
}
|
||||
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate aircraft carrier = " << carrier );
|
||||
return Failure;
|
||||
}
|
||||
|
@ -450,33 +452,33 @@ static InitPosResult checkCarrierSceneryLoaded(const SGSharedPtr<FGAICarrier> ca
|
|||
// Set current_options lon/lat given an aircraft carrier id
|
||||
static InitPosResult setFinalPosFromCarrier( const string& carrier, const string& posid )
|
||||
{
|
||||
|
||||
|
||||
SGSharedPtr<FGAICarrier> carrierRef = FGAICarrier::findCarrierByNameOrPennant(carrier);
|
||||
if (!carrierRef) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate aircraft carrier = "
|
||||
<< carrier );
|
||||
return Failure;
|
||||
}
|
||||
|
||||
|
||||
auto res = checkCarrierSceneryLoaded(carrierRef);
|
||||
if (res != VicinityPosition) {
|
||||
return res; // either failrue or keep waiting for scenery load
|
||||
}
|
||||
|
||||
|
||||
SGGeod geodPos;
|
||||
double heading;
|
||||
SGVec3d uvw;
|
||||
if (carrierRef->getParkPosition(posid, geodPos, heading, uvw)) {
|
||||
|
||||
|
||||
////////
|
||||
double lon = geodPos.getLongitudeDeg();
|
||||
double lat = geodPos.getLatitudeDeg();
|
||||
double alt = geodPos.getElevationFt() + 2.0;
|
||||
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Attempting to set starting position for "
|
||||
<< carrier << " at lat = " << lat << ", lon = " << lon
|
||||
<< ", alt = " << alt << ", heading = " << heading);
|
||||
|
||||
|
||||
fgSetDouble("/sim/presets/longitude-deg", lon);
|
||||
fgSetDouble("/sim/presets/latitude-deg", lat);
|
||||
fgSetDouble("/sim/presets/altitude-ft", alt);
|
||||
|
@ -485,7 +487,7 @@ static InitPosResult setFinalPosFromCarrier( const string& carrier, const string
|
|||
fgSetDouble("/position/latitude-deg", lat);
|
||||
fgSetDouble("/position/altitude-ft", alt);
|
||||
fgSetDouble("/orientation/heading-deg", heading);
|
||||
|
||||
|
||||
fgSetString("/sim/presets/speed-set", "UVW");
|
||||
fgSetDouble("/velocities/uBody-fps", uvw(0));
|
||||
fgSetDouble("/velocities/vBody-fps", uvw(1));
|
||||
|
@ -493,9 +495,9 @@ static InitPosResult setFinalPosFromCarrier( const string& carrier, const string
|
|||
fgSetDouble("/sim/presets/uBody-fps", uvw(0));
|
||||
fgSetDouble("/sim/presets/vBody-fps", uvw(1));
|
||||
fgSetDouble("/sim/presets/wBody-fps", uvw(2));
|
||||
|
||||
|
||||
fgSetBool("/sim/presets/onground", true);
|
||||
|
||||
|
||||
/////////
|
||||
return ExactPosition;
|
||||
}
|
||||
|
@ -503,7 +505,7 @@ static InitPosResult setFinalPosFromCarrier( const string& carrier, const string
|
|||
return Failure;
|
||||
}
|
||||
|
||||
static InitPosResult setFinalPosFromCarrierFLOLS(const string& carrier)
|
||||
static InitPosResult setFinalPosFromCarrierFLOLS(const string& carrier, bool abeam)
|
||||
{
|
||||
SGSharedPtr<FGAICarrier> carrierRef = FGAICarrier::findCarrierByNameOrPennant(carrier);
|
||||
if (!carrierRef) {
|
||||
|
@ -529,16 +531,29 @@ static InitPosResult setFinalPosFromCarrierFLOLS(const string& carrier)
|
|||
double gs = SGMiscd::deg2rad(carrierRef->getFLOLFSGlidepathAngleDeg());
|
||||
const double od = fgGetDouble("/sim/presets/offset-distance-nm");
|
||||
|
||||
// start position, but with altitude not set
|
||||
SGGeod startPos = SGGeodesy::direct(flolsPosition, headingToFLOLS + 180, od * SG_NM_TO_METER);
|
||||
// start position
|
||||
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);
|
||||
}
|
||||
|
||||
const double offsetFt = od * SG_NM_TO_METER * SG_METER_TO_FEET;
|
||||
startPos.setElevationFt(fabs(offsetFt*tan(gs)) + flolsElevationFt);
|
||||
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;
|
||||
startPos.setElevationFt(fabs(offsetFt*tan(gs)) + flolsElevationFt);
|
||||
} else {
|
||||
startPos.setElevationFt(alt);
|
||||
}
|
||||
|
||||
fgSetDouble("/sim/presets/longitude-deg", startPos.getLongitudeDeg());
|
||||
fgSetDouble("/sim/presets/latitude-deg", startPos.getLatitudeDeg());
|
||||
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/latitude-deg", startPos.getLatitudeDeg());
|
||||
fgSetDouble("/position/altitude-ft", startPos.getElevationFt());
|
||||
|
@ -576,21 +591,21 @@ bool initPosition()
|
|||
globals->get_event_mgr()->addTask("finalizePosition", &finalizePosition, 0.1);
|
||||
global_callbackRegistered = true;
|
||||
}
|
||||
|
||||
|
||||
double gs = SGMiscd::deg2rad(fgGetDouble("/sim/presets/glideslope-deg"));
|
||||
double od = fgGetDouble("/sim/presets/offset-distance-nm");
|
||||
double alt = fgGetDouble("/sim/presets/altitude-ft");
|
||||
|
||||
|
||||
bool set_pos = false;
|
||||
|
||||
|
||||
// If glideslope is specified, then calculate offset-distance or
|
||||
// altitude relative to glide slope if either of those was not
|
||||
// specified.
|
||||
if ( fabs( gs ) > 0.01 ) {
|
||||
fgSetDistOrAltFromGlideSlope();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// If we have an explicit, in-range lon/lat, don't change it, just use it.
|
||||
// If not, check for an airport-id and use that.
|
||||
// If not, default to the middle of the KSFO field.
|
||||
|
@ -604,7 +619,7 @@ bool initPosition()
|
|||
{
|
||||
set_pos = true;
|
||||
}
|
||||
|
||||
|
||||
string apt = fgGetString("/sim/presets/airport-id");
|
||||
const bool apt_req = fgGetBool("/sim/presets/airport-requested");
|
||||
string rwy_no = fgGetString("/sim/presets/runway");
|
||||
|
@ -616,31 +631,31 @@ bool initPosition()
|
|||
string carrier = fgGetString("/sim/presets/carrier");
|
||||
string parkpos = fgGetString("/sim/presets/parkpos");
|
||||
string fix = fgGetString("/sim/presets/fix");
|
||||
|
||||
|
||||
// the launcher sets this to precisely identify a navaid
|
||||
PositionedID navaidId = fgGetInt("/sim/presets/navaid-id");
|
||||
|
||||
|
||||
SGPropertyNode *hdg_preset = fgGetNode("/sim/presets/heading-deg", true);
|
||||
double hdg = hdg_preset->getDoubleValue();
|
||||
|
||||
|
||||
// save some start parameters, so that we can later say what the
|
||||
// user really requested. TODO generalize that and move it to options.cxx
|
||||
static bool start_options_saved = false;
|
||||
if (!start_options_saved) {
|
||||
start_options_saved = true;
|
||||
SGPropertyNode *opt = fgGetNode("/sim/startup/options", true);
|
||||
|
||||
|
||||
opt->setDoubleValue("latitude-deg", lat_deg);
|
||||
opt->setDoubleValue("longitude-deg", lon_deg);
|
||||
opt->setDoubleValue("heading-deg", hdg);
|
||||
opt->setStringValue("airport", apt.c_str());
|
||||
opt->setStringValue("runway", rwy_no.c_str());
|
||||
}
|
||||
|
||||
|
||||
if (hdg > 9990.0) {
|
||||
hdg = fgGetDouble("/environment/config/boundary/entry/wind-from-heading-deg", 270);
|
||||
}
|
||||
|
||||
|
||||
if ( !set_pos && !carrier.empty() ) {
|
||||
// an aircraft carrier is requested
|
||||
const auto result = setInitialPosFromCarrier( carrier );
|
||||
|
@ -649,7 +664,7 @@ bool initPosition()
|
|||
set_pos = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (apt_req && !rwy_req) {
|
||||
// ensure that if the users asks for a specific airport, but not a runway,
|
||||
// presumably because they want automatic selection, we do not look
|
||||
|
@ -657,7 +672,7 @@ bool initPosition()
|
|||
// likely missing.
|
||||
rwy_no.clear();
|
||||
}
|
||||
|
||||
|
||||
if ( !set_pos && !apt.empty() && !parkpos.empty() ) {
|
||||
// An airport + parking position is requested
|
||||
// since this depends on parking, which is part of dynamics, and hence
|
||||
|
@ -670,7 +685,7 @@ bool initPosition()
|
|||
set_pos = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( !set_pos && !apt.empty() && !rwy_no.empty() ) {
|
||||
// An airport + runway is requested
|
||||
if ( fgSetPosFromAirportIDandRwy( apt, rwy_no, rwy_req ) ) {
|
||||
|
@ -681,7 +696,7 @@ bool initPosition()
|
|||
set_pos = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( !set_pos && !apt.empty() ) {
|
||||
// An airport is requested (find runway closest to hdg)
|
||||
if ( setPosFromAirportIDandHdg( apt, hdg ) ) {
|
||||
|
@ -692,31 +707,31 @@ bool initPosition()
|
|||
set_pos = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (hdg_preset->getDoubleValue() > 9990.0)
|
||||
hdg_preset->setDoubleValue(hdg);
|
||||
|
||||
|
||||
if ( !set_pos && !vor.empty() ) {
|
||||
// a VOR is requested
|
||||
if ( fgSetPosFromNAV( vor, vor_freq, FGPositioned::VOR, navaidId ) ) {
|
||||
set_pos = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( !set_pos && !ndb.empty() ) {
|
||||
// an NDB is requested
|
||||
if ( fgSetPosFromNAV( ndb, ndb_freq, FGPositioned::NDB, navaidId ) ) {
|
||||
set_pos = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( !set_pos && !fix.empty() ) {
|
||||
// a Fix is requested
|
||||
if ( fgSetPosFromFix( fix, navaidId ) ) {
|
||||
set_pos = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( !set_pos ) {
|
||||
const std::string defaultAirportId = fgGetString("/sim/presets/airport-id");
|
||||
const FGAirport* airport = fgFindAirportID(defaultAirportId);
|
||||
|
@ -733,27 +748,27 @@ bool initPosition()
|
|||
<< "') seems to be unknown.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fgSetDouble( "/position/longitude-deg",
|
||||
fgGetDouble("/sim/presets/longitude-deg") );
|
||||
fgSetDouble( "/position/latitude-deg",
|
||||
fgGetDouble("/sim/presets/latitude-deg") );
|
||||
fgSetDouble( "/orientation/heading-deg", hdg_preset->getDoubleValue());
|
||||
|
||||
|
||||
// determine if this should be an on-ground or in-air start
|
||||
if ((fabs(gs) > 0.01 || fabs(od) > 0.1 || alt > 0.1) && carrier.empty()) {
|
||||
fgSetBool("/sim/presets/onground", false);
|
||||
} else {
|
||||
fgSetBool("/sim/presets/onground", true);
|
||||
}
|
||||
|
||||
|
||||
fgSetBool("/sim/position-finalized", false);
|
||||
|
||||
|
||||
// Initialize the longitude, latitude and altitude to the initial position
|
||||
fgSetDouble("/position/altitude-ft", fgGetDouble("/sim/presets/altitude-ft"));
|
||||
fgSetDouble("/position/longitude-deg", fgGetDouble("/sim/presets/longitude-deg"));
|
||||
fgSetDouble("/position/latitude-deg", fgGetDouble("/sim/presets/latitude-deg"));
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -820,13 +835,14 @@ void finalizePosition()
|
|||
std::string runway = fgGetString("/sim/presets/runway");
|
||||
std::string apt = fgGetString("/sim/presets/airport-id");
|
||||
const bool rwy_req = fgGetBool("/sim/presets/runway-requested");
|
||||
const bool abeam = fgGetBool("/sim/presets/carrier-abeam");
|
||||
|
||||
if (!carrier.empty())
|
||||
{
|
||||
const bool atFLOLS = rwy_req && (runway == "FLOLS");
|
||||
const bool atFLOLS = rwy_req && (runway == "FLOLS" || parkpos == "FLOLS");
|
||||
InitPosResult carrierResult;
|
||||
if (atFLOLS) {
|
||||
carrierResult = setFinalPosFromCarrierFLOLS(carrier);
|
||||
carrierResult = setFinalPosFromCarrierFLOLS(carrier, abeam);
|
||||
} else {
|
||||
carrierResult = setFinalPosFromCarrier(carrier, parkpos);
|
||||
}
|
||||
|
@ -843,7 +859,7 @@ void finalizePosition()
|
|||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else if (!apt.empty() && !parkpos.empty()) {
|
||||
// parking position depends on ATC / dynamics code to assign spaces,
|
||||
// so we wait until this point to initialise
|
||||
|
|
|
@ -2449,9 +2449,9 @@ FGMultiplayMgr::addMultiplayer(const std::string& callsign,
|
|||
}
|
||||
}
|
||||
|
||||
/* Copy values from our local <set>/sim/view[]/config/* into global
|
||||
/ai/models/multiplayer/set/sim/view[]/config/ so that we have view offsets
|
||||
available for this multiplayer aircraft. */
|
||||
// Copy values from our local <set>/sim/view[]/config/* into global
|
||||
// /ai/models/multiplayer/set/sim/view[]/config/ so that we have view offsets
|
||||
// available for this multiplayer aircraft.
|
||||
SGPropertyNode* global_set = mp->_getProps()->addChild("set");
|
||||
SGPropertyNode* global_sim = global_set->addChild("sim");
|
||||
double sim_chase_distance_m = -25;
|
||||
|
|
|
@ -62,7 +62,12 @@ public:
|
|||
_manager(manager),
|
||||
_useVBOsProp(fgGetNode("/sim/rendering/use-vbos", 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);
|
||||
|
||||
|
@ -78,6 +83,9 @@ public:
|
|||
_pagedLODMaximumProp->setIntValue(current);
|
||||
}
|
||||
_pagedLODMaximumProp->addChangeListener(this, true);
|
||||
_lodDetailed->addChangeListener(this, true);
|
||||
_lodBareDelta->addChangeListener(this, true);
|
||||
_lodRoughDelta->addChangeListener(this, true);
|
||||
}
|
||||
|
||||
~TileManagerListener()
|
||||
|
@ -85,6 +93,9 @@ public:
|
|||
_useVBOsProp->removeChangeListener(this);
|
||||
_enableCacheProp->removeChangeListener(this);
|
||||
_pagedLODMaximumProp->removeChangeListener(this);
|
||||
_lodDetailed->removeChangeListener(this);
|
||||
_lodBareDelta->removeChangeListener(this);
|
||||
_lodRoughDelta->removeChangeListener(this);
|
||||
}
|
||||
|
||||
virtual void valueChanged(SGPropertyNode* prop)
|
||||
|
@ -92,13 +103,20 @@ public:
|
|||
if (prop == _useVBOsProp) {
|
||||
bool useVBOs = prop->getBoolValue();
|
||||
_manager->_options->setPluginStringData("SimGear::USE_VBOS",
|
||||
useVBOs ? "ON" : "OFF");
|
||||
useVBOs ? "ON" : "OFF");
|
||||
} else if (prop == _enableCacheProp) {
|
||||
_manager->_enableCache = prop->getBoolValue();
|
||||
} else if (prop == _pagedLODMaximumProp) {
|
||||
int v = prop->getIntValue();
|
||||
osg::ref_ptr<osgViewer::Viewer> viewer(globals->get_renderer()->getViewer());
|
||||
viewer->getDatabasePager()->setTargetMaximumNumberOfPageLOD(v);
|
||||
int v = prop->getIntValue();
|
||||
osg::ref_ptr<osgViewer::Viewer> viewer(globals->get_renderer()->getViewer());
|
||||
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;
|
||||
SGPropertyNode_ptr _useVBOsProp,
|
||||
_enableCacheProp,
|
||||
_pagedLODMaximumProp;
|
||||
_pagedLODMaximumProp,
|
||||
_lodDetailed,
|
||||
_lodRoughDelta,
|
||||
_lodBareDelta,
|
||||
_lodRough,
|
||||
_lodBare
|
||||
;
|
||||
};
|
||||
|
||||
FGTileMgr::FGTileMgr():
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
#include "NasalPositioned.hxx"
|
||||
|
||||
|
@ -2446,7 +2447,8 @@ private:
|
|||
class NasalFPDelegateFactory : public FlightPlan::DelegateFactory
|
||||
{
|
||||
public:
|
||||
NasalFPDelegateFactory(naRef code)
|
||||
NasalFPDelegateFactory(naRef code, const std::string& id) :
|
||||
_id(id)
|
||||
{
|
||||
_nasal = globals->get_subsystem<FGNasalSys>();
|
||||
_func = code;
|
||||
|
@ -2474,7 +2476,11 @@ public:
|
|||
naFreeContext(ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::string& id() const
|
||||
{ return _id; }
|
||||
private:
|
||||
const std::string _id;
|
||||
FGNasalSys* _nasal;
|
||||
naRef _func;
|
||||
int _gcSaveKey;
|
||||
|
@ -2499,10 +2505,45 @@ static naRef f_registerFPDelegate(naContext c, naRef me, int argc, naRef* args)
|
|||
if ((argc < 1) || !naIsFunc(args[0])) {
|
||||
naRuntimeError(c, "non-function argument to registerFlightPlanDelegate");
|
||||
}
|
||||
NasalFPDelegateFactory* factory = new NasalFPDelegateFactory(args[0]);
|
||||
FlightPlan::registerDelegateFactory(factory);
|
||||
|
||||
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);
|
||||
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)
|
||||
|
@ -3295,6 +3336,7 @@ static struct { const char* name; naCFunction func; } funcs[] = {
|
|||
{ "flightplan", f_flightplan },
|
||||
{ "createFlightplan", f_createFlightplan },
|
||||
{ "registerFlightPlanDelegate", f_registerFPDelegate },
|
||||
{ "unregisterFlightPlanDelegate", f_unregisterFPDelegate},
|
||||
{ "createWP", f_createWP },
|
||||
{ "createWPFrom", f_createWPFrom },
|
||||
{ "createViaTo", f_createViaTo },
|
||||
|
|
|
@ -955,6 +955,17 @@ naRef FGNasalSys::cmdArgGhost()
|
|||
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)
|
||||
{
|
||||
_cmdArg = aNode;
|
||||
|
@ -983,6 +994,8 @@ void FGNasalSys::init()
|
|||
hashset(_globals, "thread", naInit_thread(_context));
|
||||
hashset(_globals, "utf8", naInit_utf8(_context));
|
||||
|
||||
initLogLevelConstants();
|
||||
|
||||
// Add our custom extension functions:
|
||||
for(i=0; funcs[i].name; i++)
|
||||
hashset(_globals, funcs[i].name,
|
||||
|
|
|
@ -161,6 +161,18 @@ public:
|
|||
simgear::BufferedLogCallback* log() const
|
||||
{ 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:
|
||||
//friend class FGNasalScript;
|
||||
friend class FGNasalListener;
|
||||
|
@ -180,15 +192,6 @@ private:
|
|||
|
||||
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;
|
||||
naContext _context;
|
||||
naRef _globals,
|
||||
|
|
|
@ -505,7 +505,7 @@ void FGTrafficManager::shutdown()
|
|||
|
||||
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");
|
||||
if (doDataSync && terraSync) {
|
||||
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 near_m = config->getDoubleValue("ground-level-nearplane-m");
|
||||
|
||||
View* v = 0;
|
||||
View* v = nullptr;
|
||||
// supporting two types "lookat" = 1 and "lookfrom" = 0
|
||||
const char *type = config->getParent()->getStringValue("type");
|
||||
if (!strcmp(type, "lookat")) {
|
||||
|
|
|
@ -21,7 +21,7 @@ target_link_libraries(fgai
|
|||
SimGearCore SimGearScene
|
||||
${OPENSCENEGRAPH_LIBRARIES}
|
||||
${OPENGL_LIBRARIES}
|
||||
${RTI_LIBRARIES}
|
||||
${RTI_LDFLAGS}
|
||||
)
|
||||
|
||||
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.
|
||||
//
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <direct.h> // for getcwd()
|
||||
#else // !_WIN32
|
||||
|
|
|
@ -1,13 +1,26 @@
|
|||
find_package(PNG)
|
||||
find_package(OpenGL)
|
||||
find_package(GLEW)
|
||||
find_package(Freetype)
|
||||
find_package(PNG QUIET)
|
||||
find_package(OpenGL QUIET)
|
||||
find_package(Freetype QUIET)
|
||||
|
||||
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(STATUS "libPNG: ${PNG_FOUND}")
|
||||
message(STATUS "OpenGL: ${OPENGL_FOUND}")
|
||||
message(STATUS "GLEW: ${GLEW_FOUND}")
|
||||
message(STATUS "Freetype: ${FREETYPE_FOUND}")
|
||||
return()
|
||||
endif()
|
||||
|
@ -18,21 +31,7 @@ find_path(BCMHOST_INCLUDE_DIR
|
|||
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
|
||||
GLES_utils.cxx
|
||||
GLES_utils.hxx
|
||||
)
|
||||
endif()
|
||||
|
||||
add_executable(fgpanel
|
||||
set(TARGET_SOURCES
|
||||
main.cxx
|
||||
ApplicationProperties.hxx
|
||||
ApplicationProperties.cxx
|
||||
|
@ -74,45 +73,71 @@ add_executable(fgpanel
|
|||
panel_io.hxx
|
||||
GL_utils.cxx
|
||||
GL_utils.hxx
|
||||
${TARGET_SOURCES}
|
||||
)
|
||||
|
||||
add_executable(fgpanel ${TARGET_SOURCES})
|
||||
|
||||
target_link_libraries(fgpanel
|
||||
SimGearCore
|
||||
${PNG_LIBRARIES}
|
||||
${FREETYPE_LIBRARIES}
|
||||
${OPENGL_LIBRARIES}
|
||||
${GLUT_LIBRARIES}
|
||||
${GLEW_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_include_directories(fgpanel PUBLIC
|
||||
${FREETYPE_INCLUDE_DIRS}
|
||||
${PNG_INCLUDE_DIR}
|
||||
${GLEW_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(fgpanel
|
||||
${OPENGL_LIBRARIES}
|
||||
${GLUT_LIBRARIES}
|
||||
GLEW::GLEW
|
||||
if(MSVC)
|
||||
target_compile_definitions(fgpanel PUBLIC
|
||||
-DFREEGLUT_LIB_PRAGMAS=0
|
||||
)
|
||||
else(GLUT_FOUND)
|
||||
message(STATUS "glut NOT found, can't build fgpanel")
|
||||
endif(GLUT_FOUND)
|
||||
else()
|
||||
endif(MSVC)
|
||||
|
||||
if(BCMHOST_INCLUDE_DIR)
|
||||
message(STATUS "found Raspberry Pi")
|
||||
|
||||
target_link_libraries(fgpanel
|
||||
-lGLESv2 -lEGL -lm -lbcm_host -L/opt/vc/lib
|
||||
add_executable(fgpanel-egl ${TARGET_SOURCES}
|
||||
GLES_utils.cxx
|
||||
GLES_utils.hxx
|
||||
)
|
||||
|
||||
include_directories(
|
||||
target_include_directories(fgpanel-egl PUBLIC
|
||||
${FREETYPE_INCLUDE_DIRS}
|
||||
${PNG_INCLUDE_DIR}
|
||||
${BCMHOST_INCLUDE_DIR}
|
||||
${BCMHOST_INCLUDE_DIR}/interface/vcos/pthreads
|
||||
${BCMHOST_INCLUDE_DIR}/interface/vmcs_host/linux
|
||||
)
|
||||
|
||||
add_definitions(-D_GLES2 -D_RPI)
|
||||
endif()
|
||||
target_link_libraries(fgpanel-egl
|
||||
SimGearCore
|
||||
${PNG_LIBRARIES}
|
||||
${FREETYPE_LIBRARIES}
|
||||
-lbrcmGLESv2 -lbrcmEGL -lm -lbcm_host -L/opt/vc/lib
|
||||
)
|
||||
|
||||
install(TARGETS fgpanel RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
target_compile_definitions(fgpanel-egl PUBLIC
|
||||
-D_GLES2 -D_RPI
|
||||
)
|
||||
target_compile_definitions(fgpanel PUBLIC
|
||||
-D_RPI
|
||||
)
|
||||
message(STATUS "FGPanel (Raspberry Pi) : ENABLED")
|
||||
install(TARGETS fgpanel-egl RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
else(BCMHOST_INCLUDE_DIR)
|
||||
message(STATUS "FGPanel (Raspberry Pi) : DISABLED")
|
||||
endif(BCMHOST_INCLUDE_DIR)
|
||||
|
||||
|
||||
|
||||
|
||||
if(WITH_FGPANEL)
|
||||
message(STATUS "FGPanel : ENABLED")
|
||||
install(TARGETS fgpanel RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
else(WITH_FGPANEL)
|
||||
message(WARNING "FGPanel : DISABLED")
|
||||
endif(WITH_FGPANEL)
|
||||
|
|
|
@ -89,12 +89,13 @@ void
|
|||
FGPanel::init () {
|
||||
// Textured Layer Shaders
|
||||
const char V_Textured_Layer_Shader_Str[] =
|
||||
#ifdef _GLES2
|
||||
#ifdef _RPI
|
||||
"#version 100 \n"
|
||||
"attribute vec4 a_position; \n"
|
||||
"attribute vec2 a_tex_coord; \n"
|
||||
"varying vec2 v_tex_coord; \n"
|
||||
#else
|
||||
"#version 330 \n"
|
||||
"#version 130 \n"
|
||||
"in vec4 a_position; \n"
|
||||
"in vec2 a_tex_coord; \n"
|
||||
"out vec2 v_tex_coord; \n"
|
||||
|
@ -106,13 +107,13 @@ FGPanel::init () {
|
|||
"} \n";
|
||||
|
||||
const char F_Textured_Layer_Shader_Str[] =
|
||||
#ifdef _GLES2
|
||||
#ifdef _RPI
|
||||
"#version 100 \n"
|
||||
"precision mediump float; \n"
|
||||
"varying vec2 v_tex_coord; \n"
|
||||
#else
|
||||
"#version 330 \n"
|
||||
"#version 130 \n"
|
||||
"in vec2 v_tex_coord; \n"
|
||||
"out vec4 gl_FragColor; \n"
|
||||
#endif
|
||||
"uniform sampler2D u_texture; \n"
|
||||
"void main () { \n"
|
||||
|
|
|
@ -51,8 +51,7 @@ using namespace std;
|
|||
* redraw themselves when necessary, and will pass mouse clicks on to
|
||||
* the appropriate instruments for processing.
|
||||
*/
|
||||
class FGPanel : public SGSubsystem
|
||||
{
|
||||
class FGPanel : public SGSubsystem {
|
||||
public:
|
||||
FGPanel (const SGPropertyNode_ptr root);
|
||||
virtual ~FGPanel ();
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
#include <GLUT/glut.h>
|
||||
#elif defined (_GLES2)
|
||||
#include <GLES2/gl2.h>
|
||||
#include "GLES_utils.hxx"
|
||||
#else
|
||||
#include <GL/glew.h> // Must be included before <GL/gl.h>
|
||||
#include <GL/gl.h>
|
||||
|
|
|
@ -28,8 +28,7 @@ class PropertySetter;
|
|||
|
||||
typedef vector<PropertySetter*> PropertySetterVector;
|
||||
|
||||
class FGPanelProtocol : public SGSubsystem
|
||||
{
|
||||
class FGPanelProtocol : public SGSubsystem {
|
||||
public:
|
||||
FGPanelProtocol (SGPropertyNode_ptr a_Root);
|
||||
virtual ~FGPanelProtocol ();
|
||||
|
|
|
@ -35,26 +35,32 @@ GLint FGTextLayer::Text_Layer_Color_Loc (0);
|
|||
bool
|
||||
FGTextLayer::Init () {
|
||||
const char V_Text_Layer_Shader_Str[] =
|
||||
#ifdef _GLES2
|
||||
#ifdef _RPI
|
||||
"#version 100 \n"
|
||||
"attribute vec4 a_position; \n"
|
||||
"attribute vec2 a_tex_coord; \n"
|
||||
"varying vec2 v_tex_coord; \n"
|
||||
#else
|
||||
"#version 330 \n"
|
||||
"#version 130 \n"
|
||||
"in vec4 a_position; \n"
|
||||
"in vec2 a_tex_coord; \n"
|
||||
"out vec2 v_tex_coord; \n"
|
||||
#endif
|
||||
"uniform mat4 u_mvp_matrix; \n"
|
||||
"varying vec2 v_tex_coord; \n"
|
||||
"void main () { \n"
|
||||
" gl_Position = u_mvp_matrix * a_position; \n"
|
||||
" v_tex_coord = a_tex_coord; \n"
|
||||
"} \n";
|
||||
|
||||
const char F_Text_Layer_Shader_Str[] =
|
||||
#ifdef _GLES2
|
||||
#ifdef _RPI
|
||||
"#version 100 \n"
|
||||
"precision mediump float; \n"
|
||||
#endif
|
||||
"varying vec2 v_tex_coord; \n"
|
||||
#else
|
||||
"#version 130 \n"
|
||||
"in vec2 v_tex_coord; \n"
|
||||
#endif
|
||||
"uniform sampler2D u_texture; \n"
|
||||
"uniform vec4 u_color; \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.
|
||||
|
||||
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.
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
========
|
||||
|
||||
|
|
|
@ -31,8 +31,7 @@
|
|||
|
||||
#include "FGPanel.hxx"
|
||||
|
||||
class FGReadablePanel : public FGPanel
|
||||
{
|
||||
class FGReadablePanel : public FGPanel {
|
||||
public:
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "readable-panel"; }
|
||||
|
|
|
@ -35,7 +35,7 @@ if(RTI_FOUND)
|
|||
HLAWindowDrawable.cxx
|
||||
HLAWindowDrawableClass.cxx
|
||||
)
|
||||
set(FGVIEWER_RTI_LIBRARIES ${RTI_LIBRARIES})
|
||||
set(FGVIEWER_RTI_LIBRARIES ${RTI_LDFLAGS})
|
||||
else()
|
||||
set(FGVIEWER_RTI_LIBRARIES "")
|
||||
set(FGVIEWER_RTI_SOURCES "")
|
||||
|
@ -47,9 +47,9 @@ if(X11_FOUND)
|
|||
endif()
|
||||
|
||||
target_link_libraries(fgviewer
|
||||
SimGearScene SimGearCore
|
||||
SimGearScene SimGearCore
|
||||
${OPENGL_LIBRARIES}
|
||||
${FGVIEWER_RTI_LIBRARIES}
|
||||
${FGVIEWER_RTI_LIBRARIES}
|
||||
${GDAL_LIBRARY}
|
||||
)
|
||||
install(TARGETS fgviewer RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
|
Loading…
Reference in a new issue