User:Callahanp/Flightgear and Simgear Code/From Command Line to Holding Short

From FlightGear wiki
Jump to navigation Jump to search

This is mostly old and obsolete. Maybe the basis for something updated, but maybe not. Not sure this is useful to anyone but the Author.

WIP.png Work in progress
This article or section will be worked on in the upcoming hours or days.
See history for the latest developments.

This page covers initialization in flightgear, from the command line through the Splash Page to the first frame of the aircraft in position. This page is intended to be read with flightgear and simgear open in an editor, preferably one with a global search capability. In addition you may want to step through some parts of the flightgear code in a debugger capable of setting break points. I'll assume you don't need advice on which editor and debugging tools to choose or how to set them up.

Flightgear consists of a number of subsystems, responsible for various aspects of the illusion of flight. When the command to start flightgear is given, the environment capable of supporting the display of aircraft and scenery is initialized. By reviewing this initialization process step by step, you gain an overview of the main components of flightgear and insight into areas you may want to similarly explore in detail.

We'll start by stepping through bootstrap and other modules until flightgear is up and running and the aircraft is visible, holding short, in a parking area, or placed on a runway waiting for takeoff clearance. I'll provide explanations where I think they're needed, but mostly you will just be looking at specific parts of specific modules and drawing your own conclusions and making notes. While the initialization of sub-systems does provide some insight into the organization of the code, the real functionality of the subsystems is invoked later, during frame by frame display of the simulation. A second companion wiki page goes into additional detail on each subsystem. A third wiki page details the cycle of execution for each frame.

There is ongoing work by Edward d'Auvergne in the area of initialization. Where appropriate, I'll refer to various things Edward has noted in e-mails to the flightgear-devel mailing list.

The Command Line

When you start flight gear, several activities take place

* logging is set up
* command line arguments are checked, read and saved
* a global object is created to hold state and references to managing functions of several types
* a tree of properties is built on the global object
* various static objects are created and initialized
* scenery data is downloaded using terrasync
* airport and aircraft data is scanned and made available
* scenery data is read and made available
* an interpreter is started
* connections to multiplayer servers are made
* a scene is established as a "view" with scenery, Airport data and the Aircraft
* an event loop is started to finish the initialization, display a splash screen.  The event loop then shows the first frame and then adjust the view as time goes on, based on changes in controls, weather, aircraft position, speed, attitude and time.

That's a lot, but the orchestration of all this activity takes place in just a few code files and a very few functions in those files.

We'll trace our way through the code and highlight specific lines of code that make up this process. We'll cover both the initial startup and the differences in the initialization process when resetting the application.

So here we are on the command line. We'll assume all the environment variables have been properly set, and an appopriate set of command line options have been used. The executable is install/bin/fgfs. As you might expect the entry point is called main(), but it's not where you'd expect, in main.cxx. Instead it's in bootstrap.cxx.


int main ( int argc, char **argv )

main() is the first of four stages of initialization in Flightgear. The first stage ends when main() calls fgMainInit(argc, argv)

main contains the initial entry point and final exit point for the Flightgear application. It initializes things that have to be done early on and decides if we're starting the full application or just a viewer, then calls fgviewerMain(argc, argv) or fgMainInit(argc, argv). We'll skip the viewer and focus on fgMainInit.

The main purpose of Bootstrap.cxx is to differentiate two distinct ways of starting flightgear: as a standalone [|Viewer], or as the full flight simulator. bootstrap.cxx also provides a home for globals, some error handling and final cleanup when closing flightgear.

We will follow the full flight simulator path which takes us directly into fgMainInit.

src/Main/main.cxx & main.hxx

fgMainInit( int argc, char **argv )

fgMainInit, the second initialization stage, does about 20 individual initializations. It ends with an open but blank window, and a call to fgOSMainLoop();



Below are notes from a first attempt at this document written last year.

I'm made some notes, organized by source code file and function. The result is the information below.

It is important to know that this is an area that as of March 2018 is under active discussion on the mailing list. See Edward d'Auvergne's e-mail on testing Some of what is written below needs to be reviewed and changed. These changes are underway.

Follow up discussions regarding subsystems on the flightgear-devel mailing list have resulted in changes to the way subsystems are constructed and initialized.

From Command Line to Holding Short

There are several key concepts in this startup process:

  • C++ Classes
  • Class Constructors and Object Construction
  • Initialization,
  • Binding
  • Re-initialization
  • Object destruction.


Just to get oriented a little, I'm going to list the top-level routines in the path to holding short. Initialization happens in four stages, Some stages have very little in them, others have multiple phases and many subsystems to initialize.

Startup Stage Module Routine Signature Brief Description
1. Main/bootstrap.cxx int main(int argc, int char **argv )

main() is the first of four stages of initialization in Flightgear. The first stage ends when main() calls fgMainInit(argc, argv)

