]> git.mxchange.org Git - flightgear.git/blob - src/GUI/mouse.cxx
Latest YASim changes.
[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 SG_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 <GL/gl.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/sg_path.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/viewmgr.hxx>
70
71 #include "gui.h"
72 #include "gui_local.hxx"
73
74 SG_USING_STD(string);
75
76 #ifndef SG_HAVE_NATIVE_SGI_COMPILERS
77 SG_USING_STD(cout);
78 #endif
79
80 /* --------------------------------------------------------------------
81 Mouse stuff
82 ---------------------------------------------------------------------*/
83
84 static int _mX = 0;
85 static int _mY = 0;
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;
91
92 //static time_t mouse_off_time;
93 //static int mouse_timed_out;
94
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;
100
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;
104 static int glut_active_ctrl;
105 static int glut_active_alt;
106
107 // uncomment this for view to exactly follow mouse in MOUSE_VIEW mode
108 // else smooth out the view panning to .01 radian per frame
109 // see view_offset smoothing mechanism in main.cxx
110 #define NO_SMOOTH_MOUSE_VIEW
111
112 // uncomment following to
113 #define RESET_VIEW_ON_LEAVING_MOUSE_VIEW
114
115 /* --------------------------------------------------------------------
116 Support for mouse as control yoke (david@megginson.com)
117
118 - right button toggles between pointer and yoke
119 - horizontal drag with no buttons moves ailerons
120 - vertical drag with no buttons moves elevators
121 - horizontal drag with left button moves brakes (left=on)
122 - vertical drag with left button moves throttle (up=more)
123 - horizontal drag with middle button moves rudder
124 - vertical drag with middle button moves trim
125
126 For the *_sensitivity variables, a lower number means more sensitive.
127
128 TODO: figure out how to keep pointer from leaving window in yoke mode.
129 TODO: add thresholds and null zones
130 TODO: sensitivity should be configurable at user option.
131 TODO: allow differential braking (this will be useful if FlightGear
132       ever supports tail-draggers like the DC-3)
133 ---------------------------------------------------------------------*/
134
135 MouseMode mouse_mode = MOUSE_POINTER;
136
137 static double aileron_sensitivity = 1.0/500.0;
138 static double elevator_sensitivity = 1.0/500.0;
139 static double brake_sensitivity = 1.0/250.0;
140 static double throttle_sensitivity = 1.0/250.0;
141 static double rudder_sensitivity = 1.0/500.0;
142 static double trim_sensitivity = 1.0/1000.0;
143
144 static inline int guiGetMouseButton(void)
145 {
146         return last_buttons;
147 }
148
149 static inline void guiGetMouse(int *x, int *y)
150 {
151         *x = _mX;
152         *y = _mY;
153 };
154
155 static inline int left_button( void ) {
156     return( last_buttons & (1 << GLUT_LEFT_BUTTON) );
157 }
158
159 static inline int middle_button( void ) {
160     return( last_buttons & (1 << GLUT_MIDDLE_BUTTON) );
161 }
162
163 static inline int right_button( void ) {
164     return( last_buttons & (1 << GLUT_RIGHT_BUTTON) );
165 }
166
167 void TurnCursorOn( void )
168 {
169     mouse_active = ~0;
170 #if defined(WIN32)
171     switch (mouse_mode) {
172         case MOUSE_POINTER:
173             glutSetCursor(GLUT_CURSOR_INHERIT);
174             break;
175         case MOUSE_YOKE:
176             glutSetCursor(GLUT_CURSOR_CROSSHAIR);
177             break;
178         case MOUSE_VIEW:
179             glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
180             break;
181     }
182 #endif
183 #if defined(X_CURSOR_TWEAKS)
184     glutWarpPointer( fgGetInt("/sim/startup/xsize")/2,
185                      fgGetInt("/sim/startup/ysize")/2);
186 #endif
187 }
188
189 void TurnCursorOff( void )
190 {
191     mouse_active = 0;
192 #if defined(WIN32_CURSOR_TWEAKS)
193     glutSetCursor(GLUT_CURSOR_NONE);
194 #elif defined(X_CURSOR_TWEAKS)
195     glutWarpPointer( fgGetInt("/sim/startup/xsize"),
196                      fgGetInt("/sim/startup/ysize"));
197 #endif
198 }
199
200 void maybeToggleMouse( void )
201 {
202 #if defined(WIN32_CURSOR_TWEAKS_OFF)
203     static int first_time = ~0;
204     static int mouse_changed = 0;
205
206     if ( first_time ) {
207         if(!mouse_active) {
208             mouse_changed = ~mouse_changed;
209             TurnCursorOn();
210         }
211     } else {
212         if( mouse_mode != MOUSE_POINTER )
213             return;
214         if( mouse_changed ) {
215             mouse_changed = ~mouse_changed;
216             if(mouse_active) {
217                 TurnCursorOff();
218             }
219         }
220     }
221     first_time = ~first_time;
222 #endif // #ifdef WIN32
223 }
224
225 // Call with FALSE to init and TRUE to restore
226 void BusyCursor( int restore )
227 {
228     static GLenum cursor = (GLenum) 0;
229     if( restore ) {
230         glutSetCursor(cursor);
231     } else {
232         cursor = (GLenum) glutGet( (GLenum) GLUT_WINDOW_CURSOR );
233 #if defined(WIN32_CURSOR_TWEAKS)
234         TurnCursorOn();
235 #endif
236         glutSetCursor( GLUT_CURSOR_WAIT );
237     }
238 }
239
240
241 // Center the view offsets
242 void CenterView( void ) {
243     if( mouse_mode == MOUSE_VIEW ) {
244         mouse_mode = MOUSE_POINTER;
245         _savedX = fgGetInt("/sim/startup/xsize")/2;
246         _savedY = fgGetInt("/sim/startup/ysize")/2;
247         _mVtoggle = 0;
248         Quat0();
249         build_rotmatrix(GuiQuat_mat, curGuiQuat);
250         glutSetCursor(GLUT_CURSOR_INHERIT);
251
252         // Is this necessary ??
253         if( !gui_menu_on )   TurnCursorOff();
254
255         glutWarpPointer( _savedX, _savedY );
256     }
257     globals->get_current_view()->set_goal_view_offset(0.0);
258     globals->get_current_view()->set_view_offset(0.0);
259 }
260
261
262 void guiMotionFunc ( int x, int y )
263 {
264     int ww, wh, need_warp = 0;
265     float W, H;
266     double offset;
267
268     ww = fgGetInt("/sim/startup/xsize");
269     wh = fgGetInt("/sim/startup/ysize");
270
271     if (mouse_mode == MOUSE_POINTER) {
272         // TURN MENU ON IF MOUSE AT TOP
273         if( y == 0 ) {
274             if( !gui_menu_on )
275                 guiToggleMenu();                        
276         }
277         // TURN MENU OFF IF MOUSE AT BOTTOM
278         else if( y > wh-1 ) {
279             if( gui_menu_on )
280                 guiToggleMenu();                        
281         }
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         switch (mouse_mode) {
292             case MOUSE_YOKE:
293                 if( !mouse_joystick_control ) {
294                     mouse_joystick_control = 1;
295                     fgSetString("/sim/control-mode", "mouse");
296                 } else {
297                     if ( left_button() ) {
298                         offset = (_mX - x) * brake_sensitivity;
299                         globals->get_controls()->move_brake(FGControls::ALL_WHEELS, offset);
300                         offset = (_mY - y) * throttle_sensitivity;
301                         globals->get_controls()->move_throttle(FGControls::ALL_ENGINES, offset);
302                     } else if ( right_button() ) {
303                         if( ! current_autopilot->get_HeadingEnabled() ) {
304                             offset = (x - _mX) * rudder_sensitivity;
305                             globals->get_controls()->move_rudder(offset);
306                         }
307                         if( ! current_autopilot->get_AltitudeEnabled() ) {
308                             offset = (_mY - y) * trim_sensitivity;
309                             globals->get_controls()->move_elevator_trim(offset);
310                         }
311                     } else {
312                         if( ! current_autopilot->get_HeadingEnabled() ) {
313                             offset = (x - _mX) * aileron_sensitivity;
314                             globals->get_controls()->move_aileron(offset);
315                         }
316                         if( ! current_autopilot->get_AltitudeEnabled() ) {
317                             offset = (_mY - y) * elevator_sensitivity;
318                             globals->get_controls()->move_elevator(offset);
319                         }
320                     }
321                 }
322                 // Keep the mouse in the window.
323                 if (x < 5 || x > ww-5 || y < 5 || y > wh-5) {
324                     x = ww / 2;
325                     y = wh / 2;
326                     need_warp = 1;
327                 }
328                 break;
329                 
330             case MOUSE_VIEW:
331                 if( y <= 0 ) {
332 #define CONTRAINED_MOUSE_VIEW_Y
333 #ifdef CONTRAINED_MOUSE_VIEW_Y
334                     y = 1;
335 #else
336                     y = wh-2;
337 #endif // CONTRAINED_MOUSE_VIEW_Y
338                     need_warp = 1;
339                 } else if( y >= wh-1) {
340 #ifdef CONTRAINED_MOUSE_VIEW_Y
341                     y = wh-2;
342 #else
343                     y = 1;
344 #endif // CONTRAINED_MOUSE_VIEW_Y
345                     need_warp = 1;
346                 }
347                 // wrap MOUSE_VIEW mode cursor x position
348                 if ( x <= 0 ) {
349                     need_warp = 1;
350                     x = ww-2;
351                 } else if ( x >= ww-1 ) {
352                     need_warp = 1;
353                     x = 1;
354                 }
355                 // try to get SGD_PI movement in each half of screen
356                 // do spherical pan
357                 W = ww;
358                 H = wh;
359                 if( middle_button() ) {
360                     trackball(lastGuiQuat,
361                               (2.0f * _mX - W) / W,
362                               0, //(H - 2.0f * y) / H,         // 3
363                               (2.0f * x - W) / W,
364                               0 //(H - 2.0f * _mY) / H       // 1
365                              );
366                     x = _mX;
367                     y = _mY;
368                     need_warp = 1;
369                 } else {
370                     trackball(lastGuiQuat,
371                               0, //(2.0f * _mX - W) / W,  // 0
372                               (H - 2.0f * y) / H,         // 3
373                               0, //(2.0f * x - W) / W,    // 2
374                               (H - 2.0f * _mY) / H        // 1 
375                              );
376                 }
377                 add_quats(lastGuiQuat, curGuiQuat, curGuiQuat);
378                 build_rotmatrix(GuiQuat_mat, curGuiQuat);
379                 
380                 // do horizontal pan
381                 // this could be done in above quat
382                 // but requires redoing view pipeline
383                 offset = globals->get_current_view()->get_goal_view_offset();
384                 offset += ((_mX - x) * SGD_2PI / W );
385                 while (offset < 0.0) {
386                     offset += SGD_2PI;
387                 }
388                 while (offset > SGD_2PI) {
389                     offset -= SGD_2PI;
390                 }
391                 globals->get_current_view()->set_goal_view_offset(offset);
392 #ifdef NO_SMOOTH_MOUSE_VIEW
393                 globals->get_current_view()->set_view_offset(offset);
394 #endif
395                 break;
396             
397             default:
398                 break;
399         }
400     }
401     if( need_warp)
402         glutWarpPointer(x, y);
403     
404     // Record the new mouse position.
405     _mX = x;
406     _mY = y;
407 }
408
409
410 void guiMouseFunc(int button, int updown, int x, int y)
411 {
412     int glutModifiers;
413
414     // private MOUSE_VIEW state variables
415     // to allow alternate left clicks in MOUSE_VIEW mode
416     // to toggle between current offsets and straight ahead
417     // uses _mVtoggle
418     static int _mVx, _mVy, _Vx, _Vy;
419     static float _quat[4];
420     static double _view_offset;
421     
422     // general purpose variables
423     double offset;
424             
425     glutModifiers = glutGetModifiers();
426     glut_active_shift = glutModifiers & GLUT_ACTIVE_SHIFT;
427     glut_active_ctrl  = glutModifiers & GLUT_ACTIVE_CTRL; 
428     glut_active_alt   = glutModifiers & GLUT_ACTIVE_ALT;
429     
430     // Was the left button pressed?
431     if (updown == GLUT_DOWN ) {
432         if( button == GLUT_LEFT_BUTTON)
433         {
434             switch (mouse_mode) {
435                 case MOUSE_POINTER:
436                     break;
437                 case MOUSE_YOKE:
438                     break;
439                 case MOUSE_VIEW:
440                     if(_mVtoggle) {
441                         // resume previous view offsets
442                         _mX = _mVx;
443                         _mY = _mVy;
444                         x = _Vx;
445                         y = _Vy;
446                         curGuiQuat[0] = _quat[0];
447                         curGuiQuat[1] = _quat[1];
448                         curGuiQuat[2] = _quat[2];
449                         curGuiQuat[3] = _quat[3];
450                         globals->get_current_view()->set_goal_view_offset(_view_offset);
451 #ifdef NO_SMOOTH_MOUSE_VIEW
452                         globals->get_current_view()->set_view_offset(_view_offset);
453 #endif
454                     } else {
455                         // center view
456                         _mVx = _mX;
457                         _mVy = _mY;
458                         _Vx = x;
459                         _Vy = y;
460                         _quat[0] = curGuiQuat[0];
461                         _quat[1] = curGuiQuat[1];
462                         _quat[2] = curGuiQuat[2];
463                         _quat[3] = curGuiQuat[3];
464                         x = fgGetInt("/sim/startup/xsize")/2;
465                         y = fgGetInt("/sim/startup/ysize")/2;
466                         Quat0();
467                         _view_offset =
468                             globals->get_current_view()->get_goal_view_offset();
469                         globals->get_current_view()->set_goal_view_offset(0.0);
470 #ifdef NO_SMOOTH_MOUSE_VIEW
471                         globals->get_current_view()->set_view_offset(0.0);
472 #endif
473                     }
474                     glutWarpPointer( x , y);
475                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
476                     _mVtoggle = ~_mVtoggle;
477                     break;
478             }
479         }else if ( button == GLUT_RIGHT_BUTTON) {
480             switch (mouse_mode) {
481                 case MOUSE_POINTER:
482                     mouse_mode = MOUSE_YOKE;
483                     mouse_joystick_control = 0;
484                     _savedX = x;
485                     _savedY = y;
486                     // start with zero point in center of screen
487                     _mX = fgGetInt("/sim/startup/xsize")/2;
488                     _mY = fgGetInt("/sim/startup/ysize")/2;
489                     
490                     // try to have the MOUSE_YOKE position
491                     // reflect the current stick position
492                     offset = globals->get_controls()->get_aileron();
493                     x = _mX - (int)(offset * aileron_sensitivity);
494                     offset = globals->get_controls()->get_elevator();
495                     y = _mY - (int)(offset * elevator_sensitivity);
496                     
497                     glutSetCursor(GLUT_CURSOR_CROSSHAIR);
498                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in yoke mode" );
499                     break;
500                     
501                 case MOUSE_YOKE:
502                     mouse_mode = MOUSE_VIEW;
503                     fgSetString("/sim/control-mode", "joystick");
504                     x = fgGetInt("/sim/startup/xsize")/2;
505                     y = fgGetInt("/sim/startup/ysize")/2;
506                     _mVtoggle = 0;
507                     Quat0();
508                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
509                     glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
510                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in view mode" );
511                     break;
512                     
513                 case MOUSE_VIEW:
514                     mouse_mode = MOUSE_POINTER;
515                     x = _savedX;
516                     y = _savedY;
517 #ifdef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
518                     Quat0();
519                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
520                     globals->get_current_view()->set_goal_view_offset(0.0);
521 #ifdef NO_SMOOTH_MOUSE_VIEW
522                     globals->get_current_view()->set_view_offset(0.0);
523 #endif // NO_SMOOTH_MOUSE_VIEW
524 #endif // RESET_VIEW_ON_LEAVING_MOUSE_VIEW
525                     glutSetCursor(GLUT_CURSOR_INHERIT);
526                     
527 #if defined(WIN32_CURSOR_TWEAKS_OFF)
528                     if(!gui_menu_on)
529                         TurnCursorOff();
530 #endif // WIN32_CURSOR_TWEAKS_OFF
531                                         
532                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in pointer mode" );
533                     break;
534             } // end switch (mouse_mode)
535             glutWarpPointer( x, y );
536         } // END RIGHT BUTTON
537     } // END UPDOWN == GLUT_DOWN
538     
539     // Note which button is pressed.
540     if ( updown == GLUT_DOWN ) {
541         last_buttons |=  ( 1 << button ) ;
542     } else {
543         last_buttons &= ~( 1 << button ) ;
544     }
545     
546     // If we're in pointer mode, let PUI
547     // know what's going on.
548     if (mouse_mode == MOUSE_POINTER) {
549       if (!puMouse (button, updown, x,y)) {
550         if ( current_panel != NULL ) {
551           current_panel->doMouseAction(button, updown, x, y);
552         }
553       }
554     }
555     
556     // Register the new position (if it
557     // hasn't been registered already).
558     _mX = x;
559     _mY = y;
560     
561     glutPostRedisplay ();
562 }
563