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