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 FG_MATH_EXCEPTION_CLASH
42 #include <simgear/xgl/xgl.h>
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/fgpath.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/views.hxx>
70 //#include <Network/network.h>
71 //#include <Time/fg_time.hxx>
73 #if defined( WIN32 ) && !defined( __CYGWIN__ )
74 # include <simgear/screen/win32-printer.h>
75 # include <simgear/screen/GlBitmaps.h>
79 #include "gui_local.hxx"
83 #ifndef FG_HAVE_NATIVE_SGI_COMPILERS
87 #if defined(WIN32) || defined(__CYGWIN32__)
88 #define WIN32_CURSOR_TWEAKS
89 #elif (GLUT_API_VERSION >= 4 || GLUT_XLIB_IMPLEMENTATION >= 9)
90 #define X_CURSOR_TWEAKS
93 /* --------------------------------------------------------------------
95 ---------------------------------------------------------------------*/
99 static int _savedX = 0;
100 static int _savedY = 0;
101 static int last_buttons = 0 ;
102 static int mouse_active = 0;
103 static int mouse_joystick_control = 0;
105 //static time_t mouse_off_time;
106 //static int mouse_timed_out;
108 // to allow returning to previous view
109 // on second left click in MOUSE_VIEW mode
110 // This has file scope so that it can be reset
111 // if the little rodent is moved NHV
112 static int _mVtoggle;
114 // we break up the glutGetModifiers return mask
115 // once per loop and stash what we need in these
116 static int glut_active_shift;
117 static int glut_active_ctrl;
118 static int glut_active_alt;
120 // uncomment this for view to exactly follow mouse in MOUSE_VIEW mode
121 // else smooth out the view panning to .01 radian per frame
122 // see view_offset smoothing mechanism in main.cxx
123 #define NO_SMOOTH_MOUSE_VIEW
125 // uncomment following to
126 #define RESET_VIEW_ON_LEAVING_MOUSE_VIEW
128 /* --------------------------------------------------------------------
129 Support for mouse as control yoke (david@megginson.com)
131 - right button toggles between pointer and yoke
132 - horizontal drag with no buttons moves ailerons
133 - vertical drag with no buttons moves elevators
134 - horizontal drag with left button moves brakes (left=on)
135 - vertical drag with left button moves throttle (up=more)
136 - horizontal drag with middle button moves rudder
137 - vertical drag with middle button moves trim
139 For the *_sensitivity variables, a lower number means more sensitive.
141 TODO: figure out how to keep pointer from leaving window in yoke mode.
142 TODO: add thresholds and null zones
143 TODO: sensitivity should be configurable at user option.
144 TODO: allow differential braking (this will be useful if FlightGear
145 ever supports tail-draggers like the DC-3)
146 ---------------------------------------------------------------------*/
148 MouseMode mouse_mode = MOUSE_POINTER;
150 static double aileron_sensitivity = 1.0/500.0;
151 static double elevator_sensitivity = 1.0/500.0;
152 static double brake_sensitivity = 1.0/250.0;
153 static double throttle_sensitivity = 1.0/250.0;
154 static double rudder_sensitivity = 1.0/500.0;
155 static double trim_sensitivity = 1.0/1000.0;
157 static inline int guiGetMouseButton(void)
162 static inline void guiGetMouse(int *x, int *y)
168 static inline int left_button( void ) {
169 return( last_buttons & (1 << GLUT_LEFT_BUTTON) );
172 static inline int middle_button( void ) {
173 return( last_buttons & (1 << GLUT_MIDDLE_BUTTON) );
176 static inline int right_button( void ) {
177 return( last_buttons & (1 << GLUT_RIGHT_BUTTON) );
180 void TurnCursorOn( void )
183 #if defined(WIN32_CURSOR_TWEAKS)
184 switch (mouse_mode) {
186 glutSetCursor(GLUT_CURSOR_INHERIT);
189 glutSetCursor(GLUT_CURSOR_CROSSHAIR);
192 glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
196 #if defined(X_CURSOR_TWEAKS)
197 glutWarpPointer( fgGetInt("/sim/startup/xsize")/2,
198 fgGetInt("/sim/startup/ysize")/2);
202 void TurnCursorOff( void )
205 #if defined(WIN32_CURSOR_TWEAKS)
206 glutSetCursor(GLUT_CURSOR_NONE);
207 #elif defined(X_CURSOR_TWEAKS)
208 glutWarpPointer( fgGetInt("/sim/startup/xsize"),
209 fgGetInt("/sim/startup/ysize"));
213 void maybeToggleMouse( void )
215 #if defined(WIN32_CURSOR_TWEAKS)
216 static int first_time = ~0;
217 static int mouse_changed = 0;
221 mouse_changed = ~mouse_changed;
225 if( mouse_mode != MOUSE_POINTER )
227 if( mouse_changed ) {
228 mouse_changed = ~mouse_changed;
234 first_time = ~first_time;
235 #endif // #ifdef WIN32
238 // Call with FALSE to init and TRUE to restore
239 void BusyCursor( int restore )
241 static GLenum cursor = (GLenum) 0;
243 glutSetCursor(cursor);
245 cursor = (GLenum) glutGet( (GLenum) GLUT_WINDOW_CURSOR );
246 #if defined(WIN32_CURSOR_TWEAKS)
249 glutSetCursor( GLUT_CURSOR_WAIT );
254 // Center the view offsets
255 void CenterView( void ) {
256 if( mouse_mode = MOUSE_VIEW ) {
257 mouse_mode = MOUSE_POINTER;
258 _savedX = fgGetInt("/sim/startup/xsize")/2;
259 _savedY = fgGetInt("/sim/startup/ysize")/2;
262 build_rotmatrix(GuiQuat_mat, curGuiQuat);
263 glutSetCursor(GLUT_CURSOR_INHERIT);
265 // Is this necessary ??
266 if( !gui_menu_on ) TurnCursorOff();
268 glutWarpPointer( _savedX, _savedY );
270 globals->get_current_view()->set_goal_view_offset(0.0);
271 globals->get_current_view()->set_view_offset(0.0);
275 void guiMotionFunc ( int x, int y )
277 int ww, wh, need_warp = 0;
281 if (mouse_mode == MOUSE_POINTER) {
283 glutPostRedisplay () ;
285 if( x == _mX && y == _mY)
288 // reset left click MOUSE_VIEW toggle feature
291 ww = fgGetInt("/sim/startup/xsize");
292 wh = fgGetInt("/sim/startup/ysize");
294 switch (mouse_mode) {
296 if( !mouse_joystick_control ) {
297 mouse_joystick_control = 1;
298 fgSetString("/sim/control-mode", "mouse");
300 if ( left_button() ) {
301 offset = (_mX - x) * brake_sensitivity;
302 controls.move_brake(FGControls::ALL_WHEELS, offset);
303 offset = (_mY - y) * throttle_sensitivity;
304 controls.move_throttle(FGControls::ALL_ENGINES, offset);
305 } else if ( right_button() ) {
306 if( ! current_autopilot->get_HeadingEnabled() ) {
307 offset = (x - _mX) * rudder_sensitivity;
308 controls.move_rudder(offset);
310 if( ! current_autopilot->get_AltitudeEnabled() ) {
311 offset = (_mY - y) * trim_sensitivity;
312 controls.move_elevator_trim(offset);
315 if( ! current_autopilot->get_HeadingEnabled() ) {
316 offset = (x - _mX) * aileron_sensitivity;
317 controls.move_aileron(offset);
319 if( ! current_autopilot->get_AltitudeEnabled() ) {
320 offset = (_mY - y) * elevator_sensitivity;
321 controls.move_elevator(offset);
325 // Keep the mouse in the window.
326 if (x < 5 || x > ww-5 || y < 5 || y > wh-5) {
335 #define CONTRAINED_MOUSE_VIEW_Y
336 #ifdef CONTRAINED_MOUSE_VIEW_Y
340 #endif // CONTRAINED_MOUSE_VIEW_Y
342 } else if( y >= wh-1) {
343 #ifdef CONTRAINED_MOUSE_VIEW_Y
347 #endif // CONTRAINED_MOUSE_VIEW_Y
350 // wrap MOUSE_VIEW mode cursor x position
354 } else if ( x >= ww-1 ) {
358 // try to get FG_PI movement in each half of screen
362 if( middle_button() ) {
363 trackball(lastGuiQuat,
364 (2.0f * _mX - W) / W,
365 0, //(H - 2.0f * y) / H, // 3
367 0 //(H - 2.0f * _mY) / H // 1
373 trackball(lastGuiQuat,
374 0, //(2.0f * _mX - W) / W, // 0
375 (H - 2.0f * y) / H, // 3
376 0, //(2.0f * x - W) / W, // 2
377 (H - 2.0f * _mY) / H // 1
380 add_quats(lastGuiQuat, curGuiQuat, curGuiQuat);
381 build_rotmatrix(GuiQuat_mat, curGuiQuat);
384 // this could be done in above quat
385 // but requires redoing view pipeline
386 offset = globals->get_current_view()->get_goal_view_offset();
387 offset += ((_mX - x) * FG_2PI / W );
388 while (offset < 0.0) {
391 while (offset > FG_2PI) {
394 globals->get_current_view()->set_goal_view_offset(offset);
395 #ifdef NO_SMOOTH_MOUSE_VIEW
396 globals->get_current_view()->set_view_offset(offset);
405 glutWarpPointer(x, y);
407 // Record the new mouse position.
413 void guiMouseFunc(int button, int updown, int x, int y)
417 // private MOUSE_VIEW state variables
418 // to allow alternate left clicks in MOUSE_VIEW mode
419 // to toggle between current offsets and straight ahead
421 static int _mVx, _mVy, _Vx, _Vy;
422 static float _quat[4];
423 static double _view_offset;
425 // general purpose variables
428 glutModifiers = glutGetModifiers();
429 glut_active_shift = glutModifiers & GLUT_ACTIVE_SHIFT;
430 glut_active_ctrl = glutModifiers & GLUT_ACTIVE_CTRL;
431 glut_active_alt = glutModifiers & GLUT_ACTIVE_ALT;
433 // Was the left button pressed?
434 if (updown == GLUT_DOWN ) {
435 if( button == GLUT_LEFT_BUTTON)
437 switch (mouse_mode) {
444 // resume previous view offsets
449 curGuiQuat[0] = _quat[0];
450 curGuiQuat[1] = _quat[1];
451 curGuiQuat[2] = _quat[2];
452 curGuiQuat[3] = _quat[3];
453 globals->get_current_view()->set_goal_view_offset(_view_offset);
454 #ifdef NO_SMOOTH_MOUSE_VIEW
455 globals->get_current_view()->set_view_offset(_view_offset);
463 _quat[0] = curGuiQuat[0];
464 _quat[1] = curGuiQuat[1];
465 _quat[2] = curGuiQuat[2];
466 _quat[3] = curGuiQuat[3];
467 x = fgGetInt("/sim/startup/xsize")/2;
468 y = fgGetInt("/sim/startup/ysize")/2;
471 globals->get_current_view()->get_goal_view_offset();
472 globals->get_current_view()->set_goal_view_offset(0.0);
473 #ifdef NO_SMOOTH_MOUSE_VIEW
474 globals->get_current_view()->set_view_offset(0.0);
477 glutWarpPointer( x , y);
478 build_rotmatrix(GuiQuat_mat, curGuiQuat);
479 _mVtoggle = ~_mVtoggle;
482 }else if ( button == GLUT_RIGHT_BUTTON) {
483 switch (mouse_mode) {
485 mouse_mode = MOUSE_YOKE;
486 mouse_joystick_control = 0;
489 // start with zero point in center of screen
490 _mX = fgGetInt("/sim/startup/xsize")/2;
491 _mY = fgGetInt("/sim/startup/ysize")/2;
493 // try to have the MOUSE_YOKE position
494 // reflect the current stick position
495 offset = controls.get_aileron();
496 x = _mX - (int)(offset * aileron_sensitivity);
497 offset = controls.get_elevator();
498 y = _mY - (int)(offset * elevator_sensitivity);
500 glutSetCursor(GLUT_CURSOR_CROSSHAIR);
501 FG_LOG( FG_INPUT, FG_INFO, "Mouse in yoke mode" );
505 mouse_mode = MOUSE_VIEW;
506 fgSetString("/sim/control-mode", "joystick");
507 x = fgGetInt("/sim/startup/xsize")/2;
508 y = fgGetInt("/sim/startup/ysize")/2;
511 build_rotmatrix(GuiQuat_mat, curGuiQuat);
512 glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
513 FG_LOG( FG_INPUT, FG_INFO, "Mouse in view mode" );
517 mouse_mode = MOUSE_POINTER;
520 #ifdef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
522 build_rotmatrix(GuiQuat_mat, curGuiQuat);
523 globals->get_current_view()->set_goal_view_offset(0.0);
524 #ifdef NO_SMOOTH_MOUSE_VIEW
525 globals->get_current_view()->set_view_offset(0.0);
527 #endif // RESET_VIEW_ON_LEAVING_MOUSE_VIEW
528 glutSetCursor(GLUT_CURSOR_INHERIT);
533 FG_LOG( FG_INPUT, FG_INFO, "Mouse in pointer mode" );
536 glutWarpPointer( x, y );
537 } // END RIGHT BUTTON
538 } // END UPDOWN == GLUT_DOWN
540 // Note which button is pressed.
541 if ( updown == GLUT_DOWN ) {
542 last_buttons |= ( 1 << button ) ;
544 last_buttons &= ~( 1 << button ) ;
547 // If we're in pointer mode, let PUI
548 // know what's going on.
549 if (mouse_mode == MOUSE_POINTER) {
550 if (!puMouse (button, updown, x,y)) {
551 current_panel->doMouseAction(button, updown, x, y);
555 // Register the new position (if it
556 // hasn't been registered already).
560 glutPostRedisplay ();