]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sys_shared.c
PROPERLY load the icon in SDL/X11
[xonotic/darkplaces.git] / sys_shared.c
1 #include "quakedef.h"
2
3 #define SUPPORTDLL
4
5 #ifdef WIN32
6 # ifdef _WIN64
7 #  ifndef _WIN32_WINNT
8 #   define _WIN32_WINNT 0x0502
9 #  endif
10    // for SetDllDirectory
11 # endif
12 # include <windows.h>
13 # include <mmsystem.h> // timeGetTime
14 # include <time.h> // localtime
15 #ifdef _MSC_VER
16 #pragma comment(lib, "winmm.lib")
17 #endif
18 #else
19 # include <unistd.h>
20 # include <fcntl.h>
21 # include <sys/time.h>
22 # include <time.h>
23 # ifdef SUPPORTDLL
24 #  include <dlfcn.h>
25 # endif
26 #endif
27
28 static char sys_timestring[128];
29 char *Sys_TimeString(const char *timeformat)
30 {
31         time_t mytime = time(NULL);
32 #if _MSC_VER >= 1400
33         struct tm mytm;
34         localtime_s(&mytm, &mytime);
35         strftime(sys_timestring, sizeof(sys_timestring), timeformat, &mytm);
36 #else
37         strftime(sys_timestring, sizeof(sys_timestring), timeformat, localtime(&mytime));
38 #endif
39         return sys_timestring;
40 }
41
42
43 extern qboolean host_shuttingdown;
44 void Sys_Quit (int returnvalue)
45 {
46         if (COM_CheckParm("-profilegameonly"))
47                 Sys_AllowProfiling(false);
48         host_shuttingdown = true;
49         Host_Shutdown();
50         exit(returnvalue);
51 }
52
53 #if defined(__linux__) || defined(__FreeBSD__)
54 #ifdef __cplusplus
55 extern "C"
56 #endif
57 int moncontrol(int);
58 #endif
59
60 void Sys_AllowProfiling(qboolean enable)
61 {
62 #if defined(__linux__) || defined(__FreeBSD__)
63         moncontrol(enable);
64 #endif
65 }
66
67
68 /*
69 ===============================================================================
70
71 DLL MANAGEMENT
72
73 ===============================================================================
74 */
75
76 static qboolean Sys_LoadLibraryFunctions(dllhandle_t dllhandle, const dllfunction_t *fcts, qboolean complain, qboolean has_next)
77 {
78         const dllfunction_t *func;
79         if(dllhandle)
80         {
81                 for (func = fcts; func && func->name != NULL; func++)
82                         if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
83                         {
84                                 if(complain)
85                                 {
86                                         Con_DPrintf (" - missing function \"%s\" - broken library!", func->name);
87                                         if(has_next)
88                                                 Con_DPrintf("\nContinuing with");
89                                 }
90                                 goto notfound;
91                         }
92                 return true;
93
94         notfound:
95                 for (func = fcts; func && func->name != NULL; func++)
96                         *func->funcvariable = NULL;
97         }
98         return false;
99 }
100
101 qboolean Sys_LoadLibrary (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
102 {
103 #ifdef SUPPORTDLL
104         const dllfunction_t *func;
105         dllhandle_t dllhandle = 0;
106         unsigned int i;
107
108         if (handle == NULL)
109                 return false;
110
111 #ifndef WIN32
112 #ifdef PREFER_PRELOAD
113         dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
114         if(Sys_LoadLibraryFunctions(dllhandle, fcts, false, false))
115         {
116                 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
117                 *handle = dllhandle;
118                 return true;
119         }
120         else
121                 Sys_UnloadLibrary(&dllhandle);
122 notfound:
123 #endif
124 #endif
125
126         // Initializations
127         for (func = fcts; func && func->name != NULL; func++)
128                 *func->funcvariable = NULL;
129
130         // Try every possible name
131         Con_DPrintf ("Trying to load library...");
132         for (i = 0; dllnames[i] != NULL; i++)
133         {
134                 Con_DPrintf (" \"%s\"", dllnames[i]);
135 #ifdef WIN32
136 # ifdef _WIN64
137                 SetDllDirectory("bin64");
138 # endif
139                 dllhandle = LoadLibrary (dllnames[i]);
140 # ifdef _WIN64
141                 SetDllDirectory(NULL);
142 # endif
143 #else
144                 dllhandle = dlopen (dllnames[i], RTLD_LAZY | RTLD_GLOBAL);
145 #endif
146                 if (Sys_LoadLibraryFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(com_argv[0], '/'))))
147                         break;
148                 else
149                         Sys_UnloadLibrary (&dllhandle);
150         }
151
152         // see if the names can be loaded relative to the executable path
153         // (this is for Mac OSX which does not check next to the executable)
154         if (!dllhandle && strrchr(com_argv[0], '/'))
155         {
156                 char path[MAX_OSPATH];
157                 strlcpy(path, com_argv[0], sizeof(path));
158                 strrchr(path, '/')[1] = 0;
159                 for (i = 0; dllnames[i] != NULL; i++)
160                 {
161                         char temp[MAX_OSPATH];
162                         strlcpy(temp, path, sizeof(temp));
163                         strlcat(temp, dllnames[i], sizeof(temp));
164                         Con_DPrintf (" \"%s\"", temp);
165 #ifdef WIN32
166                         dllhandle = LoadLibrary (temp);
167 #else
168                         dllhandle = dlopen (temp, RTLD_LAZY | RTLD_GLOBAL);
169 #endif
170                         if (Sys_LoadLibraryFunctions(dllhandle, fcts, true, dllnames[i+1] != NULL))
171                                 break;
172                         else
173                                 Sys_UnloadLibrary (&dllhandle);
174                 }
175         }
176
177         // No DLL found
178         if (! dllhandle)
179         {
180                 Con_DPrintf(" - failed.\n");
181                 return false;
182         }
183
184         Con_DPrintf(" - loaded.\n");
185
186         *handle = dllhandle;
187         return true;
188 #else
189         return false;
190 #endif
191 }
192
193 void Sys_UnloadLibrary (dllhandle_t* handle)
194 {
195 #ifdef SUPPORTDLL
196         if (handle == NULL || *handle == NULL)
197                 return;
198
199 #ifdef WIN32
200         FreeLibrary (*handle);
201 #else
202         dlclose (*handle);
203 #endif
204
205         *handle = NULL;
206 #endif
207 }
208
209 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
210 {
211 #ifdef SUPPORTDLL
212 #ifdef WIN32
213         return (void *)GetProcAddress (handle, name);
214 #else
215         return (void *)dlsym (handle, name);
216 #endif
217 #else
218         return NULL;
219 #endif
220 }
221
222 #ifdef WIN32
223 # define HAVE_TIMEGETTIME 1
224 # define HAVE_QUERYPERFORMANCECOUNTER 1
225 # define HAVE_Sleep 1
226 #endif
227
228 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
229 # define HAVE_CLOCKGETTIME 1
230 #endif
231
232 #ifndef WIN32
233 // FIXME improve this check, manpage hints to DST_NONE
234 # define HAVE_GETTIMEOFDAY 1
235 #endif
236
237 #ifdef FD_SET
238 # define HAVE_SELECT 1
239 #endif
240
241 #ifndef WIN32
242 // FIXME improve this check
243 # define HAVE_USLEEP 1
244 #endif
245
246 // this one is referenced elsewhere
247 cvar_t sys_usenoclockbutbenchmark = {CVAR_SAVE, "sys_usenoclockbutbenchmark", "0", "don't use ANY real timing, and simulate a clock (for benchmarking); the game then runs as fast as possible. Run a QC mod with bots that does some stuff, then does a quit at the end, to benchmark a server. NEVER do this on a public server."};
248
249 // these are not
250 static cvar_t sys_debugsleep = {0, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
251 static cvar_t sys_usesdlgetticks = {CVAR_SAVE, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
252 static cvar_t sys_usesdldelay = {CVAR_SAVE, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
253 #if HAVE_QUERYPERFORMANCECOUNTER
254 static cvar_t sys_usequeryperformancecounter = {CVAR_SAVE, "sys_usequeryperformancecounter", "0", "use windows QueryPerformanceCounter timer (which has issues on multicore/multiprocessor machines and processors which are designed to conserve power) for timing rather than timeGetTime function (which has issues on some motherboards)"};
255 #endif
256 #if HAVE_CLOCKGETTIME
257 static cvar_t sys_useclockgettime = {CVAR_SAVE, "sys_useclockgettime", "0", "use POSIX clock_gettime function (which has issues if the system clock speed is far off, as it can't get fixed by NTP) for timing rather than gettimeofday (which has issues if the system time is stepped by ntpdate, or apparently on some Xen installations)"};
258 #endif
259
260 static unsigned long benchmark_time;
261
262 void Sys_Init_Commands (void)
263 {
264         Cvar_RegisterVariable(&sys_debugsleep);
265         Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
266 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
267         if(sys_supportsdlgetticks)
268         {
269                 Cvar_RegisterVariable(&sys_usesdlgetticks);
270                 Cvar_RegisterVariable(&sys_usesdldelay);
271         }
272 #endif
273 #if HAVE_QUERYPERFORMANCECOUNTER
274         Cvar_RegisterVariable(&sys_usequeryperformancecounter);
275 #endif
276 #if HAVE_CLOCKGETTIME
277         Cvar_RegisterVariable(&sys_useclockgettime);
278 #endif
279 }
280
281 double Sys_DoubleTime(void)
282 {
283         static int first = true;
284         static double oldtime = 0.0, curtime = 0.0;
285         double newtime;
286         if(sys_usenoclockbutbenchmark.integer)
287         {
288                 benchmark_time += 1;
289                 return ((double) benchmark_time) / 1e6;
290         }
291
292         // first all the OPTIONAL timers
293
294 #if HAVE_QUERYPERFORMANCECOUNTER
295         else if (sys_usequeryperformancecounter.integer)
296         {
297                 // LordHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
298                 // QueryPerformanceCounter
299                 // platform:
300                 // Windows 95/98/ME/NT/2000/XP
301                 // features:
302                 // very accurate (CPU cycles)
303                 // known issues:
304                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
305                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
306                 double timescale;
307                 LARGE_INTEGER PerformanceFreq;
308                 LARGE_INTEGER PerformanceCount;
309
310                 if (!QueryPerformanceFrequency (&PerformanceFreq))
311                 {
312                         Con_Printf ("No hardware timer available\n");
313                         // fall back to timeGetTime
314                         Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
315                         return Sys_DoubleTime();
316                 }
317                 QueryPerformanceCounter (&PerformanceCount);
318
319                 #ifdef __BORLANDC__
320                 timescale = 1.0 / ((double) PerformanceFreq.u.LowPart + (double) PerformanceFreq.u.HighPart * 65536.0 * 65536.0);
321                 newtime = ((double) PerformanceCount.u.LowPart + (double) PerformanceCount.u.HighPart * 65536.0 * 65536.0) * timescale;
322                 #else
323                 timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
324                 newtime = ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
325                 #endif
326         }
327 #endif
328
329 #if HAVE_CLOCKGETTIME
330         else if (sys_useclockgettime.integer)
331         {
332                 struct timespec ts;
333 #  ifdef CLOCK_MONOTONIC
334                 // linux
335                 clock_gettime(CLOCK_MONOTONIC, &ts);
336 #  else
337                 // sunos
338                 clock_gettime(CLOCK_HIGHRES, &ts);
339 #  endif
340                 newtime = (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
341         }
342 #endif
343
344         // now all the FALLBACK timers
345         else if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
346         {
347                 newtime = (double) Sys_SDL_GetTicks() / 1000.0;
348         }
349 #if HAVE_GETTIMEOFDAY
350         else
351         {
352                 struct timeval tp;
353                 gettimeofday(&tp, NULL);
354                 newtime = (double) tp.tv_sec + tp.tv_usec / 1000000.0;
355         }
356 #elif HAVE_TIMEGETTIME
357         else
358         {
359                 static int firsttimegettime = true;
360                 // timeGetTime
361                 // platform:
362                 // Windows 95/98/ME/NT/2000/XP
363                 // features:
364                 // reasonable accuracy (millisecond)
365                 // issues:
366                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
367
368                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
369                 if (firsttimegettime)
370                 {
371                         timeBeginPeriod (1);
372                         firsttimegettime = false;
373                 }
374
375                 newtime = (double) timeGetTime () / 1000.0;
376         }
377 #else
378         // fallback for using the SDL timer if no other timer is available
379         else
380         {
381                 newtime = (double) Sys_SDL_GetTicks() / 1000.0;
382                 // this calls Sys_Error() if not linking against SDL
383         }
384 #endif
385
386         if (first)
387         {
388                 first = false;
389                 oldtime = newtime;
390         }
391
392         if (newtime < oldtime)
393         {
394                 // warn if it's significant
395                 if (newtime - oldtime < -0.01)
396                         Con_Printf("Sys_DoubleTime: time stepped backwards (went from %f to %f, difference %f)\n", oldtime, newtime, newtime - oldtime);
397         }
398         else if (newtime > oldtime + 1800)
399         {
400                 Con_Printf("Sys_DoubleTime: time stepped forward (went from %f to %f, difference %f)\n", oldtime, newtime, newtime - oldtime);
401         }
402         else
403                 curtime += newtime - oldtime;
404         oldtime = newtime;
405
406         return curtime;
407 }
408
409 void Sys_Sleep(int microseconds)
410 {
411         double t = 0;
412         if(sys_usenoclockbutbenchmark.integer)
413         {
414                 benchmark_time += microseconds;
415                 return;
416         }
417         if(sys_debugsleep.integer)
418         {
419                 t = Sys_DoubleTime();
420         }
421         if(sys_supportsdlgetticks && sys_usesdldelay.integer)
422         {
423                 Sys_SDL_Delay(microseconds / 1000);
424         }
425 #if HAVE_SELECT
426         else
427         {
428                 struct timeval tv;
429                 tv.tv_sec = microseconds / 1000000;
430                 tv.tv_usec = microseconds % 1000000;
431                 select(0, NULL, NULL, NULL, &tv);
432         }
433 #elif HAVE_USLEEP
434         else
435         {
436                 usleep(microseconds);
437         }
438 #elif HAVE_Sleep
439         else
440         {
441                 Sleep(microseconds / 1000);
442         }
443 #else
444         else
445         {
446                 Sys_SDL_Delay(microseconds / 1000);
447         }
448 #endif
449         if(sys_debugsleep.integer)
450         {
451                 t = Sys_DoubleTime() - t;
452                 printf("%d %d # debugsleep\n", microseconds, (unsigned int)(t * 1000000));
453         }
454 }
455
456 const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
457 {
458         const char *p = PATH;
459         const char *q;
460         if(p && name)
461         {
462                 while((q = strchr(p, ':')))
463                 {
464                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
465                         if(FS_SysFileExists(buf))
466                                 return buf;
467                         p = q + 1;
468                 }
469                 if(!q) // none found - try the last item
470                 {
471                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
472                         if(FS_SysFileExists(buf))
473                                 return buf;
474                 }
475         }
476         return name;
477 }
478
479 const char *Sys_FindExecutableName(void)
480 {
481 #if defined(WIN32)
482         return com_argv[0];
483 #else
484         static char exenamebuf[MAX_OSPATH+1];
485         ssize_t n = -1;
486 #if defined(__FreeBSD__)
487         n = readlink("/proc/curproc/file", exenamebuf, sizeof(exenamebuf)-1);
488 #elif defined(__linux__)
489         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
490 #endif
491         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
492         {
493                 exenamebuf[n] = 0;
494                 return exenamebuf;
495         }
496         if(strchr(com_argv[0], '/'))
497                 return com_argv[0]; // possibly a relative path
498         else
499                 return Sys_FindInPATH(com_argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
500 #endif
501 }
502
503 void Sys_ProvideSelfFD(void)
504 {
505         if(com_selffd != -1)
506                 return;
507         com_selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
508 }