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"
69 #include "debugging/debugging.h"
73 #include "uilib/uilib.h"
78 #include "stream/stringstream.h"
79 #include "stream/textfilestream.h"
81 #include "gtkutil/messagebox.h"
82 #include "gtkutil/image.h"
84 #include "texwindow.h"
86 #include "mainframe.h"
88 #include "preferences.h"
89 #include "environment.h"
90 #include "referencecache.h"
91 #include "stacktrace.h"
101 void error_redirect(const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
103 gboolean in_recursion;
107 in_recursion = (log_level & G_LOG_FLAG_RECURSION) != 0;
108 is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0;
109 log_level = (GLogLevelFlags) (log_level & G_LOG_LEVEL_MASK);
112 message = "(0) message";
123 case G_LOG_LEVEL_ERROR:
125 strcat(buf, "ERROR (recursed) **: ");
127 strcat(buf, "ERROR **: ");
130 case G_LOG_LEVEL_CRITICAL:
132 strcat(buf, "CRITICAL (recursed) **: ");
134 strcat(buf, "CRITICAL **: ");
137 case G_LOG_LEVEL_WARNING:
139 strcat(buf, "WARNING (recursed) **: ");
141 strcat(buf, "WARNING **: ");
144 case G_LOG_LEVEL_MESSAGE:
146 strcat(buf, "Message (recursed): ");
148 strcat(buf, "Message: ");
151 case G_LOG_LEVEL_INFO:
153 strcat(buf, "INFO (recursed): ");
155 strcat(buf, "INFO: ");
158 case G_LOG_LEVEL_DEBUG:
160 strcat(buf, "DEBUG (recursed): ");
162 strcat(buf, "DEBUG: ");
166 /* we are used for a log level that is not defined by GLib itself,
167 * try to make the best out of it.
170 strcat(buf, "LOG (recursed:");
172 strcat(buf, "LOG (");
175 gchar string[] = "0x00): ";
176 gchar *p = string + 2;
179 i = g_bit_nth_msf(log_level, -1);
182 *p = '0' + (i & 0xf);
193 strcat(buf, message);
195 strcat(buf, "\naborting...\n");
201 globalErrorStream() << buf << "\n";
204 ERROR_MESSAGE("GTK+ error: " << buf);
208 #if GDEF_COMPILER_MSVC && GDEF_DEBUG
214 #if GDEF_COMPILER_MSVC && GDEF_DEBUG
215 _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
222 Lock() : m_locked(false)
245 ScopedLock(Lock &lock) : m_lock(lock)
256 class LineLimitedTextOutputStream : public TextOutputStream {
257 TextOutputStream &outputStream;
260 LineLimitedTextOutputStream(TextOutputStream &outputStream, std::size_t count)
261 : outputStream(outputStream), count(count)
265 std::size_t write(const char *buffer, std::size_t length)
268 const char *p = buffer;
269 const char *end = buffer + length;
271 p = std::find(p, end, '\n');
281 outputStream.write(buffer, length);
287 class PopupDebugMessageHandler : public DebugMessageHandler {
288 StringOutputStream m_buffer;
291 TextOutputStream &getOutputStream()
293 if (!m_lock.locked()) {
296 return globalErrorStream();
301 getOutputStream() << "----------------\n";
302 LineLimitedTextOutputStream outputStream(getOutputStream(), 24);
303 write_stack_trace(outputStream);
304 getOutputStream() << "----------------\n";
305 globalErrorStream() << m_buffer.c_str();
306 if (!m_lock.locked()) {
307 ScopedLock lock(m_lock);
309 m_buffer << "Break into the debugger?\n";
310 bool handled = ui::alert(ui::root, m_buffer.c_str(), "Radiant - Runtime Error", ui::alert_type::YESNO,
311 ui::alert_icon::Error) == ui::alert_response::NO;
315 m_buffer << "Please report this error to the developers\n";
316 ui::alert(ui::root, m_buffer.c_str(), "Radiant - Runtime Error", ui::alert_type::OK,
317 ui::alert_icon::Error);
325 typedef Static<PopupDebugMessageHandler> GlobalPopupDebugMessageHandler;
329 GlobalErrorStream::instance().setOutputStream(getSysPrintErrorStream());
330 GlobalOutputStream::instance().setOutputStream(getSysPrintOutputStream());
335 g_strSettingsPath = environment_get_home_path();
337 Q_mkdir(g_strSettingsPath.c_str());
339 g_strAppPath = environment_get_app_path();
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)
346 StringOutputStream path(256);
347 path << g_strAppPath.c_str() << "bitmaps/";
348 BitmapsPath_set(path.c_str());
351 // we will set this right after the game selection is done
352 g_strGameToolsPath = g_strAppPath;
355 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') {
369 return string_equal(buf, version);
376 // a safe check to avoid people running broken installations
377 // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
378 // make something idiot proof and someone will make better idiots, this may be overkill
379 // let's leave it disabled in debug mode in any case
380 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=431
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);
398 StringOutputStream msg(256);
400 << "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 ui::alert(ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Default);
408 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 ui::alert(ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Error);
432 // in debug, never prompt to clean registry, turn console logging auto after a failed start
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 (ui::alert(ui::root, msg.c_str(), "Radiant - Startup Failure", ui::alert_type::YESNO,
440 ui::alert_icon::Question) == ui::alert_response::YES) {
441 g_GamesDialog.Reset();
445 msg << "Logging console output to " << SettingsPath_get()
446 << "radiant.log\nRefer to the log if Radiant fails to start again.";
448 ui::alert(ui::root, msg.c_str(), "Radiant - Console Log", ui::alert_type::OK);
451 // set without saving, the class is not in a coherent state yet
452 // just do the value change and call to start logging, CGamesDialog will pickup when relevant
453 g_GamesDialog.m_bForceLogConsole = true;
457 // create a primary .pid for global init run
458 pid = fopen(g_pidFile.c_str(), "w");
464 void remove_global_pid()
466 StringOutputStream g_pidFile(256);
467 g_pidFile << SettingsPath_get() << "radiant.pid";
470 if (remove(g_pidFile.c_str()) == -1) {
471 StringOutputStream msg(256);
472 msg << "WARNING: Could not delete " << g_pidFile.c_str();
473 ui::alert(ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Error);
478 now the secondary game dependant .pid file
479 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
481 void create_local_pid()
483 StringOutputStream g_pidGameFile(256); ///< the game-specific .pid file
484 g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
486 FILE *pid = fopen(g_pidGameFile.c_str(), "r");
489 if (remove(g_pidGameFile.c_str()) == -1) {
490 StringOutputStream msg;
491 msg << "WARNING: Could not delete " << g_pidGameFile.c_str();
492 ui::alert(ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Error);
495 // in debug, never prompt to clean registry, turn console logging auto after a failed start
497 StringOutputStream msg;
498 msg << "Radiant failed to start properly the last time it was run.\n"
499 "The failure may be caused by current preferences.\n"
500 "Do you want to reset all preferences to defaults?";
502 if (ui::alert(ui::root, msg.c_str(), "Radiant - Startup Failure", ui::alert_type::YESNO,
503 ui::alert_icon::Question) == ui::alert_response::YES) {
508 msg << "Logging console output to " << SettingsPath_get()
509 << "radiant.log\nRefer to the log if Radiant fails to start again.";
511 ui::alert(ui::root, msg.c_str(), "Radiant - Console Log", ui::alert_type::OK);
514 // force console logging on! (will go in prefs too)
515 g_GamesDialog.m_bForceLogConsole = true;
518 // create one, will remove right after entering message loop
519 pid = fopen(g_pidGameFile.c_str(), "w");
528 now the secondary game dependant .pid file
529 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
531 void remove_local_pid()
533 StringOutputStream g_pidGameFile(256);
534 g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
535 remove(g_pidGameFile.c_str());
538 void user_shortcuts_init()
540 StringOutputStream path(256);
541 path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
542 LoadCommandMap(path.c_str());
543 SaveCommandMap(path.c_str());
546 void user_shortcuts_save()
548 StringOutputStream path(256);
549 path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
550 SaveCommandMap(path.c_str());
553 int main(int argc, char *argv[])
561 lib = LoadLibrary( "dwmapi.dll" );
563 void ( WINAPI *qDwmEnableComposition )( bool bEnable ) = ( void (WINAPI *) ( bool bEnable ) )GetProcAddress( lib, "DwmEnableComposition" );
564 if ( qDwmEnableComposition ) {
565 qDwmEnableComposition( FALSE );
571 const char *mapname = NULL;
572 char const *error = NULL;
573 if (!ui::init(&argc, &argv, "<filename.map>", &error)) {
574 g_print("%s\n", error);
578 // Gtk already removed parsed `--options`
580 if (strlen(argv[1]) > 1) {
581 if (g_str_has_suffix(argv[1], ".map")) {
582 if (g_path_is_absolute(argv[1])) {
585 mapname = g_build_filename(g_get_current_dir(), argv[1], NULL);
588 g_print("bad file name, will not load: %s\n", argv[1]);
591 } else if (argc > 2) {
592 g_print("%s\n", "too many arguments");
596 // redirect Gtk warnings to the console
597 g_log_set_handler("Gdk", (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 |
599 G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), error_redirect, 0);
600 g_log_set_handler("Gtk", (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 |
602 G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), error_redirect, 0);
603 g_log_set_handler("GtkGLExt", (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 |
605 G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), error_redirect, 0);
606 g_log_set_handler("GLib", (GLogLevelFlags) (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
607 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG |
608 G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), error_redirect, 0);
609 g_log_set_handler(0, (GLogLevelFlags) (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
610 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG |
611 G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), error_redirect, 0);
613 GlobalDebugMessageHandler::instance().setHandler(GlobalPopupDebugMessageHandler::instance());
615 environment_init(argc, (char const **) argv);
619 if (!check_version()) {
627 GlobalPreferences_Init();
629 g_GamesDialog.Init();
631 g_strGameToolsPath = g_pGameDescription->mGameToolsPath;
635 g_Preferences.Init(); // must occur before create_local_pid() to allow preferences to be reset
639 // in a very particular post-.pid startup
640 // we may have the console turned on and want to keep it that way
641 // so we use a latching system
642 if (g_GamesDialog.m_bForceLogConsole) {
644 g_Console_enableLogging = true;
645 g_GamesDialog.m_bForceLogConsole = false;
649 Radiant_Initialise();
651 user_shortcuts_init();
654 g_pParentWnd = new MainFrame();
658 if (mapname != NULL) {
659 Map_LoadFile(mapname);
660 } else if (g_bLoadLastMap && !g_strLastMap.empty()) {
661 Map_LoadFile(g_strLastMap.c_str());
666 // load up shaders now that we have the map loaded
668 TextureBrowser_ShowStartupShaders(GlobalTextureBrowser());
675 // avoid saving prefs when the app is minimized
676 if (g_pParentWnd->IsSleeping()) {
677 globalOutputStream() << "Shutdown while sleeping, not saving prefs\n";
678 g_preferences_globals.disable_ini = true;
683 if (!Map_Unnamed(g_map)) {
684 g_strLastMap = Map_Name(g_map);
689 user_shortcuts_save();
693 // close the log file if any