main contains the initial entry point and final exit point for the Flightgear application. It initializes things that have to be done early on and decides if we're starting the full application or just a viewer, then calls fgviewerMain(argc, argv) or fgMainInit(argc, argv). We'll skip the viewer and focus on fgMainInit.

2. Main/main.cxx int fgMainInit( int argc, char **argv )

fgMainInit continues with about 20 individual initializations. fgMainInit is only the second stage of initialization. It ends with an open but blank window, and a call to fgOSMainLoop();

3. Viewer/fg_os_osgviewer.cxx int fgOSMainLoop()

fgOSMainLoop doesn't do initialization itself. fgOSMainLoop is the main loop for the full duration of the flightgear session. It's amazingly brief, Just four C++ statements on 12 lines of code. The loop has just two tasks: Think of them as "set up what has to be displayed" and "display it". During initialization, the setup task is initialization. The second task displays the splash screen, messages produced by the initialization and something that moves to indicate that the program is still working. Then, after initialization, the setup part of the loop does the calculation work needed for the next frame of the simulation. The display task then uses that data to show the simulation on the screen.

fgIdleFunction does the initialization work. fgMainLoop() handles the events and calculations for the ongoing simulation. These are called through a pointer named *idlefunc. The display part of the loop is the same for both of these "setup" functions.

fgIdleFunction is the third stage of initialization. During this stage, fgOSMainLoop calls fgIdleFunction repeatedly through *idleFunc. Each call to fgIdleFunction does a different part of the initialization. In between, the splash screen is being updated, showing which part of the initialization is in progress, and a moving progress bar to show that "something" is happening. When the third stage is complete, *idleFunc is set to point to fgOSMainLoop.

3. Main/main.cxx static void fgIdleFunction ( void )

fgIdleFunction handles all the third stage initializations in a number of phases. It's main work is the initialization of subsystems. It's called many times, and each phase may do one or many initializations. There are quite a few and it takes a while. When fgIdleFunction reaches its last initialization state, it calls registerMainLoop() to change *idleFunction to point to fgMainLoop. Once fgIdleFunction has completed its work it is not run again unless you do a reset, but the routine that called it, fgOSMainLoop continues to run.

4. main/main.cxx fgMainLoop fgMainLoop does not contain a loop, its running under the loop in fgOSMainLoop and will continue to run in that loop until you reset or exit flightgear. fgMainLoop handles the fourth and final stage of initialization, waiting on the loading of scenery, the airport and the aircraft data. All the final initialization work is handled by subsystems loaded in stage three. Until all subsystems are fully initialized, the splash screen continues to be displayed. Once they're all ready, the subsystems continue to run, and we find ourselves in position at an airport and ready to fly.

After initialization all of the subsystems needed to fly will continue to be invoked as needed by fgMainLoop, which is run repeatedly in the actual loop in fgOSMainLoop.

At first glance, one might think that the naming of things in this part of Flightgear is a bit off. The names might have made sense at one point in the early development of Flightgear, but things have changed and some functionality moved, but the names were not changed. So we end up with things like fgMainLoop that does not contain a loop. Does it matter? Probably not. There's other work to do that's more important. My guess is that we just need to know how this loop works and most of the time our attention will be much further down the call stack, inside one of those sub-systems.


What in the code finally recognizes that we're ready to display the scene in stage 4?

Are the final initializations done by subsystems done in any particular order?

Is what's above this point enough to know about startup and initialization?

FGcommands.cxx and fg_scene_commands.cxx contains a number of static functions not called from c++. are these only called from Nasal? Is that why they are missing from the .hxx?

Do we need to show some examples of property nodes?

Would a list of functions contained in each of the modules in src/Main be of any use? For example: fgCommands.cxx

  • static inline SGPropertyNode * get_prop (const SGPropertyNode * arg, SGPropertyNode * root)
  • static inline SGPropertyNode * get_prop2 (const SGPropertyNode * arg, SGPropertyNode * root)
  • static void split_value (double full_value, const char * mask, double * unmodifiable, double * modifiable)


would the same list with a note on its purpose and usage be of help? fgCommands.cxx |

function signature Description
static inline SGPropertyNode *

get_prop (const SGPropertyNode * arg, SGPropertyNode * root)

. gets property[0]
static inline SGPropertyNode *

get_prop2 (const SGPropertyNode * arg, SGPropertyNode * root)

gets property[1]
static void

split_value ( double full_value, const char * mask, double * unmodifiable, double * modifiable)

Get a double value and split it as required.
static void

limit_value (double * value, const SGPropertyNode * arg)

The material below are detailed notes from an initial look at the main loop. They're just lists of what gets done where. Would a new developer just read the actual functions and make their own notes? |- | Clamp or wrap a value as specified.

