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
43 #if defined(FX) && defined(XMESA)
44 # include <GL/xmesa.h>
52 #include <simgear/constants.h>
53 #include <simgear/debug/logstream.hxx>
54 #include <simgear/misc/sg_path.hxx>
55 #include <simgear/screen/screen-dump.hxx>
57 #include <Include/general.hxx>
58 //#include <Include/fg_memory.h>
59 #include <Aircraft/aircraft.hxx>
60 #include <Airports/simple.hxx>
61 //#include <Autopilot/auto_gui.hxx>
62 #include <Autopilot/newauto.hxx>
63 #include <Cockpit/panel.hxx>
64 #include <Controls/controls.hxx>
65 #include <FDM/flight.hxx>
66 #include <Main/fg_init.hxx>
67 #include <Main/fg_props.hxx>
68 #include <Main/viewmgr.hxx>
71 #include "gui_local.hxx"
76 /* --------------------------------------------------------------------
78 ---------------------------------------------------------------------*/
82 static int _savedX = 0;
83 static int _savedY = 0;
84 static int last_buttons = 0 ;
85 static int mouse_active = 0;
86 static int mouse_joystick_control = 0;
88 //static time_t mouse_off_time;
89 //static int mouse_timed_out;
91 // to allow returning to previous view
92 // on second left click in MOUSE_VIEW mode
93 // This has file scope so that it can be reset
94 // if the little rodent is moved NHV
95 static int _mVtoggle = 0;
97 // we break up the glutGetModifiers return mask
98 // once per loop and stash what we need in these
99 static int glut_active_shift = 0;
100 static int glut_active_ctrl = 0;
101 static int glut_active_alt = 0;
103 static int MOUSE_XSIZE = 0;
104 static int MOUSE_YSIZE = 0;
106 // uncomment this for view to exactly follow mouse in MOUSE_VIEW mode
107 // else smooth out the view panning to .01 radian per frame
108 // see view_offset smoothing mechanism in main.cxx
109 #define NO_SMOOTH_MOUSE_VIEW
111 // uncomment following to
112 #define RESET_VIEW_ON_LEAVING_MOUSE_VIEW
114 /* --------------------------------------------------------------------
115 Support for mouse as control yoke (david@megginson.com)
117 - right button toggles between pointer and yoke
118 - horizontal drag with no buttons moves ailerons
119 - vertical drag with no buttons moves elevators
120 - horizontal drag with left button moves brakes (left=on)
121 - vertical drag with left button moves throttle (up=more)
122 - horizontal drag with middle button moves rudder
123 - vertical drag with middle button moves trim
125 For the *_sensitivity variables, a lower number means more sensitive.
127 TODO: figure out how to keep pointer from leaving window in yoke mode.
128 TODO: add thresholds and null zones
129 TODO: sensitivity should be configurable at user option.
130 TODO: allow differential braking (this will be useful if FlightGear
131 ever supports tail-draggers like the DC-3)
132 ---------------------------------------------------------------------*/
134 MouseMode mouse_mode = MOUSE_POINTER;
136 static double aileron_sensitivity = 1.0/500.0;
137 static double elevator_sensitivity = 1.0/500.0;
138 static double brake_sensitivity = 1.0/250.0;
139 static double throttle_sensitivity = 1.0/250.0;
140 static double rudder_sensitivity = 1.0/500.0;
141 static double trim_sensitivity = 1.0/1000.0;
143 void guiInitMouse(int width, int height)
146 MOUSE_YSIZE = height;
149 static inline int guiGetMouseButton(void)
154 static inline void guiGetMouse(int *x, int *y)
160 static inline int left_button( void ) {
161 return( last_buttons & (1 << GLUT_LEFT_BUTTON) );
164 static inline int middle_button( void ) {
165 return( last_buttons & (1 << GLUT_MIDDLE_BUTTON) );
168 static inline int right_button( void ) {
169 return( last_buttons & (1 << GLUT_RIGHT_BUTTON) );
172 static inline void set_goal_view_offset( float offset )
174 globals->get_current_view()->setGoalHeadingOffset_deg(offset * SGD_RADIANS_TO_DEGREES);
177 static inline void set_view_offset( float offset )
179 globals->get_current_view()->setHeadingOffset_deg(offset * SGD_RADIANS_TO_DEGREES);
182 static inline void set_goal_view_tilt( float tilt )
184 globals->get_current_view()->setGoalPitchOffset_deg(tilt);
187 static inline void set_view_tilt( float tilt )
189 globals->get_current_view()->setPitchOffset_deg(tilt);
192 static inline float get_view_offset() {
193 return globals->get_current_view()->getHeadingOffset_deg() * SGD_DEGREES_TO_RADIANS;
196 static inline float get_goal_view_offset() {
197 return globals->get_current_view()->getGoalHeadingOffset_deg() * SGD_DEGREES_TO_RADIANS;
200 static inline void move_brake(float offset) {
201 globals->get_controls()->move_brake(FGControls::ALL_WHEELS, offset);
204 static inline void move_throttle(float offset) {
205 globals->get_controls()->move_throttle(FGControls::ALL_ENGINES, offset);
208 static inline void move_rudder(float offset) {
209 globals->get_controls()->move_rudder(offset);
212 static inline void move_elevator_trim(float offset) {
213 globals->get_controls()->move_elevator_trim(offset);
216 static inline void move_aileron(float offset) {
217 globals->get_controls()->move_aileron(offset);
220 static inline void move_elevator(float offset) {
221 globals->get_controls()->move_elevator(offset);
224 static inline float get_aileron() {
225 return globals->get_controls()->get_aileron();
228 static inline float get_elevator() {
229 return globals->get_controls()->get_elevator();
232 static inline bool AP_HeadingEnabled() {
233 return globals->get_autopilot()->get_HeadingEnabled();
236 static inline bool AP_AltitudeEnabled() {
237 return globals->get_autopilot()->get_AltitudeEnabled();
240 void TurnCursorOn( void )
244 switch (mouse_mode) {
246 glutSetCursor(GLUT_CURSOR_INHERIT);
249 glutSetCursor(GLUT_CURSOR_CROSSHAIR);
252 glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
256 #if defined(X_CURSOR_TWEAKS)
257 glutWarpPointer( MOUSE_XSIZE/2,
262 void TurnCursorOff( void )
265 #if defined(WIN32_CURSOR_TWEAKS)
266 glutSetCursor(GLUT_CURSOR_NONE);
267 #elif defined(X_CURSOR_TWEAKS)
268 glutWarpPointer( MOUSE_XSIZE,
273 void maybeToggleMouse( void )
275 #if defined(WIN32_CURSOR_TWEAKS_OFF)
276 static int first_time = ~0;
277 static int mouse_changed = 0;
281 mouse_changed = ~mouse_changed;
285 if( mouse_mode != MOUSE_POINTER )
287 if( mouse_changed ) {
288 mouse_changed = ~mouse_changed;
294 first_time = ~first_time;
295 #endif // #ifdef WIN32
298 // Call with FALSE to init and TRUE to restore
299 void BusyCursor( int restore )
301 static GLenum cursor = (GLenum) 0;
303 glutSetCursor(cursor);
305 cursor = (GLenum) glutGet( (GLenum) GLUT_WINDOW_CURSOR );
306 #if defined(WIN32_CURSOR_TWEAKS)
309 glutSetCursor( GLUT_CURSOR_WAIT );
314 // Center the view offsets
315 void CenterView( void ) {
316 if( mouse_mode == MOUSE_VIEW ) {
317 mouse_mode = MOUSE_POINTER;
318 _savedX = MOUSE_XSIZE/2;
319 _savedY = MOUSE_YSIZE/2;
322 build_rotmatrix(GuiQuat_mat, curGuiQuat);
323 glutSetCursor(GLUT_CURSOR_INHERIT);
325 // Is this necessary ??
326 #if defined(FG_OLD_MENU)
327 if( !gui_menu_on ) TurnCursorOff();
330 glutWarpPointer( _savedX, _savedY );
332 set_goal_view_offset(0.0);
333 set_view_offset(0.0);
337 //#define TRANSLATE_HUD
338 // temporary hack until pitch_offset is added to view pipeline
339 void fgTranslateHud( void ) {
341 if(mouse_mode == MOUSE_VIEW) {
343 int ww = MOUSE_XSIZE;
344 int wh = MOUSE_YSIZE;
346 float y = 4*(_mY-(wh/2));// * ((wh/SGD_PI)*SG_RADIANS_TO_DEGREES);
348 float x = get_view_offset() * SG_RADIANS_TO_DEGREES;
350 if( x < -180 ) x += 360;
351 else if( x > 180 ) x -= 360;
357 // glTranslatef( x*ww/640, y*wh/480, 0 );
358 glTranslatef( x*640/ww, y*480/wh, 0 );
360 #endif // TRANSLATE_HUD
363 void guiMotionFunc ( int x, int y )
365 int ww, wh, need_warp = 0;
372 if (mouse_mode == MOUSE_POINTER) {
373 #if defined(FG_OLD_MENU)
374 // TURN MENU ON IF MOUSE AT TOP
379 // TURN MENU OFF IF MOUSE AT BOTTOM
380 else if( y > wh-2 ) {
386 glutPostRedisplay () ;
388 if( x == _mX && y == _mY)
391 // reset left click MOUSE_VIEW toggle feature
394 switch (mouse_mode) {
396 if( !mouse_joystick_control ) {
397 mouse_joystick_control = 1;
398 fgSetString("/sim/control-mode", "mouse");
400 if ( left_button() ) {
401 move_brake( (_mX - x) * brake_sensitivity);
402 move_throttle((_mY - y) * throttle_sensitivity);
403 } else if ( right_button() ) {
404 if( ! AP_HeadingEnabled() ) {
405 move_rudder((x - _mX) * rudder_sensitivity);
407 if( ! AP_AltitudeEnabled() ) {
408 move_elevator_trim((_mY - y) * trim_sensitivity);
411 if( ! AP_HeadingEnabled() ) {
412 move_aileron((x - _mX) * aileron_sensitivity);
414 if( ! AP_AltitudeEnabled() ) {
415 move_elevator((_mY - y) * elevator_sensitivity);
419 // Keep the mouse in the window.
420 if (x < 5 || x > ww-5 || y < 5 || y > wh-5) {
429 #define CONTRAINED_MOUSE_VIEW_Y
430 #ifdef CONTRAINED_MOUSE_VIEW_Y
434 #endif // CONTRAINED_MOUSE_VIEW_Y
436 } else if( y >= wh-1) {
437 #ifdef CONTRAINED_MOUSE_VIEW_Y
441 #endif // CONTRAINED_MOUSE_VIEW_Y
444 // wrap MOUSE_VIEW mode cursor x position
448 } else if ( x >= ww-1 ) {
452 // try to get SGD_PI movement in each half of screen
456 if( middle_button() ) {
457 trackball(lastGuiQuat,
458 (2.0f * _mX - W) / W,
459 0, //(H - 2.0f * y) / H, // 3
461 0 //(H - 2.0f * _mY) / H // 1
467 trackball(lastGuiQuat,
468 0, //(2.0f * _mX - W) / W, // 0
469 (H - 2.0f * y) / H, // 3
470 0, //(2.0f * x - W) / W, // 2
471 (H - 2.0f * _mY) / H // 1
474 add_quats(lastGuiQuat, curGuiQuat, curGuiQuat);
475 build_rotmatrix(GuiQuat_mat, curGuiQuat);
478 // this could be done in above quat
479 // but requires redoing view pipeline
480 offset = get_goal_view_offset();
481 offset += ((_mX - x) * SGD_2PI / W );
482 while (offset < 0.0) {
485 while (offset > SGD_2PI) {
488 set_goal_view_offset(offset);
489 set_goal_view_tilt(asin( GuiQuat_mat[1][2]) * SGD_RADIANS_TO_DEGREES );
490 #ifdef NO_SMOOTH_MOUSE_VIEW
491 set_view_offset(offset);
492 set_view_tilt(asin( GuiQuat_mat[1][2]) * SGD_RADIANS_TO_DEGREES );
501 glutWarpPointer(x, y);
503 // Record the new mouse position.
509 void guiMouseFunc(int button, int updown, int x, int y)
513 // private MOUSE_VIEW state variables
514 // to allow alternate left clicks in MOUSE_VIEW mode
515 // to toggle between current offsets and straight ahead
517 static int _mVx, _mVy, _Vx, _Vy;
518 static float _quat[4];
519 static double _view_offset;
521 glutModifiers = glutGetModifiers();
522 glut_active_shift = glutModifiers & GLUT_ACTIVE_SHIFT;
523 glut_active_ctrl = glutModifiers & GLUT_ACTIVE_CTRL;
524 glut_active_alt = glutModifiers & GLUT_ACTIVE_ALT;
526 // Was the left button pressed?
527 if (updown == GLUT_DOWN ) {
528 if( button == GLUT_LEFT_BUTTON)
530 switch (mouse_mode) {
537 // resume previous view offsets
542 sgCopyVec4(curGuiQuat, _quat);
543 set_goal_view_offset(_view_offset);
544 set_goal_view_tilt(0.0);
545 #ifdef NO_SMOOTH_MOUSE_VIEW
546 set_view_offset(_view_offset);
554 sgCopyVec4(_quat,curGuiQuat);
558 _view_offset = get_goal_view_offset();
559 set_goal_view_offset(0.0);
560 set_goal_view_tilt(0.0);
561 #ifdef NO_SMOOTH_MOUSE_VIEW
562 set_view_offset(0.0);
566 glutWarpPointer( x , y);
567 build_rotmatrix(GuiQuat_mat, curGuiQuat);
568 _mVtoggle = ~_mVtoggle;
571 } else if ( button == GLUT_RIGHT_BUTTON) {
572 switch (mouse_mode) {
575 SG_LOG( SG_INPUT, SG_INFO, "Mouse in yoke mode" );
577 mouse_mode = MOUSE_YOKE;
578 mouse_joystick_control = 0;
581 // start with zero point in center of screen
585 // try to have the MOUSE_YOKE position
586 // reflect the current stick position
587 x = _mX - (int)(get_aileron() * aileron_sensitivity);
588 y = _mY - (int)(get_elevator() * elevator_sensitivity);
590 glutSetCursor(GLUT_CURSOR_CROSSHAIR);
594 SG_LOG( SG_INPUT, SG_INFO, "Mouse in view mode" );
596 mouse_mode = MOUSE_VIEW;
597 fgSetString("/sim/control-mode", "joystick");
599 // recenter cursor and reset
603 // #ifndef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
605 build_rotmatrix(GuiQuat_mat, curGuiQuat);
607 glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
611 SG_LOG( SG_INPUT, SG_INFO, "Mouse in pointer mode" );
613 mouse_mode = MOUSE_POINTER;
616 #ifdef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
618 build_rotmatrix(GuiQuat_mat, curGuiQuat);
619 set_goal_view_offset(0.0);
620 set_goal_view_tilt(0.0);
621 #ifdef NO_SMOOTH_MOUSE_VIEW
622 set_view_offset(0.0);
624 #endif // NO_SMOOTH_MOUSE_VIEW
625 #endif // RESET_VIEW_ON_LEAVING_MOUSE_VIEW
626 glutSetCursor(GLUT_CURSOR_INHERIT);
628 #if defined(FG_OLD_MENU)
629 #if defined(WIN32_CURSOR_TWEAKS_OFF)
632 #endif // WIN32_CURSOR_TWEAKS_OFF
633 #endif // FG_OLD_MENU
635 } // end switch (mouse_mode)
636 glutWarpPointer( x, y );
637 } // END RIGHT BUTTON
638 } // END UPDOWN == GLUT_DOWN
640 // Note which button is pressed.
641 if ( updown == GLUT_DOWN ) {
642 last_buttons |= ( 1 << button ) ;
644 last_buttons &= ~( 1 << button ) ;
647 // If we're in pointer mode, let PUI
648 // know what's going on.
649 if (mouse_mode == MOUSE_POINTER) {
650 if (!puMouse (button, updown, x,y)) {
651 if ( globals->get_current_panel() != NULL ) {
652 globals->get_current_panel()->doMouseAction(button, updown, x, y);
657 // Register the new position (if it
658 // hasn't been registered already).
662 glutPostRedisplay ();