]> git.mxchange.org Git - flightgear.git/blob - src/Main/fg_commands.cxx
bcde46e68388275fd8e4ee54a9697b324f6894b0
[flightgear.git] / src / Main / fg_commands.cxx
1 // fg_commands.cxx - internal FGFS commands.
2
3 #ifdef HAVE_CONFIG_H
4 #  include "config.h"
5 #endif
6
7 #include <string.h>             // strcmp()
8
9 #include <simgear/compiler.h>
10
11 #include STL_STRING
12 #include STL_FSTREAM
13
14 #include <simgear/sg_inlines.h>
15 #include <simgear/debug/logstream.hxx>
16 #include <simgear/math/sg_random.h>
17 #include <simgear/structure/exception.hxx>
18 #include <simgear/structure/commands.hxx>
19 #include <simgear/props/props.hxx>
20
21 #include <Cockpit/panel.hxx>
22 #include <Cockpit/panel_io.hxx>
23 #include <Cockpit/hud.hxx>
24 #include <Environment/environment.hxx>
25 #include <FDM/flight.hxx>
26 #include <GUI/gui.h>
27 #include <GUI/new_gui.hxx>
28 #include <GUI/dialog.hxx>
29 #include <Aircraft/replay.hxx>
30 #include <Scenery/tilemgr.hxx>
31 #include <Scenery/scenery.hxx>
32 #if defined(HAVE_PLIB_PSL)
33 #  include <Scripting/scriptmgr.hxx>
34 #endif
35 #include <Scripting/NasalSys.hxx>
36 #include <Sound/fg_fx.hxx>
37 #include <Time/sunsolver.hxx>
38 #include <Time/tmp.hxx>
39
40 #include "fg_init.hxx"
41 #include "fg_io.hxx"
42 #include "fg_commands.hxx"
43 #include "fg_props.hxx"
44 #include "globals.hxx"
45 #include "logger.hxx"
46 #include "util.hxx"
47 #include "viewmgr.hxx"
48 #include "main.hxx"
49
50 SG_USING_STD(string);
51 SG_USING_STD(ifstream);
52 SG_USING_STD(ofstream);
53
54
55 \f
56 ////////////////////////////////////////////////////////////////////////
57 // Static helper functions.
58 ////////////////////////////////////////////////////////////////////////
59
60
61 static inline SGPropertyNode *
62 get_prop (const SGPropertyNode * arg)
63 {
64     return fgGetNode(arg->getStringValue("property[0]", "/null"), true);
65 }
66
67 static inline SGPropertyNode *
68 get_prop2 (const SGPropertyNode * arg)
69 {
70     return fgGetNode(arg->getStringValue("property[1]", "/null"), true);
71 }
72
73
74 /**
75  * Get a double value and split it as required.
76  */
77 static void
78 split_value (double full_value, const char * mask,
79              double * unmodifiable, double * modifiable)
80 {
81     if (!strcmp("integer", mask)) {
82         *modifiable = (full_value < 0 ? ceil(full_value) : floor (full_value));
83         *unmodifiable = full_value - *modifiable;
84     } else if (!strcmp("decimal", mask)) {
85         *unmodifiable = (full_value < 0 ? ceil(full_value) : floor(full_value));
86         *modifiable = full_value - *unmodifiable;
87     } else {
88         if (strcmp("all", mask))
89             SG_LOG(SG_GENERAL, SG_ALERT, "Bad value " << mask << " for mask;"
90                    << " assuming 'all'");
91         *unmodifiable = 0;
92         *modifiable = full_value;
93     }
94 }
95
96
97 /**
98  * Clamp or wrap a value as specified.
99  */
100 static void
101 limit_value (double * value, const SGPropertyNode * arg)
102 {
103     const SGPropertyNode * min_node = arg->getChild("min");
104     const SGPropertyNode * max_node = arg->getChild("max");
105
106     bool wrap = arg->getBoolValue("wrap");
107
108     if (min_node == 0 || max_node == 0)
109         wrap = false;
110   
111     if (wrap) {                 // wrap such that min <= x < max
112         double min_val = min_node->getDoubleValue();
113         double max_val = max_node->getDoubleValue();
114         double resolution = arg->getDoubleValue("resolution");
115         if (resolution > 0.0) {
116             // snap to (min + N*resolution), taking special care to handle imprecision
117             int n = (int)floor((*value - min_val) / resolution + 0.5);
118             int steps = (int)floor((max_val - min_val) / resolution + 0.5);
119             SG_NORMALIZE_RANGE(n, 0, steps);
120             *value = min_val + resolution * n;
121         } else {
122             // plain circular wrapping
123             SG_NORMALIZE_RANGE(*value, min_val, max_val);
124         }
125     } else {                    // clamp such that min <= x <= max
126         if ((min_node != 0) && (*value < min_node->getDoubleValue()))
127             *value = min_node->getDoubleValue();
128         else if ((max_node != 0) && (*value > max_node->getDoubleValue()))
129             *value = max_node->getDoubleValue();
130     }
131 }
132
133 static bool
134 compare_values (SGPropertyNode * value1, SGPropertyNode * value2)
135 {
136     switch (value1->getType()) {
137     case SGPropertyNode::BOOL:
138         return (value1->getBoolValue() == value2->getBoolValue());
139     case SGPropertyNode::INT:
140         return (value1->getIntValue() == value2->getIntValue());
141     case SGPropertyNode::LONG:
142         return (value1->getLongValue() == value2->getLongValue());
143     case SGPropertyNode::FLOAT:
144         return (value1->getFloatValue() == value2->getFloatValue());
145     case SGPropertyNode::DOUBLE:
146         return (value1->getDoubleValue() == value2->getDoubleValue());
147     default:
148         return !strcmp(value1->getStringValue(), value2->getStringValue());
149     }
150 }
151
152
153 \f
154 ////////////////////////////////////////////////////////////////////////
155 // Command implementations.
156 ////////////////////////////////////////////////////////////////////////
157
158
159 /**
160  * Built-in command: do nothing.
161  */
162 static bool
163 do_null (const SGPropertyNode * arg)
164 {
165   return true;
166 }
167
168 #if defined(HAVE_PLIB_PSL)
169 /**
170  * Built-in command: run a PSL script.
171  *
172  * script: the PSL script to execute
173  */
174 static bool
175 do_script (const SGPropertyNode * arg)
176 {
177     FGScriptMgr * mgr = (FGScriptMgr *)globals->get_subsystem("scripting");
178     return mgr->run(arg->getStringValue("script"));
179 }
180 #endif // HAVE_PLIB_PSL
181
182 /**
183  * Built-in command: run a Nasal script.
184  */
185 static bool
186 do_nasal (const SGPropertyNode * arg)
187 {
188     return ((FGNasalSys*)globals->get_subsystem("nasal"))->handleCommand(arg);
189 }
190
191 /**
192  * Built-in command: exit FlightGear.
193  *
194  * status: the exit status to return to the operating system (defaults to 0)
195  */
196 static bool
197 do_exit (const SGPropertyNode * arg)
198 {
199     SG_LOG(SG_INPUT, SG_INFO, "Program exit requested.");
200     fgSetBool("/sim/signals/exit", true);
201
202     if (fgGetBool("/sim/startup/save-on-exit")) {
203 #ifdef _MSC_VER
204         char* envp = ::getenv( "APPDATA" );
205         if ( envp != NULL ) {
206             SGPath config( envp );
207             config.append( "flightgear.org" );
208 #else
209         if ( homedir != NULL ) {
210             SGPath config( homedir );
211             config.append( ".fgfs" );
212 #endif
213             config.append( "autosave.xml" );
214             config.create_dir( 0700 );
215             SG_LOG(SG_IO, SG_INFO, "Saving user settings to " << config.str());
216             try {
217                 writeProperties(config.str(), globals->get_props(), false, SGPropertyNode::USERARCHIVE);
218             } catch (const sg_exception &e) {
219                 guiErrorMessage("Error writing autosave.xml: ", e);
220             }
221
222             SG_LOG(SG_INPUT, SG_DEBUG, "Finished Saving user settings");
223         }
224     }
225     fgExit(arg->getIntValue("status", 0));
226     return true;
227 }
228
229
230 /**
231  * Built-in command: reinitialize one or more subsystems.
232  *
233  * subsystem[*]: the name(s) of the subsystem(s) to reinitialize; if
234  * none is specified, reinitialize all of them.
235  */
236 static bool
237 do_reinit (const SGPropertyNode * arg)
238 {
239     bool result = true;
240
241     vector<SGPropertyNode_ptr> subsystems = arg->getChildren("subsystem");
242     if (subsystems.size() == 0) {
243         globals->get_subsystem_mgr()->reinit();
244     } else {
245         for ( unsigned int i = 0; i < subsystems.size(); i++ ) {
246             const char * name = subsystems[i]->getStringValue();
247             SGSubsystem * subsystem = globals->get_subsystem(name);
248             if (subsystem == 0) {
249                 result = false;
250                 SG_LOG( SG_GENERAL, SG_ALERT,
251                         "Subsystem " << name << "not found" );
252             } else {
253                 subsystem->reinit();
254             }
255         }
256     }
257
258     globals->get_event_mgr()->reinit();
259
260     return result;
261 }
262
263 #if 0
264   //
265   // these routines look useful ??? but are never used in the code ???
266   //
267
268 /**
269  * Built-in command: suspend one or more subsystems.
270  *
271  * subsystem[*] - the name(s) of the subsystem(s) to suspend.
272  */
273 static bool
274 do_suspend (const SGPropertyNode * arg)
275 {
276     bool result = true;
277
278     vector<SGPropertyNode_ptr> subsystems = arg->getChildren("subsystem");
279     for ( unsigned int i = 0; i < subsystems.size(); i++ ) {
280         const char * name = subsystems[i]->getStringValue();
281         SGSubsystem * subsystem = globals->get_subsystem(name);
282         if (subsystem == 0) {
283             result = false;
284             SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem " << name << "not found");
285         } else {
286             subsystem->suspend();
287         }
288     }
289     return result;
290 }
291
292 /**
293  * Built-in command: suspend one or more subsystems.
294  *
295  * subsystem[*] - the name(s) of the subsystem(s) to suspend.
296  */
297 static bool
298 do_resume (const SGPropertyNode * arg)
299 {
300     bool result = true;
301
302     vector<SGPropertyNode_ptr> subsystems = arg->getChildren("subsystem");
303     for ( unsigned int i = 0; i < subsystems.size(); i++ ) {
304         const char * name = subsystems[i]->getStringValue();
305         SGSubsystem * subsystem = globals->get_subsystem(name);
306         if (subsystem == 0) {
307             result = false;
308             SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem " << name << "not found");
309         } else {
310             subsystem->resume();
311         }
312     }
313     return result;
314 }
315
316 #endif
317
318
319 /**
320  * Built-in command: load flight.
321  *
322  * file (optional): the name of the file to load (relative to current
323  *   directory).  Defaults to "fgfs.sav"
324  */
325 static bool
326 do_load (const SGPropertyNode * arg)
327 {
328   const string &file = arg->getStringValue("file", "fgfs.sav");
329   ifstream input(file.c_str());
330   if (input.good() && fgLoadFlight(input)) {
331     input.close();
332     SG_LOG(SG_INPUT, SG_INFO, "Restored flight from " << file);
333     return true;
334   } else {
335     SG_LOG(SG_INPUT, SG_WARN, "Cannot load flight from " << file);
336     return false;
337   }
338 }
339
340
341 /**
342  * Built-in command: save flight.
343  *
344  * file (optional): the name of the file to save (relative to the
345  * current directory).  Defaults to "fgfs.sav".
346  */
347 static bool
348 do_save (const SGPropertyNode * arg)
349 {
350   const string &file = arg->getStringValue("file", "fgfs.sav");
351   bool write_all = arg->getBoolValue("write-all", false);
352   SG_LOG(SG_INPUT, SG_INFO, "Saving flight");
353   ofstream output(file.c_str());
354   if (output.good() && fgSaveFlight(output, write_all)) {
355     output.close();
356     SG_LOG(SG_INPUT, SG_INFO, "Saved flight to " << file);
357     return true;
358   } else {
359     SG_LOG(SG_INPUT, SG_ALERT, "Cannot save flight to " << file);
360     return false;
361   }
362 }
363
364
365 /**
366  * Built-in command: (re)load the panel.
367  *
368  * path (optional): the file name to load the panel from 
369  * (relative to FG_ROOT).  Defaults to the value of /sim/panel/path,
370  * and if that's unspecified, to "Panels/Default/default.xml".
371  */
372 static bool
373 do_panel_load (const SGPropertyNode * arg)
374 {
375   string panel_path =
376     arg->getStringValue("path",
377                         fgGetString("/sim/panel/path",
378                                     "Panels/Default/default.xml"));
379   FGPanel * new_panel = fgReadPanel(panel_path);
380   if (new_panel == 0) {
381     SG_LOG(SG_INPUT, SG_ALERT,
382            "Error reading new panel from " << panel_path);
383     return false;
384   }
385   SG_LOG(SG_INPUT, SG_INFO, "Loaded new panel from " << panel_path);
386   globals->get_current_panel()->unbind();
387   delete globals->get_current_panel();
388   globals->set_current_panel( new_panel );
389   globals->get_current_panel()->bind();
390   return true;
391 }
392
393
394 /**
395  * Built-in command: pass a mouse click to the panel.
396  *
397  * button: the mouse button number, zero-based.
398  * is-down: true if the button is down, false if it is up.
399  * x-pos: the x position of the mouse click.
400  * y-pos: the y position of the mouse click.
401  */
402 static bool
403 do_panel_mouse_click (const SGPropertyNode * arg)
404 {
405   if (globals->get_current_panel() != 0)
406     return globals->get_current_panel()
407       ->doMouseAction(arg->getIntValue("button"),
408                       arg->getBoolValue("is-down") ? PU_DOWN : PU_UP,
409                       arg->getIntValue("x-pos"),
410                       arg->getIntValue("y-pos"));
411   else
412     return false;
413 }
414
415
416 /**
417  * Built-in command: (re)load preferences.
418  *
419  * path (optional): the file name to load the panel from (relative
420  * to FG_ROOT). Defaults to "preferences.xml".
421  */
422 static bool
423 do_preferences_load (const SGPropertyNode * arg)
424 {
425   try {
426     fgLoadProps(arg->getStringValue("path", "preferences.xml"),
427                 globals->get_props());
428   } catch (const sg_exception &e) {
429     guiErrorMessage("Error reading global preferences: ", e);
430     return false;
431   }
432   SG_LOG(SG_INPUT, SG_INFO, "Successfully read global preferences.");
433   return true;
434 }
435
436
437 static void
438 fix_hud_visibility()
439 {
440   if ( !strcmp(fgGetString("/sim/flight-model"), "ada") ) {
441       globals->get_props()->setBoolValue( "/sim/hud/visibility", true );
442       if ( globals->get_viewmgr()->get_current() == 1 ) {
443           globals->get_props()->setBoolValue( "/sim/hud/visibility", false );
444       }
445   }
446 }
447
448 static void
449 do_view_next( bool )
450 {
451     globals->get_current_view()->setHeadingOffset_deg(0.0);
452     globals->get_viewmgr()->next_view();
453     fix_hud_visibility();
454     globals->get_tile_mgr()->refresh_view_timestamps();
455 }
456
457 static void
458 do_view_prev( bool )
459 {
460     globals->get_current_view()->setHeadingOffset_deg(0.0);
461     globals->get_viewmgr()->prev_view();
462     fix_hud_visibility();
463     globals->get_tile_mgr()->refresh_view_timestamps();
464 }
465
466 /**
467  * Built-in command: cycle view.
468  */
469 static bool
470 do_view_cycle (const SGPropertyNode * arg)
471 {
472   globals->get_current_view()->setHeadingOffset_deg(0.0);
473   globals->get_viewmgr()->next_view();
474   fix_hud_visibility();
475   globals->get_tile_mgr()->refresh_view_timestamps();
476   return true;
477 }
478
479 /**
480  * Built-in command: capture screen.
481  */
482 static bool
483 do_screen_capture (const SGPropertyNode * arg)
484 {
485   fgDumpSnapShot();
486   return true;
487 }
488
489
490 /**
491  * Built-in command: hires capture screen.
492  */
493 static bool
494 do_hires_screen_capture (const SGPropertyNode * arg)
495 {
496   fgHiResDump();
497   return true;
498 }
499
500
501 /**
502  * Reload the tile cache.
503  */
504 static bool
505 do_tile_cache_reload (const SGPropertyNode * arg)
506 {
507     static const SGPropertyNode *master_freeze
508         = fgGetNode("/sim/freeze/master");
509     bool freeze = master_freeze->getBoolValue();
510     SG_LOG(SG_INPUT, SG_INFO, "ReIniting TileCache");
511     if ( !freeze ) {
512         fgSetBool("/sim/freeze/master", true);
513     }
514     if ( globals->get_tile_mgr()->init() ) {
515         // Load the local scenery data
516         double visibility_meters = fgGetDouble("/environment/visibility-m");
517         globals->get_tile_mgr()->update( visibility_meters );
518     } else {
519         SG_LOG( SG_GENERAL, SG_ALERT, 
520                 "Error in Tile Manager initialization!" );
521         exit(-1);
522     }
523     if ( !freeze ) {
524         fgSetBool("/sim/freeze/master", false);
525     }
526     return true;
527 }
528
529
530 /**
531  * Set the sea level outside air temperature and assigning that to all
532  * boundary and aloft environment layers.
533  */
534 static bool
535 do_set_sea_level_degc (const SGPropertyNode * arg)
536 {
537     double temp_sea_level_degc = arg->getDoubleValue("temp-degc", 15.0);
538
539     SGPropertyNode *node, *child;
540
541     // boundary layers
542     node = fgGetNode( "/environment/config/boundary" );
543     if ( node != NULL ) {
544       int i = 0;
545       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
546         child->setDoubleValue( "temperature-sea-level-degc",
547                                temp_sea_level_degc );
548         ++i;
549       }
550     }
551
552     // aloft layers
553     node = fgGetNode( "/environment/config/aloft" );
554     if ( node != NULL ) {
555       int i = 0;
556       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
557         child->setDoubleValue( "temperature-sea-level-degc",
558                                temp_sea_level_degc );
559         ++i;
560       }
561     }
562
563     return true;
564 }
565
566
567 /**
568  * Set the outside air temperature at the "current" altitude by first
569  * calculating the corresponding sea level temp, and assigning that to
570  * all boundary and aloft environment layers.
571  */
572 static bool
573 do_set_oat_degc (const SGPropertyNode * arg)
574 {
575     const string &temp_str = arg->getStringValue("temp-degc", "15.0");
576
577     // check for an altitude specified in the arguments, otherwise use
578     // current aircraft altitude.
579     const SGPropertyNode *altitude_ft = arg->getChild("altitude-ft");
580     if ( altitude_ft == NULL ) {
581         altitude_ft = fgGetNode("/position/altitude-ft");
582     }
583
584     FGEnvironment dummy;        // instantiate a dummy so we can leech a method
585     dummy.set_elevation_ft( altitude_ft->getDoubleValue() );
586     dummy.set_temperature_degc( atof( temp_str.c_str() ) );
587     double temp_sea_level_degc = dummy.get_temperature_sea_level_degc();
588
589     //cout << "Altitude = " << altitude_ft->getDoubleValue() << endl;
590     //cout << "Temp at alt (C) = " << atof( temp_str.c_str() ) << endl;
591     //cout << "Temp sea level (C) = " << temp_sea_level_degc << endl;
592  
593     SGPropertyNode *node, *child;
594
595     // boundary layers
596     node = fgGetNode( "/environment/config/boundary" );
597     if ( node != NULL ) {
598       int i = 0;
599       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
600         child->setDoubleValue( "temperature-sea-level-degc",
601                                temp_sea_level_degc );
602         ++i;
603       }
604     }
605
606     // aloft layers
607     node = fgGetNode( "/environment/config/aloft" );
608     if ( node != NULL ) {
609       int i = 0;
610       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
611         child->setDoubleValue( "temperature-sea-level-degc",
612                                temp_sea_level_degc );
613         ++i;
614       }
615     }
616
617     return true;
618 }
619
620 /**
621  * Set the sea level outside air dewpoint and assigning that to all
622  * boundary and aloft environment layers.
623  */
624 static bool
625 do_set_dewpoint_sea_level_degc (const SGPropertyNode * arg)
626 {
627     double dewpoint_sea_level_degc = arg->getDoubleValue("dewpoint-degc", 5.0);
628
629     SGPropertyNode *node, *child;
630
631     // boundary layers
632     node = fgGetNode( "/environment/config/boundary" );
633     if ( node != NULL ) {
634       int i = 0;
635       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
636         child->setDoubleValue( "dewpoint-sea-level-degc",
637                                dewpoint_sea_level_degc );
638         ++i;
639       }
640     }
641
642     // aloft layers
643     node = fgGetNode( "/environment/config/aloft" );
644     if ( node != NULL ) {
645       int i = 0;
646       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
647         child->setDoubleValue( "dewpoint-sea-level-degc",
648                                dewpoint_sea_level_degc );
649         ++i;
650       }
651     }
652
653     return true;
654 }
655
656
657 /**
658  * Set the outside air dewpoint at the "current" altitude by first
659  * calculating the corresponding sea level dewpoint, and assigning
660  * that to all boundary and aloft environment layers.
661  */
662 static bool
663 do_set_dewpoint_degc (const SGPropertyNode * arg)
664 {
665     const string &dewpoint_str = arg->getStringValue("dewpoint-degc", "5.0");
666
667     // check for an altitude specified in the arguments, otherwise use
668     // current aircraft altitude.
669     const SGPropertyNode *altitude_ft = arg->getChild("altitude-ft");
670     if ( altitude_ft == NULL ) {
671         altitude_ft = fgGetNode("/position/altitude-ft");
672     }
673
674     FGEnvironment dummy;        // instantiate a dummy so we can leech a method
675     dummy.set_elevation_ft( altitude_ft->getDoubleValue() );
676     dummy.set_dewpoint_degc( atof( dewpoint_str.c_str() ) );
677     double dewpoint_sea_level_degc = dummy.get_dewpoint_sea_level_degc();
678
679     //cout << "Altitude = " << altitude_ft->getDoubleValue() << endl;
680     //cout << "Dewpoint at alt (C) = " << atof( dewpoint_str.c_str() ) << endl;
681     //cout << "Dewpoint at sea level (C) = " << dewpoint_sea_level_degc << endl;
682  
683     SGPropertyNode *node, *child;
684
685     // boundary layers
686     node = fgGetNode( "/environment/config/boundary" );
687     if ( node != NULL ) {
688       int i = 0;
689       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
690         child->setDoubleValue( "dewpoint-sea-level-degc",
691                                dewpoint_sea_level_degc );
692         ++i;
693       }
694     }
695
696     // aloft layers
697     node = fgGetNode( "/environment/config/aloft" );
698     if ( node != NULL ) {
699       int i = 0;
700       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
701         child->setDoubleValue( "dewpoint-sea-level-degc",
702                                dewpoint_sea_level_degc );
703         ++i;
704       }
705     }
706
707     return true;
708 }
709
710 /**
711  * Update the lighting manually.
712  */
713 static bool
714 do_timeofday (const SGPropertyNode * arg)
715 {
716     const string &offset_type = arg->getStringValue("timeofday", "noon");
717
718     static const SGPropertyNode *longitude
719         = fgGetNode("/position/longitude-deg");
720     static const SGPropertyNode *latitude
721         = fgGetNode("/position/latitude-deg");
722     static const SGPropertyNode *cur_time_override
723         = fgGetNode("/sim/time/cur-time-override", true);
724
725     int orig_warp = globals->get_warp();
726     SGTime *t = globals->get_time_params();
727     time_t cur_time = t->get_cur_time();
728     // cout << "cur_time = " << cur_time << endl;
729     // cout << "orig_warp = " << orig_warp << endl;
730
731     int warp = 0;
732     if ( offset_type == "real" ) {
733         warp = -orig_warp;
734     } else if ( offset_type == "dawn" ) {
735         warp = fgTimeSecondsUntilSunAngle( cur_time,
736                                            longitude->getDoubleValue()
737                                              * SGD_DEGREES_TO_RADIANS,
738                                            latitude->getDoubleValue()
739                                              * SGD_DEGREES_TO_RADIANS,
740                                            90.0, true ); 
741     } else if ( offset_type == "morning" ) {
742         warp = fgTimeSecondsUntilSunAngle( cur_time,
743                                            longitude->getDoubleValue()
744                                              * SGD_DEGREES_TO_RADIANS,
745                                            latitude->getDoubleValue()
746                                              * SGD_DEGREES_TO_RADIANS,
747                                            75.0, true ); 
748     } else if ( offset_type == "noon" ) {
749         warp = fgTimeSecondsUntilSunAngle( cur_time,
750                                            longitude->getDoubleValue()
751                                              * SGD_DEGREES_TO_RADIANS,
752                                            latitude->getDoubleValue()
753                                              * SGD_DEGREES_TO_RADIANS,
754                                            0.0, true ); 
755     } else if ( offset_type == "afternoon" ) {
756         warp = fgTimeSecondsUntilSunAngle( cur_time,
757                                            longitude->getDoubleValue()
758                                              * SGD_DEGREES_TO_RADIANS,
759                                            latitude->getDoubleValue()
760                                              * SGD_DEGREES_TO_RADIANS,
761                                            60.0, false ); 
762      } else if ( offset_type == "dusk" ) {
763         warp = fgTimeSecondsUntilSunAngle( cur_time,
764                                            longitude->getDoubleValue()
765                                              * SGD_DEGREES_TO_RADIANS,
766                                            latitude->getDoubleValue()
767                                              * SGD_DEGREES_TO_RADIANS,
768                                            90.0, false ); 
769      } else if ( offset_type == "evening" ) {
770         warp = fgTimeSecondsUntilSunAngle( cur_time,
771                                            longitude->getDoubleValue()
772                                              * SGD_DEGREES_TO_RADIANS,
773                                            latitude->getDoubleValue()
774                                              * SGD_DEGREES_TO_RADIANS,
775                                            100.0, false ); 
776     } else if ( offset_type == "midnight" ) {
777         warp = fgTimeSecondsUntilSunAngle( cur_time,
778                                            longitude->getDoubleValue()
779                                              * SGD_DEGREES_TO_RADIANS,
780                                            latitude->getDoubleValue()
781                                              * SGD_DEGREES_TO_RADIANS,
782                                            180.0, false ); 
783     }
784     // cout << "warp = " << warp << endl;
785     globals->set_warp( orig_warp + warp );
786
787     t->update( longitude->getDoubleValue() * SGD_DEGREES_TO_RADIANS,
788                latitude->getDoubleValue() * SGD_DEGREES_TO_RADIANS,
789                cur_time_override->getLongValue(),
790                globals->get_warp() );
791
792     return true;
793 }
794
795
796 /**
797  * Built-in command: toggle a bool property value.
798  *
799  * property: The name of the property to toggle.
800  */
801 static bool
802 do_property_toggle (const SGPropertyNode * arg)
803 {
804   SGPropertyNode * prop = get_prop(arg);
805   return prop->setBoolValue(!prop->getBoolValue());
806 }
807
808
809 /**
810  * Built-in command: assign a value to a property.
811  *
812  * property: the name of the property to assign.
813  * value: the value to assign; or
814  * property[1]: the property to copy from.
815  */
816 static bool
817 do_property_assign (const SGPropertyNode * arg)
818 {
819   SGPropertyNode * prop = get_prop(arg);
820   const SGPropertyNode * prop2 = get_prop2(arg);
821   const SGPropertyNode * value = arg->getNode("value");
822
823   if (value != 0)
824       return prop->setUnspecifiedValue(value->getStringValue());
825   else if (prop2)
826       return prop->setUnspecifiedValue(prop2->getStringValue());
827   else
828       return false;
829 }
830
831
832 /**
833  * Built-in command: increment or decrement a property value.
834  *
835  * If the 'step' argument is present, it will be used; otherwise,
836  * the command uses 'offset' and 'factor', usually from the mouse.
837  *
838  * property: the name of the property to increment or decrement.
839  * step: the amount of the increment or decrement (default: 0).
840  * offset: offset from the current setting (used for the mouse; multiplied 
841  *         by factor)
842  * factor: scaling amount for the offset (defaults to 1).
843  * min: the minimum allowed value (default: no minimum).
844  * max: the maximum allowed value (default: no maximum).
845  * mask: 'integer' to apply only to the left of the decimal point, 
846  *       'decimal' to apply only to the right of the decimal point,
847  *       or 'all' to apply to the whole number (the default).
848  * wrap: true if the value should be wrapped when it passes min or max;
849  *       both min and max must be present for this to work (default:
850  *       false).
851  */
852 static bool
853 do_property_adjust (const SGPropertyNode * arg)
854 {
855   SGPropertyNode * prop = get_prop(arg);
856
857   double amount = 0;
858   if (arg->hasValue("step"))
859       amount = arg->getDoubleValue("step");
860   else
861       amount = (arg->getDoubleValue("factor", 1)
862                 * arg->getDoubleValue("offset"));
863           
864   double unmodifiable, modifiable;
865   split_value(prop->getDoubleValue(), arg->getStringValue("mask", "all"),
866               &unmodifiable, &modifiable);
867   modifiable += amount;
868   limit_value(&modifiable, arg);
869
870   prop->setDoubleValue(unmodifiable + modifiable);
871
872   return true;
873 }
874
875
876 /**
877  * Built-in command: multiply a property value.
878  *
879  * property: the name of the property to multiply.
880  * factor: the amount by which to multiply.
881  * min: the minimum allowed value (default: no minimum).
882  * max: the maximum allowed value (default: no maximum).
883  * mask: 'integer' to apply only to the left of the decimal point, 
884  *       'decimal' to apply only to the right of the decimal point,
885  *       or 'all' to apply to the whole number (the default).
886  * wrap: true if the value should be wrapped when it passes min or max;
887  *       both min and max must be present for this to work (default:
888  *       false).
889  */
890 static bool
891 do_property_multiply (const SGPropertyNode * arg)
892 {
893   SGPropertyNode * prop = get_prop(arg);
894   double factor = arg->getDoubleValue("factor", 1);
895
896   double unmodifiable, modifiable;
897   split_value(prop->getDoubleValue(), arg->getStringValue("mask", "all"),
898               &unmodifiable, &modifiable);
899   modifiable *= factor;
900   limit_value(&modifiable, arg);
901
902   prop->setDoubleValue(unmodifiable + modifiable);
903
904   return true;
905 }
906
907
908 /**
909  * Built-in command: swap two property values.
910  *
911  * property[0]: the name of the first property.
912  * property[1]: the name of the second property.
913  */
914 static bool
915 do_property_swap (const SGPropertyNode * arg)
916 {
917   SGPropertyNode * prop1 = get_prop(arg);
918   SGPropertyNode * prop2 = get_prop2(arg);
919
920                                 // FIXME: inefficient
921   const string & tmp = prop1->getStringValue();
922   return (prop1->setUnspecifiedValue(prop2->getStringValue()) &&
923           prop2->setUnspecifiedValue(tmp.c_str()));
924 }
925
926
927 /**
928  * Built-in command: Set a property to an axis or other moving input.
929  *
930  * property: the name of the property to set.
931  * setting: the current input setting, usually between -1.0 and 1.0.
932  * offset: the offset to shift by, before applying the factor.
933  * factor: the factor to multiply by (use negative to reverse).
934  */
935 static bool
936 do_property_scale (const SGPropertyNode * arg)
937 {
938   SGPropertyNode * prop = get_prop(arg);
939   double setting = arg->getDoubleValue("setting");
940   double offset = arg->getDoubleValue("offset", 0.0);
941   double factor = arg->getDoubleValue("factor", 1.0);
942   bool squared = arg->getBoolValue("squared", false);
943   int power = arg->getIntValue("power", (squared ? 2 : 1));
944
945   int sign = (setting < 0 ? -1 : 1);
946
947   switch (power) {
948   case 1:
949       break;
950   case 2:
951       setting = setting * setting * sign;
952       break;
953   case 3:
954       setting = setting * setting * setting;
955       break;
956   case 4:
957       setting = setting * setting * setting * setting * sign;
958       break;
959   default:
960       setting =  pow(setting, power);
961       if ((power % 2) == 0)
962           setting *= sign;
963       break;
964   }
965
966   return prop->setDoubleValue((setting + offset) * factor);
967 }
968
969
970 /**
971  * Built-in command: cycle a property through a set of values.
972  *
973  * If the current value isn't in the list, the cycle will
974  * (re)start from the beginning.
975  *
976  * property: the name of the property to cycle.
977  * value[*]: the list of values to cycle through.
978  */
979 static bool
980 do_property_cycle (const SGPropertyNode * arg)
981 {
982     SGPropertyNode * prop = get_prop(arg);
983     vector<SGPropertyNode_ptr> values = arg->getChildren("value");
984     int selection = -1;
985     int nSelections = values.size();
986
987     if (nSelections < 1) {
988         SG_LOG(SG_GENERAL, SG_ALERT, "No values for property-cycle");
989         return false;
990     }
991
992                                 // Try to find the current selection
993     for (int i = 0; i < nSelections; i++) {
994         if (compare_values(prop, values[i])) {
995             selection = i + 1;
996             break;
997         }
998     }
999
1000                                 // Default or wrap to the first selection
1001     if (selection < 0 || selection >= nSelections)
1002         selection = 0;
1003
1004     prop->setUnspecifiedValue(values[selection]->getStringValue());
1005     return true;
1006 }
1007
1008
1009 /**
1010  * Built-in command: randomize a numeric property value.
1011  *
1012  * property: the name of the property value to randomize.
1013  * min: the minimum allowed value.
1014  * max: the maximum allowed value.
1015  */
1016 static bool
1017 do_property_randomize (const SGPropertyNode * arg)
1018 {
1019     SGPropertyNode * prop = get_prop(arg);
1020     double min = arg->getDoubleValue("min", DBL_MIN);
1021     double max = arg->getDoubleValue("max", DBL_MAX);
1022     prop->setDoubleValue(sg_random() * (max - min) + min);
1023     return true;
1024 }
1025
1026
1027 /**
1028  * Built-in command: reinit the data logging system based on the
1029  * current contents of the /logger tree.
1030  */
1031 static bool
1032 do_data_logging_commit (const SGPropertyNode * arg)
1033 {
1034     FGLogger *log = (FGLogger *)globals->get_subsystem("logger");
1035     log->reinit();
1036     return true;
1037 }
1038
1039 /**
1040  * Built-in command: Add a dialog to the GUI system.  Does *not*
1041  * display the dialog.  The property node should have the same format
1042  * as a dialog XML configuration.  It must include:
1043  *
1044  * name: the name of the GUI dialog for future reference.
1045  */
1046 static bool
1047 do_dialog_new (const SGPropertyNode * arg)
1048 {
1049     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
1050
1051     // Note the casting away of const: this is *real*.  Doing a
1052     // "dialog-apply" command later on will mutate this property node.
1053     // I'm not convinced that this isn't the Right Thing though; it
1054     // allows client to create a node, pass it to dialog-new, and get
1055     // the values back from the dialog by reading the same node.
1056     // Perhaps command arguments are not as "const" as they would
1057     // seem?
1058     gui->newDialog((SGPropertyNode*)arg);
1059     return true;
1060 }
1061
1062 /**
1063  * Built-in command: Show an XML-configured dialog.
1064  *
1065  * dialog-name: the name of the GUI dialog to display.
1066  */
1067 static bool
1068 do_dialog_show (const SGPropertyNode * arg)
1069 {
1070     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
1071     gui->showDialog(arg->getStringValue("dialog-name"));
1072     return true;
1073 }
1074
1075
1076 /**
1077  * Built-in Command: Hide the active XML-configured dialog.
1078  */
1079 static bool
1080 do_dialog_close (const SGPropertyNode * arg)
1081 {
1082     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
1083     if(arg->hasValue("dialog-name"))
1084         return gui->closeDialog(arg->getStringValue("dialog-name"));
1085     return gui->closeActiveDialog();
1086 }
1087
1088
1089 /**
1090  * Update a value in the active XML-configured dialog.
1091  *
1092  * object-name: The name of the GUI object(s) (all GUI objects if omitted).
1093  */
1094 static bool
1095 do_dialog_update (const SGPropertyNode * arg)
1096 {
1097     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
1098     FGDialog * dialog;
1099     if (arg->hasValue("dialog-name"))
1100         dialog = gui->getDialog(arg->getStringValue("dialog-name"));
1101     else
1102         dialog = gui->getActiveDialog();
1103
1104     if (dialog != 0) {
1105         dialog->updateValues(arg->getStringValue("object-name"));
1106         return true;
1107     } else {
1108         return false;
1109     }
1110 }
1111
1112
1113 /**
1114  * Apply a value in the active XML-configured dialog.
1115  *
1116  * object-name: The name of the GUI object(s) (all GUI objects if omitted).
1117  */
1118 static bool
1119 do_dialog_apply (const SGPropertyNode * arg)
1120 {
1121     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
1122     FGDialog * dialog;
1123     if (arg->hasValue("dialog-name"))
1124         dialog = gui->getDialog(arg->getStringValue("dialog-name"));
1125     else
1126         dialog = gui->getActiveDialog();
1127
1128     if (dialog != 0) {
1129         dialog->applyValues(arg->getStringValue("object-name"));
1130         return true;
1131     } else {
1132         return false;
1133     }
1134 }
1135
1136
1137 /**
1138  * Redraw GUI (applying new widget colors). Doesn't reload the dialogs,
1139  * unlike reinit().
1140  */
1141 static bool
1142 do_gui_redraw (const SGPropertyNode * arg)
1143 {
1144     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
1145     gui->redraw();
1146     return true;
1147 }
1148
1149 /**
1150  * Built-in command: play an audio message (i.e. a wav file) This is
1151  * fire and forget.  Call this once per message and it will get dumped
1152  * into a queue.  Messages are played sequentially so they do not
1153  * overlap.
1154  */
1155 static bool
1156 do_play_audio_message (const SGPropertyNode * arg)
1157 {
1158     FGFX *fx = (FGFX *)globals->get_subsystem("fx");
1159     string path = arg->getStringValue("path");
1160     string file = arg->getStringValue("file");
1161     double volume = arg->getDoubleValue("volume");
1162     // cout << "playing " << path << " / " << file << endl;
1163     fx->play_message( path, file, volume );
1164
1165     return true;
1166 }
1167
1168 /**
1169  * Built-in command: commit presets (read from in /sim/presets/)
1170  */
1171 static bool
1172 do_presets_commit (const SGPropertyNode * arg)
1173 {
1174     // unbind the current fdm state so property changes
1175     // don't get lost when we subsequently delete this fdm
1176     // and create a new one.
1177     cur_fdm_state->unbind();
1178         
1179     // set position from presets
1180     fgInitPosition();
1181
1182     fgReInitSubsystems();
1183
1184     globals->get_tile_mgr()->update( fgGetDouble("/environment/visibility-m") );
1185
1186 #if 0
1187     if ( ! fgGetBool("/sim/presets/onground") ) {
1188         fgSetBool( "/sim/freeze/master", true );
1189         fgSetBool( "/sim/freeze/clock", true );
1190     }
1191 #endif
1192
1193     return true;
1194 }
1195
1196 /**
1197  * Built-in command: set log level (0 ... 7)
1198  */
1199 static bool
1200 do_log_level (const SGPropertyNode * arg)
1201 {
1202    sglog().setLogLevels( SG_ALL, (sgDebugPriority)arg->getIntValue() );
1203
1204    return true;
1205 }
1206
1207 /**
1208  * Built-in command: replay the FDR buffer
1209  */
1210 static bool
1211 do_replay (const SGPropertyNode * arg)
1212 {
1213     // freeze the master fdm
1214     fgSetInt( "/sim/freeze/replay-state", 1 );
1215
1216     FGReplay *r = (FGReplay *)(globals->get_subsystem( "replay" ));
1217
1218     fgSetDouble( "/sim/replay/start-time", r->get_start_time() );
1219     fgSetDouble( "/sim/replay/end-time", r->get_end_time() );
1220     double duration = fgGetDouble( "/sim/replay/duration" );
1221     if( duration && duration < (r->get_end_time() - r->get_start_time()) ) {
1222         fgSetDouble( "/sim/replay/time", r->get_end_time() - duration );
1223     } else {
1224         fgSetDouble( "/sim/replay/time", r->get_start_time() );
1225     }
1226
1227     // cout << "start = " << r->get_start_time()
1228     //      << "  end = " << r->get_end_time() << endl;
1229
1230     return true;
1231 }
1232
1233
1234 /**
1235  * Return terrain elevation for given longitude/latitude pair.
1236  */
1237 static bool
1238 do_terrain_elevation (const SGPropertyNode * arg)
1239 {
1240     double lon = arg->getDoubleValue("longitude-deg", 0.0);
1241     double lat = arg->getDoubleValue("latitude-deg", 0.0);
1242     double elev;
1243     bool ret = globals->get_scenery()->get_elevation_m(lat, lon, 10000.0, elev, 0);
1244     const_cast<SGPropertyNode *>(arg)->setDoubleValue("elevation-m", elev);
1245     return ret;
1246 }
1247
1248
1249 static bool
1250 do_decrease_visibility (const SGPropertyNode * arg)
1251 {
1252     double new_value = fgGetDouble("/environment/visibility-m") * 0.9;
1253     fgSetDouble("/environment/visibility-m", new_value);
1254     fgDefaultWeatherValue("visibility-m", new_value);
1255     globals->get_subsystem("environment")->reinit();
1256
1257     return true;
1258 }
1259  
1260 static bool
1261 do_increase_visibility (const SGPropertyNode * arg)
1262 {
1263     double new_value = fgGetDouble("/environment/visibility-m") * 1.1;
1264     fgSetDouble("/environment/visibility-m", new_value);
1265     fgDefaultWeatherValue("visibility-m", new_value);
1266     globals->get_subsystem("environment")->reinit();
1267
1268     return true;
1269 }
1270
1271 static bool
1272 do_hud_init(const SGPropertyNode *)
1273 {
1274     fgHUDInit(0); // minimal HUD
1275     return true;
1276 }
1277
1278 static bool
1279 do_hud_init2(const SGPropertyNode *)
1280 {
1281     fgHUDInit2(0);  // normal HUD
1282     return true;
1283 }
1284
1285 /**
1286  * An fgcommand to allow loading of xml files via nasal,
1287  * the xml file's structure will be made available within
1288  * a (definable) property tree node
1289  *
1290  * @param filename a string to hold the complete path & filename of a XML file
1291  * @param targetnode a string pointing to a location within the property tree
1292  * where to store the parsed XML file
1293  */
1294
1295 static bool 
1296 do_load_xml_to_proptree(const SGPropertyNode * node)
1297 {
1298     // SG_LOG(SG_GENERAL, SG_ALERT, "fgcommand loadxml executed");
1299
1300     SGPropertyNode * targetnode;
1301     targetnode = fgGetNode(node->getNode("targetnode")->getStringValue(),true);
1302                 
1303     const char *filename = node->getNode("filename")->getStringValue();
1304     try {
1305         fgLoadProps(filename, targetnode);
1306     } catch (const sg_exception &e) {
1307         string errmsg = "Error reading file " + string(filename) + ":\n";
1308         guiErrorMessage(errmsg.c_str(), e);
1309         return false;
1310     }
1311
1312     return  true;
1313 }
1314
1315
1316 /**
1317  * an fgcommand to allow saving of xml files via nasal,
1318  * the file's structure will be determined based on what's
1319  * encountered in the passed (source) property tree node
1320  *
1321  * @param filename a string to hold the complete path & filename of the (new)
1322  * XML file
1323  * @param sourcenode a string pointing to a location within the property tree
1324  * where to find the nodes that should be written recursively into an XML file
1325  *
1326  * TODO: 
1327  *   deal with already existing filenames, optionally return error/success
1328  *   values in a separate node to provide status information
1329  *
1330  * note: it's directly using writeProperties, which is not necessarily
1331  *       preferable, but for now it should work ...
1332  */
1333
1334 static bool 
1335 do_save_xml_from_proptree(const SGPropertyNode * node)
1336 {
1337     //TODO: do Parameter validation !
1338     SGPropertyNode * sourcenode;
1339     sourcenode = fgGetNode(node->getNode("sourcenode")->getStringValue(),true);
1340
1341     const char *filename = node->getNode("filename")->getStringValue();
1342     try {
1343         writeProperties (filename, sourcenode, true);
1344     } catch (const sg_exception &e) {
1345         string errmsg = "Error writing file " + string(filename) + ":\n";
1346         guiErrorMessage(errmsg.c_str(), e);
1347         return false;
1348     }
1349
1350     return true;
1351 }
1352
1353 static bool
1354 do_press_cockpit_button (const SGPropertyNode *arg)
1355 {
1356   const char *prefix = arg->getStringValue("prefix");
1357
1358   if (arg->getBoolValue("guarded") && fgGetDouble((string(prefix) + "-guard").c_str()) < 1)
1359     return true;
1360
1361   string prop = string(prefix) + "-button";
1362   double value;
1363
1364   if (arg->getBoolValue("latching"))
1365     value = fgGetDouble(prop.c_str()) > 0 ? 0 : 1;
1366   else
1367     value = 1;
1368
1369   fgSetDouble(prop.c_str(), value);
1370   fgSetBool(arg->getStringValue("discrete"), value > 0);
1371
1372   return true;
1373 }
1374
1375 static bool
1376 do_release_cockpit_button (const SGPropertyNode *arg)
1377 {
1378   const char *prefix = arg->getStringValue("prefix");
1379
1380   if (arg->getBoolValue("guarded")) {
1381     string prop = string(prefix) + "-guard";
1382     if (fgGetDouble(prop.c_str()) < 1) {
1383       fgSetDouble(prop.c_str(), 1);
1384       return true;
1385     }
1386   }
1387
1388   if (! arg->getBoolValue("latching")) {
1389     fgSetDouble((string(prefix) + "-button").c_str(), 0);
1390     fgSetBool(arg->getStringValue("discrete"), false);
1391   }
1392
1393   return true;
1394 }
1395
1396
1397 ////////////////////////////////////////////////////////////////////////
1398 // Command setup.
1399 ////////////////////////////////////////////////////////////////////////
1400
1401
1402 /**
1403  * Table of built-in commands.
1404  *
1405  * New commands do not have to be added here; any module in the application
1406  * can add a new command using globals->get_commands()->addCommand(...).
1407  */
1408 static struct {
1409   const char * name;
1410   SGCommandMgr::command_t command;
1411 } built_ins [] = {
1412     { "null", do_null },
1413 #if defined(HAVE_PLIB_PSL)
1414     { "script", do_script },
1415 #endif // HAVE_PLIB_PSL
1416     { "nasal", do_nasal },
1417     { "exit", do_exit },
1418     { "reinit", do_reinit },
1419     { "suspend", do_reinit },
1420     { "resume", do_reinit },
1421     { "load", do_load },
1422     { "save", do_save },
1423     { "panel-load", do_panel_load },
1424     { "panel-mouse-click", do_panel_mouse_click },
1425     { "preferences-load", do_preferences_load },
1426     { "view-cycle", do_view_cycle },
1427     { "screen-capture", do_screen_capture },
1428     { "hires-screen-capture", do_hires_screen_capture },
1429     { "tile-cache-reload", do_tile_cache_reload },
1430     { "set-sea-level-air-temp-degc", do_set_sea_level_degc },
1431     { "set-outside-air-temp-degc", do_set_oat_degc },
1432     { "set-dewpoint-sea-level-air-temp-degc", do_set_dewpoint_sea_level_degc },
1433     { "set-dewpoint-temp-degc", do_set_dewpoint_degc },
1434     { "timeofday", do_timeofday },
1435     { "property-toggle", do_property_toggle },
1436     { "property-assign", do_property_assign },
1437     { "property-adjust", do_property_adjust },
1438     { "property-multiply", do_property_multiply },
1439     { "property-swap", do_property_swap },
1440     { "property-scale", do_property_scale },
1441     { "property-cycle", do_property_cycle },
1442     { "property-randomize", do_property_randomize },
1443     { "data-logging-commit", do_data_logging_commit },
1444     { "dialog-new", do_dialog_new },
1445     { "dialog-show", do_dialog_show },
1446     { "dialog-close", do_dialog_close },
1447     { "dialog-update", do_dialog_update },
1448     { "dialog-apply", do_dialog_apply },
1449     { "gui-redraw", do_gui_redraw },
1450     { "play-audio-message", do_play_audio_message },
1451     { "presets-commit", do_presets_commit },
1452     { "log-level", do_log_level },
1453     { "replay", do_replay },
1454     { "terrain-elevation", do_terrain_elevation },
1455     { "decrease-visibility", do_decrease_visibility },
1456     { "increase-visibility", do_increase_visibility },
1457     { "hud-init", do_hud_init },
1458     { "hud-init2", do_hud_init2 },
1459     { "loadxml", do_load_xml_to_proptree},
1460     { "savexml", do_save_xml_from_proptree },    
1461     { "press-cockpit-button", do_press_cockpit_button },
1462     { "release-cockpit-button", do_release_cockpit_button },
1463     { 0, 0 }                    // zero-terminated
1464 };
1465
1466
1467 /**
1468  * Initialize the default built-in commands.
1469  *
1470  * Other commands may be added by other parts of the application.
1471  */
1472 void
1473 fgInitCommands ()
1474 {
1475   SG_LOG(SG_GENERAL, SG_INFO, "Initializing basic built-in commands:");
1476   for (int i = 0; built_ins[i].name != 0; i++) {
1477     SG_LOG(SG_GENERAL, SG_INFO, "  " << built_ins[i].name);
1478     globals->get_commands()->addCommand(built_ins[i].name,
1479                                         built_ins[i].command);
1480   }
1481
1482   typedef bool (*dummy)();
1483   fgTie( "/command/view/next", dummy(0), do_view_next );
1484   fgTie( "/command/view/prev", dummy(0), do_view_prev );
1485 }
1486
1487 // end of fg_commands.cxx