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