]> git.mxchange.org Git - flightgear.git/blob - src/GUI/mouse.cxx
Tweaks to add an altitude indicator adjustment.
[flightgear.git] / src / GUI / mouse.cxx
1 /**************************************************************************
2  * gui.cxx
3  *
4  * Written 1998 by Durk Talsma, started Juni, 1998.  For the flight gear
5  * project.
6  *
7  * Additional mouse supported added by David Megginson, 1999.
8  *
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.
13  *
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.
18  *
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.
22  *
23  * $Id$
24  **************************************************************************/
25
26
27 #ifdef HAVE_CONFIG_H
28 #  include <config.h>
29 #endif
30
31 #include <simgear/compiler.h>
32
33 #ifdef FG_MATH_EXCEPTION_CLASH
34 #  include <math.h>
35 #endif
36
37 #ifdef HAVE_WINDOWS_H
38 #  include <windows.h>
39 #endif
40
41 #include <GL/glut.h>
42 #include <simgear/xgl/xgl.h>
43
44 #if defined(FX) && defined(XMESA)
45 #  include <GL/xmesa.h>
46 #endif
47
48 #include STL_STRING
49
50 #include <stdlib.h>
51 #include <string.h>
52
53 #include <simgear/constants.h>
54 #include <simgear/debug/logstream.hxx>
55 #include <simgear/misc/fgpath.hxx>
56 #include <simgear/screen/screen-dump.hxx>
57
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>
72
73 #if defined( WIN32 ) && !defined( __CYGWIN__ )
74 #  include <simgear/screen/win32-printer.h>
75 #  include <simgear/screen/GlBitmaps.h>
76 #endif
77
78 #include "gui.h"
79 #include "gui_local.hxx"
80
81 FG_USING_STD(string);
82
83 #ifndef FG_HAVE_NATIVE_SGI_COMPILERS
84 FG_USING_STD(cout);
85 #endif
86
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
91 #endif
92
93 /* --------------------------------------------------------------------
94 Mouse stuff
95 ---------------------------------------------------------------------*/
96
97 static int _mX = 0;
98 static int _mY = 0;
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;
104
105 //static time_t mouse_off_time;
106 //static int mouse_timed_out;
107
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;
113
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;
119
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
124
125 // uncomment following to
126 #define RESET_VIEW_ON_LEAVING_MOUSE_VIEW
127
128 /* --------------------------------------------------------------------
129 Support for mouse as control yoke (david@megginson.com)
130
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
138
139 For the *_sensitivity variables, a lower number means more sensitive.
140
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 ---------------------------------------------------------------------*/
147
148 MouseMode mouse_mode = MOUSE_POINTER;
149
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;
156
157 static inline int guiGetMouseButton(void)
158 {
159         return last_buttons;
160 }
161
162 static inline void guiGetMouse(int *x, int *y)
163 {
164         *x = _mX;
165         *y = _mY;
166 };
167
168 static inline int left_button( void ) {
169     return( last_buttons & (1 << GLUT_LEFT_BUTTON) );
170 }
171
172 static inline int middle_button( void ) {
173     return( last_buttons & (1 << GLUT_MIDDLE_BUTTON) );
174 }
175
176 static inline int right_button( void ) {
177     return( last_buttons & (1 << GLUT_RIGHT_BUTTON) );
178 }
179
180 void TurnCursorOn( void )
181 {
182     mouse_active = ~0;
183 #if defined(WIN32_CURSOR_TWEAKS)
184     switch (mouse_mode) {
185         case MOUSE_POINTER:
186             glutSetCursor(GLUT_CURSOR_INHERIT);
187             break;
188         case MOUSE_YOKE:
189             glutSetCursor(GLUT_CURSOR_CROSSHAIR);
190             break;
191         case MOUSE_VIEW:
192             glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
193             break;
194     }
195 #endif
196 #if defined(X_CURSOR_TWEAKS)
197     glutWarpPointer( fgGetInt("/sim/startup/xsize")/2,
198                      fgGetInt("/sim/startup/ysize")/2);
199 #endif
200 }
201
202 void TurnCursorOff( void )
203 {
204     mouse_active = 0;
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"));
210 #endif
211 }
212
213 void maybeToggleMouse( void )
214 {
215 #if defined(WIN32_CURSOR_TWEAKS)
216     static int first_time = ~0;
217     static int mouse_changed = 0;
218
219     if ( first_time ) {
220         if(!mouse_active) {
221             mouse_changed = ~mouse_changed;
222             TurnCursorOn();
223         }
224     } else {
225         if( mouse_mode != MOUSE_POINTER )
226             return;
227         if( mouse_changed ) {
228             mouse_changed = ~mouse_changed;
229             if(mouse_active) {
230                 TurnCursorOff();
231             }
232         }
233     }
234     first_time = ~first_time;
235 #endif // #ifdef WIN32
236 }
237
238 // Call with FALSE to init and TRUE to restore
239 void BusyCursor( int restore )
240 {
241     static GLenum cursor = (GLenum) 0;
242     if( restore ) {
243         glutSetCursor(cursor);
244     } else {
245         cursor = (GLenum) glutGet( (GLenum) GLUT_WINDOW_CURSOR );
246 #if defined(WIN32_CURSOR_TWEAKS)
247         TurnCursorOn();
248 #endif
249         glutSetCursor( GLUT_CURSOR_WAIT );
250     }
251 }
252
253
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;
260         _mVtoggle = 0;
261         Quat0();
262         build_rotmatrix(GuiQuat_mat, curGuiQuat);
263         glutSetCursor(GLUT_CURSOR_INHERIT);
264
265         // Is this necessary ??
266         if( !gui_menu_on )   TurnCursorOff();
267
268         glutWarpPointer( _savedX, _savedY );
269     }
270     globals->get_current_view()->set_goal_view_offset(0.0);
271     globals->get_current_view()->set_view_offset(0.0);
272 }
273
274
275 void guiMotionFunc ( int x, int y )
276 {
277     int ww, wh, need_warp = 0;
278     float W, H;
279     double offset;
280
281     if (mouse_mode == MOUSE_POINTER) {
282         puMouse ( x, y ) ;
283         glutPostRedisplay () ;
284     } else {
285         if( x == _mX && y == _mY)
286             return;
287         
288         // reset left click MOUSE_VIEW toggle feature
289         _mVtoggle = 0;
290         
291         ww = fgGetInt("/sim/startup/xsize");
292         wh = fgGetInt("/sim/startup/ysize");
293         
294         switch (mouse_mode) {
295             case MOUSE_YOKE:
296                 if( !mouse_joystick_control ) {
297                     mouse_joystick_control = 1;
298                     fgSetString("/sim/control-mode", "mouse");
299                 } else {
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);
309                         }
310                         if( ! current_autopilot->get_AltitudeEnabled() ) {
311                             offset = (_mY - y) * trim_sensitivity;
312                             controls.move_elevator_trim(offset);
313                         }
314                     } else {
315                         if( ! current_autopilot->get_HeadingEnabled() ) {
316                             offset = (x - _mX) * aileron_sensitivity;
317                             controls.move_aileron(offset);
318                         }
319                         if( ! current_autopilot->get_AltitudeEnabled() ) {
320                             offset = (_mY - y) * elevator_sensitivity;
321                             controls.move_elevator(offset);
322                         }
323                     }
324                 }
325                 // Keep the mouse in the window.
326                 if (x < 5 || x > ww-5 || y < 5 || y > wh-5) {
327                     x = ww / 2;
328                     y = wh / 2;
329                     need_warp = 1;
330                 }
331                 break;
332                 
333             case MOUSE_VIEW:
334                 if( y <= 0 ) {
335 #define CONTRAINED_MOUSE_VIEW_Y
336 #ifdef CONTRAINED_MOUSE_VIEW_Y
337                     y = 1;
338 #else
339                     y = wh-2;
340 #endif // CONTRAINED_MOUSE_VIEW_Y
341                     need_warp = 1;
342                 } else if( y >= wh-1) {
343 #ifdef CONTRAINED_MOUSE_VIEW_Y
344                     y = wh-2;
345 #else
346                     y = 1;
347 #endif // CONTRAINED_MOUSE_VIEW_Y
348                     need_warp = 1;
349                 }
350                 // wrap MOUSE_VIEW mode cursor x position
351                 if ( x <= 0 ) {
352                     need_warp = 1;
353                     x = ww-2;
354                 } else if ( x >= ww-1 ) {
355                     need_warp = 1;
356                     x = 1;
357                 }
358                 // try to get FG_PI movement in each half of screen
359                 // do spherical pan
360                 W = ww;
361                 H = wh;
362                 if( middle_button() ) {
363                     trackball(lastGuiQuat,
364                               (2.0f * _mX - W) / W,
365                               0, //(H - 2.0f * y) / H,         // 3
366                               (2.0f * x - W) / W,
367                               0 //(H - 2.0f * _mY) / H       // 1
368                              );
369                     x = _mX;
370                     y = _mY;
371                     need_warp = 1;
372                 } else {
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 
378                              );
379                 }
380                 add_quats(lastGuiQuat, curGuiQuat, curGuiQuat);
381                 build_rotmatrix(GuiQuat_mat, curGuiQuat);
382                 
383                 // do horizontal pan
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) {
389                     offset += FG_2PI;
390                 }
391                 while (offset > FG_2PI) {
392                     offset -= FG_2PI;
393                 }
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);
397 #endif
398                 break;
399             
400             default:
401                 break;
402         }
403     }
404     if( need_warp)
405         glutWarpPointer(x, y);
406     
407     // Record the new mouse position.
408     _mX = x;
409     _mY = y;
410 }
411
412
413 void guiMouseFunc(int button, int updown, int x, int y)
414 {
415     int glutModifiers;
416
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
420     // uses _mVtoggle
421     static int _mVx, _mVy, _Vx, _Vy;
422     static float _quat[4];
423     static double _view_offset;
424     
425     // general purpose variables
426     double offset;
427             
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;
432     
433     // Was the left button pressed?
434     if (updown == GLUT_DOWN ) {
435         if( button == GLUT_LEFT_BUTTON)
436         {
437             switch (mouse_mode) {
438                 case MOUSE_POINTER:
439                     break;
440                 case MOUSE_YOKE:
441                     break;
442                 case MOUSE_VIEW:
443                     if(_mVtoggle) {
444                         // resume previous view offsets
445                         _mX = _mVx;
446                         _mY = _mVy;
447                         x = _Vx;
448                         y = _Vy;
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);
456 #endif
457                     } else {
458                         // center view
459                         _mVx = _mX;
460                         _mVy = _mY;
461                         _Vx = x;
462                         _Vy = y;
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;
469                         Quat0();
470                         _view_offset =
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);
475 #endif
476                     }
477                     glutWarpPointer( x , y);
478                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
479                     _mVtoggle = ~_mVtoggle;
480                     break;
481             }
482         }else if ( button == GLUT_RIGHT_BUTTON) {
483             switch (mouse_mode) {
484                 case MOUSE_POINTER:
485                     mouse_mode = MOUSE_YOKE;
486                     mouse_joystick_control = 0;
487                     _savedX = x;
488                     _savedY = y;
489                     // start with zero point in center of screen
490                     _mX = fgGetInt("/sim/startup/xsize")/2;
491                     _mY = fgGetInt("/sim/startup/ysize")/2;
492                     
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);
499                     
500                     glutSetCursor(GLUT_CURSOR_CROSSHAIR);
501                     FG_LOG( FG_INPUT, FG_INFO, "Mouse in yoke mode" );
502                     break;
503                     
504                 case MOUSE_YOKE:
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;
509                     _mVtoggle = 0;
510                     Quat0();
511                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
512                     glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
513                     FG_LOG( FG_INPUT, FG_INFO, "Mouse in view mode" );
514                     break;
515                     
516                 case MOUSE_VIEW:
517                     mouse_mode = MOUSE_POINTER;
518                     x = _savedX;
519                     y = _savedY;
520 #ifdef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
521                     Quat0();
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);
526 #endif
527 #endif      // RESET_VIEW_ON_LEAVING_MOUSE_VIEW
528                     glutSetCursor(GLUT_CURSOR_INHERIT);
529                     
530                     if(!gui_menu_on)
531                         TurnCursorOff();
532                     
533                     FG_LOG( FG_INPUT, FG_INFO, "Mouse in pointer mode" );
534                     break;
535             }     
536             glutWarpPointer( x, y );
537         } // END RIGHT BUTTON
538     } // END UPDOWN == GLUT_DOWN
539     
540     // Note which button is pressed.
541     if ( updown == GLUT_DOWN ) {
542         last_buttons |=  ( 1 << button ) ;
543     } else {
544         last_buttons &= ~( 1 << button ) ;
545     }
546     
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);
552       }
553     }
554     
555     // Register the new position (if it
556     // hasn't been registered already).
557     _mX = x;
558     _mY = y;
559     
560     glutPostRedisplay ();
561 }
562