]> git.mxchange.org Git - flightgear.git/blob - src/GUI/mouse.cxx
Sync with latest JSBSim CVS
[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_CURSOR_TWEAKS)
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)
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     if (mouse_mode == MOUSE_POINTER) {
269         puMouse ( x, y ) ;
270         glutPostRedisplay () ;
271     } else {
272         if( x == _mX && y == _mY)
273             return;
274         
275         // reset left click MOUSE_VIEW toggle feature
276         _mVtoggle = 0;
277         
278         ww = fgGetInt("/sim/startup/xsize");
279         wh = fgGetInt("/sim/startup/ysize");
280         
281         switch (mouse_mode) {
282             case MOUSE_YOKE:
283                 if( !mouse_joystick_control ) {
284                     mouse_joystick_control = 1;
285                     fgSetString("/sim/control-mode", "mouse");
286                 } else {
287                     if ( left_button() ) {
288                         offset = (_mX - x) * brake_sensitivity;
289                         globals->get_controls()->move_brake(FGControls::ALL_WHEELS, offset);
290                         offset = (_mY - y) * throttle_sensitivity;
291                         globals->get_controls()->move_throttle(FGControls::ALL_ENGINES, offset);
292                     } else if ( right_button() ) {
293                         if( ! current_autopilot->get_HeadingEnabled() ) {
294                             offset = (x - _mX) * rudder_sensitivity;
295                             globals->get_controls()->move_rudder(offset);
296                         }
297                         if( ! current_autopilot->get_AltitudeEnabled() ) {
298                             offset = (_mY - y) * trim_sensitivity;
299                             globals->get_controls()->move_elevator_trim(offset);
300                         }
301                     } else {
302                         if( ! current_autopilot->get_HeadingEnabled() ) {
303                             offset = (x - _mX) * aileron_sensitivity;
304                             globals->get_controls()->move_aileron(offset);
305                         }
306                         if( ! current_autopilot->get_AltitudeEnabled() ) {
307                             offset = (_mY - y) * elevator_sensitivity;
308                             globals->get_controls()->move_elevator(offset);
309                         }
310                     }
311                 }
312                 // Keep the mouse in the window.
313                 if (x < 5 || x > ww-5 || y < 5 || y > wh-5) {
314                     x = ww / 2;
315                     y = wh / 2;
316                     need_warp = 1;
317                 }
318                 break;
319                 
320             case MOUSE_VIEW:
321                 if( y <= 0 ) {
322 #define CONTRAINED_MOUSE_VIEW_Y
323 #ifdef CONTRAINED_MOUSE_VIEW_Y
324                     y = 1;
325 #else
326                     y = wh-2;
327 #endif // CONTRAINED_MOUSE_VIEW_Y
328                     need_warp = 1;
329                 } else if( y >= wh-1) {
330 #ifdef CONTRAINED_MOUSE_VIEW_Y
331                     y = wh-2;
332 #else
333                     y = 1;
334 #endif // CONTRAINED_MOUSE_VIEW_Y
335                     need_warp = 1;
336                 }
337                 // wrap MOUSE_VIEW mode cursor x position
338                 if ( x <= 0 ) {
339                     need_warp = 1;
340                     x = ww-2;
341                 } else if ( x >= ww-1 ) {
342                     need_warp = 1;
343                     x = 1;
344                 }
345                 // try to get SGD_PI movement in each half of screen
346                 // do spherical pan
347                 W = ww;
348                 H = wh;
349                 if( middle_button() ) {
350                     trackball(lastGuiQuat,
351                               (2.0f * _mX - W) / W,
352                               0, //(H - 2.0f * y) / H,         // 3
353                               (2.0f * x - W) / W,
354                               0 //(H - 2.0f * _mY) / H       // 1
355                              );
356                     x = _mX;
357                     y = _mY;
358                     need_warp = 1;
359                 } else {
360                     trackball(lastGuiQuat,
361                               0, //(2.0f * _mX - W) / W,  // 0
362                               (H - 2.0f * y) / H,         // 3
363                               0, //(2.0f * x - W) / W,    // 2
364                               (H - 2.0f * _mY) / H        // 1 
365                              );
366                 }
367                 add_quats(lastGuiQuat, curGuiQuat, curGuiQuat);
368                 build_rotmatrix(GuiQuat_mat, curGuiQuat);
369                 
370                 // do horizontal pan
371                 // this could be done in above quat
372                 // but requires redoing view pipeline
373                 offset = globals->get_current_view()->get_goal_view_offset();
374                 offset += ((_mX - x) * SGD_2PI / W );
375                 while (offset < 0.0) {
376                     offset += SGD_2PI;
377                 }
378                 while (offset > SGD_2PI) {
379                     offset -= SGD_2PI;
380                 }
381                 globals->get_current_view()->set_goal_view_offset(offset);
382 #ifdef NO_SMOOTH_MOUSE_VIEW
383                 globals->get_current_view()->set_view_offset(offset);
384 #endif
385                 break;
386             
387             default:
388                 break;
389         }
390     }
391     if( need_warp)
392         glutWarpPointer(x, y);
393     
394     // Record the new mouse position.
395     _mX = x;
396     _mY = y;
397 }
398
399
400 void guiMouseFunc(int button, int updown, int x, int y)
401 {
402     int glutModifiers;
403
404     // private MOUSE_VIEW state variables
405     // to allow alternate left clicks in MOUSE_VIEW mode
406     // to toggle between current offsets and straight ahead
407     // uses _mVtoggle
408     static int _mVx, _mVy, _Vx, _Vy;
409     static float _quat[4];
410     static double _view_offset;
411     
412     // general purpose variables
413     double offset;
414             
415     glutModifiers = glutGetModifiers();
416     glut_active_shift = glutModifiers & GLUT_ACTIVE_SHIFT;
417     glut_active_ctrl  = glutModifiers & GLUT_ACTIVE_CTRL; 
418     glut_active_alt   = glutModifiers & GLUT_ACTIVE_ALT;
419     
420     // Was the left button pressed?
421     if (updown == GLUT_DOWN ) {
422         if( button == GLUT_LEFT_BUTTON)
423         {
424             switch (mouse_mode) {
425                 case MOUSE_POINTER:
426                     break;
427                 case MOUSE_YOKE:
428                     break;
429                 case MOUSE_VIEW:
430                     if(_mVtoggle) {
431                         // resume previous view offsets
432                         _mX = _mVx;
433                         _mY = _mVy;
434                         x = _Vx;
435                         y = _Vy;
436                         curGuiQuat[0] = _quat[0];
437                         curGuiQuat[1] = _quat[1];
438                         curGuiQuat[2] = _quat[2];
439                         curGuiQuat[3] = _quat[3];
440                         globals->get_current_view()->set_goal_view_offset(_view_offset);
441 #ifdef NO_SMOOTH_MOUSE_VIEW
442                         globals->get_current_view()->set_view_offset(_view_offset);
443 #endif
444                     } else {
445                         // center view
446                         _mVx = _mX;
447                         _mVy = _mY;
448                         _Vx = x;
449                         _Vy = y;
450                         _quat[0] = curGuiQuat[0];
451                         _quat[1] = curGuiQuat[1];
452                         _quat[2] = curGuiQuat[2];
453                         _quat[3] = curGuiQuat[3];
454                         x = fgGetInt("/sim/startup/xsize")/2;
455                         y = fgGetInt("/sim/startup/ysize")/2;
456                         Quat0();
457                         _view_offset =
458                             globals->get_current_view()->get_goal_view_offset();
459                         globals->get_current_view()->set_goal_view_offset(0.0);
460 #ifdef NO_SMOOTH_MOUSE_VIEW
461                         globals->get_current_view()->set_view_offset(0.0);
462 #endif
463                     }
464                     glutWarpPointer( x , y);
465                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
466                     _mVtoggle = ~_mVtoggle;
467                     break;
468             }
469         }else if ( button == GLUT_RIGHT_BUTTON) {
470             switch (mouse_mode) {
471                 case MOUSE_POINTER:
472                     mouse_mode = MOUSE_YOKE;
473                     mouse_joystick_control = 0;
474                     _savedX = x;
475                     _savedY = y;
476                     // start with zero point in center of screen
477                     _mX = fgGetInt("/sim/startup/xsize")/2;
478                     _mY = fgGetInt("/sim/startup/ysize")/2;
479                     
480                     // try to have the MOUSE_YOKE position
481                     // reflect the current stick position
482                     offset = globals->get_controls()->get_aileron();
483                     x = _mX - (int)(offset * aileron_sensitivity);
484                     offset = globals->get_controls()->get_elevator();
485                     y = _mY - (int)(offset * elevator_sensitivity);
486                     
487                     glutSetCursor(GLUT_CURSOR_CROSSHAIR);
488                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in yoke mode" );
489                     break;
490                     
491                 case MOUSE_YOKE:
492                     mouse_mode = MOUSE_VIEW;
493                     fgSetString("/sim/control-mode", "joystick");
494                     x = fgGetInt("/sim/startup/xsize")/2;
495                     y = fgGetInt("/sim/startup/ysize")/2;
496                     _mVtoggle = 0;
497                     Quat0();
498                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
499                     glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
500                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in view mode" );
501                     break;
502                     
503                 case MOUSE_VIEW:
504                     mouse_mode = MOUSE_POINTER;
505                     x = _savedX;
506                     y = _savedY;
507 #ifdef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
508                     Quat0();
509                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
510                     globals->get_current_view()->set_goal_view_offset(0.0);
511 #ifdef NO_SMOOTH_MOUSE_VIEW
512                     globals->get_current_view()->set_view_offset(0.0);
513 #endif
514 #endif      // RESET_VIEW_ON_LEAVING_MOUSE_VIEW
515                     glutSetCursor(GLUT_CURSOR_INHERIT);
516                     
517                     if(!gui_menu_on)
518                         TurnCursorOff();
519                     
520                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in pointer mode" );
521                     break;
522             }     
523             glutWarpPointer( x, y );
524         } // END RIGHT BUTTON
525     } // END UPDOWN == GLUT_DOWN
526     
527     // Note which button is pressed.
528     if ( updown == GLUT_DOWN ) {
529         last_buttons |=  ( 1 << button ) ;
530     } else {
531         last_buttons &= ~( 1 << button ) ;
532     }
533     
534     // If we're in pointer mode, let PUI
535     // know what's going on.
536     if (mouse_mode == MOUSE_POINTER) {
537       if (!puMouse (button, updown, x,y)) {
538         if ( current_panel != NULL ) {
539           current_panel->doMouseAction(button, updown, x, y);
540         }
541       }
542     }
543     
544     // Register the new position (if it
545     // hasn't been registered already).
546     _mX = x;
547     _mY = y;
548     
549     glutPostRedisplay ();
550 }
551