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 )
170 #if defined(WIN32_CURSOR_TWEAKS)
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)
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 if (mouse_mode == MOUSE_POINTER) {
270 glutPostRedisplay () ;
272 if( x == _mX && y == _mY)
275 // reset left click MOUSE_VIEW toggle feature
278 ww = fgGetInt("/sim/startup/xsize");
279 wh = fgGetInt("/sim/startup/ysize");
281 switch (mouse_mode) {
283 if( !mouse_joystick_control ) {
284 mouse_joystick_control = 1;
285 fgSetString("/sim/control-mode", "mouse");
287 if ( left_button() ) {
288 offset = (_mX - x) * brake_sensitivity;
289 globals->get_controls()->move_brake(FGControls::ALL_WHEELS, offset);
290 offset = (_mY - y) * throttle_sensitivity;
291 globals->get_controls()->move_throttle(FGControls::ALL_ENGINES, offset);
292 } else if ( right_button() ) {
293 if( ! current_autopilot->get_HeadingEnabled() ) {
294 offset = (x - _mX) * rudder_sensitivity;
295 globals->get_controls()->move_rudder(offset);
297 if( ! current_autopilot->get_AltitudeEnabled() ) {
298 offset = (_mY - y) * trim_sensitivity;
299 globals->get_controls()->move_elevator_trim(offset);
302 if( ! current_autopilot->get_HeadingEnabled() ) {
303 offset = (x - _mX) * aileron_sensitivity;
304 globals->get_controls()->move_aileron(offset);
306 if( ! current_autopilot->get_AltitudeEnabled() ) {
307 offset = (_mY - y) * elevator_sensitivity;
308 globals->get_controls()->move_elevator(offset);
312 // Keep the mouse in the window.
313 if (x < 5 || x > ww-5 || y < 5 || y > wh-5) {
322 #define CONTRAINED_MOUSE_VIEW_Y
323 #ifdef CONTRAINED_MOUSE_VIEW_Y
327 #endif // CONTRAINED_MOUSE_VIEW_Y
329 } else if( y >= wh-1) {
330 #ifdef CONTRAINED_MOUSE_VIEW_Y
334 #endif // CONTRAINED_MOUSE_VIEW_Y
337 // wrap MOUSE_VIEW mode cursor x position
341 } else if ( x >= ww-1 ) {
345 // try to get SGD_PI movement in each half of screen
349 if( middle_button() ) {
350 trackball(lastGuiQuat,
351 (2.0f * _mX - W) / W,
352 0, //(H - 2.0f * y) / H, // 3
354 0 //(H - 2.0f * _mY) / H // 1
360 trackball(lastGuiQuat,
361 0, //(2.0f * _mX - W) / W, // 0
362 (H - 2.0f * y) / H, // 3
363 0, //(2.0f * x - W) / W, // 2
364 (H - 2.0f * _mY) / H // 1
367 add_quats(lastGuiQuat, curGuiQuat, curGuiQuat);
368 build_rotmatrix(GuiQuat_mat, curGuiQuat);
371 // this could be done in above quat
372 // but requires redoing view pipeline
373 offset = globals->get_current_view()->get_goal_view_offset();
374 offset += ((_mX - x) * SGD_2PI / W );
375 while (offset < 0.0) {
378 while (offset > SGD_2PI) {
381 globals->get_current_view()->set_goal_view_offset(offset);
382 #ifdef NO_SMOOTH_MOUSE_VIEW
383 globals->get_current_view()->set_view_offset(offset);
392 glutWarpPointer(x, y);
394 // Record the new mouse position.
400 void guiMouseFunc(int button, int updown, int x, int y)
404 // private MOUSE_VIEW state variables
405 // to allow alternate left clicks in MOUSE_VIEW mode
406 // to toggle between current offsets and straight ahead
408 static int _mVx, _mVy, _Vx, _Vy;
409 static float _quat[4];
410 static double _view_offset;
412 // general purpose variables
415 glutModifiers = glutGetModifiers();
416 glut_active_shift = glutModifiers & GLUT_ACTIVE_SHIFT;
417 glut_active_ctrl = glutModifiers & GLUT_ACTIVE_CTRL;
418 glut_active_alt = glutModifiers & GLUT_ACTIVE_ALT;
420 // Was the left button pressed?
421 if (updown == GLUT_DOWN ) {
422 if( button == GLUT_LEFT_BUTTON)
424 switch (mouse_mode) {
431 // resume previous view offsets
436 curGuiQuat[0] = _quat[0];
437 curGuiQuat[1] = _quat[1];
438 curGuiQuat[2] = _quat[2];
439 curGuiQuat[3] = _quat[3];
440 globals->get_current_view()->set_goal_view_offset(_view_offset);
441 #ifdef NO_SMOOTH_MOUSE_VIEW
442 globals->get_current_view()->set_view_offset(_view_offset);
450 _quat[0] = curGuiQuat[0];
451 _quat[1] = curGuiQuat[1];
452 _quat[2] = curGuiQuat[2];
453 _quat[3] = curGuiQuat[3];
454 x = fgGetInt("/sim/startup/xsize")/2;
455 y = fgGetInt("/sim/startup/ysize")/2;
458 globals->get_current_view()->get_goal_view_offset();
459 globals->get_current_view()->set_goal_view_offset(0.0);
460 #ifdef NO_SMOOTH_MOUSE_VIEW
461 globals->get_current_view()->set_view_offset(0.0);
464 glutWarpPointer( x , y);
465 build_rotmatrix(GuiQuat_mat, curGuiQuat);
466 _mVtoggle = ~_mVtoggle;
469 }else if ( button == GLUT_RIGHT_BUTTON) {
470 switch (mouse_mode) {
472 mouse_mode = MOUSE_YOKE;
473 mouse_joystick_control = 0;
476 // start with zero point in center of screen
477 _mX = fgGetInt("/sim/startup/xsize")/2;
478 _mY = fgGetInt("/sim/startup/ysize")/2;
480 // try to have the MOUSE_YOKE position
481 // reflect the current stick position
482 offset = globals->get_controls()->get_aileron();
483 x = _mX - (int)(offset * aileron_sensitivity);
484 offset = globals->get_controls()->get_elevator();
485 y = _mY - (int)(offset * elevator_sensitivity);
487 glutSetCursor(GLUT_CURSOR_CROSSHAIR);
488 SG_LOG( SG_INPUT, SG_INFO, "Mouse in yoke mode" );
492 mouse_mode = MOUSE_VIEW;
493 fgSetString("/sim/control-mode", "joystick");
494 x = fgGetInt("/sim/startup/xsize")/2;
495 y = fgGetInt("/sim/startup/ysize")/2;
498 build_rotmatrix(GuiQuat_mat, curGuiQuat);
499 glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
500 SG_LOG( SG_INPUT, SG_INFO, "Mouse in view mode" );
504 mouse_mode = MOUSE_POINTER;
507 #ifdef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
509 build_rotmatrix(GuiQuat_mat, curGuiQuat);
510 globals->get_current_view()->set_goal_view_offset(0.0);
511 #ifdef NO_SMOOTH_MOUSE_VIEW
512 globals->get_current_view()->set_view_offset(0.0);
514 #endif // RESET_VIEW_ON_LEAVING_MOUSE_VIEW
515 glutSetCursor(GLUT_CURSOR_INHERIT);
520 SG_LOG( SG_INPUT, SG_INFO, "Mouse in pointer mode" );
523 glutWarpPointer( x, y );
524 } // END RIGHT BUTTON
525 } // END UPDOWN == GLUT_DOWN
527 // Note which button is pressed.
528 if ( updown == GLUT_DOWN ) {
529 last_buttons |= ( 1 << button ) ;
531 last_buttons &= ~( 1 << button ) ;
534 // If we're in pointer mode, let PUI
535 // know what's going on.
536 if (mouse_mode == MOUSE_POINTER) {
537 if (!puMouse (button, updown, x,y)) {
538 if ( current_panel != NULL ) {
539 current_panel->doMouseAction(button, updown, x, y);
544 // Register the new position (if it
545 // hasn't been registered already).
549 glutPostRedisplay ();