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