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