FlightGear next
main.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileName: main.cxx
3 * SPDX-FileComment: top level sim routines
4 * SPDX-FileCopyrightText: Copyright (C) 1997 - 2002 Curtis L. Olson - http://www.flightgear.org/~curt
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include <config.h>
9
10#include <simgear/compiler.h>
11#include <simgear/props/props_io.hxx>
12
13#include <iostream>
14
15#include <osg/Camera>
16#include <osg/GraphicsContext>
17#include <osgDB/Registry>
18
19
20// Class references
21#include <simgear/canvas/VGInitOperation.hxx>
22#include <simgear/debug/logdelta.hxx>
23#include <simgear/emesary/Emesary.hxx>
24#include <simgear/emesary/notifications.hxx>
25#include <simgear/io/raw_socket.hxx>
26#include <simgear/math/SGMath.hxx>
27#include <simgear/math/sg_random.hxx>
28#include <simgear/misc/strutils.hxx>
29#include <simgear/nasal/NasalEmesaryInterface.hxx>
30#include <simgear/props/AtomicChangeListener.hxx>
31#include <simgear/props/props.hxx>
32#include <simgear/scene/material/Effect.hxx>
33#include <simgear/scene/material/matlib.hxx>
34#include <simgear/scene/model/modellib.hxx>
35#include <simgear/scene/tsync/terrasync.hxx>
36#include <simgear/structure/commands.hxx>
37#include <simgear/timing/sg_time.hxx>
38
40#include <GUI/MessageBox.hxx>
41#include <GUI/gui.h>
42#include <Main/locale.hxx>
44#include <Scenery/scenery.hxx>
46#include <Time/TimeManager.hxx>
50#include <Viewer/renderer.hxx>
51#include <Viewer/splash.hxx>
52
53#include "fg_commands.hxx"
54#include "fg_init.hxx"
55#include "fg_io.hxx"
56#include "fg_os.hxx"
57#include "fg_props.hxx"
58#include "main.hxx"
59#include "options.hxx"
60#include "positioninit.hxx"
62#include "subsystemFactory.hxx"
63#include "util.hxx"
66
67#include <simgear/embedded_resources/EmbeddedResourceManager.hxx>
68#include <EmbeddedResources/FlightGear-resources.hxx>
69
70#if defined(HAVE_QT)
71#include <GUI/QtLauncher.hxx>
72#endif
73
74#ifdef __OpenBSD__
75#include <sys/resource.h>
76#endif
77
78using namespace flightgear;
79
80using std::cerr;
81using std::vector;
82
83// The atexit() function handler should know when the graphical subsystem
84// is initialized.
85extern int _bootstrap_OSInit;
86
87static SGPropertyNode_ptr frame_signal;
88
89#ifdef NASAL_BACKGROUND_GC_THREAD
90static SGPropertyNode_ptr nasal_gc_threaded;
91static SGPropertyNode_ptr nasal_gc_threaded_wait;
92static SGSharedPtr<simgear::Notifications::MainLoopNotification> mln_begin(new simgear::Notifications::MainLoopNotification(simgear::Notifications::MainLoopNotification::Type::Begin));
93static SGSharedPtr<simgear::Notifications::MainLoopNotification> mln_end(new simgear::Notifications::MainLoopNotification(simgear::Notifications::MainLoopNotification::Type::End));
94static SGSharedPtr<simgear::Notifications::MainLoopNotification> mln_started(new simgear::Notifications::MainLoopNotification(simgear::Notifications::MainLoopNotification::Type::Started));
95static SGSharedPtr<simgear::Notifications::MainLoopNotification> mln_stopped(new simgear::Notifications::MainLoopNotification(simgear::Notifications::MainLoopNotification::Type::Stopped));
96static SGSharedPtr<simgear::Notifications::NasalGarbageCollectionConfigurationNotification> ngccn;
97#endif
98// This method is usually called after OSG has finished rendering a frame in what OSG calls an idle handler and
99// is reposonsible for invoking all of the relevant per frame processing; most of which is handled by subsystems.
100static void fgMainLoop( void )
101{
102#ifdef NASAL_BACKGROUND_GC_THREAD
103 //
104 // the Nasal GC will automatically run when (during allocation) it discovers that more space is needed.
105 // This has a cost of between 5ms and 50ms (depending on the amount of currently active Nasal).
106 // The result is unscheduled and unpredictable pauses during normal operation when the garbage collector
107 // runs; which typically occurs at intervals between 1sec and 20sec.
108 //
109 // The solution to this, which overall increases CPU load, is to use a thread to do this; as Nasal is thread safe
110 // so what we do is to launch the garbage collection at the end of the main loop and then wait for completion at the start of the
111 // next main loop.
112 // So although the overall CPU is increased it has little effect on the frame rate; if anything it is an overall benefit
113 // as there are no unscheduled long duration frames.
114 //
115 // The implementation appears to work fine without waiting for completion at the start of the frame - so
116 // this wait at the start can be disabled by setting the property /sim/nasal-gc-threaded-wait to false.
117
118 // first we see if the config has changed. The notification will return true from SetActive/SetWait when the
119 // value has been changed - and thus we notify the Nasal system that it should configure itself accordingly.
120 auto use_threaded_gc = nasal_gc_threaded->getBoolValue();
121 auto threaded_wait = nasal_gc_threaded_wait->getBoolValue();
122 bool notify_gc_config = false;
123 notify_gc_config = ngccn->SetActive(use_threaded_gc);
124 notify_gc_config |= ngccn->SetWait(threaded_wait);
125 if (notify_gc_config)
126 simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(ngccn);
127
128 simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(mln_begin);
129#endif
130
131 if (sglog().has_popup()) {
132 std::string s = sglog().get_popup();
133 flightgear::modalMessageBox("Alert", s, "");
134 }
135
136 frame_signal->fireValueChanged();
137
138 // Fetch the subsystem manager.
139 auto mgr = globals->get_subsystem_mgr();
140
141 // compute simulated time (allowing for pause, warp, etc) and
142 // real elapsed time
143 double sim_dt, real_dt;
144 mgr->get_subsystem<TimeManager>()->computeTimeDeltas(sim_dt, real_dt);
145
146 // update all subsystems
147 mgr->update(sim_dt);
148
149 // flush commands waiting in the queue
150 SGCommandMgr::instance()->executedQueuedCommands();
151 simgear::AtomicChangeListener::fireChangeListeners();
152
153#ifdef NASAL_BACKGROUND_GC_THREAD
154 simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(mln_end);
155#endif
156}
157
158static void initTerrasync()
159{
160 // add the terrasync root as a data path so data can be retrieved from it
161 // (even if we are in read-only mode)
162 SGPath terraSyncDir(globals->get_terrasync_dir());
163 globals->append_data_path(terraSyncDir, false /* = ahead of FG_ROOT */);
164
165 if (fgGetBool("/sim/fghome-readonly", false)) {
166 SG_LOG(SG_GENERAL, SG_ALERT, "initTerrasync() failing because /sim/fghome-readonly is true");
167 return;
168 }
169
170 // make fg-root dir available so existing Scenery data can be copied, and
171 // hence not downloaded again.
172 fgSetString("/sim/terrasync/installation-dir", (globals->get_fg_root() / "Scenery").utf8Str());
173
174 auto terra_sync = globals->get_subsystem_mgr()->add<simgear::SGTerraSync>();
175 terra_sync->setRoot(globals->get_props());
176
177 terra_sync->bind();
178 terra_sync->init();
179
180 if (fgGetBool("/sim/terrasync/enabled")) {
181 flightgear::addSentryTag("terrasync", "enabled");
182 }
183}
184
185static void fgSetVideoOptions()
186{
187 SGPath userDataPath = globals->get_fg_home();
188 SGPath autosaveFile = globals->autosaveFilePath(userDataPath);
189 if (autosaveFile.exists()) return;
190
191 std::string vendor = fgGetString("/sim/rendering/gl-info/gl-vendor");
192 SGPath path(globals->get_fg_root());
193 path.append("Video");
194 path.append(vendor);
195 if (path.exists())
196 {
197 std::string renderer = fgGetString("/sim/rendering/gl-info/gl-renderer");
198 size_t pos = renderer.find("x86/");
199 if (pos == std::string::npos) {
200 pos = renderer.find('/');
201 }
202 if (pos == std::string::npos) {
203 pos = renderer.find(" (");
204 }
205 if (pos != std::string::npos) {
206 renderer = renderer.substr(0, pos);
207 }
208 path.append(renderer+".xml");
209 if (path.exists()) {
210 SG_LOG(SG_INPUT, SG_INFO, "Reading video settings from " << path);
211 try {
212 SGPropertyNode *r_prop = fgGetNode("/sim/rendering");
213 readProperties(path, r_prop);
214 } catch (sg_exception& e) {
215 SG_LOG(SG_INPUT, SG_WARN, "failed to read video settings:" << e.getMessage()
216 << "(from " << e.getOrigin() << ")");
217 }
218 }
219 }
220}
221
223{
224 flightgear::addSentryTag("gl-version", fgGetString("/sim/rendering/gl-info/gl-version"));
225 flightgear::addSentryTag("gl-renderer", fgGetString("/sim/rendering/gl-info/gl-vendor"));
226 flightgear::addSentryTag("gl-vendor", fgGetString("/sim/rendering/gl-info/gl-renderer"));
227
228#if defined(SG_MAC)
229 // Mac users can't upgrade their drivers, so complaining about
230 // versions doesn't help them much
231 return;
232#endif
233
234 // format of these strings is not standardised, so be careful about
235 // parsing them.
236 std::string versionString(fgGetString("/sim/rendering/gl-info/gl-version"));
237 string_list parts = simgear::strutils::split(versionString);
238 if (parts.size() == 3) {
239 if (parts[1].find("NVIDIA") != std::string::npos) {
240 // driver version number, dot-seperared
241 string_list driverVersion = simgear::strutils::split(parts[2], ".");
242 if (!driverVersion.empty()) {
243 int majorDriverVersion = simgear::strutils::to_int(driverVersion[0]);
244 if (majorDriverVersion < 300) {
245 std::ostringstream ss;
246 ss << "Please upgrade to at least version 300 of the nVidia drivers (installed version is " << parts[2] << ")";
247
248 flightgear::modalMessageBox("Outdated graphics drivers",
249 "FlightGear has detected outdated drivers for your graphics card.",
250 ss.str());
251 }
252 }
253 } // of NVIDIA-style version string
254 } // of three parts
255}
256
257namespace flightgear {
258
260{
261 // stash current frame signal property
262 frame_signal = fgGetNode("/sim/signals/frame", true);
263
264#ifdef NASAL_BACKGROUND_GC_THREAD
265 nasal_gc_threaded = fgGetNode("/sim/nasal-gc-threaded", true);
266 nasal_gc_threaded_wait = fgGetNode("/sim/nasal-gc-threaded-wait", true);
267#endif
268 // init the Emesary receiver for Nasal
269 nasal::initMainLoopRecipient();
270
272}
273
275{
276 nasal::shutdownMainLoopRecipient();
277 frame_signal.reset();
278#ifdef NASAL_BACKGROUND_GC_THREAD
279 nasal_gc_threaded.reset();
280 nasal_gc_threaded_wait.reset();
281#endif
282}
283
284} // namespace flightgear
285
286// This is the top level master main function that is registered as
287// our idle function
288
289// The first few passes take care of initialization things (a couple
290// per pass) and once everything has been initialized fgMainLoop from
291// then on.
292
293static int idle_state = 0;
294
295static void fgIdleFunction ( void ) {
296 // Specify our current idle function state. This is used to run all
297 // our initializations out of the idle callback so that we can get a
298 // splash screen up and running right away.
299
300 // Fetch the subsystem manager.
301 auto mgr = globals->get_subsystem_mgr();
302
303 if ( idle_state == 0 ) {
304 if (globals->get_renderer()->runInitOperation()) {
307 idle_state+=2;
308 fgSplashProgress("loading-aircraft-list");
309 fgSetBool("/sim/rendering/initialized", true);
310 }
311
312 } else if ( idle_state == 2 ) {
314 idle_state++;
315 fgSplashProgress("loading-nav-dat");
316
317 } else if ( idle_state == 3 ) {
318
319 bool done = fgInitNav();
320 if (done) {
321 ++idle_state;
322 fgSplashProgress("init-scenery");
323 }
324 } else if ( idle_state == 4 ) {
325 idle_state++;
326
327 mgr->add<TimeManager>();
328
329 // Do some quick general initializations
330 if( !fgInitGeneral()) {
331 throw sg_exception("General initialization failed");
332 }
333
334 // now we have commands up
336
338 // Initialize the property-based built-in commands
342
344
346 // Initialize the material manager
348 globals->set_matlib( new SGMaterialLib );
349
350 } else if (( idle_state == 5 ) || (idle_state == 2005)) {
351 idle_state+=2;
353
354 simgear::SGModelLib::init(globals->get_fg_root().utf8Str(), globals->get_props());
355
356 auto timeManager = mgr->get_subsystem<TimeManager>();
357 timeManager->init();
358
360 // Initialize the TG scenery subsystem.
362
363 auto scenery = mgr->add<FGScenery>();
364 scenery->init();
365 scenery->bind();
366
367 fgSplashProgress("creating-subsystems");
368 } else if (( idle_state == 7 ) || (idle_state == 2007)) {
369 bool isReset = (idle_state == 2007);
370 idle_state = 8; // from the next state on, reset & startup are identical
371 SGTimeStamp st;
372 st.stamp();
373
374 try {
375 fgCreateSubsystems(isReset);
376 } catch (std::exception& e) {
377 // attempt to trace location of illegal argument / invalid string
378 // position errors on startup
379 flightgear::sentryReportException(std::string{"Creating subsystems: caught:"} + e.what());
380 throw;
381 }
382
383 SG_LOG(SG_GENERAL, SG_INFO, "Creating subsystems took:" << st.elapsedMSec());
384 fgSplashProgress("binding-subsystems");
385
386 } else if ( idle_state == 8 ) {
387 idle_state++;
388 SGTimeStamp st;
389 st.stamp();
390 mgr->bind();
391 SG_LOG(SG_GENERAL, SG_INFO, "Binding subsystems took:" << st.elapsedMSec());
392
393 fgSplashProgress("init-subsystems");
394 } else if ( idle_state == 9 ) {
395 SGSubsystem::InitStatus status = mgr->incrementalInit();
396 if ( status == SGSubsystem::INIT_DONE) {
397 ++idle_state;
398 fgSplashProgress("finishing-subsystems");
399 } else {
400 fgSplashProgress("init-subsystems");
401 }
402
403 } else if ( idle_state == 10 ) {
404 idle_state = 900;
406 fgSplashProgress("finalize-position");
407 } else if ( idle_state == 900 ) {
408 idle_state = 1000;
409
410 // setup OpenGL view parameters
411 globals->get_renderer()->setupView();
412
413 globals->get_renderer()->resize(fgGetInt("/sim/startup/xsize"),
414 fgGetInt("/sim/startup/ysize"));
416 new simgear::canvas::VGInitOperation());
417
418 int session = fgGetInt("/sim/session",0);
419 session++;
420 fgSetInt("/sim/session",session);
421 }
422
423 if ( idle_state == 1000 ) {
424 sglog().setStartupLoggingEnabled(false);
425
426 // We've finished all our initialization steps, from now on we
427 // run the main loop.
428 fgSetBool("sim/sceneryloaded", false);
430
431#ifdef NASAL_BACKGROUND_GC_THREAD
432 ngccn = new simgear::Notifications::NasalGarbageCollectionConfigurationNotification(nasal_gc_threaded->getBoolValue(), nasal_gc_threaded_wait->getBoolValue());
433 simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(ngccn);
434 simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(mln_started);
435#endif
436 flightgear::addSentryBreadcrumb("entering main loop", "info");
437 }
438
439 if ( idle_state == 2000 ) {
440 flightgear::addSentryBreadcrumb("starting reset", "info");
442 idle_state = 2005;
443 }
444}
445
451
453{
454 bool secureMode = true;
455 if (Options::sharedInstance()->isOptionSet("allow-nasal-from-sockets")) {
456 SG_LOG(SG_GENERAL, SG_MANDATORY_INFO, "\n!! Network connections allowed to use Nasal !!\n"
457 "Network connections will be allowed full access to the simulator \n"
458 "including running arbitrary scripts. Ensure you have adequate security\n"
459 "(such as a firewall which blocks external connections).\n");
460 secureMode = false;
461 }
462
463 // it's by design that we overwrite any existing property tree value
464 // here - this prevents an aircraft or add-on setting the property
465 // value underneath us, eg in their -set.xml
466 SGPropertyNode_ptr secureFlag = fgGetNode("/sim/secure-flag", true);
467 secureFlag->setBoolValue(secureMode);
468 secureFlag->setAttributes(SGPropertyNode::READ |
469 SGPropertyNode::PRESERVE |
470 SGPropertyNode::PROTECTED);
471}
472
473// Propose NVIDIA Optimus / AMD Xpress to use high-end GPU
474#if defined(SG_WINDOWS)
475extern "C" {
476 _declspec(dllexport) unsigned int NvOptimusEnablement = 0x00000001;
477 _declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
478}
479#endif
480
481static void rotateOldLogFiles()
482{
483 const int maxLogCount = 10;
484 const auto homePath = globals->get_fg_home();
485
486 for (int i = maxLogCount; i > 0; --i) {
487 const auto name = "fgfs_" + std::to_string(i - 1) + ".log";
488 SGPath curLogFile = homePath / name;
489 if (curLogFile.exists()) {
490 auto newName = "fgfs_" + std::to_string(i) + ".log";
491 curLogFile.rename(homePath / newName);
492 }
493 }
494
495 SGPath p = homePath / "fgfs.log";
496 if (!p.exists())
497 return;
498 SGPath log0Path = homePath / "fgfs_0.log";
499 if (!p.rename(log0Path)) {
500 std::cerr << "Failed to rename " << p.str() << " to " << log0Path.str() << std::endl;
501 }
502}
503
504static void logToHome(const std::string& pri)
505{
506 sgDebugPriority fileLogLevel = SG_INFO;
507 // https://sourceforge.net/p/flightgear/codetickets/2100/
508 if (!pri.empty()) {
509 try {
510 fileLogLevel = std::min(fileLogLevel, logstream::priorityFromString(pri));
511 } catch (std::exception& ) {
512 // let's not worry about this, and just log at INFO
513 }
514 }
515
516 SGPath logPath = globals->get_fg_home();
517 logPath.append("fgfs.log");
518 if (logPath.exists()) {
520 }
521
522 sglog().logToFile(logPath, SG_ALL, fileLogLevel);
523}
524
525struct SGLogDeltasListener : SGPropertyChangeListener
526{
527 void valueChanged(SGPropertyNode* node) override
528 {
529 std::string value = node->getStringValue();
530 std::cerr << __FILE__ << ":" << __LINE__ << ": sglogdeltas value=" << value << "\n";
531 logDeltaSet(value.c_str());
532 }
533};
535
536// Main top level initialization
537int fgMainInit( int argc, char **argv )
538{
539 sglog().setLogLevels( SG_ALL, SG_WARN );
540 sglog().setStartupLoggingEnabled(true);
541
542 globals = new FGGlobals;
543 auto initHomeResult = fgInitHome();
544 if (initHomeResult == InitHomeAbort) {
545 flightgear::fatalMessageBoxThenExit("Unable to create lock file",
546 "FlightGear was unable to create the lock file in FG_HOME");
547 }
548
549#if defined(HAVE_QT)
550 flightgear::initApp(argc, argv);
551#endif
552
553 // check if the launcher is requested, since it affects config file parsing
554 bool showLauncher = flightgear::Options::checkForArgEnable(argc, argv, "launcher");
555 // an Info.plist bundle can't define command line arguments, but it can set
556 // environment variables. This avoids needed a wrapper shell-script on OS-X.
557 showLauncher |= (::getenv("FG_LAUNCHER") != nullptr);
558
559#if defined(HAVE_QT)
560 if (showLauncher && (initHomeResult == InitHomeReadOnly)) {
561 // show this message early, if we can
563 if (r == flightgear::LockFileReset) {
564 SG_LOG( SG_GENERAL, SG_MANDATORY_INFO, "Deleting lock file at user request");
565 flightgear::addSentryBreadcrumb("deleting lock-file at user request", "info");
567 fgSetBool("/sim/fghome-readonly", false);
568 } else if (r == flightgear::LockFileQuit) {
569 return EXIT_SUCCESS;
570 }
571 }
572#endif
573
574 {
575 SGPropertyNode* sglogdeltas = globals->get_props()->getNode("/sim/sg-log-deltas", true /*create*/);
576 assert(sglogdeltas);
577 sglogdeltas->addChangeListener(&s_sglogdeltas_listener, false /*initial*/);
578 const char* sglogdeltas_value = getenv("SG_LOG_DELTAS");
579 if (sglogdeltas_value) {
580 sglogdeltas->setStringValue(sglogdeltas_value);
581 }
582 }
583
584 globals->get_props()->getNode("/sim", true /*create*/)->setAttribute(SGPropertyNode::VALUE_CHANGED_DOWN, true);
585
586 {
587 SGPropertyNode* active = globals->get_props()->getNode("/sim/property-locking/active", true /*create*/);
588 SGPropertyNode* verbose = globals->get_props()->getNode("/sim/property-locking/verbose", true /*create*/);
589 SGPropertyNode* timing = globals->get_props()->getNode("/sim/property-locking/timing", true /*create*/);
590 SGPropertyNode* parent_listeners = globals->get_props()->getNode("/sim/property-locking/parent_listeners", true /*create*/);
591 SGPropertyLockControl(active, verbose, timing, parent_listeners);
592 }
593
594 const bool readOnlyFGHome = fgGetBool("/sim/fghome-readonly");
595 if (!readOnlyFGHome) {
596 // now home is initialised, we can log to a file inside it
597 const auto level = flightgear::Options::getArgValue(argc, argv, "--log-level");
598 logToHome(level);
599 }
600
601 if (readOnlyFGHome) {
602 flightgear::addSentryTag("fghome-readonly", "true");
603 }
604
605 std::string version(FLIGHTGEAR_VERSION);
606 SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Version " << version );
607 SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Build Type " << FG_BUILD_TYPE );
608 SG_LOG( SG_GENERAL, SG_INFO, "Built with " << SG_COMPILER_STR);
609
610
611 flightgear::addSentryTag("osg-version", osgGetVersion());
612
613#ifdef __OpenBSD__
614 {
615 /* OpenBSD defaults to a small maximum data segment, which can cause
616 flightgear to crash with SIGBUS, so output a warning if this is likely.
617 */
618 struct rlimit rlimit;
619 int e = getrlimit(RLIMIT_DATA, &rlimit);
620 if (e) {
621 SG_LOG( SG_GENERAL, SG_INFO, "This is OpenBSD; getrlimit() failed: " << strerror(errno));
622 }
623 else {
624 unsigned long long required = 4ULL * (1ULL<<30);
625 if (rlimit.rlim_cur < required) {
626 SG_LOG( SG_GENERAL, SG_POPUP, ""
627 << "Max data segment (" << rlimit.rlim_cur << "bytes) too small.\n"
628 << "This can cause Flightgear to crash due to SIGBUS.\n"
629 << "E.g. increase with 'ulimit -d " << required/1024 << "'."
630 );
631 }
632 }
633 }
634#endif
635
636 // seed the random number generator
637 sg_srandom_time();
638
639 string_list *col = new string_list;
640 globals->set_channel_options_list( col );
641
642 if (showLauncher) {
643 // to minimise strange interactions when launcher and config files
644 // set overlaping options, we disable the default files. Users can
645 // still explicitly request config files via --config options if they choose.
647 }
648
649 // Load the configuration parameters. (Command line options
650 // override config file options. Config file options override
651 // defaults.)
652 int configResult = fgInitConfig(argc, argv, false);
653 if (configResult == flightgear::FG_OPTIONS_ERROR) {
654 return EXIT_FAILURE;
655 } else if (configResult == flightgear::FG_OPTIONS_EXIT) {
656 return EXIT_SUCCESS;
657 }
658
659 bool didUseLauncher = false; /* <didUseLauncher> is set but unused. */
660#if defined(HAVE_QT)
661 if (showLauncher) {
662#if !defined(SG_MAC)
663 // Do a quick GL version check using OSG before running the launcher.
664 // We skip it on Mac because using OSG before Qt might be problematic.
667 return EXIT_FAILURE;
668 }
669#endif
670
671 flightgear::addSentryBreadcrumb("starting launcher", "info");
673 return EXIT_SUCCESS;
674 }
675
676 didUseLauncher = true;
677 flightgear::addSentryBreadcrumb("completed launcher", "info");
678 }
679#else
680 if (showLauncher) {
681 SG_LOG(SG_GENERAL, SG_ALERT, "\n!Launcher requested, but FlightGear was compiled without Qt support!\n");
682 }
683#endif
684
686 fgInitAircraftPaths(false);
687
688 auto errorManager = globals->get_subsystem_mgr()->add<flightgear::ErrorReporter>();
689 errorManager->preinit();
690
691 configResult = fgInitAircraft(false, didUseLauncher);
692 if (configResult == flightgear::FG_OPTIONS_ERROR) {
693 return EXIT_FAILURE;
694 } else if ((configResult == flightgear::FG_OPTIONS_EXIT) ||
695 (configResult == flightgear::FG_OPTIONS_SHOW_AIRCRAFT))
696 {
697 return EXIT_SUCCESS;
698 }
699
701
703 if (configResult == flightgear::FG_OPTIONS_ERROR) {
704 return EXIT_FAILURE;
705 } else if (configResult == flightgear::FG_OPTIONS_EXIT) {
706 return EXIT_SUCCESS;
707 }
708
709 // Set the lists of allowed paths for cases where a path comes from an
710 // untrusted source, such as the global property tree (this uses $FG_HOME
711 // and other paths set by Options::processOptions()).
713
714 const auto& resMgr = simgear::EmbeddedResourceManager::createInstance();
715 initFlightGearEmbeddedResources();
716 // The language was set in processOptions()
717 const std::string locale = globals->get_locale()->getPreferredLanguage();
718 // Must always be done after all resources have been added to 'resMgr'
719 resMgr->selectLocale(locale);
720 SG_LOG(SG_GENERAL, SG_INFO,
721 "EmbeddedResourceManager: selected locale '" << locale << "'");
722
723 if (fgGetBool("/sim/autosave-migration/did-migrate", false)) {
724 // inform the user we did migration. This is the earliest point
725 // we can do it, since now the locale is set
726 auto locale = globals->get_locale();
727 const auto title = locale->getLocalizedString("settings-migration-title", "sys", "Settings migrated");
728 const auto msg = locale->getLocalizedString("settings-migration-text", "sys",
729 "Saved settings were migrated from a previous version of FlightGear. "
730 "If you encounter any problems when using the system, try restoring "
731 "the default settings, before reporting a problem. "
732 "Saved settings can affect the appearance, performance and features of the simulator.");
733 flightgear::modalMessageBox(title, msg);
734 }
735
736 globals->get_locale()->loadAircraftTranslations();
737 globals->get_locale()->loadAddonTranslations();
738
739 // Copy the property nodes for the menus added by registered add-ons
740 addons::AddonManager::instance()->addAddonMenusToFGMenubar();
741
742 // The GraphicsPreset subsystem is special - it's not added together with
743 // the rest of the subsystems because it should be present before all the
744 // graphics-related stuff is initialized.
745 auto presets = globals->get_subsystem_mgr()->add<flightgear::GraphicsPresets>();
746 presets->applyInitialPreset();
747
748 // Initialize the Window/Graphics environment.
749
750 fgOSInit(&argc, argv);
752
754
755 // Initialize sockets (WinSock needs this)
756 simgear::Socket::initSockets();
757
760
761 globals->get_renderer()->postinit();
762
764
765 //try to disable the screensaver
767
768 // pass control off to the master event handler
769 int result = fgOSMainLoop();
771
774
775 const bool requestLauncherRestart = fgGetBool("/sim/restart-launcher-on-exit");
776
777#ifdef NASAL_BACKGROUND_GC_THREAD
778 simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(mln_stopped);
779#endif
780
781 simgear::clearEffectCache();
782 simgear::canvas::vgShutdown();
783
784 // clean up here; ensure we null globals to avoid
785 // confusing the atexit() handler
786 delete globals;
787 globals = nullptr;
788
789 // delete the NavCache here. This will cause the destruction of many cached
790 // objects (eg, airports, navaids, runways).
792
793#if defined(HAVE_QT)
794 if (requestLauncherRestart) {
795 string_list originalArgs;
796 for (int arg = 1; arg < argc; ++arg) {
797 originalArgs.push_back(argv[arg]);
798 }
799
800 if (!showLauncher) {
801 // The user specified the --restart-launcher option but without the
802 // --launcher option. So let's add the --launcher option so that the
803 // launcher actually starts instead of the simulator again.
804 originalArgs.push_back("--launcher");
805 }
806
807 flightgear::addSentryBreadcrumb("Requested to restart launcher", "info");
808 flightgear::startLaunchOnExit(originalArgs);
809 }
810#endif
811 return result;
812}
#define p(x)
#define i(x)
int _bootstrap_OSInit
Bucket for subsystem pointers representing the sim's state.
Definition globals.hxx:79
void init() override
Definition scenery.cxx:406
void init() override
void applyInitialPreset()
init() is called too late (after fgOSInit), so we call this method early, to load the initial preset ...
static NavDataCache * instance()
void setShouldLoadDefaultConfig(bool load)
when using the built-in launcher, we disable the default config files.
Definition options.cxx:3598
OptionResult processOptions()
apply option values to the simulation state (set properties, etc).
Definition options.cxx:3025
static bool checkForArgEnable(int argc, char *argv[], const std::string &checkArg)
Return true when user explicitly enabled boolean option, otherwise false.
Definition options.cxx:3681
static std::string getArgValue(int argc, char *argv[], const char *checkArg)
getArgValue - get the value of an argument if it exists, or an empty string otherwise
Definition options.cxx:3693
static Options * sharedInstance()
Definition options.cxx:2345
WindowVector windows
Vector of all the registered windows.
static WindowSystemAdapter * getWSA()
Get the global WindowSystemAdapter.
static const std::unique_ptr< AddonManager > & createInstance()
static const std::unique_ptr< AddonManager > & instance()
const char * name
void fgInitCommands()
Initialize the default built-in commands.
void fgInitSceneCommands()
Initialize the default built-in commands.
void fgPostInitSubsystems()
Definition fg_init.cxx:1171
bool fgInitNav()
Initialize vor/ndb/ils/fix list management and query systems (as well as simple airport db list) This...
Definition fg_init.cxx:892
int fgInitAircraft(bool reinit, bool didUseLauncher)
Definition fg_init.cxx:803
void fgShutdownHome()
Definition fg_init.cxx:637
int fgInitConfig(int argc, char **argv, bool reinit)
Definition fg_init.cxx:688
void fgCreateSubsystems(bool duringReset)
Definition fg_init.cxx:997
void fgStartNewReset()
Definition fg_init.cxx:1283
bool fgInitGeneral()
Definition fg_init.cxx:937
void fgInitAircraftPaths(bool reinit)
Definition fg_init.cxx:788
void fgDeleteLockFile()
Definition fg_init.cxx:651
InitHomeResult fgInitHome()
Definition fg_init.cxx:529
void fgOutputSettings()
Definition fg_init.cxx:972
@ InitHomeAbort
Definition fg_init.hxx:46
@ InitHomeReadOnly
Definition fg_init.hxx:44
void fgOSOpenWindow()
void fgOSResetProperties()
void fgRegisterIdleHandler(fgIdleHandler func)
void fgOSInit(int *argc, char **argv)
int fgOSMainLoop()
void fgOSCloseWindow()
static int status
int fgGetInt(const char *name, int defaultValue)
Get an int value for a property.
Definition fg_props.cxx:532
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
Definition fg_props.cxx:556
bool fgSetInt(const char *name, int val)
Set an int value for a property.
Definition fg_props.cxx:568
FGGlobals * globals
Definition globals.cxx:142
std::vector< std::string > string_list
Definition globals.hxx:36
FlightGear Localization Support.
void fgInitSecureMode()
Definition main.cxx:452
static void rotateOldLogFiles()
Definition main.cxx:481
int fgMainInit(int argc, char **argv)
Definition main.cxx:537
void fgResetIdleState()
Definition main.cxx:446
static void initTerrasync()
Definition main.cxx:158
static SGPropertyNode_ptr frame_signal
Definition main.cxx:87
static void fgMainLoop(void)
Definition main.cxx:100
static void logToHome(const std::string &pri)
Definition main.cxx:504
static int idle_state
Definition main.cxx:293
static void fgSetVideoOptions()
Definition main.cxx:185
static SGLogDeltasListener s_sglogdeltas_listener
Definition main.cxx:534
static void checkOpenGLVersion()
Definition main.cxx:222
static void fgIdleFunction(void)
Definition main.cxx:295
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
bool runLauncherDialog()
MessageBoxResult modalMessageBox(const std::string &caption, const std::string &msg, const std::string &moreText)
void registerMainLoop()
Definition main.cxx:259
void startLaunchOnExit(const std::vector< std::string > &originalCommandLine)
@ brief helper to re-open the launcher once FLightGear exits cleanly
void initApp(int &argc, char **argv, bool doInitQSettings)
void unregisterMainLoopProperties()
Definition main.cxx:274
void registerSubsystemCommands(SGCommandMgr *cmdMgr)
void addSentryBreadcrumb(const std::string &, const std::string &)
void addSentryTag(const char *, const char *)
void warnAboutGLVersion()
void sentryReportException(const std::string &, const std::string &)
void fatalMessageBoxThenExit(const std::string &caption, const std::string &msg, const std::string &moreText, int exitStatus, bool reportToSentry)
bool initPosition()
@ FG_OPTIONS_SHOW_AIRCRAFT
Definition options.hxx:58
@ FG_OPTIONS_EXIT
Definition options.hxx:56
@ FG_OPTIONS_ERROR
Definition options.hxx:55
LockFileDialogResult showLockFileDialog()
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
Definition proptest.cpp:25
bool fgSetBool(char const *name, bool val)
Set a bool value for a property.
Definition proptest.cpp:24
bool fgSetString(char const *name, char const *str)
Set a string value for a property.
Definition proptest.cpp:26
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
bool fgPreliminaryGLVersionCheck()
Attempt to create an off-screen pixel buffer to check whether our target OpenGL version is available ...
void fgOSDisableScreensaver()
Attempt to disable the screensaver.
void fgSplashProgress(const char *identifier, unsigned int percent)
Set progress information.
Definition splash.cxx:863
void valueChanged(SGPropertyNode *node) override
Definition main.cxx:527
void fgInitAllowedPaths()
Allowed paths here are absolute, and may contain one *, which matches any string.
Definition util.cxx:112