]> git.mxchange.org Git - flightgear.git/blob - src/Main/main.cxx
Reset: Clear the pager queues, preserve properties.
[flightgear.git] / src / Main / main.cxx
1 // main.cxx -- top level sim routines
2 //
3 // Written by Curtis Olson, started May 1997.
4 //
5 // Copyright (C) 1997 - 2002  Curtis L. Olson  - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <simgear/compiler.h>
29
30 #include <iostream>
31
32 #include <osg/Camera>
33 #include <osg/GraphicsContext>
34 #include <osgDB/Registry>
35
36 // Class references
37 #include <simgear/canvas/VGInitOperation.hxx>
38 #include <simgear/scene/model/modellib.hxx>
39 #include <simgear/scene/material/matlib.hxx>
40 #include <simgear/scene/material/Effect.hxx>
41 #include <simgear/props/AtomicChangeListener.hxx>
42 #include <simgear/props/props.hxx>
43 #include <simgear/timing/sg_time.hxx>
44 #include <simgear/io/raw_socket.hxx>
45 #include <simgear/scene/tsync/terrasync.hxx>
46 #include <simgear/math/SGMath.hxx>
47 #include <simgear/math/sg_random.h>
48
49 #include <Model/panelnode.hxx>
50 #include <Scenery/scenery.hxx>
51 #include <Scenery/tilemgr.hxx>
52 #include <Sound/soundmanager.hxx>
53 #include <Time/TimeManager.hxx>
54 #include <GUI/gui.h>
55 #include <Viewer/splash.hxx>
56 #include <Viewer/renderer.hxx>
57 #include <Viewer/WindowSystemAdapter.hxx>
58 #include <Navaids/NavDataCache.hxx>
59
60 #include "fg_commands.hxx"
61 #include "fg_io.hxx"
62 #include "main.hxx"
63 #include "util.hxx"
64 #include "fg_init.hxx"
65 #include "fg_os.hxx"
66 #include "fg_props.hxx"
67 #include "positioninit.hxx"
68 #include "subsystemFactory.hxx"
69 #include "options.hxx"
70
71 using namespace flightgear;
72
73 using std::cerr;
74 using std::vector;
75
76 // The atexit() function handler should know when the graphical subsystem
77 // is initialized.
78 extern int _bootstrap_OSInit;
79
80 static SGPropertyNode_ptr frame_signal;
81 static TimeManager* timeMgr;
82
83 // What should we do when we have nothing else to do?  Let's get ready
84 // for the next move and update the display?
85 static void fgMainLoop( void )
86 {
87     frame_signal->fireValueChanged();
88
89     // compute simulated time (allowing for pause, warp, etc) and
90     // real elapsed time
91     double sim_dt, real_dt;
92     timeMgr->computeTimeDeltas(sim_dt, real_dt);
93
94     // update all subsystems
95     globals->get_subsystem_mgr()->update(sim_dt);
96
97     simgear::AtomicChangeListener::fireChangeListeners();
98 }
99
100 static void initTerrasync()
101 {
102     if (fgGetBool("/sim/fghome-readonly", false)) {
103         return;
104     }
105     
106     // start TerraSync up now, so it can be synchronizing shared models
107     // and airports data in parallel with a nav-cache rebuild.
108     SGPath tsyncCache(globals->get_fg_home());
109     tsyncCache.append("terrasync-cache.xml");
110     
111     // wipe the cache file if requested
112     if (flightgear::Options::sharedInstance()->isOptionSet("restore-defaults")) {
113         SG_LOG(SG_GENERAL, SG_INFO, "restore-defaults requested, wiping terrasync update cache at " <<
114                tsyncCache);
115         if (tsyncCache.exists()) {
116             tsyncCache.remove();
117         }
118     }
119     
120     fgSetString("/sim/terrasync/cache-path", tsyncCache.c_str());
121     
122     simgear::SGTerraSync* terra_sync = new simgear::SGTerraSync();
123     terra_sync->setRoot(globals->get_props());
124     globals->add_subsystem("terrasync", terra_sync);
125     
126     terra_sync->bind();
127     terra_sync->init();
128     
129     // add the terrasync root as a data path so data can be retrieved from it
130     std::string terraSyncDir(fgGetString("/sim/terrasync/scenery-dir"));
131     globals->append_data_path(terraSyncDir);
132 }
133
134 static void registerMainLoop()
135 {
136     // stash current frame signal property
137     frame_signal = fgGetNode("/sim/signals/frame", true);
138     timeMgr = (TimeManager*) globals->get_subsystem("time");
139     fgRegisterIdleHandler( fgMainLoop );
140 }
141
142 // This is the top level master main function that is registered as
143 // our idle function
144
145 // The first few passes take care of initialization things (a couple
146 // per pass) and once everything has been initialized fgMainLoop from
147 // then on.
148
149 static int idle_state = 0;
150
151 static void fgIdleFunction ( void ) {
152     // Specify our current idle function state.  This is used to run all
153     // our initializations out of the idle callback so that we can get a
154     // splash screen up and running right away.
155     
156     if ( idle_state == 0 ) {
157         if (guiInit())
158         {
159             idle_state+=2;
160             fgSplashProgress("loading-aircraft-list");
161         }
162
163     } else if ( idle_state == 2 ) {
164         initTerrasync();
165         idle_state++;
166         fgSplashProgress("loading-nav-dat");
167
168     } else if ( idle_state == 3 ) {
169         
170         bool done = fgInitNav();
171         if (done) {
172           ++idle_state;
173           fgSplashProgress("init-scenery");
174         } else {
175           fgSplashProgress("loading-nav-dat");
176         }
177       
178     } else if ( idle_state == 4 ) {
179         idle_state++;
180
181         TimeManager* t = new TimeManager;
182         globals->add_subsystem("time", t, SGSubsystemMgr::INIT);
183         
184         // Do some quick general initializations
185         if( !fgInitGeneral()) {
186             throw sg_exception("General initialization failed");
187         }
188
189         ////////////////////////////////////////////////////////////////////
190         // Initialize the property-based built-in commands
191         ////////////////////////////////////////////////////////////////////
192         fgInitCommands();
193
194         flightgear::registerSubsystemCommands(globals->get_commands());
195
196         ////////////////////////////////////////////////////////////////////
197         // Initialize the material manager
198         ////////////////////////////////////////////////////////////////////
199         globals->set_matlib( new SGMaterialLib );
200         simgear::SGModelLib::setPanelFunc(FGPanelNode::load);
201  
202     } else if (( idle_state == 5 ) || (idle_state == 2005)) {
203         idle_state+=2;
204         flightgear::initPosition();
205         flightgear::initTowerLocationListener();
206         
207         simgear::SGModelLib::init(globals->get_fg_root(), globals->get_props());
208         
209         TimeManager* timeManager = (TimeManager*) globals->get_subsystem("time");
210         timeManager->init();
211         
212         ////////////////////////////////////////////////////////////////////
213         // Initialize the TG scenery subsystem.
214         ////////////////////////////////////////////////////////////////////
215         
216         globals->set_scenery( new FGScenery );
217         globals->get_scenery()->init();
218         globals->get_scenery()->bind();
219         globals->set_tile_mgr( new FGTileMgr );
220         
221         fgSplashProgress("creating-subsystems");
222     } else if (( idle_state == 7 ) || (idle_state == 2007)) {
223         bool isReset = (idle_state == 2007);
224         idle_state = 8; // from the next state on, reset & startup are identical
225         SGTimeStamp st;
226         st.stamp();
227         fgCreateSubsystems(isReset);
228         SG_LOG(SG_GENERAL, SG_INFO, "Creating subsystems took:" << st.elapsedMSec());
229         fgSplashProgress("binding-subsystems");
230       
231     } else if ( idle_state == 8 ) {
232         idle_state++;
233         SGTimeStamp st;
234         st.stamp();
235         globals->get_subsystem_mgr()->bind();
236         SG_LOG(SG_GENERAL, SG_INFO, "Binding subsystems took:" << st.elapsedMSec());
237
238         fgSplashProgress("init-subsystems");
239     } else if ( idle_state == 9 ) {
240         SGSubsystem::InitStatus status = globals->get_subsystem_mgr()->incrementalInit();
241         if ( status == SGSubsystem::INIT_DONE) {
242           ++idle_state;
243           fgSplashProgress("finishing-subsystems");
244         } else {
245           fgSplashProgress("init-subsystems");
246         }
247       
248     } else if ( idle_state == 10 ) {
249         idle_state = 900;
250         fgPostInitSubsystems();
251         fgSplashProgress("finalize-position");
252     } else if ( idle_state == 900 ) {
253         idle_state = 1000;
254         
255         // setup OpenGL view parameters
256         globals->get_renderer()->setupView();
257
258         globals->get_renderer()->resize( fgGetInt("/sim/startup/xsize"),
259                                          fgGetInt("/sim/startup/ysize") );
260         WindowSystemAdapter::getWSA()->windows[0]->gc->add(
261           new simgear::canvas::VGInitOperation()
262         );
263
264         int session = fgGetInt("/sim/session",0);
265         session++;
266         fgSetInt("/sim/session",session);
267     }
268
269     if ( idle_state == 1000 ) {
270         // We've finished all our initialization steps, from now on we
271         // run the main loop.
272         fgSetBool("sim/sceneryloaded", false);
273         registerMainLoop();
274     }
275     
276     if ( idle_state == 2000 ) {
277         fgStartNewReset();
278         idle_state = 2005;
279     }
280 }
281
282 void fgResetIdleState()
283 {
284     idle_state = 2000;
285     fgRegisterIdleHandler( &fgIdleFunction );
286 }
287
288
289 static void upper_case_property(const char *name)
290 {
291     using namespace simgear;
292     SGPropertyNode *p = fgGetNode(name, false);
293     if (!p) {
294         p = fgGetNode(name, true);
295         p->setStringValue("");
296     } else {
297         props::Type t = p->getType();
298         if (t == props::NONE || t == props::UNSPECIFIED)
299             p->setStringValue("");
300         else
301             assert(t == props::STRING);
302     }
303     SGPropertyChangeListener* muc = new FGMakeUpperCase;
304     globals->addListenerToCleanup(muc);
305     p->addChangeListener(muc);
306 }
307
308 // see http://code.google.com/p/flightgear-bugs/issues/detail?id=385
309 // for the details of this.
310 static void ATIScreenSizeHack()
311 {
312     osg::ref_ptr<osg::Camera> hackCam = new osg::Camera;
313     hackCam->setRenderOrder(osg::Camera::PRE_RENDER);
314     int prettyMuchAnyInt = 1;
315     hackCam->setViewport(0, 0, prettyMuchAnyInt, prettyMuchAnyInt);
316     globals->get_renderer()->addCamera(hackCam, false);
317 }
318
319 // Propose NVIDIA Optimus to use high-end GPU
320 #if defined(SG_WINDOWS)
321 extern "C" {
322     _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
323 }
324 #endif
325
326 static void logToFile()
327 {
328     SGPath logPath = globals->get_fg_home();
329     logPath.append("fgfs.log");
330     if (logPath.exists()) {
331         SGPath prevLogPath = globals->get_fg_home();
332         prevLogPath.append("fgfs_0.log");
333         logPath.rename(prevLogPath);
334         // bit strange, we need to restore the correct value of logPath now
335         logPath = globals->get_fg_home();
336         logPath.append("fgfs.log");
337     }
338     sglog().logToFile(logPath, SG_ALL, SG_INFO);
339 }
340
341 // Main top level initialization
342 int fgMainInit( int argc, char **argv ) {
343
344     // set default log levels
345     sglog().setLogLevels( SG_ALL, SG_ALERT );
346
347     globals = new FGGlobals;
348     if (!fgInitHome()) {
349         return EXIT_FAILURE;
350     }
351     
352     if (!fgGetBool("/sim/fghome-readonly")) {
353         // now home is initialised, we can log to a file inside it
354         logToFile();
355     }
356     
357     std::string version;
358 #ifdef FLIGHTGEAR_VERSION
359     version = FLIGHTGEAR_VERSION;
360 #else
361     version = "unknown version";
362 #endif
363     SG_LOG( SG_GENERAL, SG_INFO, "FlightGear:  Version "
364             << version );
365     SG_LOG( SG_GENERAL, SG_INFO, "Built with " << SG_COMPILER_STR << std::endl );
366
367     // Allocate global data structures.  This needs to happen before
368     // we parse command line options
369
370     
371     
372     // seed the random number generator
373     sg_srandom_time();
374
375     string_list *col = new string_list;
376     globals->set_channel_options_list( col );
377
378     fgValidatePath("", false);  // initialize static variables
379     upper_case_property("/sim/presets/airport-id");
380     upper_case_property("/sim/presets/runway");
381     upper_case_property("/sim/tower/airport-id");
382     upper_case_property("/autopilot/route-manager/input");
383
384     // Load the configuration parameters.  (Command line options
385     // override config file options.  Config file options override
386     // defaults.)
387     int configResult = fgInitConfig(argc, argv, false);
388     if (configResult == flightgear::FG_OPTIONS_ERROR) {
389         return EXIT_FAILURE;
390     } else if (configResult == flightgear::FG_OPTIONS_EXIT) {
391         return EXIT_SUCCESS;
392     }
393     
394     configResult = fgInitAircraft(false);
395     if (configResult == flightgear::FG_OPTIONS_ERROR) {
396         return EXIT_FAILURE;
397     } else if (configResult == flightgear::FG_OPTIONS_EXIT) {
398         return EXIT_SUCCESS;
399     }
400     
401     configResult = flightgear::Options::sharedInstance()->processOptions();
402     if (configResult == flightgear::FG_OPTIONS_ERROR) {
403         return EXIT_FAILURE;
404     } else if (configResult == flightgear::FG_OPTIONS_EXIT) {
405         return EXIT_SUCCESS;
406     }
407     
408     // Initialize the Window/Graphics environment.
409     fgOSInit(&argc, argv);
410     _bootstrap_OSInit++;
411
412     fgRegisterIdleHandler( &fgIdleFunction );
413
414     // Initialize sockets (WinSock needs this)
415     simgear::Socket::initSockets();
416
417     // Clouds3D requires an alpha channel
418     fgOSOpenWindow(true /* request stencil buffer */);
419     fgOSResetProperties();
420     
421     // Initialize the splash screen right away
422     fntInit();
423     fgSplashInit();
424
425     if (fgGetBool("/sim/ati-viewport-hack", true)) {
426         SG_LOG(SG_GENERAL, SG_ALERT, "Enabling ATI viewport hack");
427         ATIScreenSizeHack();
428     }
429     
430     fgOutputSettings();
431     
432     // pass control off to the master event handler
433     int result = fgOSMainLoop();
434     frame_signal.clear();
435     fgOSCloseWindow();
436     
437     simgear::clearEffectCache();
438     
439     // clean up here; ensure we null globals to avoid
440     // confusing the atexit() handler
441     delete globals;
442     globals = NULL;
443     
444     // delete the NavCache here. This will cause the destruction of many cached
445     // objects (eg, airports, navaids, runways).
446     delete flightgear::NavDataCache::instance();
447   
448     return result;
449 }