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"
95 void error_redirect (const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
97 gboolean in_recursion;
101 in_recursion = (log_level & G_LOG_FLAG_RECURSION) != 0;
102 is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0;
103 log_level = (GLogLevelFlags) (log_level & G_LOG_LEVEL_MASK);
106 message = "(0) message";
109 strcpy (buf, domain);
116 case G_LOG_LEVEL_ERROR:
118 strcat (buf, "ERROR (recursed) **: ");
120 strcat (buf, "ERROR **: ");
122 case G_LOG_LEVEL_CRITICAL:
124 strcat (buf, "CRITICAL (recursed) **: ");
126 strcat (buf, "CRITICAL **: ");
128 case G_LOG_LEVEL_WARNING:
130 strcat (buf, "WARNING (recursed) **: ");
132 strcat (buf, "WARNING **: ");
134 case G_LOG_LEVEL_MESSAGE:
136 strcat (buf, "Message (recursed): ");
138 strcat (buf, "Message: ");
140 case G_LOG_LEVEL_INFO:
142 strcat (buf, "INFO (recursed): ");
144 strcat (buf, "INFO: ");
146 case G_LOG_LEVEL_DEBUG:
148 strcat (buf, "DEBUG (recursed): ");
150 strcat (buf, "DEBUG: ");
153 /* we are used for a log level that is not defined by GLib itself,
154 * try to make the best out of it.
157 strcat (buf, "LOG (recursed:");
159 strcat (buf, "LOG (");
162 gchar string[] = "0x00): ";
163 gchar *p = string + 2;
166 i = g_bit_nth_msf (log_level, -1);
169 *p = '0' + (i & 0xf);
173 strcat (buf, string);
178 strcat (buf, message);
180 strcat (buf, "\naborting...\n");
184 printf ("%s\n", buf);
186 ERROR_MESSAGE("GTK+ error: " << buf);
189 #if defined (_DEBUG) && defined (WIN32) && defined (_MSC_VER)
195 #if defined (_DEBUG) && defined (WIN32) && defined (_MSC_VER)
196 _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
204 Lock() : m_locked(false)
225 ScopedLock(Lock& lock) : m_lock(lock)
235 class LineLimitedTextOutputStream : public TextOutputStream
237 TextOutputStream& outputStream;
240 LineLimitedTextOutputStream(TextOutputStream& outputStream, std::size_t count)
241 : outputStream(outputStream), count(count)
244 std::size_t write(const char* buffer, std::size_t length)
248 const char* p = buffer;
249 const char* end = buffer+length;
252 p = std::find(p, end, '\n');
264 outputStream.write(buffer, length);
270 class PopupDebugMessageHandler : public DebugMessageHandler
272 StringOutputStream m_buffer;
275 TextOutputStream& getOutputStream()
281 return globalErrorStream();
285 getOutputStream() << "----------------\n";
286 LineLimitedTextOutputStream outputStream(getOutputStream(), 24);
287 write_stack_trace(outputStream);
288 getOutputStream() << "----------------\n";
291 ScopedLock lock(m_lock);
293 m_buffer << "Break into the debugger?\n";
294 globalErrorStream() << m_buffer.c_str();
295 bool handled = gtk_MessageBox(0, m_buffer.c_str(), "Radiant - Runtime Error", eMB_YESNO, eMB_ICONERROR) == eIDNO;
299 m_buffer << "Please report this error to the developers\n";
300 globalErrorStream() << m_buffer.c_str();
301 gtk_MessageBox(0, m_buffer.c_str(), "Radiant - Runtime Error", eMB_OK, eMB_ICONERROR);
310 typedef Static<PopupDebugMessageHandler> GlobalPopupDebugMessageHandler;
314 GlobalErrorStream::instance().setOutputStream(getSysPrintErrorStream());
315 GlobalOutputStream::instance().setOutputStream(getSysPrintOutputStream());
320 const char* home = environment_get_home_path();
324 StringOutputStream path(256);
325 path << home << RADIANT_VERSION << '/';
326 g_strSettingsPath = path.c_str();
329 Q_mkdir(g_strSettingsPath.c_str());
331 g_strAppPath = environment_get_app_path();
333 // radiant is installed in the parent dir of "tools/"
334 // NOTE: this is not very easy for debugging
335 // maybe add options to lookup in several places?
336 // (for now I had to create symlinks)
338 StringOutputStream path(256);
339 path << g_strAppPath.c_str() << "bitmaps/";
340 BitmapsPath_set(path.c_str());
343 // we will set this right after the game selection is done
344 g_strGameToolsPath = g_strAppPath;
347 bool check_version_file(const char* filename, const char* version)
349 TextFileInputStream file(filename);
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);
368 // a safe check to avoid people running broken installations
369 // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
370 // make something idiot proof and someone will make better idiots, this may be overkill
371 // let's leave it disabled in debug mode in any case
372 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=431
374 #define CHECK_VERSION
377 // locate and open RADIANT_MAJOR and RADIANT_MINOR
378 bool bVerIsGood = true;
380 StringOutputStream ver_file_name(256);
381 ver_file_name << AppPath_get() << "RADIANT_MAJOR";
382 bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MAJOR_VERSION);
385 StringOutputStream ver_file_name(256);
386 ver_file_name << AppPath_get() << "RADIANT_MINOR";
387 bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MINOR_VERSION);
392 StringOutputStream msg(256);
393 msg << "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n"
394 "Make sure you run the right/latest editor binary you installed\n"
396 gtk_MessageBox(0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONDEFAULT);
404 void create_global_pid()
407 the global prefs loading / game selection dialog might fail for any reason we don't know about
408 we need to catch when it happens, to cleanup the stateful prefs which might be killing it
409 and to turn on console logging for lookup of the problem
410 this is the first part of the two step .pid system
411 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
413 StringOutputStream g_pidFile(256); ///< the global .pid file (only for global part of the startup)
415 g_pidFile << SettingsPath_get() << "radiant.pid";
418 pid = fopen (g_pidFile.c_str(), "r");
423 if (remove (g_pidFile.c_str()) == -1)
425 StringOutputStream msg(256);
426 msg << "WARNING: Could not delete " << g_pidFile.c_str();
427 gtk_MessageBox (0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
430 // in debug, never prompt to clean registry, turn console logging auto after a failed start
432 StringOutputStream msg(256);
433 msg << "Radiant failed to start properly the last time it was run.\n"
434 "The failure may be related to current global preferences.\n"
435 "Do you want to reset global preferences to defaults?";
437 if (gtk_MessageBox (0, msg.c_str(), "Radiant - Startup Failure", eMB_YESNO, eMB_ICONQUESTION) == eIDYES)
439 g_GamesDialog.Reset();
443 msg << "Logging console output to " << SettingsPath_get() << "radiant.log\nRefer to the log if Radiant fails to start again.";
445 gtk_MessageBox (0, msg.c_str(), "Radiant - Console Log", eMB_OK);
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;
454 // create a primary .pid for global init run
455 pid = fopen (g_pidFile.c_str(), "w");
460 void remove_global_pid()
462 StringOutputStream g_pidFile(256);
463 g_pidFile << SettingsPath_get() << "radiant.pid";
466 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()
480 StringOutputStream g_pidGameFile(256); ///< the game-specific .pid file
481 g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
483 FILE *pid = fopen (g_pidGameFile.c_str(), "r");
487 if (remove (g_pidGameFile.c_str()) == -1)
489 StringOutputStream msg;
490 msg << "WARNING: Could not delete " << g_pidGameFile.c_str();
491 gtk_MessageBox (0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
494 // in debug, never prompt to clean registry, turn console logging auto after a failed start
496 StringOutputStream msg;
497 msg << "Radiant failed to start properly the last time it was run.\n"
498 "The failure may be caused by current preferences.\n"
499 "Do you want to reset all preferences to defaults?";
501 if (gtk_MessageBox (0, msg.c_str(), "Radiant - Startup Failure", eMB_YESNO, eMB_ICONQUESTION) == eIDYES)
507 msg << "Logging console output to " << SettingsPath_get() << "radiant.log\nRefer to the log if Radiant fails to start again.";
509 gtk_MessageBox (0, msg.c_str(), "Radiant - Console Log", eMB_OK);
512 // force console logging on! (will go in prefs too)
513 g_GamesDialog.m_bForceLogConsole = true;
518 // create one, will remove right after entering message loop
519 pid = fopen (g_pidGameFile.c_str(), "w");
527 now the secondary game dependant .pid file
528 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
530 void remove_local_pid()
532 StringOutputStream g_pidGameFile(256);
533 g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
534 remove(g_pidGameFile.c_str());
537 void user_shortcuts_init()
539 StringOutputStream path(256);
540 path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
541 LoadCommandMap(path.c_str());
542 SaveCommandMap(path.c_str());
545 int main (int argc, char* argv[])
551 gtk_disable_setlocale();
552 gtk_init(&argc, &argv);
554 // redirect Gtk warnings to the console
555 g_log_set_handler ("Gdk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
556 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, 0);
557 g_log_set_handler ("Gtk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
558 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, 0);
559 g_log_set_handler ("GtkGLExt", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
560 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, 0);
561 g_log_set_handler ("GLib", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
562 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, 0);
563 g_log_set_handler (0, (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
564 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, 0);
566 GlobalDebugMessageHandler::instance().setHandler(GlobalPopupDebugMessageHandler::instance());
568 environment_init(argc, argv);
581 GlobalPreferences_Init();
583 g_GamesDialog.Init();
585 g_strGameToolsPath = g_pGameDescription->mGameToolsPath;
589 g_Preferences.Init(); // must occur before create_local_pid() to allow preferences to be reset
593 // in a very particular post-.pid startup
594 // we may have the console turned on and want to keep it that way
595 // so we use a latching system
596 if (g_GamesDialog.m_bForceLogConsole)
599 g_Console_enableLogging = true;
600 g_GamesDialog.m_bForceLogConsole = false;
604 Radiant_Initialise();
608 user_shortcuts_init();
611 g_pParentWnd = new MainFrame();
615 if (g_bLoadLastMap && !g_strLastMap.empty())
617 Map_LoadFile(g_strLastMap.c_str());
624 // load up shaders now that we have the map loaded
626 TextureBrowser_ShowStartupShaders(GlobalTextureBrowser());
633 // avoid saving prefs when the app is minimized
634 if (g_pParentWnd->IsSleeping())
636 globalOutputStream() << "Shutdown while sleeping, not saving prefs\n";
637 g_preferences_globals.disable_ini = true;
642 if (!Map_Unnamed(g_map))
644 g_strLastMap = Map_Name(g_map);
649 global_accel_destroy();
653 // close the log file if any