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