]> git.mxchange.org Git - flightgear.git/blob - src/Input/input.cxx
Fix a clash with some standard MSVC defines (yeah MS) :-(
[flightgear.git] / src / Input / input.cxx
1 // input.cxx -- handle user input from various sources.
2 //
3 // Written by David Megginson, started May 2001.
4 //
5 // Copyright (C) 2001 David Megginson, david@megginson.com
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26
27 #include <simgear/compiler.h>
28
29 #include STL_FSTREAM
30 #include STL_STRING
31
32 #include <simgear/constants.h>
33 #include <simgear/debug/logstream.hxx>
34 #include <simgear/misc/props.hxx>
35
36 #include <Aircraft/aircraft.hxx>
37 #include <Autopilot/auto_gui.hxx>
38 #include <Autopilot/newauto.hxx>
39 #include <Cockpit/hud.hxx>
40 #include <Cockpit/panel.hxx>
41 #include <Cockpit/panel_io.hxx>
42 #include <GUI/gui.h>
43 #include <Scenery/tilemgr.hxx>
44 #include <Objects/matlib.hxx>
45 #include <Time/light.hxx>
46 #include <Time/tmp.hxx>
47
48 #ifndef FG_OLD_WEATHER
49 #  include <WeatherCM/FGLocalWeatherDatabase.h>
50 #else
51 #  include <Weather/weather.hxx>
52 #endif
53
54 #include <Main/bfi.hxx>
55 #include <Main/globals.hxx>
56 #include <Main/keyboard.hxx>
57 #include <Main/fg_props.hxx>
58 #include <Main/options.hxx>
59
60 #include "input.hxx"
61
62 SG_USING_STD(ifstream);
63 SG_USING_STD(string);
64
65
66 \f
67 ////////////////////////////////////////////////////////////////////////
68 // Implementation of FGBinding.
69 ////////////////////////////////////////////////////////////////////////
70
71 FGBinding::FGBinding ()
72   : _action(ACTION_NONE), _node(0), _adjust_step(0), _assign_value(0)
73 {
74 }
75
76 FGBinding::FGBinding (const SGPropertyNode * node)
77   : _action(ACTION_NONE), _node(0), _adjust_step(0), _assign_value(0)
78 {
79   read(node);
80 }
81
82 FGBinding::~FGBinding ()
83 {
84   // no op
85 }
86
87 void
88 FGBinding::setAction (Action action)
89 {
90   _action = action;
91 }
92
93 void
94 FGBinding::setProperty (SGPropertyNode * node)
95 {
96   _node = node;
97 }
98
99 void
100 FGBinding::setAdjustStep (const SGValue * step)
101 {
102   _adjust_step = step;
103 }
104
105 void
106 FGBinding::setAssignValue (const SGValue * value)
107 {
108   _assign_value = value;
109 }
110
111 void
112 FGBinding::read (const SGPropertyNode * node)
113 {
114   if (node->hasValue("action")) {
115     string action = node->getStringValue("action");
116     if (action == "none")
117       _action = ACTION_NONE;
118     else if (action == "switch")
119       _action = ACTION_SWITCH;
120     else if (action == "adjust")
121       _action = ACTION_ADJUST;
122     else if (action == "assign")
123       _action = ACTION_ASSIGN;
124     else
125       SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unrecognized action type "
126              << action);
127   }
128
129   if (node->hasValue("control"))
130     _node = fgGetNode(node->getStringValue("control"), true);
131
132   if (node->hasValue("step"))
133     _adjust_step = node->getChild("step")->getValue();
134
135   if (node->hasValue("value"))
136     _assign_value = node->getChild("value")->getValue();
137 }
138
139 void
140 FGBinding::fire () const
141 {
142   if (_node == 0) {
143     SG_LOG(SG_INPUT, SG_ALERT, "No control property attached to binding");
144     return;
145   }
146
147   switch (_action) {
148
149   case ACTION_NONE:
150     break;
151
152   case ACTION_SWITCH:
153     _node->setBoolValue(!_node->getBoolValue());
154     break;
155
156   case ACTION_ADJUST:
157     if  (_adjust_step == 0) {
158       SG_LOG(SG_INPUT, SG_ALERT, "No step provided for adjust binding");
159       break;
160     }
161     switch (_node->getType()) {
162     case SGValue::BOOL:
163       if (_adjust_step->getBoolValue())
164         _node->setBoolValue(!_node->getBoolValue());
165       break;
166     case SGValue::INT:
167       _node->setIntValue(_node->getIntValue() + _adjust_step->getIntValue());
168       break;
169     case SGValue::LONG:
170       _node->setLongValue(_node->getLongValue() + _adjust_step->getLongValue());
171       break;
172     case SGValue::FLOAT:
173       _node->setFloatValue(_node->getFloatValue()
174                            + _adjust_step->getFloatValue());
175       break;
176     case SGValue::DOUBLE:
177     case SGValue::UNKNOWN:      // force to double
178       _node->setDoubleValue(_node->getDoubleValue()
179                             + _adjust_step->getDoubleValue());
180       break;
181     case SGValue::STRING:
182       SG_LOG(SG_INPUT, SG_ALERT, "Cannot increment or decrement string value"
183              << _node->getStringValue());
184       break;
185     }
186     break;
187
188   case ACTION_ASSIGN:
189     if  (_assign_value == 0) {
190       SG_LOG(SG_INPUT, SG_ALERT, "No value provided for assign binding");
191       break;
192     }
193     switch (_node->getType()) {
194     case SGValue::BOOL:
195       _node->setBoolValue(_assign_value->getBoolValue());
196       break;
197     case SGValue::INT:
198       _node->setIntValue(_assign_value->getIntValue());
199       break;
200     case SGValue::LONG:
201       _node->setLongValue(_assign_value->getLongValue());
202       break;
203     case SGValue::FLOAT:
204       _node->setFloatValue(_assign_value->getFloatValue());
205       break;
206     case SGValue::DOUBLE:
207       _node->setDoubleValue(_assign_value->getDoubleValue());
208       break;
209     case SGValue::STRING:
210       _node->setStringValue(_assign_value->getStringValue());
211       break;
212     case SGValue::UNKNOWN:
213       _node->setUnknownValue(_assign_value->getStringValue());
214       break;
215     }
216     break;
217   }
218 }
219
220
221 \f
222 ////////////////////////////////////////////////////////////////////////
223 // Implementation of FGInput.
224 ////////////////////////////////////////////////////////////////////////
225
226                                 // From main.cxx
227 extern void fgReshape( int width, int height );
228
229 FGInput current_input;
230
231
232 FGInput::FGInput ()
233 {
234   // no op
235 }
236
237 FGInput::~FGInput ()
238 {
239   // no op
240 }
241
242 void
243 FGInput::init ()
244 {
245                                 // Read the keyboard bindings.
246                                 // TODO: zero the old bindings first.
247   const SGPropertyNode * keyboard =
248     globals->get_props()->getNode("/input/keyboard", true);
249   vector<const SGPropertyNode *> keys = keyboard->getChildren("key");
250
251   for (unsigned int i = 0; i < keys.size(); i++) {
252     int index = keys[i]->getIndex();
253     int modifiers = FG_MOD_NONE;
254     if (keys[i]->getBoolValue("mod-shift"))
255       modifiers |= FG_MOD_SHIFT;
256     if (keys[i]->getBoolValue("mod-ctrl"))
257       modifiers |= FG_MOD_CTRL;
258     if (keys[i]->getBoolValue("mod-alt"))
259       modifiers |= FG_MOD_ALT;
260     SG_LOG(SG_INPUT, SG_INFO, "Binding key " << index
261            << " with modifiers " << modifiers);
262
263     vector<const SGPropertyNode *> bindings = keys[i]->getChildren("binding");
264     for (unsigned int j = 0; j < bindings.size(); j++) {
265       SG_LOG(SG_INPUT, SG_INFO, "  Adding binding " << j);
266       _key_bindings[modifiers][index].push_back(FGBinding(bindings[j]));
267     }
268   }
269 }
270
271 void
272 FGInput::bind ()
273 {
274   // no op
275 }
276
277 void
278 FGInput::unbind ()
279 {
280   // no op
281 }
282
283 void 
284 FGInput::update ()
285 {
286   // we'll do something here with the joystick
287 }
288
289 void
290 FGInput::doKey (int k, int modifiers, int x, int y)
291 {
292   float fov, tmp;
293   static bool winding_ccw = true;
294   int speed;
295
296   SG_LOG(SG_INPUT, SG_INFO, "User pressed key " << k
297          << " with modifiers " << modifiers);
298
299   if (_key_bindings[modifiers].find(k) != _key_bindings[modifiers].end()) {
300     const vector<FGBinding> &bindings = _key_bindings[modifiers][k];
301     for (unsigned int i = 0; i < bindings.size(); i++) {
302       bindings[i].fire();
303     }
304     return;
305   }
306
307   SG_LOG(SG_INPUT, SG_INFO, "(No user binding.)");
308
309                                 // Use the old, default actions.
310   FGInterface *f = current_aircraft.fdm_state;
311   FGViewer *v = globals->get_current_view();
312   
313   // everything after here will be removed sooner or later...
314
315   if (modifiers & FG_MOD_SHIFT) {
316
317         switch (k) {
318         case 7: // Ctrl-G key
319             current_autopilot->set_AltitudeMode( 
320                   FGAutopilot::FG_ALTITUDE_GS1 );
321             current_autopilot->set_AltitudeEnabled(
322                   ! current_autopilot->get_AltitudeEnabled()
323                 );
324             return;
325         case 18: // Ctrl-R key
326             // temporary
327             winding_ccw = !winding_ccw;
328             if ( winding_ccw ) {
329                 glFrontFace ( GL_CCW );
330             } else {
331                 glFrontFace ( GL_CW );
332             }
333             return;
334         case 19: // Ctrl-S key
335             current_autopilot->set_AutoThrottleEnabled(
336                   ! current_autopilot->get_AutoThrottleEnabled()
337                 );
338             return;
339         case 20: // Ctrl-T key
340             current_autopilot->set_AltitudeMode( 
341                   FGAutopilot::FG_ALTITUDE_TERRAIN );
342             current_autopilot->set_AltitudeEnabled(
343                   ! current_autopilot->get_AltitudeEnabled()
344                 );
345             return;
346         case 49: // numeric keypad 1
347             v->set_goal_view_offset( SGD_PI * 0.75 );
348             return;
349         case 50: // numeric keypad 2
350             v->set_goal_view_offset( SGD_PI );
351             return;
352         case 51: // numeric keypad 3
353             v->set_goal_view_offset( SGD_PI * 1.25 );
354             return;
355         case 52: // numeric keypad 4
356             v->set_goal_view_offset( SGD_PI * 0.50 );
357             return;
358         case 54: // numeric keypad 6
359             v->set_goal_view_offset( SGD_PI * 1.50 );
360             return;
361         case 55: // numeric keypad 7
362             v->set_goal_view_offset( SGD_PI * 0.25 );
363             return;
364         case 56: // numeric keypad 8
365             v->set_goal_view_offset( 0.00 );
366             return;
367         case 57: // numeric keypad 9
368             v->set_goal_view_offset( SGD_PI * 1.75 );
369             return;
370         case 72: // H key
371             HUD_brightkey( true );
372             return;
373         case 73: // I key
374             // Minimal Hud
375             fgHUDInit2(&current_aircraft);
376             return;
377         case 77: // M key
378             globals->inc_warp( -60 );
379             fgUpdateSkyAndLightingParams();
380             return;
381         case 84: // T key
382             globals->inc_warp_delta( -30 );
383             fgUpdateSkyAndLightingParams();
384             return;
385         case 87: // W key
386 #if defined(FX) && !defined(WIN32)
387             global_fullscreen = ( !global_fullscreen );
388 #  if defined(XMESA_FX_FULLSCREEN) && defined(XMESA_FX_WINDOW)
389             XMesaSetFXmode( global_fullscreen ? 
390                             XMESA_FX_FULLSCREEN : XMESA_FX_WINDOW );
391 #  endif
392 #endif
393             return;
394         case 88: // X key
395             fov = globals->get_current_view()->get_fov();
396             fov *= 1.05;
397             if ( fov > FG_FOV_MAX ) {
398                 fov = FG_FOV_MAX;
399             }
400             globals->get_current_view()->set_fov(fov);
401             // v->force_update_fov_math();
402             return;
403         case 90: // Z key
404 #ifndef FG_OLD_WEATHER
405             tmp = WeatherDatabase->getWeatherVisibility();
406             tmp /= 1.10;
407             WeatherDatabase->setWeatherVisibility( tmp );
408 #else
409             tmp = current_weather.get_visibility();   // in meters
410             tmp /= 1.10;
411             current_weather.set_visibility( tmp );
412 #endif
413             return;
414
415 // START SPECIALS
416
417         case 256+GLUT_KEY_F1: {
418             ifstream input("fgfs.sav");
419             if (input.good() && fgLoadFlight(input)) {
420                 input.close();
421                 SG_LOG(SG_INPUT, SG_INFO, "Restored flight from fgfs.sav");
422             } else {
423                 SG_LOG(SG_INPUT, SG_ALERT, "Cannot load flight from fgfs.sav");
424             }
425             return;
426         }
427         case 256+GLUT_KEY_F2: {
428             SG_LOG(SG_INPUT, SG_INFO, "Saving flight");
429             ofstream output("fgfs.sav");
430             if (output.good() && fgSaveFlight(output)) {
431                 output.close();
432                 SG_LOG(SG_INPUT, SG_INFO, "Saved flight to fgfs.sav");
433             } else {
434                 SG_LOG(SG_INPUT, SG_ALERT, "Cannot save flight to fgfs.sav");
435             }
436             return;
437         }
438         case 256+GLUT_KEY_F3: {
439             string panel_path =
440                 fgGetString("/sim/panel/path", "Panels/Default/default.xml");
441             FGPanel * new_panel = fgReadPanel(panel_path);
442             if (new_panel == 0) {
443                 SG_LOG(SG_INPUT, SG_ALERT,
444                        "Error reading new panel from " << panel_path);
445                 return;
446             }
447             SG_LOG(SG_INPUT, SG_INFO, "Loaded new panel from " << panel_path);
448             current_panel->unbind();
449             delete current_panel;
450             current_panel = new_panel;
451             return;
452         }
453         case 256+GLUT_KEY_F4: {
454             SGPath props_path(globals->get_fg_root());
455             props_path.append("preferences.xml");
456             SG_LOG(SG_INPUT, SG_INFO, "Rereading global preferences");
457             if (!readProperties(props_path.str(), globals->get_props())) {
458                 SG_LOG(SG_INPUT, SG_ALERT,
459                        "Failed to reread global preferences from "
460                        << props_path.str());
461             } else {
462                 SG_LOG(SG_INPUT, SG_INFO, "Finished Reading global preferences");
463             }
464             return;
465         }
466         case 256+GLUT_KEY_F5: {
467             current_panel->setYOffset(current_panel->getYOffset() - 5);
468             fgReshape(fgGetInt("/sim/startup/xsize"),
469                       fgGetInt("/sim/startup/ysize"));
470             return;
471         }
472         case 256+GLUT_KEY_F6: {
473             current_panel->setYOffset(current_panel->getYOffset() + 5);
474             fgReshape(fgGetInt("/sim/startup/xsize"),
475                       fgGetInt("/sim/startup/ysize"));
476             return;
477         }
478         case 256+GLUT_KEY_F7: {
479             current_panel->setXOffset(current_panel->getXOffset() - 5);
480             return;
481         }
482         case 256+GLUT_KEY_F8: {
483             current_panel->setXOffset(current_panel->getXOffset() + 5);
484             return;
485         }
486         // case 256+GLUT_KEY_F9: {
487         //     return;
488         // }
489         case 256+GLUT_KEY_F10: {
490             fgToggleFDMdataLogging();
491             return;
492         }
493         // case 256+GLUT_KEY_F11: {
494         //     return;
495         // }
496         // case 256+GLUT_KEY_F12: {
497         //     return;
498         // }
499         case 256+GLUT_KEY_END: // numeric keypad 1
500             v->set_goal_view_offset( SGD_PI * 0.75 );
501             return;
502         case 256+GLUT_KEY_DOWN: // numeric keypad 2
503             v->set_goal_view_offset( SGD_PI );
504             return;
505         case 256+GLUT_KEY_PAGE_DOWN: // numeric keypad 3
506             v->set_goal_view_offset( SGD_PI * 1.25 );
507             return;
508         case 256+GLUT_KEY_LEFT: // numeric keypad 4
509             v->set_goal_view_offset( SGD_PI * 0.50 );
510             return;
511         case 256+GLUT_KEY_RIGHT: // numeric keypad 6
512             v->set_goal_view_offset( SGD_PI * 1.50 );
513             return;
514         case 256+GLUT_KEY_HOME: // numeric keypad 7
515             v->set_goal_view_offset( SGD_PI * 0.25 );
516             return;
517         case 256+GLUT_KEY_UP: // numeric keypad 8
518             v->set_goal_view_offset( 0.00 );
519             return;
520         case 256+GLUT_KEY_PAGE_UP: // numeric keypad 9
521             v->set_goal_view_offset( SGD_PI * 1.75 );
522             return;
523
524 // END SPECIALS
525
526         }
527
528
529     } else {
530         SG_LOG( SG_INPUT, SG_DEBUG, "" );
531         switch (k) {
532         case 98: // b key
533             int b_ret;
534             double b_set;
535             b_ret = int( controls.get_brake( 0 ) );
536             b_set = double(!b_ret);
537             controls.set_brake( FGControls::ALL_WHEELS, b_set);
538             return;
539         case 44: // , key
540             if (controls.get_brake(0) > 0.0) {
541                 controls.set_brake(0, 0.0);
542             } else {
543                 controls.set_brake(0, 1.0);
544             }
545             return;
546         case 46: // . key
547             if (controls.get_brake(1) > 0.0) {
548                 controls.set_brake(1, 0.0);
549             } else {
550                 controls.set_brake(1, 1.0);
551             }
552             return;
553         case 104: // h key
554             HUD_masterswitch( true );
555             return;
556         case 105: // i key
557             fgHUDInit(&current_aircraft);  // normal HUD
558             return;
559         case 109: // m key
560             globals->inc_warp( 60 );
561             fgUpdateSkyAndLightingParams();
562             return;
563         case 112: // p key
564             globals->set_freeze( ! globals->get_freeze() );
565
566             {
567                 SGBucket p( f->get_Longitude() * SGD_RADIANS_TO_DEGREES,
568                             f->get_Latitude() * SGD_RADIANS_TO_DEGREES );
569                 SGPath tile_path( globals->get_fg_root() );
570                 tile_path.append( "Scenery" );
571                 tile_path.append( p.gen_base_path() );
572                 tile_path.append( p.gen_index_str() );
573
574                 // printf position and attitude information
575                 SG_LOG( SG_INPUT, SG_INFO,
576                         "Lon = " << f->get_Longitude() * SGD_RADIANS_TO_DEGREES
577                         << "  Lat = " << f->get_Latitude() * SGD_RADIANS_TO_DEGREES
578                         << "  Altitude = " << f->get_Altitude() * SG_FEET_TO_METER
579                         );
580                 SG_LOG( SG_INPUT, SG_INFO,
581                         "Heading = " << f->get_Psi() * SGD_RADIANS_TO_DEGREES 
582                         << "  Roll = " << f->get_Phi() * SGD_RADIANS_TO_DEGREES
583                         << "  Pitch = " << f->get_Theta() * SGD_RADIANS_TO_DEGREES );
584                 SG_LOG( SG_INPUT, SG_INFO, tile_path.c_str());
585             }
586             return;
587         case 116: // t key
588             globals->inc_warp_delta( 30 );
589             fgUpdateSkyAndLightingParams();
590             return;
591         case 118: // v key
592             // handles GUI state as well as Viewer LookAt Direction
593             CenterView();
594             globals->set_current_view( globals->get_viewmgr()->next_view() );
595             fgReshape( fgGetInt("/sim/startup/xsize"),
596                        fgGetInt("/sim/startup/ysize") );
597             return;
598         case 120: // x key
599             fov = globals->get_current_view()->get_fov();
600             fov /= 1.05;
601             if ( fov < FG_FOV_MIN ) {
602                 fov = FG_FOV_MIN;
603             }
604             globals->get_current_view()->set_fov(fov);
605             // v->force_update_fov_math();
606             return;
607         case 122: // z key
608 #ifndef FG_OLD_WEATHER
609             tmp = WeatherDatabase->getWeatherVisibility();
610             tmp *= 1.10;
611             WeatherDatabase->setWeatherVisibility( tmp );
612 #else
613             tmp = current_weather.get_visibility();   // in meters
614             tmp *= 1.10;
615             current_weather.set_visibility( tmp );
616 #endif
617             return;
618         case 27: // ESC
619             // if( fg_DebugOutput ) {
620             //   fclose( fg_DebugOutput );
621             // }
622             SG_LOG( SG_INPUT, SG_ALERT, 
623                     "Program exit requested." );
624             ConfirmExitDialog();
625             return;
626
627 // START SPECIALS
628
629         case 256+GLUT_KEY_F2: // F2 Reload Tile Cache...
630             {
631                 bool freeze = globals->get_freeze();
632                 SG_LOG(SG_INPUT, SG_INFO, "ReIniting TileCache");
633                 if ( !freeze ) 
634                     globals->set_freeze( true );
635                 BusyCursor(0);
636                 if ( global_tile_mgr.init() ) {
637                     // Load the local scenery data
638                     global_tile_mgr.update( 
639                         cur_fdm_state->get_Longitude() * SGD_RADIANS_TO_DEGREES,
640                         cur_fdm_state->get_Latitude() * SGD_RADIANS_TO_DEGREES );
641                 } else {
642                     SG_LOG( SG_GENERAL, SG_ALERT, 
643                             "Error in Tile Manager initialization!" );
644                     exit(-1);
645                 }
646                 BusyCursor(1);
647                 if ( !freeze )
648                    globals->set_freeze( false );
649                 return;
650             }
651         case 256+GLUT_KEY_F3: // F3 Take a screen shot
652             fgDumpSnapShot();
653             return;
654         case 256+GLUT_KEY_F4: // F4 Update lighting manually
655             fgUpdateSkyAndLightingParams();
656             return;
657         case 256+GLUT_KEY_F6: // F6 toggles Autopilot target location
658             if ( current_autopilot->get_HeadingMode() !=
659                  FGAutopilot::FG_HEADING_WAYPOINT ) {
660                 current_autopilot->set_HeadingMode(
661                     FGAutopilot::FG_HEADING_WAYPOINT );
662                 current_autopilot->set_HeadingEnabled( true );
663             } else {
664                 current_autopilot->set_HeadingMode(
665                     FGAutopilot::FG_TC_HEADING_LOCK );
666             }
667             return;
668         case 256+GLUT_KEY_F8: {// F8 toggles fog ... off fastest nicest...
669             const string &fog = fgGetString("/sim/rendering/fog");
670             if (fog == "disabled") {
671               fgSetString("/sim/rendering/fog", "fastest");
672               SG_LOG(SG_INPUT, SG_INFO, "Fog enabled, hint=fastest");
673             } else if (fog == "fastest") {
674               fgSetString("/sim/rendering/fog", "nicest");
675               SG_LOG(SG_INPUT, SG_INFO, "Fog enabled, hint=nicest");
676             } else if (fog == "nicest") {
677               fgSetString("/sim/rendering/fog", "disabled");
678               SG_LOG(SG_INPUT, SG_INFO, "Fog disabled");
679             } else {
680               fgSetString("/sim/rendering/fog", "disabled");
681               SG_LOG(SG_INPUT, SG_ALERT, "Unrecognized fog type "
682                      << fog << ", changed to 'disabled'");
683             }
684             return;
685         }
686         case 256+GLUT_KEY_F9: // F9 toggles textures on and off...
687             SG_LOG( SG_INPUT, SG_INFO, "Toggling texture" );
688             if ( fgGetBool("/sim/rendering/textures")) {
689                 fgSetBool("/sim/rendering/textures", false);
690                 material_lib.set_step( 1 );
691             } else {
692                 fgSetBool("/sim/rendering/textures", true);
693                 material_lib.set_step( 0 );
694             }
695             return;
696         case 256+GLUT_KEY_F10: // F10 toggles menu on and off...
697             SG_LOG(SG_INPUT, SG_INFO, "Invoking call back function");
698             guiToggleMenu();
699             return;
700         case 256+GLUT_KEY_F11: // F11 Altitude Dialog.
701             SG_LOG(SG_INPUT, SG_INFO, "Invoking Altitude call back function");
702             NewAltitude( NULL );
703             return;
704         case 256+GLUT_KEY_F12: // F12 Heading Dialog...
705             SG_LOG(SG_INPUT, SG_INFO, "Invoking Heading call back function");
706             NewHeading( NULL );
707             return;
708         }
709
710 // END SPECIALS
711
712     }
713 }
714
715
716 void
717 FGInput::action (const SGPropertyNode * binding)
718 {
719   const string &action = binding->getStringValue("action", "");
720   const string &control = binding->getStringValue("control", "");
721   bool repeatable = binding->getBoolValue("repeatable", false);
722   int step = binding->getIntValue("step", 0.0);
723
724   if (control == "") {
725     SG_LOG(SG_INPUT, SG_ALERT, "No control specified for key "
726            << binding->getIndex());
727     return;
728   }
729
730   else if (action == "") {
731     SG_LOG(SG_INPUT, SG_ALERT, "No action specified for key "
732            << binding->getIndex());
733     return;
734   }
735
736   else if (action == "switch") {
737     fgSetBool(control, !fgGetBool(control));
738   }
739
740   else if (action == "adjust") {
741     const SGValue * step = binding->getValue("step");
742     if (step == 0) {
743       SG_LOG(SG_INPUT, SG_ALERT, "No step supplied for adjust action for key "
744              << binding->getIndex());
745       return;
746     }
747     SGValue * target = fgGetValue(control, true);
748                                 // Use the target's type...
749     switch (target->getType()) {
750     case SGValue::BOOL:
751     case SGValue::INT:
752       target->setIntValue(target->getIntValue() + step->getIntValue());
753       break;
754     case SGValue::LONG:
755       target->setLongValue(target->getLongValue() + step->getLongValue());
756       break;
757     case SGValue::FLOAT:
758       target->setFloatValue(target->getFloatValue() + step->getFloatValue());
759       break;
760     case SGValue::DOUBLE:
761     case SGValue::UNKNOWN:      // treat unknown as a double
762       target->setDoubleValue(target->getDoubleValue()
763                              + step->getDoubleValue());
764       break;
765     case SGValue::STRING:
766       SG_LOG(SG_INPUT, SG_ALERT, "Failed attempt to adjust string property "
767              << control);
768       break;
769     }
770   }
771
772   else if (action == "assign") {
773     const SGValue * value = binding->getValue("value");
774     if (value == 0) {
775       SG_LOG(SG_INPUT, SG_ALERT, "No value supplied for assign action for key "
776              << binding->getIndex());
777       return;
778     }
779     SGValue * target = fgGetValue(control, true);
780                                 // Use the target's type...
781     switch (target->getType()) {
782     case SGValue::BOOL:
783       target->setBoolValue(value->getBoolValue());
784       break;
785     case SGValue::INT:
786       target->setIntValue(value->getIntValue());
787       break;
788     case SGValue::LONG:
789       target->setLongValue(value->getLongValue());
790       break;
791     case SGValue::FLOAT:
792       target->setFloatValue(value->getFloatValue());
793       break;
794     case SGValue::DOUBLE:
795       target->setDoubleValue(value->getDoubleValue());
796       break;
797     case SGValue::STRING:
798       target->setStringValue(value->getStringValue());
799       break;
800     case SGValue::UNKNOWN:
801       target->setUnknownValue(value->getStringValue());
802       break;
803     }
804   }
805
806   else {
807     SG_LOG(SG_INPUT, SG_ALERT, "Unknown action " << action
808            << " for key " << binding->getIndex());
809   }
810 }
811
812
813 #if 0
814     FGViewer *v = globals->get_current_view();
815
816     SG_LOG( SG_INPUT, SG_DEBUG, "Special key hit = " << k );
817
818     if ( GLUT_ACTIVE_SHIFT && glutGetModifiers() ) {
819         SG_LOG( SG_INPUT, SG_DEBUG, " SHIFTED" );
820         switch (k)
821     } else
822 }
823
824 #endif
825
826
827 // end of input.cxx