]> git.mxchange.org Git - flightgear.git/blob - src/GUI/mouse.cxx
SG-ified logstream.
[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 <simgear/xgl/xgl.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/fgpath.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/views.hxx>
70 //#include <Network/network.h>
71 //#include <Time/fg_time.hxx>
72
73 #if defined( WIN32 ) && !defined( __CYGWIN__ )
74 #  include <simgear/screen/win32-printer.h>
75 #  include <simgear/screen/GlBitmaps.h>
76 #endif
77
78 #include "gui.h"
79 #include "gui_local.hxx"
80
81 SG_USING_STD(string);
82
83 #ifndef SG_HAVE_NATIVE_SGI_COMPILERS
84 SG_USING_STD(cout);
85 #endif
86
87 /* --------------------------------------------------------------------
88 Mouse stuff
89 ---------------------------------------------------------------------*/
90
91 static int _mX = 0;
92 static int _mY = 0;
93 static int _savedX = 0;
94 static int _savedY = 0;
95 static int last_buttons = 0 ;
96 static int mouse_active = 0;
97 static int mouse_joystick_control = 0;
98
99 //static time_t mouse_off_time;
100 //static int mouse_timed_out;
101
102 // to allow returning to previous view
103 // on second left click in MOUSE_VIEW mode
104 // This has file scope so that it can be reset
105 // if the little rodent is moved  NHV
106 static  int _mVtoggle;
107
108 // we break up the glutGetModifiers return mask
109 // once per loop and stash what we need in these
110 static int glut_active_shift;
111 static int glut_active_ctrl;
112 static int glut_active_alt;
113
114 // uncomment this for view to exactly follow mouse in MOUSE_VIEW mode
115 // else smooth out the view panning to .01 radian per frame
116 // see view_offset smoothing mechanism in main.cxx
117 #define NO_SMOOTH_MOUSE_VIEW
118
119 // uncomment following to
120 #define RESET_VIEW_ON_LEAVING_MOUSE_VIEW
121
122 /* --------------------------------------------------------------------
123 Support for mouse as control yoke (david@megginson.com)
124
125 - right button toggles between pointer and yoke
126 - horizontal drag with no buttons moves ailerons
127 - vertical drag with no buttons moves elevators
128 - horizontal drag with left button moves brakes (left=on)
129 - vertical drag with left button moves throttle (up=more)
130 - horizontal drag with middle button moves rudder
131 - vertical drag with middle button moves trim
132
133 For the *_sensitivity variables, a lower number means more sensitive.
134
135 TODO: figure out how to keep pointer from leaving window in yoke mode.
136 TODO: add thresholds and null zones
137 TODO: sensitivity should be configurable at user option.
138 TODO: allow differential braking (this will be useful if FlightGear
139       ever supports tail-draggers like the DC-3)
140 ---------------------------------------------------------------------*/
141
142 MouseMode mouse_mode = MOUSE_POINTER;
143
144 static double aileron_sensitivity = 1.0/500.0;
145 static double elevator_sensitivity = 1.0/500.0;
146 static double brake_sensitivity = 1.0/250.0;
147 static double throttle_sensitivity = 1.0/250.0;
148 static double rudder_sensitivity = 1.0/500.0;
149 static double trim_sensitivity = 1.0/1000.0;
150
151 static inline int guiGetMouseButton(void)
152 {
153         return last_buttons;
154 }
155
156 static inline void guiGetMouse(int *x, int *y)
157 {
158         *x = _mX;
159         *y = _mY;
160 };
161
162 static inline int left_button( void ) {
163     return( last_buttons & (1 << GLUT_LEFT_BUTTON) );
164 }
165
166 static inline int middle_button( void ) {
167     return( last_buttons & (1 << GLUT_MIDDLE_BUTTON) );
168 }
169
170 static inline int right_button( void ) {
171     return( last_buttons & (1 << GLUT_RIGHT_BUTTON) );
172 }
173
174 void TurnCursorOn( void )
175 {
176     mouse_active = ~0;
177 #if defined(WIN32_CURSOR_TWEAKS)
178     switch (mouse_mode) {
179         case MOUSE_POINTER:
180             glutSetCursor(GLUT_CURSOR_INHERIT);
181             break;
182         case MOUSE_YOKE:
183             glutSetCursor(GLUT_CURSOR_CROSSHAIR);
184             break;
185         case MOUSE_VIEW:
186             glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
187             break;
188     }
189 #endif
190 #if defined(X_CURSOR_TWEAKS)
191     glutWarpPointer( fgGetInt("/sim/startup/xsize")/2,
192                      fgGetInt("/sim/startup/ysize")/2);
193 #endif
194 }
195
196 void TurnCursorOff( void )
197 {
198     mouse_active = 0;
199 #if defined(WIN32_CURSOR_TWEAKS)
200     glutSetCursor(GLUT_CURSOR_NONE);
201 #elif defined(X_CURSOR_TWEAKS)
202     glutWarpPointer( fgGetInt("/sim/startup/xsize"),
203                      fgGetInt("/sim/startup/ysize"));
204 #endif
205 }
206
207 void maybeToggleMouse( void )
208 {
209 #if defined(WIN32_CURSOR_TWEAKS)
210     static int first_time = ~0;
211     static int mouse_changed = 0;
212
213     if ( first_time ) {
214         if(!mouse_active) {
215             mouse_changed = ~mouse_changed;
216             TurnCursorOn();
217         }
218     } else {
219         if( mouse_mode != MOUSE_POINTER )
220             return;
221         if( mouse_changed ) {
222             mouse_changed = ~mouse_changed;
223             if(mouse_active) {
224                 TurnCursorOff();
225             }
226         }
227     }
228     first_time = ~first_time;
229 #endif // #ifdef WIN32
230 }
231
232 // Call with FALSE to init and TRUE to restore
233 void BusyCursor( int restore )
234 {
235     static GLenum cursor = (GLenum) 0;
236     if( restore ) {
237         glutSetCursor(cursor);
238     } else {
239         cursor = (GLenum) glutGet( (GLenum) GLUT_WINDOW_CURSOR );
240 #if defined(WIN32_CURSOR_TWEAKS)
241         TurnCursorOn();
242 #endif
243         glutSetCursor( GLUT_CURSOR_WAIT );
244     }
245 }
246
247
248 // Center the view offsets
249 void CenterView( void ) {
250     if( mouse_mode == MOUSE_VIEW ) {
251         mouse_mode = MOUSE_POINTER;
252         _savedX = fgGetInt("/sim/startup/xsize")/2;
253         _savedY = fgGetInt("/sim/startup/ysize")/2;
254         _mVtoggle = 0;
255         Quat0();
256         build_rotmatrix(GuiQuat_mat, curGuiQuat);
257         glutSetCursor(GLUT_CURSOR_INHERIT);
258
259         // Is this necessary ??
260         if( !gui_menu_on )   TurnCursorOff();
261
262         glutWarpPointer( _savedX, _savedY );
263     }
264     globals->get_current_view()->set_goal_view_offset(0.0);
265     globals->get_current_view()->set_view_offset(0.0);
266 }
267
268
269 void guiMotionFunc ( int x, int y )
270 {
271     int ww, wh, need_warp = 0;
272     float W, H;
273     double offset;
274
275     if (mouse_mode == MOUSE_POINTER) {
276         puMouse ( x, y ) ;
277         glutPostRedisplay () ;
278     } else {
279         if( x == _mX && y == _mY)
280             return;
281         
282         // reset left click MOUSE_VIEW toggle feature
283         _mVtoggle = 0;
284         
285         ww = fgGetInt("/sim/startup/xsize");
286         wh = fgGetInt("/sim/startup/ysize");
287         
288         switch (mouse_mode) {
289             case MOUSE_YOKE:
290                 if( !mouse_joystick_control ) {
291                     mouse_joystick_control = 1;
292                     fgSetString("/sim/control-mode", "mouse");
293                 } else {
294                     if ( left_button() ) {
295                         offset = (_mX - x) * brake_sensitivity;
296                         controls.move_brake(FGControls::ALL_WHEELS, offset);
297                         offset = (_mY - y) * throttle_sensitivity;
298                         controls.move_throttle(FGControls::ALL_ENGINES, offset);
299                     } else if ( right_button() ) {
300                         if( ! current_autopilot->get_HeadingEnabled() ) {
301                             offset = (x - _mX) * rudder_sensitivity;
302                             controls.move_rudder(offset);
303                         }
304                         if( ! current_autopilot->get_AltitudeEnabled() ) {
305                             offset = (_mY - y) * trim_sensitivity;
306                             controls.move_elevator_trim(offset);
307                         }
308                     } else {
309                         if( ! current_autopilot->get_HeadingEnabled() ) {
310                             offset = (x - _mX) * aileron_sensitivity;
311                             controls.move_aileron(offset);
312                         }
313                         if( ! current_autopilot->get_AltitudeEnabled() ) {
314                             offset = (_mY - y) * elevator_sensitivity;
315                             controls.move_elevator(offset);
316                         }
317                     }
318                 }
319                 // Keep the mouse in the window.
320                 if (x < 5 || x > ww-5 || y < 5 || y > wh-5) {
321                     x = ww / 2;
322                     y = wh / 2;
323                     need_warp = 1;
324                 }
325                 break;
326                 
327             case MOUSE_VIEW:
328                 if( y <= 0 ) {
329 #define CONTRAINED_MOUSE_VIEW_Y
330 #ifdef CONTRAINED_MOUSE_VIEW_Y
331                     y = 1;
332 #else
333                     y = wh-2;
334 #endif // CONTRAINED_MOUSE_VIEW_Y
335                     need_warp = 1;
336                 } else if( y >= wh-1) {
337 #ifdef CONTRAINED_MOUSE_VIEW_Y
338                     y = wh-2;
339 #else
340                     y = 1;
341 #endif // CONTRAINED_MOUSE_VIEW_Y
342                     need_warp = 1;
343                 }
344                 // wrap MOUSE_VIEW mode cursor x position
345                 if ( x <= 0 ) {
346                     need_warp = 1;
347                     x = ww-2;
348                 } else if ( x >= ww-1 ) {
349                     need_warp = 1;
350                     x = 1;
351                 }
352                 // try to get SGD_PI movement in each half of screen
353                 // do spherical pan
354                 W = ww;
355                 H = wh;
356                 if( middle_button() ) {
357                     trackball(lastGuiQuat,
358                               (2.0f * _mX - W) / W,
359                               0, //(H - 2.0f * y) / H,         // 3
360                               (2.0f * x - W) / W,
361                               0 //(H - 2.0f * _mY) / H       // 1
362                              );
363                     x = _mX;
364                     y = _mY;
365                     need_warp = 1;
366                 } else {
367                     trackball(lastGuiQuat,
368                               0, //(2.0f * _mX - W) / W,  // 0
369                               (H - 2.0f * y) / H,         // 3
370                               0, //(2.0f * x - W) / W,    // 2
371                               (H - 2.0f * _mY) / H        // 1 
372                              );
373                 }
374                 add_quats(lastGuiQuat, curGuiQuat, curGuiQuat);
375                 build_rotmatrix(GuiQuat_mat, curGuiQuat);
376                 
377                 // do horizontal pan
378                 // this could be done in above quat
379                 // but requires redoing view pipeline
380                 offset = globals->get_current_view()->get_goal_view_offset();
381                 offset += ((_mX - x) * SGD_2PI / W );
382                 while (offset < 0.0) {
383                     offset += SGD_2PI;
384                 }
385                 while (offset > SGD_2PI) {
386                     offset -= SGD_2PI;
387                 }
388                 globals->get_current_view()->set_goal_view_offset(offset);
389 #ifdef NO_SMOOTH_MOUSE_VIEW
390                 globals->get_current_view()->set_view_offset(offset);
391 #endif
392                 break;
393             
394             default:
395                 break;
396         }
397     }
398     if( need_warp)
399         glutWarpPointer(x, y);
400     
401     // Record the new mouse position.
402     _mX = x;
403     _mY = y;
404 }
405
406
407 void guiMouseFunc(int button, int updown, int x, int y)
408 {
409     int glutModifiers;
410
411     // private MOUSE_VIEW state variables
412     // to allow alternate left clicks in MOUSE_VIEW mode
413     // to toggle between current offsets and straight ahead
414     // uses _mVtoggle
415     static int _mVx, _mVy, _Vx, _Vy;
416     static float _quat[4];
417     static double _view_offset;
418     
419     // general purpose variables
420     double offset;
421             
422     glutModifiers = glutGetModifiers();
423     glut_active_shift = glutModifiers & GLUT_ACTIVE_SHIFT;
424     glut_active_ctrl  = glutModifiers & GLUT_ACTIVE_CTRL; 
425     glut_active_alt   = glutModifiers & GLUT_ACTIVE_ALT;
426     
427     // Was the left button pressed?
428     if (updown == GLUT_DOWN ) {
429         if( button == GLUT_LEFT_BUTTON)
430         {
431             switch (mouse_mode) {
432                 case MOUSE_POINTER:
433                     break;
434                 case MOUSE_YOKE:
435                     break;
436                 case MOUSE_VIEW:
437                     if(_mVtoggle) {
438                         // resume previous view offsets
439                         _mX = _mVx;
440                         _mY = _mVy;
441                         x = _Vx;
442                         y = _Vy;
443                         curGuiQuat[0] = _quat[0];
444                         curGuiQuat[1] = _quat[1];
445                         curGuiQuat[2] = _quat[2];
446                         curGuiQuat[3] = _quat[3];
447                         globals->get_current_view()->set_goal_view_offset(_view_offset);
448 #ifdef NO_SMOOTH_MOUSE_VIEW
449                         globals->get_current_view()->set_view_offset(_view_offset);
450 #endif
451                     } else {
452                         // center view
453                         _mVx = _mX;
454                         _mVy = _mY;
455                         _Vx = x;
456                         _Vy = y;
457                         _quat[0] = curGuiQuat[0];
458                         _quat[1] = curGuiQuat[1];
459                         _quat[2] = curGuiQuat[2];
460                         _quat[3] = curGuiQuat[3];
461                         x = fgGetInt("/sim/startup/xsize")/2;
462                         y = fgGetInt("/sim/startup/ysize")/2;
463                         Quat0();
464                         _view_offset =
465                             globals->get_current_view()->get_goal_view_offset();
466                         globals->get_current_view()->set_goal_view_offset(0.0);
467 #ifdef NO_SMOOTH_MOUSE_VIEW
468                         globals->get_current_view()->set_view_offset(0.0);
469 #endif
470                     }
471                     glutWarpPointer( x , y);
472                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
473                     _mVtoggle = ~_mVtoggle;
474                     break;
475             }
476         }else if ( button == GLUT_RIGHT_BUTTON) {
477             switch (mouse_mode) {
478                 case MOUSE_POINTER:
479                     mouse_mode = MOUSE_YOKE;
480                     mouse_joystick_control = 0;
481                     _savedX = x;
482                     _savedY = y;
483                     // start with zero point in center of screen
484                     _mX = fgGetInt("/sim/startup/xsize")/2;
485                     _mY = fgGetInt("/sim/startup/ysize")/2;
486                     
487                     // try to have the MOUSE_YOKE position
488                     // reflect the current stick position
489                     offset = controls.get_aileron();
490                     x = _mX - (int)(offset * aileron_sensitivity);
491                     offset = controls.get_elevator();
492                     y = _mY - (int)(offset * elevator_sensitivity);
493                     
494                     glutSetCursor(GLUT_CURSOR_CROSSHAIR);
495                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in yoke mode" );
496                     break;
497                     
498                 case MOUSE_YOKE:
499                     mouse_mode = MOUSE_VIEW;
500                     fgSetString("/sim/control-mode", "joystick");
501                     x = fgGetInt("/sim/startup/xsize")/2;
502                     y = fgGetInt("/sim/startup/ysize")/2;
503                     _mVtoggle = 0;
504                     Quat0();
505                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
506                     glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
507                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in view mode" );
508                     break;
509                     
510                 case MOUSE_VIEW:
511                     mouse_mode = MOUSE_POINTER;
512                     x = _savedX;
513                     y = _savedY;
514 #ifdef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
515                     Quat0();
516                     build_rotmatrix(GuiQuat_mat, curGuiQuat);
517                     globals->get_current_view()->set_goal_view_offset(0.0);
518 #ifdef NO_SMOOTH_MOUSE_VIEW
519                     globals->get_current_view()->set_view_offset(0.0);
520 #endif
521 #endif      // RESET_VIEW_ON_LEAVING_MOUSE_VIEW
522                     glutSetCursor(GLUT_CURSOR_INHERIT);
523                     
524                     if(!gui_menu_on)
525                         TurnCursorOff();
526                     
527                     SG_LOG( SG_INPUT, SG_INFO, "Mouse in pointer mode" );
528                     break;
529             }     
530             glutWarpPointer( x, y );
531         } // END RIGHT BUTTON
532     } // END UPDOWN == GLUT_DOWN
533     
534     // Note which button is pressed.
535     if ( updown == GLUT_DOWN ) {
536         last_buttons |=  ( 1 << button ) ;
537     } else {
538         last_buttons &= ~( 1 << button ) ;
539     }
540     
541     // If we're in pointer mode, let PUI
542     // know what's going on.
543     if (mouse_mode == MOUSE_POINTER) {
544       if (!puMouse (button, updown, x,y)) {
545         current_panel->doMouseAction(button, updown, x, y);
546       }
547     }
548     
549     // Register the new position (if it
550     // hasn't been registered already).
551     _mX = x;
552     _mY = y;
553     
554     glutPostRedisplay ();
555 }
556