]> git.mxchange.org Git - flightgear.git/blob - src/GUI/mouse.cxx
Fix the nmea and garmin output to a) fake a GSA sentence, b) fix a y2k bug
[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 <Include/fg_memory.h>
59 #include <Aircraft/aircraft.hxx>
60 #include <Airports/simple.hxx>
61 //#include <Autopilot/auto_gui.hxx>
62 #include <Autopilot/newauto.hxx>
63 #include <Cockpit/panel.hxx>
64 #include <Controls/controls.hxx>
65 #include <FDM/flight.hxx>
66 #include <Main/fg_init.hxx>
67 #include <Main/fg_props.hxx>
68 #include <Main/viewmgr.hxx>
69
70 #include "gui.h"
71 #include "gui_local.hxx"
72
73 SG_USING_STD(string);
74 SG_USING_STD(cout);
75
76 /* --------------------------------------------------------------------
77 Mouse stuff
78 ---------------------------------------------------------------------*/
79
80 static int _mX = 0;
81 static int _mY = 0;
82 static int _savedX = 0;
83 static int _savedY = 0;
84 static int last_buttons = 0 ;
85 static int mouse_active = 0;
86 static int mouse_joystick_control = 0;
87
88 //static time_t mouse_off_time;
89 //static int mouse_timed_out;
90
91 // to allow returning to previous view
92 // on second left click in MOUSE_VIEW mode
93 // This has file scope so that it can be reset
94 // if the little rodent is moved  NHV
95 static  int _mVtoggle = 0;
96
97 // we break up the glutGetModifiers return mask
98 // once per loop and stash what we need in these
99 static int glut_active_shift = 0;
100 static int glut_active_ctrl = 0;
101 static int glut_active_alt = 0;
102
103 static int MOUSE_XSIZE = 0;
104 static int MOUSE_YSIZE = 0;
105
106 // uncomment this for view to exactly follow mouse in MOUSE_VIEW mode
107 // else smooth out the view panning to .01 radian per frame
108 // see view_offset smoothing mechanism in main.cxx
109 #define NO_SMOOTH_MOUSE_VIEW
110
111 // uncomment following to
112 #define RESET_VIEW_ON_LEAVING_MOUSE_VIEW
113
114 /* --------------------------------------------------------------------
115 Support for mouse as control yoke (david@megginson.com)
116
117 - right button toggles between pointer and yoke
118 - horizontal drag with no buttons moves ailerons
119 - vertical drag with no buttons moves elevators
120 - horizontal drag with left button moves brakes (left=on)
121 - vertical drag with left button moves throttle (up=more)
122 - horizontal drag with middle button moves rudder
123 - vertical drag with middle button moves trim
124
125 For the *_sensitivity variables, a lower number means more sensitive.
126
127 TODO: figure out how to keep pointer from leaving window in yoke mode.
128 TODO: add thresholds and null zones
129 TODO: sensitivity should be configurable at user option.
130 TODO: allow differential braking (this will be useful if FlightGear
131       ever supports tail-draggers like the DC-3)
132 ---------------------------------------------------------------------*/
133
134 MouseMode mouse_mode = MOUSE_POINTER;
135
136 static double aileron_sensitivity = 1.0/500.0;
137 static double elevator_sensitivity = 1.0/500.0;
138 static double brake_sensitivity = 1.0/250.0;
139 static double throttle_sensitivity = 1.0/250.0;
140 static double rudder_sensitivity = 1.0/500.0;
141 static double trim_sensitivity = 1.0/1000.0;
142
143 void guiInitMouse(int width, int height)
144 {
145         MOUSE_XSIZE = width;
146         MOUSE_YSIZE = height;
147 }
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 static inline void set_goal_view_offset( float offset )
173 {
174         globals->get_current_view()->setGoalHeadingOffset_deg(offset * SGD_RADIANS_TO_DEGREES);
175 }
176
177 static inline void set_view_offset( float offset )
178 {
179         globals->get_current_view()->setHeadingOffset_deg(offset * SGD_RADIANS_TO_DEGREES);
180 }
181
182 static inline void set_goal_view_tilt( float tilt )
183 {
184         globals->get_current_view()->setGoalPitchOffset_deg(tilt);
185 }
186
187 static inline void set_view_tilt( float tilt )
188 {
189         globals->get_current_view()->setPitchOffset_deg(tilt);
190 }
191
192 static inline float get_view_offset() {
193         return globals->get_current_view()->getHeadingOffset_deg() * SGD_DEGREES_TO_RADIANS;
194 }
195
196 static inline float get_goal_view_offset() {
197         return globals->get_current_view()->getGoalHeadingOffset_deg() * SGD_DEGREES_TO_RADIANS;
198 }
199
200 static inline void move_brake(float offset) {
201         globals->get_controls()->move_brake(FGControls::ALL_WHEELS, offset);
202 }
203
204 static inline void move_throttle(float offset) {
205         globals->get_controls()->move_throttle(FGControls::ALL_ENGINES, offset);
206 }
207
208 static inline void move_rudder(float offset) {
209         globals->get_controls()->move_rudder(offset);
210 }
211
212 static inline void move_elevator_trim(float offset) {
213         globals->get_controls()->move_elevator_trim(offset);
214 }
215
216 static inline void move_aileron(float offset) {
217         globals->get_controls()->move_aileron(offset);
218 }
219
220 static inline void move_elevator(float offset) {
221         globals->get_controls()->move_elevator(offset);
222 }
223
224 static inline float get_aileron() {
225         return globals->get_controls()->get_aileron();
226 }
227
228 static inline float get_elevator() {
229         return globals->get_controls()->get_elevator();
230 }
231
232 static inline bool AP_HeadingEnabled() {
233         return globals->get_autopilot()->get_HeadingEnabled();
234 }
235
236 static inline bool AP_AltitudeEnabled() {
237         return globals->get_autopilot()->get_AltitudeEnabled();
238 }
239
240 void TurnCursorOn( void )
241 {
242     mouse_active = ~0;
243 #if defined(WIN32)
244     switch (mouse_mode) {
245         case MOUSE_POINTER:
246             glutSetCursor(GLUT_CURSOR_INHERIT);
247             break;
248         case MOUSE_YOKE:
249             glutSetCursor(GLUT_CURSOR_CROSSHAIR);
250             break;
251         case MOUSE_VIEW:
252             glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
253             break;
254     }
255 #endif
256 #if defined(X_CURSOR_TWEAKS)
257     glutWarpPointer( MOUSE_XSIZE/2,
258                      MOUSE_YSIZE/2);
259 #endif
260 }
261
262 void TurnCursorOff( void )
263 {
264     mouse_active = 0;
265 #if defined(WIN32_CURSOR_TWEAKS)
266     glutSetCursor(GLUT_CURSOR_NONE);
267 #elif defined(X_CURSOR_TWEAKS)
268     glutWarpPointer( MOUSE_XSIZE,
269                      MOUSE_YSIZE);
270 #endif
271 }
272
273 void maybeToggleMouse( void )
274 {
275 #if defined(WIN32_CURSOR_TWEAKS_OFF)
276     static int first_time = ~0;
277     static int mouse_changed = 0;
278
279     if ( first_time ) {
280         if(!mouse_active) {
281             mouse_changed = ~mouse_changed;
282             TurnCursorOn();
283         }
284     } else {
285         if( mouse_mode != MOUSE_POINTER )
286             return;
287         if( mouse_changed ) {
288             mouse_changed = ~mouse_changed;
289             if(mouse_active) {
290                 TurnCursorOff();
291             }
292         }
293     }
294     first_time = ~first_time;
295 #endif // #ifdef WIN32
296 }
297
298 // Call with FALSE to init and TRUE to restore
299 void BusyCursor( int restore )
300 {
301     static GLenum cursor = (GLenum) 0;
302     if( restore ) {
303         glutSetCursor(cursor);
304     } else {
305         cursor = (GLenum) glutGet( (GLenum) GLUT_WINDOW_CURSOR );
306 #if defined(WIN32_CURSOR_TWEAKS)
307         TurnCursorOn();
308 #endif
309         glutSetCursor( GLUT_CURSOR_WAIT );
310     }
311 }
312
313
314 // Center the view offsets
315 void CenterView( void ) {
316     if( mouse_mode == MOUSE_VIEW ) {
317         mouse_mode = MOUSE_POINTER;
318         _savedX = MOUSE_XSIZE/2;
319         _savedY = MOUSE_YSIZE/2;
320         _mVtoggle = 0;
321         Quat0();
322         build_rotmatrix(GuiQuat_mat, curGuiQuat);
323         glutSetCursor(GLUT_CURSOR_INHERIT);
324
325         // Is this necessary ??
326 #if defined(FG_OLD_MENU)
327         if( !gui_menu_on )   TurnCursorOff();
328 #endif
329
330         glutWarpPointer( _savedX, _savedY );
331     }
332     set_goal_view_offset(0.0);
333     set_view_offset(0.0);
334 }
335
336
337 //#define TRANSLATE_HUD
338 // temporary hack until pitch_offset is added to view pipeline
339 void fgTranslateHud( void ) {
340 #ifdef TRANSLATE_HUD
341     if(mouse_mode == MOUSE_VIEW) {
342
343         int ww = MOUSE_XSIZE;
344         int wh = MOUSE_YSIZE;
345
346         float y = 4*(_mY-(wh/2));// * ((wh/SGD_PI)*SG_RADIANS_TO_DEGREES);
347         
348         float x =  get_view_offset() * SG_RADIANS_TO_DEGREES;
349
350         if( x < -180 )      x += 360;
351         else if( x > 180 )      x -= 360;
352
353         x *= ww/90.0;
354         //      x *= ww/180.0;
355         //      x *= ww/360.0;
356
357         //      glTranslatef( x*ww/640, y*wh/480, 0 );
358         glTranslatef( x*640/ww, y*480/wh, 0 );
359     }
360 #endif // TRANSLATE_HUD
361 }
362
363 void guiMotionFunc ( int x, int y )
364 {
365     int ww, wh, need_warp = 0;
366     float W, H;
367     double offset;
368
369     ww = MOUSE_XSIZE;
370     wh = MOUSE_YSIZE;
371
372     if (mouse_mode == MOUSE_POINTER) {
373 #if defined(FG_OLD_MENU)
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 #endif
385         puMouse ( x, y ) ;
386         glutPostRedisplay () ;
387     } else {
388         if( x == _mX && y == _mY)
389             return;
390         
391         // reset left click MOUSE_VIEW toggle feature
392         _mVtoggle = 0;
393         
394         switch (mouse_mode) {
395             case MOUSE_YOKE:
396                 if( !mouse_joystick_control ) {
397                     mouse_joystick_control = 1;
398                     fgSetString("/sim/control-mode", "mouse");
399                 } else {
400                     if ( left_button() ) {
401                         move_brake(   (_mX - x) * brake_sensitivity);
402                         move_throttle((_mY - y) * throttle_sensitivity);
403                     } else if ( right_button() ) {
404                         if( ! AP_HeadingEnabled() ) {
405                             move_rudder((x - _mX) * rudder_sensitivity);
406                         }
407                         if( ! AP_AltitudeEnabled() ) {
408                             move_elevator_trim((_mY - y) * trim_sensitivity);
409                         }
410                     } else {
411                         if( ! AP_HeadingEnabled() ) {
412                             move_aileron((x - _mX) * aileron_sensitivity);
413                         }
414                         if( ! AP_AltitudeEnabled() ) {
415                             move_elevator((_mY - y) * elevator_sensitivity);
416                         }
417                     }
418                 }
419                 // Keep the mouse in the window.
420                 if (x < 5 || x > ww-5 || y < 5 || y > wh-5) {
421                     x = ww / 2;
422                     y = wh / 2;
423                     need_warp = 1;
424                 }
425                 break;
426                 
427             case MOUSE_VIEW:
428                 if( y <= 0 ) {
429 #define CONTRAINED_MOUSE_VIEW_Y
430 #ifdef CONTRAINED_MOUSE_VIEW_Y
431                     y = 1;
432 #else
433                     y = wh-2;
434 #endif // CONTRAINED_MOUSE_VIEW_Y
435                     need_warp = 1;
436                 } else if( y >= wh-1) {
437 #ifdef CONTRAINED_MOUSE_VIEW_Y
438                     y = wh-2;
439 #else
440                     y = 1;
441 #endif // CONTRAINED_MOUSE_VIEW_Y
442                     need_warp = 1;
443                 }
444                 // wrap MOUSE_VIEW mode cursor x position
445                 if ( x <= 0 ) {
446                     need_warp = 1;
447                     x = ww-2;
448                 } else if ( x >= ww-1 ) {
449                     need_warp = 1;
450                     x = 1;
451                 }
452                 // try to get SGD_PI movement in each half of screen
453                 // do spherical pan
454                 W = ww;
455                 H = wh;
456                 if( middle_button() ) {
457                     trackball(lastGuiQuat,
458                               (2.0f * _mX - W) / W,
459                               0, //(H - 2.0f * y) / H,         // 3
460                               (2.0f * x - W) / W,
461                               0 //(H - 2.0f * _mY) / H       // 1
462                              );
463                     x = _mX;
464                     y = _mY;
465                     need_warp = 1;
466                 } else {
467                     trackball(lastGuiQuat,
468                               0, //(2.0f * _mX - W) / W,  // 0
469                               (H - 2.0f * y) / H,         // 3
470                               0, //(2.0f * x - W) / W,    // 2
471                               (H - 2.0f * _mY) / H        // 1 
472                              );
473                 }
474                 add_quats(lastGuiQuat, curGuiQuat, curGuiQuat);
475                 build_rotmatrix(GuiQuat_mat, curGuiQuat);
476                 
477                 // do horizontal pan
478                 // this could be done in above quat
479                 // but requires redoing view pipeline
480                 offset = get_goal_view_offset();
481                 offset += ((_mX - x) * SGD_2PI / W );
482                 while (offset < 0.0) {
483                     offset += SGD_2PI;
484                 }
485                 while (offset > SGD_2PI) {
486                     offset -= SGD_2PI;
487                 }
488                 set_goal_view_offset(offset);
489                 set_goal_view_tilt(asin( GuiQuat_mat[1][2]) * SGD_RADIANS_TO_DEGREES );
490 #ifdef NO_SMOOTH_MOUSE_VIEW
491                 set_view_offset(offset);
492                 set_view_tilt(asin( GuiQuat_mat[1][2]) * SGD_RADIANS_TO_DEGREES );
493 #endif
494                 break;
495             
496             default:
497                 break;
498         }
499     }
500     if( need_warp)
501         glutWarpPointer(x, y);
502     
503     // Record the new mouse position.
504     _mX = x;
505     _mY = y;
506 }
507
508
509 void guiMouseFunc(int button, int updown, int x, int y)
510 {
511     int glutModifiers;
512
513     // private MOUSE_VIEW state variables
514     // to allow alternate left clicks in MOUSE_VIEW mode
515     // to toggle between current offsets and straight ahead
516     // uses _mVtoggle
517     static int _mVx, _mVy, _Vx, _Vy;
518     static float _quat[4];
519     static double _view_offset;
520     
521     glutModifiers = glutGetModifiers();
522     glut_active_shift = glutModifiers & GLUT_ACTIVE_SHIFT;
523     glut_active_ctrl  = glutModifiers & GLUT_ACTIVE_CTRL; 
524     glut_active_alt   = glutModifiers & GLUT_ACTIVE_ALT;
525     
526     // Was the left button pressed?
527     if (updown == GLUT_DOWN ) {
528         if( button == GLUT_LEFT_BUTTON)
529         {
530             switch (mouse_mode) {
531                 case MOUSE_POINTER:
532                     break;
533                 case MOUSE_YOKE:
534                     break;
535                 case MOUSE_VIEW:
536                     if(_mVtoggle) {
537                         // resume previous view offsets
538                         _mX = _mVx;
539                         _mY = _mVy;
540                         x = _Vx;
541                         y = _Vy;
542                         sgCopyVec4(curGuiQuat, _quat);
543                         set_goal_view_offset(_view_offset);
544                         set_goal_view_tilt(0.0);
545 #ifdef NO_SMOOTH_MOUSE_VIEW
546                         set_view_offset(_view_offset);
547 #endif
548                     } else {
549                         // center view
550                         _mVx = _mX;
551                         _mVy = _mY;
552                         _Vx = x;
553                         _Vy = y;
554                         sgCopyVec4(_quat,curGuiQuat);
555                         x = MOUSE_XSIZE/2;
556                         y = MOUSE_YSIZE/2;
557                         Quat0();
558                         _view_offset = get_goal_view_offset();
559                         set_goal_view_offset(0.0);
560                         set_goal_view_tilt(0.0);
561 #ifdef NO_SMOOTH_MOUSE_VIEW
562                         set_view_offset(0.0);
563                         set_view_tilt(0.0);
564 #endif
565                     }
566                     glutWarpPointer( x , y);
567                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
568                     _mVtoggle = ~_mVtoggle;
569                     break;
570             }
571         } else if ( button == GLUT_RIGHT_BUTTON) {
572             switch (mouse_mode) {
573                                 
574                 case MOUSE_POINTER:
575                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in yoke mode" );
576                                         
577                     mouse_mode = MOUSE_YOKE;
578                     mouse_joystick_control = 0;
579                     _savedX = x;
580                     _savedY = y;
581                     // start with zero point in center of screen
582                     _mX = MOUSE_XSIZE/2;
583                     _mY = MOUSE_YSIZE/2;
584                     
585                     // try to have the MOUSE_YOKE position
586                     // reflect the current stick position
587                     x = _mX - (int)(get_aileron() * aileron_sensitivity);
588                     y = _mY - (int)(get_elevator() * elevator_sensitivity);
589                     
590                     glutSetCursor(GLUT_CURSOR_CROSSHAIR);
591                     break;
592                     
593                 case MOUSE_YOKE:
594                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in view mode" );
595                                         
596                     mouse_mode = MOUSE_VIEW;
597                     fgSetString("/sim/control-mode", "joystick");
598                                         
599                                         // recenter cursor and reset 
600                     x = MOUSE_XSIZE/2;
601                     y = MOUSE_YSIZE/2;
602                     _mVtoggle = 0;
603 // #ifndef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
604                     Quat0();
605                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
606 // #endif
607                     glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
608                     break;
609                     
610                 case MOUSE_VIEW:
611                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in pointer mode" );
612                                         
613                     mouse_mode = MOUSE_POINTER;
614                     x = _savedX;
615                     y = _savedY;
616 #ifdef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
617                     Quat0();
618                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
619                     set_goal_view_offset(0.0);
620                     set_goal_view_tilt(0.0);
621 #ifdef NO_SMOOTH_MOUSE_VIEW
622                     set_view_offset(0.0);
623                     set_view_tilt(0.0);
624 #endif // NO_SMOOTH_MOUSE_VIEW
625 #endif // RESET_VIEW_ON_LEAVING_MOUSE_VIEW
626                     glutSetCursor(GLUT_CURSOR_INHERIT);
627
628 #if defined(FG_OLD_MENU)                    
629 #if defined(WIN32_CURSOR_TWEAKS_OFF)
630                     if(!gui_menu_on)
631                         TurnCursorOff();
632 #endif // WIN32_CURSOR_TWEAKS_OFF
633 #endif // FG_OLD_MENU
634                     break;
635             } // end switch (mouse_mode)
636             glutWarpPointer( x, y );
637         } // END RIGHT BUTTON
638     } // END UPDOWN == GLUT_DOWN
639     
640     // Note which button is pressed.
641     if ( updown == GLUT_DOWN ) {
642         last_buttons |=  ( 1 << button ) ;
643     } else {
644         last_buttons &= ~( 1 << button ) ;
645     }
646     
647     // If we're in pointer mode, let PUI
648     // know what's going on.
649     if (mouse_mode == MOUSE_POINTER) {
650       if (!puMouse (button, updown, x,y)) {
651         if ( globals->get_current_panel() != NULL ) {
652           globals->get_current_panel()->doMouseAction(button, updown, x, y);
653         }
654       }
655     }
656     
657     // Register the new position (if it
658     // hasn't been registered already).
659     _mX = x;
660     _mY = y;
661     
662     glutPostRedisplay ();
663 }
664
665
666
667