2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
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.
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.
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
22 /*! \mainpage GtkRadiant Documentation Index
24 \section intro_sec Introduction
26 This documentation is generated from comments in the source code.
28 \section links_sec Useful Links
30 \link include/itextstream.h include/itextstream.h \endlink - Global output and error message streams, similar to std::cout and std::cerr. \n
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
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
42 ::CopiedString - automatic string memory management \n
43 Array - automatic array memory management \n
44 HashTable - generic hashtable, similar to std::hash_map \n
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
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
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
58 DefaultAllocator - Memory allocation using new/delete, compliant with std::allocator interface \n
60 \link debugging/debugging.h debugging/debugging.h \endlink - Debugging macros \n
68 #include "debugging/debugging.h"
72 #include <gtk/gtkmain.h>
77 #include "stream/stringstream.h"
78 #include "stream/textfilestream.h"
80 #include "gtkutil/messagebox.h"
81 #include "gtkutil/image.h"
83 #include "texwindow.h"
85 #include "mainframe.h"
87 #include "preferences.h"
88 #include "environment.h"
89 #include "referencecache.h"
90 #include "stacktrace.h"
99 void error_redirect( const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data ){
100 gboolean in_recursion;
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 );
109 message = "(0) message";
113 strcpy( buf, domain );
122 case G_LOG_LEVEL_ERROR:
123 if ( in_recursion ) {
124 strcat( buf, "ERROR (recursed) **: " );
127 strcat( buf, "ERROR **: " );
130 case G_LOG_LEVEL_CRITICAL:
131 if ( in_recursion ) {
132 strcat( buf, "CRITICAL (recursed) **: " );
135 strcat( buf, "CRITICAL **: " );
138 case G_LOG_LEVEL_WARNING:
139 if ( in_recursion ) {
140 strcat( buf, "WARNING (recursed) **: " );
143 strcat( buf, "WARNING **: " );
146 case G_LOG_LEVEL_MESSAGE:
147 if ( in_recursion ) {
148 strcat( buf, "Message (recursed): " );
151 strcat( buf, "Message: " );
154 case G_LOG_LEVEL_INFO:
155 if ( in_recursion ) {
156 strcat( buf, "INFO (recursed): " );
159 strcat( buf, "INFO: " );
162 case G_LOG_LEVEL_DEBUG:
163 if ( in_recursion ) {
164 strcat( buf, "DEBUG (recursed): " );
167 strcat( buf, "DEBUG: " );
171 /* we are used for a log level that is not defined by GLib itself,
172 * try to make the best out of it.
174 if ( in_recursion ) {
175 strcat( buf, "LOG (recursed:" );
178 strcat( buf, "LOG (" );
181 gchar string[] = "0x00): ";
182 gchar *p = string + 2;
185 i = g_bit_nth_msf( log_level, -1 );
188 *p = '0' + ( i & 0xf );
193 strcat( buf, string );
196 strcat( buf, "): " );
200 strcat( buf, message );
202 strcat( buf, "\naborting...\n" );
209 globalErrorStream() << buf << "\n";
211 // FIXME why are warnings is_fatal?
215 ERROR_MESSAGE( "GTK+ error: " << buf );
218 #if defined ( _DEBUG ) && defined ( WIN32 ) && defined ( _MSC_VER )
223 #if defined ( _DEBUG ) && defined ( WIN32 ) && defined ( _MSC_VER )
224 _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
232 Lock() : m_locked( false ){
240 bool locked() const {
249 ScopedLock( Lock& lock ) : m_lock( lock ){
257 class LineLimitedTextOutputStream : public TextOutputStream
259 TextOutputStream& outputStream;
262 LineLimitedTextOutputStream( TextOutputStream& outputStream, std::size_t count )
263 : outputStream( outputStream ), count( count ){
265 std::size_t write( const char* buffer, std::size_t length ){
267 const char* p = buffer;
268 const char* end = buffer + length;
271 p = std::find( p, end, '\n' );
276 if ( --count == 0 ) {
281 outputStream.write( buffer, length );
287 class PopupDebugMessageHandler : public DebugMessageHandler
289 StringOutputStream m_buffer;
292 TextOutputStream& getOutputStream(){
293 if ( !m_lock.locked() ) {
296 return globalErrorStream();
298 bool handleMessage(){
299 getOutputStream() << "----------------\n";
300 LineLimitedTextOutputStream outputStream( getOutputStream(), 24 );
301 write_stack_trace( outputStream );
302 getOutputStream() << "----------------\n";
303 globalErrorStream() << m_buffer.c_str();
304 if ( !m_lock.locked() ) {
305 ScopedLock lock( m_lock );
307 m_buffer << "Break into the debugger?\n";
308 bool handled = gtk_MessageBox( 0, m_buffer.c_str(), "Radiant - Runtime Error", eMB_YESNO, eMB_ICONERROR ) == eIDNO;
312 m_buffer << "Please report this error to the developers\n";
313 gtk_MessageBox( 0, m_buffer.c_str(), "Radiant - Runtime Error", eMB_OK, eMB_ICONERROR );
321 typedef Static<PopupDebugMessageHandler> GlobalPopupDebugMessageHandler;
324 GlobalErrorStream::instance().setOutputStream( getSysPrintErrorStream() );
325 GlobalOutputStream::instance().setOutputStream( getSysPrintOutputStream() );
329 const char* home = environment_get_home_path();
333 StringOutputStream path( 256 );
334 path << home << "1." << RADIANT_MAJOR_VERSION "." << RADIANT_MINOR_VERSION << '/';
335 g_strSettingsPath = path.c_str();
338 Q_mkdir( g_strSettingsPath.c_str() );
340 g_strAppPath = environment_get_app_path();
342 // radiant is installed in the parent dir of "tools/"
343 // NOTE: this is not very easy for debugging
344 // maybe add options to lookup in several places?
345 // (for now I had to create symlinks)
347 StringOutputStream path( 256 );
348 path << g_strAppPath.c_str() << "bitmaps/";
349 BitmapsPath_set( path.c_str() );
352 // we will set this right after the game selection is done
353 g_strGameToolsPath = g_strAppPath;
356 bool check_version_file( const char* filename, const char* version ){
357 TextFileInputStream file( filename );
358 if ( !file.failed() ) {
360 buf[file.read( buf, 9 )] = '\0';
362 // chomp it (the hard way)
364 while ( buf[chomp] >= '0' && buf[chomp] <= '9' )
368 return string_equal( buf, version );
373 bool check_version(){
374 // a safe check to avoid people running broken installations
375 // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
376 // make something idiot proof and someone will make better idiots, this may be overkill
377 // let's leave it disabled in debug mode in any case
378 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=431
380 #define CHECK_VERSION
383 // locate and open RADIANT_MAJOR and RADIANT_MINOR
384 bool bVerIsGood = true;
386 StringOutputStream ver_file_name( 256 );
387 ver_file_name << AppPath_get() << "RADIANT_MAJOR";
388 bVerIsGood = check_version_file( ver_file_name.c_str(), RADIANT_MAJOR_VERSION );
391 StringOutputStream ver_file_name( 256 );
392 ver_file_name << AppPath_get() << "RADIANT_MINOR";
393 bVerIsGood = check_version_file( ver_file_name.c_str(), RADIANT_MINOR_VERSION );
397 StringOutputStream msg( 256 );
398 msg << "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n"
399 "Make sure you run the right/latest editor binary you installed\n"
401 gtk_MessageBox( 0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONDEFAULT );
409 void create_global_pid(){
411 the global prefs loading / game selection dialog might fail for any reason we don't know about
412 we need to catch when it happens, to cleanup the stateful prefs which might be killing it
413 and to turn on console logging for lookup of the problem
414 this is the first part of the two step .pid system
415 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
417 StringOutputStream g_pidFile( 256 ); ///< the global .pid file (only for global part of the startup)
419 g_pidFile << SettingsPath_get() << "radiant.pid";
422 pid = fopen( g_pidFile.c_str(), "r" );
426 if ( remove( g_pidFile.c_str() ) == -1 ) {
427 StringOutputStream msg( 256 );
428 msg << "WARNING: Could not delete " << g_pidFile.c_str();
429 gtk_MessageBox( 0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
432 // in debug, never prompt to clean registry, turn console logging auto after a failed start
433 #if !defined( _DEBUG )
434 StringOutputStream msg( 256 );
435 msg << "Radiant failed to start properly the last time it was run.\n"
436 "The failure may be related to current global preferences.\n"
437 "Do you want to reset global preferences to defaults?";
439 if ( gtk_MessageBox( 0, msg.c_str(), "Radiant - Startup Failure", eMB_YESNO, eMB_ICONQUESTION ) == eIDYES ) {
440 g_GamesDialog.Reset();
444 msg << "Logging console output to " << SettingsPath_get() << "radiant.log\nRefer to the log if Radiant fails to start again.";
446 gtk_MessageBox( 0, msg.c_str(), "Radiant - Console Log", eMB_OK );
449 // set without saving, the class is not in a coherent state yet
450 // just do the value change and call to start logging, CGamesDialog will pickup when relevant
451 g_GamesDialog.m_bForceLogConsole = true;
455 // create a primary .pid for global init run
456 pid = fopen( g_pidFile.c_str(), "w" );
462 void remove_global_pid(){
463 StringOutputStream g_pidFile( 256 );
464 g_pidFile << SettingsPath_get() << "radiant.pid";
467 if ( remove( g_pidFile.c_str() ) == -1 ) {
468 StringOutputStream msg( 256 );
469 msg << "WARNING: Could not delete " << g_pidFile.c_str();
470 gtk_MessageBox( 0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
475 now the secondary game dependant .pid file
476 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
478 void create_local_pid(){
479 StringOutputStream g_pidGameFile( 256 ); ///< the game-specific .pid file
480 g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
482 FILE *pid = fopen( g_pidGameFile.c_str(), "r" );
485 if ( remove( g_pidGameFile.c_str() ) == -1 ) {
486 StringOutputStream msg;
487 msg << "WARNING: Could not delete " << g_pidGameFile.c_str();
488 gtk_MessageBox( 0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
491 // in debug, never prompt to clean registry, turn console logging auto after a failed start
492 #if !defined( _DEBUG )
493 StringOutputStream msg;
494 msg << "Radiant failed to start properly the last time it was run.\n"
495 "The failure may be caused by current preferences.\n"
496 "Do you want to reset all preferences to defaults?";
498 if ( gtk_MessageBox( 0, msg.c_str(), "Radiant - Startup Failure", eMB_YESNO, eMB_ICONQUESTION ) == eIDYES ) {
503 msg << "Logging console output to " << SettingsPath_get() << "radiant.log\nRefer to the log if Radiant fails to start again.";
505 gtk_MessageBox( 0, msg.c_str(), "Radiant - Console Log", eMB_OK );
508 // force console logging on! (will go in prefs too)
509 g_GamesDialog.m_bForceLogConsole = true;
514 // create one, will remove right after entering message loop
515 pid = fopen( g_pidGameFile.c_str(), "w" );
524 now the secondary game dependant .pid file
525 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
527 void remove_local_pid(){
528 StringOutputStream g_pidGameFile( 256 );
529 g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
530 remove( g_pidGameFile.c_str() );
533 void user_shortcuts_init(){
534 StringOutputStream path( 256 );
535 path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
536 LoadCommandMap( path.c_str() );
537 SaveCommandMap( path.c_str() );
540 void user_shortcuts_save(){
541 StringOutputStream path( 256 );
542 path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
543 SaveCommandMap( path.c_str() );
546 void add_local_rc_files(){
548 StringOutputStream path( 512 );
549 path << AppPath_get() << ".gtkrc-2.0.radiant";
550 gtk_rc_add_default_file( path.c_str() );
554 StringOutputStream path( 512 );
555 path << AppPath_get() << ".gtkrc-2.0.win";
556 gtk_rc_add_default_file( path.c_str() );
561 int main( int argc, char* argv[] ){
568 lib = LoadLibrary( "dwmapi.dll" );
570 void ( WINAPI *qDwmEnableComposition )( bool bEnable ) = ( void (WINAPI *) ( bool bEnable ) )GetProcAddress( lib, "DwmEnableComposition" );
571 if ( qDwmEnableComposition ) {
573 for ( int i = 1; i < argc; ++i ){
574 if ( !stricmp( argv[i], "-aero" ) ){
576 qDwmEnableComposition( TRUE );
582 qDwmEnableComposition( FALSE );
590 gtk_disable_setlocale();
592 gtk_init( &argc, &argv );
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 );
606 GlobalDebugMessageHandler::instance().setHandler( GlobalPopupDebugMessageHandler::instance() );
608 environment_init( argc, argv );
612 add_local_rc_files();
614 if ( !check_version() ) {
622 GlobalPreferences_Init();
624 g_GamesDialog.Init();
626 g_strGameToolsPath = g_pGameDescription->mGameToolsPath;
630 g_Preferences.Init(); // must occur before create_local_pid() to allow preferences to be reset
634 // in a very particular post-.pid startup
635 // we may have the console turned on and want to keep it that way
636 // so we use a latching system
637 if ( g_GamesDialog.m_bForceLogConsole ) {
639 g_Console_enableLogging = true;
640 g_GamesDialog.m_bForceLogConsole = false;
644 Radiant_Initialise();
648 user_shortcuts_init();
651 g_pParentWnd = new MainFrame();
656 if( openCmdMap && *openCmdMap ){
657 Map_LoadFile( openCmdMap );
661 if ( g_bLoadLastMap && !g_strLastMap.empty() ) {
662 Map_LoadFile( g_strLastMap.c_str() );
669 // load up shaders now that we have the map loaded
671 TextureBrowser_ShowStartupShaders( GlobalTextureBrowser() );
678 // avoid saving prefs when the app is minimized
679 if ( g_pParentWnd->IsSleeping() ) {
680 globalOutputStream() << "Shutdown while sleeping, not saving prefs\n";
681 g_preferences_globals.disable_ini = true;
686 if ( !Map_Unnamed( g_map ) ) {
687 g_strLastMap = Map_Name( g_map );
692 user_shortcuts_save();
694 global_accel_destroy();
698 // close the log file if any
699 Sys_LogFile( false );