]> git.mxchange.org Git - flightgear.git/blob - src/GUI/gui_funcs.cxx
remove depreciated gui_local.[ch]xx:
[flightgear.git] / src / GUI / gui_funcs.cxx
1 /**************************************************************************
2  * gui_funcs.cxx
3  *
4  * Based on gui.cxx and renamed on 2002/08/13 by Erik Hofman.
5  *
6  * Written 1998 by Durk Talsma, started Juni, 1998.  For the flight gear
7  * project.
8  *
9  * Additional mouse supported added by David Megginson, 1999.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of the
14  * License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
24  *
25  * $Id$
26  **************************************************************************/
27
28
29 #ifdef HAVE_CONFIG_H
30 #  include <config.h>
31 #endif
32
33 #include <simgear/compiler.h>
34
35 #ifdef SG_MATH_EXCEPTION_CLASH
36 #  include <math.h>
37 #endif
38
39 #ifdef HAVE_WINDOWS_H
40 #  include <windows.h>
41 #endif
42
43 #include SG_GL_H
44
45 #if defined(FX) && defined(XMESA)
46 #  include <GL/xmesa.h>
47 #endif
48
49 #include STL_FSTREAM
50 #include STL_STRING
51
52 #include <stdlib.h>
53 #include <string.h>
54
55 // for help call back
56 #ifdef WIN32
57 # include <shellapi.h>
58 # ifdef __CYGWIN__
59 #  include <sys/cygwin.h>
60 # endif
61 #endif
62
63 #include <sstream>
64
65 #include <simgear/constants.h>
66 #include <simgear/debug/logstream.hxx>
67 #include <simgear/misc/sg_path.hxx>
68 #include <simgear/screen/screen-dump.hxx>
69
70 #include <Include/general.hxx>
71 #include <Aircraft/aircraft.hxx>
72 #include <Aircraft/controls.hxx>
73 #include <Airports/simple.hxx>
74 #include <Cockpit/panel.hxx>
75 #include <FDM/flight.hxx>
76 #include <Main/main.hxx>
77 #include <Main/fg_init.hxx>
78 #include <Main/fg_io.hxx>
79 #include <Main/globals.hxx>
80 #include <Main/fg_props.hxx>
81 #include <Main/renderer.hxx>
82 #include <Main/viewmgr.hxx>
83 #include <GUI/new_gui.hxx>
84
85 #if defined( WIN32 ) && !defined( __CYGWIN__ ) && !defined(__MINGW32__)
86 #  include <simgear/screen/win32-printer.h>
87 #  include <simgear/screen/GlBitmaps.h>
88 #endif
89
90 #include "gui.h"
91
92 SG_USING_STD(string);
93 SG_USING_STD(cout);
94
95
96 #if defined( TR_HIRES_SNAP)
97 #include <simgear/screen/tr.h>
98 extern void fgUpdateHUD( GLfloat x_start, GLfloat y_start,
99                          GLfloat x_end, GLfloat y_end );
100 #endif
101
102
103 const __fg_gui_fn_t __fg_gui_fn[] = {
104
105         // File
106         {"reInit", reInit},
107 #ifdef TR_HIRES_SNAP
108         {"dumpHiResSnapShot", fgHiResDumpWrapper},
109 #endif
110         {"dumpSnapShot", fgDumpSnapShotWrapper},
111 #if defined( WIN32 ) && !defined( __CYGWIN__) && !defined(__MINGW32__)
112         {"printScreen", printScreen},
113 #endif
114         // Help
115         {"helpCb", helpCb},
116
117         // Structure termination
118         {"", NULL}
119 };
120
121
122 /* ================ General Purpose Functions ================ */
123
124 // General Purpose Message Box. Makes sure no more than 5 different
125 // messages are displayed at the same time, and none of them are
126 // duplicates. (5 is a *lot*, but this will hardly ever be reached
127 // and we don't want to miss any, either.)
128 void mkDialog (const char *txt)
129 {
130     NewGUI *gui = (NewGUI *)globals->get_subsystem("gui");
131     if (!gui)
132         return;
133     SGPropertyNode *master = gui->getDialogProperties("message");
134     if (!master)
135         return;
136
137     const int maxdialogs = 5;
138     string name;
139     SGPropertyNode *msg = fgGetNode("/sim/gui/dialogs", true);
140     int i;
141     for (i = 0; i < maxdialogs; i++) {
142         std::ostringstream s;
143         s << "message-" << i;
144         name = s.str();
145
146         if (!msg->getNode(name.c_str(), false))
147             break;
148
149         if (!strcmp(txt, msg->getNode(name.c_str())->getStringValue("message"))) {
150             SG_LOG(SG_GENERAL, SG_WARN, "mkDialog(): duplicate of message " << txt);
151             return;
152         }
153     }
154     if (i == maxdialogs)
155         return;
156     msg = msg->getNode(name.c_str(), true);
157     msg->setStringValue("message", txt);
158     msg = msg->getNode("dialog", true);
159     copyProperties(master, msg);
160     msg->setStringValue("name", name.c_str());
161     gui->newDialog(msg);
162     gui->showDialog(name.c_str());
163 }
164
165 // Message Box to report an error.
166 void guiErrorMessage (const char *txt)
167 {
168     SG_LOG(SG_GENERAL, SG_ALERT, txt);
169     mkDialog(txt);
170 }
171
172 // Message Box to report a throwable (usually an exception).
173 void guiErrorMessage (const char *txt, const sg_throwable &throwable)
174 {
175     string msg = txt;
176     msg += '\n';
177     msg += throwable.getFormattedMessage();
178     if (!throwable.getOrigin().empty()) {
179         msg += "\n (reported by ";
180         msg += throwable.getOrigin();
181         msg += ')';
182     }
183     SG_LOG(SG_GENERAL, SG_ALERT, msg);
184     mkDialog(msg.c_str());
185 }
186
187
188
189 /* -----------------------------------------------------------------------
190 the Gui callback functions 
191 ____________________________________________________________________*/
192
193
194 // Hier Neu :-) This is my newly added code
195 // Added by David Findlay <nedz@bigpond.com>
196 // on Sunday 3rd of December
197
198
199 void helpCb ()
200 {
201     string command;
202         
203 #if defined(FX) && !defined(WIN32)
204 #  if defined(XMESA_FX_FULLSCREEN) && defined(XMESA_FX_WINDOW)
205     if ( globals->get_fullscreen() ) {
206         globals->set_fullscreen(false);
207         XMesaSetFXmode( XMESA_FX_WINDOW );
208     }
209 #  endif
210 #endif
211         
212     SGPath path( globals->get_fg_root() );
213     path.append( "Docs/index.html" );
214         
215 #if !defined(WIN32)
216
217     string help_app = fgGetString("/sim/startup/browser-app");
218
219     if ( system("xwininfo -name Netscape > /dev/null 2>&1") == 0 ) {
220         command = help_app + " -remote \"openURL(" + path.str() + ")\"";
221     } else {
222         command = help_app + " " + path.str();
223     }
224     command += " &";
225     system( command.c_str() );
226
227 #else // WIN32
228
229     // Look for favorite browser
230     char Dummy[1024], ExecName[1024], browserParameter[1024];
231     char win32_name[1024];
232 # ifdef __CYGWIN__
233     cygwin32_conv_to_full_win32_path(path.c_str(),win32_name);
234 # else
235     strncpy(win32_name,path.c_str(), 1024);
236 # endif
237     Dummy[0] = 0;
238     FindExecutable(win32_name, Dummy, ExecName);
239     snprintf(browserParameter, 1024, "file:///%s", win32_name);
240     ShellExecute ( NULL, "open", ExecName, browserParameter, Dummy,
241                    SW_SHOWNORMAL ) ;
242
243 #endif
244         
245     mkDialog ("Help started in your web browser window.");
246 }
247
248 #if defined( TR_HIRES_SNAP)
249 void fgHiResDump()
250 {
251     FILE *f;
252     string message;
253     bool show_pu_cursor = false;
254     bool menu_status = fgGetBool("/sim/menubar/visibility");
255     char *filename = new char [24];
256     static int count = 1;
257
258     static const SGPropertyNode *master_freeze
259         = fgGetNode("/sim/freeze/master");
260
261     bool freeze = master_freeze->getBoolValue();
262     if ( !freeze ) {
263         fgSetBool("/sim/freeze/master", true);
264     }
265
266     fgSetBool("/sim/menubar/visibility", false);
267     TurnCursorOff();
268     if ( !puCursorIsHidden() ) {
269         show_pu_cursor = true;
270         puHideCursor();
271     }
272
273     FGRenderer *renderer = globals->get_renderer();
274 //     renderer->init();
275     renderer->resize( fgGetInt("/sim/startup/xsize"),
276                       fgGetInt("/sim/startup/ysize") );
277
278     // we need two render frames here to clear the menu and cursor
279     // ... not sure why but doing an extra fgRenderFrame() shouldn't
280     // hurt anything
281     //renderer->update( true );
282     //renderer->update( true );
283
284     // This ImageSize stuff is a temporary hack
285     // should probably use 128x128 tile size and
286     // support any image size
287
288     // This should be a requester to get multiplier from user
289     int multiplier = fgGetInt("/sim/startup/hires-multiplier", 3);
290     int width = fgGetInt("/sim/startup/xsize");
291     int height = fgGetInt("/sim/startup/ysize");
292         
293     /* allocate buffer large enough to store one tile */
294     GLubyte *tile = (GLubyte *)malloc(width * height * 3 * sizeof(GLubyte));
295     if (!tile) {
296         printf("Malloc of tile buffer failed!\n");
297         return;
298     }
299
300     int imageWidth  = multiplier*width;
301     int imageHeight = multiplier*height;
302
303     /* allocate buffer to hold a row of tiles */
304     GLubyte *buffer
305         = (GLubyte *)malloc(imageWidth * height * 3 * sizeof(GLubyte));
306     if (!buffer) {
307         free(tile);
308         printf("Malloc of tile row buffer failed!\n");
309         return;
310     }
311     TRcontext *tr = trNew();
312     trTileSize(tr, width, height, 0);
313     trTileBuffer(tr, GL_RGB, GL_UNSIGNED_BYTE, tile);
314     trImageSize(tr, imageWidth, imageHeight);
315     trRowOrder(tr, TR_TOP_TO_BOTTOM);
316     // OSGFIXME
317 //     sgFrustum *frustum = ssgGetFrustum();
318 //     trFrustum(tr,
319 //               frustum->getLeft(), frustum->getRight(),
320 //               frustum->getBot(),  frustum->getTop(), 
321 //               frustum->getNear(), frustum->getFar());
322         
323     /* Prepare ppm output file */
324     while (count < 1000) {
325         snprintf(filename, 24, "fgfs-screen-%03d.ppm", count++);
326         if ( (f = fopen(filename, "r")) == NULL )
327             break;
328         fclose(f);
329     }
330     f = fopen(filename, "wb");
331     if (!f) {
332         printf("Couldn't open image file: %s\n", filename);
333         free(buffer);
334         free(tile);
335         return;
336     }
337     fprintf(f,"P6\n");
338     fprintf(f,"# ppm-file created by %s\n", "trdemo2");
339     fprintf(f,"%i %i\n", imageWidth, imageHeight);
340     fprintf(f,"255\n");
341
342     /* just to be safe... */
343     glPixelStorei(GL_PACK_ALIGNMENT, 1);
344
345     /* Because the HUD and Panel change the ViewPort we will
346      * need to handle some lowlevel stuff ourselves */
347     int ncols = trGet(tr, TR_COLUMNS);
348     int nrows = trGet(tr, TR_ROWS);
349
350     bool do_hud = fgGetBool("/sim/hud/visibility");
351     GLfloat hud_col_step = 640.0 / ncols;
352     GLfloat hud_row_step = 480.0 / nrows;
353         
354     bool do_panel = fgPanelVisible();
355     GLfloat panel_col_step = globals->get_current_panel()->getWidth() / ncols;
356     GLfloat panel_row_step = globals->get_current_panel()->getHeight() / nrows;
357
358     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
359     glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
360     glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
361     glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
362     glHint(GL_FOG_HINT, GL_NICEST);
363         
364     /* Draw tiles */
365     int more = 1;
366     while (more) {
367         trBeginTile(tr);
368         int curColumn = trGet(tr, TR_CURRENT_COLUMN);
369         int curRow =  trGet(tr, TR_CURRENT_ROW);
370
371         renderer->update( false );
372         // OSGFIXME
373 //         if ( do_hud )
374 //             fgUpdateHUD( curColumn*hud_col_step,      curRow*hud_row_step,
375 //                          (curColumn+1)*hud_col_step, (curRow+1)*hud_row_step );
376         // OSGFIXME
377 //         if (do_panel)
378 //             globals->get_current_panel()->update(
379 //                                    curColumn*panel_col_step, panel_col_step,
380 //                                    curRow*panel_row_step,    panel_row_step );
381         more = trEndTile(tr);
382
383         /* save tile into tile row buffer*/
384         int curTileWidth = trGet(tr, TR_CURRENT_TILE_WIDTH);
385         int bytesPerImageRow = imageWidth * 3*sizeof(GLubyte);
386         int bytesPerTileRow = (width) * 3*sizeof(GLubyte);
387         int xOffset = curColumn * bytesPerTileRow;
388         int bytesPerCurrentTileRow = (curTileWidth) * 3*sizeof(GLubyte);
389         int i;
390         for (i=0;i<height;i++) {
391             memcpy(buffer + i*bytesPerImageRow + xOffset, /* Dest */
392                    tile + i*bytesPerTileRow,              /* Src */
393                    bytesPerCurrentTileRow);               /* Byte count*/
394         }
395
396         if (curColumn == trGet(tr, TR_COLUMNS)-1) {
397             /* write this buffered row of tiles to the file */
398             int curTileHeight = trGet(tr, TR_CURRENT_TILE_HEIGHT);
399             int bytesPerImageRow = imageWidth * 3*sizeof(GLubyte);
400             int i;
401             for (i=0;i<curTileHeight;i++) {
402                 /* Remember, OpenGL images are bottom to top.  Have to reverse. */
403                 GLubyte *rowPtr = buffer + (curTileHeight-1-i) * bytesPerImageRow;
404                 fwrite(rowPtr, 1, imageWidth*3, f);
405             }
406         }
407
408     }
409
410     renderer->resize( width, height );
411
412     trDelete(tr);
413
414     glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE);
415     glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
416     glHint(GL_POINT_SMOOTH_HINT, GL_DONT_CARE);
417     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE);
418     if ( (!strcmp(fgGetString("/sim/rendering/fog"), "disabled")) ||
419          (!fgGetBool("/sim/rendering/shading"))) {
420         // if fastest fog requested, or if flat shading force fastest
421         glHint ( GL_FOG_HINT, GL_FASTEST );
422     } else if ( !strcmp(fgGetString("/sim/rendering/fog"), "nicest") ) {
423         glHint ( GL_FOG_HINT, GL_DONT_CARE );
424     }
425
426     fclose(f);
427
428     message = "Snapshot saved to \"";
429     message += filename;
430     message += "\".";
431     mkDialog (message.c_str());
432
433     free(tile);
434     free(buffer);
435
436     delete [] filename;
437
438     if ( show_pu_cursor ) {
439         puShowCursor();
440     }
441
442     TurnCursorOn();
443     fgSetBool("/sim/menubar/visibility", menu_status);
444
445     if ( !freeze ) {
446         fgSetBool("/sim/freeze/master", false);
447     }
448 }
449 #endif // #if defined( TR_HIRES_SNAP)
450
451
452 #if defined( WIN32 ) && !defined( __CYGWIN__) && !defined(__MINGW32__)
453
454 void rotateView( double roll, double pitch, double yaw )
455 {
456         // rotate view
457 }
458
459 GlBitmap *b1 = NULL;
460 GLubyte *hiResScreenCapture( int multiplier )
461 {
462     float oldfov = fgGetDouble("/sim/current-view/field-of-view");
463     float fov = oldfov / multiplier;
464     FGViewer *v = globals->get_current_view();
465     fgSetDouble("/sim/current-view/field-of-view", fov);
466 //     globals->get_renderer()->init();
467     int cur_width = fgGetInt("/sim/startup/xsize");
468     int cur_height = fgGetInt("/sim/startup/ysize");
469     delete( b1 );
470     // New empty (mostly) bitmap
471     b1 = new GlBitmap( GL_RGB, 1, 1, (unsigned char *)"123" );
472     int x,y;
473     for ( y = 0; y < multiplier; y++ ) {
474         for ( x = 0; x < multiplier; x++ ) {
475             globals->get_renderer()->resize( cur_width, cur_height );
476             // pan to tile
477             rotateView( 0, (y*fov)-((multiplier-1)*fov/2), (x*fov)-((multiplier-1)*fov/2) );
478             globals->get_renderer()->update( false );
479             // restore view
480             GlBitmap b2;
481             b1->copyBitmap( &b2, cur_width*x, cur_height*y );
482         }
483     }
484     fgSetDouble("/sim/current-view/field-of-view", oldfov);
485     return b1->getBitmap();
486 }
487 #endif
488
489 #if defined( WIN32 ) && !defined( __CYGWIN__) && !defined(__MINGW32__)
490 // win32 print screen function
491 void printScreen () {
492     bool show_pu_cursor = false;
493     TurnCursorOff();
494     if ( !puCursorIsHidden() ) {
495         show_pu_cursor = true;
496         puHideCursor();
497     }
498
499     CGlPrinter p( CGlPrinter::PRINT_BITMAP );
500     int cur_width = fgGetInt("/sim/startup/xsize");
501     int cur_height = fgGetInt("/sim/startup/ysize");
502     p.Begin( "FlightGear", cur_width*3, cur_height*3 );
503     p.End( hiResScreenCapture(3) );
504
505     if ( show_pu_cursor ) {
506         puShowCursor();
507     }
508     TurnCursorOn();
509 }
510 #endif // #ifdef WIN32
511
512
513 void fgDumpSnapShotWrapper () {
514     fgDumpSnapShot();
515 }
516
517
518 void fgHiResDumpWrapper () {
519     fgHiResDump();
520 }
521
522
523 // do a screen snap shot
524 bool fgDumpSnapShot () {
525     bool show_pu_cursor = false;
526     static SGConstPropertyNode_ptr master_freeze = fgGetNode("/sim/freeze/master");
527
528     bool freeze = master_freeze->getBoolValue();
529     if ( !freeze ) {
530         fgSetBool("/sim/freeze/master", true);
531     }
532
533     TurnCursorOff();
534     if ( !puCursorIsHidden() ) {
535         show_pu_cursor = true;
536         puHideCursor();
537     }
538     fgSetBool("/sim/signals/screenshot", true);
539
540     FGRenderer *renderer = globals->get_renderer();
541     renderer->resize( fgGetInt("/sim/startup/xsize"),
542                       fgGetInt("/sim/startup/ysize") );
543
544     // we need two render frames here to clear the menu and cursor
545     // ... not sure why but doing an extra fgRenderFrame() shouldn't
546     // hurt anything
547     renderer->update( true );
548     renderer->update( true );
549
550     string dir = fgGetString("/sim/paths/screenshot-dir", fgGetString("/sim/fg-current"));
551     SGPath path(dir + '/');
552     if (path.create_dir( 0755 )) {
553         SG_LOG(SG_GENERAL, SG_ALERT, "Cannot create screenshot directory '"
554                 << dir << "'. Trying home directory.");
555         dir = fgGetString("/sim/fg-home");
556     }
557
558     char filename[24];
559     static int count = 1;
560     while (count < 1000) {
561         snprintf(filename, 24, "fgfs-screen-%03d.ppm", count++);
562
563         SGPath p(dir);
564         p.append(filename);
565         if (!p.exists()) {
566             path.set(p.str());
567             break;
568         }
569     }
570
571     int result = sg_glDumpWindow(path.c_str(),
572                                  fgGetInt("/sim/startup/xsize"),
573                                  fgGetInt("/sim/startup/ysize"));
574
575     fgSetString("/sim/paths/screenshot-last", path.c_str());
576     fgSetBool("/sim/signals/screenshot", false);
577
578     if ( show_pu_cursor ) {
579         puShowCursor();
580     }
581
582     TurnCursorOn();
583
584     if ( !freeze ) {
585         fgSetBool("/sim/freeze/master", false);
586     }
587     return result != 0;
588 }
589
590 // do an entire scenegraph dump
591 void fgDumpSceneGraph()
592 {
593     char *filename = new char [24];
594     string message;
595     static int count = 1;
596
597     FGRenderer *renderer = globals->get_renderer();
598
599     static const SGPropertyNode *master_freeze
600         = fgGetNode("/sim/freeze/master");
601
602     bool freeze = master_freeze->getBoolValue();
603     if ( !freeze ) {
604         fgSetBool("/sim/freeze/master", true);
605     }
606
607     while (count < 1000) {
608         FILE *fp;
609         snprintf(filename, 24, "fgfs-graph-%03d.osg", count++);
610         if ( (fp = fopen(filename, "r")) == NULL )
611             break;
612         fclose(fp);
613     }
614
615     if ( fgDumpSceneGraphToFile(filename)) {
616         message = "Entire scene graph saved to \"";
617         message += filename;
618         message += "\".";
619     } else {
620         message = "Failed to save to \"";
621         message += filename;
622         message += "\".";
623     }
624
625     mkDialog (message.c_str());
626
627     delete [] filename;
628
629     if ( !freeze ) {
630         fgSetBool("/sim/freeze/master", false);
631     }
632 }
633
634     
635 // do an terrain branch dump
636 void fgDumpTerrainBranch()
637 {
638     char *filename = new char [24];
639     string message;
640     static int count = 1;
641
642     FGRenderer *renderer = globals->get_renderer();
643
644     static const SGPropertyNode *master_freeze
645         = fgGetNode("/sim/freeze/master");
646
647     bool freeze = master_freeze->getBoolValue();
648     if ( !freeze ) {
649         fgSetBool("/sim/freeze/master", true);
650     }
651
652     while (count < 1000) {
653         FILE *fp;
654         snprintf(filename, 24, "fgfs-graph-%03d.osg", count++);
655         if ( (fp = fopen(filename, "r")) == NULL )
656             break;
657         fclose(fp);
658     }
659
660     if ( fgDumpTerrainBranchToFile(filename)) {
661         message = "Terrain graph saved to \"";
662         message += filename;
663         message += "\".";
664     } else {
665         message = "Failed to save to \"";
666         message += filename;
667         message += "\".";
668     }
669
670     mkDialog (message.c_str());
671
672     delete [] filename;
673
674     if ( !freeze ) {
675         fgSetBool("/sim/freeze/master", false);
676     }
677 }
678
679     
680