From c36ad5d94bdd760b0116c18d7f65417049d4c23c Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Thu, 6 Feb 2025 12:34:01 -0500
Subject: [PATCH] Merged changes from sdl2-compat: don't call into the newer
SDL during startup.
---
src/SDL12_compat.c | 272 ++++++++++++++++++++++++++++++++++------
src/SDL12_compat_objc.m | 13 ++
2 files changed, 249 insertions(+), 36 deletions(-)
diff --git a/src/SDL12_compat.c b/src/SDL12_compat.c
index 6b87d59e6..51e538035 100644
--- a/src/SDL12_compat.c
+++ b/src/SDL12_compat.c
@@ -1069,6 +1069,45 @@ SDL20_atoi(const char *str)
return SDL20_strtol(str, NULL, 10);
}
+/* you can use SDL20_atoi once we're past startup. */
+static int
+SDL12COMPAT_atoi(const char *str)
+{
+ int retval = 0;
+ int multiplier = 1;
+ int signmult = 1;
+ const char *ptr;
+
+ while (*str == ' ') {
+ str++;
+ }
+
+ if (*str == '-') {
+ signmult = -1;
+ str++;
+ while (*str == ' ') {
+ str++;
+ }
+ }
+
+ ptr = str;
+ while (SDL_TRUE) {
+ if ((*ptr < '0') || (*ptr > '9')) {
+ break;
+ }
+ ptr++;
+ }
+ ptr--;
+
+ while (ptr != str) {
+ retval += ((int) (*ptr - '0')) * multiplier;
+ multiplier *= 10;
+ ptr--;
+ }
+
+ return (retval + (((int) (*ptr - '0')) * multiplier)) * signmult;
+}
+
static char *
SDL12COMPAT_stpcpy(char *dst, const char *src)
{
@@ -1103,6 +1142,78 @@ SDL12COMPAT_itoa(char *dst, int val)
} while (ptr > dst);
}
+/* you can use SDL20_strlen once we're past startup. */
+static int SDL12COMPAT_strlen(const char *str)
+{
+ int retval = 0;
+ while (str[retval]) {
+ retval++;
+ }
+ return retval;
+}
+
+/* you can use SDL20_strcmp once we're past startup. */
+static SDL_bool SDL12COMPAT_strequal(const char *a, const char *b)
+{
+ while (SDL_TRUE) {
+ const char cha = *a;
+ if (cha != *b) {
+ return SDL_FALSE;
+ } else if (!cha) {
+ break;
+ }
+ a++;
+ b++;
+ }
+ return SDL_TRUE;
+}
+
+/* log a string using platform-specific code for before SDL2 is fully available. */
+static void SDL12COMPAT_LogAtStartup(const char *str)
+{
+ #ifdef _WIN32
+ OutputDebugStringA(str);
+ #elif defined(__APPLE__)
+ extern void SDL12COMPAT_NSLog(const char *prefix, const char *text);
+ SDL12COMPAT_NSLog(NULL, str);
+ #else
+ fputs(str, stderr);
+ fputs("\n", stderr);
+ #endif
+}
+
+/* this talks right to the OS environment table. Don't use SDL20_setenv at startup. */
+static void SDL12COMPAT_SetEnvAtStartup(const char *name, const char *value)
+{
+ #ifdef _WIN32
+ SetEnvironmentVariableA(name, value);
+ #else /* we might need other platforms, or a simple `return;` for platforms without an environment table. */
+ if (value) {
+ setenv(name, value, 1);
+ } else {
+ unsetenv(name);
+ }
+ #endif
+}
+
+/* this talks right to the OS environment table. Don't use SDL20_getenv at startup. */
+static const char *SDL12COMPAT_GetEnvAtStartup(const char *name)
+{
+ #ifdef _WIN32
+ static char buf[256]; /* overflows will just report as environment variable being unset. But most of our environment vars don't come through here. */
+ const DWORD rc = GetEnvironmentVariableA(name, buf, (DWORD) sizeof (buf));
+ return ((rc != 0) && (rc < sizeof (buf))) ? buf : NULL;
+ #else /* we might need other platforms, or a simple `return NULL;` for platforms without an environment table. */
+ return getenv(name);
+ #endif
+}
+
+/* this can't call into SDL20_getenv because things aren't set up yet, so try for platform-specific getenv checks. */
+static SDL_bool SDL12COMPAT_CheckDebugLogging(void)
+{
+ const char *value = SDL12COMPAT_GetEnvAtStartup("SDL12COMPAT_DEBUG_LOGGING");
+ return ((value != NULL) && SDL12COMPAT_strequal(value, "1")) ? SDL_TRUE : SDL_FALSE;
+}
/* Obviously we can't use SDL_LoadObject() to load SDL2. :) */
static char loaderror[256];
@@ -1428,6 +1539,7 @@ SDL12Compat_GetHintInt(const char *name, int default_value)
return SDL20_atoi(val);
}
+/* DO NOT USE SDL2 FUNCTIONS IN HERE! */
static void
SDL12Compat_ApplyQuirks(SDL_bool force_x11)
{
@@ -1435,21 +1547,37 @@ SDL12Compat_ApplyQuirks(SDL_bool force_x11)
int i;
if (WantDebugLogging) {
- SDL20_Log("This app appears to be named '%s'", exe_name);
+ const char *lead = "sdl12-compat: This app appears to be named:";
+ char msg[256];
+ if ((SDL12COMPAT_strlen(lead) + SDL12COMPAT_strlen(exe_name) + 2) <= (int) (sizeof (msg))) {
+ char *p = msg;
+ p = SDL12COMPAT_stpcpy(p, lead);
+ p = SDL12COMPAT_stpcpy(p, " ");
+ p = SDL12COMPAT_stpcpy(p, exe_name);
+ SDL12COMPAT_LogAtStartup(msg);
+ } else {
+ SDL12COMPAT_LogAtStartup(lead);
+ SDL12COMPAT_LogAtStartup(exe_name);
+ }
}
#ifdef __linux__
if (force_x11) {
- const char *videodriver_env = SDL20_getenv("SDL_VIDEODRIVER");
- if (videodriver_env && (SDL20_strcmp(videodriver_env, "x11") != 0)) {
+ const char *videodriver_env = SDL12COMPAT_GetEnvAtStartup("SDL_VIDEODRIVER");
+ if (videodriver_env && !SDL12COMPAT_strequal(videodriver_env, "x11")) {
if (WantDebugLogging) {
- SDL20_Log("This app looks like it requires X11, but the SDL_VIDEODRIVER environment variable is set to \"%s\". If you have issues, try setting SDL_VIDEODRIVER=x11", videodriver_env);
+ SDL12COMPAT_LogAtStartup("sdl12-compat: This app looks like it requires X11, but the SDL_VIDEODRIVER environment variable is currently set to:");
+ SDL12COMPAT_LogAtStartup("");
+ SDL12COMPAT_LogAtStartup(videodriver_env);
+ SDL12COMPAT_LogAtStartup("");
+ SDL12COMPAT_LogAtStartup("If you have issues, try setting SDL_VIDEODRIVER=x11");
+
}
} else {
if (WantDebugLogging) {
- SDL20_Log("sdl12-compat: We are forcing this app to use X11, because it probably talks to an X server directly, outside of SDL. If possible, this app should be fixed, to be compatible with Wayland, etc.");
+ SDL12COMPAT_LogAtStartup("sdl12-compat: We are forcing this app to use X11, because it probably talks to an X server directly, outside of SDL. If possible, this app should be fixed, to be compatible with Wayland, etc.");
}
- SDL20_setenv("SDL_VIDEODRIVER", "x11", 1);
+ SDL12COMPAT_SetEnvAtStartup("SDL_VIDEODRIVER", "x11");
}
}
#endif
@@ -1457,23 +1585,58 @@ SDL12Compat_ApplyQuirks(SDL_bool force_x11)
if (*exe_name == '\0') {
return;
}
+
for (i = 0; i < (int) SDL_arraysize(quirks); i++) {
- if (!SDL20_strcmp(exe_name, quirks[i].exe_name)) {
- if (!SDL20_getenv(quirks[i].hint_name)) {
+ if (SDL12COMPAT_strequal(exe_name, quirks[i].exe_name)) {
+ const char *var = SDL12COMPAT_GetEnvAtStartup(quirks[i].hint_name);
+ if (!var) {
if (WantDebugLogging) {
- SDL20_Log("Applying compatibility quirk %s=\"%s\" for \"%s\"", quirks[i].hint_name, quirks[i].hint_value, exe_name);
+ char msg[256];
+ char *p = msg;
+ p = SDL12COMPAT_stpcpy(p, "sdl12-compat: Applying compatibility quirk ");
+ p = SDL12COMPAT_stpcpy(p, quirks[i].hint_name);
+ p = SDL12COMPAT_stpcpy(p, "=\"");
+ p = SDL12COMPAT_stpcpy(p, quirks[i].hint_value);
+ p = SDL12COMPAT_stpcpy(p, "\".");
+ SDL12COMPAT_LogAtStartup(msg);
}
- SDL20_setenv(quirks[i].hint_name, quirks[i].hint_value, 1);
+ SDL12COMPAT_SetEnvAtStartup(quirks[i].hint_name, quirks[i].hint_value);
} else {
if (WantDebugLogging) {
- SDL20_Log("Not applying compatibility quirk %s=\"%s\" for \"%s\" due to environment variable override (\"%s\")\n",
- quirks[i].hint_name, quirks[i].hint_value, exe_name, SDL20_getenv(quirks[i].hint_name));
+ char msg[256];
+ char varbuf[32];
+ char *p = msg;
+ int j;
+
+ /* copy the start of untrusted string var to a small array to prevent buffer overflows */
+ for (j = 0; j < ((int) SDL_arraysize(varbuf)); j++) {
+ varbuf[j] = var[j];
+ if (var[j] == 0) {
+ break;
+ }
+ }
+
+ if (j == SDL_arraysize(varbuf)) { /* truncate and terminate the string if necessary. */
+ SDL12COMPAT_stpcpy(varbuf + (SDL_arraysize(varbuf) - 6), "[...]");
+ }
+
+ p = SDL12COMPAT_stpcpy(p, "sdl12-compat: Not applying compatibility quirk ");
+ p = SDL12COMPAT_stpcpy(p, quirks[i].hint_name);
+ p = SDL12COMPAT_stpcpy(p, "=\"");
+ p = SDL12COMPAT_stpcpy(p, quirks[i].hint_value);
+ p = SDL12COMPAT_stpcpy(p, "\" due to environment variable override (\"");
+ p = SDL12COMPAT_stpcpy(p, varbuf);
+ p = SDL12COMPAT_stpcpy(p, "\").");
+ SDL12COMPAT_LogAtStartup(msg);
}
}
}
}
}
+/* DO NOT CALL THINGS THAT USE SDL ALLOCATORS HERE. It runs before main(), so app-supplied allocators are not set at this point.
+ This means no SDL_Log, no hint subsystem, nothing that might call SDL_SetError! In fact, favor code in this file, using
+ platform-specific #ifdefs, to calling into SDL2 at all, if you can help it. */
static int
LoadSDL20(void)
{
@@ -1497,51 +1660,88 @@ LoadSDL20(void)
}
#endif
+ WantDebugLogging = SDL12COMPAT_CheckDebugLogging();
+
okay = LoadSDL20Library();
if (!okay) {
- SDL12COMPAT_stpcpy(loaderror, "Failed loading SDL2 library.");
+ SDL12COMPAT_stpcpy(loaderror, "sdl12-compat: Failed loading SDL2 library.");
} else {
#define SDL20_SYM(rc,fn,params,args,ret) SDL20_##fn = (SDL20_##fn##_t) LoadSDL20Symbol("SDL_" #fn, &okay);
#include "SDL20_syms.h"
if (okay) {
+ char sdl2verstr[16];
+ char sdl2reqverstr[16];
+ char sdl12compatverstr[16];
SDL_version v;
+ SDL_version reqv;
+ char *p;
+
SDL20_GetVersion(&v);
+
+ reqv.major = (SDL20_REQUIRED_VER / 1000);
+ reqv.minor = ((SDL20_REQUIRED_VER % 1000) / 100);
+ reqv.patch = (SDL20_REQUIRED_VER % 100);
+
+ #define SETVERSTR(str, major, minor, micro) { \
+ char value[16]; \
+ p = str; \
+ SDL12COMPAT_itoa(value, major); p = SDL12COMPAT_stpcpy(p, value); *p++ = '.'; \
+ SDL12COMPAT_itoa(value, minor); p = SDL12COMPAT_stpcpy(p, value); *p++ = '.'; \
+ SDL12COMPAT_itoa(value, micro); p = SDL12COMPAT_stpcpy(p, value); \
+ }
+
+ SETVERSTR(sdl2verstr, v.major, v.minor, v.patch);
+ SETVERSTR(sdl2reqverstr, reqv.major, reqv.minor, reqv.patch);
+ SETVERSTR(sdl12compatverstr, 1, 2, SDL12_COMPAT_VERSION);
+
+ #undef SETVERSTR
+
LinkedSDL2VersionInt = SDL_VERSIONNUM(v.major, v.minor, v.patch);
okay = (LinkedSDL2VersionInt >= SDL20_REQUIRED_VER);
if (!okay) {
- char value[12];
- char *p = SDL12COMPAT_stpcpy(loaderror, "SDL2 ");
-
- SDL12COMPAT_itoa(value, v.major);
- p = SDL12COMPAT_stpcpy(p, value); *p++ = '.';
- SDL12COMPAT_itoa(value, v.minor);
- p = SDL12COMPAT_stpcpy(p, value); *p++ = '.';
- SDL12COMPAT_itoa(value, v.patch);
- p = SDL12COMPAT_stpcpy(p, value);
-
- SDL12COMPAT_stpcpy(p, " library is too old.");
+ p = loaderror;
+ p = SDL12COMPAT_stpcpy(p, "sdl12-compat ");
+ p = SDL12COMPAT_stpcpy(p, sdl12compatverstr);
+ p = SDL12COMPAT_stpcpy(p, ": SDL2 library is too old (have ");
+ p = SDL12COMPAT_stpcpy(p, sdl2verstr);
+ p = SDL12COMPAT_stpcpy(p, ", but need at least ");
+ p = SDL12COMPAT_stpcpy(p, sdl2reqverstr);
+ p = SDL12COMPAT_stpcpy(p, ").");
} else {
- WantDebugLogging = SDL12Compat_GetHintBoolean("SDL12COMPAT_DEBUG_LOGGING", SDL_FALSE);
if (WantDebugLogging) {
+ char debugmsg[128]; /* can't use SDL log or malloc, just write to a stack buffer and do a simple platform-specific logging. */
+
+ p = debugmsg;
+ p = SDL12COMPAT_stpcpy(p, "sdl12-compat ");
+ p = SDL12COMPAT_stpcpy(p, sdl12compatverstr);
+ p = SDL12COMPAT_stpcpy(p, ", ");
+
#if defined(__DATE__) && defined(__TIME__)
- SDL20_Log("sdl12-compat 1.2.%d, built on " __DATE__ " at " __TIME__ ", talking to SDL2 %d.%d.%d", SDL12_COMPAT_VERSION, v.major, v.minor, v.patch);
- #else
- SDL20_Log("sdl12-compat 1.2.%d, talking to SDL2 %d.%d.%d", SDL12_COMPAT_VERSION, v.major, v.minor, v.patch);
+ p = SDL12COMPAT_stpcpy(p, "built on " __DATE__ " at " __TIME__ ", ");
#endif
+
+ p = SDL12COMPAT_stpcpy(p, "talking to SDL2 ");
+ p = SDL12COMPAT_stpcpy(p, sdl2verstr);
+
+ SDL12COMPAT_LogAtStartup(debugmsg);
}
+
SDL12Compat_ApplyQuirks(force_x11); /* Apply and maybe print a list of any enabled quirks. */
#ifdef __linux__
{
- const char *viddrv = SDL20_getenv("SDL_VIDEODRIVER");
- if (viddrv && (SDL20_strcmp(viddrv, "x11") == 0) && SDL12Compat_GetHintBoolean("SDL12COMPAT_FORCE_XINITTHREADS", SDL_TRUE)) {
- void *lib = dlopen("libX11.so.6", RTLD_GLOBAL|RTLD_NOW);
- if (lib) {
- int (*pXInitThreads)(void) = (int(*)(void)) dlsym(lib, "XInitThreads");
- if (pXInitThreads) {
- pXInitThreads();
+ const char *envvar = SDL12COMPAT_GetEnvAtStartup("SDL_VIDEODRIVER");
+ if (envvar && SDL12COMPAT_strequal(envvar, "x11")) {
+ envvar = SDL12COMPAT_GetEnvAtStartup("SDL12COMPAT_FORCE_XINITTHREADS");
+ if (envvar && (SDL12COMPAT_atoi(envvar) != 0)) {
+ void *lib = dlopen("libX11.so.6", RTLD_GLOBAL|RTLD_NOW);
+ if (lib) {
+ int (*pXInitThreads)(void) = (int(*)(void)) dlsym(lib, "XInitThreads");
+ if (pXInitThreads) {
+ pXInitThreads();
+ }
+ /* leave the library open, so the XInitThreads sticks. */
}
- /* leave the library open, so the XInitThreads sticks. */
}
}
}
diff --git a/src/SDL12_compat_objc.m b/src/SDL12_compat_objc.m
index 25eeed757..31f0ec8f2 100644
--- a/src/SDL12_compat_objc.m
+++ b/src/SDL12_compat_objc.m
@@ -63,6 +63,19 @@ SDL12_PRIVATE void error_dialog(const char *errorMsg)
alert.informativeText = [NSString stringWithCString:errorMsg encoding:NSASCIIStringEncoding];
[alert runModal];
}
+
+SDL12_PRIVATE void SDL12COMPAT_NSLog(const char *prefix, const char *text)
+{
+ @autoreleasepool {
+ NSString *nsText = [NSString stringWithUTF8String:text];
+ if (prefix && *prefix) {
+ NSString *nsPrefix = [NSString stringWithUTF8String:prefix];
+ NSLog(@"%@%@", nsPrefix, nsText);
+ } else {
+ NSLog(@"%@", nsText);
+ }
+ }
+}
#endif
/* vi: set ts=4 sw=4 expandtab: */