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