]> git.mxchange.org Git - flightgear.git/blob - src/GUI/gui.cxx
08e19fbc08c421582a943c594a43755384ba36ea
[flightgear.git] / src / GUI / gui.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 FG_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 <Aircraft/aircraft.hxx>
60 #include <Airports/simple.hxx>
61 #include <Autopilot/auto_gui.hxx>
62 #include <Autopilot/newauto.hxx>
63 #include <Cockpit/panel.hxx>
64 #include <Controls/controls.hxx>
65 #include <FDM/flight.hxx>
66 #include <Main/options.hxx>
67 #include <Main/fg_init.hxx>
68 #include <Main/views.hxx>
69 #ifdef FG_NETWORK_OLK
70 #include <NetworkOLK/network.h>
71 #endif
72 #include <Time/fg_time.hxx>
73
74 #if defined( WIN32 ) && !defined( __CYGWIN__ )
75 #  include <simgear/screen/win32-printer.h>
76 #  include <simgear/screen/GlBitmaps.h>
77 #endif
78
79 /*
80  * trackball.h
81  * A virtual trackball implementation
82  * Written by Gavin Bell for Silicon Graphics, November 1988.
83  */
84 /*
85  * Pass the x and y coordinates of the last and current positions of
86  * the mouse, scaled so they are from (-1.0 ... 1.0).
87  *
88  * The resulting rotation is returned as a quaternion rotation in the
89  * first paramater.
90  */
91 void
92 trackball(float q[4], float p1x, float p1y, float p2x, float p2y);
93
94 /*
95  * Given two quaternions, add them together to get a third quaternion.
96  * Adding quaternions to get a compound rotation is analagous to adding
97  * translations to get a compound translation.  When incrementally
98  * adding rotations, the first argument here should be the new
99
100   * rotation, the second and third the total rotation (which will be
101  * over-written with the resulting new total rotation).
102  */
103 void
104 add_quats(float *q1, float *q2, float *dest);
105
106 /*
107  * A useful function, builds a rotation matrix in Matrix based on
108  * given quaternion.
109  */
110 void
111 build_rotmatrix(float m[4][4], float q[4]);
112
113 /*
114  * This function computes a quaternion based on an axis (defined by
115  * the given vector) and an angle about which to rotate.  The angle is
116  * expressed in radians.  The result is put into the third argument.
117  */
118 void
119 axis_to_quat(float a[3], float phi, float q[4]);
120
121
122 #include "gui.h"
123
124 FG_USING_STD(string);
125
126 #ifndef FG_HAVE_NATIVE_SGI_COMPILERS
127 FG_USING_STD(cout);
128 #endif
129
130 #if defined(WIN32) || defined(__CYGWIN32__)
131 #define WIN32_CURSOR_TWEAKS
132 #elif (GLUT_API_VERSION >= 4 || GLUT_XLIB_IMPLEMENTATION >= 9)
133 #define X_CURSOR_TWEAKS
134 #endif
135
136 // hack, should come from an include someplace
137 extern void fgInitVisuals( void );
138 extern void fgReshape( int width, int height );
139 extern void fgRenderFrame( void );
140
141 puFont guiFnt = 0;
142 fntTexFont *guiFntHandle = 0;
143
144 static puMenuBar    *mainMenuBar = 0;
145 //static puButton     *hideMenuButton = 0;
146
147 static puDialogBox  *dialogBox = 0;
148 static puFrame      *dialogFrame = 0;
149 static puText       *dialogBoxMessage = 0;
150 static puOneShot    *dialogBoxOkButton = 0;
151
152
153 static puDialogBox  *YNdialogBox = 0;
154 static puFrame      *YNdialogFrame = 0;
155 static puText       *YNdialogBoxMessage = 0;
156 static puOneShot    *YNdialogBoxOkButton = 0;
157 static puOneShot    *YNdialogBoxNoButton = 0;
158
159 static char msg_OK[]     = "OK";
160 static char msg_NO[]     = "NO";
161 static char msg_YES[]    = "YES";
162 static char msg_CANCEL[] = "Cancel";
163 static char msg_RESET[]  = "Reset";
164
165 char *gui_msg_OK;     // "OK"
166 char *gui_msg_NO;     // "NO"
167 char *gui_msg_YES;    // "YES"
168 char *gui_msg_CANCEL; // "CANCEL"
169 char *gui_msg_RESET;  // "RESET"
170
171 static char global_dialog_string[256];
172
173 // from autopilot.cxx
174 // extern void NewAltitude( puObject *cb );
175 // extern void NewHeading( puObject *cb );
176 // extern void fgAPAdjust( puObject * );
177 // extern void NewTgtAirport( puObject *cb );
178 // bool fgAPTerrainFollowEnabled( void );
179 // bool fgAPAltitudeEnabled( void );
180 // bool fgAPHeadingEnabled( void );
181 // bool fgAPWayPointEnabled( void );
182 // bool fgAPAutoThrottleEnabled( void );
183
184 // from cockpit.cxx
185 extern void fgLatLonFormatToggle( puObject *);
186
187 /* --------------------------------------------------------------------
188 Mouse stuff
189 ---------------------------------------------------------------------*/
190
191 static int _mX = 0;
192 static int _mY = 0;
193 static int _savedX = 0;
194 static int _savedY = 0;
195 static int last_buttons = 0 ;
196 static int mouse_active = 0;
197 static int menu_on = 0;
198 static int mouse_joystick_control = 0;
199
200 //static time_t mouse_off_time;
201 //static int mouse_timed_out;
202
203 // to allow returning to previous view
204 // on second left click in MOUSE_VIEW mode
205 // This has file scope so that it can be reset
206 // if the little rodent is moved  NHV
207 static  int _mVtoggle;
208
209 // we break up the glutGetModifiers return mask
210 // once per loop and stash what we need in these
211 static int glut_active_shift;
212 static int glut_active_ctrl;
213 static int glut_active_alt;
214
215 static float lastquat[4];
216 static float curquat[4];
217 static float _quat0[4];
218 float quat_mat[4][4];
219
220 // uncomment this for view to exactly follow mouse in MOUSE_VIEW mode
221 // else smooth out the view panning to .01 radian per frame
222 // see view_offset smoothing mechanism in main.cxx
223 #define NO_SMOOTH_MOUSE_VIEW
224
225 // uncomment following to
226 #define RESET_VIEW_ON_LEAVING_MOUSE_VIEW
227
228 /* --------------------------------------------------------------------
229 Support for mouse as control yoke (david@megginson.com)
230
231 - right button toggles between pointer and yoke
232 - horizontal drag with no buttons moves ailerons
233 - vertical drag with no buttons moves elevators
234 - horizontal drag with left button moves brakes (left=on)
235 - vertical drag with left button moves throttle (up=more)
236 - horizontal drag with middle button moves rudder
237 - vertical drag with middle button moves trim
238
239 For the *_sensitivity variables, a lower number means more sensitive.
240
241 TODO: figure out how to keep pointer from leaving window in yoke mode.
242 TODO: add thresholds and null zones
243 TODO: sensitivity should be configurable at user option.
244 TODO: allow differential braking (this will be useful if FlightGear
245       ever supports tail-draggers like the DC-3)
246 ---------------------------------------------------------------------*/
247
248 typedef enum {
249     MOUSE_POINTER,
250     MOUSE_YOKE,
251     MOUSE_VIEW
252 } MouseMode;
253
254 MouseMode mouse_mode = MOUSE_POINTER;
255
256 static double aileron_sensitivity = 1.0/500.0;
257 static double elevator_sensitivity = 1.0/500.0;
258 static double brake_sensitivity = 1.0/250.0;
259 static double throttle_sensitivity = 1.0/250.0;
260 static double rudder_sensitivity = 1.0/500.0;
261 static double trim_sensitivity = 1.0/1000.0;
262
263 static inline void Quat0( void ) {
264     curquat[0] = _quat0[0];
265     curquat[1] = _quat0[1];
266     curquat[2] = _quat0[2];
267     curquat[3] = _quat0[3];
268 }
269
270 static inline int left_button( void ) {
271     return( last_buttons & (1 << GLUT_LEFT_BUTTON) );
272 }
273
274 static inline int middle_button( void ) {
275     return( last_buttons & (1 << GLUT_MIDDLE_BUTTON) );
276 }
277
278 static inline int right_button( void ) {
279     return( last_buttons & (1 << GLUT_RIGHT_BUTTON) );
280 }
281
282 static inline void TurnCursorOn( void )
283 {
284     mouse_active = ~0;
285 #if defined(WIN32_CURSOR_TWEAKS)
286     switch (mouse_mode) {
287         case MOUSE_POINTER:
288             glutSetCursor(GLUT_CURSOR_INHERIT);
289             break;
290         case MOUSE_YOKE:
291             glutSetCursor(GLUT_CURSOR_CROSSHAIR);
292             break;
293         case MOUSE_VIEW:
294             glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
295             break;
296     }
297 #endif
298 #if defined(X_CURSOR_TWEAKS)
299     glutWarpPointer( current_view.get_winWidth()/2, current_view.get_winHeight()/2);
300 #endif
301 }
302
303 static inline void TurnCursorOff( void )
304 {
305     mouse_active = 0;
306 #if defined(WIN32_CURSOR_TWEAKS)
307     glutSetCursor(GLUT_CURSOR_NONE);
308 #elif defined(X_CURSOR_TWEAKS)
309     glutWarpPointer( current_view.get_winWidth(), current_view.get_winHeight());
310 #endif
311 }
312
313 void maybeToggleMouse( void )
314 {
315 #if defined(WIN32_CURSOR_TWEAKS)
316     static int first_time = ~0;
317     static int mouse_changed = 0;
318
319     if ( first_time ) {
320         if(!mouse_active) {
321             mouse_changed = ~mouse_changed;
322             TurnCursorOn();
323         }
324     } else {
325         if( mouse_mode != MOUSE_POINTER )
326             return;
327         if( mouse_changed ) {
328             mouse_changed = ~mouse_changed;
329             if(mouse_active) {
330                 TurnCursorOff();
331             }
332         }
333     }
334     first_time = ~first_time;
335 #endif // #ifdef WIN32
336 }
337
338 // Call with FALSE to init and TRUE to restore
339 void BusyCursor( int restore )
340 {
341     static GLenum cursor = (GLenum) 0;
342     if( restore ) {
343         glutSetCursor(cursor);
344     } else {
345         cursor = (GLenum) glutGet( (GLenum) GLUT_WINDOW_CURSOR );
346 #if defined(WIN32_CURSOR_TWEAKS)
347         TurnCursorOn();
348 #endif
349         glutSetCursor( GLUT_CURSOR_WAIT );
350     }
351 }
352
353 int guiGetMouseButton(void)
354 {
355     return last_buttons;
356 }
357
358 void guiGetMouse(int *x, int *y)
359 {
360     *x = _mX;
361     *y = _mY;
362 };
363
364 void guiMotionFunc ( int x, int y )
365 {
366     int ww, wh, need_warp = 0;
367     float W, H;
368     double offset;
369 //  FGTime *t = FGTime::cur_time_params;
370 //  if( mouse_timed_out ) {
371 //      if( t->get_cur_time() > mouse_off_time ) {
372 //          moused_timed_out = 0;
373 //          TurnCursorOn();
374 //          glutPostRedisplay () ;
375 //      }
376 //  }
377
378     if (mouse_mode == MOUSE_POINTER) {
379         puMouse ( x, y ) ;
380         glutPostRedisplay () ;
381     } else {
382         if( x == _mX && y == _mY)
383             return;
384         
385         // reset left click MOUSE_VIEW toggle feature
386         _mVtoggle = 0;
387         
388         ww = current_view.get_winWidth();
389         wh = current_view.get_winHeight();
390         
391         switch (mouse_mode) {
392             case MOUSE_YOKE:
393                 if( !mouse_joystick_control ) {
394                     mouse_joystick_control = 1;
395                     current_options.set_control_mode( fgOPTIONS::FG_MOUSE );
396                 } else {
397                     if ( left_button() ) {
398                         offset = (_mX - x) * brake_sensitivity;
399                         controls.move_brake(FGControls::ALL_WHEELS, offset);
400                         offset = (_mY - y) * throttle_sensitivity;
401                         controls.move_throttle(FGControls::ALL_ENGINES, offset);
402                     } else if ( right_button() ) {
403                         if( ! current_autopilot->get_HeadingEnabled() ) {
404                             offset = (x - _mX) * rudder_sensitivity;
405                             controls.move_rudder(offset);
406                         }
407                         if( ! current_autopilot->get_AltitudeEnabled() ) {
408                             offset = (_mY - y) * trim_sensitivity;
409                             controls.move_elevator_trim(offset);
410                         }
411                     } else {
412                         if( ! current_autopilot->get_HeadingEnabled() ) {
413                             offset = (x - _mX) * aileron_sensitivity;
414                             controls.move_aileron(offset);
415                         }
416                         if( ! current_autopilot->get_AltitudeEnabled() ) {
417                             offset = (_mY - y) * elevator_sensitivity;
418                             controls.move_elevator(offset);
419                         }
420                     }
421                 }
422                 // Keep the mouse in the window.
423                 if (x < 5 || x > ww-5 || y < 5 || y > wh-5) {
424                     x = ww / 2;
425                     y = wh / 2;
426                     need_warp = 1;
427                 }
428                 break;
429                 
430             case MOUSE_VIEW:
431                 if( y <= 0 ) {
432 #define CONTRAINED_MOUSE_VIEW_Y
433 #ifdef CONTRAINED_MOUSE_VIEW_Y
434                     y = 1;
435 #else
436                     y = wh-2;
437 #endif // CONTRAINED_MOUSE_VIEW_Y
438                     need_warp = 1;
439                 } else if( y >= wh-1) {
440 #ifdef CONTRAINED_MOUSE_VIEW_Y
441                     y = wh-2;
442 #else
443                     y = 1;
444 #endif // CONTRAINED_MOUSE_VIEW_Y
445                     need_warp = 1;
446                 }
447                 // wrap MOUSE_VIEW mode cursor x position
448                 if ( x <= 0 ) {
449                     need_warp = 1;
450                     x = ww-2;
451                 } else if ( x >= ww-1 ) {
452                     need_warp = 1;
453                     x = 1;
454                 }
455                 // try to get FG_PI movement in each half of screen
456                 // do spherical pan
457                 W = ww;
458                 H = wh;
459                 if( middle_button() ) {
460                     trackball(lastquat,
461                               (2.0f * _mX - W) / W,
462                               0, //(H - 2.0f * y) / H,         // 3
463                               (2.0f * x - W) / W,
464                               0 //(H - 2.0f * _mY) / H       // 1
465                              );
466                     x = _mX;
467                     y = _mY;
468                     need_warp = 1;
469                 } else {
470                     trackball(lastquat,
471                               0, //(2.0f * _mX - W) / W,  // 0
472                               (H - 2.0f * y) / H,         // 3
473                               0, //(2.0f * x - W) / W,    // 2
474                               (H - 2.0f * _mY) / H        // 1 
475                              );
476                 }
477                 add_quats(lastquat, curquat, curquat);
478                 build_rotmatrix(quat_mat, curquat);
479                 
480                 // do horizontal pan
481                 // this could be done in above quat
482                 // but requires redoing view pipeline
483                 offset = current_view.get_goal_view_offset();
484                 offset += ((_mX - x) * FG_2PI / W );
485                 while (offset < 0.0) {
486                     offset += FG_2PI;
487                 }
488                 while (offset > FG_2PI) {
489                     offset -= FG_2PI;
490                 }
491                 current_view.set_goal_view_offset(offset);
492 #ifdef NO_SMOOTH_MOUSE_VIEW
493                 current_view.set_view_offset(offset);
494 #endif
495                 break;
496             
497             default:
498                 break;
499         }
500     }
501     if( need_warp)
502         glutWarpPointer(x, y);
503     
504     // Record the new mouse position.
505     _mX = x;
506     _mY = y;
507 }
508
509
510 void guiMouseFunc(int button, int updown, int x, int y)
511 {
512     int glutModifiers;
513
514     // private MOUSE_VIEW state variables
515     // to allow alternate left clicks in MOUSE_VIEW mode
516     // to toggle between current offsets and straight ahead
517     // uses _mVtoggle
518     static int _mVx, _mVy, _Vx, _Vy;
519     static float _quat[4];
520     static double _view_offset;
521     
522     // general purpose variables
523     double offset;
524             
525     glutModifiers = glutGetModifiers();
526     glut_active_shift = glutModifiers & GLUT_ACTIVE_SHIFT;
527     glut_active_ctrl  = glutModifiers & GLUT_ACTIVE_CTRL; 
528     glut_active_alt   = glutModifiers & GLUT_ACTIVE_ALT;
529     
530     // Was the left button pressed?
531     if (updown == GLUT_DOWN ) {
532         if( button == GLUT_LEFT_BUTTON)
533         {
534             switch (mouse_mode) {
535                 case MOUSE_POINTER:
536                     break;
537                 case MOUSE_YOKE:
538                     break;
539                 case MOUSE_VIEW:
540                     if(_mVtoggle) {
541                         // resume previous view offsets
542                         _mX = _mVx;
543                         _mY = _mVy;
544                         x = _Vx;
545                         y = _Vy;
546                         curquat[0] = _quat[0];
547                         curquat[1] = _quat[1];
548                         curquat[2] = _quat[2];
549                         curquat[3] = _quat[3];
550                         current_view.set_goal_view_offset(_view_offset);
551 #ifdef NO_SMOOTH_MOUSE_VIEW
552                         current_view.set_view_offset(_view_offset);
553 #endif
554                     } else {
555                         // center view
556                         _mVx = _mX;
557                         _mVy = _mY;
558                         _Vx = x;
559                         _Vy = y;
560                         _quat[0] = curquat[0];
561                         _quat[1] = curquat[1];
562                         _quat[2] = curquat[2];
563                         _quat[3] = curquat[3];
564                         x = current_view.get_winWidth()/2;
565                         y = current_view.get_winHeight()/2;
566                         Quat0();
567                         _view_offset = current_view.get_goal_view_offset();
568                         current_view.set_goal_view_offset(0.0);
569 #ifdef NO_SMOOTH_MOUSE_VIEW
570                         current_view.set_view_offset(0.0);
571 #endif
572                     }
573                     glutWarpPointer( x , y);
574                     build_rotmatrix(quat_mat, curquat);
575                     _mVtoggle = ~_mVtoggle;
576                     break;
577             }
578         }else if ( button == GLUT_RIGHT_BUTTON) {
579             switch (mouse_mode) {
580                 case MOUSE_POINTER:
581                     mouse_mode = MOUSE_YOKE;
582                     mouse_joystick_control = 0;
583                     _savedX = x;
584                     _savedY = y;
585                     // start with zero point in center of screen
586                     _mX = current_view.get_winWidth()/2;
587                     _mY = current_view.get_winHeight()/2;
588                     
589                     // try to have the MOUSE_YOKE position
590                     // reflect the current stick position
591                     offset = controls.get_aileron();
592                     x = _mX - (int)(offset * aileron_sensitivity);
593                     offset = controls.get_elevator();
594                     y = _mY - (int)(offset * elevator_sensitivity);
595                     
596                     glutSetCursor(GLUT_CURSOR_CROSSHAIR);
597                     FG_LOG( FG_INPUT, FG_INFO, "Mouse in yoke mode" );
598                     break;
599                     
600                 case MOUSE_YOKE:
601                     mouse_mode = MOUSE_VIEW;
602                     current_options.set_control_mode( fgOPTIONS::FG_JOYSTICK );
603                     x = current_view.get_winWidth()/2;
604                     y = current_view.get_winHeight()/2;
605                     _mVtoggle = 0;
606                     Quat0();
607                     build_rotmatrix(quat_mat, curquat);
608                     glutSetCursor(GLUT_CURSOR_LEFT_RIGHT);
609                     FG_LOG( FG_INPUT, FG_INFO, "Mouse in view mode" );
610                     break;
611                     
612                 case MOUSE_VIEW:
613                     mouse_mode = MOUSE_POINTER;
614                     x = _savedX;
615                     y = _savedY;
616 #ifdef RESET_VIEW_ON_LEAVING_MOUSE_VIEW
617                     Quat0();
618                     build_rotmatrix(quat_mat, curquat);
619                     current_view.set_goal_view_offset(0.0);
620 #ifdef NO_SMOOTH_MOUSE_VIEW
621                     current_view.set_view_offset(0.0);
622 #endif
623 #endif      // RESET_VIEW_ON_LEAVING_MOUSE_VIEW
624                     glutSetCursor(GLUT_CURSOR_INHERIT);
625                     
626                     if(!menu_on)
627                         TurnCursorOff();
628                     
629                     FG_LOG( FG_INPUT, FG_INFO, "Mouse in pointer mode" );
630                     break;
631             }     
632             glutWarpPointer( x, y );
633         } // END RIGHT BUTTON
634     } // END UPDOWN == GLUT_DOWN
635     
636     // Note which button is pressed.
637     if ( updown == GLUT_DOWN ) {
638         last_buttons |=  ( 1 << button ) ;
639     } else {
640         last_buttons &= ~( 1 << button ) ;
641     }
642     
643     // If we're in pointer mode, let PUI
644     // know what's going on.
645     if (mouse_mode == MOUSE_POINTER) {
646       if (!puMouse (button, updown, x,y)) {
647         current_panel.doMouseAction(button, updown, x, y);
648       }
649     }
650     
651     // Register the new position (if it
652     // hasn't been registered already).
653     _mX = x;
654     _mY = y;
655     
656     glutPostRedisplay ();
657 }
658
659 /* ================ General Purpose Functions ================ */
660
661 // Intercept the Escape Key
662 void ConfirmExitDialog(void)
663 {
664     FG_PUSH_PUI_DIALOG( YNdialogBox );
665 }
666
667 // General Purpose Message Box
668 void mkDialog (const char *txt)
669 {
670     strncpy(global_dialog_string, txt, 256);
671     dialogBoxMessage->setLabel(global_dialog_string);
672     FG_PUSH_PUI_DIALOG( dialogBox );
673 }
674
675 // Repair any damage done to the Panel by other Gui Items
676 void guiFixPanel( void )
677 {
678     int toggle_pause;
679
680     if ( current_options.get_panel_status() ) {
681         FGView *v = &current_view;
682         FGTime *t = FGTime::cur_time_params;
683
684         if( (toggle_pause = !t->getPause()) )
685             t->togglePauseMode();
686
687         if(toggle_pause)
688             t->togglePauseMode();
689     }
690 }
691
692 // Toggle the Menu and Mouse display state
693 void guiToggleMenu(void)
694 {
695     if( menu_on ) {
696         // printf("Hiding Menu\n");
697         mainMenuBar->hide  ();
698 #if defined(WIN32_CURSOR_TWEAKS)
699         if( mouse_mode == MOUSE_POINTER )
700             TurnCursorOff();
701 #endif // #ifdef WIN32_CURSOR_TWEAKS
702     } else {
703         // printf("Showing Menu\n");
704         mainMenuBar->reveal();
705 #ifdef WIN32
706         TurnCursorOn();
707 #endif // #ifdef WIN32
708     }
709     menu_on = ~menu_on;
710 }
711     
712 /* -----------------------------------------------------------------------
713 the Gui callback functions 
714 ____________________________________________________________________*/
715
716 void reInit(puObject *cb)
717 {
718     BusyCursor(0);
719     Quat0();
720     build_rotmatrix(quat_mat, curquat);
721     fgReInitSubsystems();
722     BusyCursor(1);
723 }
724         
725 // This is the accessor function
726 void guiTogglePanel(puObject *cb)
727 {
728     current_options.toggle_panel();
729 }
730     
731 //void MenuHideMenuCb(puObject *cb)
732 void hideMenuCb (puObject *cb)
733 {
734     guiToggleMenu();
735 }
736
737 void goodBye(puObject *)
738 {
739     // FG_LOG( FG_INPUT, FG_ALERT,
740     //      "Program exiting normally at user request." );
741     cout << "Program exiting normally at user request." << endl;
742
743 #ifdef FG_NETWORK_OLK    
744     if ( current_options.get_network_olk() ) {
745         if ( net_is_registered == 0 ) fgd_send_com( "8", FGFS_host);
746     }
747 #endif
748
749     //  if(gps_bug)
750     //      fclose(gps_bug);
751
752     exit(-1);
753 }
754
755
756 void goAwayCb (puObject *me)
757 {
758     FG_POP_PUI_DIALOG( dialogBox );
759 }
760
761 void mkDialogInit (void)
762 {
763     //  printf("mkDialogInit\n");
764     int x = (current_options.get_xsize()/2 - 400/2);
765     int y = (current_options.get_ysize()/2 - 100/2);
766     dialogBox = new puDialogBox (x, y); // 150, 50
767     {
768         dialogFrame = new puFrame (0,0,400,100);
769         dialogBoxMessage  =  new puText         (10, 70);
770         dialogBoxMessage  -> setLabel           ("");
771         dialogBoxOkButton =  new puOneShot      (180, 10, 240, 50);
772         dialogBoxOkButton -> setLegend          (gui_msg_OK);
773         dialogBoxOkButton -> makeReturnDefault  (TRUE );
774         dialogBoxOkButton -> setCallback        (goAwayCb);
775     }
776     FG_FINALIZE_PUI_DIALOG( dialogBox );
777 }
778
779 void MayBeGoodBye(puObject *)
780 {
781     ConfirmExitDialog(); 
782 }
783
784 void goAwayYesNoCb(puObject *me)
785 {
786     FG_POP_PUI_DIALOG( YNdialogBox);
787 }
788
789 void ConfirmExitDialogInit(void)
790 {
791     char msg[] = "Really Quit";
792     char *s;
793
794     //  printf("ConfirmExitDialogInit\n");
795     int len = 200 - puGetStringWidth( puGetDefaultLabelFont(), msg )/2;
796
797     int x = (current_options.get_xsize()/2 - 400/2);
798     int y = (current_options.get_ysize()/2 - 100/2);
799         
800     YNdialogBox = new puDialogBox (x, y); // 150, 50
801     //  YNdialogBox = new puDialogBox (150, 50);
802     {
803         YNdialogFrame = new puFrame (0,0,400, 100);
804         
805         YNdialogBoxMessage  =  new puText         (len, 70);
806         YNdialogBoxMessage  -> setDefaultValue    (msg);
807         YNdialogBoxMessage  -> getDefaultValue    (&s);
808         YNdialogBoxMessage  -> setLabel           (s);
809         
810         YNdialogBoxOkButton =  new puOneShot      (100, 10, 160, 50);
811         YNdialogBoxOkButton -> setLegend          (gui_msg_OK);
812         YNdialogBoxOkButton -> setCallback        (goodBye);
813         
814         YNdialogBoxNoButton =  new puOneShot      (240, 10, 300, 50);
815         YNdialogBoxNoButton -> setLegend          (gui_msg_NO);
816         //      YNdialogBoxNoButton -> makeReturnDefault  (TRUE );
817         YNdialogBoxNoButton -> setCallback        (goAwayYesNoCb);
818     }
819     FG_FINALIZE_PUI_DIALOG( YNdialogBox );
820 }
821
822 void notCb (puObject *)
823 {
824     mkDialog ("This function isn't implemented yet");
825 }
826
827 void helpCb (puObject *)
828 {
829     string command;
830         
831 #if defined(FX) && !defined(WIN32)
832 #  if defined(XMESA_FX_FULLSCREEN) && defined(XMESA_FX_WINDOW)
833     if ( global_fullscreen ) {
834         global_fullscreen = false;
835         XMesaSetFXmode( XMESA_FX_WINDOW );
836     }
837 #  endif
838 #endif
839         
840 #if !defined(WIN32)
841     string url = "http://www.flightgear.org/Docs/InstallGuide/getstart.html";
842         
843     if ( system("xwininfo -name Netscape > /dev/null 2>&1") == 0 ) {
844         command = "netscape -remote \"openURL(" + url + ")\" &";
845     } else {
846         command = "netscape " + url + " &";
847     }
848 #else
849     command = "webrun.bat";
850 #endif
851         
852     system( command.c_str() );
853     //string text = "Help started in netscape window.";
854
855     //mkDialog (text.c_str());
856     mkDialog ("Help started in netscape window.");
857 }
858
859 #if defined( WIN32 ) && !defined( __CYGWIN__)
860
861 static void rotateView( double roll, double pitch, double yaw )
862 {
863         // rotate view
864 }
865
866 static GlBitmap *b1 = NULL;
867 extern FGInterface cur_view_fdm;
868 GLubyte *hiResScreenCapture( int multiplier )
869 {
870         float oldfov = current_options.get_fov();
871         float fov = oldfov / multiplier;
872         FGView *v = &current_view;
873         current_options.set_fov(fov);
874         v->force_update_fov_math();
875     fgInitVisuals();
876     int cur_width = current_view.get_winWidth( );
877     int cur_height = current_view.get_winHeight( );
878         if (b1) delete( b1 );
879         // New empty (mostly) bitmap
880         b1 = new GlBitmap( GL_RGB, 1, 1, (unsigned char *)"123" );
881         int x,y;
882         for ( y = 0; y < multiplier; y++ )
883         {
884                 for ( x = 0; x < multiplier; x++ )
885                 {
886                         fgReshape( cur_width, cur_height );
887                         // pan to tile
888                         rotateView( 0, (y*fov)-((multiplier-1)*fov/2), (x*fov)-((multiplier-1)*fov/2) );
889                         fgRenderFrame();
890                         // restore view
891                         GlBitmap b2;
892                         b1->copyBitmap( &b2, cur_width*x, cur_height*y );
893                 }
894         }
895         current_view.UpdateViewParams(cur_view_fdm);
896         current_options.set_fov(oldfov);
897         v->force_update_fov_math();
898         return b1->getBitmap();
899 }
900 #endif
901
902
903 #if defined( WIN32 ) && !defined( __CYGWIN__)
904 // win32 print screen function
905 void printScreen ( puObject *obj ) {
906     bool show_pu_cursor = false;
907     TurnCursorOff();
908     if ( !puCursorIsHidden() ) {
909         show_pu_cursor = true;
910         puHideCursor();
911     }
912     BusyCursor( 0 );
913     mainMenuBar->hide();
914
915     CGlPrinter p( CGlPrinter::PRINT_BITMAP );
916     int cur_width = current_view.get_winWidth( );
917     int cur_height = current_view.get_winHeight( );
918     p.Begin( "FlightGear", cur_width*3, cur_height*3 );
919         p.End( hiResScreenCapture(3) );
920
921     if( menu_on ) {
922         mainMenuBar->reveal();
923     }
924     BusyCursor(1);
925     if ( show_pu_cursor ) {
926         puShowCursor();
927     }
928     TurnCursorOn();
929 }
930 #endif // #ifdef WIN32
931
932
933 void dumpSnapShot ( puObject *obj ) {
934     fgDumpSnapShot();
935 }
936
937
938 // do a screen snap shot
939 void fgDumpSnapShot () {
940     bool show_pu_cursor = false;
941
942     mainMenuBar->hide();
943     TurnCursorOff();
944     if ( !puCursorIsHidden() ) {
945         show_pu_cursor = true;
946         puHideCursor();
947     }
948
949     fgInitVisuals();
950     fgReshape( current_options.get_xsize(), current_options.get_ysize() );
951
952     // we need two render frames here to clear the menu and cursor
953     // ... not sure why but doing an extra fgFenderFrame() shoulnd't
954     // hurt anything
955     fgRenderFrame();
956     fgRenderFrame();
957
958     my_glDumpWindow( "fgfs-screen.ppm", 
959                      current_options.get_xsize(), 
960                      current_options.get_ysize() );
961     
962     mkDialog ("Snap shot saved to fgfs-screen.ppm");
963
964     if ( show_pu_cursor ) {
965         puShowCursor();
966     }
967
968     TurnCursorOn();
969     if( menu_on ) {
970         mainMenuBar->reveal();
971     }
972
973 }
974
975
976 /// The beginnings of teleportation :-)
977 //  Needs cleaning up but works
978 //  These statics should disapear when this is a class
979 static puDialogBox     *AptDialog = 0;
980 static puFrame         *AptDialogFrame = 0;
981 static puText          *AptDialogMessage = 0;
982 static puInput         *AptDialogInput = 0;
983
984 static char NewAirportId[16];
985 static char NewAirportLabel[] = "Enter New Airport ID"; 
986
987 static puOneShot       *AptDialogOkButton = 0;
988 static puOneShot       *AptDialogCancelButton = 0;
989 static puOneShot       *AptDialogResetButton = 0;
990
991 void AptDialog_Cancel(puObject *)
992 {
993     FG_POP_PUI_DIALOG( AptDialog );
994 }
995
996 void AptDialog_OK (puObject *)
997 {
998     FGPath path( current_options.get_fg_root() );
999     path.append( "Airports" );
1000     path.append( "simple.gdbm" );
1001     FGAirports airports( path.c_str() );
1002     FGAirport a;
1003     
1004     FGTime *t = FGTime::cur_time_params;
1005     int PauseMode = t->getPause();
1006     if(!PauseMode)
1007         t->togglePauseMode();
1008
1009     char *s;
1010     AptDialogInput->getValue(&s);
1011     string AptId(s);
1012
1013         cout << "AptDialog_OK " << AptId << " " << AptId.length() << endl;
1014     
1015     AptDialog_Cancel( NULL );
1016     
1017     if ( AptId.length() ) {
1018         // set initial position from airport id
1019         FG_LOG( FG_GENERAL, FG_INFO,
1020                 "Attempting to set starting position from airport code "
1021                 << AptId );
1022
1023         if ( airports.search( AptId, &a ) )
1024         {
1025             current_options.set_airport_id( AptId.c_str() );
1026             BusyCursor(0);
1027             fgReInitSubsystems();
1028             BusyCursor(1);
1029         } else {
1030             AptId  += " not in database.";
1031             mkDialog(AptId.c_str());
1032         }
1033     }
1034     if( PauseMode != t->getPause() )
1035         t->togglePauseMode();
1036 }
1037
1038 void AptDialog_Reset(puObject *)
1039 {
1040     //  strncpy( NewAirportId, current_options.get_airport_id().c_str(), 16 );
1041     sprintf( NewAirportId, "%s", current_options.get_airport_id().c_str() );
1042     AptDialogInput->setValue ( NewAirportId );
1043     AptDialogInput->setCursor( 0 ) ;
1044 }
1045
1046 void NewAirport(puObject *cb)
1047 {
1048     //  strncpy( NewAirportId, current_options.get_airport_id().c_str(), 16 );
1049     sprintf( NewAirportId, "%s", current_options.get_airport_id().c_str() );
1050 //      cout << "NewAirport " << NewAirportId << endl;
1051     AptDialogInput->setValue( NewAirportId );
1052
1053     FG_PUSH_PUI_DIALOG( AptDialog );
1054 }
1055
1056 static void NewAirportInit(void)
1057 {
1058     sprintf( NewAirportId, "%s", current_options.get_airport_id().c_str() );
1059     int len = 150 - puGetStringWidth( puGetDefaultLabelFont(),
1060                                       NewAirportLabel ) / 2;
1061
1062     AptDialog = new puDialogBox (150, 50);
1063     {
1064         AptDialogFrame   = new puFrame           (0,0,350, 150);
1065         AptDialogMessage = new puText            (len, 110);
1066         AptDialogMessage ->    setLabel          (NewAirportLabel);
1067
1068         AptDialogInput   = new puInput           (50, 70, 300, 100);
1069         AptDialogInput   ->    setValue          (NewAirportId);
1070         AptDialogInput   ->    acceptInput();
1071
1072         AptDialogOkButton     =  new puOneShot   (50, 10, 110, 50);
1073         AptDialogOkButton     ->     setLegend   (gui_msg_OK);
1074         AptDialogOkButton     ->     setCallback (AptDialog_OK);
1075         AptDialogOkButton     ->     makeReturnDefault(TRUE);
1076
1077         AptDialogCancelButton =  new puOneShot   (140, 10, 210, 50);
1078         AptDialogCancelButton ->     setLegend   (gui_msg_CANCEL);
1079         AptDialogCancelButton ->     setCallback (AptDialog_Cancel);
1080
1081         AptDialogResetButton  =  new puOneShot   (240, 10, 300, 50);
1082         AptDialogResetButton  ->     setLegend   (gui_msg_RESET);
1083         AptDialogResetButton  ->     setCallback (AptDialog_Reset);
1084     }
1085         cout << "NewAirportInit " << NewAirportId << endl;
1086     FG_FINALIZE_PUI_DIALOG( AptDialog );
1087 }
1088
1089 #ifdef FG_NETWORK_OLK
1090
1091 /// The beginnings of networking :-)
1092 //  Needs cleaning up but works
1093 //  These statics should disapear when this is a class
1094 static puDialogBox     *NetIdDialog = 0;
1095 static puFrame         *NetIdDialogFrame = 0;
1096 static puText          *NetIdDialogMessage = 0;
1097 static puInput         *NetIdDialogInput = 0;
1098
1099 static char NewNetId[16];
1100 static char NewNetIdLabel[] = "Enter New Callsign"; 
1101 extern char *fgd_callsign;
1102
1103 static puOneShot       *NetIdDialogOkButton = 0;
1104 static puOneShot       *NetIdDialogCancelButton = 0;
1105
1106 void NetIdDialog_Cancel(puObject *)
1107 {
1108     FG_POP_PUI_DIALOG( NetIdDialog );
1109 }
1110
1111 void NetIdDialog_OK (puObject *)
1112 {
1113     string NetId;
1114     
1115     FGTime *t = FGTime::cur_time_params;
1116     int PauseMode = t->getPause();
1117     if(!PauseMode)
1118         t->togglePauseMode();
1119 /*  
1120    The following needs some cleanup because 
1121    "string options.NetId" and "char *net_callsign" 
1122 */
1123     NetIdDialogInput->getValue(&net_callsign);
1124     NetId = net_callsign;
1125     
1126     NetIdDialog_Cancel( NULL );
1127     current_options.set_net_id( NetId.c_str() );
1128     strcpy( fgd_callsign, net_callsign);
1129 //    strcpy( fgd_callsign, current_options.get_net_id().c_str());
1130 /* Entering a callsign indicates : user wants Net HUD Info */
1131     net_hud_display = 1;
1132
1133     if( PauseMode != t->getPause() )
1134         t->togglePauseMode();
1135 }
1136
1137 void NewCallSign(puObject *cb)
1138 {
1139     sprintf( NewNetId, "%s", current_options.get_net_id().c_str() );
1140 //    sprintf( NewNetId, "%s", fgd_callsign );
1141     NetIdDialogInput->setValue( NewNetId );
1142
1143     FG_PUSH_PUI_DIALOG( NetIdDialog );
1144 }
1145
1146 static void NewNetIdInit(void)
1147 {
1148     sprintf( NewNetId, "%s", current_options.get_net_id().c_str() );
1149 //    sprintf( NewNetId, "%s", fgd_callsign );
1150     int len = 150 - puGetStringWidth( puGetDefaultLabelFont(),
1151                                       NewNetIdLabel ) / 2;
1152
1153     NetIdDialog = new puDialogBox (150, 50);
1154     {
1155         NetIdDialogFrame   = new puFrame           (0,0,350, 150);
1156         NetIdDialogMessage = new puText            (len, 110);
1157         NetIdDialogMessage ->    setLabel          (NewNetIdLabel);
1158
1159         NetIdDialogInput   = new puInput           (50, 70, 300, 100);
1160         NetIdDialogInput   ->    setValue          (NewNetId);
1161         NetIdDialogInput   ->    acceptInput();
1162
1163         NetIdDialogOkButton     =  new puOneShot   (50, 10, 110, 50);
1164         NetIdDialogOkButton     ->     setLegend   (gui_msg_OK);
1165         NetIdDialogOkButton     ->     setCallback (NetIdDialog_OK);
1166         NetIdDialogOkButton     ->     makeReturnDefault(TRUE);
1167
1168         NetIdDialogCancelButton =  new puOneShot   (240, 10, 300, 50);
1169         NetIdDialogCancelButton ->     setLegend   (gui_msg_CANCEL);
1170         NetIdDialogCancelButton ->     setCallback (NetIdDialog_Cancel);
1171
1172     }
1173     FG_FINALIZE_PUI_DIALOG( NetIdDialog );
1174 }
1175
1176 static void net_display_toggle( puObject *cb)
1177 {
1178         net_hud_display = (net_hud_display) ? 0 : 1;
1179         printf("Toggle net_hud_display : %d\n", net_hud_display);
1180 }
1181
1182 static void net_register( puObject *cb)
1183 {
1184         fgd_send_com( "1", FGFS_host );
1185         net_is_registered = 0;
1186         printf("Registering to deamon\n");
1187 }
1188
1189 static void net_unregister( puObject *cb)
1190 {
1191         fgd_send_com( "8", FGFS_host );
1192         net_is_registered = -1;
1193         printf("Unregistering from deamon\n");
1194 }
1195
1196
1197 /*************** Deamon communication **********/
1198
1199 //  These statics should disapear when this is a class
1200 static puDialogBox     *NetFGDDialog = 0;
1201 static puFrame         *NetFGDDialogFrame = 0;
1202 static puText          *NetFGDDialogMessage = 0;
1203 //static puInput         *NetFGDDialogInput = 0;
1204
1205 //static char NewNetId[16];
1206 static char NewNetFGDLabel[] = "Scan for deamon                        "; 
1207 static char NewFGDHost[64] = "olk.mcp.de"; 
1208 static int  NewFGDPortLo = 10000;
1209 static int  NewFGDPortHi = 10001;
1210  
1211 //extern char *fgd_callsign;
1212 extern u_short base_port, end_port;
1213 extern int fgd_ip, verbose, current_port;
1214 extern char *fgd_host;
1215
1216
1217 static puOneShot       *NetFGDDialogOkButton = 0;
1218 static puOneShot       *NetFGDDialogCancelButton = 0;
1219 static puOneShot       *NetFGDDialogScanButton = 0;
1220
1221 static puInput         *NetFGDHostDialogInput = 0;
1222 static puInput         *NetFGDPortLoDialogInput = 0;
1223 static puInput         *NetFGDPortHiDialogInput = 0;
1224
1225 void NetFGDDialog_Cancel(puObject *)
1226 {
1227     FG_POP_PUI_DIALOG( NetFGDDialog );
1228 }
1229
1230 void NetFGDDialog_OK (puObject *)
1231 {
1232     char *NetFGD;    
1233
1234     FGTime *t = FGTime::cur_time_params;
1235     int PauseMode = t->getPause();
1236     if(!PauseMode) t->togglePauseMode();
1237     NetFGDHostDialogInput->getValue( &NetFGD );
1238     strcpy( fgd_host, NetFGD);
1239     NetFGDPortLoDialogInput->getValue( (int *) &base_port );
1240     NetFGDPortHiDialogInput->getValue( (int *) &end_port );
1241     NetFGDDialog_Cancel( NULL );
1242     if( PauseMode != t->getPause() )
1243         t->togglePauseMode();
1244 }
1245
1246 void NetFGDDialog_SCAN (puObject *)
1247 {
1248     char *NetFGD;
1249     int fgd_port;
1250     
1251     FGTime *t = FGTime::cur_time_params;
1252     int PauseMode = t->getPause();
1253     if(!PauseMode) t->togglePauseMode();
1254 //    printf("Vor getvalue %s\n");
1255     NetFGDHostDialogInput->getValue( &NetFGD );
1256 //    printf("Vor strcpy %s\n", (char *) NetFGD);
1257     strcpy( fgd_host, NetFGD);
1258     NetFGDPortLoDialogInput->getValue( (int *) &base_port );
1259     NetFGDPortHiDialogInput->getValue( (int *) &end_port );
1260     printf("FGD: %s  Port-Start: %d Port-End: %d\n", fgd_host, 
1261                  base_port, end_port);
1262     net_resolv_fgd(fgd_host);
1263     printf("Resolve : %d\n", net_r);
1264     if( PauseMode != t->getPause() )  t->togglePauseMode();
1265     if ( net_r == 0 ) {
1266       fgd_port = 10000;
1267       strcpy( fgd_name, "");
1268       for( current_port = base_port; ( current_port <= end_port); current_port++) {
1269           fgd_send_com("0" , FGFS_host);
1270           sprintf( NewNetFGDLabel , "Scanning for deamon Port: %d", current_port); 
1271           printf("FGD: searching %s\n", fgd_name);
1272           if ( strcmp( fgd_name, "") != 0 ) {
1273              sprintf( NewNetFGDLabel , "Found %s at Port: %d", 
1274                                               fgd_name, current_port);
1275              fgd_port = current_port;
1276              current_port = end_port+1;
1277           }
1278       }
1279       current_port = end_port = base_port = fgd_port;
1280     }
1281     NetFGDDialog_Cancel( NULL );
1282 }
1283
1284
1285 void net_fgd_scan(puObject *cb)
1286 {
1287     NewFGDPortLo = base_port;
1288     NewFGDPortHi = end_port;
1289     strcpy( NewFGDHost, fgd_host);
1290     NetFGDPortLoDialogInput->setValue( NewFGDPortLo );
1291     NetFGDPortHiDialogInput->setValue( NewFGDPortHi );
1292     NetFGDHostDialogInput->setValue( NewFGDHost );
1293
1294     FG_PUSH_PUI_DIALOG( NetFGDDialog );
1295 }
1296
1297
1298 static void NewNetFGDInit(void)
1299 {
1300 //    sprintf( NewNetId, "%s", current_options.get_net_id().c_str() );
1301 //    sprintf( NewNetId, "%s", fgd_callsign );
1302     int len = 170 - puGetStringWidth( puGetDefaultLabelFont(),
1303                                       NewNetFGDLabel ) / 2;
1304
1305     NetFGDDialog = new puDialogBox (310, 30);
1306     {
1307         NetFGDDialogFrame   = new puFrame           (0,0,320, 170);
1308         NetFGDDialogMessage = new puText            (len, 140);
1309         NetFGDDialogMessage ->    setLabel          (NewNetFGDLabel);
1310
1311         NetFGDPortLoDialogInput   = new puInput           (50, 70, 127, 100);
1312         NetFGDPortLoDialogInput   ->    setValue          (NewFGDPortLo);
1313         NetFGDPortLoDialogInput   ->    acceptInput();
1314
1315         NetFGDPortHiDialogInput   = new puInput           (199, 70, 275, 100);
1316         NetFGDPortHiDialogInput   ->    setValue          (NewFGDPortHi);
1317         NetFGDPortHiDialogInput   ->    acceptInput();
1318
1319         NetFGDHostDialogInput   = new puInput           (50, 100, 275, 130);
1320         NetFGDHostDialogInput   ->    setValue          (NewFGDHost);
1321         NetFGDHostDialogInput   ->    acceptInput();
1322
1323         NetFGDDialogScanButton     =  new puOneShot   (130, 10, 200, 50);
1324         NetFGDDialogScanButton     ->     setLegend   ("Scan");
1325         NetFGDDialogScanButton     ->     setCallback (NetFGDDialog_SCAN);
1326         NetFGDDialogScanButton     ->     makeReturnDefault(FALSE);
1327
1328         NetFGDDialogOkButton     =  new puOneShot   (50, 10, 120, 50);
1329         NetFGDDialogOkButton     ->     setLegend   (gui_msg_OK);
1330         NetFGDDialogOkButton     ->     setCallback (NetFGDDialog_OK);
1331         NetFGDDialogOkButton     ->     makeReturnDefault(TRUE);
1332
1333         NetFGDDialogCancelButton =  new puOneShot   (210, 10, 280, 50);
1334         NetFGDDialogCancelButton ->     setLegend   (gui_msg_CANCEL);
1335         NetFGDDialogCancelButton ->     setCallback (NetFGDDialog_Cancel);
1336
1337     }
1338     FG_FINALIZE_PUI_DIALOG( NetFGDDialog );
1339 }
1340
1341 /*
1342 static void net_display_toggle( puObject *cb)
1343 {
1344         net_hud_display = (net_hud_display) ? 0 : 1;
1345         printf("Toggle net_hud_display : %d\n", net_hud_display);
1346 }
1347
1348 */
1349
1350 #endif
1351
1352 /***************  End Networking  **************/
1353
1354
1355
1356 /* -----------------------------------------------------------------------
1357 The menu stuff 
1358 ---------------------------------------------------------------------*/
1359 char *fileSubmenu               [] = {
1360     "Exit", /* "Close", "---------", */
1361 #if defined( WIN32 ) && !defined( __CYGWIN__)
1362     "Print",
1363 #endif
1364     "Snap Shot",
1365     /* "---------", "Save", */ 
1366     "Reset", NULL
1367 };
1368 puCallback fileSubmenuCb        [] = {
1369     MayBeGoodBye, /* hideMenuCb, NULL, */
1370 #if defined( WIN32 ) && !defined( __CYGWIN__)
1371     printScreen, 
1372 #endif
1373     /* NULL, notCb, */
1374     dumpSnapShot,
1375     reInit, NULL
1376 };
1377
1378 /*
1379 char *editSubmenu               [] = {
1380     "Edit text", NULL
1381 };
1382 puCallback editSubmenuCb        [] = {
1383     notCb, NULL
1384 };
1385 */
1386
1387 char *viewSubmenu               [] = {
1388     /* "Cockpit View > ", "View >","------------", */ "Toggle Panel...", NULL
1389 };
1390 puCallback viewSubmenuCb        [] = {
1391     /* notCb, notCb, NULL, */ guiTogglePanel, NULL
1392 };
1393
1394 char *aircraftSubmenu           [] = {
1395     "Autopilot", "Heading", "Altitude", "Navigation", "Airport", 
1396     /* "Communication", */ NULL
1397 };
1398 puCallback aircraftSubmenuCb    [] = {
1399     fgAPAdjust, NewHeading, NewAltitude, fgLatLonFormatToggle, NewTgtAirport, 
1400     /* notCb, */ NULL
1401 };
1402
1403 char *environmentSubmenu        [] = {
1404     "Airport", /* "Terrain", "Weather", */ NULL
1405 };
1406 puCallback environmentSubmenuCb [] = {
1407     NewAirport, /* notCb, notCb, */ NULL
1408 };
1409
1410 /*
1411 char *optionsSubmenu            [] = {
1412     "Preferences", "Realism & Reliablity...", NULL
1413 };
1414 puCallback optionsSubmenuCb     [] = {
1415     notCb, notCb, NULL
1416 };
1417 */
1418
1419 #ifdef FG_NETWORK_OLK
1420 char *networkSubmenu            [] = {
1421     "Unregister from FGD ", /* "Send MSG to All", "Send MSG", "Show Pilots", */
1422     "Register to FGD",
1423     "Scan for Deamons", "Enter Callsign", /* "Display Netinfos", */
1424     "Toggle Display", NULL
1425 };
1426 puCallback networkSubmenuCb     [] = {
1427     /* notCb, notCb, notCb, notCb, */ 
1428     net_unregister, 
1429     net_register, 
1430     net_fgd_scan, NewCallSign, 
1431     net_display_toggle, NULL
1432 };
1433 #endif
1434
1435 char *helpSubmenu               [] = {
1436     /* "About...", */ "Help", NULL
1437 };
1438 puCallback helpSubmenuCb        [] = {
1439     /* notCb, */ helpCb, NULL
1440 };
1441
1442
1443 /* -------------------------------------------------------------------------
1444 init the gui
1445 _____________________________________________________________________*/
1446
1447
1448 void guiInit()
1449 {
1450     char *mesa_win_state;
1451
1452     // Initialize PUI
1453     puInit();
1454     puSetDefaultStyle         ( PUSTYLE_SMALL_BEVELLED ); //PUSTYLE_DEFAULT
1455     puSetDefaultColourScheme  (0.8, 0.8, 0.8, 0.4);
1456
1457     // Initialize our GLOBAL GUI STRINGS
1458     gui_msg_OK     = msg_OK;     // "OK"
1459     gui_msg_NO     = msg_NO;     // "NO"
1460     gui_msg_YES    = msg_YES;    // "YES"
1461     gui_msg_CANCEL = msg_CANCEL; // "CANCEL"
1462     gui_msg_RESET  = msg_RESET;  // "RESET"
1463
1464     // Next check home directory
1465     FGPath fntpath;
1466     char* envp = ::getenv( "FG_FONTS" );
1467     if ( envp != NULL ) {
1468         fntpath.set( envp );
1469     } else {
1470         fntpath.set( current_options.get_fg_root() );
1471         fntpath.append( "Fonts" );
1472     }
1473
1474     // Install our fast fonts
1475     fntpath.append( "typewriter.txf" );
1476     guiFntHandle = new fntTexFont ;
1477     guiFntHandle -> load ( (char *)fntpath.c_str() ) ;
1478     puFont GuiFont ( guiFntHandle, 15 ) ;
1479     puSetDefaultFonts( GuiFont, GuiFont ) ;
1480     guiFnt = puGetDefaultLabelFont();
1481   
1482     if ( current_options.get_mouse_pointer() == 0 ) {
1483         // no preference specified for mouse pointer, attempt to autodetect...
1484         // Determine if we need to render the cursor, or if the windowing
1485         // system will do it.  First test if we are rendering with glide.
1486         if ( strstr ( general.get_glRenderer(), "Glide" ) ) {
1487             // Test for the MESA_GLX_FX env variable
1488             if ( (mesa_win_state = getenv( "MESA_GLX_FX" )) != NULL) {
1489                 // test if we are fullscreen mesa/glide
1490                 if ( (mesa_win_state[0] == 'f') ||
1491                      (mesa_win_state[0] == 'F') ) {
1492                     puShowCursor ();
1493                 }
1494             }
1495         }
1496         mouse_active = ~mouse_active;
1497     } else if ( current_options.get_mouse_pointer() == 1 ) {
1498         // don't show pointer
1499     } else if ( current_options.get_mouse_pointer() == 2 ) {
1500         // force showing pointer
1501         puShowCursor();
1502         mouse_active = ~mouse_active;
1503     }
1504         
1505     // MOUSE_VIEW mode stuff
1506     trackball(_quat0, 0.0, 0.0, 0.0, 0.0);  
1507     Quat0();
1508     build_rotmatrix(quat_mat, curquat);
1509
1510     // Set up our Dialog Boxes
1511     ConfirmExitDialogInit();
1512     NewAirportInit();
1513 #ifdef FG_NETWORK_OLK
1514     NewNetIdInit();
1515     NewNetFGDInit();
1516 #endif
1517     mkDialogInit();
1518     
1519     // Make the menu bar
1520     mainMenuBar = new puMenuBar ();
1521     mainMenuBar -> add_submenu ("File", fileSubmenu, fileSubmenuCb);
1522     // mainMenuBar -> add_submenu ("Edit", editSubmenu, editSubmenuCb);
1523     mainMenuBar -> add_submenu ("View", viewSubmenu, viewSubmenuCb);
1524     mainMenuBar -> add_submenu ("Aircraft", aircraftSubmenu, aircraftSubmenuCb);
1525     mainMenuBar -> add_submenu ("Environment", environmentSubmenu, environmentSubmenuCb);
1526     // mainMenuBar -> add_submenu ("Options", optionsSubmenu, optionsSubmenuCb);
1527 #ifdef FG_NETWORK_OLK
1528     mainMenuBar -> add_submenu ("Network", networkSubmenu, networkSubmenuCb);
1529 #endif
1530     mainMenuBar -> add_submenu ("Help", helpSubmenu, helpSubmenuCb);
1531     mainMenuBar-> close ();
1532     // Set up menu bar toggle
1533     menu_on = ~0;
1534 }
1535
1536
1537
1538 /*
1539  * Trackball code:
1540  *
1541  * Implementation of a virtual trackball.
1542  * Implemented by Gavin Bell, lots of ideas from Thant Tessman and
1543  *   the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129.
1544  *
1545  * Vector manip code:
1546  *
1547  * Original code from:
1548  * David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli
1549  *
1550  * Much mucking with by:
1551  * Gavin Bell
1552  */
1553 #if defined(_WIN32) && !defined( __CYGWIN32__ )
1554 #pragma warning (disable:4244)          /* disable bogus conversion warnings */
1555 #endif
1556 #include <math.h>
1557 #include <stdio.h>
1558 //#include "trackball.h"
1559
1560 /*
1561  * This size should really be based on the distance from the center of
1562  * rotation to the point on the object underneath the mouse.  That
1563  * point would then track the mouse as closely as possible.  This is a
1564  * simple example, though, so that is left as an Exercise for the
1565  * Programmer.
1566  */
1567 #define TRACKBALLSIZE  (0.8f)
1568 #define SQRT(x) sqrt(x)
1569
1570 /*
1571  * Local function prototypes (not defined in trackball.h)
1572  */
1573 static float tb_project_to_sphere(float, float, float);
1574 static void normalize_quat(float [4]);
1575
1576 static void
1577 vzero(float *v)
1578 {
1579     v[0] = 0.0;
1580     v[1] = 0.0;
1581     v[2] = 0.0;
1582 }
1583
1584 static void
1585 vset(float *v, float x, float y, float z)
1586 {
1587     v[0] = x;
1588     v[1] = y;
1589     v[2] = z;
1590 }
1591
1592 static void
1593 vsub(const float *src1, const float *src2, float *dst)
1594 {
1595     dst[0] = src1[0] - src2[0];
1596     dst[1] = src1[1] - src2[1];
1597     dst[2] = src1[2] - src2[2];
1598 }
1599
1600 static void
1601 vcopy(const float *v1, float *v2)
1602 {
1603     register int i;
1604     for (i = 0 ; i < 3 ; i++)
1605         v2[i] = v1[i];
1606 }
1607
1608 static void
1609 vcross(const float *v1, const float *v2, float *cross)
1610 {
1611     float temp[3];
1612
1613     temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]);
1614     temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]);
1615     temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]);
1616     vcopy(temp, cross);
1617 }
1618
1619 static float
1620 vlength(const float *v)
1621 {
1622     float tmp = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
1623     return SQRT(tmp);
1624 }
1625
1626 static void
1627 vscale(float *v, float div)
1628 {
1629     v[0] *= div;
1630     v[1] *= div;
1631     v[2] *= div;
1632 }
1633
1634 static void
1635 vnormal(float *v)
1636 {
1637     vscale(v,1.0/vlength(v));
1638 }
1639
1640 static float
1641 vdot(const float *v1, const float *v2)
1642 {
1643     return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
1644 }
1645
1646 static void
1647 vadd(const float *src1, const float *src2, float *dst)
1648 {
1649     dst[0] = src1[0] + src2[0];
1650     dst[1] = src1[1] + src2[1];
1651     dst[2] = src1[2] + src2[2];
1652 }
1653
1654 /*
1655  *  Given an axis and angle, compute quaternion.
1656  */
1657 void
1658 axis_to_quat(float a[3], float phi, float q[4])
1659 {
1660     double sinphi2, cosphi2;
1661     double phi2 = phi/2.0;
1662     sinphi2 = sin(phi2);
1663     cosphi2 = cos(phi2);
1664     vnormal(a);
1665     vcopy(a,q);
1666     vscale(q,sinphi2);
1667     q[3] = cosphi2;
1668 }
1669
1670 /*
1671  * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet
1672  * if we are away from the center of the sphere.
1673  */
1674 static float
1675 tb_project_to_sphere(float r, float x, float y)
1676 {
1677     float d, t, z, tmp;
1678
1679     tmp = x*x + y*y;
1680     d = SQRT(tmp);
1681     if (d < r * 0.70710678118654752440) {    /* Inside sphere */
1682         tmp = r*r - d*d;
1683         z = SQRT(tmp);
1684     } else {           /* On hyperbola */
1685         t = r / 1.41421356237309504880;
1686         z = t*t / d;
1687     }
1688     return z;
1689 }
1690
1691 /*
1692  * Quaternions always obey:  a^2 + b^2 + c^2 + d^2 = 1.0
1693  * If they don't add up to 1.0, dividing by their magnitued will
1694  * renormalize them.
1695  *
1696  * Note: See the following for more information on quaternions:
1697  *
1698  * - Shoemake, K., Animating rotation with quaternion curves, Computer
1699  *   Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985.
1700  * - Pletinckx, D., Quaternion calculus as a basic tool in computer
1701  *   graphics, The Visual Computer 5, 2-13, 1989.
1702  */
1703 static void
1704 normalize_quat(float q[4])
1705 {
1706     int i;
1707     float mag, tmp;
1708
1709     tmp = q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3];
1710     mag = 1.0 / SQRT(tmp);
1711     for (i = 0; i < 4; i++)
1712         q[i] *= mag;
1713 }
1714
1715 /*
1716  * Ok, simulate a track-ball.  Project the points onto the virtual
1717  * trackball, then figure out the axis of rotation, which is the cross
1718  * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0)
1719  * Note:  This is a deformed trackball-- is a trackball in the center,
1720  * but is deformed into a hyperbolic sheet of rotation away from the
1721  * center.  This particular function was chosen after trying out
1722  * several variations.
1723  *
1724  * It is assumed that the arguments to this routine are in the range
1725  * (-1.0 ... 1.0)
1726  */
1727 void
1728 trackball(float q[4], float p1x, float p1y, float p2x, float p2y)
1729 {
1730     float a[3]; /* Axis of rotation */
1731     float phi;  /* how much to rotate about axis */
1732     float p1[3], p2[3], d[3];
1733     float t;
1734
1735     if (p1x == p2x && p1y == p2y) {
1736         /* Zero rotation */
1737         vzero(q);
1738         q[3] = 1.0;
1739         return;
1740     }
1741
1742     /*
1743      * First, figure out z-coordinates for projection of P1 and P2 to
1744      * deformed sphere
1745      */
1746     vset(p1,p1x,p1y,tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y));
1747     vset(p2,p2x,p2y,tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y));
1748
1749     /*
1750      *  Now, we want the cross product of P1 and P2
1751      */
1752     vcross(p2,p1,a);
1753
1754     /*
1755      *  Figure out how much to rotate around that axis.
1756      */
1757     vsub(p1,p2,d);
1758     t = vlength(d) / (2.0*TRACKBALLSIZE);
1759
1760     /*
1761      * Avoid problems with out-of-control values...
1762      */
1763     if (t > 1.0) t = 1.0;
1764     if (t < -1.0) t = -1.0;
1765     phi = 2.0 * asin(t);
1766
1767     axis_to_quat(a,phi,q);
1768 }
1769
1770 /*
1771  * Given two rotations, e1 and e2, expressed as quaternion rotations,
1772  * figure out the equivalent single rotation and stuff it into dest.
1773  *
1774  * This routine also normalizes the result every RENORMCOUNT times it is
1775  * called, to keep error from creeping in.
1776  *
1777  * NOTE: This routine is written so that q1 or q2 may be the same
1778  * as dest (or each other).
1779  */
1780
1781 #define RENORMCOUNT 97
1782
1783 void
1784 add_quats(float q1[4], float q2[4], float dest[4])
1785 {
1786     static int count=0;
1787     float t1[4], t2[4], t3[4];
1788     float tf[4];
1789
1790 #if 0
1791 printf("q1 = %f %f %f %f\n", q1[0], q1[1], q1[2], q1[3]);
1792 printf("q2 = %f %f %f %f\n", q2[0], q2[1], q2[2], q2[3]);
1793 #endif
1794
1795     vcopy(q1,t1);
1796     vscale(t1,q2[3]);
1797
1798     vcopy(q2,t2);
1799     vscale(t2,q1[3]);
1800
1801     vcross(q2,q1,t3);
1802     vadd(t1,t2,tf);
1803     vadd(t3,tf,tf);
1804     tf[3] = q1[3] * q2[3] - vdot(q1,q2);
1805
1806 #if 0
1807 printf("tf = %f %f %f %f\n", tf[0], tf[1], tf[2], tf[3]);
1808 #endif
1809
1810     dest[0] = tf[0];
1811     dest[1] = tf[1];
1812     dest[2] = tf[2];
1813     dest[3] = tf[3];
1814
1815     if (++count > RENORMCOUNT) {
1816         count = 0;
1817         normalize_quat(dest);
1818     }
1819 }
1820
1821 /*
1822  * Build a rotation matrix, given a quaternion rotation.
1823  *
1824  */
1825 void
1826 build_rotmatrix(float m[4][4], float q[4])
1827 {
1828 //#define TRANSPOSED_QUAT
1829 #ifndef TRANSPOSED_QUAT
1830     m[0][0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]);
1831     m[0][1] = 2.0 * (q[0] * q[1] - q[2] * q[3]);
1832     m[0][2] = 2.0 * (q[2] * q[0] + q[1] * q[3]);
1833     m[0][3] = 0.0;
1834
1835     m[1][0] = 2.0 * (q[0] * q[1] + q[2] * q[3]);
1836     m[1][1]= 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]);
1837     m[1][2] = 2.0 * (q[1] * q[2] - q[0] * q[3]);
1838     m[1][3] = 0.0;
1839
1840     m[2][0] = 2.0 * (q[2] * q[0] - q[1] * q[3]);
1841     m[2][1] = 2.0 * (q[1] * q[2] + q[0] * q[3]);
1842     m[2][2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]);
1843     
1844     m[2][3] = 0.0;
1845     m[3][0] = 0.0;
1846     m[3][1] = 0.0;
1847     m[3][2] = 0.0;
1848     m[3][3] = 1.0;
1849 #else //  TRANSPOSED_QUAT
1850     m[0][0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]);
1851     m[0][1] = 2.0 * (q[0] * q[1] + q[2] * q[3]);
1852     m[0][2] = 2.0 * (q[2] * q[0] - q[1] * q[3]);
1853     m[0][3] = 0.0;
1854
1855     m[1][0] = 2.0 * (q[0] * q[1] - q[2] * q[3]);
1856     m[1][1] = 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]);
1857     m[1][2] = 2.0 * (q[1] * q[2] + q[0] * q[3]);
1858     m[1][3] = 0.0;
1859
1860     m[2][0] = 2.0 * (q[2] * q[0] + q[1] * q[3]);
1861     m[2][1] = 2.0 * (q[1] * q[2] - q[0] * q[3]);
1862     m[2][2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]);
1863     m[2][3] = 0.0;
1864     
1865     m[3][0] = 0.0;
1866     m[3][1] = 0.0;
1867     m[3][2] = 0.0;
1868     m[3][3] = 1.0;
1869 #endif // 0
1870 }
1871