]> git.mxchange.org Git - flightgear.git/blob - src/Main/globals.cxx
NavDisplay: fix update lag when switching range or centre.
[flightgear.git] / src / Main / globals.cxx
1 // globals.cxx -- Global state that needs to be shared among the sim modules
2 //
3 // Written by Curtis Olson, started July 2000.
4 //
5 // Copyright (C) 2000  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 Foundation,
19 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26
27 #include <boost/foreach.hpp>
28 #include <algorithm>
29
30 #include <simgear/structure/commands.hxx>
31 #include <simgear/misc/sg_path.hxx>
32 #include <simgear/misc/sg_dir.hxx>
33 #include <simgear/timing/sg_time.hxx>
34 #include <simgear/ephemeris/ephemeris.hxx>
35 #include <simgear/magvar/magvar.hxx>
36 #include <simgear/scene/material/matlib.hxx>
37 #include <simgear/structure/subsystem_mgr.hxx>
38 #include <simgear/structure/event_mgr.hxx>
39 #include <simgear/sound/soundmgr_openal.hxx>
40 #include <simgear/misc/ResourceManager.hxx>
41 #include <simgear/props/propertyObject.hxx>
42 #include <simgear/props/props_io.hxx>
43 #include <simgear/scene/model/placement.hxx>
44
45 #include <Aircraft/controls.hxx>
46 #include <Airports/runways.hxx>
47 #include <ATCDCL/ATCmgr.hxx>
48 #include <Autopilot/route_mgr.hxx>
49 #include <Cockpit/panel.hxx>
50 #include <GUI/FGFontCache.hxx>
51 #include <GUI/gui.h>
52 #include <Model/acmodel.hxx>
53 #include <Model/modelmgr.hxx>
54 #include <MultiPlayer/multiplaymgr.hxx>
55 #include <Scenery/scenery.hxx>
56 #include <Scenery/tilemgr.hxx>
57 #include <Navaids/navlist.hxx>
58
59 #include "globals.hxx"
60 #include "renderer.hxx"
61 #include "viewmgr.hxx"
62
63 #include "fg_props.hxx"
64 #include "fg_io.hxx"
65
66 class AircraftResourceProvider : public simgear::ResourceProvider
67 {
68 public:
69   AircraftResourceProvider() :
70     simgear::ResourceProvider(simgear::ResourceManager::PRIORITY_HIGH)
71   {
72   }
73   
74   virtual SGPath resolve(const std::string& aResource, SGPath&) const
75   {
76     string_list pieces(sgPathBranchSplit(aResource));
77     if ((pieces.size() < 3) || (pieces.front() != "Aircraft")) {
78       return SGPath(); // not an Aircraft path
79     }
80     
81   // test against the aircraft-dir property
82     const char* aircraftDir = fgGetString("/sim/aircraft-dir");
83     string_list aircraftDirPieces(sgPathBranchSplit(aircraftDir));
84     if (!aircraftDirPieces.empty() && (aircraftDirPieces.back() == pieces[1])) {
85         // current aircraft-dir matches resource aircraft
86         SGPath r(aircraftDir);
87         for (unsigned int i=2; i<pieces.size(); ++i) {
88           r.append(pieces[i]);
89         }
90         
91         if (r.exists()) {
92           SG_LOG(SG_IO, SG_DEBUG, "found path:" << aResource << " via /sim/aircraft-dir: " << r.str());
93           return r;
94         }
95     }
96   
97   // try each aircraft dir in turn
98     std::string res(aResource, 9); // resource path with 'Aircraft/' removed
99     const string_list& dirs(globals->get_aircraft_paths());
100     string_list::const_iterator it = dirs.begin();
101     for (; it != dirs.end(); ++it) {
102       SGPath p(*it, res);
103       if (p.exists()) {
104         SG_LOG(SG_IO, SG_DEBUG, "found path:" << aResource << " in aircraft dir: " << *it);
105         return p;
106       }
107     } // of aircraft path iteration
108     
109     return SGPath(); // not found
110   }
111 };
112
113 ////////////////////////////////////////////////////////////////////////
114 // Implementation of FGGlobals.
115 ////////////////////////////////////////////////////////////////////////
116
117 // global global :-)
118 FGGlobals *globals;
119
120
121 // Constructor
122 FGGlobals::FGGlobals() :
123     props( new SGPropertyNode ),
124     initial_state( NULL ),
125     locale( NULL ),
126     renderer( new FGRenderer ),
127     subsystem_mgr( new SGSubsystemMgr ),
128     event_mgr( new SGEventMgr ),
129     sim_time_sec( 0.0 ),
130     fg_root( "" ),
131     time_params( NULL ),
132     ephem( NULL ),
133     mag( NULL ),
134     matlib( NULL ),
135     route_mgr( NULL ),
136     current_panel( NULL ),
137     ATC_mgr( NULL ),
138     controls( NULL ),
139     viewmgr( NULL ),
140     commands( SGCommandMgr::instance() ),
141     acmodel( NULL ),
142     model_mgr( NULL ),
143     channel_options_list( NULL ),
144     initial_waypoints( NULL ),
145     scenery( NULL ),
146     tile_mgr( NULL ),
147     fontcache ( new FGFontCache ),
148     navlist( NULL ),
149     loclist( NULL ),
150     gslist( NULL ),
151     dmelist( NULL ),
152     tacanlist( NULL ),
153     carrierlist( NULL ),
154     channellist( NULL ),
155     haveUserSettings(false)
156 {
157   simgear::ResourceManager::instance()->addProvider(new AircraftResourceProvider());
158   simgear::PropertyObjectBase::setDefaultRoot(props);
159 }
160
161 // Destructor
162 FGGlobals::~FGGlobals() 
163 {
164     // save user settings (unless already saved)
165     saveUserSettings();
166
167     // The AIModels manager performs a number of actions upon
168     // Shutdown that implicitly assume that other subsystems
169     // are still operational (Due to the dynamic allocation and
170     // deallocation of AIModel objects. To ensure we can safely
171     // shut down all subsystems, make sure we take down the 
172     // AIModels system first.
173     SGSubsystem* ai = subsystem_mgr->remove("ai-model");
174     if (ai) {
175         ai->unbind();
176         delete ai;
177     }
178     SGSubsystem* sound = subsystem_mgr->remove("sound");
179
180     subsystem_mgr->shutdown();
181     subsystem_mgr->unbind();
182     delete subsystem_mgr;
183     
184     delete renderer;
185     renderer = NULL;
186     
187     delete time_params;
188     delete mag;
189     delete matlib;
190     delete route_mgr;
191     delete current_panel;
192
193     delete ATC_mgr;
194
195     if (controls)
196     {
197         controls->unbind();
198         delete controls;
199     }
200
201     delete channel_options_list;
202     delete initial_waypoints;
203     delete scenery;
204     delete fontcache;
205
206     delete navlist;
207     delete loclist;
208     delete gslist;
209     delete dmelist;
210     delete tacanlist;
211     delete carrierlist;
212     delete channellist;
213     delete sound;
214 }
215
216
217 // set the fg_root path
218 void FGGlobals::set_fg_root (const string &root) {
219     fg_root = root;
220
221     // append /data to root if it exists
222     SGPath tmp( fg_root );
223     tmp.append( "data" );
224     tmp.append( "version" );
225     if ( tmp.exists() ) {
226         fgGetNode("BAD_FG_ROOT", true)->setStringValue(fg_root);
227         fg_root += "/data";
228         fgGetNode("GOOD_FG_ROOT", true)->setStringValue(fg_root);
229         SG_LOG(SG_GENERAL, SG_ALERT, "***\n***\n*** Warning: changing bad FG_ROOT/--fg-root to '"
230                 << fg_root << "'\n***\n***");
231     }
232
233     // remove /sim/fg-root before writing to prevent hijacking
234     SGPropertyNode *n = fgGetNode("/sim", true);
235     n->removeChild("fg-root", 0, false);
236     n = n->getChild("fg-root", 0, true);
237     n->setStringValue(fg_root.c_str());
238     n->setAttribute(SGPropertyNode::WRITE, false);
239     
240     simgear::ResourceManager::instance()->addBasePath(fg_root, 
241       simgear::ResourceManager::PRIORITY_DEFAULT);
242 }
243
244 void FGGlobals::append_fg_scenery (const string &paths)
245 {
246 //    fg_scenery.clear();
247     SGPropertyNode* sim = fgGetNode("/sim", true);
248
249   // find first unused fg-scenery property in /sim
250     int propIndex = 0;
251     while (sim->getChild("fg-scenery", propIndex) != NULL) {
252       ++propIndex; 
253     }
254   
255     BOOST_FOREACH(const SGPath& path, sgPathSplit( paths )) {
256         if (!path.exists()) {
257           SG_LOG(SG_GENERAL, SG_WARN, "scenery path not found:" << path.str());
258           continue;
259         }
260
261       // check for duplicates
262       string_list::const_iterator ex = std::find(fg_scenery.begin(), fg_scenery.end(), path.str());
263       if (ex != fg_scenery.end()) {
264         SG_LOG(SG_GENERAL, SG_INFO, "skipping duplicate add of scenery path:" << path.str());
265         continue;
266       }
267       
268         simgear::Dir dir(path);
269         SGPath terrainDir(dir.file("Terrain"));
270         SGPath objectsDir(dir.file("Objects"));
271         
272       // this code used to add *either* the base dir, OR add the 
273       // Terrain and Objects subdirs, but the conditional logic was commented
274       // out, such that all three dirs are added. Unfortunately there's
275       // no information as to why the change was made.
276         fg_scenery.push_back(path.str());
277         
278         if (terrainDir.exists()) {
279           fg_scenery.push_back(terrainDir.str());
280         }
281         
282         if (objectsDir.exists()) {
283           fg_scenery.push_back(objectsDir.str());
284         }
285         
286         // insert a marker for FGTileEntry::load(), so that
287         // FG_SCENERY=A:B becomes list ["A/Terrain", "A/Objects", "",
288         // "B/Terrain", "B/Objects", ""]
289         fg_scenery.push_back("");
290         
291       // make scenery dirs available to Nasal
292         SGPropertyNode* n = sim->getChild("fg-scenery", propIndex++, true);
293         n->setStringValue(path.str());
294         n->setAttribute(SGPropertyNode::WRITE, false);
295     } // of path list iteration
296 }
297
298 void FGGlobals::append_aircraft_path(const std::string& path)
299 {
300   SGPath dirPath(path);
301   if (!dirPath.exists()) {
302     SG_LOG(SG_GENERAL, SG_WARN, "aircraft path not found:" << path);
303     return;
304   }
305   
306   unsigned int index = fg_aircraft_dirs.size();  
307   fg_aircraft_dirs.push_back(path);
308   
309 // make aircraft dirs available to Nasal
310   SGPropertyNode* sim = fgGetNode("/sim", true);
311   sim->removeChild("fg-aircraft", index, false);
312   SGPropertyNode* n = sim->getChild("fg-aircraft", index, true);
313   n->setStringValue(path);
314   n->setAttribute(SGPropertyNode::WRITE, false);
315 }
316
317 void FGGlobals::append_aircraft_paths(const std::string& path)
318 {
319   string_list paths = sgPathSplit(path);
320   for (unsigned int p = 0; p<paths.size(); ++p) {
321     append_aircraft_path(paths[p]);
322   }
323 }
324
325 SGPath FGGlobals::resolve_aircraft_path(const std::string& branch) const
326 {
327   return simgear::ResourceManager::instance()->findPath(branch);
328 }
329
330 SGPath FGGlobals::resolve_maybe_aircraft_path(const std::string& branch) const
331 {
332   return simgear::ResourceManager::instance()->findPath(branch);
333 }
334
335 FGRenderer *
336 FGGlobals::get_renderer () const
337 {
338    return renderer;
339 }
340
341 SGSubsystemMgr *
342 FGGlobals::get_subsystem_mgr () const
343 {
344     return subsystem_mgr;
345 }
346
347 SGSubsystem *
348 FGGlobals::get_subsystem (const char * name)
349 {
350     return subsystem_mgr->get_subsystem(name);
351 }
352
353 void
354 FGGlobals::add_subsystem (const char * name,
355                           SGSubsystem * subsystem,
356                           SGSubsystemMgr::GroupType type,
357                           double min_time_sec)
358 {
359     subsystem_mgr->add(name, subsystem, type, min_time_sec);
360 }
361
362 SGSoundMgr *
363 FGGlobals::get_soundmgr () const
364 {
365     if (subsystem_mgr)
366         return (SGSoundMgr*) subsystem_mgr->get_subsystem("sound");
367
368     return NULL;
369 }
370
371 SGEventMgr *
372 FGGlobals::get_event_mgr () const
373 {
374     return event_mgr;
375 }
376
377 const SGGeod &
378 FGGlobals::get_aircraft_position() const
379 {
380     if( acmodel != NULL ) {
381         SGModelPlacement * mp = acmodel->get3DModel();
382         if( mp != NULL )
383             return mp->getPosition();
384     }
385     throw sg_exception("Can't get aircraft position", "FGGlobals::get_aircraft_position()" );
386 }
387
388 SGVec3d
389 FGGlobals::get_aircraft_positon_cart() const
390 {
391     return SGVec3d::fromGeod(get_aircraft_position());
392 }
393
394
395 // Save the current state as the initial state.
396 void
397 FGGlobals::saveInitialState ()
398 {
399   initial_state = new SGPropertyNode();
400
401   // copy properties which are READ/WRITEable - but not USERARCHIVEd or PRESERVEd
402   int checked  = SGPropertyNode::READ+SGPropertyNode::WRITE+
403                  SGPropertyNode::USERARCHIVE+SGPropertyNode::PRESERVE;
404   int expected = SGPropertyNode::READ+SGPropertyNode::WRITE;
405   if (!copyProperties(props, initial_state, expected, checked))
406     SG_LOG(SG_GENERAL, SG_ALERT, "Error saving initial state");
407     
408   // delete various properties from the initial state, since we want to
409   // preserve their values even if doing a restore
410   // => Properties should now use the PRESERVE flag to protect their values
411   // on sim-reset. Remove some specific properties for backward compatibility.
412   SGPropertyNode* sim = initial_state->getChild("sim");
413   SGPropertyNode* cameraGroupNode = sim->getNode("rendering/camera-group");
414   if (cameraGroupNode) {
415     cameraGroupNode->removeChild("camera");
416     cameraGroupNode->removeChild("gui");
417   }
418 }
419
420
421 // Restore the saved initial state, if any
422 void
423 FGGlobals::restoreInitialState ()
424 {
425     if ( initial_state == 0 ) {
426         SG_LOG(SG_GENERAL, SG_ALERT,
427                "No initial state available to restore!!!");
428         return;
429     }
430     // copy properties which are READ/WRITEable - but not USERARCHIVEd or PRESERVEd
431     int checked  = SGPropertyNode::READ+SGPropertyNode::WRITE+
432                    SGPropertyNode::USERARCHIVE+SGPropertyNode::PRESERVE;
433     int expected = SGPropertyNode::READ+SGPropertyNode::WRITE;
434     if ( copyProperties(initial_state, props, expected, checked)) {
435         SG_LOG( SG_GENERAL, SG_INFO, "Initial state restored successfully" );
436     } else {
437         SG_LOG( SG_GENERAL, SG_INFO,
438                 "Some errors restoring initial state (read-only props?)" );
439     }
440
441 }
442
443 // Load user settings from autosave.xml
444 void
445 FGGlobals::loadUserSettings()
446 {
447     // dummy method for now.
448     //TODO Move code loading autosave.xml in here after the 2.6.0 release.
449     haveUserSettings = true;
450 }
451
452 // Save user settings in autosave.xml
453 void
454 FGGlobals::saveUserSettings()
455 {
456     // only save settings when we have (tried) to load the previous
457     // settings (otherwise user data was lost)
458     if (!haveUserSettings)
459         return;
460
461     if (fgGetBool("/sim/startup/save-on-exit")) {
462       // don't save settings more than once on shutdown
463       haveUserSettings = false;
464
465       SGPath autosaveFile(fgGetString("/sim/fg-home"));
466       autosaveFile.append( "autosave.xml" );
467       autosaveFile.create_dir( 0700 );
468       SG_LOG(SG_IO, SG_INFO, "Saving user settings to " << autosaveFile.str());
469       try {
470         writeProperties(autosaveFile.str(), globals->get_props(), false, SGPropertyNode::USERARCHIVE);
471       } catch (const sg_exception &e) {
472         guiErrorMessage("Error writing autosave.xml: ", e);
473       }
474       SG_LOG(SG_INPUT, SG_DEBUG, "Finished Saving user settings");
475     }
476 }
477
478 FGViewer *
479 FGGlobals::get_current_view () const
480 {
481   return viewmgr->get_current_view();
482 }
483
484 long int FGGlobals::get_warp() const
485 {
486   return fgGetInt("/sim/time/warp");
487 }
488
489 void FGGlobals::set_warp( long int w )
490 {
491   fgSetInt("/sim/time/warp", w);
492 }
493
494 long int FGGlobals::get_warp_delta() const
495 {
496   return fgGetInt("/sim/time/warp-delta");
497 }
498
499 void FGGlobals::set_warp_delta( long int d )
500 {
501   fgSetInt("/sim/time/warp-delta", d);
502 }
503     
504 // end of globals.cxx