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