+
+
+static const char *Sys_SigDesc(int sig)
+{
+ switch (sig)
+ {
+ // Windows only supports the C99 signals
+ case SIGINT: return "Interrupt";
+ case SIGILL: return "Illegal instruction";
+ case SIGABRT: return "Aborted";
+ case SIGFPE: return "Floating point exception";
+ case SIGSEGV: return "Segmentation fault";
+ case SIGTERM: return "Termination";
+#ifndef WIN32
+ // POSIX has several others worth catching
+ case SIGHUP: return "Hangup";
+ case SIGQUIT: return "Quit";
+ case SIGBUS: return "Bus error (bad memory access)";
+ case SIGPIPE: return "Broken pipe";
+#endif
+ default: return "Yo dawg, we bugged out while bugging out";
+ }
+}
+
+/** Halt and try not to catch fire.
+ * Writing to any file could corrupt it,
+ * any uneccessary code could crash while we crash.
+ * Try to use only POSIX async-signal-safe library functions here (see: man signal-safety).
+ */
+static void Sys_HandleCrash(int sig)
+{
+#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+ // Before doing anything else grab the stack frame addresses
+ #include <execinfo.h>
+ void *stackframes[32];
+ int framecount = backtrace(stackframes, 32);
+ char **btstrings;
+#endif
+ char dialogtext[3072];
+ const char *sigdesc;
+
+ // Break any loop and disable Sys_HandleSignal()
+ if (host.state == host_failing || host.state == host_failed)
+ return;
+ host.state = host_failing;
+
+ sigdesc = Sys_SigDesc(sig);
+
+ // set output to blocking stderr and print header, backtrace, version
+ sys.outfd = fileno(stderr); // not async-signal-safe :(
+#ifndef WIN32
+ fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
+ Sys_Print("\n\n\x1B[1;37;41m Engine Crash: ", 30);
+ Sys_Print(sigdesc, strlen(sigdesc));
+ Sys_Print(" \x1B[m\n", 8);
+ #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+ // the first two addresses will be in this function and in signal() in libc
+ backtrace_symbols_fd(stackframes + 2, framecount - 2, sys.outfd);
+ #endif
+ Sys_Print("\x1B[1m", 4);
+ Sys_Print(engineversion, strlen(engineversion));
+ Sys_Print("\x1B[m\n", 4);
+#else // Windows console doesn't support colours
+ Sys_Print("\n\nEngine Crash: ", 16);
+ Sys_Print(sigdesc, strlen(sigdesc));
+ Sys_Print("\n", 1);
+ Sys_Print(engineversion, strlen(engineversion));
+ Sys_Print("\n", 1);
+#endif
+
+ // DP8 TODO: send a disconnect message indicating we crashed, see Sys_Error() and Host_Error()
+
+ // don't want a dead window left blocking the OS UI or the crash dialog
+ VID_Shutdown();
+ S_StopAllSounds();
+
+ // prepare the dialogtext: signal, backtrace, version
+ // the dp_st* funcs are POSIX async-signal-safe IF we don't trigger their warnings
+ dp_strlcpy(dialogtext, sigdesc, sizeof(dialogtext));
+ dp_strlcat(dialogtext, "\n\n", sizeof(dialogtext));
+#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+ btstrings = backtrace_symbols(stackframes + 2, framecount - 2); // calls malloc :(
+ if (btstrings)
+ for (int i = 0; i < framecount - 2; ++i)
+ {
+ dp_strlcat(dialogtext, btstrings[i], sizeof(dialogtext));
+ dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
+ }
+#endif
+ dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
+ dp_strlcat(dialogtext, engineversion, sizeof(dialogtext));
+
+ host.state = host_failed; // make Sys_HandleSignal() call _Exit()
+ Sys_SDL_Dialog("Engine Crash", dialogtext);
+
+ fflush(stderr); // not async-signal-safe :(
+
+ // Continue execution with default signal handling.
+ // A real crash will be re-triggered so the platform can handle it,
+ // a fake crash (kill -SEGV) will cause a graceful shutdown.
+ signal(sig, SIG_DFL);
+}
+
+static void Sys_HandleSignal(int sig)
+{
+ const char *sigdesc;
+
+ // Break any loop, eg if each Sys_Print triggers a SIGPIPE
+ if (host.state == host_shutdown || host.state == host_failing)
+ return;
+
+ sigdesc = Sys_SigDesc(sig);
+ Sys_Print("\nReceived ", 10);
+ Sys_Print(sigdesc, strlen(sigdesc));
+ Sys_Print(" signal, exiting...\n", 20);
+ if (host.state == host_failed)
+ {
+ // user is trying to kill the process while the SDL dialog is open
+ fflush(stderr); // not async-signal-safe :(
+ _Exit(sig);
+ }
+ host.state = host_shutdown;
+}
+
+/// SDL2 only handles SIGINT and SIGTERM by default and doesn't log anything
+static void Sys_InitSignals(void)
+{
+ // Windows only supports the C99 signals
+ signal(SIGINT, Sys_HandleSignal);
+ signal(SIGILL, Sys_HandleCrash);
+ signal(SIGABRT, Sys_HandleCrash);
+ signal(SIGFPE, Sys_HandleCrash);
+ signal(SIGSEGV, Sys_HandleCrash);
+ signal(SIGTERM, Sys_HandleSignal);
+#ifndef WIN32
+ // POSIX has several others worth catching
+ signal(SIGHUP, Sys_HandleSignal);
+ signal(SIGQUIT, Sys_HandleSignal);
+ signal(SIGBUS, Sys_HandleCrash);
+ signal(SIGPIPE, Sys_HandleSignal);
+#endif
+}
+
+// Cloudwalk: Most overpowered function declaration...
+static inline double Sys_UpdateTime (double newtime, double oldtime)
+{
+ double time = newtime - oldtime;
+
+ if (time < 0)
+ {
+ // warn if it's significant
+ if (time < -0.01)
+ Con_Printf(CON_WARN "Host_UpdateTime: time stepped backwards (went from %f to %f, difference %f)\n", oldtime, newtime, time);
+ time = 0;
+ }
+ else if (time >= 1800)
+ {
+ Con_Printf(CON_WARN "Host_UpdateTime: time stepped forward (went from %f to %f, difference %f)\n", oldtime, newtime, time);
+ time = 0;
+ }
+
+ return time;
+}
+
+#ifdef __EMSCRIPTEN__
+ #include <emscripten.h>
+#endif
+/// JS+WebGL doesn't support a main loop, only a function called to run a frame.
+static void Sys_Frame(void)
+{
+ double time, newtime, sleeptime;
+#ifdef __EMSCRIPTEN__
+ static double sleepstarttime = 0;
+ host.sleeptime = Sys_DirtyTime() - sleepstarttime;
+#endif
+
+ if (setjmp(host.abortframe)) // Something bad happened, or the server disconnected
+ host.state = host_active; // In case we were loading
+
+ if (host.state >= host_shutdown) // see Sys_HandleCrash() comments
+ {
+#ifdef __EMSCRIPTEN__
+ emscripten_cancel_main_loop();
+#endif
+#ifdef __ANDROID__
+ Sys_AllowProfiling(false);
+#endif
+ Host_Shutdown();
+ exit(0);
+ }
+
+ newtime = Sys_DirtyTime();
+ host.realtime += time = Sys_UpdateTime(newtime, host.dirtytime);
+ host.dirtytime = newtime;
+
+ sleeptime = Host_Frame(time);
+ sleeptime -= Sys_DirtyTime() - host.dirtytime; // execution time
+
+#ifdef __EMSCRIPTEN__
+ // This platform doesn't support a main loop... it will sleep when Sys_Frame() returns.
+ // Not using emscripten_sleep() via Sys_Sleep() because it would cause two sleeps per frame.
+ if (!vid_vsync.integer) // see VID_SetVsync_c()
+ emscripten_set_main_loop_timing(EM_TIMING_SETTIMEOUT, host.restless ? 0 : sleeptime * 1000.0);
+ sleepstarttime = Sys_DirtyTime();
+#else
+ host.sleeptime = Sys_Sleep(sleeptime);
+#endif
+}
+
+/** main() but renamed so we can wrap it in sys_sdl.c and sys_null.c
+ * to avoid needing to include SDL.h in this file (would make the dedicated server require SDL).
+ * SDL builds need SDL.h in the file where main() is defined because SDL renames and wraps main().
+ */
+int Sys_Main(int argc, char *argv[])
+{
+ sys.argc = argc;
+ sys.argv = (const char **)argv;
+
+ // COMMANDLINEOPTION: Console: -nostdout disables text output to the terminal the game was launched from
+ // COMMANDLINEOPTION: -noterminal disables console output on stdout
+ if(Sys_CheckParm("-noterminal") || Sys_CheckParm("-nostdout"))
+ sys_stdout.string = "0";
+ // COMMANDLINEOPTION: -stderr moves console output to stderr
+ else if(Sys_CheckParm("-stderr"))
+ sys_stdout.string = "2";
+ // too early for Cvar_SetQuick
+ sys_stdout.value = sys_stdout.integer = atoi(sys_stdout.string);
+ Sys_UpdateOutFD_c(&sys_stdout);
+#ifndef WIN32
+ fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL, 0) | O_NONBLOCK);
+ // stdout/stderr will be set to blocking in Sys_Print() if so configured, or during a fatal error.
+ fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) | O_NONBLOCK);
+ fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) | O_NONBLOCK);
+#endif
+
+ sys.selffd = -1;
+ Sys_ProvideSelfFD(); // may call Con_Printf() so must be after sys.outfd is set
+
+#ifdef __ANDROID__
+ Sys_AllowProfiling(true);
+#endif
+
+ Sys_InitSignals();
+
+#ifdef WIN32
+ Sys_SetTimerResolution();
+#endif
+
+ Host_Init();
+#ifdef __EMSCRIPTEN__
+ emscripten_set_main_loop(Sys_Frame, 0, true); // doesn't return
+#else
+ while(true)
+ Sys_Frame();
+#endif
+
+ return 0;
+}