1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
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.
11
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.
16
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
20 */
21
22 /*! \mainpage GtkRadiant Documentation Index
23
24 \section intro_sec Introduction
25
26 This documentation is generated from comments in the source code.
27
28 \section links_sec Useful Links
29
30 \link include/itextstream.h include/itextstream.h \endlink - Global output and error message streams, similar to std::cout and std::cerr. \n
31
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
37
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
41
42 Array - automatic array memory management \n
43 HashTable - generic hashtable, similar to std::hash_map \n
44
45 \link math/Vector2.h math/Vector3.h math/Vector4.h \endlink - Vectors \n
46 \link math/matrix.h math/matrix.h \endlink - Matrices \n
47 \link math/quaternion.h math/quaternion.h \endlink - Quaternions \n
48 \link math/Plane3.h math/Plane3.h \endlink - Planes \n
49 \link math/aabb.h math/aabb.h \endlink - AABBs \n
50
51 Callback MemberCaller FunctionCaller - callbacks similar to using boost::function with boost::bind \n
52 SmartPointer SmartReference - smart-pointer and smart-reference similar to Loki's SmartPtr \n
53
54 \link generic/bitfield.h generic/bitfield.h \endlink - Type-safe bitfield \n
55 \link generic/enumeration.h generic/enumeration.h \endlink - Type-safe enumeration \n
56
57 DefaultAllocator - Memory allocation using new/delete, compliant with std::allocator interface \n
58
59 \link debugging/debugging.h debugging/debugging.h \endlink - Debugging macros \n
60
61 */
62
63 #include "commands.h"
64 #include "version.h"
65 #include "radiant_i18n.h"
66
67 #include "debugging/debugging.h"
68
69 #include "iundo.h"
70
71 #include "os/file.h"
72 #include "os/path.h"
73 #include "stream/stringstream.h"
74 #include "stream/textfilestream.h"
75 #include "modulesystem/moduleregistry.h"
76 #include "stacktrace.h"
77 #include "gtkutil/messagebox.h"
78
79 #include "log/console.h"
80 #include "sidebar/texturebrowser.h"
81 #include "sidebar/surfaceinspector/surfaceinspector.h"
82 #include "map/map.h"
83 #include "settings/PreferenceDialog.h"
84 #include "settings/GameManager.h"
85 #include "referencecache/referencecache.h"
86 #include "ui/mru/MRU.h"
87 #include "ui/splash/Splash.h"
88 #include "ui/mainframe/mainframe.h"
89 #include "server.h"
90 #include "environment.h"
91
92 #include <gtk/gtk.h>
93 #include <gtk/gtkgl.h>
94
95 #ifdef WIN32
96 #include <windows.h>
97 #endif
98
99 #include <locale.h>
100
101 #undef main
102
gtk_error_redirect(const gchar * domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)103 static void gtk_error_redirect (const gchar* domain, GLogLevelFlags log_level, const gchar* message, gpointer user_data)
104 {
105 gboolean in_recursion;
106 StringOutputStream buf(256);
107
108 in_recursion = (log_level & G_LOG_FLAG_RECURSION) != 0;
109 const gboolean is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0;
110 log_level = (GLogLevelFlags) (log_level & G_LOG_LEVEL_MASK);
111
112 #ifndef DEBUG
113 if (log_level == G_LOG_LEVEL_DEBUG)
114 return;
115 #endif
116
117 if (!message)
118 message = "(0) message";
119
120 if (domain)
121 buf << domain;
122 else
123 buf << "**";
124 buf << "-";
125
126 switch (log_level) {
127 case G_LOG_LEVEL_ERROR:
128 if (in_recursion)
129 buf << "ERROR (recursed) **: ";
130 else
131 buf << "ERROR **: ";
132 break;
133 case G_LOG_LEVEL_CRITICAL:
134 if (in_recursion)
135 buf << "CRITICAL (recursed) **: ";
136 else
137 buf << "CRITICAL **: ";
138 break;
139 case G_LOG_LEVEL_WARNING:
140 if (in_recursion)
141 buf << "WARNING (recursed) **: ";
142 else
143 buf << "WARNING **: ";
144 break;
145 case G_LOG_LEVEL_MESSAGE:
146 if (in_recursion)
147 buf << "Message (recursed): ";
148 else
149 buf << "Message: ";
150 break;
151 case G_LOG_LEVEL_INFO:
152 if (in_recursion)
153 buf << "INFO (recursed): ";
154 else
155 buf << "INFO: ";
156 break;
157 case G_LOG_LEVEL_DEBUG:
158 if (in_recursion)
159 buf << "DEBUG (recursed): ";
160 else
161 buf << "DEBUG: ";
162 break;
163 default:
164 /* we are used for a log level that is not defined by GLib itself,
165 * try to make the best out of it. */
166 if (in_recursion)
167 buf << "LOG (recursed:";
168 else
169 buf << "LOG (";
170 if (log_level) {
171 gchar string[] = "0x00): ";
172 gchar* p = string + 2;
173 const guint i = g_bit_nth_msf(log_level, -1);
174 *p++ = i >> 4;
175 *p = '0' + (i & 0xf);
176 if (*p > '9')
177 *p += 'A' - '9' - 1;
178
179 buf << string;
180 } else
181 buf << "): ";
182 }
183
184 buf << message;
185 if (is_fatal)
186 buf << "aborting...\n";
187
188 // spam it...
189 globalOutputStream() << buf.toString();
190
191 if (is_fatal) {
192 ERROR_MESSAGE("GTK+ error: " << buf.toString());
193 }
194 }
195
196 class Lock
197 {
198 bool m_locked;
199 public:
Lock()200 Lock () :
201 m_locked(false)
202 {
203 }
lock(void)204 void lock (void)
205 {
206 m_locked = true;
207 }
unlock(void)208 void unlock (void)
209 {
210 m_locked = false;
211 }
locked() const212 bool locked () const
213 {
214 return m_locked;
215 }
216 };
217
218 class ScopedLock
219 {
220 Lock& m_lock;
221 public:
ScopedLock(Lock & lock)222 ScopedLock (Lock& lock) :
223 m_lock(lock)
224 {
225 m_lock.lock();
226 }
~ScopedLock(void)227 ~ScopedLock (void)
228 {
229 m_lock.unlock();
230 }
231 };
232
233 class LineLimitedTextOutputStream: public TextOutputStream
234 {
235 TextOutputStream& outputStream;
236 std::size_t count;
237 public:
LineLimitedTextOutputStream(TextOutputStream & outputStream,std::size_t count)238 LineLimitedTextOutputStream (TextOutputStream& outputStream, std::size_t count) :
239 outputStream(outputStream), count(count)
240 {
241 }
write(const char * buffer,std::size_t length)242 std::size_t write (const char* buffer, std::size_t length)
243 {
244 if (count != 0) {
245 const char* p = buffer;
246 const char* end = buffer + length;
247 for (;;) {
248 p = std::find(p, end, '\n');
249 if (p == end) {
250 break;
251 }
252 ++p;
253 if (--count == 0) {
254 length = p - buffer;
255 break;
256 }
257 }
258 outputStream.write(buffer, length);
259 }
260 return length;
261 }
262 };
263
264 class PopupDebugMessageHandler: public DebugMessageHandler
265 {
266 StringOutputStream m_buffer;
267 Lock m_lock;
268 public:
getOutputStream(void)269 TextOutputStream& getOutputStream (void)
270 {
271 if (!m_lock.locked()) {
272 return m_buffer;
273 }
274 return globalErrorStream();
275 }
handleMessage(void)276 bool handleMessage (void)
277 {
278 getOutputStream() << "----------------\n";
279 LineLimitedTextOutputStream outputStream(getOutputStream(), 24);
280 write_stack_trace(outputStream);
281 getOutputStream() << "----------------\n";
282 globalErrorStream() << m_buffer.toString();
283 if (!m_lock.locked()) {
284 ScopedLock lock(m_lock);
285 #ifdef DEBUG
286 m_buffer << "Break into the debugger?\n";
287 bool handled = gtk_MessageBox(0, m_buffer.toString(), _("Radiant - Runtime Error"), eMB_YESNO, eMB_ICONERROR) == eIDNO;
288 m_buffer.clear();
289 return handled;
290 #else
291 m_buffer << "Please report this error to the developers\n";
292 gtkutil::errorDialog(m_buffer.toString());
293 m_buffer.clear();
294 #endif
295 }
296 return true;
297 }
298 };
299
300 typedef Static<PopupDebugMessageHandler> GlobalPopupDebugMessageHandler;
301
streams_init(void)302 static void streams_init (void)
303 {
304 GlobalErrorStream::instance().setOutputStream(getSysPrintErrorStream());
305 GlobalOutputStream::instance().setOutputStream(getSysPrintOutputStream());
306 GlobalWarningStream::instance().setOutputStream(getSysPrintWarningStream());
307 }
308
309 /**
310 * @brief now the secondary game dependant .pid file
311 */
create_local_pid(void)312 static void create_local_pid (void)
313 {
314 std::string g_pidGameFile = GlobalRegistry().get(RKEY_SETTINGS_PATH) + "/radiant.pid";
315
316 FILE *pid = fopen(g_pidGameFile.c_str(), "r");
317 if (pid != 0) {
318 fclose(pid);
319
320 if (!file_remove(g_pidGameFile)) {
321 std::string msg = _("WARNING: Could not delete game pid at ") + g_pidGameFile;
322 gtkutil::errorDialog(msg);
323 }
324
325 // in debug, never prompt to clean registry, turn console logging auto after a failed start
326 #ifndef DEBUG
327 std::string startupFailure = _("UFORadiant failed to start properly the last time it was run.\n"
328 "The failure may be caused by current preferences.\n"
329 "Do you want to reset all preferences to defaults?");
330
331 if (gtk_MessageBox(0, startupFailure, _("UFORadiant - Startup Failure"), eMB_YESNO, eMB_ICONQUESTION) == eIDYES) {
332 GlobalRegistry().reset();
333 }
334
335 std::string msg = "Logging console output to " + GlobalRegistry().get(RKEY_SETTINGS_PATH)
336 + "radiant.log\nRefer to the log if Radiant fails to start again.";
337
338 gtkutil::errorDialog(msg);
339 #endif
340 } else {
341 // create one, will remove right after entering message loop
342 pid = fopen(g_pidGameFile.c_str(), "w");
343 if (pid)
344 fclose(pid);
345 }
346 }
347
348 /**
349 * @brief now the secondary game dependant .pid file
350 */
remove_local_pid(void)351 static void remove_local_pid (void)
352 {
353 std::string g_pidGameFile = GlobalRegistry().get(RKEY_SETTINGS_PATH) + "/radiant.pid";
354 file_remove(g_pidGameFile);
355 }
356
main(int argc,char * argv[])357 int main (int argc, char* argv[])
358 {
359 streams_init();
360
361 #ifdef WIN32
362 HMODULE lib;
363 lib = LoadLibrary("dwmapi.dll");
364 if (lib != 0) {
365 HRESULT (WINAPI *dwmEnableComposition) (UINT) = (HRESULT (WINAPI *) (UINT)) GetProcAddress(lib, "DwmEnableComposition");
366 if (dwmEnableComposition) {
367 dwmEnableComposition(FALSE);
368 }
369 FreeLibrary(lib);
370 }
371 #endif
372
373 /** @todo support system wide locale dirs */
374 bindtextdomain(GETTEXT_PACKAGE, "i18n");
375 /* set encoding to utf-8 to prevent errors for Windows */
376 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
377
378 gtk_init(&argc, &argv);
379 gtk_gl_init(&argc, &argv);
380 gdk_gl_init(&argc, &argv);
381
382 /* reset some locale settings back to standard c
383 * this is e.g. needed for parsing float values from textfiles */
384 setlocale(LC_NUMERIC, "C");
385 setlocale(LC_TIME, "C");
386
387 // redirect Gtk warnings to the console
388 g_log_set_handler("Gdk", (GLogLevelFlags) (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING
389 | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION),
390 gtk_error_redirect, 0);
391 g_log_set_handler("Gtk", (GLogLevelFlags) (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING
392 | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION),
393 gtk_error_redirect, 0);
394 g_log_set_handler("GtkGLExt", (GLogLevelFlags) (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING
395 | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION),
396 gtk_error_redirect, 0);
397 g_log_set_handler("GLib", (GLogLevelFlags) (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING
398 | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION),
399 gtk_error_redirect, 0);
400 g_log_set_handler(0, (GLogLevelFlags) (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING
401 | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION),
402 gtk_error_redirect, 0);
403
404 GlobalDebugMessageHandler::instance().setHandler(GlobalPopupDebugMessageHandler::instance());
405
406 // Retrieve the application path and such
407 Environment::Instance().init(argc, argv);
408
409 // Initialise and instantiate the registry
410 GlobalModuleServer::instance().set(GlobalRadiantModuleServer());
411 StaticModuleRegistryList().instance().registerModule("registry");
412 GlobalRegistryModuleRef registryRef;
413
414 // Tell the Environment class to store the paths into the Registry
415 Environment::Instance().savePathsToRegistry();
416
417 // The settings path is set, start logging now
418 Sys_LogFile(true);
419
420 // Try to load all the XML files into the registry
421 GlobalRegistry().init();
422
423 ui::Splash::Instance().show();
424
425 create_local_pid();
426
427 Radiant_Initialise();
428
429 g_pParentWnd = new MainFrame();
430
431 Commands_Init();
432
433 ui::SurfaceInspector::Instance().init();
434
435 ui::Splash::Instance().hide();
436
437 if (GlobalMRU().loadLastMap() && GlobalMRU().getLastMapName() != "") {
438 GlobalMap().loadFile(GlobalMRU().getLastMapName());
439 } else if (Environment::Instance().getArgc() == 2) {
440 const std::string mapname = Environment::Instance().getArgv(1);
441 if (file_readable(mapname))
442 GlobalMap().loadFile(mapname);
443 else
444 GlobalMap().createNew();
445 } else {
446 GlobalMap().createNew();
447 }
448
449 remove_local_pid();
450
451 // load up shaders now that we have the map loaded
452 GlobalTextureBrowser().showStartupShaders();
453
454 // Save the paths *once again* into the registry, to overwrite bogus stuff in there
455 Environment::Instance().savePathsToRegistry();
456
457 gtk_main();
458
459 GlobalMap().free();
460
461 delete g_pParentWnd;
462
463 Radiant_Shutdown();
464
465 // close the log file if any
466 Sys_LogFile(false);
467
468 return EXIT_SUCCESS;
469 }
470