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