DarkPlaces
Game engine based on the Quake 1 engine by id Software, developed by LadyHavoc
 
sys_shared.c
Go to the documentation of this file.
1#ifdef WIN32
2# ifndef DONT_USE_SETDLLDIRECTORY
3# define _WIN32_WINNT 0x0502
4# endif
5#endif
6
7#define SUPPORTDLL
8
9#ifdef WIN32
10# include <windows.h>
11# include <mmsystem.h> // timeGetTime
12# include <time.h> // localtime
13# include <conio.h> // _kbhit, _getch, _putch
14# include <io.h> // write; Include this BEFORE darkplaces.h because it uses strncpy which trips DP_STATIC_ASSERT
15#ifdef _MSC_VER
16#pragma comment(lib, "winmm.lib")
17#endif
18#else
19# ifdef __FreeBSD__
20# include <sys/sysctl.h>
21# endif
22# ifdef __ANDROID__
23# include <android/log.h>
24# endif
25# include <unistd.h>
26# include <fcntl.h>
27# include <sys/time.h>
28# include <time.h>
29# ifdef SUPPORTDLL
30# include <dlfcn.h>
31# endif
32#endif
33
34#include <signal.h>
35
36#include "quakedef.h"
37#include "taskqueue.h"
38#include "thread.h"
39#include "libcurl.h"
40
41
43
44
45size_t Sys_TimeString(char buf[], size_t bufsize, const char *timeformat)
46{
47 time_t mytime = time(NULL);
48 size_t strlen;
49#if _MSC_VER >= 1400
50 struct tm mytm;
51 localtime_s(&mytm, &mytime);
52 strlen = strftime(buf, bufsize, timeformat, &mytm);
53#else
54 strlen = strftime(buf, bufsize, timeformat, localtime(&mytime));
55#endif
56 if (!strlen) // means the array contents are undefined (but it's not always an error)
57 buf[0] = '\0'; // better fix it
58 return strlen;
59}
60
61
62#ifdef __cplusplus
63extern "C"
64#endif
66{
67#ifdef __ANDROID__
68#ifdef USE_PROFILER
69 extern void monstartup(const char *libname);
70 extern void moncleanup(void);
71 if (enable)
72 monstartup("libmain.so");
73 else
74 moncleanup();
75#endif
76#elif (defined(__linux__) && (defined(__GLIBC__) || defined(__GNU_LIBRARY__))) || defined(__FreeBSD__)
77 extern int moncontrol(int);
78 moncontrol(enable);
79#endif
80}
81
82
83/*
84===============================================================================
85
86DLL MANAGEMENT
87
88===============================================================================
89*/
90
91static qbool Sys_LoadDependencyFunctions(dllhandle_t dllhandle, const dllfunction_t *fcts, qbool complain, qbool has_next)
92{
93 const dllfunction_t *func;
94 if(dllhandle)
95 {
96 for (func = fcts; func && func->name != NULL; func++)
97 if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
98 {
99 if(complain)
100 {
101 Con_DPrintf (" - missing function \"%s\" - broken library!", func->name);
102 if(has_next)
103 Con_DPrintf("\nContinuing with");
104 }
105 goto notfound;
106 }
107 return true;
108
109 notfound:
110 for (func = fcts; func && func->name != NULL; func++)
111 *func->funcvariable = NULL;
112 }
113 return false;
114}
115
117{
118 dllhandle_t dllhandle = 0;
119
120 if (handle == NULL)
121 return false;
122#ifdef WIN32
123 dllhandle = LoadLibrary (NULL);
124#else
125 dllhandle = dlopen (NULL, RTLD_NOW | RTLD_GLOBAL);
126#endif
127 *handle = dllhandle;
128 return true;
129}
130
131qbool Sys_LoadDependency (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
132{
133#ifdef SUPPORTDLL
134 const dllfunction_t *func;
135 dllhandle_t dllhandle = 0;
136 unsigned int i;
137
138 if (handle == NULL)
139 return false;
140
141#ifndef WIN32
142#ifdef PREFER_PRELOAD
143 dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
144 if(Sys_LoadDependencyFunctions(dllhandle, fcts, false, false))
145 {
146 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
147 *handle = dllhandle;
148 return true;
149 }
150 else
151 Sys_FreeLibrary(&dllhandle);
152notfound:
153#endif
154#endif
155
156 // Initializations
157 for (func = fcts; func && func->name != NULL; func++)
158 *func->funcvariable = NULL;
159
160 // Try every possible name
161 Con_DPrintf ("Trying to load library...");
162 for (i = 0; dllnames[i] != NULL; i++)
163 {
164 Con_DPrintf (" \"%s\"", dllnames[i]);
165#ifdef WIN32
166# ifndef DONT_USE_SETDLLDIRECTORY
167# ifdef _WIN64
168 SetDllDirectory("bin64");
169# else
170 SetDllDirectory("bin32");
171# endif
172# endif
173#endif
174 if(Sys_LoadLibrary(dllnames[i], &dllhandle))
175 {
176 if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
177 break;
178 else
179 Sys_FreeLibrary (&dllhandle);
180 }
181 }
182
183 // see if the names can be loaded relative to the executable path
184 // (this is for Mac OSX which does not check next to the executable)
185 if (!dllhandle && strrchr(sys.argv[0], '/'))
186 {
187 char path[MAX_OSPATH];
188 dp_strlcpy(path, sys.argv[0], sizeof(path));
189 strrchr(path, '/')[1] = 0;
190 for (i = 0; dllnames[i] != NULL; i++)
191 {
192 char temp[MAX_OSPATH];
193 dp_strlcpy(temp, path, sizeof(temp));
194 dp_strlcat(temp, dllnames[i], sizeof(temp));
195 Con_DPrintf (" \"%s\"", temp);
196
197 if(Sys_LoadLibrary(temp, &dllhandle))
198 {
199 if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
200 break;
201 else
202 Sys_FreeLibrary (&dllhandle);
203 }
204 }
205 }
206
207 // No DLL found
208 if (! dllhandle)
209 {
210 Con_DPrintf(" - failed.\n");
211 return false;
212 }
213
214 Con_DPrintf(" - loaded.\n");
215 Con_Printf("Loaded library \"%s\"\n", dllnames[i]);
216
217 *handle = dllhandle;
218 return true;
219#else
220 return false;
221#endif
222}
223
225{
226 dllhandle_t dllhandle = 0;
227
228 if(handle == NULL)
229 return false;
230
231#ifdef SUPPORTDLL
232# ifdef WIN32
233 dllhandle = LoadLibrary (name);
234# else
235 dllhandle = dlopen (name, RTLD_LAZY | RTLD_GLOBAL);
236# endif
237#endif
238 if(!dllhandle)
239 return false;
240
241 *handle = dllhandle;
242 return true;
243}
244
246{
247#ifdef SUPPORTDLL
248 if (handle == NULL || *handle == NULL)
249 return;
250
251#ifdef WIN32
252 FreeLibrary (*handle);
253#else
254 dlclose (*handle);
255#endif
256
257 *handle = NULL;
258#endif
259}
260
261void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
262{
263#ifdef SUPPORTDLL
264#ifdef WIN32
265 return (void *)GetProcAddress (handle, name);
266#else
267 return (void *)dlsym (handle, name);
268#endif
269#else
270 return NULL;
271#endif
272}
273
274
275
276#ifdef WIN32
277# define HAVE_TIMEGETTIME 1
278# define HAVE_QUERYPERFORMANCECOUNTER 1
279# define HAVE_WIN32_USLEEP 1
280# define HAVE_Sleep 1
281#else
282# if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
283# define HAVE_CLOCKGETTIME 1
284# endif
285# if _POSIX_VERSION >= 200112L
286// MacOS advertises POSIX support but doesn't implement clock_nanosleep().
287// POSIX deprecated and removed usleep() so select() seems like a safer choice.
288# if defined(MACOSX)
289# define HAVE_SELECT_POSIX 1
290# else
291# define HAVE_CLOCK_NANOSLEEP 1
292# endif
293# endif
294#endif
295
296#ifdef FD_SET
297# define HAVE_SELECT 1
298#endif
299
300// these are referenced elsewhere
301cvar_t sys_usenoclockbutbenchmark = {CF_SHARED, "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."};
302cvar_t sys_libdir = {CF_READONLY | CF_SHARED, "sys_libdir", "", "Default engine library directory"};
303
304// these are not
305static cvar_t sys_debugsleep = {CF_SHARED, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
306static cvar_t sys_usesdlgetticks = {CF_SHARED, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (low precision, for debugging)"};
307static cvar_t sys_usesdldelay = {CF_SHARED, "sys_usesdldelay", "0", "use SDL_Delay() (low precision, for debugging)"};
308#if HAVE_QUERYPERFORMANCECOUNTER
309static cvar_t sys_usequeryperformancecounter = {CF_SHARED | CF_ARCHIVE, "sys_usequeryperformancecounter", "1", "use windows QueryPerformanceCounter timer (which has issues on systems lacking constant-rate TSCs synchronised across all cores, such as ancient PCs or VMs) for timing rather than timeGetTime function (which is low precision and had issues on some old motherboards)"};
310#endif
311
312static cvar_t sys_stdout = {CF_SHARED, "sys_stdout", "1", "0: nothing is written to stdout (-nostdout cmdline option sets this), 1: normal messages are written to stdout, 2: normal messages are written to stderr (-stderr cmdline option sets this)"};
313#ifndef WIN32
314static cvar_t sys_stdout_blocks = {CF_SHARED, "sys_stdout_blocks", "0", "1: writes to stdout and stderr streams will block (causing a stutter or complete halt) if the buffer is full, ensuring no messages are lost at a price"};
315#endif
316
317static double benchmark_time; // actually always contains an integer amount of milliseconds, will eventually "overflow"
318
319/*
320================
321Sys_CheckParm
322
323Returns the position (1 to argc-1) in the program's argument list
324where the given parameter apears, or 0 if not present
325================
326*/
327int Sys_CheckParm (const char *parm)
328{
329 int i;
330
331 for (i=1 ; i<sys.argc ; i++)
332 {
333 if (!sys.argv[i])
334 continue; // NEXTSTEP sometimes clears appkit vars.
335 if (!strcmp (parm,sys.argv[i]))
336 return i;
337 }
338
339 return 0;
340}
341
342static void Sys_UpdateOutFD_c(cvar_t *var)
343{
344 switch (sys_stdout.integer)
345 {
346 case 0: sys.outfd = -1; break;
347 default:
348 case 1: sys.outfd = fileno(stdout); break;
349 case 2: sys.outfd = fileno(stderr); break;
350 }
351}
352
354{
358#if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME
360 {
363 }
364#endif
365#if HAVE_QUERYPERFORMANCECOUNTER
366 Cvar_RegisterVariable(&sys_usequeryperformancecounter);
367#endif
368
371#ifndef WIN32
373#endif
374}
375
376#ifdef WIN32
377static LARGE_INTEGER PerformanceFreq;
380static void Sys_SetTimerResolution(void)
381{
382 NTSTATUS(NTAPI *qNtQueryTimerResolution)(OUT PULONG MinRes, OUT PULONG MaxRes, OUT PULONG CurrentRes);
383 NTSTATUS(NTAPI *qNtSetTimerResolution)(IN ULONG DesiredRes, IN BOOLEAN SetRes, OUT PULONG ActualRes);
384 const char* ntdll_names [] =
385 {
386 "ntdll.dll",
387 NULL
388 };
389 dllfunction_t ntdll_funcs[] =
390 {
391 {"NtQueryTimerResolution", (void **) &qNtQueryTimerResolution},
392 {"NtSetTimerResolution", (void **) &qNtSetTimerResolution},
393 {NULL, NULL}
394 };
395 dllhandle_t ntdll;
396 unsigned long WorstRes, BestRes, CurrentRes;
397
398 timeBeginPeriod(1); // 1ms, documented
399
400 // the best Windows can manage (typically 0.5ms)
401 // http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtSetTimerResolution.html
402 if (Sys_LoadDependency(ntdll_names, &ntdll, ntdll_funcs))
403 {
404 qNtQueryTimerResolution(&WorstRes, &BestRes, &CurrentRes); // no pointers may be NULL
405 if (CurrentRes > BestRes)
406 qNtSetTimerResolution(BestRes, true, &CurrentRes);
407
408 Sys_FreeLibrary(&ntdll);
409 }
410
411 // Microsoft says the freq is fixed at boot and consistent across all processors
412 // and that it need only be queried once and cached.
413 QueryPerformanceFrequency (&PerformanceFreq);
414}
415#endif // WIN32
416
417double Sys_DirtyTime(void)
418{
419 // first all the OPTIONAL timers
420
421 // benchmark timer (fake clock)
423 {
424 double old_benchmark_time = benchmark_time;
425 benchmark_time += 1;
426 if(benchmark_time == old_benchmark_time)
427 Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
428 return benchmark_time * 0.000001;
429 }
430
432 return (double) Sys_SDL_GetTicks() / 1000.0;
433
434#if HAVE_QUERYPERFORMANCECOUNTER
435 if (sys_usequeryperformancecounter.integer)
436 {
437 // QueryPerformanceCounter
438 // platform:
439 // Windows 95/98/ME/NT/2000/XP
440 // features:
441 // + very accurate (constant-rate TSCs on modern systems)
442 // known issues:
443 // - does not necessarily match realtime too well (tends to get faster and faster in win98)
444 // - wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
445 // - higher access latency on Vista
446 // Microsoft says on Win 7 or later, latency and overhead are very low, synchronisation is excellent.
447 LARGE_INTEGER PerformanceCount;
448
449 if (PerformanceFreq.QuadPart)
450 {
451 QueryPerformanceCounter (&PerformanceCount);
452 return (double)PerformanceCount.QuadPart * (1.0 / (double)PerformanceFreq.QuadPart);
453 }
454 else
455 {
456 Con_Printf("No hardware timer available\n");
457 // fall back to other clock sources
458 Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
459 }
460 }
461#endif
462
463#if HAVE_CLOCKGETTIME
464 {
465 struct timespec ts;
466# ifdef CLOCK_MONOTONIC_RAW
467 // Linux-specific, SDL_GetPerformanceCounter() uses it
468 clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
469# elif defined(CLOCK_MONOTONIC)
470 // POSIX
471 clock_gettime(CLOCK_MONOTONIC, &ts);
472# else
473 // sunos
474 clock_gettime(CLOCK_HIGHRES, &ts);
475# endif
476 return (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
477 }
478#endif
479
480 // now all the FALLBACK timers
481#if HAVE_TIMEGETTIME
482 {
483 // timeGetTime
484 // platform:
485 // Windows 95/98/ME/NT/2000/XP
486 // features:
487 // reasonable accuracy (millisecond)
488 // issues:
489 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
490 // requires Sys_SetTimerResolution()
491 return (double) timeGetTime() / 1000.0;
492 }
493#else
494 // fallback for using the SDL timer if no other timer is available
495 // this calls Sys_Error() if not linking against SDL
496 return (double) Sys_SDL_GetTicks() / 1000.0;
497#endif
498}
499
500double Sys_Sleep(double time)
501{
502 double dt;
503 uint32_t msec, usec, nsec;
504
505 if (time < 1.0/1000000.0 || host.restless)
506 return 0; // not sleeping this frame
507 if (time >= 1)
508 time = 0.999999; // simpler, also ensures values are in range for all platform APIs
509 msec = time * 1000;
510 usec = time * 1000000;
511 nsec = time * 1000000000;
512
514 {
515 double old_benchmark_time = benchmark_time;
516 benchmark_time += usec;
517 if(benchmark_time == old_benchmark_time)
518 Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
519 return 0;
520 }
521
523 Con_Printf("sys_debugsleep: requested %u, ", usec);
524 dt = Sys_DirtyTime();
525
526 // less important on newer libcurl so no need to disturb dedicated servers
527 if (cls.state != ca_dedicated && Curl_Select(msec))
528 {
529 // a transfer is ready or we finished sleeping
530 }
532 Sys_SDL_Delay(msec);
533#if HAVE_SELECT
535 {
536 struct timeval tv;
537 lhnetsocket_t *s;
538 fd_set fdreadset;
539 int lastfd = -1;
540
541 FD_ZERO(&fdreadset);
543 {
544 if (s->address.addresstype == LHNETADDRESSTYPE_INET4 || s->address.addresstype == LHNETADDRESSTYPE_INET6)
545 {
546 if (lastfd < s->inetsocket)
547 lastfd = s->inetsocket;
548 #if defined(WIN32) && !defined(_MSC_VER)
549 FD_SET((int)s->inetsocket, &fdreadset);
550 #else
551 FD_SET((unsigned int)s->inetsocket, &fdreadset);
552 #endif
553 }
554 }
555 tv.tv_sec = 0;
556 tv.tv_usec = usec;
557 // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
558 // (so much for POSIX...), not with an empty fd_set either.
559 select(lastfd + 1, &fdreadset, NULL, NULL, &tv);
560 }
561#endif
562#if HAVE_CLOCK_NANOSLEEP
563 else
564 {
565 struct timespec ts;
566 ts.tv_sec = 0;
567 ts.tv_nsec = nsec;
568 clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
569 }
570#elif HAVE_SELECT_POSIX
571 else
572 {
573 struct timeval tv;
574 tv.tv_sec = 0;
575 tv.tv_usec = usec;
576 select(0, NULL, NULL, NULL, &tv);
577 }
578#elif HAVE_WIN32_USLEEP // Windows XP/2003 minimum
579 else
580 {
581 HANDLE timer;
582 LARGE_INTEGER sleeptime;
583
584 // takes 100ns units, negative indicates relative time
585 sleeptime.QuadPart = -((int64_t)nsec / 100);
586 timer = CreateWaitableTimer(NULL, true, NULL);
587 SetWaitableTimer(timer, &sleeptime, 0, NULL, NULL, 0);
588 WaitForSingleObject(timer, INFINITE);
589 CloseHandle(timer);
590 }
591#elif HAVE_Sleep
592 else
593 Sleep(msec);
594#else
595 else
596 Sys_SDL_Delay(msec);
597#endif
598
599 dt = Sys_DirtyTime() - dt;
601 Con_Printf("got %u, oversleep %d\n", (uint32_t)(dt * 1000000), (uint32_t)(dt * 1000000) - usec);
602 return (dt < 0 || dt >= 1800) ? 0 : dt;
603}
604
605
606/*
607===============================================================================
608
609STDIO
610
611===============================================================================
612*/
613
614// NOTE: use only POSIX async-signal-safe library functions here (see: man signal-safety)
615void Sys_Print(const char *text, size_t textlen)
616{
617#ifdef __ANDROID__
618 if (developer.integer > 0)
619 {
620 __android_log_write(ANDROID_LOG_DEBUG, sys.argv[0], text);
621 }
622#else
623 if(sys.outfd < 0)
624 return;
625 #ifndef WIN32
626 // BUG: for some reason, NDELAY also affects stdout (1) when used on stdin (0).
627 // this is because both go to /dev/tty by default!
628 {
629 int origflags = fcntl(sys.outfd, F_GETFL, 0);
631 fcntl(sys.outfd, F_SETFL, origflags & ~O_NONBLOCK);
632 #else
633 #define write _write
634 #endif
635 while(*text && textlen)
636 {
637 fs_offset_t written = (fs_offset_t)write(sys.outfd, text, textlen);
638 if(written <= 0)
639 break; // sorry, I cannot do anything about this error - without an output
640 text += written;
641 textlen -= written;
642 }
643 #ifndef WIN32
645 fcntl(sys.outfd, F_SETFL, origflags);
646 }
647 #endif
648 //fprintf(stdout, "%s", text);
649#endif
650}
651
652void Sys_Printf(const char *fmt, ...)
653{
654 va_list argptr;
655 char msg[MAX_INPUTLINE];
656 int msglen;
657
658 va_start(argptr,fmt);
659 msglen = dpvsnprintf(msg, sizeof(msg), fmt, argptr);
660 va_end(argptr);
661
662 if (msglen >= 0)
663 Sys_Print(msg, msglen);
664}
665
668{
669 static char text[MAX_INPUTLINE];
670#ifdef WIN32
671 static unsigned int len = 0;
672 int c;
673
674 // read a line out
675 while (_kbhit ())
676 {
677 c = _getch ();
678 if (c == '\r')
679 {
680 text[len] = '\0';
681 _putch ('\n');
682 len = 0;
683 return text;
684 }
685 if (c == '\b')
686 {
687 if (len)
688 {
689 _putch (c);
690 _putch (' ');
691 _putch (c);
692 len--;
693 }
694 continue;
695 }
696 if (len < sizeof (text) - 1)
697 {
698 _putch (c);
699 text[len] = c;
700 len++;
701 }
702 }
703#else
704 fd_set fdset;
705 struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
706
707 FD_ZERO(&fdset);
708 FD_SET(fileno(stdin), &fdset);
709 if (select(1, &fdset, NULL, NULL, &timeout) != -1 && FD_ISSET(fileno(stdin), &fdset))
710 return fgets(text, sizeof(text), stdin);
711#endif
712 return NULL;
713}
714
715
716/*
717===============================================================================
718
719Startup and Shutdown
720
721===============================================================================
722*/
723
724void Sys_Error (const char *error, ...)
725{
726 va_list argptr;
727 char string[MAX_INPUTLINE];
728 int i;
729
730 // Disable Sys_HandleSignal() but not Sys_HandleCrash()
732
733 // set output to blocking stderr
734 sys.outfd = fileno(stderr);
735#ifndef WIN32
736 fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
737#endif
738
739 va_start (argptr,error);
740 dpvsnprintf (string, sizeof (string), error, argptr);
741 va_end (argptr);
742
743 Con_Printf(CON_ERROR "Engine Aborted: %s\n^9%s\n", string, engineversion);
744
745 dp_strlcat(string, "\n\n", sizeof(string));
746 dp_strlcat(string, engineversion, sizeof(string));
747
748 // Most shutdown funcs can't be called here as they could error while we error.
749
750 // DP8 TODO: send a disconnect message indicating we aborted, see Host_Error() and Sys_HandleCrash()
751
752 if (cls.demorecording)
754 if (sv.active)
755 {
756 sv.active = false; // make SV_DropClient() skip the QC stuff to avoid recursive errors
757 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
758 if (host_client->active)
759 SV_DropClient(false, "Server aborted!"); // closes demo file
760 }
761 // don't want a dead window left blocking the OS UI or the abort dialog
762 VID_Shutdown();
764
765 host.state = host_failed; // make Sys_HandleSignal() call _Exit()
766 Sys_SDL_Dialog("Engine Aborted", string);
767
768 fflush(stderr);
769 exit (1);
770}
771
772#ifndef WIN32
773static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
774{
775 const char *p = PATH;
776 const char *q;
777 if(p && name)
778 {
779 while((q = strchr(p, ':')))
780 {
781 dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
783 return buf;
784 p = q + 1;
785 }
786 if(!q) // none found - try the last item
787 {
788 dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
790 return buf;
791 }
792 }
793 return name;
794}
795#endif
796
797static const char *Sys_FindExecutableName(void)
798{
799#if defined(WIN32)
800 return sys.argv[0];
801#else
802 static char exenamebuf[MAX_OSPATH+1];
803 ssize_t n = -1;
804#if defined(__FreeBSD__)
805 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
806 size_t exenamebuflen = sizeof(exenamebuf)-1;
807 if (sysctl(mib, 4, exenamebuf, &exenamebuflen, NULL, 0) == 0)
808 {
809 n = exenamebuflen;
810 }
811#elif defined(__linux__)
812 n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
813#endif
814 if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
815 {
816 exenamebuf[n] = 0;
817 return exenamebuf;
818 }
819 if(strchr(sys.argv[0], '/'))
820 return sys.argv[0]; // possibly a relative path
821 else
822 return Sys_FindInPATH(sys.argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
823#endif
824}
825
827{
828 if(sys.selffd != -1)
829 return;
831}
832
833// for x86 cpus only... (x64 has SSE2_PRESENT)
834#if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
835// code from SDL, shortened as we can expect CPUID to work
836static int CPUID_Features(void)
837{
838 int features = 0;
839# if (defined(__GNUC__) || defined(__clang__) || defined(__TINYC__)) && defined(__i386__)
840 __asm__ (
841" movl %%ebx,%%edi\n"
842" xorl %%eax,%%eax \n"
843" incl %%eax \n"
844" cpuid # Get family/model/stepping/features\n"
845" movl %%edx,%0 \n"
846" movl %%edi,%%ebx\n"
847 : "=m" (features)
848 :
849 : "%eax", "%ecx", "%edx", "%edi"
850 );
851# elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
852 __asm {
853 xor eax, eax
854 inc eax
855 cpuid ; Get family/model/stepping/features
856 mov features, edx
857 }
858# else
859# error SSE_POSSIBLE set but no CPUID implementation
860# endif
861 return features;
862}
863#endif
864
865#ifdef SSE_POSSIBLE
866qbool Sys_HaveSSE(void)
867{
868 // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
869 if(Sys_CheckParm("-nosse"))
870 return false;
871#ifdef SSE_PRESENT
872 return true;
873#else
874 // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
875 if(Sys_CheckParm("-forcesse") || Sys_CheckParm("-forcesse2"))
876 return true;
877 if(CPUID_Features() & (1 << 25))
878 return true;
879 return false;
880#endif
881}
882
884{
885 // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
886 if(Sys_CheckParm("-nosse") || Sys_CheckParm("-nosse2"))
887 return false;
888#ifdef SSE2_PRESENT
889 return true;
890#else
891 // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
892 if(Sys_CheckParm("-forcesse2"))
893 return true;
894 if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
895 return true;
896 return false;
897#endif
898}
899#endif
900
902#if defined(__linux__)
903#include <sys/resource.h>
904#include <errno.h>
905
906void Sys_InitProcessNice (void)
907{
908 struct rlimit lim;
909 sys.nicepossible = false;
910 if(Sys_CheckParm("-nonice"))
911 return;
912 errno = 0;
913 sys.nicelevel = getpriority(PRIO_PROCESS, 0);
914 if(errno)
915 {
916 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
917 return;
918 }
919 if(getrlimit(RLIMIT_NICE, &lim))
920 {
921 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
922 return;
923 }
924 if(lim.rlim_cur != RLIM_INFINITY && sys.nicelevel < (int) (20 - lim.rlim_cur))
925 {
926 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
927 return;
928 }
929 sys.nicepossible = true;
930 sys.isnice = false;
931}
932void Sys_MakeProcessNice (void)
933{
934 if(!sys.nicepossible)
935 return;
936 if(sys.isnice)
937 return;
938 Con_DPrintf("Process is becoming 'nice'...\n");
939 if(setpriority(PRIO_PROCESS, 0, 19))
940 Con_Printf(CON_ERROR "Failed to raise nice level to %d\n", 19);
941 sys.isnice = true;
942}
943void Sys_MakeProcessMean (void)
944{
945 if(!sys.nicepossible)
946 return;
947 if(!sys.isnice)
948 return;
949 Con_DPrintf("Process is becoming 'mean'...\n");
950 if(setpriority(PRIO_PROCESS, 0, sys.nicelevel))
951 Con_Printf(CON_ERROR "Failed to lower nice level to %d\n", sys.nicelevel);
952 sys.isnice = false;
953}
954#else
956{
957}
959{
960}
962{
963}
964#endif
965
966
967static const char *Sys_SigDesc(int sig)
968{
969 switch (sig)
970 {
971 // Windows only supports the C99 signals
972 case SIGINT: return "Interrupt";
973 case SIGILL: return "Illegal instruction";
974 case SIGABRT: return "Aborted";
975 case SIGFPE: return "Floating point exception";
976 case SIGSEGV: return "Segmentation fault";
977 case SIGTERM: return "Termination";
978#ifndef WIN32
979 // POSIX has several others worth catching
980 case SIGHUP: return "Hangup";
981 case SIGQUIT: return "Quit";
982 case SIGBUS: return "Bus error (bad memory access)";
983 case SIGPIPE: return "Broken pipe";
984#endif
985 default: return "Yo dawg, we bugged out while bugging out";
986 }
987}
988
994static void Sys_HandleCrash(int sig)
995{
996#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
997 // Before doing anything else grab the stack frame addresses
998 #include <execinfo.h>
999 void *stackframes[32];
1000 int framecount = backtrace(stackframes, 32);
1001 char **btstrings;
1002#endif
1003 char dialogtext[3072];
1004 const char *sigdesc;
1005
1006 // Break any loop and disable Sys_HandleSignal()
1008 return;
1010
1011 sigdesc = Sys_SigDesc(sig);
1012
1013 // set output to blocking stderr and print header, backtrace, version
1014 sys.outfd = fileno(stderr); // not async-signal-safe :(
1015#ifndef WIN32
1016 fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
1017 Sys_Print("\n\n\x1B[1;37;41m Engine Crash: ", 30);
1018 Sys_Print(sigdesc, strlen(sigdesc));
1019 Sys_Print(" \x1B[m\n", 8);
1020 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
1021 // the first two addresses will be in this function and in signal() in libc
1022 backtrace_symbols_fd(stackframes + 2, framecount - 2, sys.outfd);
1023 #endif
1024 Sys_Print("\x1B[1m", 4);
1026 Sys_Print("\x1B[m\n", 4);
1027#else // Windows console doesn't support colours
1028 Sys_Print("\n\nEngine Crash: ", 16);
1029 Sys_Print(sigdesc, strlen(sigdesc));
1030 Sys_Print("\n", 1);
1032 Sys_Print("\n", 1);
1033#endif
1034
1035 // DP8 TODO: send a disconnect message indicating we crashed, see Sys_Error() and Host_Error()
1036
1037 // don't want a dead window left blocking the OS UI or the crash dialog
1038 VID_Shutdown();
1040
1041 // prepare the dialogtext: signal, backtrace, version
1042 // the dp_st* funcs are POSIX async-signal-safe IF we don't trigger their warnings
1043 dp_strlcpy(dialogtext, sigdesc, sizeof(dialogtext));
1044 dp_strlcat(dialogtext, "\n\n", sizeof(dialogtext));
1045#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
1046 btstrings = backtrace_symbols(stackframes + 2, framecount - 2); // calls malloc :(
1047 if (btstrings)
1048 for (int i = 0; i < framecount - 2; ++i)
1049 {
1050 dp_strlcat(dialogtext, btstrings[i], sizeof(dialogtext));
1051 dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
1052 }
1053#endif
1054 dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
1055 dp_strlcat(dialogtext, engineversion, sizeof(dialogtext));
1056
1057 host.state = host_failed; // make Sys_HandleSignal() call _Exit()
1058 Sys_SDL_Dialog("Engine Crash", dialogtext);
1059
1060 fflush(stderr); // not async-signal-safe :(
1061
1062 // Continue execution with default signal handling.
1063 // A real crash will be re-triggered so the platform can handle it,
1064 // a fake crash (kill -SEGV) will cause a graceful shutdown.
1065 signal(sig, SIG_DFL);
1066}
1067
1068static void Sys_HandleSignal(int sig)
1069{
1070 const char *sigdesc;
1071
1072 // Break any loop, eg if each Sys_Print triggers a SIGPIPE
1074 return;
1075
1076 sigdesc = Sys_SigDesc(sig);
1077 Sys_Print("\nReceived ", 10);
1078 Sys_Print(sigdesc, strlen(sigdesc));
1079 Sys_Print(" signal, exiting...\n", 20);
1080 if (host.state == host_failed)
1081 {
1082 // user is trying to kill the process while the SDL dialog is open
1083 fflush(stderr); // not async-signal-safe :(
1084 _Exit(sig);
1085 }
1087}
1088
1090static void Sys_InitSignals(void)
1091{
1092 // Windows only supports the C99 signals
1093 signal(SIGINT, Sys_HandleSignal);
1094 signal(SIGILL, Sys_HandleCrash);
1095 signal(SIGABRT, Sys_HandleCrash);
1096 signal(SIGFPE, Sys_HandleCrash);
1097 signal(SIGSEGV, Sys_HandleCrash);
1098 signal(SIGTERM, Sys_HandleSignal);
1099#ifndef WIN32
1100 // POSIX has several others worth catching
1101 signal(SIGHUP, Sys_HandleSignal);
1102 signal(SIGQUIT, Sys_HandleSignal);
1103 signal(SIGBUS, Sys_HandleCrash);
1104 signal(SIGPIPE, Sys_HandleSignal);
1105#endif
1106}
1107
1108// Cloudwalk: Most overpowered function declaration...
1109static inline double Sys_UpdateTime (double newtime, double oldtime)
1110{
1111 double time = newtime - oldtime;
1112
1113 if (time < 0)
1114 {
1115 // warn if it's significant
1116 if (time < -0.01)
1117 Con_Printf(CON_WARN "Host_UpdateTime: time stepped backwards (went from %f to %f, difference %f)\n", oldtime, newtime, time);
1118 time = 0;
1119 }
1120 else if (time >= 1800)
1121 {
1122 Con_Printf(CON_WARN "Host_UpdateTime: time stepped forward (went from %f to %f, difference %f)\n", oldtime, newtime, time);
1123 time = 0;
1124 }
1125
1126 return time;
1127}
1128
1129#ifdef __EMSCRIPTEN__
1130 #include <emscripten.h>
1131#endif
1133static void Sys_Frame(void)
1134{
1135 double time, newtime, sleeptime;
1136#ifdef __EMSCRIPTEN__
1137 static double sleepstarttime = 0;
1138 host.sleeptime = Sys_DirtyTime() - sleepstarttime;
1139#endif
1140
1141 if (setjmp(host.abortframe)) // Something bad happened, or the server disconnected
1142 host.state = host_active; // In case we were loading
1143
1144 if (host.state >= host_shutdown) // see Sys_HandleCrash() comments
1145 {
1146#ifdef __EMSCRIPTEN__
1147 emscripten_cancel_main_loop();
1148#endif
1149#ifdef __ANDROID__
1150 Sys_AllowProfiling(false);
1151#endif
1152 Host_Shutdown();
1153 exit(0);
1154 }
1155
1156 newtime = Sys_DirtyTime();
1158 host.dirtytime = newtime;
1159
1160 sleeptime = Host_Frame(time);
1161 sleeptime -= Sys_DirtyTime() - host.dirtytime; // execution time
1162
1163#ifdef __EMSCRIPTEN__
1164 // This platform doesn't support a main loop... it will sleep when Sys_Frame() returns.
1165 // Not using emscripten_sleep() via Sys_Sleep() because it would cause two sleeps per frame.
1166 if (!vid_vsync.integer) // see VID_SetVsync_c()
1167 emscripten_set_main_loop_timing(EM_TIMING_SETTIMEOUT, host.restless ? 0 : sleeptime * 1000.0);
1168 sleepstarttime = Sys_DirtyTime();
1169#else
1170 host.sleeptime = Sys_Sleep(sleeptime);
1171#endif
1172}
1173
1178int Sys_Main(int argc, char *argv[])
1179{
1180 sys.argc = argc;
1181 sys.argv = (const char **)argv;
1182
1183 // COMMANDLINEOPTION: Console: -nostdout disables text output to the terminal the game was launched from
1184 // COMMANDLINEOPTION: -noterminal disables console output on stdout
1185 if(Sys_CheckParm("-noterminal") || Sys_CheckParm("-nostdout"))
1186 sys_stdout.string = "0";
1187 // COMMANDLINEOPTION: -stderr moves console output to stderr
1188 else if(Sys_CheckParm("-stderr"))
1189 sys_stdout.string = "2";
1190 // too early for Cvar_SetQuick
1193#ifndef WIN32
1194 fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL, 0) | O_NONBLOCK);
1195 // stdout/stderr will be set to blocking in Sys_Print() if so configured, or during a fatal error.
1196 fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) | O_NONBLOCK);
1197 fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) | O_NONBLOCK);
1198#endif
1199
1200 sys.selffd = -1;
1201 Sys_ProvideSelfFD(); // may call Con_Printf() so must be after sys.outfd is set
1202
1203#ifdef __ANDROID__
1204 Sys_AllowProfiling(true);
1205#endif
1206
1208
1209#ifdef WIN32
1210 Sys_SetTimerResolution();
1211#endif
1212
1213 Host_Init();
1214#ifdef __EMSCRIPTEN__
1215 emscripten_set_main_loop(Sys_Frame, 0, true); // doesn't return
1216#else
1217 while(true)
1218 Sys_Frame();
1219#endif
1220
1221 return 0;
1222}
void CL_Stop_f(cmd_state_t *cmd)
Definition cl_demo.c:307
client_static_t cls
Definition cl_main.c:116
@ ca_dedicated
Definition client.h:530
cmd_state_t * cmd_local
command interpreter for local commands injected by SVQC, CSQC, MQC, server or client engine code uses...
Definition cmd.c:25
#define CF_READONLY
cvar cannot be changed from the console or the command buffer, and is considered CF_PERSISTENT
Definition cmd.h:54
#define CF_SHARED
Definition cmd.h:67
#define CF_ARCHIVE
cvar should have its set value saved to config.cfg and persist across sessions
Definition cmd.h:53
#define List_For_Each_Entry(pos, head, type, member)
Definition com_list.h:121
int dpvsnprintf(char *buffer, size_t buffersize, const char *format, va_list args)
Returns the number of printed characters, excluding the final '\0' or returns -1 if the buffer isn't ...
Definition common.c:1010
int dpsnprintf(char *buffer, size_t buffersize, const char *format,...)
Returns the number of printed characters, excluding the final '\0' or returns -1 if the buffer isn't ...
Definition common.c:997
#define dp_strlcat(dst, src, dsize)
Definition common.h:304
#define dp_strlcpy(dst, src, dsize)
Definition common.h:303
cvar_t timeformat
Definition host.c:56
void Con_DPrintf(const char *fmt,...)
A Con_Printf that only shows up if the "developer" cvar is set.
Definition console.c:1544
void Con_Printf(const char *fmt,...)
Prints to all appropriate console targets.
Definition console.c:1514
#define CON_WARN
Definition console.h:101
#define CON_ERROR
Definition console.h:102
float time
string model
void Cvar_SetValueQuick(cvar_t *var, float value)
Definition cvar.c:473
void Cvar_RegisterVariable(cvar_t *variable)
registers a cvar that already has the name, string, and optionally the archive elements set.
Definition cvar.c:599
void Cvar_RegisterCallback(cvar_t *variable, void(*callback)(cvar_t *))
Definition cvar.c:495
char engineversion[128]
version string for the corner of the console, crash messages, status command, etc
Definition host.c:304
#define n(x, y)
int FS_SysOpenFD(const char *filepath, const char *mode, qbool nonblocking)
Definition fs.c:2468
#define O_NONBLOCK
Definition fs.c:61
qbool FS_SysFileExists(const char *path)
Look for a file in the filesystem only.
Definition fs.c:3744
int64_t fs_offset_t
Definition fs.h:37
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition glquake.h:657
const GLchar * name
Definition glquake.h:601
cvar_t developer
Definition host.c:48
host_static_t host
Definition host.c:41
void Host_Shutdown(void)
Definition host.c:571
void Host_Init(void)
Definition host.c:377
double Host_Frame(double time)
Definition host.c:618
@ host_failed
crashed or aborted, SDL dialog open
Definition host.h:29
@ host_active
Definition host.h:25
@ host_failing
crashing (inside crash handler)
Definition host.h:28
@ host_shutdown
states >= host_shutdown cause graceful shutdown, see Sys_HandleCrash() comments
Definition host.h:27
lhnetsocket_t lhnet_socketlist
Definition lhnet.c:717
@ LHNETADDRESSTYPE_INET6
Definition lhnet.h:15
@ LHNETADDRESSTYPE_INET4
Definition lhnet.h:14
bool Curl_Select(int timeout_ms)
Definition libcurl.c:1263
string fgets(float fhandle)
float strlen(string s)
void error(string err,...)
string argv(float n)
int i
#define MAX_INPUTLINE
maximum size of console commandline, QuakeC strings, and many other text processing buffers
Definition qdefs.h:94
#define MAX_OSPATH
max length of a filesystem pathname
Definition qdefs.h:175
#define NULL
Definition qtypes.h:12
bool qbool
Definition qtypes.h:9
server_t sv
local server
Definition sv_main.c:223
void SV_DropClient(qbool leaving, const char *reason,...)
Definition sv_main.c:1018
server_static_t svs
persistant server info
Definition sv_main.c:224
cvar_t sv_checkforpacketsduringsleep
Definition sv_main.c:76
client_t * host_client
Definition sv_main.c:29
void S_StopAllSounds(void)
Definition snd_main.c:1710
cactive_t state
Definition client.h:568
qbool demorecording
Definition client.h:584
qbool active
false = empty client slot
Definition server.h:185
Definition cvar.h:66
float value
Definition cvar.h:74
int integer
Definition cvar.h:73
const char * string
Definition cvar.h:71
void ** funcvariable
Definition sys.h:175
const char * name
Definition sys.h:174
double sleeptime
time spent sleeping after the last frame
Definition host.h:48
qbool restless
don't sleep
Definition host.h:49
double dirtytime
the main loop wall time for this frame, equal to Sys_DirtyTime() at the start of this host frame
Definition host.h:47
jmp_buf abortframe
Definition host.h:43
int state
Definition host.h:44
double realtime
the accumulated mainloop time since application started (with filtering), without any slowmo or clamp...
Definition host.h:46
llist_t list
Definition lhnet.h:47
struct client_s * clients
client slots
Definition server.h:30
int maxclients
number of svs.clients slots (updated by maxplayers command)
Definition server.h:28
qbool active
false if only a net client
Definition server.h:66
Definition sys.h:145
int nicelevel
Definition sys.h:150
qbool nicepossible
Definition sys.h:151
qbool isnice
Definition sys.h:152
int outfd
Definition sys.h:149
int argc
Definition sys.h:146
int selffd
Definition sys.h:148
const char ** argv
Definition sys.h:147
unsigned int Sys_SDL_GetTicks(void)
Definition sys_null.c:27
void Sys_SDL_Dialog(const char *title, const char *string)
Definition sys_null.c:13
#define Sys_HaveSSE()
Definition sys.h:140
#define Sys_HaveSSE2()
Definition sys.h:141
void * dllhandle_t
Definition sys.h:169
void Sys_SDL_Delay(unsigned int milliseconds)
Definition sys_null.c:32
qbool sys_supportsdlgetticks
Definition sys_null.c:26
static const char * Sys_FindExecutableName(void)
Definition sys_shared.c:797
static cvar_t sys_usesdldelay
Definition sys_shared.c:307
static cvar_t sys_usesdlgetticks
Definition sys_shared.c:306
size_t Sys_TimeString(char buf[], size_t bufsize, const char *timeformat)
Definition sys_shared.c:45
static cvar_t sys_stdout_blocks
Definition sys_shared.c:314
static const char * Sys_SigDesc(int sig)
Definition sys_shared.c:967
void Sys_Print(const char *text, size_t textlen)
(may) output text to terminal which launched program is POSIX async-signal-safe textlen excludes any ...
Definition sys_shared.c:615
void Sys_InitProcessNice(void)
called to set process priority for dedicated servers
Definition sys_shared.c:955
void Sys_Printf(const char *fmt,...)
used to report failures inside Con_Printf()
Definition sys_shared.c:652
void Sys_Error(const char *error,...)
Causes the entire program to exit ASAP.
Definition sys_shared.c:724
void * Sys_GetProcAddress(dllhandle_t handle, const char *name)
Definition sys_shared.c:261
static double benchmark_time
Definition sys_shared.c:317
static const char * Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
Definition sys_shared.c:773
char * Sys_ConsoleInput(void)
Reads a line from POSIX stdin or the Windows console.
Definition sys_shared.c:667
void Sys_MakeProcessNice(void)
Definition sys_shared.c:958
qbool Sys_LoadLibrary(const char *name, dllhandle_t *handle)
Definition sys_shared.c:224
void Sys_AllowProfiling(qbool enable)
Definition sys_shared.c:65
static void Sys_UpdateOutFD_c(cvar_t *var)
Definition sys_shared.c:342
static cvar_t sys_stdout
Definition sys_shared.c:312
qbool Sys_LoadDependency(const char **dllnames, dllhandle_t *handle, const dllfunction_t *fcts)
Definition sys_shared.c:131
int Sys_Main(int argc, char *argv[])
main() but renamed so we can wrap it in sys_sdl.c and sys_null.c to avoid needing to include SDL....
static void Sys_InitSignals(void)
SDL2 only handles SIGINT and SIGTERM by default and doesn't log anything.
static qbool Sys_LoadDependencyFunctions(dllhandle_t dllhandle, const dllfunction_t *fcts, qbool complain, qbool has_next)
Definition sys_shared.c:91
double Sys_DirtyTime(void)
Definition sys_shared.c:417
void Sys_ProvideSelfFD(void)
Definition sys_shared.c:826
void Sys_MakeProcessMean(void)
Definition sys_shared.c:961
double Sys_Sleep(double time)
called to yield for a little bit so as not to hog cpu when paused or debugging
Definition sys_shared.c:500
static void Sys_Frame(void)
JS+WebGL doesn't support a main loop, only a function called to run a frame.
static double Sys_UpdateTime(double newtime, double oldtime)
cvar_t sys_libdir
Definition sys_shared.c:302
void Sys_Init_Commands(void)
called after command system is initialized but before first Con_Print
Definition sys_shared.c:353
static cvar_t sys_debugsleep
Definition sys_shared.c:305
cvar_t sys_usenoclockbutbenchmark
Definition sys_shared.c:301
void Sys_FreeLibrary(dllhandle_t *handle)
Definition sys_shared.c:245
sys_t sys
Definition sys_shared.c:42
int Sys_CheckParm(const char *parm)
Definition sys_shared.c:327
static void Sys_HandleCrash(int sig)
Halt and try not to catch fire.
Definition sys_shared.c:994
static void Sys_HandleSignal(int sig)
qbool Sys_LoadSelf(dllhandle_t *handle)
Definition sys_shared.c:116
cvar_t vid_vsync
Definition vid_shared.c:149
void VID_Shutdown(void)
Called at shutdown.
Definition vid_null.c:28