Module colspan="3" Description & Steps
src/Main/bootstrap Main Bootstrap provides the top level of the stack for executing either fgviewerMain or fgMainInit.
  • Enable Floating Point features with InitFPE
  • Set locale settings specific to using C or C++
  • Uninstall if requested
  • Initialize logging
  • Initialize some static objects in OSG to prevent crashes on exit
  • set the std:: terminate function
  • set the atexit function
  • pass control to the viewer with fgViewerMain or initialize using fgMainInit
  • catch throwables and other fatal error conditions, providing appropriate error messages
src/Main/Main fgMainInit( int argc, char **argv )
  • set logging level and enable logging
  • setup globals as an instance of FGGlobals.
    • see src/Main/globals.cxx constructor FGGlobals::FGGlobals()
  • make sure there's a valid FG_HOME directory and set up logging there
  • set the version & log the version, build type and Jenkins or Hudson build id
  • seed the random number generator
  • set up a few things in globals
  • decide if we're using a launcher
    • see below src/Main/Main.cxx Launcher for details
  • set read-only mode when more than one flightgear instance is found
  • load configuration data
    • see src/Main/Main.cxxfgInitConfig
  • initalize aircraft paths
    • see: src/Main/fg_init.cxx fgInitAircraftPaths and fgInitAircraft
  • create an instance of the addon manager
    • see below src/Add-ons/AddonManager.cxx
  • check that the paths are allowed when they come from an untrusted source
  • create an instance of an embedded resource manager
    • see below simgear::EmbeddedResourceManager createInstance
    • and initFlightGearEmbeddedResources
  • get the preferred language and set the locale
  • initialize the windows/graphics environment
    • see viewer/fg_os_osgviewer.cxx fgosinit(int* argc, char** argv)
  • register fgIdleFunction
    • see fgRegisterIdleHandler in Main/fg_os_common.cxx and fgIdleFUnction in Main/main.cxx
  • Initialize sockets
    • simgear::Socket::initSockets()
  • setup alpha channel for Clouds3D
    • fgOSOpenWindow(true /* request stencil buffer */);
    • fgOSResetProperties();
  • fntInit();
  • globals->get_renderer()->preinit();
  • ATIScreenSizeHack
  • fgOutputSettings();
  • disable the screensaver
  • pass control off to the master event handler
    • See viewer/fg_os_osgviewer.cxx fgOSMainLoop() (next)
  • cleanup
    • frame_signal.clear();
    • fgOSCloseWindow();
    • simgear::clearEffectCache();
    • delete globals;
    • globals = NULL;
  • delete the NavCache here. This will cause the destruction of many cached objects (eg, airports, navaids, runways).
    • delete flightgear::NavDataCache::instance();
  • return to bootstrap.cxx Main return result;
viewer/fg_os_osgviewer.cxx fgOSMainLoop()
  • Release the viewer's context at end of frame hint
    • viewer->setReleaseContextAtEndOfFrameHint(false)
  • If the viewer is not realized yet, realize it. We still see nothing in the window
    • viewer->realize()
here's the loop:
  • get the idle handler
    • fgIdleHandler idleFunc = globals->get_renderer()->getEventHandler()->getIdleHandler();
  • run the idle handler It's not clear what this is. It's either fgMainLoop or fgIdleFunction
    • (*idleFunc)()
  • update the screen
    • globals->get_renderer()->update();
  • timestamp the frame
    • viewer->frame( globals->get_sim_time_sec() );
