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