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
41 #include <Main/fg_os.hxx>
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 <Aircraft/aircraft.hxx>
59 #include <Airports/simple.hxx>
60 #include <Autopilot/auto_gui.hxx>
61 #include <Cockpit/panel.hxx>
62 #include <Controls/controls.hxx>
63 #include <FDM/flight.hxx>
64 #include <Main/fg_init.hxx>
65 #include <Main/fg_props.hxx>
66 #include <Main/viewmgr.hxx>
69 #include "gui_local.hxx"
74 /* --------------------------------------------------------------------
76 ---------------------------------------------------------------------*/
80 static int _savedX = 0;
81 static int _savedY = 0;
82 static int last_buttons = 0 ;
83 static int mouse_active = 0;
84 static int mouse_joystick_control = 0;
86 //static time_t mouse_off_time;
87 //static int mouse_timed_out;
89 // to allow returning to previous view
90 // on second left click in MOUSE_VIEW mode
91 // This has file scope so that it can be reset
92 // if the little rodent is moved NHV
93 static int _mVtoggle = 0;
95 static int MOUSE_XSIZE = 0;
96 static int MOUSE_YSIZE = 0;
98 // uncomment this for view to exactly follow mouse in MOUSE_VIEW mode
99 // else smooth out the view panning to .01 radian per frame
100 // see view_offset smoothing mechanism in main.cxx
101 #define NO_SMOOTH_MOUSE_VIEW
103 // uncomment following to
104 #define RESET_VIEW_ON_LEAVING_MOUSE_VIEW
106 /* --------------------------------------------------------------------
107 Support for mouse as control yoke (david@megginson.com)
109 - right button toggles between pointer and yoke
110 - horizontal drag with no buttons moves ailerons
111 - vertical drag with no buttons moves elevators
112 - horizontal drag with left button moves brakes (left=on)
113 - vertical drag with left button moves throttle (up=more)
114 - horizontal drag with middle button moves rudder
115 - vertical drag with middle button moves trim
117 For the *_sensitivity variables, a lower number means more sensitive.
119 TODO: figure out how to keep pointer from leaving window in yoke mode.
120 TODO: add thresholds and null zones
121 TODO: sensitivity should be configurable at user option.
122 TODO: allow differential braking (this will be useful if FlightGear
123 ever supports tail-draggers like the DC-3)
124 ---------------------------------------------------------------------*/
126 MouseMode mouse_mode = MOUSE_POINTER;
128 static double aileron_sensitivity = 1.0/500.0;
129 static double elevator_sensitivity = 1.0/500.0;
130 static double brake_sensitivity = 1.0/250.0;
131 static double throttle_sensitivity = 1.0/250.0;
132 static double rudder_sensitivity = 1.0/500.0;
133 static double trim_sensitivity = 1.0/1000.0;
135 void guiInitMouse(int width, int height)
138 MOUSE_YSIZE = height;
141 static inline int guiGetMouseButton(void)
146 static inline void guiGetMouse(int *x, int *y)
152 static inline int left_button( void ) {
153 return( last_buttons & (1 << MOUSE_BUTTON_LEFT) );
156 static inline int middle_button( void ) {
157 return( last_buttons & (1 << MOUSE_BUTTON_MIDDLE) );
160 static inline int right_button( void ) {
161 return( last_buttons & (1 << MOUSE_BUTTON_RIGHT) );
164 static inline void set_goal_view_offset( float offset )
166 globals->get_current_view()->setGoalHeadingOffset_deg(offset * SGD_RADIANS_TO_DEGREES);
169 static inline void set_view_offset( float offset )
171 globals->get_current_view()->setHeadingOffset_deg(offset * SGD_RADIANS_TO_DEGREES);
174 static inline void set_goal_view_tilt( float tilt )
176 globals->get_current_view()->setGoalPitchOffset_deg(tilt);
179 static inline void set_view_tilt( float tilt )
181 globals->get_current_view()->setPitchOffset_deg(tilt);
184 static inline float get_view_offset() {
185 return globals->get_current_view()->getHeadingOffset_deg() * SGD_DEGREES_TO_RADIANS;
188 static inline float get_goal_view_offset() {
189 return globals->get_current_view()->getGoalHeadingOffset_deg() * SGD_DEGREES_TO_RADIANS;
192 static inline void move_brake(float offset) {
193 globals->get_controls()->move_brake_left(offset);
194 globals->get_controls()->move_brake_right(offset);
197 static inline void move_throttle(float offset) {
198 globals->get_controls()->move_throttle(FGControls::ALL_ENGINES, offset);
201 static inline void move_rudder(float offset) {
202 globals->get_controls()->move_rudder(offset);
205 static inline void move_elevator_trim(float offset) {
206 globals->get_controls()->move_elevator_trim(offset);
209 static inline void move_aileron(float offset) {
210 globals->get_controls()->move_aileron(offset);
213 static inline void move_elevator(float offset) {
214 globals->get_controls()->move_elevator(offset);
217 static inline float get_aileron() {
218 return globals->get_controls()->get_aileron();
221 static inline float get_elevator() {
222 return globals->get_controls()->get_elevator();
225 static inline bool AP_HeadingEnabled() {
226 static const SGPropertyNode *heading_enabled
227 = fgGetNode("/autopilot/locks/heading");
228 return ( strcmp( heading_enabled->getStringValue(), "" ) != 0 );
231 static inline bool AP_AltitudeEnabled() {
232 static const SGPropertyNode *altitude_enabled
233 = fgGetNode("/autopilot/locks/altitude");
234 return ( strcmp( altitude_enabled->getStringValue(), "" ) != 0 );
237 void TurnCursorOn( void )
241 switch (mouse_mode) {
243 fgSetMouseCursor(MOUSE_CURSOR_INHERIT);
246 fgSetMouseCursor(MOUSE_CURSOR_CROSSHAIR);
249 fgSetMouseCursor(MOUSE_CURSOR_LEFT_RIGHT);
253 #if defined(X_CURSOR_TWEAKS)
254 fgWarpMouse( MOUSE_XSIZE/2, MOUSE_YSIZE/2 );
258 void TurnCursorOff( void )
261 #if defined(WIN32_CURSOR_TWEAKS)
262 fgSetMouseCursor(MOUSE_CURSOR_NONE);
263 #elif defined(X_CURSOR_TWEAKS)
264 fgWarpMouse( MOUSE_XSIZE, MOUSE_YSIZE );
268 void maybeToggleMouse( void )
270 #if defined(WIN32_CURSOR_TWEAKS_OFF)
271 static int first_time = ~0;
272 static int mouse_changed = 0;
276 mouse_changed = ~mouse_changed;
280 if( mouse_mode != MOUSE_POINTER )
282 if( mouse_changed ) {
283 mouse_changed = ~mouse_changed;
289 first_time = ~first_time;
290 #endif // #ifdef WIN32
293 // Call with FALSE to init and TRUE to restore
294 void BusyCursor( int restore )
296 static int cursor = MOUSE_CURSOR_POINTER;
298 fgSetMouseCursor(cursor);
300 cursor = fgGetMouseCursor();
301 #if defined(WIN32_CURSOR_TWEAKS)
304 fgSetMouseCursor( MOUSE_CURSOR_WAIT );
309 // Center the view offsets
310 void CenterView( void ) {
311 if( mouse_mode == MOUSE_VIEW ) {
312 mouse_mode = MOUSE_POINTER;
313 _savedX = MOUSE_XSIZE/2;
314 _savedY = MOUSE_YSIZE/2;
317 build_rotmatrix(GuiQuat_mat, curGuiQuat);
318 fgSetMouseCursor(MOUSE_CURSOR_POINTER);
320 // Is this necessary ??
321 #if defined(FG_OLD_MENU)
322 if( !gui_menu_on ) TurnCursorOff();
325 fgWarpMouse( _savedX, _savedY );
327 set_goal_view_offset(0.0);
328 set_view_offset(0.0);
332 //#define TRANSLATE_HUD
333 // temporary hack until pitch_offset is added to view pipeline
334 void fgTranslateHud( void ) {
336 if(mouse_mode == MOUSE_VIEW) {
338 int ww = MOUSE_XSIZE;
339 int wh = MOUSE_YSIZE;
341 float y = 4*(_mY-(wh/2));// * ((wh/SGD_PI)*SG_RADIANS_TO_DEGREES);
343 float x = get_view_offset() * SG_RADIANS_TO_DEGREES;
345 if( x < -180 ) x += 360;
346 else if( x > 180 ) x -= 360;
352 // glTranslatef( x*ww/640, y*wh/480, 0 );
353 glTranslatef( x*640/ww, y*480/wh, 0 );
355 #endif // TRANSLATE_HUD
358 void guiMotionFunc ( int x, int y )
360 int ww, wh, need_warp = 0;
367 if (mouse_mode == MOUSE_POINTER) {
368 #if defined(FG_OLD_MENU)
369 // TURN MENU ON IF MOUSE AT TOP
374 // TURN MENU OFF IF MOUSE AT BOTTOM
375 else if( y > wh-2 ) {
383 if( x == _mX && y == _mY)
386 // reset left click MOUSE_VIEW toggle feature
389 switch (mouse_mode) {
391 if( !mouse_joystick_control ) {
392 mouse_joystick_control = 1;
393 fgSetString("/sim/control-mode", "mouse");
395 if ( left_button() ) {
396 move_brake( (_mX - x) * brake_sensitivity);
397 move_throttle((_mY - y) * throttle_sensitivity);
398 } else if ( right_button() ) {
399 if( ! AP_HeadingEnabled() ) {
400 move_rudder((x - _mX) * rudder_sensitivity);
402 if( ! AP_AltitudeEnabled() ) {
403 move_elevator_trim((_mY - y) * trim_sensitivity);
406 if( ! AP_HeadingEnabled() ) {
407 move_aileron((x - _mX) * aileron_sensitivity);
409 if( ! AP_AltitudeEnabled() ) {
410 move_elevator((_mY - y) * elevator_sensitivity);
414 // Keep the mouse in the window.
415 if (x < 5 || x > ww-5 || y < 5 || y > wh-5) {
424 #define CONTRAINED_MOUSE_VIEW_Y
425 #ifdef CONTRAINED_MOUSE_VIEW_Y
429 #endif // CONTRAINED_MOUSE_VIEW_Y
431 } else if( y >= wh-1) {
432 #ifdef CONTRAINED_MOUSE_VIEW_Y
436 #endif // CONTRAINED_MOUSE_VIEW_Y
439 // wrap MOUSE_VIEW mode cursor x position
443 } else if ( x >= ww-1 ) {
447 // try to get SGD_PI movement in each half of screen
451 if( middle_button() ) {
452 trackball(lastGuiQuat,
453 (2.0f * _mX - W) / W,
454 0, //(H - 2.0f * y) / H, // 3
456 0 //(H - 2.0f * _mY) / H // 1
462 trackball(lastGuiQuat,
463 0, //(2.0f * _mX - W) / W, // 0
464 (H - 2.0f * y) / H, // 3
465 0, //(2.0f * x - W) / W, // 2
466 (H - 2.0f * _mY) / H // 1
469 add_quats(lastGuiQuat, curGuiQuat, curGuiQuat);
470 build_rotmatrix(GuiQuat_mat, curGuiQuat);
473 // this could be done in above quat
474 // but requires redoing view pipeline
475 offset = get_goal_view_offset();
476 offset += ((_mX - x) * SGD_2PI / W );
477 while (offset < 0.0) {
480 while (offset > SGD_2PI) {
483 set_goal_view_offset(offset);
484 set_goal_view_tilt(asin( GuiQuat_mat[1][2]) * SGD_RADIANS_TO_DEGREES );
485 #ifdef NO_SMOOTH_MOUSE_VIEW
486 set_view_offset(offset);
487 set_view_tilt(asin( GuiQuat_mat[1][2]) * SGD_RADIANS_TO_DEGREES );
498 // Record the new mouse position.
504 void guiMouseFunc(int button, int updown, int x, int y)
507 // private MOUSE_VIEW state variables
508 // to allow alternate left clicks in MOUSE_VIEW mode
509 // to toggle between current offsets and straight ahead
511 static int _mVx, _mVy, _Vx, _Vy;
512 static float _quat[4];
513 static double _view_offset;
515 // Was the left button pressed?
516 if ( updown == MOUSE_BUTTON_DOWN ) {
517 if( button == MOUSE_BUTTON_LEFT)
519 switch (mouse_mode) {
526 // resume previous view offsets
531 sgCopyVec4(curGuiQuat, _quat);
532 set_goal_view_offset(_view_offset);
533 set_goal_view_tilt(0.0);
534 #ifdef NO_SMOOTH_MOUSE_VIEW
535 set_view_offset(_view_offset);
543 sgCopyVec4(_quat,curGuiQuat);
547 _view_offset = get_goal_view_offset();
548 set_goal_view_offset(0.0);
549 set_goal_view_tilt(0.0);
550 #ifdef NO_SMOOTH_MOUSE_VIEW
551 set_view_offset(0.0);
556 build_rotmatrix(GuiQuat_mat, curGuiQuat);
557 _mVtoggle = ~_mVtoggle;
560 } else if ( button == MOUSE_BUTTON_RIGHT) {
561 switch (mouse_mode) {
564 SG_LOG( SG_INPUT, SG_INFO, "Mouse in yoke mode" );
566 mouse_mode = MOUSE_YOKE;
567 mouse_joystick_control = 0;
570 // start with zero point in center of screen
574 // try to have the MOUSE_YOKE position
575 // reflect the current stick position
576 x = _mX - (int)(get_aileron() * aileron_sensitivity);
577 y = _mY - (int)(get_elevator() * elevator_sensitivity);
579 fgSetMouseCursor(MOUSE_CURSOR_CROSSHAIR);
583 SG_LOG( SG_INPUT, SG_INFO, "Mouse in view mode" );
585 mouse_mode = MOUSE_VIEW;
586 fgSetString("/sim/control-mode", "joystick");
588 // recenter cursor and reset
592 // #ifndef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
594 build_rotmatrix(GuiQuat_mat, curGuiQuat);
596 fgSetMouseCursor(MOUSE_CURSOR_LEFTRIGHT);
600 SG_LOG( SG_INPUT, SG_INFO, "Mouse in pointer mode" );
602 mouse_mode = MOUSE_POINTER;
605 #ifdef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
607 build_rotmatrix(GuiQuat_mat, curGuiQuat);
608 set_goal_view_offset(0.0);
609 set_goal_view_tilt(0.0);
610 #ifdef NO_SMOOTH_MOUSE_VIEW
611 set_view_offset(0.0);
613 #endif // NO_SMOOTH_MOUSE_VIEW
614 #endif // RESET_VIEW_ON_LEAVING_MOUSE_VIEW
615 fgSetMouseCursor(MOUSE_CURSOR_POINTER);
617 #if defined(FG_OLD_MENU)
618 #if defined(WIN32_CURSOR_TWEAKS_OFF)
621 #endif // WIN32_CURSOR_TWEAKS_OFF
622 #endif // FG_OLD_MENU
624 } // end switch (mouse_mode)
626 } // END RIGHT BUTTON
629 // Update the button state
630 if ( updown == MOUSE_BUTTON_DOWN ) {
631 last_buttons |= ( 1 << button ) ;
633 last_buttons &= ~( 1 << button ) ;
636 // If we're in pointer mode, let PUI
637 // know what's going on.
638 if (mouse_mode == MOUSE_POINTER) {
639 if (!puMouse (button, updown , x,y)) {
640 if ( globals->get_current_panel() != NULL ) {
641 globals->get_current_panel()->doMouseAction(button, updown, x, y);
646 // Register the new position (if it
647 // hasn't been registered already).