Main/main.cxx fgIdleFunc
  • normal startup has states 0, 2, 3, 4, 5, 7, 8, 9, 10, 900 1000
  • reset startup has 2000, 2005, 2007, 8, 9, 10, 900, 1000
  • state 1000 sets fgMainLoop as the idle function. a reset sets it back to fgIdleFunc
  • state 0 checks the openGL version and sets video options
    • see checkOpenGLVersion() and fgSetVideoOptions()
  • state 2 sets /sim/rendering/initialized true and initializes terrasync with initTerrasync()
  • state 3 loads nav data
    • fgInitNav()
  • state 4 initalizes scenery, but there's not call here
  • state 5
    • initializes some general items with fgInitGeneral()
    • adds the TimeManager
      • globals->add_new_subsystem<TimeManager>(SGSubsystemMgr::INIT)
    • initializes property based commands
      • fgInitCommands(); and fgInitSceneCommands();
    • registers Subsystem Commands
      • flightgear::registerSubsystemCommands(globals->get_commands());
    • initializes the material manager
      • globals->set_matlib( new SGMaterialLib )
      • simgear::SGModelLib::setPanelFunc(FGPanelNode::load)
  • state 7
    • Initializes Position
      • flightgear::initPosition(); and flightgear::initTowerLocationListener();
    • Initializes somethingin SGModelLib
      • simgear::SGModelLib::init(globals->get_fg_root().local8BitStr(), globals->get_props());
    • initializes the time manager instantiated in state 5
      • auto timeManager = globals->get_subsystem<TimeManager>(); timeManager->init();
  • state 8
  • Create subsystems
    • fgCreateSubsystems(isReset)
  • log the time spent creating subsystems
  • state 9
    • bind subsystems
    • globals->get_subsystem_mgr()->bind();
  • state 10
    • initialize subsystems
      • SGSubsystem::InitStatus status = globals->get_subsystem_mgr()->incrementalInit();
    • remains in state 10 until SGSubsystem::INIT_DONE
  • state 900
    • do what needs doing after systems are initialized
      • fgPostInitSubsystems();
  • state 1000
    • setup OpenGl view Parameters
      • globals->get_renderer()->setupView();
    • resize the window to startup size
      • globals->get_renderer()->resize(x,y)
    • unsure what this does
      • WindowSystemAdapter::getWSA()->windows[0]->gc->add(new simgear::canvas::VGInitOperation() )
  • increment /sim/session
  • State 1000 takes a break and continues with the next call
    • sglog().setStartupLoggingEnabled(false)
  • turns off logging for startup
  • sets sim/scenery loaded to false
  • changes the idle function
    • registerMainLoop();
  • state 2000
    • Reset the application
      • fgStartNewReset();
  • state 2005 same as state 5
  • state 2007 same as state 7

Note fgResetIdleState() sets the state to 2000 and re-registers fgIdleFunction as the idle handler.

Main/main.cxx We seem to have arrived. We're at the hold short point.

Minor functions on the Path to Holding Short

sglog().setLogLevels( SG_ALL, SG_INFO )
sglog().setStartupLoggingEnabled(true); || /simgear/debug/logstream.cxx || Pretty obvious what this does. || What does sglog() actually return?
Module Description Steps
src/Main/globals Provides a global object to contain references to objects and data needed in initializing, running and terminating flightgear
  • Provides global access to
    • the property tree
    • subsystem, event, command and resource manager objects and methods
    • position, current view and aircraft orientation data
    • FG_ROOT and FG_HOME directory paths
    • time, comm channel, waypoint, and user settings data
  • uses foreach from the boost library
<boost/foreach.h> Example
<algorithm> Example Example
std::string version(FLIGHTGEAR_VERSION)
src/Main/Main.cxx Launcher
src/Add-ons/AddonManager.cxx addons::AddonManager::createInstance()
initFlightGearEmbeddedResources see file build/flightgear/src/EmbeddedResources/FlightGear-resources.cxx,automatically generated by fgrcc
viewer/fg_os_osgviewer.cxx fgosinit(int* argc, char** argv)
Main/fg_os_common.cxx fgRegisterIdleHandler( & fgIdleFunction
detectSIMD() bootstrap.cxx returns true if the cpu supports sse2.
gethostname(_hostname, 256) unistd.h glibc returns the hostname of your computer
signal(SIGPIPE, SIG_IGN) signal.h directs SIGPIPE to the SIG_IGN signal handler - Portability: use sigaction() instead
signal(SIGSEGV, segfault_handler) signal.h Flightgear formats the message with a backtrace and exits with std:abort()
segfault_handler (int signo) bootstrap.cxx
initFPE(flightgear::Options::checkForArg(argc, argv, "enable-fpe")) main/options.cxx
checks the command line arguments for the enable-fpe option. Calls InitFPE with the result.
see bootstrap.cxx for more details
signal(SIGFPE, handleFPE) signal.h
We handle Floating Point Exceptions
setlocale(LC_ALL, "")
setlocale(LC_NUMERIC, "C")
setlocale(LC_COLLATE, "C")
return fgUninstall() fg_init.cxx Command line options are checked to determine if uninstall should be called.
sglog() /simgear/debug/logstream.cxx This initializes the log. see logstream.cxx for details
std::set_terminate(fg_terminate); <exception>
sets the standard template library terminate routine
atexit(fgExitCleanup) stdlib.h
registers the given function to be called at normal process termination, either via exit(3) or via return from the program's main(). Functions so registered are called in the reverse order of their registration; no arguments are passed
fgviewerMain(argc, argv) flightgear/Viewer/fgviewer.cxx see viewer topics for details. This is called in bootstrap.cxx if the command line arguments include --viewer
fgMainInit(argc, argv) Main/main.cxx Starts to initialize flightgear. This is called in bootstrap.cxx if command line arguments do not contain --viewer
catch block various termination for most or all errors. Read the end of bootstrap.cxx for more information
flightgear::shutdownQtApp() bootstrap
Done separately from atexit. see bootstrap.cxx for more information
CrashRpt.h #if defined(HAVE_CRASHRPT). This only happens on windows.