1 // input.cxx -- handle user input from various sources.
3 // Written by David Megginson, started May 2001.
5 // Copyright (C) 2001 David Megginson, david@megginson.com
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.
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.
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.
27 #include <simgear/compiler.h>
34 #include <simgear/constants.h>
35 #include <simgear/debug/logstream.hxx>
36 #include <simgear/misc/props.hxx>
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>
45 #include <Scenery/tilemgr.hxx>
46 #include <Objects/matlib.hxx>
47 #include <Time/light.hxx>
48 #include <Time/tmp.hxx>
50 #ifndef FG_OLD_WEATHER
51 # include <WeatherCM/FGLocalWeatherDatabase.h>
53 # include <Weather/weather.hxx>
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>
64 SG_USING_STD(ifstream);
69 ////////////////////////////////////////////////////////////////////////
70 // Implementation of FGBinding.
71 ////////////////////////////////////////////////////////////////////////
73 FGBinding::FGBinding ()
74 : _action(ACTION_NONE), _node(0), _adjust_step(0), _assign_value(0)
78 FGBinding::FGBinding (const SGPropertyNode * node)
79 : _action(ACTION_NONE), _node(0), _adjust_step(0), _assign_value(0)
84 FGBinding::~FGBinding ()
90 FGBinding::setAction (Action action)
96 FGBinding::setProperty (SGPropertyNode * node)
102 FGBinding::setAdjustStep (const SGValue * step)
108 FGBinding::setAssignValue (const SGValue * value)
110 _assign_value = value;
114 FGBinding::read (const SGPropertyNode * node)
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;
127 SG_LOG(SG_INPUT, SG_ALERT, "Ignoring unrecognized action type "
131 if (node->hasValue("control"))
132 _node = fgGetNode(node->getStringValue("control"), true);
134 if (node->hasValue("step"))
135 _adjust_step = node->getChild("step")->getValue();
137 if (node->hasValue("value"))
138 _assign_value = node->getChild("value")->getValue();
142 FGBinding::fire () const
145 SG_LOG(SG_INPUT, SG_ALERT, "No control property attached to binding");
155 _node->setBoolValue(!_node->getBoolValue());
159 if (_adjust_step == 0) {
160 SG_LOG(SG_INPUT, SG_ALERT, "No step provided for adjust binding");
163 switch (_node->getType()) {
165 if (_adjust_step->getBoolValue())
166 _node->setBoolValue(!_node->getBoolValue());
169 _node->setIntValue(_node->getIntValue() + _adjust_step->getIntValue());
172 _node->setLongValue(_node->getLongValue() + _adjust_step->getLongValue());
175 _node->setFloatValue(_node->getFloatValue()
176 + _adjust_step->getFloatValue());
178 case SGValue::DOUBLE:
179 case SGValue::UNKNOWN: // force to double
180 _node->setDoubleValue(_node->getDoubleValue()
181 + _adjust_step->getDoubleValue());
183 case SGValue::STRING:
184 SG_LOG(SG_INPUT, SG_ALERT, "Cannot increment or decrement string value"
185 << _node->getStringValue());
191 if (_assign_value == 0) {
192 SG_LOG(SG_INPUT, SG_ALERT, "No value provided for assign binding");
195 switch (_node->getType()) {
197 _node->setBoolValue(_assign_value->getBoolValue());
200 _node->setIntValue(_assign_value->getIntValue());
203 _node->setLongValue(_assign_value->getLongValue());
206 _node->setFloatValue(_assign_value->getFloatValue());
208 case SGValue::DOUBLE:
209 _node->setDoubleValue(_assign_value->getDoubleValue());
211 case SGValue::STRING:
212 _node->setStringValue(_assign_value->getStringValue());
214 case SGValue::UNKNOWN:
215 _node->setUnknownValue(_assign_value->getStringValue());
224 ////////////////////////////////////////////////////////////////////////
225 // Implementation of FGInput.
226 ////////////////////////////////////////////////////////////////////////
229 extern void fgReshape( int width, int height );
231 FGInput current_input;
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");
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;
264 SG_LOG(SG_INPUT, SG_ALERT, "No code provided for key "
265 << keys[i]->getStringValue("name", "[unnamed]"));
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]));
294 // we'll do something here with the joystick
298 FGInput::doKey (int k, int modifiers, int x, int y)
301 static bool winding_ccw = true;
304 SG_LOG(SG_INPUT, SG_INFO, "User pressed key " << k
305 << " with modifiers " << modifiers);
307 const vector<FGBinding> * bindings = _find_bindings(k, modifiers);
309 for (unsigned int i = 0; i < bindings->size(); i++)
310 (*bindings)[i].fire();
314 SG_LOG(SG_INPUT, SG_INFO, "(No user binding.)");
316 // Use the old, default actions.
317 FGInterface *f = current_aircraft.fdm_state;
318 FGViewer *v = globals->get_current_view();
320 // everything after here will be removed sooner or later...
322 if (modifiers & FG_MOD_SHIFT) {
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()
332 case 18: // Ctrl-R key
334 winding_ccw = !winding_ccw;
336 glFrontFace ( GL_CCW );
338 glFrontFace ( GL_CW );
341 case 19: // Ctrl-S key
342 current_autopilot->set_AutoThrottleEnabled(
343 ! current_autopilot->get_AutoThrottleEnabled()
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()
353 case 49: // numeric keypad 1
354 v->set_goal_view_offset( SGD_PI * 0.75 );
356 case 50: // numeric keypad 2
357 v->set_goal_view_offset( SGD_PI );
359 case 51: // numeric keypad 3
360 v->set_goal_view_offset( SGD_PI * 1.25 );
362 case 52: // numeric keypad 4
363 v->set_goal_view_offset( SGD_PI * 0.50 );
365 case 54: // numeric keypad 6
366 v->set_goal_view_offset( SGD_PI * 1.50 );
368 case 55: // numeric keypad 7
369 v->set_goal_view_offset( SGD_PI * 0.25 );
371 case 56: // numeric keypad 8
372 v->set_goal_view_offset( 0.00 );
374 case 57: // numeric keypad 9
375 v->set_goal_view_offset( SGD_PI * 1.75 );
378 HUD_brightkey( true );
382 fgHUDInit2(¤t_aircraft);
385 globals->inc_warp( -60 );
386 fgUpdateSkyAndLightingParams();
389 globals->inc_warp_delta( -30 );
390 fgUpdateSkyAndLightingParams();
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 );
402 fov = globals->get_current_view()->get_fov();
404 if ( fov > FG_FOV_MAX ) {
407 globals->get_current_view()->set_fov(fov);
408 // v->force_update_fov_math();
411 #ifndef FG_OLD_WEATHER
412 tmp = WeatherDatabase->getWeatherVisibility();
414 WeatherDatabase->setWeatherVisibility( tmp );
416 tmp = current_weather.get_visibility(); // in meters
418 current_weather.set_visibility( tmp );
424 case 256+GLUT_KEY_F1: {
425 ifstream input("fgfs.sav");
426 if (input.good() && fgLoadFlight(input)) {
428 SG_LOG(SG_INPUT, SG_INFO, "Restored flight from fgfs.sav");
430 SG_LOG(SG_INPUT, SG_ALERT, "Cannot load flight from fgfs.sav");
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)) {
439 SG_LOG(SG_INPUT, SG_INFO, "Saved flight to fgfs.sav");
441 SG_LOG(SG_INPUT, SG_ALERT, "Cannot save flight to fgfs.sav");
445 case 256+GLUT_KEY_F3: {
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);
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();
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());
470 SG_LOG(SG_INPUT, SG_INFO, "Finished Reading global preferences");
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"));
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"));
486 case 256+GLUT_KEY_F7: {
487 current_panel->setXOffset(current_panel->getXOffset() - 5);
490 case 256+GLUT_KEY_F8: {
491 current_panel->setXOffset(current_panel->getXOffset() + 5);
494 // case 256+GLUT_KEY_F9: {
497 case 256+GLUT_KEY_F10: {
498 fgToggleFDMdataLogging();
501 // case 256+GLUT_KEY_F11: {
504 // case 256+GLUT_KEY_F12: {
507 case 256+GLUT_KEY_END: // numeric keypad 1
508 v->set_goal_view_offset( SGD_PI * 0.75 );
510 case 256+GLUT_KEY_DOWN: // numeric keypad 2
511 v->set_goal_view_offset( SGD_PI );
513 case 256+GLUT_KEY_PAGE_DOWN: // numeric keypad 3
514 v->set_goal_view_offset( SGD_PI * 1.25 );
516 case 256+GLUT_KEY_LEFT: // numeric keypad 4
517 v->set_goal_view_offset( SGD_PI * 0.50 );
519 case 256+GLUT_KEY_RIGHT: // numeric keypad 6
520 v->set_goal_view_offset( SGD_PI * 1.50 );
522 case 256+GLUT_KEY_HOME: // numeric keypad 7
523 v->set_goal_view_offset( SGD_PI * 0.25 );
525 case 256+GLUT_KEY_UP: // numeric keypad 8
526 v->set_goal_view_offset( 0.00 );
528 case 256+GLUT_KEY_PAGE_UP: // numeric keypad 9
529 v->set_goal_view_offset( SGD_PI * 1.75 );
538 SG_LOG( SG_INPUT, SG_DEBUG, "" );
543 b_ret = int( controls.get_brake( 0 ) );
544 b_set = double(!b_ret);
545 controls.set_brake( FGControls::ALL_WHEELS, b_set);
548 if (controls.get_brake(0) > 0.0) {
549 controls.set_brake(0, 0.0);
551 controls.set_brake(0, 1.0);
555 if (controls.get_brake(1) > 0.0) {
556 controls.set_brake(1, 0.0);
558 controls.set_brake(1, 1.0);
562 HUD_masterswitch( true );
565 fgHUDInit(¤t_aircraft); // normal HUD
568 globals->inc_warp( 60 );
569 fgUpdateSkyAndLightingParams();
572 globals->set_freeze( ! globals->get_freeze() );
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() );
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
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());
596 globals->inc_warp_delta( 30 );
597 fgUpdateSkyAndLightingParams();
600 // handles GUI state as well as Viewer LookAt Direction
602 globals->set_current_view( globals->get_viewmgr()->next_view() );
603 fgReshape( fgGetInt("/sim/startup/xsize"),
604 fgGetInt("/sim/startup/ysize") );
607 fov = globals->get_current_view()->get_fov();
609 if ( fov < FG_FOV_MIN ) {
612 globals->get_current_view()->set_fov(fov);
613 // v->force_update_fov_math();
616 #ifndef FG_OLD_WEATHER
617 tmp = WeatherDatabase->getWeatherVisibility();
619 WeatherDatabase->setWeatherVisibility( tmp );
621 tmp = current_weather.get_visibility(); // in meters
623 current_weather.set_visibility( tmp );
627 // if( fg_DebugOutput ) {
628 // fclose( fg_DebugOutput );
630 SG_LOG( SG_INPUT, SG_ALERT,
631 "Program exit requested." );
637 case 256+GLUT_KEY_F2: // F2 Reload Tile Cache...
639 bool freeze = globals->get_freeze();
640 SG_LOG(SG_INPUT, SG_INFO, "ReIniting TileCache");
642 globals->set_freeze( true );
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 );
650 SG_LOG( SG_GENERAL, SG_ALERT,
651 "Error in Tile Manager initialization!" );
656 globals->set_freeze( false );
659 case 256+GLUT_KEY_F3: // F3 Take a screen shot
662 case 256+GLUT_KEY_F4: // F4 Update lighting manually
663 fgUpdateSkyAndLightingParams();
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 );
672 current_autopilot->set_HeadingMode(
673 FGAutopilot::FG_TC_HEADING_LOCK );
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");
688 fgSetString("/sim/rendering/fog", "disabled");
689 SG_LOG(SG_INPUT, SG_ALERT, "Unrecognized fog type "
690 << fog << ", changed to 'disabled'");
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 );
700 fgSetBool("/sim/rendering/textures", true);
701 material_lib.set_step( 0 );
704 case 256+GLUT_KEY_F10: // F10 toggles menu on and off...
705 SG_LOG(SG_INPUT, SG_INFO, "Invoking call back function");
708 case 256+GLUT_KEY_F11: // F11 Altitude Dialog.
709 SG_LOG(SG_INPUT, SG_INFO, "Invoking Altitude call back function");
712 case 256+GLUT_KEY_F12: // F12 Heading Dialog...
713 SG_LOG(SG_INPUT, SG_INFO, "Invoking Heading call back function");
725 FGInput::action (const SGPropertyNode * binding)
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);
733 SG_LOG(SG_INPUT, SG_ALERT, "No control specified for key "
734 << binding->getIndex());
738 else if (action == "") {
739 SG_LOG(SG_INPUT, SG_ALERT, "No action specified for key "
740 << binding->getIndex());
744 else if (action == "switch") {
745 fgSetBool(control, !fgGetBool(control));
748 else if (action == "adjust") {
749 const SGValue * step = binding->getValue("step");
751 SG_LOG(SG_INPUT, SG_ALERT, "No step supplied for adjust action for key "
752 << binding->getIndex());
755 SGValue * target = fgGetValue(control, true);
756 // Use the target's type...
757 switch (target->getType()) {
760 target->setIntValue(target->getIntValue() + step->getIntValue());
763 target->setLongValue(target->getLongValue() + step->getLongValue());
766 target->setFloatValue(target->getFloatValue() + step->getFloatValue());
768 case SGValue::DOUBLE:
769 case SGValue::UNKNOWN: // treat unknown as a double
770 target->setDoubleValue(target->getDoubleValue()
771 + step->getDoubleValue());
773 case SGValue::STRING:
774 SG_LOG(SG_INPUT, SG_ALERT, "Failed attempt to adjust string property "
780 else if (action == "assign") {
781 const SGValue * value = binding->getValue("value");
783 SG_LOG(SG_INPUT, SG_ALERT, "No value supplied for assign action for key "
784 << binding->getIndex());
787 SGValue * target = fgGetValue(control, true);
788 // Use the target's type...
789 switch (target->getType()) {
791 target->setBoolValue(value->getBoolValue());
794 target->setIntValue(value->getIntValue());
797 target->setLongValue(value->getLongValue());
800 target->setFloatValue(value->getFloatValue());
802 case SGValue::DOUBLE:
803 target->setDoubleValue(value->getDoubleValue());
805 case SGValue::STRING:
806 target->setStringValue(value->getStringValue());
808 case SGValue::UNKNOWN:
809 target->setUnknownValue(value->getStringValue());
815 SG_LOG(SG_INPUT, SG_ALERT, "Unknown action " << action
816 << " for key " << binding->getIndex());
821 const vector<FGBinding> *
822 FGInput::_find_bindings (int k, int modifiers)
824 keyboard_map::const_iterator it = _key_bindings[modifiers].find(k);
826 // Try it straight, first.
827 if (it != _key_bindings[modifiers].end())
828 return &(_key_bindings[modifiers][k]);
830 // Try removing the control modifier
832 else if ((modifiers&FG_MOD_CTRL) && iscntrl(k))
833 return _find_bindings(k, modifiers&~FG_MOD_CTRL);
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);
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);