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");
185 globalErrorStream() << buf << "\n";
187 // FIXME why are warnings is_fatal?
191 ERROR_MESSAGE("GTK+ error: " << buf);
194 #if defined (_DEBUG) && defined (WIN32) && defined (_MSC_VER)
200 #if defined (_DEBUG) && defined (WIN32) && defined (_MSC_VER)
201 _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
209 Lock() : m_locked(false)
230 ScopedLock(Lock& lock) : m_lock(lock)
240 class LineLimitedTextOutputStream : public TextOutputStream
242 TextOutputStream& outputStream;
245 LineLimitedTextOutputStream(TextOutputStream& outputStream, std::size_t count)
246 : outputStream(outputStream), count(count)
249 std::size_t write(const char* buffer, std::size_t length)
253 const char* p = buffer;
254 const char* end = buffer+length;
257 p = std::find(p, end, '\n');
269 outputStream.write(buffer, length);
275 class PopupDebugMessageHandler : public DebugMessageHandler
277 StringOutputStream m_buffer;
280 TextOutputStream& getOutputStream()
286 return globalErrorStream();
290 getOutputStream() << "----------------\n";
291 LineLimitedTextOutputStream outputStream(getOutputStream(), 24);
292 write_stack_trace(outputStream);
293 getOutputStream() << "----------------\n";
294 globalErrorStream() << m_buffer.c_str();
297 ScopedLock lock(m_lock);
299 m_buffer << "Break into the debugger?\n";
300 bool handled = gtk_MessageBox(0, m_buffer.c_str(), "Radiant - Runtime Error", eMB_YESNO, eMB_ICONERROR) == eIDNO;
304 m_buffer << "Please report this error to the developers\n";
305 gtk_MessageBox(0, m_buffer.c_str(), "Radiant - Runtime Error", eMB_OK, eMB_ICONERROR);
313 typedef Static<PopupDebugMessageHandler> GlobalPopupDebugMessageHandler;
317 GlobalErrorStream::instance().setOutputStream(getSysPrintErrorStream());
318 GlobalOutputStream::instance().setOutputStream(getSysPrintOutputStream());
323 const char* home = environment_get_home_path();
327 StringOutputStream path(256);
328 path << home << "1." << RADIANT_MAJOR_VERSION "." << RADIANT_MINOR_VERSION << '/';
329 g_strSettingsPath = path.c_str();
332 Q_mkdir(g_strSettingsPath.c_str());
334 g_strAppPath = environment_get_app_path();
336 // radiant is installed in the parent dir of "tools/"
337 // NOTE: this is not very easy for debugging
338 // maybe add options to lookup in several places?
339 // (for now I had to create symlinks)
341 StringOutputStream path(256);
342 path << g_strAppPath.c_str() << "bitmaps/";
343 BitmapsPath_set(path.c_str());
346 // we will set this right after the game selection is done
347 g_strGameToolsPath = g_strAppPath;
350 bool check_version_file(const char* filename, const char* version)
352 TextFileInputStream file(filename);
356 buf[file.read(buf, 9)] = '\0';
358 // chomp it (the hard way)
360 while(buf[chomp] >= '0' && buf[chomp] <= '9')
364 return string_equal(buf, version);
371 // a safe check to avoid people running broken installations
372 // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
373 // make something idiot proof and someone will make better idiots, this may be overkill
374 // let's leave it disabled in debug mode in any case
375 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=431
377 #define CHECK_VERSION
380 // locate and open RADIANT_MAJOR and RADIANT_MINOR
381 bool bVerIsGood = true;
383 StringOutputStream ver_file_name(256);
384 ver_file_name << AppPath_get() << "RADIANT_MAJOR";
385 bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MAJOR_VERSION);
388 StringOutputStream ver_file_name(256);
389 ver_file_name << AppPath_get() << "RADIANT_MINOR";
390 bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MINOR_VERSION);
395 StringOutputStream msg(256);
396 msg << "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n"
397 "Make sure you run the right/latest editor binary you installed\n"
399 gtk_MessageBox(0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONDEFAULT);
407 void create_global_pid()
410 the global prefs loading / game selection dialog might fail for any reason we don't know about
411 we need to catch when it happens, to cleanup the stateful prefs which might be killing it
412 and to turn on console logging for lookup of the problem
413 this is the first part of the two step .pid system
414 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
416 StringOutputStream g_pidFile(256); ///< the global .pid file (only for global part of the startup)
418 g_pidFile << SettingsPath_get() << "radiant.pid";
421 pid = fopen (g_pidFile.c_str(), "r");
426 if (remove (g_pidFile.c_str()) == -1)
428 StringOutputStream msg(256);
429 msg << "WARNING: Could not delete " << g_pidFile.c_str();
430 gtk_MessageBox (0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
433 // in debug, never prompt to clean registry, turn console logging auto after a failed start
435 StringOutputStream msg(256);
436 msg << "Radiant failed to start properly the last time it was run.\n"
437 "The failure may be related to current global preferences.\n"
438 "Do you want to reset global preferences to defaults?";
440 if (gtk_MessageBox (0, msg.c_str(), "Radiant - Startup Failure", eMB_YESNO, eMB_ICONQUESTION) == eIDYES)
442 g_GamesDialog.Reset();
446 msg << "Logging console output to " << SettingsPath_get() << "radiant.log\nRefer to the log if Radiant fails to start again.";
448 gtk_MessageBox (0, msg.c_str(), "Radiant - Console Log", eMB_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");
463 void remove_global_pid()
465 StringOutputStream g_pidFile(256);
466 g_pidFile << SettingsPath_get() << "radiant.pid";
469 if (remove (g_pidFile.c_str()) == -1)
471 StringOutputStream msg(256);
472 msg << "WARNING: Could not delete " << g_pidFile.c_str();
473 gtk_MessageBox (0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
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");
490 if (remove (g_pidGameFile.c_str()) == -1)
492 StringOutputStream msg;
493 msg << "WARNING: Could not delete " << g_pidGameFile.c_str();
494 gtk_MessageBox (0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
497 // in debug, never prompt to clean registry, turn console logging auto after a failed start
499 StringOutputStream msg;
500 msg << "Radiant failed to start properly the last time it was run.\n"
501 "The failure may be caused by current preferences.\n"
502 "Do you want to reset all preferences to defaults?";
504 if (gtk_MessageBox (0, msg.c_str(), "Radiant - Startup Failure", eMB_YESNO, eMB_ICONQUESTION) == eIDYES)
510 msg << "Logging console output to " << SettingsPath_get() << "radiant.log\nRefer to the log if Radiant fails to start again.";
512 gtk_MessageBox (0, msg.c_str(), "Radiant - Console Log", eMB_OK);
515 // force console logging on! (will go in prefs too)
516 g_GamesDialog.m_bForceLogConsole = true;
521 // create one, will remove right after entering message loop
522 pid = fopen (g_pidGameFile.c_str(), "w");
530 now the secondary game dependant .pid file
531 http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
533 void remove_local_pid()
535 StringOutputStream g_pidGameFile(256);
536 g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
537 remove(g_pidGameFile.c_str());
540 void user_shortcuts_init()
542 StringOutputStream path(256);
543 path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
544 LoadCommandMap(path.c_str());
545 SaveCommandMap(path.c_str());
548 int main (int argc, char* argv[])
554 gtk_disable_setlocale();
555 gtk_init(&argc, &argv);
557 // redirect Gtk warnings to the console
558 g_log_set_handler ("Gdk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
559 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), error_redirect, 0);
560 g_log_set_handler ("Gtk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
561 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), error_redirect, 0);
562 g_log_set_handler ("GtkGLExt", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
563 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), error_redirect, 0);
564 g_log_set_handler ("GLib", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
565 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), error_redirect, 0);
566 g_log_set_handler (0, (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
567 G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), error_redirect, 0);
569 GlobalDebugMessageHandler::instance().setHandler(GlobalPopupDebugMessageHandler::instance());
571 environment_init(argc, argv);
584 GlobalPreferences_Init();
586 g_GamesDialog.Init();
588 g_strGameToolsPath = g_pGameDescription->mGameToolsPath;
592 g_Preferences.Init(); // must occur before create_local_pid() to allow preferences to be reset
596 // in a very particular post-.pid startup
597 // we may have the console turned on and want to keep it that way
598 // so we use a latching system
599 if (g_GamesDialog.m_bForceLogConsole)
602 g_Console_enableLogging = true;
603 g_GamesDialog.m_bForceLogConsole = false;
607 Radiant_Initialise();
611 user_shortcuts_init();
614 g_pParentWnd = new MainFrame();
618 if (g_bLoadLastMap && !g_strLastMap.empty())
620 Map_LoadFile(g_strLastMap.c_str());
627 // load up shaders now that we have the map loaded
629 TextureBrowser_ShowStartupShaders(GlobalTextureBrowser());
636 // avoid saving prefs when the app is minimized
637 if (g_pParentWnd->IsSleeping())
639 globalOutputStream() << "Shutdown while sleeping, not saving prefs\n";
640 g_preferences_globals.disable_ini = true;
645 if (!Map_Unnamed(g_map))
647 g_strLastMap = Map_Name(g_map);
652 global_accel_destroy();
656 // close the log file if any