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 MemberCaller0 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
65 #include "globaldefs.h"
67 #include "debugging/debugging.h"
71 #include "uilib/uilib.h"
76 #include "stream/stringstream.h"
77 #include "stream/textfilestream.h"
79 #include "gtkutil/messagebox.h"
80 #include "gtkutil/image.h"
82 #include "texwindow.h"
84 #include "mainframe.h"
86 #include "preferences.h"
87 #include "environment.h"
88 #include "referencecache.h"
89 #include "stacktrace.h"
98 void error_redirect( const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data ){
99 gboolean in_recursion;
103 in_recursion = ( log_level & G_LOG_FLAG_RECURSION ) != 0;
104 is_fatal = ( log_level & G_LOG_FLAG_FATAL ) != 0;
105 log_level = (GLogLevelFlags) ( log_level & G_LOG_LEVEL_MASK );
108 message = "(0) message";
112 strcpy( buf, domain );
121 case G_LOG_LEVEL_ERROR:
122 if ( in_recursion ) {
123 strcat( buf, "ERROR (recursed) **: " );
126 strcat( buf, "ERROR **: " );
129 case G_LOG_LEVEL_CRITICAL:
130 if ( in_recursion ) {
131 strcat( buf, "CRITICAL (recursed) **: " );
134 strcat( buf, "CRITICAL **: " );
137 case G_LOG_LEVEL_WARNING:
138 if ( in_recursion ) {
139 strcat( buf, "WARNING (recursed) **: " );
142 strcat( buf, "WARNING **: " );
145 case G_LOG_LEVEL_MESSAGE:
146 if ( in_recursion ) {
147 strcat( buf, "Message (recursed): " );
150 strcat( buf, "Message: " );
153 case G_LOG_LEVEL_INFO:
154 if ( in_recursion ) {
155 strcat( buf, "INFO (recursed): " );
158 strcat( buf, "INFO: " );
161 case G_LOG_LEVEL_DEBUG:
162 if ( in_recursion ) {
163 strcat( buf, "DEBUG (recursed): " );
166 strcat( buf, "DEBUG: " );
170 /* we are used for a log level that is not defined by GLib itself,
171 * try to make the best out of it.
173 if ( in_recursion ) {
174 strcat( buf, "LOG (recursed:" );
177 strcat( buf, "LOG (" );
180 gchar string[] = "0x00): ";
181 gchar *p = string + 2;
184 i = g_bit_nth_msf( log_level, -1 );
187 *p = '0' + ( i & 0xf );
192 strcat( buf, string );
195 strcat( buf, "): " );
199 strcat( buf, message );
201 strcat( buf, "\naborting...\n" );
208 globalErrorStream() << buf << "\n";
211 ERROR_MESSAGE( "GTK+ error: " << buf );
215 #if GDEF_COMPILER_MSVC && GDEF_DEBUG
220 #if GDEF_COMPILER_MSVC && GDEF_DEBUG
221 _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
229 Lock() : m_locked( false ){
237 bool locked() const {
246 ScopedLock( Lock& lock ) : m_lock( lock ){
254 class LineLimitedTextOutputStream : public TextOutputStream
256 TextOutputStream& outputStream;
259 LineLimitedTextOutputStream( TextOutputStream& outputStream, std::size_t count )
260 : outputStream( outputStream ), count( count ){
262 std::size_t write( const char* buffer, std::size_t length ){
264 const char* p = buffer;
265 const char* end = buffer + length;
268 p = std::find( p, end, '\n' );
273 if ( --count == 0 ) {
278 outputStream.write( buffer, length );
284 class PopupDebugMessageHandler : public DebugMessageHandler
286 StringOutputStream m_buffer;
289 TextOutputStream& getOutputStream(){
290 if ( !m_lock.locked() ) {
293 return globalErrorStream();
295 bool handleMessage(){
296 getOutputStream() << "----------------\n";
297 LineLimitedTextOutputStream outputStream( getOutputStream(), 24 );
298 write_stack_trace( outputStream );
299 getOutputStream() << "----------------\n";
300 globalErrorStream() << m_buffer.c_str();
301 if ( !m_lock.locked() ) {
302 ScopedLock lock( m_lock );
304 m_buffer << "Break into the debugger?\n";
305 bool handled = ui::alert(ui::root, m_buffer.c_str(), RADIANT_NAME " - Runtime Error", ui::alert_type::YESNO, ui::alert_icon::Error) == ui::alert_response::NO;
309 m_buffer << "Please report this error to the developers\n";
310 ui::alert(ui::root, m_buffer.c_str(), RADIANT_NAME " - Runtime Error", ui::alert_type::OK, ui::alert_icon::Error);
318 typedef Static<PopupDebugMessageHandler> GlobalPopupDebugMessageHandler;
321 GlobalErrorStream::instance().setOutputStream( getSysPrintErrorStream() );
322 GlobalOutputStream::instance().setOutputStream( getSysPrintOutputStream() );
326 g_strSettingsPath = environment_get_home_path();
328 Q_mkdir( g_strSettingsPath.c_str() );
330 g_strAppFilePath = environment_get_app_filepath();
331 g_strAppPath = environment_get_app_path();
332 g_strLibPath = environment_get_lib_path();
333 g_strDataPath = environment_get_data_path();
335 // radiant is installed in the parent dir of "tools/"
336 // NOTE: this is not very easy for debugging
337 // maybe add options to lookup in several places?
338 // (for now I had to create symlinks)
340 StringOutputStream path( 256 );
341 path << g_strDataPath.c_str() << "bitmaps/";
342 BitmapsPath_set( path.c_str() );
345 // we will set this right after the game selection is done
346 g_strGameToolsPath = g_strDataPath;
349 bool check_version_file( const char* filename, const char* version ){
350 TextFileInputStream file( filename );
351 if ( !file.failed() ) {
353 buf[file.read( buf, 9 )] = '\0';
355 // chomp it (the hard way)
357 while ( buf[chomp] >= '0' && buf[chomp] <= '9' )
361 return string_equal( buf, version );
366 void create_global_pid(){
368 the global prefs loading / game selection dialog might fail for any reason we don't know about
369 we need to catch when it happens, to cleanup the stateful prefs which might be killing it
370 and to turn on console logging for lookup of the problem
371 this is the first part of the two step .pid system
372 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
374 StringOutputStream g_pidFile( 256 ); ///< the global .pid file (only for global part of the startup)
376 g_pidFile << SettingsPath_get() << "radiant.pid";
379 pid = fopen( g_pidFile.c_str(), "r" );
383 if ( remove( g_pidFile.c_str() ) == -1 ) {
384 StringOutputStream msg( 256 );
385 msg << "WARNING: Could not delete " << g_pidFile.c_str();
386 ui::alert( ui::root, msg.c_str(), RADIANT_NAME, ui::alert_type::OK, ui::alert_icon::Error );
389 // in debug, never prompt to clean registry, turn console logging auto after a failed start
391 StringOutputStream msg(256);
392 msg << RADIANT_NAME " failed to start properly the last time it was run.\n"
393 "The failure may be related to current global preferences.\n"
394 "Do you want to reset global preferences to defaults?";
396 if (ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
397 g_GamesDialog.Reset();
401 msg << "Logging console output to " << SettingsPath_get()
402 << "radiant.log\nRefer to the log if " RADIANT_NAME " fails to start again.";
404 ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Console Log", ui::alert_type::OK);
407 // set without saving, the class is not in a coherent state yet
408 // just do the value change and call to start logging, CGamesDialog will pickup when relevant
409 g_GamesDialog.m_bForceLogConsole = true;
410 Sys_EnableLogFile( true );
413 // create a primary .pid for global init run
414 pid = fopen( g_pidFile.c_str(), "w" );
420 void remove_global_pid(){
421 StringOutputStream g_pidFile( 256 );
422 g_pidFile << SettingsPath_get() << "radiant.pid";
425 if ( remove( g_pidFile.c_str() ) == -1 ) {
426 StringOutputStream msg( 256 );
427 msg << "WARNING: Could not delete " << g_pidFile.c_str();
428 ui::alert( ui::root, msg.c_str(), RADIANT_NAME, ui::alert_type::OK, ui::alert_icon::Error );
433 now the secondary game dependant .pid file
434 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
436 void create_local_pid(){
437 StringOutputStream g_pidGameFile( 256 ); ///< the game-specific .pid file
438 g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
440 FILE *pid = fopen( g_pidGameFile.c_str(), "r" );
443 if ( remove( g_pidGameFile.c_str() ) == -1 ) {
444 StringOutputStream msg;
445 msg << "WARNING: Could not delete " << g_pidGameFile.c_str();
446 ui::alert( ui::root, msg.c_str(), RADIANT_NAME, ui::alert_type::OK, ui::alert_icon::Error );
449 // in debug, never prompt to clean registry, turn console logging auto after a failed start
451 StringOutputStream msg;
452 msg << RADIANT_NAME " failed to start properly the last time it was run.\n"
453 "The failure may be caused by current preferences.\n"
454 "Do you want to reset all preferences to defaults?";
456 if (ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
461 msg << "Logging console output to " << SettingsPath_get()
462 << "radiant.log\nRefer to the log if " RADIANT_NAME " fails to start again.";
464 ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Console Log", ui::alert_type::OK);
467 // force console logging on! (will go in prefs too)
468 g_GamesDialog.m_bForceLogConsole = true;
469 Sys_EnableLogFile( true );
473 // create one, will remove right after entering message loop
474 pid = fopen( g_pidGameFile.c_str(), "w" );
483 now the secondary game dependant .pid file
484 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
486 void remove_local_pid(){
487 StringOutputStream g_pidGameFile( 256 );
488 g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
489 remove( g_pidGameFile.c_str() );
492 void user_shortcuts_init(){
493 StringOutputStream path( 256 );
494 path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
495 LoadCommandMap( path.c_str() );
496 SaveCommandMap( path.c_str() );
499 void user_shortcuts_save(){
500 StringOutputStream path( 256 );
501 path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
502 SaveCommandMap( path.c_str() );
505 void add_local_rc_files(){
506 #define GARUX_DISABLE_GTKTHEME
507 #ifndef GARUX_DISABLE_GTKTHEME
508 /* FIXME: HACK: not GTK3 compatible
509 https://developer.gnome.org/gtk2/stable/gtk2-Resource-Files.html#gtk-rc-add-default-file
510 https://developer.gnome.org/gtk3/stable/gtk3-Resource-Files.html#gtk-rc-add-default-file
511 > gtk_rc_add_default_file has been deprecated since version 3.0 and should not be used in newly-written code.
512 > Use GtkStyleContext with a custom GtkStyleProvider instead
516 StringOutputStream path( 512 );
517 path << AppPath_get() << ".gtkrc-2.0.radiant";
518 gtk_rc_add_default_file( path.c_str() );
522 StringOutputStream path( 512 );
523 path << AppPath_get() << ".gtkrc-2.0.win";
524 gtk_rc_add_default_file( path.c_str() );
527 #endif // GARUX_DISABLE_GTKTHEME
530 /* HACK: If ui::main is not called yet,
531 gtk_main_quit will not quit, so tell main
532 to not call ui::main. This happens when a
533 map is loaded from command line and require
534 a restart because of wrong format.
535 Delete this when the code to not have to
536 restart to load another format is merged. */
537 bool g_dontStart = false;
539 int main( int argc, char* argv[] ){
541 // HACK: force legacy GL backend as we don't support GL3 yet
542 setenv("GDK_GL", "LEGACY", 0);
543 #if GDEF_OS_LINUX || GDEF_OS_BSD
544 setenv("GDK_BACKEND", "x11", 0);
546 #endif // GTK_TARGET == 3
553 lib = LoadLibrary( "dwmapi.dll" );
555 void ( WINAPI *qDwmEnableComposition )( bool bEnable ) = ( void (WINAPI *) ( bool bEnable ) )GetProcAddress( lib, "DwmEnableComposition" );
556 if ( qDwmEnableComposition ) {
558 for ( int i = 1; i < argc; ++i ){
559 if ( !stricmp( argv[i], "-aero" ) ){
561 qDwmEnableComposition( TRUE );
567 qDwmEnableComposition( FALSE );
575 const char* mapname = NULL;
578 StringOutputStream mapname_buffer( 256 );
581 char const *error = NULL;
583 if ( !ui::init( &argc, &argv, "<filename.map>", &error) ) {
584 g_print( "%s\n", error );
588 // Gtk already removed parsed `--options`
590 if ( strlen( argv[1] ) > 1 ) {
593 if ( g_str_has_suffix( mapname, ".map" ) ) {
594 if ( !g_path_is_absolute( mapname ) ) {
595 mapname = g_build_filename( g_get_current_dir(), mapname, NULL );
599 mapname_buffer << PathCleaned( mapname );
600 mapname = mapname_buffer.c_str();
604 g_print( "bad file name, will not load: %s\n", mapname );
610 g_print ( "%s\n", "too many arguments" );
614 // redirect Gtk warnings to the console
615 g_log_set_handler( "Gdk", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
616 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
617 g_log_set_handler( "Gtk", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
618 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
619 g_log_set_handler( "GtkGLExt", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
620 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
621 g_log_set_handler( "GLib", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
622 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
623 g_log_set_handler( 0, (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
624 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
626 GlobalDebugMessageHandler::instance().setHandler( GlobalPopupDebugMessageHandler::instance() );
628 environment_init(argc, (char const **) argv);
632 add_local_rc_files();
638 GlobalPreferences_Init();
640 g_GamesDialog.Init();
642 g_strGameToolsPath = g_pGameDescription->mGameToolsPath;
646 g_Preferences.Init(); // must occur before create_local_pid() to allow preferences to be reset
650 // in a very particular post-.pid startup
651 // we may have the console turned on and want to keep it that way
652 // so we use a latching system
653 if ( g_GamesDialog.m_bForceLogConsole ) {
654 Sys_EnableLogFile( true );
655 g_Console_enableLogging = true;
656 g_GamesDialog.m_bForceLogConsole = false;
660 Radiant_Initialise();
662 user_shortcuts_init();
665 g_pParentWnd = new MainFrame();
670 if( openCmdMap && *openCmdMap ){
671 Map_LoadFile( openCmdMap );
675 if ( mapname != NULL ) {
676 Map_LoadFile( mapname );
678 else if ( g_bLoadLastMap && !g_strLastMap.empty() ) {
679 Map_LoadFile( g_strLastMap.c_str() );
686 // load up shaders now that we have the map loaded
688 TextureBrowser_ShowStartupShaders( GlobalTextureBrowser() );
693 /* HACK: If ui::main is not called yet,
694 gtk_main_quit will not quit, so tell main
695 to not call ui::main. This happens when a
696 map is loaded from command line and require
697 a restart because of wrong format.
698 Delete this when the code to not have to
699 restart to load another format is merged. */
705 // avoid saving prefs when the app is minimized
706 if ( g_pParentWnd->IsSleeping() ) {
707 globalOutputStream() << "Shutdown while sleeping, not saving prefs\n";
708 g_preferences_globals.disable_ini = true;
713 if ( !Map_Unnamed( g_map ) ) {
714 g_strLastMap = Map_Name( g_map );
719 user_shortcuts_save();
723 // close the log file if any
724 Sys_EnableLogFile( false );