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
99 static int _mVtoggle = 0;
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 = 0;
104 static int glut_active_ctrl = 0;
105 static int glut_active_alt = 0;
107 static int MOUSE_XSIZE = 0;
108 static int MOUSE_YSIZE = 0;
110 // uncomment following to
111 #define RESET_VIEW_ON_LEAVING_MOUSE_VIEW
113 /* --------------------------------------------------------------------
114 Support for mouse as control yoke (david@megginson.com)
116 - right button toggles between pointer and yoke
117 - horizontal drag with no buttons moves ailerons
118 - vertical drag with no buttons moves elevators
119 - horizontal drag with left button moves brakes (left=on)
120 - vertical drag with left button moves throttle (up=more)
121 - horizontal drag with middle button moves rudder
122 - vertical drag with middle button moves trim
124 For the *_sensitivity variables, a lower number means more sensitive.
126 TODO: figure out how to keep pointer from leaving window in yoke mode.
127 TODO: add thresholds and null zones
128 TODO: sensitivity should be configurable at user option.
129 TODO: allow differential braking (this will be useful if FlightGear
130 ever supports tail-draggers like the DC-3)
131 ---------------------------------------------------------------------*/
133 MouseMode mouse_mode = MOUSE_POINTER;
135 static double aileron_sensitivity = 1.0/500.0;
136 static double elevator_sensitivity = 1.0/500.0;
137 static double brake_sensitivity = 1.0/250.0;
138 static double throttle_sensitivity = 1.0/250.0;
139 static double rudder_sensitivity = 1.0/500.0;
140 static double trim_sensitivity = 1.0/1000.0;
142 void guiInitMouse(int width, int height)
145 MOUSE_YSIZE = height;
148 static inline int guiGetMouseButton(void)
153 static inline void guiGetMouse(int *x, int *y)
159 static inline int left_button( void ) {
160 return( last_buttons & (1 << GLUT_LEFT_BUTTON) );
163 static inline int middle_button( void ) {
164 return( last_buttons & (1 << GLUT_MIDDLE_BUTTON) );
167 static inline int right_button( void ) {
168 return( last_buttons & (1 << GLUT_RIGHT_BUTTON) );
171 static inline void set_goal_view_offset( float offset )
173 globals->get_current_view()->set_goal_view_offset(offset);
176 static inline void set_view_offset( float offset )
178 globals->get_current_view()->set_view_offset(offset);
181 static inline float get_view_offset() {
182 return globals->get_current_view()->get_view_offset();
185 static inline float get_goal_view_offset() {
186 return globals->get_current_view()->get_goal_view_offset();
189 static inline void set_goal_view_tilt( float tilt )
191 globals->get_current_view()->set_goal_view_tilt(tilt);
194 static inline void set_view_tilt( float tilt )
196 globals->get_current_view()->set_view_tilt(tilt);
199 static inline float get_view_tilt() {
200 return globals->get_current_view()->get_view_tilt();
203 static inline void move_brake(float offset) {
204 globals->get_controls()->move_brake(FGControls::ALL_WHEELS, offset);
207 static inline void move_throttle(float offset) {
208 globals->get_controls()->move_throttle(FGControls::ALL_ENGINES, offset);
211 static inline void move_rudder(float offset) {
212 globals->get_controls()->move_rudder(offset);
215 static inline void move_elevator_trim(float offset) {
216 globals->get_controls()->move_elevator_trim(offset);
219 static inline void move_aileron(float offset) {
220 globals->get_controls()->move_aileron(offset);
223 static inline void move_elevator(float offset) {
224 globals->get_controls()->move_elevator(offset);
227 static inline float get_aileron() {
228 return globals->get_controls()->get_aileron();
231 static inline float get_elevator() {
232 return globals->get_controls()->get_elevator();
235 static inline bool AP_HeadingEnabled() {
236 return current_autopilot->get_HeadingEnabled();
239 static inline bool AP_AltitudeEnabled() {
240 return current_autopilot->get_AltitudeEnabled();
243 void TurnCursorOn( void )
247 switch (mouse_mode) {
249 glutSetCursor(GLUT_CURSOR_INHERIT);
252 glutSetCursor(GLUT_CURSOR_CROSSHAIR);
255 glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
259 #if defined(X_CURSOR_TWEAKS)
260 glutWarpPointer( MOUSE_XSIZE/2,
265 void TurnCursorOff( void )
268 #if defined(WIN32_CURSOR_TWEAKS)
269 glutSetCursor(GLUT_CURSOR_NONE);
270 #elif defined(X_CURSOR_TWEAKS)
271 glutWarpPointer( MOUSE_XSIZE,
276 void maybeToggleMouse( void )
278 #if defined(WIN32_CURSOR_TWEAKS_OFF)
279 static int first_time = ~0;
280 static int mouse_changed = 0;
284 mouse_changed = ~mouse_changed;
288 if( mouse_mode != MOUSE_POINTER )
290 if( mouse_changed ) {
291 mouse_changed = ~mouse_changed;
297 first_time = ~first_time;
298 #endif // #ifdef WIN32
301 // Call with FALSE to init and TRUE to restore
302 void BusyCursor( int restore )
304 static GLenum cursor = (GLenum) 0;
306 glutSetCursor(cursor);
308 cursor = (GLenum) glutGet( (GLenum) GLUT_WINDOW_CURSOR );
309 #if defined(WIN32_CURSOR_TWEAKS)
312 glutSetCursor( GLUT_CURSOR_WAIT );
317 // Center the view offsets
318 void CenterView( void ) {
319 if( mouse_mode == MOUSE_VIEW ) {
320 mouse_mode = MOUSE_POINTER;
321 _savedX = MOUSE_XSIZE/2;
322 _savedY = MOUSE_YSIZE/2;
325 build_rotmatrix(GuiQuat_mat, curGuiQuat);
326 glutSetCursor(GLUT_CURSOR_INHERIT);
328 // Is this necessary ??
329 if( !gui_menu_on ) TurnCursorOff();
331 glutWarpPointer( _savedX, _savedY );
333 set_goal_view_offset(0.0);
334 set_view_offset(0.0);
338 //#define TRANSLATE_HUD
339 // temporary hack until pitch_offset is added to view pipeline
340 void fgTranslateHud( void ) {
342 if(mouse_mode == MOUSE_VIEW) {
344 int ww = MOUSE_XSIZE;
345 int wh = MOUSE_YSIZE;
347 float y = 4*(_mY-(wh/2));// * ((wh/SGD_PI)*SG_RADIANS_TO_DEGREES);
349 float x = get_view_offset() * SG_RADIANS_TO_DEGREES;
351 if( x < -180 ) x += 360;
352 else if( x > 180 ) x -= 360;
358 // glTranslatef( x*ww/640, y*wh/480, 0 );
359 glTranslatef( x*640/ww, y*480/wh, 0 );
361 #endif // TRANSLATE_HUD
364 void guiMotionFunc ( int x, int y )
366 int ww, wh, need_warp = 0;
373 if (mouse_mode == MOUSE_POINTER) {
374 // TURN MENU ON IF MOUSE AT TOP
379 // TURN MENU OFF IF MOUSE AT BOTTOM
380 else if( y > wh-2 ) {
385 glutPostRedisplay () ;
387 if( x == _mX && y == _mY)
390 // reset left click MOUSE_VIEW toggle feature
393 switch (mouse_mode) {
395 if( !mouse_joystick_control ) {
396 mouse_joystick_control = 1;
397 fgSetString("/sim/control-mode", "mouse");
399 if ( left_button() ) {
400 move_brake( (_mX - x) * brake_sensitivity);
401 move_throttle((_mY - y) * throttle_sensitivity);
402 } else if ( right_button() ) {
403 if( ! AP_HeadingEnabled() ) {
404 move_rudder((x - _mX) * rudder_sensitivity);
406 if( ! AP_AltitudeEnabled() ) {
407 move_elevator_trim((_mY - y) * trim_sensitivity);
410 if( ! AP_HeadingEnabled() ) {
411 move_aileron((x - _mX) * aileron_sensitivity);
413 if( ! AP_AltitudeEnabled() ) {
414 move_elevator((_mY - y) * elevator_sensitivity);
418 // Keep the mouse in the window.
419 if (x < 5 || x > ww-5 || y < 5 || y > wh-5) {
430 } else if( y >= wh-1) {
434 // wrap MOUSE_VIEW mode cursor x position
438 } else if ( x >= ww-1 ) {
444 float scale = SGD_PI / MOUSE_XSIZE;
445 float dx = (_mX - x) * scale;
446 float dy = (_mY - y) * scale;
448 float newOffset = get_view_offset() + dx;
449 set_goal_view_offset(newOffset);
450 set_view_offset(newOffset);
452 float newTilt = get_view_tilt() + dy;
453 set_goal_view_tilt(newTilt);
454 set_view_tilt(newTilt);
460 glutWarpPointer(x, y);
462 // Record the new mouse position.
468 void guiMouseFunc(int button, int updown, int x, int y)
472 // private MOUSE_VIEW state variables
473 // to allow alternate left clicks in MOUSE_VIEW mode
474 // to toggle between current offsets and straight ahead
476 static int _mVx, _mVy, _Vx, _Vy;
477 static float _quat[4];
478 static double _view_offset;
480 // general purpose variables
483 glutModifiers = glutGetModifiers();
484 glut_active_shift = glutModifiers & GLUT_ACTIVE_SHIFT;
485 glut_active_ctrl = glutModifiers & GLUT_ACTIVE_CTRL;
486 glut_active_alt = glutModifiers & GLUT_ACTIVE_ALT;
488 // Was the left button pressed?
489 if (updown == GLUT_DOWN ) {
490 if( button == GLUT_LEFT_BUTTON)
492 switch (mouse_mode) {
499 // resume previous view offsets
504 sgCopyVec4(curGuiQuat, _quat);
505 set_goal_view_offset(_view_offset);
506 #ifdef NO_SMOOTH_MOUSE_VIEW
507 set_view_offset(_view_offset);
515 sgCopyVec4(_quat,curGuiQuat);
519 _view_offset = get_goal_view_offset();
520 set_goal_view_offset(0.0);
521 #ifdef NO_SMOOTH_MOUSE_VIEW
522 set_view_offset(0.0);
525 glutWarpPointer( x , y);
526 build_rotmatrix(GuiQuat_mat, curGuiQuat);
527 _mVtoggle = ~_mVtoggle;
530 } else if ( button == GLUT_RIGHT_BUTTON) {
531 switch (mouse_mode) {
534 SG_LOG( SG_INPUT, SG_INFO, "Mouse in yoke mode" );
536 mouse_mode = MOUSE_YOKE;
537 mouse_joystick_control = 0;
540 // start with zero point in center of screen
544 // try to have the MOUSE_YOKE position
545 // reflect the current stick position
546 x = _mX - (int)(get_aileron() * aileron_sensitivity);
547 y = _mY - (int)(get_elevator() * elevator_sensitivity);
549 glutSetCursor(GLUT_CURSOR_CROSSHAIR);
553 SG_LOG( SG_INPUT, SG_INFO, "Mouse in view mode" );
555 mouse_mode = MOUSE_VIEW;
556 fgSetString("/sim/control-mode", "joystick");
558 // recenter cursor and reset
562 // #ifndef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
564 build_rotmatrix(GuiQuat_mat, curGuiQuat);
566 glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
570 SG_LOG( SG_INPUT, SG_INFO, "Mouse in pointer mode" );
572 mouse_mode = MOUSE_POINTER;
575 #ifdef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
577 build_rotmatrix(GuiQuat_mat, curGuiQuat);
578 set_goal_view_offset(0.0);
579 #ifdef NO_SMOOTH_MOUSE_VIEW
580 set_view_offset(0.0);
581 #endif // NO_SMOOTH_MOUSE_VIEW
582 #endif // RESET_VIEW_ON_LEAVING_MOUSE_VIEW
583 glutSetCursor(GLUT_CURSOR_INHERIT);
585 #if defined(WIN32_CURSOR_TWEAKS_OFF)
588 #endif // WIN32_CURSOR_TWEAKS_OFF
590 } // end switch (mouse_mode)
591 glutWarpPointer( x, y );
592 } // END RIGHT BUTTON
593 } // END UPDOWN == GLUT_DOWN
595 // Note which button is pressed.
596 if ( updown == GLUT_DOWN ) {
597 last_buttons |= ( 1 << button ) ;
599 last_buttons &= ~( 1 << button ) ;
602 // If we're in pointer mode, let PUI
603 // know what's going on.
604 if (mouse_mode == MOUSE_POINTER) {
605 if (!puMouse (button, updown, x,y)) {
606 if ( current_panel != NULL ) {
607 current_panel->doMouseAction(button, updown, x, y);
612 // Register the new position (if it
613 // hasn't been registered already).
617 glutPostRedisplay ();