c04867108bf4dfb4c102c71b5b94b3bcf327f7dc
[xonotic/netradiant.git] / radiant / main.cpp
1 /*
2    Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 /*! \mainpage GtkRadiant Documentation Index
23
24    \section intro_sec Introduction
25
26    This documentation is generated from comments in the source code.
27
28    \section links_sec Useful Links
29
30    \link include/itextstream.h include/itextstream.h \endlink - Global output and error message streams, similar to std::cout and std::cerr. \n
31
32    FileInputStream - similar to std::ifstream (binary mode) \n
33    FileOutputStream - similar to std::ofstream (binary mode) \n
34    TextFileInputStream - similar to std::ifstream (text mode) \n
35    TextFileOutputStream - similar to std::ofstream (text mode) \n
36    StringOutputStream - similar to std::stringstream \n
37
38    \link string/string.h string/string.h \endlink - C-style string comparison and memory management. \n
39    \link os/path.h os/path.h \endlink - Path manipulation for radiant's standard path format \n
40    \link os/file.h os/file.h \endlink - OS file-system access. \n
41
42    ::CopiedString - automatic string memory management \n
43    Array - automatic array memory management \n
44    HashTable - generic hashtable, similar to std::hash_map \n
45
46    \link math/vector.h math/vector.h \endlink - Vectors \n
47    \link math/matrix.h math/matrix.h \endlink - Matrices \n
48    \link math/quaternion.h math/quaternion.h \endlink - Quaternions \n
49    \link math/plane.h math/plane.h \endlink - Planes \n
50    \link math/aabb.h math/aabb.h \endlink - AABBs \n
51
52    Callback MemberCaller FunctionCaller - callbacks similar to using boost::function with boost::bind \n
53    SmartPointer SmartReference - smart-pointer and smart-reference similar to Loki's SmartPtr \n
54
55    \link generic/bitfield.h generic/bitfield.h \endlink - Type-safe bitfield \n
56    \link generic/enumeration.h generic/enumeration.h \endlink - Type-safe enumeration \n
57
58    DefaultAllocator - Memory allocation using new/delete, compliant with std::allocator interface \n
59
60    \link debugging/debugging.h debugging/debugging.h \endlink - Debugging macros \n
61
62  */
63
64 #include "main.h"
65
66 #include "version.h"
67
68 #include "debugging/debugging.h"
69
70 #include "iundo.h"
71
72 #include <gtk/gtkmain.h>
73
74 #include "cmdlib.h"
75 #include "os/file.h"
76 #include "os/path.h"
77 #include "stream/stringstream.h"
78 #include "stream/textfilestream.h"
79
80 #include "gtkutil/messagebox.h"
81 #include "gtkutil/image.h"
82 #include "console.h"
83 #include "texwindow.h"
84 #include "map.h"
85 #include "mainframe.h"
86 #include "commands.h"
87 #include "preferences.h"
88 #include "environment.h"
89 #include "referencecache.h"
90 #include "stacktrace.h"
91
92 #ifdef WIN32
93 #include <windows.h>
94 #endif
95
96 void show_splash();
97 void hide_splash();
98
99 void error_redirect( const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data ){
100         gboolean in_recursion;
101         gboolean is_fatal;
102         char buf[256];
103
104         in_recursion = ( log_level & G_LOG_FLAG_RECURSION ) != 0;
105         is_fatal = ( log_level & G_LOG_FLAG_FATAL ) != 0;
106         log_level = (GLogLevelFlags) ( log_level & G_LOG_LEVEL_MASK );
107
108         if ( !message ) {
109                 message = "(0) message";
110         }
111
112         if ( domain ) {
113                 strcpy( buf, domain );
114         }
115         else{
116                 strcpy( buf, "**" );
117         }
118         strcat( buf, "-" );
119
120         switch ( log_level )
121         {
122         case G_LOG_LEVEL_ERROR:
123                 if ( in_recursion ) {
124                         strcat( buf, "ERROR (recursed) **: " );
125                 }
126                 else{
127                         strcat( buf, "ERROR **: " );
128                 }
129                 break;
130         case G_LOG_LEVEL_CRITICAL:
131                 if ( in_recursion ) {
132                         strcat( buf, "CRITICAL (recursed) **: " );
133                 }
134                 else{
135                         strcat( buf, "CRITICAL **: " );
136                 }
137                 break;
138         case G_LOG_LEVEL_WARNING:
139                 if ( in_recursion ) {
140                         strcat( buf, "WARNING (recursed) **: " );
141                 }
142                 else{
143                         strcat( buf, "WARNING **: " );
144                 }
145                 break;
146         case G_LOG_LEVEL_MESSAGE:
147                 if ( in_recursion ) {
148                         strcat( buf, "Message (recursed): " );
149                 }
150                 else{
151                         strcat( buf, "Message: " );
152                 }
153                 break;
154         case G_LOG_LEVEL_INFO:
155                 if ( in_recursion ) {
156                         strcat( buf, "INFO (recursed): " );
157                 }
158                 else{
159                         strcat( buf, "INFO: " );
160                 }
161                 break;
162         case G_LOG_LEVEL_DEBUG:
163                 if ( in_recursion ) {
164                         strcat( buf, "DEBUG (recursed): " );
165                 }
166                 else{
167                         strcat( buf, "DEBUG: " );
168                 }
169                 break;
170         default:
171                 /* we are used for a log level that is not defined by GLib itself,
172                  * try to make the best out of it.
173                  */
174                 if ( in_recursion ) {
175                         strcat( buf, "LOG (recursed:" );
176                 }
177                 else{
178                         strcat( buf, "LOG (" );
179                 }
180                 if ( log_level ) {
181                         gchar string[] = "0x00): ";
182                         gchar *p = string + 2;
183                         guint i;
184
185                         i = g_bit_nth_msf( log_level, -1 );
186                         *p = i >> 4;
187                         p++;
188                         *p = '0' + ( i & 0xf );
189                         if ( *p > '9' ) {
190                                 *p += 'A' - '9' - 1;
191                         }
192
193                         strcat( buf, string );
194                 }
195                 else{
196                         strcat( buf, "): " );
197                 }
198         }
199
200         strcat( buf, message );
201         if ( is_fatal ) {
202                 strcat( buf, "\naborting...\n" );
203         }
204         else{
205                 strcat( buf, "\n" );
206         }
207
208         // spam it...
209         globalErrorStream() << buf << "\n";
210
211         if (is_fatal) {
212             ERROR_MESSAGE( "GTK+ error: " << buf );
213     }
214 }
215
216 #if defined ( _DEBUG ) && defined ( WIN32 ) && defined ( _MSC_VER )
217 #include "crtdbg.h"
218 #endif
219
220 void crt_init(){
221 #if defined ( _DEBUG ) && defined ( WIN32 ) && defined ( _MSC_VER )
222         _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
223 #endif
224 }
225
226 class Lock
227 {
228 bool m_locked;
229 public:
230 Lock() : m_locked( false ){
231 }
232 void lock(){
233         m_locked = true;
234 }
235 void unlock(){
236         m_locked = false;
237 }
238 bool locked() const {
239         return m_locked;
240 }
241 };
242
243 class ScopedLock
244 {
245 Lock& m_lock;
246 public:
247 ScopedLock( Lock& lock ) : m_lock( lock ){
248         m_lock.lock();
249 }
250 ~ScopedLock(){
251         m_lock.unlock();
252 }
253 };
254
255 class LineLimitedTextOutputStream : public TextOutputStream
256 {
257 TextOutputStream& outputStream;
258 std::size_t count;
259 public:
260 LineLimitedTextOutputStream( TextOutputStream& outputStream, std::size_t count )
261         : outputStream( outputStream ), count( count ){
262 }
263 std::size_t write( const char* buffer, std::size_t length ){
264         if ( count != 0 ) {
265                 const char* p = buffer;
266                 const char* end = buffer + length;
267                 for (;; )
268                 {
269                         p = std::find( p, end, '\n' );
270                         if ( p == end ) {
271                                 break;
272                         }
273                         ++p;
274                         if ( --count == 0 ) {
275                                 length = p - buffer;
276                                 break;
277                         }
278                 }
279                 outputStream.write( buffer, length );
280         }
281         return length;
282 }
283 };
284
285 class PopupDebugMessageHandler : public DebugMessageHandler
286 {
287 StringOutputStream m_buffer;
288 Lock m_lock;
289 public:
290 TextOutputStream& getOutputStream(){
291         if ( !m_lock.locked() ) {
292                 return m_buffer;
293         }
294         return globalErrorStream();
295 }
296 bool handleMessage(){
297         getOutputStream() << "----------------\n";
298         LineLimitedTextOutputStream outputStream( getOutputStream(), 24 );
299         write_stack_trace( outputStream );
300         getOutputStream() << "----------------\n";
301         globalErrorStream() << m_buffer.c_str();
302         if ( !m_lock.locked() ) {
303                 ScopedLock lock( m_lock );
304 #if defined _DEBUG
305                 m_buffer << "Break into the debugger?\n";
306                 bool handled = gtk_MessageBox( 0, m_buffer.c_str(), "Radiant - Runtime Error", eMB_YESNO, eMB_ICONERROR ) == eIDNO;
307                 m_buffer.clear();
308                 return handled;
309 #else
310                 m_buffer << "Please report this error to the developers\n";
311                 gtk_MessageBox( 0, m_buffer.c_str(), "Radiant - Runtime Error", eMB_OK, eMB_ICONERROR );
312                 m_buffer.clear();
313 #endif
314         }
315         return true;
316 }
317 };
318
319 typedef Static<PopupDebugMessageHandler> GlobalPopupDebugMessageHandler;
320
321 void streams_init(){
322         GlobalErrorStream::instance().setOutputStream( getSysPrintErrorStream() );
323         GlobalOutputStream::instance().setOutputStream( getSysPrintOutputStream() );
324 }
325
326 void paths_init(){
327         const char* home = environment_get_home_path();
328         Q_mkdir( home );
329
330         {
331                 StringOutputStream path( 256 );
332                 path << home << "1." << RADIANT_MAJOR_VERSION "." << RADIANT_MINOR_VERSION << '/';
333                 g_strSettingsPath = path.c_str();
334         }
335
336         Q_mkdir( g_strSettingsPath.c_str() );
337
338         g_strAppPath = environment_get_app_path();
339
340         // radiant is installed in the parent dir of "tools/"
341         // NOTE: this is not very easy for debugging
342         // maybe add options to lookup in several places?
343         // (for now I had to create symlinks)
344         {
345                 StringOutputStream path( 256 );
346                 path << g_strAppPath.c_str() << "bitmaps/";
347                 BitmapsPath_set( path.c_str() );
348         }
349
350         // we will set this right after the game selection is done
351         g_strGameToolsPath = g_strAppPath;
352 }
353
354 bool check_version_file( const char* filename, const char* version ){
355         TextFileInputStream file( filename );
356         if ( !file.failed() ) {
357                 char buf[10];
358                 buf[file.read( buf, 9 )] = '\0';
359
360                 // chomp it (the hard way)
361                 int chomp = 0;
362                 while ( buf[chomp] >= '0' && buf[chomp] <= '9' )
363                         chomp++;
364                 buf[chomp] = '\0';
365
366                 return string_equal( buf, version );
367         }
368         return false;
369 }
370
371 bool check_version(){
372         // a safe check to avoid people running broken installations
373         // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
374         // make something idiot proof and someone will make better idiots, this may be overkill
375         // let's leave it disabled in debug mode in any case
376         // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=431
377 #ifndef _DEBUG
378 #define CHECK_VERSION
379 #endif
380 #ifdef CHECK_VERSION
381         // locate and open RADIANT_MAJOR and RADIANT_MINOR
382         bool bVerIsGood = true;
383         {
384                 StringOutputStream ver_file_name( 256 );
385                 ver_file_name << AppPath_get() << "RADIANT_MAJOR";
386                 bVerIsGood = check_version_file( ver_file_name.c_str(), RADIANT_MAJOR_VERSION );
387         }
388         {
389                 StringOutputStream ver_file_name( 256 );
390                 ver_file_name << AppPath_get() << "RADIANT_MINOR";
391                 bVerIsGood = check_version_file( ver_file_name.c_str(), RADIANT_MINOR_VERSION );
392         }
393
394         if ( !bVerIsGood ) {
395                 StringOutputStream msg( 256 );
396                 msg << "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n"
397                                                                                                           "Make sure you run the right/latest editor binary you installed\n"
398                         << AppPath_get();
399                 gtk_MessageBox( 0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONDEFAULT );
400         }
401         return bVerIsGood;
402 #else
403         return true;
404 #endif
405 }
406
407 void create_global_pid(){
408         /*!
409            the global prefs loading / game selection dialog might fail for any reason we don't know about
410            we need to catch when it happens, to cleanup the stateful prefs which might be killing it
411            and to turn on console logging for lookup of the problem
412            this is the first part of the two step .pid system
413            http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
414          */
415         StringOutputStream g_pidFile( 256 ); ///< the global .pid file (only for global part of the startup)
416
417         g_pidFile << SettingsPath_get() << "radiant.pid";
418
419         FILE *pid;
420         pid = fopen( g_pidFile.c_str(), "r" );
421         if ( pid != 0 ) {
422                 fclose( pid );
423
424                 if ( remove( g_pidFile.c_str() ) == -1 ) {
425                         StringOutputStream msg( 256 );
426                         msg << "WARNING: Could not delete " << g_pidFile.c_str();
427                         gtk_MessageBox( 0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
428                 }
429
430                 // in debug, never prompt to clean registry, turn console logging auto after a failed start
431 #if !defined( _DEBUG )
432                 StringOutputStream msg( 256 );
433                 msg << "Radiant failed to start properly the last time it was run.\n"
434                            "The failure may be related to current global preferences.\n"
435                            "Do you want to reset global preferences to defaults?";
436
437                 if ( gtk_MessageBox( 0, msg.c_str(), "Radiant - Startup Failure", eMB_YESNO, eMB_ICONQUESTION ) == eIDYES ) {
438                         g_GamesDialog.Reset();
439                 }
440
441                 msg.clear();
442                 msg << "Logging console output to " << SettingsPath_get() << "radiant.log\nRefer to the log if Radiant fails to start again.";
443
444                 gtk_MessageBox( 0, msg.c_str(), "Radiant - Console Log", eMB_OK );
445 #endif
446
447                 // set without saving, the class is not in a coherent state yet
448                 // just do the value change and call to start logging, CGamesDialog will pickup when relevant
449                 g_GamesDialog.m_bForceLogConsole = true;
450                 Sys_LogFile( true );
451         }
452
453         // create a primary .pid for global init run
454         pid = fopen( g_pidFile.c_str(), "w" );
455         if ( pid ) {
456                 fclose( pid );
457         }
458 }
459
460 void remove_global_pid(){
461         StringOutputStream g_pidFile( 256 );
462         g_pidFile << SettingsPath_get() << "radiant.pid";
463
464         // close the primary
465         if ( remove( g_pidFile.c_str() ) == -1 ) {
466                 StringOutputStream msg( 256 );
467                 msg << "WARNING: Could not delete " << g_pidFile.c_str();
468                 gtk_MessageBox( 0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
469         }
470 }
471
472 /*!
473    now the secondary game dependant .pid file
474    http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
475  */
476 void create_local_pid(){
477         StringOutputStream g_pidGameFile( 256 ); ///< the game-specific .pid file
478         g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
479
480         FILE *pid = fopen( g_pidGameFile.c_str(), "r" );
481         if ( pid != 0 ) {
482                 fclose( pid );
483                 if ( remove( g_pidGameFile.c_str() ) == -1 ) {
484                         StringOutputStream msg;
485                         msg << "WARNING: Could not delete " << g_pidGameFile.c_str();
486                         gtk_MessageBox( 0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
487                 }
488
489                 // in debug, never prompt to clean registry, turn console logging auto after a failed start
490 #if !defined( _DEBUG )
491                 StringOutputStream msg;
492                 msg << "Radiant failed to start properly the last time it was run.\n"
493                            "The failure may be caused by current preferences.\n"
494                            "Do you want to reset all preferences to defaults?";
495
496                 if ( gtk_MessageBox( 0, msg.c_str(), "Radiant - Startup Failure", eMB_YESNO, eMB_ICONQUESTION ) == eIDYES ) {
497                         Preferences_Reset();
498                 }
499
500                 msg.clear();
501                 msg << "Logging console output to " << SettingsPath_get() << "radiant.log\nRefer to the log if Radiant fails to start again.";
502
503                 gtk_MessageBox( 0, msg.c_str(), "Radiant - Console Log", eMB_OK );
504 #endif
505
506                 // force console logging on! (will go in prefs too)
507                 g_GamesDialog.m_bForceLogConsole = true;
508                 Sys_LogFile( true );
509         }
510         else
511         {
512                 // create one, will remove right after entering message loop
513                 pid = fopen( g_pidGameFile.c_str(), "w" );
514                 if ( pid ) {
515                         fclose( pid );
516                 }
517         }
518 }
519
520
521 /*!
522    now the secondary game dependant .pid file
523    http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
524  */
525 void remove_local_pid(){
526         StringOutputStream g_pidGameFile( 256 );
527         g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
528         remove( g_pidGameFile.c_str() );
529 }
530
531 void user_shortcuts_init(){
532         StringOutputStream path( 256 );
533         path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
534         LoadCommandMap( path.c_str() );
535         SaveCommandMap( path.c_str() );
536 }
537
538 void user_shortcuts_save(){
539         StringOutputStream path( 256 );
540         path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
541         SaveCommandMap( path.c_str() );
542 }
543
544 int main( int argc, char* argv[] ){
545         crt_init();
546
547         streams_init();
548
549 #ifdef WIN32
550         HMODULE lib;
551         lib = LoadLibrary( "dwmapi.dll" );
552         if ( lib != 0 ) {
553                 void ( WINAPI *qDwmEnableComposition )( bool bEnable ) = ( void (WINAPI *) ( bool bEnable ) )GetProcAddress( lib, "DwmEnableComposition" );
554                 if ( qDwmEnableComposition ) {
555                         qDwmEnableComposition( FALSE );
556                 }
557                 FreeLibrary( lib );
558         }
559 #endif
560
561         static GOptionEntry entries[] = {
562                 { NULL }
563         };
564         GError *error = NULL;
565         const char* mapname = NULL;
566
567         gtk_disable_setlocale();
568         if ( !gtk_init_with_args( &argc, &argv, "<filename.map>", entries, NULL, &error) ) {
569                 g_print( "%s\n", error->message );
570                 return -1;
571         }
572
573         // Gtk already removed parsed `--options`
574         if (argc == 2) {
575                 if ( strlen( argv[1] ) > 1 ) {
576                         if ( g_str_has_suffix( argv[1], ".map" ) ) {
577                                 if ( g_path_is_absolute( argv[1] ) ) {
578                                         mapname = argv[1];
579                                 }
580                                 else {
581                                         mapname = g_build_filename( g_get_current_dir(), argv[1], NULL );
582                                 }
583                         }
584                         else {
585                                 g_print( "bad file name, will not load: %s\n", argv[1] );
586                         }
587                 }
588         }
589         else if (argc > 2) {
590                 g_print ( "%s\n", "too many arguments" );
591                 return -1;
592         }
593
594         // redirect Gtk warnings to the console
595         g_log_set_handler( "Gdk", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
596                                                                                                 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
597         g_log_set_handler( "Gtk", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
598                                                                                                 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
599         g_log_set_handler( "GtkGLExt", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
600                                                                                                          G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
601         g_log_set_handler( "GLib", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
602                                                                                                  G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
603         g_log_set_handler( 0, (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
604                                                                                         G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
605
606         GlobalDebugMessageHandler::instance().setHandler( GlobalPopupDebugMessageHandler::instance() );
607
608         environment_init( argc, argv );
609
610         paths_init();
611
612         if ( !check_version() ) {
613                 return EXIT_FAILURE;
614         }
615
616         show_splash();
617
618         create_global_pid();
619
620         GlobalPreferences_Init();
621
622         g_GamesDialog.Init();
623
624         g_strGameToolsPath = g_pGameDescription->mGameToolsPath;
625
626         remove_global_pid();
627
628         g_Preferences.Init(); // must occur before create_local_pid() to allow preferences to be reset
629
630         create_local_pid();
631
632         // in a very particular post-.pid startup
633         // we may have the console turned on and want to keep it that way
634         // so we use a latching system
635         if ( g_GamesDialog.m_bForceLogConsole ) {
636                 Sys_LogFile( true );
637                 g_Console_enableLogging = true;
638                 g_GamesDialog.m_bForceLogConsole = false;
639         }
640
641
642         Radiant_Initialise();
643
644         global_accel_init();
645
646         user_shortcuts_init();
647
648         g_pParentWnd = 0;
649         g_pParentWnd = new MainFrame();
650
651         hide_splash();
652
653         if ( mapname != NULL ) {
654                 Map_LoadFile( mapname );
655         }
656         else if ( g_bLoadLastMap && !g_strLastMap.empty() ) {
657                 Map_LoadFile( g_strLastMap.c_str() );
658         }
659         else
660         {
661                 Map_New();
662         }
663
664         // load up shaders now that we have the map loaded
665         // eviltypeguy
666         TextureBrowser_ShowStartupShaders( GlobalTextureBrowser() );
667
668
669         remove_local_pid();
670
671         gtk_main();
672
673         // avoid saving prefs when the app is minimized
674         if ( g_pParentWnd->IsSleeping() ) {
675                 globalOutputStream() << "Shutdown while sleeping, not saving prefs\n";
676                 g_preferences_globals.disable_ini = true;
677         }
678
679         Map_Free();
680
681         if ( !Map_Unnamed( g_map ) ) {
682                 g_strLastMap = Map_Name( g_map );
683         }
684
685         delete g_pParentWnd;
686
687         user_shortcuts_save();
688
689         global_accel_destroy();
690
691         Radiant_Shutdown();
692
693         // close the log file if any
694         Sys_LogFile( false );
695
696         return EXIT_SUCCESS;
697 }