]> git.mxchange.org Git - flightgear.git/blob - src/GUI/mouse.cxx
Delay deletion of AI traffic until after it has had time to release the frequency...
[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 FG_GLUT_H
42
43 #if defined(FX) && defined(XMESA)
44 #  include <GL/xmesa.h>
45 #endif
46
47 #include STL_STRING
48
49 #include <stdlib.h>
50 #include <string.h>
51
52 #include <simgear/constants.h>
53 #include <simgear/debug/logstream.hxx>
54 #include <simgear/misc/sg_path.hxx>
55 #include <simgear/screen/screen-dump.hxx>
56
57 #include <Include/general.hxx>
58 #include <Aircraft/aircraft.hxx>
59 #include <Airports/simple.hxx>
60 #include <Autopilot/auto_gui.hxx>
61 #include <Cockpit/panel.hxx>
62 #include <Controls/controls.hxx>
63 #include <FDM/flight.hxx>
64 #include <Main/fg_init.hxx>
65 #include <Main/fg_props.hxx>
66 #include <Main/viewmgr.hxx>
67
68 #include "gui.h"
69 #include "gui_local.hxx"
70
71 SG_USING_STD(string);
72 SG_USING_STD(cout);
73
74 /* --------------------------------------------------------------------
75 Mouse stuff
76 ---------------------------------------------------------------------*/
77
78 static int _mX = 0;
79 static int _mY = 0;
80 static int _savedX = 0;
81 static int _savedY = 0;
82 static int last_buttons = 0 ;
83 static int mouse_active = 0;
84 static int mouse_joystick_control = 0;
85
86 //static time_t mouse_off_time;
87 //static int mouse_timed_out;
88
89 // to allow returning to previous view
90 // on second left click in MOUSE_VIEW mode
91 // This has file scope so that it can be reset
92 // if the little rodent is moved  NHV
93 static  int _mVtoggle = 0;
94
95 // we break up the glutGetModifiers return mask
96 // once per loop and stash what we need in these
97 static int glut_active_shift = 0;
98 static int glut_active_ctrl = 0;
99 static int glut_active_alt = 0;
100
101 static int MOUSE_XSIZE = 0;
102 static int MOUSE_YSIZE = 0;
103
104 // uncomment this for view to exactly follow mouse in MOUSE_VIEW mode
105 // else smooth out the view panning to .01 radian per frame
106 // see view_offset smoothing mechanism in main.cxx
107 #define NO_SMOOTH_MOUSE_VIEW
108
109 // uncomment following to
110 #define RESET_VIEW_ON_LEAVING_MOUSE_VIEW
111
112 /* --------------------------------------------------------------------
113 Support for mouse as control yoke (david@megginson.com)
114
115 - right button toggles between pointer and yoke
116 - horizontal drag with no buttons moves ailerons
117 - vertical drag with no buttons moves elevators
118 - horizontal drag with left button moves brakes (left=on)
119 - vertical drag with left button moves throttle (up=more)
120 - horizontal drag with middle button moves rudder
121 - vertical drag with middle button moves trim
122
123 For the *_sensitivity variables, a lower number means more sensitive.
124
125 TODO: figure out how to keep pointer from leaving window in yoke mode.
126 TODO: add thresholds and null zones
127 TODO: sensitivity should be configurable at user option.
128 TODO: allow differential braking (this will be useful if FlightGear
129       ever supports tail-draggers like the DC-3)
130 ---------------------------------------------------------------------*/
131
132 MouseMode mouse_mode = MOUSE_POINTER;
133
134 static double aileron_sensitivity = 1.0/500.0;
135 static double elevator_sensitivity = 1.0/500.0;
136 static double brake_sensitivity = 1.0/250.0;
137 static double throttle_sensitivity = 1.0/250.0;
138 static double rudder_sensitivity = 1.0/500.0;
139 static double trim_sensitivity = 1.0/1000.0;
140
141 void guiInitMouse(int width, int height)
142 {
143         MOUSE_XSIZE = width;
144         MOUSE_YSIZE = height;
145 }
146
147 static inline int guiGetMouseButton(void)
148 {
149         return last_buttons;
150 }
151
152 static inline void guiGetMouse(int *x, int *y)
153 {
154         *x = _mX;
155         *y = _mY;
156 };
157
158 static inline int left_button( void ) {
159     return( last_buttons & (1 << GLUT_LEFT_BUTTON) );
160 }
161
162 static inline int middle_button( void ) {
163     return( last_buttons & (1 << GLUT_MIDDLE_BUTTON) );
164 }
165
166 static inline int right_button( void ) {
167     return( last_buttons & (1 << GLUT_RIGHT_BUTTON) );
168 }
169
170 static inline void set_goal_view_offset( float offset )
171 {
172         globals->get_current_view()->setGoalHeadingOffset_deg(offset * SGD_RADIANS_TO_DEGREES);
173 }
174
175 static inline void set_view_offset( float offset )
176 {
177         globals->get_current_view()->setHeadingOffset_deg(offset * SGD_RADIANS_TO_DEGREES);
178 }
179
180 static inline void set_goal_view_tilt( float tilt )
181 {
182         globals->get_current_view()->setGoalPitchOffset_deg(tilt);
183 }
184
185 static inline void set_view_tilt( float tilt )
186 {
187         globals->get_current_view()->setPitchOffset_deg(tilt);
188 }
189
190 static inline float get_view_offset() {
191         return globals->get_current_view()->getHeadingOffset_deg() * SGD_DEGREES_TO_RADIANS;
192 }
193
194 static inline float get_goal_view_offset() {
195         return globals->get_current_view()->getGoalHeadingOffset_deg() * SGD_DEGREES_TO_RADIANS;
196 }
197
198 static inline void move_brake(float offset) {
199         globals->get_controls()->move_brake_left(offset);
200         globals->get_controls()->move_brake_right(offset);
201 }
202
203 static inline void move_throttle(float offset) {
204         globals->get_controls()->move_throttle(FGControls::ALL_ENGINES, offset);
205 }
206
207 static inline void move_rudder(float offset) {
208         globals->get_controls()->move_rudder(offset);
209 }
210
211 static inline void move_elevator_trim(float offset) {
212         globals->get_controls()->move_elevator_trim(offset);
213 }
214
215 static inline void move_aileron(float offset) {
216         globals->get_controls()->move_aileron(offset);
217 }
218
219 static inline void move_elevator(float offset) {
220         globals->get_controls()->move_elevator(offset);
221 }
222
223 static inline float get_aileron() {
224         return globals->get_controls()->get_aileron();
225 }
226
227 static inline float get_elevator() {
228         return globals->get_controls()->get_elevator();
229 }
230
231 static inline bool AP_HeadingEnabled() {
232     static const SGPropertyNode *heading_enabled
233         = fgGetNode("/autopilot/locks/heading");
234     return ( strcmp( heading_enabled->getStringValue(), "" ) != 0 );
235 }
236
237 static inline bool AP_AltitudeEnabled() {
238     static const SGPropertyNode *altitude_enabled
239         = fgGetNode("/autopilot/locks/altitude");
240     return ( strcmp( altitude_enabled->getStringValue(), "" ) != 0 );
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 defined(FG_OLD_MENU)
330         if( !gui_menu_on )   TurnCursorOff();
331 #endif
332
333         glutWarpPointer( _savedX, _savedY );
334     }
335     set_goal_view_offset(0.0);
336     set_view_offset(0.0);
337 }
338
339
340 //#define TRANSLATE_HUD
341 // temporary hack until pitch_offset is added to view pipeline
342 void fgTranslateHud( void ) {
343 #ifdef TRANSLATE_HUD
344     if(mouse_mode == MOUSE_VIEW) {
345
346         int ww = MOUSE_XSIZE;
347         int wh = MOUSE_YSIZE;
348
349         float y = 4*(_mY-(wh/2));// * ((wh/SGD_PI)*SG_RADIANS_TO_DEGREES);
350         
351         float x =  get_view_offset() * SG_RADIANS_TO_DEGREES;
352
353         if( x < -180 )      x += 360;
354         else if( x > 180 )      x -= 360;
355
356         x *= ww/90.0;
357         //      x *= ww/180.0;
358         //      x *= ww/360.0;
359
360         //      glTranslatef( x*ww/640, y*wh/480, 0 );
361         glTranslatef( x*640/ww, y*480/wh, 0 );
362     }
363 #endif // TRANSLATE_HUD
364 }
365
366 void guiMotionFunc ( int x, int y )
367 {
368     int ww, wh, need_warp = 0;
369     float W, H;
370     double offset;
371
372     ww = MOUSE_XSIZE;
373     wh = MOUSE_YSIZE;
374
375     if (mouse_mode == MOUSE_POINTER) {
376 #if defined(FG_OLD_MENU)
377         // TURN MENU ON IF MOUSE AT TOP
378         if( y < 1 ) {
379             if( !gui_menu_on )
380                 guiToggleMenu();                        
381         }
382         // TURN MENU OFF IF MOUSE AT BOTTOM
383         else if( y > wh-2 ) {
384             if( gui_menu_on )
385                 guiToggleMenu();                        
386         }
387 #endif
388         puMouse ( x, y ) ;
389         glutPostRedisplay () ;
390     } else {
391         if( x == _mX && y == _mY)
392             return;
393         
394         // reset left click MOUSE_VIEW toggle feature
395         _mVtoggle = 0;
396         
397         switch (mouse_mode) {
398             case MOUSE_YOKE:
399                 if( !mouse_joystick_control ) {
400                     mouse_joystick_control = 1;
401                     fgSetString("/sim/control-mode", "mouse");
402                 } else {
403                     if ( left_button() ) {
404                         move_brake(   (_mX - x) * brake_sensitivity);
405                         move_throttle((_mY - y) * throttle_sensitivity);
406                     } else if ( right_button() ) {
407                         if( ! AP_HeadingEnabled() ) {
408                             move_rudder((x - _mX) * rudder_sensitivity);
409                         }
410                         if( ! AP_AltitudeEnabled() ) {
411                             move_elevator_trim((_mY - y) * trim_sensitivity);
412                         }
413                     } else {
414                         if( ! AP_HeadingEnabled() ) {
415                             move_aileron((x - _mX) * aileron_sensitivity);
416                         }
417                         if( ! AP_AltitudeEnabled() ) {
418                             move_elevator((_mY - y) * elevator_sensitivity);
419                         }
420                     }
421                 }
422                 // Keep the mouse in the window.
423                 if (x < 5 || x > ww-5 || y < 5 || y > wh-5) {
424                     x = ww / 2;
425                     y = wh / 2;
426                     need_warp = 1;
427                 }
428                 break;
429                 
430             case MOUSE_VIEW:
431                 if( y <= 0 ) {
432 #define CONTRAINED_MOUSE_VIEW_Y
433 #ifdef CONTRAINED_MOUSE_VIEW_Y
434                     y = 1;
435 #else
436                     y = wh-2;
437 #endif // CONTRAINED_MOUSE_VIEW_Y
438                     need_warp = 1;
439                 } else if( y >= wh-1) {
440 #ifdef CONTRAINED_MOUSE_VIEW_Y
441                     y = wh-2;
442 #else
443                     y = 1;
444 #endif // CONTRAINED_MOUSE_VIEW_Y
445                     need_warp = 1;
446                 }
447                 // wrap MOUSE_VIEW mode cursor x position
448                 if ( x <= 0 ) {
449                     need_warp = 1;
450                     x = ww-2;
451                 } else if ( x >= ww-1 ) {
452                     need_warp = 1;
453                     x = 1;
454                 }
455                 // try to get SGD_PI movement in each half of screen
456                 // do spherical pan
457                 W = ww;
458                 H = wh;
459                 if( middle_button() ) {
460                     trackball(lastGuiQuat,
461                               (2.0f * _mX - W) / W,
462                               0, //(H - 2.0f * y) / H,         // 3
463                               (2.0f * x - W) / W,
464                               0 //(H - 2.0f * _mY) / H       // 1
465                              );
466                     x = _mX;
467                     y = _mY;
468                     need_warp = 1;
469                 } else {
470                     trackball(lastGuiQuat,
471                               0, //(2.0f * _mX - W) / W,  // 0
472                               (H - 2.0f * y) / H,         // 3
473                               0, //(2.0f * x - W) / W,    // 2
474                               (H - 2.0f * _mY) / H        // 1 
475                              );
476                 }
477                 add_quats(lastGuiQuat, curGuiQuat, curGuiQuat);
478                 build_rotmatrix(GuiQuat_mat, curGuiQuat);
479                 
480                 // do horizontal pan
481                 // this could be done in above quat
482                 // but requires redoing view pipeline
483                 offset = get_goal_view_offset();
484                 offset += ((_mX - x) * SGD_2PI / W );
485                 while (offset < 0.0) {
486                     offset += SGD_2PI;
487                 }
488                 while (offset > SGD_2PI) {
489                     offset -= SGD_2PI;
490                 }
491                 set_goal_view_offset(offset);
492                 set_goal_view_tilt(asin( GuiQuat_mat[1][2]) * SGD_RADIANS_TO_DEGREES );
493 #ifdef NO_SMOOTH_MOUSE_VIEW
494                 set_view_offset(offset);
495                 set_view_tilt(asin( GuiQuat_mat[1][2]) * SGD_RADIANS_TO_DEGREES );
496 #endif
497                 break;
498             
499             default:
500                 break;
501         }
502     }
503     if( need_warp)
504         glutWarpPointer(x, y);
505     
506     // Record the new mouse position.
507     _mX = x;
508     _mY = y;
509 }
510
511
512 void guiMouseFunc(int button, int updown, int x, int y)
513 {
514     int glutModifiers;
515
516     // private MOUSE_VIEW state variables
517     // to allow alternate left clicks in MOUSE_VIEW mode
518     // to toggle between current offsets and straight ahead
519     // uses _mVtoggle
520     static int _mVx, _mVy, _Vx, _Vy;
521     static float _quat[4];
522     static double _view_offset;
523     
524     glutModifiers = glutGetModifiers();
525     glut_active_shift = glutModifiers & GLUT_ACTIVE_SHIFT;
526     glut_active_ctrl  = glutModifiers & GLUT_ACTIVE_CTRL; 
527     glut_active_alt   = glutModifiers & GLUT_ACTIVE_ALT;
528     
529     // Was the left button pressed?
530     if (updown == GLUT_DOWN ) {
531         if( button == GLUT_LEFT_BUTTON)
532         {
533             switch (mouse_mode) {
534                 case MOUSE_POINTER:
535                     break;
536                 case MOUSE_YOKE:
537                     break;
538                 case MOUSE_VIEW:
539                     if(_mVtoggle) {
540                         // resume previous view offsets
541                         _mX = _mVx;
542                         _mY = _mVy;
543                         x = _Vx;
544                         y = _Vy;
545                         sgCopyVec4(curGuiQuat, _quat);
546                         set_goal_view_offset(_view_offset);
547                         set_goal_view_tilt(0.0);
548 #ifdef NO_SMOOTH_MOUSE_VIEW
549                         set_view_offset(_view_offset);
550 #endif
551                     } else {
552                         // center view
553                         _mVx = _mX;
554                         _mVy = _mY;
555                         _Vx = x;
556                         _Vy = y;
557                         sgCopyVec4(_quat,curGuiQuat);
558                         x = MOUSE_XSIZE/2;
559                         y = MOUSE_YSIZE/2;
560                         Quat0();
561                         _view_offset = get_goal_view_offset();
562                         set_goal_view_offset(0.0);
563                         set_goal_view_tilt(0.0);
564 #ifdef NO_SMOOTH_MOUSE_VIEW
565                         set_view_offset(0.0);
566                         set_view_tilt(0.0);
567 #endif
568                     }
569                     glutWarpPointer( x , y);
570                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
571                     _mVtoggle = ~_mVtoggle;
572                     break;
573             }
574         } else if ( button == GLUT_RIGHT_BUTTON) {
575             switch (mouse_mode) {
576                                 
577                 case MOUSE_POINTER:
578                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in yoke mode" );
579                                         
580                     mouse_mode = MOUSE_YOKE;
581                     mouse_joystick_control = 0;
582                     _savedX = x;
583                     _savedY = y;
584                     // start with zero point in center of screen
585                     _mX = MOUSE_XSIZE/2;
586                     _mY = MOUSE_YSIZE/2;
587                     
588                     // try to have the MOUSE_YOKE position
589                     // reflect the current stick position
590                     x = _mX - (int)(get_aileron() * aileron_sensitivity);
591                     y = _mY - (int)(get_elevator() * elevator_sensitivity);
592                     
593                     glutSetCursor(GLUT_CURSOR_CROSSHAIR);
594                     break;
595                     
596                 case MOUSE_YOKE:
597                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in view mode" );
598                                         
599                     mouse_mode = MOUSE_VIEW;
600                     fgSetString("/sim/control-mode", "joystick");
601                                         
602                                         // recenter cursor and reset 
603                     x = MOUSE_XSIZE/2;
604                     y = MOUSE_YSIZE/2;
605                     _mVtoggle = 0;
606 // #ifndef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
607                     Quat0();
608                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
609 // #endif
610                     glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
611                     break;
612                     
613                 case MOUSE_VIEW:
614                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in pointer mode" );
615                                         
616                     mouse_mode = MOUSE_POINTER;
617                     x = _savedX;
618                     y = _savedY;
619 #ifdef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
620                     Quat0();
621                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
622                     set_goal_view_offset(0.0);
623                     set_goal_view_tilt(0.0);
624 #ifdef NO_SMOOTH_MOUSE_VIEW
625                     set_view_offset(0.0);
626                     set_view_tilt(0.0);
627 #endif // NO_SMOOTH_MOUSE_VIEW
628 #endif // RESET_VIEW_ON_LEAVING_MOUSE_VIEW
629                     glutSetCursor(GLUT_CURSOR_INHERIT);
630
631 #if defined(FG_OLD_MENU)                    
632 #if defined(WIN32_CURSOR_TWEAKS_OFF)
633                     if(!gui_menu_on)
634                         TurnCursorOff();
635 #endif // WIN32_CURSOR_TWEAKS_OFF
636 #endif // FG_OLD_MENU
637                     break;
638             } // end switch (mouse_mode)
639             glutWarpPointer( x, y );
640         } // END RIGHT BUTTON
641     } // END UPDOWN == GLUT_DOWN
642     
643     // Note which button is pressed.
644     if ( updown == GLUT_DOWN ) {
645         last_buttons |=  ( 1 << button ) ;
646     } else {
647         last_buttons &= ~( 1 << button ) ;
648     }
649     
650     // If we're in pointer mode, let PUI
651     // know what's going on.
652     if (mouse_mode == MOUSE_POINTER) {
653       if (!puMouse (button, updown, x,y)) {
654         if ( globals->get_current_panel() != NULL ) {
655           globals->get_current_panel()->doMouseAction(button, updown, x, y);
656         }
657       }
658     }
659     
660     // Register the new position (if it
661     // hasn't been registered already).
662     _mX = x;
663     _mY = y;
664     
665     glutPostRedisplay ();
666 }
667
668
669
670