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)
101 gboolean in_recursion;
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);
110 message = "(0) message";
113 strcpy (buf, domain);
120 case G_LOG_LEVEL_ERROR:
122 strcat (buf, "ERROR (recursed) **: ");
124 strcat (buf, "ERROR **: ");
126 case G_LOG_LEVEL_CRITICAL:
128 strcat (buf, "CRITICAL (recursed) **: ");
130 strcat (buf, "CRITICAL **: ");
132 case G_LOG_LEVEL_WARNING:
134 strcat (buf, "WARNING (recursed) **: ");
136 strcat (buf, "WARNING **: ");
138 case G_LOG_LEVEL_MESSAGE:
140 strcat (buf, "Message (recursed): ");
142 strcat (buf, "Message: ");
144 case G_LOG_LEVEL_INFO:
146 strcat (buf, "INFO (recursed): ");
148 strcat (buf, "INFO: ");
150 case G_LOG_LEVEL_DEBUG:
152 strcat (buf, "DEBUG (recursed): ");
154 strcat (buf, "DEBUG: ");
157 /* we are used for a log level that is not defined by GLib itself,
158 * try to make the best out of it.
161 strcat (buf, "LOG (recursed:");
163 strcat (buf, "LOG (");
166 gchar string[] = "0x00): ";
167 gchar *p = string + 2;
170 i = g_bit_nth_msf (log_level, -1);
173 *p = '0' + (i & 0xf);
177 strcat (buf, string);
182 strcat (buf, message);
184 strcat (buf, "\naborting...\n");
189 globalErrorStream() << buf << "\n";
191 // FIXME why are warnings is_fatal?
195 ERROR_MESSAGE("GTK+ error: " << buf);
198 #if defined (_DEBUG) && defined (WIN32) && defined (_MSC_VER)
204 #if defined (_DEBUG) && defined (WIN32) && defined (_MSC_VER)
205 _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
213 Lock() : m_locked(false)
234 ScopedLock(Lock& lock) : m_lock(lock)
244 class LineLimitedTextOutputStream : public TextOutputStream
246 TextOutputStream& outputStream;
249 LineLimitedTextOutputStream(TextOutputStream& outputStream, std::size_t count)
250 : outputStream(outputStream), count(count)
253 std::size_t write(const char* buffer, std::size_t length)
257 const char* p = buffer;
258 const char* end = buffer+length;
261 p = std::find(p, end, '\n');
273 outputStream.write(buffer, length);
279 class PopupDebugMessageHandler : public DebugMessageHandler
281 StringOutputStream m_buffer;
284 TextOutputStream& getOutputStream()
290 return globalErrorStream();
294 getOutputStream() << "----------------\n";
295 LineLimitedTextOutputStream outputStream(getOutputStream(), 24);
296 write_stack_trace(outputStream);
297 getOutputStream() << "----------------\n";
298 globalErrorStream() << m_buffer.c_str();
301 ScopedLock lock(m_lock);
303 m_buffer << "Break into the debugger?\n";
304 bool handled = gtk_MessageBox(0, m_buffer.c_str(), "Radiant - Runtime Error", eMB_YESNO, eMB_ICONERROR) == eIDNO;
308 m_buffer << "Please report this error to the developers\n";
309 gtk_MessageBox(0, m_buffer.c_str(), "Radiant - Runtime Error", eMB_OK, eMB_ICONERROR);
317 typedef Static<PopupDebugMessageHandler> GlobalPopupDebugMessageHandler;
321 GlobalErrorStream::instance().setOutputStream(getSysPrintErrorStream());
322 GlobalOutputStream::instance().setOutputStream(getSysPrintOutputStream());
327 const char* home = environment_get_home_path();
331 StringOutputStream path(256);
332 path << home << "1." << RADIANT_MAJOR_VERSION "." << RADIANT_MINOR_VERSION << '/';
333 g_strSettingsPath = path.c_str();
336 Q_mkdir(g_strSettingsPath.c_str());
338 g_strAppPath = environment_get_app_path();
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)
345 StringOutputStream path(256);
346 path << g_strAppPath.c_str() << "bitmaps/";
347 BitmapsPath_set(path.c_str());
350 // we will set this right after the game selection is done
351 g_strGameToolsPath = g_strAppPath;
354 bool check_version_file(const char* filename, const char* version)
356 TextFileInputStream file(filename);
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);
375 // a safe check to avoid people running broken installations
376 // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
377 // make something idiot proof and someone will make better idiots, this may be overkill
378 // let's leave it disabled in debug mode in any case
379 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=431
381 #define CHECK_VERSION
384 // locate and open RADIANT_MAJOR and RADIANT_MINOR
385 bool bVerIsGood = true;
387 StringOutputStream ver_file_name(256);
388 ver_file_name << AppPath_get() << "RADIANT_MAJOR";
389 bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MAJOR_VERSION);
392 StringOutputStream ver_file_name(256);
393 ver_file_name << AppPath_get() << "RADIANT_MINOR";
394 bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MINOR_VERSION);
399 StringOutputStream msg(256);
400 msg << "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n"
401 "Make sure you run the right/latest editor binary you installed\n"
403 gtk_MessageBox(0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONDEFAULT);
411 void create_global_pid()
414 the global prefs loading / game selection dialog might fail for any reason we don't know about
415 we need to catch when it happens, to cleanup the stateful prefs which might be killing it
416 and to turn on console logging for lookup of the problem
417 this is the first part of the two step .pid system
418 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
420 StringOutputStream g_pidFile(256); ///< the global .pid file (only for global part of the startup)
422 g_pidFile << SettingsPath_get() << "radiant.pid";
425 pid = fopen (g_pidFile.c_str(), "r");
430 if (remove (g_pidFile.c_str()) == -1)
432 StringOutputStream msg(256);
433 msg << "WARNING: Could not delete " << g_pidFile.c_str();
434 gtk_MessageBox (0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
437 // in debug, never prompt to clean registry, turn console logging auto after a failed start
439 StringOutputStream msg(256);
440 msg << "Radiant failed to start properly the last time it was run.\n"
441 "The failure may be related to current global preferences.\n"
442 "Do you want to reset global preferences to defaults?";
444 if (gtk_MessageBox (0, msg.c_str(), "Radiant - Startup Failure", eMB_YESNO, eMB_ICONQUESTION) == eIDYES)
446 g_GamesDialog.Reset();
450 msg << "Logging console output to " << SettingsPath_get() << "radiant.log\nRefer to the log if Radiant fails to start again.";
452 gtk_MessageBox (0, msg.c_str(), "Radiant - Console Log", eMB_OK);
455 // set without saving, the class is not in a coherent state yet
456 // just do the value change and call to start logging, CGamesDialog will pickup when relevant
457 g_GamesDialog.m_bForceLogConsole = true;
461 // create a primary .pid for global init run
462 pid = fopen (g_pidFile.c_str(), "w");
467 void remove_global_pid()
469 StringOutputStream g_pidFile(256);
470 g_pidFile << SettingsPath_get() << "radiant.pid";
473 if (remove (g_pidFile.c_str()) == -1)
475 StringOutputStream msg(256);
476 msg << "WARNING: Could not delete " << g_pidFile.c_str();
477 gtk_MessageBox (0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
482 now the secondary game dependant .pid file
483 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
485 void create_local_pid()
487 StringOutputStream g_pidGameFile(256); ///< the game-specific .pid file
488 g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
490 FILE *pid = fopen (g_pidGameFile.c_str(), "r");
494 if (remove (g_pidGameFile.c_str()) == -1)
496 StringOutputStream msg;
497 msg << "WARNING: Could not delete " << g_pidGameFile.c_str();
498 gtk_MessageBox (0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
501 // in debug, never prompt to clean registry, turn console logging auto after a failed start
503 StringOutputStream msg;
504 msg << "Radiant failed to start properly the last time it was run.\n"
505 "The failure may be caused by current preferences.\n"
506 "Do you want to reset all preferences to defaults?";
508 if (gtk_MessageBox (0, msg.c_str(), "Radiant - Startup Failure", eMB_YESNO, eMB_ICONQUESTION) == eIDYES)
514 msg << "Logging console output to " << SettingsPath_get() << "radiant.log\nRefer to the log if Radiant fails to start again.";
516 gtk_MessageBox (0, msg.c_str(), "Radiant - Console Log", eMB_OK);
519 // force console logging on! (will go in prefs too)
520 g_GamesDialog.m_bForceLogConsole = true;
525 // create one, will remove right after entering message loop
526 pid = fopen (g_pidGameFile.c_str(), "w");
534 now the secondary game dependant .pid file
535 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
537 void remove_local_pid()
539 StringOutputStream g_pidGameFile(256);
540 g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
541 remove(g_pidGameFile.c_str());
544 void user_shortcuts_init()
546 StringOutputStream path(256);
547 path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
548 LoadCommandMap(path.c_str());
549 SaveCommandMap(path.c_str());
552 void user_shortcuts_save()
554 StringOutputStream path(256);
555 path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
556 SaveCommandMap(path.c_str());
559 int main (int argc, char* argv[])
567 lib = LoadLibrary("dwmapi.dll");
570 void (WINAPI *DwmEnableComposition) (bool bEnable) = (void (WINAPI *) (bool bEnable)) GetProcAddress(lib, "DwmEnableComposition");
571 if(DwmEnableComposition)
572 DwmEnableComposition(FALSE);
577 gtk_disable_setlocale();
578 gtk_init(&argc, &argv);
580 // redirect Gtk warnings to the console
581 g_log_set_handler ("Gdk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
582 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), error_redirect, 0);
583 g_log_set_handler ("Gtk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
584 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), error_redirect, 0);
585 g_log_set_handler ("GtkGLExt", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
586 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), error_redirect, 0);
587 g_log_set_handler ("GLib", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
588 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), error_redirect, 0);
589 g_log_set_handler (0, (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
590 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), error_redirect, 0);
592 GlobalDebugMessageHandler::instance().setHandler(GlobalPopupDebugMessageHandler::instance());
594 environment_init(argc, argv);
607 GlobalPreferences_Init();
609 g_GamesDialog.Init();
611 g_strGameToolsPath = g_pGameDescription->mGameToolsPath;
615 g_Preferences.Init(); // must occur before create_local_pid() to allow preferences to be reset
619 // in a very particular post-.pid startup
620 // we may have the console turned on and want to keep it that way
621 // so we use a latching system
622 if (g_GamesDialog.m_bForceLogConsole)
625 g_Console_enableLogging = true;
626 g_GamesDialog.m_bForceLogConsole = false;
630 Radiant_Initialise();
634 user_shortcuts_init();
637 g_pParentWnd = new MainFrame();
641 if (g_bLoadLastMap && !g_strLastMap.empty())
643 Map_LoadFile(g_strLastMap.c_str());
650 // load up shaders now that we have the map loaded
652 TextureBrowser_ShowStartupShaders(GlobalTextureBrowser());
659 // avoid saving prefs when the app is minimized
660 if (g_pParentWnd->IsSleeping())
662 globalOutputStream() << "Shutdown while sleeping, not saving prefs\n";
663 g_preferences_globals.disable_ini = true;
668 if (!Map_Unnamed(g_map))
670 g_strLastMap = Map_Name(g_map);
675 user_shortcuts_save();
677 global_accel_destroy();
681 // close the log file if any