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