]> git.mxchange.org Git - flightgear.git/blob - src/Input/input.cxx
f3dcdb756afbc885836ef3e2d4c4cb6467cb04ba
[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]->getIndex();
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, "Key stroke not bound = "
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     if (_key_bindings[modifiers].find(k) != _key_bindings[modifiers].end()) {
308         const vector<FGBinding> &bindings = _key_bindings[modifiers][k];
309         for (unsigned int i = 0; i < bindings.size(); i++) {
310             bindings[i].fire();
311         }
312         return;
313     }
314
315     SG_LOG(SG_INPUT, SG_INFO, "(No user binding.)");
316
317     // Use the old, default actions.
318     FGInterface *f = current_aircraft.fdm_state;
319     FGViewer *v = globals->get_current_view();
320   
321     // everything after here will be removed sooner or later...
322
323     if (modifiers & FG_MOD_SHIFT) {
324
325         switch (k) {
326         case 7: // Ctrl-G key
327             current_autopilot->set_AltitudeMode( 
328                                                 FGAutopilot::FG_ALTITUDE_GS1 );
329             current_autopilot->set_AltitudeEnabled(
330                                                    ! current_autopilot->get_AltitudeEnabled()
331                                                    );
332             return;
333         case 18: // Ctrl-R key
334             // temporary
335             winding_ccw = !winding_ccw;
336             if ( winding_ccw ) {
337                 glFrontFace ( GL_CCW );
338             } else {
339                 glFrontFace ( GL_CW );
340             }
341             return;
342         case 19: // Ctrl-S key
343             current_autopilot->set_AutoThrottleEnabled(
344                                                        ! current_autopilot->get_AutoThrottleEnabled()
345                                                        );
346             return;
347         case 20: // Ctrl-T key
348             current_autopilot->set_AltitudeMode( 
349                                                 FGAutopilot::FG_ALTITUDE_TERRAIN );
350             current_autopilot->set_AltitudeEnabled(
351                                                    ! current_autopilot->get_AltitudeEnabled()
352                                                    );
353             return;
354         case 49: // numeric keypad 1
355             v->set_goal_view_offset( SGD_PI * 0.75 );
356             return;
357         case 50: // numeric keypad 2
358             v->set_goal_view_offset( SGD_PI );
359             return;
360         case 51: // numeric keypad 3
361             v->set_goal_view_offset( SGD_PI * 1.25 );
362             return;
363         case 52: // numeric keypad 4
364             v->set_goal_view_offset( SGD_PI * 0.50 );
365             return;
366         case 54: // numeric keypad 6
367             v->set_goal_view_offset( SGD_PI * 1.50 );
368             return;
369         case 55: // numeric keypad 7
370             v->set_goal_view_offset( SGD_PI * 0.25 );
371             return;
372         case 56: // numeric keypad 8
373             v->set_goal_view_offset( 0.00 );
374             return;
375         case 57: // numeric keypad 9
376             v->set_goal_view_offset( SGD_PI * 1.75 );
377             return;
378         case 72: // H key
379             HUD_brightkey( true );
380             return;
381         case 73: // I key
382             // Minimal Hud
383             fgHUDInit2(&current_aircraft);
384             return;
385         case 77: // M key
386             globals->inc_warp( -60 );
387             fgUpdateSkyAndLightingParams();
388             return;
389         case 84: // T key
390             globals->inc_warp_delta( -30 );
391             fgUpdateSkyAndLightingParams();
392             return;
393         case 87: // W key
394 #if defined(FX) && !defined(WIN32)
395             global_fullscreen = ( !global_fullscreen );
396 #  if defined(XMESA_FX_FULLSCREEN) && defined(XMESA_FX_WINDOW)
397             XMesaSetFXmode( global_fullscreen ? 
398                             XMESA_FX_FULLSCREEN : XMESA_FX_WINDOW );
399 #  endif
400 #endif
401             return;
402         case 88: // X key
403             fov = globals->get_current_view()->get_fov();
404             fov *= 1.05;
405             if ( fov > FG_FOV_MAX ) {
406                 fov = FG_FOV_MAX;
407             }
408             globals->get_current_view()->set_fov(fov);
409             // v->force_update_fov_math();
410             return;
411         case 90: // Z key
412 #ifndef FG_OLD_WEATHER
413             tmp = WeatherDatabase->getWeatherVisibility();
414             tmp /= 1.10;
415             WeatherDatabase->setWeatherVisibility( tmp );
416 #else
417             tmp = current_weather.get_visibility();   // in meters
418             tmp /= 1.10;
419             current_weather.set_visibility( tmp );
420 #endif
421             return;
422
423             // START SPECIALS
424
425         case 256+GLUT_KEY_F1: {
426             ifstream input("fgfs.sav");
427             if (input.good() && fgLoadFlight(input)) {
428                 input.close();
429                 SG_LOG(SG_INPUT, SG_INFO, "Restored flight from fgfs.sav");
430             } else {
431                 SG_LOG(SG_INPUT, SG_ALERT, "Cannot load flight from fgfs.sav");
432             }
433             return;
434         }
435         case 256+GLUT_KEY_F2: {
436             SG_LOG(SG_INPUT, SG_INFO, "Saving flight");
437             ofstream output("fgfs.sav");
438             if (output.good() && fgSaveFlight(output)) {
439                 output.close();
440                 SG_LOG(SG_INPUT, SG_INFO, "Saved flight to fgfs.sav");
441             } else {
442                 SG_LOG(SG_INPUT, SG_ALERT, "Cannot save flight to fgfs.sav");
443             }
444             return;
445         }
446         case 256+GLUT_KEY_F3: {
447             string panel_path =
448                 fgGetString("/sim/panel/path", "Panels/Default/default.xml");
449             FGPanel * new_panel = fgReadPanel(panel_path);
450             if (new_panel == 0) {
451                 SG_LOG(SG_INPUT, SG_ALERT,
452                        "Error reading new panel from " << panel_path);
453                 return;
454             }
455             SG_LOG(SG_INPUT, SG_INFO, "Loaded new panel from " << panel_path);
456             current_panel->unbind();
457             delete current_panel;
458             current_panel = new_panel;
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 #if 0
822 FGViewer *v = globals->get_current_view();
823
824 SG_LOG( SG_INPUT, SG_DEBUG, "Special key hit = " << k );
825
826 if ( GLUT_ACTIVE_SHIFT && glutGetModifiers() ) {
827     SG_LOG( SG_INPUT, SG_DEBUG, " SHIFTED" );
828     switch (k)
829         } else
830         }
831
832 #endif
833
834
835 // end of input.cxx