transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / radiant / main.cpp
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \r
7 GtkRadiant is free software; you can redistribute it and/or modify\r
8 it under the terms of the GNU General Public License as published by\r
9 the Free Software Foundation; either version 2 of the License, or\r
10 (at your option) any later version.\r
11 \r
12 GtkRadiant is distributed in the hope that it will be useful,\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 GNU General Public License for more details.\r
16 \r
17 You should have received a copy of the GNU General Public License\r
18 along with GtkRadiant; if not, write to the Free Software\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
20 */\r
21 \r
22 #if defined (__linux__) || defined (__APPLE__)\r
23   #include <gdk/gdkx.h>\r
24   #include <pwd.h>\r
25   #include <unistd.h> \r
26   #ifdef __linux__\r
27     #include <mntent.h>\r
28   #endif\r
29   #include <dirent.h>\r
30   #include <pthread.h>\r
31   #include <sys/wait.h>\r
32   #include <signal.h>\r
33   #include <sys/stat.h>\r
34 #endif\r
35 \r
36 #include <gtk/gtk.h>\r
37 #include "stdafx.h"\r
38 #include <assert.h>\r
39 #include <sys/types.h>\r
40 #include <sys/stat.h>\r
41 \r
42 #include <stdlib.h>\r
43 \r
44 #include "watchbsp.h"\r
45 #include "filters.h"\r
46 \r
47 bool g_bBuildList = false;\r
48 int g_argc;\r
49 char** g_argv;\r
50 \r
51 // =============================================================================\r
52 // Splash screen\r
53 \r
54 // get rid of it when debugging\r
55 #if defined (_DEBUG)\r
56   #define SKIP_SPLASH\r
57 #endif\r
58 \r
59 static GtkWidget *splash_screen;\r
60 \r
61 // called based on a timer, or in particular cases when we don't want to keep it around\r
62 gint try_destroy_splash (gpointer data)\r
63 {\r
64   if (splash_screen)\r
65   {\r
66     gtk_widget_destroy (splash_screen);\r
67     splash_screen = NULL;\r
68   }\r
69   return FALSE;\r
70 }\r
71 \r
72 static void create_splash ()\r
73 {\r
74   GtkWidget *alert_frame, *alert_frame1, *pixmap;\r
75 \r
76   splash_screen = gtk_window_new (GTK_WINDOW_POPUP);\r
77   gtk_window_position (GTK_WINDOW (splash_screen), GTK_WIN_POS_CENTER);\r
78   gtk_widget_realize (splash_screen);\r
79 \r
80   alert_frame1 = gtk_frame_new (NULL);\r
81   gtk_widget_show (alert_frame1);\r
82   gtk_container_add (GTK_CONTAINER (splash_screen), alert_frame1);\r
83   gtk_frame_set_shadow_type (GTK_FRAME (alert_frame1), GTK_SHADOW_OUT);\r
84 \r
85   alert_frame = gtk_frame_new (NULL);\r
86   gtk_widget_show (alert_frame);\r
87 \r
88   gtk_container_add (GTK_CONTAINER (alert_frame1), alert_frame);\r
89   gtk_frame_set_shadow_type (GTK_FRAME (alert_frame), GTK_SHADOW_IN);\r
90   gtk_container_border_width (GTK_CONTAINER (alert_frame), 3);\r
91 \r
92   pixmap = gtk_preview_new (GTK_PREVIEW_COLOR);\r
93   gtk_widget_show (pixmap);\r
94   gtk_container_add (GTK_CONTAINER (alert_frame), pixmap);\r
95 \r
96   CString str;\r
97   guint16 width, height;\r
98   unsigned char *buf;\r
99 \r
100   str = g_strGameToolsPath;\r
101   str += "bitmaps/splash.bmp";\r
102 \r
103   unsigned char* load_bitmap_file (const char* filename, guint16* width, guint16* height);\r
104   buf = load_bitmap_file (str.GetBuffer (), &width, &height);\r
105 \r
106   if (!buf)\r
107   {\r
108     str = g_strBitmapsPath;\r
109     str += "splash.bmp";\r
110 \r
111     buf = load_bitmap_file (str.GetBuffer (), &width, &height);\r
112   }\r
113 \r
114   if (buf)\r
115   {\r
116     GtkPreview *preview = GTK_PREVIEW (pixmap);\r
117     gtk_preview_size (preview, width, height);\r
118     for (int y = 0; y < height; y++)\r
119       gtk_preview_draw_row (preview, buf+y*width*3, 0, y, width);\r
120   }\r
121 \r
122   gtk_widget_show_all (splash_screen);\r
123 \r
124   while (gtk_events_pending ())\r
125     gtk_main_iteration ();\r
126 }\r
127 \r
128 // =============================================================================\r
129 // Loki stuff\r
130 \r
131 #if defined (__linux__) || defined (__APPLE__)\r
132 \r
133 /* A short game name, could be used as argv[0] */\r
134 static char game_name[100] = "";\r
135 \r
136 /* The directory where the data files can be found (run directory) */\r
137 static char datapath[PATH_MAX];\r
138 \r
139 char *loki_gethomedir(void)\r
140 {\r
141   char *home = NULL;\r
142 \r
143   home = getenv("HOME");\r
144   if ( home == NULL )\r
145   {\r
146     uid_t id = getuid();\r
147     struct passwd *pwd;\r
148 \r
149     setpwent();\r
150     while ( (pwd = getpwent()) != NULL )\r
151     {\r
152       if ( pwd->pw_uid == id )\r
153       {\r
154         home = pwd->pw_dir;\r
155         break;\r
156       }\r
157     }\r
158     endpwent();\r
159   }\r
160   return home;\r
161 }\r
162 \r
163 /* Must be called BEFORE loki_initialize */\r
164 void loki_setgamename(const char *n)\r
165 {\r
166   strncpy(game_name, n, sizeof(game_name));\r
167 }\r
168 \r
169   #ifdef __linux__\r
170 /* Code to determine the mount point of a CD-ROM */\r
171 int loki_getmountpoint(const char *device, char *mntpt, int max_size)\r
172 {\r
173   char devpath[PATH_MAX], mntdevpath[PATH_MAX];\r
174   FILE * mountfp;\r
175   struct mntent *mntent;\r
176   int mounted;\r
177 \r
178   /* Nothing to do with no device file */\r
179   if ( device == NULL )\r
180   {\r
181     *mntpt = '\0';\r
182     return -1;\r
183   }\r
184 \r
185   /* Get the fully qualified path of the CD-ROM device */\r
186   if ( realpath(device, devpath) == NULL )\r
187   {\r
188     perror("realpath() on your CD-ROM failed");\r
189     return(-1);\r
190   }\r
191 \r
192   /* Get the mount point */\r
193   mounted = -1;\r
194   memset(mntpt, 0, max_size);\r
195   mountfp = setmntent( _PATH_MNTTAB, "r" );\r
196   if ( mountfp != NULL )\r
197   {\r
198     mounted = 0;\r
199     while ( (mntent = getmntent( mountfp )) != NULL )\r
200     {\r
201       char *tmp, mntdev[1024];\r
202 \r
203       strcpy(mntdev, mntent->mnt_fsname);\r
204       if ( strcmp(mntent->mnt_type, "supermount") == 0 )\r
205       {\r
206         tmp = strstr(mntent->mnt_opts, "dev=");\r
207         if ( tmp )\r
208         {\r
209           strcpy(mntdev, tmp+strlen("dev="));\r
210           tmp = strchr(mntdev, ',');\r
211           if ( tmp )\r
212           {\r
213             *tmp = '\0';\r
214           }\r
215         }\r
216       }\r
217       if ( strncmp(mntdev, "/dev", 4) ||\r
218            realpath(mntdev, mntdevpath) == NULL )\r
219       {\r
220         continue;\r
221       }\r
222       if ( strcmp( mntdevpath, devpath ) == 0 )\r
223       {\r
224         mounted = 1;\r
225         assert((int)strlen( mntent->mnt_dir ) < max_size);\r
226         strncpy( mntpt, mntent->mnt_dir, max_size-1);\r
227         mntpt[max_size-1] = '\0';\r
228         break;\r
229       }\r
230     }\r
231     endmntent( mountfp );\r
232   }\r
233   return(mounted);\r
234\r
235   #endif\r
236 \r
237 /* \r
238     This function gets the directory containing the running program.\r
239     argv0 - the 0'th argument to the program\r
240 */\r
241 // FIXME TTimo\r
242 // I don't understand this function. It looks like something cut from another piece of software\r
243 // we somehow get the g_strAppPath from it, but it's done through a weird scan across $PATH env. var.\r
244 // even worse, it doesn't behave the same in all cases .. works well when ran through gdb and borks when ran from a shell\r
245 void loki_initpaths(char *argv0)\r
246 {\r
247   char temppath[PATH_MAX]; //, env[100];\r
248   char *home; //, *ptr, *data_env;\r
249 \r
250   home = loki_gethomedir();\r
251   if ( home == NULL )\r
252   {\r
253     home = ".";\r
254   }\r
255 \r
256   if (*game_name == 0) /* Game name defaults to argv[0] */\r
257     loki_setgamename(argv0);\r
258 \r
259   strcpy(temppath, argv0);  /* If this overflows, it's your own fault :) */\r
260   if ( ! strrchr(temppath, '/') )\r
261   {\r
262     char *path;\r
263     char *last;\r
264     int found;\r
265 \r
266     found = 0;\r
267     path = getenv("PATH");\r
268     do\r
269     {\r
270       /* Initialize our filename variable */\r
271       temppath[0] = '\0';\r
272 \r
273       /* Get next entry from path variable */\r
274       last = strchr(path, ':');\r
275       if ( ! last )\r
276         last = path+strlen(path);\r
277 \r
278       /* Perform tilde expansion */\r
279       if ( *path == '~' )\r
280       {\r
281         strcpy(temppath, home);\r
282         ++path;\r
283       }\r
284 \r
285       /* Fill in the rest of the filename */\r
286       if ( last > (path+1) )\r
287       {\r
288         strncat(temppath, path, (last-path));\r
289         strcat(temppath, "/");\r
290       }\r
291       strcat(temppath, "./");\r
292       strcat(temppath, argv0);\r
293 \r
294       /* See if it exists, and update path */\r
295       if ( access(temppath, X_OK) == 0 )\r
296       {\r
297         ++found;\r
298       }\r
299       path = last+1;\r
300 \r
301     } while ( *last && !found );\r
302 \r
303   } else\r
304   {\r
305     /* Increment argv0 to the basename */\r
306     argv0 = strrchr(argv0, '/')+1;\r
307   }\r
308 \r
309   /* Now canonicalize it to a full pathname for the data path */\r
310   if ( realpath(temppath, datapath) )\r
311   {\r
312     /* There should always be '/' in the path */\r
313     *(strrchr(datapath, '/')) = '\0';\r
314   }\r
315 }\r
316 \r
317 char *loki_getdatapath(void)\r
318 {\r
319   return(datapath);\r
320 }\r
321 \r
322 #endif\r
323 \r
324 // end of Loki stuff\r
325 // =============================================================================\r
326 \r
327 void error_redirect (const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)\r
328 {\r
329   gboolean in_recursion;\r
330   gboolean is_fatal;\r
331   char buf[256];\r
332 \r
333   in_recursion = (log_level & G_LOG_FLAG_RECURSION) != 0;\r
334   is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0;\r
335   log_level = (GLogLevelFlags) (log_level & G_LOG_LEVEL_MASK);\r
336 \r
337   if (!message)\r
338     message = "(NULL) message";\r
339 \r
340   if (domain)\r
341     strcpy (buf, domain);\r
342   else\r
343     strcpy (buf, "**");\r
344   strcat (buf, "-");\r
345 \r
346   switch (log_level)\r
347   {\r
348   case G_LOG_LEVEL_ERROR:\r
349     if (in_recursion)\r
350       strcat (buf, "ERROR (recursed) **: ");\r
351     else\r
352       strcat (buf, "ERROR **: ");\r
353     break;\r
354   case G_LOG_LEVEL_CRITICAL:\r
355     if (in_recursion)\r
356       strcat (buf, "CRITICAL (recursed) **: ");\r
357     else\r
358       strcat (buf, "CRITICAL **: ");\r
359     break;\r
360   case G_LOG_LEVEL_WARNING:\r
361     if (in_recursion)\r
362       strcat (buf, "WARNING (recursed) **: ");\r
363     else\r
364       strcat (buf, "WARNING **: ");\r
365     break;\r
366   case G_LOG_LEVEL_MESSAGE:\r
367     if (in_recursion)\r
368       strcat (buf, "Message (recursed): ");\r
369     else\r
370       strcat (buf, "Message: ");\r
371     break;\r
372   case G_LOG_LEVEL_INFO:\r
373     if (in_recursion)\r
374       strcat (buf, "INFO (recursed): ");\r
375     else\r
376       strcat (buf, "INFO: ");\r
377     break;\r
378   case G_LOG_LEVEL_DEBUG:\r
379     if (in_recursion)\r
380       strcat (buf, "DEBUG (recursed): ");\r
381     else\r
382       strcat (buf, "DEBUG: ");\r
383     break;\r
384   default:\r
385     /* we are used for a log level that is not defined by GLib itself,\r
386      * try to make the best out of it.\r
387      */\r
388     if (in_recursion)\r
389       strcat (buf, "LOG (recursed:");\r
390     else\r
391       strcat (buf, "LOG (");\r
392     if (log_level)\r
393     {\r
394       gchar string[] = "0x00): ";\r
395       gchar *p = string + 2;\r
396       guint i;\r
397 \r
398       i = g_bit_nth_msf (log_level, -1);\r
399       *p = i >> 4;\r
400       p++;\r
401       *p = '0' + (i & 0xf);\r
402       if (*p > '9')\r
403         *p += 'A' - '9' - 1;\r
404 \r
405       strcat (buf, string);\r
406     } else\r
407       strcat (buf, "): ");\r
408   }\r
409 \r
410   strcat (buf, message);\r
411   if (is_fatal)\r
412     strcat (buf, "\naborting...\n");\r
413   else\r
414     strcat (buf, "\n");\r
415 \r
416   printf ("%s\n", buf);\r
417   Sys_FPrintf (SYS_WRN, buf);\r
418   // TTimo NOTE: in some cases it may be handy to log only to the file\r
419 //  Sys_FPrintf (SYS_NOCON, buf);\r
420 }\r
421 \r
422 int main (int argc, char* argv[])\r
423 {\r
424   char *libgl, *ptr;\r
425   int i, j, k;\r
426 \r
427 #ifdef _WIN32\r
428   libgl = "opengl32.dll";\r
429 #endif\r
430 \r
431 #if defined (__linux__)\r
432   libgl = "libGL.so.1";\r
433 #endif\r
434 \r
435 #ifdef __APPLE__\r
436   libgl = "/usr/X11R6/lib/libGL.1.dylib";\r
437 #endif\r
438 \r
439 #if defined (__linux__) || defined (__APPLE__)\r
440   // Give away unnecessary root privileges.\r
441   // Important: must be done before calling gtk_init().\r
442   char *loginname;\r
443   struct passwd *pw;\r
444   seteuid(getuid());\r
445   if (geteuid() == 0 && (loginname = getlogin()) != NULL &&\r
446       (pw = getpwnam(loginname)) != NULL)\r
447     setuid(pw->pw_uid);\r
448 #endif\r
449 \r
450   gtk_disable_setlocale();\r
451 \r
452   gtk_init(&argc, &argv);\r
453 \r
454   if ((ptr = getenv ("Q3R_LIBGL")) != NULL)\r
455     libgl = ptr;\r
456 \r
457   for (i = 1; i < argc; i++)\r
458   {\r
459     char* param = argv[i];\r
460 \r
461     if (param[0] == '-' && param[1] == '-')\r
462     {\r
463       param += 2;\r
464 \r
465       if ((strcmp (param, "libgl") == 0) && (i != argc))\r
466       {\r
467         libgl = argv[i+1];\r
468         argv[i] = argv[i+1] = NULL;\r
469         i++;\r
470       } else if (strcmp (param, "builddefs") == 0)\r
471       {\r
472         g_bBuildList = true;\r
473         argv[i] = NULL;\r
474       }\r
475     }\r
476   }\r
477 \r
478   for (i = 1; i < argc; i++)\r
479   {\r
480     for (k = i; k < argc; k++)\r
481       if (argv[k] != NULL)\r
482         break;\r
483 \r
484     if (k > i)\r
485     {\r
486       k -= i;\r
487       for (j = i + k; j < argc; j++)\r
488         argv[j-k] = argv[j];\r
489       argc -= k;\r
490     }\r
491   }\r
492 \r
493   g_argc = argc;\r
494   g_argv = argv;\r
495 \r
496   g_strPluginsDir = "plugins/";\r
497   g_strModulesDir = "modules/";\r
498 \r
499 #ifdef _WIN32\r
500   // get path to the editor\r
501   char* pBuffer = g_strAppPath.GetBufferSetLength(_MAX_PATH + 1);\r
502   GetModuleFileName(NULL, pBuffer, _MAX_PATH);\r
503   pBuffer[g_strAppPath.ReverseFind('\\') + 1] = '\0';\r
504   QE_ConvertDOSToUnixName(pBuffer, pBuffer);\r
505   g_strAppPath.ReleaseBuffer();\r
506 \r
507   g_strBitmapsPath = g_strAppPath;\r
508   g_strBitmapsPath += "bitmaps/";\r
509 \r
510 #if 0\r
511   // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=639\r
512   // now check if we are running from a network installation\r
513   // use a dummy file as the flag\r
514   FILE *f_netrun;\r
515   CString strNetrun;\r
516   strNetrun = g_strAppPath; strNetrun += NETRUN_FILENAME;\r
517   f_netrun = fopen(strNetrun.GetBuffer(), "r");\r
518   if (f_netrun)\r
519   {\r
520     fclose(f_netrun);\r
521     g_PrefsDlg.m_bUseHomePath = true;\r
522   }\r
523 #endif\r
524   CGameDialog::UpdateNetrun(false); // read the netrun configuration\r
525 \r
526   if (CGameDialog::GetNetrun())\r
527   {\r
528     // we have to find a per-user g_strTempPath\r
529     // this behaves the same as on Linux\r
530     g_strTempPath = getenv("USERPROFILE");\r
531     if (!g_strTempPath.GetLength())\r
532     {\r
533       CString msg;\r
534       msg = "Radiant is configured to run from a network installation.\n";\r
535       msg += "I couldn't find the environement variable USERPROFILE\n";\r
536       msg += "I'm going to use C:\\RadiantSettings. Please set USERPROFILE\n";\r
537       gtk_MessageBox (NULL, msg, "Radiant - Network mode", MB_OK);\r
538       g_strTempPath = "C:\\";\r
539     }\r
540     g_strTempPath += "\\RadiantSettings\\";\r
541     Q_mkdir(g_strTempPath.GetBuffer(), 0755);\r
542     g_strTempPath += RADIANT_VERSION;\r
543     g_strTempPath += "\\";\r
544     Q_mkdir(g_strTempPath.GetBuffer(), 0755);\r
545   }\r
546   else\r
547   {\r
548     // use the core path as temp (to save commandlist.txt, and do the .pid files)\r
549     g_strTempPath = g_strAppPath;\r
550   }\r
551 \r
552 #endif\r
553 \r
554 #if defined (__linux__) || defined (__APPLE__)\r
555   Str home;\r
556   home = g_get_home_dir ();\r
557   AddSlash (home);\r
558   home += ".radiant/";\r
559   Q_mkdir (home.GetBuffer (), 0775);\r
560   home += RADIANT_VERSION;\r
561   Q_mkdir (home.GetBuffer (), 0775);\r
562   g_strTempPath = home.GetBuffer ();\r
563   AddSlash (g_strTempPath);\r
564 \r
565   loki_initpaths(argv[0]);\r
566 \r
567   // NOTE: we build g_strAppPath with a '/' (or '\' on WIN32)\r
568   // it's a general convention in Radiant to have the slash at the end of directories\r
569   char real[PATH_MAX];\r
570   realpath (loki_getdatapath(), real);\r
571   if (real[strlen(real)-1] != '/')\r
572     strcat(real, "/");\r
573 \r
574   g_strAppPath = real;\r
575 \r
576 #if 0\r
577   printf("g_strAppPath: %s\n", g_strAppPath.GetBuffer());\r
578 #endif\r
579 \r
580   // radiant is installed in the parent dir of "tools/"\r
581   // NOTE: this is not very easy for debugging\r
582   // maybe add options to lookup in several places?\r
583   // (for now I had to create symlinks)\r
584   g_strBitmapsPath = g_strAppPath;\r
585   g_strBitmapsPath += "bitmaps/";\r
586 #if 0\r
587   printf("g_strBitmapsPath: %s\n", g_strBitmapsPath.GetBuffer());\r
588 #endif\r
589   \r
590   // we will set this right after the game selection is done\r
591   g_strGameToolsPath = g_strAppPath;\r
592 \r
593 #endif\r
594 \r
595   // init the DTD path\r
596   g_strDTDPath = g_strAppPath;\r
597   g_strDTDPath += "dtds/";\r
598 \r
599   /*!\r
600   the global prefs loading / game selection dialog might fail for any reason we don't know about\r
601   we need to catch when it happens, to cleanup the stateful prefs which might be killing it\r
602   and to turn on console logging for lookup of the problem\r
603   this is the first part of the two step .pid system\r
604   http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297\r
605   */\r
606   g_pidFile = g_strTempPath.GetBuffer ();\r
607   g_pidFile += "radiant.pid";\r
608 \r
609   FILE *pid;\r
610   pid = fopen (g_pidFile.GetBuffer(), "r");\r
611   if (pid != NULL)\r
612   {\r
613     fclose (pid);\r
614     CString msg;\r
615 \r
616     if (remove (g_pidFile.GetBuffer ()) == -1)\r
617     {\r
618       msg = "WARNING: Could not delete "; msg += g_pidFile;\r
619       gtk_MessageBox (NULL, msg, "Radiant", MB_OK | MB_ICONERROR );\r
620     }\r
621 \r
622     // in debug, never prompt to clean registry, turn console logging auto after a failed start\r
623 #if !defined(_DEBUG)\r
624     msg = "Found the file ";\r
625     msg += g_pidFile;\r
626     msg += ".\nThis indicates that Radiant failed during the game selection startup last time it was run.\n"\r
627            "Choose YES to clean Radiant's registry settings and shut down Radiant.\n"\r
628            "WARNING: the global prefs will be lost if you choose YES.";\r
629 \r
630     if (gtk_MessageBox (NULL, msg, "Radiant - Reset global startup?", MB_YESNO | MB_ICONQUESTION) == IDYES)\r
631     {\r
632       // remove global prefs and shutdown\r
633       g_PrefsDlg.mGamesDialog.Reset();\r
634       // remove the prefs file (like a full reset of the registry)\r
635       //remove (g_PrefsDlg.m_inipath->str);\r
636       gtk_MessageBox(NULL, "Removed global settings, choose OK to close Radiant.", "Radiant", MB_OK );\r
637       _exit(-1);\r
638     }\r
639     msg = "Logging console output to ";\r
640     msg += g_strTempPath;\r
641     msg += "radiant.log\nRefer to the log if Radiant fails to start again.";\r
642 \r
643     gtk_MessageBox (NULL, msg, "Radiant - Console Log", MB_OK);\r
644 #endif\r
645 \r
646     // set without saving, the class is not in a coherent state yet\r
647     // just do the value change and call to start logging, CGamesDialog will pickup when relevant\r
648     g_PrefsDlg.mGamesDialog.m_bLogConsole = true;\r
649     g_PrefsDlg.mGamesDialog.m_bForceLogConsole = true;\r
650     Sys_LogFile();\r
651   }\r
652 \r
653   // create a primary .pid for global init run\r
654   pid = fopen (g_pidFile.GetBuffer(), "w");\r
655   if (pid)\r
656     fclose (pid);\r
657   \r
658   // a safe check to avoid people running broken installations\r
659   // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)\r
660   // make something idiot proof and someone will make better idiots, this may be overkill\r
661   // let's leave it disabled in debug mode in any case\r
662   // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=431\r
663 #ifndef _DEBUG\r
664 #define CHECK_VERSION\r
665 #endif\r
666 #ifdef CHECK_VERSION  \r
667   // locate and open RADIANT_MAJOR and RADIANT_MINOR\r
668   qboolean bVerIsGood = true;\r
669   Str ver_file_name;\r
670   ver_file_name = g_strAppPath;\r
671   ver_file_name += "RADIANT_MAJOR";\r
672   FILE *ver_file = fopen (ver_file_name.GetBuffer(), "r");\r
673   if (ver_file)\r
674   {\r
675     char buf[10];\r
676     int chomp;\r
677     fread(buf, 1, 10, ver_file);\r
678     // chomp it (the hard way)\r
679     chomp = 0;\r
680     while(buf[chomp] >= '0' && buf[chomp] <= '9')\r
681       chomp++;\r
682     buf[chomp] = '\0';\r
683     if (strcmp(buf, RADIANT_MAJOR_VERSION))\r
684     {\r
685       Sys_Printf("ERROR: file RADIANT_MAJOR doesn't match ('%s')\n", buf);\r
686       bVerIsGood = false;\r
687     }\r
688   }\r
689   else\r
690   {\r
691     Sys_Printf("ERROR: can't find RADIANT_MAJOR in '%s'\n", ver_file_name.GetBuffer());\r
692     bVerIsGood = false;\r
693   }\r
694   ver_file_name = g_strAppPath;\r
695   ver_file_name += "RADIANT_MINOR";\r
696   ver_file = fopen (ver_file_name.GetBuffer(), "r");\r
697   if (ver_file)\r
698   {\r
699     char buf[10];\r
700     int chomp;\r
701     fread(buf, 1, 10, ver_file);\r
702     // chomp it (the hard way)\r
703     chomp = 0;\r
704     while(buf[chomp] >= '0' && buf[chomp] <= '9')\r
705       chomp++;\r
706     buf[chomp] = '\0';\r
707     if (strcmp(buf, RADIANT_MINOR_VERSION))\r
708     {\r
709       Sys_Printf("ERROR: file RADIANT_MINOR doesn't match ('%s')\n", buf);\r
710       bVerIsGood = false;\r
711     }\r
712   }\r
713   else\r
714   {\r
715     Sys_Printf("ERROR: can't find RADIANT_MINOR in '%s'\n", ver_file_name.GetBuffer());\r
716     bVerIsGood = false;\r
717   }\r
718   if (!bVerIsGood)\r
719   {\r
720     CString msg;\r
721     msg = "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n";\r
722     msg += "Make sure you run the right/latest editor binary you installed\n";\r
723     msg += g_strAppPath; msg += "\n";\r
724     msg += "Check http://www.qeradiant.com/faq/index.cgi?file=219 for more information";\r
725     gtk_MessageBox(NULL, msg.GetBuffer(), "Radiant", MB_OK, "http://www.qeradiant.com/faq/index.cgi?file=219");\r
726     _exit(-1);\r
727   }\r
728 #endif\r
729   \r
730   g_qeglobals.disable_ini = false;\r
731   g_PrefsDlg.Init ();\r
732 \r
733   // close the primary\r
734   if (remove (g_pidFile.GetBuffer ()) == -1)\r
735   {\r
736     CString msg;\r
737     msg = "WARNING: Could not delete "; msg += g_pidGameFile;\r
738     gtk_MessageBox (NULL, msg, "Radiant", MB_OK | MB_ICONERROR );\r
739   }\r
740   \r
741   /*!\r
742   now the secondary game dependant .pid file\r
743   http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297\r
744   */\r
745   g_pidGameFile = g_PrefsDlg.m_rc_path->str;\r
746   g_pidGameFile += "radiant-game.pid";\r
747 \r
748   pid = fopen (g_pidGameFile.GetBuffer(), "r");\r
749   if (pid != NULL)\r
750   {\r
751     fclose (pid);\r
752     CString msg;\r
753     if (remove (g_pidGameFile.GetBuffer ()) == -1)\r
754     {\r
755       msg = "WARNING: Could not delete "; msg += g_pidGameFile;\r
756       gtk_MessageBox (NULL, msg, "Radiant", MB_OK | MB_ICONERROR );\r
757     }\r
758 \r
759     msg = "Found the file ";\r
760     msg += g_pidGameFile;\r
761     msg += ".\nThis indicates that Radiant failed to load the last time it was run.\n"\r
762            "Choose YES to clean Radiant's registry settings and shut down Radiant.\n"\r
763            "WARNING: preferences will be lost if you choose YES.";\r
764 \r
765     // in debug, never prompt to clean registry, turn console logging auto after a failed start\r
766 #if !defined(_DEBUG)\r
767     //bleh\r
768     if (gtk_MessageBox (NULL, msg, "Radiant - Clean Registry?", MB_YESNO | MB_ICONQUESTION) == IDYES)\r
769     {\r
770       // remove the game prefs files\r
771       remove (g_PrefsDlg.m_inipath->str);\r
772       char buf[PATH_MAX];\r
773       sprintf(buf, "%sSavedInfo.bin", g_PrefsDlg.m_rc_path->str);\r
774       remove(buf);\r
775       // remove the global pref too\r
776       g_PrefsDlg.mGamesDialog.Reset();\r
777       gtk_MessageBox(NULL, "Cleaned registry settings, choose OK to close Radiant.\nThe next time Radiant runs it will use default settings.", "Radiant", MB_OK );\r
778       _exit(-1);\r
779     }\r
780     msg = "Logging console output to ";\r
781     msg += g_strTempPath;\r
782     msg += "radiant.log\nRefer to the log if Radiant fails to start again.";\r
783 \r
784     gtk_MessageBox (NULL, msg, "Radiant - Console Log", MB_OK);\r
785 #endif\r
786 \r
787     // force console logging on! (will go in prefs too)\r
788     g_PrefsDlg.mGamesDialog.m_bLogConsole = true;\r
789     g_PrefsDlg.mGamesDialog.SavePrefs();\r
790     Sys_LogFile();\r
791 \r
792     g_PrefsDlg.LoadPrefs();\r
793 \r
794   } else\r
795   {\r
796     // create one, will remove right after entering message loop\r
797     pid = fopen (g_pidGameFile.GetBuffer(), "w");\r
798     if (pid)\r
799       fclose (pid);\r
800 \r
801     g_PrefsDlg.LoadPrefs();\r
802 \r
803 #ifndef _DEBUG // I can't be arsed about that prompt in debug mode\r
804     // if console logging is on in the prefs, warn about performance hit\r
805     if (g_PrefsDlg.mGamesDialog.m_bLogConsole)\r
806     {\r
807       if (gtk_MessageBox (NULL, "Preferences indicate that console logging is on. This affects performance.\n"\r
808                           "Turn it off?", "Radiant", MB_YESNO | MB_ICONQUESTION) == IDYES)\r
809       {\r
810         g_PrefsDlg.mGamesDialog.m_bLogConsole = false;\r
811         g_PrefsDlg.mGamesDialog.SavePrefs();\r
812       }\r
813     }\r
814 #endif\r
815     // toggle console logging if necessary\r
816     Sys_LogFile();\r
817   }\r
818 \r
819   // FIXME http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=639\r
820   // we should search in g_strTempPath, then move over to look at g_strAppPath?\r
821 #ifdef _WIN32\r
822   // fine tune the look of the app using a gtk rc file\r
823   // we try to load an RC file placed in the application directory\r
824   // build the full path\r
825   Str sRCFile;\r
826   sRCFile = g_strAppPath;\r
827   sRCFile += "radiantgtkrc";\r
828   // we load that file with g_new in gtk_rc_parse_file (gtkrc.c), change the '/' into '\\'\r
829   pBuffer = (char *)sRCFile.GetBuffer();\r
830   for (i=0; i<sRCFile.GetLength(); i++)\r
831   {\r
832     if (pBuffer[i]=='/')\r
833     {\r
834       pBuffer[i] = '\\';\r
835     }\r
836   }\r
837   // check the file exists\r
838   if (access(sRCFile.GetBuffer(), R_OK) != 0)\r
839     Sys_Printf("RC file %s not found\n", sRCFile.GetBuffer());\r
840   else\r
841   {\r
842     Sys_Printf ("Attemping to load RC file %s\n", sRCFile.GetBuffer());\r
843     gtk_rc_parse (sRCFile.GetBuffer());\r
844   }\r
845 #endif\r
846 \r
847 #ifndef SKIP_SPLASH\r
848   create_splash ();\r
849 #endif\r
850 \r
851   if (!QGL_Init(libgl, ""))\r
852   {\r
853     Sys_FPrintf (SYS_ERR, "Failed to load OpenGL libraries\n");\r
854     _exit (1);\r
855     return 1;\r
856   }\r
857 \r
858 #if defined (__linux__) || defined (__APPLE__)\r
859   if ((qglXQueryExtension == NULL) || (qglXQueryExtension(GDK_DISPLAY(),NULL,NULL) != True))\r
860   {\r
861     Sys_FPrintf (SYS_ERR, "glXQueryExtension failed\n");\r
862     _exit (1);\r
863     return 1;\r
864   }\r
865 #endif\r
866 \r
867   // redirect Gtk warnings to the console\r
868   g_log_set_handler ("Gdk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|\r
869                                              G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, NULL);\r
870   g_log_set_handler ("Gtk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|\r
871                                              G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, NULL);\r
872 \r
873   // spog - creates new filters list for the first time\r
874   g_qeglobals.d_savedinfo.filters = NULL; //initialise to NULL\r
875   g_qeglobals.d_savedinfo.filters = FilterUpdate(g_qeglobals.d_savedinfo.filters);\r
876 \r
877   g_pParentWnd = new MainFrame ();\r
878 \r
879   if (g_PrefsDlg.m_bLoadLastMap && g_PrefsDlg.m_strLastMap.GetLength() > 0)\r
880     Map_LoadFile(g_PrefsDlg.m_strLastMap.GetBuffer());\r
881   else\r
882     Map_New();\r
883 \r
884   // load up shaders now that we have the map loaded\r
885   // eviltypeguy\r
886   Texture_ShowStartupShaders ();\r
887 \r
888 #ifndef SKIP_SPLASH\r
889   gdk_window_raise (splash_screen->window);\r
890   gtk_window_set_transient_for (GTK_WINDOW (splash_screen), GTK_WINDOW (g_pParentWnd->m_pWidget));\r
891   gtk_timeout_add (1000, try_destroy_splash, NULL);\r
892 #endif\r
893   \r
894   g_pParentWnd->GetSynapseServer().DumpActiveClients();\r
895 \r
896   //++timo: temporary debug\r
897   g_pParentWnd->DoWatchBSP();\r
898 \r
899   gtk_main ();\r
900 \r
901   // close the log file if any\r
902   // NOTE: don't save prefs past this point!\r
903   g_PrefsDlg.mGamesDialog.m_bLogConsole = false;\r
904   // set the console window to NULL to avoid Sys_Printf crashing\r
905   g_qeglobals_gui.d_edit = NULL;\r
906   Sys_LogFile();\r
907 \r
908   // NOTE TTimo not sure what this _exit(0) call is worth\r
909   //   restricting it to linux build\r
910 #ifdef __linux__\r
911   _exit (0);\r
912 #endif\r
913   return 0;\r
914 }\r
915 \r
916 // ydnar: quick and dirty fix, just make the buffer bigger\r
917 #define BIG_PATH_MAX    4096\r
918 \r
919 // TTimo: decompose the BSP command into several steps so we can monitor them eventually\r
920 void QE_ExpandBspString (char *bspaction, GPtrArray *out_array, char *mapname)\r
921 {\r
922   const char  *in;\r
923   char  *out;\r
924   char  src[BIG_PATH_MAX];\r
925   char  rsh[BIG_PATH_MAX];\r
926   char  base[BIG_PATH_MAX];\r
927 \r
928   strcpy(src, mapname);\r
929   strlwr(src);\r
930   in = strstr(src, "maps/");\r
931   if (!in)\r
932   {\r
933     in = strstr(src, "maps/");\r
934   }\r
935   if (in)\r
936   {\r
937     in += 5;\r
938     strcpy(base, in);\r
939     out = base;\r
940     while (*out)\r
941     {\r
942       if (*out == '\\')\r
943       {\r
944         *out = '/';\r
945       }\r
946       out++;\r
947     }\r
948   } else\r
949   {\r
950     ExtractFileName (mapname, base);\r
951   }\r
952 \r
953   // this important step alters the map name to add fs_game\r
954   // NOTE: it used to add fs_basepath too\r
955   // the fs_basepath addition moved to being in the project file during the bug fixing rush\r
956   // but it may not have been the right thing to do\r
957 \r
958   // HACK: halflife compiler tools don't support -fs_game\r
959   // HACK: neither does JKII/SoF2/ etc..\r
960   // do we use & have fs_game?\r
961   \r
962   if (g_pGameDescription->mGameFile != "hl.game" &&\r
963       *ValueForKey(g_qeglobals.d_project_entity,"gamename") != '\0')\r
964     {\r
965       // set with fs_game\r
966       sprintf(src, "-fs_game %s \"%s\"", ValueForKey(g_qeglobals.d_project_entity,"gamename"), mapname);\r
967     }\r
968     else\r
969     {\r
970       sprintf(src, "\"%s\"", mapname);\r
971   }\r
972 \r
973   rsh[0] = 0;\r
974 \r
975   QE_ConvertDOSToUnixName(src, src);\r
976 \r
977   // initialise the first step\r
978   out = new char[BIG_PATH_MAX]; //% PATH_MAX\r
979   g_ptr_array_add( out_array, out );\r
980 \r
981   in = ValueForKey( g_qeglobals.d_project_entity, bspaction );\r
982   while (*in)\r
983   {\r
984     if (in[0] == '!')\r
985     {\r
986       strcpy (out, rsh);\r
987       out += strlen(rsh);\r
988       in++;\r
989       continue;\r
990     }\r
991     if (in[0] == '#')\r
992     {\r
993       char tmp[2048];\r
994       // we process these only if monitoring\r
995       if (g_PrefsDlg.m_bWatchBSP)\r
996       {\r
997         // -connect global option (the only global option so far anyway)\r
998         strcpy (tmp, " -connect 127.0.0.1:39000 ");\r
999         strcpy (out, tmp);\r
1000         out += strlen(tmp);\r
1001       }\r
1002       in++;\r
1003       continue;\r
1004     }\r
1005     if (in[0] == '$')\r
1006     {\r
1007       // $ expansion\r
1008       strcpy (out, src);\r
1009       out += strlen(src);\r
1010       in++;\r
1011       continue;\r
1012     }\r
1013     if (in[0] == '@')\r
1014     {\r
1015       *out++ = '"';\r
1016       in++;\r
1017       continue;\r
1018     }\r
1019     if (in[0] == '&')\r
1020       if (in[1] == '&')\r
1021       {\r
1022         // start a new step\r
1023         *out = 0;\r
1024         in = in + 2;\r
1025         out = new char[BIG_PATH_MAX];   //% PATH_MAX\r
1026         g_ptr_array_add( out_array, out );\r
1027       }\r
1028     *out++ = *in++;\r
1029   }\r
1030   *out = 0;\r
1031 }\r
1032 \r
1033 void FindReplace(CString& strContents, const char* pTag, const char* pValue)\r
1034 {\r
1035   if (strcmp(pTag, pValue) == 0)\r
1036     return;\r
1037   for (int nPos = strContents.Find(pTag); nPos >= 0; nPos = strContents.Find(pTag))\r
1038   {\r
1039     int nRightLen = strContents.GetLength() - strlen(pTag) - nPos;\r
1040     CString strLeft = strContents.Left(nPos);\r
1041     CString strRight = strContents.Right(nRightLen);\r
1042     strLeft += pValue;\r
1043     strLeft += strRight;\r
1044     strContents = strLeft;\r
1045   }\r
1046 }\r
1047 \r
1048 // save the map, deals with regioning\r
1049 void SaveWithRegion(char *name)\r
1050 {\r
1051   strcpy (name, currentmap);\r
1052   if (region_active)\r
1053   {\r
1054     // temporary cut the region to save regular map\r
1055     region_active = false;\r
1056     Map_SaveFile (name, false);\r
1057     region_active = true;\r
1058     StripExtension (name);\r
1059     strcat (name, ".reg");\r
1060   }\r
1061 \r
1062   Map_SaveFile (name, region_active);\r
1063 }\r
1064 \r
1065 void RunBsp (char *command)\r
1066 {\r
1067   GPtrArray *sys;\r
1068   char  batpath[BIG_PATH_MAX];  //% PATH_MAX\r
1069   char  temppath[BIG_PATH_MAX]; //% PATH_MAX\r
1070   char  name[BIG_PATH_MAX];             //% PATH_MAX\r
1071   char  cWork[BIG_PATH_MAX];    //% PATH_MAX\r
1072   FILE  *hFile;\r
1073   unsigned int   i;\r
1074 \r
1075   SetInspectorMode(W_CONSOLE);\r
1076 \r
1077   strcpy (temppath, g_strTempPath.GetBuffer ());\r
1078 \r
1079   SaveWithRegion(name);\r
1080 \r
1081   const char *rsh = ValueForKey(g_qeglobals.d_project_entity, "rshcmd");\r
1082   if (rsh == NULL)\r
1083   {\r
1084     CString strPath, strFile;\r
1085 \r
1086     ExtractPath_and_Filename(name, strPath, strFile);\r
1087     AddSlash(strPath);\r
1088     strncpy(cWork, strPath, 1024);\r
1089     strcat(cWork, strFile);\r
1090   } else\r
1091   {\r
1092     strcpy(cWork, name);\r
1093   }\r
1094 \r
1095   // get the array ready\r
1096   //++timo TODO: free the array, free the strings ourselves with delete[]\r
1097   sys = g_ptr_array_new();\r
1098 \r
1099   QE_ExpandBspString (command, sys, cWork);\r
1100 \r
1101   if (g_PrefsDlg.m_bWatchBSP)\r
1102   {\r
1103     // grab the file name for engine running\r
1104     char *bspname = new char[1024];\r
1105     ExtractFileName( currentmap, bspname );\r
1106     StripExtension( bspname );\r
1107     g_pParentWnd->GetWatchBSP()->DoMonitoringLoop( sys, bspname );\r
1108   } else\r
1109   {\r
1110     // write all the steps in a single BAT / .sh file and run it, don't bother monitoring it\r
1111     CString strSys;\r
1112     for (i=0; i < sys->len; i++ )\r
1113     {\r
1114       strSys += (char *)g_ptr_array_index( sys, i);\r
1115 #ifdef _WIN32  // write temp\junk.txt in win32... NOTE: stops output to shell prompt window\r
1116       if (i==0) \r
1117         strSys += " >";\r
1118       else \r
1119         strSys += " >>";\r
1120       strSys += "\"";\r
1121       strSys += temppath;\r
1122       strSys += "junk.txt\"";\r
1123 #endif\r
1124       strSys += "\n";\r
1125     };\r
1126 \r
1127 #if defined (__linux__) || defined (__APPLE__)\r
1128 \r
1129     // write qe3bsp.sh\r
1130     sprintf (batpath, "%sqe3bsp.sh", temppath);\r
1131     Sys_Printf("Writing the compile script to '%s'\n", batpath);\r
1132     Sys_Printf("The build output will be saved in '%sjunk.txt'\n", temppath);\r
1133     hFile = fopen(batpath, "w");\r
1134     if (!hFile)\r
1135       Error ("Can't write to %s", batpath);\r
1136     fprintf (hFile, "#!/bin/sh \n\n");\r
1137     fprintf (hFile, strSys.GetBuffer());\r
1138     fclose (hFile);\r
1139     chmod (batpath, 0744);\r
1140 #endif\r
1141 \r
1142 #ifdef _WIN32\r
1143     sprintf (batpath, "%sqe3bsp.bat", temppath);\r
1144     Sys_Printf("Writing the compile script to '%s'\n", batpath);\r
1145     Sys_Printf("The build output will be saved in '%sjunk.txt'\n", temppath);\r
1146     hFile = fopen(batpath, "w");\r
1147     if (!hFile)\r
1148       Error ("Can't write to %s", batpath);\r
1149     fprintf (hFile, strSys.GetBuffer());\r
1150     fclose (hFile); \r
1151 #endif\r
1152 \r
1153     Pointfile_Delete ();\r
1154 \r
1155 #if defined (__linux__) || defined (__APPLE__)\r
1156 \r
1157     pid_t pid;\r
1158 \r
1159     pid = fork ();\r
1160     switch (pid)\r
1161     {\r
1162     case -1:\r
1163       Error ("CreateProcess failed");\r
1164       break;\r
1165     case 0:\r
1166       execlp (batpath, batpath, NULL);\r
1167       printf ("execlp error !");\r
1168       _exit (0);\r
1169       break;\r
1170     default:\r
1171       break;\r
1172     }\r
1173 #endif\r
1174 \r
1175 #ifdef _WIN32\r
1176     Sys_Printf ("Running bsp command...\n");\r
1177     Sys_Printf ("\n%s\n", strSys.GetBuffer());\r
1178 \r
1179     WinExec( batpath, SW_SHOWNORMAL );\r
1180 #endif\r
1181   }\r
1182 #ifdef _DEBUG\r
1183   // yeah, do it .. but not now right before 1.1-TA-beta release\r
1184   Sys_Printf("TODO: erase GPtrArray\n");\r
1185 #endif\r
1186 }\r
1187 \r
1188 #if 0\r
1189 \r
1190 #ifdef _WIN32\r
1191 \r
1192 int WINAPI QEW_SetupPixelFormat(HDC hDC, qboolean zbuffer )\r
1193 {\r
1194   static PIXELFORMATDESCRIPTOR pfd = {\r
1195     sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd\r
1196     1,                              // version number\r
1197     PFD_DRAW_TO_WINDOW |            // support window\r
1198     PFD_SUPPORT_OPENGL |            // support OpenGL\r
1199     PFD_DOUBLEBUFFER,               // double buffered\r
1200     PFD_TYPE_RGBA,                  // RGBA type\r
1201     24,                             // 24-bit color depth\r
1202     0, 0, 0, 0, 0, 0,               // color bits ignored\r
1203     0,                              // no alpha buffer\r
1204     0,                              // shift bit ignored\r
1205     0,                              // no accumulation buffer\r
1206     0, 0, 0, 0,                     // accum bits ignored\r
1207     32,                             // depth bits\r
1208     0,                              // no stencil buffer\r
1209     0,                              // no auxiliary buffer\r
1210     PFD_MAIN_PLANE,                 // main layer\r
1211     0,                              // reserved\r
1212     0, 0, 0                         // layer masks ignored\r
1213   };                              //\r
1214   int pixelformat = 0;            \r
1215 \r
1216   zbuffer = true;\r
1217   if ( !zbuffer )\r
1218     pfd.cDepthBits = 0;\r
1219 \r
1220   if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 )\r
1221   {\r
1222     LPVOID lpMsgBuf;\r
1223     FormatMessage(\r
1224                  FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
1225                  FORMAT_MESSAGE_FROM_SYSTEM | \r
1226                  FORMAT_MESSAGE_IGNORE_INSERTS,\r
1227                  NULL,\r
1228                  GetLastError(),\r
1229                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language\r
1230                  (LPTSTR) &lpMsgBuf,\r
1231                  0,\r
1232                  NULL \r
1233                  );\r
1234     Sys_FPrintf (SYS_WRN, "GetLastError: %s", lpMsgBuf);\r
1235     Error ("ChoosePixelFormat failed");\r
1236   }\r
1237 \r
1238   if (!SetPixelFormat(hDC, pixelformat, &pfd))\r
1239     Error ("SetPixelFormat failed");\r
1240 \r
1241   return pixelformat;\r
1242 }\r
1243 \r
1244 #endif\r
1245 \r
1246 #endif\r