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