]> git.mxchange.org Git - flightgear.git/blob - src/Main/globals.cxx
Reset: listener cleanup
[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 #include <simgear/scene/model/modellib.hxx>
43
44 #include <Aircraft/controls.hxx>
45 #include <Airports/runways.hxx>
46 #include <ATCDCL/ATISmgr.hxx>
47 #include <Autopilot/route_mgr.hxx>
48 #include <GUI/FGFontCache.hxx>
49 #include <GUI/gui.h>
50 #include <MultiPlayer/multiplaymgr.hxx>
51 #include <Scenery/scenery.hxx>
52 #include <Scenery/tilemgr.hxx>
53 #include <Navaids/navlist.hxx>
54 #include <Viewer/renderer.hxx>
55 #include <Viewer/viewmgr.hxx>
56 #include <Sound/sample_queue.hxx>
57
58 #include "globals.hxx"
59 #include "locale.hxx"
60
61 #include "fg_props.hxx"
62 #include "fg_io.hxx"
63
64 class AircraftResourceProvider : public simgear::ResourceProvider
65 {
66 public:
67   AircraftResourceProvider() :
68     simgear::ResourceProvider(simgear::ResourceManager::PRIORITY_HIGH)
69   {
70   }
71   
72   virtual SGPath resolve(const std::string& aResource, SGPath&) const
73   {
74     string_list pieces(sgPathBranchSplit(aResource));
75     if ((pieces.size() < 3) || (pieces.front() != "Aircraft")) {
76       return SGPath(); // not an Aircraft path
77     }
78     
79   // test against the aircraft-dir property
80     const char* aircraftDir = fgGetString("/sim/aircraft-dir");
81     string_list aircraftDirPieces(sgPathBranchSplit(aircraftDir));
82     if (!aircraftDirPieces.empty() && (aircraftDirPieces.back() == pieces[1])) {
83         // current aircraft-dir matches resource aircraft
84         SGPath r(aircraftDir);
85         for (unsigned int i=2; i<pieces.size(); ++i) {
86           r.append(pieces[i]);
87         }
88         
89         if (r.exists()) {
90           return r;
91         }
92     }
93   
94   // try each aircraft dir in turn
95     std::string res(aResource, 9); // resource path with 'Aircraft/' removed
96     const string_list& dirs(globals->get_aircraft_paths());
97     string_list::const_iterator it = dirs.begin();
98     for (; it != dirs.end(); ++it) {
99       SGPath p(*it, res);
100       if (p.exists()) {
101         return p;
102       }
103     } // of aircraft path iteration
104     
105     return SGPath(); // not found
106   }
107 };
108
109 class CurrentAircraftDirProvider : public simgear::ResourceProvider
110 {
111 public:
112   CurrentAircraftDirProvider() :
113     simgear::ResourceProvider(simgear::ResourceManager::PRIORITY_HIGH)
114   {
115   }
116   
117   virtual SGPath resolve(const std::string& aResource, SGPath&) const
118   {
119     const char* aircraftDir = fgGetString("/sim/aircraft-dir");
120     SGPath p(aircraftDir);
121     p.append(aResource);
122     return p.exists() ? p : SGPath();
123   }
124 };
125
126 ////////////////////////////////////////////////////////////////////////
127 // Implementation of FGGlobals.
128 ////////////////////////////////////////////////////////////////////////
129
130 // global global :-)
131 FGGlobals *globals = NULL;
132
133
134 // Constructor
135 FGGlobals::FGGlobals() :
136     initial_state( NULL ),
137     renderer( new FGRenderer ),
138     subsystem_mgr( new SGSubsystemMgr ),
139     event_mgr( new SGEventMgr ),
140     sim_time_sec( 0.0 ),
141     fg_root( "" ),
142     fg_home( "" ),
143     time_params( NULL ),
144     ephem( NULL ),
145     matlib( NULL ),
146     route_mgr( NULL ),
147     ATIS_mgr( NULL ),
148     controls( NULL ),
149     viewmgr( NULL ),
150     commands( SGCommandMgr::instance() ),
151     channel_options_list( NULL ),
152     initial_waypoints( NULL ),
153     fontcache ( new FGFontCache ),
154     channellist( NULL ),
155     haveUserSettings(false),
156     _chatter_queue(NULL)
157 {
158     SGPropertyNode* root = new SGPropertyNode;
159     props = SGPropertyNode_ptr(root);
160     locale = new FGLocale(props);
161     
162     simgear::ResourceManager::instance()->addProvider(new AircraftResourceProvider);
163     simgear::ResourceManager::instance()->addProvider(new CurrentAircraftDirProvider);
164     initProperties();
165 }
166
167 void FGGlobals::initProperties()
168 {
169     simgear::PropertyObjectBase::setDefaultRoot(props);
170     
171     positionLon = props->getNode("position/longitude-deg", true);
172     positionLat = props->getNode("position/latitude-deg", true);
173     positionAlt = props->getNode("position/altitude-ft", true);
174     
175     viewLon = props->getNode("sim/current-view/viewer-lon-deg", true);
176     viewLat = props->getNode("sim/current-view/viewer-lat-deg", true);
177     viewAlt = props->getNode("sim/current-view/viewer-elev-ft", true);
178     
179     orientPitch = props->getNode("orientation/pitch-deg", true);
180     orientHeading = props->getNode("orientation/heading-deg", true);
181     orientRoll = props->getNode("orientation/roll-deg", true);
182
183 }
184
185 // Destructor
186 FGGlobals::~FGGlobals() 
187 {
188     // save user settings (unless already saved)
189     saveUserSettings();
190
191     // The AIModels manager performs a number of actions upon
192     // Shutdown that implicitly assume that other subsystems
193     // are still operational (Due to the dynamic allocation and
194     // deallocation of AIModel objects. To ensure we can safely
195     // shut down all subsystems, make sure we take down the 
196     // AIModels system first.
197     SGSubsystemRef ai = subsystem_mgr->get_subsystem("ai-model");
198     if (ai) {
199         subsystem_mgr->remove("ai-model");
200         ai->unbind();
201         ai.clear(); // ensure AI is deleted now, not at end of this method
202     }
203     
204     subsystem_mgr->shutdown();
205     subsystem_mgr->unbind();    
206
207     subsystem_mgr->remove("aircraft-model");
208     subsystem_mgr->remove("tile-manager");
209     subsystem_mgr->remove("model-manager");
210     _tile_mgr.clear();
211
212     // renderer touches subsystems during its destruction
213     set_renderer(NULL);
214     _scenery.clear();
215     _chatter_queue.clear();
216     
217     delete subsystem_mgr;
218     subsystem_mgr = NULL; // important so ::get_subsystem returns NULL 
219
220     delete time_params;
221     set_matlib(NULL);
222     delete route_mgr;
223     delete ATIS_mgr;
224     delete channel_options_list;
225     delete initial_waypoints;
226     delete fontcache;
227     delete channellist;
228
229     simgear::PropertyObjectBase::setDefaultRoot(NULL);
230     simgear::SGModelLib::resetPropertyRoot();
231     
232     delete locale;
233     locale = NULL;
234     
235     cleanupListeners();
236     
237     props.clear();
238     
239     delete commands;
240 }
241
242 // set the fg_root path
243 void FGGlobals::set_fg_root (const std::string &root) {
244     SGPath tmp(root);
245     fg_root = tmp.realpath();
246
247     // append /data to root if it exists
248     tmp.append( "data" );
249     tmp.append( "version" );
250     if ( tmp.exists() ) {
251         fgGetNode("BAD_FG_ROOT", true)->setStringValue(fg_root);
252         fg_root += "/data";
253         fgGetNode("GOOD_FG_ROOT", true)->setStringValue(fg_root);
254         SG_LOG(SG_GENERAL, SG_ALERT, "***\n***\n*** Warning: changing bad FG_ROOT/--fg-root to '"
255                 << fg_root << "'\n***\n***");
256     }
257
258     // remove /sim/fg-root before writing to prevent hijacking
259     SGPropertyNode *n = fgGetNode("/sim", true);
260     n->removeChild("fg-root", 0, false);
261     n = n->getChild("fg-root", 0, true);
262     n->setStringValue(fg_root.c_str());
263     n->setAttribute(SGPropertyNode::WRITE, false);
264     
265     simgear::ResourceManager::instance()->addBasePath(fg_root, 
266       simgear::ResourceManager::PRIORITY_DEFAULT);
267 }
268
269 // set the fg_home path
270 void FGGlobals::set_fg_home (const std::string &home) {
271     SGPath tmp(home);
272     fg_home = tmp.realpath();
273 }
274
275 PathList FGGlobals::get_data_paths() const
276 {
277     PathList r(additional_data_paths);
278     r.push_back(SGPath(fg_root));
279     return r;
280 }
281
282 PathList FGGlobals::get_data_paths(const std::string& suffix) const
283 {
284     PathList r;
285     BOOST_FOREACH(SGPath p, get_data_paths()) {
286         p.append(suffix);
287         if (p.exists()) {
288             r.push_back(p);
289         }
290     }
291
292     return r;
293 }
294
295 void FGGlobals::append_data_path(const SGPath& path)
296 {
297     if (!path.exists()) {
298         SG_LOG(SG_GENERAL, SG_WARN, "adding non-existant data path:" << path);
299     }
300     
301     additional_data_paths.push_back(path);
302 }
303
304 SGPath FGGlobals::find_data_dir(const std::string& pathSuffix) const
305 {
306     BOOST_FOREACH(SGPath p, additional_data_paths) {
307         p.append(pathSuffix);
308         if (p.exists()) {
309             return p;
310         }
311     }
312     
313     SGPath rootPath(fg_root);
314     rootPath.append(pathSuffix);
315     if (rootPath.exists()) {
316         return rootPath;
317     }
318     
319     SG_LOG(SG_GENERAL, SG_WARN, "dir not found in any data path:" << pathSuffix);
320     return SGPath();
321 }
322
323 void FGGlobals::append_fg_scenery (const std::string &paths)
324 {
325 //    fg_scenery.clear();
326     SGPropertyNode* sim = fgGetNode("/sim", true);
327
328   // find first unused fg-scenery property in /sim
329     int propIndex = 0;
330     while (sim->getChild("fg-scenery", propIndex) != NULL) {
331       ++propIndex; 
332     }
333   
334     BOOST_FOREACH(const SGPath& path, sgPathSplit( paths )) {
335         SGPath abspath(path.realpath());
336         if (!abspath.exists()) {
337           SG_LOG(SG_GENERAL, SG_WARN, "scenery path not found:" << abspath.str());
338           continue;
339         }
340
341       // check for duplicates
342       string_list::const_iterator ex = std::find(fg_scenery.begin(), fg_scenery.end(), abspath.str());
343       if (ex != fg_scenery.end()) {
344         SG_LOG(SG_GENERAL, SG_INFO, "skipping duplicate add of scenery path:" << abspath.str());
345         continue;
346       }
347       
348         simgear::Dir dir(abspath);
349         SGPath terrainDir(dir.file("Terrain"));
350         SGPath objectsDir(dir.file("Objects"));
351         
352       // this code used to add *either* the base dir, OR add the 
353       // Terrain and Objects subdirs, but the conditional logic was commented
354       // out, such that all three dirs are added. Unfortunately there's
355       // no information as to why the change was made.
356         fg_scenery.push_back(abspath.str());
357         
358         if (terrainDir.exists()) {
359           fg_scenery.push_back(terrainDir.str());
360         }
361         
362         if (objectsDir.exists()) {
363           fg_scenery.push_back(objectsDir.str());
364         }
365         
366         // insert a marker for FGTileEntry::load(), so that
367         // FG_SCENERY=A:B becomes list ["A/Terrain", "A/Objects", "",
368         // "B/Terrain", "B/Objects", ""]
369         fg_scenery.push_back("");
370         
371       // make scenery dirs available to Nasal
372         SGPropertyNode* n = sim->getChild("fg-scenery", propIndex++, true);
373         n->setStringValue(abspath.str());
374         n->setAttribute(SGPropertyNode::WRITE, false);
375     } // of path list iteration
376 }
377
378 void FGGlobals::append_aircraft_path(const std::string& path)
379 {
380   SGPath dirPath(path);
381   if (!dirPath.exists()) {
382     SG_LOG(SG_GENERAL, SG_WARN, "aircraft path not found:" << path);
383     return;
384   }
385   std::string abspath = dirPath.realpath();
386   
387   unsigned int index = fg_aircraft_dirs.size();  
388   fg_aircraft_dirs.push_back(abspath);
389   
390 // make aircraft dirs available to Nasal
391   SGPropertyNode* sim = fgGetNode("/sim", true);
392   sim->removeChild("fg-aircraft", index, false);
393   SGPropertyNode* n = sim->getChild("fg-aircraft", index, true);
394   n->setStringValue(abspath);
395   n->setAttribute(SGPropertyNode::WRITE, false);
396 }
397
398 void FGGlobals::append_aircraft_paths(const std::string& path)
399 {
400   string_list paths = sgPathSplit(path);
401   for (unsigned int p = 0; p<paths.size(); ++p) {
402     append_aircraft_path(paths[p]);
403   }
404 }
405
406 SGPath FGGlobals::resolve_aircraft_path(const std::string& branch) const
407 {
408   return simgear::ResourceManager::instance()->findPath(branch);
409 }
410
411 SGPath FGGlobals::resolve_maybe_aircraft_path(const std::string& branch) const
412 {
413   return simgear::ResourceManager::instance()->findPath(branch);
414 }
415
416 SGPath FGGlobals::resolve_resource_path(const std::string& branch) const
417 {
418   return simgear::ResourceManager::instance()
419     ->findPath(branch, SGPath(fgGetString("/sim/aircraft-dir")));
420 }
421
422 FGRenderer *
423 FGGlobals::get_renderer () const
424 {
425    return renderer;
426 }
427
428 void FGGlobals::set_renderer(FGRenderer *render)
429 {
430     if (render == renderer) {
431         return;
432     }
433     
434     delete renderer;
435     renderer = render;
436 }
437
438 SGSubsystemMgr *
439 FGGlobals::get_subsystem_mgr () const
440 {
441     return subsystem_mgr;
442 }
443
444 SGSubsystem *
445 FGGlobals::get_subsystem (const char * name)
446 {
447     if (!subsystem_mgr) {
448         return NULL;
449     }
450     
451     return subsystem_mgr->get_subsystem(name);
452 }
453
454 void
455 FGGlobals::add_subsystem (const char * name,
456                           SGSubsystem * subsystem,
457                           SGSubsystemMgr::GroupType type,
458                           double min_time_sec)
459 {
460     subsystem_mgr->add(name, subsystem, type, min_time_sec);
461 }
462
463 SGSoundMgr *
464 FGGlobals::get_soundmgr () const
465 {
466     if (subsystem_mgr)
467         return (SGSoundMgr*) subsystem_mgr->get_subsystem("sound");
468
469     return NULL;
470 }
471
472 SGEventMgr *
473 FGGlobals::get_event_mgr () const
474 {
475     return event_mgr;
476 }
477
478 SGGeod
479 FGGlobals::get_aircraft_position() const
480 {
481   return SGGeod::fromDegFt(positionLon->getDoubleValue(),
482                            positionLat->getDoubleValue(),
483                            positionAlt->getDoubleValue());
484 }
485
486 SGVec3d
487 FGGlobals::get_aircraft_position_cart() const
488 {
489     return SGVec3d::fromGeod(get_aircraft_position());
490 }
491
492 void FGGlobals::get_aircraft_orientation(double& heading, double& pitch, double& roll)
493 {
494   heading = orientHeading->getDoubleValue();
495   pitch = orientPitch->getDoubleValue();
496   roll = orientRoll->getDoubleValue();
497 }
498
499 SGGeod
500 FGGlobals::get_view_position() const
501 {
502   return SGGeod::fromDegFt(viewLon->getDoubleValue(),
503                            viewLat->getDoubleValue(),
504                            viewAlt->getDoubleValue());
505 }
506
507 SGVec3d
508 FGGlobals::get_view_position_cart() const
509 {
510   return SGVec3d::fromGeod(get_view_position());
511 }
512
513 static void treeDumpRefCounts(int depth, SGPropertyNode* nd)
514 {
515     for (int i=0; i<nd->nChildren(); ++i) {
516         SGPropertyNode* cp = nd->getChild(i);
517         if (SGReferenced::count(cp) > 1) {
518             SG_LOG(SG_GENERAL, SG_INFO, "\t" << cp->getPath() << " refcount:" << SGReferenced::count(cp));
519         }
520         
521         treeDumpRefCounts(depth + 1, cp);
522     }
523 }
524
525 void
526 FGGlobals::resetPropertyRoot()
527 {
528     delete locale;
529     
530     cleanupListeners();
531     
532     // we don't strictly need to clear these (they will be reset when we
533     // initProperties again), but trying to reduce false-positives when dumping
534     // ref-counts.
535     positionLon.clear();
536     positionLat.clear();
537     positionAlt.clear();
538     viewLon.clear();
539     viewLat.clear();
540     viewAlt.clear();
541     orientPitch.clear();
542     orientHeading.clear();
543     orientRoll.clear();
544     
545     SG_LOG(SG_GENERAL, SG_INFO, "root props refcount:" << props.getNumRefs());
546     treeDumpRefCounts(0, props);
547
548     //BaseStackSnapshot::dumpAll(std::cout);
549     
550     props = new SGPropertyNode;
551     initProperties();
552     locale = new FGLocale(props);
553     
554     // remove /sim/fg-root before writing to prevent hijacking
555     SGPropertyNode *n = props->getNode("/sim", true);
556     n->removeChild("fg-root", 0, false);
557     n = n->getChild("fg-root", 0, true);
558     n->setStringValue(fg_root.c_str());
559     n->setAttribute(SGPropertyNode::WRITE, false);
560 }
561
562 // Save the current state as the initial state.
563 void
564 FGGlobals::saveInitialState ()
565 {
566   initial_state = new SGPropertyNode();
567
568   // copy properties which are READ/WRITEable - but not USERARCHIVEd or PRESERVEd
569   int checked  = SGPropertyNode::READ+SGPropertyNode::WRITE+
570                  SGPropertyNode::USERARCHIVE+SGPropertyNode::PRESERVE;
571   int expected = SGPropertyNode::READ+SGPropertyNode::WRITE;
572   if (!copyProperties(props, initial_state, expected, checked))
573     SG_LOG(SG_GENERAL, SG_ALERT, "Error saving initial state");
574     
575   // delete various properties from the initial state, since we want to
576   // preserve their values even if doing a restore
577   // => Properties should now use the PRESERVE flag to protect their values
578   // on sim-reset. Remove some specific properties for backward compatibility.
579   SGPropertyNode* sim = initial_state->getChild("sim");
580   SGPropertyNode* cameraGroupNode = sim->getNode("rendering/camera-group");
581   if (cameraGroupNode) {
582     cameraGroupNode->removeChild("camera");
583     cameraGroupNode->removeChild("gui");
584   }
585 }
586
587 static std::string autosaveName()
588 {
589     std::ostringstream os;
590     string_list versionParts = simgear::strutils::split(VERSION, ".");
591     if (versionParts.size() < 2) {
592         return "autosave.xml";
593     }
594     
595     os << "autosave_" << versionParts[0] << "_" << versionParts[1] << ".xml";
596     return os.str();
597 }
598
599 // Restore the saved initial state, if any
600 void
601 FGGlobals::restoreInitialState ()
602 {
603     if ( initial_state == 0 ) {
604         SG_LOG(SG_GENERAL, SG_ALERT,
605                "No initial state available to restore!!!");
606         return;
607     }
608     // copy properties which are READ/WRITEable - but not USERARCHIVEd or PRESERVEd
609     int checked  = SGPropertyNode::READ+SGPropertyNode::WRITE+
610                    SGPropertyNode::USERARCHIVE+SGPropertyNode::PRESERVE;
611     int expected = SGPropertyNode::READ+SGPropertyNode::WRITE;
612     if ( copyProperties(initial_state, props, expected, checked)) {
613         SG_LOG( SG_GENERAL, SG_INFO, "Initial state restored successfully" );
614     } else {
615         SG_LOG( SG_GENERAL, SG_INFO,
616                 "Some errors restoring initial state (read-only props?)" );
617     }
618
619 }
620
621 // Load user settings from autosave.xml
622 void
623 FGGlobals::loadUserSettings(const SGPath& dataPath)
624 {
625     // remember that we have (tried) to load any existing autsave.xml
626     haveUserSettings = true;
627
628     SGPath autosaveFile = simgear::Dir(dataPath).file(autosaveName());
629     SGPropertyNode autosave;
630     if (autosaveFile.exists()) {
631       SG_LOG(SG_INPUT, SG_INFO, "Reading user settings from " << autosaveFile.str());
632       try {
633           readProperties(autosaveFile.str(), &autosave, SGPropertyNode::USERARCHIVE);
634       } catch (sg_exception& e) {
635           SG_LOG(SG_INPUT, SG_WARN, "failed to read user settings:" << e.getMessage()
636             << "(from " << e.getOrigin() << ")");
637       }
638     }
639     copyProperties(&autosave, globals->get_props());
640 }
641
642 // Save user settings in autosave.xml
643 void
644 FGGlobals::saveUserSettings()
645 {
646     // only save settings when we have (tried) to load the previous
647     // settings (otherwise user data was lost)
648     if (!haveUserSettings)
649         return;
650
651     if (fgGetBool("/sim/startup/save-on-exit")) {
652       // don't save settings more than once on shutdown
653       haveUserSettings = false;
654
655       SGPath autosaveFile(globals->get_fg_home());
656       autosaveFile.append(autosaveName());
657       autosaveFile.create_dir( 0700 );
658       SG_LOG(SG_IO, SG_INFO, "Saving user settings to " << autosaveFile.str());
659       try {
660         writeProperties(autosaveFile.str(), globals->get_props(), false, SGPropertyNode::USERARCHIVE);
661       } catch (const sg_exception &e) {
662         guiErrorMessage("Error writing autosave:", e);
663       }
664       SG_LOG(SG_INPUT, SG_DEBUG, "Finished Saving user settings");
665     }
666 }
667
668 FGViewer *
669 FGGlobals::get_current_view () const
670 {
671   return viewmgr->get_current_view();
672 }
673
674 long int FGGlobals::get_warp() const
675 {
676   return fgGetInt("/sim/time/warp");
677 }
678
679 void FGGlobals::set_warp( long int w )
680 {
681   fgSetInt("/sim/time/warp", w);
682 }
683
684 long int FGGlobals::get_warp_delta() const
685 {
686   return fgGetInt("/sim/time/warp-delta");
687 }
688
689 void FGGlobals::set_warp_delta( long int d )
690 {
691   fgSetInt("/sim/time/warp-delta", d);
692 }
693
694 FGScenery* FGGlobals::get_scenery () const
695 {
696     return _scenery.get();
697 }
698
699 void FGGlobals::set_scenery ( FGScenery *s )
700 {
701     _scenery = s;
702 }
703
704 FGTileMgr* FGGlobals::get_tile_mgr () const
705 {
706     return _tile_mgr.get();
707 }
708
709 void FGGlobals::set_tile_mgr ( FGTileMgr *t )
710 {
711     _tile_mgr = t;
712 }
713
714 void FGGlobals::set_matlib( SGMaterialLib *m )
715 {
716     if (matlib)
717         delete matlib;
718     matlib = m;
719 }
720
721 FGSampleQueue* FGGlobals::get_chatter_queue() const
722 {
723     return _chatter_queue;
724 }
725
726 void FGGlobals::set_chatter_queue(FGSampleQueue* queue)
727 {
728     _chatter_queue = queue;
729 }
730
731 void FGGlobals::addListenerToCleanup(SGPropertyChangeListener* l)
732 {
733     _listeners_to_cleanup.push_back(l);
734 }
735
736 void FGGlobals::cleanupListeners()
737 {
738     SGPropertyChangeListenerVec::iterator i = _listeners_to_cleanup.begin();
739     for (; i != _listeners_to_cleanup.end(); ++i) {
740         delete *i;
741     }
742     _listeners_to_cleanup.clear();
743 }
744
745 // end of globals.cxx