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