]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sys_shared.c
work with SDL2
[xonotic/darkplaces.git] / sys_shared.c
1 #ifdef WIN32
2 # ifndef DONT_USE_SETDLLDIRECTORY
3 #  define _WIN32_WINNT 0x0502
4 # endif
5 #endif
6
7 #include "quakedef.h"
8
9 #define SUPPORTDLL
10
11 #ifdef WIN32
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 # ifndef DONT_USE_SETDLLDIRECTORY
137 #  ifdef _WIN64
138                 SetDllDirectory("bin64");
139 #  else
140                 SetDllDirectory("bin32");
141 #  endif
142 # endif
143                 dllhandle = LoadLibrary (dllnames[i]);
144                 // no need to unset this - we want ALL dlls to be loaded from there, anyway
145 #else
146                 dllhandle = dlopen (dllnames[i], RTLD_LAZY | RTLD_GLOBAL);
147 #endif
148                 if (Sys_LoadLibraryFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(com_argv[0], '/'))))
149                         break;
150                 else
151                         Sys_UnloadLibrary (&dllhandle);
152         }
153
154         // see if the names can be loaded relative to the executable path
155         // (this is for Mac OSX which does not check next to the executable)
156         if (!dllhandle && strrchr(com_argv[0], '/'))
157         {
158                 char path[MAX_OSPATH];
159                 strlcpy(path, com_argv[0], sizeof(path));
160                 strrchr(path, '/')[1] = 0;
161                 for (i = 0; dllnames[i] != NULL; i++)
162                 {
163                         char temp[MAX_OSPATH];
164                         strlcpy(temp, path, sizeof(temp));
165                         strlcat(temp, dllnames[i], sizeof(temp));
166                         Con_DPrintf (" \"%s\"", temp);
167 #ifdef WIN32
168                         dllhandle = LoadLibrary (temp);
169 #else
170                         dllhandle = dlopen (temp, RTLD_LAZY | RTLD_GLOBAL);
171 #endif
172                         if (Sys_LoadLibraryFunctions(dllhandle, fcts, true, dllnames[i+1] != NULL))
173                                 break;
174                         else
175                                 Sys_UnloadLibrary (&dllhandle);
176                 }
177         }
178
179         // No DLL found
180         if (! dllhandle)
181         {
182                 Con_DPrintf(" - failed.\n");
183                 return false;
184         }
185
186         Con_DPrintf(" - loaded.\n");
187
188         *handle = dllhandle;
189         return true;
190 #else
191         return false;
192 #endif
193 }
194
195 void Sys_UnloadLibrary (dllhandle_t* handle)
196 {
197 #ifdef SUPPORTDLL
198         if (handle == NULL || *handle == NULL)
199                 return;
200
201 #ifdef WIN32
202         FreeLibrary (*handle);
203 #else
204         dlclose (*handle);
205 #endif
206
207         *handle = NULL;
208 #endif
209 }
210
211 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
212 {
213 #ifdef SUPPORTDLL
214 #ifdef WIN32
215         return (void *)GetProcAddress (handle, name);
216 #else
217         return (void *)dlsym (handle, name);
218 #endif
219 #else
220         return NULL;
221 #endif
222 }
223
224 #ifdef WIN32
225 # define HAVE_TIMEGETTIME 1
226 # define HAVE_QUERYPERFORMANCECOUNTER 1
227 # define HAVE_Sleep 1
228 #endif
229
230 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
231 # define HAVE_CLOCKGETTIME 1
232 #endif
233
234 #ifndef WIN32
235 // FIXME improve this check, manpage hints to DST_NONE
236 # define HAVE_GETTIMEOFDAY 1
237 #endif
238
239 #ifndef WIN32
240 // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
241 // (so much for POSIX...)
242 # ifdef FD_SET
243 #  define HAVE_SELECT 1
244 # endif
245 #endif
246
247 #ifndef WIN32
248 // FIXME improve this check
249 # define HAVE_USLEEP 1
250 #endif
251
252 // this one is referenced elsewhere
253 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."};
254
255 // these are not
256 static cvar_t sys_debugsleep = {0, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
257 static cvar_t sys_usesdlgetticks = {CVAR_SAVE, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
258 static cvar_t sys_usesdldelay = {CVAR_SAVE, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
259 #if HAVE_QUERYPERFORMANCECOUNTER
260 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)"};
261 #endif
262 #if HAVE_CLOCKGETTIME
263 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)"};
264 #endif
265
266 static double benchmark_time; // actually always contains an integer amount of milliseconds, will eventually "overflow"
267
268 void Sys_Init_Commands (void)
269 {
270         Cvar_RegisterVariable(&sys_debugsleep);
271         Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
272 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
273         if(sys_supportsdlgetticks)
274         {
275                 Cvar_RegisterVariable(&sys_usesdlgetticks);
276                 Cvar_RegisterVariable(&sys_usesdldelay);
277         }
278 #endif
279 #if HAVE_QUERYPERFORMANCECOUNTER
280         Cvar_RegisterVariable(&sys_usequeryperformancecounter);
281 #endif
282 #if HAVE_CLOCKGETTIME
283         Cvar_RegisterVariable(&sys_useclockgettime);
284 #endif
285 }
286
287 double Sys_DirtyTime(void)
288 {
289         // first all the OPTIONAL timers
290
291         // benchmark timer (fake clock)
292         if(sys_usenoclockbutbenchmark.integer)
293         {
294                 double old_benchmark_time = benchmark_time;
295                 benchmark_time += 1;
296                 if(benchmark_time == old_benchmark_time)
297                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
298                 return benchmark_time * 0.000001;
299         }
300 #if HAVE_QUERYPERFORMANCECOUNTER
301         if (sys_usequeryperformancecounter.integer)
302         {
303                 // LordHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
304                 // QueryPerformanceCounter
305                 // platform:
306                 // Windows 95/98/ME/NT/2000/XP
307                 // features:
308                 // very accurate (CPU cycles)
309                 // known issues:
310                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
311                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
312                 double timescale;
313                 LARGE_INTEGER PerformanceFreq;
314                 LARGE_INTEGER PerformanceCount;
315
316                 if (QueryPerformanceFrequency (&PerformanceFreq))
317                 {
318                         QueryPerformanceCounter (&PerformanceCount);
319         
320                         #ifdef __BORLANDC__
321                         timescale = 1.0 / ((double) PerformanceFreq.u.LowPart + (double) PerformanceFreq.u.HighPart * 65536.0 * 65536.0);
322                         return ((double) PerformanceCount.u.LowPart + (double) PerformanceCount.u.HighPart * 65536.0 * 65536.0) * timescale;
323                         #else
324                         timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
325                         return ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
326                         #endif
327                 }
328                 else
329                 {
330                         Con_Printf("No hardware timer available\n");
331                         // fall back to other clock sources
332                         Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
333                 }
334         }
335 #endif
336
337 #if HAVE_CLOCKGETTIME
338         if (sys_useclockgettime.integer)
339         {
340                 struct timespec ts;
341 #  ifdef CLOCK_MONOTONIC
342                 // linux
343                 clock_gettime(CLOCK_MONOTONIC, &ts);
344 #  else
345                 // sunos
346                 clock_gettime(CLOCK_HIGHRES, &ts);
347 #  endif
348                 return (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
349         }
350 #endif
351
352         // now all the FALLBACK timers
353         if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
354                 return (double) Sys_SDL_GetTicks() / 1000.0;
355 #if HAVE_GETTIMEOFDAY
356         {
357                 struct timeval tp;
358                 gettimeofday(&tp, NULL);
359                 return (double) tp.tv_sec + tp.tv_usec / 1000000.0;
360         }
361 #elif HAVE_TIMEGETTIME
362         {
363                 static int firsttimegettime = true;
364                 // timeGetTime
365                 // platform:
366                 // Windows 95/98/ME/NT/2000/XP
367                 // features:
368                 // reasonable accuracy (millisecond)
369                 // issues:
370                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
371
372                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
373                 if (firsttimegettime)
374                 {
375                         timeBeginPeriod(1);
376                         firsttimegettime = false;
377                 }
378
379                 return (double) timeGetTime() / 1000.0;
380         }
381 #else
382         // fallback for using the SDL timer if no other timer is available
383         // this calls Sys_Error() if not linking against SDL
384         return (double) Sys_SDL_GetTicks() / 1000.0;
385 #endif
386 }
387
388 void Sys_Sleep(int microseconds)
389 {
390         double t = 0;
391         if(sys_usenoclockbutbenchmark.integer)
392         {
393                 if(microseconds)
394                 {
395                         double old_benchmark_time = benchmark_time;
396                         benchmark_time += microseconds;
397                         if(benchmark_time == old_benchmark_time)
398                                 Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
399                 }
400                 return;
401         }
402         if(sys_debugsleep.integer)
403         {
404                 t = Sys_DirtyTime();
405         }
406         if(sys_supportsdlgetticks && sys_usesdldelay.integer)
407         {
408                 Sys_SDL_Delay(microseconds / 1000);
409         }
410 #if HAVE_SELECT
411         else
412         {
413                 struct timeval tv;
414                 tv.tv_sec = microseconds / 1000000;
415                 tv.tv_usec = microseconds % 1000000;
416                 select(0, NULL, NULL, NULL, &tv);
417         }
418 #elif HAVE_USLEEP
419         else
420         {
421                 usleep(microseconds);
422         }
423 #elif HAVE_Sleep
424         else
425         {
426                 Sleep(microseconds / 1000);
427         }
428 #else
429         else
430         {
431                 Sys_SDL_Delay(microseconds / 1000);
432         }
433 #endif
434         if(sys_debugsleep.integer)
435         {
436                 t = Sys_DirtyTime() - t;
437                 Sys_PrintfToTerminal("%d %d # debugsleep\n", microseconds, (unsigned int)(t * 1000000));
438         }
439 }
440
441 void Sys_PrintfToTerminal(const char *fmt, ...)
442 {
443         va_list argptr;
444         char msg[MAX_INPUTLINE];
445
446         va_start(argptr,fmt);
447         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
448         va_end(argptr);
449
450         Sys_PrintToTerminal(msg);
451 }
452
453 #ifndef WIN32
454 static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
455 {
456         const char *p = PATH;
457         const char *q;
458         if(p && name)
459         {
460                 while((q = strchr(p, ':')))
461                 {
462                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
463                         if(FS_SysFileExists(buf))
464                                 return buf;
465                         p = q + 1;
466                 }
467                 if(!q) // none found - try the last item
468                 {
469                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
470                         if(FS_SysFileExists(buf))
471                                 return buf;
472                 }
473         }
474         return name;
475 }
476 #endif
477
478 static const char *Sys_FindExecutableName(void)
479 {
480 #if defined(WIN32)
481         return com_argv[0];
482 #else
483         static char exenamebuf[MAX_OSPATH+1];
484         ssize_t n = -1;
485 #if defined(__FreeBSD__)
486         n = readlink("/proc/curproc/file", exenamebuf, sizeof(exenamebuf)-1);
487 #elif defined(__linux__)
488         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
489 #endif
490         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
491         {
492                 exenamebuf[n] = 0;
493                 return exenamebuf;
494         }
495         if(strchr(com_argv[0], '/'))
496                 return com_argv[0]; // possibly a relative path
497         else
498                 return Sys_FindInPATH(com_argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
499 #endif
500 }
501
502 void Sys_ProvideSelfFD(void)
503 {
504         if(com_selffd != -1)
505                 return;
506         com_selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
507 }
508
509 // for x86 cpus only...  (x64 has SSE2_PRESENT)
510 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
511 // code from SDL, shortened as we can expect CPUID to work
512 static int CPUID_Features(void)
513 {
514         int features = 0;
515 # if defined(__GNUC__) && defined(__i386__)
516         __asm__ (
517 "        movl    %%ebx,%%edi\n"
518 "        xorl    %%eax,%%eax                                           \n"
519 "        incl    %%eax                                                 \n"
520 "        cpuid                       # Get family/model/stepping/features\n"
521 "        movl    %%edx,%0                                              \n"
522 "        movl    %%edi,%%ebx\n"
523         : "=m" (features)
524         :
525         : "%eax", "%ecx", "%edx", "%edi"
526         );
527 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
528         __asm {
529         xor     eax, eax
530         inc     eax
531         cpuid                       ; Get family/model/stepping/features
532         mov     features, edx
533         }
534 # else
535 #  error SSE_POSSIBLE set but no CPUID implementation
536 # endif
537         return features;
538 }
539 #endif
540
541 #ifdef SSE_POSSIBLE
542 qboolean Sys_HaveSSE(void)
543 {
544         // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
545         if(COM_CheckParm("-nosse"))
546                 return false;
547 #ifdef SSE_PRESENT
548         return true;
549 #else
550         // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
551         if(COM_CheckParm("-forcesse") || COM_CheckParm("-forcesse2"))
552                 return true;
553         if(CPUID_Features() & (1 << 25))
554                 return true;
555         return false;
556 #endif
557 }
558
559 qboolean Sys_HaveSSE2(void)
560 {
561         // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
562         if(COM_CheckParm("-nosse") || COM_CheckParm("-nosse2"))
563                 return false;
564 #ifdef SSE2_PRESENT
565         return true;
566 #else
567         // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
568         if(COM_CheckParm("-forcesse2"))
569                 return true;
570         if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
571                 return true;
572         return false;
573 #endif
574 }
575 #endif
576
577 /// called to set process priority for dedicated servers
578 #if defined(__linux__)
579 #include <sys/resource.h>
580 #include <errno.h>
581 static int nicelevel;
582 static qboolean nicepossible;
583 static qboolean isnice;
584 void Sys_InitProcessNice (void)
585 {
586         struct rlimit lim;
587         nicepossible = false;
588         if(COM_CheckParm("-nonice"))
589                 return;
590         errno = 0;
591         nicelevel = getpriority(PRIO_PROCESS, 0);
592         if(errno)
593         {
594                 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
595                 return;
596         }
597         if(getrlimit(RLIMIT_NICE, &lim))
598         {
599                 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
600                 return;
601         }
602         if(lim.rlim_cur != RLIM_INFINITY && nicelevel < (int) (20 - lim.rlim_cur))
603         {
604                 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
605                 return;
606         }
607         nicepossible = true;
608         isnice = false;
609 }
610 void Sys_MakeProcessNice (void)
611 {
612         if(!nicepossible)
613                 return;
614         if(isnice)
615                 return;
616         Con_DPrintf("Process is becoming 'nice'...\n");
617         if(setpriority(PRIO_PROCESS, 0, 19))
618                 Con_Printf("Failed to raise nice level to %d\n", 19);
619         isnice = true;
620 }
621 void Sys_MakeProcessMean (void)
622 {
623         if(!nicepossible)
624                 return;
625         if(!isnice)
626                 return;
627         Con_DPrintf("Process is becoming 'mean'...\n");
628         if(setpriority(PRIO_PROCESS, 0, nicelevel))
629                 Con_Printf("Failed to lower nice level to %d\n", nicelevel);
630         isnice = false;
631 }
632 #else
633 void Sys_InitProcessNice (void)
634 {
635 }
636 void Sys_MakeProcessNice (void)
637 {
638 }
639 void Sys_MakeProcessMean (void)
640 {
641 }
642 #endif