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