1 /**************************************************************************
4 * Written 1998 by Durk Talsma, started Juni, 1998. For the flight gear
7 * Additional mouse supported added by David Megginson, 1999.
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 **************************************************************************/
31 #include <simgear/compiler.h>
33 #ifdef SG_MATH_EXCEPTION_CLASH
44 #if defined(FX) && defined(XMESA)
45 # include <GL/xmesa.h>
53 #include <simgear/constants.h>
54 #include <simgear/debug/logstream.hxx>
55 #include <simgear/misc/sg_path.hxx>
56 #include <simgear/screen/screen-dump.hxx>
58 #include <Include/general.hxx>
59 //#include <Include/fg_memory.h>
60 #include <Aircraft/aircraft.hxx>
61 #include <Airports/simple.hxx>
62 //#include <Autopilot/auto_gui.hxx>
63 #include <Autopilot/newauto.hxx>
64 #include <Cockpit/panel.hxx>
65 #include <Controls/controls.hxx>
66 #include <FDM/flight.hxx>
67 #include <Main/fg_init.hxx>
68 #include <Main/fg_props.hxx>
69 #include <Main/viewmgr.hxx>
72 #include "gui_local.hxx"
76 #ifndef SG_HAVE_NATIVE_SGI_COMPILERS
80 /* --------------------------------------------------------------------
82 ---------------------------------------------------------------------*/
86 static int _savedX = 0;
87 static int _savedY = 0;
88 static int last_buttons = 0 ;
89 static int mouse_active = 0;
90 static int mouse_joystick_control = 0;
92 //static time_t mouse_off_time;
93 //static int mouse_timed_out;
95 // to allow returning to previous view
96 // on second left click in MOUSE_VIEW mode
97 // This has file scope so that it can be reset
98 // if the little rodent is moved NHV
101 // we break up the glutGetModifiers return mask
102 // once per loop and stash what we need in these
103 static int glut_active_shift;
104 static int glut_active_ctrl;
105 static int glut_active_alt;
107 // uncomment this for view to exactly follow mouse in MOUSE_VIEW mode
108 // else smooth out the view panning to .01 radian per frame
109 // see view_offset smoothing mechanism in main.cxx
110 #define NO_SMOOTH_MOUSE_VIEW
112 // uncomment following to
113 #define RESET_VIEW_ON_LEAVING_MOUSE_VIEW
115 /* --------------------------------------------------------------------
116 Support for mouse as control yoke (david@megginson.com)
118 - right button toggles between pointer and yoke
119 - horizontal drag with no buttons moves ailerons
120 - vertical drag with no buttons moves elevators
121 - horizontal drag with left button moves brakes (left=on)
122 - vertical drag with left button moves throttle (up=more)
123 - horizontal drag with middle button moves rudder
124 - vertical drag with middle button moves trim
126 For the *_sensitivity variables, a lower number means more sensitive.
128 TODO: figure out how to keep pointer from leaving window in yoke mode.
129 TODO: add thresholds and null zones
130 TODO: sensitivity should be configurable at user option.
131 TODO: allow differential braking (this will be useful if FlightGear
132 ever supports tail-draggers like the DC-3)
133 ---------------------------------------------------------------------*/
135 MouseMode mouse_mode = MOUSE_POINTER;
137 static double aileron_sensitivity = 1.0/500.0;
138 static double elevator_sensitivity = 1.0/500.0;
139 static double brake_sensitivity = 1.0/250.0;
140 static double throttle_sensitivity = 1.0/250.0;
141 static double rudder_sensitivity = 1.0/500.0;
142 static double trim_sensitivity = 1.0/1000.0;
144 static inline int guiGetMouseButton(void)
149 static inline void guiGetMouse(int *x, int *y)
155 static inline int left_button( void ) {
156 return( last_buttons & (1 << GLUT_LEFT_BUTTON) );
159 static inline int middle_button( void ) {
160 return( last_buttons & (1 << GLUT_MIDDLE_BUTTON) );
163 static inline int right_button( void ) {
164 return( last_buttons & (1 << GLUT_RIGHT_BUTTON) );
167 void TurnCursorOn( void )
171 switch (mouse_mode) {
173 glutSetCursor(GLUT_CURSOR_INHERIT);
176 glutSetCursor(GLUT_CURSOR_CROSSHAIR);
179 glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
183 #if defined(X_CURSOR_TWEAKS)
184 glutWarpPointer( fgGetInt("/sim/startup/xsize")/2,
185 fgGetInt("/sim/startup/ysize")/2);
189 void TurnCursorOff( void )
192 #if defined(WIN32_CURSOR_TWEAKS)
193 glutSetCursor(GLUT_CURSOR_NONE);
194 #elif defined(X_CURSOR_TWEAKS)
195 glutWarpPointer( fgGetInt("/sim/startup/xsize"),
196 fgGetInt("/sim/startup/ysize"));
200 void maybeToggleMouse( void )
202 #if defined(WIN32_CURSOR_TWEAKS_OFF)
203 static int first_time = ~0;
204 static int mouse_changed = 0;
208 mouse_changed = ~mouse_changed;
212 if( mouse_mode != MOUSE_POINTER )
214 if( mouse_changed ) {
215 mouse_changed = ~mouse_changed;
221 first_time = ~first_time;
222 #endif // #ifdef WIN32
225 // Call with FALSE to init and TRUE to restore
226 void BusyCursor( int restore )
228 static GLenum cursor = (GLenum) 0;
230 glutSetCursor(cursor);
232 cursor = (GLenum) glutGet( (GLenum) GLUT_WINDOW_CURSOR );
233 #if defined(WIN32_CURSOR_TWEAKS)
236 glutSetCursor( GLUT_CURSOR_WAIT );
241 // Center the view offsets
242 void CenterView( void ) {
243 if( mouse_mode == MOUSE_VIEW ) {
244 mouse_mode = MOUSE_POINTER;
245 _savedX = fgGetInt("/sim/startup/xsize")/2;
246 _savedY = fgGetInt("/sim/startup/ysize")/2;
249 build_rotmatrix(GuiQuat_mat, curGuiQuat);
250 glutSetCursor(GLUT_CURSOR_INHERIT);
252 // Is this necessary ??
253 if( !gui_menu_on ) TurnCursorOff();
255 glutWarpPointer( _savedX, _savedY );
257 globals->get_current_view()->set_goal_view_offset(0.0);
258 globals->get_current_view()->set_view_offset(0.0);
262 void guiMotionFunc ( int x, int y )
264 int ww, wh, need_warp = 0;
268 ww = fgGetInt("/sim/startup/xsize");
269 wh = fgGetInt("/sim/startup/ysize");
271 if (mouse_mode == MOUSE_POINTER) {
272 // TURN MENU ON IF MOUSE AT TOP
277 // TURN MENU OFF IF MOUSE AT BOTTOM
278 else if( y > wh-1 ) {
283 glutPostRedisplay () ;
285 if( x == _mX && y == _mY)
288 // reset left click MOUSE_VIEW toggle feature
291 switch (mouse_mode) {
293 if( !mouse_joystick_control ) {
294 mouse_joystick_control = 1;
295 fgSetString("/sim/control-mode", "mouse");
297 if ( left_button() ) {
298 offset = (_mX - x) * brake_sensitivity;
299 globals->get_controls()->move_brake(FGControls::ALL_WHEELS, offset);
300 offset = (_mY - y) * throttle_sensitivity;
301 globals->get_controls()->move_throttle(FGControls::ALL_ENGINES, offset);
302 } else if ( right_button() ) {
303 if( ! current_autopilot->get_HeadingEnabled() ) {
304 offset = (x - _mX) * rudder_sensitivity;
305 globals->get_controls()->move_rudder(offset);
307 if( ! current_autopilot->get_AltitudeEnabled() ) {
308 offset = (_mY - y) * trim_sensitivity;
309 globals->get_controls()->move_elevator_trim(offset);
312 if( ! current_autopilot->get_HeadingEnabled() ) {
313 offset = (x - _mX) * aileron_sensitivity;
314 globals->get_controls()->move_aileron(offset);
316 if( ! current_autopilot->get_AltitudeEnabled() ) {
317 offset = (_mY - y) * elevator_sensitivity;
318 globals->get_controls()->move_elevator(offset);
322 // Keep the mouse in the window.
323 if (x < 5 || x > ww-5 || y < 5 || y > wh-5) {
332 #define CONTRAINED_MOUSE_VIEW_Y
333 #ifdef CONTRAINED_MOUSE_VIEW_Y
337 #endif // CONTRAINED_MOUSE_VIEW_Y
339 } else if( y >= wh-1) {
340 #ifdef CONTRAINED_MOUSE_VIEW_Y
344 #endif // CONTRAINED_MOUSE_VIEW_Y
347 // wrap MOUSE_VIEW mode cursor x position
351 } else if ( x >= ww-1 ) {
355 // try to get SGD_PI movement in each half of screen
359 if( middle_button() ) {
360 trackball(lastGuiQuat,
361 (2.0f * _mX - W) / W,
362 0, //(H - 2.0f * y) / H, // 3
364 0 //(H - 2.0f * _mY) / H // 1
370 trackball(lastGuiQuat,
371 0, //(2.0f * _mX - W) / W, // 0
372 (H - 2.0f * y) / H, // 3
373 0, //(2.0f * x - W) / W, // 2
374 (H - 2.0f * _mY) / H // 1
377 add_quats(lastGuiQuat, curGuiQuat, curGuiQuat);
378 build_rotmatrix(GuiQuat_mat, curGuiQuat);
381 // this could be done in above quat
382 // but requires redoing view pipeline
383 offset = globals->get_current_view()->get_goal_view_offset();
384 offset += ((_mX - x) * SGD_2PI / W );
385 while (offset < 0.0) {
388 while (offset > SGD_2PI) {
391 globals->get_current_view()->set_goal_view_offset(offset);
392 #ifdef NO_SMOOTH_MOUSE_VIEW
393 globals->get_current_view()->set_view_offset(offset);
402 glutWarpPointer(x, y);
404 // Record the new mouse position.
410 void guiMouseFunc(int button, int updown, int x, int y)
414 // private MOUSE_VIEW state variables
415 // to allow alternate left clicks in MOUSE_VIEW mode
416 // to toggle between current offsets and straight ahead
418 static int _mVx, _mVy, _Vx, _Vy;
419 static float _quat[4];
420 static double _view_offset;
422 // general purpose variables
425 glutModifiers = glutGetModifiers();
426 glut_active_shift = glutModifiers & GLUT_ACTIVE_SHIFT;
427 glut_active_ctrl = glutModifiers & GLUT_ACTIVE_CTRL;
428 glut_active_alt = glutModifiers & GLUT_ACTIVE_ALT;
430 // Was the left button pressed?
431 if (updown == GLUT_DOWN ) {
432 if( button == GLUT_LEFT_BUTTON)
434 switch (mouse_mode) {
441 // resume previous view offsets
446 curGuiQuat[0] = _quat[0];
447 curGuiQuat[1] = _quat[1];
448 curGuiQuat[2] = _quat[2];
449 curGuiQuat[3] = _quat[3];
450 globals->get_current_view()->set_goal_view_offset(_view_offset);
451 #ifdef NO_SMOOTH_MOUSE_VIEW
452 globals->get_current_view()->set_view_offset(_view_offset);
460 _quat[0] = curGuiQuat[0];
461 _quat[1] = curGuiQuat[1];
462 _quat[2] = curGuiQuat[2];
463 _quat[3] = curGuiQuat[3];
464 x = fgGetInt("/sim/startup/xsize")/2;
465 y = fgGetInt("/sim/startup/ysize")/2;
468 globals->get_current_view()->get_goal_view_offset();
469 globals->get_current_view()->set_goal_view_offset(0.0);
470 #ifdef NO_SMOOTH_MOUSE_VIEW
471 globals->get_current_view()->set_view_offset(0.0);
474 glutWarpPointer( x , y);
475 build_rotmatrix(GuiQuat_mat, curGuiQuat);
476 _mVtoggle = ~_mVtoggle;
479 }else if ( button == GLUT_RIGHT_BUTTON) {
480 switch (mouse_mode) {
482 mouse_mode = MOUSE_YOKE;
483 mouse_joystick_control = 0;
486 // start with zero point in center of screen
487 _mX = fgGetInt("/sim/startup/xsize")/2;
488 _mY = fgGetInt("/sim/startup/ysize")/2;
490 // try to have the MOUSE_YOKE position
491 // reflect the current stick position
492 offset = globals->get_controls()->get_aileron();
493 x = _mX - (int)(offset * aileron_sensitivity);
494 offset = globals->get_controls()->get_elevator();
495 y = _mY - (int)(offset * elevator_sensitivity);
497 glutSetCursor(GLUT_CURSOR_CROSSHAIR);
498 SG_LOG( SG_INPUT, SG_INFO, "Mouse in yoke mode" );
502 mouse_mode = MOUSE_VIEW;
503 fgSetString("/sim/control-mode", "joystick");
504 x = fgGetInt("/sim/startup/xsize")/2;
505 y = fgGetInt("/sim/startup/ysize")/2;
508 build_rotmatrix(GuiQuat_mat, curGuiQuat);
509 glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
510 SG_LOG( SG_INPUT, SG_INFO, "Mouse in view mode" );
514 mouse_mode = MOUSE_POINTER;
517 #ifdef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
519 build_rotmatrix(GuiQuat_mat, curGuiQuat);
520 globals->get_current_view()->set_goal_view_offset(0.0);
521 #ifdef NO_SMOOTH_MOUSE_VIEW
522 globals->get_current_view()->set_view_offset(0.0);
523 #endif // NO_SMOOTH_MOUSE_VIEW
524 #endif // RESET_VIEW_ON_LEAVING_MOUSE_VIEW
525 glutSetCursor(GLUT_CURSOR_INHERIT);
527 #if defined(WIN32_CURSOR_TWEAKS_OFF)
530 #endif // WIN32_CURSOR_TWEAKS_OFF
532 SG_LOG( SG_INPUT, SG_INFO, "Mouse in pointer mode" );
534 } // end switch (mouse_mode)
535 glutWarpPointer( x, y );
536 } // END RIGHT BUTTON
537 } // END UPDOWN == GLUT_DOWN
539 // Note which button is pressed.
540 if ( updown == GLUT_DOWN ) {
541 last_buttons |= ( 1 << button ) ;
543 last_buttons &= ~( 1 << button ) ;
546 // If we're in pointer mode, let PUI
547 // know what's going on.
548 if (mouse_mode == MOUSE_POINTER) {
549 if (!puMouse (button, updown, x,y)) {
550 if ( current_panel != NULL ) {
551 current_panel->doMouseAction(button, updown, x, y);
556 // Register the new position (if it
557 // hasn't been registered already).
561 glutPostRedisplay ();