From 90a04a4baabad2b00b6bbfdd14addb73877c2a0a Mon Sep 17 00:00:00 2001 From: Bertrand Coconnier Date: Mon, 30 Dec 2019 00:30:26 +0100 Subject: [PATCH 01/98] [JSBSim] Relax the assert when trying to clip with max < min. Throwing an exception was too harsh. Instead the clipping is ignored and a warning message issued. --- src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp b/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp index 7c0503c0a..0098f3f56 100644 --- a/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp @@ -236,10 +236,10 @@ void FGFCSComponent::Clip(void) double range = vmax - vmin; if (range < 0.0) { - cerr << "Trying to clip with a max value " << ClipMax->GetName() - << " lower than the min value " << ClipMin->GetName() - << endl; - throw("JSBSim aborts"); + cerr << "Trying to clip with a max value (" << vmax << ") from " + << ClipMax->GetName() << " lower than the min value (" << vmin + << ") from " << ClipMin->GetName() << "." << endl + << "Clipping is ignored." << endl; return; } From 1a7783c35889075c3ef0832354152363ea3098ee Mon Sep 17 00:00:00 2001 From: Edward d'Auvergne Date: Thu, 9 Jan 2020 12:37:57 +0100 Subject: [PATCH 02/98] TestSuite: Reactivation of the ENABLE_AUTOTESTING CMake variable. This was accidentally disabled by 22de9d30b518646894ac190cc6b04371daa6d5c2. --- test_suite/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_suite/CMakeLists.txt b/test_suite/CMakeLists.txt index 89ce2d831..bcd803974 100644 --- a/test_suite/CMakeLists.txt +++ b/test_suite/CMakeLists.txt @@ -135,6 +135,7 @@ set_target_properties(fgfs_test_suite RUNTIME_OUTPUT_DIRECTORY "${TESTSUITE_OUTPUT_DIR}" ) if(ENABLE_AUTOTESTING) + set(TEST_SUITE_COMMAND "fgfs_test_suite") set(TEST_SUITE_COMMENT "Running the full FlightGear test suite") else() set(TEST_SUITE_COMMENT "Building the FlightGear test suite.") @@ -162,7 +163,7 @@ if (MSVC) endif() add_custom_target(test_suite - fgfs_test_suite + ${TEST_SUITE_COMMAND} DEPENDS fgfs_test_suite COMMENT ${TEST_SUITE_COMMENT} ) From 03ed61509c4e6085e39655268a3903d6da59b873 Mon Sep 17 00:00:00 2001 From: Geoff McLane Date: Sat, 11 Jan 2020 20:22:46 +0100 Subject: [PATCH 03/98] Use CMAKE_MSVCIDE_RUN_PATH to allow the add_custom_target to run This is only for a MSVC build - sets the runtime path for the exe run in the following 'add_custom_target( ... run fgrcc ...)' ... modified: src/EmbeddedResources/CMakeLists.txt --- src/EmbeddedResources/CMakeLists.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/EmbeddedResources/CMakeLists.txt b/src/EmbeddedResources/CMakeLists.txt index d72d9629b..d49df99bf 100644 --- a/src/EmbeddedResources/CMakeLists.txt +++ b/src/EmbeddedResources/CMakeLists.txt @@ -1,6 +1,17 @@ add_executable(fgrcc fgrcc.cxx fgrcc.hxx) target_link_libraries(fgrcc SimGearCore ${PLATFORM_LIBS}) +# On Windows, make sure fgrcc can be run (it needs third-party libraries) in add_custom_target +if(MSVC) + set_target_properties(fgrcc PROPERTIES DEBUG_POSTFIX d) + if(MSVC_3RDPARTY_ROOT AND MSVC_3RDPARTY_DIR) + set(CMAKE_MSVCIDE_RUN_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/bin) + else() + message(FATAL_ERROR + "Either MSVC_3RDPARTY_ROOT or MSVC_3RDPARTY_DIR is empty or unset") + endif() +endif() + add_custom_target( embeddedresources COMMAND From db295a9afca5e4fb24f9e84a35dd0ff6bd48c3a6 Mon Sep 17 00:00:00 2001 From: jano Date: Tue, 14 Jan 2020 11:10:28 +0100 Subject: [PATCH 04/98] introduction of a zero lag mode when pilots are synched in time, eg using ntp. --- src/AIModel/AIMultiplayer.cxx | 112 +++++++++++++++++++++------------- src/AIModel/AIMultiplayer.hxx | 8 ++- 2 files changed, 77 insertions(+), 43 deletions(-) diff --git a/src/AIModel/AIMultiplayer.cxx b/src/AIModel/AIMultiplayer.cxx index be6985fc4..7995b34c6 100644 --- a/src/AIModel/AIMultiplayer.cxx +++ b/src/AIModel/AIMultiplayer.cxx @@ -50,6 +50,12 @@ FGAIMultiplayer::FGAIMultiplayer() : lastUpdateTime = 0; playerLag = 0.03; compensateLag = 1; + realTime = false; + lastTime=0.0; + lagPpsAveraged = 1.0; + rawLag = 0.0; + rawLagMod = 0.0; + lagModAveraged = 0.0; _searchOrder = PREFER_DATA; } @@ -152,73 +158,96 @@ void FGAIMultiplayer::update(double dt) // requested time to the most recent available packet. This is the // target we want to reach in average. double lag = it->second.lag; + + rawLag = curentPkgTime - curtime; + realTime = false; //default behaviour + if (!mTimeOffsetSet) { mTimeOffsetSet = true; mTimeOffset = curentPkgTime - curtime - lag; + lastTime = curentPkgTime; + lagModAveraged = remainder((curentPkgTime - curtime), 3600.0); + props->setDoubleValue("lag/pps-averaged", lagPpsAveraged); + props->setDoubleValue("lag/lag-mod-averaged", lagModAveraged); } else { - double offset = 0.0; + if ((curentPkgTime - lastTime) != 0) { + lagPpsAveraged = 0.99 * lagPpsAveraged + 0.01 * fabs( 1 / (lastTime - curentPkgTime)); + lastTime = curentPkgTime; + rawLagMod = remainder(rawLag, 3600.0); + lagModAveraged = lagModAveraged *0.99 + 0.01 * rawLagMod; + props->setDoubleValue("lag/pps-averaged", lagPpsAveraged); + props->setDoubleValue("lag/lag-mod-averaged", lagModAveraged); + } - //spectator mode, more late to be in the interpolation zone + double offset = 0.0; + + //spectator mode, more late to be in the interpolation zone if (compensateLag == 3) { offset = curentPkgTime -curtime -lag + playerLag; - // old behaviour + // old behaviour } else if (compensateLag == 1) { offset = curentPkgTime - curtime - lag; // using the prediction mode to display the mpaircraft in the futur/past with given playerlag value //currently compensatelag = 2 + } else if (fabs(lagModAveraged) < 0.3) { + mTimeOffset = (round(rawLag/3600))*3600; //real time mode if close enough + realTime = true; + } else { offset = curentPkgTime - curtime + 0.48*lag + playerLag; } - if ((!mAllowExtrapolation && offset + lag < mTimeOffset) - || (offset - 10 > mTimeOffset)) { - mTimeOffset = offset; - SG_LOG(SG_AI, SG_DEBUG, "Resetting time offset adjust system to " - "avoid extrapolation: time offset = " << mTimeOffset); - } else { - // the error of the offset, respectively the negative error to avoid - // a minus later ... - double err = offset - mTimeOffset; - // limit errors leading to shorter lag values somehow, that is late - // arriving packets will pessimize the overall lag much more than - // early packets will shorten the overall lag - double sysSpeed; - //trying to slow the rudderlag phenomenon thus using more the prediction system - //if we are off by less than 1.5s, do a little correction, and bigger step above 1.5s - if (fabs(err) < 1.5) { - if (err < 0) { - sysSpeed = mLagAdjustSystemSpeed*err*0.01; - } else { - sysSpeed = SGMiscd::min(0.5*err*err, 0.05); - } - } else { - if (err < 0) { + if (!realTime) { - // Ok, we have some very late packets and nothing newer increase the - // lag by the given speedadjust - sysSpeed = mLagAdjustSystemSpeed*err; - } else { - // We have a too pessimistic display delay shorten that a small bit - sysSpeed = SGMiscd::min(0.1*err*err, 0.5); - } - } + if ((!mAllowExtrapolation && offset + lag < mTimeOffset) + || (offset - 10 > mTimeOffset)) { + mTimeOffset = offset; + SG_LOG(SG_AI, SG_DEBUG, "Resetting time offset adjust system to " + "avoid extrapolation: time offset = " << mTimeOffset); + } else { + // the error of the offset, respectively the negative error to avoid + // a minus later ... + double err = offset - mTimeOffset; + // limit errors leading to shorter lag values somehow, that is late + // arriving packets will pessimize the overall lag much more than + // early packets will shorten the overall lag + double sysSpeed; + //trying to slow the rudderlag phenomenon thus using more the prediction system + //if we are off by less than 1.5s, do a little correction, and bigger step above 1.5s + if (fabs(err) < 1.5) { + if (err < 0) { + sysSpeed = mLagAdjustSystemSpeed*err*0.01; + } else { + sysSpeed = SGMiscd::min(0.5*err*err, 0.05); + } + } else { + if (err < 0) { + + // Ok, we have some very late packets and nothing newer increase the + // lag by the given speedadjust + sysSpeed = mLagAdjustSystemSpeed*err; + } else { + // We have a too pessimistic display delay shorten that a small bit + sysSpeed = SGMiscd::min(0.1*err*err, 0.5); + } + } // simple euler integration for that first order system including some // overshooting guard to prevent to aggressive system speeds // (stiff systems) to explode the systems state - double systemIncrement = dt*sysSpeed; - if (fabs(err) < fabs(systemIncrement)) - systemIncrement = err; - mTimeOffset += systemIncrement; + double systemIncrement = dt*sysSpeed; + if (fabs(err) < fabs(systemIncrement)) + systemIncrement = err; + mTimeOffset += systemIncrement; - SG_LOG(SG_AI, SG_DEBUG, "Offset adjust system: time offset = " + SG_LOG(SG_AI, SG_DEBUG, "Offset adjust system: time offset = " << mTimeOffset << ", expected longitudinal position error due to " " current adjustment of the offset: " << fabs(norm(it->second.linearVel)*systemIncrement)); + } } } - // Compute the time in the feeders time scale which fits the current time // we need to double tInterp = curtime + mTimeOffset; @@ -227,9 +256,10 @@ void FGAIMultiplayer::update(double dt) SGQuatf ecOrient; SGVec3f ecLinearVel; - if (tInterp <= curentPkgTime) { + if (tInterp < curentPkgTime) { // Ok, we need a time prevous to the last available packet, // that is good ... + // the case tInterp = curentPkgTime need to be in the interpolation, to avoid a bug zeroing the position // Find the first packet before the target time MotionInfo::iterator nextIt = mMotionInfo.upper_bound(tInterp); diff --git a/src/AIModel/AIMultiplayer.hxx b/src/AIModel/AIMultiplayer.hxx index fdfaed525..0f44b4c56 100644 --- a/src/AIModel/AIMultiplayer.hxx +++ b/src/AIModel/AIMultiplayer.hxx @@ -86,11 +86,15 @@ private: typedef std::map > PropertyMap; PropertyMap mPropertyMap; - double mTimeOffset; bool mTimeOffsetSet; - double playerLag; + bool realTime; int compensateLag; + double playerLag; + double mTimeOffset; double lastUpdateTime; + double lastTime; + double lagPpsAveraged; + double rawLag, rawLagMod, lagModAveraged; /// Properties which are for now exposed for testing bool mAllowExtrapolation; From 94b12aa99242790b6d14f155dfa669ced36d72e1 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Sat, 18 Jan 2020 14:29:17 +0100 Subject: [PATCH 05/98] Postpone running the simulation until after initialization and do an extra trim right after creation to properly initialize the output parameters. --- src/FDM/JSBSim/JSBSim.cxx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/FDM/JSBSim/JSBSim.cxx b/src/FDM/JSBSim/JSBSim.cxx index 156dd1e37..b95ab032c 100644 --- a/src/FDM/JSBSim/JSBSim.cxx +++ b/src/FDM/JSBSim/JSBSim.cxx @@ -198,6 +198,7 @@ FGJSBsim::FGJSBsim( double dt ) PropertyManager = new FGPropertyManager( (FGPropertyNode*)globals->get_props() ); fdmex = new FGFDMExec( PropertyManager ); + fdmex->Hold(); // Register ground callback. fdmex->SetGroundCallback( new FGFSGroundCallback(this) ); @@ -383,6 +384,11 @@ FGJSBsim::FGJSBsim( double dt ) mesh = new AircraftMesh(fgGetDouble("/fdm/jsbsim/metrics/bw-ft"), fgGetDouble("/fdm/jsbsim/metrics/cbarw-ft")); + + // Trim once to initialize all output parameters + FGTrim *fgtrim = new FGTrim(fdmex,tFull); + fgtrim->DoTrim(); + delete fgtrim; } /******************************************************************************/ @@ -427,6 +433,7 @@ void FGJSBsim::init() common_init(); copy_to_JSBsim(); + fdmex->Resume(); fdmex->RunIC(); //loop JSBSim once w/o integrating if (fgGetBool("/sim/presets/running")) { Propulsion->InitRunning(-1); From 2d2d5dbb766708b9e869a5abc6ff471593394dde Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Wed, 22 Jan 2020 13:45:28 +0100 Subject: [PATCH 06/98] Switch to C++11 threads, mutexes and lock_guards. Switching to C++11 condition_variables requires some more thought. --- src/Navaids/NavDataCache.cxx | 26 +++++++++++++------------- src/Traffic/TrafficMgr.cxx | 10 +++++----- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Navaids/NavDataCache.cxx b/src/Navaids/NavDataCache.cxx index 22f5dd8e1..50d49fe25 100644 --- a/src/Navaids/NavDataCache.cxx +++ b/src/Navaids/NavDataCache.cxx @@ -30,6 +30,7 @@ #include #include // for int64_t #include // for std::ostringstream +#include #ifdef SYSTEM_SQLITE // the standard sqlite3.h doesn't give a way to set SQLITE_UINT64_TYPE, @@ -54,7 +55,6 @@ #include #include #include -#include #include
#include
@@ -172,7 +172,7 @@ public: bool isFinished() const { - SGGuard g(_lock); + std::lock_guard g(_lock); return _isFinished; } @@ -183,7 +183,7 @@ public: _cache->doRebuild(); SG_LOG(SG_NAVCACHE, SG_INFO, "cache rebuild took:" << st.elapsedMSec() << "msec"); - SGGuard g(_lock); + std::lock_guard g(_lock); _isFinished = true; _phase = NavDataCache::REBUILD_DONE; } @@ -191,7 +191,7 @@ public: NavDataCache::RebuildPhase currentPhase() const { NavDataCache::RebuildPhase ph; - SGGuard g(_lock); + std::lock_guard g(_lock); ph = _phase; return ph; } @@ -199,14 +199,14 @@ public: unsigned int completionPercent() const { unsigned int perc = 0; - SGGuard g(_lock); + std::lock_guard g(_lock); perc = _completionPercent; return perc; } void setProgress(NavDataCache::RebuildPhase ph, unsigned int percent) { - SGGuard g(_lock); + std::lock_guard g(_lock); _phase = ph; _completionPercent = percent; } @@ -215,7 +215,7 @@ private: NavDataCache* _cache; NavDataCache::RebuildPhase _phase; unsigned int _completionPercent; - mutable SGMutex _lock; + mutable std::mutex _lock; bool _isFinished; }; @@ -2529,7 +2529,7 @@ public: break; } else if (err == SQLITE_ROW) { PositionedID r = sqlite3_column_int64(query, 0); - SGGuard g(lock); + std::lock_guard g(lock); results.push_back(r); } else if (err == SQLITE_BUSY) { // sleep a tiny amount @@ -2540,11 +2540,11 @@ public: } } - SGGuard g(lock); + std::lock_guard g(lock); isComplete = true; } - SGMutex lock; + std::mutex lock; sqlite3* db; sqlite3_stmt_ptr query; PositionedIDVec results; @@ -2576,7 +2576,7 @@ NavDataCache::ThreadedGUISearch::ThreadedGUISearch(const std::string& term, bool NavDataCache::ThreadedGUISearch::~ThreadedGUISearch() { { - SGGuard g(d->lock); + std::lock_guard g(d->lock); d->quit = true; } @@ -2589,7 +2589,7 @@ PositionedIDVec NavDataCache::ThreadedGUISearch::results() const { PositionedIDVec r; { - SGGuard g(d->lock); + std::lock_guard g(d->lock); r = std::move(d->results); } return r; @@ -2597,7 +2597,7 @@ PositionedIDVec NavDataCache::ThreadedGUISearch::results() const bool NavDataCache::ThreadedGUISearch::isComplete() const { - SGGuard g(d->lock); + std::lock_guard g(d->lock); return d->isComplete; } diff --git a/src/Traffic/TrafficMgr.cxx b/src/Traffic/TrafficMgr.cxx index a021ef880..15b070f72 100644 --- a/src/Traffic/TrafficMgr.cxx +++ b/src/Traffic/TrafficMgr.cxx @@ -45,6 +45,7 @@ #include #include #include +#include #include @@ -59,11 +60,10 @@ #include #include #include +#include #include #include -#include -#include #include #include @@ -124,7 +124,7 @@ public: bool isFinished() const { - SGGuard g(_lock); + std::lock_guard g(_lock); return _isFinished; } @@ -137,7 +137,7 @@ public: } } - SGGuard g(_lock); + std::lock_guard g(_lock); _isFinished = true; } @@ -394,7 +394,7 @@ private: } FGTrafficManager* _trafficManager; - mutable SGMutex _lock; + mutable std::mutex _lock; bool _isFinished; bool _cancelThread; simgear::PathList _trafficDirPaths; From 123eb41819554cc0bc99a510d4822428007b44f2 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Wed, 22 Jan 2020 14:53:27 +0100 Subject: [PATCH 07/98] Copy the initial properties a second time after initilization. --- src/FDM/fdm_shell.cxx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/FDM/fdm_shell.cxx b/src/FDM/fdm_shell.cxx index 40693b5b0..f6ec66079 100644 --- a/src/FDM/fdm_shell.cxx +++ b/src/FDM/fdm_shell.cxx @@ -190,6 +190,12 @@ void FDMShell::update(double dt) fgSetBool("/sim/fdm-initialized", true); fgSetBool("/sim/signals/fdm-initialized", true); + + if (!copyProperties(_props->getNode("fdm", true), + _initialFdmProperties)) + { + SG_LOG(SG_FLIGHT, SG_ALERT, "Failed to save initial FDM property state"); + } } } From 1cc0d9e97f014bc43691c1ee769dd1671327ba64 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Wed, 22 Jan 2020 14:54:11 +0100 Subject: [PATCH 08/98] Fold of running JSBSim until after the trimming routine. --- src/FDM/JSBSim/JSBSim.cxx | 7 ++++--- src/FDM/JSBSim/JSBSim.hxx | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/FDM/JSBSim/JSBSim.cxx b/src/FDM/JSBSim/JSBSim.cxx index b95ab032c..aba6bcb61 100644 --- a/src/FDM/JSBSim/JSBSim.cxx +++ b/src/FDM/JSBSim/JSBSim.cxx @@ -154,7 +154,7 @@ std::map FGJSBsim::TURBULENCE_TYPE_NAMES; static FGTurbulenceSeverityTable TurbulenceSeverityTable; FGJSBsim::FGJSBsim( double dt ) - : FGInterface(dt) + : FGInterface(dt), delta_t(dt) { bool result; if( TURBULENCE_TYPE_NAMES.empty() ) { @@ -237,7 +237,7 @@ FGJSBsim::FGJSBsim( double dt ) terrain = fgGetNode("/sim/fdm/surface", true); - fdmex->Setdt( dt ); +// fdmex->Setdt( dt ); result = fdmex->LoadModel( aircraft_path, engine_path, systems_path, fgGetString("/sim/aero"), false ); @@ -433,7 +433,6 @@ void FGJSBsim::init() common_init(); copy_to_JSBsim(); - fdmex->Resume(); fdmex->RunIC(); //loop JSBSim once w/o integrating if (fgGetBool("/sim/presets/running")) { Propulsion->InitRunning(-1); @@ -466,6 +465,8 @@ void FGJSBsim::init() } do_trim(); needTrim = false; + fdmex->Setdt( delta_t ); + fdmex->Resume(); } copy_from_JSBsim(); //update the bus diff --git a/src/FDM/JSBSim/JSBSim.hxx b/src/FDM/JSBSim/JSBSim.hxx index b601f490d..caed83c5a 100644 --- a/src/FDM/JSBSim/JSBSim.hxx +++ b/src/FDM/JSBSim/JSBSim.hxx @@ -302,6 +302,7 @@ private: JSBSim::FGColumnVector3 hook_root_struct; double hook_length; + double delta_t; bool crashed; AircraftMesh_ptr mesh; From cf5bfc5dd9d34d2f9825be991aac07db5a116076 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Thu, 23 Jan 2020 11:54:33 +0100 Subject: [PATCH 09/98] On-ground trimming needs fdmex active while in-air trimming does not for reliable operation. This might be a problem within the trimming routines but for now this works. --- src/FDM/JSBSim/JSBSim.cxx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/FDM/JSBSim/JSBSim.cxx b/src/FDM/JSBSim/JSBSim.cxx index aba6bcb61..dd6869d34 100644 --- a/src/FDM/JSBSim/JSBSim.cxx +++ b/src/FDM/JSBSim/JSBSim.cxx @@ -462,11 +462,17 @@ void FGJSBsim::init() fgic->SetVNorthFpsIC(gndVelNED(1)); fgic->SetVEastFpsIC(gndVelNED(2)); fgic->SetVDownFpsIC(gndVelNED(3)); + + fdmex->Resume(); + do_trim(); + } + else + { + do_trim(); + fdmex->Resume(); } - do_trim(); - needTrim = false; fdmex->Setdt( delta_t ); - fdmex->Resume(); + needTrim = false; } copy_from_JSBsim(); //update the bus From 973bf320742fd4919c09f68c3918f5a0037cfe7b Mon Sep 17 00:00:00 2001 From: Bertrand Coconnier Date: Sat, 25 Jan 2020 16:05:30 +0100 Subject: [PATCH 10/98] [JSBSim] Added the ability to log properties in a CSV file. --- src/FDM/JSBSim/JSBSim.cxx | 4 ++++ src/Main/options.cxx | 1 + 2 files changed, 5 insertions(+) diff --git a/src/FDM/JSBSim/JSBSim.cxx b/src/FDM/JSBSim/JSBSim.cxx index dd6869d34..5a9fcf92e 100644 --- a/src/FDM/JSBSim/JSBSim.cxx +++ b/src/FDM/JSBSim/JSBSim.cxx @@ -389,6 +389,10 @@ FGJSBsim::FGJSBsim( double dt ) FGTrim *fgtrim = new FGTrim(fdmex,tFull); fgtrim->DoTrim(); delete fgtrim; + + string directive_file = fgGetString("/sim/jsbsim/output-directive-file"); + if (!directive_file.empty()) + fdmex->SetOutputDirectives(directive_file); } /******************************************************************************/ diff --git a/src/Main/options.cxx b/src/Main/options.cxx index 147659aca..c93ec85fe 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -1846,6 +1846,7 @@ struct OptionDesc { {"prop", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptSetProperty}, {"load-tape", true, OPTION_FUNC, "", false, "", fgOptLoadTape }, {"developer", true, OPTION_IGNORE | OPTION_BOOL, "", false, "", nullptr }, + {"jsbsim-output-directive-file", true, OPTION_STRING, "/sim/jsbsim/output-directive-file", false, "", nullptr }, {0} }; From 3405ea2aaac1e4be21b5d50d50cfaa72205938f1 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Sun, 26 Jan 2020 14:10:07 +0100 Subject: [PATCH 11/98] Joystick axes can get initialized to extreme values, at least on Linux. Hold of working with the axis values until all values have become sane at least once --- src/Input/FGJoystickInput.cxx | 20 ++++++++++++++++++-- src/Input/FGJoystickInput.hxx | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Input/FGJoystickInput.cxx b/src/Input/FGJoystickInput.cxx index c161d449c..545e77a54 100644 --- a/src/Input/FGJoystickInput.cxx +++ b/src/Input/FGJoystickInput.cxx @@ -30,6 +30,8 @@ # include #endif +#include + #include #include "FGDeviceConfigurationMap.hxx" #include
@@ -118,6 +120,7 @@ void FGJoystickInput::init() for (int i = 0; i < MAX_JOYSTICKS; i++) { jsJoystick * js = new jsJoystick(i); joysticks[i].plibJS.reset(js); + initializing[i] = true; if (js->notWorking()) { SG_LOG(SG_INPUT, SG_DEBUG, "Joystick " << i << " not found"); @@ -337,12 +340,25 @@ void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, float delay; jsJoystick * js = joy->plibJS.get(); - if (js == 0 || js->notWorking()) + if (js == 0 || js->notWorking()) { + initializing[index] = true; return; + } js->read(&buttons, axis_values); - if (js->notWorking()) // If js is disconnected + if (js->notWorking()) { // If js is disconnected + initializing[index] = true; return; + } + + // Joystick axes can get initialized to extreme values, at least on Linux. + // https://sourceforge.net/p/flightgear/codetickets/2185/ + if (initializing[index]) { + for (int j = 0; j < joy->naxes; j++) { + if (fabsf(axis_values[j] > 0.5f)) return; + } + initializing[index] = false; + } // Update device status SGPropertyNode_ptr status = status_node->getChild("joystick", index, true); diff --git a/src/Input/FGJoystickInput.hxx b/src/Input/FGJoystickInput.hxx index 642950d6f..8a6f425c4 100644 --- a/src/Input/FGJoystickInput.hxx +++ b/src/Input/FGJoystickInput.hxx @@ -105,6 +105,7 @@ private: void clearAxesAndButtons(); }; + bool initializing[MAX_JOYSTICKS]; joystick joysticks[MAX_JOYSTICKS]; void updateJoystick(int index, joystick* joy, double dt); }; From d4459c82055309d0c7aba039ec01b5e0fab2704c Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Sun, 26 Jan 2020 14:30:24 +0100 Subject: [PATCH 12/98] Fix a typo --- src/Input/FGJoystickInput.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Input/FGJoystickInput.cxx b/src/Input/FGJoystickInput.cxx index 545e77a54..501bf3c01 100644 --- a/src/Input/FGJoystickInput.cxx +++ b/src/Input/FGJoystickInput.cxx @@ -355,7 +355,7 @@ void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, // https://sourceforge.net/p/flightgear/codetickets/2185/ if (initializing[index]) { for (int j = 0; j < joy->naxes; j++) { - if (fabsf(axis_values[j] > 0.5f)) return; + if (fabsf(axis_values[j]) > 0.5f) return; } initializing[index] = false; } From 2d0b44ee63d56054f097e40da87fc1c33035eccb Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Sun, 26 Jan 2020 17:44:19 +0100 Subject: [PATCH 13/98] If js is valid (again), create a new instance of jsJoystick so removing an joystick from and USB port and attaching it again get the joystick in a working order. --- src/Input/FGJoystickInput.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Input/FGJoystickInput.cxx b/src/Input/FGJoystickInput.cxx index 501bf3c01..cf356ffc5 100644 --- a/src/Input/FGJoystickInput.cxx +++ b/src/Input/FGJoystickInput.cxx @@ -342,6 +342,10 @@ void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, jsJoystick * js = joy->plibJS.get(); if (js == 0 || js->notWorking()) { initializing[index] = true; + if (js) { + jsJoystick * js = new jsJoystick(index); + joysticks[index].plibJS.reset(js); + } return; } From 65a20b582935566eb658ace6a242a3230065eabb Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Mon, 27 Jan 2020 09:15:53 +0100 Subject: [PATCH 14/98] Read the raw values and check them against the saturation value, if one of them is larger then initialization of the joystick is still in progress. --- src/Input/FGJoystickInput.cxx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Input/FGJoystickInput.cxx b/src/Input/FGJoystickInput.cxx index cf356ffc5..2f1c21fc6 100644 --- a/src/Input/FGJoystickInput.cxx +++ b/src/Input/FGJoystickInput.cxx @@ -358,8 +358,12 @@ void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, // Joystick axes can get initialized to extreme values, at least on Linux. // https://sourceforge.net/p/flightgear/codetickets/2185/ if (initializing[index]) { + float axis_values[MAX_JOYSTICK_AXES]; + int buttons; + + js->rawRead(&buttons, axis_values); for (int j = 0; j < joy->naxes; j++) { - if (fabsf(axis_values[j]) > 0.5f) return; + if (fabsf(axis_values[j]) > js->getSaturation(j)) return; } initializing[index] = false; } From f6285bc5284fb2039baadaf85b2e5d3e69a3e157 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Mon, 27 Jan 2020 09:30:09 +0000 Subject: [PATCH 15/98] Fix directional lights for AMD Some AMD drivers do not like triangles for point sprites, which breaks directional lights. This works around this by allowing users to set /rendering/triangle-directional-lights=false which falls back to the non-directional implementation of a point. --- src/Viewer/renderer_compositor.cxx | 6 +- src/Viewer/renderer_compositor.hxx | 2 +- src/Viewer/renderer_legacy.cxx | 127 +++++++++++++++-------------- src/Viewer/renderer_legacy.hxx | 24 +++--- 4 files changed, 81 insertions(+), 78 deletions(-) diff --git a/src/Viewer/renderer_compositor.cxx b/src/Viewer/renderer_compositor.cxx index 16d6cf594..151d894b2 100644 --- a/src/Viewer/renderer_compositor.cxx +++ b/src/Viewer/renderer_compositor.cxx @@ -414,6 +414,7 @@ FGRenderer::init( void ) _splash_alpha = fgGetNode("/sim/startup/splash-alpha", true); _point_sprites = fgGetNode("/sim/rendering/point-sprites", true); + _triangle_directional_lights = fgGetNode("/sim/rendering/triangle-directional-lights", true); _distance_attenuation = fgGetNode("/sim/rendering/distance-attenuation", true); _horizon_effect = fgGetNode("/sim/rendering/horizon-effect", true); @@ -424,8 +425,9 @@ FGRenderer::init( void ) bool use_point_sprites = _point_sprites->getBoolValue(); bool distance_attenuation = _distance_attenuation->getBoolValue(); + bool triangles = _triangle_directional_lights->getBoolValue(); - SGConfigureDirectionalLights( use_point_sprites, distance_attenuation ); + SGConfigureDirectionalLights( use_point_sprites, distance_attenuation, triangles ); if (const char* tc = fgGetString("/sim/rendering/texture-compression", NULL)) { if (strcmp(tc, "false") == 0 || strcmp(tc, "off") == 0 || @@ -657,7 +659,7 @@ FGRenderer::update( ) { GLint max_texture_size; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); - // abritrary range change as sometimes during init the device reports somewhat dubious values, so + // abritrary range change as sometimes during init the device reports somewhat dubious values, so // we know that the size must be greater than size and that probably 9999999 is unreasonably large if (max_texture_size > 0 && max_texture_size < 9999999) SGSceneFeatures::instance()->setMaxTextureSize(max_texture_size); diff --git a/src/Viewer/renderer_compositor.hxx b/src/Viewer/renderer_compositor.hxx index dd228945e..e5fff12d1 100644 --- a/src/Viewer/renderer_compositor.hxx +++ b/src/Viewer/renderer_compositor.hxx @@ -95,7 +95,7 @@ protected: SGPropertyNode_ptr _scenery_loaded, _position_finalized; SGPropertyNode_ptr _splash_alpha; - SGPropertyNode_ptr _point_sprites, _enhanced_lighting, _distance_attenuation; + SGPropertyNode_ptr _point_sprites, _enhanced_lighting, _distance_attenuation, _triangle_directional_lights; SGPropertyNode_ptr _textures; SGPropertyNode_ptr _cloud_status, _visibility_m; SGPropertyNode_ptr _xsize, _ysize; diff --git a/src/Viewer/renderer_legacy.cxx b/src/Viewer/renderer_legacy.cxx index 508258ca9..b4dfbd68f 100644 --- a/src/Viewer/renderer_legacy.cxx +++ b/src/Viewer/renderer_legacy.cxx @@ -179,26 +179,26 @@ public: glPushAttrib(GL_ALL_ATTRIB_BITS); glPushClientAttrib(~0u); - + // HUD can be NULL HUD *hud = static_cast(globals->get_subsystem("hud")); if (hud) { hud->draw(state); } - + glPopClientAttrib(); glPopAttrib(); } virtual osg::Object* cloneType() const { return new SGHUDDrawable; } virtual osg::Object* clone(const osg::CopyOp&) const { return new SGHUDDrawable; } - + private: }; class FGLightSourceUpdateCallback : public osg::NodeCallback { public: - + /** * @param isSun true if the light is the actual sun i.e., for * illuminating the moon. @@ -209,19 +209,19 @@ public: : NodeCallback(nc, op), _isSun(nc._isSun) {} META_Object(flightgear,FGLightSourceUpdateCallback); - + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { assert(dynamic_cast(node)); osg::LightSource* lightSource = static_cast(node); osg::Light* light = lightSource->getLight(); - + FGLight *l = static_cast(globals->get_subsystem("lighting")); if (!l) { // lighting is down during re-init return; } - + if (_isSun) { light->setAmbient(Vec4(0.0f, 0.0f, 0.0f, 0.0f)); light->setDiffuse(Vec4(1.0f, 1.0f, 1.0f, 1.0f)); @@ -364,12 +364,12 @@ FGRenderer::FGRenderer() : _root->setName("fakeRoot"); _updateVisitor = new SGUpdateVisitor; - + // when Rembrandt is enabled, we use this group to access the whole // scene. Since the only child is the _viewerSceneRoot, we could // simply copy the reference, we don't need the additional group. _deferredRealRoot = new osg::Group; - + _numCascades = 4; _cascadeFar[0] = 5.f; _cascadeFar[1] = 50.f; @@ -383,12 +383,12 @@ FGRenderer::~FGRenderer() for (; i != _listeners.end(); ++i) { delete *i; } - + // replace the viewer's scene completely if (getViewer()) { getViewer()->setSceneData(new osg::Group); } - + delete _sky; } @@ -421,7 +421,7 @@ FGRenderer::preinit( void ) camera->addChild(_splash); } } - + _frameStamp = new osg::FrameStamp; viewer->setFrameStamp(_frameStamp.get()); // Scene doesn't seem to pass the frame stamp to the update @@ -429,7 +429,7 @@ FGRenderer::preinit( void ) _updateVisitor->setFrameStamp(_frameStamp.get()); viewer->setUpdateVisitor(_updateVisitor.get()); fgSetDouble("/sim/startup/splash-alpha", 1.0); - + // hide the menubar if it overlaps the window, so the splash screen // is completely visible. We reset this value when the splash screen // is fading out. @@ -470,7 +470,7 @@ FGRenderer::addChangeListener(SGPropertyChangeListener* l, const char* path) _listeners.push_back(l); fgAddChangeListener(l, path); } - + void FGRenderer::init( void ) { @@ -507,7 +507,7 @@ FGRenderer::init( void ) _pipeline = makeRenderingPipeline(_renderer, 0); _scenery_loaded = fgGetNode("/sim/sceneryloaded", true); _position_finalized = fgGetNode("/sim/position-finalized", true); - + _panel_hotspots = fgGetNode("/sim/panel-hotspots", true); _virtual_cockpit = fgGetNode("/sim/virtual-cockpit", true); @@ -519,17 +519,19 @@ FGRenderer::init( void ) _point_sprites = fgGetNode("/sim/rendering/point-sprites", true); _distance_attenuation = fgGetNode("/sim/rendering/distance-attenuation", true); + _triangle_directional_lights = fgGetNode("/sim/rendering/triangle-directional-lights", true); _horizon_effect = fgGetNode("/sim/rendering/horizon-effect", true); _altitude_ft = fgGetNode("/position/altitude-ft", true); _cloud_status = fgGetNode("/environment/clouds/status", true); _visibility_m = fgGetNode("/environment/visibility-m", true); - + bool use_point_sprites = _point_sprites->getBoolValue(); bool distance_attenuation = _distance_attenuation->getBoolValue(); + bool triangles = _triangle_directional_lights->getBoolValue(); - SGConfigureDirectionalLights( use_point_sprites, distance_attenuation ); + SGConfigureDirectionalLights( use_point_sprites, distance_attenuation, triangles ); if (const char* tc = fgGetString("/sim/rendering/texture-compression", NULL)) { if (strcmp(tc, "false") == 0 || strcmp(tc, "off") == 0 || @@ -550,9 +552,9 @@ FGRenderer::init( void ) } SGSceneFeatures::instance()->setTextureCompressionPath(globals->get_texture_cache_dir()); // create sky, but can't build until setupView, since we depend -// on other subsystems to be inited, eg Ephemeris +// on other subsystems to be inited, eg Ephemeris _sky = new SGSky; - + SGPath texture_path(globals->get_fg_root()); texture_path.append("Textures"); texture_path.append("Sky"); @@ -560,7 +562,7 @@ FGRenderer::init( void ) SGCloudLayer * layer = new SGCloudLayer(texture_path.local8BitStr()); _sky->add_cloud_layer(layer); } - + _sky->texture_path( texture_path.local8BitStr() ); if (!_classicalRenderer) { @@ -621,7 +623,7 @@ FGRenderer::buildClassicalPipeline(CameraGroup* cgroup, unsigned flags, osg::Cam // The camera group will always update the camera camera->setReferenceFrame(Transform::ABSOLUTE_RF); info->name = "classic"; - + Camera* farCamera = 0; if ((flags & (CameraGroup::GUI | CameraGroup::ORTHO)) == 0) { farCamera = new Camera; @@ -1094,7 +1096,7 @@ FGRenderer::buildDeferredPipeline(CameraGroup* cgroup, unsigned flags, osg::Came return buildCameraFromRenderingPipeline(_pipeline, cgroup, flags, camera, view, projection, gc); } -osg::Camera* +osg::Camera* FGRenderer::buildDeferredFullscreenCamera( flightgear::CameraInfo* info, const FGRenderingPipeline::Pass* pass ) { osg::Camera* camera = new osg::Camera; @@ -1149,7 +1151,7 @@ FGRenderer::buildDeferredFullscreenCamera( flightgear::CameraInfo* info, const F return camera; } -osg::Camera* +osg::Camera* FGRenderer::buildDeferredFullscreenCamera( flightgear::CameraInfo* info, osg::GraphicsContext* gc, const FGRenderingPipeline::Stage* stage ) { osg::Camera* camera = buildDeferredFullscreenCamera(info, static_cast(stage)); @@ -1269,7 +1271,7 @@ FGRenderer::buildLightingSkyCloudsPass(FGRenderingPipeline::Pass* pass) Group* group = new Group; group->setName("skyCloudsGroup"); group->setNodeMask(simgear::BACKGROUND_BIT); - + StateSet* ss = group->getOrCreateStateSet(); ss->setAttributeAndModes( new osg::ColorMask( true, true, true, false ), osg::StateAttribute::ON ); group->addChild( _sky->getPreRoot() ); @@ -1345,7 +1347,7 @@ CameraInfo* FGRenderer::buildCameraFromRenderingPipeline(FGRenderingPipeline* rp { CameraInfo* info = new CameraInfo(flags); buildBuffers(rpipe, info); - + for (size_t i = 0; i < rpipe->stages.size(); ++i) { osg::ref_ptr stage = rpipe->stages[i]; buildStage(info, stage, cgroup, camera, view, projection, gc); @@ -1359,26 +1361,26 @@ CameraInfo* FGRenderer::buildCameraFromRenderingPipeline(FGRenderingPipeline* rp void FGRenderer::setupRoot() { osg::StateSet* stateSet = _root->getOrCreateStateSet(); - + stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - + stateSet->setAttribute(new osg::Depth(osg::Depth::LESS)); stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); - + stateSet->setAttribute(new osg::BlendFunc); stateSet->setMode(GL_BLEND, osg::StateAttribute::OFF); - + stateSet->setMode(GL_FOG, osg::StateAttribute::OFF); - + // this will be set below stateSet->setMode(GL_NORMALIZE, osg::StateAttribute::OFF); - + osg::Material* material = new osg::Material; stateSet->setAttribute(material); - + stateSet->setTextureAttribute(0, new osg::TexEnv); stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF); - + osg::Hint* hint = new osg::Hint(GL_FOG_HINT, GL_DONT_CARE); hint->setUpdateCallback(new FGHintUpdateCallback("/sim/rendering/fog")); stateSet->setAttribute(hint); @@ -1395,7 +1397,7 @@ void FGRenderer::setupRoot() hint->setUpdateCallback(new FGHintUpdateCallback("/sim/rendering/perspective-correction")); stateSet->setAttribute(hint); } - + void FGRenderer::setupView( void ) { @@ -1409,7 +1411,7 @@ FGRenderer::setupView( void ) osg::PolygonOffset::setFactorMultiplier(1); setupRoot(); - + // build the sky Ephemeris* ephemerisSub = globals->get_subsystem(); @@ -1427,11 +1429,11 @@ FGRenderer::setupView( void ) *ephemerisSub->data(), fgGetNode("/environment", true), opt.get()); - + viewer->getCamera() ->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); - - + + // need to update the light on every frame // OSG LightSource objects are rather confusing. OSG only supports // the 10 lights specified by OpenGL itself; if more than one @@ -1441,7 +1443,7 @@ FGRenderer::setupView( void ) // LightSource is just a shortcut for setting up a state set that // has the corresponding OpenGL light enabled: a LightSource will // affect geometry anywhere in the scene graph that has its light - // number enabled in a state set. + // number enabled in a state set. osg::ref_ptr lightSource = new LightSource; lightSource->setName("FGLightSource"); lightSource->getLight()->setDataVariance(Object::DYNAMIC); @@ -1450,7 +1452,7 @@ FGRenderer::setupView( void ) lightSource->setLocalStateSetModes(osg::StateAttribute::ON); lightSource->setUpdateCallback(new FGLightSourceUpdateCallback); _viewerSceneRoot->addChild(lightSource); - + // we need a white diffuse light for the phase of the moon osg::ref_ptr sunLight = new osg::LightSource; sunLight->setName("sunLightSource"); @@ -1459,23 +1461,23 @@ FGRenderer::setupView( void ) sunLight->setUpdateCallback(new FGLightSourceUpdateCallback(true)); sunLight->setReferenceFrame(osg::LightSource::RELATIVE_RF); sunLight->setLocalStateSetModes(osg::StateAttribute::ON); - + // Hang a StateSet above the sky subgraph in order to turn off // light 0 Group* skyGroup = _sky->getPreRoot(); StateSet* skySS = skyGroup->getOrCreateStateSet(); skySS->setMode(GL_LIGHT0, StateAttribute::OFF); sunLight->addChild(skyGroup); - + if ( _classicalRenderer ) { _root->addChild(sunLight); } - + osg::Group* sceneGroup = globals->get_scenery()->get_scene_graph(); sceneGroup->setName("rendererScene"); sceneGroup->setNodeMask(~simgear::BACKGROUND_BIT); _root->addChild(sceneGroup); - + // setup state-set for main scenery (including models and aircraft) osg::StateSet* stateSet = sceneGroup->getOrCreateStateSet(); stateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON); @@ -1525,7 +1527,7 @@ FGRenderer::setupView( void ) guiCamera->insertChild(0, geode); guiCamera->insertChild(0, FGPanelNode::create2DPanelNode()); } - + osg::Switch* sw = new osg::Switch; sw->setName("scenerySwitch"); sw->setUpdateCallback(new FGScenerySwitchCallback); @@ -1538,7 +1540,7 @@ FGRenderer::setupView( void ) _viewerSceneRoot->addChild(_sky->getCloudRoot()); _viewerSceneRoot->addChild(FGCreateRedoutNode()); } - + // Attach empty program to the scene root so that shader programs // don't leak into state sets (effects) that shouldn't have one. stateSet = _viewerSceneRoot->getOrCreateStateSet(); @@ -1612,7 +1614,7 @@ FGRenderer::update( ) { flightgear::View *current__view = globals->get_current_view(); // Force update of center dependent values ... current__view->set_dirty(); - + osg::Camera *camera = viewer->getCamera(); osg::Vec4 clear_color = _altitude_ft->getDoubleValue() < 250000 @@ -1625,7 +1627,7 @@ FGRenderer::update( ) { camera->setClearColor(clear_color); updateSky(); - + // need to call the update visitor once _frameStamp->setCalendarTime(*globals->get_time_params()->getGmt()); _updateVisitor->setViewData(current__view->getViewPosition(), @@ -1655,18 +1657,18 @@ FGRenderer::updateSky() // update fog params if visibility has changed double visibility_meters = _visibility_m->getDoubleValue(); _sky->set_visibility(visibility_meters); - + double altitude_m = _altitude_ft->getDoubleValue() * SG_FEET_TO_METER; _sky->modify_vis( altitude_m, 0.0 /* time factor, now unused */); FGLight *l = static_cast(globals->get_subsystem("lighting")); - + // The sun and moon distances are scaled down versions // of the actual distance to get both the moon and the sun // within the range of the far clip plane. // Moon distance: 384,467 kilometers // Sun distance: 150,000,000 kilometers - + double sun_horiz_eff, moon_horiz_eff; if (_horizon_effect->getBoolValue()) { sun_horiz_eff @@ -1682,7 +1684,7 @@ FGRenderer::updateSky() } - + SGSkyState sstate; sstate.pos = globals->get_current_view()->getViewPosition(); sstate.pos_geod = globals->get_current_view()->getPosition(); @@ -1692,7 +1694,7 @@ FGRenderer::updateSky() sstate.sun_dist = 50000.0 * sun_horiz_eff; sstate.moon_dist = 40000.0 * moon_horiz_eff; sstate.sun_angle = l->get_sun_angle(); - + SGSkyColor scolor; scolor.sky_color = SGVec3f(l->sky_color().data()); scolor.adj_sky_color = SGVec3f(l->adj_sky_color().data()); @@ -1706,7 +1708,7 @@ FGRenderer::updateSky() _sky->reposition( sstate, *ephemerisSub->data(), delta_time_sec ); _sky->repaint( scolor, *ephemerisSub->data() ); } - + void FGRenderer::resize( int width, int height ) { @@ -1799,12 +1801,12 @@ PickList FGRenderer::pick(const osg::Vec2& windowPos) ++hit) { const osg::NodePath& np = hit->nodePath; osg::NodePath::const_reverse_iterator npi; - + for (npi = np.rbegin(); npi != np.rend(); ++npi) { SGSceneUserData* ud = SGSceneUserData::getSceneUserData(*npi); if (!ud || (ud->getNumPickCallbacks() == 0)) continue; - + for (unsigned i = 0; i < ud->getNumPickCallbacks(); ++i) { SGPickCallback* pickCallback = ud->getPickCallback(i); if (!pickCallback) @@ -1821,7 +1823,7 @@ PickList FGRenderer::pick(const osg::Vec2& windowPos) } // of installed pick callbacks iteration } // of reverse node path walk } - + return result; } @@ -1848,7 +1850,7 @@ FGRenderer::removeCamera(osg::Camera* camera) { _viewerSceneRoot->removeChild(camera); } - + void FGRenderer::setPlanes( double zNear, double zFar ) { @@ -1939,7 +1941,7 @@ public: } cout << endl; } - + void doTraversal(Camera* camera, Node* root, Viewport* viewport) { ref_ptr projection @@ -2019,7 +2021,7 @@ public: modelview = createOrReuseMatrix(*getModelViewMatrix() * camera.getViewMatrix()); } - else { // pre multiply + else { // pre multiply projection = createOrReuseMatrix(camera.getProjectionMatrix() * (*getProjectionMatrix())); modelview = createOrReuseMatrix(camera.getViewMatrix() @@ -2034,10 +2036,10 @@ public: pushViewport(camera.getViewport()); pushProjectionMatrix(projection); - pushModelViewMatrix(modelview, camera.getReferenceFrame()); + pushModelViewMatrix(modelview, camera.getReferenceFrame()); traverse(camera); - + // restore the previous model view matrix. popModelViewMatrix(); @@ -2079,4 +2081,3 @@ bool printVisibleSceneInfo(FGRenderer* renderer) } // end of renderer.cxx - diff --git a/src/Viewer/renderer_legacy.hxx b/src/Viewer/renderer_legacy.hxx index a3dd3272a..174abc13a 100644 --- a/src/Viewer/renderer_legacy.hxx +++ b/src/Viewer/renderer_legacy.hxx @@ -64,7 +64,7 @@ public: void resize(int width, int height ); void update(); - + /** Just pick into the scene and return the pick callbacks on the way ... */ PickList pick(const osg::Vec2& windowPos); @@ -85,7 +85,7 @@ public: void addCamera(osg::Camera* camera, bool useSceneData); void removeCamera(osg::Camera* camera); - + /** Add a camera to the group. The camera is added to the viewer * as a slave. See osgViewer::Viewer::addSlave. * @param flags properties of the camera; see CameraGroup::Flags @@ -126,20 +126,20 @@ public: protected: osg::ref_ptr viewer; osg::ref_ptr eventHandler; - + osg::ref_ptr _frameStamp; osg::ref_ptr _updateVisitor; osg::ref_ptr _viewerSceneRoot; osg::ref_ptr _deferredRealRoot; osg::ref_ptr _root; - + SGPropertyNode_ptr _scenery_loaded, _position_finalized; - - + + SGPropertyNode_ptr _splash_alpha; - SGPropertyNode_ptr _point_sprites, _enhanced_lighting, _distance_attenuation; + SGPropertyNode_ptr _point_sprites, _enhanced_lighting, _distance_attenuation, _triangle_directional_lights; SGPropertyNode_ptr _textures; - SGPropertyNode_ptr _cloud_status, _visibility_m; + SGPropertyNode_ptr _cloud_status, _visibility_m; SGPropertyNode_ptr _xsize, _ysize; SGPropertyNode_ptr _panel_hotspots, _sim_delta_sec, _horizon_effect, _altitude_ft; SGPropertyNode_ptr _virtual_cockpit; @@ -155,7 +155,7 @@ protected: typedef std::vector SGPropertyChangeListenerVec; SGPropertyChangeListenerVec _listeners; - + flightgear::CameraInfo* buildCameraFromRenderingPipeline(FGRenderingPipeline* rpipe, flightgear::CameraGroup* cgroup, unsigned flags, osg::Camera* camera, const osg::Matrix& view, const osg::Matrix& projection, osg::GraphicsContext* gc); @@ -186,11 +186,11 @@ protected: osg::ref_ptr _depthInColor; osg::ref_ptr _pipeline; - + void addChangeListener(SGPropertyChangeListener* l, const char* path); - + void updateSky(); - + void setupRoot(); SplashScreen* _splash; From b920a09fcf789d665d64da9a8f5346e7735c1afb Mon Sep 17 00:00:00 2001 From: legoboyvdlp R Date: Mon, 27 Jan 2020 16:27:46 +0000 Subject: [PATCH 16/98] Correct whitespace in NasalPositioned.cxx; fix segfault in NasalPositioned by adding null check in the legGhostGetMember method; add test case for segfault --- src/Scripting/NasalPositioned.cxx | 104 ++++++++++-------- .../unit_tests/Navaids/test_flightplan.cxx | 78 ++++++++----- .../unit_tests/Navaids/test_flightplan.hxx | 4 +- 3 files changed, 109 insertions(+), 77 deletions(-) diff --git a/src/Scripting/NasalPositioned.cxx b/src/Scripting/NasalPositioned.cxx index e4b788df0..1760e7b52 100644 --- a/src/Scripting/NasalPositioned.cxx +++ b/src/Scripting/NasalPositioned.cxx @@ -346,7 +346,7 @@ naRef ghostForPositioned(naContext c, FGPositionedRef pos) if (!pos) { return naNil(); } - + switch (pos->type()) { case FGPositioned::VOR: case FGPositioned::NDB: @@ -478,7 +478,7 @@ static naRef waypointNavaid(naContext c, Waypt* wpt) if (!pos || (!FGNavRecord::isNavaidType(pos) && !fgpositioned_cast(pos))) { return naNil(); } - + return ghostForPositioned(c, wpt->source()); } @@ -616,7 +616,7 @@ static bool waypointCommonSetMember(naContext c, Waypt* wpt, const char* fieldNa // nothing changed return false; } - + return true; } @@ -701,6 +701,12 @@ static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* o { const char* fieldName = naStr_data(field); FlightPlan::Leg* leg = (FlightPlan::Leg*) g; + if (!leg) { + *out = naNil(); + naRuntimeError(c, "leg ghost member fetched, but no associated leg object found"); + return ""; + } + Waypt* wpt = leg->waypoint(); if (!strcmp(fieldName, "parents")) { @@ -732,7 +738,11 @@ static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* o } else if (!strcmp(fieldName, "hold_count")) { *out = naNum(leg->holdCount()); } else { // check for fields defined on the underlying waypoint - return waypointCommonGetMember(c, wpt, fieldName, out); + if (wpt) { // FIXME null check shouldn't be required, check refcount + return waypointCommonGetMember(c, wpt, fieldName, out); + } else { + naRuntimeError(c, "leg ghost member fetched, but no underlying waypoint object found"); + } } return ""; // success @@ -749,7 +759,7 @@ static void legGhostSetMember(naContext c, void* g, naRef field, naRef value) { const char* fieldName = naStr_data(field); FlightPlan::Leg* leg = (FlightPlan::Leg*) g; - + bool didChange = false; if (!strcmp(fieldName, "hold_count")) { const int count = static_cast(value.num); @@ -759,13 +769,13 @@ static void legGhostSetMember(naContext c, void* g, naRef field, naRef value) } else if (!strcmp(fieldName, "hold_heading_radial_deg")) { if (!leg->convertWaypointToHold()) naRuntimeError(c, "couldn't convert leg waypoint into a hold"); - + // now we can call the base method didChange = waypointCommonSetMember(c, leg->waypoint(), fieldName, value); } else { didChange = waypointCommonSetMember(c, leg->waypoint(), fieldName, value); } - + if (didChange) { leg->markWaypointDirty(); } @@ -874,7 +884,7 @@ static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef va fp->setDestination(static_cast(nullptr)); return; } - + FGRunway* rwy = runwayGhost(value); if (rwy){ fp->setDestination(rwy); @@ -1256,7 +1266,7 @@ static int geodFromArgs(naRef* args, int offset, int argc, SGGeod& result) result = wayptGhost(args[offset])->position(); return 1; } - + if (gt == &FPLegGhostType) { result = fpLegGhost(args[offset])->waypoint()->position(); return 1; @@ -1366,30 +1376,30 @@ static naRef f_geodtocart(naContext c, naRef me, int argc, naRef* args) */ static naRef f_get_cart_ground_intersection(naContext c, naRef me, int argc, naRef* args) { - SGVec3d dir; - SGVec3d pos; + SGVec3d dir; + SGVec3d pos; - if (argc != 2) - naRuntimeError(c, "geod_hash get_cart_ground_intersection(position: hash{x,y,z}, direction:hash{x,y,z}) expects 2 arguments"); + if (argc != 2) + naRuntimeError(c, "geod_hash get_cart_ground_intersection(position: hash{x,y,z}, direction:hash{x,y,z}) expects 2 arguments"); - if (!vec3dFromHash(args[0], pos)) - naRuntimeError(c, "geod_hash get_cart_ground_intersection(position:hash{x,y,z}, direction:hash{x,y,z}) expects argument(0) to be hash of position containing x,y,z"); + if (!vec3dFromHash(args[0], pos)) + naRuntimeError(c, "geod_hash get_cart_ground_intersection(position:hash{x,y,z}, direction:hash{x,y,z}) expects argument(0) to be hash of position containing x,y,z"); - if (!vec3dFromHash(args[1], dir)) - naRuntimeError(c, "geod_hash get_cart_ground_intersection(position: hash{x,y,z}, direction:hash{x,y,z}) expects argument(1) to be hash of direction containing x,y,z"); + if (!vec3dFromHash(args[1], dir)) + naRuntimeError(c, "geod_hash get_cart_ground_intersection(position: hash{x,y,z}, direction:hash{x,y,z}) expects argument(1) to be hash of direction containing x,y,z"); - SGVec3d nearestHit; - if (!globals->get_scenery()->get_cart_ground_intersection(pos, dir, nearestHit)) - return naNil(); + SGVec3d nearestHit; + if (!globals->get_scenery()->get_cart_ground_intersection(pos, dir, nearestHit)) + return naNil(); - const SGGeod geodHit = SGGeod::fromCart(nearestHit); + const SGGeod geodHit = SGGeod::fromCart(nearestHit); - // build a hash for returned intersection - naRef intersection_h = naNewHash(c); - hashset(c, intersection_h, "lat", naNum(geodHit.getLatitudeDeg())); - hashset(c, intersection_h, "lon", naNum(geodHit.getLongitudeDeg())); - hashset(c, intersection_h, "elevation", naNum(geodHit.getElevationM())); - return intersection_h; + // build a hash for returned intersection + naRef intersection_h = naNewHash(c); + hashset(c, intersection_h, "lat", naNum(geodHit.getLatitudeDeg())); + hashset(c, intersection_h, "lon", naNum(geodHit.getLongitudeDeg())); + hashset(c, intersection_h, "elevation", naNum(geodHit.getElevationM())); + return intersection_h; } // convert from aircraft reference frame to global (ECEF) cartesian @@ -1439,11 +1449,11 @@ static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args) double elev = argc == 3 ? naNumValue(args[2]).num : 10000; const simgear::BVHMaterial *material; SGGeod geod = SGGeod::fromDegM(lon, lat, elev); - + const auto scenery = globals->get_scenery(); if (scenery == nullptr) return naNil(); - + if(!scenery->get_elevation_m(geod, elev, &material)) return naNil(); @@ -1783,7 +1793,7 @@ static naRef f_airport_approaches(naContext c, naRef me, int argc, naRef* args) appIds.insert(app->ident()); } } - + for (auto s : appIds) { naVec_append(approaches, stringToNasal(c, s)); } @@ -1792,13 +1802,13 @@ static naRef f_airport_approaches(naContext c, naRef me, int argc, naRef* args) RunwayVec runways; if (star) runways = star->runways(); - + for (unsigned int s=0; snumApproaches(); ++s) { Approach* app = apt->getApproachByIndex(s); if ((ty != PROCEDURE_INVALID) && (app->type() != ty)) { continue; } - + naRef procId = stringToNasal(c, app->ident()); naVec_append(approaches, procId); } @@ -2166,24 +2176,24 @@ static naRef f_findByIdent(naContext c, naRef me, int argc, naRef* args) if ((argc < 2) || !naIsString(args[0]) || !naIsString(args[1]) ) { naRuntimeError(c, "finxByIdent: expects ident and type as first two args"); } - + std::string ident(naStr_data(args[0])); std::string typeSpec(naStr_data(args[1])); - + // optional specify search pos as final argument SGGeod pos = globals->get_aircraft_position(); geodFromArgs(args, 2, argc, pos); FGPositioned::TypeFilter filter(FGPositioned::TypeFilter::fromString(typeSpec)); - + naRef r = naNewVector(c); FGPositionedList matches = FGPositioned::findAllWithIdent(ident, &filter); FGPositioned::sortByRange(matches, pos); - + for (auto f : matches) { naVec_append(r, ghostForPositioned(c, f)); } - + return r; } @@ -2235,7 +2245,7 @@ static naRef f_formatLatLon(naContext c, naRef me, int argc, naRef* args) if (argOffset == 0) { naRuntimeError(c, "invalid arguments to formatLatLon, expect a geod or lat,lon"); } - + simgear::strutils::LatLonFormat format = static_cast(fgGetInt("/sim/lon-lat-format")); if (argOffset < argc && naIsNum(args[argOffset])) { @@ -2244,7 +2254,7 @@ static naRef f_formatLatLon(naContext c, naRef me, int argc, naRef* args) naRuntimeError(c, "invalid lat-lon format requested"); } } - + const auto s = simgear::strutils::formatGeodAsString(p, format); return stringToNasal(c, s); } @@ -2254,13 +2264,13 @@ static naRef f_parseStringAsLatLonValue(naContext c, naRef me, int argc, naRef* if ((argc < 1) || !naIsString(args[0])) { naRuntimeError(c, "Missing / bad argument to parseStringAsLatLonValue"); } - + double value; bool ok = simgear::strutils::parseStringAsLatLonValue(naStr_data(args[0]), value); if (!ok) { return naNil(); } - + return naNum(value); } @@ -2400,7 +2410,7 @@ public: { callDelegateMethod("activated"); } - + void sequence() override { callDelegateMethod("sequence"); @@ -2555,7 +2565,7 @@ static naRef f_airwaySearch(naContext c, naRef me, int argc, naRef* args) static FGPositionedRef positionedFromArg(naRef ref) { - if (!naIsGhost(ref)) + if (!naIsGhost(ref)) return {}; naGhostType* gt = naGhost_type(ref); @@ -2592,7 +2602,7 @@ static naRef f_findAirway(naContext c, naRef me, int argc, naRef* args) if (argc >= 2) { pos = positionedFromArg(args[1]); if (naIsString(args[1])) { - // level spec, + // level spec, } } @@ -2675,7 +2685,7 @@ static naRef f_createViaTo(naContext c, naRef me, int argc, naRef* args) std::string airwayName = naStr_data(args[0]); AirwayRef airway = Airway::findByIdent(airwayName, Airway::Both); if (!airway) { - naRuntimeError(c, "createViaTo: couldn't find airway with provided name: %s", + naRuntimeError(c, "createViaTo: couldn't find airway with provided name: %s", naStr_data(args[0])); } @@ -2717,8 +2727,8 @@ static naRef f_createViaFromTo(naContext c, naRef me, int argc, naRef* args) std::string airwayName = naStr_data(args[1]); AirwayRef airway = Airway::findByIdentAndNavaid(airwayName, from); if (!airway) { - naRuntimeError(c, "createViaFromTo: couldn't find airway with provided name: %s from wp %s", - naStr_data(args[0]), + naRuntimeError(c, "createViaFromTo: couldn't find airway with provided name: %s from wp %s", + naStr_data(args[0]), from->ident().c_str()); } diff --git a/test_suite/unit_tests/Navaids/test_flightplan.cxx b/test_suite/unit_tests/Navaids/test_flightplan.cxx index 3a0f194e9..fa41e6a0e 100644 --- a/test_suite/unit_tests/Navaids/test_flightplan.cxx +++ b/test_suite/unit_tests/Navaids/test_flightplan.cxx @@ -24,6 +24,13 @@ void FlightplanTests::setUp() { FGTestApi::setUp::initTestGlobals("flightplan"); FGTestApi::setUp::initNavDataCache(); + + globals->get_subsystem_mgr()->init(); + + FGTestApi::setUp::initStandardNasal(); + + globals->get_subsystem_mgr()->postinit(); + globals->get_subsystem_mgr()->bind(); } @@ -57,13 +64,13 @@ void FlightplanTests::testBasic() CPPUNIT_ASSERT_EQUAL(fp1->numLegs(), 5); CPPUNIT_ASSERT(fp1->legAtIndex(0)->waypoint()->source()->ident() == "23L"); - + CPPUNIT_ASSERT(fp1->legAtIndex(1)->waypoint()->source()->ident() == "TNT"); CPPUNIT_ASSERT(fp1->legAtIndex(1)->waypoint()->source()->name() == "TRENT VOR-DME"); CPPUNIT_ASSERT(fp1->legAtIndex(2)->waypoint()->source()->ident() == "CLN"); CPPUNIT_ASSERT(fp1->legAtIndex(2)->waypoint()->source()->name() == "CLACTON VOR-DME"); - + CPPUNIT_ASSERT(fp1->legAtIndex(4)->waypoint()->source()->ident() == "24"); } @@ -147,24 +154,24 @@ void FlightplanTests::testRoutePathVec() FlightPlanRef fp1 = makeTestFP("KNUQ", "14L", "PHNL", "22R", "ROKME WOVAB"); RoutePath rtepath(fp1); - + SGGeodVec vec = rtepath.pathForIndex(0); - + FGAirportRef ksfo = FGAirport::findByIdent("KSFO"); FGFixRef rokme = fgpositioned_cast(FGPositioned::findClosestWithIdent("ROKME", ksfo->geod())); auto depRwy = fp1->departureRunway(); - + CPPUNIT_ASSERT_DOUBLES_EQUAL(depRwy->geod().getLongitudeDeg(), vec.front().getLongitudeDeg(), 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL(depRwy->geod().getLatitudeDeg(), vec.front().getLatitudeDeg(), 0.01); - + SGGeodVec vec1 = rtepath.pathForIndex(1); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLongitudeDeg(), vec1.back().getLongitudeDeg(), 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLatitudeDeg(), vec1.back().getLatitudeDeg(), 0.01); - + SGGeodVec vec2 = rtepath.pathForIndex(2); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLongitudeDeg(), vec2.front().getLongitudeDeg(), 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLatitudeDeg(), vec2.front().getLatitudeDeg(), 0.01); - + //CPPUNIT_ASSERT(vec.front() } @@ -172,22 +179,22 @@ void FlightplanTests::testRoutPathWpt0Midflight() { // test behaviour of RoutePath when WP0 is not a runway // happens for the Airbus ND which removes past wpts when sequencing - + FlightPlanRef fp1 = makeTestFP("KNUQ", "14L", "PHNL", "22R", "ROKME WOVAB"); // actually delete leg 0 so we start at ROKME fp1->deleteIndex(0); - + RoutePath rtepath(fp1); - + SGGeodVec vec = rtepath.pathForIndex(0); - + FGAirportRef ksfo = FGAirport::findByIdent("KSFO"); FGFixRef rokme = fgpositioned_cast(FGPositioned::findClosestWithIdent("ROKME", ksfo->geod())); - + CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLongitudeDeg(), vec.front().getLongitudeDeg(), 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLatitudeDeg(), vec.front().getLatitudeDeg(), 0.01); - + SGGeodVec vec2 = rtepath.pathForIndex(1); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLongitudeDeg(), vec2.front().getLongitudeDeg(), 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLatitudeDeg(), vec2.front().getLatitudeDeg(), 0.01); @@ -199,11 +206,11 @@ void FlightplanTests::testBasicAirways() { Airway* awy = Airway::findByIdent("J547", Airway::HighLevel); CPPUNIT_ASSERT_EQUAL(awy->ident(), std::string("J547")); - + FGAirportRef kord = FGAirport::findByIdent("KORD"); FlightPlanRef f = new FlightPlan; f->setDeparture(kord); - + CPPUNIT_ASSERT(awy->findEnroute("KITOK")); CPPUNIT_ASSERT(awy->findEnroute("LESUB")); @@ -214,12 +221,12 @@ void FlightplanTests::testBasicAirways() auto wptKUBBS = f->waypointFromString("KUBBS"); auto wptFNT = f->waypointFromString("FNT"); - + CPPUNIT_ASSERT(awy->canVia(wptKUBBS, wptFNT)); - + WayptVec path = awy->via(wptKUBBS, wptFNT); CPPUNIT_ASSERT_EQUAL(4, static_cast(path.size())); - + CPPUNIT_ASSERT_EQUAL(std::string("PMM"), path.at(0)->ident()); CPPUNIT_ASSERT_EQUAL(std::string("HASTE"), path.at(1)->ident()); CPPUNIT_ASSERT_EQUAL(std::string("DEWIT"), path.at(2)->ident()); @@ -231,16 +238,16 @@ void FlightplanTests::testAirwayNetworkRoute() FGAirportRef egph = FGAirport::findByIdent("EGPH"); FlightPlanRef f = new FlightPlan; f->setDeparture(egph); - + auto highLevelNet = Airway::highLevel(); - + auto wptTLA = f->waypointFromString("TLA"); auto wptCNA = f->waypointFromString("CNA"); - + WayptVec route; bool ok = highLevelNet->route(wptTLA, wptCNA, route); CPPUNIT_ASSERT(ok); - + CPPUNIT_ASSERT_EQUAL(static_cast(route.size()), 18); } @@ -250,25 +257,25 @@ void FlightplanTests::testParseICAORoute() FlightPlanRef f = new FlightPlan; f->setDeparture(kord); f->setDestination(FGAirport::findByIdent("KSAN")); - + const char* route = "DCT JOT J26 IRK J96 SLN J18 GCK J96 CIM J134 GUP J96 KEYKE J134 DRK J78 LANCY J96 PKE"; // const char* route = "DCT KUBBS J547 FNT Q824 HOCKE Q905 SIKBO Q907 MIILS N400A TUDEP NATW GISTI DCT SLANY UL9 DIKAS UL18 GAVGO UL9 KONAN UL607 UBIDU Y863 RUDUS T109 HAREM T104 ANORA STAR"; bool ok = f->parseICAORouteString(route); CPPUNIT_ASSERT(ok); - - - + + + } void FlightplanTests::testParseICANLowLevelRoute() { const char* route = "DCT DPA V6 IOW V216 LAA V210 GOSIP V83 ACHES V210 BLOKE V83 ALS V210 RSK V95 INW V12 HOXOL V264 OATES V12 JUWSO V264 PKE"; - + FGAirportRef kord = FGAirport::findByIdent("KORD"); FlightPlanRef f = new FlightPlan; f->setDeparture(kord); f->setDestination(FGAirport::findByIdent("KSAN")); - + bool ok = f->parseICAORouteString(route); CPPUNIT_ASSERT(ok); } @@ -320,3 +327,16 @@ void FlightplanTests::testBug1814() CPPUNIT_ASSERT_DOUBLES_EQUAL(137, leg->distanceNm(), 0.5); CPPUNIT_ASSERT_DOUBLES_EQUAL(101, f->legAtIndex(2)->distanceNm(), 0.5); } + +void FlightplanTests::testSegfaultWaypointGhost() { + // checking for a segfault here, no segfault indicates success. A runtime error in the log is acceptable here. + bool ok = FGTestApi::executeNasal(R"( + var fp = createFlightplan(); + fp.departure = airportinfo("BIKF"); + fp.destination = airportinfo("EGLL"); + var wp = fp.getWP(1); + fp.deleteWP(1); + print(wp.wp_name); + )"); + CPPUNIT_ASSERT(ok); +} diff --git a/test_suite/unit_tests/Navaids/test_flightplan.hxx b/test_suite/unit_tests/Navaids/test_flightplan.hxx index a52df3fb8..d73bd955e 100644 --- a/test_suite/unit_tests/Navaids/test_flightplan.hxx +++ b/test_suite/unit_tests/Navaids/test_flightplan.hxx @@ -38,9 +38,10 @@ class FlightplanTests : public CppUnit::TestFixture CPPUNIT_TEST(testBasicAirways); CPPUNIT_TEST(testAirwayNetworkRoute); CPPUNIT_TEST(testBug1814); + CPPUNIT_TEST(testSegfaultWaypointGhost); CPPUNIT_TEST(testRoutPathWpt0Midflight); CPPUNIT_TEST(testRoutePathVec); - + // CPPUNIT_TEST(testParseICAORoute); // CPPUNIT_TEST(testParseICANLowLevelRoute); CPPUNIT_TEST_SUITE_END(); @@ -62,6 +63,7 @@ public: void testParseICAORoute(); void testParseICANLowLevelRoute(); void testBug1814(); + void testSegfaultWaypointGhost(); void testRoutPathWpt0Midflight(); void testRoutePathVec(); }; From 6df768fa5f8535bcf28c000e98be429979bfd0ba Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Mon, 27 Jan 2020 18:12:09 +0100 Subject: [PATCH 17/98] Store the initial axis values and if just one of them changes declare the joystick initialized --- src/Input/FGJoystickInput.cxx | 30 ++++++++++++++++++------------ src/Input/FGJoystickInput.hxx | 4 +++- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/Input/FGJoystickInput.cxx b/src/Input/FGJoystickInput.cxx index 2f1c21fc6..c35228ef2 100644 --- a/src/Input/FGJoystickInput.cxx +++ b/src/Input/FGJoystickInput.cxx @@ -120,7 +120,7 @@ void FGJoystickInput::init() for (int i = 0; i < MAX_JOYSTICKS; i++) { jsJoystick * js = new jsJoystick(i); joysticks[i].plibJS.reset(js); - initializing[i] = true; + joysticks[i].initializing = true; if (js->notWorking()) { SG_LOG(SG_INPUT, SG_DEBUG, "Joystick " << i << " not found"); @@ -341,31 +341,37 @@ void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, jsJoystick * js = joy->plibJS.get(); if (js == 0 || js->notWorking()) { - initializing[index] = true; + joysticks[index].initializing = true; if (js) { - jsJoystick * js = new jsJoystick(index); - joysticks[index].plibJS.reset(js); + joysticks[index].plibJS.reset( new jsJoystick(index) ); } return; } js->read(&buttons, axis_values); if (js->notWorking()) { // If js is disconnected - initializing[index] = true; + joysticks[index].initializing = true; return; } // Joystick axes can get initialized to extreme values, at least on Linux. + // Wait until one of the axes get a different value before continuing. // https://sourceforge.net/p/flightgear/codetickets/2185/ - if (initializing[index]) { - float axis_values[MAX_JOYSTICK_AXES]; - int buttons; + if (joysticks[index].initializing) { - js->rawRead(&buttons, axis_values); - for (int j = 0; j < joy->naxes; j++) { - if (fabsf(axis_values[j]) > js->getSaturation(j)) return; + if (!joysticks[index].initialized) { + js->read(NULL, joysticks[index].values); + joysticks[index].initialized = true; } - initializing[index] = false; + + int j; + for (j = 0; j < joy->naxes; j++) { + if (axis_values[j] != joysticks[index].values[j]) break; + } + if (j == joy->naxes) { + return; + } + joysticks[index].initializing = false; } // Update device status diff --git a/src/Input/FGJoystickInput.hxx b/src/Input/FGJoystickInput.hxx index 8a6f425c4..247225575 100644 --- a/src/Input/FGJoystickInput.hxx +++ b/src/Input/FGJoystickInput.hxx @@ -101,11 +101,13 @@ private: axis * axes; FGButton * buttons; bool predefined; + bool initializing = true; + bool initialized = false; + float values[MAX_JOYSTICK_AXES]; void clearAxesAndButtons(); }; - bool initializing[MAX_JOYSTICKS]; joystick joysticks[MAX_JOYSTICKS]; void updateJoystick(int index, joystick* joy, double dt); }; From c770358f130e78499c636f0a063979c171e5c1f3 Mon Sep 17 00:00:00 2001 From: jano Date: Fri, 24 Jan 2020 10:42:57 +0100 Subject: [PATCH 18/98] added the velocity for the ufo, used in lag compensation --- src/FDM/UFO.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/FDM/UFO.cxx b/src/FDM/UFO.cxx index 14a462bba..9422c5988 100644 --- a/src/FDM/UFO.cxx +++ b/src/FDM/UFO.cxx @@ -189,6 +189,11 @@ void FGUFO::update( double dt ) { set_V_north(cos(heading) * velocity * SG_METER_TO_FEET); set_V_east(sin(heading) * velocity * SG_METER_TO_FEET); set_V_down(-real_climb_rate); + + //we assume the ufo only fly along the roll axis, providing a velocity to allow lag compensation + _set_Velocities_Body( velocity * SG_METER_TO_FEET, + 0 , + 0 ); } From 395a6546923215570a1e3ee799d8e2ff6c1d71a4 Mon Sep 17 00:00:00 2001 From: jano Date: Sun, 26 Jan 2020 10:22:15 +0100 Subject: [PATCH 19/98] make the aiship speed respect strictly the target speed when close enough, should improve drifting carrier on mp --- src/AIModel/AIShip.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/AIModel/AIShip.cxx b/src/AIModel/AIShip.cxx index ab91ddc2b..0c3e76f25 100644 --- a/src/AIModel/AIShip.cxx +++ b/src/AIModel/AIShip.cxx @@ -287,6 +287,8 @@ void FGAIShip::Run(double dt) { if (speed_diff < 0.0) speed -= _speed_constant * dt; + } else { + speed = tgt_speed; } // do not allow unreasonable speeds From e4c393ba9c2963f2d64c3834b733952be1be3ed6 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Fri, 31 Jan 2020 10:03:31 +0100 Subject: [PATCH 20/98] Revert delayed running of fdmex for in-air starts since it does more damage than good. --- src/FDM/JSBSim/JSBSim.cxx | 15 ++++----------- src/FDM/JSBSim/JSBSim.hxx | 1 - 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/FDM/JSBSim/JSBSim.cxx b/src/FDM/JSBSim/JSBSim.cxx index 5a9fcf92e..8e1558955 100644 --- a/src/FDM/JSBSim/JSBSim.cxx +++ b/src/FDM/JSBSim/JSBSim.cxx @@ -154,7 +154,7 @@ std::map FGJSBsim::TURBULENCE_TYPE_NAMES; static FGTurbulenceSeverityTable TurbulenceSeverityTable; FGJSBsim::FGJSBsim( double dt ) - : FGInterface(dt), delta_t(dt) + : FGInterface(dt) { bool result; if( TURBULENCE_TYPE_NAMES.empty() ) { @@ -237,7 +237,7 @@ FGJSBsim::FGJSBsim( double dt ) terrain = fgGetNode("/sim/fdm/surface", true); -// fdmex->Setdt( dt ); + fdmex->Setdt( dt ); result = fdmex->LoadModel( aircraft_path, engine_path, systems_path, fgGetString("/sim/aero"), false ); @@ -437,6 +437,7 @@ void FGJSBsim::init() common_init(); copy_to_JSBsim(); + fdmex->Resume(); fdmex->RunIC(); //loop JSBSim once w/o integrating if (fgGetBool("/sim/presets/running")) { Propulsion->InitRunning(-1); @@ -466,16 +467,8 @@ void FGJSBsim::init() fgic->SetVNorthFpsIC(gndVelNED(1)); fgic->SetVEastFpsIC(gndVelNED(2)); fgic->SetVDownFpsIC(gndVelNED(3)); - - fdmex->Resume(); - do_trim(); } - else - { - do_trim(); - fdmex->Resume(); - } - fdmex->Setdt( delta_t ); + do_trim(); needTrim = false; } diff --git a/src/FDM/JSBSim/JSBSim.hxx b/src/FDM/JSBSim/JSBSim.hxx index caed83c5a..b601f490d 100644 --- a/src/FDM/JSBSim/JSBSim.hxx +++ b/src/FDM/JSBSim/JSBSim.hxx @@ -302,7 +302,6 @@ private: JSBSim::FGColumnVector3 hook_root_struct; double hook_length; - double delta_t; bool crashed; AircraftMesh_ptr mesh; From 955357739798398f88b74212771a3c8f1d1de1ca Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Sun, 16 Feb 2020 20:27:44 +0000 Subject: [PATCH 21/98] Add support for tarballs in terrasync --- scripts/python/TerraSync/terrasync/main.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scripts/python/TerraSync/terrasync/main.py b/scripts/python/TerraSync/terrasync/main.py index 8e4bcc5fb..82ffb7635 100755 --- a/scripts/python/TerraSync/terrasync/main.py +++ b/scripts/python/TerraSync/terrasync/main.py @@ -184,6 +184,7 @@ class DirIndex: def __init__(self, dirIndexFile): self.d = [] self.f = [] + self.t = [] self.version = 0 self.path = None # will be a VirtualPath instance when set @@ -221,6 +222,9 @@ class DirIndex: elif tokens[0] == "f": self.f.append({ 'name': tokens[1], 'hash': tokens[2], 'size': tokens[3] }) + elif tokens[0] == "t": + self.t.append({ 'name': tokens[1], 'hash': tokens[2], 'size': tokens[3] }) + def _sanityCheck(self): if self.path is None: assert self._rawContents is not None @@ -239,6 +243,9 @@ class DirIndex: def getDirectories(self): return self.d + def getTarballs(self): + return self.t + def getFiles(self): return self.f @@ -639,6 +646,14 @@ class TerraSync: subdir['hash']) serverDirs.append(d) + for tarball in dirIndex.getTarballs(): + # Tarballs are handled the same as normal files. + f = tarball['name'] + self.processFileEntry(virtualBase / f, + join(relativeBase, f), + tarball['hash']) + serverFiles.append(f) + localFullPath = join(self.target, relativeBase) localFiles = [ f for f in listdir(localFullPath) if isfile(join(localFullPath, f)) ] From 1f0c21a371cbb6078e846cbcc6cc28014e46c8c3 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Wed, 19 Feb 2020 20:32:22 +0000 Subject: [PATCH 22/98] Warn when Nasal module not set in defaults.xml --- src/Scripting/NasalSys.cxx | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index f7a6001e6..783688ec1 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -186,7 +186,7 @@ public: bool isSingleShot() const { return _singleShot; } - + const std::string& name() const { return _name; } private: @@ -215,7 +215,7 @@ static void f_timerObj_setSimTime(TimerObj& timer, naContext c, naRef value) //////////////////// /// Timestamp - used to provide millisecond based timing of operations. See SGTimeStamp //// -/// 0.373404us to perform elapsedUSec from Nasal : Tested i7-4790K, Win64 +/// 0.373404us to perform elapsedUSec from Nasal : Tested i7-4790K, Win64 class TimeStampObj : public SGReferenced { public: @@ -353,7 +353,7 @@ FGNasalSys::~FGNasalSys() SG_LOG(SG_GENERAL, SG_ALERT, "Nasal was not shutdown"); } sglog().removeCallback(_log.get()); - + nasalSys = nullptr; } @@ -367,7 +367,7 @@ bool FGNasalSys::parseAndRunWithOutput(const std::string& source, std::string& o return false; } naRef result = callWithContext(ctx, code, 0, 0, naNil()); - + // if there was a result value, try to convert it to a string // value. if (!naIsNil(result)) { @@ -376,7 +376,7 @@ bool FGNasalSys::parseAndRunWithOutput(const std::string& source, std::string& o output = naStr_data(s); } } - + naFreeContext(ctx); return true; } @@ -1055,7 +1055,7 @@ void FGNasalSys::shutdown() delete t; } _nasalTimers.clear(); - + naClearSaved(); _string = naNil(); // will be freed by _context @@ -1068,19 +1068,19 @@ void FGNasalSys::shutdown() _globals = naNil(); naGC(); - + // Destroy all queued ghosts : important to ensure persistent timers are // destroyed now. nasal::ghostProcessDestroyList(); - + if (!_persistentTimers.empty()) { SG_LOG(SG_NASAL, SG_DEV_WARN, "Extant persistent timer count:" << _persistentTimers.size()); - + for (auto pt : _persistentTimers) { SG_LOG(SG_NASAL, SG_DEV_WARN, "Extant:" << pt << " : " << pt->name()); } } - + _inited = false; } @@ -1173,8 +1173,14 @@ void FGNasalSys::addModule(string moduleName, simgear::PathList scripts) if (!module_node->hasChild("enabled",0)) { SGPropertyNode* node = module_node->getChild("enabled",0,true); - node->setBoolValue(true); - node->setAttribute(SGPropertyNode::USERARCHIVE,true); + node->setBoolValue(false); + node->setAttribute(SGPropertyNode::USERARCHIVE,false); + SG_LOG(SG_NASAL, SG_ALERT, "Nasal module " << + moduleName << + " present in FGDATA/Nasal but not configured in defaults.xml. " << + " Please add an entry to defaults.xml, and set " + << node->getPath() << + "=true to load the module on-demand at runtime when required."); } } } @@ -1377,7 +1383,7 @@ naRef FGNasalSys::parse(naContext ctx, const char* filename, " in "<< filename <<", line " << errLine; errors = errorMessageStream.str(); SG_LOG(SG_NASAL, SG_ALERT, errors); - + return naNil(); } @@ -1547,7 +1553,7 @@ naRef FGNasalSys::setListener(naContext c, int argc, naRef* args) node->getPath()); } } - + naRef code = argc > 1 ? args[1] : naNil(); if(!(naIsCode(code) || naIsCCode(code) || naIsFunc(code))) { naRuntimeError(c, "setlistener() with invalid function argument"); @@ -1555,7 +1561,7 @@ naRef FGNasalSys::setListener(naContext c, int argc, naRef* args) } int init = argc > 2 && naIsNum(args[2]) ? int(args[2].num) : 0; // do not trigger when created - int type = argc > 3 && naIsNum(args[3]) ? int(args[3].num) : 1; // trigger will always be triggered when the property is written + int type = argc > 3 && naIsNum(args[3]) ? int(args[3].num) : 1; // trigger will always be triggered when the property is written FGNasalListener *nl = new FGNasalListener(node, code, this, gcSave(code), _listenerId, init, type); From d238393d2e49c197d638e2945dcb5afc64377edc Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 24 Dec 2019 20:58:09 +0000 Subject: [PATCH 23/98] Add NetBeans .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9fd04c510..3772ef42b 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ build*/ *.qmlc CMakeLists.txt.user *.pro.user +nbproject From a3731875a1bc02317408a5c9fafd827c2c3fcf67 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 24 Dec 2019 21:54:44 +0000 Subject: [PATCH 24/98] Make Launcher ratings settings persistent --- src/GUI/AircraftSearchFilterModel.cxx | 28 +++++++++++++++++++++++++++ src/GUI/AircraftSearchFilterModel.hxx | 4 ++++ src/GUI/LauncherController.cxx | 14 ++++++++++++++ src/GUI/LauncherController.hxx | 3 ++- src/GUI/qml/AircraftList.qml | 4 ++++ src/GUI/qml/AircraftListView.qml | 3 +++ src/GUI/qml/AircraftRatingsPanel.qml | 1 + src/GUI/qml/RatingSlider.qml | 1 + 8 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/GUI/AircraftSearchFilterModel.cxx b/src/GUI/AircraftSearchFilterModel.cxx index a906f5cd0..1ff079814 100644 --- a/src/GUI/AircraftSearchFilterModel.cxx +++ b/src/GUI/AircraftSearchFilterModel.cxx @@ -1,5 +1,8 @@ #include "AircraftSearchFilterModel.hxx" +#include +#include + #include "AircraftModel.hxx" #include @@ -187,3 +190,28 @@ bool AircraftProxyModel::filterAircraft(const QModelIndex &sourceIndex) const return false; } +void AircraftProxyModel::loadRatingsSettings() +{ + QSettings settings; + m_ratingsFilter = settings.value("enable-ratings-filter", true).toBool(); + QVariantList vRatings = settings.value("ratings-filter").toList(); + if (vRatings.size() == 4) { + for (int i=0; i < 4; ++i) { + m_ratings[i] = vRatings.at(i).toInt(); + } + } + + invalidate(); +} + +void AircraftProxyModel::saveRatingsSettings() +{ + QSettings settings; + settings.setValue("enable-ratings-filter", m_ratingsFilter); + QVariantList vRatings; + for (int i=0; i < 4; ++i) { + vRatings.append(m_ratings.at(i)); + } + settings.setValue("ratings-filter", vRatings); +} + diff --git a/src/GUI/AircraftSearchFilterModel.hxx b/src/GUI/AircraftSearchFilterModel.hxx index e33bf6b2b..f40cb859b 100644 --- a/src/GUI/AircraftSearchFilterModel.hxx +++ b/src/GUI/AircraftSearchFilterModel.hxx @@ -29,6 +29,10 @@ public: Q_INVOKABLE void selectVariantForAircraftURI(QUrl uri); + Q_INVOKABLE void loadRatingsSettings(); + + Q_INVOKABLE void saveRatingsSettings(); + QList ratings() const { return m_ratings; diff --git a/src/GUI/LauncherController.cxx b/src/GUI/LauncherController.cxx index 44a61ac60..291a901b7 100644 --- a/src/GUI/LauncherController.cxx +++ b/src/GUI/LauncherController.cxx @@ -722,6 +722,20 @@ QVariantList LauncherController::defaultSplashUrls() const return urls; } +QVariant LauncherController::loadUISetting(QString name, QVariant defaultValue) const +{ + QSettings settings; + if (!settings.contains(name)) + return defaultValue; + return settings.value(name); +} + +void LauncherController::saveUISetting(QString name, QVariant value) const +{ + QSettings settings; + settings.setValue(name, value); +} + void LauncherController::onAircraftInstalledCompleted(QModelIndex index) { maybeUpdateSelectedAircraft(index); diff --git a/src/GUI/LauncherController.hxx b/src/GUI/LauncherController.hxx index 29264c006..c1f17b55c 100644 --- a/src/GUI/LauncherController.hxx +++ b/src/GUI/LauncherController.hxx @@ -142,7 +142,8 @@ public: // used on the summary screen Q_INVOKABLE QVariantList defaultSplashUrls() const; - + Q_INVOKABLE QVariant loadUISetting(QString name, QVariant defaultValue) const; + Q_INVOKABLE void saveUISetting(QString name, QVariant value) const; LaunchConfig* config() const { return m_config; } diff --git a/src/GUI/qml/AircraftList.qml b/src/GUI/qml/AircraftList.qml index 1dbf327e2..faf410bac 100644 --- a/src/GUI/qml/AircraftList.qml +++ b/src/GUI/qml/AircraftList.qml @@ -17,6 +17,10 @@ FocusScope } } + Component.onCompleted: { + _launcher.browseAircraftModel.loadRatingsSettings(); + } + Rectangle { id: tabBar diff --git a/src/GUI/qml/AircraftListView.qml b/src/GUI/qml/AircraftListView.qml index ed0958084..6fe7bce66 100644 --- a/src/GUI/qml/AircraftListView.qml +++ b/src/GUI/qml/AircraftListView.qml @@ -12,6 +12,9 @@ Item { function updateSelectionFromLauncher() { + if (!model) + return; + model.selectVariantForAircraftURI(_launcher.selectedAircraft); var row = model.indexForURI(_launcher.selectedAircraft); if (row >= 0) { diff --git a/src/GUI/qml/AircraftRatingsPanel.qml b/src/GUI/qml/AircraftRatingsPanel.qml index 3e6f46463..9df0585be 100644 --- a/src/GUI/qml/AircraftRatingsPanel.qml +++ b/src/GUI/qml/AircraftRatingsPanel.qml @@ -15,6 +15,7 @@ ListHeaderBox onCheckedChanged: { _launcher.browseAircraftModel.ratingsFilterEnabled = checked + _launcher.saveUISetting("enable-ratings-filter", checked); } label: qsTr("Filter using ratings") diff --git a/src/GUI/qml/RatingSlider.qml b/src/GUI/qml/RatingSlider.qml index 2a2d3c90d..ea6964177 100644 --- a/src/GUI/qml/RatingSlider.qml +++ b/src/GUI/qml/RatingSlider.qml @@ -15,5 +15,6 @@ Slider { onValueChanged: { ratings[ratingIndex] = value + _launcher.browseAircraftModel.saveRatingsSettings(); } } From f14b8ba6d48dc8ebb98bcebd7f673ed472444a6f Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 24 Dec 2019 22:41:42 +0000 Subject: [PATCH 25/98] Validate Nasal makeTimer arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the callback arguments can’t be validated, catch is and actually report a failure, instead of creating a non-functional timer object --- src/Scripting/NasalSys.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index 783688ec1..75640ef81 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -590,6 +590,8 @@ static naRef f_makeTimer(naContext c, naRef me, int argc, naRef* args) } else if ((argc == 3) && naIsFunc(args[2])) { self = args[1]; func = args[2]; + } else { + naRuntimeError(c, "bad function/self arguments to maketimer"); } TimerObj* timerObj = new TimerObj(c, nasalSys, func, self, args[0].num); From ff70578d57be513d278cbab7b1f9d74e6b525bae Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 24 Dec 2019 23:14:58 +0000 Subject: [PATCH 26/98] Launcher: Fix a QML warning on startup --- src/GUI/qml/AircraftGridView.qml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/GUI/qml/AircraftGridView.qml b/src/GUI/qml/AircraftGridView.qml index 5af2cf11a..04dd63496 100644 --- a/src/GUI/qml/AircraftGridView.qml +++ b/src/GUI/qml/AircraftGridView.qml @@ -11,6 +11,9 @@ Item { function updateSelectionFromLauncher() { + if (!model) + return; + var row = model.indexForURI(_launcher.selectedAircraft); if (row >= 0) { view.currentIndex = row; From 219ceacc7a5cda32f0d33a35b5a4907cc0ae8621 Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 24 Feb 2020 11:32:57 +0000 Subject: [PATCH 27/98] macOS Catalina device filtering --- src/Input/FGMacOSXEventInput.cxx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Input/FGMacOSXEventInput.cxx b/src/Input/FGMacOSXEventInput.cxx index b8dc29eee..d7f6a33f4 100644 --- a/src/Input/FGMacOSXEventInput.cxx +++ b/src/Input/FGMacOSXEventInput.cxx @@ -252,8 +252,21 @@ void FGMacOSXEventInputPrivate::matchedDevice(IOHIDDeviceRef device) 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 From 517cf19ec6442ada01061f4ebc852100a3678652 Mon Sep 17 00:00:00 2001 From: Slawek Mikula Date: Wed, 1 Jan 2020 08:52:26 +0100 Subject: [PATCH 28/98] - ATC radio list - adding selectors to radio R1/R2 for corresponding frequencies --- src/ATC/atcdialog.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ATC/atcdialog.cxx b/src/ATC/atcdialog.cxx index b98893f28..ef378295f 100644 --- a/src/ATC/atcdialog.cxx +++ b/src/ATC/atcdialog.cxx @@ -162,6 +162,8 @@ void FGATCDialogNew::frequencyDisplay(const std::string& ident) buf[7] = '\0'; entry->setStringValue("text[1]/label", buf); + entry->setStringValue("button[0]/binding/value", buf); + entry->setStringValue("button[1]/binding/value", buf); } _gui->showDialog(dialog_name); From 32d6f753052c0688be7bfde7112f1906681e2cf4 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Sat, 7 Mar 2020 09:22:22 +0100 Subject: [PATCH 29/98] Process the buttons even if the axes are not declared initialized --- src/Input/FGJoystickInput.cxx | 69 +++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/Input/FGJoystickInput.cxx b/src/Input/FGJoystickInput.cxx index c35228ef2..1144fca99 100644 --- a/src/Input/FGJoystickInput.cxx +++ b/src/Input/FGJoystickInput.cxx @@ -337,6 +337,7 @@ void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, int modifiers = fgGetKeyModifiers(); int buttons; bool pressed, last_state; + bool axes_initialized; float delay; jsJoystick * js = joy->plibJS.get(); @@ -357,6 +358,7 @@ void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, // Joystick axes can get initialized to extreme values, at least on Linux. // Wait until one of the axes get a different value before continuing. // https://sourceforge.net/p/flightgear/codetickets/2185/ + axes_initialized = true; if (joysticks[index].initializing) { if (!joysticks[index].initialized) { @@ -368,16 +370,19 @@ void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, for (j = 0; j < joy->naxes; j++) { if (axis_values[j] != joysticks[index].values[j]) break; } - if (j == joy->naxes) { - return; + if (j < joy->naxes) { + joysticks[index].initializing = false; + } else { + axes_initialized = false; } - joysticks[index].initializing = false; } // Update device status SGPropertyNode_ptr status = status_node->getChild("joystick", index, true); - for (int j = 0; j < MAX_JOYSTICK_AXES; j++) { - status->getChild("axis", j, true)->setFloatValue(axis_values[j]); + if (axes_initialized) { + for (int j = 0; j < MAX_JOYSTICK_AXES; j++) { + status->getChild("axis", j, true)->setFloatValue(axis_values[j]); + } } for (int j = 0; j < MAX_JOYSTICK_BUTTONS; j++) { @@ -385,36 +390,38 @@ void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, } // Fire bindings for the axes. - for (int j = 0; j < joy->naxes; j++) { - axis &a = joy->axes[j]; + if (axes_initialized) { + for (int j = 0; j < joy->naxes; j++) { + axis &a = joy->axes[j]; - // Do nothing if the axis position - // is unchanged; only a change in - // position fires the bindings. - // But only if there are bindings - if (fabs(axis_values[j] - a.last_value) > a.tolerance - && a.bindings[KEYMOD_NONE].size() > 0 ) { - a.last_value = axis_values[j]; - for (unsigned int k = 0; k < a.bindings[KEYMOD_NONE].size(); k++) - a.bindings[KEYMOD_NONE][k]->fire(axis_values[j]); - } + // Do nothing if the axis position + // is unchanged; only a change in + // position fires the bindings. + // But only if there are bindings + if (fabs(axis_values[j] - a.last_value) > a.tolerance + && a.bindings[KEYMOD_NONE].size() > 0 ) { + a.last_value = axis_values[j]; + for (unsigned int k = 0; k < a.bindings[KEYMOD_NONE].size(); k++) + a.bindings[KEYMOD_NONE][k]->fire(axis_values[j]); + } - // do we have to emulate axis buttons? - last_state = joy->axes[j].low.last_state || joy->axes[j].high.last_state; - pressed = axis_values[j] < a.low_threshold || axis_values[j] > a.high_threshold; - delay = (pressed ? last_state ? a.interval_sec : a.delay_sec : a.release_delay_sec ); - if(pressed || last_state) a.last_dt += dt; - else a.last_dt = 0; - if(a.last_dt >= delay) { - if (a.low.bindings[modifiers].size()) - joy->axes[j].low.update( modifiers, axis_values[j] < a.low_threshold ); + // do we have to emulate axis buttons? + last_state = joy->axes[j].low.last_state || joy->axes[j].high.last_state; + pressed = axis_values[j] < a.low_threshold || axis_values[j] > a.high_threshold; + delay = (pressed ? last_state ? a.interval_sec : a.delay_sec : a.release_delay_sec ); + if(pressed || last_state) a.last_dt += dt; + else a.last_dt = 0; + if(a.last_dt >= delay) { + if (a.low.bindings[modifiers].size()) + joy->axes[j].low.update( modifiers, axis_values[j] < a.low_threshold ); - if (a.high.bindings[modifiers].size()) - joy->axes[j].high.update( modifiers, axis_values[j] > a.high_threshold ); + if (a.high.bindings[modifiers].size()) + joy->axes[j].high.update( modifiers, axis_values[j] > a.high_threshold ); - a.last_dt -= delay; - } - } // of axes iteration + a.last_dt -= delay; + } + } // of axes iteration + } // axes_initialized // Fire bindings for the buttons. for (int j = 0; j < joy->nbuttons; j++) { From 410c249ba80d76a2d6061231cf802835c73d3c1f Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 24 Feb 2020 11:32:43 +0000 Subject: [PATCH 30/98] Launcher: enable/disable all add-on types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow enable/disable in the UI of everything in the ‘add-ons’ page of the launcher. --- src/GUI/AddOnsController.cxx | 97 ++++++++------ src/GUI/AddOnsController.hxx | 23 ++-- src/GUI/AircraftModel.cxx | 25 +++- src/GUI/CMakeLists.txt | 2 + src/GUI/CatalogListModel.cxx | 30 +++-- src/GUI/CatalogListModel.hxx | 1 + src/GUI/LauncherController.cxx | 27 ---- src/GUI/LauncherMainWindow.cxx | 3 +- src/GUI/LocalAircraftCache.hxx | 3 +- src/GUI/PathListModel.cxx | 188 ++++++++++++++++++++++++++++ src/GUI/PathListModel.hxx | 56 +++++++++ src/GUI/QtLauncher.cxx | 3 +- src/GUI/assets/icons8-hide-50.png | Bin 0 -> 1073 bytes src/GUI/qml/AddOns.qml | 60 ++------- src/GUI/qml/AddOnsDelegate.qml | 7 +- src/GUI/qml/CatalogDelegate.qml | 34 ++++- src/GUI/qml/Checkbox.qml | 6 +- src/GUI/qml/EnableDisableButton.qml | 36 ++++++ src/GUI/qml/PathListDelegate.qml | 24 +++- src/GUI/resources.qrc | 2 + 20 files changed, 467 insertions(+), 160 deletions(-) create mode 100644 src/GUI/PathListModel.cxx create mode 100644 src/GUI/PathListModel.hxx create mode 100644 src/GUI/assets/icons8-hide-50.png create mode 100644 src/GUI/qml/EnableDisableButton.qml diff --git a/src/GUI/AddOnsController.cxx b/src/GUI/AddOnsController.cxx index 43d9988f7..06c8d2579 100644 --- a/src/GUI/AddOnsController.cxx +++ b/src/GUI/AddOnsController.cxx @@ -22,12 +22,15 @@ #include "AddonsModel.hxx" #include "InstallSceneryDialog.hxx" #include "QtLauncher.hxx" +#include "PathListModel.hxx" +#include "LaunchConfig.hxx" ////////////////////////////////////////////////////////////////////////// -AddOnsController::AddOnsController(LauncherMainWindow *parent) : +AddOnsController::AddOnsController(LauncherMainWindow *parent, LaunchConfig* config) : QObject(parent), - m_launcher(parent) + m_launcher(parent), + m_config(config) { m_catalogs = new CatalogListModel(this, simgear::pkg::RootRef(globals->packageRoot())); @@ -38,10 +41,25 @@ AddOnsController::AddOnsController(LauncherMainWindow *parent) : connect(m_addonsModuleModel, &AddonsModel::modulesChanged, this, &AddOnsController::onAddonsChanged); using namespace flightgear::addons; - QSettings settings; - m_sceneryPaths = settings.value("scenery-paths").toStringList(); - m_aircraftPaths = settings.value("aircraft-paths").toStringList(); + m_sceneryPaths = new PathListModel(this); + connect(m_sceneryPaths, &PathListModel::enabledPathsChanged, [this] () { + m_sceneryPaths->saveToSettings("scenery-paths"); + flightgear::launcherSetSceneryPaths(); + }); + m_sceneryPaths->loadFromSettings("scenery-paths"); + + m_aircraftPaths = new PathListModel(this); + connect(m_aircraftPaths, &PathListModel::enabledPathsChanged, [this] () { + m_aircraftPaths->saveToSettings("aircraft-paths"); + + auto aircraftCache = LocalAircraftCache::instance(); + aircraftCache->setPaths(m_aircraftPaths->enabledPaths()); + aircraftCache->scanDirs(); + }); + m_aircraftPaths->loadFromSettings("aircraft-paths"); + + QSettings settings; int size = settings.beginReadArray("addon-modules"); for (int i = 0; i < size; ++i) { settings.setArrayIndex(i); @@ -66,14 +84,18 @@ AddOnsController::AddOnsController(LauncherMainWindow *parent) : qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "AddOnsControllers", "no"); qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "CatalogListModel", "no"); qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "AddonsModel", "no"); + qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "PathListMode", "no"); + + connect(m_config, &LaunchConfig::collect, + this, &AddOnsController::collectArgs); } -QStringList AddOnsController::aircraftPaths() const +PathListModel* AddOnsController::aircraftPaths() const { return m_aircraftPaths; } -QStringList AddOnsController::sceneryPaths() const +PathListModel* AddOnsController::sceneryPaths() const { return m_sceneryPaths; } @@ -119,6 +141,7 @@ QString AddOnsController::addAircraftPath() const } } + m_aircraftPaths->appendPath(path); return path; } @@ -206,6 +229,7 @@ QString AddOnsController::addSceneryPath() const } } + m_sceneryPaths->appendPath(path); return path; } @@ -227,37 +251,6 @@ void AddOnsController::openDirectory(QString path) QDesktopServices::openUrl(u); } -void AddOnsController::setAircraftPaths(QStringList aircraftPaths) -{ - if (m_aircraftPaths == aircraftPaths) - return; - - m_aircraftPaths = aircraftPaths; - emit aircraftPathsChanged(m_aircraftPaths); - - - QSettings settings; - settings.setValue("aircraft-paths", m_aircraftPaths); - auto aircraftCache = LocalAircraftCache::instance(); - aircraftCache->setPaths(m_aircraftPaths); - aircraftCache->scanDirs(); -} - -void AddOnsController::setSceneryPaths(QStringList sceneryPaths) -{ - if (m_sceneryPaths == sceneryPaths) - return; - - m_sceneryPaths = sceneryPaths; - - QSettings settings; - settings.setValue("scenery-paths", m_sceneryPaths); - - flightgear::launcherSetSceneryPaths(); - - emit sceneryPathsChanged(m_sceneryPaths); -} - void AddOnsController::setAddons(AddonsModel* addons) { @@ -342,3 +335,31 @@ void AddOnsController::onAddonsChanged() } settings.endArray(); } + +void AddOnsController::collectArgs() +{ + // scenery paths + Q_FOREACH(QString path, m_sceneryPaths->enabledPaths()) { + m_config->setArg("fg-scenery", path); + } + + // aircraft paths + Q_FOREACH(QString path, m_aircraftPaths->enabledPaths()) { + m_config->setArg("fg-aircraft", path); + } + + // add-on module paths + // we could query this directly from AddonsModel, but this is simpler right now + QSettings settings; + int size = settings.beginReadArray("addon-modules"); + for (int i = 0; i < size; ++i) { + settings.setArrayIndex(i); + + QString path = settings.value("path").toString(); + bool enable = settings.value("enable").toBool(); + if (enable) { + m_config->setArg("addon", path); + } + } + settings.endArray(); +} diff --git a/src/GUI/AddOnsController.hxx b/src/GUI/AddOnsController.hxx index fab9119a8..2a9dea74e 100644 --- a/src/GUI/AddOnsController.hxx +++ b/src/GUI/AddOnsController.hxx @@ -7,13 +7,15 @@ class CatalogListModel; class AddonsModel; class LauncherMainWindow; +class PathListModel; +class LaunchConfig; class AddOnsController : public QObject { Q_OBJECT - Q_PROPERTY(QStringList aircraftPaths READ aircraftPaths WRITE setAircraftPaths NOTIFY aircraftPathsChanged) - Q_PROPERTY(QStringList sceneryPaths READ sceneryPaths WRITE setSceneryPaths NOTIFY sceneryPathsChanged) + Q_PROPERTY(PathListModel* aircraftPaths READ aircraftPaths CONSTANT) + Q_PROPERTY(PathListModel* sceneryPaths READ sceneryPaths CONSTANT) Q_PROPERTY(QStringList modulePaths READ modulePaths WRITE setModulePaths NOTIFY modulePathsChanged) Q_PROPERTY(CatalogListModel* catalogs READ catalogs CONSTANT) @@ -22,10 +24,10 @@ class AddOnsController : public QObject Q_PROPERTY(bool showNoOfficialHangar READ showNoOfficialHangar NOTIFY showNoOfficialHangarChanged) public: - explicit AddOnsController(LauncherMainWindow *parent = nullptr); + explicit AddOnsController(LauncherMainWindow *parent, LaunchConfig* config); - QStringList aircraftPaths() const; - QStringList sceneryPaths() const; + PathListModel* aircraftPaths() const; + PathListModel* sceneryPaths() const; QStringList modulePaths() const; CatalogListModel* catalogs() const @@ -50,8 +52,6 @@ public: Q_INVOKABLE void officialCatalogAction(QString s); signals: - void aircraftPathsChanged(QStringList aircraftPaths); - void sceneryPathsChanged(QStringList sceneryPaths); void modulePathsChanged(QStringList modulePaths); void modulesChanged(); @@ -59,12 +59,12 @@ signals: void showNoOfficialHangarChanged(); public slots: - void setAircraftPaths(QStringList aircraftPaths); - void setSceneryPaths(QStringList sceneryPaths); void setModulePaths(QStringList modulePaths); void setAddons(AddonsModel* addons); void onAddonsChanged(void); + void collectArgs(); + private: bool shouldShowOfficialCatalogMessage() const; void onCatalogsChanged(); @@ -72,9 +72,10 @@ private: LauncherMainWindow* m_launcher; CatalogListModel* m_catalogs = nullptr; AddonsModel* m_addonsModuleModel = nullptr; + LaunchConfig* m_config = nullptr; - QStringList m_aircraftPaths; - QStringList m_sceneryPaths; + PathListModel* m_aircraftPaths = nullptr; + PathListModel* m_sceneryPaths = nullptr; QStringList m_addonModulePaths; }; diff --git a/src/GUI/AircraftModel.cxx b/src/GUI/AircraftModel.cxx index b94a0cc9c..5a1d799ea 100644 --- a/src/GUI/AircraftModel.cxx +++ b/src/GUI/AircraftModel.cxx @@ -42,6 +42,19 @@ const int STANDARD_THUMBNAIL_HEIGHT = 128; using namespace simgear::pkg; +bool isPackageFailure(Delegate::StatusCode status) +{ + switch (status) { + case Delegate::STATUS_SUCCESS: + case Delegate::STATUS_REFRESHED: + case Delegate::STATUS_IN_PROGRESS: + return false; + + default: + return true; + } +} + class PackageDelegate : public simgear::pkg::Delegate { public: @@ -77,8 +90,8 @@ protected: void installProgress(InstallRef aInstall, unsigned int bytes, unsigned int total) override { - Q_UNUSED(bytes); - Q_UNUSED(total); + Q_UNUSED(bytes) + Q_UNUSED(total) QModelIndex mi(indexForPackage(aInstall->package())); m_model->dataChanged(mi, mi); } @@ -104,7 +117,7 @@ protected: void installStatusChanged(InstallRef aInstall, StatusCode aReason) override { - Q_UNUSED(aReason); + Q_UNUSED(aReason) QModelIndex mi(indexForPackage(aInstall->package())); m_model->dataChanged(mi, mi); } @@ -261,7 +274,7 @@ QVariant AircraftItemModel::data(const QModelIndex& index, int role) const } if (row >= m_cachedLocalAircraftCount) { - quint32 packageIndex = row - m_cachedLocalAircraftCount; + quint32 packageIndex = static_cast(row - m_cachedLocalAircraftCount); const PackageRef& pkg(m_packages[packageIndex]); InstallRef ex = pkg->existingInstall(); @@ -393,6 +406,10 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const Delega return LocalAircraftCache::PackageUpdateAvailable; } + const auto status = i->status(); + if (isPackageFailure(status)) + return LocalAircraftCache::PackageInstallFailed; + return LocalAircraftCache::PackageInstalled; } else { return LocalAircraftCache::PackageNotInstalled; diff --git a/src/GUI/CMakeLists.txt b/src/GUI/CMakeLists.txt index 9715df6b9..ffcee4862 100644 --- a/src/GUI/CMakeLists.txt +++ b/src/GUI/CMakeLists.txt @@ -124,6 +124,8 @@ if (HAVE_QT) AddonsModel.hxx PixmapImageItem.cxx PixmapImageItem.hxx + PathListModel.cxx + PathListModel.hxx ${uic_sources} ${qrc_sources} ${qml_sources}) diff --git a/src/GUI/CatalogListModel.cxx b/src/GUI/CatalogListModel.cxx index 5ac82bf73..7704727e7 100644 --- a/src/GUI/CatalogListModel.cxx +++ b/src/GUI/CatalogListModel.cxx @@ -93,13 +93,13 @@ void CatalogListModel::resetData() int CatalogListModel::rowCount(const QModelIndex& parent) const { - return m_catalogs.size(); + Q_UNUSED(parent) + return static_cast(m_catalogs.size()); } QVariant CatalogListModel::data(const QModelIndex& index, int role) const { - simgear::pkg::CatalogRef cat = m_catalogs.at(index.row()); - + const auto cat = m_catalogs.at(static_cast(index.row())); if (role == Qt::DisplayRole) { QString name = QString::fromStdString(cat->name()); QString desc; @@ -139,6 +139,8 @@ QVariant CatalogListModel::data(const QModelIndex& index, int role) const return translateStatusForCatalog(cat); } else if (role == CatalogIsNewlyAdded) { return (cat == m_newlyAddedCatalog); + } else if (role == CatalogEnabled) { + return cat->isUserEnabled(); } return QVariant(); @@ -146,13 +148,18 @@ QVariant CatalogListModel::data(const QModelIndex& index, int role) const bool CatalogListModel::setData(const QModelIndex &index, const QVariant &value, int role) { + auto cat = m_catalogs.at(static_cast(index.row())); + if (role == CatalogEnabled) { + cat->setUserEnabled(value.toBool()); + return true; + } return false; } Qt::ItemFlags CatalogListModel::flags(const QModelIndex &index) const { Qt::ItemFlags r = Qt::ItemIsSelectable; - const auto cat = m_catalogs.at(index.row()); + const auto cat = m_catalogs.at(static_cast(index.row())); if (cat->isEnabled()) { r |= Qt::ItemIsEnabled; } @@ -168,28 +175,27 @@ QHash CatalogListModel::roleNames() const result[CatalogNameRole] = "name"; result[CatalogStatusRole] = "status"; result[CatalogIsNewlyAdded] = "isNewlyAdded"; - + result[CatalogEnabled] = "enabled"; return result; } void CatalogListModel::removeCatalog(int index) { - if ((index < 0) || (index >= m_catalogs.size())) { + if ((index < 0) || (index >= static_cast(m_catalogs.size()))) { return; } - const std::string removeId = m_catalogs.at(index)->id(); + const std::string removeId = m_catalogs.at(static_cast(index))->id(); m_packageRoot->removeCatalogById(removeId); resetData(); } void CatalogListModel::refreshCatalog(int index) { - if ((index < 0) || (index >= m_catalogs.size())) { + if ((index < 0) || (index >= static_cast(m_catalogs.size()))) { return; } - - m_catalogs.at(index)->refresh(); + m_catalogs.at(static_cast(index))->refresh(); } void CatalogListModel::installDefaultCatalog() @@ -221,7 +227,7 @@ int CatalogListModel::indexOf(QUrl url) if (it == m_catalogs.end()) return -1; - return std::distance(m_catalogs.begin(), it); + return static_cast(std::distance(m_catalogs.begin(), it)); } void CatalogListModel::finalizeAddCatalog() @@ -237,7 +243,7 @@ void CatalogListModel::finalizeAddCatalog() return; } - const int row = std::distance(m_catalogs.begin(), it); + const int row = static_cast(std::distance(m_catalogs.begin(), it)); m_newlyAddedCatalog.clear(); emit isAddingCatalogChanged(); emit statusOfAddingCatalogChanged(); diff --git a/src/GUI/CatalogListModel.hxx b/src/GUI/CatalogListModel.hxx index 435cc3bfa..c79e0d249 100644 --- a/src/GUI/CatalogListModel.hxx +++ b/src/GUI/CatalogListModel.hxx @@ -38,6 +38,7 @@ const int CatalogStatusRole = Qt::UserRole + 5; const int CatalogDescriptionRole = Qt::UserRole + 6; const int CatalogNameRole = Qt::UserRole + 7; const int CatalogIsNewlyAdded = Qt::UserRole + 8; +const int CatalogEnabled = Qt::UserRole + 9; class CatalogDelegate; diff --git a/src/GUI/LauncherController.cxx b/src/GUI/LauncherController.cxx index 291a901b7..7211cfa9f 100644 --- a/src/GUI/LauncherController.cxx +++ b/src/GUI/LauncherController.cxx @@ -102,8 +102,6 @@ LauncherController::LauncherController(QObject *parent, QWindow* window) : this, &LauncherController::updateSelectedAircraft); QSettings settings; - LocalAircraftCache::instance()->setPaths(settings.value("aircraft-paths").toStringList()); - LocalAircraftCache::instance()->scanDirs(); m_aircraftModel->setPackageRoot(globals->packageRoot()); m_aircraftGridMode = settings.value("aircraftGridMode").toBool(); @@ -280,31 +278,6 @@ void LauncherController::collectAircraftArgs() m_config->setArg("state", state); } } - - // scenery paths - QSettings settings; - Q_FOREACH(QString path, settings.value("scenery-paths").toStringList()) { - m_config->setArg("fg-scenery", path); - } - - // aircraft paths - Q_FOREACH(QString path, settings.value("aircraft-paths").toStringList()) { - m_config->setArg("fg-aircraft", path); - } - - // add-on module paths - int size = settings.beginReadArray("addon-modules"); - for (int i = 0; i < size; ++i) { - settings.setArrayIndex(i); - - QString path = settings.value("path").toString(); - bool enable = settings.value("enable").toBool(); - if (enable) { - m_config->setArg("addon", path); - } - } - settings.endArray(); - } void LauncherController::saveAircraft() diff --git a/src/GUI/LauncherMainWindow.cxx b/src/GUI/LauncherMainWindow.cxx index 0af6c0ed1..23acdbe66 100644 --- a/src/GUI/LauncherMainWindow.cxx +++ b/src/GUI/LauncherMainWindow.cxx @@ -68,9 +68,8 @@ LauncherMainWindow::LauncherMainWindow() : connect(qa, &QAction::triggered, m_controller, &LauncherController::quit); m_controller->initialRestoreSettings(); - flightgear::launcherSetSceneryPaths(); - auto addOnsCtl = new AddOnsController(this); + auto addOnsCtl = new AddOnsController(this, m_controller->config()); //////////// #if defined(Q_OS_WIN) diff --git a/src/GUI/LocalAircraftCache.hxx b/src/GUI/LocalAircraftCache.hxx index db229fd84..e789a3844 100644 --- a/src/GUI/LocalAircraftCache.hxx +++ b/src/GUI/LocalAircraftCache.hxx @@ -126,7 +126,8 @@ public: PackageUpdateAvailable, PackageQueued, PackageDownloading, - NotPackaged + NotPackaged, + PackageInstallFailed }; Q_ENUMS(PackageStatus) diff --git a/src/GUI/PathListModel.cxx b/src/GUI/PathListModel.cxx new file mode 100644 index 000000000..05f44ccfc --- /dev/null +++ b/src/GUI/PathListModel.cxx @@ -0,0 +1,188 @@ +#include "PathListModel.hxx" + +#include +#include + +PathListModel::PathListModel(QObject *pr) : + QAbstractListModel(pr) +{ + +} + +PathListModel::~PathListModel() +{ + +} + +void PathListModel::loadFromSettings(QString key) +{ + QSettings settings; + if (!settings.contains(key)) + return; + + QVariantList vl = settings.value(key).toList(); + mPaths.clear(); + mPaths.reserve(static_cast(vl.size())); + + beginResetModel(); + + Q_FOREACH(QVariant v, vl) { + QVariantMap m = v.toMap(); + PathEntry entry; + entry.path = m.value("path").toString(); + if (entry.path.isEmpty()) { + continue; + } + + entry.enabled = m.value("enabled", QVariant{true}).toBool(); + mPaths.push_back(entry); + } + + endResetModel(); + emit enabledPathsChanged(); + emit countChanged(); +} + +void PathListModel::saveToSettings(QString key) const +{ + QVariantList vl; + for (const auto &e : mPaths) { + QVariantMap v; + v["path"] = e.path; + v["enabled"] = e.enabled; + vl.append(v); + } + + QSettings settings; + settings.setValue(key, vl); +} + +QStringList PathListModel::readEnabledPaths(QString settingsKey) +{ + QSettings settings; + if (!settings.contains(settingsKey)) + return {}; + + QStringList result; + QVariantList vl = settings.value(settingsKey).toList(); + Q_FOREACH(QVariant v, vl) { + QVariantMap m = v.toMap(); + if (!m.value("enabled").toBool()) + continue; + + result.append(m.value("path").toString()); + } + + return result; +} + +QStringList PathListModel::enabledPaths() const +{ + QStringList result; + for (const auto& e : mPaths) { + if (e.enabled) { + result.append(e.path); + } + } + return result; +} + +int PathListModel::count() +{ + return static_cast(mPaths.size()); +} + +int PathListModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return static_cast(mPaths.size()); +} + +QVariant PathListModel::data(const QModelIndex &index, int role) const +{ + int row = index.row(); + const auto& entry = mPaths.at(static_cast(row)); + switch (role) { + case Qt::DisplayRole: + case PathRole: + return entry.path; + + case PathEnabledRole: + return entry.enabled; + + default: + break; + } + + return {}; +} + +bool PathListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + int row = index.row(); + auto& entry = mPaths.at(static_cast(row)); + if (role == PathEnabledRole) { + entry.enabled = value.toBool(); + emit dataChanged(index, index, {PathEnabledRole}); + emit enabledPathsChanged(); + return true; + } + + return false; +} + +QHash PathListModel::roleNames() const +{ + QHash result = QAbstractListModel::roleNames(); + result[Qt::DisplayRole] = "path"; + result[PathEnabledRole] = "enabled"; + return result; +} + +void PathListModel::removePath(int index) +{ + if ((index < 0) || (index >= static_cast(mPaths.size()))) { + qWarning() << Q_FUNC_INFO << "index invalid:" << index; + return; + } + + beginRemoveRows({}, index, index); + auto it = mPaths.begin() + index; + mPaths.erase(it); + endRemoveRows(); + emit enabledPathsChanged(); + emit countChanged(); +} + +void PathListModel::appendPath(QString path) +{ + PathEntry entry; + entry.path = path; + entry.enabled = true; // enable by default + const int newRow = static_cast(mPaths.size()); + beginInsertRows({}, newRow, newRow); + mPaths.push_back(entry); + endInsertRows(); + emit enabledPathsChanged(); + emit countChanged(); +} + +void PathListModel::sawpIndices(int indexA, int indexB) +{ + if ((indexA < 0) || (indexA >= static_cast(mPaths.size()))) { + qWarning() << Q_FUNC_INFO << "index invalid:" << indexA; + return; + } + + if ((indexB < 0) || (indexB >= static_cast(mPaths.size()))) { + qWarning() << Q_FUNC_INFO << "index invalid:" << indexB; + return; + } + + std::swap(mPaths[static_cast(indexA)], + mPaths[static_cast(indexB)]); + emit dataChanged(index(indexA), index(indexA)); + emit dataChanged(index(indexB), index(indexB)); + emit enabledPathsChanged(); +} + diff --git a/src/GUI/PathListModel.hxx b/src/GUI/PathListModel.hxx new file mode 100644 index 000000000..57dd74e62 --- /dev/null +++ b/src/GUI/PathListModel.hxx @@ -0,0 +1,56 @@ +#ifndef PATHLISTMODEL_HXX +#define PATHLISTMODEL_HXX + +#include + +#include + +const int PathRole = Qt::UserRole + 1; +const int PathEnabledRole = Qt::UserRole + 2; + +class PathListModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(int count READ count NOTIFY countChanged) +public: + + PathListModel(QObject* pr); + ~PathListModel() override; + + void loadFromSettings(QString key); + void saveToSettings(QString key) const; + + int rowCount(const QModelIndex& parent) const override; + + QVariant data(const QModelIndex& index, int role) const override; + + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + + QHash roleNames() const override; + + static QStringList readEnabledPaths(QString settingsKey); + + QStringList enabledPaths() const; + + int count(); +signals: + void enabledPathsChanged(); + void countChanged(); + +public slots: + void removePath(int index); + void appendPath(QString path); + + void sawpIndices(int indexA, int indexB); + +private: + struct PathEntry { + QString path; + bool enabled = true; + }; + + std::vector mPaths; +}; + +#endif // PATHLISTMODEL_HXX diff --git a/src/GUI/QtLauncher.cxx b/src/GUI/QtLauncher.cxx index a7f1133a3..cdf36999e 100644 --- a/src/GUI/QtLauncher.cxx +++ b/src/GUI/QtLauncher.cxx @@ -74,6 +74,7 @@ #include "LauncherMainWindow.hxx" #include "LaunchConfig.hxx" #include "UnitsModel.hxx" +#include "PathListModel.hxx" using namespace flightgear; using namespace simgear::pkg; @@ -403,7 +404,7 @@ void launcherSetSceneryPaths() // positions QSettings settings; // append explicit scenery paths - Q_FOREACH(QString path, settings.value("scenery-paths").toStringList()) { + Q_FOREACH(QString path, PathListModel::readEnabledPaths("scenery-paths")) { globals->append_fg_scenery(path.toStdString()); } diff --git a/src/GUI/assets/icons8-hide-50.png b/src/GUI/assets/icons8-hide-50.png new file mode 100644 index 0000000000000000000000000000000000000000..d0c37a0226734ab3a3db5302dd27295feaede296 GIT binary patch literal 1073 zcmV-11kU@3P)e&eK_e(CqX^4emN&2)RHikRHqD$K*4phgd(PQ&F5{e$ z&L1}C?DbvN_g{OhZ+&agK?fcDukks)L{GZ|X%%0fg56GXGUs52KxPkm+Z#@6=#ZJJ z=#ZIAOlv^52k+rRe1(VIQ>u@l( zH}ZUkCxpS7bfjc{#&99HpYSAZz(UNxEcD}6Y{21y%`jfY#goVbqse5?UsxN19uHl3 z9Df>1=w&DP3r)wvxGTOF$z0on&&%;`;^UwAdrTOV>8*u47E7@;b}}R^WU8;3MzKIn z=Johw#6H77D}krD7lYC9L1E;VV|A2I1exW+)EvfDxDf~BU+12tqZbzGv+~h-8ndHp zvoH;=jPDacW?i)39N%Z*#iakIFtbso-FR2Nh*u}Fr5F)1&zz*aRWfJEvspTw*2EYp z_#nns7ZzYwG(03M$P^Dn+4?HBMKUkp7yKs7{r++Kem>ehSZH5{@1tzHIx34r*c%OY zMp8v=it?Ll_^6hev*)m|x75Naqo-Gj#qt5(Un4efUR0>yW6Z6gcPQ#CDha5J%qt~! z{ZVIM4f$Mo_S}PWt0{eykeE|VAtKwvPA}1+HkqR(HnXG7V2MtboXm$=<0*+kR=iD9 zUQCX}-?Gi>I-x*ZDHNC~hU)NLR!EK)pB~|JRqzSUuA!IZ_X|pVZNS}$-}K9DEU`O3 z>g=r{pNCJQ&NrA}%h5vlI@ygSRdlvQ`KpqDdhvtmWXe+WZBb`a6%=#$5ztFxyn5q?Q6ApTHp&ReZH^eoGY#_G(Dr{nn zpo~lxjtJ`{-EZ#0@o4jQ6MZ^OK0+0tTsSkzvg!3`qfXT(^GIxaGro7>wb(WylrX0@ z(cgbaON0Zs30Ftw$AyBgQ54BsAAJo8Q~7pzMf?ZuY=uCIi{#lNE4SZ}SHw%2@L6r2 z*(k3Zvg|qGhpg;ibKMOPlanB$N3Z!~;&i!79ZPykP8E{ck*`yei1hg)8F! zBAK_8*JBfo0byYl3H>gdW9|?(xS2RZDEe*@()JNy^s614+jeo@CtM`#Q-1YR?fF_D zNQ-0|xuB~D142eyA(SwSg;HmRaEG;DAiN##2mz<4=MwOw!IZzAZ3G=MQxzRDQx$1y r-qz+=T1JmRVr#nt>7auS+K7JvxKD)=r7+?D00000NkvXXu0mjf=gI+~ literal 0 HcmV?d00001 diff --git a/src/GUI/qml/AddOns.qml b/src/GUI/qml/AddOns.qml index cf32c38a5..4f95b21a4 100644 --- a/src/GUI/qml/AddOns.qml +++ b/src/GUI/qml/AddOns.qml @@ -102,12 +102,7 @@ Item { description: qsTr("To use aircraft you download yourself, FlightGear needs to " + "know the folder(s) containing the aircraft data.") showAddButton: true - onAdd: { - var newPath =_addOns.addAircraftPath(); - if (newPath !== "") { - _addOns.aircraftPaths.push(newPath) - } - } + onAdd: _addOns.addAircraftPath(); } Rectangle { @@ -127,21 +122,10 @@ Item { model: _addOns.aircraftPaths delegate: PathListDelegate { width: aircraftPathsColumn.width - deletePromptText: qsTr("Remove the aircraft folder: '%1' from the list? (The folder contents will not be changed)").arg(modelData); - modelCount: _addOns.aircraftPaths.length - - onPerformDelete: { - var modifiedPaths = _addOns.aircraftPaths.slice() - modifiedPaths.splice(model.index, 1); - _addOns.aircraftPaths = modifiedPaths; - } - - onPerformMove: { - var modifiedPaths = _addOns.aircraftPaths.slice() - modifiedPaths.splice(model.index, 1); - modifiedPaths.splice(newIndex, 0, modelData) - _addOns.aircraftPaths = modifiedPaths; - } + deletePromptText: qsTr("Remove the aircraft folder: '%1' from the list? (The folder contents will not be changed)").arg(model.path); + modelCount: _addOns.aircraftPaths.count + onPerformDelete: _addOns.aircraftPaths.removePath(model.index) + onPerformMove: _addOns.aircraftPaths.sawpIndices(model.index, newIndex); } } @@ -241,12 +225,7 @@ Item { "to know the folders containing the scenery data. " + "Adjust the order of the list to control which scenery is used in a region."); showAddButton: true - onAdd: { - var newPath =_addOns.addSceneryPath(); - if (newPath !== "") { - _addOns.sceneryPaths.push(newPath) - } - } + onAdd: _addOns.addSceneryPath(); } Rectangle { @@ -267,21 +246,10 @@ Item { delegate: PathListDelegate { width: sceneryPathsColumn.width - deletePromptText: qsTr("Remove the scenery folder: '%1' from the list? (The folder contents will not be changed)").arg(modelData); - modelCount: _addOns.sceneryPaths.length - - onPerformDelete: { - var modifiedPaths = _addOns.sceneryPaths.slice() - modifiedPaths.splice(model.index, 1); - _addOns.sceneryPaths = modifiedPaths; - } - - onPerformMove: { - var modifiedPaths = _addOns.sceneryPaths.slice() - modifiedPaths.splice(model.index, 1); - modifiedPaths.splice(newIndex, 0, modelData) - _addOns.sceneryPaths = modifiedPaths; - } + deletePromptText: qsTr("Remove the scenery folder: '%1' from the list? (The folder contents will not be changed)").arg(model.path); + modelCount: _addOns.sceneryPaths.count + onPerformDelete: _addOns.sceneryPaths.removePath(model.index) + onPerformMove: _addOns.sceneryPaths.swapIndices(model.index, newIndex); } } @@ -304,14 +272,10 @@ Item { var path = _addOns.installCustomScenery(); if (path !== "") { // insert into scenery paths if not already present - var sceneryPaths = _addOns.sceneryPaths - for (var i = 0; i < sceneryPaths.length; i++) { - if (sceneryPaths[i] === path) - return; // found, we are are done - } + // not found, add it - _addOns.sceneryPaths.push(path); + _addOns.sceneryPaths.appendPath(path); } } } diff --git a/src/GUI/qml/AddOnsDelegate.qml b/src/GUI/qml/AddOnsDelegate.qml index 2839b9d19..c74c66c2b 100644 --- a/src/GUI/qml/AddOnsDelegate.qml +++ b/src/GUI/qml/AddOnsDelegate.qml @@ -41,12 +41,9 @@ Item { width: delegateRoot.width Checkbox { - id: chkbox + id: enableCheckbox checked: model.enable - width: 30 anchors.left: parent.left - anchors.right: addonsDelegateHover.left - anchors.rightMargin: Style.margin height: contentRect.height onCheckedChanged: { _addOns.modules.enable(model.index, checked) @@ -59,7 +56,7 @@ Item { anchors.top: parent.top anchors.bottom: parent.bottom anchors.right: parent.right - anchors.left: chkbox.right + anchors.left: enableCheckbox.right hoverEnabled: true acceptedButtons: Qt.NoButton diff --git a/src/GUI/qml/CatalogDelegate.qml b/src/GUI/qml/CatalogDelegate.qml index 376b678ad..3cffffc75 100644 --- a/src/GUI/qml/CatalogDelegate.qml +++ b/src/GUI/qml/CatalogDelegate.qml @@ -26,12 +26,19 @@ Item { } } + function isDisabled() + { + return (model.status !== CatalogListModel.Ok) || + (enableCheckbox.checked === false); + } + Item { anchors.top: divider.bottom height: catalogTextColumn.childrenRect.height + Style.margin * 2 width: parent.width + Column { id: catalogTextColumn @@ -42,11 +49,28 @@ Item { anchors.rightMargin: Style.margin spacing: Style.margin - StyledText { - font.pixelSize: Style.subHeadingFontPixelSize - font.bold: true - width: parent.width - text: model.name + Row { + spacing: Style.margin + height: headingText.height + + Checkbox { + id: enableCheckbox + checked: model.enabled + height: parent.height + onCheckedChanged: model.enable = checked; + // only allow the user to toggle enable/disable if + // the catalog is valid + visible: (model.status === CatalogListModel.Ok) + } + + StyledText { + id: headingText + font.pixelSize: Style.subHeadingFontPixelSize + font.bold: true + width: catalogTextColumn.width - enableCheckbox.width + text: model.name + font.strikeout: delegateRoot.isDisabled(); + } } StyledText { diff --git a/src/GUI/qml/Checkbox.qml b/src/GUI/qml/Checkbox.qml index fb17da7ee..f2c1ffc9d 100644 --- a/src/GUI/qml/Checkbox.qml +++ b/src/GUI/qml/Checkbox.qml @@ -1,4 +1,5 @@ import QtQuick 2.4 +import "." Item { property bool checked: false @@ -11,7 +12,7 @@ Item { id: checkBox width: 18 height: 18 - border.color: mouseArea.containsMouse ? "#68A6E1" : "#9f9f9f" + border.color: mouseArea.containsMouse ? Style.frameColor : Style.inactiveThemeColor border.width: 1 anchors.left: parent.left anchors.leftMargin: 8 @@ -22,7 +23,7 @@ Item { height: 12 anchors.centerIn: parent id: checkMark - color: "#9f9f9f" + color: Style.themeColor visible: checked } } @@ -41,5 +42,6 @@ Item { onClicked: { checked = !checked } + cursorShape: Qt.PointingHandCursor } } diff --git a/src/GUI/qml/EnableDisableButton.qml b/src/GUI/qml/EnableDisableButton.qml new file mode 100644 index 000000000..345e8d4a0 --- /dev/null +++ b/src/GUI/qml/EnableDisableButton.qml @@ -0,0 +1,36 @@ +import QtQuick 2.0 +import "." + +Item { + id: root + + width: height + height: icon.implicitHeight + 1 + + property bool enable: true + + signal clicked(); + + Image { + id: icon + source: "qrc:///svg/icon-hide" + } + + MouseArea { + id: mouse + // hoverEnabled: true + onClicked: root.clicked(); + anchors.fill: parent + } + +// Text { +// anchors.right: root.left +// anchors.rightMargin: Style.margin +// anchors.verticalCenter: root.verticalCenter +// visible: mouse.containsMouse +// color: Style.baseTextColor +// font.pixelSize: Style.baseFontPixelSize +// text: root.enable ? qsTr("Click_to_disable") +// : qsTr("Click_to_enable") +// } +} diff --git a/src/GUI/qml/PathListDelegate.qml b/src/GUI/qml/PathListDelegate.qml index 7e9c37a07..99138b728 100644 --- a/src/GUI/qml/PathListDelegate.qml +++ b/src/GUI/qml/PathListDelegate.qml @@ -36,9 +36,23 @@ Item { height: Math.max(label.implicitHeight, pathDeleteButton.height) width: delegateRoot.width + Checkbox { + id: enableCheckbox + checked: model.enabled + anchors.left: parent.left + height: parent.height + onCheckedChanged: { + model.enabled = checked; + } + } + MouseArea { id: pathDelegateHover - anchors.fill: parent + anchors.left: enableCheckbox.right + anchors.leftMargin: Style.margin + anchors.right: parent.right + height: parent.height + hoverEnabled: true acceptedButtons: Qt.NoButton @@ -46,10 +60,10 @@ Item { // MouseArea, so nested containsMouse logic works ClickableText { id: label - text: modelData + text: model.path onClicked: { // open the location - _addOns.openDirectory(modelData) + _addOns.openDirectory(model.path) } anchors.left: parent.left anchors.right: reorderButton.left @@ -58,6 +72,8 @@ Item { verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap + + font.strikeout: enableCheckbox.checked === false } DeleteButton { @@ -88,7 +104,7 @@ Item { delegateRoot.performMove(model.index + 1) } } - } + } // of MouseArea for hover YesNoPanel { id: confirmDeletePath diff --git a/src/GUI/resources.qrc b/src/GUI/resources.qrc index 3a7f3f820..db19b19c3 100644 --- a/src/GUI/resources.qrc +++ b/src/GUI/resources.qrc @@ -129,6 +129,7 @@ qml/AircraftGridView.qml qml/AircraftListView.qml qml/GridToggleButton.qml + qml/EnableDisableButton.qml preview-close.png @@ -147,5 +148,6 @@ assets/icons8-helicopter.svg assets/icons8-grid-view.svg assets/icons8-menu.svg + assets/icons8-hide-50.png From a405d0508bd85e0728160cde446a16ac6c510d05 Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 9 Mar 2020 16:28:22 +0000 Subject: [PATCH 31/98] Fix a dumb allocation bug on Windows --- src/Main/bootstrap.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Main/bootstrap.cxx b/src/Main/bootstrap.cxx index 09f3c7618..b0799a967 100644 --- a/src/Main/bootstrap.cxx +++ b/src/Main/bootstrap.cxx @@ -142,7 +142,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR* wideArgs = CommandLineToArgvW(GetCommandLineW(), &numArgs); std::vector utf8Args; - utf8Args.reserve(numArgs); + utf8Args.resize(numArgs); for (int a = 0; a < numArgs; ++a) { const auto s = simgear::strutils::convertWStringToUtf8(wideArgs[a]); From de172de5b339e4fffac57b328f2ec5963fb2ef74 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 10 Mar 2020 10:28:56 +0000 Subject: [PATCH 32/98] Launcher: Fix a typo breaking path re-ordering --- src/GUI/PathListModel.cxx | 2 +- src/GUI/PathListModel.hxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GUI/PathListModel.cxx b/src/GUI/PathListModel.cxx index 05f44ccfc..a52313538 100644 --- a/src/GUI/PathListModel.cxx +++ b/src/GUI/PathListModel.cxx @@ -167,7 +167,7 @@ void PathListModel::appendPath(QString path) emit countChanged(); } -void PathListModel::sawpIndices(int indexA, int indexB) +void PathListModel::swapIndices(int indexA, int indexB) { if ((indexA < 0) || (indexA >= static_cast(mPaths.size()))) { qWarning() << Q_FUNC_INFO << "index invalid:" << indexA; diff --git a/src/GUI/PathListModel.hxx b/src/GUI/PathListModel.hxx index 57dd74e62..3ecd78407 100644 --- a/src/GUI/PathListModel.hxx +++ b/src/GUI/PathListModel.hxx @@ -42,7 +42,7 @@ public slots: void removePath(int index); void appendPath(QString path); - void sawpIndices(int indexA, int indexB); + void swapIndices(int indexA, int indexB); private: struct PathEntry { From 3f14d53286642dc6d7179c8101e617a0d2506868 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 10 Mar 2020 14:00:40 +0000 Subject: [PATCH 33/98] FlightPlans: fix save/load of restrictions, deleting legs from Nasal - Fix Leg ownership to avoid segfaults when deleting them via Nasal - Modify FP save/load code so restrictions and holds are correctly saved and restored Extend the tests to cover these cases. --- src/Navaids/FlightPlan.cxx | 143 ++++++++++-------- src/Navaids/FlightPlan.hxx | 21 ++- src/Navaids/route.cxx | 2 +- src/Scripting/NasalPositioned.cxx | 9 +- test_suite/unit_tests/Navaids/CMakeLists.txt | 2 + test_suite/unit_tests/Navaids/TestSuite.cxx | 2 + .../unit_tests/Navaids/test_flightplan.cxx | 86 +++++++++-- .../unit_tests/Navaids/test_flightplan.hxx | 6 +- .../unit_tests/Navaids/test_fpNasal.cxx | 105 +++++++++++++ .../unit_tests/Navaids/test_fpNasal.hxx | 46 ++++++ 10 files changed, 326 insertions(+), 96 deletions(-) create mode 100644 test_suite/unit_tests/Navaids/test_fpNasal.cxx create mode 100644 test_suite/unit_tests/Navaids/test_fpNasal.hxx diff --git a/src/Navaids/FlightPlan.cxx b/src/Navaids/FlightPlan.cxx index 61c94662a..2cd97607a 100644 --- a/src/Navaids/FlightPlan.cxx +++ b/src/Navaids/FlightPlan.cxx @@ -75,6 +75,9 @@ const string_list static_icaoFlightTypeCode = { namespace flightgear { +// implemented in route.cxx +const char* restrictionToString(RouteRestriction aRestrict); + typedef std::vector FPDelegateFactoryVec; static FPDelegateFactoryVec static_delegateFactories; @@ -109,11 +112,6 @@ FlightPlan::~FlightPlan() delete d; } } - -// delete legs - for (auto l : _legs) { - delete l; - } } FlightPlan* FlightPlan::clone(const string& newIdent) const @@ -160,7 +158,7 @@ string FlightPlan::ident() const return _ident; } -FlightPlan::Leg* FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex) +FlightPlan::LegRef FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex) { if (!aWpt) { return nullptr; @@ -189,9 +187,7 @@ void FlightPlan::insertWayptsAtIndex(const WayptVec& wps, int aIndex) index = _legs.size(); } - LegVec::iterator it = _legs.begin(); - it += index; - + auto it = _legs.begin() + index; int endIndex = index + wps.size() - 1; if (_currentIndex >= endIndex) { _currentIndex += wps.size(); @@ -199,7 +195,7 @@ void FlightPlan::insertWayptsAtIndex(const WayptVec& wps, int aIndex) LegVec newLegs; for (WayptRef wp : wps) { - newLegs.push_back(new Leg(this, wp)); + newLegs.push_back(LegRef{new Leg(this, wp)}); } lockDelegates(); @@ -223,12 +219,11 @@ void FlightPlan::deleteIndex(int aIndex) lockDelegates(); _waypointsChanged = true; - LegVec::iterator it = _legs.begin(); - it += index; - Leg* l = *it; + auto it = _legs.begin() + index; + LegRef l = *it; _legs.erase(it); - delete l; - + l->_parent = nullptr; // orphan the leg so it's clear from Nasal + if (_currentIndex == index) { // current waypoint was removed _currentWaypointChanged = true; @@ -255,43 +250,18 @@ void FlightPlan::clear() _cruiseDataChanged = true; _currentIndex = -1; - for (Leg* l : _legs) { - delete l; - } _legs.clear(); notifyCleared(); unlockDelegates(); } -class RemoveWithFlag -{ -public: - RemoveWithFlag(WayptFlag f) : flag(f), delCount(0) { } - - int numDeleted() const { return delCount; } - - bool operator()(FlightPlan::Leg* leg) const - { - if (leg->waypoint()->flag(flag)) { - delete leg; - ++delCount; - return true; - } - - return false; - } -private: - WayptFlag flag; - mutable int delCount; -}; - int FlightPlan::clearWayptsWithFlag(WayptFlag flag) { int count = 0; // first pass, fix up currentIndex for (int i=0; i<_currentIndex; ++i) { - Leg* l = _legs[i]; + const auto& l = _legs.at(i); if (l->waypoint()->flag(flag)) { ++count; } @@ -311,13 +281,21 @@ int FlightPlan::clearWayptsWithFlag(WayptFlag flag) // and let the use re-activate. // http://code.google.com/p/flightgear-bugs/issues/detail?id=1134 if (currentIsBeingCleared) { - SG_LOG(SG_GENERAL, SG_INFO, "currentIsBeingCleared:" << currentIsBeingCleared); + SG_LOG(SG_GENERAL, SG_INFO, "FlightPlan::clearWayptsWithFlag: currentIsBeingCleared:" << currentIsBeingCleared); _currentIndex = -1; } // now delete and remove - RemoveWithFlag rf(flag); - auto it = std::remove_if(_legs.begin(), _legs.end(), rf); + int numDeleted = 0; + auto it = std::remove_if(_legs.begin(), _legs.end(), + [flag, &numDeleted](const LegRef& leg) + { + if (leg->waypoint()->flag(flag)) { + ++numDeleted; + return true; + } + return false; + }); if (it == _legs.end()) { return 0; // nothing was cleared, don't fire the delegate } @@ -335,7 +313,7 @@ int FlightPlan::clearWayptsWithFlag(WayptFlag flag) } unlockDelegates(); - return rf.numDeleted(); + return numDeleted; } bool FlightPlan::isActive() const @@ -407,14 +385,14 @@ int FlightPlan::findWayptIndex(const FGPositionedRef aPos) const return -1; } -FlightPlan::Leg* FlightPlan::currentLeg() const +FlightPlan::LegRef FlightPlan::currentLeg() const { if ((_currentIndex < 0) || (_currentIndex >= numLegs())) return nullptr; return legAtIndex(_currentIndex); } -FlightPlan::Leg* FlightPlan::previousLeg() const +FlightPlan::LegRef FlightPlan::previousLeg() const { if (_currentIndex <= 0) { return nullptr; @@ -423,7 +401,7 @@ FlightPlan::Leg* FlightPlan::previousLeg() const return legAtIndex(_currentIndex - 1); } -FlightPlan::Leg* FlightPlan::nextLeg() const +FlightPlan::LegRef FlightPlan::nextLeg() const { if ((_currentIndex < 0) || ((_currentIndex + 1) >= numLegs())) { return nullptr; @@ -432,19 +410,19 @@ FlightPlan::Leg* FlightPlan::nextLeg() const return legAtIndex(_currentIndex + 1); } -FlightPlan::Leg* FlightPlan::legAtIndex(int index) const +FlightPlan::LegRef FlightPlan::legAtIndex(int index) const { if ((index < 0) || (index >= numLegs())) { throw sg_range_exception("index out of range", "FlightPlan::legAtIndex"); } - return _legs[index]; + return _legs.at(index); } -int FlightPlan::findLegIndex(const Leg *l) const +int FlightPlan::findLegIndex(const Leg* l) const { for (unsigned int i=0; i<_legs.size(); ++i) { - if (_legs[i] == l) { + if (_legs.at(i).get() == l) { return i; } } @@ -778,8 +756,11 @@ void FlightPlan::saveToProperties(SGPropertyNode* d) const // route nodes SGPropertyNode* routeNode = d->getChild("route", 0, true); for (unsigned int i=0; i<_legs.size(); ++i) { - Waypt* wpt = _legs[i]->waypoint(); - wpt->saveAsNode(routeNode->getChild("wp", i, true)); + auto leg = _legs.at(i); + Waypt* wpt = leg->waypoint(); + auto legNode = routeNode->getChild("wp", i, true); + wpt->saveAsNode(legNode); + leg->writeToProperties(legNode); } // of waypoint iteration } @@ -1119,7 +1100,20 @@ void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData) SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); if (routeNode.valid()) { for (auto wpNode : routeNode->getChildren("wp")) { - Leg* l = new Leg{this, Waypt::createFromProperties(this, wpNode)}; + auto wp = Waypt::createFromProperties(this, wpNode); + LegRef l = new Leg{this, wp}; + // sync leg restrictions with waypoint ones + if (wp->speedRestriction() != RESTRICT_NONE) { + l->setSpeed(wp->speedRestriction(), wp->speed()); + } + + if (wp->altitudeRestriction() != RESTRICT_NONE) { + l->setAltitude(wp->altitudeRestriction(), wp->altitudeFt()); + } + + if (wpNode->hasChild("hold-count")) { + l->setHoldCount(wpNode->getIntValue("hold-count")); + } _legs.push_back(l); } // of route iteration } @@ -1135,7 +1129,7 @@ void FlightPlan::loadVersion1XMLRoute(SGPropertyNode_ptr routeData) SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); for (int i=0; inChildren(); ++i) { SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i); - Leg* l = new Leg(this, parseVersion1XMLWaypt(wpNode)); + LegRef l = new Leg(this, parseVersion1XMLWaypt(wpNode)); _legs.push_back(l); } // of route iteration _waypointsChanged = true; @@ -1218,7 +1212,7 @@ bool FlightPlan::loadPlainTextFormat(const SGPath& path) throw sg_io_exception("Failed to create waypoint from line '" + line + "'."); } - _legs.push_back(new Leg(this, w)); + _legs.push_back(LegRef{new Leg(this, w)}); } // of line iteration } catch (sg_exception& e) { SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load route from: '" << path << "'. " << e.getMessage()); @@ -1425,26 +1419,23 @@ void FlightPlan::activate() _currentIndex = 0; _currentWaypointChanged = true; - for (unsigned int i=0; i < _legs.size(); ) { + for (unsigned int i=1; i < _legs.size(); ) { if (_legs[i]->waypoint()->type() == "via") { WayptRef preceeding = _legs[i - 1]->waypoint(); Via* via = static_cast(_legs[i]->waypoint()); WayptVec wps = via->expandToWaypoints(preceeding); // delete the VIA leg - LegVec::iterator it = _legs.begin(); - it += i; - Leg* l = *it; + auto it = _legs.begin() + i; + LegRef l = *it; _legs.erase(it); - delete l; // create new legs and insert - it = _legs.begin(); - it += i; + it = _legs.begin() + i; LegVec newLegs; for (WayptRef wp : wps) { - newLegs.push_back(new Leg(this, wp)); + newLegs.push_back(LegRef{new Leg(this, wp)}); } _waypointsChanged = true; @@ -1630,6 +1621,28 @@ int FlightPlan::Leg::holdCount() const return _holdCount; } +void FlightPlan::Leg::writeToProperties(SGPropertyNode* aProp) const +{ + if (_speedRestrict != RESTRICT_NONE) { + aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict)); + if (_speedRestrict == SPEED_RESTRICT_MACH) { + aProp->setDoubleValue("speed", speedMach()); + } else { + aProp->setDoubleValue("speed", _speed); + } + } + + if (_altRestrict != RESTRICT_NONE) { + aProp->setStringValue("alt-restrict", restrictionToString(_altRestrict)); + aProp->setDoubleValue("altitude-ft", _altitudeFt); + } + + if (_holdCount > 0) { + aProp->setDoubleValue("hold-count", _holdCount); + } +} + + void FlightPlan::rebuildLegData() { _totalDistance = 0.0; diff --git a/src/Navaids/FlightPlan.hxx b/src/Navaids/FlightPlan.hxx index aa5b145f5..ed2c32acc 100644 --- a/src/Navaids/FlightPlan.hxx +++ b/src/Navaids/FlightPlan.hxx @@ -80,7 +80,7 @@ public: /** * flight-plan leg encapsulation */ - class Leg + class Leg : public SGReferenced { public: FlightPlan* owner() const @@ -138,6 +138,8 @@ public: Leg* cloneFor(FlightPlan* owner) const; + void writeToProperties(SGPropertyNode* node) const; + const FlightPlan* _parent; RouteRestriction _speedRestrict = RESTRICT_NONE, _altRestrict = RESTRICT_NONE; @@ -157,6 +159,8 @@ public: /// total distance of this leg from departure point mutable double _distanceAlongPath = 11.0; }; + + using LegRef = SGSharedPtr; class Delegate { @@ -191,7 +195,7 @@ public: bool _deleteWithPlan = false; }; - Leg* insertWayptAtIndex(Waypt* aWpt, int aIndex); + LegRef insertWayptAtIndex(Waypt* aWpt, int aIndex); void insertWayptsAtIndex(const WayptVec& wps, int aIndex); void deleteIndex(int index); @@ -211,15 +215,14 @@ public: bool isActive() const; - Leg* currentLeg() const; - Leg* nextLeg() const; - Leg* previousLeg() const; + LegRef currentLeg() const; + LegRef nextLeg() const; + LegRef previousLeg() const; int numLegs() const { return static_cast(_legs.size()); } - Leg* legAtIndex(int index) const; - int findLegIndex(const Leg* l) const; + LegRef legAtIndex(int index) const; int findWayptIndex(const SGGeod& aPos) const; int findWayptIndex(const FGPositionedRef aPos) const; @@ -373,6 +376,8 @@ public: private: friend class Leg; + int findLegIndex(const Leg* l) const; + void lockDelegates(); void unlockDelegates(); @@ -426,7 +431,7 @@ private: double _totalDistance; void rebuildLegData(); - typedef std::vector LegVec; + using LegVec = std::vector; LegVec _legs; std::vector _delegates; diff --git a/src/Navaids/route.cxx b/src/Navaids/route.cxx index a61f9a7f2..d2973b52b 100644 --- a/src/Navaids/route.cxx +++ b/src/Navaids/route.cxx @@ -175,7 +175,7 @@ static RouteRestriction restrictionFromString(const char* aStr) "Route restrictFromString"); } -static const char* restrictionToString(RouteRestriction aRestrict) +const char* restrictionToString(RouteRestriction aRestrict) { switch (aRestrict) { case RESTRICT_AT: return "at"; diff --git a/src/Scripting/NasalPositioned.cxx b/src/Scripting/NasalPositioned.cxx index 1760e7b52..1246ad951 100644 --- a/src/Scripting/NasalPositioned.cxx +++ b/src/Scripting/NasalPositioned.cxx @@ -235,7 +235,9 @@ static void wayptGhostDestroy(void* g) static void legGhostDestroy(void* g) { - // nothing for now + FlightPlan::Leg* leg = (FlightPlan::Leg*) g; + if (!FlightPlan::Leg::put(leg)) // unref + delete leg; } @@ -382,6 +384,7 @@ naRef ghostForLeg(naContext c, const FlightPlan::Leg* leg) return naNil(); } + FlightPlan::Leg::get(leg); // take a ref return naNewGhost2(c, &FPLegGhostType, (void*) leg); } @@ -3143,7 +3146,7 @@ static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef* args) static naRef f_leg_path(naContext c, naRef me, int argc, naRef* args) { FlightPlan::Leg* leg = fpLegGhost(me); - if (!leg) { + if (!leg || !leg->owner()) { naRuntimeError(c, "leg.setAltitude called on non-flightplan-leg object"); } @@ -3165,7 +3168,7 @@ static naRef f_leg_path(naContext c, naRef me, int argc, naRef* args) static naRef f_leg_courseAndDistanceFrom(naContext c, naRef me, int argc, naRef* args) { FlightPlan::Leg* leg = fpLegGhost(me); - if (!leg) { + if (!leg || !leg->owner()) { naRuntimeError(c, "leg.courseAndDistanceFrom called on non-flightplan-leg object"); } diff --git a/test_suite/unit_tests/Navaids/CMakeLists.txt b/test_suite/unit_tests/Navaids/CMakeLists.txt index bc0bfc66d..cc6ec4d0c 100644 --- a/test_suite/unit_tests/Navaids/CMakeLists.txt +++ b/test_suite/unit_tests/Navaids/CMakeLists.txt @@ -2,6 +2,7 @@ set(TESTSUITE_SOURCES ${TESTSUITE_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx ${CMAKE_CURRENT_SOURCE_DIR}/test_flightplan.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/test_fpNasal.cxx ${CMAKE_CURRENT_SOURCE_DIR}/test_navaids2.cxx ${CMAKE_CURRENT_SOURCE_DIR}/test_aircraftPerformance.cxx ${CMAKE_CURRENT_SOURCE_DIR}/test_routeManager.cxx @@ -11,6 +12,7 @@ set(TESTSUITE_SOURCES set(TESTSUITE_HEADERS ${TESTSUITE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/test_flightplan.hxx + ${CMAKE_CURRENT_SOURCE_DIR}/test_fpNasal.hxx ${CMAKE_CURRENT_SOURCE_DIR}/test_aircraftPerformance.hxx ${CMAKE_CURRENT_SOURCE_DIR}/test_routeManager.hxx PARENT_SCOPE diff --git a/test_suite/unit_tests/Navaids/TestSuite.cxx b/test_suite/unit_tests/Navaids/TestSuite.cxx index 9e2af8918..7ae0e8ed2 100644 --- a/test_suite/unit_tests/Navaids/TestSuite.cxx +++ b/test_suite/unit_tests/Navaids/TestSuite.cxx @@ -21,9 +21,11 @@ #include "test_navaids2.hxx" #include "test_aircraftPerformance.hxx" #include "test_routeManager.hxx" +#include "test_fpNasal.hxx" // Set up the unit tests. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(FlightplanTests, "Unit tests"); +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(FPNasalTests, "Unit tests"); CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(NavaidsTests, "Unit tests"); CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AircraftPerformanceTests, "Unit tests"); CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(RouteManagerTests, "Unit tests"); diff --git a/test_suite/unit_tests/Navaids/test_flightplan.cxx b/test_suite/unit_tests/Navaids/test_flightplan.cxx index fa41e6a0e..fe5fbd7be 100644 --- a/test_suite/unit_tests/Navaids/test_flightplan.cxx +++ b/test_suite/unit_tests/Navaids/test_flightplan.cxx @@ -25,12 +25,9 @@ void FlightplanTests::setUp() FGTestApi::setUp::initTestGlobals("flightplan"); FGTestApi::setUp::initNavDataCache(); - globals->get_subsystem_mgr()->init(); - - FGTestApi::setUp::initStandardNasal(); - - globals->get_subsystem_mgr()->postinit(); globals->get_subsystem_mgr()->bind(); + globals->get_subsystem_mgr()->init(); + globals->get_subsystem_mgr()->postinit(); } @@ -61,7 +58,7 @@ void FlightplanTests::testBasic() CPPUNIT_ASSERT(fp1->destinationAirport()->ident() == "EHAM"); CPPUNIT_ASSERT(fp1->destinationRunway()->ident() == "24"); - CPPUNIT_ASSERT_EQUAL(fp1->numLegs(), 5); + CPPUNIT_ASSERT_EQUAL(5, fp1->numLegs()); CPPUNIT_ASSERT(fp1->legAtIndex(0)->waypoint()->source()->ident() == "23L"); @@ -328,15 +325,72 @@ void FlightplanTests::testBug1814() CPPUNIT_ASSERT_DOUBLES_EQUAL(101, f->legAtIndex(2)->distanceNm(), 0.5); } -void FlightplanTests::testSegfaultWaypointGhost() { - // checking for a segfault here, no segfault indicates success. A runtime error in the log is acceptable here. - bool ok = FGTestApi::executeNasal(R"( - var fp = createFlightplan(); - fp.departure = airportinfo("BIKF"); - fp.destination = airportinfo("EGLL"); - var wp = fp.getWP(1); - fp.deleteWP(1); - print(wp.wp_name); - )"); +void FlightplanTests::testLoadSaveMachRestriction() +{ + const std::string fpXML = R"( + + 2 + + SAWG + 25 + + + SUMU + + + + navaid + PUGLI + -60.552200 + -40.490000 + + + basic + at + 36000 + mach + 1.24 + SV002 + -115.50531 + 37.89523 + + + navaid + SIGUL + -60.552200 + -40.490000 + + + + )"; + + std::istringstream stream(fpXML); + FlightPlanRef f = new FlightPlan; + bool ok = f->load(stream); + CPPUNIT_ASSERT(ok); + + auto leg = f->legAtIndex(1); + CPPUNIT_ASSERT_EQUAL(SPEED_RESTRICT_MACH, leg->speedRestriction()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.24, leg->speedMach(), 0.01); + + auto firstLeg = f->legAtIndex(0); + firstLeg->setSpeed(SPEED_RESTRICT_MACH, 1.56); + + // upgrade to a hold and set the count + f->legAtIndex(2)->setHoldCount(8); + + // round trip through XML to check :) + std::ostringstream ss; + f->save(ss); + + std::istringstream iss(ss.str()); + FlightPlanRef f2 = new FlightPlan; + ok = f2->load(iss); CPPUNIT_ASSERT(ok); + + auto leg3 = f2->legAtIndex(0); + CPPUNIT_ASSERT_EQUAL(SPEED_RESTRICT_MACH, leg3->speedRestriction()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.56, leg3->speedMach(), 0.01); + + CPPUNIT_ASSERT_EQUAL(8, f2->legAtIndex(2)->holdCount()); } diff --git a/test_suite/unit_tests/Navaids/test_flightplan.hxx b/test_suite/unit_tests/Navaids/test_flightplan.hxx index d73bd955e..1be0b5209 100644 --- a/test_suite/unit_tests/Navaids/test_flightplan.hxx +++ b/test_suite/unit_tests/Navaids/test_flightplan.hxx @@ -38,10 +38,10 @@ class FlightplanTests : public CppUnit::TestFixture CPPUNIT_TEST(testBasicAirways); CPPUNIT_TEST(testAirwayNetworkRoute); CPPUNIT_TEST(testBug1814); - CPPUNIT_TEST(testSegfaultWaypointGhost); CPPUNIT_TEST(testRoutPathWpt0Midflight); CPPUNIT_TEST(testRoutePathVec); - + CPPUNIT_TEST(testLoadSaveMachRestriction); + // CPPUNIT_TEST(testParseICAORoute); // CPPUNIT_TEST(testParseICANLowLevelRoute); CPPUNIT_TEST_SUITE_END(); @@ -63,9 +63,9 @@ public: void testParseICAORoute(); void testParseICANLowLevelRoute(); void testBug1814(); - void testSegfaultWaypointGhost(); void testRoutPathWpt0Midflight(); void testRoutePathVec(); + void testLoadSaveMachRestriction(); }; #endif // FG_FLIGHTPLAN_UNIT_TESTS_HXX diff --git a/test_suite/unit_tests/Navaids/test_fpNasal.cxx b/test_suite/unit_tests/Navaids/test_fpNasal.cxx new file mode 100644 index 000000000..bd702e7de --- /dev/null +++ b/test_suite/unit_tests/Navaids/test_fpNasal.cxx @@ -0,0 +1,105 @@ +#include "test_fpNasal.hxx" + +#include "test_suite/FGTestApi/testGlobals.hxx" +#include "test_suite/FGTestApi/NavDataCache.hxx" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace flightgear; + + +// Set up function for each test. +void FPNasalTests::setUp() +{ + FGTestApi::setUp::initTestGlobals("flightplan"); + FGTestApi::setUp::initNavDataCache(); + + // flightplan() acces needs the route manager + globals->add_new_subsystem(); + + globals->get_subsystem_mgr()->bind(); + globals->get_subsystem_mgr()->init(); + + FGTestApi::setUp::initStandardNasal(); + globals->get_subsystem_mgr()->postinit(); +} + + +// Clean up after each test. +void FPNasalTests::tearDown() +{ + FGTestApi::tearDown::shutdownTestGlobals(); +} + +static FlightPlanRef makeTestFP(const std::string& depICAO, const std::string& depRunway, + const std::string& destICAO, const std::string& destRunway, + const std::string& waypoints) +{ + FlightPlanRef f = new FlightPlan; + FGTestApi::setUp::populateFPWithNasal(f, depICAO, depRunway, destICAO, destRunway, waypoints); + return f; +} + +void FPNasalTests::testBasic() +{ + + FlightPlanRef fp1 = makeTestFP("EGCC", "23L", "EHAM", "24", + "TNT CLN"); + fp1->setIdent("testplan"); + +// setup the FP on the route-manager, so flightplan() call works + auto rm = globals->get_subsystem(); + rm->setFlightPlan(fp1); + rm->activate(); + + // modify leg data dfrom Nasal + bool ok = FGTestApi::executeNasal(R"( + var fp = flightplan(); # retrieve the global flightplan + var leg = fp.getWP(3); + leg.setAltitude(6000, 'AT'); + )"); + CPPUNIT_ASSERT(ok); + + // check the value updated in the leg + CPPUNIT_ASSERT_EQUAL(RESTRICT_AT, fp1->legAtIndex(3)->altitudeRestriction()); + CPPUNIT_ASSERT_EQUAL(6000, fp1->legAtIndex(3)->altitudeFt()); + +// insert some waypoints from Nasal + + ok = FGTestApi::executeNasal(R"( + var fp = flightplan(); + var leg = fp.getWP(2); + var newWP = createWPFrom(navinfo(leg.lat, leg.lon, 'COA')[0]); + fp.insertWPAfter(newWP, 2); + )"); + CPPUNIT_ASSERT(ok); + + CPPUNIT_ASSERT_EQUAL(string{"COSTA VOR-DME"}, fp1->legAtIndex(3)->waypoint()->source()->name()); +} + + +void FPNasalTests::testSegfaultWaypointGhost() +{ + // checking for a segfault here, no segfault indicates success. A runtime error in the log is acceptable here. + bool ok = FGTestApi::executeNasal(R"( + var fp = createFlightplan(); + fp.departure = airportinfo("BIKF"); + fp.destination = airportinfo("EGLL"); + var wp = fp.getWP(1); + fp.deleteWP(1); + print(wp.wp_name); + )"); + CPPUNIT_ASSERT(ok); +} diff --git a/test_suite/unit_tests/Navaids/test_fpNasal.hxx b/test_suite/unit_tests/Navaids/test_fpNasal.hxx new file mode 100644 index 000000000..5a8bddc6c --- /dev/null +++ b/test_suite/unit_tests/Navaids/test_fpNasal.hxx @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 Edward d'Auvergne + * + * This file is part of the program FlightGear. + * + * 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, see . + */ + + +#pragma once + +#include +#include + + +// The flight plan unit tests. +class FPNasalTests : public CppUnit::TestFixture +{ + // Set up the test suite. + CPPUNIT_TEST_SUITE(FPNasalTests); + CPPUNIT_TEST(testBasic); + CPPUNIT_TEST(testSegfaultWaypointGhost); + CPPUNIT_TEST_SUITE_END(); + +public: + // Set up function for each test. + void setUp(); + + // Clean up after each test. + void tearDown(); + + // The tests. + void testBasic(); + void testSegfaultWaypointGhost(); +}; From cab6581d3df0cf0d44df2191fa54da62fb4093b6 Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Thu, 16 Jan 2020 09:33:53 +0100 Subject: [PATCH 34/98] [swift] Pointers to nodes for get/set data Calling fgSetXY/fgGetXY takes to long (especially when calling the often needed properties a few times per second in main loop). Switching to pointers for these nodes, initialized within service-class constructor. --- src/Network/Swift/service.cpp | 120 ++++++++++++++++++++++------------ src/Network/Swift/service.h | 38 +++++++++++ 2 files changed, 116 insertions(+), 42 deletions(-) diff --git a/src/Network/Swift/service.cpp b/src/Network/Swift/service.cpp index 1adb6b17c..8b1f28b50 100644 --- a/src/Network/Swift/service.cpp +++ b/src/Network/Swift/service.cpp @@ -35,6 +35,40 @@ namespace FGSwiftBus { CService::CService() { + // Initialize node pointers + versionNode = fgGetNode("/sim/version/flightgear"); + textMessageNode = fgGetNode("/sim/message/copilot"); + aircraftModelPathNode = fgGetNode("/sim/aircraft-dir"); + aircraftDescriptionNode = fgGetNode("/sim/description"); + isPausedNode = fgGetNode("/sim/freeze/master"); + latitudeNode = fgGetNode("/position/latitude-deg"); + longitudeNode = fgGetNode("/position/longitude-deg"); + altitudeMSLNode = fgGetNode("/position/altitude-ft"); + heightAGLNode = fgGetNode("/position/altitude-agl-ft"); + groundSpeedNode = fgGetNode("/velocities/groundspeed-kt"); + pitchNode = fgGetNode("/orientation/pitch-deg"); + rollNode = fgGetNode("/orientation/roll-deg"); + trueHeadingNode = fgGetNode("/orientation/heading-deg"); + wheelsOnGroundNode = fgGetNode("/gear/gear/wow"); + com1ActiveNode = fgGetNode("/instrumentation/comm/frequencies/selected-mhz"); + com1StandbyNode = fgGetNode("/instrumentation/comm/frequencies/standby-mhz"); + com2ActiveNode = fgGetNode("/instrumentation/comm[1]/frequencies/selected-mhz"); + com2StandbyNode = fgGetNode("/instrumentation/comm[1]/frequencies/standby-mhz"); + transponderCodeNode = fgGetNode("/instrumentation/transponder/id-code"); + transponderModeNode = fgGetNode("/instrumentation/transponder/inputs/knob-mode"); + transponderIdentNode = fgGetNode("/instrumentation/transponder/ident"); + beaconLightsNode = fgGetNode("/controls/lighting/beacon"); + landingLightsNode = fgGetNode("/controls/lighting/landing-lights"); + navLightsNode = fgGetNode("/controls/lighting/nav-lights"); + strobeLightsNode = fgGetNode("/controls/lighting/strobe"); + taxiLightsNode = fgGetNode("/controls/lighting/taxi-light"); + altimeterServiceableNode = fgGetNode("/instrumentation/altimeter/serviceable"); + pressAltitudeFtNode = fgGetNode("/instrumentation/altimeter/pressure-alt-ft"); + flapsDeployRatioNode = fgGetNode("/surface-positions/flap-pos-norm"); + gearDeployRatioNode = fgGetNode("/gear/gear/position-norm"); + speedBrakeDeployRatioNode = fgGetNode("/surface-positions/speedbrake-pos-norm"); + aircraftNameNode = fgGetNode("/sim/aircraft"); + SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus Service initialized"); } @@ -58,12 +92,12 @@ std::string CService::getVersionNumber() void CService::addTextMessage(const std::string& text) { if (text.empty()) { return; } - fgSetString("/sim/messages/copilot", text); + textMessageNode->setStringValue(text); } std::string CService::getAircraftModelPath() const { - return fgGetString("/sim/aircraft-dir"); + return aircraftModelPathNode->getStringValue(); } std::string CService::getAircraftLivery() const @@ -78,176 +112,178 @@ std::string CService::getAircraftIcaoCode() const std::string CService::getAircraftDescription() const { - return fgGetString("/sim/description"); + return aircraftDescriptionNode->getStringValue(); } bool CService::isPaused() const { - return fgGetBool("/sim/freeze/master"); + return isPausedNode->getBoolValue(); } double CService::getLatitude() const { - return fgGetDouble("/position/latitude-deg"); + return latitudeNode->getDoubleValue(); } double CService::getLongitude() const { - return fgGetDouble("/position/longitude-deg"); + return longitudeNode->getDoubleValue(); } double CService::getAltitudeMSL() const { - return fgGetDouble("/position/altitude-ft"); + return altitudeMSLNode->getDoubleValue(); } double CService::getHeightAGL() const { - return fgGetDouble("/position/altitude-agl-ft"); + return heightAGLNode->getDoubleValue(); } double CService::getGroundSpeed() const { - return fgGetDouble("/velocities/groundspeed-kt"); + return groundSpeedNode->getDoubleValue(); } double CService::getPitch() const { - return fgGetDouble("/orientation/pitch-deg"); + return pitchNode->getDoubleValue(); } double CService::getRoll() const { - return fgGetDouble("/orientation/roll-deg"); + return rollNode->getDoubleValue(); } double CService::getTrueHeading() const { - return fgGetDouble("/orientation/heading-deg"); + return trueHeadingNode->getDoubleValue(); } bool CService::getAllWheelsOnGround() const { - return fgGetBool("/gear/gear/wow"); + return wheelsOnGroundNode->getBoolValue(); } int CService::getCom1Active() const { - return fgGetDouble("/instrumentation/comm/frequencies/selected-mhz") * 1000; + return com1ActiveNode->getDoubleValue() * 1000; } int CService::getCom1Standby() const { - return fgGetDouble("/instrumentation/comm/frequencies/standby-mhz") * 1000; + return com1StandbyNode->getDoubleValue() * 1000; } int CService::getCom2Active() const { - return fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz") * 1000; + return com2ActiveNode->getDoubleValue() * 1000; } int CService::getCom2Standby() const { - return fgGetDouble("/instrumentation/comm[1]/frequencies/standby-mhz") * 1000; + return com2StandbyNode->getDoubleValue() * 1000; } int CService::getTransponderCode() const { - return fgGetInt("/instrumentation/transponder/id-code"); + return transponderCodeNode->getIntValue(); } int CService::getTransponderMode() const { - return fgGetInt("/instrumentation/transponder/inputs/knob-mode"); + return transponderModeNode->getIntValue(); } bool CService::getTransponderIdent() const { - return fgGetBool("/instrumentation/transponder/ident"); + return transponderIdentNode->getBoolValue(); } bool CService::getBeaconLightsOn() const { - return fgGetBool("/controls/lighting/beacon"); + return beaconLightsNode->getBoolValue(); } bool CService::getLandingLightsOn() const { - return fgGetBool("/controls/lighting/landing-lights"); + return landingLightsNode->getBoolValue(); } bool CService::getNavLightsOn() const { - return fgGetBool("/controls/lighting/nav-lights"); + return navLightsNode->getBoolValue(); } bool CService::getStrobeLightsOn() const { - return fgGetBool("/controls/lighting/strobe"); + return strobeLightsNode->getBoolValue(); } bool CService::getTaxiLightsOn() const { - return fgGetBool("/controls/lighting/taxi-light"); + return taxiLightsNode->getBoolValue(); } double CService::getPressAlt() const { - if (fgGetBool("/instrumentation/altimeter/serviceable")){ - return fgGetDouble("/instrumentation/altimeter/pressure-alt-ft"); + if (altimeterServiceableNode->getBoolValue()){ + return pressAltitudeFtNode->getDoubleValue(); } else { - return fgGetDouble("/position/altitude-ft"); + return altitudeMSLNode->getDoubleValue(); } } void CService::setCom1Active(int freq) { - fgSetDouble("/instrumentation/comm/frequencies/selected-mhz", freq / (double)1000); + com1ActiveNode->setDoubleValue(freq /(double)1000); } void CService::setCom1Standby(int freq) -{ - fgSetDouble("/instrumentation/comm/frequencies/standby-mhz", freq / (double)1000); +{ + com1StandbyNode->setDoubleValue(freq /(double)1000); } void CService::setCom2Active(int freq) { - fgSetDouble("/instrumentation/comm[1]/frequencies/selected-mhz", freq / (double)1000); + com2ActiveNode->setDoubleValue(freq /(double)1000); } void CService::setCom2Standby(int freq) { - fgSetDouble("/instrumentation/comm[1]/frequencies/standby-mhz", freq / (double)1000); + com2StandbyNode->setDoubleValue(freq /(double)1000); } void CService::setTransponderCode(int code) { - fgSetInt("/instrumentation/transponder/id-code", code); + transponderCodeNode->setIntValue(code); } void CService::setTransponderMode(int mode) { - fgSetInt("/instrumentation/transponder/inputs/knob-mode", mode); + transponderModeNode->setIntValue(mode); } double CService::getFlapsDeployRatio() const { - return fgGetFloat("/surface-positions/flap-pos-norm"); + return flapsDeployRatioNode->getFloatValue(); } double CService::getGearDeployRatio() const { - return fgGetFloat("/gear/gear/position-norm"); + return gearDeployRatioNode->getFloatValue(); } int CService::getNumberOfEngines() const -{ +{ + // TODO Use correct property return 2; } std::vector CService::getEngineN1Percentage() const { + // TODO use correct engine numbers std::vector list; const auto number = static_cast(getNumberOfEngines()); list.reserve(number); @@ -259,26 +295,26 @@ std::vector CService::getEngineN1Percentage() const double CService::getSpeedBrakeRatio() const { - return fgGetFloat("/surface-positions/speedbrake-pos-norm"); + return speedBrakeDeployRatioNode->getFloatValue(); } std::string CService::getAircraftModelFilename() const { - std::string modelFileName = fgGetString("/sim/aircraft"); + std::string modelFileName = getAircraftName(); modelFileName.append("-set.xml"); return modelFileName; } std::string CService::getAircraftModelString() const { - std::string modelName = fgGetString("/sim/aircraft"); + std::string modelName = getAircraftName(); std::string modelString = "FG " + modelName; return modelString; } std::string CService::getAircraftName() const { - return fgGetString("/sim/aircraft"); + return aircraftNameNode->getStringValue(); } static const char* introspection_service = DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE; diff --git a/src/Network/Swift/service.h b/src/Network/Swift/service.h index a8747e810..1d7e9df02 100644 --- a/src/Network/Swift/service.h +++ b/src/Network/Swift/service.h @@ -199,6 +199,44 @@ public: protected: DBusHandlerResult dbusMessageHandler(const CDBusMessage& message) override; +private: + SGPropertyNode* versionNode; + SGPropertyNode* textMessageNode; + SGPropertyNode* aircraftModelPathNode; + //SGPropertyNode* aircraftLiveryNode; + //SGPropertyNode* aircraftIcaoCodeNode; + SGPropertyNode* aircraftDescriptionNode; + SGPropertyNode* isPausedNode; + SGPropertyNode* latitudeNode; + SGPropertyNode* longitudeNode; + SGPropertyNode* altitudeMSLNode; + SGPropertyNode* heightAGLNode; + SGPropertyNode* groundSpeedNode; + SGPropertyNode* pitchNode; + SGPropertyNode* rollNode; + SGPropertyNode* trueHeadingNode; + SGPropertyNode* wheelsOnGroundNode; + SGPropertyNode* com1ActiveNode; + SGPropertyNode* com1StandbyNode; + SGPropertyNode* com2ActiveNode; + SGPropertyNode* com2StandbyNode; + SGPropertyNode* transponderCodeNode; + SGPropertyNode* transponderModeNode; + SGPropertyNode* transponderIdentNode; + SGPropertyNode* beaconLightsNode; + SGPropertyNode* landingLightsNode; + SGPropertyNode* navLightsNode; + SGPropertyNode* strobeLightsNode; + SGPropertyNode* taxiLightsNode; + SGPropertyNode* altimeterServiceableNode; + SGPropertyNode* pressAltitudeFtNode; + SGPropertyNode* flapsDeployRatioNode; + SGPropertyNode* gearDeployRatioNode; + SGPropertyNode* speedBrakeDeployRatioNode; + //SGPropertyNode* numberEnginesNode; + //SGPropertyNode* engineN1PercentageNode; + SGPropertyNode* aircraftNameNode; + }; } // namespace FGSwiftBus From 0423472f7459eee8eac1f4950c1f53826eb55260 Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Sun, 19 Jan 2020 19:18:48 +0100 Subject: [PATCH 35/98] [swift] Sending ground elevation --- src/Network/Swift/service.cpp | 10 ++++++++++ src/Network/Swift/service.h | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/src/Network/Swift/service.cpp b/src/Network/Swift/service.cpp index 8b1f28b50..0b8ee3ca9 100644 --- a/src/Network/Swift/service.cpp +++ b/src/Network/Swift/service.cpp @@ -68,6 +68,7 @@ CService::CService() gearDeployRatioNode = fgGetNode("/gear/gear/position-norm"); speedBrakeDeployRatioNode = fgGetNode("/surface-positions/speedbrake-pos-norm"); aircraftNameNode = fgGetNode("/sim/aircraft"); + groundElevation = fgGetNode("/position/ground-elev-m"); SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus Service initialized"); } @@ -298,6 +299,11 @@ double CService::getSpeedBrakeRatio() const return speedBrakeDeployRatioNode->getFloatValue(); } +double CService::getGroundElevation() const +{ + return groundElevation->getDoubleValue(); +} + std::string CService::getAircraftModelFilename() const { std::string modelFileName = getAircraftName(); @@ -482,6 +488,10 @@ DBusHandlerResult CService::dbusMessageHandler(const CDBusMessage& message_) queueDBusCall([=]() { sendDBusReply(sender, serial, getPressAlt()); }); + } else if (message.getMethodName() == "getGroundElevation") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getGroundElevation()); + }); } else if (message.getMethodName() == "setCom1ActiveKhz") { maybeSendEmptyDBusReply(wantsReply, sender, serial); int frequency = 0; diff --git a/src/Network/Swift/service.h b/src/Network/Swift/service.h index 1d7e9df02..eb0d71e6c 100644 --- a/src/Network/Swift/service.h +++ b/src/Network/Swift/service.h @@ -193,6 +193,9 @@ public: //! Get the ratio how much the speedbrakes surfaces are extended (0.0 is fully retracted, and 1.0 is fully extended) double getSpeedBrakeRatio() const; + //! Get ground elevation at aircraft current position + double getGroundElevation() const; + //! Perform generic processing int process(); @@ -233,6 +236,7 @@ private: SGPropertyNode* flapsDeployRatioNode; SGPropertyNode* gearDeployRatioNode; SGPropertyNode* speedBrakeDeployRatioNode; + SGPropertyNode* groundElevation; //SGPropertyNode* numberEnginesNode; //SGPropertyNode* engineN1PercentageNode; SGPropertyNode* aircraftNameNode; From fc0a8b1c606d8effca4443a2d78994ce5da68100 Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Thu, 23 Jan 2020 21:09:19 +0100 Subject: [PATCH 36/98] [swift] Fix message node --- src/Network/Swift/service.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Network/Swift/service.cpp b/src/Network/Swift/service.cpp index 0b8ee3ca9..224527ed3 100644 --- a/src/Network/Swift/service.cpp +++ b/src/Network/Swift/service.cpp @@ -37,7 +37,7 @@ CService::CService() { // Initialize node pointers versionNode = fgGetNode("/sim/version/flightgear"); - textMessageNode = fgGetNode("/sim/message/copilot"); + textMessageNode = fgGetNode("/sim/messages/copilot"); aircraftModelPathNode = fgGetNode("/sim/aircraft-dir"); aircraftDescriptionNode = fgGetNode("/sim/description"); isPausedNode = fgGetNode("/sim/freeze/master"); From e6a9515b5968a9eb2c922c7cb2843abf6aee225d Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Sun, 26 Jan 2020 21:08:28 +0100 Subject: [PATCH 37/98] [swift] Enhancing version compatibility check --- src/Network/Swift/service.cpp | 7 ++++--- src/Network/Swift/service.h | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Network/Swift/service.cpp b/src/Network/Swift/service.cpp index 224527ed3..61b257f80 100644 --- a/src/Network/Swift/service.cpp +++ b/src/Network/Swift/service.cpp @@ -31,12 +31,13 @@ #include #include +#define FGSWIFTBUS_API_VERSION 1; + namespace FGSwiftBus { CService::CService() { // Initialize node pointers - versionNode = fgGetNode("/sim/version/flightgear"); textMessageNode = fgGetNode("/sim/messages/copilot"); aircraftModelPathNode = fgGetNode("/sim/aircraft-dir"); aircraftDescriptionNode = fgGetNode("/sim/description"); @@ -85,9 +86,9 @@ const std::string& CService::ObjectPath() } // Static method -std::string CService::getVersionNumber() +int CService::getVersionNumber() { - return fgGetString("/sim/version/flightgear"); + return FGSWIFTBUS_API_VERSION; } void CService::addTextMessage(const std::string& text) diff --git a/src/Network/Swift/service.h b/src/Network/Swift/service.h index eb0d71e6c..991608f4e 100644 --- a/src/Network/Swift/service.h +++ b/src/Network/Swift/service.h @@ -65,7 +65,7 @@ public: static const std::string& ObjectPath(); //! Getting flightgear version - static std::string getVersionNumber(); + static int getVersionNumber(); ////! Add a text message to the on-screen display, with RGB components in the range [0,1] void addTextMessage(const std::string& text); @@ -203,7 +203,6 @@ protected: DBusHandlerResult dbusMessageHandler(const CDBusMessage& message) override; private: - SGPropertyNode* versionNode; SGPropertyNode* textMessageNode; SGPropertyNode* aircraftModelPathNode; //SGPropertyNode* aircraftLiveryNode; From d14073cd24a70e03defac7dfaf0901ad36ba15b3 Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Mon, 27 Jan 2020 22:27:22 +0100 Subject: [PATCH 38/98] [swift] Adding aircraft to property tree and map --- src/GUI/MapWidget.cxx | 2 +- src/Network/Swift/SwiftAircraft.cpp | 28 ++++++++++++++++++++-- src/Network/Swift/SwiftAircraft.h | 5 ++-- src/Network/Swift/SwiftAircraftManager.cpp | 19 ++++++++++++--- src/Network/Swift/SwiftAircraftManager.h | 2 +- src/Network/Swift/traffic.cpp | 4 +++- 6 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/GUI/MapWidget.cxx b/src/GUI/MapWidget.cxx index b73bee8a8..f0d4b2848 100644 --- a/src/GUI/MapWidget.cxx +++ b/src/GUI/MapWidget.cxx @@ -2002,7 +2002,7 @@ MapWidget::DrawAIObject::DrawAIObject(SGPropertyNode* m, const SGGeod& g) : heading = model->getDoubleValue("orientation/true-heading-deg"); if ((name == "aircraft") || (name == "multiplayer") || - (name == "wingman") || (name == "tanker")) + (name == "wingman") || (name == "tanker") || (name == "swift")) { speedKts = static_cast(model->getDoubleValue("velocities/true-airspeed-kt")); label = model->getStringValue("callsign", "<>"); diff --git a/src/Network/Swift/SwiftAircraft.cpp b/src/Network/Swift/SwiftAircraft.cpp index f4bf52140..d14f90fa6 100644 --- a/src/Network/Swift/SwiftAircraft.cpp +++ b/src/Network/Swift/SwiftAircraft.cpp @@ -42,7 +42,7 @@ #include #include -FGSwiftAircraft::FGSwiftAircraft(std::string callsign, std::string modelpath) +FGSwiftAircraft::FGSwiftAircraft(std::string callsign, std::string modelpath, SGPropertyNode* p) { using namespace simgear; _model = SGModelLib::loadModel(modelpath); @@ -52,10 +52,14 @@ FGSwiftAircraft::FGSwiftAircraft(std::string callsign, std::string modelpath) aip.setVisible(true); aip.update(); globals->get_scenery()->get_models_branch()->addChild(aip.getSceneGraph()); + + props = p; + props->setStringValue("callsign", callsign); + props->setBoolValue("valid",true); } } -bool FGSwiftAircraft::updatePosition(SGGeod newPosition, SGVec3d orientation) +bool FGSwiftAircraft::updatePosition(SGGeod newPosition, SGVec3d orientation, double groundspeed) { position = newPosition; @@ -64,12 +68,32 @@ bool FGSwiftAircraft::updatePosition(SGGeod newPosition, SGVec3d orientation) aip.setRollDeg(orientation.y()); aip.setHeadingDeg(orientation.z()); aip.update(); + + //Update props + props->setDoubleValue("orientation/pitch-deg", orientation.x()); + props->setDoubleValue("orientation/roll-deg", orientation.y()); + props->setDoubleValue("orientation/true-heading-deg", orientation.z()); + SGVec3d cartPos = SGVec3d::fromGeod(position); + + props->setDoubleValue("position/global-x", cartPos.x()); + props->setDoubleValue("position/global-y", cartPos.y()); + props->setDoubleValue("position/global-z", cartPos.z()); + + + props->setDoubleValue("position/latitude-deg", position.getLatitudeDeg()); + props->setDoubleValue("position/longitude-deg", position.getLongitudeDeg()); + props->setDoubleValue("position/altitude-ft", position.getElevationFt()); + + props->setDoubleValue("velocities/true-airspeed-kt", groundspeed); + return true; } FGSwiftAircraft::~FGSwiftAircraft() { + props->setBoolValue("valid",false); + props->setIntValue("id",-1); aip.setVisible(false); } diff --git a/src/Network/Swift/SwiftAircraft.h b/src/Network/Swift/SwiftAircraft.h index 62949236e..4b71ef83c 100644 --- a/src/Network/Swift/SwiftAircraft.h +++ b/src/Network/Swift/SwiftAircraft.h @@ -41,8 +41,8 @@ class PagedLOD; class FGSwiftAircraft { public: - FGSwiftAircraft(std::string callsign, std::string modelpath); - bool updatePosition(SGGeod newPosition, SGVec3d orientation); + FGSwiftAircraft(std::string callsign, std::string modelpath, SGPropertyNode* p); + bool updatePosition(SGGeod newPosition, SGVec3d orientation, double groundspeed); ~FGSwiftAircraft(); std::string getName() { return _model->getName(); }; double getLatDeg(); @@ -52,6 +52,7 @@ public: private: SGGeod position; + SGPropertyNode* props; osg::ref_ptr _model; SGModelPlacement aip; inline bool operator<(std::string extCallsign); diff --git a/src/Network/Swift/SwiftAircraftManager.cpp b/src/Network/Swift/SwiftAircraftManager.cpp index df8beac97..580f0a9b7 100644 --- a/src/Network/Swift/SwiftAircraftManager.cpp +++ b/src/Network/Swift/SwiftAircraftManager.cpp @@ -19,6 +19,7 @@ #include "SwiftAircraftManager.h" #include "SwiftAircraft.h" +#include
FGSwiftAircraftManager::FGSwiftAircraftManager() { @@ -33,17 +34,29 @@ bool FGSwiftAircraftManager::addPlane(std::string callsign, std::string modelStr if (aircraftByCallsign.find(callsign) != aircraftByCallsign.end()) return false; - FGSwiftAircraft* curAircraft = new FGSwiftAircraft(callsign, modelString); + const char* typeString = "swift"; + SGPropertyNode* root = globals->get_props()->getNode("ai/models",true); + SGPropertyNode* p; + int i; + for(i = 0; i < 10000; i++){ + p = root->getNode(typeString,i,false); + + if(!p || !p->getBoolValue("valid",false)) + break; + } + p = root->getNode(typeString,i,true); + p->setIntValue("id",i); + FGSwiftAircraft* curAircraft = new FGSwiftAircraft(callsign, modelString, p); aircraftByCallsign.insert(std::pair(callsign, curAircraft)); return true; } -void FGSwiftAircraftManager::updatePlanes(std::vector callsigns, std::vector positions, std::vector orientations, std::vector onGrounds) +void FGSwiftAircraftManager::updatePlanes(std::vector callsigns, std::vector positions, std::vector orientations, std::vector groundspeeds, std::vector onGrounds) { for (int i = 0; i < callsigns.size(); i++) { auto it = aircraftByCallsign.find(callsigns.at(i)); if (it != aircraftByCallsign.end()) { - it->second->updatePosition(positions.at(i), orientations.at(i)); + it->second->updatePosition(positions.at(i), orientations.at(i), groundspeeds.at(i)); } } diff --git a/src/Network/Swift/SwiftAircraftManager.h b/src/Network/Swift/SwiftAircraftManager.h index fbdbb4239..9f6991905 100644 --- a/src/Network/Swift/SwiftAircraftManager.h +++ b/src/Network/Swift/SwiftAircraftManager.h @@ -30,7 +30,7 @@ public: ~FGSwiftAircraftManager(); std::map aircraftByCallsign; bool addPlane(std::string callsign, std::string modelString); - void updatePlanes(std::vector callsigns, std::vector positions, std::vector orientations, std::vector onGrounds); + void updatePlanes(std::vector callsigns, std::vector positions, std::vector orientations, std::vector groundspeeds, std::vector onGrounds); void getRemoteAircraftData(std::vector& callsigns, std::vector& latitudesDeg, std::vector& longitudesDeg, std::vector& elevationsM, std::vector& verticalOffsets) const; void removePlane(std::string callsign); diff --git a/src/Network/Swift/traffic.cpp b/src/Network/Swift/traffic.cpp index e5d493ed2..7226bce26 100644 --- a/src/Network/Swift/traffic.cpp +++ b/src/Network/Swift/traffic.cpp @@ -192,6 +192,7 @@ DBusHandlerResult CTraffic::dbusMessageHandler(const CDBusMessage& message_) std::vector pitches; std::vector rolls; std::vector headings; + std::vector groundspeeds; std::vector onGrounds; message.beginArgumentRead(); message.getArgument(callsigns); @@ -201,6 +202,7 @@ DBusHandlerResult CTraffic::dbusMessageHandler(const CDBusMessage& message_) message.getArgument(pitches); message.getArgument(rolls); message.getArgument(headings); + message.getArgument(groundspeeds); message.getArgument(onGrounds); queueDBusCall([=]() { std::vector positions; @@ -215,7 +217,7 @@ DBusHandlerResult CTraffic::dbusMessageHandler(const CDBusMessage& message_) positions.push_back(newPos); orientations.push_back(vec); } - acm->updatePlanes(callsigns, positions, orientations, onGrounds); + acm->updatePlanes(callsigns, positions, orientations, groundspeeds, onGrounds); }); } else if (message.getMethodName() == "getRemoteAircraftData") { std::vector requestedcallsigns; From eb3fba187183013d2fae59169c4f6f8832d32300 Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Tue, 28 Jan 2020 17:51:15 +0100 Subject: [PATCH 39/98] [swift] Adding swift aircrafts to TCAS --- src/Instrumentation/tcas.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Instrumentation/tcas.cxx b/src/Instrumentation/tcas.cxx index 7021fa999..8fffdfb9e 100644 --- a/src/Instrumentation/tcas.cxx +++ b/src/Instrumentation/tcas.cxx @@ -590,7 +590,7 @@ bool TCAS::ThreatDetector::checkTransponder(const SGPropertyNode* pModel, float velocityKt) { const string name = pModel->getName(); - if (name != "multiplayer" && name != "aircraft") + if (name != "multiplayer" && name != "aircraft" && name != "swift") { // assume non-MP/non-AI planes (e.g. ships) have no transponder return false; From ee0d03b4d045d651a9ac34ba59246325f49b8774 Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Wed, 29 Jan 2020 10:21:02 +0100 Subject: [PATCH 40/98] [swift] Fix memory leak in traffic-service --- src/Network/Swift/traffic.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Network/Swift/traffic.cpp b/src/Network/Swift/traffic.cpp index 7226bce26..9a6c3f916 100644 --- a/src/Network/Swift/traffic.cpp +++ b/src/Network/Swift/traffic.cpp @@ -45,6 +45,8 @@ CTraffic::CTraffic() CTraffic::~CTraffic() { cleanup(); + delete acm; + acm = nullptr; SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus Traffic stopped"); } From 182df4e6ae6a137baf6e595db213bceb01f4c112 Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Thu, 30 Jan 2020 09:07:54 +0100 Subject: [PATCH 41/98] [swift] Fix segfault on stopping server --- src/Network/Swift/plugin.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Network/Swift/plugin.cpp b/src/Network/Swift/plugin.cpp index 9ad727020..f269dea6a 100644 --- a/src/Network/Swift/plugin.cpp +++ b/src/Network/Swift/plugin.cpp @@ -44,7 +44,10 @@ CPlugin::CPlugin() CPlugin::~CPlugin() { - m_dbusConnection->close(); + if(m_dbusConnection) + { + m_dbusConnection->close(); + } m_shouldStop = true; if (m_dbusThread.joinable()) { m_dbusThread.join(); } } From f4b60ccd90ce66aa74bdf1de354b64ccbfabfbfb Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Thu, 30 Jan 2020 09:20:44 +0100 Subject: [PATCH 42/98] [swift] Code cleanup --- src/Network/Swift/SwiftAircraft.cpp | 4 ++-- src/Network/Swift/SwiftAircraft.h | 4 ++-- src/Network/Swift/SwiftAircraftManager.cpp | 23 +++++++++++----------- src/Network/Swift/SwiftAircraftManager.h | 4 ++-- src/Network/Swift/service.cpp | 16 ++++----------- src/Network/Swift/swift_connection.cxx | 13 ++++++------ src/Network/Swift/swift_connection.hxx | 2 +- src/Network/Swift/traffic.cpp | 2 +- 8 files changed, 30 insertions(+), 38 deletions(-) diff --git a/src/Network/Swift/SwiftAircraft.cpp b/src/Network/Swift/SwiftAircraft.cpp index d14f90fa6..58f6008bd 100644 --- a/src/Network/Swift/SwiftAircraft.cpp +++ b/src/Network/Swift/SwiftAircraft.cpp @@ -42,7 +42,7 @@ #include #include -FGSwiftAircraft::FGSwiftAircraft(std::string callsign, std::string modelpath, SGPropertyNode* p) +FGSwiftAircraft::FGSwiftAircraft(const std::string& callsign, const std::string& modelpath, SGPropertyNode* p) { using namespace simgear; _model = SGModelLib::loadModel(modelpath); @@ -112,7 +112,7 @@ double FGSwiftAircraft::getFudgeFactor() return 0; } -inline bool FGSwiftAircraft::operator<(std::string extCallsign) +inline bool FGSwiftAircraft::operator<(const std::string& extCallsign) { return _model->getName().compare(extCallsign); } diff --git a/src/Network/Swift/SwiftAircraft.h b/src/Network/Swift/SwiftAircraft.h index 4b71ef83c..45bc8dd3c 100644 --- a/src/Network/Swift/SwiftAircraft.h +++ b/src/Network/Swift/SwiftAircraft.h @@ -41,7 +41,7 @@ class PagedLOD; class FGSwiftAircraft { public: - FGSwiftAircraft(std::string callsign, std::string modelpath, SGPropertyNode* p); + FGSwiftAircraft(const std::string& callsign, const std::string& modelpath, SGPropertyNode* p); bool updatePosition(SGGeod newPosition, SGVec3d orientation, double groundspeed); ~FGSwiftAircraft(); std::string getName() { return _model->getName(); }; @@ -55,6 +55,6 @@ private: SGPropertyNode* props; osg::ref_ptr _model; SGModelPlacement aip; - inline bool operator<(std::string extCallsign); + inline bool operator<(const std::string& extCallsign); }; #endif \ No newline at end of file diff --git a/src/Network/Swift/SwiftAircraftManager.cpp b/src/Network/Swift/SwiftAircraftManager.cpp index 580f0a9b7..edec49eaf 100644 --- a/src/Network/Swift/SwiftAircraftManager.cpp +++ b/src/Network/Swift/SwiftAircraftManager.cpp @@ -20,16 +20,15 @@ #include "SwiftAircraftManager.h" #include "SwiftAircraft.h" #include
+#include FGSwiftAircraftManager::FGSwiftAircraftManager() -{ -} += default; FGSwiftAircraftManager::~FGSwiftAircraftManager() -{ -} += default; -bool FGSwiftAircraftManager::addPlane(std::string callsign, std::string modelString) +bool FGSwiftAircraftManager::addPlane(const std::string& callsign, std::string modelString) { if (aircraftByCallsign.find(callsign) != aircraftByCallsign.end()) return false; @@ -46,14 +45,14 @@ bool FGSwiftAircraftManager::addPlane(std::string callsign, std::string modelStr } p = root->getNode(typeString,i,true); p->setIntValue("id",i); - FGSwiftAircraft* curAircraft = new FGSwiftAircraft(callsign, modelString, p); + auto* curAircraft = new FGSwiftAircraft(callsign, std::move(modelString), p); aircraftByCallsign.insert(std::pair(callsign, curAircraft)); return true; } void FGSwiftAircraftManager::updatePlanes(std::vector callsigns, std::vector positions, std::vector orientations, std::vector groundspeeds, std::vector onGrounds) { - for (int i = 0; i < callsigns.size(); i++) { + for (long unsigned int i = 0; i < callsigns.size(); i++) { auto it = aircraftByCallsign.find(callsigns.at(i)); if (it != aircraftByCallsign.end()) { it->second->updatePosition(positions.at(i), orientations.at(i), groundspeeds.at(i)); @@ -71,15 +70,15 @@ void FGSwiftAircraftManager::getRemoteAircraftData(std::vector& cal elevationsM.clear(); verticalOffsets.clear(); - for (int i = 0; i < requestedCallsigns.size(); i++) { - auto it = aircraftByCallsign.find(requestedCallsigns.at(i)); + for (const auto & requestedCallsign : requestedCallsigns) { + auto it = aircraftByCallsign.find(requestedCallsign); if (it != aircraftByCallsign.end()) { double latDeg = it->second->getLatDeg(); double lonDeg = it->second->getLongDeg(); double groundElevation = it->second->getGroundElevation(); double fudgeFactor = it->second->getFudgeFactor(); - - callsigns.push_back(requestedCallsigns.at(i)); + (void)fudgeFactor; + callsigns.push_back(requestedCallsign); latitudesDeg.push_back(latDeg); longitudesDeg.push_back(lonDeg); elevationsM.push_back(groundElevation); @@ -89,7 +88,7 @@ void FGSwiftAircraftManager::getRemoteAircraftData(std::vector& cal } -void FGSwiftAircraftManager::removePlane(std::string callsign) +void FGSwiftAircraftManager::removePlane(const std::string& callsign) { auto it = aircraftByCallsign.find(callsign); if (it != aircraftByCallsign.end()) { diff --git a/src/Network/Swift/SwiftAircraftManager.h b/src/Network/Swift/SwiftAircraftManager.h index 9f6991905..b3a1f4452 100644 --- a/src/Network/Swift/SwiftAircraftManager.h +++ b/src/Network/Swift/SwiftAircraftManager.h @@ -29,11 +29,11 @@ public: FGSwiftAircraftManager(); ~FGSwiftAircraftManager(); std::map aircraftByCallsign; - bool addPlane(std::string callsign, std::string modelString); + bool addPlane(const std::string& callsign, std::string modelString); void updatePlanes(std::vector callsigns, std::vector positions, std::vector orientations, std::vector groundspeeds, std::vector onGrounds); void getRemoteAircraftData(std::vector& callsigns, std::vector& latitudesDeg, std::vector& longitudesDeg, std::vector& elevationsM, std::vector& verticalOffsets) const; - void removePlane(std::string callsign); + void removePlane(const std::string& callsign); void removeAllPlanes(); }; #endif \ No newline at end of file diff --git a/src/Network/Swift/service.cpp b/src/Network/Swift/service.cpp index 61b257f80..0d72a4af3 100644 --- a/src/Network/Swift/service.cpp +++ b/src/Network/Swift/service.cpp @@ -19,17 +19,9 @@ #include "service.h" #include
-#include #include -#include #include -#include -#include -#include #include -#include -#include -#include #define FGSWIFTBUS_API_VERSION 1; @@ -169,22 +161,22 @@ bool CService::getAllWheelsOnGround() const int CService::getCom1Active() const { - return com1ActiveNode->getDoubleValue() * 1000; + return (int)com1ActiveNode->getDoubleValue() * 1000; } int CService::getCom1Standby() const { - return com1StandbyNode->getDoubleValue() * 1000; + return (int)com1StandbyNode->getDoubleValue() * 1000; } int CService::getCom2Active() const { - return com2ActiveNode->getDoubleValue() * 1000; + return (int)com2ActiveNode->getDoubleValue() * 1000; } int CService::getCom2Standby() const { - return com2StandbyNode->getDoubleValue() * 1000; + return (int)com2StandbyNode->getDoubleValue() * 1000; } int CService::getTransponderCode() const diff --git a/src/Network/Swift/swift_connection.cxx b/src/Network/Swift/swift_connection.cxx index d8011222c..7ce55e4b4 100644 --- a/src/Network/Swift/swift_connection.cxx +++ b/src/Network/Swift/swift_connection.cxx @@ -51,7 +51,8 @@ bool SwiftConnection::startServer(const SGPropertyNode* arg, SGPropertyNode* roo bool SwiftConnection::stopServer(const SGPropertyNode* arg, SGPropertyNode* root) { - SwiftConnection::plug->~CPlugin(); + delete SwiftConnection::plug; + SwiftConnection::plug = nullptr; fgSetBool("/sim/swift/serverRunning", false); serverRunning = false; return true; @@ -68,9 +69,9 @@ SwiftConnection::~SwiftConnection() shutdown(); } -void SwiftConnection::init(void) +void SwiftConnection::init() { - if (initialized == false) { + if (!initialized) { globals->get_commands()->addCommand("swiftStart", this, &SwiftConnection::startServer); globals->get_commands()->addCommand("swiftStop", this, &SwiftConnection::stopServer); fgSetBool("/sim/swift/available", true); @@ -81,14 +82,14 @@ void SwiftConnection::init(void) void SwiftConnection::update(double delta_time_sec) { - if (serverRunning == true) { + if (serverRunning) { SwiftConnection::plug->fastLoop(); } } -void SwiftConnection::shutdown(void) +void SwiftConnection::shutdown() { - if (initialized == true) { + if (initialized) { globals->get_commands()->removeCommand("swiftStart"); globals->get_commands()->removeCommand("swiftStop"); fgSetBool("/sim/swift/available", false); diff --git a/src/Network/Swift/swift_connection.hxx b/src/Network/Swift/swift_connection.hxx index 088823729..d7732acc3 100644 --- a/src/Network/Swift/swift_connection.hxx +++ b/src/Network/Swift/swift_connection.hxx @@ -52,7 +52,7 @@ public: bool startServer(const SGPropertyNode* arg, SGPropertyNode* root); bool stopServer(const SGPropertyNode* arg, SGPropertyNode* root); - FGSwiftBus::CPlugin* plug; + FGSwiftBus::CPlugin* plug{}; private: bool serverRunning = false; diff --git a/src/Network/Swift/traffic.cpp b/src/Network/Swift/traffic.cpp index 9a6c3f916..2bf8dd9b6 100644 --- a/src/Network/Swift/traffic.cpp +++ b/src/Network/Swift/traffic.cpp @@ -209,7 +209,7 @@ DBusHandlerResult CTraffic::dbusMessageHandler(const CDBusMessage& message_) queueDBusCall([=]() { std::vector positions; std::vector orientations; - for (int i = 0; i < latitudes.size(); i++) { + for (long unsigned int i = 0; i < latitudes.size(); i++) { SGGeod newPos; newPos.setLatitudeDeg(latitudes.at(i)); newPos.setLongitudeDeg(longitudes.at(i)); From 34915125a17903a6165dc356085c57a6590ec121 Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Sun, 23 Feb 2020 20:57:30 +0100 Subject: [PATCH 43/98] [swift] Fix frequency casting regression --- src/Network/Swift/service.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Network/Swift/service.cpp b/src/Network/Swift/service.cpp index 0d72a4af3..27de5fee1 100644 --- a/src/Network/Swift/service.cpp +++ b/src/Network/Swift/service.cpp @@ -160,23 +160,23 @@ bool CService::getAllWheelsOnGround() const } int CService::getCom1Active() const -{ - return (int)com1ActiveNode->getDoubleValue() * 1000; +{ + return (int)(com1ActiveNode->getDoubleValue() * 1000); } int CService::getCom1Standby() const { - return (int)com1StandbyNode->getDoubleValue() * 1000; + return (int)(com1StandbyNode->getDoubleValue() * 1000); } int CService::getCom2Active() const { - return (int)com2ActiveNode->getDoubleValue() * 1000; + return (int)(com2ActiveNode->getDoubleValue() * 1000); } int CService::getCom2Standby() const { - return (int)com2StandbyNode->getDoubleValue() * 1000; + return (int)(com2StandbyNode->getDoubleValue() * 1000); } int CService::getTransponderCode() const From aca625eda24451536d46458ee1826f7d6c37e9bb Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Sat, 29 Feb 2020 14:31:52 +0100 Subject: [PATCH 44/98] [swift] Fetching ground elevation for interpolation --- src/Network/Swift/SwiftAircraft.cpp | 24 ++++++++---- src/Network/Swift/SwiftAircraft.h | 11 +++--- src/Network/Swift/SwiftAircraftManager.cpp | 45 ++++++++++++++-------- src/Network/Swift/SwiftAircraftManager.h | 2 + src/Network/Swift/traffic.cpp | 21 ++++++++++ 5 files changed, 75 insertions(+), 28 deletions(-) diff --git a/src/Network/Swift/SwiftAircraft.cpp b/src/Network/Swift/SwiftAircraft.cpp index 58f6008bd..110dbd43d 100644 --- a/src/Network/Swift/SwiftAircraft.cpp +++ b/src/Network/Swift/SwiftAircraft.cpp @@ -57,9 +57,10 @@ FGSwiftAircraft::FGSwiftAircraft(const std::string& callsign, const std::string& props->setStringValue("callsign", callsign); props->setBoolValue("valid",true); } + initPos = false; } -bool FGSwiftAircraft::updatePosition(SGGeod newPosition, SGVec3d orientation, double groundspeed) +bool FGSwiftAircraft::updatePosition(SGGeod newPosition, SGVec3d orientation, double groundspeed, bool initPos) { position = newPosition; @@ -69,6 +70,8 @@ bool FGSwiftAircraft::updatePosition(SGGeod newPosition, SGVec3d orientation, do aip.setHeadingDeg(orientation.z()); aip.update(); + this->initPos = initPos; + //Update props props->setDoubleValue("orientation/pitch-deg", orientation.x()); props->setDoubleValue("orientation/roll-deg", orientation.y()); @@ -97,17 +100,17 @@ FGSwiftAircraft::~FGSwiftAircraft() aip.setVisible(false); } -double FGSwiftAircraft::getLatDeg() +double FGSwiftAircraft::getLatDeg() const { return position.getLatitudeDeg(); } -double FGSwiftAircraft::getLongDeg() +double FGSwiftAircraft::getLongDeg() const { return position.getLongitudeDeg(); } -double FGSwiftAircraft::getFudgeFactor() +double FGSwiftAircraft::getFudgeFactor() const { return 0; } @@ -117,10 +120,15 @@ inline bool FGSwiftAircraft::operator<(const std::string& extCallsign) return _model->getName().compare(extCallsign); } -double FGSwiftAircraft::getGroundElevation() +double FGSwiftAircraft::getGroundElevation(double latitudeDeg, double longitudeDeg) const { - double alt; - globals->get_scenery()->get_elevation_m(position,alt,0); - return alt; + if(!initPos) { return std::numeric_limits::quiet_NaN(); } + double alt = 0; + SGGeod pos; + pos.setElevationFt(30000); + pos.setLatitudeDeg(latitudeDeg); + pos.setLongitudeDeg(longitudeDeg); + globals->get_scenery()->get_elevation_m(pos,alt,0,_model.get()); + return alt; } \ No newline at end of file diff --git a/src/Network/Swift/SwiftAircraft.h b/src/Network/Swift/SwiftAircraft.h index 45bc8dd3c..58484b8e1 100644 --- a/src/Network/Swift/SwiftAircraft.h +++ b/src/Network/Swift/SwiftAircraft.h @@ -42,15 +42,16 @@ class FGSwiftAircraft { public: FGSwiftAircraft(const std::string& callsign, const std::string& modelpath, SGPropertyNode* p); - bool updatePosition(SGGeod newPosition, SGVec3d orientation, double groundspeed); + bool updatePosition(SGGeod newPosition, SGVec3d orientation, double groundspeed, bool initPos); ~FGSwiftAircraft(); std::string getName() { return _model->getName(); }; - double getLatDeg(); - double getLongDeg(); - double getGroundElevation(); - double getFudgeFactor(); + double getLatDeg() const; + double getLongDeg() const; + double getGroundElevation(double, double) const; + double getFudgeFactor() const; private: + bool initPos; SGGeod position; SGPropertyNode* props; osg::ref_ptr _model; diff --git a/src/Network/Swift/SwiftAircraftManager.cpp b/src/Network/Swift/SwiftAircraftManager.cpp index edec49eaf..eaf4e070a 100644 --- a/src/Network/Swift/SwiftAircraftManager.cpp +++ b/src/Network/Swift/SwiftAircraftManager.cpp @@ -55,7 +55,7 @@ void FGSwiftAircraftManager::updatePlanes(std::vector callsigns, st for (long unsigned int i = 0; i < callsigns.size(); i++) { auto it = aircraftByCallsign.find(callsigns.at(i)); if (it != aircraftByCallsign.end()) { - it->second->updatePosition(positions.at(i), orientations.at(i), groundspeeds.at(i)); + it->second->updatePosition(positions.at(i), orientations.at(i), groundspeeds.at(i),true); } } @@ -63,6 +63,8 @@ void FGSwiftAircraftManager::updatePlanes(std::vector callsigns, st void FGSwiftAircraftManager::getRemoteAircraftData(std::vector& callsigns, std::vector& latitudesDeg, std::vector& longitudesDeg, std::vector& elevationsM, std::vector& verticalOffsets) const { + if (callsigns.empty() || aircraftByCallsign.empty()) { return; } + const auto requestedCallsigns = callsigns; callsigns.clear(); latitudesDeg.clear(); @@ -71,21 +73,24 @@ void FGSwiftAircraftManager::getRemoteAircraftData(std::vector& cal verticalOffsets.clear(); for (const auto & requestedCallsign : requestedCallsigns) { - auto it = aircraftByCallsign.find(requestedCallsign); - if (it != aircraftByCallsign.end()) { - double latDeg = it->second->getLatDeg(); - double lonDeg = it->second->getLongDeg(); - double groundElevation = it->second->getGroundElevation(); - double fudgeFactor = it->second->getFudgeFactor(); - (void)fudgeFactor; - callsigns.push_back(requestedCallsign); - latitudesDeg.push_back(latDeg); - longitudesDeg.push_back(lonDeg); - elevationsM.push_back(groundElevation); - verticalOffsets.push_back(0); - } - } + const auto it = aircraftByCallsign.find(requestedCallsign); + if(it == aircraftByCallsign.end()) { continue; } + const FGSwiftAircraft *aircraft = it->second; + assert(aircraft); + + const double latDeg = aircraft->getLatDeg(); + const double lonDeg = aircraft->getLongDeg(); + double groundElevation = aircraft->getGroundElevation(latDeg, lonDeg); + + double fudgeFactor = aircraft->getFudgeFactor(); + (void)fudgeFactor; + callsigns.push_back(requestedCallsign); + latitudesDeg.push_back(latDeg); + longitudesDeg.push_back(lonDeg); + elevationsM.push_back(groundElevation); + verticalOffsets.push_back(0); + } } void FGSwiftAircraftManager::removePlane(const std::string& callsign) @@ -104,3 +109,13 @@ void FGSwiftAircraftManager::removeAllPlanes() aircraftByCallsign.erase(it); } } + +double FGSwiftAircraftManager::getElevationAtPosition(const std::string &callsign, double latitudeDeg, + double longitudeDeg, double altitudeMeters) const +{ + auto it = aircraftByCallsign.find(callsign); + if(it != aircraftByCallsign.end()){ + return it->second->getGroundElevation(latitudeDeg, longitudeDeg); + } + return std::numeric_limits::quiet_NaN(); +} diff --git a/src/Network/Swift/SwiftAircraftManager.h b/src/Network/Swift/SwiftAircraftManager.h index b3a1f4452..83c99f1d6 100644 --- a/src/Network/Swift/SwiftAircraftManager.h +++ b/src/Network/Swift/SwiftAircraftManager.h @@ -18,6 +18,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "SwiftAircraft.h" +#include #include #ifndef FGSWIFTAIRCRAFTMANAGER_H @@ -35,5 +36,6 @@ public: std::vector& elevationsM, std::vector& verticalOffsets) const; void removePlane(const std::string& callsign); void removeAllPlanes(); + double getElevationAtPosition(const std::string &callsign, double latitudeDeg, double longitudeDeg, double altitudeMeters) const; }; #endif \ No newline at end of file diff --git a/src/Network/Swift/traffic.cpp b/src/Network/Swift/traffic.cpp index 2bf8dd9b6..65aa6b267 100644 --- a/src/Network/Swift/traffic.cpp +++ b/src/Network/Swift/traffic.cpp @@ -241,6 +241,27 @@ DBusHandlerResult CTraffic::dbusMessageHandler(const CDBusMessage& message_) reply.appendArgument(verticalOffsets); sendDBusMessage(reply); }); + } + else if (message.getMethodName() == "getElevationAtPosition") + { + std::string callsign; + double latitudeDeg; + double longitudeDeg; + double altitudeMeters; + message.beginArgumentRead(); + message.getArgument(callsign); + message.getArgument(latitudeDeg); + message.getArgument(longitudeDeg); + message.getArgument(altitudeMeters); + queueDBusCall([ = ]() + { + double elevation = acm->getElevationAtPosition(callsign, latitudeDeg, longitudeDeg, altitudeMeters); + CDBusMessage reply = CDBusMessage::createReply(sender, serial); + reply.beginArgumentWrite(); + reply.appendArgument(callsign); + reply.appendArgument(elevation); + sendDBusMessage(reply); + }); } else { // Unknown message. Tell DBus that we cannot handle it return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; From e2135369ce7eeda64cba592c02956ca651f1155e Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Wed, 4 Mar 2020 12:42:37 +0100 Subject: [PATCH 45/98] Minor code fixes --- src/Network/Swift/SwiftAircraft.cpp | 15 +++-- src/Network/Swift/SwiftAircraft.h | 8 +-- src/Network/Swift/SwiftAircraftManager.cpp | 21 ++++--- src/Network/Swift/SwiftAircraftManager.h | 2 +- src/Network/Swift/plugin.cpp | 2 +- src/Network/Swift/service.h | 72 +++++++++++----------- src/Network/Swift/traffic.cpp | 12 ++-- src/Network/Swift/traffic.h | 2 +- 8 files changed, 69 insertions(+), 65 deletions(-) diff --git a/src/Network/Swift/SwiftAircraft.cpp b/src/Network/Swift/SwiftAircraft.cpp index 110dbd43d..7b0d639f2 100644 --- a/src/Network/Swift/SwiftAircraft.cpp +++ b/src/Network/Swift/SwiftAircraft.cpp @@ -42,7 +42,7 @@ #include #include -FGSwiftAircraft::FGSwiftAircraft(const std::string& callsign, const std::string& modelpath, SGPropertyNode* p) +FGSwiftAircraft::FGSwiftAircraft(const std::string& callsign, const std::string& modelpath, SGPropertyNode_ptr p) { using namespace simgear; _model = SGModelLib::loadModel(modelpath); @@ -57,7 +57,6 @@ FGSwiftAircraft::FGSwiftAircraft(const std::string& callsign, const std::string& props->setStringValue("callsign", callsign); props->setBoolValue("valid",true); } - initPos = false; } bool FGSwiftAircraft::updatePosition(SGGeod newPosition, SGVec3d orientation, double groundspeed, bool initPos) @@ -120,15 +119,15 @@ inline bool FGSwiftAircraft::operator<(const std::string& extCallsign) return _model->getName().compare(extCallsign); } -double FGSwiftAircraft::getGroundElevation(double latitudeDeg, double longitudeDeg) const +double FGSwiftAircraft::getGroundElevation(const SGGeod& pos) const { if(!initPos) { return std::numeric_limits::quiet_NaN(); } double alt = 0; - SGGeod pos; - pos.setElevationFt(30000); - pos.setLatitudeDeg(latitudeDeg); - pos.setLongitudeDeg(longitudeDeg); - globals->get_scenery()->get_elevation_m(pos,alt,0,_model.get()); + SGGeod posReq; + posReq.setElevationFt(30000); + posReq.setLatitudeDeg(pos.getLatitudeDeg()); + posReq.setLongitudeDeg(pos.getLongitudeDeg()); + globals->get_scenery()->get_elevation_m(posReq,alt,0,_model.get()); return alt; } \ No newline at end of file diff --git a/src/Network/Swift/SwiftAircraft.h b/src/Network/Swift/SwiftAircraft.h index 58484b8e1..2bad224d5 100644 --- a/src/Network/Swift/SwiftAircraft.h +++ b/src/Network/Swift/SwiftAircraft.h @@ -41,19 +41,19 @@ class PagedLOD; class FGSwiftAircraft { public: - FGSwiftAircraft(const std::string& callsign, const std::string& modelpath, SGPropertyNode* p); + FGSwiftAircraft(const std::string& callsign, const std::string& modelpath, SGPropertyNode_ptr p); bool updatePosition(SGGeod newPosition, SGVec3d orientation, double groundspeed, bool initPos); ~FGSwiftAircraft(); std::string getName() { return _model->getName(); }; double getLatDeg() const; double getLongDeg() const; - double getGroundElevation(double, double) const; + double getGroundElevation(const SGGeod& pos) const; double getFudgeFactor() const; private: - bool initPos; + bool initPos = false; SGGeod position; - SGPropertyNode* props; + SGPropertyNode_ptr props; osg::ref_ptr _model; SGModelPlacement aip; inline bool operator<(const std::string& extCallsign); diff --git a/src/Network/Swift/SwiftAircraftManager.cpp b/src/Network/Swift/SwiftAircraftManager.cpp index eaf4e070a..8b2e6adfb 100644 --- a/src/Network/Swift/SwiftAircraftManager.cpp +++ b/src/Network/Swift/SwiftAircraftManager.cpp @@ -34,8 +34,8 @@ bool FGSwiftAircraftManager::addPlane(const std::string& callsign, std::string m return false; const char* typeString = "swift"; - SGPropertyNode* root = globals->get_props()->getNode("ai/models",true); - SGPropertyNode* p; + SGPropertyNode_ptr root = globals->get_props()->getNode("ai/models",true); + SGPropertyNode_ptr p; int i; for(i = 0; i < 10000; i++){ p = root->getNode(typeString,i,false); @@ -45,7 +45,7 @@ bool FGSwiftAircraftManager::addPlane(const std::string& callsign, std::string m } p = root->getNode(typeString,i,true); p->setIntValue("id",i); - auto* curAircraft = new FGSwiftAircraft(callsign, std::move(modelString), p); + auto curAircraft = new FGSwiftAircraft(callsign, std::move(modelString), p); aircraftByCallsign.insert(std::pair(callsign, curAircraft)); return true; } @@ -79,9 +79,13 @@ void FGSwiftAircraftManager::getRemoteAircraftData(std::vector& cal const FGSwiftAircraft *aircraft = it->second; assert(aircraft); - const double latDeg = aircraft->getLatDeg(); - const double lonDeg = aircraft->getLongDeg(); - double groundElevation = aircraft->getGroundElevation(latDeg, lonDeg); + + SGGeod pos; + pos.setLatitudeDeg(aircraft->getLatDeg()); + pos.setLongitudeDeg(aircraft->getLongDeg()); + const double latDeg = pos.getLatitudeDeg(); + const double lonDeg = pos.getLongitudeDeg(); + double groundElevation = aircraft->getGroundElevation(pos); double fudgeFactor = aircraft->getFudgeFactor(); (void)fudgeFactor; @@ -110,12 +114,11 @@ void FGSwiftAircraftManager::removeAllPlanes() } } -double FGSwiftAircraftManager::getElevationAtPosition(const std::string &callsign, double latitudeDeg, - double longitudeDeg, double altitudeMeters) const +double FGSwiftAircraftManager::getElevationAtPosition(const std::string &callsign, const SGGeod& pos) const { auto it = aircraftByCallsign.find(callsign); if(it != aircraftByCallsign.end()){ - return it->second->getGroundElevation(latitudeDeg, longitudeDeg); + return it->second->getGroundElevation(pos); } return std::numeric_limits::quiet_NaN(); } diff --git a/src/Network/Swift/SwiftAircraftManager.h b/src/Network/Swift/SwiftAircraftManager.h index 83c99f1d6..0c1e731c2 100644 --- a/src/Network/Swift/SwiftAircraftManager.h +++ b/src/Network/Swift/SwiftAircraftManager.h @@ -36,6 +36,6 @@ public: std::vector& elevationsM, std::vector& verticalOffsets) const; void removePlane(const std::string& callsign); void removeAllPlanes(); - double getElevationAtPosition(const std::string &callsign, double latitudeDeg, double longitudeDeg, double altitudeMeters) const; + double getElevationAtPosition(const std::string &callsign, const SGGeod& pos) const; }; #endif \ No newline at end of file diff --git a/src/Network/Swift/plugin.cpp b/src/Network/Swift/plugin.cpp index f269dea6a..15a8ec6be 100644 --- a/src/Network/Swift/plugin.cpp +++ b/src/Network/Swift/plugin.cpp @@ -82,7 +82,7 @@ void CPlugin::startServer() float CPlugin::startServerDeferred(float, float, int, void* refcon) { - auto* plugin = static_cast(refcon); + auto plugin = static_cast(refcon); if (!plugin->m_isRunning) { plugin->startServer(); plugin->m_isRunning = true; diff --git a/src/Network/Swift/service.h b/src/Network/Swift/service.h index 991608f4e..d4c264eea 100644 --- a/src/Network/Swift/service.h +++ b/src/Network/Swift/service.h @@ -203,42 +203,42 @@ protected: DBusHandlerResult dbusMessageHandler(const CDBusMessage& message) override; private: - SGPropertyNode* textMessageNode; - SGPropertyNode* aircraftModelPathNode; - //SGPropertyNode* aircraftLiveryNode; - //SGPropertyNode* aircraftIcaoCodeNode; - SGPropertyNode* aircraftDescriptionNode; - SGPropertyNode* isPausedNode; - SGPropertyNode* latitudeNode; - SGPropertyNode* longitudeNode; - SGPropertyNode* altitudeMSLNode; - SGPropertyNode* heightAGLNode; - SGPropertyNode* groundSpeedNode; - SGPropertyNode* pitchNode; - SGPropertyNode* rollNode; - SGPropertyNode* trueHeadingNode; - SGPropertyNode* wheelsOnGroundNode; - SGPropertyNode* com1ActiveNode; - SGPropertyNode* com1StandbyNode; - SGPropertyNode* com2ActiveNode; - SGPropertyNode* com2StandbyNode; - SGPropertyNode* transponderCodeNode; - SGPropertyNode* transponderModeNode; - SGPropertyNode* transponderIdentNode; - SGPropertyNode* beaconLightsNode; - SGPropertyNode* landingLightsNode; - SGPropertyNode* navLightsNode; - SGPropertyNode* strobeLightsNode; - SGPropertyNode* taxiLightsNode; - SGPropertyNode* altimeterServiceableNode; - SGPropertyNode* pressAltitudeFtNode; - SGPropertyNode* flapsDeployRatioNode; - SGPropertyNode* gearDeployRatioNode; - SGPropertyNode* speedBrakeDeployRatioNode; - SGPropertyNode* groundElevation; - //SGPropertyNode* numberEnginesNode; - //SGPropertyNode* engineN1PercentageNode; - SGPropertyNode* aircraftNameNode; + SGPropertyNode_ptr textMessageNode; + SGPropertyNode_ptr aircraftModelPathNode; + //SGPropertyNode_ptr aircraftLiveryNode; + //SGPropertyNode_ptr aircraftIcaoCodeNode; + SGPropertyNode_ptr aircraftDescriptionNode; + SGPropertyNode_ptr isPausedNode; + SGPropertyNode_ptr latitudeNode; + SGPropertyNode_ptr longitudeNode; + SGPropertyNode_ptr altitudeMSLNode; + SGPropertyNode_ptr heightAGLNode; + SGPropertyNode_ptr groundSpeedNode; + SGPropertyNode_ptr pitchNode; + SGPropertyNode_ptr rollNode; + SGPropertyNode_ptr trueHeadingNode; + SGPropertyNode_ptr wheelsOnGroundNode; + SGPropertyNode_ptr com1ActiveNode; + SGPropertyNode_ptr com1StandbyNode; + SGPropertyNode_ptr com2ActiveNode; + SGPropertyNode_ptr com2StandbyNode; + SGPropertyNode_ptr transponderCodeNode; + SGPropertyNode_ptr transponderModeNode; + SGPropertyNode_ptr transponderIdentNode; + SGPropertyNode_ptr beaconLightsNode; + SGPropertyNode_ptr landingLightsNode; + SGPropertyNode_ptr navLightsNode; + SGPropertyNode_ptr strobeLightsNode; + SGPropertyNode_ptr taxiLightsNode; + SGPropertyNode_ptr altimeterServiceableNode; + SGPropertyNode_ptr pressAltitudeFtNode; + SGPropertyNode_ptr flapsDeployRatioNode; + SGPropertyNode_ptr gearDeployRatioNode; + SGPropertyNode_ptr speedBrakeDeployRatioNode; + SGPropertyNode_ptr groundElevation; + //SGPropertyNode_ptr numberEnginesNode; + //SGPropertyNode_ptr engineN1PercentageNode; + SGPropertyNode_ptr aircraftNameNode; }; } // namespace FGSwiftBus diff --git a/src/Network/Swift/traffic.cpp b/src/Network/Swift/traffic.cpp index 65aa6b267..854d2e8bf 100644 --- a/src/Network/Swift/traffic.cpp +++ b/src/Network/Swift/traffic.cpp @@ -38,15 +38,13 @@ namespace FGSwiftBus { CTraffic::CTraffic() { - acm = new FGSwiftAircraftManager(); + acm.reset(new FGSwiftAircraftManager()); SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus Traffic started"); } CTraffic::~CTraffic() { cleanup(); - delete acm; - acm = nullptr; SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus Traffic stopped"); } @@ -64,7 +62,7 @@ const std::string& CTraffic::ObjectPath() void CTraffic::planeLoaded(void* id, bool succeeded, void* self) { - auto* traffic = static_cast(self); + auto traffic = static_cast(self); auto planeIt = traffic->m_planesById.find(id); if (planeIt == traffic->m_planesById.end()) { return; } @@ -255,7 +253,11 @@ DBusHandlerResult CTraffic::dbusMessageHandler(const CDBusMessage& message_) message.getArgument(altitudeMeters); queueDBusCall([ = ]() { - double elevation = acm->getElevationAtPosition(callsign, latitudeDeg, longitudeDeg, altitudeMeters); + SGGeod pos; + pos.setLatitudeDeg(latitudeDeg); + pos.setLongitudeDeg(longitudeDeg); + pos.setElevationM(altitudeMeters); + double elevation = acm->getElevationAtPosition(callsign, pos); CDBusMessage reply = CDBusMessage::createReply(sender, serial); reply.beginArgumentWrite(); reply.appendArgument(callsign); diff --git a/src/Network/Swift/traffic.h b/src/Network/Swift/traffic.h index 711540a67..bbc040001 100644 --- a/src/Network/Swift/traffic.h +++ b/src/Network/Swift/traffic.h @@ -99,7 +99,7 @@ private: bool m_emitSimFrame = true; - FGSwiftAircraftManager* acm; + std::unique_ptr acm; static void planeLoaded(void* id, bool succeeded, void* self); }; From 40a85cae282339cf9cb45811f748eedb6560bee4 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 10 Mar 2020 16:10:55 +0000 Subject: [PATCH 46/98] Launcher: fix some warnings, maybe a crash Avoid binding loop warning, and change logic around re-starting the aircraft dirs scan, to hopefully clarify the crash Brendan and others are seeing. --- src/GUI/AddOnsController.cxx | 8 ++++---- src/GUI/LocalAircraftCache.cxx | 10 ++++++++-- src/GUI/QtLauncher.cxx | 2 +- src/GUI/qml/PathListDelegate.qml | 4 +++- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/GUI/AddOnsController.cxx b/src/GUI/AddOnsController.cxx index 06c8d2579..6dfb7777d 100644 --- a/src/GUI/AddOnsController.cxx +++ b/src/GUI/AddOnsController.cxx @@ -44,20 +44,20 @@ AddOnsController::AddOnsController(LauncherMainWindow *parent, LaunchConfig* con m_sceneryPaths = new PathListModel(this); connect(m_sceneryPaths, &PathListModel::enabledPathsChanged, [this] () { - m_sceneryPaths->saveToSettings("scenery-paths"); + m_sceneryPaths->saveToSettings("scenery-paths-v2"); flightgear::launcherSetSceneryPaths(); }); - m_sceneryPaths->loadFromSettings("scenery-paths"); + m_sceneryPaths->loadFromSettings("scenery-paths-v2"); m_aircraftPaths = new PathListModel(this); connect(m_aircraftPaths, &PathListModel::enabledPathsChanged, [this] () { - m_aircraftPaths->saveToSettings("aircraft-paths"); + m_aircraftPaths->saveToSettings("aircraft-paths-v2"); auto aircraftCache = LocalAircraftCache::instance(); aircraftCache->setPaths(m_aircraftPaths->enabledPaths()); aircraftCache->scanDirs(); }); - m_aircraftPaths->loadFromSettings("aircraft-paths"); + m_aircraftPaths->loadFromSettings("aircraft-paths-v2"); QSettings settings; int size = settings.beginReadArray("addon-modules"); diff --git a/src/GUI/LocalAircraftCache.cxx b/src/GUI/LocalAircraftCache.cxx index 3290c6194..10e51faed 100644 --- a/src/GUI/LocalAircraftCache.cxx +++ b/src/GUI/LocalAircraftCache.cxx @@ -253,6 +253,7 @@ public: { m_done = true; } + Q_SIGNALS: void addedItems(); @@ -522,10 +523,15 @@ AircraftItemPtr LocalAircraftCache::findItemWithUri(QUrl aircraftUri) const void LocalAircraftCache::abandonCurrentScan() { if (m_scanThread) { + // don't fire onScanFinished when the thread ends + disconnect(m_scanThread.get(), &AircraftScanThread::finished, this, + &LocalAircraftCache::onScanFinished); + m_scanThread->setDone(); - m_scanThread->wait(1000); + if (!m_scanThread->wait(2000)) { + qWarning() << Q_FUNC_INFO << "scan thread failed to terminate"; + } m_scanThread.reset(); - qWarning() << Q_FUNC_INFO << "current scan abandonded"; } } diff --git a/src/GUI/QtLauncher.cxx b/src/GUI/QtLauncher.cxx index cdf36999e..9c9e1b9c5 100644 --- a/src/GUI/QtLauncher.cxx +++ b/src/GUI/QtLauncher.cxx @@ -404,7 +404,7 @@ void launcherSetSceneryPaths() // positions QSettings settings; // append explicit scenery paths - Q_FOREACH(QString path, PathListModel::readEnabledPaths("scenery-paths")) { + Q_FOREACH(QString path, PathListModel::readEnabledPaths("scenery-paths-v2")) { globals->append_fg_scenery(path.toStdString()); } diff --git a/src/GUI/qml/PathListDelegate.qml b/src/GUI/qml/PathListDelegate.qml index 99138b728..f8fd34a36 100644 --- a/src/GUI/qml/PathListDelegate.qml +++ b/src/GUI/qml/PathListDelegate.qml @@ -42,7 +42,9 @@ Item { anchors.left: parent.left height: parent.height onCheckedChanged: { - model.enabled = checked; + if (model.enabled !== checked) { + model.enabled = checked; + } } } From e50971107569c7bec06e9e39de3ed40c109fb929 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 10 Mar 2020 17:02:08 +0000 Subject: [PATCH 47/98] Fix some warnings from current Apple clang - remove some unused vars - remove use of std::mem_fn / std::bind2nd --- src/Input/FGEventInput.cxx | 6 ++---- src/Scenery/SceneryPager.cxx | 5 +++-- src/Scripting/NasalPositioned_cppbind.cxx | 4 +++- src/Scripting/NasalSys.cxx | 10 +++------- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/Input/FGEventInput.cxx b/src/Input/FGEventInput.cxx index 2d62e7809..4b0a17aea 100644 --- a/src/Input/FGEventInput.cxx +++ b/src/Input/FGEventInput.cxx @@ -537,9 +537,6 @@ bool FGReportSetting::Test() return d; } -static const char* hexTable = "0123456789ABCDEF"; - - std::string FGReportSetting::reportBytes(const std::string& moduleName) const { FGNasalSys *nas = globals->get_subsystem(); @@ -576,7 +573,8 @@ std::string FGReportSetting::reportBytes(const std::string& moduleName) const // can't access FGInputDevice here to check debugEvents flag #if 0 std::ostringstream byteString; - + static const char* hexTable = "0123456789ABCDEF"; + for (int i=0; i(s[i]); byteString << hexTable[uc >> 4]; diff --git a/src/Scenery/SceneryPager.cxx b/src/Scenery/SceneryPager.cxx index 892b7bc1a..99b2d84ba 100644 --- a/src/Scenery/SceneryPager.cxx +++ b/src/Scenery/SceneryPager.cxx @@ -110,8 +110,9 @@ void SceneryPager::signalEndFrame() } if (!_pagerRequests.empty()) { arePagerRequests = true; - for_each(_pagerRequests.begin(), _pagerRequests.end(), - bind2nd(mem_fun_ref(&PagerRequest::doRequest), this)); + for (auto req : _pagerRequests) { + req.doRequest(this); + } _pagerRequests.clear(); } if (areDeleteRequests && !arePagerRequests) { diff --git a/src/Scripting/NasalPositioned_cppbind.cxx b/src/Scripting/NasalPositioned_cppbind.cxx index 0cfd60f54..f18ff332f 100644 --- a/src/Scripting/NasalPositioned_cppbind.cxx +++ b/src/Scripting/NasalPositioned_cppbind.cxx @@ -108,7 +108,9 @@ std::vector extract( const std::vector& in, T (C2::*getter)() const ) { std::vector ret(in.size()); - std::transform(in.begin(), in.end(), ret.begin(), std::mem_fun(getter)); + std::transform(in.begin(), in.end(), ret.begin(), [getter](const C1& c) + { return (c->*getter)(); } + ); return ret; } diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index 75640ef81..45ea02307 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -219,15 +219,12 @@ static void f_timerObj_setSimTime(TimerObj& timer, naContext c, naRef value) class TimeStampObj : public SGReferenced { public: - TimeStampObj(Context *c, FGNasalSys* sys) : - _sys(sys) + TimeStampObj(Context *c) { timestamp.stamp(); } - virtual ~TimeStampObj() - { - } + virtual ~TimeStampObj() = default; void stamp() { @@ -243,7 +240,6 @@ public: } private: SGTimeStamp timestamp; - FGNasalSys* _sys; }; typedef SGSharedPtr TimeStampObjRef; @@ -600,7 +596,7 @@ static naRef f_makeTimer(naContext c, naRef me, int argc, naRef* args) static naRef f_maketimeStamp(naContext c, naRef me, int argc, naRef* args) { - TimeStampObj* timeStampObj = new TimeStampObj(c, nasalSys); + TimeStampObj* timeStampObj = new TimeStampObj(c); return nasal::to_nasal(c, timeStampObj); } From ecb48062ef4d4b2e9658677afeed960467698c7a Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 10 Mar 2020 17:25:03 +0000 Subject: [PATCH 48/98] Add CMake target to update Launcher translations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Invoke ‘make ts’ (or equivalent for your build system) to update the .XLF files for the launcher. --- CMakeModules/Translations.cmake | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/CMakeModules/Translations.cmake b/CMakeModules/Translations.cmake index 49e4c2c2d..22be097f5 100644 --- a/CMakeModules/Translations.cmake +++ b/CMakeModules/Translations.cmake @@ -19,9 +19,10 @@ if (${do_translate} AND NOT TARGET Qt5::lrelease) "\n(on Linux You may need to install an additional package containing the Qt5 translation tools)") endif() +# FIXME - determine this based on subdirs of TRANSLATIONS_SRC_DIR +set(LANGUAGES en_US de es nl fr it pl pt ru zh_CN) + if (${do_translate}) - # FIXME - determine this based on subdirs of TRANSLATIONS_SRC_DIR - set(LANGUAGES en_US de es nl fr it pl pt ru zh_CN) set(translation_res "${PROJECT_BINARY_DIR}/translations.qrc") add_custom_target(fgfs_qm_files ALL) @@ -55,3 +56,14 @@ if (${do_translate}) # set this so config.h can detect it set(HAVE_QRC_TRANSLATIONS TRUE) endif() # of do translate + +add_custom_target(ts) +foreach(lang ${LANGUAGES}) + add_custom_target( + ts_${lang} + COMMAND Qt5::lupdate ${CMAKE_SOURCE_DIR}/src/gui + -locations relative -ts ${TRANSLATIONS_SRC_DIR}/${lang}/FlightGear-Qt.xlf + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + add_dependencies(ts ts_${lang}) +endforeach() \ No newline at end of file From d0c12149c363123f4fe753e82fc96be9b93c2fc3 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 10 Mar 2020 17:29:33 +0000 Subject: [PATCH 49/98] Fixes to translation updating: use correct path. --- CMakeModules/Translations.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeModules/Translations.cmake b/CMakeModules/Translations.cmake index 22be097f5..9ca6b1307 100644 --- a/CMakeModules/Translations.cmake +++ b/CMakeModules/Translations.cmake @@ -61,8 +61,8 @@ add_custom_target(ts) foreach(lang ${LANGUAGES}) add_custom_target( ts_${lang} - COMMAND Qt5::lupdate ${CMAKE_SOURCE_DIR}/src/gui - -locations relative -ts ${TRANSLATIONS_SRC_DIR}/${lang}/FlightGear-Qt.xlf + COMMAND Qt5::lupdate ${CMAKE_SOURCE_DIR}/src/GUI + -locations relative -no-ui-lines -ts ${TRANSLATIONS_SRC_DIR}/${lang}/FlightGear-Qt.xlf WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) add_dependencies(ts ts_${lang}) From c12824bc94d1a042b292fe31b3fcaf77944e90f7 Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 11 Mar 2020 17:14:38 +0000 Subject: [PATCH 50/98] Basic Canvas tests, including image pixel ops --- src/Canvas/FGCanvasSystemAdapter.cxx | 2 +- src/Canvas/canvas_mgr.cxx | 34 ++--- src/Viewer/CameraGroup_legacy.cxx | 5 +- test_suite/FGTestApi/testGlobals.cxx | 4 +- test_suite/FGTestApi/testGlobals.hxx | 2 +- test_suite/simgear_tests/CMakeLists.txt | 1 + .../simgear_tests/canvas/CMakeLists.txt | 12 ++ test_suite/simgear_tests/canvas/TestSuite.cxx | 24 ++++ .../simgear_tests/canvas/testCanvas.cxx | 120 ++++++++++++++++++ .../simgear_tests/canvas/testCanvas.hxx | 49 +++++++ 10 files changed, 232 insertions(+), 21 deletions(-) create mode 100644 test_suite/simgear_tests/canvas/CMakeLists.txt create mode 100644 test_suite/simgear_tests/canvas/TestSuite.cxx create mode 100644 test_suite/simgear_tests/canvas/testCanvas.cxx create mode 100644 test_suite/simgear_tests/canvas/testCanvas.hxx diff --git a/src/Canvas/FGCanvasSystemAdapter.cxx b/src/Canvas/FGCanvasSystemAdapter.cxx index 7a2fb7fe1..bacb73d97 100644 --- a/src/Canvas/FGCanvasSystemAdapter.cxx +++ b/src/Canvas/FGCanvasSystemAdapter.cxx @@ -127,7 +127,7 @@ namespace canvas SG_LOG( SG_IO, SG_ALERT, "FGCanvasSystemAdapter: Failed to get HTTP subsystem" ); - return 0; + return nullptr; } } diff --git a/src/Canvas/canvas_mgr.cxx b/src/Canvas/canvas_mgr.cxx index 17ff96cbc..f652d6b05 100644 --- a/src/Canvas/canvas_mgr.cxx +++ b/src/Canvas/canvas_mgr.cxx @@ -71,21 +71,23 @@ CanvasMgr::CanvasMgr(): void CanvasMgr::init() { _gui_camera = flightgear::getGUICamera(flightgear::CameraGroup::getDefault()); - assert(_gui_camera.valid()); - - sc::Canvas::addPlacementFactory - ( - "object", - boost::bind - ( - &FGODGauge::set_aircraft_texture, - _1, - boost::bind(&sc::Canvas::getTexture, _2), - boost::bind(&sc::Canvas::getCullCallback, _2), - _2 - ) - ); - sc::Canvas::addPlacementFactory("scenery-object", &addSceneObjectPlacement); + if (_gui_camera.valid()) { + // add our two placement factories + sc::Canvas::addPlacementFactory + ( + "object", + boost::bind + ( + &FGODGauge::set_aircraft_texture, + _1, + boost::bind(&sc::Canvas::getTexture, _2), + boost::bind(&sc::Canvas::getCullCallback, _2), + _2 + ) + ); + + sc::Canvas::addPlacementFactory("scenery-object", &addSceneObjectPlacement); + } simgear::canvas::CanvasMgr::init(); } @@ -98,7 +100,7 @@ void CanvasMgr::shutdown() sc::Canvas::removePlacementFactory("object"); sc::Canvas::removePlacementFactory("scenery-object"); - _gui_camera = 0; + _gui_camera = 0; } //------------------------------------------------------------------------------ diff --git a/src/Viewer/CameraGroup_legacy.cxx b/src/Viewer/CameraGroup_legacy.cxx index c3d25ac3d..2e946a568 100644 --- a/src/Viewer/CameraGroup_legacy.cxx +++ b/src/Viewer/CameraGroup_legacy.cxx @@ -1205,9 +1205,12 @@ const CameraInfo* CameraGroup::getGUICamera() const Camera* getGUICamera(CameraGroup* cgroup) { + if (!cgroup) + return nullptr; + const CameraInfo* info = cgroup->getGUICamera(); if (!info) { - return NULL; + return nullptr; } return info->getCamera(MAIN_CAMERA); diff --git a/test_suite/FGTestApi/testGlobals.cxx b/test_suite/FGTestApi/testGlobals.cxx index 2f402c887..4144ecaa0 100644 --- a/test_suite/FGTestApi/testGlobals.cxx +++ b/test_suite/FGTestApi/testGlobals.cxx @@ -104,7 +104,7 @@ bool logPositionToKML(const std::string& testName) return true; } -void initStandardNasal() +void initStandardNasal(bool withCanvas) { fgInitAllowedPaths(); @@ -123,7 +123,7 @@ void initStandardNasal() props->setBoolValue("sim/rendering/precipitation-aircraft-enable", false); // disable various larger modules - nasalNode->setBoolValue("canvas/enabled", false); + nasalNode->setBoolValue("canvas/enabled", withCanvas); nasalNode->setBoolValue("jetways/enabled", false); nasalNode->setBoolValue("jetways_edit/enabled", false); nasalNode->setBoolValue("local_weather/enabled", false); diff --git a/test_suite/FGTestApi/testGlobals.hxx b/test_suite/FGTestApi/testGlobals.hxx index a243f6fcf..051b4fdbf 100644 --- a/test_suite/FGTestApi/testGlobals.hxx +++ b/test_suite/FGTestApi/testGlobals.hxx @@ -24,7 +24,7 @@ void initTestGlobals(const std::string& testName); bool logPositionToKML(const std::string& testName); -void initStandardNasal(); +void initStandardNasal(bool withCanvas = false); void populateFPWithoutNasal(flightgear::FlightPlanRef f, const std::string& depICAO, const std::string& depRunway, diff --git a/test_suite/simgear_tests/CMakeLists.txt b/test_suite/simgear_tests/CMakeLists.txt index 59cb6bb4f..e1f7a98f0 100644 --- a/test_suite/simgear_tests/CMakeLists.txt +++ b/test_suite/simgear_tests/CMakeLists.txt @@ -2,6 +2,7 @@ foreach( simgear_test_category math props + canvas ) add_subdirectory(${simgear_test_category}) diff --git a/test_suite/simgear_tests/canvas/CMakeLists.txt b/test_suite/simgear_tests/canvas/CMakeLists.txt new file mode 100644 index 000000000..d437cec8d --- /dev/null +++ b/test_suite/simgear_tests/canvas/CMakeLists.txt @@ -0,0 +1,12 @@ +set(TESTSUITE_SOURCES + ${TESTSUITE_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/testCanvas.cxx + PARENT_SCOPE +) + +set(TESTSUITE_HEADERS + ${TESTSUITE_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/testCanvas.hxx + PARENT_SCOPE +) diff --git a/test_suite/simgear_tests/canvas/TestSuite.cxx b/test_suite/simgear_tests/canvas/TestSuite.cxx new file mode 100644 index 000000000..b5e695037 --- /dev/null +++ b/test_suite/simgear_tests/canvas/TestSuite.cxx @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2016 Edward d'Auvergne + * + * This file is part of the program FlightGear. + * + * 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, see . + */ + +#include "testCanvas.hxx" + + +// Set up the tests. +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CanvasTests, "Simgear unit tests"); diff --git a/test_suite/simgear_tests/canvas/testCanvas.cxx b/test_suite/simgear_tests/canvas/testCanvas.cxx new file mode 100644 index 000000000..1b5652595 --- /dev/null +++ b/test_suite/simgear_tests/canvas/testCanvas.cxx @@ -0,0 +1,120 @@ +#include "testCanvas.hxx" + +#include +#include + +#include "test_suite/FGTestApi/testGlobals.hxx" + +#include
+#include
+ +#include +#include + +void verifyPixel(osg::Image* img, int x, int y, const uint8_t* pixel) +{ + auto rawPixel = img->data(x, y); + CPPUNIT_ASSERT(memcmp(rawPixel, pixel, 4) == 0); +} + +void CanvasTests::setUp() +{ + FGTestApi::setUp::initTestGlobals("canvas"); + + // Canvas needs loadxml command + fgInitCommands(); + + simgear::canvas::Canvas::setSystemAdapter( + simgear::canvas::SystemAdapterPtr(new canvas::FGCanvasSystemAdapter) + ); + + globals->add_subsystem("Canvas", new CanvasMgr, SGSubsystemMgr::DISPLAY); + + globals->get_subsystem_mgr()->bind(); + globals->get_subsystem_mgr()->init(); + + FGTestApi::setUp::initStandardNasal(true /* withCanvas */); + globals->get_subsystem_mgr()->postinit(); +} + +// Clean up after each test. +void CanvasTests::tearDown() +{ + FGTestApi::tearDown::shutdownTestGlobals(); +} + +void CanvasTests::testCanvasBasic() +{ + bool ok = FGTestApi::executeNasal(R"( + var my_canvas = canvas.new({ + "name": "PFD-Test", + "size": [512, 512], + "view": [768, 1024], + "mipmapping": 1 + }); + + globals.pfd = my_canvas; + )"); + CPPUNIT_ASSERT(ok); + + auto canvasPtr = globals->get_subsystem()->getCanvas("PFD-Test"); + CPPUNIT_ASSERT(canvasPtr.valid()); + + ok = FGTestApi::executeNasal(R"( + globals.pfd.createGroup("wibble"); + )"); + CPPUNIT_ASSERT(ok); + + auto wibbleGroupPtr = canvasPtr->getGroup("wibble"); + CPPUNIT_ASSERT(wibbleGroupPtr.valid()); +} + +void CanvasTests::testImagePixelOps() +{ + bool ok = FGTestApi::executeNasal(R"( + var my_canvas = canvas.new({ + "name": "ImagePixelOpsCanvas", + "size": [512, 512], + "view": [768, 1024], + "mipmapping": 1 + }); + + var g = my_canvas.createGroup("root"); + var img = g.createChild('image', 'ops-image'); + + # deliberate non-power-of-two sizes here + img.setSize(150, 170); + + globals.pixelOps = my_canvas; + )"); + CPPUNIT_ASSERT(ok); + + auto canvasPtr = globals->get_subsystem()->getCanvas("ImagePixelOpsCanvas"); + CPPUNIT_ASSERT(canvasPtr.valid()); + + auto imagePtr = canvasPtr->getRootGroup()->getElementById("ops-image"); + CPPUNIT_ASSERT(imagePtr.valid()); + + auto image = dynamic_cast(imagePtr.get()); + image->setFill(osg::Vec4(1.0, 0.0, 0.5, 1.0)); + + image->fillRect(SGRecti(20, 40, 70, 55), "#7f3f1f"); + + ok = FGTestApi::executeNasal(R"( + var canvas = globals.pixelOps; + var img = canvas.getGroup('root').getElementById('ops-image'); + + print('Image is:', img); + img.fillRect([5, 8, 180, 37], '#aabbcc'); + )"); + CPPUNIT_ASSERT(ok); + + auto osgImage = image->getImage(); + const uint8_t testColor1[] = {0xff, 0xcc, 0xbb, 0xaa}; // little endian + verifyPixel(osgImage.get(), 5, 8, testColor1); + verifyPixel(osgImage.get(), 149, 44, testColor1); + + // test pixles should not have been touched by the second fillRect + const uint8_t testColor2[] = {0xff, 0x1f, 0x3f, 0x7f}; // little endian + verifyPixel(osgImage.get(), 20, 45, testColor2); +} diff --git a/test_suite/simgear_tests/canvas/testCanvas.hxx b/test_suite/simgear_tests/canvas/testCanvas.hxx new file mode 100644 index 000000000..402cdf26e --- /dev/null +++ b/test_suite/simgear_tests/canvas/testCanvas.hxx @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Edward d'Auvergne + * + * This file is part of the program FlightGear. + * + * 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, see . + */ + + +#pragma once + + +#include +#include + + +// The unit tests. +class CanvasTests : public CppUnit::TestFixture +{ + // Set up the test suite. + CPPUNIT_TEST_SUITE(CanvasTests); + CPPUNIT_TEST(testCanvasBasic); + CPPUNIT_TEST(testImagePixelOps); + CPPUNIT_TEST_SUITE_END(); + +public: + // Set up function for each test. + void setUp(); + + // Clean up after each test. + void tearDown(); + + // The tests. + void testCanvasBasic(); + void testImagePixelOps(); +}; + + From 20a99531300eec84908cf1db83cee987acdc000a Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Sun, 8 Mar 2020 21:19:27 +0000 Subject: [PATCH 51/98] src/MultiPlayer/multiplaymgr.cxx: Don't load complete -set.xml of multiplayer aircraft. When loading -set.xml, we load into temporary 'set' node, then copy across only set/sim/view[]/config/* values into /ai/models/multiplayer[]/set/sim/view[]/config/. This ensures that we have just the information required for multiplayer views, and avoids problems where some properties in /ai/models/multiplayer[]/ were being archived to autosave_*.xml and loaded in later flightgear sessions. --- src/MultiPlayer/multiplaymgr.cxx | 59 +++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/MultiPlayer/multiplaymgr.cxx b/src/MultiPlayer/multiplaymgr.cxx index 6418f7eb6..10ad9bdbf 100644 --- a/src/MultiPlayer/multiplaymgr.cxx +++ b/src/MultiPlayer/multiplaymgr.cxx @@ -2421,7 +2421,15 @@ FGMultiplayMgr::addMultiplayer(const std::string& callsign, actually it's just a suffix. */ for (auto path: dir_contents) { - set = mp->_getProps()->addChild("set"); + /* Load into a local SGPropertyNode. + + As of 2020-03-08 we don't load directly into the global property tree + because that appears to result in runtime-specific multiplayer values + being written to autosave*.xml and reloaded next time fgfs is run, + which results in lots of bogus properties within /ai/multiplayer. So + instead we load into a local SGPropertyNode, then copy selected values + into the global tree below. */ + set = new SGPropertyNode; bool ok = true; try { readProperties(path, set); @@ -2436,26 +2444,43 @@ FGMultiplayMgr::addMultiplayer(const std::string& callsign, break; } } - mp->_getProps()->removeChildren("set"); + delete set; set = NULL; } } } - /* Copy [set]/sim/chase-distance-m (or -25 if not present) into - mp->set/sim/view[]/config/z-offset-m if not there. This attempts to mimic - what fgdata/defaults.xml does when it defines default views. Also copy - view[1]'s config into other views if not specified. */ + /* Copy values from our local /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; if (set) { - sim_chase_distance_m = set->getDoubleValue("sim/chase-distance-m", sim_chase_distance_m); + SGPropertyNode* sim = set->getChild("sim"); + if (sim) { + /* Override if present. */ + sim_chase_distance_m = sim->getDoubleValue("chase-distance-m", sim_chase_distance_m); + global_sim->setDoubleValue("chase-distance-m", sim_chase_distance_m); + + simgear::PropertyList views = sim->getChildren("view"); + for (auto view: views) { + int view_index = view->getIndex(); + SGPropertyNode* global_view = global_sim->addChild("view", view_index, false /*append*/); + assert(global_view->getIndex() == view_index); + SGPropertyNode* config = view->getChild("config"); + SGPropertyNode* global_config = global_view->addChild("config"); + if (config) { + int config_children_n = config->nChildren(); + for (int i=0; igetChild(i); + global_config->setDoubleValue(node->getName(), node->getDoubleValue()); + } + } + } + } } - else { - set = mp->_getProps()->addChild("set"); - set->setDoubleValue("sim/chase-distance-m", sim_chase_distance_m); - } - SGPropertyNode* set_sim = set->getNode("sim"); - + /* For views that are similar to Helicopter View, copy across Helicopter View target offsets if not specified. E.g. this allows Tower View AGL to work on aircraft that don't know about it but need non-zero target-*-offset-m values @@ -2464,14 +2489,16 @@ FGMultiplayMgr::addMultiplayer(const std::string& callsign, This mimics what fgdata:Nasal/view.nas:manager does for the user aircraft's views. */ - SGPropertyNode* view_1 = set_sim->getNode("view", 1); + SGPropertyNode* view_1 = global_sim->getNode("view", 1); std::initializer_list views_with_default_z_offset_m = {1, 2, 3, 5, 7}; for (int j: views_with_default_z_offset_m) { - SGPropertyNode* v = set_sim->getChild("view", j); + SGPropertyNode* v = global_sim->getChild("view", j); if (!v) { - v = set_sim->addChild("view", j, false /*append*/); + v = global_sim->addChild("view", j, false /*append*/); } SGPropertyNode* z_offset_m = v->getChild("config/z-offset-m"); + /* Setting config/z-offset-m default to here mimics + what fgdata/defaults.xml does when it defines default views. */ if (!z_offset_m) { v->setDoubleValue("config/z-offset-m", sim_chase_distance_m); } From ab7e7ec7b4240f4a81c338e2d62d48690d6e34b2 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Wed, 11 Mar 2020 00:07:56 +0000 Subject: [PATCH 52/98] src/Main/globals.cxx: don't load /ai/models/ from autosave.xml file. This cleans things up if earlier 'next' build as added spurious property items to autosave. --- src/Main/globals.cxx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Main/globals.cxx b/src/Main/globals.cxx index 9aeb37ac7..bb9b5d9f4 100644 --- a/src/Main/globals.cxx +++ b/src/Main/globals.cxx @@ -808,6 +808,14 @@ FGGlobals::loadUserSettings(SGPath userDataPath) } else { tryAutosaveMigration(userDataPath, &autosave); } + /* Before 2020-03-10, we could save portions of the /ai/models/ tree, which + confuses things when loaded again. So delete any such items if they have + been loaded. */ + SGPropertyNode* ai = autosave.getNode("ai"); + if (ai) { + SGPropertyNode* ai_models = ai->getNode("models"); + ai->removeChildren("models"); + } copyProperties(&autosave, globals->get_props()); } From 3cb05a920792c23c03bf27023777e4cf1c8c32f6 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Thu, 12 Mar 2020 10:31:38 +0100 Subject: [PATCH 53/98] Do not try to initialize all 16 joysticks every frame if they where not found previously. Try it once every second instead. --- src/Input/FGJoystickInput.cxx | 6 +++++- src/Input/FGJoystickInput.hxx | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Input/FGJoystickInput.cxx b/src/Input/FGJoystickInput.cxx index 1144fca99..979664c55 100644 --- a/src/Input/FGJoystickInput.cxx +++ b/src/Input/FGJoystickInput.cxx @@ -344,7 +344,11 @@ void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, if (js == 0 || js->notWorking()) { joysticks[index].initializing = true; if (js) { - joysticks[index].plibJS.reset( new jsJoystick(index) ); + joysticks[index].init_dt += dt; + if (joysticks[index].init_dt >= 1.0) { + joysticks[index].plibJS.reset( new jsJoystick(index) ); + joysticks[index].init_dt = 0.0; + } } return; } diff --git a/src/Input/FGJoystickInput.hxx b/src/Input/FGJoystickInput.hxx index 247225575..c2d6239b5 100644 --- a/src/Input/FGJoystickInput.hxx +++ b/src/Input/FGJoystickInput.hxx @@ -104,6 +104,7 @@ private: bool initializing = true; bool initialized = false; float values[MAX_JOYSTICK_AXES]; + double init_dt = 0.0f; void clearAxesAndButtons(); }; From 5b41f374c6db097adad857a445c704446ce6f475 Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 12 Mar 2020 10:51:49 +0000 Subject: [PATCH 54/98] L10N fixes: handle UTF8 paths in more places. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Still not complete, but now we set the command line args to be UTF-8 on Windows, we can strip out more of the ‘local 8-bit’ places (which screw up, generally). --- src/FDM/JSBSim/input_output/FGModelLoader.cpp | 2 +- src/Main/options.cxx | 29 +++++------ src/Model/acmodel.cxx | 2 +- src/Scripting/NasalSys.cxx | 50 +++++-------------- 4 files changed, 27 insertions(+), 56 deletions(-) diff --git a/src/FDM/JSBSim/input_output/FGModelLoader.cpp b/src/FDM/JSBSim/input_output/FGModelLoader.cpp index 6170b1bc6..1dbebae82 100644 --- a/src/FDM/JSBSim/input_output/FGModelLoader.cpp +++ b/src/FDM/JSBSim/input_output/FGModelLoader.cpp @@ -57,7 +57,7 @@ Element_ptr FGModelLoader::Open(Element *el) if (!fname.empty()) { FGXMLFileRead XMLFileRead; - SGPath path(SGPath::fromLocal8Bit(fname.c_str())); + SGPath path(SGPath::fromUtf8(fname)); if (path.isRelative()) path = model->FindFullPathName(path); diff --git a/src/Main/options.cxx b/src/Main/options.cxx index c93ec85fe..75cded346 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -719,7 +719,7 @@ clearLocation () static int fgOptAddon(const char *arg) { - const SGPath addonPath = SGPath::fromLocal8Bit(arg); + const SGPath addonPath = SGPath::fromUtf8(arg); const auto& addonManager = addons::AddonManager::instance(); try { @@ -904,7 +904,7 @@ fgOptRoc( const char *arg ) static int fgOptFgScenery( const char *arg ) { - globals->append_fg_scenery(SGPath::pathsFromLocal8Bit(arg)); + globals->append_fg_scenery(SGPath::pathsFromUtf8(arg)); return FG_OPTIONS_OK; } @@ -921,7 +921,7 @@ fgOptEnhancedLighting( const char *arg ) static int fgOptAllowNasalRead( const char *arg ) { - PathList paths = SGPath::pathsFromLocal8Bit(arg); + PathList paths = SGPath::pathsFromUtf8(arg); if(paths.size() == 0) { SG_LOG(SG_GENERAL, SG_WARN, "--allow-nasal-read requires a list of directories to allow"); } @@ -1121,7 +1121,7 @@ fgOptLogDir(const char* arg) if (!strcmp(arg, "desktop")) { dirPath = SGPath::desktop(); } else { - dirPath = SGPath::fromLocal8Bit(arg); + dirPath = SGPath::fromUtf8(arg); } if (!dirPath.isDir()) { @@ -1556,7 +1556,7 @@ fgOptLoadTape(const char* arg) class DelayedTapeLoader : SGPropertyChangeListener { public: DelayedTapeLoader( const char * tape ) : - _tape(SGPath::fromLocal8Bit(tape)) + _tape(SGPath::fromUtf8(tape)) { SGPropertyNode_ptr n = fgGetNode("/sim/signals/fdm-initialized", true); n->addChangeListener( this ); @@ -2104,7 +2104,7 @@ void Options::init(int argc, char **argv, const SGPath& appDataPath) processArgResult(result); } else { // XML properties file - SGPath f = SGPath::fromLocal8Bit(argv[i]); + SGPath f = SGPath::fromUtf8(argv[i]); if (!f.exists()) { SG_LOG(SG_GENERAL, SG_ALERT, "config file not found:" << f); } else { @@ -2191,7 +2191,7 @@ void Options::init(int argc, char **argv, const SGPath& appDataPath) void Options::initPaths() { for (const string& pathOpt : valuesForOption("fg-aircraft")) { - PathList paths = SGPath::pathsFromLocal8Bit(pathOpt); + PathList paths = SGPath::pathsFromUtf8(pathOpt); globals->append_aircraft_paths(paths); } @@ -2241,7 +2241,7 @@ void Options::initAircraft() } if (isOptionSet("aircraft-dir")) { - SGPath aircraftDirPath = SGPath::fromLocal8Bit(valueForOption("aircraft-dir").c_str()); + SGPath aircraftDirPath = SGPath::fromUtf8(valueForOption("aircraft-dir")); globals->append_read_allowed_paths(aircraftDirPath); // Set this now, so it's available in FindAndCacheAircraft. Use realpath() @@ -2565,8 +2565,7 @@ OptionResult Options::processOptions() } // Download dir fix-up - SGPath downloadDir = SGPath::fromLocal8Bit( - valueForOption("download-dir").c_str()); + SGPath downloadDir = SGPath::fromUtf8(valueForOption("download-dir")); if (downloadDir.isNull()) { downloadDir = defaultDownloadDir(); SG_LOG(SG_GENERAL, SG_INFO, @@ -2589,8 +2588,7 @@ OptionResult Options::processOptions() globals->set_download_dir(downloadDir); // Texture Cache directory handling - SGPath textureCacheDir = SGPath::fromLocal8Bit( - valueForOption("texture-cache-dir").c_str()); + SGPath textureCacheDir = SGPath::fromUtf8(valueForOption("texture-cache-dir")); if (textureCacheDir.isNull()) { textureCacheDir = defaultTextureCacheDir(); SG_LOG(SG_GENERAL, SG_INFO, @@ -2612,8 +2610,7 @@ OptionResult Options::processOptions() // TerraSync directory fixup - SGPath terrasyncDir = SGPath::fromLocal8Bit( - valueForOption("terrasync-dir").c_str()); + SGPath terrasyncDir = SGPath::fromUtf8(valueForOption("terrasync-dir")); if (terrasyncDir.isNull()) { terrasyncDir = downloadDir / "TerraSync"; // No “default” qualifier here, because 'downloadDir' may be non-default @@ -2946,13 +2943,13 @@ void Options::setupRoot(int argc, char **argv) } if (isOptionSet("fg-root")) { - root = SGPath::fromLocal8Bit(valueForOption("fg-root").c_str()); // easy! + root = SGPath::fromUtf8(valueForOption("fg-root")); // easy! SG_LOG(SG_GENERAL, SG_INFO, "set from command-line argument: fg_root = " << root ); } else { // Next check if fg-root is set as an env variable char *envp = ::getenv( "FG_ROOT" ); if ( envp != nullptr ) { - root = SGPath::fromLocal8Bit(envp); + root = SGPath::fromEnv("FG_ROOT"); SG_LOG(SG_GENERAL, SG_INFO, "set from FG_ROOT env var: fg_root = " << root ); } else { #if defined(HAVE_QT) diff --git a/src/Model/acmodel.cxx b/src/Model/acmodel.cxx index 6b36dd362..5f2d59717 100644 --- a/src/Model/acmodel.cxx +++ b/src/Model/acmodel.cxx @@ -110,7 +110,7 @@ FGAircraftModel::init () // no models loaded, load the glider instead if (!_aircraft.get()) { SG_LOG(SG_AIRCRAFT, SG_ALERT, "(Falling back to glider.ac.)"); - osg::Node* model = fgLoad3DModelPanel( SGPath::fromLocal8Bit("Models/Geometry/glider.ac"), + osg::Node* model = fgLoad3DModelPanel( SGPath::fromUtf8("Models/Geometry/glider.ac"), globals->get_props()); _aircraft.reset(new SGModelPlacement); _aircraft->init(model); diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index 45ea02307..5a407c190 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -247,34 +247,6 @@ typedef nasal::Ghost NasalTimeStampObj; /////////////////////////////////////////////////////////////////////////// -// Read and return file contents in a single buffer. Note use of -// stat() to get the file size. This is a win32 function, believe it -// or not. :) Note the REALLY IMPORTANT use of the "b" flag to fopen. -// Text mode brain damage will kill us if we're trying to do bytewise -// I/O. -static char* readfile(const char* file, int* lenOut) -{ - struct stat data; - if(stat(file, &data) != 0) return 0; - FILE* f = fopen(file, "rb"); - if(!f) return 0; - char* buf = new char[data.st_size]; - *lenOut = fread(buf, 1, data.st_size, f); - fclose(f); - if(*lenOut != data.st_size) { - // Shouldn't happen, but warn anyway since it represents a - // platform bug and not a typical runtime error (missing file, - // etc...) - SG_LOG(SG_NASAL, SG_ALERT, - "ERROR in Nasal initialization: " << - "short count returned from fread() of " << file << - ". Check your C library!"); - delete[] buf; - return 0; - } - return buf; -} - FGNasalSys::FGNasalSys() : _inited(false) { @@ -1292,19 +1264,21 @@ void FGNasalSys::logNasalStack(naContext context) // name. bool FGNasalSys::loadModule(SGPath file, const char* module) { - int len = 0; - std::string pdata = file.local8BitStr(); - char* buf = readfile(pdata.c_str(), &len); - if(!buf) { - SG_LOG(SG_NASAL, SG_ALERT, - "Nasal error: could not read script file " << file - << " into module " << module); + if (!file.exists()) { + SG_LOG(SG_NASAL, SG_ALERT, "Cannot load module, missing file:" << file); return false; } - bool ok = createModule(module, pdata.c_str(), buf, len); - delete[] buf; - return ok; + sg_ifstream file_in(file); + string buf; + while (!file_in.eof()) { + char bytes[8192]; + file_in.read(bytes, 8192); + buf.append(bytes, file_in.gcount()); + } + file_in.close(); + auto pathStr = file.utf8Str(); + return createModule(module, pathStr.c_str(), buf.data(), buf.length()); } // Parse and run. Save the local variables namespace, as it will From 27410761f34f82f520fe8c36d9b0185be4ffb198 Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 12 Mar 2020 14:23:44 +0000 Subject: [PATCH 55/98] Lots of UTF8 path correctness fixes --- src/AIModel/AIBase.cxx | 2 +- src/Canvas/FGCanvasSystemAdapter.cxx | 10 +++++----- src/Cockpit/NavDisplay.cxx | 2 +- src/Cockpit/wxradar.cxx | 2 +- src/Environment/ephemeris.cxx | 2 +- src/GUI/AddonsModel.cxx | 2 +- src/GUI/FGFontCache.cxx | 30 +++++++++++----------------- src/Main/fg_init.cxx | 5 ++--- src/Main/globals.cxx | 4 +--- src/Navaids/SHPParser.cxx | 7 ++++++- src/Scenery/tilemgr.cxx | 4 ++-- src/Time/TimeManager.cxx | 4 +--- 12 files changed, 34 insertions(+), 40 deletions(-) diff --git a/src/AIModel/AIBase.cxx b/src/AIModel/AIBase.cxx index 9271f2fc9..e0fb4f256 100644 --- a/src/AIModel/AIBase.cxx +++ b/src/AIModel/AIBase.cxx @@ -544,7 +544,7 @@ std::vector FGAIBase::resolveModelPath(ModelSearchOrder searchOrder for (SGPath p : globals->get_data_paths("AI")) { p.append(model_path); if (p.exists()) { - SG_LOG(SG_AI, SG_DEBUG, "Found AI model: " << p.local8BitStr()); + SG_LOG(SG_AI, SG_DEBUG, "Found AI model: " << p); path_list.push_back(p.local8BitStr()); break; } diff --git a/src/Canvas/FGCanvasSystemAdapter.cxx b/src/Canvas/FGCanvasSystemAdapter.cxx index bacb73d97..7743b020e 100644 --- a/src/Canvas/FGCanvasSystemAdapter.cxx +++ b/src/Canvas/FGCanvasSystemAdapter.cxx @@ -52,7 +52,7 @@ namespace canvas "canvas::Text: using font file " << path ); - simgear::canvas::FontPtr font = osgText::readFontFile(path.local8BitStr()); + simgear::canvas::FontPtr font = osgText::readFontFile(path.utf8Str()); if( !font ) SG_LOG ( @@ -86,9 +86,9 @@ namespace canvas SGPath valid_path = fgValidatePath(p, false); if( !valid_path.isNull() ) #if OSG_VERSION_LESS_THAN(3,4,0) - return osgDB::readRefImageFile(valid_path.local8BitStr()); + return osgDB::readRefImageFile(valid_path.utf8Str()); #else - return osgDB::readRefImageFile(valid_path.local8BitStr()); + return osgDB::readRefImageFile(valid_path.utf8Str()); #endif SG_LOG(SG_IO, SG_ALERT, "canvas::Image: reading '" << path << "' denied"); @@ -98,9 +98,9 @@ namespace canvas SGPath tpath = globals->resolve_resource_path(path); if( !tpath.isNull() ) #if OSG_VERSION_LESS_THAN(3,4,0) - return osgDB::readImageFile(tpath.local8BitStr()); + return osgDB::readImageFile(tpath.utf8Str()); #else - return osgDB::readRefImageFile(tpath.local8BitStr()); + return osgDB::readRefImageFile(tpath.utf8Str()); #endif SG_LOG(SG_IO, SG_ALERT, "canvas::Image: No such image: '" << path << "'"); diff --git a/src/Cockpit/NavDisplay.cxx b/src/Cockpit/NavDisplay.cxx index bfabb9065..9885d81ff 100644 --- a/src/Cockpit/NavDisplay.cxx +++ b/src/Cockpit/NavDisplay.cxx @@ -777,7 +777,7 @@ NavDisplay::updateFont() } osg::ref_ptr fontOptions = new osgDB::ReaderWriter::Options("monochrome"); - osg::ref_ptr font = osgText::readFontFile(tpath.local8BitStr(), fontOptions.get()); + osg::ref_ptr font = osgText::readFontFile(tpath.utf8Str(), fontOptions.get()); if (font != 0) { _font = font; diff --git a/src/Cockpit/wxradar.cxx b/src/Cockpit/wxradar.cxx index 1165d93de..61a1b49bd 100644 --- a/src/Cockpit/wxradar.cxx +++ b/src/Cockpit/wxradar.cxx @@ -1084,7 +1084,7 @@ wxRadarBg::updateFont() } osg::ref_ptr fontOptions = new osgDB::ReaderWriter::Options("monochrome"); - osg::ref_ptr font = osgText::readFontFile(tpath.local8BitStr(), fontOptions.get()); + osg::ref_ptr font = osgText::readFontFile(tpath.utf8Str(), fontOptions.get()); if (font != 0) { _font = font; diff --git a/src/Environment/ephemeris.cxx b/src/Environment/ephemeris.cxx index db9d59b1e..046e50084 100644 --- a/src/Environment/ephemeris.cxx +++ b/src/Environment/ephemeris.cxx @@ -54,7 +54,7 @@ void Ephemeris::init() { SGPath ephem_data_path(globals->get_fg_root()); ephem_data_path.append("Astro"); - _impl.reset(new SGEphemeris(ephem_data_path.local8BitStr())); + _impl.reset(new SGEphemeris(ephem_data_path)); tieStar("/ephemeris/sun/xs", _impl->get_sun(), &Star::getxs); tieStar("/ephemeris/sun/ys", _impl->get_sun(), &Star::getys); diff --git a/src/GUI/AddonsModel.cxx b/src/GUI/AddonsModel.cxx index 1d605051d..ba4ee041f 100644 --- a/src/GUI/AddonsModel.cxx +++ b/src/GUI/AddonsModel.cxx @@ -37,7 +37,7 @@ AddonsModel::AddonsModel(QObject* pr) : int roleValue = IdRole; for (auto it = m_roles.begin(); it != m_roles.end(); ++it) { - QByteArray name = (*it).toLocal8Bit(); + QByteArray name = it->toUtf8(); m_roleToName[roleValue] = name; m_nameToRole[*it] = roleValue++; } diff --git a/src/GUI/FGFontCache.cxx b/src/GUI/FGFontCache.cxx index fda16272b..6c6b02d20 100644 --- a/src/GUI/FGFontCache.cxx +++ b/src/GUI/FGFontCache.cxx @@ -27,6 +27,7 @@ #include #include +#include #include
@@ -211,26 +212,19 @@ FGFontCache::getfntpath(const std::string& name) bool FGFontCache::initializeFonts() { - static std::string fontext("txf"); + static std::string fontext(",txf"); init(); - std::string fontPath = _path.local8BitStr(); - ulDir* fontdir = ulOpenDir(fontPath.c_str()); - if (!fontdir) - return false; - const ulDirEnt *dirEntry; - while ((dirEntry = ulReadDir(fontdir)) != 0) { - SGPath path(_path); - path.append(dirEntry->d_name); - if (path.extension() == fontext) { - fntTexFont* f = new fntTexFont; - std::string ps = path.local8BitStr(); - if (f->load(ps.c_str())) - _texFonts[std::string(dirEntry->d_name)] = f; - else - delete f; - } + + auto dir = simgear::Dir(_path); + for (auto p : dir.children(simgear::Dir::TYPE_FILE, fontext)) { + fntTexFont* f = new fntTexFont; + // FIXME : this will fail when fonts are on UTF8 paths + if (f->load(p.local8BitStr())) + _texFonts[p.file()] = f; + else + delete f; } - ulCloseDir(fontdir); + return true; } diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index cfdda7660..2ff531d71 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -483,7 +483,7 @@ bool fgInitHome() } char buf[16]; - std::string ps = pidPath.local8BitStr(); + std::string ps = pidPath.utf8Str(); // do open+unlink trick to the file is deleted on exit, even if we // crash or exit(-1) @@ -873,8 +873,7 @@ void fgCreateSubsystems(bool duringReset) { SGPath mpath( globals->get_fg_root() ); mpath.append( fgGetString("/sim/rendering/materials-file") ); - if ( ! globals->get_matlib()->load(globals->get_fg_root().local8BitStr(), mpath.local8BitStr(), - globals->get_props()) ) { + if ( ! globals->get_matlib()->load(globals->get_fg_root(), mpath, globals->get_props()) ) { throw sg_io_exception("Error loading materials file", mpath); } diff --git a/src/Main/globals.cxx b/src/Main/globals.cxx index bb9b5d9f4..1d5025b47 100644 --- a/src/Main/globals.cxx +++ b/src/Main/globals.cxx @@ -364,9 +364,7 @@ void FGGlobals::append_fg_scenery (const SGPath &path) // tell the ResouceManager about the scenery path // needed to load Models from this scenery path - simgear::ResourceManager::instance()->addBasePath(abspath.local8BitStr(), - simgear::ResourceManager::PRIORITY_DEFAULT); - simgear::Dir dir(abspath); + simgear::ResourceManager::instance()->addBasePath(abspath, simgear::ResourceManager::PRIORITY_DEFAULT); fg_scenery.push_back(abspath); extra_read_allowed_paths.push_back(abspath); diff --git a/src/Navaids/SHPParser.cxx b/src/Navaids/SHPParser.cxx index 2d31bf237..58f597760 100644 --- a/src/Navaids/SHPParser.cxx +++ b/src/Navaids/SHPParser.cxx @@ -91,8 +91,13 @@ namespace flightgear void SHPParser::parsePolyLines(const SGPath& aPath, PolyLine::Type aTy, PolyLineList& aResult, bool aClosed) { - std::string s = aPath.local8BitStr(); +#if defined(SG_WINDOWS) + const auto ws = aPath.wstr(); + gzFile file = gzopen_w(ws.c_str(), "rb"); +#else + const auto s = aPath.utf8Str(); gzFile file = gzopen(s.c_str(), "rb"); +#endif if (!file) { throw sg_io_exception("couldn't open file:", aPath); } diff --git a/src/Scenery/tilemgr.cxx b/src/Scenery/tilemgr.cxx index 9eb762044..e6049a613 100644 --- a/src/Scenery/tilemgr.cxx +++ b/src/Scenery/tilemgr.cxx @@ -172,10 +172,10 @@ void FGTileMgr::reinit() for (it = sc.begin(); it != sc.end(); ++it) { fp.push_back(it->local8BitStr()); } - _options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().local8BitStr()); + _options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().utf8Str()); if (_terra_sync) { - _options->setPluginStringData("SimGear::TERRASYNC_ROOT", globals->get_terrasync_dir().local8BitStr()); + _options->setPluginStringData("SimGear::TERRASYNC_ROOT", globals->get_terrasync_dir().utf8Str()); } if (!_disableNasalHooks->getBoolValue()) diff --git a/src/Time/TimeManager.cxx b/src/Time/TimeManager.cxx index c00bab291..1f6325cb0 100644 --- a/src/Time/TimeManager.cxx +++ b/src/Time/TimeManager.cxx @@ -386,9 +386,7 @@ void TimeManager::throttleUpdateRate() // periodic time updater wrapper void TimeManager::updateLocalTime() { - SGPath zone(globals->get_fg_root()); - zone.append("Timezone"); - _impl->updateLocal(globals->get_aircraft_position(), zone.local8BitStr()); + _impl->updateLocal(globals->get_aircraft_position(), globals->get_fg_root() / "Timezone"); } void TimeManager::initTimeOffset() From 19eaaf04ee116b83fe7228d2d9d89bfd3ca02b59 Mon Sep 17 00:00:00 2001 From: James Turner Date: Fri, 13 Mar 2020 09:51:21 +0000 Subject: [PATCH 56/98] Remove some redundant CMake output --- CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b54b97379..bebdc5416 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -370,10 +370,7 @@ if (ENABLE_QT) find_package(Qt5 5.4 COMPONENTS Widgets Network Qml Quick Svg) if (Qt5Widgets_FOUND) message(STATUS "Will enable Qt launcher GUI") - message(STATUS " Qt5Widgets version: ${Qt5Widgets_VERSION_STRING}") - message(STATUS " Qt5Widgets include dir: ${Qt5Widgets_INCLUDE_DIRS}") set(HAVE_QT 1) - include (Translations) else() # don't try to build FGQCanvas if Qt wasn't found correctly From e492ddd5a0fe25a8e6dec360477bda811172c20a Mon Sep 17 00:00:00 2001 From: James Turner Date: Fri, 13 Mar 2020 09:53:49 +0000 Subject: [PATCH 57/98] Event more local8Bit -> UTF8 fixes --- src/Main/fg_scene_commands.cxx | 4 ++-- src/Main/main.cxx | 2 +- src/Model/acmodel.cxx | 2 +- src/Scenery/tilemgr.cxx | 5 ++--- src/Scripting/NasalSys.cxx | 2 +- src/Sound/VoiceSynthesizer.cxx | 2 +- src/Sound/flitevoice.cxx | 2 +- src/Viewer/CameraGroup_compositor.cxx | 2 +- src/Viewer/renderer_compositor.cxx | 2 +- src/Viewer/renderer_legacy.cxx | 6 +++--- 10 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/Main/fg_scene_commands.cxx b/src/Main/fg_scene_commands.cxx index beb907584..4f942a245 100644 --- a/src/Main/fg_scene_commands.cxx +++ b/src/Main/fg_scene_commands.cxx @@ -247,8 +247,8 @@ do_materials_reload (const SGPropertyNode * arg, SGPropertyNode * root) SGMaterialLib* new_matlib = new SGMaterialLib; SGPath mpath( globals->get_fg_root() ); mpath.append( fgGetString("/sim/rendering/materials-file") ); - bool loaded = new_matlib->load(globals->get_fg_root().local8BitStr(), - mpath.local8BitStr(), + bool loaded = new_matlib->load(globals->get_fg_root(), + mpath, globals->get_props()); if ( ! loaded ) { diff --git a/src/Main/main.cxx b/src/Main/main.cxx index c856b4c33..ec7842878 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -359,7 +359,7 @@ static void fgIdleFunction ( void ) { flightgear::initPosition(); flightgear::initTowerLocationListener(); - simgear::SGModelLib::init(globals->get_fg_root().local8BitStr(), globals->get_props()); + simgear::SGModelLib::init(globals->get_fg_root().utf8Str(), globals->get_props()); auto timeManager = globals->get_subsystem(); timeManager->init(); diff --git a/src/Model/acmodel.cxx b/src/Model/acmodel.cxx index 5f2d59717..bff3d8d0d 100644 --- a/src/Model/acmodel.cxx +++ b/src/Model/acmodel.cxx @@ -33,7 +33,7 @@ static osg::Node * fgLoad3DModelPanel(const SGPath &path, SGPropertyNode *prop_root) { bool loadPanels = true; - osg::Node* node = simgear::SGModelLib::loadModel(path.local8BitStr(), prop_root, NULL, loadPanels); + osg::Node* node = simgear::SGModelLib::loadModel(path.utf8Str(), prop_root, NULL, loadPanels); if (node) node->setNodeMask(~SG_NODEMASK_TERRAIN_BIT); return node; diff --git a/src/Scenery/tilemgr.cxx b/src/Scenery/tilemgr.cxx index e6049a613..a29a6aa4b 100644 --- a/src/Scenery/tilemgr.cxx +++ b/src/Scenery/tilemgr.cxx @@ -168,9 +168,8 @@ void FGTileMgr::reinit() osgDB::FilePathList &fp = _options->getDatabasePathList(); const PathList &sc = globals->get_fg_scenery(); fp.clear(); - PathList::const_iterator it; - for (it = sc.begin(); it != sc.end(); ++it) { - fp.push_back(it->local8BitStr()); + for (auto it = sc.begin(); it != sc.end(); ++it) { + fp.push_back(it->utf8Str()); } _options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().utf8Str()); diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index 5a407c190..836987835 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -774,7 +774,7 @@ static naRef f_open(naContext c, naRef me, int argc, naRef* args) std::wstring wmodestr = simgear::strutils::convertUtf8ToWString(modestr); f = _wfopen(fp.c_str(), wmodestr.c_str()); #else - std::string fp = filename.local8BitStr(); + std::string fp = filename.utf8Str(); f = fopen(fp.c_str(), modestr); #endif if(!f) naRuntimeError(c, strerror(errno)); diff --git a/src/Sound/VoiceSynthesizer.cxx b/src/Sound/VoiceSynthesizer.cxx index ef51c3b5b..c67068eee 100644 --- a/src/Sound/VoiceSynthesizer.cxx +++ b/src/Sound/VoiceSynthesizer.cxx @@ -76,7 +76,7 @@ string FLITEVoiceSynthesizer::getVoicePath( voice_t voice ) { if( voice < 0 || voice >= VOICE_UNKNOWN ) return string(""); SGPath voicePath = globals->get_fg_root() / "ATC" / VOICE_FILES[voice]; - return voicePath.local8BitStr(); + return voicePath.utf8Str(); } string FLITEVoiceSynthesizer::getVoicePath( const string & voice ) diff --git a/src/Sound/flitevoice.cxx b/src/Sound/flitevoice.cxx index 9cf0e4687..235f03e26 100644 --- a/src/Sound/flitevoice.cxx +++ b/src/Sound/flitevoice.cxx @@ -41,7 +41,7 @@ FGFLITEVoice::FGFLITEVoice(FGVoiceMgr * mgr, const SGPropertyNode_ptr node, cons SGPath voice = globals->get_fg_root() / "ATC" / node->getStringValue("htsvoice", "cmu_us_arctic_slt.htsvoice"); - _synthesizer = new FLITEVoiceSynthesizer(voice.local8BitStr()); + _synthesizer = new FLITEVoiceSynthesizer(voice.utf8Str()); SGSoundMgr *smgr = globals->get_subsystem(); _sgr = smgr->find(sampleGroupRefName, true); diff --git a/src/Viewer/CameraGroup_compositor.cxx b/src/Viewer/CameraGroup_compositor.cxx index 5631e8950..98c190b39 100644 --- a/src/Viewer/CameraGroup_compositor.cxx +++ b/src/Viewer/CameraGroup_compositor.cxx @@ -690,7 +690,7 @@ void CameraGroup::buildCamera(SGPropertyNode* cameraNode) std::string compositor_path = cameraNode->getStringValue("compositor", default_compositor.c_str()); osg::ref_ptr options = - SGReaderWriterOptions::fromPath(globals->get_fg_root().local8BitStr()); + SGReaderWriterOptions::fromPath(globals->get_fg_root()); options->setPropertyNode(globals->get_props()); Compositor *compositor = Compositor::create(_viewer, window->gc, diff --git a/src/Viewer/renderer_compositor.cxx b/src/Viewer/renderer_compositor.cxx index 151d894b2..032b58748 100644 --- a/src/Viewer/renderer_compositor.cxx +++ b/src/Viewer/renderer_compositor.cxx @@ -529,7 +529,7 @@ FGRenderer::setupView( void ) // Moon diameter: 3,476 kilometers // Sun diameter: 1,390,000 kilometers osg::ref_ptr opt; - opt = simgear::SGReaderWriterOptions::fromPath(globals->get_fg_root().local8BitStr()); + opt = simgear::SGReaderWriterOptions::fromPath(globals->get_fg_root()); opt->setPropertyNode(globals->get_props()); _sky->build( 80000.0, 80000.0, 463.3, 361.8, diff --git a/src/Viewer/renderer_legacy.cxx b/src/Viewer/renderer_legacy.cxx index b4dfbd68f..8d4c01db8 100644 --- a/src/Viewer/renderer_legacy.cxx +++ b/src/Viewer/renderer_legacy.cxx @@ -1136,7 +1136,7 @@ FGRenderer::buildDeferredFullscreenCamera( flightgear::CameraInfo* info, const F g->setUseDisplayList(false); simgear::EffectGeode* eg = new simgear::EffectGeode; osg::ref_ptr opt; - opt = SGReaderWriterOptions::fromPath(globals->get_fg_root().local8BitStr()); + opt = SGReaderWriterOptions::fromPath(globals->get_fg_root()); opt->setPropertyNode(globals->get_props()); simgear::Effect* effect = simgear::makeEffect(pass->effect, true, opt.get()); if (effect) { @@ -1189,7 +1189,7 @@ FGRenderer::buildDeferredDisplayCamera( osg::Camera* camera, flightgear::CameraI g->setUseDisplayList(false); //DEBUG simgear::EffectGeode* eg = new simgear::EffectGeode; osg::ref_ptr opt; - opt = SGReaderWriterOptions::fromPath(globals->get_fg_root().local8BitStr()); + opt = SGReaderWriterOptions::fromPath(globals->get_fg_root()); opt->setPropertyNode(globals->get_props()); simgear::Effect* effect = simgear::makeEffect(stage->effect, true, opt.get()); if (!effect) { @@ -1422,7 +1422,7 @@ FGRenderer::setupView( void ) // Moon diameter: 3,476 kilometers // Sun diameter: 1,390,000 kilometers osg::ref_ptr opt; - opt = SGReaderWriterOptions::fromPath(globals->get_fg_root().local8BitStr()); + opt = SGReaderWriterOptions::fromPath(globals->get_fg_root()); opt->setPropertyNode(globals->get_props()); _sky->build( 80000.0, 80000.0, 463.3, 361.8, From 2c9d64dcc66892b51610df61b1c083aeeab59ff7 Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Fri, 13 Mar 2020 11:13:30 +0100 Subject: [PATCH 58/98] Fix broken sound mute function Fixes #2146 --- src/Main/fg_props.cxx | 10 ---------- src/Sound/soundmanager.cxx | 6 ++++-- src/Sound/soundmanager.hxx | 1 + 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/Main/fg_props.cxx b/src/Main/fg_props.cxx index 2058f0db1..eee6d0006 100644 --- a/src/Main/fg_props.cxx +++ b/src/Main/fg_props.cxx @@ -230,16 +230,6 @@ setFreeze (bool f) { frozen = f; - // Stop sound on a pause - SGSoundMgr *smgr = globals->get_subsystem(); - if ( smgr != NULL ) { - if ( f ) { - smgr->suspend(); - } else if (fgGetBool("/sim/sound/working")) { - smgr->resume(); - } - } - // Pause the particle system simgear::Particles::setFrozen(f); } diff --git a/src/Sound/soundmanager.cxx b/src/Sound/soundmanager.cxx index bf6b72a23..a78a79514 100644 --- a/src/Sound/soundmanager.cxx +++ b/src/Sound/soundmanager.cxx @@ -76,6 +76,8 @@ void FGSoundManager::init() _velocityEastFPS = fgGetNode("velocities/speed-east-fps", true); _velocityDownFPS = fgGetNode("velocities/speed-down-fps", true); + _frozen = fgGetNode("sim/freeze/master"); + SGPropertyNode_ptr scenery_loaded = fgGetNode("sim/sceneryloaded", true); scenery_loaded->addChangeListener(_listener.get()); @@ -159,9 +161,9 @@ bool FGSoundManager::stationaryView() const // Actual sound update is triggered by the subsystem manager. void FGSoundManager::update(double dt) { - if (is_active() && _is_initialized && _sound_working->getBoolValue()) + if (is_working() && _is_initialized && _sound_working->getBoolValue()) { - bool enabled = _sound_enabled->getBoolValue(); + bool enabled = _sound_enabled->getBoolValue() && !_frozen->getBoolValue(); if (enabled != _enabled) { if (enabled) diff --git a/src/Sound/soundmanager.hxx b/src/Sound/soundmanager.hxx index 94810a6be..b4387a6b6 100644 --- a/src/Sound/soundmanager.hxx +++ b/src/Sound/soundmanager.hxx @@ -63,6 +63,7 @@ private: bool _is_initialized, _enabled; SGPropertyNode_ptr _sound_working, _sound_enabled, _volume, _device_name; SGPropertyNode_ptr _velocityNorthFPS, _velocityEastFPS, _velocityDownFPS; + SGPropertyNode_ptr _frozen; std::unique_ptr _listener; std::map _synthesizers; From 7e9930e871d7ab606633479cb26c0fa96429eb78 Mon Sep 17 00:00:00 2001 From: James Turner Date: Fri, 13 Mar 2020 11:27:11 +0000 Subject: [PATCH 59/98] Move fnt code from PLIB into 3rdparty This enables us to fix TXF loading to use UTF8 paths, required for handling non-ASCII paths on Windows. --- 3rdparty/CMakeLists.txt | 2 + 3rdparty/fonts/CMakeLists.txt | 14 + 3rdparty/fonts/README | 2 + 3rdparty/fonts/fnt.cxx | 257 +++++++ 3rdparty/fonts/fnt.h | 357 +++++++++ 3rdparty/fonts/fntBitmap.cxx | 1017 +++++++++++++++++++++++++ 3rdparty/fonts/fntLocal.h | 112 +++ 3rdparty/fonts/fntTXF.cxx | 341 +++++++++ CMakeLists.txt | 2 +- CMakeModules/FindPLIB.cmake | 4 +- CMakeModules/SetupFGFSLibraries.cmake | 2 + src/Cockpit/panel.cxx | 2 +- src/GUI/FGFontCache.cxx | 17 +- src/GUI/fonts.cxx | 4 +- src/Instrumentation/HUD/HUD.cxx | 2 +- 15 files changed, 2118 insertions(+), 17 deletions(-) create mode 100644 3rdparty/fonts/CMakeLists.txt create mode 100644 3rdparty/fonts/README create mode 100644 3rdparty/fonts/fnt.cxx create mode 100644 3rdparty/fonts/fnt.h create mode 100644 3rdparty/fonts/fntBitmap.cxx create mode 100644 3rdparty/fonts/fntLocal.h create mode 100755 3rdparty/fonts/fntTXF.cxx mode change 100644 => 100755 src/GUI/FGFontCache.cxx diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 2bf7bfdc0..f8654d336 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -28,6 +28,8 @@ if (ENABLE_HID_INPUT) add_subdirectory(hidapi) endif() +add_subdirectory(fonts) + if (ENABLE_PLIB_JOYSTICK) add_subdirectory(joystick) endif() diff --git a/3rdparty/fonts/CMakeLists.txt b/3rdparty/fonts/CMakeLists.txt new file mode 100644 index 000000000..04982db1b --- /dev/null +++ b/3rdparty/fonts/CMakeLists.txt @@ -0,0 +1,14 @@ + +set (FNT_SOURCES + fnt.cxx + fntTXF.cxx + fntBitmap.cxx +) + +add_library(PLIBFont STATIC ${FNT_SOURCES}) + +target_link_libraries(PLIBFont SimGearCore) + +target_include_directories(PLIBFont PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + + diff --git a/3rdparty/fonts/README b/3rdparty/fonts/README new file mode 100644 index 000000000..6ae250a9e --- /dev/null +++ b/3rdparty/fonts/README @@ -0,0 +1,2 @@ +This is PLIB's 'fnt' library, extracted and modified +- added UTF8 path support, especially for Windows \ No newline at end of file diff --git a/3rdparty/fonts/fnt.cxx b/3rdparty/fonts/fnt.cxx new file mode 100644 index 000000000..5cee71608 --- /dev/null +++ b/3rdparty/fonts/fnt.cxx @@ -0,0 +1,257 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fnt.cxx 1939 2004-08-05 00:41:53Z puggles $ +*/ + +#include "fntLocal.h" + +#include +#include + +fntFont:: fntFont () {} +fntFont::~fntFont () {} + +int fntTexFont::load ( const SGPath& path, GLenum mag, GLenum min ) +{ + if (path.extension() == "txf") { + return loadTXF ( path, mag, min ) ; + } else { + const auto ps = path.utf8Str(); + fntSetError ( SG_WARN, + "fnt::load: Error - Unrecognised file format for '%s'", ps.c_str() ) ; + return FNT_FALSE ; + } +} + +float fntTexFont::low_putch ( sgVec3 curpos, float pointsize, + float italic, char c ) +{ + unsigned int cc = (unsigned char) c ; + + /* Auto case-convert if character is absent from font. */ + + if ( ! exists [ cc ] ) + { + if ( cc >= 'A' && cc <= 'Z' ) + cc = cc - 'A' + 'a' ; + else + if ( cc >= 'a' && cc <= 'z' ) + cc = cc - 'a' + 'A' ; + + if ( cc == ' ' ) + { + curpos [ 0 ] += pointsize / 2.0f ; + return pointsize / 2.0f ; + } + } + + /* + We might want to consider making some absent characters from + others (if they exist): lowercase 'l' could be made into digit '1' + or letter 'O' into digit '0'...or vice versa. We could also + make 'b', 'd', 'p' and 'q' by mirror-imaging - this would + save a little more texture memory in some fonts. + */ + + if ( ! exists [ cc ] ) + return 0.0f ; + + glBegin ( GL_TRIANGLE_STRIP ) ; + glTexCoord2f ( t_left [cc], t_bot[cc] ) ; + glVertex3f ( curpos[0] + v_left [cc] * pointsize, + curpos[1] + v_bot [cc] * pointsize, + curpos[2] ) ; + + glTexCoord2f ( t_left [cc], t_top[cc] ) ; + glVertex3f ( curpos[0] + (italic + v_left [cc]) * pointsize, + curpos[1] + v_top [cc] * pointsize, + curpos[2] ) ; + + glTexCoord2f ( t_right[cc], t_bot[cc] ) ; + glVertex3f ( curpos[0] + v_right[cc] * pointsize, + curpos[1] + v_bot [cc] * pointsize, + curpos[2] ) ; + + glTexCoord2f ( t_right[cc], t_top[cc] ) ; + glVertex3f ( curpos[0] + (italic + v_right[cc]) * pointsize, + curpos[1] + v_top [cc] * pointsize, + curpos[2] ) ; + glEnd () ; + + float ww = ( gap + ( fixed_pitch ? width : widths[cc] ) ) * pointsize ; + curpos[0] += ww ; + return ww ; +} + + + +void fntTexFont::setGlyph ( char c, float wid, + float tex_left, float tex_right, + float tex_bot , float tex_top , + float vtx_left, float vtx_right, + float vtx_bot , float vtx_top ) +{ + unsigned int cc = (unsigned char) c ; + + exists[cc] = FNT_TRUE ; + + widths[cc] = wid; + + t_left[cc] = tex_left ; t_right[cc] = tex_right ; + t_bot [cc] = tex_bot ; t_top [cc] = tex_top ; + + v_left[cc] = vtx_left ; v_right[cc] = vtx_right ; + v_bot [cc] = vtx_bot ; v_top [cc] = vtx_top ; +} + + +int fntTexFont::getGlyph ( char c, float* wid, + float *tex_left, float *tex_right, + float *tex_bot , float *tex_top , + float *vtx_left, float *vtx_right, + float *vtx_bot , float *vtx_top ) +{ + unsigned int cc = (unsigned char) c ; + + if ( ! exists[cc] ) return FNT_FALSE ; + + if ( wid != NULL ) *wid = widths [cc] ; + + if ( tex_left != NULL ) *tex_left = t_left [cc] ; + if ( tex_right != NULL ) *tex_right = t_right[cc] ; + if ( tex_bot != NULL ) *tex_bot = t_bot [cc] ; + if ( tex_top != NULL ) *tex_top = t_top [cc] ; + + if ( vtx_left != NULL ) *vtx_left = v_left [cc] ; + if ( vtx_right != NULL ) *vtx_right = v_right[cc] ; + if ( vtx_bot != NULL ) *vtx_bot = v_bot [cc] ; + if ( vtx_top != NULL ) *vtx_top = v_top [cc] ; + + return FNT_TRUE ; +} + + +void fntTexFont::getBBox ( const char *s, + float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) +{ + float h_pos = 0.0f ; + float v_pos = 0.0f ; + float l, r, b, t ; + + l = r = b = t = 0.0f ; + + while ( *s != '\0' ) + { + if ( *s == '\n' ) + { + h_pos = 0.0f ; + v_pos -= 1.333f ; + s++ ; + continue ; + } + + unsigned int cc = (unsigned char) *(s++) ; + + if ( ! exists [ cc ] ) + { + if ( cc >= 'A' && cc <= 'Z' ) + cc = cc - 'A' + 'a' ; + else + if ( cc >= 'a' && cc <= 'z' ) + cc = cc - 'a' + 'A' ; + + if ( cc == ' ' ) + { + r += 0.5f ; + h_pos += 0.5f ; + + continue ; + } + } + + if ( ! exists [ cc ] ) + continue ; + + if ( italic >= 0 ) + { + if ( l > h_pos + v_left [cc] ) l = h_pos + v_left [cc] ; + if ( r < gap + h_pos + v_right[cc]+italic ) r = gap + h_pos + v_right[cc] + italic ; + } + else + { + if ( l > h_pos + v_left [cc]+italic ) l = h_pos + v_left [cc] + italic ; + if ( r < gap + h_pos + v_right[cc] ) r = gap + h_pos + v_right[cc] ; + } + + + if ( b > v_pos + v_bot [cc] ) b = v_pos + v_bot [cc] ; + if ( t < v_pos + v_top [cc] ) t = v_pos + v_top [cc] ; + + h_pos += gap + ( fixed_pitch ? width : widths[cc] ) ; + } + + if ( left != NULL ) *left = l * pointsize ; + if ( right != NULL ) *right = r * pointsize ; + if ( top != NULL ) *top = t * pointsize ; + if ( bot != NULL ) *bot = b * pointsize ; +} + + +void fntTexFont::puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) +{ + SGfloat origx = curpos[0] ; + + if ( ! bound ) + bind_texture () ; + + while ( *s != '\0' ) + { + if (*s == '\n') + { + curpos[0] = origx ; + curpos[1] -= pointsize ; + } + else + low_putch ( curpos, pointsize, italic, *s ) ; + + s++ ; + } +} + +void fntInit () +{ + /* Empty right now */ +} + + +void fntSetError(int level, const char* format, ...) +{ + const int bufferLength = 2048; + char buffer[2048]; + va_list args; + va_start (args, format); + vsnprintf(buffer,bufferLength,format, args); + va_end (args); + + SG_LOG(SG_GUI, static_cast(level), buffer); +} diff --git a/3rdparty/fonts/fnt.h b/3rdparty/fonts/fnt.h new file mode 100644 index 000000000..a80591ec5 --- /dev/null +++ b/3rdparty/fonts/fnt.h @@ -0,0 +1,357 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fnt.h 1923 2004-04-06 12:53:17Z sjbaker $ +*/ + + +#ifndef _FNT_H_ +#define _FNT_H_ 1 + +#include + +#include +#include + +#define FNTMAX_CHAR 256 +#define FNT_TRUE 1 +#define FNT_FALSE 0 + +class SGPath; + +class fntFont +{ +public: + fntFont () ; + + virtual ~fntFont () ; + + virtual void getBBox ( const char *s, float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) = 0 ; + virtual void putch ( sgVec3 curpos, float pointsize, float italic, char c ) = 0 ; + virtual void puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) = 0 ; + virtual void begin () = 0 ; + virtual void end () = 0 ; + + virtual int load ( const SGPath& fname, GLenum mag = GL_NEAREST, + GLenum min = GL_LINEAR_MIPMAP_LINEAR ) = 0 ; + + virtual void setFixedPitch ( int fix ) = 0 ; + virtual int isFixedPitch () const = 0 ; + + virtual void setWidth ( float w ) = 0 ; + virtual void setGap ( float g ) = 0 ; + + virtual float getWidth () const = 0 ; + virtual float getGap () const = 0 ; + + virtual int hasGlyph ( char c ) const = 0 ; +} ; + + +class fntTexFont : public fntFont +{ +private: + GLuint texture ; + int bound ; + int fixed_pitch ; + + float width ; /* The width of a character in fixed-width mode */ + float gap ; /* Set the gap between characters */ + + int exists [ FNTMAX_CHAR ] ; /* TRUE if character exists in tex-map */ + + /* + The quadrilaterals that describe the characters + in the font are drawn with the following texture + and spatial coordinates. The texture coordinates + are in (S,T) space, with (0,0) at the bottom left + of the image and (1,1) at the top-right. + + The spatial coordinates are relative to the current + 'cursor' position. They should be scaled such that + 1.0 represent the height of a capital letter. Hence, + characters like 'y' which have a descender will be + given a negative v_bot. Most capitals will have + v_bot==0.0 and v_top==1.0. + */ + + /* Nominal baseline widths */ + + float widths [ FNTMAX_CHAR ] ; + + /* Texture coordinates */ + + float t_top [ FNTMAX_CHAR ] ; /* Top edge of each character [0..1] */ + float t_bot [ FNTMAX_CHAR ] ; /* Bottom edge of each character [0..1] */ + float t_left [ FNTMAX_CHAR ] ; /* Left edge of each character [0..1] */ + float t_right [ FNTMAX_CHAR ] ; /* Right edge of each character [0..1] */ + + /* Vertex coordinates. */ + + float v_top [ FNTMAX_CHAR ] ; + float v_bot [ FNTMAX_CHAR ] ; + float v_left [ FNTMAX_CHAR ] ; + float v_right [ FNTMAX_CHAR ] ; + + void bind_texture () + { + glEnable ( GL_TEXTURE_2D ) ; +#ifdef GL_VERSION_1_1 + glBindTexture ( GL_TEXTURE_2D, texture ) ; +#else + /* For ancient SGI machines */ + glBindTextureEXT ( GL_TEXTURE_2D, texture ) ; +#endif + } + + float low_putch ( sgVec3 curpos, float pointsize, float italic, char c ) ; + + int loadTXF ( const SGPath& path, GLenum mag = GL_NEAREST, + GLenum min = GL_LINEAR_MIPMAP_LINEAR ) ; +public: + + fntTexFont () + { + bound = FNT_FALSE ; + fixed_pitch = FNT_TRUE ; + texture = 0 ; + width = 1.0f ; + gap = 0.1f ; + + memset ( exists, FNT_FALSE, FNTMAX_CHAR * sizeof(int) ) ; + } + + fntTexFont ( const SGPath& path, GLenum mag = GL_NEAREST, + GLenum min = GL_LINEAR_MIPMAP_LINEAR ) : fntFont () + { + bound = FNT_FALSE ; + fixed_pitch = FNT_TRUE ; + texture = 0 ; + width = 1.0f ; + gap = 0.1f ; + + memset ( exists, FNT_FALSE, FNTMAX_CHAR * sizeof(int) ) ; + load ( path, mag, min ) ; + } + + ~fntTexFont () + { + if ( texture != 0 ) + { +#ifdef GL_VERSION_1_1 + glDeleteTextures ( 1, &texture ) ; +#else + /* For ancient SGI machines */ + glDeleteTexturesEXT ( 1, &texture ) ; +#endif + } + } + + int load ( const SGPath& path, GLenum mag = GL_NEAREST, + GLenum min = GL_LINEAR_MIPMAP_LINEAR ) override ; + + void setFixedPitch ( int fix ) { fixed_pitch = fix ; } + int isFixedPitch () const { return fixed_pitch ; } + + void setWidth ( float w ) { width = w ; } + void setGap ( float g ) { gap = g ; } + + float getWidth () const { return width ; } + float getGap () const { return gap ; } + + + void setGlyph ( char c, float wid, + float tex_left, float tex_right, + float tex_bot , float tex_top , + float vtx_left, float vtx_right, + float vtx_bot , float vtx_top ) ; + void setGlyph ( char c, + float tex_left, float tex_right, + float tex_bot , float tex_top , + float vtx_left, float vtx_right, + float vtx_bot , float vtx_top ) /* deprecated */ + { + setGlyph ( c, vtx_right, + tex_left, tex_right, tex_bot, tex_top, + vtx_left, vtx_right, vtx_bot, vtx_top ) ; + } + + int getGlyph ( char c, float* wid, + float *tex_left = NULL, float *tex_right = NULL, + float *tex_bot = NULL, float *tex_top = NULL, + float *vtx_left = NULL, float *vtx_right = NULL, + float *vtx_bot = NULL, float *vtx_top = NULL) ; + int getGlyph ( char c, + float *tex_left = NULL, float *tex_right = NULL, + float *tex_bot = NULL, float *tex_top = NULL, + float *vtx_left = NULL, float *vtx_right = NULL, + float *vtx_bot = NULL, float *vtx_top = NULL) /* deprecated */ + { + return getGlyph ( c, NULL, + tex_left, tex_right, tex_bot, tex_top, + vtx_left, vtx_right, vtx_bot, vtx_top ) ; + } + + int hasGlyph ( char c ) const { return exists[ (GLubyte) c ] ; } + + void getBBox ( const char *s, float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) ; + + void begin () + { + bind_texture () ; + bound = FNT_TRUE ; + } + + void end () + { + bound = FNT_FALSE ; + } + + void puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) ; + + void putch ( sgVec3 curpos, float pointsize, float italic, char c ) + { + if ( ! bound ) + bind_texture () ; + + low_putch ( curpos, pointsize, italic, c ) ; + } + +} ; + + + +class fntBitmapFont : public fntFont +{ + protected: + + const GLubyte **data; + int first; + int count; + int height; + float xorig, yorig; + int fix; + float wid, gap; + + public: + + // data is a null-terminated list of glyph images: + // data[i][0] - image width + // data[i][1..n] - packed bitmap + + fntBitmapFont ( const GLubyte **data, int first, int height, + float xorig, float yorig ) ; + + virtual ~fntBitmapFont () ; + + virtual void getBBox ( const char *s, float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) ; + + virtual void putch ( sgVec3 curpos, float pointsize, float italic, char c ) ; + virtual void puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) ; + + virtual void begin () ; + virtual void end () ; + + virtual int load ( const SGPath& fname, GLenum mag, GLenum min ) override { return -1; } + + virtual void setFixedPitch ( int f ) { fix = f; } + virtual int isFixedPitch () const { return fix; } + + virtual void setWidth ( float w ) { wid = w; } + virtual void setGap ( float g ) { gap = g; } + + virtual float getWidth () const { return wid; } + virtual float getGap () const { return gap; } + + virtual int hasGlyph ( char c ) const ; +}; + + +/* Builtin Bitmap Fonts */ + +#define FNT_BITMAP_8_BY_13 0 +#define FNT_BITMAP_9_BY_15 1 +#define FNT_BITMAP_HELVETICA_10 2 +#define FNT_BITMAP_HELVETICA_12 3 +#define FNT_BITMAP_HELVETICA_18 4 +#define FNT_BITMAP_TIMES_ROMAN_10 5 +#define FNT_BITMAP_TIMES_ROMAN_24 6 + +fntBitmapFont *fntGetBitmapFont(int id); + + + +class fntRenderer +{ + fntFont *font ; + + sgVec3 curpos ; + + float pointsize ; + float italic ; + +public: + fntRenderer () + { + start2f ( 0.0f, 0.0f ) ; + font = NULL ; + pointsize = 10 ; + italic = 0 ; + } + + void start3fv ( sgVec3 pos ) { sgCopyVec3 ( curpos, pos ) ; } + void start2fv ( sgVec2 pos ) { sgCopyVec2 ( curpos, pos ) ; curpos[2]=0.0f ; } + void start2f ( float x, float y ) { sgSetVec3 ( curpos, x, y, 0.0f ) ; } + void start3f ( float x, float y, float z ) { sgSetVec3 ( curpos, x, y, z ) ; } + + void getCursor ( float *x, float *y, float *z ) const + { + if ( x != NULL ) *x = curpos [ 0 ] ; + if ( y != NULL ) *y = curpos [ 1 ] ; + if ( z != NULL ) *z = curpos [ 2 ] ; + } + + void setFont ( fntFont *f ) { font = f ; } + fntFont *getFont () const { return font ; } + + void setSlant ( float i ) { italic = i ; } + void setPointSize ( float p ) { pointsize = p ; } + + float getSlant () const { return italic ; } + float getPointSize () const { return pointsize ; } + + void begin () { font->begin () ; } + void end () { font->end () ; } + + void putch ( char c ) { font->putch ( curpos, pointsize, italic, c ) ; } + void puts ( const char *s ) { font->puts ( curpos, pointsize, italic, s ) ; } +} ; + + +void fntInit () ; + +#endif + diff --git a/3rdparty/fonts/fntBitmap.cxx b/3rdparty/fonts/fntBitmap.cxx new file mode 100644 index 000000000..22cf2a501 --- /dev/null +++ b/3rdparty/fonts/fntBitmap.cxx @@ -0,0 +1,1017 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fntBitmap.cxx 2036 2005-07-14 16:37:55Z fayjf $ +*/ + + +#include "fnt.h" + + +fntBitmapFont::fntBitmapFont( const GLubyte **_data, int _first, int _height, + float _xorig, float _yorig ) +{ + data = _data; + first = _first; + count = 0; + height = _height; + xorig = _xorig; + yorig = _yorig; + fix = 0; + wid = 0; + gap = 0; + + while (data[count] != NULL) + count++; +} + + +fntBitmapFont::~fntBitmapFont() +{ +} + + +void fntBitmapFont::begin () +{ + glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); + glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +} + + +void fntBitmapFont::end () +{ + glPopClientAttrib(); +} + + +void fntBitmapFont::putch ( sgVec3 curpos, float pointsize, float italic, char c ) +{ + if (c == '\n') { + curpos[1] -= height; + } else { + glRasterPos2i(0,0); // beware - coordinate may be negative + glBitmap(0, 0, 0, 0, curpos[0], curpos[1], NULL); + int i = (GLubyte) c - first; + if (i >= 0 && i < count) { + glBitmap(data[i][0], height, xorig, yorig, (float) data[i][0], 0, data[i] + 1); + curpos[0] += data[i][0]; + } + } +} + + +void fntBitmapFont::puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) +{ + float x0 = curpos[0]; + glRasterPos2i(0,0); // beware - coordinate may be negative + glBitmap(0, 0, 0, 0, curpos[0], curpos[1], NULL); + for (int i = 0; s[i] != '\0'; i++) { + if (s[i] == '\n') { + curpos[0] = x0; + curpos[1] -= height; + glRasterPos2i(0,0); // beware - coordinate may be negative + glBitmap(0, 0, 0, 0, curpos[0], curpos[1], NULL); + } else { + int j = (GLubyte) s[i] - first; + if (j >= 0 && j < count) { + glBitmap(data[j][0], height, xorig, yorig, (float) data[j][0], 0, data[j] + 1); + curpos[0] += data[j][0]; + } + } + } +} + + +int fntBitmapFont::hasGlyph ( char c ) const +{ + int i = (GLubyte) c - first; + return i >= 0 && i < count && data[i][0] > 0; +} + + +void fntBitmapFont::getBBox ( const char *s, float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) +{ + float l, r, b, t; + + if (count > 0) { + int x1 = 0, y1 = 0, x = 0, y = 0; + for (int i = 0; s[i] != '\0'; i++) { + if (s[i] == '\n') { + if (x > x1) x1 = x; + y -= height; + x = 0; + } else { + int j = (GLubyte) s[i] - first; + if (j >= 0 && j < count && data[j][0] > 0) { + x += data[j][0]; + y1 = y; + } + } + } + if (x > x1) x1 = x; + l = - xorig; + r = (float) x1 - xorig; + b = (float) y1 - yorig; + t = (float) height - yorig; + } else { + l = r = b = t = 0.0f; + } + + if (left != NULL) *left = l; + if (right != NULL) *right = r; + if (bot != NULL) *bot = b; + if (top != NULL) *top = t; +} + + + +// This is to match the FreeGLUT definitions below + +struct SFG_Font { + const char* Name; /* The source font name */ + int Quantity; /* Number of chars in font */ + int Height; /* Height of the characters */ + const GLubyte** Characters; /* The characters mapping */ + + float xorig, yorig; /* Relative origin of the character */ +}; + + +/* + * Below is the font data taken from FreeGLUT. Nothing is changed except + * the structures are made "static" to avoid namespace pollution. + */ + + +/* + * freeglut_font_data.c + * + * This file has been automatically generated by the genfonts utility. + * + * Copyright (c) 1999-2000 by Pawel W. Olszta + * Written by Pawel W. Olszta, + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Sotware. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +/* + * Following fonts are defined in this file: + * + * 1. fgFontFixed8x13 <-misc-fixed-medium-r-normal--13-120-75-75-C-80-iso8859-1> + * 2. fgFontFixed9x15 <-misc-fixed-medium-r-normal--15-140-75-75-C-90-iso8859-1> + * 3. fgFontHelvetica10 <-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1> + * 4. fgFontHelvetica12 <-adobe-helvetica-medium-r-normal--12-120-75-75-p-67-iso8859-1> + * 5. fgFontHelvetica18 <-adobe-helvetica-medium-r-normal--18-180-75-75-p-98-iso8859-1> + * 6. fgFontTimesRoman10 <-adobe-times-medium-r-normal--10-100-75-75-p-54-iso8859-1> + * 7. fgFontTimesRoman24 <-adobe-times-medium-r-normal--24-240-75-75-p-124-iso8859-1> + */ + +static const GLubyte Fixed8x13_Character_032[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* blank */ +static const GLubyte Fixed8x13_Character_097[] = { 8, 0, 0,116,140,132,124, 4,120, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Fixed8x13_Character_098[] = { 8, 0, 0,184,196,132,132,196,184,128,128,128, 0, 0}; +static const GLubyte Fixed8x13_Character_099[] = { 8, 0, 0,120,132,128,128,132,120, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_100[] = { 8, 0, 0,116,140,132,132,140,116, 4, 4, 4, 0, 0}; +static const GLubyte Fixed8x13_Character_101[] = { 8, 0, 0,120,132,128,252,132,120, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_102[] = { 8, 0, 0, 64, 64, 64, 64,248, 64, 64, 68, 56, 0, 0}; +static const GLubyte Fixed8x13_Character_103[] = { 8,120,132,120,128,112,136,136,116, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_104[] = { 8, 0, 0,132,132,132,132,196,184,128,128,128, 0, 0}; +static const GLubyte Fixed8x13_Character_105[] = { 8, 0, 0,248, 32, 32, 32, 32, 96, 0, 32, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_106[] = { 8,112,136,136, 8, 8, 8, 8, 24, 0, 8, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_107[] = { 8, 0, 0,132,136,144,224,144,136,128,128,128, 0, 0}; +static const GLubyte Fixed8x13_Character_108[] = { 8, 0, 0,248, 32, 32, 32, 32, 32, 32, 32, 96, 0, 0}; +static const GLubyte Fixed8x13_Character_109[] = { 8, 0, 0,130,146,146,146,146,236, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_110[] = { 8, 0, 0,132,132,132,132,196,184, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_111[] = { 8, 0, 0,120,132,132,132,132,120, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_112[] = { 8,128,128,128,184,196,132,196,184, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_113[] = { 8, 4, 4, 4,116,140,132,140,116, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_114[] = { 8, 0, 0, 64, 64, 64, 64, 68,184, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_115[] = { 8, 0, 0,120,132, 24, 96,132,120, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_116[] = { 8, 0, 0, 56, 68, 64, 64, 64,248, 64, 64, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_117[] = { 8, 0, 0,116,136,136,136,136,136, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_119[] = { 8, 0, 0, 68,170,146,146,130,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_118[] = { 8, 0, 0, 32, 80, 80,136,136,136, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_120[] = { 8, 0, 0,132, 72, 48, 48, 72,132, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_121[] = { 8,120,132, 4,116,140,132,132,132, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_122[] = { 8, 0, 0,252, 64, 32, 16, 8,252, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Fixed8x13_Character_065[] = { 8, 0, 0,132,132,132,252,132,132,132, 72, 48, 0, 0}; /* "A" */ +static const GLubyte Fixed8x13_Character_066[] = { 8, 0, 0,252, 66, 66, 66,124, 66, 66, 66,252, 0, 0}; +static const GLubyte Fixed8x13_Character_067[] = { 8, 0, 0,120,132,128,128,128,128,128,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_068[] = { 8, 0, 0,252, 66, 66, 66, 66, 66, 66, 66,252, 0, 0}; +static const GLubyte Fixed8x13_Character_069[] = { 8, 0, 0,252,128,128,128,240,128,128,128,252, 0, 0}; +static const GLubyte Fixed8x13_Character_070[] = { 8, 0, 0,128,128,128,128,240,128,128,128,252, 0, 0}; +static const GLubyte Fixed8x13_Character_071[] = { 8, 0, 0,116,140,132,156,128,128,128,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_072[] = { 8, 0, 0,132,132,132,132,252,132,132,132,132, 0, 0}; +static const GLubyte Fixed8x13_Character_073[] = { 8, 0, 0,248, 32, 32, 32, 32, 32, 32, 32,248, 0, 0}; +static const GLubyte Fixed8x13_Character_074[] = { 8, 0, 0,112,136, 8, 8, 8, 8, 8, 8, 60, 0, 0}; +static const GLubyte Fixed8x13_Character_075[] = { 8, 0, 0,132,136,144,160,192,160,144,136,132, 0, 0}; +static const GLubyte Fixed8x13_Character_076[] = { 8, 0, 0,252,128,128,128,128,128,128,128,128, 0, 0}; +static const GLubyte Fixed8x13_Character_077[] = { 8, 0, 0,130,130,130,146,146,170,198,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_078[] = { 8, 0, 0,132,132,132,140,148,164,196,132,132, 0, 0}; +static const GLubyte Fixed8x13_Character_079[] = { 8, 0, 0,120,132,132,132,132,132,132,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_080[] = { 8, 0, 0,128,128,128,128,248,132,132,132,248, 0, 0}; +static const GLubyte Fixed8x13_Character_081[] = { 8, 0, 4,120,148,164,132,132,132,132,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_082[] = { 8, 0, 0,132,136,144,160,248,132,132,132,248, 0, 0}; +static const GLubyte Fixed8x13_Character_083[] = { 8, 0, 0,120,132, 4, 4,120,128,128,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_084[] = { 8, 0, 0, 16, 16, 16, 16, 16, 16, 16, 16,254, 0, 0}; +static const GLubyte Fixed8x13_Character_085[] = { 8, 0, 0,120,132,132,132,132,132,132,132,132, 0, 0}; +static const GLubyte Fixed8x13_Character_087[] = { 8, 0, 0, 68,170,146,146,146,130,130,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_086[] = { 8, 0, 0, 16, 40, 40, 40, 68, 68, 68,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_088[] = { 8, 0, 0,130,130, 68, 40, 16, 40, 68,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_089[] = { 8, 0, 0, 16, 16, 16, 16, 16, 40, 68,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_090[] = { 8, 0, 0,252,128,128, 64, 32, 16, 8, 4,252, 0, 0}; /* "Z" */ +static const GLubyte Fixed8x13_Character_048[] = { 8, 0, 0, 48, 72,132,132,132,132,132, 72, 48, 0, 0}; /* "0" */ +static const GLubyte Fixed8x13_Character_049[] = { 8, 0, 0,248, 32, 32, 32, 32, 32,160, 96, 32, 0, 0}; +static const GLubyte Fixed8x13_Character_050[] = { 8, 0, 0,252,128, 64, 48, 8, 4,132,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_051[] = { 8, 0, 0,120,132, 4, 4, 56, 16, 8, 4,252, 0, 0}; +static const GLubyte Fixed8x13_Character_052[] = { 8, 0, 0, 8, 8,252,136,136, 72, 40, 24, 8, 0, 0}; +static const GLubyte Fixed8x13_Character_053[] = { 8, 0, 0,120,132, 4, 4,196,184,128,128,252, 0, 0}; +static const GLubyte Fixed8x13_Character_054[] = { 8, 0, 0,120,132,132,196,184,128,128, 64, 56, 0, 0}; +static const GLubyte Fixed8x13_Character_055[] = { 8, 0, 0, 64, 64, 32, 32, 16, 16, 8, 4,252, 0, 0}; +static const GLubyte Fixed8x13_Character_056[] = { 8, 0, 0,120,132,132,132,120,132,132,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_057[] = { 8, 0, 0,112, 8, 4, 4,116,140,132,132,120, 0, 0}; /* "9" */ +static const GLubyte Fixed8x13_Character_096[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0, 16, 96,224, 0, 0}; /* "`" */ +static const GLubyte Fixed8x13_Character_126[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0,144,168, 72, 0, 0}; /* "~" */ +static const GLubyte Fixed8x13_Character_033[] = { 8, 0, 0,128, 0,128,128,128,128,128,128,128, 0, 0}; /* "!" */ +static const GLubyte Fixed8x13_Character_064[] = { 8, 0, 0,120,128,148,172,164,156,132,132,120, 0, 0}; /* "@" */ +static const GLubyte Fixed8x13_Character_035[] = { 8, 0, 0, 0, 72, 72,252, 72,252, 72, 72, 0, 0, 0}; /* "#" */ +static const GLubyte Fixed8x13_Character_036[] = { 8, 0, 0, 0, 32,240, 40,112,160,120, 32, 0, 0, 0}; /* "$" */ +static const GLubyte Fixed8x13_Character_037[] = { 8, 0, 0,136, 84, 72, 32, 16, 16, 72,164, 68, 0, 0}; /* "%" */ +static const GLubyte Fixed8x13_Character_094[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0,136, 80, 32, 0, 0}; /* "^" */ +static const GLubyte Fixed8x13_Character_038[] = { 8, 0, 0,116,136,148, 96,144,144, 96, 0, 0, 0, 0}; /* "&" */ +static const GLubyte Fixed8x13_Character_042[] = { 8, 0, 0, 0, 0, 72, 48,252, 48, 72, 0, 0, 0, 0}; /* "*" */ +static const GLubyte Fixed8x13_Character_040[] = { 8, 0, 0, 32, 64, 64,128,128,128, 64, 64, 32, 0, 0}; /* "(" */ +static const GLubyte Fixed8x13_Character_041[] = { 8, 0, 0,128, 64, 64, 32, 32, 32, 64, 64,128, 0, 0}; /* ")" */ +static const GLubyte Fixed8x13_Character_045[] = { 8, 0, 0, 0, 0, 0, 0,252, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Fixed8x13_Character_095[] = { 8, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Fixed8x13_Character_061[] = { 8, 0, 0, 0, 0,252, 0, 0,252, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Fixed8x13_Character_043[] = { 8, 0, 0, 0, 0, 32, 32,248, 32, 32, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Fixed8x13_Character_091[] = { 8, 0, 0,240,128,128,128,128,128,128,128,240, 0, 0}; /* "[" */ +static const GLubyte Fixed8x13_Character_123[] = { 8, 0, 0, 56, 64, 64, 32,192, 32, 64, 64, 56, 0, 0}; /* "{" */ +static const GLubyte Fixed8x13_Character_125[] = { 8, 0, 0,224, 16, 16, 32, 24, 32, 16, 16,224, 0, 0}; /* "}" */ +static const GLubyte Fixed8x13_Character_093[] = { 8, 0, 0,240, 16, 16, 16, 16, 16, 16, 16,240, 0, 0}; /* "]" */ +static const GLubyte Fixed8x13_Character_059[] = { 8, 0,128, 96,112, 0, 0, 32,112, 32, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Fixed8x13_Character_058[] = { 8, 0, 64,224, 64, 0, 0, 64,224, 64, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Fixed8x13_Character_044[] = { 8, 0,128, 96,112, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Fixed8x13_Character_046[] = { 8, 0, 64,224, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Fixed8x13_Character_060[] = { 8, 0, 0, 8, 16, 32, 64,128, 64, 32, 16, 8, 0, 0}; /* "<" */ +static const GLubyte Fixed8x13_Character_062[] = { 8, 0, 0,128, 64, 32, 16, 8, 16, 32, 64,128, 0, 0}; /* ">" */ +static const GLubyte Fixed8x13_Character_047[] = { 8, 0, 0,128,128, 64, 32, 16, 8, 4, 2, 2, 0, 0}; /* "/" */ +static const GLubyte Fixed8x13_Character_063[] = { 8, 0, 0, 16, 0, 16, 16, 8, 4,132,132,120, 0, 0}; /* "?" */ +static const GLubyte Fixed8x13_Character_092[] = { 8, 0, 0, 2, 2, 4, 8, 16, 32, 64,128,128, 0, 0}; /* "\" */ +static const GLubyte Fixed8x13_Character_034[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0,144,144,144, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Fixed8x13_Character_039[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 32, 0, 0}; /* """ */ +static const GLubyte Fixed8x13_Character_124[] = { 8, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0}; /* """ */ + + +/* The font characters mapping: */ +static const GLubyte* Fixed8x13_Character_Map[] = {Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_032,Fixed8x13_Character_033,Fixed8x13_Character_034,Fixed8x13_Character_035,Fixed8x13_Character_036,Fixed8x13_Character_037,Fixed8x13_Character_038,Fixed8x13_Character_039,Fixed8x13_Character_040, + Fixed8x13_Character_041,Fixed8x13_Character_042,Fixed8x13_Character_043,Fixed8x13_Character_044,Fixed8x13_Character_045,Fixed8x13_Character_046,Fixed8x13_Character_047,Fixed8x13_Character_048,Fixed8x13_Character_049,Fixed8x13_Character_050,Fixed8x13_Character_051,Fixed8x13_Character_052,Fixed8x13_Character_053,Fixed8x13_Character_054,Fixed8x13_Character_055,Fixed8x13_Character_056,Fixed8x13_Character_057,Fixed8x13_Character_058,Fixed8x13_Character_059,Fixed8x13_Character_060,Fixed8x13_Character_061,Fixed8x13_Character_062,Fixed8x13_Character_063,Fixed8x13_Character_064,Fixed8x13_Character_065,Fixed8x13_Character_066,Fixed8x13_Character_067,Fixed8x13_Character_068,Fixed8x13_Character_069,Fixed8x13_Character_070,Fixed8x13_Character_071,Fixed8x13_Character_072,Fixed8x13_Character_073,Fixed8x13_Character_074,Fixed8x13_Character_075,Fixed8x13_Character_076,Fixed8x13_Character_077,Fixed8x13_Character_078,Fixed8x13_Character_079,Fixed8x13_Character_080,Fixed8x13_Character_081,Fixed8x13_Character_082, + Fixed8x13_Character_083,Fixed8x13_Character_084,Fixed8x13_Character_085,Fixed8x13_Character_086,Fixed8x13_Character_087,Fixed8x13_Character_088,Fixed8x13_Character_089,Fixed8x13_Character_090,Fixed8x13_Character_091,Fixed8x13_Character_092,Fixed8x13_Character_093,Fixed8x13_Character_094,Fixed8x13_Character_095,Fixed8x13_Character_096,Fixed8x13_Character_097,Fixed8x13_Character_098,Fixed8x13_Character_099,Fixed8x13_Character_100,Fixed8x13_Character_101,Fixed8x13_Character_102,Fixed8x13_Character_103,Fixed8x13_Character_104,Fixed8x13_Character_105,Fixed8x13_Character_106,Fixed8x13_Character_107,Fixed8x13_Character_108,Fixed8x13_Character_109,Fixed8x13_Character_110,Fixed8x13_Character_111,Fixed8x13_Character_112,Fixed8x13_Character_113,Fixed8x13_Character_114,Fixed8x13_Character_115,Fixed8x13_Character_116,Fixed8x13_Character_117,Fixed8x13_Character_118,Fixed8x13_Character_119,Fixed8x13_Character_120,Fixed8x13_Character_121,Fixed8x13_Character_122,Fixed8x13_Character_123,Fixed8x13_Character_124, + Fixed8x13_Character_125,Fixed8x13_Character_126,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042, + Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042, + Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042, + Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontFixed8x13 = { "-misc-fixed-medium-r-normal--13-120-75-75-C-80-iso8859-1", 93, 13, Fixed8x13_Character_Map, -1.0f, 2.0f }; + +static const GLubyte Fixed9x15_Character_032[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* blank */ +static const GLubyte Fixed9x15_Character_097[] = { 9, 0, 0, 0, 0, 0, 0,122, 0,134, 0,130, 0,126, 0, 2, 0, 2, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Fixed9x15_Character_098[] = { 9, 0, 0, 0, 0, 0, 0,188, 0,194, 0,130, 0,130, 0,130, 0,194, 0,188, 0,128, 0,128, 0,128, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_099[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,128, 0,128, 0,128, 0,130, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_100[] = { 9, 0, 0, 0, 0, 0, 0,122, 0,134, 0,130, 0,130, 0,130, 0,134, 0,122, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_101[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,128, 0,128, 0,254, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_102[] = { 9, 0, 0, 0, 0, 0, 0, 32, 0, 32, 0, 32, 0, 32, 0,248, 0, 32, 0, 32, 0, 34, 0, 34, 0, 28, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_103[] = { 9,124, 0,130, 0,130, 0,124, 0,128, 0,120, 0,132, 0,132, 0,132, 0,122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_104[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,130, 0,130, 0,194, 0,188, 0,128, 0,128, 0,128, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_105[] = { 9, 0, 0, 0, 0, 0, 0,248, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0,224, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_106[] = { 9,120, 0,132, 0,132, 0,132, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 28, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_107[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,140, 0,176, 0,192, 0,176, 0,140, 0,130, 0,128, 0,128, 0,128, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_108[] = { 9, 0, 0, 0, 0, 0, 0,248, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0,224, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_109[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,146, 0,146, 0,146, 0,146, 0,146, 0,236, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_110[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,130, 0,130, 0,194, 0,188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_111[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_112[] = { 9,128, 0,128, 0,128, 0,188, 0,194, 0,130, 0,130, 0,130, 0,194, 0,188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_113[] = { 9, 2, 0, 2, 0, 2, 0,122, 0,134, 0,130, 0,130, 0,130, 0,134, 0,122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_114[] = { 9, 0, 0, 0, 0, 0, 0, 64, 0, 64, 0, 64, 0, 64, 0, 66, 0, 98, 0,156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_115[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0, 2, 0,124, 0,128, 0,130, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_116[] = { 9, 0, 0, 0, 0, 0, 0, 28, 0, 34, 0, 32, 0, 32, 0, 32, 0, 32, 0,252, 0, 32, 0, 32, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_117[] = { 9, 0, 0, 0, 0, 0, 0,122, 0,132, 0,132, 0,132, 0,132, 0,132, 0,132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_119[] = { 9, 0, 0, 0, 0, 0, 0, 68, 0,170, 0,146, 0,146, 0,146, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_118[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 40, 0, 40, 0, 68, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_120[] = { 9, 0, 0, 0, 0, 0, 0,130, 0, 68, 0, 40, 0, 16, 0, 40, 0, 68, 0,130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_121[] = { 9,120, 0,132, 0, 4, 0,116, 0,140, 0,132, 0,132, 0,132, 0,132, 0,132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_122[] = { 9, 0, 0, 0, 0, 0, 0,254, 0, 64, 0, 32, 0, 16, 0, 8, 0, 4, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Fixed9x15_Character_065[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,254, 0,130, 0,130, 0,130, 0, 68, 0, 40, 0, 16, 0, 0, 0, 0, 0}; /* "A" */ +static const GLubyte Fixed9x15_Character_066[] = { 9, 0, 0, 0, 0, 0, 0,252, 0, 66, 0, 66, 0, 66, 0, 66, 0,124, 0, 66, 0, 66, 0, 66, 0,252, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_067[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_068[] = { 9, 0, 0, 0, 0, 0, 0,252, 0, 66, 0, 66, 0, 66, 0, 66, 0, 66, 0, 66, 0, 66, 0, 66, 0,252, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_069[] = { 9, 0, 0, 0, 0, 0, 0,254, 0, 64, 0, 64, 0, 64, 0, 64, 0,120, 0, 64, 0, 64, 0, 64, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_070[] = { 9, 0, 0, 0, 0, 0, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0,120, 0, 64, 0, 64, 0, 64, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_071[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,142, 0,128, 0,128, 0,128, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_072[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,130, 0,130, 0,254, 0,130, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_073[] = { 9, 0, 0, 0, 0, 0, 0,248, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0,248, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_074[] = { 9, 0, 0, 0, 0, 0, 0,120, 0,132, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 30, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_075[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,132, 0,136, 0,144, 0,160, 0,224, 0,144, 0,136, 0,132, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_076[] = { 9, 0, 0, 0, 0, 0, 0,254, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_077[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,146, 0,146, 0,170, 0,170, 0,198, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_078[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,134, 0,138, 0,146, 0,162, 0,194, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_079[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_080[] = { 9, 0, 0, 0, 0, 0, 0,128, 0,128, 0,128, 0,128, 0,128, 0,252, 0,130, 0,130, 0,130, 0,252, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_081[] = { 9, 0, 0, 6, 0, 8, 0,124, 0,146, 0,162, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_082[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,132, 0,136, 0,144, 0,252, 0,130, 0,130, 0,130, 0,252, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_083[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0, 2, 0, 12, 0,112, 0,128, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_084[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_085[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_087[] = { 9, 0, 0, 0, 0, 0, 0, 68, 0,170, 0,146, 0,146, 0,146, 0,146, 0,130, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_086[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 40, 0, 40, 0, 40, 0, 68, 0, 68, 0, 68, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_088[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0, 68, 0, 40, 0, 16, 0, 16, 0, 40, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_089[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 40, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_090[] = { 9, 0, 0, 0, 0, 0, 0,254, 0,128, 0,128, 0, 64, 0, 32, 0, 16, 0, 8, 0, 4, 0, 2, 0,254, 0, 0, 0, 0, 0}; /* "Z" */ +static const GLubyte Fixed9x15_Character_048[] = { 9, 0, 0, 0, 0, 0, 0, 56, 0, 68, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0, 68, 0, 56, 0, 0, 0, 0, 0}; /* "0" */ +static const GLubyte Fixed9x15_Character_049[] = { 9, 0, 0, 0, 0, 0, 0,254, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0,144, 0, 80, 0, 48, 0, 16, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_050[] = { 9, 0, 0, 0, 0, 0, 0,254, 0,128, 0, 64, 0, 48, 0, 8, 0, 4, 0, 2, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_051[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0, 2, 0, 2, 0, 2, 0, 28, 0, 8, 0, 4, 0, 2, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_052[] = { 9, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 4, 0,254, 0,132, 0, 68, 0, 36, 0, 20, 0, 12, 0, 4, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_053[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0, 2, 0, 2, 0, 2, 0,194, 0,188, 0,128, 0,128, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_054[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,194, 0,188, 0,128, 0,128, 0, 64, 0, 60, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_055[] = { 9, 0, 0, 0, 0, 0, 0, 64, 0, 64, 0, 32, 0, 32, 0, 16, 0, 8, 0, 4, 0, 2, 0, 2, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_056[] = { 9, 0, 0, 0, 0, 0, 0, 56, 0, 68, 0,130, 0,130, 0, 68, 0, 56, 0, 68, 0,130, 0, 68, 0, 56, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_057[] = { 9, 0, 0, 0, 0, 0, 0,120, 0, 4, 0, 2, 0, 2, 0,122, 0,134, 0,130, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; /* "9" */ +static const GLubyte Fixed9x15_Character_096[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 32, 0, 64, 0,192, 0, 0, 0, 0, 0}; /* "`" */ +static const GLubyte Fixed9x15_Character_126[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,140, 0,146, 0, 98, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte Fixed9x15_Character_033[] = { 9, 0, 0, 0, 0, 0, 0,128, 0,128, 0, 0, 0, 0, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0, 0, 0}; /* "!" */ +static const GLubyte Fixed9x15_Character_064[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,128, 0,128, 0,154, 0,166, 0,162, 0,158, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte Fixed9x15_Character_035[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 72, 0, 72, 0,252, 0, 72, 0, 72, 0,252, 0, 72, 0, 72, 0, 0, 0, 0, 0, 0, 0}; /* "#" */ +static const GLubyte Fixed9x15_Character_036[] = { 9, 0, 0, 0, 0, 16, 0,124, 0,146, 0, 18, 0, 18, 0, 20, 0, 56, 0, 80, 0,144, 0,146, 0,124, 0, 16, 0, 0, 0}; /* "$" */ +static const GLubyte Fixed9x15_Character_037[] = { 9, 0, 0, 0, 0, 0, 0,132, 0, 74, 0, 74, 0, 36, 0, 16, 0, 16, 0, 72, 0,164, 0,164, 0, 66, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte Fixed9x15_Character_094[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,130, 0, 68, 0, 40, 0, 16, 0, 0, 0, 0, 0}; /* "^" */ +static const GLubyte Fixed9x15_Character_038[] = { 9, 0, 0, 0, 0, 0, 0, 98, 0,148, 0,136, 0,148, 0, 98, 0, 96, 0,144, 0,144, 0,144, 0, 96, 0, 0, 0, 0, 0}; /* "&" */ +static const GLubyte Fixed9x15_Character_042[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0,146, 0, 84, 0, 56, 0, 84, 0,146, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "*" */ +static const GLubyte Fixed9x15_Character_040[] = { 9, 0, 0, 0, 0, 32, 0, 64, 0, 64, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0, 64, 0, 64, 0, 32, 0, 0, 0}; /* "(" */ +static const GLubyte Fixed9x15_Character_041[] = { 9, 0, 0, 0, 0,128, 0, 64, 0, 64, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 64, 0, 64, 0,128, 0, 0, 0}; /* ")" */ +static const GLubyte Fixed9x15_Character_045[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Fixed9x15_Character_095[] = { 9, 0, 0, 0, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Fixed9x15_Character_061[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 0, 0, 0, 0, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Fixed9x15_Character_043[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0,254, 0, 16, 0, 16, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Fixed9x15_Character_091[] = { 9, 0, 0, 0, 0,240, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,240, 0, 0, 0}; /* "[" */ +static const GLubyte Fixed9x15_Character_123[] = { 9, 0, 0, 0, 0, 56, 0, 64, 0, 64, 0, 64, 0, 32, 0,192, 0,192, 0, 32, 0, 64, 0, 64, 0, 64, 0, 56, 0, 0, 0}; /* "{" */ +static const GLubyte Fixed9x15_Character_125[] = { 9, 0, 0, 0, 0,224, 0, 16, 0, 16, 0, 16, 0, 32, 0, 24, 0, 24, 0, 32, 0, 16, 0, 16, 0, 16, 0,224, 0, 0, 0}; /* "}" */ +static const GLubyte Fixed9x15_Character_093[] = { 9, 0, 0, 0, 0,240, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0,240, 0, 0, 0}; /* "]" */ +static const GLubyte Fixed9x15_Character_059[] = { 9,128, 0, 64, 0, 64, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Fixed9x15_Character_058[] = { 9, 0, 0, 0, 0, 0, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Fixed9x15_Character_044[] = { 9,128, 0, 64, 0, 64, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Fixed9x15_Character_046[] = { 9, 0, 0, 0, 0, 0, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Fixed9x15_Character_060[] = { 9, 0, 0, 0, 0, 0, 0, 8, 0, 16, 0, 32, 0, 64, 0,128, 0,128, 0, 64, 0, 32, 0, 16, 0, 8, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte Fixed9x15_Character_062[] = { 9, 0, 0, 0, 0, 0, 0,128, 0, 64, 0, 32, 0, 16, 0, 8, 0, 8, 0, 16, 0, 32, 0, 64, 0,128, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte Fixed9x15_Character_047[] = { 9, 0, 0, 0, 0, 0, 0,128, 0, 64, 0, 64, 0, 32, 0, 16, 0, 16, 0, 8, 0, 4, 0, 4, 0, 2, 0, 0, 0, 0, 0}; /* "/" */ +static const GLubyte Fixed9x15_Character_063[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 16, 0, 16, 0, 8, 0, 4, 0, 2, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; /* "?" */ +static const GLubyte Fixed9x15_Character_092[] = { 9, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 4, 0, 8, 0, 16, 0, 16, 0, 32, 0, 64, 0, 64, 0,128, 0, 0, 0, 0, 0}; /* "\" */ +static const GLubyte Fixed9x15_Character_034[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,144, 0,144, 0,144, 0, 0, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Fixed9x15_Character_039[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 32, 0, 32, 0, 32, 0, 0, 0, 0, 0}; /* "'" */ +static const GLubyte Fixed9x15_Character_124[] = { 9, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 0, 0, 0, 0}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* Fixed9x15_Character_Map[] = {Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_032,Fixed9x15_Character_033,Fixed9x15_Character_034,Fixed9x15_Character_035,Fixed9x15_Character_036,Fixed9x15_Character_037,Fixed9x15_Character_038,Fixed9x15_Character_039,Fixed9x15_Character_040, + Fixed9x15_Character_041,Fixed9x15_Character_042,Fixed9x15_Character_043,Fixed9x15_Character_044,Fixed9x15_Character_045,Fixed9x15_Character_046,Fixed9x15_Character_047,Fixed9x15_Character_048,Fixed9x15_Character_049,Fixed9x15_Character_050,Fixed9x15_Character_051,Fixed9x15_Character_052,Fixed9x15_Character_053,Fixed9x15_Character_054,Fixed9x15_Character_055,Fixed9x15_Character_056,Fixed9x15_Character_057,Fixed9x15_Character_058,Fixed9x15_Character_059,Fixed9x15_Character_060,Fixed9x15_Character_061,Fixed9x15_Character_062,Fixed9x15_Character_063,Fixed9x15_Character_064,Fixed9x15_Character_065,Fixed9x15_Character_066,Fixed9x15_Character_067,Fixed9x15_Character_068,Fixed9x15_Character_069,Fixed9x15_Character_070,Fixed9x15_Character_071,Fixed9x15_Character_072,Fixed9x15_Character_073,Fixed9x15_Character_074,Fixed9x15_Character_075,Fixed9x15_Character_076,Fixed9x15_Character_077,Fixed9x15_Character_078,Fixed9x15_Character_079,Fixed9x15_Character_080,Fixed9x15_Character_081,Fixed9x15_Character_082, + Fixed9x15_Character_083,Fixed9x15_Character_084,Fixed9x15_Character_085,Fixed9x15_Character_086,Fixed9x15_Character_087,Fixed9x15_Character_088,Fixed9x15_Character_089,Fixed9x15_Character_090,Fixed9x15_Character_091,Fixed9x15_Character_092,Fixed9x15_Character_093,Fixed9x15_Character_094,Fixed9x15_Character_095,Fixed9x15_Character_096,Fixed9x15_Character_097,Fixed9x15_Character_098,Fixed9x15_Character_099,Fixed9x15_Character_100,Fixed9x15_Character_101,Fixed9x15_Character_102,Fixed9x15_Character_103,Fixed9x15_Character_104,Fixed9x15_Character_105,Fixed9x15_Character_106,Fixed9x15_Character_107,Fixed9x15_Character_108,Fixed9x15_Character_109,Fixed9x15_Character_110,Fixed9x15_Character_111,Fixed9x15_Character_112,Fixed9x15_Character_113,Fixed9x15_Character_114,Fixed9x15_Character_115,Fixed9x15_Character_116,Fixed9x15_Character_117,Fixed9x15_Character_118,Fixed9x15_Character_119,Fixed9x15_Character_120,Fixed9x15_Character_121,Fixed9x15_Character_122,Fixed9x15_Character_123,Fixed9x15_Character_124, + Fixed9x15_Character_125,Fixed9x15_Character_126,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042, + Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042, + Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042, + Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontFixed9x15 = { "-misc-fixed-medium-r-normal--15-140-75-75-C-90-iso8859-1", 93, 15, Fixed9x15_Character_Map, -1.0f, 3.0f }; + +static const GLubyte Helvetica10_Character_032[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte Helvetica10_Character_097[] = { 5, 0, 0,104,144,144,112, 16,224, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Helvetica10_Character_098[] = { 6, 0, 0,176,200,136,136,200,176,128,128, 0, 0, 0}; /* "b" */ +static const GLubyte Helvetica10_Character_099[] = { 5, 0, 0, 96,144,128,128,144, 96, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte Helvetica10_Character_100[] = { 6, 0, 0,104,152,136,136,152,104, 8, 8, 0, 0, 0}; /* "d" */ +static const GLubyte Helvetica10_Character_101[] = { 5, 0, 0, 96,144,128,240,144, 96, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte Helvetica10_Character_102[] = { 4, 0, 0, 64, 64, 64, 64, 64,224, 64, 48, 0, 0, 0}; /* "f" */ +static const GLubyte Helvetica10_Character_103[] = { 6,112, 8,104,152,136,136,152,104, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte Helvetica10_Character_104[] = { 6, 0, 0,136,136,136,136,200,176,128,128, 0, 0, 0}; /* "h" */ +static const GLubyte Helvetica10_Character_105[] = { 2, 0, 0,128,128,128,128,128,128, 0,128, 0, 0, 0}; /* "i" */ +static const GLubyte Helvetica10_Character_106[] = { 2, 0,128,128,128,128,128,128,128, 0,128, 0, 0, 0}; /* "j" */ +static const GLubyte Helvetica10_Character_107[] = { 5, 0, 0,144,144,160,192,160,144,128,128, 0, 0, 0}; /* "k" */ +static const GLubyte Helvetica10_Character_108[] = { 2, 0, 0,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "l" */ +static const GLubyte Helvetica10_Character_109[] = { 8, 0, 0,146,146,146,146,146,236, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte Helvetica10_Character_110[] = { 6, 0, 0,136,136,136,136,200,176, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte Helvetica10_Character_111[] = { 6, 0, 0,112,136,136,136,136,112, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte Helvetica10_Character_112[] = { 6,128,128,176,200,136,136,200,176, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte Helvetica10_Character_113[] = { 6, 8, 8,104,152,136,136,152,104, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte Helvetica10_Character_114[] = { 4, 0, 0,128,128,128,128,192,160, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte Helvetica10_Character_115[] = { 5, 0, 0, 96,144, 16, 96,144, 96, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte Helvetica10_Character_116[] = { 4, 0, 0, 96, 64, 64, 64, 64,224, 64, 64, 0, 0, 0}; /* "t" */ +static const GLubyte Helvetica10_Character_117[] = { 5, 0, 0,112,144,144,144,144,144, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte Helvetica10_Character_118[] = { 6, 0, 0, 32, 32, 80, 80,136,136, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte Helvetica10_Character_119[] = { 8, 0, 0, 40, 40, 84, 84,146,146, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte Helvetica10_Character_120[] = { 6, 0, 0,136,136, 80, 32, 80,136, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte Helvetica10_Character_121[] = { 5,128, 64, 64, 96,160,160,144,144, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte Helvetica10_Character_122[] = { 5, 0, 0,240,128, 64, 32, 16,240, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Helvetica10_Character_065[] = { 7, 0, 0,130,130,124, 68, 40, 40, 16, 16, 0, 0, 0}; /* "A" */ +static const GLubyte Helvetica10_Character_066[] = { 7, 0, 0,240,136,136,136,240,136,136,240, 0, 0, 0}; /* "B" */ +static const GLubyte Helvetica10_Character_067[] = { 8, 0, 0,120,132,128,128,128,128,132,120, 0, 0, 0}; /* "C" */ +static const GLubyte Helvetica10_Character_068[] = { 8, 0, 0,240,136,132,132,132,132,136,240, 0, 0, 0}; /* "D" */ +static const GLubyte Helvetica10_Character_069[] = { 7, 0, 0,248,128,128,128,248,128,128,248, 0, 0, 0}; /* "E" */ +static const GLubyte Helvetica10_Character_070[] = { 6, 0, 0,128,128,128,128,240,128,128,248, 0, 0, 0}; /* "F" */ +static const GLubyte Helvetica10_Character_071[] = { 8, 0, 0,116,140,132,140,128,128,132,120, 0, 0, 0}; /* "G" */ +static const GLubyte Helvetica10_Character_072[] = { 8, 0, 0,132,132,132,132,252,132,132,132, 0, 0, 0}; /* "H" */ +static const GLubyte Helvetica10_Character_073[] = { 3, 0, 0,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "I" */ +static const GLubyte Helvetica10_Character_074[] = { 5, 0, 0, 96,144, 16, 16, 16, 16, 16, 16, 0, 0, 0}; /* "J" */ +static const GLubyte Helvetica10_Character_075[] = { 7, 0, 0,136,136,144,144,224,160,144,136, 0, 0, 0}; /* "K" */ +static const GLubyte Helvetica10_Character_076[] = { 6, 0, 0,240,128,128,128,128,128,128,128, 0, 0, 0}; /* "L" */ +static const GLubyte Helvetica10_Character_077[] = { 9, 0, 0, 0, 0,146, 0,146, 0,146, 0,170, 0,170, 0,198, 0,198, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte Helvetica10_Character_078[] = { 8, 0, 0,140,140,148,148,164,164,196,196, 0, 0, 0}; /* "N" */ +static const GLubyte Helvetica10_Character_079[] = { 8, 0, 0,120,132,132,132,132,132,132,120, 0, 0, 0}; /* "O" */ +static const GLubyte Helvetica10_Character_080[] = { 7, 0, 0,128,128,128,128,240,136,136,240, 0, 0, 0}; /* "P" */ +static const GLubyte Helvetica10_Character_081[] = { 8, 0, 2,124,140,148,132,132,132,132,120, 0, 0, 0}; /* "Q" */ +static const GLubyte Helvetica10_Character_082[] = { 7, 0, 0,136,136,136,136,240,136,136,240, 0, 0, 0}; /* "R" */ +static const GLubyte Helvetica10_Character_083[] = { 7, 0, 0,112,136,136, 8,112,128,136,112, 0, 0, 0}; /* "S" */ +static const GLubyte Helvetica10_Character_084[] = { 5, 0, 0, 32, 32, 32, 32, 32, 32, 32,248, 0, 0, 0}; /* "T" */ +static const GLubyte Helvetica10_Character_085[] = { 8, 0, 0,120,132,132,132,132,132,132,132, 0, 0, 0}; /* "U" */ +static const GLubyte Helvetica10_Character_086[] = { 7, 0, 0, 16, 40, 40, 68, 68, 68,130,130, 0, 0, 0}; /* "V" */ +static const GLubyte Helvetica10_Character_087[] = { 9, 0, 0, 0, 0, 34, 0, 34, 0, 34, 0, 85, 0, 73, 0, 73, 0,136,128,136,128, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte Helvetica10_Character_088[] = { 7, 0, 0,136,136, 80, 80, 32, 80,136,136, 0, 0, 0}; /* "X" */ +static const GLubyte Helvetica10_Character_089[] = { 7, 0, 0, 16, 16, 16, 40, 40, 68, 68,130, 0, 0, 0}; /* "Y" */ +static const GLubyte Helvetica10_Character_090[] = { 7, 0, 0,248,128, 64, 32, 32, 16, 8,248, 0, 0, 0}; /* "Z" */ +static const GLubyte Helvetica10_Character_048[] = { 6, 0, 0,112,136,136,136,136,136,136,112, 0, 0, 0}; /* "0" */ +static const GLubyte Helvetica10_Character_049[] = { 6, 0, 0, 64, 64, 64, 64, 64, 64,192, 64, 0, 0, 0}; /* "1" */ +static const GLubyte Helvetica10_Character_050[] = { 6, 0, 0,248,128, 64, 48, 8, 8,136,112, 0, 0, 0}; /* "2" */ +static const GLubyte Helvetica10_Character_051[] = { 6, 0, 0,112,136, 8, 8, 48, 8,136,112, 0, 0, 0}; /* "3" */ +static const GLubyte Helvetica10_Character_052[] = { 6, 0, 0, 16, 16,248,144, 80, 80, 48, 16, 0, 0, 0}; /* "4" */ +static const GLubyte Helvetica10_Character_053[] = { 6, 0, 0,112,136, 8, 8,240,128,128,248, 0, 0, 0}; /* "5" */ +static const GLubyte Helvetica10_Character_054[] = { 6, 0, 0,112,136,136,200,176,128,136,112, 0, 0, 0}; /* "6" */ +static const GLubyte Helvetica10_Character_055[] = { 6, 0, 0, 64, 64, 32, 32, 16, 16, 8,248, 0, 0, 0}; /* "7" */ +static const GLubyte Helvetica10_Character_056[] = { 6, 0, 0,112,136,136,136,112,136,136,112, 0, 0, 0}; /* "8" */ +static const GLubyte Helvetica10_Character_057[] = { 6, 0, 0,112,136, 8,104,152,136,136,112, 0, 0, 0}; /* "9" */ +static const GLubyte Helvetica10_Character_096[] = { 3, 0, 0, 0, 0, 0, 0, 0,128,128, 64, 0, 0, 0}; /* "`" */ +static const GLubyte Helvetica10_Character_126[] = { 7, 0, 0, 0, 0, 0,152,100, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte Helvetica10_Character_033[] = { 3, 0, 0,128, 0,128,128,128,128,128,128, 0, 0, 0}; /* "!" */ +static const GLubyte Helvetica10_Character_064[] = { 11, 62, 0, 64, 0,155, 0,164,128,164,128,162, 64,146, 64, 77, 64, 32,128, 31, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte Helvetica10_Character_035[] = { 6, 0, 0, 80, 80,248, 40,124, 40, 40, 0, 0, 0, 0}; /* "#" */ +static const GLubyte Helvetica10_Character_036[] = { 6, 0, 32,112,168, 40,112,160,168,112, 32, 0, 0, 0}; /* "$" */ +static const GLubyte Helvetica10_Character_037[] = { 9, 0, 0, 0, 0, 38, 0, 41, 0, 22, 0, 16, 0, 8, 0,104, 0,148, 0,100, 0, 0, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte Helvetica10_Character_094[] = { 6, 0, 0, 0, 0, 0,136, 80, 80, 32, 32, 0, 0, 0}; /* "^" */ +static const GLubyte Helvetica10_Character_038[] = { 8, 0, 0,100,152,152,164, 96, 80, 80, 32, 0, 0, 0}; /* "&" */ +static const GLubyte Helvetica10_Character_042[] = { 4, 0, 0, 0, 0, 0, 0, 0,160, 64,160, 0, 0, 0}; /* "*" */ +static const GLubyte Helvetica10_Character_040[] = { 4, 32, 64, 64,128,128,128,128, 64, 64, 32, 0, 0, 0}; /* "(" */ +static const GLubyte Helvetica10_Character_041[] = { 4,128, 64, 64, 32, 32, 32, 32, 64, 64,128, 0, 0, 0}; /* ")" */ +static const GLubyte Helvetica10_Character_045[] = { 7, 0, 0, 0, 0, 0,248, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Helvetica10_Character_095[] = { 6,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Helvetica10_Character_061[] = { 5, 0, 0, 0, 0,240, 0,240, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Helvetica10_Character_043[] = { 6, 0, 0, 0, 32, 32,248, 32, 32, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Helvetica10_Character_091[] = { 3,192,128,128,128,128,128,128,128,128,192, 0, 0, 0}; /* "[" */ +static const GLubyte Helvetica10_Character_123[] = { 3, 32, 64, 64, 64, 64,128, 64, 64, 64, 32, 0, 0, 0}; /* "{" */ +static const GLubyte Helvetica10_Character_125[] = { 3,128, 64, 64, 64, 64, 32, 64, 64, 64,128, 0, 0, 0}; /* "}" */ +static const GLubyte Helvetica10_Character_093[] = { 3,192, 64, 64, 64, 64, 64, 64, 64, 64,192, 0, 0, 0}; /* "]" */ +static const GLubyte Helvetica10_Character_059[] = { 3,128, 64, 64, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Helvetica10_Character_058[] = { 3, 0, 0,128, 0, 0, 0, 0,128, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Helvetica10_Character_044[] = { 3,128, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Helvetica10_Character_046[] = { 3, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Helvetica10_Character_060[] = { 6, 0, 0, 0, 32, 64,128, 64, 32, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte Helvetica10_Character_062[] = { 6, 0, 0, 0,128, 64, 32, 64,128, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte Helvetica10_Character_047[] = { 3, 0, 0,128,128, 64, 64, 64, 64, 32, 32, 0, 0, 0}; /* "/" */ +static const GLubyte Helvetica10_Character_063[] = { 6, 0, 0, 64, 0, 64, 64, 32, 16,144, 96, 0, 0, 0}; /* "?" */ +static const GLubyte Helvetica10_Character_092[] = { 3, 0, 0, 32, 32, 64, 64, 64, 64,128,128, 0, 0, 0}; /* "\" */ +static const GLubyte Helvetica10_Character_034[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0,160,160, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Helvetica10_Character_039[] = { 3, 0, 0, 0, 0, 0, 0, 0,128, 64, 64, 0, 0, 0}; /* "'" */ +static const GLubyte Helvetica10_Character_124[] = { 3, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* Helvetica10_Character_Map[] = {Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_032,Helvetica10_Character_033,Helvetica10_Character_034,Helvetica10_Character_035,Helvetica10_Character_036,Helvetica10_Character_037, + Helvetica10_Character_038,Helvetica10_Character_039,Helvetica10_Character_040,Helvetica10_Character_041,Helvetica10_Character_042,Helvetica10_Character_043,Helvetica10_Character_044,Helvetica10_Character_045,Helvetica10_Character_046,Helvetica10_Character_047,Helvetica10_Character_048,Helvetica10_Character_049,Helvetica10_Character_050,Helvetica10_Character_051,Helvetica10_Character_052,Helvetica10_Character_053,Helvetica10_Character_054,Helvetica10_Character_055,Helvetica10_Character_056,Helvetica10_Character_057,Helvetica10_Character_058,Helvetica10_Character_059,Helvetica10_Character_060,Helvetica10_Character_061,Helvetica10_Character_062,Helvetica10_Character_063,Helvetica10_Character_064,Helvetica10_Character_065,Helvetica10_Character_066,Helvetica10_Character_067,Helvetica10_Character_068,Helvetica10_Character_069,Helvetica10_Character_070,Helvetica10_Character_071,Helvetica10_Character_072,Helvetica10_Character_073,Helvetica10_Character_074,Helvetica10_Character_075,Helvetica10_Character_076, + Helvetica10_Character_077,Helvetica10_Character_078,Helvetica10_Character_079,Helvetica10_Character_080,Helvetica10_Character_081,Helvetica10_Character_082,Helvetica10_Character_083,Helvetica10_Character_084,Helvetica10_Character_085,Helvetica10_Character_086,Helvetica10_Character_087,Helvetica10_Character_088,Helvetica10_Character_089,Helvetica10_Character_090,Helvetica10_Character_091,Helvetica10_Character_092,Helvetica10_Character_093,Helvetica10_Character_094,Helvetica10_Character_095,Helvetica10_Character_096,Helvetica10_Character_097,Helvetica10_Character_098,Helvetica10_Character_099,Helvetica10_Character_100,Helvetica10_Character_101,Helvetica10_Character_102,Helvetica10_Character_103,Helvetica10_Character_104,Helvetica10_Character_105,Helvetica10_Character_106,Helvetica10_Character_107,Helvetica10_Character_108,Helvetica10_Character_109,Helvetica10_Character_110,Helvetica10_Character_111,Helvetica10_Character_112,Helvetica10_Character_113,Helvetica10_Character_114,Helvetica10_Character_115, + Helvetica10_Character_116,Helvetica10_Character_117,Helvetica10_Character_118,Helvetica10_Character_119,Helvetica10_Character_120,Helvetica10_Character_121,Helvetica10_Character_122,Helvetica10_Character_123,Helvetica10_Character_124,Helvetica10_Character_125,Helvetica10_Character_126,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042, + Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042, + Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042, + Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontHelvetica10 = { "-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1", 93, 13, Helvetica10_Character_Map, -1.0f, 2.0f }; + +static const GLubyte Helvetica12_Character_032[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte Helvetica12_Character_097[] = { 7, 0, 0, 0,116,136,136,120, 8,136,112, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Helvetica12_Character_098[] = { 7, 0, 0, 0,176,200,136,136,136,200,176,128,128, 0, 0, 0}; /* "b" */ +static const GLubyte Helvetica12_Character_099[] = { 7, 0, 0, 0,112,136,128,128,128,136,112, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte Helvetica12_Character_100[] = { 7, 0, 0, 0,104,152,136,136,136,152,104, 8, 8, 0, 0, 0}; /* "d" */ +static const GLubyte Helvetica12_Character_101[] = { 7, 0, 0, 0,112,136,128,248,136,136,112, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte Helvetica12_Character_102[] = { 4, 0, 0, 0, 64, 64, 64, 64, 64, 64,224, 64, 48, 0, 0, 0}; /* "f" */ +static const GLubyte Helvetica12_Character_103[] = { 7,112,136, 8,104,152,136,136,136,152,104, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte Helvetica12_Character_104[] = { 7, 0, 0, 0,136,136,136,136,136,200,176,128,128, 0, 0, 0}; /* "h" */ +static const GLubyte Helvetica12_Character_105[] = { 3, 0, 0, 0,128,128,128,128,128,128,128, 0,128, 0, 0, 0}; /* "i" */ +static const GLubyte Helvetica12_Character_106[] = { 4,128, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 64, 0, 0, 0}; /* "j" */ +static const GLubyte Helvetica12_Character_107[] = { 6, 0, 0, 0,136,144,160,192,192,160,144,128,128, 0, 0, 0}; /* "k" */ +static const GLubyte Helvetica12_Character_108[] = { 3, 0, 0, 0,128,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "l" */ +static const GLubyte Helvetica12_Character_109[] = { 9, 0, 0, 0, 0, 0, 0,146, 0,146, 0,146, 0,146, 0,146, 0,218, 0,164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte Helvetica12_Character_110[] = { 7, 0, 0, 0,136,136,136,136,136,200,176, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte Helvetica12_Character_111[] = { 7, 0, 0, 0,112,136,136,136,136,136,112, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte Helvetica12_Character_112[] = { 7,128,128,128,176,200,136,136,136,200,176, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte Helvetica12_Character_113[] = { 7, 8, 8, 8,104,152,136,136,136,152,104, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte Helvetica12_Character_114[] = { 4, 0, 0, 0,128,128,128,128,128,192,160, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte Helvetica12_Character_115[] = { 6, 0, 0, 0, 96,144, 16, 96,128,144, 96, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte Helvetica12_Character_116[] = { 4, 0, 0, 0, 96, 64, 64, 64, 64, 64,224, 64, 64, 0, 0, 0}; /* "t" */ +static const GLubyte Helvetica12_Character_117[] = { 7, 0, 0, 0,104,152,136,136,136,136,136, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte Helvetica12_Character_118[] = { 7, 0, 0, 0, 32, 32, 80, 80,136,136,136, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte Helvetica12_Character_119[] = { 10, 0, 0, 0, 0, 0, 0, 34, 0, 34, 0, 85, 0, 73, 0, 73, 0,136,128,136,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte Helvetica12_Character_120[] = { 6, 0, 0, 0,132,132, 72, 48, 48, 72,132, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte Helvetica12_Character_121[] = { 7,128, 64, 32, 32, 80, 80,144,136,136,136, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte Helvetica12_Character_122[] = { 6, 0, 0, 0,240,128, 64, 64, 32, 16,240, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Helvetica12_Character_065[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,124, 0, 68, 0, 68, 0, 40, 0, 40, 0, 16, 0, 0, 0, 0, 0, 0, 0}; /* "A" */ +static const GLubyte Helvetica12_Character_066[] = { 8, 0, 0, 0,248,132,132,132,248,132,132,132,248, 0, 0, 0}; /* "B" */ +static const GLubyte Helvetica12_Character_067[] = { 9, 0, 0, 0, 0, 0, 0, 60, 0, 66, 0,128, 0,128, 0,128, 0,128, 0,128, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "C" */ +static const GLubyte Helvetica12_Character_068[] = { 9, 0, 0, 0, 0, 0, 0,248, 0,132, 0,130, 0,130, 0,130, 0,130, 0,130, 0,132, 0,248, 0, 0, 0, 0, 0, 0, 0}; /* "D" */ +static const GLubyte Helvetica12_Character_069[] = { 8, 0, 0, 0,252,128,128,128,252,128,128,128,252, 0, 0, 0}; /* "E" */ +static const GLubyte Helvetica12_Character_070[] = { 8, 0, 0, 0,128,128,128,128,248,128,128,128,252, 0, 0, 0}; /* "F" */ +static const GLubyte Helvetica12_Character_071[] = { 9, 0, 0, 0, 0, 0, 0, 58, 0, 70, 0,130, 0,130, 0,142, 0,128, 0,128, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "G" */ +static const GLubyte Helvetica12_Character_072[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,130, 0,254, 0,130, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "H" */ +static const GLubyte Helvetica12_Character_073[] = { 3, 0, 0, 0,128,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "I" */ +static const GLubyte Helvetica12_Character_074[] = { 7, 0, 0, 0,112,136,136, 8, 8, 8, 8, 8, 8, 0, 0, 0}; /* "J" */ +static const GLubyte Helvetica12_Character_075[] = { 8, 0, 0, 0,130,132,136,144,224,160,144,136,132, 0, 0, 0}; /* "K" */ +static const GLubyte Helvetica12_Character_076[] = { 7, 0, 0, 0,248,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "L" */ +static const GLubyte Helvetica12_Character_077[] = { 11, 0, 0, 0, 0, 0, 0,136,128,136,128,148,128,148,128,162,128,162,128,193,128,193,128,128,128, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte Helvetica12_Character_078[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,134, 0,138, 0,138, 0,146, 0,162, 0,162, 0,194, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "N" */ +static const GLubyte Helvetica12_Character_079[] = { 10, 0, 0, 0, 0, 0, 0, 60, 0, 66, 0,129, 0,129, 0,129, 0,129, 0,129, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "O" */ +static const GLubyte Helvetica12_Character_080[] = { 8, 0, 0, 0,128,128,128,128,248,132,132,132,248, 0, 0, 0}; /* "P" */ +static const GLubyte Helvetica12_Character_081[] = { 10, 0, 0, 0, 0, 0, 0, 61, 0, 66, 0,133, 0,137, 0,129, 0,129, 0,129, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "Q" */ +static const GLubyte Helvetica12_Character_082[] = { 8, 0, 0, 0,132,132,132,136,248,132,132,132,248, 0, 0, 0}; /* "R" */ +static const GLubyte Helvetica12_Character_083[] = { 8, 0, 0, 0,120,132,132, 4, 24, 96,128,132,120, 0, 0, 0}; /* "S" */ +static const GLubyte Helvetica12_Character_084[] = { 7, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16, 16,254, 0, 0, 0}; /* "T" */ +static const GLubyte Helvetica12_Character_085[] = { 8, 0, 0, 0,120,132,132,132,132,132,132,132,132, 0, 0, 0}; /* "U" */ +static const GLubyte Helvetica12_Character_086[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 40, 0, 40, 0, 68, 0, 68, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "V" */ +static const GLubyte Helvetica12_Character_087[] = { 11, 0, 0, 0, 0, 0, 0, 34, 0, 34, 0, 34, 0, 85, 0, 85, 0, 73, 0,136,128,136,128,136,128, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte Helvetica12_Character_088[] = { 9, 0, 0, 0, 0, 0, 0,130, 0, 68, 0, 68, 0, 40, 0, 16, 0, 40, 0, 68, 0, 68, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "X" */ +static const GLubyte Helvetica12_Character_089[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0, 16, 0, 40, 0, 68, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "Y" */ +static const GLubyte Helvetica12_Character_090[] = { 9, 0, 0, 0, 0, 0, 0,254, 0,128, 0, 64, 0, 32, 0, 16, 0, 8, 0, 4, 0, 2, 0,254, 0, 0, 0, 0, 0, 0, 0}; /* "Z" */ +static const GLubyte Helvetica12_Character_048[] = { 7, 0, 0, 0,112,136,136,136,136,136,136,136,112, 0, 0, 0}; /* "0" */ +static const GLubyte Helvetica12_Character_049[] = { 7, 0, 0, 0, 32, 32, 32, 32, 32, 32, 32,224, 32, 0, 0, 0}; /* "1" */ +static const GLubyte Helvetica12_Character_050[] = { 7, 0, 0, 0,248,128,128, 64, 32, 16, 8,136,112, 0, 0, 0}; /* "2" */ +static const GLubyte Helvetica12_Character_051[] = { 7, 0, 0, 0,112,136,136, 8, 8, 48, 8,136,112, 0, 0, 0}; /* "3" */ +static const GLubyte Helvetica12_Character_052[] = { 7, 0, 0, 0, 8, 8,252,136, 72, 40, 40, 24, 8, 0, 0, 0}; /* "4" */ +static const GLubyte Helvetica12_Character_053[] = { 7, 0, 0, 0,112,136,136, 8, 8,240,128,128,248, 0, 0, 0}; /* "5" */ +static const GLubyte Helvetica12_Character_054[] = { 7, 0, 0, 0,112,136,136,136,200,176,128,136,112, 0, 0, 0}; /* "6" */ +static const GLubyte Helvetica12_Character_055[] = { 7, 0, 0, 0, 64, 64, 32, 32, 32, 16, 16, 8,248, 0, 0, 0}; /* "7" */ +static const GLubyte Helvetica12_Character_056[] = { 7, 0, 0, 0,112,136,136,136,136,112,136,136,112, 0, 0, 0}; /* "8" */ +static const GLubyte Helvetica12_Character_057[] = { 7, 0, 0, 0,112,136, 8, 8,120,136,136,136,112, 0, 0, 0}; /* "9" */ +static const GLubyte Helvetica12_Character_096[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,192,128, 64, 0, 0, 0}; /* "`" */ +static const GLubyte Helvetica12_Character_126[] = { 8, 0, 0, 0, 0, 0, 0,152,100, 0, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte Helvetica12_Character_033[] = { 3, 0, 0, 0,128, 0,128,128,128,128,128,128,128, 0, 0, 0}; /* "!" */ +static const GLubyte Helvetica12_Character_064[] = { 12, 0, 0, 0, 0, 62, 0, 64, 0,155, 0,166,128,162, 64,162, 64,146, 64, 77, 64, 96,128, 31, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte Helvetica12_Character_035[] = { 7, 0, 0, 0, 80, 80, 80,252, 40,252, 40, 40, 0, 0, 0, 0}; /* "#" */ +static const GLubyte Helvetica12_Character_036[] = { 7, 0, 0, 32,112,168,168, 40,112,160,168,112, 32, 0, 0, 0}; /* "$" */ +static const GLubyte Helvetica12_Character_037[] = { 11, 0, 0, 0, 0, 0, 0, 35, 0, 20,128, 20,128, 19, 0, 8, 0,104, 0,148, 0,148, 0, 98, 0, 0, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte Helvetica12_Character_094[] = { 6, 0, 0, 0, 0, 0, 0, 0, 0,136, 80, 32, 0, 0, 0, 0}; /* "^" */ +static const GLubyte Helvetica12_Character_038[] = { 9, 0, 0, 0, 0, 0, 0,114, 0,140, 0,132, 0,138, 0, 80, 0, 48, 0, 72, 0, 72, 0, 48, 0, 0, 0, 0, 0, 0, 0}; /* "&" */ +static const GLubyte Helvetica12_Character_042[] = { 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,160, 64,160, 0, 0, 0}; /* "*" */ +static const GLubyte Helvetica12_Character_040[] = { 4, 32, 64, 64,128,128,128,128,128,128, 64, 64, 32, 0, 0, 0}; /* "(" */ +static const GLubyte Helvetica12_Character_041[] = { 4,128, 64, 64, 32, 32, 32, 32, 32, 32, 64, 64,128, 0, 0, 0}; /* ")" */ +static const GLubyte Helvetica12_Character_045[] = { 8, 0, 0, 0, 0, 0, 0,248, 0, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Helvetica12_Character_095[] = { 7, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Helvetica12_Character_061[] = { 7, 0, 0, 0, 0, 0,248, 0,248, 0, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Helvetica12_Character_043[] = { 7, 0, 0, 0, 0, 32, 32,248, 32, 32, 0, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Helvetica12_Character_091[] = { 3,192,128,128,128,128,128,128,128,128,128,128,192, 0, 0, 0}; /* "[" */ +static const GLubyte Helvetica12_Character_123[] = { 4, 48, 64, 64, 64, 64, 64,128, 64, 64, 64, 64, 48, 0, 0, 0}; /* "{" */ +static const GLubyte Helvetica12_Character_125[] = { 4,192, 32, 32, 32, 32, 32, 16, 32, 32, 32, 32,192, 0, 0, 0}; /* "}" */ +static const GLubyte Helvetica12_Character_093[] = { 3,192, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,192, 0, 0, 0}; /* "]" */ +static const GLubyte Helvetica12_Character_059[] = { 3, 0,128, 64, 64, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Helvetica12_Character_058[] = { 3, 0, 0, 0,128, 0, 0, 0, 0,128, 0, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Helvetica12_Character_044[] = { 4, 0,128, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Helvetica12_Character_046[] = { 3, 0, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Helvetica12_Character_060[] = { 7, 0, 0, 0, 0, 12, 48,192, 48, 12, 0, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte Helvetica12_Character_062[] = { 7, 0, 0, 0, 0,192, 48, 12, 48,192, 0, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte Helvetica12_Character_047[] = { 4, 0, 0, 0,128,128,128, 64, 64, 64, 32, 32, 32, 0, 0, 0}; /* "/" */ +static const GLubyte Helvetica12_Character_063[] = { 7, 0, 0, 0, 32, 0, 32, 32, 16, 16,136,136,112, 0, 0, 0}; /* "?" */ +static const GLubyte Helvetica12_Character_092[] = { 4, 0, 0, 0, 32, 32, 32, 64, 64, 64,128,128,128, 0, 0, 0}; /* "\" */ +static const GLubyte Helvetica12_Character_034[] = { 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,160,160,160, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Helvetica12_Character_039[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 64, 64, 0, 0}; /* "'" */ +static const GLubyte Helvetica12_Character_124[] = { 3, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 0}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* Helvetica12_Character_Map[] = {Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_032,Helvetica12_Character_033,Helvetica12_Character_034,Helvetica12_Character_035,Helvetica12_Character_036,Helvetica12_Character_037, + Helvetica12_Character_038,Helvetica12_Character_039,Helvetica12_Character_040,Helvetica12_Character_041,Helvetica12_Character_042,Helvetica12_Character_043,Helvetica12_Character_044,Helvetica12_Character_045,Helvetica12_Character_046,Helvetica12_Character_047,Helvetica12_Character_048,Helvetica12_Character_049,Helvetica12_Character_050,Helvetica12_Character_051,Helvetica12_Character_052,Helvetica12_Character_053,Helvetica12_Character_054,Helvetica12_Character_055,Helvetica12_Character_056,Helvetica12_Character_057,Helvetica12_Character_058,Helvetica12_Character_059,Helvetica12_Character_060,Helvetica12_Character_061,Helvetica12_Character_062,Helvetica12_Character_063,Helvetica12_Character_064,Helvetica12_Character_065,Helvetica12_Character_066,Helvetica12_Character_067,Helvetica12_Character_068,Helvetica12_Character_069,Helvetica12_Character_070,Helvetica12_Character_071,Helvetica12_Character_072,Helvetica12_Character_073,Helvetica12_Character_074,Helvetica12_Character_075,Helvetica12_Character_076, + Helvetica12_Character_077,Helvetica12_Character_078,Helvetica12_Character_079,Helvetica12_Character_080,Helvetica12_Character_081,Helvetica12_Character_082,Helvetica12_Character_083,Helvetica12_Character_084,Helvetica12_Character_085,Helvetica12_Character_086,Helvetica12_Character_087,Helvetica12_Character_088,Helvetica12_Character_089,Helvetica12_Character_090,Helvetica12_Character_091,Helvetica12_Character_092,Helvetica12_Character_093,Helvetica12_Character_094,Helvetica12_Character_095,Helvetica12_Character_096,Helvetica12_Character_097,Helvetica12_Character_098,Helvetica12_Character_099,Helvetica12_Character_100,Helvetica12_Character_101,Helvetica12_Character_102,Helvetica12_Character_103,Helvetica12_Character_104,Helvetica12_Character_105,Helvetica12_Character_106,Helvetica12_Character_107,Helvetica12_Character_108,Helvetica12_Character_109,Helvetica12_Character_110,Helvetica12_Character_111,Helvetica12_Character_112,Helvetica12_Character_113,Helvetica12_Character_114,Helvetica12_Character_115, + Helvetica12_Character_116,Helvetica12_Character_117,Helvetica12_Character_118,Helvetica12_Character_119,Helvetica12_Character_120,Helvetica12_Character_121,Helvetica12_Character_122,Helvetica12_Character_123,Helvetica12_Character_124,Helvetica12_Character_125,Helvetica12_Character_126,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042, + Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042, + Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042, + Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontHelvetica12 = { "-adobe-helvetica-medium-r-normal--12-120-75-75-p-67-iso8859-1", 93, 15, Helvetica12_Character_Map, -1.0f, 3.0f }; + +static const GLubyte Helvetica18_Character_032[] = { 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte Helvetica18_Character_097[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0,118, 0,238, 0,198, 0,198, 0,230, 0,126, 0, 14, 0,198, 0,238, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Helvetica18_Character_098[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0,222, 0,255, 0,227, 0,193,128,193,128,193,128,193,128,227, 0,255, 0,222, 0,192, 0,192, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "b" */ +static const GLubyte Helvetica18_Character_099[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0,127, 0, 99, 0,192, 0,192, 0,192, 0,192, 0, 99, 0,127, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte Helvetica18_Character_100[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 61,128,127,128, 99,128,193,128,193,128,193,128,193,128, 99,128,127,128, 61,128, 1,128, 1,128, 1,128, 1,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "d" */ +static const GLubyte Helvetica18_Character_101[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,127, 0,227, 0,192, 0,192, 0,255, 0,195, 0,195, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte Helvetica18_Character_102[] = { 6, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48,252,252, 48, 48, 60, 28, 0, 0, 0, 0}; /* "f" */ +static const GLubyte Helvetica18_Character_103[] = { 11, 28, 0,127, 0, 99, 0, 1,128, 61,128,127,128, 99,128,193,128,193,128,193,128,193,128, 97,128,127,128, 61,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte Helvetica18_Character_104[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,227, 0,223, 0,206, 0,192, 0,192, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "h" */ +static const GLubyte Helvetica18_Character_105[] = { 4, 0, 0, 0, 0,192,192,192,192,192,192,192,192,192,192, 0, 0,192,192, 0, 0, 0, 0}; /* "i" */ +static const GLubyte Helvetica18_Character_106[] = { 4,224,240, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 48, 48, 0, 0, 0, 0}; /* "j" */ +static const GLubyte Helvetica18_Character_107[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0,199, 0,198, 0,206, 0,204, 0,216, 0,248, 0,240, 0,216, 0,204, 0,198, 0,192, 0,192, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "k" */ +static const GLubyte Helvetica18_Character_108[] = { 4, 0, 0, 0, 0,192,192,192,192,192,192,192,192,192,192,192,192,192,192, 0, 0, 0, 0}; /* "l" */ +static const GLubyte Helvetica18_Character_109[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0,198, 48,198, 48,198, 48,198, 48,198, 48,198, 48,198, 48,231, 48,222,240,204, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte Helvetica18_Character_110[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,227, 0,223, 0,206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte Helvetica18_Character_111[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0,127, 0, 99, 0,193,128,193,128,193,128,193,128, 99, 0,127, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte Helvetica18_Character_112[] = { 11,192, 0,192, 0,192, 0,192, 0,222, 0,255, 0,227, 0,193,128,193,128,193,128,193,128,227, 0,255, 0,222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte Helvetica18_Character_113[] = { 11, 1,128, 1,128, 1,128, 1,128, 61,128,127,128, 99,128,193,128,193,128,193,128,193,128, 99,128,127,128, 61,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte Helvetica18_Character_114[] = { 6, 0, 0, 0, 0,192,192,192,192,192,192,192,224,216,216, 0, 0, 0, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte Helvetica18_Character_115[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0,120, 0,252, 0,198, 0, 6, 0, 62, 0,252, 0,192, 0,198, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte Helvetica18_Character_116[] = { 6, 0, 0, 0, 0, 24, 56, 48, 48, 48, 48, 48, 48,252,252, 48, 48, 48, 0, 0, 0, 0, 0}; /* "t" */ +static const GLubyte Helvetica18_Character_117[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,115, 0,251, 0,199, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte Helvetica18_Character_118[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 24, 0, 60, 0, 36, 0,102, 0,102, 0,102, 0,195, 0,195, 0,195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte Helvetica18_Character_119[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 25,128, 25,128, 57,192, 41, 64,105, 96,102, 96,102, 96,198, 48,198, 48,198, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte Helvetica18_Character_120[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,195, 0,231, 0,102, 0, 60, 0, 24, 0, 24, 0, 60, 0,102, 0,231, 0,195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte Helvetica18_Character_121[] = { 10,112, 0,112, 0, 24, 0, 24, 0, 24, 0, 24, 0, 60, 0, 36, 0,102, 0,102, 0,102, 0,195, 0,195, 0,195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte Helvetica18_Character_122[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0,254, 0,254, 0,192, 0, 96, 0, 48, 0, 24, 0, 12, 0, 6, 0,254, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Helvetica18_Character_065[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0,192, 48,192, 48, 96, 96, 96, 96,127,224, 63,192, 48,192, 48,192, 25,128, 25,128, 15, 0, 15, 0, 6, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "A" */ +static const GLubyte Helvetica18_Character_066[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,255,128,255,192,192,224,192, 96,192, 96,192,224,255,192,255,128,193,128,192,192,192,192,193,192,255,128,255, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "B" */ +static const GLubyte Helvetica18_Character_067[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 15,128, 63,224,112,112, 96, 48,224, 0,192, 0,192, 0,192, 0,192, 0,224, 0, 96, 48,112,112, 63,224, 15,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "C" */ +static const GLubyte Helvetica18_Character_068[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255,128,193,192,192,192,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192,192,193,192,255,128,255, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "D" */ +static const GLubyte Helvetica18_Character_069[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0,255,128,255,128,192, 0,192, 0,192, 0,192, 0,255, 0,255, 0,192, 0,192, 0,192, 0,192, 0,255,128,255,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "E" */ +static const GLubyte Helvetica18_Character_070[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,255, 0,255, 0,192, 0,192, 0,192, 0,192, 0,255,128,255,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "F" */ +static const GLubyte Helvetica18_Character_071[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 15,176, 63,240,112,112, 96, 48,224, 48,193,240,193,240,192, 0,192, 0,224, 48, 96, 48,112,112, 63,224, 15,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "G" */ +static const GLubyte Helvetica18_Character_072[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,255,224,255,224,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96, 0, 0, 0, 0, 0, 0, 0, 0}; /* "H" */ +static const GLubyte Helvetica18_Character_073[] = { 6, 0, 0, 0, 0,192,192,192,192,192,192,192,192,192,192,192,192,192,192, 0, 0, 0, 0}; /* "I" */ +static const GLubyte Helvetica18_Character_074[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,231, 0,195, 0,195, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "J" */ +static const GLubyte Helvetica18_Character_075[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,192,112,192,224,193,192,195,128,199, 0,206, 0,252, 0,248, 0,220, 0,206, 0,199, 0,195,128,193,192,192,224, 0, 0, 0, 0, 0, 0, 0, 0}; /* "K" */ +static const GLubyte Helvetica18_Character_076[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "L" */ +static const GLubyte Helvetica18_Character_077[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0,195, 12,195, 12,199,140,196,140,204,204,204,204,216,108,216,108,240, 60,240, 60,224, 28,224, 28,192, 12,192, 12, 0, 0, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte Helvetica18_Character_078[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,192, 96,192,224,193,224,193,224,195, 96,198, 96,198, 96,204, 96,204, 96,216, 96,240, 96,240, 96,224, 96,192, 96, 0, 0, 0, 0, 0, 0, 0, 0}; /* "N" */ +static const GLubyte Helvetica18_Character_079[] = { 15, 0, 0, 0, 0, 0, 0, 0, 0, 15,128, 63,224,112,112, 96, 48,224, 56,192, 24,192, 24,192, 24,192, 24,224, 56, 96, 48,112,112, 63,224, 15,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "O" */ +static const GLubyte Helvetica18_Character_080[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,255, 0,255,128,193,192,192,192,192,192,193,192,255,128,255, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "P" */ +static const GLubyte Helvetica18_Character_081[] = { 15, 0, 0, 0, 0, 0, 0, 0, 48, 15,176, 63,224,112,240, 97,176,225,184,192, 24,192, 24,192, 24,192, 24,224, 56, 96, 48,112,112, 63,224, 15,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Q" */ +static const GLubyte Helvetica18_Character_082[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0,192,192,192,192,192,192,192,192,193,128,193,128,255, 0,255,128,193,192,192,192,192,192,193,192,255,128,255, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "R" */ +static const GLubyte Helvetica18_Character_083[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0,127,192,224,224,192, 96, 0, 96, 0,224, 3,192, 31, 0,124, 0,224, 0,192, 96,224,224,127,192, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "S" */ +static const GLubyte Helvetica18_Character_084[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0,255,192,255,192, 0, 0, 0, 0, 0, 0, 0, 0}; /* "T" */ +static const GLubyte Helvetica18_Character_085[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 31, 0,127,192, 96,192,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96, 0, 0, 0, 0, 0, 0, 0, 0}; /* "U" */ +static const GLubyte Helvetica18_Character_086[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 15, 0, 15, 0, 25,128, 25,128, 25,128, 48,192, 48,192, 48,192, 96, 96, 96, 96, 96, 96,192, 48,192, 48, 0, 0, 0, 0, 0, 0, 0, 0}; /* "V" */ +static const GLubyte Helvetica18_Character_087[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 0, 24, 24, 0, 28, 56, 0, 52, 44, 0, 54,108, 0, 54,108, 0,102,102, 0,102,102, 0, 98, 70, 0, 99,198, 0,195,195, 0,193,131, 0,193,131, 0,193,131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte Helvetica18_Character_088[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,192, 96,224,224, 96,192,113,192, 49,128, 27, 0, 14, 0, 14, 0, 27, 0, 49,128,113,192, 96,192,224,224,192, 96, 0, 0, 0, 0, 0, 0, 0, 0}; /* "X" */ +static const GLubyte Helvetica18_Character_089[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 15, 0, 25,128, 48,192, 48,192, 96, 96, 96, 96,192, 48,192, 48, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Y" */ +static const GLubyte Helvetica18_Character_090[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0,255,192,255,192,192, 0, 96, 0, 48, 0, 24, 0, 28, 0, 12, 0, 6, 0, 3, 0, 1,128, 0,192,255,192,255,192, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Z" */ +static const GLubyte Helvetica18_Character_048[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,102, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,102, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "0" */ +static const GLubyte Helvetica18_Character_049[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0,248, 0,248, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "1" */ +static const GLubyte Helvetica18_Character_050[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255, 0,192, 0,224, 0,112, 0, 56, 0, 28, 0, 14, 0, 7, 0, 3, 0,195, 0,254, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "2" */ +static const GLubyte Helvetica18_Character_051[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,199, 0,195, 0, 3, 0, 7, 0, 30, 0, 28, 0, 6, 0,195, 0,195, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "3" */ +static const GLubyte Helvetica18_Character_052[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 0,255,128,255,128,195, 0, 99, 0, 51, 0, 51, 0, 27, 0, 15, 0, 7, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "4" */ +static const GLubyte Helvetica18_Character_053[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,124, 0,254, 0,199, 0,195, 0, 3, 0, 3, 0,199, 0,254, 0,252, 0,192, 0,192, 0,254, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "5" */ +static const GLubyte Helvetica18_Character_054[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,227, 0,195, 0,195, 0,195, 0,254, 0,220, 0,192, 0,192, 0, 99, 0,127, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "6" */ +static const GLubyte Helvetica18_Character_055[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 96, 0, 48, 0, 48, 0, 48, 0, 24, 0, 24, 0, 12, 0, 12, 0, 6, 0, 3, 0,255, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "7" */ +static const GLubyte Helvetica18_Character_056[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,231, 0,195, 0,195, 0,102, 0,126, 0,102, 0,195, 0,195, 0,231, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "8" */ +static const GLubyte Helvetica18_Character_057[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,124, 0,254, 0,198, 0, 3, 0, 3, 0, 59, 0,127, 0,195, 0,195, 0,195, 0,199, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "9" */ +static const GLubyte Helvetica18_Character_096[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192,192,128,128, 64, 0, 0, 0, 0}; /* "`" */ +static const GLubyte Helvetica18_Character_126[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,204, 0,126, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte Helvetica18_Character_033[] = { 6, 0, 0, 0, 0,192,192, 0, 0,128,128,192,192,192,192,192,192,192,192, 0, 0, 0, 0}; /* "!" */ +static const GLubyte Helvetica18_Character_064[] = { 18, 0, 0, 0, 7,224, 0, 31,240, 0, 56, 0, 0,112, 0, 0,103,112, 0,207,248, 0,204,204, 0,204,102, 0,204,102, 0,204, 99, 0,198, 51, 0,103,115, 0, 99,179, 0, 48, 6, 0, 28, 14, 0, 15,252, 0, 3,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte Helvetica18_Character_035[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 36, 0, 36, 0,255,128,255,128, 18, 0, 18, 0, 18, 0,127,192,127,192, 9, 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "#" */ +static const GLubyte Helvetica18_Character_036[] = { 10, 0, 0, 0, 0, 8, 0, 8, 0, 62, 0,127, 0,235,128,201,128, 9,128, 15, 0, 62, 0,120, 0,232, 0,200, 0,203, 0,127, 0, 62, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "$" */ +static const GLubyte Helvetica18_Character_037[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 24,120, 24,252, 12,204, 12,204, 6,252, 6,120, 3, 0,123, 0,253,128,205,128,204,192,252,192,120, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte Helvetica18_Character_094[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,130, 0,198, 0,108, 0, 56, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "^" */ +static const GLubyte Helvetica18_Character_038[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 60,112,126,224,231,192,195,128,195,192,198,192,238,192,124, 0, 60, 0,102, 0,102, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "&" */ +static const GLubyte Helvetica18_Character_042[] = { 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,136,112,112,248, 32, 32, 0, 0, 0, 0}; /* "*" */ +static const GLubyte Helvetica18_Character_040[] = { 6, 16, 48, 96, 96,192,192,192,192,192,192,192,192,192,192, 96, 96, 48, 16, 0, 0, 0, 0}; /* "(" */ +static const GLubyte Helvetica18_Character_041[] = { 6,128,192, 96, 96, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 96, 96,192,128, 0, 0, 0, 0}; /* ")" */ +static const GLubyte Helvetica18_Character_045[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Helvetica18_Character_095[] = { 10,255,248,255,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Helvetica18_Character_061[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 0,254, 0, 0, 0, 0, 0,254, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Helvetica18_Character_043[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 24, 0, 24, 0, 24, 0,255, 0,255, 0, 24, 0, 24, 0, 24, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Helvetica18_Character_091[] = { 5,240,240,192,192,192,192,192,192,192,192,192,192,192,192,192,192,240,240, 0, 0, 0, 0}; /* "[" */ +static const GLubyte Helvetica18_Character_123[] = { 7, 12, 24, 48, 48, 48, 48, 48, 48, 96,192, 96, 48, 48, 48, 48, 48, 24, 12, 0, 0, 0, 0}; /* "{" */ +static const GLubyte Helvetica18_Character_125[] = { 7,192, 96, 48, 48, 48, 48, 48, 48, 24, 12, 24, 48, 48, 48, 48, 48, 96,192, 0, 0, 0, 0}; /* "}" */ +static const GLubyte Helvetica18_Character_093[] = { 5,240,240, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,240,240, 0, 0, 0, 0}; /* "]" */ +static const GLubyte Helvetica18_Character_059[] = { 5, 0,128, 64, 64,192,192, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Helvetica18_Character_058[] = { 5, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Helvetica18_Character_044[] = { 5, 0,128, 64, 64,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Helvetica18_Character_046[] = { 5, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Helvetica18_Character_060[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 15, 0, 60, 0,112, 0,192, 0,112, 0, 60, 0, 15, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte Helvetica18_Character_062[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,192, 0,240, 0, 60, 0, 14, 0, 3, 0, 14, 0, 60, 0,240, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte Helvetica18_Character_047[] = { 5, 0, 0, 0, 0,192,192, 64, 64, 96, 96, 32, 32, 48, 48, 16, 16, 24, 24, 0, 0, 0, 0}; /* "/" */ +static const GLubyte Helvetica18_Character_063[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 48, 0, 0, 0, 0, 0, 48, 0, 48, 0, 48, 0, 56, 0, 28, 0, 14, 0,198, 0,198, 0,254, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "?" */ +static const GLubyte Helvetica18_Character_092[] = { 5, 0, 0, 0, 0, 24, 24, 16, 16, 48, 48, 32, 32, 96, 96, 64, 64,192,192, 0, 0, 0, 0}; /* "\" */ +static const GLubyte Helvetica18_Character_034[] = { 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,144,144,216,216,216, 0, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Helvetica18_Character_039[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 64, 64,192,192, 0, 0, 0, 0}; /* "'" */ +static const GLubyte Helvetica18_Character_124[] = { 4, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* Helvetica18_Character_Map[] = {Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_032,Helvetica18_Character_033,Helvetica18_Character_034,Helvetica18_Character_035,Helvetica18_Character_036,Helvetica18_Character_037, + Helvetica18_Character_038,Helvetica18_Character_039,Helvetica18_Character_040,Helvetica18_Character_041,Helvetica18_Character_042,Helvetica18_Character_043,Helvetica18_Character_044,Helvetica18_Character_045,Helvetica18_Character_046,Helvetica18_Character_047,Helvetica18_Character_048,Helvetica18_Character_049,Helvetica18_Character_050,Helvetica18_Character_051,Helvetica18_Character_052,Helvetica18_Character_053,Helvetica18_Character_054,Helvetica18_Character_055,Helvetica18_Character_056,Helvetica18_Character_057,Helvetica18_Character_058,Helvetica18_Character_059,Helvetica18_Character_060,Helvetica18_Character_061,Helvetica18_Character_062,Helvetica18_Character_063,Helvetica18_Character_064,Helvetica18_Character_065,Helvetica18_Character_066,Helvetica18_Character_067,Helvetica18_Character_068,Helvetica18_Character_069,Helvetica18_Character_070,Helvetica18_Character_071,Helvetica18_Character_072,Helvetica18_Character_073,Helvetica18_Character_074,Helvetica18_Character_075,Helvetica18_Character_076, + Helvetica18_Character_077,Helvetica18_Character_078,Helvetica18_Character_079,Helvetica18_Character_080,Helvetica18_Character_081,Helvetica18_Character_082,Helvetica18_Character_083,Helvetica18_Character_084,Helvetica18_Character_085,Helvetica18_Character_086,Helvetica18_Character_087,Helvetica18_Character_088,Helvetica18_Character_089,Helvetica18_Character_090,Helvetica18_Character_091,Helvetica18_Character_092,Helvetica18_Character_093,Helvetica18_Character_094,Helvetica18_Character_095,Helvetica18_Character_096,Helvetica18_Character_097,Helvetica18_Character_098,Helvetica18_Character_099,Helvetica18_Character_100,Helvetica18_Character_101,Helvetica18_Character_102,Helvetica18_Character_103,Helvetica18_Character_104,Helvetica18_Character_105,Helvetica18_Character_106,Helvetica18_Character_107,Helvetica18_Character_108,Helvetica18_Character_109,Helvetica18_Character_110,Helvetica18_Character_111,Helvetica18_Character_112,Helvetica18_Character_113,Helvetica18_Character_114,Helvetica18_Character_115, + Helvetica18_Character_116,Helvetica18_Character_117,Helvetica18_Character_118,Helvetica18_Character_119,Helvetica18_Character_120,Helvetica18_Character_121,Helvetica18_Character_122,Helvetica18_Character_123,Helvetica18_Character_124,Helvetica18_Character_125,Helvetica18_Character_126,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042, + Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042, + Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042, + Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontHelvetica18 = { "-adobe-helvetica-medium-r-normal--18-180-75-75-p-98-iso8859-1", 93, 22, Helvetica18_Character_Map, -1.0f, 4.0f }; + +static const GLubyte TimesRoman10_Character_032[] = { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte TimesRoman10_Character_097[] = { 4, 0, 0, 0,224,160, 96, 32,192, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte TimesRoman10_Character_098[] = { 5, 0, 0, 0,224,144,144,144,224,128,128, 0, 0, 0}; /* "b" */ +static const GLubyte TimesRoman10_Character_099[] = { 4, 0, 0, 0, 96,128,128,128, 96, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte TimesRoman10_Character_100[] = { 5, 0, 0, 0,104,144,144,144,112, 16, 48, 0, 0, 0}; /* "d" */ +static const GLubyte TimesRoman10_Character_101[] = { 4, 0, 0, 0, 96,128,192,160, 96, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte TimesRoman10_Character_102[] = { 4, 0, 0, 0,224, 64, 64, 64,224, 64, 48, 0, 0, 0}; /* "f" */ +static const GLubyte TimesRoman10_Character_103[] = { 5, 0,224,144, 96, 64,160,160,112, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte TimesRoman10_Character_104[] = { 5, 0, 0, 0,216,144,144,144,224,128,128, 0, 0, 0}; /* "h" */ +static const GLubyte TimesRoman10_Character_105[] = { 3, 0, 0, 0, 64, 64, 64, 64,192, 0, 64, 0, 0, 0}; /* "i" */ +static const GLubyte TimesRoman10_Character_106[] = { 3, 0,128, 64, 64, 64, 64, 64,192, 0, 64, 0, 0, 0}; /* "j" */ +static const GLubyte TimesRoman10_Character_107[] = { 5, 0, 0, 0,152,144,224,160,144,128,128, 0, 0, 0}; /* "k" */ +static const GLubyte TimesRoman10_Character_108[] = { 4, 0, 0, 0,224, 64, 64, 64, 64, 64,192, 0, 0, 0}; /* "l" */ +static const GLubyte TimesRoman10_Character_109[] = { 8, 0, 0, 0,219,146,146,146,236, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte TimesRoman10_Character_110[] = { 5, 0, 0, 0,216,144,144,144,224, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte TimesRoman10_Character_111[] = { 5, 0, 0, 0, 96,144,144,144, 96, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte TimesRoman10_Character_112[] = { 5, 0,192,128,224,144,144,144,224, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte TimesRoman10_Character_113[] = { 5, 0, 56, 16,112,144,144,144,112, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte TimesRoman10_Character_114[] = { 4, 0, 0, 0,224, 64, 64, 96,160, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte TimesRoman10_Character_115[] = { 4, 0, 0, 0,224, 32, 96,128,224, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte TimesRoman10_Character_116[] = { 4, 0, 0, 0, 48, 64, 64, 64,224, 64, 0, 0, 0, 0}; /* "t" */ +static const GLubyte TimesRoman10_Character_117[] = { 5, 0, 0, 0,104,144,144,144,144, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte TimesRoman10_Character_118[] = { 5, 0, 0, 0, 32, 96, 80,144,216, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte TimesRoman10_Character_119[] = { 8, 0, 0, 0, 40,108, 84,146,219, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte TimesRoman10_Character_120[] = { 6, 0, 0, 0,216, 80, 32, 80,216, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte TimesRoman10_Character_121[] = { 5, 0, 64, 64, 32, 48, 80, 72,220, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte TimesRoman10_Character_122[] = { 5, 0, 0, 0,240,144, 64, 32,240, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte TimesRoman10_Character_065[] = { 8, 0, 0, 0,238, 68,124, 40, 40, 56, 16, 0, 0, 0}; /* "A" */ +static const GLubyte TimesRoman10_Character_066[] = { 6, 0, 0, 0,240, 72, 72,112, 72, 72,240, 0, 0, 0}; /* "B" */ +static const GLubyte TimesRoman10_Character_067[] = { 7, 0, 0, 0,120,196,128,128,128,196,124, 0, 0, 0}; /* "C" */ +static const GLubyte TimesRoman10_Character_068[] = { 7, 0, 0, 0,248, 76, 68, 68, 68, 76,248, 0, 0, 0}; /* "D" */ +static const GLubyte TimesRoman10_Character_069[] = { 6, 0, 0, 0,248, 72, 64,112, 64, 72,248, 0, 0, 0}; /* "E" */ +static const GLubyte TimesRoman10_Character_070[] = { 6, 0, 0, 0,224, 64, 64,112, 64, 72,248, 0, 0, 0}; /* "F" */ +static const GLubyte TimesRoman10_Character_071[] = { 7, 0, 0, 0,120,196,132,156,128,196,124, 0, 0, 0}; /* "G" */ +static const GLubyte TimesRoman10_Character_072[] = { 8, 0, 0, 0,238, 68, 68,124, 68, 68,238, 0, 0, 0}; /* "H" */ +static const GLubyte TimesRoman10_Character_073[] = { 4, 0, 0, 0,224, 64, 64, 64, 64, 64,224, 0, 0, 0}; /* "I" */ +static const GLubyte TimesRoman10_Character_074[] = { 4, 0, 0, 0,192,160, 32, 32, 32, 32,112, 0, 0, 0}; /* "J" */ +static const GLubyte TimesRoman10_Character_075[] = { 7, 0, 0, 0,236, 72, 80, 96, 80, 72,236, 0, 0, 0}; /* "K" */ +static const GLubyte TimesRoman10_Character_076[] = { 6, 0, 0, 0,248, 72, 64, 64, 64, 64,224, 0, 0, 0}; /* "L" */ +static const GLubyte TimesRoman10_Character_077[] = { 10, 0, 0, 0, 0, 0, 0,235,128, 73, 0, 85, 0, 85, 0, 99, 0, 99, 0,227,128, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte TimesRoman10_Character_078[] = { 8, 0, 0, 0,228, 76, 76, 84, 84,100,238, 0, 0, 0}; /* "N" */ +static const GLubyte TimesRoman10_Character_079[] = { 7, 0, 0, 0,120,204,132,132,132,204,120, 0, 0, 0}; /* "O" */ +static const GLubyte TimesRoman10_Character_080[] = { 6, 0, 0, 0,224, 64, 64,112, 72, 72,240, 0, 0, 0}; /* "P" */ +static const GLubyte TimesRoman10_Character_081[] = { 7, 0, 12, 24,112,204,132,132,132,204,120, 0, 0, 0}; /* "Q" */ +static const GLubyte TimesRoman10_Character_082[] = { 7, 0, 0, 0,236, 72, 80,112, 72, 72,240, 0, 0, 0}; /* "R" */ +static const GLubyte TimesRoman10_Character_083[] = { 5, 0, 0, 0,224,144, 16, 96,192,144,112, 0, 0, 0}; /* "S" */ +static const GLubyte TimesRoman10_Character_084[] = { 6, 0, 0, 0,112, 32, 32, 32, 32,168,248, 0, 0, 0}; /* "T" */ +static const GLubyte TimesRoman10_Character_085[] = { 8, 0, 0, 0, 56,108, 68, 68, 68, 68,238, 0, 0, 0}; /* "U" */ +static const GLubyte TimesRoman10_Character_086[] = { 8, 0, 0, 0, 16, 16, 40, 40,108, 68,238, 0, 0, 0}; /* "V" */ +static const GLubyte TimesRoman10_Character_087[] = { 10, 0, 0, 0, 0, 0, 0, 34, 0, 34, 0, 85, 0, 85, 0,201,128,136,128,221,192, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte TimesRoman10_Character_088[] = { 8, 0, 0, 0,238, 68, 40, 16, 40, 68,238, 0, 0, 0}; /* "X" */ +static const GLubyte TimesRoman10_Character_089[] = { 8, 0, 0, 0, 56, 16, 16, 40, 40, 68,238, 0, 0, 0}; /* "Y" */ +static const GLubyte TimesRoman10_Character_090[] = { 6, 0, 0, 0,248,136, 64, 32, 16,136,248, 0, 0, 0}; /* "Z" */ +static const GLubyte TimesRoman10_Character_048[] = { 5, 0, 0, 0, 96,144,144,144,144,144, 96, 0, 0, 0}; /* "0" */ +static const GLubyte TimesRoman10_Character_049[] = { 5, 0, 0, 0,224, 64, 64, 64, 64,192, 64, 0, 0, 0}; /* "1" */ +static const GLubyte TimesRoman10_Character_050[] = { 5, 0, 0, 0,240, 64, 32, 32, 16,144, 96, 0, 0, 0}; /* "2" */ +static const GLubyte TimesRoman10_Character_051[] = { 5, 0, 0, 0,224, 16, 16, 96, 16,144, 96, 0, 0, 0}; /* "3" */ +static const GLubyte TimesRoman10_Character_052[] = { 5, 0, 0, 0, 16, 16,248,144, 80, 48, 16, 0, 0, 0}; /* "4" */ +static const GLubyte TimesRoman10_Character_053[] = { 5, 0, 0, 0,224,144, 16, 16,224, 64,112, 0, 0, 0}; /* "5" */ +static const GLubyte TimesRoman10_Character_054[] = { 5, 0, 0, 0, 96,144,144,144,224, 64, 48, 0, 0, 0}; /* "6" */ +static const GLubyte TimesRoman10_Character_055[] = { 5, 0, 0, 0, 64, 64, 64, 32, 32,144,240, 0, 0, 0}; /* "7" */ +static const GLubyte TimesRoman10_Character_056[] = { 5, 0, 0, 0, 96,144,144, 96,144,144, 96, 0, 0, 0}; /* "8" */ +static const GLubyte TimesRoman10_Character_057[] = { 5, 0, 0, 0,192, 32,112,144,144,144, 96, 0, 0, 0}; /* "9" */ +static const GLubyte TimesRoman10_Character_096[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0,192,128, 0, 0, 0}; /* "`" */ +static const GLubyte TimesRoman10_Character_126[] = { 7, 0, 0, 0, 0, 0,152,100, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte TimesRoman10_Character_033[] = { 3, 0, 0, 0,128, 0,128,128,128,128,128, 0, 0, 0}; /* "!" */ +static const GLubyte TimesRoman10_Character_064[] = { 9, 0, 0, 62, 0, 64, 0,146, 0,173, 0,165, 0,165, 0,157, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte TimesRoman10_Character_035[] = { 5, 0, 0, 0, 80, 80,248, 80,248, 80, 80, 0, 0, 0}; /* "#" */ +static const GLubyte TimesRoman10_Character_036[] = { 5, 0, 0, 32,224,144, 16, 96,128,144,112, 32, 0, 0}; /* "$" */ +static const GLubyte TimesRoman10_Character_037[] = { 8, 0, 0, 0, 68, 42, 42, 86,168,164,126, 0, 0, 0}; /* "%" */ +static const GLubyte TimesRoman10_Character_094[] = { 5, 0, 0, 0, 0, 0, 0, 0,160,160, 64, 0, 0, 0}; /* "^" */ +static const GLubyte TimesRoman10_Character_038[] = { 8, 0, 0, 0,118,141,152,116,110, 80, 48, 0, 0, 0}; /* "&" */ +static const GLubyte TimesRoman10_Character_042[] = { 5, 0, 0, 0, 0, 0, 0, 0,160, 64,160, 0, 0, 0}; /* "*" */ +static const GLubyte TimesRoman10_Character_040[] = { 4, 0, 32, 64, 64,128,128,128, 64, 64, 32, 0, 0, 0}; /* "(" */ +static const GLubyte TimesRoman10_Character_041[] = { 4, 0,128, 64, 64, 32, 32, 32, 64, 64,128, 0, 0, 0}; /* ")" */ +static const GLubyte TimesRoman10_Character_045[] = { 7, 0, 0, 0, 0, 0,240, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte TimesRoman10_Character_095[] = { 5,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte TimesRoman10_Character_061[] = { 6, 0, 0, 0, 0,248, 0,248, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte TimesRoman10_Character_043[] = { 6, 0, 0, 0, 32, 32,248, 32, 32, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte TimesRoman10_Character_091[] = { 3, 0,192,128,128,128,128,128,128,128,192, 0, 0, 0}; /* "[" */ +static const GLubyte TimesRoman10_Character_123[] = { 4, 0, 32, 64, 64, 64,128, 64, 64, 64, 32, 0, 0, 0}; /* "{" */ +static const GLubyte TimesRoman10_Character_125[] = { 4, 0,128, 64, 64, 64, 32, 64, 64, 64,128, 0, 0, 0}; /* "}" */ +static const GLubyte TimesRoman10_Character_093[] = { 3, 0,192, 64, 64, 64, 64, 64, 64, 64,192, 0, 0, 0}; /* "]" */ +static const GLubyte TimesRoman10_Character_059[] = { 3, 0,128,128,128, 0, 0, 0,128, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte TimesRoman10_Character_058[] = { 3, 0, 0, 0,128, 0, 0, 0,128, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte TimesRoman10_Character_044[] = { 3, 0,128,128,128, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte TimesRoman10_Character_046[] = { 3, 0, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte TimesRoman10_Character_060[] = { 5, 0, 0, 0, 32, 64,128, 64, 32, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte TimesRoman10_Character_062[] = { 5, 0, 0, 0,128, 64, 32, 64,128, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte TimesRoman10_Character_047[] = { 3, 0, 0, 0,128,128, 64, 64, 64, 32, 32, 0, 0, 0}; /* "/" */ +static const GLubyte TimesRoman10_Character_063[] = { 4, 0, 0, 0, 64, 0, 64, 64, 32,160,224, 0, 0, 0}; /* "?" */ +static const GLubyte TimesRoman10_Character_092[] = { 3, 0, 0, 0, 32, 32, 64, 64, 64,128,128, 0, 0, 0}; /* "\" */ +static const GLubyte TimesRoman10_Character_034[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0,160,160, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte TimesRoman10_Character_039[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0, 64, 64,192, 0, 0}; /* "'" */ +static const GLubyte TimesRoman10_Character_124[] = { 2,128,128,128,128,128,128,128,128,128,128,128,128,128}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* TimesRoman10_Character_Map[] = {TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_032,TimesRoman10_Character_033,TimesRoman10_Character_034,TimesRoman10_Character_035, +TimesRoman10_Character_036,TimesRoman10_Character_037,TimesRoman10_Character_038,TimesRoman10_Character_039,TimesRoman10_Character_040,TimesRoman10_Character_041,TimesRoman10_Character_042,TimesRoman10_Character_043,TimesRoman10_Character_044,TimesRoman10_Character_045,TimesRoman10_Character_046,TimesRoman10_Character_047,TimesRoman10_Character_048,TimesRoman10_Character_049,TimesRoman10_Character_050,TimesRoman10_Character_051,TimesRoman10_Character_052,TimesRoman10_Character_053,TimesRoman10_Character_054,TimesRoman10_Character_055,TimesRoman10_Character_056,TimesRoman10_Character_057,TimesRoman10_Character_058,TimesRoman10_Character_059,TimesRoman10_Character_060,TimesRoman10_Character_061,TimesRoman10_Character_062,TimesRoman10_Character_063,TimesRoman10_Character_064,TimesRoman10_Character_065,TimesRoman10_Character_066,TimesRoman10_Character_067,TimesRoman10_Character_068,TimesRoman10_Character_069,TimesRoman10_Character_070,TimesRoman10_Character_071,TimesRoman10_Character_072, +TimesRoman10_Character_073,TimesRoman10_Character_074,TimesRoman10_Character_075,TimesRoman10_Character_076,TimesRoman10_Character_077,TimesRoman10_Character_078,TimesRoman10_Character_079,TimesRoman10_Character_080,TimesRoman10_Character_081,TimesRoman10_Character_082,TimesRoman10_Character_083,TimesRoman10_Character_084,TimesRoman10_Character_085,TimesRoman10_Character_086,TimesRoman10_Character_087,TimesRoman10_Character_088,TimesRoman10_Character_089,TimesRoman10_Character_090,TimesRoman10_Character_091,TimesRoman10_Character_092,TimesRoman10_Character_093,TimesRoman10_Character_094,TimesRoman10_Character_095,TimesRoman10_Character_096,TimesRoman10_Character_097,TimesRoman10_Character_098,TimesRoman10_Character_099,TimesRoman10_Character_100,TimesRoman10_Character_101,TimesRoman10_Character_102,TimesRoman10_Character_103,TimesRoman10_Character_104,TimesRoman10_Character_105,TimesRoman10_Character_106,TimesRoman10_Character_107,TimesRoman10_Character_108,TimesRoman10_Character_109, +TimesRoman10_Character_110,TimesRoman10_Character_111,TimesRoman10_Character_112,TimesRoman10_Character_113,TimesRoman10_Character_114,TimesRoman10_Character_115,TimesRoman10_Character_116,TimesRoman10_Character_117,TimesRoman10_Character_118,TimesRoman10_Character_119,TimesRoman10_Character_120,TimesRoman10_Character_121,TimesRoman10_Character_122,TimesRoman10_Character_123,TimesRoman10_Character_124,TimesRoman10_Character_125,TimesRoman10_Character_126,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042, +TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042, +TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042, +TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontTimesRoman10 = { "-adobe-times-medium-r-normal--10-100-75-75-p-54-iso8859-1", 93, 13, TimesRoman10_Character_Map, 0.0f, 3.0f }; + +static const GLubyte TimesRoman24_Character_032[] = { 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte TimesRoman24_Character_097[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,113,128,251, 0,199, 0,195, 0,195, 0, 99, 0, 59, 0, 15, 0, 3, 0, 99, 0,103, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte TimesRoman24_Character_098[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 0,115,128, 97,128, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 97,128,115,128,110, 0, 96, 0, 96, 0, 96, 0, 96, 0,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "b" */ +static const GLubyte TimesRoman24_Character_099[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,127, 0,112,128,224, 0,192, 0,192, 0,192, 0,192, 0,192, 0, 65,128, 99,128, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte TimesRoman24_Character_100[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30,192,115,128, 97,128,193,128,193,128,193,128,193,128,193,128,193,128, 97,128,115,128, 29,128, 1,128, 1,128, 1,128, 1,128, 3,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "d" */ +static const GLubyte TimesRoman24_Character_101[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,127, 0,112,128,224, 0,192, 0,192, 0,192, 0,255,128,193,128, 65,128, 99, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte TimesRoman24_Character_102[] = { 7, 0, 0, 0, 0, 0, 0,120, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,254, 48, 48, 48, 22, 14, 0, 0, 0, 0, 0}; /* "f" */ +static const GLubyte TimesRoman24_Character_103[] = { 12, 0, 0, 63, 0,241,192,192, 96,192, 32, 96, 96, 63,192,127, 0, 96, 0, 48, 0, 62, 0, 51, 0, 97,128, 97,128, 97,128, 97,128, 51, 0, 31,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte TimesRoman24_Character_104[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,241,224, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192,113,192,111,128,103, 0, 96, 0, 96, 0, 96, 0, 96, 0,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "h" */ +static const GLubyte TimesRoman24_Character_105[] = { 6, 0, 0, 0, 0, 0, 0,240, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,224, 0, 0, 0, 96, 96, 0, 0, 0, 0, 0}; /* "i" */ +static const GLubyte TimesRoman24_Character_106[] = { 6, 0,192,224, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,112, 0, 0, 0, 48, 48, 0, 0, 0, 0, 0}; /* "j" */ +static const GLubyte TimesRoman24_Character_107[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,243,224, 97,192, 99,128,103, 0,110, 0,108, 0,120, 0,104, 0,100, 0,102, 0, 99, 0,103,192, 96, 0, 96, 0, 96, 0, 96, 0,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "k" */ +static const GLubyte TimesRoman24_Character_108[] = { 6, 0, 0, 0, 0, 0, 0,240, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,224, 0, 0, 0, 0, 0}; /* "l" */ +static const GLubyte TimesRoman24_Character_109[] = { 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,241,227,192, 96,193,128, 96,193,128, 96,193,128, 96,193,128, 96,193,128, 96,193,128, 96,193,128, 96,193,128,113,227,128,111,159, 0,231, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte TimesRoman24_Character_110[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,241,224, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192,113,192,111,128,231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte TimesRoman24_Character_111[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,115,128, 97,128,192,192,192,192,192,192,192,192,192,192,192,192, 97,128,115,128, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte TimesRoman24_Character_112[] = { 12, 0, 0,240, 0, 96, 0, 96, 0, 96, 0, 96, 0,110, 0,115,128, 97,128, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 97,128,115,128,238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte TimesRoman24_Character_113[] = { 12, 0, 0, 3,192, 1,128, 1,128, 1,128, 1,128, 29,128,115,128, 97,128,193,128,193,128,193,128,193,128,193,128,193,128, 97,128,115,128, 29,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte TimesRoman24_Character_114[] = { 8, 0, 0, 0, 0, 0, 0,240, 96, 96, 96, 96, 96, 96, 96, 96,118,110,230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte TimesRoman24_Character_115[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,248, 0,198, 0,131, 0, 3, 0, 7, 0, 30, 0,124, 0,112, 0,224, 0,194, 0,102, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte TimesRoman24_Character_116[] = { 7, 0, 0, 0, 0, 0, 0, 28, 50, 48, 48, 48, 48, 48, 48, 48, 48, 48,254,112, 48, 16, 0, 0, 0, 0, 0, 0, 0}; /* "t" */ +static const GLubyte TimesRoman24_Character_117[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28,224, 62,192,113,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192,225,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte TimesRoman24_Character_118[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 14, 0, 14, 0, 26, 0, 25, 0, 25, 0, 49, 0, 48,128, 48,128, 96,128, 96,192,241,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte TimesRoman24_Character_119[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 16, 0, 14, 56, 0, 14, 56, 0, 26, 40, 0, 26,100, 0, 25,100, 0, 49,100, 0, 48,194, 0, 48,194, 0, 96,194, 0, 96,195, 0,241,231,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte TimesRoman24_Character_120[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,241,224, 96,192, 33,128, 51,128, 27, 0, 14, 0, 12, 0, 26, 0, 57, 0, 49,128, 96,192,241,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte TimesRoman24_Character_121[] = { 11, 0, 0,224, 0,240, 0, 24, 0, 8, 0, 12, 0, 4, 0, 14, 0, 14, 0, 26, 0, 25, 0, 25, 0, 49, 0, 48,128, 48,128, 96,128, 96,192,241,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte TimesRoman24_Character_122[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,195, 0, 97, 0,112, 0, 48, 0, 56, 0, 24, 0, 28, 0, 14, 0,134, 0,195, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte TimesRoman24_Character_065[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 31,128, 48, 6, 0, 16, 6, 0, 16, 12, 0, 24, 12, 0, 8, 12, 0, 15,248, 0, 12, 24, 0, 4, 24, 0, 4, 48, 0, 6, 48, 0, 2, 48, 0, 2, 96, 0, 1, 96, 0, 1,192, 0, 1,192, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "A" */ +static const GLubyte TimesRoman24_Character_066[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,224, 48,120, 48, 24, 48, 12, 48, 12, 48, 12, 48, 24, 48, 56, 63,224, 48, 64, 48, 48, 48, 24, 48, 24, 48, 24, 48, 48, 48,112,255,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "B" */ +static const GLubyte TimesRoman24_Character_067[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 30, 56, 56, 8, 96, 4, 96, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0, 96, 4, 96, 4, 56, 12, 28, 60, 7,228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "C" */ +static const GLubyte TimesRoman24_Character_068[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,192, 0, 48,112, 0, 48, 56, 0, 48, 12, 0, 48, 12, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 12, 0, 48, 12, 0, 48, 56, 0, 48,112, 0,255,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "D" */ +static const GLubyte TimesRoman24_Character_069[] = { 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,248, 48, 24, 48, 8, 48, 8, 48, 0, 48, 0, 48, 64, 48, 64, 63,192, 48, 64, 48, 64, 48, 0, 48, 0, 48, 16, 48, 16, 48, 48,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "E" */ +static const GLubyte TimesRoman24_Character_070[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 32, 48, 32, 63,224, 48, 32, 48, 32, 48, 0, 48, 0, 48, 16, 48, 16, 48, 48,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "F" */ +static const GLubyte TimesRoman24_Character_071[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 0, 30, 56, 0, 56, 28, 0, 96, 12, 0, 96, 12, 0,192, 12, 0,192, 12, 0,192, 63, 0,192, 0, 0,192, 0, 0,192, 0, 0,192, 0, 0, 96, 4, 0, 96, 4, 0, 56, 12, 0, 28, 60, 0, 7,228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "G" */ +static const GLubyte TimesRoman24_Character_072[] = { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 31,128, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 63,254, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0,252, 31,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "H" */ +static const GLubyte TimesRoman24_Character_073[] = { 8, 0, 0, 0, 0, 0, 0,252, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,252, 0, 0, 0, 0, 0}; /* "I" */ +static const GLubyte TimesRoman24_Character_074[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,120, 0,204, 0,198, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 31,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "J" */ +static const GLubyte TimesRoman24_Character_075[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 31, 0, 48, 14, 0, 48, 28, 0, 48, 56, 0, 48,112, 0, 48,224, 0, 49,192, 0, 51,128, 0, 63, 0, 0, 62, 0, 0, 51, 0, 0, 49,128, 0, 48,192, 0, 48, 96, 0, 48, 48, 0, 48, 24, 0,252,126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "K" */ +static const GLubyte TimesRoman24_Character_076[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,248, 48, 24, 48, 8, 48, 8, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "L" */ +static const GLubyte TimesRoman24_Character_077[] = { 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,248, 33,248, 32, 96, 96, 32, 96, 96, 32,208, 96, 32,208, 96, 33,136, 96, 33,136, 96, 35, 8, 96, 35, 4, 96, 38, 4, 96, 38, 2, 96, 44, 2, 96, 44, 2, 96, 56, 1, 96, 56, 1, 96, 48, 0,224,240, 0,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte TimesRoman24_Character_078[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,248, 12, 0, 32, 28, 0, 32, 28, 0, 32, 52, 0, 32,100, 0, 32,100, 0, 32,196, 0, 33,132, 0, 33,132, 0, 35, 4, 0, 38, 4, 0, 38, 4, 0, 44, 4, 0, 56, 4, 0, 56, 4, 0, 48, 4, 0,240, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "N" */ +static const GLubyte TimesRoman24_Character_079[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 0, 28, 56, 0, 56, 28, 0, 96, 6, 0, 96, 6, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0, 96, 6, 0, 96, 6, 0, 56, 28, 0, 28, 56, 0, 7,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "O" */ +static const GLubyte TimesRoman24_Character_080[] = { 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 63,192, 48,112, 48, 48, 48, 24, 48, 24, 48, 24, 48, 48, 48,112,255,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "P" */ +static const GLubyte TimesRoman24_Character_081[] = { 18, 0, 0, 0, 0, 15, 0, 0, 56, 0, 0,112, 0, 0,224, 0, 1,192, 0, 7,224, 0, 28, 56, 0, 56, 28, 0, 96, 6, 0, 96, 6, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0, 96, 6, 0, 96, 6, 0, 56, 28, 0, 28, 56, 0, 7,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Q" */ +static const GLubyte TimesRoman24_Character_082[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 30, 48, 28, 48, 56, 48,112, 48, 96, 48,192, 49,192, 51,128, 63,192, 48,112, 48, 48, 48, 56, 48, 24, 48, 56, 48, 48, 48,112,255,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "R" */ +static const GLubyte TimesRoman24_Character_083[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,158, 0,241,128,192,192,128, 96,128, 96, 0, 96, 0,224, 3,192, 15,128, 30, 0,120, 0,224, 0,192, 64,192, 64,192,192, 99,192, 30, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "S" */ +static const GLubyte TimesRoman24_Character_084[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15,192, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0,131, 4,131, 4,195, 12,255,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "T" */ +static const GLubyte TimesRoman24_Character_085[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 0, 28, 48, 0, 24, 8, 0, 48, 8, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0,252, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "U" */ +static const GLubyte TimesRoman24_Character_086[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,128, 0, 1,128, 0, 1,128, 0, 3,192, 0, 3, 64, 0, 3, 96, 0, 6, 32, 0, 6, 32, 0, 6, 48, 0, 12, 16, 0, 12, 24, 0, 24, 8, 0, 24, 8, 0, 24, 12, 0, 48, 4, 0, 48, 6, 0,252, 31,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "V" */ +static const GLubyte TimesRoman24_Character_087[] = { 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,131, 0, 1,131, 0, 1,131,128, 3,135,128, 3, 70,128, 3, 70,192, 6, 70, 64, 6, 76, 64, 6, 76, 96, 12, 44, 96, 12, 44, 32, 24, 44, 32, 24, 24, 48, 24, 24, 16, 48, 24, 16, 48, 24, 24,252,126,126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte TimesRoman24_Character_088[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 15,192, 48, 3,128, 24, 7, 0, 8, 14, 0, 4, 12, 0, 6, 24, 0, 2, 56, 0, 1,112, 0, 0,224, 0, 0,192, 0, 1,192, 0, 3,160, 0, 3, 16, 0, 6, 8, 0, 14, 12, 0, 28, 6, 0,126, 15,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "X" */ +static const GLubyte TimesRoman24_Character_089[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 3,192, 3, 64, 6, 96, 6, 32, 12, 48, 28, 16, 24, 24, 56, 8, 48, 12,252, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Y" */ +static const GLubyte TimesRoman24_Character_090[] = { 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,248,224, 24,112, 8, 48, 8, 56, 0, 24, 0, 28, 0, 14, 0, 6, 0, 7, 0, 3, 0, 3,128, 1,192,128,192,128,224,192,112,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Z" */ +static const GLubyte TimesRoman24_Character_048[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 51, 0, 97,128, 97,128,225,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, 97,128, 97,128, 51, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "0" */ +static const GLubyte TimesRoman24_Character_049[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0,120, 0, 24, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "1" */ +static const GLubyte TimesRoman24_Character_050[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,128,255,192, 96, 64, 48, 0, 24, 0, 12, 0, 4, 0, 6, 0, 3, 0, 3, 0, 1,128, 1,128,129,128,129,128, 67,128,127, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "2" */ +static const GLubyte TimesRoman24_Character_051[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,120, 0,230, 0,195, 0, 1, 0, 1,128, 1,128, 1,128, 3,128, 7, 0, 30, 0, 12, 0, 6, 0,131, 0,131, 0, 71, 0,126, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "3" */ +static const GLubyte TimesRoman24_Character_052[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 0, 3, 0,255,192,255,192,195, 0, 67, 0, 99, 0, 35, 0, 51, 0, 19, 0, 27, 0, 11, 0, 7, 0, 7, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "4" */ +static const GLubyte TimesRoman24_Character_053[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,126, 0,227,128,193,128, 0,192, 0,192, 0,192, 0,192, 1,192, 3,128, 15,128,126, 0,120, 0, 96, 0, 32, 0, 32, 0, 31,128, 31,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "5" */ +static const GLubyte TimesRoman24_Character_054[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,123,128, 97,128,224,192,192,192,192,192,192,192,192,192,193,128,243,128,238, 0, 96, 0,112, 0, 48, 0, 24, 0, 14, 0, 3,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "6" */ +static const GLubyte TimesRoman24_Character_055[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 24, 0, 12, 0, 12, 0, 12, 0, 4, 0, 6, 0, 6, 0, 2, 0, 3, 0, 3, 0, 1, 0, 1,128,129,128,192,192,255,192,127,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "7" */ +static const GLubyte TimesRoman24_Character_056[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,115,128,225,128,192,192,192,192,192,192, 65,192, 97,128, 55, 0, 30, 0, 30, 0, 51, 0, 97,128, 97,128, 97,128, 51, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "8" */ +static const GLubyte TimesRoman24_Character_057[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,240, 0, 28, 0, 6, 0, 3, 0, 3,128, 1,128, 29,128,115,192, 97,192,192,192,192,192,192,192,192,192,193,192, 97,128,119,128, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "9" */ +static const GLubyte TimesRoman24_Character_096[] = { 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96,224,128,192, 96, 0, 0, 0, 0, 0}; /* "`" */ +static const GLubyte TimesRoman24_Character_126[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,131,128,199,192,124, 96, 56, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte TimesRoman24_Character_033[] = { 8, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0,192,192,192,192,192,192,192,192,192,192,192,192, 0, 0, 0, 0, 0}; /* "!" */ +static const GLubyte TimesRoman24_Character_064[] = { 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,240, 0, 14, 12, 0, 24, 0, 0, 48, 0, 0, 97,222, 0, 99,123, 0,198, 57,128,198, 24,128,198, 24,192,198, 24, 64,198, 12, 64,195, 12, 64,195,140, 64,225,252, 64, 96,236,192,112, 0,128, 56, 1,128, 28, 3, 0, 15, 14, 0, 3,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte TimesRoman24_Character_035[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 34, 0, 34, 0, 34, 0, 34, 0,255,192,255,192, 17, 0, 17, 0, 17, 0,127,224,127,224, 8,128, 8,128, 8,128, 8,128, 8,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "#" */ +static const GLubyte TimesRoman24_Character_036[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 63, 0,229,192,196,192,132, 96,132, 96, 4, 96, 4,224, 7,192, 7,128, 30, 0, 60, 0,116, 0,100, 0,100, 32,100, 96, 52,224, 31,128, 4, 0, 4, 0, 0, 0, 0, 0, 0, 0}; /* "$" */ +static const GLubyte TimesRoman24_Character_037[] = { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 60, 0, 24,114, 0, 12, 97, 0, 4, 96,128, 6, 96,128, 3, 48,128, 1, 25,128, 1,143, 0,120,192, 0,228, 64, 0,194, 96, 0,193, 48, 0,193, 16, 0, 97, 24, 0, 51,252, 0, 30, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte TimesRoman24_Character_094[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,128,193,128, 65, 0, 99, 0, 34, 0, 54, 0, 20, 0, 28, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "^" */ +static const GLubyte TimesRoman24_Character_038[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 60, 0,127,126, 0,225,225, 0,192,192, 0,193,192, 0,193,160, 0, 99, 32, 0, 55, 16, 0, 30, 24, 0, 14, 62, 0, 15, 0, 0, 29,128, 0, 24,192, 0, 24, 64, 0, 24, 64, 0, 12,192, 0, 7,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "&" */ +static const GLubyte TimesRoman24_Character_042[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 28, 0,201,128,235,128, 28, 0,235,128,201,128, 28, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "*" */ +static const GLubyte TimesRoman24_Character_040[] = { 8, 0, 4, 8, 16, 48, 32, 96, 96,192,192,192,192,192,192,192,192, 96, 96, 32, 48, 16, 8, 4, 0, 0, 0, 0, 0}; /* "(" */ +static const GLubyte TimesRoman24_Character_041[] = { 8, 0,128, 64, 32, 48, 16, 24, 24, 12, 12, 12, 12, 12, 12, 12, 12, 24, 24, 16, 48, 32, 64,128, 0, 0, 0, 0, 0}; /* ")" */ +static const GLubyte TimesRoman24_Character_045[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,240,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte TimesRoman24_Character_095[] = { 13, 0, 0,255,248,255,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte TimesRoman24_Character_061[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,240,255,240, 0, 0, 0, 0,255,240,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte TimesRoman24_Character_043[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0,255,240,255,240, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte TimesRoman24_Character_091[] = { 8, 0, 0,248,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,248, 0, 0, 0, 0, 0}; /* "[" */ +static const GLubyte TimesRoman24_Character_123[] = { 10, 0, 0, 7, 0, 12, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 16, 0, 48, 0, 32, 0,192, 0, 32, 0, 48, 0, 16, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 12, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "{" */ +static const GLubyte TimesRoman24_Character_125[] = { 10, 0, 0,224, 0, 48, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 8, 0, 12, 0, 4, 0, 3, 0, 4, 0, 12, 0, 8, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 48, 0,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "}" */ +static const GLubyte TimesRoman24_Character_093[] = { 8, 0, 0,248, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,248, 0, 0, 0, 0, 0}; /* "]" */ +static const GLubyte TimesRoman24_Character_059[] = { 7, 0, 0, 0,192, 96, 32,224,192, 0, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte TimesRoman24_Character_058[] = { 6, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte TimesRoman24_Character_044[] = { 7, 0, 0, 0,192, 96, 32,224,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte TimesRoman24_Character_046[] = { 6, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte TimesRoman24_Character_060[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 1,192, 7, 0, 28, 0,112, 0,192, 0,112, 0, 28, 0, 7, 0, 1,192, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte TimesRoman24_Character_062[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192, 0,112, 0, 28, 0, 7, 0, 1,192, 0, 96, 1,192, 7, 0, 28, 0,112, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte TimesRoman24_Character_047[] = { 7, 0, 0, 0,192,192,192, 96, 96, 32, 48, 48, 16, 24, 24, 8, 12, 12, 4, 6, 6, 3, 3, 3, 0, 0, 0, 0, 0}; /* "/" */ +static const GLubyte TimesRoman24_Character_063[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 48, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0, 24, 0, 24, 0, 12, 0, 14, 0, 7, 0,195, 0,195, 0,131, 0,198, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "?" */ +static const GLubyte TimesRoman24_Character_092[] = { 7, 0, 0, 0, 0, 0, 0, 6, 6, 4, 12, 12, 8, 24, 24, 16, 48, 48, 32, 96, 96, 64,192,192, 0, 0, 0, 0, 0}; /* "\" */ +static const GLubyte TimesRoman24_Character_034[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,136, 0,204, 0,204, 0,204, 0,204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte TimesRoman24_Character_039[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192, 96, 32,224,192, 0, 0, 0, 0, 0}; /* "'" */ +static const GLubyte TimesRoman24_Character_124[] = { 6, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 0, 0}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* TimesRoman24_Character_Map[] = {TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_032,TimesRoman24_Character_033,TimesRoman24_Character_034,TimesRoman24_Character_035, +TimesRoman24_Character_036,TimesRoman24_Character_037,TimesRoman24_Character_038,TimesRoman24_Character_039,TimesRoman24_Character_040,TimesRoman24_Character_041,TimesRoman24_Character_042,TimesRoman24_Character_043,TimesRoman24_Character_044,TimesRoman24_Character_045,TimesRoman24_Character_046,TimesRoman24_Character_047,TimesRoman24_Character_048,TimesRoman24_Character_049,TimesRoman24_Character_050,TimesRoman24_Character_051,TimesRoman24_Character_052,TimesRoman24_Character_053,TimesRoman24_Character_054,TimesRoman24_Character_055,TimesRoman24_Character_056,TimesRoman24_Character_057,TimesRoman24_Character_058,TimesRoman24_Character_059,TimesRoman24_Character_060,TimesRoman24_Character_061,TimesRoman24_Character_062,TimesRoman24_Character_063,TimesRoman24_Character_064,TimesRoman24_Character_065,TimesRoman24_Character_066,TimesRoman24_Character_067,TimesRoman24_Character_068,TimesRoman24_Character_069,TimesRoman24_Character_070,TimesRoman24_Character_071,TimesRoman24_Character_072, +TimesRoman24_Character_073,TimesRoman24_Character_074,TimesRoman24_Character_075,TimesRoman24_Character_076,TimesRoman24_Character_077,TimesRoman24_Character_078,TimesRoman24_Character_079,TimesRoman24_Character_080,TimesRoman24_Character_081,TimesRoman24_Character_082,TimesRoman24_Character_083,TimesRoman24_Character_084,TimesRoman24_Character_085,TimesRoman24_Character_086,TimesRoman24_Character_087,TimesRoman24_Character_088,TimesRoman24_Character_089,TimesRoman24_Character_090,TimesRoman24_Character_091,TimesRoman24_Character_092,TimesRoman24_Character_093,TimesRoman24_Character_094,TimesRoman24_Character_095,TimesRoman24_Character_096,TimesRoman24_Character_097,TimesRoman24_Character_098,TimesRoman24_Character_099,TimesRoman24_Character_100,TimesRoman24_Character_101,TimesRoman24_Character_102,TimesRoman24_Character_103,TimesRoman24_Character_104,TimesRoman24_Character_105,TimesRoman24_Character_106,TimesRoman24_Character_107,TimesRoman24_Character_108,TimesRoman24_Character_109, +TimesRoman24_Character_110,TimesRoman24_Character_111,TimesRoman24_Character_112,TimesRoman24_Character_113,TimesRoman24_Character_114,TimesRoman24_Character_115,TimesRoman24_Character_116,TimesRoman24_Character_117,TimesRoman24_Character_118,TimesRoman24_Character_119,TimesRoman24_Character_120,TimesRoman24_Character_121,TimesRoman24_Character_122,TimesRoman24_Character_123,TimesRoman24_Character_124,TimesRoman24_Character_125,TimesRoman24_Character_126,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042, +TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042, +TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042, +TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontTimesRoman24 = { "-adobe-times-medium-r-normal--24-240-75-75-p-124-iso8859-1", 93, 28, TimesRoman24_Character_Map, -1.0f, 6.0f }; + + + +/* + * End of code from FreeGLUT. + */ + + +#define ARGS(X) (X.Characters, 1, X.Height, X.xorig, X.yorig) + +static fntBitmapFont fntFixed8x13 ARGS( fgFontFixed8x13 ); +static fntBitmapFont fntFixed9x15 ARGS( fgFontFixed9x15 ); +static fntBitmapFont fntHelvetica10 ARGS( fgFontHelvetica10 ); +static fntBitmapFont fntHelvetica12 ARGS( fgFontHelvetica12 ); +static fntBitmapFont fntHelvetica18 ARGS( fgFontHelvetica18 ); +static fntBitmapFont fntTimesRoman10 ARGS( fgFontTimesRoman10 ); +static fntBitmapFont fntTimesRoman24 ARGS( fgFontTimesRoman24 ); + + +fntBitmapFont *fntGetBitmapFont(int id) +{ + fntBitmapFont *fnt = NULL; + switch (id) { + case FNT_BITMAP_8_BY_13: fnt = &fntFixed8x13; break; + case FNT_BITMAP_9_BY_15: fnt = &fntFixed9x15; break; + case FNT_BITMAP_HELVETICA_10: fnt = &fntHelvetica10; break; + case FNT_BITMAP_HELVETICA_12: fnt = &fntHelvetica12; break; + case FNT_BITMAP_HELVETICA_18: fnt = &fntHelvetica18; break; + case FNT_BITMAP_TIMES_ROMAN_10: fnt = &fntTimesRoman10; break; + case FNT_BITMAP_TIMES_ROMAN_24: fnt = &fntTimesRoman24; break; + } + return fnt; +} diff --git a/3rdparty/fonts/fntLocal.h b/3rdparty/fonts/fntLocal.h new file mode 100644 index 000000000..be020e918 --- /dev/null +++ b/3rdparty/fonts/fntLocal.h @@ -0,0 +1,112 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fntLocal.h 1727 2002-11-09 21:27:21Z ude $ +*/ + + +#include "fnt.h" + + +extern int _fntIsSwapped ; +extern FILE *_fntCurrImageFd ; + + +inline void _fnt_swab_short ( unsigned short *x ) +{ + if ( _fntIsSwapped ) + *x = (( *x >> 8 ) & 0x00FF ) | + (( *x << 8 ) & 0xFF00 ) ; +} + + +inline void _fnt_swab_int ( unsigned int *x ) +{ + if ( _fntIsSwapped ) + *x = (( *x >> 24 ) & 0x000000FF ) | + (( *x >> 8 ) & 0x0000FF00 ) | + (( *x << 8 ) & 0x00FF0000 ) | + (( *x << 24 ) & 0xFF000000 ) ; +} + + +inline void _fnt_swab_int_array ( int *x, int leng ) +{ + if ( ! _fntIsSwapped ) + return ; + + for ( int i = 0 ; i < leng ; i++ ) + { + *x = (( *x >> 24 ) & 0x000000FF ) | + (( *x >> 8 ) & 0x0000FF00 ) | + (( *x << 8 ) & 0x00FF0000 ) | + (( *x << 24 ) & 0xFF000000 ) ; + x++ ; + } +} + + +inline unsigned char _fnt_readByte () +{ + unsigned char x ; + fread ( & x, sizeof(unsigned char), 1, _fntCurrImageFd ) ; + return x ; +} + +inline unsigned short _fnt_readShort () +{ + unsigned short x ; + fread ( & x, sizeof(unsigned short), 1, _fntCurrImageFd ) ; + _fnt_swab_short ( & x ) ; + return x ; +} + +inline unsigned int _fnt_readInt () +{ + unsigned int x ; + fread ( & x, sizeof(unsigned int), 1, _fntCurrImageFd ) ; + _fnt_swab_int ( & x ) ; + return x ; +} + + +#define FNT_BYTE_FORMAT 0 +#define FNT_BITMAP_FORMAT 1 + +struct TXF_Glyph +{ + unsigned short ch ; + unsigned char w ; + unsigned char h ; + signed char x_off ; + signed char y_off ; + signed char step ; + signed char unknown ; + short x ; + short y ; + + sgVec2 tx0 ; sgVec2 vx0 ; + sgVec2 tx1 ; sgVec2 vx1 ; + sgVec2 tx2 ; sgVec2 vx2 ; + sgVec2 tx3 ; sgVec2 vx3 ; +} ; + + +void fntSetError(int, const char* format, ...); \ No newline at end of file diff --git a/3rdparty/fonts/fntTXF.cxx b/3rdparty/fonts/fntTXF.cxx new file mode 100755 index 000000000..2036e0ea1 --- /dev/null +++ b/3rdparty/fonts/fntTXF.cxx @@ -0,0 +1,341 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fntTXF.cxx 1735 2002-12-01 18:21:48Z sjbaker $ +*/ + + +#include "fntLocal.h" + +#include +#include + +FILE *_fntCurrImageFd ; +int _fntIsSwapped = FALSE ; + +static void tex_make_mip_maps ( GLubyte *image, int xsize, + int ysize, int zsize ) +{ + GLubyte *texels [ 20 ] ; /* One element per level of MIPmap */ + + for ( int l = 0 ; l < 20 ; l++ ) + texels [ l ] = NULL ; + + texels [ 0 ] = image ; + + int lev ; + + for ( lev = 0 ; (( xsize >> (lev+1) ) != 0 || + ( ysize >> (lev+1) ) != 0 ) ; lev++ ) + { + /* Suffix '1' is the higher level map, suffix '2' is the lower level. */ + + int l1 = lev ; + int l2 = lev+1 ; + int w1 = xsize >> l1 ; + int h1 = ysize >> l1 ; + int w2 = xsize >> l2 ; + int h2 = ysize >> l2 ; + + if ( w1 <= 0 ) w1 = 1 ; + if ( h1 <= 0 ) h1 = 1 ; + if ( w2 <= 0 ) w2 = 1 ; + if ( h2 <= 0 ) h2 = 1 ; + + texels [ l2 ] = new GLubyte [ w2 * h2 * zsize ] ; + + for ( int x2 = 0 ; x2 < w2 ; x2++ ) + for ( int y2 = 0 ; y2 < h2 ; y2++ ) + for ( int c = 0 ; c < zsize ; c++ ) + { + int x1 = x2 + x2 ; + int x1_1 = ( x1 + 1 ) % w1 ; + int y1 = y2 + y2 ; + int y1_1 = ( y1 + 1 ) % h1 ; + + int t1 = texels [ l1 ] [ (y1 * w1 + x1 ) * zsize + c ] ; + int t2 = texels [ l1 ] [ (y1_1 * w1 + x1 ) * zsize + c ] ; + int t3 = texels [ l1 ] [ (y1 * w1 + x1_1) * zsize + c ] ; + int t4 = texels [ l1 ] [ (y1_1 * w1 + x1_1) * zsize + c ] ; + + texels [ l2 ] [ (y2 * w2 + x2) * zsize + c ] = + ( t1 + t2 + t3 + t4 ) / 4 ; + } + } + + texels [ lev+1 ] = NULL ; + + if ( ! ((xsize & (xsize-1))==0) || + ! ((ysize & (ysize-1))==0) ) + { + fntSetError ( SG_ALERT, + "TXFloader: TXF Map is not a power-of-two in size!" ) ; + } + + glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 ) ; + + int map_level = 0 ; + +#ifdef PROXY_TEXTURES_ARE_NOT_BROKEN + int ww ; + + do + { + glTexImage2D ( GL_PROXY_TEXTURE_2D, + map_level, zsize, xsize, ysize, FALSE /* Border */, + (zsize==1)?GL_LUMINANCE: + (zsize==2)?GL_LUMINANCE_ALPHA: + (zsize==3)?GL_RGB: + GL_RGBA, + GL_UNSIGNED_BYTE, NULL ) ; + + glGetTexLevelParameteriv ( GL_PROXY_TEXTURE_2D, 0,GL_TEXTURE_WIDTH, &ww ) ; + + if ( ww == 0 ) + { + delete [] texels [ 0 ] ; + xsize >>= 1 ; + ysize >>= 1 ; + + for ( int l = 0 ; texels [ l ] != NULL ; l++ ) + texels [ l ] = texels [ l+1 ] ; + + if ( xsize < 64 && ysize < 64 ) + { + fntSetError ( SG_ALERT, + "FNT: OpenGL will not accept a font texture?!?" ) ; + } + } + } while ( ww == 0 ) ; +#endif + + for ( int i = 0 ; texels [ i ] != NULL ; i++ ) + { + int w = xsize>>i ; + int h = ysize>>i ; + + if ( w <= 0 ) w = 1 ; + if ( h <= 0 ) h = 1 ; + + glTexImage2D ( GL_TEXTURE_2D, + map_level, zsize, w, h, FALSE /* Border */, + (zsize==1)?GL_LUMINANCE: + (zsize==2)?GL_LUMINANCE_ALPHA: + (zsize==3)?GL_RGB: + GL_RGBA, + GL_UNSIGNED_BYTE, (GLvoid *) texels[i] ) ; + map_level++ ; + delete [] texels [ i ] ; + } +} + + + +int fntTexFont::loadTXF ( const SGPath& path, GLenum mag, GLenum min ) +{ + FILE *fd ; + const auto ps = path.utf8Str(); +#if defined(SG_WINDOWS) + std::wstring fp = path.wstr(); + fd = _wfopen(fp.c_str(), L"rb"); +#else + fd = fopen(ps.c_str(), "rb"); +#endif + if ( fd == nullptr ) + { + fntSetError ( SG_WARN, + "fntLoadTXF: Failed to open '%s' for reading.", ps.c_str() ) ; + return FNT_FALSE ; + } + + _fntCurrImageFd = fd ; + + unsigned char magic [ 4 ] ; + + if ( (int)fread ( &magic, sizeof (unsigned int), 1, fd ) != 1 ) + { + fntSetError ( SG_WARN, + "fntLoadTXF: '%s' an empty file!", ps.c_str() ) ; + return FNT_FALSE ; + } + + if ( magic [ 0 ] != 0xFF || magic [ 1 ] != 't' || + magic [ 2 ] != 'x' || magic [ 3 ] != 'f' ) + { + fntSetError ( SG_WARN, + "fntLoadTXF: '%s' is not a 'txf' font file.", ps.c_str() ) ; + return FNT_FALSE ; + } + + _fntIsSwapped = FALSE ; + int endianness = _fnt_readInt () ; + + _fntIsSwapped = ( endianness != 0x12345678 ) ; + + int format = _fnt_readInt () ; + int tex_width = _fnt_readInt () ; + int tex_height = _fnt_readInt () ; + int max_height = _fnt_readInt () ; + _fnt_readInt () ; + int num_glyphs = _fnt_readInt () ; + + int w = tex_width ; + int h = tex_height ; + + float xstep = 0.5f / (float) w ; + float ystep = 0.5f / (float) h ; + + int i, j ; + + /* + Load the TXF_Glyph array + */ + + TXF_Glyph glyph ; + + for ( i = 0 ; i < num_glyphs ; i++ ) + { + glyph . ch = _fnt_readShort () ; + + glyph . w = _fnt_readByte () ; + glyph . h = _fnt_readByte () ; + glyph . x_off = _fnt_readByte () ; + glyph . y_off = _fnt_readByte () ; + glyph . step = _fnt_readByte () ; + glyph . unknown = _fnt_readByte () ; + glyph . x = _fnt_readShort () ; + glyph . y = _fnt_readShort () ; + + setGlyph ( (char) glyph.ch, + (float) glyph.step / (float) max_height, + (float) glyph.x / (float) w + xstep, + (float)( glyph.x + glyph.w ) / (float) w + xstep, + (float) glyph.y / (float) h + ystep, + (float)( glyph.y + glyph.h ) / (float) h + ystep, + (float) glyph.x_off / (float) max_height, + (float)( glyph.x_off + glyph.w ) / (float) max_height, + (float) glyph.y_off / (float) max_height, + (float)( glyph.y_off + glyph.h ) / (float) max_height ) ; + } + + exists [ ' ' ] = FALSE ; + + /* + Load the image part of the file + */ + + int ntexels = w * h ; + + unsigned char *teximage ; + unsigned char *texbitmap ; + + switch ( format ) + { + case FNT_BYTE_FORMAT: + { + unsigned char *orig = new unsigned char [ ntexels ] ; + + if ( (int)fread ( orig, 1, ntexels, fd ) != ntexels ) + { + fntSetError ( SG_WARN, + "fntLoadTXF: Premature EOF in '%s'.", ps.c_str() ) ; + return FNT_FALSE ; + } + + teximage = new unsigned char [ 2 * ntexels ] ; + + for ( i = 0 ; i < ntexels ; i++ ) + { + teximage [ i*2 ] = orig [ i ] ; + teximage [ i*2 + 1 ] = orig [ i ] ; + } + + delete [] orig ; + } + break ; + + case FNT_BITMAP_FORMAT: + { + int stride = (w + 7) >> 3; + + texbitmap = new unsigned char [ stride * h ] ; + + if ( (int)fread ( texbitmap, 1, stride * h, fd ) != stride * h ) + { + delete [] texbitmap ; + fntSetError ( SG_WARN, + "fntLoadTXF: Premature EOF in '%s'.", ps.c_str() ) ; + return FNT_FALSE ; + } + + teximage = new unsigned char [ 2 * ntexels ] ; + + for ( i = 0 ; i < 2 * ntexels ; i++ ) + teximage [ i ] = 0 ; + + for (i = 0; i < h; i++) + for (j = 0; j < w; j++) + if (texbitmap[i * stride + (j >> 3)] & (1 << (j & 7))) + { + teximage[(i * w + j) * 2 ] = 255; + teximage[(i * w + j) * 2 + 1] = 255; + } + + delete [] texbitmap ; + } + break ; + + default: + fntSetError ( SG_WARN, + "fntLoadTXF: Unrecognised format type in '%s'.", ps.c_str() ) ; + return FNT_FALSE ; + } + + fclose ( fd ) ; + + fixed_pitch = FALSE ; + +#ifdef GL_VERSION_1_1 + glGenTextures ( 1, & texture ) ; + glBindTexture ( GL_TEXTURE_2D, texture ) ; +#else + /* This is only useful on some ancient SGI hardware */ + glGenTexturesEXT ( 1, & texture ) ; + glBindTextureEXT ( GL_TEXTURE_2D, texture ) ; +#endif + + tex_make_mip_maps ( teximage, w, h, 2 ) ; + + glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ) ; + + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ) ; +#ifdef GL_VERSION_1_1 + glBindTexture ( GL_TEXTURE_2D, 0 ) ; +#else + glBindTextureEXT ( GL_TEXTURE_2D, 0 ) ; +#endif + + return FNT_TRUE ; +} + + diff --git a/CMakeLists.txt b/CMakeLists.txt index bebdc5416..3818b1110 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -382,7 +382,7 @@ else() endif (ENABLE_QT) ############################################################################## -find_package(PLIB REQUIRED puaux pu fnt) +find_package(PLIB REQUIRED puaux pu) # FlightGear and SimGear versions need to match major + minor # split version string into components, note CMAKE_MATCH_0 is the entire regexp match diff --git a/CMakeModules/FindPLIB.cmake b/CMakeModules/FindPLIB.cmake index 62ee344f3..297718f2b 100644 --- a/CMakeModules/FindPLIB.cmake +++ b/CMakeModules/FindPLIB.cmake @@ -109,9 +109,9 @@ if(${PLIB_LIBRARIES} STREQUAL "PLIB_LIBRARIES-NOTFOUND") # handle MSVC confusion over pu/pui naming, by removing # 'pu' and then adding it back list(REMOVE_ITEM outDeps "pu" "fnt" "sg") - list(APPEND outDeps ${PUNAME} "fnt" "sg") + list(APPEND outDeps ${PUNAME} "sg") elseif (${c} STREQUAL "puaux") - list(APPEND outDeps ${PUNAME} "fnt" "sg") + list(APPEND outDeps ${PUNAME} "sg") elseif (${c} STREQUAL "ssg") list(APPEND outDeps "sg") endif() diff --git a/CMakeModules/SetupFGFSLibraries.cmake b/CMakeModules/SetupFGFSLibraries.cmake index f73df77dd..3eba04915 100644 --- a/CMakeModules/SetupFGFSLibraries.cmake +++ b/CMakeModules/SetupFGFSLibraries.cmake @@ -66,6 +66,8 @@ function(setup_fgfs_libraries target) target_link_libraries(${target} PLIBJoystick) endif() + target_link_libraries(${target} PLIBFont) + if(SYSTEM_HTS_ENGINE) target_link_libraries(${target} flite_hts ${HTS_ENGINE_LIBRARIES}) else() diff --git a/src/Cockpit/panel.cxx b/src/Cockpit/panel.cxx index 18b8fe78b..9cad531a4 100644 --- a/src/Cockpit/panel.cxx +++ b/src/Cockpit/panel.cxx @@ -47,7 +47,7 @@ #include -#include +#include "fnt.h" #include #include diff --git a/src/GUI/FGFontCache.cxx b/src/GUI/FGFontCache.cxx old mode 100644 new mode 100755 index 6c6b02d20..4ca96e4f6 --- a/src/GUI/FGFontCache.cxx +++ b/src/GUI/FGFontCache.cxx @@ -13,17 +13,13 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // -#ifdef HAVE_CONFIG_H -# include -#endif - -#ifdef HAVE_WINDOWS_H -#include -#endif +#include #include "FGFontCache.hxx" -#include +// this is our one in 3rdparty +#include "fnt.h" + #include #include @@ -212,14 +208,13 @@ FGFontCache::getfntpath(const std::string& name) bool FGFontCache::initializeFonts() { - static std::string fontext(",txf"); + static std::string fontext(".txf"); init(); auto dir = simgear::Dir(_path); for (auto p : dir.children(simgear::Dir::TYPE_FILE, fontext)) { fntTexFont* f = new fntTexFont; - // FIXME : this will fail when fonts are on UTF8 paths - if (f->load(p.local8BitStr())) + if (f->load(p)) _texFonts[p.file()] = f; else delete f; diff --git a/src/GUI/fonts.cxx b/src/GUI/fonts.cxx index 6b7b858fd..ee9f0f591 100644 --- a/src/GUI/fonts.cxx +++ b/src/GUI/fonts.cxx @@ -3,7 +3,9 @@ #endif #include -#include + +#include "fnt.h" + /** * fonts.cxx generated by the genfonts utility by Pawel W. Olszta. diff --git a/src/Instrumentation/HUD/HUD.cxx b/src/Instrumentation/HUD/HUD.cxx index 2258a0fa4..45df2d094 100644 --- a/src/Instrumentation/HUD/HUD.cxx +++ b/src/Instrumentation/HUD/HUD.cxx @@ -35,7 +35,7 @@ #include #include -#include +#include "fnt.h" #include
#include
From c2ce25136a3e94adf346d24d7e8a2596fa5fc9e2 Mon Sep 17 00:00:00 2001 From: James Turner Date: Fri, 13 Mar 2020 12:22:22 +0000 Subject: [PATCH 60/98] Tweak HTS code to allow UTF-8 paths on Windows --- 3rdparty/hts_engine_API/lib/HTS_misc.c | 39 +++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) mode change 100644 => 100755 3rdparty/hts_engine_API/lib/HTS_misc.c diff --git a/3rdparty/hts_engine_API/lib/HTS_misc.c b/3rdparty/hts_engine_API/lib/HTS_misc.c old mode 100644 new mode 100755 index 290ef16e1..6a350fce2 --- a/3rdparty/hts_engine_API/lib/HTS_misc.c +++ b/3rdparty/hts_engine_API/lib/HTS_misc.c @@ -75,14 +75,51 @@ typedef struct _HTS_Data { size_t index; } HTS_Data; +#if defined(_WIN32) + +#include + +#define MAX_PATH_SIZE 8192 +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) + +// Encode 'path' which is assumed UTF-8 string, into UNICODE string. +// wbuf and wbuf_len is a target buffer and its length. +static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) { + char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p; + + strncpy(buf, path, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + + // Trim trailing slashes. Leave backslash for paths like "X:\" + p = buf + strlen(buf) - 1; + while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0'; + + // Convert to Unicode and back. If doubly-converted string does not + // match the original, something is fishy, reject. + memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); + WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), + NULL, NULL); + if (strcmp(buf, buf2) != 0) { + wbuf[0] = L'\0'; + } +} +#endif + /* HTS_fopen_from_fn: wrapper for fopen */ HTS_File *HTS_fopen_from_fn(const char *name, const char *opt) { HTS_File *fp = (HTS_File *) HTS_calloc(1, sizeof(HTS_File)); fp->type = HTS_FILE; +#if defined(_WIN32) + wchar_t wpath[MAX_PATH_SIZE], wmode[10]; + to_wchar(name, wpath, ARRAY_SIZE(wpath)); + to_wchar(opt, wmode, ARRAY_SIZE(wmode)); + fp->pointer = (void*) _wfopen(wpath, wmode); +#else fp->pointer = (void *) fopen(name, opt); - +#endif if (fp->pointer == NULL) { HTS_error(0, "HTS_fopen: Cannot open %s.\n", name); HTS_free(fp); From 9389c6564fccbe7c26440aa6e1d2c81a2556d4f7 Mon Sep 17 00:00:00 2001 From: James Turner Date: Fri, 13 Mar 2020 16:46:54 +0000 Subject: [PATCH 61/98] Nasal geodinfo: warn when scenery is not loaded --- src/Scripting/NasalPositioned.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Scripting/NasalPositioned.cxx b/src/Scripting/NasalPositioned.cxx index 1246ad951..a78f04add 100644 --- a/src/Scripting/NasalPositioned.cxx +++ b/src/Scripting/NasalPositioned.cxx @@ -1457,8 +1457,10 @@ static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args) if (scenery == nullptr) return naNil(); - if(!scenery->get_elevation_m(geod, elev, &material)) - return naNil(); + if(!scenery->get_elevation_m(geod, elev, &material)) { + SG_LOG(SG_TERRAIN, SG_DEV_WARN, "Nasal geodinfo() querying location with no loaded tiles:" << geod); + return naNil(); + } naRef vec = naNewVector(c); naVec_append(vec, naNum(elev)); From b049865cb2579e72f2cc3d30b90d0551cdc4674c Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Fri, 13 Mar 2020 18:28:34 +0100 Subject: [PATCH 62/98] fixes #2123 Relocation splash distorted. Mark images loaded from splash with origin of splash to prevent use of DDS texture cache and therefore fix distortion by preventing rescale to power of two --- src/Viewer/splash.cxx | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Viewer/splash.cxx b/src/Viewer/splash.cxx index 7064ef539..6fc8cf6dc 100644 --- a/src/Viewer/splash.cxx +++ b/src/Viewer/splash.cxx @@ -45,6 +45,7 @@ #include #include #include +#include #include
#include
@@ -56,6 +57,7 @@ #include + static const char* LICENSE_URL_TEXT = "Licensed under the GNU GPL. See http://www.flightgear.org for more information"; class SplashScreenUpdateCallback : public osg::NodeCallback { @@ -81,13 +83,21 @@ SplashScreen::~SplashScreen() void SplashScreen::createNodes() { + std::string splashImage = selectSplashImage(); #if OSG_VERSION_LESS_THAN(3,4,0) - _splashImage = osgDB::readImageFile(selectSplashImage()); + _splashImage = osgDB::readImageFile(splashImage); #else - _splashImage = osgDB::readRefImageFile(selectSplashImage()); + auto staticOptions = simgear::SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions()); + staticOptions->setLoadOriginHint(simgear::SGReaderWriterOptions::LoadOriginHint::ORIGIN_SPLASH_SCREEN); + _splashImage = osgDB::readRefImageFile(splashImage, staticOptions); +// _splashImage = osgDB::readRefImageFile(selectSplashImage()); #endif - int width = _splashImage->s(); + if (!_splashImage){ + SG_LOG(SG_VIEW, SG_INFO, "Splash Image " << splashImage << " failed to load"); + return; + } + int width = _splashImage->s(); int height = _splashImage->t(); _splashImageAspectRatio = static_cast(width) / height; @@ -287,9 +297,12 @@ void SplashScreen::setupLogoImage() #if OSG_VERSION_LESS_THAN(3,4,0) _logoImage = osgDB::readImageFile(logoPath.utf8Str()); #else - _logoImage = osgDB::readRefImageFile(logoPath.utf8Str()); + auto staticOptions = simgear::SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions()); + staticOptions->setLoadOriginHint(simgear::SGReaderWriterOptions::LoadOriginHint::ORIGIN_SPLASH_SCREEN); + _logoImage = osgDB::readRefImageFile(logoPath.utf8Str(), staticOptions); #endif if (!_logoImage) { + SG_LOG(SG_VIEW, SG_INFO, "Splash logo image " << logoPath << " failed to load"); return; } From fb348d1a8aa388c16d4b61d34bab2a13c7b0da93 Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Sat, 14 Mar 2020 14:04:22 +0100 Subject: [PATCH 63/98] Fix a false compiler warnig about brackets not protecting the next if statement --- src/Scripting/NasalPositioned.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Scripting/NasalPositioned.cxx b/src/Scripting/NasalPositioned.cxx index a78f04add..3219b9909 100644 --- a/src/Scripting/NasalPositioned.cxx +++ b/src/Scripting/NasalPositioned.cxx @@ -1457,10 +1457,10 @@ static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args) if (scenery == nullptr) return naNil(); - if(!scenery->get_elevation_m(geod, elev, &material)) { - SG_LOG(SG_TERRAIN, SG_DEV_WARN, "Nasal geodinfo() querying location with no loaded tiles:" << geod); - return naNil(); - } + if(!scenery->get_elevation_m(geod, elev, &material)) { + SG_LOG(SG_TERRAIN, SG_DEV_WARN, "Nasal geodinfo() querying location with no loaded tiles:" << geod); + return naNil(); + } naRef vec = naNewVector(c); naVec_append(vec, naNum(elev)); From 36ba1e3dceac18814e693f38aed14080da9c50b5 Mon Sep 17 00:00:00 2001 From: James Turner Date: Sat, 14 Mar 2020 21:01:36 +0000 Subject: [PATCH 64/98] UTF-8 support: replace Nasal io.stat with our own. Replace the default io.stat with one which uses our validator, and uses SGPath to call _wstat on Windows. This does mean we return less information in the stat() result, but it seems nothing actually uses the result apart from checking if the file exists. --- src/Scripting/NasalSys.cxx | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index 836987835..711a53c13 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -781,6 +781,58 @@ static naRef f_open(naContext c, naRef me, int argc, naRef* args) return naIOGhost(c, f); } +static naRef ftype(naContext ctx, const SGPath& f) +{ + const char* t = "unk"; + if (f.isFile()) t = "reg"; + else if(f.isDir()) t = "dir"; + return naStr_fromdata(naNewString(ctx), t, strlen(t)); +} + +// io.stat with UTF-8 path support, replaces the default one in +// Nasal iolib.c which does not hsupport UTF-8 paths +static naRef f_custom_stat(naContext ctx, naRef me, int argc, naRef* args) +{ + naRef pathArg = argc > 0 ? naStringValue(ctx, args[0]) : naNil(); + if (!naIsString(pathArg)) + naRuntimeError(ctx, "bad argument to stat()"); + + const auto path = SGPath::fromUtf8(naStr_data(pathArg)); + if (!path.exists()) { + return naNil(); + } + + const SGPath filename = fgValidatePath(path, false ); + if (filename.isNull()) { + SG_LOG(SG_NASAL, SG_ALERT, "stat(): reading '" << + naStr_data(pathArg) << "' denied (unauthorized directory - authorization" + " no longer follows symlinks; to authorize reading additional " + "directories, pass them to --allow-nasal-read)"); + naRuntimeError(ctx, "stat(): access denied (unauthorized directory)"); + return naNil(); + } + + naRef result = naNewVector(ctx); + naVec_setsize(ctx, result, 12); + + // every use in fgdata/Nasal only uses stat() to test existence of + // files, not to check any of this information. + int n = 0; + naVec_set(result, n++, naNum(0)); // device + naVec_set(result, n++, naNum(0)); // inode + naVec_set(result, n++, naNum(0)); // mode + naVec_set(result, n++, naNum(0)); // nlink + naVec_set(result, n++, naNum(0)); // uid + naVec_set(result, n++, naNum(0)); // guid + naVec_set(result, n++, naNum(0)); // rdev + naVec_set(result, n++, naNum(filename.sizeInBytes())); // size + naVec_set(result, n++, naNum(0)); // atime + naVec_set(result, n++, naNum(0)); // mtime + naVec_set(result, n++, naNum(0)); // ctime + naVec_set(result, n++, ftype(ctx, filename)); + return result; +} + // Parse XML file. // parsexml( [, [, [, [, ]]]]); // @@ -937,6 +989,7 @@ void FGNasalSys::init() naNewFunc(_context, naNewCCode(_context, funcs[i].func))); nasal::Hash io_module = getGlobals().get("io"); io_module.set("open", f_open); + io_module.set("stat", f_custom_stat); // And our SGPropertyNode wrapper hashset(_globals, "props", genPropsModule()); From 2dcf9c30bb63353cd5dea81adca0b53851b96b52 Mon Sep 17 00:00:00 2001 From: James Turner Date: Sun, 15 Mar 2020 15:00:55 +0000 Subject: [PATCH 65/98] Further path -> UTF8 fixes for Windows Requires corresponding SimGear change. With this last round, we work with FGData on non-Latin1 path on Windows. (Final fixes are for Nasal io.readfile, which needed io.stat to work, and SGSky allowing correct SGPath use) --- src/AIModel/AIBase.cxx | 11 ++++++----- src/Aircraft/replay.cxx | 4 ++-- src/GUI/WindowsFileDialog.cxx | 5 ++--- src/Scenery/terrain_pgt.cxx | 2 +- src/Viewer/renderer_compositor.cxx | 8 +++----- src/Viewer/renderer_legacy.cxx | 8 +++----- 6 files changed, 17 insertions(+), 21 deletions(-) mode change 100644 => 100755 src/Viewer/renderer_legacy.cxx diff --git a/src/AIModel/AIBase.cxx b/src/AIModel/AIBase.cxx index e0fb4f256..140a872a8 100644 --- a/src/AIModel/AIBase.cxx +++ b/src/AIModel/AIBase.cxx @@ -516,7 +516,7 @@ void FGAIBase::Transform() { */ std::vector FGAIBase::resolveModelPath(ModelSearchOrder searchOrder) { - std::vector path_list; + string_list path_list; if (searchOrder == DATA_ONLY) { SG_LOG(SG_AI, SG_DEBUG, "Resolving model path: DATA only"); @@ -536,7 +536,8 @@ std::vector FGAIBase::resolveModelPath(ModelSearchOrder searchOrder } } else { // No model, so fall back to the default - path_list.push_back(fgGetString("/sim/multiplay/default-model", default_model)); + const SGPath defaultModelPath = SGPath::fromUtf8(fgGetString("/sim/multiplay/default-model", default_model)); + path_list.push_back(defaultModelPath.utf8Str()); } } else { SG_LOG(SG_AI, SG_DEBUG, "Resolving model path: PREFER_AI/PREFER_DATA"); @@ -545,7 +546,7 @@ std::vector FGAIBase::resolveModelPath(ModelSearchOrder searchOrder p.append(model_path); if (p.exists()) { SG_LOG(SG_AI, SG_DEBUG, "Found AI model: " << p); - path_list.push_back(p.local8BitStr()); + path_list.push_back(p.utf8Str()); break; } } @@ -566,8 +567,8 @@ std::vector FGAIBase::resolveModelPath(ModelSearchOrder searchOrder for (SGPath p : globals->get_data_paths()) { p.append(fallback_path); if (p.exists()) { - SG_LOG(SG_AI, SG_DEBUG, "Found fallback model path for index " << _fallback_model_index << ": " << p.local8BitStr()); - path_list.push_back(p.local8BitStr()); + SG_LOG(SG_AI, SG_DEBUG, "Found fallback model path for index " << _fallback_model_index << ": " << p); + path_list.push_back(p.utf8Str()); break; } } diff --git a/src/Aircraft/replay.cxx b/src/Aircraft/replay.cxx index 626001e6a..731a9349c 100644 --- a/src/Aircraft/replay.cxx +++ b/src/Aircraft/replay.cxx @@ -822,7 +822,7 @@ FGReplay::saveTape(const SGPath& Filename, SGPropertyNode* MetaDataProps) bool ok = true; /* open output stream *******************************************/ - gzContainerWriter output(Filename.local8BitStr(), FlightRecorderFileMagic); + gzContainerWriter output(Filename, FlightRecorderFileMagic); if (!output.good()) { SG_LOG(SG_SYSTEMS, SG_ALERT, "Cannot open file" << Filename); @@ -940,7 +940,7 @@ FGReplay::loadTape(const SGPath& Filename, bool Preview, SGPropertyNode* UserDat bool ok = true; /* open input stream ********************************************/ - gzContainerReader input(Filename.local8BitStr(), FlightRecorderFileMagic); + gzContainerReader input(Filename, FlightRecorderFileMagic); if (input.eof() || !input.good()) { SG_LOG(SG_SYSTEMS, SG_ALERT, "Cannot open file " << Filename); diff --git a/src/GUI/WindowsFileDialog.cxx b/src/GUI/WindowsFileDialog.cxx index 8a5b2ff97..077b8f8f1 100644 --- a/src/GUI/WindowsFileDialog.cxx +++ b/src/GUI/WindowsFileDialog.cxx @@ -47,9 +47,8 @@ static int CALLBACK BrowseFolderCallback( if (uMsg == BFFM_INITIALIZED) { // set the initial directory now WindowsFileDialog* dlg = reinterpret_cast(lpData); - std::string s = dlg->getDirectory().local8BitStr(); - LPCTSTR path = s.c_str(); - ::SendMessage(hwnd, BFFM_SETSELECTION, true, (LPARAM) path); + const auto w = dlg->getDirectory().wstr(); + ::SendMessageW(hwnd, BFFM_SETSELECTIONW, true, (LPARAM) w.c_str()); } return 0; } diff --git a/src/Scenery/terrain_pgt.cxx b/src/Scenery/terrain_pgt.cxx index 3d440bc8a..d39d8dcf1 100644 --- a/src/Scenery/terrain_pgt.cxx +++ b/src/Scenery/terrain_pgt.cxx @@ -82,7 +82,7 @@ void FGPgtTerrain::init( osg::Group* terrain ) { fp.push_back(it->local8BitStr()); } - options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().local8BitStr()); + options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().utf8Str()); options->setPluginStringData("SimGear::BARE_LOD_RANGE", fgGetString("/sim/rendering/static-lod/bare-delta", boost::lexical_cast(SG_OBJECT_RANGE_BARE))); options->setPluginStringData("SimGear::ROUGH_LOD_RANGE", fgGetString("/sim/rendering/static-lod/rough-delta", boost::lexical_cast(SG_OBJECT_RANGE_ROUGH))); diff --git a/src/Viewer/renderer_compositor.cxx b/src/Viewer/renderer_compositor.cxx index 032b58748..b787e7e99 100644 --- a/src/Viewer/renderer_compositor.cxx +++ b/src/Viewer/renderer_compositor.cxx @@ -451,15 +451,13 @@ FGRenderer::init( void ) // on other subsystems to be inited, eg Ephemeris _sky = new SGSky; - SGPath texture_path(globals->get_fg_root()); - texture_path.append("Textures"); - texture_path.append("Sky"); + const SGPath texture_path = globals->get_fg_root() / "Textures" / "Sky"; for (int i = 0; i < FGEnvironmentMgr::MAX_CLOUD_LAYERS; i++) { - SGCloudLayer * layer = new SGCloudLayer(texture_path.local8BitStr()); + SGCloudLayer * layer = new SGCloudLayer(texture_path); _sky->add_cloud_layer(layer); } - _sky->texture_path( texture_path.local8BitStr() ); + _sky->set_texture_path( texture_path ); // XXX: Should always be true eventHandler->setChangeStatsCameraRenderOrder( true ); diff --git a/src/Viewer/renderer_legacy.cxx b/src/Viewer/renderer_legacy.cxx old mode 100644 new mode 100755 index 8d4c01db8..0b35ed1f2 --- a/src/Viewer/renderer_legacy.cxx +++ b/src/Viewer/renderer_legacy.cxx @@ -555,15 +555,13 @@ FGRenderer::init( void ) // on other subsystems to be inited, eg Ephemeris _sky = new SGSky; - SGPath texture_path(globals->get_fg_root()); - texture_path.append("Textures"); - texture_path.append("Sky"); + const SGPath texture_path = globals->get_fg_root() / "Textures" / "Sky"; for (int i = 0; i < FGEnvironmentMgr::MAX_CLOUD_LAYERS; i++) { - SGCloudLayer * layer = new SGCloudLayer(texture_path.local8BitStr()); + SGCloudLayer * layer = new SGCloudLayer(texture_path); _sky->add_cloud_layer(layer); } - _sky->texture_path( texture_path.local8BitStr() ); + _sky->set_texture_path( texture_path ); if (!_classicalRenderer) { eventHandler->setChangeStatsCameraRenderOrder( true ); From 904e55d657a918d5c179f9e64ba083990ef1d860 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Sun, 15 Mar 2020 15:05:20 +0000 Subject: [PATCH 66/98] src/MultiPlayer/multiplaymgr.cxx: use SGPropertyNode_ptr to avoid explicit delete. As suggested by James Turner --- src/MultiPlayer/multiplaymgr.cxx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/MultiPlayer/multiplaymgr.cxx b/src/MultiPlayer/multiplaymgr.cxx index 10ad9bdbf..56cb1d67d 100644 --- a/src/MultiPlayer/multiplaymgr.cxx +++ b/src/MultiPlayer/multiplaymgr.cxx @@ -2381,7 +2381,7 @@ FGMultiplayMgr::addMultiplayer(const std::string& callsign, */ mp->_getProps()->removeChildren("set"); - SGPropertyNode* set = NULL; + SGPropertyNode_ptr set; if (simgear::strutils::ends_with(modelName, ".xml") && simgear::strutils::starts_with(modelName, "Aircraft/")) { @@ -2444,8 +2444,7 @@ FGMultiplayMgr::addMultiplayer(const std::string& callsign, break; } } - delete set; - set = NULL; + set.reset(); } } } From a3d75e8573f789e92f319dd294619ed49ba7dd8b Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 16 Mar 2020 11:07:11 +0000 Subject: [PATCH 67/98] Voice synth: reduce some log output levels --- src/Sound/VoiceSynthesizer.cxx | 3 +-- src/Sound/flitevoice.cxx | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Sound/VoiceSynthesizer.cxx b/src/Sound/VoiceSynthesizer.cxx index c67068eee..befb0adbf 100644 --- a/src/Sound/VoiceSynthesizer.cxx +++ b/src/Sound/VoiceSynthesizer.cxx @@ -53,7 +53,7 @@ void FLITEVoiceSynthesizer::WorkerThread::run() // marker value indicating termination requested if ((request.speed < 0.0) && (request.volume < 0.0)) { - SG_LOG(SG_SOUND, SG_INFO, "FLITE synthesis thread exiting"); + SG_LOG(SG_SOUND, SG_DEBUG, "FLITE synthesis thread exiting"); return; } @@ -106,7 +106,6 @@ FLITEVoiceSynthesizer::~FLITEVoiceSynthesizer() // push the special marker value _requests.push(SynthesizeRequest::cancelThreadRequest()); _worker->join(); - SG_LOG(SG_SOUND, SG_INFO, "FLITE synthesis thread joined OK"); Flite_HTS_Engine_clear(_engine); } diff --git a/src/Sound/flitevoice.cxx b/src/Sound/flitevoice.cxx index 235f03e26..ce39fa2f6 100644 --- a/src/Sound/flitevoice.cxx +++ b/src/Sound/flitevoice.cxx @@ -49,7 +49,7 @@ FGFLITEVoice::FGFLITEVoice(FGVoiceMgr * mgr, const SGPropertyNode_ptr node, cons node->getNode("text", true)->addChangeListener(this); - SG_LOG(SG_SOUND, SG_INFO, "FLITEVoice initialized for sample-group '" << sampleGroupRefName + SG_LOG(SG_SOUND, SG_DEBUG, "FLITEVoice initialized for sample-group '" << sampleGroupRefName << "'. Samples will be named '" << _sampleName << "' " << "voice is '" << voice << "'"); } From 1e07dab5c5d894b8d4a3684458ecf89e8aa54b2f Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Sat, 29 Feb 2020 14:40:35 +0000 Subject: [PATCH 68/98] src/Viewer/ViewPropertyEvaluator.cxx: fix getSequenceDoubleValue()'s handling of default value. If node's string value is '' then return - used to return 0.0 which is incorrect. --- src/Viewer/ViewPropertyEvaluator.cxx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Viewer/ViewPropertyEvaluator.cxx b/src/Viewer/ViewPropertyEvaluator.cxx index 472d8f0b7..f9703e7cd 100644 --- a/src/Viewer/ViewPropertyEvaluator.cxx +++ b/src/Viewer/ViewPropertyEvaluator.cxx @@ -488,7 +488,12 @@ namespace ViewPropertyEvaluator { { SGPropertyNode* node = getSequenceNode(sequence); if (node) { - return node->getDoubleValue(); + if (node->getStringValue()[0] != 0) { + return node->getDoubleValue(); + } + /* If we reach here, the node exists but its value is an empty + string, so node->getDoubleValue() would return 0 which isn't + useful, so instead we return default_. */ } return default_; } From db255233f758821a96bba012b2141c12dc2f9f51 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Tue, 3 Mar 2020 00:07:08 +0000 Subject: [PATCH 69/98] src/Viewer/ViewPropertyEvaluator.*: added getBoolValue(). --- src/Viewer/ViewPropertyEvaluator.cxx | 22 ++++++++++++++++++++++ src/Viewer/ViewPropertyEvaluator.hxx | 3 +++ 2 files changed, 25 insertions(+) diff --git a/src/Viewer/ViewPropertyEvaluator.cxx b/src/Viewer/ViewPropertyEvaluator.cxx index f9703e7cd..8c980dd7a 100644 --- a/src/Viewer/ViewPropertyEvaluator.cxx +++ b/src/Viewer/ViewPropertyEvaluator.cxx @@ -498,6 +498,17 @@ namespace ViewPropertyEvaluator { return default_; } + bool getSequenceBoolValue(Sequence& sequence, bool default_) + { + SGPropertyNode* node = getSequenceNode(sequence); + if (node) { + if (node->getStringValue()[0] != 0) { + return node->getBoolValue(); + } + } + return default_; + } + const std::string& getStringValue(const char* spec) { std::shared_ptr sequence = getSequence(spec); @@ -515,6 +526,17 @@ namespace ViewPropertyEvaluator { return ret; } + bool getBoolValue(const char* spec, bool default_) + { + std::shared_ptr sequence = getSequence(spec); + if (sequence->_nodes.size() != 1 || sequence->_nodes.front()->_begin[0] != '(') { + SG_LOG(SG_VIEW, SG_DEBUG, "bad sequence for getBoolValue() - must have outermost '(...)': '" << spec); + abort(); + } + bool ret = getSequenceBoolValue(*sequence, default_); + return ret; + } + std::ostream& operator << (std::ostream& out, const Dump& dump) { out << "ViewPropertyEvaluator\n"; diff --git a/src/Viewer/ViewPropertyEvaluator.hxx b/src/Viewer/ViewPropertyEvaluator.hxx index 4779feba6..66be22d92 100644 --- a/src/Viewer/ViewPropertyEvaluator.hxx +++ b/src/Viewer/ViewPropertyEvaluator.hxx @@ -106,6 +106,9 @@ namespace ViewPropertyEvaluator { node, instead it always calls ->getDoubleValue(). */ double getDoubleValue(const char* spec, double default_=0); + + /* Similar to getDoubleValue(). */ + bool getBoolValue(const char* spec, bool default_=false); /* Outputs detailed information about all specs that have been seen. From 368443a4b36e73b11b5529eda5868617157c6367 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Sun, 1 Mar 2020 18:12:32 +0000 Subject: [PATCH 70/98] src/Viewer/view.cxx: Fixed Walker Orbit view. The fix is to add handling of target-lon-deg-path etc in View::recalcLookAt. --- src/Viewer/view.cxx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Viewer/view.cxx b/src/Viewer/view.cxx index f87a2549e..355ace49c 100644 --- a/src/Viewer/view.cxx +++ b/src/Viewer/view.cxx @@ -1031,6 +1031,25 @@ View::recalcLookAt () ); } + _target.setLongitudeDeg( + ViewPropertyEvaluator::getDoubleValue( + "((/sim/view[(/sim/current-view/view-number-raw)]/config/root)(/sim/view[(/sim/current-view/view-number-raw)]/config/target-lon-deg-path))", + _target.getLongitudeDeg() + ) + ); + _target.setLatitudeDeg( + ViewPropertyEvaluator::getDoubleValue( + "((/sim/view[(/sim/current-view/view-number-raw)]/config/root)(/sim/view[(/sim/current-view/view-number-raw)]/config/target-lat-deg-path))", + _target.getLatitudeDeg() + ) + ); + _target.setElevationFt( + ViewPropertyEvaluator::getDoubleValue( + "((/sim/view[(/sim/current-view/view-number-raw)]/config/root)(/sim/view[(/sim/current-view/view-number-raw)]/config/target-alt-ft-path))", + _target.getElevationFt() + ) + ); + if (_lookat_agl) { handleAGL(); } From 25a64b2cbadf03f16af51091414128dfaa29f337 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Sun, 1 Mar 2020 18:14:12 +0000 Subject: [PATCH 71/98] src/Viewer/view.cxx: View::recalcLookAt(): Use new view[]/config/eye-fixed tag. We only look at config/eye-lon-deg-path etc if eye-fixed is true. This tag should be set to true for all views where the eye position is fixed - e.g. all Tower views plus Fly-By view in fgdata:defaults.xml. This allows b26 turret views to work without requiring modifications to the aircraft (which would break usage with older releases of fgfs). --- src/Viewer/view.cxx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Viewer/view.cxx b/src/Viewer/view.cxx index 355ace49c..cb640ef75 100644 --- a/src/Viewer/view.cxx +++ b/src/Viewer/view.cxx @@ -1000,17 +1000,9 @@ View::recalcLookAt () SGGeodesy::SGCartToGeod(targetCart2, _target); _position = _target; - - const std::string& tail = ViewPropertyEvaluator::getStringValue("(/sim/view[(/sim/current-view/view-number-raw)]/config/eye-lon-deg-path)"); - - if (tail != "") { - /* will typically be /sim/tower/longitude-deg, so that this view's - eye position is from the tower rather than relative to the aircraft. - If we are viewing a multiplayer aircraft, the nearest tower - will be in /sim/view[]/config/root/sim/tower/longitude-deg (see - FGEnvironmentMgr::updateClosestAirport()), so we use a prefix to select - either /sim/tower/longitude-deg or multiplayer's tower. */ + bool eye_fixed = ViewPropertyEvaluator::getBoolValue("(/sim/view[(/sim/current-view/view-number-raw)]/config/eye-fixed)"); + if (eye_fixed) { _position.setLongitudeDeg( ViewPropertyEvaluator::getDoubleValue( "((/sim/view[(/sim/current-view/view-number-raw)]/config/root)(/sim/view[(/sim/current-view/view-number-raw)]/config/eye-lon-deg-path))", From 7688007c6886605ef7d509b02e19df57ac2cd37f Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 16 Mar 2020 15:50:40 +0000 Subject: [PATCH 72/98] Guard against potential cause of crash: https://sourceforge.net/p/flightgear/codetickets/2138/ --- src/GUI/NavaidSearchModel.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/GUI/NavaidSearchModel.cxx b/src/GUI/NavaidSearchModel.cxx index 4a4a9232f..5b43175aa 100644 --- a/src/GUI/NavaidSearchModel.cxx +++ b/src/GUI/NavaidSearchModel.cxx @@ -34,6 +34,9 @@ QString fixNavaidName(QString s) QStringList words = s.split(QChar(' ')); QStringList changedWords; Q_FOREACH(QString w, words) { + if (w.isEmpty()) + continue; + QString up = w.toUpper(); // expand common abbreviations From d52f1b5d4763c8c97c863114ea6f13d3f4bc71f3 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 17 Mar 2020 09:47:59 +0000 Subject: [PATCH 73/98] Improve in-sim launcher behaviour Restrict the UI to the pieces which make sense. --- src/GUI/LauncherController.cxx | 1 + src/GUI/LauncherController.hxx | 7 +++++++ src/GUI/QtFileDialog.cxx | 10 ---------- src/GUI/qml/Launcher.qml | 20 ++++++++++++++++---- src/GUI/qml/Summary.qml | 2 ++ src/Main/main.cxx | 6 ++++-- src/Main/options.cxx | 2 -- src/Viewer/fg_os_osgviewer.cxx | 1 - 8 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/GUI/LauncherController.cxx b/src/GUI/LauncherController.cxx index 7211cfa9f..58b85d447 100644 --- a/src/GUI/LauncherController.cxx +++ b/src/GUI/LauncherController.cxx @@ -183,6 +183,7 @@ void LauncherController::setInAppMode() m_inAppMode = true; m_keepRunningInAppMode = true; m_appModeResult = true; + emit inAppChanged(); } bool LauncherController::keepRunningInAppMode() const diff --git a/src/GUI/LauncherController.hxx b/src/GUI/LauncherController.hxx index c1f17b55c..823a75fb8 100644 --- a/src/GUI/LauncherController.hxx +++ b/src/GUI/LauncherController.hxx @@ -85,6 +85,8 @@ class LauncherController : public QObject Q_PROPERTY(QSize minimumWindowSize READ minWindowSize WRITE setMinWindowSize NOTIFY minWindowSizeChanged) Q_PROPERTY(QUrl flyIconUrl READ flyIconUrl NOTIFY selectedAircraftChanged) + + Q_PROPERTY(bool inAppMode READ inApp NOTIFY inAppChanged) Q_PROPERTY(bool aircraftGridMode READ aircraftGridMode WRITE setAircraftGridMode NOTIFY aircraftGridModeChanged) public: @@ -194,6 +196,10 @@ public: return m_aircraftGridMode; } + bool inApp() const + { + return m_inAppMode; + } signals: void selectedAircraftChanged(QUrl selectedAircraft); @@ -208,6 +214,7 @@ signals: void aircraftGridModeChanged(bool aircraftGridMode); + void inAppChanged(); public slots: void setSelectedAircraft(QUrl selectedAircraft); diff --git a/src/GUI/QtFileDialog.cxx b/src/GUI/QtFileDialog.cxx index 71a54edc9..deb65a9a7 100644 --- a/src/GUI/QtFileDialog.cxx +++ b/src/GUI/QtFileDialog.cxx @@ -38,16 +38,6 @@ QtFileDialog::~QtFileDialog() {} void QtFileDialog::exec() { - int fakeargc = 1; - static char fakeargv0[] = "fgfs"; - static char * fakeargv[2] = {fakeargv0, 0}; - // This does nothing if it has already been run, so the fake argc/argv are - // only used if run without launcher. Don't attempt to initialize the - // QSettings, because this would require FGGlobals to be initialized (for - // globals->get_fg_home()), which would prevent using this function at - // early startup. - flightgear::initApp(fakeargc, fakeargv, false /* doInitQSettings */); - // concatenate filter patterns, as Qt uses a single string std::string filter=""; for( string_list::const_iterator it = _filterPatterns.begin(); it != _filterPatterns.end();++it ) { diff --git a/src/GUI/qml/Launcher.qml b/src/GUI/qml/Launcher.qml index 40b8eb77c..d7b43ab29 100644 --- a/src/GUI/qml/Launcher.qml +++ b/src/GUI/qml/Launcher.qml @@ -6,7 +6,7 @@ Item { id: root // order of this model sets the order of buttons in the sidebar ListModel { - id: pagesModel + id: startupPagesModel ListElement { title: qsTr("Summary"); pageSource: "qrc:///qml/Summary.qml"; iconPath: "qrc:///svg/toolbox-summary"; state:"loader" } ListElement { title: qsTr("Aircraft"); pageSource: "qrc:///qml/AircraftList.qml"; iconPath: "qrc:///svg/toolbox-aircraft"; state:"loader" } @@ -28,6 +28,18 @@ Item { } + ListModel { + id: inAppPagesModel + ListElement { title: qsTr("Summary"); pageSource: "qrc:///qml/Summary.qml"; iconPath: "qrc:///svg/toolbox-summary"; state:"loader" } + ListElement { title: qsTr("Aircraft"); pageSource: "qrc:///qml/AircraftList.qml"; iconPath: "qrc:///svg/toolbox-aircraft"; state:"loader" } + + ListElement { + title: qsTr("Location"); pageSource: "qrc:///qml/Location.qml"; + iconPath: "qrc:///toolbox-location"; state:"loader" + } + } + + Component.onCompleted: { _launcher.minimumWindowSize = Qt.size(Style.strutSize * 12, sidebar.minimumHeight); @@ -35,7 +47,7 @@ Item { Connections { target: _location - onSkipFromArgsChanged: pagesModel.setProperty(2, "buttonDisabled", _location.skipFromArgs) + onSkipFromArgsChanged: startupPagesModel.setProperty(2, "buttonDisabled", _location.skipFromArgs) } state: "loader" @@ -82,7 +94,7 @@ Item { id: sidebar height: parent.height z: 1 - pagesModel: pagesModel + pagesModel: _launcher.inAppMode ? inAppPagesModel : startupPagesModel selectedPage: 0 // open on the summary page onShowMenu: menu.show(); @@ -127,7 +139,7 @@ Item { function selectPage(index) { sidebar.setSelectedPage(index); - var page = pagesModel.get(index); + var page = sidebar.pagesModel.get(index); pageLoader.source = page.pageSource root.state = page.state } diff --git a/src/GUI/qml/Summary.qml b/src/GUI/qml/Summary.qml index 9f959e033..425636ac7 100644 --- a/src/GUI/qml/Summary.qml +++ b/src/GUI/qml/Summary.qml @@ -297,6 +297,7 @@ Item { text: qsTr("Settings:") horizontalAlignment: Text.AlignRight font.pixelSize: Style.headingFontPixelSize + visible: !_launcher.inAppMode } StyledText { @@ -306,6 +307,7 @@ Item { maximumLineCount: 2 elide: Text.ElideRight width: summaryGrid.middleColumnWidth + visible: !_launcher.inAppMode } Item { diff --git a/src/Main/main.cxx b/src/Main/main.cxx index ec7842878..d8be13396 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -541,6 +541,10 @@ int fgMainInit( int argc, char **argv ) if (!fgInitHome()) { return EXIT_FAILURE; } + +#if defined(HAVE_QT) + flightgear::initApp(argc, argv); +#endif const bool readOnlyFGHome = fgGetBool("/sim/fghome-readonly"); if (!readOnlyFGHome) { @@ -624,10 +628,8 @@ int fgMainInit( int argc, char **argv ) } #if defined(HAVE_QT) - flightgear::initApp(argc, argv); if (showLauncher) { flightgear::checkKeyboardModifiersForSettingFGRoot(); - if (!flightgear::runLauncherDialog()) { return EXIT_SUCCESS; } diff --git a/src/Main/options.cxx b/src/Main/options.cxx index 75cded346..7c820215d 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -2953,7 +2953,6 @@ void Options::setupRoot(int argc, char **argv) SG_LOG(SG_GENERAL, SG_INFO, "set from FG_ROOT env var: fg_root = " << root ); } else { #if defined(HAVE_QT) - flightgear::initApp(argc, argv); root = SetupRootDialog::restoreUserSelectedRoot(); #endif if (root.isNull()) { @@ -2979,7 +2978,6 @@ void Options::setupRoot(int argc, char **argv) // a command-line, env-var or default root this check can fail and // we still want to use the GUI in that case if (versionComp != 0) { - flightgear::initApp(argc, argv); SetupRootDialog::runDialog(usingDefaultRoot); } #else diff --git a/src/Viewer/fg_os_osgviewer.cxx b/src/Viewer/fg_os_osgviewer.cxx index dbe2db560..6083db188 100644 --- a/src/Viewer/fg_os_osgviewer.cxx +++ b/src/Viewer/fg_os_osgviewer.cxx @@ -361,7 +361,6 @@ void fgOSInit(int* argc, char** argv) #if defined(HAVE_QT) global_usingGraphicsWindowQt = fgGetBool("/sim/rendering/graphics-window-qt", false); if (global_usingGraphicsWindowQt) { - flightgear::initApp(*argc, argv); SG_LOG(SG_GL, SG_INFO, "Using Qt implementation of GraphicsWindow"); flightgear::initQtWindowingSystem(); } else { From cf8ec5998e11e63364dc2c5a55de1217a37280f0 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 17 Mar 2020 09:58:29 +0000 Subject: [PATCH 74/98] Launcher: disable grid mode for update tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the UI doesn’t work well in grid mode, and the list of updates is limited, force list mode for the updates tab. https://sourceforge.net/p/flightgear/codetickets/2172/ --- src/GUI/qml/AircraftList.qml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/GUI/qml/AircraftList.qml b/src/GUI/qml/AircraftList.qml index faf410bac..4b514c53f 100644 --- a/src/GUI/qml/AircraftList.qml +++ b/src/GUI/qml/AircraftList.qml @@ -28,6 +28,7 @@ FocusScope width: parent.width GridToggleButton { + id: gridModeToggle anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: Style.margin @@ -132,7 +133,8 @@ FocusScope Loader { id: aircraftContent - source: _launcher.aircraftGridMode ? "qrc:///qml/AircraftGridView.qml" + // we use gridModeToggle vis to mean enabled, effectively + source: (gridModeToggle.visible && _launcher.aircraftGridMode) ? "qrc:///qml/AircraftGridView.qml" : "qrc:///qml/AircraftListView.qml" anchors { @@ -186,6 +188,10 @@ FocusScope __model: _launcher.installedAircraftModel __header: emptyHeader } + + PropertyChanges { + target: gridModeToggle; visible: true + } }, State { @@ -195,6 +201,10 @@ FocusScope __model: _launcher.searchAircraftModel __header: emptyHeader } + + PropertyChanges { + target: gridModeToggle; visible: true + } }, State { @@ -204,6 +214,10 @@ FocusScope __model: _launcher.browseAircraftModel __header: _addOns.showNoOfficialHangar ? noDefaultCatalogHeader : ratingsHeader } + + PropertyChanges { + target: gridModeToggle; visible: true + } }, State { @@ -213,6 +227,10 @@ FocusScope __model: _launcher.aircraftWithUpdatesModel __header: (_launcher.aircraftWithUpdatesModel.count > 0) ? updateAllHeader : emptyHeader } + + PropertyChanges { + target: gridModeToggle; visible: false + } } ] From ffbbe54cbcc5598dfffe4dc4c387575549b9b5df Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 17 Mar 2020 11:14:11 +0000 Subject: [PATCH 75/98] Launcher: create popup windows on demand This avoids some flickering issues on macOS, and reduces resource use generally. --- src/GUI/qml/AircraftVariantChoice.qml | 99 ++++++++++--------- src/GUI/qml/HistoryPopup.qml | 94 +++++++++--------- src/GUI/qml/NumericalEdit.qml | 134 +++++++++++++------------- 3 files changed, 168 insertions(+), 159 deletions(-) diff --git a/src/GUI/qml/AircraftVariantChoice.qml b/src/GUI/qml/AircraftVariantChoice.qml index 72366387f..43d564d7f 100644 --- a/src/GUI/qml/AircraftVariantChoice.qml +++ b/src/GUI/qml/AircraftVariantChoice.qml @@ -92,10 +92,9 @@ Rectangle { screenPos = _launcher.mapToGlobal(title, Qt.point(0, 0)) } - popupFrame.x = screenPos.x; - popupFrame.y = screenPos.y; - popupFrame.show() - tracker.window = popupFrame + var pop = popup.createObject(root, {x:screenPos.x, y:screenPos.y }) + tracker.window = pop; + pop.show(); } } @@ -108,59 +107,63 @@ Rectangle { id: tracker } - Window { - id: popupFrame + Component { + id: popup - width: root.width - flags: Qt.Popup - height: choicesColumn.childrenRect.height - color: "white" + Window { + id: popupFrame - Rectangle { - border.width: 1 - border.color: Style.minorFrameColor - anchors.fill: parent - } + width: root.width + flags: Qt.Popup + height: choicesColumn.childrenRect.height + color: "white" - Column { - id: choicesColumn + Rectangle { + border.width: 1 + border.color: Style.minorFrameColor + anchors.fill: parent + } - Repeater { - // would prefer the model to be conditional on visiblity, - // but this trips up the Window sizing on Linux (Ubuntu at - // least) and we get a mis-aligned origin - model: aircraftInfo.variantNames + Column { + id: choicesColumn - delegate: Item { - width: popupFrame.width - height: choiceText.implicitHeight + Repeater { + // would prefer the model to be conditional on visiblity, + // but this trips up the Window sizing on Linux (Ubuntu at + // least) and we get a mis-aligned origin + model: aircraftInfo.variantNames - Text { - id: choiceText - text: modelData + delegate: Item { + width: popupFrame.width + height: choiceText.implicitHeight - // allow override the size in case the title size is enormous - font.pixelSize: (popupFontPixelSize > 0) ? popupFontPixelSize : title.font.pixelSize + Text { + id: choiceText + text: modelData - color: choiceArea.containsMouse ? Style.themeColor : Style.baseTextColor - anchors { - left: parent.left - right: parent.right - margins: 4 + // allow override the size in case the title size is enormous + font.pixelSize: (root.popupFontPixelSize > 0) ? root.popupFontPixelSize : title.font.pixelSize + + color: choiceArea.containsMouse ? Style.themeColor : Style.baseTextColor + anchors { + left: parent.left + right: parent.right + margins: 4 + } } - } - MouseArea { - id: choiceArea - hoverEnabled: true - anchors.fill: parent - onClicked: { - popupFrame.hide() - root.selected(model.index) + MouseArea { + id: choiceArea + hoverEnabled: true + anchors.fill: parent + onClicked: { + popupFrame.close() + root.selected(model.index) + } } - } - } // of delegate Item - } // of Repeater - } - } // of popup frame + } // of delegate Item + } // of Repeater + } + } // of popup frame + } // of popup component } diff --git a/src/GUI/qml/HistoryPopup.qml b/src/GUI/qml/HistoryPopup.qml index a731fff39..83aa58a50 100644 --- a/src/GUI/qml/HistoryPopup.qml +++ b/src/GUI/qml/HistoryPopup.qml @@ -37,11 +37,12 @@ Item { hoverEnabled: root.enabled enabled: root.enabled onClicked: { - var screenPos = _launcher.mapToGlobal(button, Qt.point(-popupFrame.width, 0)) - popupFrame.x = screenPos.x; - popupFrame.y = screenPos.y; - popupFrame.visible = true - tracker.window = popupFrame + var pop = popup.createObject(root) + var screenPos = _launcher.mapToGlobal(button, Qt.point(-pop.width, 0)) + pop.y = screenPos.y; + pop.x = screenPos.x; + tracker.window = pop; + pop.show(); } } } @@ -50,51 +51,54 @@ Item { id: tracker } - Window { - id: popupFrame + Component { + id: popup - flags: Qt.Popup - height: choicesColumn.childrenRect.height + Style.margin * 2 - width: choicesColumn.childrenRect.width + Style.margin * 2 - visible: false - color: "white" + Window { + id: popupFrame - Rectangle { - border.width: 1 - border.color: Style.minorFrameColor - anchors.fill: parent - } + flags: Qt.Popup + height: choicesColumn.childrenRect.height + Style.margin * 2 + width: choicesColumn.childrenRect.width + Style.margin * 2 + color: "white" - // text repeater - Column { - id: choicesColumn - spacing: Style.margin - x: Style.margin - y: Style.margin + Rectangle { + border.width: 1 + border.color: Style.minorFrameColor + anchors.fill: parent + } - Repeater { - id: choicesRepeater - model: root.model - delegate: - StyledText { - id: choiceText + // text repeater + Column { + id: choicesColumn + spacing: Style.margin + x: Style.margin + y: Style.margin - // Taken from TableViewItemDelegateLoader.qml to follow QML role conventions - text: model && model.hasOwnProperty(displayRole) ? model[displayRole] // Qml ListModel and QAbstractItemModel - : modelData && modelData.hasOwnProperty(displayRole) ? modelData[displayRole] // QObjectList / QObject - : modelData != undefined ? modelData : "" // Models without role - height: implicitHeight + Style.margin + Repeater { + id: choicesRepeater + model: root.model + delegate: + StyledText { + id: choiceText - MouseArea { - width: popupFrame.width // full width of the popup - height: parent.height - onClicked: { - popupFrame.visible = false - root.selected(model.index); + // Taken from TableViewItemDelegateLoader.qml to follow QML role conventions + text: model && model.hasOwnProperty(displayRole) ? model[displayRole] // Qml ListModel and QAbstractItemModel + : modelData && modelData.hasOwnProperty(displayRole) ? modelData[displayRole] // QObjectList / QObject + : modelData != undefined ? modelData : "" // Models without role + height: implicitHeight + Style.margin + + MouseArea { + width: popupFrame.width // full width of the popup + height: parent.height + onClicked: { + root.selected(model.index); + popupFrame.close() + } } - } - } // of Text delegate - } // text repeater - } // text column - } // of popup Window + } // of Text delegate + } // text repeater + } // text column + } // of popup Window + } } diff --git a/src/GUI/qml/NumericalEdit.qml b/src/GUI/qml/NumericalEdit.qml index 6bc417b5a..8cb3d934f 100644 --- a/src/GUI/qml/NumericalEdit.qml +++ b/src/GUI/qml/NumericalEdit.qml @@ -101,6 +101,15 @@ FocusScope { } } + function showUnitsMenu() + { + + var screenPos = _launcher.mapToGlobal(editFrame, Qt.point(0, editFrame.height)) + var pop = popup.createObject(root, {x:screenPos.x, y:screenPos.y }) + tracker.window = pop; + pop.show(); + } + Component.onCompleted: { // ensure any initial value is accepted by our mode. // this stops people passing in completely wrong quantities @@ -196,7 +205,7 @@ FocusScope { anchors.left: parent.left anchors.margins: Style.margin text: visible ? units.shortText : "" - onClicked: unitSelectionPopup.show() + onClicked: root.showUnitsMenu() clickable: (units.numChoices > 1) } @@ -251,7 +260,7 @@ FocusScope { anchors.baseline: edit.baseline anchors.right: upDownArea.left text: visible ? units.shortText : "" - onClicked: unitSelectionPopup.show() + onClicked: root.showUnitsMenu(); clickable: (units.numChoices > 1) } @@ -324,76 +333,69 @@ FocusScope { id: tracker } - Window { - id: unitSelectionPopup - visible: false - flags: Qt.Popup - color: "white" - height: choicesColumn.childrenRect.height + Style.margin * 2 - width: choicesColumn.width + Style.margin * 2 + Component { + id: popup + Window { + id: unitSelectionPopup + flags: Qt.Popup + color: "white" + height: choicesColumn.childrenRect.height + Style.margin * 2 + width: choicesColumn.width + Style.margin * 2 - function show() - { - var screenPos = _launcher.mapToGlobal(editFrame, Qt.point(0, editFrame.height)) - unitSelectionPopup.x = screenPos.x; - unitSelectionPopup.y = screenPos.y; - unitSelectionPopup.visible = true - tracker.window = unitSelectionPopup - } - - Rectangle { - border.width: 1 - border.color: Style.minorFrameColor - anchors.fill: parent - } - - // choice layout column - Column { - id: choicesColumn - spacing: Style.margin - x: Style.margin - y: Style.margin - width: menuWidth - - - function calculateMenuWidth() - { - var minWidth = 0; - for (var i = 0; i < choicesRepeater.count; i++) { - minWidth = Math.max(minWidth, choicesRepeater.itemAt(i).implicitWidth); - } - return minWidth; + Rectangle { + border.width: 1 + border.color: Style.minorFrameColor + anchors.fill: parent } - readonly property int menuWidth: calculateMenuWidth() + // choice layout column + Column { + id: choicesColumn + spacing: Style.margin + x: Style.margin + y: Style.margin + width: menuWidth - // main item repeater - Repeater { - id: choicesRepeater - model: units - delegate: - Text { - id: choiceText - readonly property bool selected: units.selectedIndex === model.index - text: model.longName - height: implicitHeight + Style.margin - font.pixelSize: Style.baseFontPixelSize - color: choiceArea.containsMouse ? Style.themeColor : Style.baseTextColor + function calculateMenuWidth() + { + var minWidth = 0; + for (var i = 0; i < choicesRepeater.count; i++) { + minWidth = Math.max(minWidth, choicesRepeater.itemAt(i).implicitWidth); + } + return minWidth; + } - MouseArea { - id: choiceArea - width: unitSelectionPopup.width // full width of the popup - height: parent.height - hoverEnabled: true + readonly property int menuWidth: calculateMenuWidth() - onClicked: { - root.changeToUnits(model.index); - unitSelectionPopup.visible = false; + // main item repeater + Repeater { + id: choicesRepeater + model: units + delegate: + Text { + id: choiceText + readonly property bool selected: units.selectedIndex === model.index + + text: model.longName + height: implicitHeight + Style.margin + font.pixelSize: Style.baseFontPixelSize + color: choiceArea.containsMouse ? Style.themeColor : Style.baseTextColor + + MouseArea { + id: choiceArea + width: unitSelectionPopup.width // full width of the popup + height: parent.height + hoverEnabled: true + + onClicked: { + root.changeToUnits(model.index); + unitSelectionPopup.close(); + } } - } - } // of Text delegate - } // text repeater - } // text column - } + } // of Text delegate + } // text repeater + } // text column + } + } // of popup component } From a22d7943a4a477a89ea38258a0142a45c2bedffd Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 17 Mar 2020 11:46:26 +0000 Subject: [PATCH 76/98] Launcher: set package locale based on user language This should in theory enable localised strings in catalogs, but need to validate the full workflow here. --- src/GUI/QtLauncher.cxx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/GUI/QtLauncher.cxx b/src/GUI/QtLauncher.cxx index 9c9e1b9c5..0f45e10e7 100644 --- a/src/GUI/QtLauncher.cxx +++ b/src/GUI/QtLauncher.cxx @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -452,6 +453,15 @@ bool runLauncherDialog() fgInitPackageRoot(); + // setup package language + auto lang = options->valueForOption("language"); + if (!lang.empty()) { + globals->packageRoot()->setLocale(lang); + } else { + const auto langName = QLocale::languageToString(QLocale{}.language()); + globals->packageRoot()->setLocale(langName.toStdString()); + } + // startup the HTTP system now since packages needs it FGHTTPClient* http = globals->add_new_subsystem(); From f903cdfa507ef141b9b99a66a8b55c1a919aeddd Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 17 Mar 2020 16:05:24 +0000 Subject: [PATCH 77/98] Linux/macOS: use flock() to implement exclusive launch. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous implementation wasn’t correct, use a better one now. In the case of a non-clean exit we will leave a stale .pid file in FG_HOME, but we clear stale files on the next launch. --- src/Main/fg_init.cxx | 78 +++++++++++++++++++++++++++++--------------- src/Main/main.cxx | 3 +- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 2ff531d71..3e3b170f3 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -473,35 +473,56 @@ bool fgInitHome() } #else // write our PID, and check writeability - SGPath pidPath(dataPath, "fgfs.pid"); - if (pidPath.exists()) { - SG_LOG(SG_GENERAL, SG_ALERT, "flightgear instance already running, switching to FG_HOME read-only."); - // set a marker property so terrasync/navcache don't try to write - // from secondary instances - fgSetBool("/sim/fghome-readonly", true); - return true; - } - - char buf[16]; + SGPath pidPath(dataPath, "fgfs_lock.pid"); std::string ps = pidPath.utf8Str(); - // do open+unlink trick to the file is deleted on exit, even if we - // crash or exit(-1) - ssize_t len = snprintf(buf, 16, "%d", getpid()); - int fd = ::open(ps.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644); - if (fd >= 0) { - result = ::write(fd, buf, len) == len; - if( ::unlink(ps.c_str()) != 0 ) // delete file when app quits - result = false; - } + if (pidPath.exists()) { + int fd = ::open(ps.c_str(), O_RDONLY, 0644); + if (fd < 0) { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to open local file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } + + int err = ::flock(fd, LOCK_EX | LOCK_NB); + if (err < 0) { + if ( errno == EWOULDBLOCK) { + SG_LOG(SG_GENERAL, SG_ALERT, "flightgear instance already running, switching to FG_HOME read-only."); + // set a marker property so terrasync/navcache don't try to write + // from secondary instances + fgSetBool("/sim/fghome-readonly", true); + return true; + } else { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to lock file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } + } + + // we locked it! + result = true; + } else { + char buf[16]; + std::string ps = pidPath.utf8Str(); - if (!result) { - flightgear::fatalMessageBoxWithoutExit( - "File permissions problem", - "Can't write to user-data storage folder, check file permissions " - "and FG_HOME.", - "User-data at '" + dataPath.utf8Str() + "'."); - return false; + ssize_t len = snprintf(buf, 16, "%d\n", getpid()); + int fd = ::open(ps.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to open local file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } + + write(fd, buf, len); + + int err = flock(fd, LOCK_EX); + if (err != 0) { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to lock file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } + + result = true; } #endif fgSetBool("/sim/fghome-readonly", false); @@ -514,6 +535,11 @@ void fgShutdownHome() if (static_fgHomeWriteMutex) { CloseHandle(static_fgHomeWriteMutex); } +#else + if (fgGetBool("/sim/fghome-readonly") == false) { + SGPath pidPath = globals->get_fg_home() / "fgfs_lock.pid"; + pidPath.remove(); + } #endif } diff --git a/src/Main/main.cxx b/src/Main/main.cxx index d8be13396..6bb2828d6 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -706,7 +706,8 @@ int fgMainInit( int argc, char **argv ) int result = fgOSMainLoop(); frame_signal.clear(); fgOSCloseWindow(); - + fgShutdownHome(); + simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(mln_stopped); simgear::clearEffectCache(); From 177fc565da17d3009416050fb4c78ec273a86924 Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 18 Mar 2020 16:34:02 +0000 Subject: [PATCH 78/98] Launcher: initial carrier support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow selecting carriers from scenarios, and starting at either a parking position, or a distance offset from the FLOLS (effectively a crude ‘on-final’) Extend the —carrier startup option to accept a runway ident of FLOLS, in conjunction with the existing —offset-distance argument. --- src/AIModel/AICarrier.cxx | 68 ++++--- src/AIModel/AICarrier.hxx | 11 +- src/AIModel/AIManager.cxx | 58 +++--- src/AIModel/AIManager.hxx | 4 +- src/GUI/CMakeLists.txt | 2 + src/GUI/CarriersLocationModel.cxx | 132 +++++++++++++ src/GUI/CarriersLocationModel.hxx | 61 ++++++ src/GUI/LauncherController.cxx | 2 + src/GUI/LocationController.cxx | 224 ++++++++++++++++++----- src/GUI/LocationController.hxx | 38 ++++ src/GUI/ModelDataExtractor.cxx | 54 ++++-- src/GUI/ModelDataExtractor.hxx | 8 +- src/GUI/NavaidSearchModel.cxx | 11 +- src/GUI/NavaidSearchModel.hxx | 2 +- src/GUI/assets/aircraft-carrier-icon.png | Bin 0 -> 1483 bytes src/GUI/assets/icons8-cargo-ship-50.png | Bin 0 -> 890 bytes src/GUI/qml/IconButton.qml | 30 +++ src/GUI/qml/Location.qml | 50 ++++- src/GUI/qml/LocationCarrierView.qml | 214 ++++++++++++++++++++++ src/GUI/resources.qrc | 4 + src/Main/options.cxx | 2 +- src/Main/positioninit.cxx | 158 +++++++++++----- 22 files changed, 962 insertions(+), 171 deletions(-) create mode 100644 src/GUI/CarriersLocationModel.cxx create mode 100644 src/GUI/CarriersLocationModel.hxx create mode 100644 src/GUI/assets/aircraft-carrier-icon.png create mode 100644 src/GUI/assets/icons8-cargo-ship-50.png create mode 100644 src/GUI/qml/IconButton.qml create mode 100644 src/GUI/qml/LocationCarrierView.qml diff --git a/src/AIModel/AICarrier.cxx b/src/AIModel/AICarrier.cxx index 13422c61a..617b532f0 100644 --- a/src/AIModel/AICarrier.cxx +++ b/src/AIModel/AICarrier.cxx @@ -17,9 +17,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#ifdef HAVE_CONFIG_H -# include -#endif +#include #include #include @@ -35,11 +33,13 @@ #include "AICarrier.hxx" -FGAICarrier::FGAICarrier() : FGAIShip(otCarrier), deck_altitude(65.0065) { +FGAICarrier::FGAICarrier() : + FGAIShip(otCarrier), + deck_altitude(65.0065) +{ } -FGAICarrier::~FGAICarrier() { -} +FGAICarrier::~FGAICarrier() = default; void FGAICarrier::readFromScenario(SGPropertyNode* scFileNode) { if (!scFileNode) @@ -67,11 +67,14 @@ void FGAICarrier::readFromScenario(SGPropertyNode* scFileNode) { // Transform to the right coordinate frame, configuration is done in // the usual x-back, y-right, z-up coordinates, computations // in the simulation usual body x-forward, y-right, z-down coordinates - flols_off(0) = - flols->getDoubleValue("x-offset-m", 0); - flols_off(1) = flols->getDoubleValue("y-offset-m", 0); - flols_off(2) = - flols->getDoubleValue("z-offset-m", 0); + _flolsPosOffset(0) = - flols->getDoubleValue("x-offset-m", 0); + _flolsPosOffset(1) = flols->getDoubleValue("y-offset-m", 0); + _flolsPosOffset(2) = - flols->getDoubleValue("z-offset-m", 0); + + _flolsHeadingOffsetDeg = flols->getDoubleValue("heading-offset-deg", 0.0); + _flolsApproachAngle = flols->getDoubleValue("glidepath-angle-deg", 3.0); } else - flols_off = SGVec3d::zeros(); + _flolsPosOffset = SGVec3d::zeros(); std::vector props = scFileNode->getChildren("parking-pos"); std::vector::const_iterator it; @@ -180,13 +183,13 @@ void FGAICarrier::update(double dt) { // rotate the eyepoint wrt carrier vector into the carriers frame eyeWrtCarrier = ec2body.transform(eyeWrtCarrier); // the eyepoints vector wrt the flols position - SGVec3d eyeWrtFlols = eyeWrtCarrier - flols_off; + SGVec3d eyeWrtFlols = eyeWrtCarrier - _flolsPosOffset; // the distance from the eyepoint to the flols dist = norm(eyeWrtFlols); // now the angle, positive angles are upwards - if (fabs(dist) < SGLimits::min()) { + if (fabs(dist) < SGLimits::min()) { angle = 0; } else { double sAngle = -eyeWrtFlols(2)/dist; @@ -327,11 +330,9 @@ bool FGAICarrier::getParkPosition(const string& id, SGGeod& geodPos, { // FIXME: does not yet cover rotation speeds. - list::iterator it = ppositions.begin(); - while (it != ppositions.end()) { + for (const auto& ppos : ppositions) { // Take either the specified one or the first one ... - if ((*it).name == id || id.empty()) { - ParkPosition ppos = *it; + if (ppos.name == id || id.empty()) { SGVec3d cartPos = getCartPosAt(ppos.offset); geodPos = SGGeod::fromCart(cartPos); hdng = hdg + ppos.heading_deg; @@ -341,12 +342,28 @@ bool FGAICarrier::getParkPosition(const string& id, SGGeod& geodPos, uvw = SGVec3d(chdng*speed_fps, shdng*speed_fps, 0); return true; } - ++it; } return false; } +bool FGAICarrier::getFLOLSPositionHeading(SGGeod& geodPos, double& heading) const +{ + SGVec3d cartPos = getCartPosAt(_flolsPosOffset); + geodPos = SGGeod::fromCart(cartPos); + + // at present we don't support a heading offset for the FLOLS, so + // heading is just the carrier heading + heading = hdg + _flolsHeadingOffsetDeg; + + return true; +} + +double FGAICarrier::getFLOLFSGlidepathAngleDeg() const +{ + return _flolsApproachAngle; +} + // find relative wind void FGAICarrier::UpdateWind( double dt) { @@ -371,8 +388,7 @@ void FGAICarrier::UpdateWind( double dt) { + (rel_wind_speed_from_north_kts * rel_wind_speed_from_north_kts)); //calculate the relative wind direction - rel_wind_from_deg = atan2(rel_wind_speed_from_east_kts, rel_wind_speed_from_north_kts) - * SG_RADIANS_TO_DEGREES; + rel_wind_from_deg = SGMiscd::rad2deg(atan2(rel_wind_speed_from_east_kts, rel_wind_speed_from_north_kts)); //calculate rel wind rel_wind = rel_wind_from_deg - hdg; @@ -640,7 +656,7 @@ SGSharedPtr FGAICarrier::findCarrierByNameOrPennant(const std::stri return {}; } - for (const auto aiObject : aiManager->get_ai_list()) { + for (const auto& aiObject : aiManager->get_ai_list()) { if (aiObject->isa(FGAIBase::otCarrier)) { SGSharedPtr c = static_cast(aiObject.get()); if ((c->sign == namePennant) || (c->_getName() == namePennant)) { @@ -652,7 +668,7 @@ SGSharedPtr FGAICarrier::findCarrierByNameOrPennant(const std::stri return {}; } -void FGAICarrier::extractNamesPennantsFromScenario(SGPropertyNode_ptr xmlNode, SGPropertyNode_ptr scenario) +void FGAICarrier::extractCarriersFromScenario(SGPropertyNode_ptr xmlNode, SGPropertyNode_ptr scenario) { for (auto c : xmlNode->getChildren("entry")) { if (c->getStringValue("type") != std::string("carrier")) @@ -673,6 +689,14 @@ void FGAICarrier::extractNamesPennantsFromScenario(SGPropertyNode_ptr xmlNode, S // 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); - if (!pennant.empty()) carrierNode->addChild("name")->setStringValue(pennant); + if (!pennant.empty()) { + carrierNode->addChild("name")->setStringValue(pennant); + carrierNode->addChild("pennant-number")->setStringValue(pennant); + } + + // extact parkings + for (auto p : c->getChildren("parking-pos")) { + carrierNode->addChild("parking-pos")->setStringValue(p->getStringValue("name")); + } } } diff --git a/src/AIModel/AICarrier.hxx b/src/AIModel/AICarrier.hxx index ad5ce1fe8..e07d82480 100644 --- a/src/AIModel/AICarrier.hxx +++ b/src/AIModel/AICarrier.hxx @@ -86,8 +86,11 @@ public: * This is used to support 'start on a carrier', since we can quickly find * the corresponding scenario file to be loaded. */ - static void extractNamesPennantsFromScenario(SGPropertyNode_ptr xmlNode, SGPropertyNode_ptr scenario); + static void extractCarriersFromScenario(SGPropertyNode_ptr xmlNode, SGPropertyNode_ptr scenario); + bool getFLOLSPositionHeading(SGGeod &pos, double &heading) const; + + double getFLOLFSGlidepathAngleDeg() const; private: /// Is sufficient to be private, stores a possible position to place an /// aircraft on start @@ -115,8 +118,10 @@ private: list ppositions; // List of positions where an aircraft can start. string sign; // The sign of this carrier. - // these describe the flols - SGVec3d flols_off; + // these describe the flols + SGVec3d _flolsPosOffset; + double _flolsHeadingOffsetDeg = 0.0; ///< angle in degrees offset from the carrier centerline + double _flolsApproachAngle = 3.0; ///< glidepath angle for the FLOLS double dist; // the distance of the eyepoint from the flols double angle; diff --git a/src/AIModel/AIManager.cxx b/src/AIModel/AIManager.cxx index 6a292c7c9..4625572ff 100644 --- a/src/AIModel/AIManager.cxx +++ b/src/AIModel/AIManager.cxx @@ -71,11 +71,11 @@ public: _unloadScript = nasalScripts->getStringValue("unload"); std::string loadScript = nasalScripts->getStringValue("load"); if (!loadScript.empty()) { - FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal"); + FGNasalSys* nasalSys = globals->get_subsystem(); std::string moduleName = "scenario_" + _internalName; nasalSys->createModule(moduleName.c_str(), moduleName.c_str(), loadScript.c_str(), loadScript.size(), - 0); + nullptr); } } @@ -85,7 +85,7 @@ public: [](FGAIBasePtr ai) { ai->setDie(true); }); - FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal"); + FGNasalSys* nasalSys = globals->get_subsystem(); if (!nasalSys) return; @@ -93,7 +93,7 @@ public: if (!_unloadScript.empty()) { nasalSys->createModule(moduleName.c_str(), moduleName.c_str(), _unloadScript.c_str(), _unloadScript.size(), - 0); + nullptr); } nasalSys->deleteModule(moduleName.c_str()); @@ -165,16 +165,19 @@ FGAIManager::init() { registerScenarios(); } -void FGAIManager::registerScenarios() +void FGAIManager::registerScenarios(SGPropertyNode_ptr root) { - // depending on if we're using a carrier startup, this function may get - // called early or during normal FGAIManager init, so guard against double - // invocation. - // we clear this flag on shudtdown so reset works as expected - if (static_haveRegisteredScenarios) - return; - - static_haveRegisteredScenarios = true; + if (!root) { + // depending on if we're using a carrier startup, this function may get + // called early or during normal FGAIManager init, so guard against double + // invocation. + // we clear this flag on shudtdown so reset works as expected + if (static_haveRegisteredScenarios) + return; + + static_haveRegisteredScenarios = true; + root = globals->get_props(); + } // find all scenarios at standard locations (for driving the GUI) std::vector scenarioSearchPaths; @@ -184,27 +187,29 @@ void FGAIManager::registerScenarios() // add-on scenario directories const auto& addonsManager = flightgear::addons::AddonManager::instance(); - for (auto a : addonsManager->registeredAddons()) { - scenarioSearchPaths.push_back(a->getBasePath() / "Scenarios"); + if (addonsManager) { + for (auto a : addonsManager->registeredAddons()) { + scenarioSearchPaths.push_back(a->getBasePath() / "Scenarios"); + } } - - SGPropertyNode_ptr scenariosNode = fgGetNode("/sim/ai/scenarios", true); + + SGPropertyNode_ptr scenariosNode = root->getNode("/sim/ai/scenarios", true); for (auto p : scenarioSearchPaths) { if (!p.exists()) continue; simgear::Dir dir(p); for (auto xmlPath : dir.children(simgear::Dir::TYPE_FILE, ".xml")) { - registerScenarioFile(xmlPath); + registerScenarioFile(root, xmlPath); } // of xml files in the scenario dir iteration } // of scenario dirs iteration } -SGPropertyNode_ptr FGAIManager::registerScenarioFile(const SGPath& xmlPath) +SGPropertyNode_ptr FGAIManager::registerScenarioFile(SGPropertyNode_ptr root, const SGPath& xmlPath) { if (!xmlPath.exists()) return {}; - auto scenariosNode = fgGetNode("/sim/ai/scenarios", true); + auto scenariosNode = root->getNode("/sim/ai/scenarios", true); SGPropertyNode_ptr sNode; try { @@ -235,7 +240,7 @@ SGPropertyNode_ptr FGAIManager::registerScenarioFile(const SGPath& xmlPath) sNode->setStringValue("description", xs->getStringValue("description")); } - FGAICarrier::extractNamesPennantsFromScenario(xs, sNode); + FGAICarrier::extractCarriersFromScenario(xs, sNode); } // of scenarios in the XML file } catch (std::exception&) { SG_LOG(SG_AI, SG_WARN, "Skipping malformed scenario file:" << xmlPath); @@ -367,7 +372,7 @@ FGAIManager::update(double dt) for (FGAIBase* base : ai_list) { try { if (base->isa(FGAIBase::otThermal)) { - processThermal(dt, (FGAIThermal*)base); + processThermal(dt, static_cast(base)); } else { base->update(dt); } @@ -428,7 +433,7 @@ bool FGAIManager::isVisible(const SGGeod& pos) const int FGAIManager::getNumAiObjects() const { - return ai_list.size(); + return static_cast(ai_list.size()); } void @@ -489,12 +494,14 @@ bool FGAIManager::loadScenarioCommand(const SGPropertyNode* args, SGPropertyNode bool FGAIManager::unloadScenarioCommand(const SGPropertyNode * arg, SGPropertyNode * root) { + SG_UNUSED(root); std::string name = arg->getStringValue("name"); return unloadScenario(name); } bool FGAIManager::addObjectCommand(const SGPropertyNode* arg, const SGPropertyNode* root) { + SG_UNUSED(root); if (!arg){ return false; } @@ -506,7 +513,7 @@ FGAIBasePtr FGAIManager::addObject(const SGPropertyNode* definition) { const std::string& type = definition->getStringValue("type", "aircraft"); - FGAIBase* ai = NULL; + FGAIBase* ai = nullptr; if (type == "tanker") { // refueling scenarios ai = new FGAITanker; } else if (type == "wingman") { @@ -545,6 +552,7 @@ FGAIBasePtr FGAIManager::addObject(const SGPropertyNode* definition) bool FGAIManager::removeObjectCommand(const SGPropertyNode* arg, const SGPropertyNode* root) { + SG_UNUSED(root); if (!arg) { return false; } @@ -703,7 +711,7 @@ FGAIManager::calcCollision(double alt, double lat, double lon, double fuse_range } ++ai_list_itr; } - return 0; + return nullptr; } double diff --git a/src/AIModel/AIManager.hxx b/src/AIModel/AIManager.hxx index d386d862f..9ee51634d 100644 --- a/src/AIModel/AIManager.hxx +++ b/src/AIModel/AIManager.hxx @@ -73,8 +73,8 @@ public: * we need carrier scenarios to start the position-init process for a * carrier start. */ - static void registerScenarios(); - static SGPropertyNode_ptr registerScenarioFile(const SGPath& p); + static void registerScenarios(SGPropertyNode_ptr root = {}); + static SGPropertyNode_ptr registerScenarioFile(SGPropertyNode_ptr root, const SGPath& p); static SGPropertyNode_ptr loadScenarioFile(const std::string& id); FGAIBasePtr addObject(const SGPropertyNode* definition); diff --git a/src/GUI/CMakeLists.txt b/src/GUI/CMakeLists.txt index ffcee4862..905881d1b 100644 --- a/src/GUI/CMakeLists.txt +++ b/src/GUI/CMakeLists.txt @@ -126,6 +126,8 @@ if (HAVE_QT) PixmapImageItem.hxx PathListModel.cxx PathListModel.hxx + CarriersLocationModel.cxx + CarriersLocationModel.hxx ${uic_sources} ${qrc_sources} ${qml_sources}) diff --git a/src/GUI/CarriersLocationModel.cxx b/src/GUI/CarriersLocationModel.cxx new file mode 100644 index 000000000..9cebe1246 --- /dev/null +++ b/src/GUI/CarriersLocationModel.cxx @@ -0,0 +1,132 @@ +#include "CarriersLocationModel.hxx" + +#include + +#include + +#include "AIModel/AIManager.hxx" + +CarriersLocationModel::CarriersLocationModel(QObject *parent) + : QAbstractListModel(parent) +{ + SGPropertyNode_ptr localRoot(new SGPropertyNode); + FGAIManager::registerScenarios(localRoot); + +// this code encodes some scenario structre, sorry + for (auto s : localRoot->getNode("sim/ai/scenarios")->getChildren("scenario")) { + const std::string scenarioId = s->getStringValue("id"); + for (auto c : s->getChildren("carrier")) { + processCarrier(scenarioId, c); + } + } +} + +void CarriersLocationModel::processCarrier(const string &scenario, SGPropertyNode_ptr carrierNode) +{ + 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")); + SGGeod geod = SGGeod::fromDeg(carrierNode->getDoubleValue("longitude"), + carrierNode->getDoubleValue("latitude")); + + QStringList parkings; + for (auto c : carrierNode->getChildren("parking-pos")) { + parkings.append(QString::fromStdString(c->getStringValue())); + } + + mCarriers.push_back(Carrier{ + QString::fromStdString(scenario), + pennant, + name, + geod, + tacan, + parkings + }); +} + +int CarriersLocationModel::rowCount(const QModelIndex &parent) const +{ + // For list models only the root node (an invalid parent) should return the list's size. For all + // other (valid) parents, rowCount() should return 0 so that it does not become a tree model. + if (parent.isValid()) + return 0; + + return static_cast(mCarriers.size()); +} + +QVariant CarriersLocationModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + const auto& c = mCarriers.at(static_cast(index.row())); + switch (role) { + case Qt::DisplayRole: + case NameRole: return c.mName; + // case GeodRole: return QVariant::fromValue(c.mInitialLocation); + case IdentRole: return c.mCallsign; + case IconRole: return QPixmap(":/svg/aircraft-carrier"); + default: + break; + } + + return {}; +} + +QHash CarriersLocationModel::roleNames() const +{ + QHash result = QAbstractListModel::roleNames(); + + result[GeodRole] = "geod"; + result[GuidRole] = "guid"; + result[IdentRole] = "ident"; + result[NameRole] = "name"; + result[IconRole] = "icon"; + result[TypeRole] = "type"; + result[NavFrequencyRole] = "frequency"; + return result; +} + +int CarriersLocationModel::indexOf(const QString name) const +{ + auto it = std::find_if(mCarriers.begin(), mCarriers.end(), [name] + (const Carrier& carrier) + { return name == carrier.mName || name == carrier.mCallsign; }); + if (it == mCarriers.end()) + return -1; + + return static_cast(std::distance(mCarriers.begin(), it)); +} + +SGGeod CarriersLocationModel::geodForIndex(int index) const +{ + const auto uIndex = static_cast(index); + if ((index < 0) || (uIndex >= mCarriers.size())) { + return {}; + } + + const auto& c = mCarriers.at(uIndex); + return c.mInitialLocation; +} + +QString CarriersLocationModel::pennantForIndex(int index) const +{ + const auto uIndex = static_cast(index); + if ((index < 0) || (uIndex >= mCarriers.size())) { + return {}; + } + + const auto& c = mCarriers.at(uIndex); + return c.mCallsign; +} + +QStringList CarriersLocationModel::parkingsForIndex(int index) const +{ + const auto uIndex = static_cast(index); + if ((index < 0) || (uIndex >= mCarriers.size())) { + return {}; + } + + const auto& c = mCarriers.at(uIndex); + return c.mParkings; +} diff --git a/src/GUI/CarriersLocationModel.hxx b/src/GUI/CarriersLocationModel.hxx new file mode 100644 index 000000000..2bc4fa9d8 --- /dev/null +++ b/src/GUI/CarriersLocationModel.hxx @@ -0,0 +1,61 @@ +#ifndef CARRIERSMODEL_H +#define CARRIERSMODEL_H + +#include + +#include +#include + +#include + +class CarriersLocationModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit CarriersLocationModel(QObject *parent = nullptr); + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + QHash roleNames() const override; + + // copied from NavaidSearchModel + enum Roles { + GeodRole = Qt::UserRole + 1, + GuidRole = Qt::UserRole + 2, + IdentRole = Qt::UserRole + 3, + NameRole = Qt::UserRole + 4, + IconRole = Qt::UserRole + 5, + TypeRole = Qt::UserRole + 6, + NavFrequencyRole = Qt::UserRole + 7 + }; + + int indexOf(const QString name) const; + + SGGeod geodForIndex(int index) const; + QString pennantForIndex(int index) const; + + QStringList parkingsForIndex(int index) const; +private: + + struct Carrier + { + QString mScenario; // scenario ID for loading + QString mCallsign; // pennant-number + QString mName; + SGGeod mInitialLocation; + // icon? + QString mTACAN; + QStringList mParkings; + }; + + using CarrierVec = std::vector; + CarrierVec mCarriers; + + void processCarrier(const std::string& scenario, SGPropertyNode_ptr carrierNode); +}; + +#endif // CARRIERSMODEL_H diff --git a/src/GUI/LauncherController.cxx b/src/GUI/LauncherController.cxx index 58b85d447..106dc75fa 100644 --- a/src/GUI/LauncherController.cxx +++ b/src/GUI/LauncherController.cxx @@ -52,6 +52,7 @@ #include "NavaidSearchModel.hxx" #include "FlightPlanController.hxx" #include "ModelDataExtractor.hxx" +#include "CarriersLocationModel.hxx" using namespace simgear::pkg; @@ -142,6 +143,7 @@ void LauncherController::initQML() qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "MPServers", "Singleton API"); qmlRegisterType("FlightGear", 1, 0, "NavaidSearch"); + qmlRegisterType("FlightGear", 1, 0, "CarriersModel"); qmlRegisterUncreatableType("FlightGear", 1, 0, "Units", "Only for enum"); qmlRegisterType("FlightGear", 1, 0, "UnitsModel"); diff --git a/src/GUI/LocationController.cxx b/src/GUI/LocationController.cxx index a11c0e11a..2e1a05eff 100644 --- a/src/GUI/LocationController.cxx +++ b/src/GUI/LocationController.cxx @@ -35,6 +35,7 @@ #include "LaunchConfig.hxx" #include "DefaultAircraftLocator.hxx" #include "NavaidSearchModel.hxx" +#include "CarriersLocationModel.hxx" #include #include @@ -53,10 +54,8 @@ const unsigned int MAX_RECENT_LOCATIONS = 64; QVariant savePositionList(const FGPositionedList& posList) { QVariantList vl; - FGPositionedList::const_iterator it; - for (it = posList.begin(); it != posList.end(); ++it) { + for (const auto& pos : posList) { QVariantMap vm; - FGPositionedRef pos = *it; vm.insert("ident", QString::fromStdString(pos->ident())); vm.insert("type", pos->type()); vm.insert("lat", pos->geod().getLatitudeDeg()); @@ -70,7 +69,7 @@ FGPositionedList loadPositionedList(QVariant v) { QVariantList vl = v.toList(); FGPositionedList result; - result.reserve(vl.size()); + result.reserve(static_cast(vl.size())); NavDataCache* cache = NavDataCache::instance(); Q_FOREACH(QVariant v, vl) { @@ -93,9 +92,10 @@ FGPositionedList loadPositionedList(QVariant v) LocationController::LocationController(QObject *parent) : QObject(parent) { - m_searchModel = new NavaidSearchModel; + m_searchModel = new NavaidSearchModel(this); m_detailQml = new QmlPositioned(this); m_baseQml = new QmlPositioned(this); + m_carriersModel = new CarriersLocationModel(this); m_defaultAltitude = QuantityValue{Units::FeetMSL, 6000}; m_defaultAirspeed = QuantityValue{Units::Knots, 120}; @@ -112,9 +112,7 @@ LocationController::LocationController(QObject *parent) : this, &LocationController::descriptionChanged); } -LocationController::~LocationController() -{ -} +LocationController::~LocationController() = default; void LocationController::setLaunchConfig(LaunchConfig *config) { @@ -196,35 +194,62 @@ void LocationController::setBaseGeod(QmlGeod geod) if (m_locationIsLatLon && (m_geodLocation == geod.geod())) return; + clearLocation(); m_locationIsLatLon = true; m_geodLocation = geod.geod(); + emit baseLocationChanged(); +} + +QString LocationController::carrierName() const +{ + return m_carrierName; +} + +void LocationController::setCarrierLocation(QString name) +{ + const auto cIndex = m_carriersModel->indexOf(name); + clearLocation(); + if (cIndex < 0) { + qWarning() << "invalid carrier name:" << name; + return; + } + + m_locationIsCarrier = true; + m_carrierName = name; + m_geodLocation = m_carriersModel->geodForIndex(cIndex); + m_carrierParkings = m_carriersModel->parkingsForIndex(cIndex); + + emit baseLocationChanged(); +} + +void LocationController::clearLocation() +{ + m_locationIsLatLon = false; + m_locationIsCarrier = false; m_location.clear(); + m_carrierName.clear(); m_airportLocation.clear(); m_detailLocation.clear(); + m_detailQml->setGuid(0); + m_baseQml->setGuid(0); + m_carrierParkings.clear(); + m_carrierParking.clear(); emit baseLocationChanged(); } void LocationController::setBaseLocation(QmlPositioned* pos) { if (!pos) { - m_location.clear(); - m_detailLocation.clear(); - m_detailQml->setGuid(0); - m_baseQml->setGuid(0); - m_airportLocation.clear(); - m_locationIsLatLon = false; - emit baseLocationChanged(); + clearLocation(); return; } if (pos->inner() == m_location) return; - m_locationIsLatLon = false; + clearLocation(); m_location = pos->inner(); m_baseQml->setGuid(pos->guid()); - m_detailLocation.clear(); - m_detailQml->setGuid(0); if (FGPositioned::isAirportType(m_location.ptr())) { m_airportLocation = static_cast(m_location.ptr()); @@ -249,7 +274,6 @@ void LocationController::setDetailLocation(QmlPositioned* pos) m_detailLocation.clear(); m_detailQml->setInner({}); } else { - qInfo() << Q_FUNC_INFO << "pos:" << pos->ident(); m_detailLocation = pos->inner(); m_useActiveRunway = false; m_useAvailableParking = false; @@ -261,7 +285,7 @@ void LocationController::setDetailLocation(QmlPositioned* pos) QmlGeod LocationController::baseGeod() const { - if (m_locationIsLatLon) + if (m_locationIsLatLon || m_locationIsCarrier) return m_geodLocation; if (m_location) @@ -368,6 +392,33 @@ QmlGeod LocationController::parseStringAsGeod(QString string) const return QmlGeod(g); } +QString LocationController::carrierParking() const +{ + if (!m_locationIsCarrier) + return {}; + return m_carrierParking; +} + +void LocationController::setCarrierParking(QString name) +{ + if (!m_locationIsCarrier) { + qWarning() << "active location is not a carrier"; + return; + } + + if (m_carrierParking == name) + return; + + if (!m_carrierParkings.contains(name)) { + qWarning() << "parking '" << name << "' not found in carrier parking list"; + return; + } + + m_carrierParking = name; + m_useCarrierFLOLS = false; + emit configChanged(); +} + QmlPositioned *LocationController::detail() const { return m_detailQml; @@ -378,6 +429,11 @@ QmlPositioned *LocationController::baseLocation() const return m_baseQml; } +QStringList LocationController::carrierParkings() const +{ + return m_carrierParkings; +} + void LocationController::setOffsetRadial(QuantityValue offsetRadial) { if (m_offsetRadial == offsetRadial) @@ -436,27 +492,26 @@ void LocationController::setUseAvailableParking(bool useAvailableParking) emit configChanged(); } +void LocationController::setUseCarrierFLOLS(bool useCarrierFLOLS) +{ + if (!m_locationIsCarrier) { + qWarning() << "location is not a carrier"; + return; + } + + if (m_useCarrierFLOLS == useCarrierFLOLS) + return; + + m_useCarrierFLOLS = useCarrierFLOLS; + m_carrierParking.clear(); + emit configChanged(); +} + void LocationController::restoreLocation(QVariantMap l) { - try { - if (l.contains("location-lat")) { - m_locationIsLatLon = true; - m_geodLocation = SGGeod::fromDeg(l.value("location-lon").toDouble(), - l.value("location-lat").toDouble()); - m_location.clear(); - m_airportLocation.clear(); - m_baseQml->setInner(nullptr); - } else if (l.contains("location-id")) { - m_location = NavDataCache::instance()->loadById(l.value("location-id").toULongLong()); - m_locationIsLatLon = false; - if (FGPositioned::isAirportType(m_location.ptr())) { - m_airportLocation = static_cast(m_location.ptr()); - } else { - m_airportLocation.clear(); - } - m_baseQml->setInner(m_location); - } + clearLocation(); + try { m_altitudeEnabled = l.contains("altitude"); m_speedEnabled = l.contains("speed"); m_headingEnabled = l.contains("heading"); @@ -470,10 +525,33 @@ void LocationController::restoreLocation(QVariantMap l) m_offsetDistance = l.value("offset-distance", QVariant::fromValue(m_defaultOffsetDistance)).value(); m_tuneNAV1 = l.value("tune-nav1-radio").toBool(); + if (l.contains("location-lat")) { + m_locationIsLatLon = true; + m_geodLocation = SGGeod::fromDeg(l.value("location-lon").toDouble(), + l.value("location-lat").toDouble()); + } else if (l.contains("carrier")) { + setCarrierLocation(l.value("carrier").toString()); + if (l.contains("carrier-flols")) { + setUseCarrierFLOLS(l.value("carrier-flols").toBool()); + // overwrite value form above, intentionally + m_offsetDistance = l.value("location-carrier-flols-distance", QVariant::fromValue(m_defaultOffsetDistance)).value(); + } else if (l.contains("carrier-parking")) { + setCarrierParking(l.value("carrier-parking").toString()); + } + } else if (l.contains("location-id")) { + m_location = NavDataCache::instance()->loadById(l.value("location-id").toLongLong()); + m_locationIsLatLon = false; + if (FGPositioned::isAirportType(m_location.ptr())) { + m_airportLocation = static_cast(m_location.ptr()); + } else { + m_airportLocation.clear(); + } + m_baseQml->setInner(m_location); + } + if (m_airportLocation) { m_useActiveRunway = false; m_useAvailableParking = false; - m_detailLocation.clear(); if (l.contains("location-apt-runway")) { QString runway = l.value("location-apt-runway").toString().toUpper(); @@ -502,10 +580,7 @@ void LocationController::restoreLocation(QVariantMap l) } // of location is an airport } catch (const sg_exception&) { qWarning() << "Errors restoring saved location, clearing"; - m_location.clear(); - m_airportLocation.clear(); - m_baseQml->setInner(nullptr); - m_offsetEnabled = false; + clearLocation(); } baseLocationChanged(); @@ -515,6 +590,10 @@ void LocationController::restoreLocation(QVariantMap l) bool LocationController::shouldStartPaused() const { + if (m_useCarrierFLOLS) { + return true; + } + if (!m_location) { return false; // defaults to on-ground at the default airport } @@ -535,6 +614,14 @@ QVariantMap LocationController::saveLocation() const if (m_locationIsLatLon) { locationSet.insert("location-lat", m_geodLocation.getLatitudeDeg()); locationSet.insert("location-lon", m_geodLocation.getLongitudeDeg()); + } else if (m_locationIsCarrier) { + locationSet.insert("carrier", m_carrierName); + if (m_useCarrierFLOLS) { + locationSet.insert("carrier-flols", true); + locationSet.insert("location-carrier-flols-distance", QVariant::fromValue(m_offsetDistance)); + } else if (!m_carrierParking.isEmpty()) { + locationSet.insert("carrier-parking", m_carrierParking); + } } else if (m_location) { locationSet.insert("location-id", static_cast(m_location->guid())); @@ -588,7 +675,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"; + "airport-id" << "runway" << "parkpos" << "carrier"; Q_FOREACH(QString s, props) { SGPropertyNode* c = presets->getChild(s.toStdString()); @@ -604,6 +691,8 @@ void LocationController::setLocationProperties() fgSetDouble("/position/longitude-deg", m_geodLocation.getLongitudeDeg()); applyPositionOffset(); + applyAltitude(); + applyAirspeed(); return; } @@ -612,6 +701,27 @@ void LocationController::setLocationProperties() fgSetDouble("/sim/presets/altitude-ft", -9999.0); fgSetDouble("/sim/presets/heading-deg", 9999.0); + if (m_locationIsCarrier) { + fgSetString("/sim/presets/carrier", m_carrierName.toStdString()); + + if (m_useCarrierFLOLS) { + fgSetBool("/sim/presets/runway-requested", true ); + // 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); + + applyAirspeed(); + } else if (!m_carrierParking.isEmpty()) { + fgSetString("/sim/presets/parkpos", m_carrierParking.toStdString()); + } + + if (m_tuneNAV1) { + // tune TACAN to the carrier + qInfo() << "Implement TACAN tuning"; + } + return; + } + if (!m_location) { return; } @@ -681,7 +791,7 @@ void LocationController::setLocationProperties() break; default: break; - }; + } // set disambiguation property globals->get_props()->setIntValue("/sim/presets/navaid-id", @@ -796,6 +906,21 @@ void LocationController::onCollectConfig() return; } + if (m_locationIsCarrier) { + m_config->setArg("carrier", m_carrierName); + + if (!m_carrierParking.isEmpty()) { + m_config->setArg("parkpos", m_carrierParking); + } else if (m_useCarrierFLOLS) { + m_config->setArg("runway", QStringLiteral("FLOLS")); + const double offsetNm = m_offsetDistance.convertToUnit(Units::NauticalMiles).value; + m_config->setArg("offset-distance", QString::number(offsetNm)); + applyAirspeed(); + } + + return; + } + if (!m_location) { return; } @@ -852,7 +977,7 @@ void LocationController::onCollectConfig() break; default: break; - }; + } // set disambiguation property m_config->setProperty("/sim/presets/navaid-id", QString::number(m_location->guid())); @@ -912,6 +1037,11 @@ QString LocationController::description() const return tr("at position %1").arg(QString::fromStdString(s)); } + if (m_locationIsCarrier) { + QString pennant = m_carriersModel->pennantForIndex(m_carriersModel->indexOf(m_carrierName)); + return tr("on carrier %1 (%2)").arg(m_carrierName).arg(pennant); + } + return tr("No location selected"); } @@ -952,7 +1082,7 @@ QString LocationController::description() const if (m_offsetEnabled) { offsetDesc = tr("%1nm %2 of"). arg(offsetNm, 0, 'f', 1). - arg(compassPointFromHeading(m_offsetRadial.value)); + arg(compassPointFromHeading(static_cast(m_offsetRadial.value))); } QString navaidType; diff --git a/src/GUI/LocationController.hxx b/src/GUI/LocationController.hxx index b54b88f84..1310b0ea3 100644 --- a/src/GUI/LocationController.hxx +++ b/src/GUI/LocationController.hxx @@ -32,6 +32,7 @@ #include "UnitsModel.hxx" class NavaidSearchModel; +class CarriersLocationModel; class LocationController : public QObject { @@ -40,6 +41,7 @@ class LocationController : public QObject Q_PROPERTY(QString description READ description NOTIFY descriptionChanged) Q_PROPERTY(NavaidSearchModel* searchModel MEMBER m_searchModel CONSTANT) + Q_PROPERTY(CarriersLocationModel* carriersModel MEMBER m_carriersModel CONSTANT) Q_PROPERTY(QList airportRunways READ airportRunways NOTIFY baseLocationChanged) Q_PROPERTY(QList airportParkings READ airportParkings NOTIFY baseLocationChanged) @@ -68,6 +70,13 @@ class LocationController : public QObject Q_PROPERTY(QmlPositioned* detail READ detail CONSTANT) Q_PROPERTY(bool isBaseLatLon READ isBaseLatLon NOTIFY baseLocationChanged) + Q_PROPERTY(bool isCarrier READ isCarrier NOTIFY baseLocationChanged) + Q_PROPERTY(QString carrier READ carrierName WRITE setCarrierLocation NOTIFY baseLocationChanged) + 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) + + // allow collecting the location properties to be disabled, if the // user is setting conflicting ones Q_PROPERTY(bool skipFromArgs MEMBER m_skipFromArgs NOTIFY skipFromArgsChanged) @@ -110,6 +119,9 @@ public: QmlGeod baseGeod() const; void setBaseGeod(QmlGeod geod); + QString carrierName() const; + void setCarrierLocation(QString name); + bool isAirportLocation() const; bool offsetEnabled() const @@ -138,6 +150,9 @@ public: Q_INVOKABLE QmlGeod parseStringAsGeod(QString string) const; + QString carrierParking() const; + void setCarrierParking(QString name); + bool tuneNAV1() const { return m_tuneNAV1; @@ -161,6 +176,19 @@ public: { return m_altitude; } + + bool isCarrier() const + { + return m_locationIsCarrier; + } + + QStringList carrierParkings() const; + + bool useCarrierFLOLS() const + { + return m_useCarrierFLOLS; + } + public slots: void setOffsetRadial(QuantityValue offsetRadial); @@ -174,6 +202,8 @@ public slots: void setUseAvailableParking(bool useAvailableParking); + void setUseCarrierFLOLS(bool useCarrierFLOLS); + Q_SIGNALS: void descriptionChanged(); void offsetChanged(); @@ -186,6 +216,7 @@ private Q_SLOTS: void onRestoreCurrentLocation(); void onSaveCurrentLocation(); private: + void clearLocation(); void onSearchComplete(); void addToRecent(FGPositionedRef pos); @@ -197,12 +228,15 @@ private: void applyOnFinal(); NavaidSearchModel* m_searchModel = nullptr; + CarriersLocationModel* m_carriersModel = nullptr; FGPositionedRef m_location; FGAirportRef m_airportLocation; // valid if m_location is an FGAirport FGPositionedRef m_detailLocation; // parking stand or runway detail bool m_locationIsLatLon = false; SGGeod m_geodLocation; + bool m_locationIsCarrier = false; + QString m_carrierName; FGPositionedList m_recentLocations; LaunchConfig* m_config = nullptr; @@ -227,6 +261,10 @@ private: bool m_speedEnabled = false; bool m_altitudeEnabled = false; bool m_skipFromArgs = false; + + bool m_useCarrierFLOLS = false; + QString m_carrierParking; + QStringList m_carrierParkings; }; #endif // LOCATION_CONTROLLER_HXX diff --git a/src/GUI/ModelDataExtractor.cxx b/src/GUI/ModelDataExtractor.cxx index a05efb7c0..b34ba80ca 100644 --- a/src/GUI/ModelDataExtractor.cxx +++ b/src/GUI/ModelDataExtractor.cxx @@ -24,35 +24,49 @@ QVariant ModelDataExtractor::data() const return m_model->data(m, role); } - if (m_value.isArray()) { + if (!m_stringsModel.empty()) { + if ((m_index < 0) || (m_index >= m_stringsModel.size())) + return {}; + + return m_stringsModel.at(m_index); + } + + if (m_rawModel.isArray()) { quint32 uIndex = static_cast(m_index); - auto v = m_value.property(uIndex); + auto v = m_rawModel.property(uIndex); if (v.isQObject()) { // handle the QList case auto obj = v.toQObject(); return obj->property(m_role.toUtf8().constData()); } - return m_value.property(uIndex).toVariant(); + return m_rawModel.property(uIndex).toVariant(); } + qWarning() << "Unable to convert model data:" << m_rawModel.toString(); return {}; } -void ModelDataExtractor::setModel(QJSValue model) +void ModelDataExtractor::clear() { - if (m_value.equals(model)) - return; - if (m_model) { // disconnect from everything disconnect(m_model, nullptr, this, nullptr); m_model = nullptr; } - m_value = model; - if (m_value.isQObject()) { - m_model = qobject_cast(m_value.toQObject()); +} + +void ModelDataExtractor::setModel(QJSValue raw) +{ + if (m_rawModel.strictlyEquals(raw)) + return; + + clear(); + m_rawModel = raw; + + if (raw.isQObject()) { + m_model = qobject_cast(raw.toQObject()); if (m_model) { connect(m_model, &QAbstractItemModel::modelReset, this, &ModelDataExtractor::dataChanged); @@ -61,10 +75,24 @@ void ModelDataExtractor::setModel(QJSValue model) // ToDo: handle rows added / removed } else { - qWarning() << "object but not a QAIM" << m_value.toQObject(); + qWarning() << "object but not a QAIM" << raw.toQObject(); + } + } else if (raw.isArray()) { + + } else if (raw.isVariant() || raw.isObject()) { + // special case the QStringList case + + // for reasons I don't understand yet, QStringList returned as a + // property value to JS, does not show up as a variant-in-JS-Value above + // (so ::isVariant returns false), but conversion to a variant + // works. Hence the 'raw.isObject' above + + const auto var = raw.toVariant(); + if (var.type() == QVariant::StringList) { + m_stringsModel = var.toStringList(); + } else { + qWarning() << Q_FUNC_INFO << "variant but not a QStringList" << var; } - } else { - // might be null, or an array } emit modelChanged(); diff --git a/src/GUI/ModelDataExtractor.hxx b/src/GUI/ModelDataExtractor.hxx index 8c673b5b1..dca4afa1b 100644 --- a/src/GUI/ModelDataExtractor.hxx +++ b/src/GUI/ModelDataExtractor.hxx @@ -20,7 +20,7 @@ public: QJSValue model() const { - return m_value; + return m_rawModel; } int index() const @@ -52,8 +52,12 @@ private slots: void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); private: + void clear(); + QAbstractItemModel* m_model = nullptr; - QJSValue m_value; + QJSValue m_rawModel; + QStringList m_stringsModel; + int m_index = 0; QString m_role; }; diff --git a/src/GUI/NavaidSearchModel.cxx b/src/GUI/NavaidSearchModel.cxx index 5b43175aa..d67e0f38f 100644 --- a/src/GUI/NavaidSearchModel.cxx +++ b/src/GUI/NavaidSearchModel.cxx @@ -153,10 +153,17 @@ void NavaidSearchModel::clear() qlonglong NavaidSearchModel::guidAtIndex(int index) const { - if ((index < 0) || (index >= m_ids.size())) + const size_t uIndex = static_cast(index); + if ((index < 0) || (uIndex >= m_ids.size())) return 0; - return m_ids.at(index); + return m_ids.at(uIndex); +} + +NavaidSearchModel::NavaidSearchModel(QObject *parent) : + QAbstractListModel(parent) +{ + } void NavaidSearchModel::setSearch(QString t, NavaidSearchModel::AircraftType aircraft) diff --git a/src/GUI/NavaidSearchModel.hxx b/src/GUI/NavaidSearchModel.hxx index fa58755a9..0ca9c7915 100644 --- a/src/GUI/NavaidSearchModel.hxx +++ b/src/GUI/NavaidSearchModel.hxx @@ -51,7 +51,7 @@ class NavaidSearchModel : public QAbstractListModel }; public: - NavaidSearchModel() { } + NavaidSearchModel(QObject* parent = nullptr); enum AircraftType { diff --git a/src/GUI/assets/aircraft-carrier-icon.png b/src/GUI/assets/aircraft-carrier-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1597e0a56aeb04accd9d91113b3671897cebae56 GIT binary patch literal 1483 zcmZ`(YdjNr82;1b7G~r&G%|NHLowvGHJ2urN$ONvHn+%SW5_va7L{Bpw?=0wNy?-S zom_GoR++-Vk;_i$hsq_=#qF$*=fnB%JkRfW-uL;v-`g?SZv6GGU2&My|*!dF@*US{*;!v95NyJOP+Xhp%lItF~5=W2{ zPn;_$eBx74%z>dmjc(^(P`*%8sIkQrkD$s?nHocZz`4eJVBI>}0!A!t#8b1|4N?P8 zN6!;EW>-2bSXIc!w3$xI^`qIwNIcyp&P5)BYcwueTVZv~GA+L{7pC1-+SiE(1?8AKY8l$lB)@Or=b2#WE0i9K7cX1We6#3Nta>k*Za%ZL$GSrOqlBGFuS zaJLO6XGe9MF)XpVcZy$!cHZXN6V%-MF7D9UKd>{!-u-92$oSYJDuj z{o$@QJ_J$d_2Vix0aYK$E89c7S@P)sTxVICp-0kKz22>fS_UAlCWKRXei3qQ#T2HP zbES)&5Az5Lnui`#*@ruFjwhscQljS7=Lt?mO4N2NF?Ev3VQF!Ia2WP9c6EfvBH z^L1*eTuLf;r%)|r1;T!^I}kf^IQvW zqT~>a(keTVLoTF~X#VP*OZv|6H>9YZv*}Nd;!MUoE_?Gs1a=+=*p-1-<<3|rA?Khc&WJ;VjGtm4>;L(CUHN*+>F+_gSG|2`};lu zm@&GuA^lh+A-(5N!Er_Cw5yPm;|Ex=mq|AcY-J(*M>~)mYsEq;vi|d>-=5Vs--EH+ zDW$vE-`6y7T5EPMR6)V_3s0L>cm-q`H;s#PY5xTY^oJr@x7yD^elu!Udr1^pQ(85F(t`blm+>70)_sP zO3cy9qC7T_Eu6*k;7wr}l90?HbRIwTK|C}K6>6R7(;_*gtyF+Bn@sW>CF~5u)nCqf zl#C=xu=Xd)5Hj2#nu{*_%8a%9;X8%1?qP3jwKD2Qyp%+a9TKa^zQI@D<-dR5P0ewmk~`YWZ*lJV%o?;)U%^kmJV7x5 z#7$)l7jyeTxasfXC{5idy^k{4xY8J9|jd>E|~V5KuhBKIZ0P9Rob)jwtp zcbN~b*4^bheD~e5*?RA^aQlT9MC3X2G<)(_v~!Ynras5V_RhKNmZ7U62Eqork+DIL zWchZmM5F?HnRca{{$CZKhd*GWzBJBk^AE<$DQJrfbav}QrzammCu}V+>k*opdBvVq zVH{KB*)b_$pdXNcKwZGF(Wb;^REl0Izk9FvZy_1@!wf2gLBoc`)5HQWXbjo{ZEay? zN3z&lH=Y@cnPzI9{PXkOQ6BTj# c6g`9zOGCxPhh3j{)DkZNPrMKAfg73iFQKP*XaE2J literal 0 HcmV?d00001 diff --git a/src/GUI/assets/icons8-cargo-ship-50.png b/src/GUI/assets/icons8-cargo-ship-50.png new file mode 100644 index 0000000000000000000000000000000000000000..d1de899f3fbfeb581aee70a82501ad79b84e3f77 GIT binary patch literal 890 zcmV-=1BLvFP)S}JL4-szPF$SRc3W0=R}VAY z9jG7fbaC&0Z~dq5J*WSwp+=1wHEPtTQKLqU8vn&dm>IN)g~0J_LyIMY7O+sL0~t1y z1@fL|P84&a*gw740;^)Aq_5YcE?Z+LP^CgGugDuHx(Qs4UCH^ZFN)@gZh?HBt4{O?+AVYCb|2BaoFihB>UqsTJof zOD%tu4GtoZcXQ3>dYpGr&O1~#*zZ7Y&GioBoF3<1pL2H%+M-;FXY+a!ekqJUDURLB zye{I({M=Etct3XJENsB9xz?2SF5@9Q+~%Is*?$*N_8!HLI#-6pYOjf;EyD*bylsWC zvoR|Y{WRD(0P%ldGuE2FS9>7WdcV=JKRa%p=YoU4$(p)vYjh61`76P_nl|a*f_0(= zGKRfE^JMElBaoqet|;JfO>NxfY4>T-zOz4`$ z9aScrfvq{|cib=P^%T~0nLC2J@g~+)xYxSmuGhX=j^Sy|syZd?R9gY(3a@fd_?MSO z-V`y7SMiDXRb48M;W+NWsuu2yC?Z?2FYj|9t`!Ea^>}=Vm+&!uNYOXrL0nSM@NIH8 z=k;uAJtY6M^F%Y^eE!sd(`Y+|Q~j<2cTaM6SKuBHrgnP=&fyVJcE@pMMz~Kn&DXId ziOcd^x&>E=2cWqOa0YMSL-8_T6gP?orqyD{rJ(J2OT5iICU*4(+$45%mB`m`@TvHB z#yj{^?COL#mRI8foFrWPi#lKl?#mkwB1alnBfiv09o|(Jb7WMU%ZU`WqJ`V99GS!U zxCVFPx)$#4XIY>0O&-YtiRhg Q`2YX_07*qoM6N<$f;+vVeE 0 + + RadioButton { + id: parkingRadio + anchors.verticalCenter: parent.verticalCenter + group: radioGroup + onClicked: { + if (selected) parkingChoice.setLocation(); + } + } + + StyledText { + text: qsTr("Parking") + anchors.verticalCenter: parent.verticalCenter + enabled: parkingRadio.selected + } + + PopupChoice { + id: parkingChoice + model: _location.carrierParkings + // displayRole: "modelData" + width: parent.width * 0.5 + anchors.verticalCenter: parent.verticalCenter + enabled: parkingRadio.selected + + onCurrentIndexChanged: { + setLocation(); + } + + function syncCurrentIndex() + { + for (var i=0; i < _location.carrierParkings.length; ++i) { + if (_location.carrierParkings[i] === _location.carrierParking) { + currentIndex = i; + return; + } + } + + // not found, default to available + currentIndex = 0; + } + + function setLocation() + { + _location.carrierParking = _location.carrierParkings[currentIndex] + //diagram.selection = _location.airportParkings[currentIndex] + } + } + } + + ToggleSwitch { + anchors.left: parent.left + anchors.leftMargin: Style.margin + label: qsTr("Tune navigation radio (TACAN) to carrier") + checked: _location.tuneNAV1 + onCheckedChanged: { + _location.tuneNAV1 = checked + } + } + } // main layout column + } // main panel rectangle +} diff --git a/src/GUI/resources.qrc b/src/GUI/resources.qrc index db19b19c3..a835b9d97 100644 --- a/src/GUI/resources.qrc +++ b/src/GUI/resources.qrc @@ -91,6 +91,7 @@ qml/LineEdit.qml qml/LocationAirportView.qml qml/LocationNavaidView.qml + qml/LocationCarrierView.qml qml/icons8-linear-spinner.gif qml/icons8-ellipsis-filled-50.png qml/RadioButton.qml @@ -130,6 +131,7 @@ qml/AircraftListView.qml qml/GridToggleButton.qml qml/EnableDisableButton.qml + qml/IconButton.qml preview-close.png @@ -149,5 +151,7 @@ assets/icons8-grid-view.svg assets/icons8-menu.svg assets/icons8-hide-50.png + assets/icons8-cargo-ship-50.png + assets/aircraft-carrier-icon.png diff --git a/src/Main/options.cxx b/src/Main/options.cxx index 7c820215d..ea292a55d 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -1423,7 +1423,7 @@ fgOptScenario( const char *arg ) } // create description node - auto n = FGAIManager::registerScenarioFile(path); + 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; diff --git a/src/Main/positioninit.cxx b/src/Main/positioninit.cxx index 6389858de..d53b4990a 100644 --- a/src/Main/positioninit.cxx +++ b/src/Main/positioninit.cxx @@ -49,6 +49,7 @@ using std::endl; using std::string; + namespace flightgear { @@ -68,8 +69,11 @@ static bool global_callbackRegistered = false; static void finalizePosition(); +namespace { // annonymous namepsace to avoid warnings about inline classes + // Set current tower position lon/lat given an airport id -static bool fgSetTowerPosFromAirportID( const string& id) { +bool fgSetTowerPosFromAirportID( const string& id) +{ const FGAirport *a = fgFindAirportID( id); if (a) { SGGeod tower = a->getTowerLocation(); @@ -80,12 +84,12 @@ static bool fgSetTowerPosFromAirportID( const string& id) { } else { return false; } - } class FGTowerLocationListener : public SGPropertyChangeListener { - void valueChanged(SGPropertyNode* node) { + void valueChanged(SGPropertyNode* node) override + { string id(node->getStringValue()); if (fgGetBool("/sim/tower/auto-position",true)) { @@ -116,6 +120,9 @@ class FGClosestTowerLocationListener : public SGPropertyChangeListener } }; +} // of anonymous namespace + + void initTowerLocationListener() { SGPropertyChangeListener* tll = new FGTowerLocationListener(); @@ -160,10 +167,7 @@ static void fgApplyStartOffset(const SGGeod& aStartPos, double aHeading, double aHeading = aTargetHeading; } - SGGeod offset; - double az2; // dummy - SGGeodesy::direct(startPos, offsetAzimuth + 180, offsetDistance, offset, az2); - startPos = offset; + startPos = SGGeodesy::direct(startPos, offsetAzimuth + 180, offsetDistance); } setInitialPosition(startPos, aHeading); @@ -184,7 +188,7 @@ std::tuple runwayStartPos(FGRunwayRef runway) // add a margin, try to keep the entire aeroplane comfortable off the // runway. double margin = startOffset + (runway->widthM() * 1.5); - FGTaxiNodeRef taxiNode = groundNet ? groundNet->findNearestNodeOffRunway(pos, runway, margin) : 0; + FGTaxiNodeRef taxiNode = groundNet ? groundNet->findNearestNodeOffRunway(pos, runway, margin) : FGTaxiNodeRef{}; if (taxiNode) { // set this so multiplayer.nas can inform the user fgSetBool("/sim/presets/avoided-mp-runway", true); @@ -313,11 +317,10 @@ static bool fgSetPosFromAirportIDandRwy( const string& id, const string& rwy, bo } -static void fgSetDistOrAltFromGlideSlope() { - // cout << "fgSetDistOrAltFromGlideSlope()" << endl; +static void fgSetDistOrAltFromGlideSlope() +{ string apt_id = fgGetString("/sim/presets/airport-id"); - double gs = fgGetDouble("/sim/presets/glideslope-deg") - * SG_DEGREES_TO_RADIANS ; + double gs = SGMiscd::deg2rad(fgGetDouble("/sim/presets/glideslope-deg")); double od = fgGetDouble("/sim/presets/offset-distance-nm"); double alt = fgGetDouble("/sim/presets/altitude-ft"); @@ -363,9 +366,7 @@ static bool fgSetPosFromNAV( const string& id, FGPositioned::Type type, PositionedID guid) { - FGNavRecord* nav = 0; - - + FGNavRecordRef nav; if (guid != 0) { nav = FGPositioned::loadById(guid); if (!nav) @@ -383,14 +384,14 @@ static bool fgSetPosFromNAV( const string& id, if( navlist.size() > 1 ) { std::ostringstream buf; buf << "Ambigous NAV-ID: '" << id << "'. Specify id and frequency. Available stations:" << endl; - for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) { + for( const auto& nav : navlist ) { // NDB stored in kHz, VOR stored in MHz * 100 :-P - double factor = (*it)->type() == FGPositioned::NDB ? 1.0 : 1/100.0; - string unit = (*it)->type() == FGPositioned::NDB ? "kHz" : "MHz"; - buf << (*it)->ident() << " " - << std::setprecision(5) << (double)((*it)->get_freq() * factor) << " " - << (*it)->get_lat() << "/" << (*it)->get_lon() - << endl; + double factor = nav->type() == FGPositioned::NDB ? 1.0 : 1/100.0; + string unit = nav->type() == FGPositioned::NDB ? "kHz" : "MHz"; + buf << nav->ident() << " " + << std::setprecision(5) << static_cast(nav->get_freq() * factor) << " " + << nav->get_lat() << "/" << nav->get_lon() + << endl; } SG_LOG( SG_GENERAL, SG_ALERT, buf.str() ); @@ -413,7 +414,7 @@ static InitPosResult setInitialPosFromCarrier( const string& carrier ) // so our PagedLOD is loaded fgSetDouble("/sim/presets/longitude-deg", initialPos.second.getLongitudeDeg()); fgSetDouble("/sim/presets/latitude-deg", initialPos.second.getLatitudeDeg()); - SG_LOG( SG_GENERAL, SG_INFO, "Initial carrier pos = " << initialPos.second ); + SG_LOG( SG_GENERAL, SG_DEBUG, "Initial carrier pos = " << initialPos.second ); return VicinityPosition; } @@ -421,6 +422,31 @@ static InitPosResult setInitialPosFromCarrier( const string& carrier ) return Failure; } +static InitPosResult checkCarrierSceneryLoaded(const SGSharedPtr carrierRef) +{ + SGVec3d cartPos = carrierRef->getCartPos(); + auto framestamp = globals->get_renderer()->getViewer()->getFrameStamp(); + simgear::CheckSceneryVisitor csnv(globals->get_scenery()->getPager(), + toOsg(cartPos), + 100.0 /* range in metres */, + framestamp); + + // currently the PagedLODs will not be loaded by the DatabasePager + // while the splashscreen is there, so CheckSceneryVisitor force-loads + // missing objects in the main thread + carrierRef->getSceneBranch()->accept(csnv); + if (!csnv.isLoaded()) { + return ContinueWaiting; + } + + // and then wait for the load to actually be synced to the main thread + if (carrierRef->getSceneBranch()->getNumChildren() < 1) { + return ContinueWaiting; + } + + return VicinityPosition; +} + // Set current_options lon/lat given an aircraft carrier id static InitPosResult setFinalPosFromCarrier( const string& carrier, const string& posid ) { @@ -432,24 +458,9 @@ static InitPosResult setFinalPosFromCarrier( const string& carrier, const string return Failure; } - SGVec3d cartPos = carrierRef->getCartPos(); - auto framestamp = globals->get_renderer()->getViewer()->getFrameStamp(); - simgear::CheckSceneryVisitor csnv(globals->get_scenery()->getPager(), - toOsg(cartPos), - 100.0 /* range in metres */, - framestamp); - - // currently the PagedLODs will not be loaded by the DatabasePager - // while the splashscreen is there, so CheckSceneryVisitor force-loads - // missing objects in the main thread - carrierRef->getSceneBranch()->accept(csnv); - if (!csnv.isLoaded()) { - return ContinueWaiting; - } - - // and then wait for the load to actually be synced to the main thread - if (carrierRef->getSceneBranch()->getNumChildren() < 1) { - return ContinueWaiting; + auto res = checkCarrierSceneryLoaded(carrierRef); + if (res != VicinityPosition) { + return res; // either failrue or keep waiting for scenery load } SGGeod geodPos; @@ -492,10 +503,55 @@ static InitPosResult setFinalPosFromCarrier( const string& carrier, const string return Failure; } +static InitPosResult setFinalPosFromCarrierFLOLS(const string& carrier) +{ + SGSharedPtr 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 failure or keep waiting for scenery load + } + + SGGeod flolsPosition; + double headingToFLOLS; + if (!carrierRef->getFLOLSPositionHeading(flolsPosition, headingToFLOLS)) { + SG_LOG( SG_GENERAL, SG_ALERT, "Unable to compiute FLOLS position for carrier = " + << carrier ); + return Failure; + } + + const auto flolsElevationFt = flolsPosition.getElevationFt(); + 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); + + const double offsetFt = od * SG_NM_TO_METER * SG_METER_TO_FEET; + startPos.setElevationFt(fabs(offsetFt*tan(gs)) + flolsElevationFt); + + 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("/position/longitude-deg", startPos.getLongitudeDeg()); + fgSetDouble("/position/latitude-deg", startPos.getLatitudeDeg()); + fgSetDouble("/position/altitude-ft", startPos.getElevationFt()); + fgSetDouble("/orientation/heading-deg", headingToFLOLS); + fgSetBool("/sim/presets/onground", false); + + return ExactPosition; +} + // Set current_options lon/lat given a fix ident and GUID static bool fgSetPosFromFix( const string& id, PositionedID guid ) { - FGPositioned* fix = NULL; + FGPositionedRef fix; if (guid != 0) { fix = FGPositioned::loadById(guid); } else { @@ -521,8 +577,7 @@ bool initPosition() global_callbackRegistered = true; } - double gs = fgGetDouble("/sim/presets/glideslope-deg") - * SG_DEGREES_TO_RADIANS ; + double gs = SGMiscd::deg2rad(fgGetDouble("/sim/presets/glideslope-deg")); double od = fgGetDouble("/sim/presets/offset-distance-nm"); double alt = fgGetDouble("/sim/presets/altitude-ft"); @@ -762,20 +817,29 @@ void finalizePosition() */ std::string carrier = fgGetString("/sim/presets/carrier"); std::string parkpos = fgGetString("/sim/presets/parkpos"); + std::string runway = fgGetString("/sim/presets/runway"); std::string apt = fgGetString("/sim/presets/airport-id"); + const bool rwy_req = fgGetBool("/sim/presets/runway-requested"); if (!carrier.empty()) { - const auto res = setFinalPosFromCarrier(carrier, parkpos); - if (res == ExactPosition) { + const bool atFLOLS = rwy_req && (runway == "FLOLS"); + InitPosResult carrierResult; + if (atFLOLS) { + carrierResult = setFinalPosFromCarrierFLOLS(carrier); + } else { + carrierResult = setFinalPosFromCarrier(carrier, parkpos); + } + if (carrierResult == ExactPosition) { done = true; - } else if (res == Failure) { + } else if (carrierResult == Failure) { SG_LOG(SG_GENERAL, SG_ALERT, "secondary carrier init failed"); done = true; } else { done = false; // 60 second timeout on waiting for the carrier to load if (global_finalizeTime.elapsedMSec() > 60000) { + SG_LOG(SG_GENERAL, SG_ALERT, "Timeout waiting for carrier scenery to load, will start on the water."); done = true; } } From 26b527f1516569db5596d0e427629149acce737d Mon Sep 17 00:00:00 2001 From: James Turner Date: Fri, 20 Mar 2020 11:40:32 -0700 Subject: [PATCH 79/98] Fix Linux compilation --- src/Main/fg_init.cxx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 3e3b170f3..f01234535 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -40,6 +40,7 @@ # include # include # include +# include #endif #include @@ -513,9 +514,14 @@ bool fgInitHome() return false; } - write(fd, buf, len); - - int err = flock(fd, LOCK_EX); + int err = write(fd, buf, len); + if (err != 0) { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to write to lock file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } + + err = flock(fd, LOCK_EX); if (err != 0) { SG_LOG(SG_GENERAL, SG_ALERT, "failed to lock file:" << pidPath << "\n\tdue to:" << simgear::strutils::error_string(errno)); From 162ba266d0e9ef34e8a057c5e168e57771cf111d Mon Sep 17 00:00:00 2001 From: James Turner Date: Fri, 20 Mar 2020 11:40:47 -0700 Subject: [PATCH 80/98] Fix a warning --- src/Main/globals.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Main/globals.cxx b/src/Main/globals.cxx index 1d5025b47..29d867891 100644 --- a/src/Main/globals.cxx +++ b/src/Main/globals.cxx @@ -811,7 +811,6 @@ FGGlobals::loadUserSettings(SGPath userDataPath) been loaded. */ SGPropertyNode* ai = autosave.getNode("ai"); if (ai) { - SGPropertyNode* ai_models = ai->getNode("models"); ai->removeChildren("models"); } copyProperties(&autosave, globals->get_props()); From 6849311d495267204a87ea352c070fb9a442df26 Mon Sep 17 00:00:00 2001 From: James Turner Date: Fri, 20 Mar 2020 18:43:12 +0000 Subject: [PATCH 81/98] Own the SGReaderWriterOptions object Prevents it being unintentionally freed during the read call, causing a crash depending on the OSG version (some versions take a kung-fu death grip) --- src/Viewer/splash.cxx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Viewer/splash.cxx b/src/Viewer/splash.cxx index 6fc8cf6dc..467af2fb8 100644 --- a/src/Viewer/splash.cxx +++ b/src/Viewer/splash.cxx @@ -60,6 +60,8 @@ static const char* LICENSE_URL_TEXT = "Licensed under the GNU GPL. See http://www.flightgear.org for more information"; +using namespace simgear; + class SplashScreenUpdateCallback : public osg::NodeCallback { public: virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) @@ -87,10 +89,9 @@ void SplashScreen::createNodes() #if OSG_VERSION_LESS_THAN(3,4,0) _splashImage = osgDB::readImageFile(splashImage); #else - auto staticOptions = simgear::SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions()); - staticOptions->setLoadOriginHint(simgear::SGReaderWriterOptions::LoadOriginHint::ORIGIN_SPLASH_SCREEN); + osg::ref_ptr staticOptions = SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions()); + staticOptions->setLoadOriginHint(SGReaderWriterOptions::LoadOriginHint::ORIGIN_SPLASH_SCREEN); _splashImage = osgDB::readRefImageFile(splashImage, staticOptions); -// _splashImage = osgDB::readRefImageFile(selectSplashImage()); #endif if (!_splashImage){ From 5f0c10d94d26cb5a5e7fcab19940ba2e0ff85883 Mon Sep 17 00:00:00 2001 From: James Turner Date: Fri, 20 Mar 2020 22:24:44 +0000 Subject: [PATCH 82/98] Fix incorrect write() return code handling --- src/Main/fg_init.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index f01234535..a1b3460bc 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -515,7 +515,7 @@ bool fgInitHome() } int err = write(fd, buf, len); - if (err != 0) { + if (err < 0) { SG_LOG(SG_GENERAL, SG_ALERT, "failed to write to lock file:" << pidPath << "\n\tdue to:" << simgear::strutils::error_string(errno)); return false; From 1e330f9445bd049100be1e9cb98ed75875e20934 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Sun, 22 Mar 2020 19:20:56 +0000 Subject: [PATCH 83/98] src/Viewer/ViewPropertyEvaluator.cxx: cope with root node being bool. Aircraft rah-66 has a bug where it sets root node to type BOOL, which breaks ViewPropertyEvaluator::getDoubleValue's handling of default values - the string value of the node is no longer an empty string so we ended up returning 0.0 or 1.0 instead of default_. The fix is to special-case BOOL. Have also special-cased handling of the root node, as this is a fairly important part of how ViewPropertyEvaluator works. --- src/Viewer/ViewPropertyEvaluator.cxx | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/Viewer/ViewPropertyEvaluator.cxx b/src/Viewer/ViewPropertyEvaluator.cxx index 8c980dd7a..6f8a10ac0 100644 --- a/src/Viewer/ViewPropertyEvaluator.cxx +++ b/src/Viewer/ViewPropertyEvaluator.cxx @@ -487,15 +487,26 @@ namespace ViewPropertyEvaluator { double getSequenceDoubleValue(Sequence& sequence, double default_) { SGPropertyNode* node = getSequenceNode(sequence); - if (node) { - if (node->getStringValue()[0] != 0) { - return node->getDoubleValue(); - } + if (!node) { + return default_; + } + if (!node->getParent()) { + /* Root node. */ + return default_; + } + if (node->getType() == simgear::props::BOOL) { + /* 2020-03-22: there is a problem with aircraft rah-66 setting type + of the root node to bool which gives string value "false". So we + force a return of default_. */ + return default_; + } + if (node->getStringValue()[0] == 0) { /* If we reach here, the node exists but its value is an empty string, so node->getDoubleValue() would return 0 which isn't useful, so instead we return default_. */ + return default_; } - return default_; + return node->getDoubleValue(); } bool getSequenceBoolValue(Sequence& sequence, bool default_) @@ -725,5 +736,9 @@ namespace ViewPropertyEvaluator { } return out; } - + + void dump() + { + std::cerr << Dump() << "\n"; + } } From cb830b70ff3ef59cf9e611ec6318fcf2abbd60aa Mon Sep 17 00:00:00 2001 From: James Turner Date: Sat, 21 Mar 2020 09:32:28 +0000 Subject: [PATCH 84/98] Launcher: add setting for texture-cache --- src/GUI/qml/Settings.qml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/GUI/qml/Settings.qml b/src/GUI/qml/Settings.qml index 15d25c748..c328835b1 100644 --- a/src/GUI/qml/Settings.qml +++ b/src/GUI/qml/Settings.qml @@ -450,6 +450,20 @@ Item { enabled: !renderSection.rembrandt property var data: [0, 2, 4]; defaultIndex: 0 + }, + + SettingCheckbox { + id: compressTextures + setting: "texture-compression" + keywords: ["texture", "compresseion", "memory", "dxt", "cache"] + // no option, since we need to set a property + advanced: true + + label: qsTr("Cache graphics for faster loading") + description: qsTr("By converting images used in rendering to an optimised format " + + "loading times and memory use can be improved. This will consume " + + "some disk space and take initial time while images are converted, " + + "but subsequent loads will be faster, and use less memory.") } ] @@ -465,6 +479,8 @@ Item { if (alsEnabled) { _config.setProperty("/sim/rendering/shaders/skydome", true); } + + _config.setProperty("/sim/rendering/texture-cache/cache-enabled", compressTextures.value); } } From 86e81735c5dd0ec5ae43a3a2aeb684b4269f0e88 Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 23 Mar 2020 10:06:19 +0000 Subject: [PATCH 85/98] Launcher: fix a typo --- src/GUI/qml/AddOns.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GUI/qml/AddOns.qml b/src/GUI/qml/AddOns.qml index 4f95b21a4..4c96acc3b 100644 --- a/src/GUI/qml/AddOns.qml +++ b/src/GUI/qml/AddOns.qml @@ -125,7 +125,7 @@ Item { deletePromptText: qsTr("Remove the aircraft folder: '%1' from the list? (The folder contents will not be changed)").arg(model.path); modelCount: _addOns.aircraftPaths.count onPerformDelete: _addOns.aircraftPaths.removePath(model.index) - onPerformMove: _addOns.aircraftPaths.sawpIndices(model.index, newIndex); + onPerformMove: _addOns.aircraftPaths.swapIndices(model.index, newIndex); } } From 5dc2f20848c92a424e8d48594e9b36111d248c87 Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 23 Mar 2020 16:33:39 +0000 Subject: [PATCH 86/98] Launcher: fix scanning of aircraft dirs on startup https://sourceforge.net/p/flightgear/codetickets/2195/ --- src/GUI/AddOnsController.cxx | 9 ++++++++- src/GUI/LocalAircraftCache.cxx | 5 ++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/GUI/AddOnsController.cxx b/src/GUI/AddOnsController.cxx index 6dfb7777d..3428dfe4d 100644 --- a/src/GUI/AddOnsController.cxx +++ b/src/GUI/AddOnsController.cxx @@ -50,6 +50,14 @@ AddOnsController::AddOnsController(LauncherMainWindow *parent, LaunchConfig* con m_sceneryPaths->loadFromSettings("scenery-paths-v2"); m_aircraftPaths = new PathListModel(this); + m_aircraftPaths->loadFromSettings("aircraft-paths-v2"); + + // sync up the aircraft cache now + auto aircraftCache = LocalAircraftCache::instance(); + aircraftCache->setPaths(m_aircraftPaths->enabledPaths()); + aircraftCache->scanDirs(); + + // watch for future changes connect(m_aircraftPaths, &PathListModel::enabledPathsChanged, [this] () { m_aircraftPaths->saveToSettings("aircraft-paths-v2"); @@ -57,7 +65,6 @@ AddOnsController::AddOnsController(LauncherMainWindow *parent, LaunchConfig* con aircraftCache->setPaths(m_aircraftPaths->enabledPaths()); aircraftCache->scanDirs(); }); - m_aircraftPaths->loadFromSettings("aircraft-paths-v2"); QSettings settings; int size = settings.beginReadArray("addon-modules"); diff --git a/src/GUI/LocalAircraftCache.cxx b/src/GUI/LocalAircraftCache.cxx index 10e51faed..d65464b1d 100644 --- a/src/GUI/LocalAircraftCache.cxx +++ b/src/GUI/LocalAircraftCache.cxx @@ -438,12 +438,11 @@ void LocalAircraftCache::scanDirs() QStringList dirs = m_paths; - Q_FOREACH(SGPath ap, globals->get_aircraft_paths()) { + for (SGPath ap : globals->get_aircraft_paths()) { dirs << QString::fromStdString(ap.utf8Str()); } - SGPath rootAircraft(globals->get_fg_root()); - rootAircraft.append("Aircraft"); + SGPath rootAircraft = globals->get_fg_root() / "Aircraft"; dirs << QString::fromStdString(rootAircraft.utf8Str()); m_scanThread.reset(new AircraftScanThread(dirs)); From 9034ed5d46afd7330f41b3c1909698ac9dfe3a4b Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Mon, 23 Mar 2020 17:41:45 +0100 Subject: [PATCH 87/98] Splash Screen fixes: Specify type for options that are passed into the readRefImageFile (ref: devlist Andreas Mueller 14/03) --- src/Viewer/splash.cxx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Viewer/splash.cxx b/src/Viewer/splash.cxx index 467af2fb8..8e69b53aa 100644 --- a/src/Viewer/splash.cxx +++ b/src/Viewer/splash.cxx @@ -295,11 +295,12 @@ void SplashScreen::setupLogoImage() return; } -#if OSG_VERSION_LESS_THAN(3,4,0) - _logoImage = osgDB::readImageFile(logoPath.utf8Str()); -#else - auto staticOptions = simgear::SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions()); + osg::ref_ptr staticOptions = simgear::SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions()); staticOptions->setLoadOriginHint(simgear::SGReaderWriterOptions::LoadOriginHint::ORIGIN_SPLASH_SCREEN); + +#if OSG_VERSION_LESS_THAN(3, 4, 0) + _logoImage = osgDB::readImageFile(logoPath.utf8Str(), staticOptions); +#else _logoImage = osgDB::readRefImageFile(logoPath.utf8Str(), staticOptions); #endif if (!_logoImage) { From 6f676ee85b907284b36da3867fded3596b1134c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Garc=C3=ADa=20Li=C3=B1=C3=A1n?= Date: Sat, 14 Mar 2020 20:35:59 +0100 Subject: [PATCH 88/98] Compositor: Added $FG_ROOT/Compositor/Effects as a resource provider. --- src/Main/globals.cxx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Main/globals.cxx b/src/Main/globals.cxx index 29d867891..1432ade88 100644 --- a/src/Main/globals.cxx +++ b/src/Main/globals.cxx @@ -133,6 +133,17 @@ public: } }; +class CompositorEffectsProvider : public simgear::ResourceProvider { +public: + CompositorEffectsProvider() : + simgear::ResourceProvider(simgear::ResourceManager::PRIORITY_NORMAL) { + } + SGPath resolve(const std::string &aResource, SGPath&) const override { + const SGPath p = globals->get_fg_root() / "Compositor" / aResource; + return p.exists() ? p : SGPath(); + } +}; + //////////////////////////////////////////////////////////////////////// // Implementation of FGGlobals. //////////////////////////////////////////////////////////////////////// @@ -165,6 +176,9 @@ FGGlobals::FGGlobals() : resMgr->addProvider(new AircraftResourceProvider()); resMgr->addProvider(new CurrentAircraftDirProvider()); resMgr->addProvider(new flightgear::addons::ResourceProvider()); +#ifdef ENABLE_COMPOSITOR + resMgr->addProvider(new CompositorEffectsProvider()); +#endif initProperties(); } From f93aadbae60882e9ceef9517190e6164f36715d4 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 24 Mar 2020 20:35:25 +0000 Subject: [PATCH 89/98] Clang: fix a warning (char used as array subscript) --- 3rdparty/fonts/fntTXF.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/fonts/fntTXF.cxx b/3rdparty/fonts/fntTXF.cxx index 2036e0ea1..36c5bd175 100755 --- a/3rdparty/fonts/fntTXF.cxx +++ b/3rdparty/fonts/fntTXF.cxx @@ -235,7 +235,7 @@ int fntTexFont::loadTXF ( const SGPath& path, GLenum mag, GLenum min ) (float)( glyph.y_off + glyph.h ) / (float) max_height ) ; } - exists [ ' ' ] = FALSE ; + exists [ static_cast(' ') ] = FALSE ; /* Load the image part of the file From 2c056507cef742bd789e661ec0debdad52fa4d9c Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 24 Mar 2020 20:36:06 +0000 Subject: [PATCH 90/98] Launcher: reduce timeout for HTTP updates This helps download performance from inside the launcher --- src/GUI/LauncherController.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GUI/LauncherController.cxx b/src/GUI/LauncherController.cxx index 106dc75fa..84322c053 100644 --- a/src/GUI/LauncherController.cxx +++ b/src/GUI/LauncherController.cxx @@ -108,7 +108,7 @@ LauncherController::LauncherController(QObject *parent, QWindow* window) : m_aircraftGridMode = settings.value("aircraftGridMode").toBool(); m_subsystemIdleTimer = new QTimer(this); - m_subsystemIdleTimer->setInterval(10); + m_subsystemIdleTimer->setInterval(5); connect(m_subsystemIdleTimer, &QTimer::timeout, []() {globals->get_subsystem_mgr()->update(0.0);}); m_subsystemIdleTimer->start(); From 7fe641f3b378295bfbbb4d61e5fd6e2223282177 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 24 Mar 2020 20:36:19 +0000 Subject: [PATCH 91/98] Clang: fix some warnings --- src/Network/HTTPClient.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Network/HTTPClient.cxx b/src/Network/HTTPClient.cxx index 1e3e72faa..641599103 100644 --- a/src/Network/HTTPClient.cxx +++ b/src/Network/HTTPClient.cxx @@ -47,10 +47,10 @@ typedef nasal::Ghost NasalPackage; typedef nasal::Ghost NasalCatalog; typedef nasal::Ghost NasalInstall; -const char* OFFICIAL_CATALOG_ID = "org.flightgear.fgaddon.trunk"; +static const char* OFFICIAL_CATALOG_ID = "org.flightgear.fgaddon.trunk"; // fallback URL is used when looking up a version-specific catalog fails -const char* FALLBACK_CATALOG_URL = "http://mirrors.ibiblio.org/flightgear/ftp/Aircraft-trunk/catalog.xml"; +static const char* FALLBACK_CATALOG_URL = "http://mirrors.ibiblio.org/flightgear/ftp/Aircraft-trunk/catalog.xml"; namespace { @@ -274,7 +274,7 @@ void FGHTTPClient::postinit() pkg::Root* packageRoot = globals->packageRoot(); if (packageRoot) { - FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal"); + FGNasalSys* nasalSys = globals->get_subsystem(); nasal::Hash nasalGlobals = nasalSys->getGlobals(); nasal::Hash nasalPkg = nasalGlobals.createHash("pkg"); // module nasalPkg.set("root", packageRoot); From 7811b11565b485ad7bd6bf12588c526e60489750 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 24 Mar 2020 22:37:13 +0000 Subject: [PATCH 92/98] Fix Yasim ground interface recursion bug The base (non-FlightGear) implementation of YASim ground getBody would always call itself. Fortunately we always replace it with the FG-specific subclass (FGGround). --- src/FDM/YASim/FGGround.hpp | 22 +++++++++++----------- src/FDM/YASim/Ground.cpp | 11 +---------- src/FDM/YASim/Ground.hpp | 7 +++---- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/FDM/YASim/FGGround.hpp b/src/FDM/YASim/FGGround.hpp index 78325b955..df85489f7 100644 --- a/src/FDM/YASim/FGGround.hpp +++ b/src/FDM/YASim/FGGround.hpp @@ -18,26 +18,26 @@ public: FGGround(FGInterface *iface); virtual ~FGGround(); - virtual void getGroundPlane(const double pos[3], + void getGroundPlane(const double pos[3], double plane[4], float vel[3], - unsigned int &body); + unsigned int &body) override; - virtual void getGroundPlane(const double pos[3], + void getGroundPlane(const double pos[3], double plane[4], float vel[3], const simgear::BVHMaterial **material, - unsigned int &body); + unsigned int &body) override; - virtual bool getBody(double t, double bodyToWorld[16], double linearVel[3], - double angularVel[3], unsigned int &id); + bool getBody(double t, double bodyToWorld[16], double linearVel[3], + double angularVel[3], unsigned int &id) override; - virtual bool caughtWire(const double pos[4][3]); + bool caughtWire(const double pos[4][3]) override; - virtual bool getWire(double end[2][3], float vel[2][3]); + bool getWire(double end[2][3], float vel[2][3]) override; - virtual void releaseWire(void); + void releaseWire(void) override; - virtual float getCatapult(const double pos[3], - double end[2][3], float vel[2][3]); + float getCatapult(const double pos[3], + double end[2][3], float vel[2][3]) override; void setTimeOffset(double toff); diff --git a/src/FDM/YASim/Ground.cpp b/src/FDM/YASim/Ground.cpp index af01a065c..47274aaa7 100644 --- a/src/FDM/YASim/Ground.cpp +++ b/src/FDM/YASim/Ground.cpp @@ -8,14 +8,6 @@ #include "Ground.hpp" namespace yasim { -Ground::Ground() -{ -} - -Ground::~Ground() -{ -} - void Ground::getGroundPlane(const double pos[3], double plane[4], float vel[3], unsigned int &body) @@ -47,8 +39,7 @@ void Ground::getGroundPlane(const double pos[3], bool Ground::getBody(double t, double bodyToWorld[16], double linearVel[3], double angularVel[3], unsigned int &body) { - return getBody(t, bodyToWorld, linearVel, - angularVel, body); + return false; } bool Ground::caughtWire(const double pos[4][3]) diff --git a/src/FDM/YASim/Ground.hpp b/src/FDM/YASim/Ground.hpp index 433caca76..52d6284b0 100644 --- a/src/FDM/YASim/Ground.hpp +++ b/src/FDM/YASim/Ground.hpp @@ -8,8 +8,7 @@ namespace yasim { class Ground { public: - Ground(); - virtual ~Ground(); + virtual ~Ground() = default; virtual void getGroundPlane(const double pos[3], double plane[4], float vel[3], @@ -20,8 +19,8 @@ public: const simgear::BVHMaterial **material, unsigned int &body); - virtual bool getBody(double t, double bodyToWorld[16], double linearVel[3], - double angularVel[3], unsigned int &id); + virtual bool getBody(double t, double bodyToWorld[16], double linearVel[3], + double angularVel[3], unsigned int &id); virtual bool caughtWire(const double pos[4][3]); From d4b1e42b834fdd47c6bf9a5b8208b82e869667a8 Mon Sep 17 00:00:00 2001 From: Lars Toenning Date: Wed, 25 Mar 2020 17:16:29 +0100 Subject: [PATCH 93/98] Fix playback duration during replay Fixes #1856 Playback starts at endTime - duration if checkbox is set to active. Duration and checkbox status will be saved for umcoming sessions. --- src/Aircraft/replay.cxx | 17 +++++++++-------- src/Aircraft/replay.hxx | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Aircraft/replay.cxx b/src/Aircraft/replay.cxx index 731a9349c..504929b9b 100644 --- a/src/Aircraft/replay.cxx +++ b/src/Aircraft/replay.cxx @@ -139,12 +139,13 @@ FGReplay::clear() void FGReplay::init() { - disable_replay = fgGetNode("/sim/replay/disable", true); - replay_master = fgGetNode("/sim/replay/replay-state", true); - replay_time = fgGetNode("/sim/replay/time", true); - replay_time_str = fgGetNode("/sim/replay/time-str", true); - replay_looped = fgGetNode("/sim/replay/looped", true); - speed_up = fgGetNode("/sim/speed-up", true); + disable_replay = fgGetNode("/sim/replay/disable", true); + replay_master = fgGetNode("/sim/replay/replay-state", true); + replay_time = fgGetNode("/sim/replay/time", true); + replay_time_str = fgGetNode("/sim/replay/time-str", true); + replay_looped = fgGetNode("/sim/replay/looped", true); + replay_duration_act = fgGetNode("/sim/replay/duration-act", true); + speed_up = fgGetNode("/sim/speed-up", true); // alias to keep backward compatibility fgGetNode("/sim/freeze/replay-state", true)->alias(replay_master); @@ -431,8 +432,8 @@ FGReplay::update( double dt ) fgSetDouble( "/sim/replay/start-time", startTime ); fgSetDouble( "/sim/replay/end-time", endTime ); double duration = 0; - if (replay_looped->getBoolValue()) - fgGetDouble("/sim/replay/duration"); + if (replay_duration_act->getBoolValue()) + duration = fgGetDouble("/sim/replay/duration"); if( duration && (duration < (endTime - startTime)) ) { current_time = endTime - duration; } else { diff --git a/src/Aircraft/replay.hxx b/src/Aircraft/replay.hxx index b0315cd65..7377ef7ab 100644 --- a/src/Aircraft/replay.hxx +++ b/src/Aircraft/replay.hxx @@ -118,6 +118,7 @@ private: SGPropertyNode_ptr replay_time; SGPropertyNode_ptr replay_time_str; SGPropertyNode_ptr replay_looped; + SGPropertyNode_ptr replay_duration_act; SGPropertyNode_ptr speed_up; double m_high_res_time; // default: 60 secs of high res data From a27dfe1feb8d3b5bae957531f1a8e634b3b5b8ef Mon Sep 17 00:00:00 2001 From: Erik Hofman Date: Thu, 26 Mar 2020 09:55:11 +0100 Subject: [PATCH 94/98] Move more modern C++ idioms and use std::unique_ptr for moving data ownership around --- src/Sound/VoiceSynthesizer.cxx | 6 +++++- src/Sound/beacon.cxx | 32 ++++++++++++++++++++------------ src/Sound/morse.cxx | 9 ++++++--- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/Sound/VoiceSynthesizer.cxx b/src/Sound/VoiceSynthesizer.cxx index befb0adbf..7ae061214 100644 --- a/src/Sound/VoiceSynthesizer.cxx +++ b/src/Sound/VoiceSynthesizer.cxx @@ -123,7 +123,11 @@ SGSoundSample * FLITEVoiceSynthesizer::synthesize(const std::string & text, doub ALsizei rate, count; if ( FALSE == Flite_HTS_Engine_synthesize_samples_mono16(_engine, text.c_str(), &data, &count, &rate)) return NULL; - return new SGSoundSample(&data, + auto buf = std::unique_ptr{ + reinterpret_cast( data ), + free + }; + return new SGSoundSample(buf, count * sizeof(short), rate, SG_SAMPLE_MONO16); diff --git a/src/Sound/beacon.cxx b/src/Sound/beacon.cxx index 46f0013a4..90dcf0074 100644 --- a/src/Sound/beacon.cxx +++ b/src/Sound/beacon.cxx @@ -43,24 +43,32 @@ bool FGBeacon::init() { unsigned char *ptr; size_t i, len; - const unsigned char* inner_buf = (const unsigned char*)malloc( INNER_SIZE ); - const unsigned char* middle_buf = (const unsigned char*)malloc(MIDDLE_SIZE); - const unsigned char* outer_buf = (const unsigned char*)malloc( OUTER_SIZE ); + auto inner_buf = std::unique_ptr{ + reinterpret_cast( malloc( INNER_SIZE ) ), + free + }; + auto middle_buf = std::unique_ptr{ + reinterpret_cast( malloc( MIDDLE_SIZE ) ), + free + }; + auto outer_buf = std::unique_ptr{ + reinterpret_cast( malloc( OUTER_SIZE ) ), + free + }; // Make inner marker beacon sound len= (int)(INNER_DIT_LEN / 2.0 ); unsigned char inner_dit[INNER_DIT_LEN]; - make_tone( inner_dit, INNER_FREQ, len, INNER_DIT_LEN, - TRANSITION_BYTES ); + make_tone( inner_dit, INNER_FREQ, len, INNER_DIT_LEN, TRANSITION_BYTES ); - ptr = (unsigned char*)inner_buf; + ptr = inner_buf.get(); for ( i = 0; i < 6; ++i ) { memcpy( ptr, inner_dit, INNER_DIT_LEN ); ptr += INNER_DIT_LEN; } try { - inner = new SGSoundSample( &inner_buf, INNER_SIZE, BYTES_PER_SECOND ); + inner = new SGSoundSample( inner_buf, INNER_SIZE, BYTES_PER_SECOND ); inner->set_reference_dist( 10.0 ); inner->set_max_dist( 20.0 ); @@ -75,12 +83,12 @@ bool FGBeacon::init() { make_tone( middle_dah, MIDDLE_FREQ, len, MIDDLE_DAH_LEN, TRANSITION_BYTES ); - ptr = (unsigned char*)middle_buf; + ptr = (unsigned char*)middle_buf.get(); memcpy( ptr, middle_dit, MIDDLE_DIT_LEN ); ptr += MIDDLE_DIT_LEN; memcpy( ptr, middle_dah, MIDDLE_DAH_LEN ); - middle = new SGSoundSample( &middle_buf, MIDDLE_SIZE, BYTES_PER_SECOND); + middle = new SGSoundSample( middle_buf, MIDDLE_SIZE, BYTES_PER_SECOND); middle->set_reference_dist( 10.0 ); middle->set_max_dist( 20.0 ); @@ -89,13 +97,13 @@ bool FGBeacon::init() { unsigned char outer_dah[OUTER_DAH_LEN]; make_tone( outer_dah, OUTER_FREQ, len, OUTER_DAH_LEN, TRANSITION_BYTES ); - - ptr = (unsigned char*)outer_buf; + + ptr = (unsigned char*)outer_buf.get(); memcpy( ptr, outer_dah, OUTER_DAH_LEN ); ptr += OUTER_DAH_LEN; memcpy( ptr, outer_dah, OUTER_DAH_LEN ); - outer = new SGSoundSample( &outer_buf, OUTER_SIZE, BYTES_PER_SECOND ); + outer = new SGSoundSample( outer_buf, OUTER_SIZE, BYTES_PER_SECOND ); outer->set_reference_dist( 10.0 ); outer->set_max_dist( 20.0 ); } catch ( sg_io_exception &e ) { diff --git a/src/Sound/morse.cxx b/src/Sound/morse.cxx index 9e4d5eced..ffef13c59 100644 --- a/src/Sound/morse.cxx +++ b/src/Sound/morse.cxx @@ -190,10 +190,13 @@ SGSoundSample *FGMorse::make_ident( const std::string& id, const int freq ) { length += 2 * SPACE_SIZE; // 2. Allocate space for the message - const unsigned char* buffer = (const unsigned char *)malloc(length); + auto buffer = std::unique_ptr{ + reinterpret_cast( malloc( length ) ), + free + }; // 3. Assemble the message; - unsigned char *buf_ptr = (unsigned char*)buffer; + unsigned char *buf_ptr = buffer.get(); for ( i = 0; i < (int)id.length(); ++i ) { if ( idptr[i] >= 'A' && idptr[i] <= 'Z' ) { @@ -232,7 +235,7 @@ SGSoundSample *FGMorse::make_ident( const std::string& id, const int freq ) { buf_ptr += SPACE_SIZE; // 4. create the simple sound and return - SGSoundSample *sample = new SGSoundSample( &buffer, length, + SGSoundSample *sample = new SGSoundSample( buffer, length, BYTES_PER_SECOND ); sample->set_reference_dist( 10.0 ); From 0483f2996ac2761d3705148947db97644efd1038 Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 26 Mar 2020 16:56:01 +0000 Subject: [PATCH 95/98] Simplify Launcher thumbnail display code Remove old code paths in the AircraftModel/LocalCache, especially --- src/GUI/AircraftModel.cxx | 116 +++++---------------------------- src/GUI/AircraftModel.hxx | 2 - src/GUI/LocalAircraftCache.cxx | 17 ----- src/GUI/LocalAircraftCache.hxx | 3 - src/GUI/ThumbnailImageItem.cxx | 45 +++++++------ 5 files changed, 41 insertions(+), 142 deletions(-) diff --git a/src/GUI/AircraftModel.cxx b/src/GUI/AircraftModel.cxx index 5a1d799ea..ab7b23316 100644 --- a/src/GUI/AircraftModel.cxx +++ b/src/GUI/AircraftModel.cxx @@ -38,8 +38,6 @@ #include "QmlAircraftInfo.hxx" -const int STANDARD_THUMBNAIL_HEIGHT = 128; - using namespace simgear::pkg; bool isPackageFailure(Delegate::StatusCode status) @@ -70,18 +68,7 @@ public: } protected: - void catalogRefreshed(CatalogRef aCatalog, StatusCode aReason) override - { - if (aReason == STATUS_IN_PROGRESS) { - // nothing to do - } else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) { - m_model->refreshPackages(); - } else { - qWarning() << "failed refresh of" - << QString::fromStdString(aCatalog->url()) << ":" << aReason << endl; - } - } - + void catalogRefreshed(CatalogRef aCatalog, StatusCode aReason) override; void startInstall(InstallRef aInstall) override { QModelIndex mi(indexForPackage(aInstall->package())); @@ -128,44 +115,6 @@ protected: m_model->dataChanged(mi, mi); } - - virtual void dataForThumbnail(const std::string& aThumbnailUrl, - size_t length, const uint8_t* bytes) override - { - QImage img = QImage::fromData(QByteArray::fromRawData(reinterpret_cast(bytes), length)); - if (img.isNull()) { - qWarning() << "failed to load image data for URL:" << - QString::fromStdString(aThumbnailUrl); - return; - } - - QPixmap pix = QPixmap::fromImage(img); - if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) { - pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT, Qt::SmoothTransformation); - } - - QString url = QString::fromStdString(aThumbnailUrl); - m_model->m_downloadedPixmapCache.insert(url, pix); - - // notify any affected items. Linear scan here avoids another map/dict structure. - for (auto pkg : m_model->m_packages) { - const size_t variantCount = pkg->variants().size(); - bool notifyChanged = false; - - for (size_t v=0; v < variantCount; ++v) { - const Package::Thumbnail& thumb(pkg->thumbnailForVariant(v)); - if (thumb.url == aThumbnailUrl) { - notifyChanged = true; - } - } - - if (notifyChanged) { - QModelIndex mi = indexForPackage(pkg); - m_model->dataChanged(mi, mi); - } - } // of packages iteration - } - private: QModelIndex indexForPackage(const PackageRef& ref) const { @@ -182,6 +131,22 @@ private: AircraftItemModel* m_model; }; +void PackageDelegate::catalogRefreshed(CatalogRef aCatalog, StatusCode aReason) +{ + if (aReason == STATUS_IN_PROGRESS) { + // nothing to do + } else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) { + m_model->refreshPackages(); + } else { + qWarning() << "failed refresh of" + << QString::fromStdString(aCatalog->url()) << ":" << aReason << endl; + } +} + +////////////////////////////////////////////////////////////////////////// +/// \brief AircraftItemModel::AircraftItemModel +/// \param pr +/// AircraftItemModel::AircraftItemModel(QObject* pr) : QAbstractListModel(pr) { @@ -322,16 +287,12 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta } return item->description; - } else if (role == Qt::DecorationRole) { - return item->thumbnail(); } else if (role == AircraftPathRole) { return item->path; } else if (role == AircraftAuthorsRole) { return item->authors; } else if ((role >= AircraftRatingRole) && (role < AircraftVariantDescriptionRole)) { return item->ratings[role - AircraftRatingRole]; - } else if (role == AircraftThumbnailRole) { - return item->thumbnail(); } else if (role == AircraftPackageIdRole) { // can we fake an ID? otherwise fall through to a null variant } else if (role == AircraftPackageStatusRole) { @@ -367,10 +328,6 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const DelegateState& state, int role) const { - if (role == Qt::DecorationRole) { - role = AircraftThumbnailRole; - } - if (role >= AircraftVariantDescriptionRole) { int variantIndex = role - AircraftVariantDescriptionRole; QString desc = QString::fromStdString(item->nameForVariant(variantIndex)); @@ -418,8 +375,6 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const Delega // this value wants the number of aditional variants, i.e not // including the primary. Hence the -1 term. return static_cast(item->variants().size() - 1); - } else if (role == AircraftThumbnailRole) { - return packageThumbnail(item, state); } else if (role == AircraftAuthorsRole) { std::string authors = item->getLocalisedProp("author", state.variant); if (!authors.empty()) { @@ -453,42 +408,6 @@ QVariant AircraftItemModel::packageRating(const PackageRef& p, int ratingIndex) return LocalAircraftCache::ratingFromProperties(p->properties()->getChild("rating"), ratingIndex); } -QVariant AircraftItemModel::packageThumbnail(PackageRef p, const DelegateState& ds, bool download) const -{ - const Package::Thumbnail& thumb(p->thumbnailForVariant(ds.variant)); - if (thumb.url.empty()) { - return QVariant(); - } - - QString urlQString(QString::fromStdString(thumb.url)); - if (m_downloadedPixmapCache.contains(urlQString)) { - // cache hit, easy - return m_downloadedPixmapCache.value(urlQString); - } - -// check the on-disk store. - InstallRef ex = p->existingInstall(); - if (ex.valid()) { - SGPath thumbPath = ex->path() / thumb.path; - if (thumbPath.exists()) { - QPixmap pix; - pix.load(QString::fromStdString(thumbPath.utf8Str())); - // resize to the standard size - if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) { - pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT); - } - m_downloadedPixmapCache[urlQString] = pix; - return pix; - } - } // of have existing install - - if (download) { - m_packageRoot->requestThumbnailData(thumb.url); - } - - return QVariant(); -} - bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value, int role) { int row = index.row(); @@ -517,7 +436,6 @@ QHash AircraftItemModel::roleNames() const result[AircraftAuthorsRole] = "authors"; result[AircraftVariantCountRole] = "variantCount"; result[AircraftLongDescriptionRole] = "description"; - result[AircraftThumbnailRole] = "thumbnail"; result[AircraftPackageSizeRole] = "packageSizeBytes"; result[AircraftPackageStatusRole] = "packageStatus"; diff --git a/src/GUI/AircraftModel.hxx b/src/GUI/AircraftModel.hxx index a0f12cf17..f1b8f3bac 100644 --- a/src/GUI/AircraftModel.hxx +++ b/src/GUI/AircraftModel.hxx @@ -50,7 +50,6 @@ const int AircraftURIRole = Qt::UserRole + 14; const int AircraftIsHelicopterRole = Qt::UserRole + 16; const int AircraftIsSeaplaneRole = Qt::UserRole + 17; const int AircraftPackageRefRole = Qt::UserRole + 19; -const int AircraftThumbnailRole = Qt::UserRole + 20; const int AircraftStatusRole = Qt::UserRole + 22; const int AircraftMinVersionRole = Qt::UserRole + 23; @@ -153,7 +152,6 @@ private: simgear::pkg::RootRef m_packageRoot; simgear::pkg::PackageList m_packages; - mutable QHash m_downloadedPixmapCache; int m_cachedLocalAircraftCount = 0; }; diff --git a/src/GUI/LocalAircraftCache.cxx b/src/GUI/LocalAircraftCache.cxx index d65464b1d..a3d88ae20 100644 --- a/src/GUI/LocalAircraftCache.cxx +++ b/src/GUI/LocalAircraftCache.cxx @@ -177,23 +177,6 @@ void AircraftItem::toDataStream(QDataStream& ds) const ds << tags; } -QPixmap AircraftItem::thumbnail(bool loadIfRequired) const -{ - if (m_thumbnail.isNull() && loadIfRequired) { - QFileInfo info(path); - QDir dir = info.dir(); - if (dir.exists(thumbnailPath)) { - m_thumbnail.load(dir.filePath(thumbnailPath)); - // resize to the standard size - if (m_thumbnail.height() > STANDARD_THUMBNAIL_HEIGHT) { - m_thumbnail = m_thumbnail.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT, Qt::SmoothTransformation); - } - } - } - - return m_thumbnail; -} - int AircraftItem::indexOfVariant(QUrl uri) const { const QString path = uri.toLocalFile(); diff --git a/src/GUI/LocalAircraftCache.hxx b/src/GUI/LocalAircraftCache.hxx index e789a3844..3efb1f978 100644 --- a/src/GUI/LocalAircraftCache.hxx +++ b/src/GUI/LocalAircraftCache.hxx @@ -49,8 +49,6 @@ struct AircraftItem void toDataStream(QDataStream& ds) const; - QPixmap thumbnail(bool loadIfRequired = true) const; - int indexOfVariant(QUrl uri) const; bool excluded = false; @@ -75,7 +73,6 @@ struct AircraftItem QUrl supportUrl; QVariant status(int variant); private: - mutable QPixmap m_thumbnail; }; class LocalAircraftCache : public QObject diff --git a/src/GUI/ThumbnailImageItem.cxx b/src/GUI/ThumbnailImageItem.cxx index b4a24be6a..a2444470e 100644 --- a/src/GUI/ThumbnailImageItem.cxx +++ b/src/GUI/ThumbnailImageItem.cxx @@ -27,30 +27,33 @@ public: void startInstall(pkg::InstallRef) override {} void installProgress(pkg::InstallRef, unsigned int, unsigned int) override {} void finishInstall(pkg::InstallRef, StatusCode ) override {} - void dataForThumbnail(const std::string& aThumbnailUrl, - size_t length, const uint8_t* bytes) override - { - if (aThumbnailUrl != owner->url().toString().toStdString()) { - return; - } - - QImage img = QImage::fromData(QByteArray::fromRawData(reinterpret_cast(bytes), length)); - if (img.isNull()) { - if (length > 0) { - // warn if we had valid bytes but couldn't load it, i.e corrupted data or similar - qWarning() << "failed to load image data for URL:" << QString::fromStdString(aThumbnailUrl); - owner->clearImage(); - } - return; - } - - owner->setImage(img); - } + size_t length, const uint8_t* bytes) override; ThumbnailImageItem* owner; }; +void ThumbnailImageItem::ThumbnailPackageDelegate::dataForThumbnail(const std::string& aThumbnailUrl, + size_t length, const uint8_t* bytes) +{ + if (aThumbnailUrl != owner->url().toString().toStdString()) { + return; + } + + const auto iLength = static_cast(length); + QImage img = QImage::fromData(QByteArray::fromRawData(reinterpret_cast(bytes), iLength)); + if (img.isNull()) { + if (length > 0) { + // warn if we had valid bytes but couldn't load it, i.e corrupted data or similar + qWarning() << "failed to load image data for URL:" << QString::fromStdString(aThumbnailUrl); + owner->clearImage(); + } + return; + } + + owner->setImage(img); +} + ThumbnailImageItem::ThumbnailImageItem(QQuickItem* parent) : QQuickItem(parent), m_delegate(new ThumbnailPackageDelegate(this)), @@ -81,7 +84,7 @@ QSGNode *ThumbnailImageItem::updatePaintNode(QSGNode* oldNode, QQuickItem::Updat textureNode->setOwnsTexture(true); } - QSGTexture* tex = window()->createTextureFromImage(m_image); + QSGTexture* tex = window()->createTextureFromImage(m_image, QQuickWindow::TextureIsOpaque); textureNode->setTexture(tex); textureNode->markDirty(QSGBasicGeometryNode::DirtyMaterial); m_imageDirty = false; @@ -123,7 +126,7 @@ void ThumbnailImageItem::setAircraftUri(QString uri) pkg::Root* root = globals->packageRoot(); pkg::PackageRef package = root->getPackageById(packageId); if (package) { - int variant = package->indexOfVariant(packageId); + auto variant = package->indexOfVariant(packageId); const auto thumbnail = package->thumbnailForVariant(variant); m_imageUrl = QUrl(QString::fromStdString(thumbnail.url)); if (m_imageUrl.isValid()) { From 8ddf4d6699698b195baa8ddc81e029706839d3de Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 26 Mar 2020 17:10:14 +0000 Subject: [PATCH 96/98] Fix reset-data-path dialog logic Use correct root path key in QSettings, everywhere Use the direct Win32 API until Qt wrapper is fixed. --- src/GUI/LauncherController.cxx | 10 +++------- src/GUI/QtLauncher.cxx | 13 ++++++++----- src/GUI/SetupRootDialog.cxx | 20 +++++++++++++++----- src/GUI/SetupRootDialog.hxx | 4 ++++ src/Main/main.cxx | 1 - 5 files changed, 30 insertions(+), 18 deletions(-) mode change 100644 => 100755 src/GUI/SetupRootDialog.cxx mode change 100644 => 100755 src/Main/main.cxx diff --git a/src/GUI/LauncherController.cxx b/src/GUI/LauncherController.cxx index 84322c053..bd9be2439 100644 --- a/src/GUI/LauncherController.cxx +++ b/src/GUI/LauncherController.cxx @@ -53,6 +53,7 @@ #include "FlightPlanController.hxx" #include "ModelDataExtractor.hxx" #include "CarriersLocationModel.hxx" +#include "SetupRootDialog.hxx" using namespace simgear::pkg; @@ -767,7 +768,7 @@ void LauncherController::requestChangeDataPath() { QString currentLocText; QSettings settings; - QString root = settings.value("fg-root").toString(); + QString root = settings.value(SetupRootDialog::rootPathKey()).toString(); if (root.isNull()) { currentLocText = tr("Currently the built-in data files are being used"); } @@ -791,12 +792,7 @@ void LauncherController::requestChangeDataPath() return; } - { - QSettings settings; - // set the option to the magic marker value - settings.setValue("fg-root", "!ask"); - } // scope the ensure settings are written nicely - + SetupRootDialog::askRootOnNextLaunch(); flightgear::restartTheApp(); } diff --git a/src/GUI/QtLauncher.cxx b/src/GUI/QtLauncher.cxx index 0f45e10e7..c1e18e5ce 100644 --- a/src/GUI/QtLauncher.cxx +++ b/src/GUI/QtLauncher.cxx @@ -356,19 +356,22 @@ void initQSettings() bool checkKeyboardModifiersForSettingFGRoot() { initQSettings(); - +#if defined(Q_OS_WIN) + const auto altState = GetAsyncKeyState(VK_MENU); + const auto shiftState = GetAsyncKeyState(VK_SHIFT); + if ((altState < 0) || (shiftState < 0)) +#else Qt::KeyboardModifiers mods = qApp->queryKeyboardModifiers(); - if (mods & (Qt::AltModifier | Qt::ShiftModifier)) { + if (mods & (Qt::AltModifier | Qt::ShiftModifier)) +#endif + { qWarning() << "Alt/shift pressed during launch"; - QSettings settings; - settings.setValue("fg-root", "!ask"); return true; } return false; } - void restartTheApp() { QStringList fgArgs; diff --git a/src/GUI/SetupRootDialog.cxx b/src/GUI/SetupRootDialog.cxx old mode 100644 new mode 100755 index 756380878..685db1e61 --- a/src/GUI/SetupRootDialog.cxx +++ b/src/GUI/SetupRootDialog.cxx @@ -40,7 +40,9 @@ #include #include -static QString rootPathKey() +#include "QtLauncher.hxx" + +QString SetupRootDialog::rootPathKey() { // return a settings key like fg-root-2018-3-0 return QString("fg-root-") + QString(FLIGHTGEAR_VERSION).replace('.', '-'); @@ -99,7 +101,8 @@ SGPath SetupRootDialog::restoreUserSelectedRoot() { QSettings settings; QString path = settings.value(rootPathKey()).toString(); - if (path == "!ask") { + bool ask = flightgear::checkKeyboardModifiersForSettingFGRoot(); + if (ask || (path == QStringLiteral("!ask"))) { bool ok = runDialog(ManualChoiceRequested); Q_ASSERT(ok); // run dialog either exit()s or sets fg_root, so this @@ -108,7 +111,7 @@ SGPath SetupRootDialog::restoreUserSelectedRoot() } if (path.isEmpty()) { - return std::string(); // use the default path + return SGPath{}; // use the default path } if (validatePath(path) && validateVersion(path)) { @@ -118,7 +121,7 @@ SGPath SetupRootDialog::restoreUserSelectedRoot() // let's see if the default root is acceptable, in which case we will // switch to it. (This gives a more friendly upgrade experience). if (defaultRootAcceptable()) { - return std::string(); // use the default path + return SGPath{}; // use the default path } // okay, we don't have an acceptable FG_DATA anywhere we can find, we @@ -131,6 +134,13 @@ SGPath SetupRootDialog::restoreUserSelectedRoot() } } +void SetupRootDialog::askRootOnNextLaunch() +{ + QSettings settings; + // set the option to the magic marker value + settings.setValue(rootPathKey(), "!ask"); +} + bool SetupRootDialog::validatePath(QString path) { // check assorted files exist in the root location, to avoid any chance of @@ -216,7 +226,7 @@ void SetupRootDialog::onUseDefaults() m_browsedPath = QString::fromStdString(r.utf8Str()); globals->set_fg_root(r); QSettings settings; - settings.remove("fg-root"); // remove any setting + settings.remove(rootPathKey()); // remove any setting accept(); } diff --git a/src/GUI/SetupRootDialog.hxx b/src/GUI/SetupRootDialog.hxx index d294c1938..79f758e3f 100644 --- a/src/GUI/SetupRootDialog.hxx +++ b/src/GUI/SetupRootDialog.hxx @@ -41,6 +41,10 @@ public: static bool runDialog(bool usingDefaultRoot); static SGPath restoreUserSelectedRoot(); + + static void askRootOnNextLaunch(); + + static QString rootPathKey(); private slots: void onBrowse(); diff --git a/src/Main/main.cxx b/src/Main/main.cxx old mode 100644 new mode 100755 index 6bb2828d6..9727d9a29 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -629,7 +629,6 @@ int fgMainInit( int argc, char **argv ) #if defined(HAVE_QT) if (showLauncher) { - flightgear::checkKeyboardModifiersForSettingFGRoot(); if (!flightgear::runLauncherDialog()) { return EXIT_SUCCESS; } From 3060cf10c63ade328fca07c7793bff2ad482c9cb Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 31 Mar 2020 13:56:01 +0100 Subject: [PATCH 97/98] Remove a logging call. --- src/Airports/runwayprefs.cxx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Airports/runwayprefs.cxx b/src/Airports/runwayprefs.cxx index 3353820a7..76fec534b 100644 --- a/src/Airports/runwayprefs.cxx +++ b/src/Airports/runwayprefs.cxx @@ -148,15 +148,10 @@ void RunwayList::set(const std::string & tp, const std::string & lst) // timeOffsetInDays = weekday - currTimeDate->getGmt()->tm_wday; // timeCopy = timeCopy.substr(2,timeCopy.length()); type = tp; - - - - BOOST_FOREACH(std::string s, strutils::split(lst, ",")) { - std::string ident = strutils::strip(s); - + for (const auto s : strutils::split(lst, ",")) { + auto ident = strutils::strip(s); // http://code.google.com/p/flightgear-bugs/issues/detail?id=1137 if ((ident.size() < 2) || !isdigit(ident[1])) { - SG_LOG(SG_GENERAL, SG_INFO, "RunwayList::set: padding runway ident '" << ident << "'"); ident = "0" + ident; } From 2904321959a48856d865d5303cf4ca89bdbb81fd Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 1 Apr 2020 10:47:29 +0100 Subject: [PATCH 98/98] Launcher: favourite aircraft support --- src/GUI/AircraftModel.cxx | 45 ++++++++++++++++-- src/GUI/AircraftModel.hxx | 9 +++- src/GUI/AircraftSearchFilterModel.cxx | 16 +++++++ src/GUI/AircraftSearchFilterModel.hxx | 3 ++ src/GUI/CMakeLists.txt | 2 + src/GUI/HoverArea.cxx | 33 +++++++++++++ src/GUI/HoverArea.hxx | 31 ++++++++++++ src/GUI/LauncherController.cxx | 5 ++ src/GUI/LauncherController.hxx | 3 ++ .../assets/icons8-christmas-star-filled.png | Bin 0 -> 612 bytes .../assets/icons8-christmas-star-outline.png | Bin 0 -> 497 bytes src/GUI/qml/AircraftCompactDelegate.qml | 38 +++++++++++---- src/GUI/qml/AircraftGridDelegate.qml | 18 +++++-- src/GUI/qml/AircraftList.qml | 25 ++++++++++ src/GUI/qml/FavouriteToggleButton.qml | 32 +++++++++++++ src/GUI/resources.qrc | 3 ++ 16 files changed, 248 insertions(+), 15 deletions(-) create mode 100644 src/GUI/HoverArea.cxx create mode 100644 src/GUI/HoverArea.hxx create mode 100644 src/GUI/assets/icons8-christmas-star-filled.png create mode 100644 src/GUI/assets/icons8-christmas-star-outline.png create mode 100644 src/GUI/qml/FavouriteToggleButton.qml diff --git a/src/GUI/AircraftModel.cxx b/src/GUI/AircraftModel.cxx index ab7b23316..22c8c301b 100644 --- a/src/GUI/AircraftModel.cxx +++ b/src/GUI/AircraftModel.cxx @@ -23,6 +23,7 @@ #include #include #include +#include // Simgear #include @@ -157,6 +158,8 @@ AircraftItemModel::AircraftItemModel(QObject* pr) : this, &AircraftItemModel::onScanAddedItems); connect(cache, &LocalAircraftCache::cleared, this, &AircraftItemModel::onLocalCacheCleared); + + loadFavourites(); } AircraftItemModel::~AircraftItemModel() @@ -238,6 +241,12 @@ QVariant AircraftItemModel::data(const QModelIndex& index, int role) const return m_delegateStates.at(row).variant; } + if (role == AircraftIsFavouriteRole) { + // recursive call here, hope that's okay + const auto uri = data(index, AircraftURIRole).toUrl(); + return m_favourites.contains(uri); + } + if (row >= m_cachedLocalAircraftCount) { quint32 packageIndex = static_cast(row - m_cachedLocalAircraftCount); const PackageRef& pkg(m_packages[packageIndex]); @@ -421,6 +430,18 @@ bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value, m_delegateStates[row].variant = newValue; 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); + } + + saveFavourites(); + emit dataChanged(index, index); } return false; @@ -441,6 +462,7 @@ QHash AircraftItemModel::roleNames() const result[AircraftInstallDownloadedSizeRole] = "downloadedBytes"; result[AircraftVariantRole] = "activeVariant"; + result[AircraftIsFavouriteRole] = "favourite"; result[AircraftStatusRole] = "aircraftStatus"; result[AircraftMinVersionRole] = "requiredFGVersion"; @@ -560,7 +582,7 @@ QString AircraftItemModel::nameForAircraftURI(QUrl uri) const QString ident = uri.path(); PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString()); if (pkg) { - int variantIndex = pkg->indexOfVariant(ident.toStdString()); + const auto variantIndex = pkg->indexOfVariant(ident.toStdString()); return QString::fromStdString(pkg->nameForVariant(variantIndex)); } } else { @@ -572,7 +594,7 @@ QString AircraftItemModel::nameForAircraftURI(QUrl uri) const void AircraftItemModel::onScanAddedItems(int addedCount) { - Q_UNUSED(addedCount); + Q_UNUSED(addedCount) const auto items = LocalAircraftCache::instance()->allItems(); const int newItemCount = items.size() - m_cachedLocalAircraftCount; const int firstRow = m_cachedLocalAircraftCount; @@ -631,7 +653,7 @@ bool AircraftItemModel::isIndexRunnable(const QModelIndex& index) const return true; // local file, always runnable } - quint32 packageIndex = index.row() - m_cachedLocalAircraftCount; + quint32 packageIndex = static_cast(index.row() - m_cachedLocalAircraftCount); const PackageRef& pkg(m_packages[packageIndex]); InstallRef ex = pkg->existingInstall(); if (!ex.valid()) { @@ -641,4 +663,21 @@ 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); +} diff --git a/src/GUI/AircraftModel.hxx b/src/GUI/AircraftModel.hxx index f1b8f3bac..5590f5cf7 100644 --- a/src/GUI/AircraftModel.hxx +++ b/src/GUI/AircraftModel.hxx @@ -50,6 +50,7 @@ const int AircraftURIRole = Qt::UserRole + 14; const int AircraftIsHelicopterRole = Qt::UserRole + 16; const int AircraftIsSeaplaneRole = Qt::UserRole + 17; const int AircraftPackageRefRole = Qt::UserRole + 19; +const int AircraftIsFavouriteRole = Qt::UserRole + 20; const int AircraftStatusRole = Qt::UserRole + 22; const int AircraftMinVersionRole = Qt::UserRole + 23; @@ -145,14 +146,20 @@ private: void installSucceeded(QModelIndex index); void installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason); + void loadFavourites(); + void saveFavourites(); + +private: PackageDelegate* m_delegate = nullptr; QVector m_delegateStates; simgear::pkg::RootRef m_packageRoot; simgear::pkg::PackageList m_packages; - + + QVector m_favourites; int m_cachedLocalAircraftCount = 0; + }; #endif // of FG_GUI_AIRCRAFT_MODEL diff --git a/src/GUI/AircraftSearchFilterModel.cxx b/src/GUI/AircraftSearchFilterModel.cxx index 1ff079814..f4b5f18bc 100644 --- a/src/GUI/AircraftSearchFilterModel.cxx +++ b/src/GUI/AircraftSearchFilterModel.cxx @@ -14,6 +14,8 @@ AircraftProxyModel::AircraftProxyModel(QObject *pr, QAbstractItemModel * source) setSortCaseSensitivity(Qt::CaseInsensitive); setFilterCaseSensitivity(Qt::CaseInsensitive); + + // important we sort on the primary name role and not Qt::DisplayRole // otherwise the aircraft jump when switching variant setSortRole(AircraftVariantDescriptionRole); @@ -113,6 +115,15 @@ void AircraftProxyModel::setHaveUpdateFilterEnabled(bool e) invalidate(); } +void AircraftProxyModel::setShowFavourites(bool e) +{ + if (e == m_onlyShowFavourites) + return; + + m_onlyShowFavourites = e; + invalidate(); +} + bool AircraftProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); @@ -145,6 +156,11 @@ bool AircraftProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour } } + if (m_onlyShowFavourites) { + if (!index.data(AircraftIsFavouriteRole).toBool()) + return false; + } + // if there is no search active, i.e we are browsing, we might apply the // ratings filter. if (m_filterString.isEmpty() && !m_onlyShowInstalled && m_ratingsFilter) { diff --git a/src/GUI/AircraftSearchFilterModel.hxx b/src/GUI/AircraftSearchFilterModel.hxx index f40cb859b..d42e73c0a 100644 --- a/src/GUI/AircraftSearchFilterModel.hxx +++ b/src/GUI/AircraftSearchFilterModel.hxx @@ -60,6 +60,8 @@ public slots: void setInstalledFilterEnabled(bool e); void setHaveUpdateFilterEnabled(bool e); + + void setShowFavourites(bool e); protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; @@ -69,6 +71,7 @@ private: bool m_ratingsFilter = true; bool m_onlyShowInstalled = false; bool m_onlyShowWithUpdate = false; + bool m_onlyShowFavourites = false; QList m_ratings; QString m_filterString; diff --git a/src/GUI/CMakeLists.txt b/src/GUI/CMakeLists.txt index 905881d1b..a3aed0468 100644 --- a/src/GUI/CMakeLists.txt +++ b/src/GUI/CMakeLists.txt @@ -172,6 +172,8 @@ if (HAVE_QT) RouteDiagram.hxx ModelDataExtractor.cxx ModelDataExtractor.hxx + HoverArea.cxx + HoverArea.hxx ) set_property(TARGET fgqmlui PROPERTY AUTOMOC ON) diff --git a/src/GUI/HoverArea.cxx b/src/GUI/HoverArea.cxx new file mode 100644 index 000000000..1fc262fe8 --- /dev/null +++ b/src/GUI/HoverArea.cxx @@ -0,0 +1,33 @@ +#include "HoverArea.hxx" + +#include +#include + +HoverArea::HoverArea() +{ + connect(this, &QQuickItem::windowChanged, [this](QQuickWindow* win) { + if (win) { + win->installEventFilter(this); + } + }); +} + + + +bool HoverArea::eventFilter(QObject *sender, QEvent *event) +{ + Q_UNUSED(sender) + if (event->type() == QEvent::MouseMove) { + QMouseEvent* me = static_cast(event); + const auto local = mapFromScene(me->pos()); + const bool con = contains(local); + if (con != m_containsMouse) { + m_containsMouse = con; + emit containsMouseChanged(con); + } + } + + return false; +} + + diff --git a/src/GUI/HoverArea.hxx b/src/GUI/HoverArea.hxx new file mode 100644 index 000000000..8bea2c7b4 --- /dev/null +++ b/src/GUI/HoverArea.hxx @@ -0,0 +1,31 @@ +#ifndef HOVERAREA_HXX +#define HOVERAREA_HXX + +#include + +class HoverArea : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged); + +public: + HoverArea(); + + bool containsMouse() const + { + return m_containsMouse; + } + +signals: + + void containsMouseChanged(bool containsMouse); + +protected: + bool eventFilter(QObject* sender, QEvent* event) override; + +private: + bool m_containsMouse = false; +}; + +#endif // HOVERAREA_HXX diff --git a/src/GUI/LauncherController.cxx b/src/GUI/LauncherController.cxx index bd9be2439..fb2fe59e7 100644 --- a/src/GUI/LauncherController.cxx +++ b/src/GUI/LauncherController.cxx @@ -54,6 +54,7 @@ #include "ModelDataExtractor.hxx" #include "CarriersLocationModel.hxx" #include "SetupRootDialog.hxx" +#include "HoverArea.hxx" using namespace simgear::pkg; @@ -92,6 +93,9 @@ LauncherController::LauncherController(QObject *parent, QWindow* window) : m_aircraftSearchModel = new AircraftProxyModel(this, m_aircraftModel); + m_favouriteAircraftModel = new AircraftProxyModel(this, m_aircraftModel); + m_favouriteAircraftModel->setShowFavourites(true); + m_aircraftHistory = new RecentAircraftModel(m_aircraftModel, this); connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted, @@ -167,6 +171,7 @@ void LauncherController::initQML() qmlRegisterType("FlightGear", 1, 0, "NavaidDiagram"); qmlRegisterType("FlightGear", 1, 0, "RouteDiagram"); qmlRegisterType("FlightGear", 1, 0, "RadioButtonGroup"); + qmlRegisterType("FlightGear", 1, 0, "HoverArea"); qmlRegisterType("FlightGear", 1, 0, "ModelDataExtractor"); diff --git a/src/GUI/LauncherController.hxx b/src/GUI/LauncherController.hxx index 823a75fb8..8074df0a9 100644 --- a/src/GUI/LauncherController.hxx +++ b/src/GUI/LauncherController.hxx @@ -51,6 +51,7 @@ class LauncherController : public QObject Q_PROPERTY(AircraftProxyModel* aircraftWithUpdatesModel MEMBER m_aircraftWithUpdatesModel CONSTANT) Q_PROPERTY(AircraftProxyModel* browseAircraftModel MEMBER m_browseAircraftModel CONSTANT) Q_PROPERTY(AircraftProxyModel* searchAircraftModel MEMBER m_aircraftSearchModel CONSTANT) + Q_PROPERTY(AircraftProxyModel* favouriteAircraftModel MEMBER m_favouriteAircraftModel CONSTANT) Q_PROPERTY(AircraftItemModel* baseAircraftModel MEMBER m_aircraftModel CONSTANT) @@ -269,6 +270,8 @@ private: AircraftProxyModel* m_aircraftSearchModel; AircraftProxyModel* m_browseAircraftModel; AircraftProxyModel* m_aircraftWithUpdatesModel; + AircraftProxyModel* m_favouriteAircraftModel; + MPServersModel* m_serversModel = nullptr; LocationController* m_location = nullptr; FlightPlanController* m_flightPlan = nullptr; diff --git a/src/GUI/assets/icons8-christmas-star-filled.png b/src/GUI/assets/icons8-christmas-star-filled.png new file mode 100644 index 0000000000000000000000000000000000000000..1c54e821a1acb3d7733e65827e66ca171b5b0d85 GIT binary patch literal 612 zcmV-q0-ODbP)o!Iq$y1B}~LO6C2tKWsSb1}#Bx2xtRZ zH#eMXwwzUij^X*Z(`0hM;qB4)jgp&MpFH)m9&6RWb!Di+9NRIOm=*xYxlhschmF> zEbYPkEv2x((tK9{Kz_>m93wUeEH~i3>|idfPWBb54ZW zik`6!P-^Z*2l#3PPFn|nhp&-dk;9{Kt7>4{H581(hx4ALus?xv(mnC{QN=0000#Ixbk`zu6{LaCqBeaE+Z08* z@dH?&q5{{p%31`X6cI%vCQ?Em{UZn=kZN4a(K}x6+&cr8=z+t$f1c+&?|WwE@SmX) zQ5c;i4X$Ilq^2-V;5&XOd5fZ$K@xGN-O0P~C1dan$J=ReI}$JBS&VPBD|r__N76}L zit$%;w-O)ASsTDm#02`Vf~DZBU>x`H1RD__@w<`H4J=|EyK1%0##DwsmBZPM9E-RK z9U&OPQ&eNVjdz)ot>aPMp)O-MbJG#@$KEGg4DA61uo)+LiJnGR%|QjLq20n@9`Q`L zCU}KY1(Kh{^U!?2*;az8v?K{$OJ8sv`ht^$rrLyi2>nVF7%&2C#- zQ+VIRV1I8lG=&XdG@frWYo{aB?TsplPV7jpUqLVK;a9}nSgQnQr^R89U{I~8&EaRv z|3oCQHW!>klTWh|t~&acKE^p|ITrA@ZvRRdgV~I585bIG#-x2{pEpy+;33}NS^@4P nUf@9)gY#;Ast7tl^Y5_-ripn|KQ01B00000NkvXXu0mjfWxw0H literal 0 HcmV?d00001 diff --git a/src/GUI/qml/AircraftCompactDelegate.qml b/src/GUI/qml/AircraftCompactDelegate.qml index 2d86400b4..b97daee98 100644 --- a/src/GUI/qml/AircraftCompactDelegate.qml +++ b/src/GUI/qml/AircraftCompactDelegate.qml @@ -1,5 +1,7 @@ import QtQuick 2.4 import FlightGear.Launcher 1.0 +import FlightGear 1.0 + import "." Item { @@ -77,19 +79,39 @@ Item { spacing: Style.margin - AircraftVariantChoice { - id: titleBox - + Item { + height: titleBox.height width: parent.width - aircraft: model.uri; - currentIndex: model.activeVariant - onSelected: { - model.activeVariant = index - root.select(model.uri) + FavouriteToggleButton { + id: favourite + checked: model.favourite + anchors.verticalCenter: parent.verticalCenter + onToggle: { + model.favourite = on; + } + } + + AircraftVariantChoice { + id: titleBox + + anchors { + left: favourite.right + leftMargin: Style.margin + right: parent.right + } + + aircraft: model.uri; + currentIndex: model.activeVariant + onSelected: { + model.activeVariant = index + root.select(model.uri) + } } } + + StyledText { id: description width: parent.width diff --git a/src/GUI/qml/AircraftGridDelegate.qml b/src/GUI/qml/AircraftGridDelegate.qml index 99ad7eb33..0d8f4b7f6 100644 --- a/src/GUI/qml/AircraftGridDelegate.qml +++ b/src/GUI/qml/AircraftGridDelegate.qml @@ -1,5 +1,6 @@ import QtQuick 2.4 import FlightGear.Launcher 1.0 +import FlightGear 1.0 import "." Item { @@ -87,10 +88,21 @@ Item { } } - MouseArea { + FavouriteToggleButton { + id: favourite + anchors { + left: parent.left + top: parent.top + margins: Style.margin + } + + visible: hover.containsMouse || model.favourite + checked: model.favourite + onToggle: { model.favourite = on; } + } + + HoverArea { id: hover anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.NoButton } } // of root item diff --git a/src/GUI/qml/AircraftList.qml b/src/GUI/qml/AircraftList.qml index 4b514c53f..516ab8fa1 100644 --- a/src/GUI/qml/AircraftList.qml +++ b/src/GUI/qml/AircraftList.qml @@ -50,6 +50,16 @@ FocusScope active: root.state == "installed" } + TabButton { + id: favouritesButton + text: qsTr("Favourites") + onClicked: { + root.state = "favourites" + root.updateSelectionFromLauncher(); + } + active: root.state == "favourites" + } + TabButton { id: browseButton text: qsTr("Browse") @@ -231,7 +241,22 @@ FocusScope PropertyChanges { target: gridModeToggle; visible: false } + }, + + State { + name: "favourites" + + PropertyChanges { + target: root + __model: _launcher.favouriteAircraftModel + __header: emptyHeader + } + + PropertyChanges { + target: gridModeToggle; visible: true + } } + ] function showDetails(uri) diff --git a/src/GUI/qml/FavouriteToggleButton.qml b/src/GUI/qml/FavouriteToggleButton.qml new file mode 100644 index 000000000..218457a5e --- /dev/null +++ b/src/GUI/qml/FavouriteToggleButton.qml @@ -0,0 +1,32 @@ +import QtQuick 2.4 +import "." + +Item { + id: root + property bool checked: false + + implicitWidth: icon.width + Style.margin + implicitHeight: icon.height + Style.margin + + signal toggle(var on); + + + Image { + id: icon + source: { + var b = mouse.containsMouse ? !root.checked : root.checked; + return b ? "qrc:///favourite-icon-filled" : "qrc:///favourite-icon-outline"; + } + + anchors.centerIn: parent + } + + MouseArea { + id: mouse + anchors.fill: parent + hoverEnabled: true + onClicked: root.toggle(!root.checked); + + cursorShape: Qt.PointingHandCursor + } +} diff --git a/src/GUI/resources.qrc b/src/GUI/resources.qrc index a835b9d97..79ee6f85a 100644 --- a/src/GUI/resources.qrc +++ b/src/GUI/resources.qrc @@ -132,6 +132,9 @@ qml/GridToggleButton.qml qml/EnableDisableButton.qml qml/IconButton.qml + qml/FavouriteToggleButton.qml + assets/icons8-christmas-star-filled.png + assets/icons8-christmas-star-outline.png preview-close.png