Browse Source

Added thread-safe environment functions

Also marked the existing functions as unsafe, as they can cause crashes if used in multi-threaded applications.

As a bonus, since the new functions are hashtable based, hint environment lookups are much faster.
Sam Lantinga 7 months ago
parent
commit
90e01040c5
49 changed files with 696 additions and 252 deletions
  1. 3 3
      docs/README-vita.md
  2. 207 3
      include/SDL3/SDL_stdinc.h
  3. 2 0
      src/SDL.c
  4. 5 0
      src/SDL_hashtable.c
  5. 1 0
      src/SDL_hashtable.h
  6. 4 4
      src/SDL_hints.c
  7. 1 1
      src/audio/SDL_audiodev.c
  8. 2 1
      src/core/android/SDL_android.c
  9. 5 5
      src/core/linux/SDL_ibus.c
  10. 2 2
      src/core/linux/SDL_ime.c
  11. 3 1
      src/core/linux/SDL_sandbox.c
  12. 6 6
      src/dialog/unix/SDL_zenitydialog.c
  13. 2 2
      src/dynapi/SDL_dynapi.c
  14. 11 3
      src/dynapi/SDL_dynapi.sym
  15. 11 3
      src/dynapi/SDL_dynapi_overrides.h
  16. 11 3
      src/dynapi/SDL_dynapi_procs.h
  17. 1 1
      src/filesystem/cocoa/SDL_sysfilesystem.m
  18. 1 1
      src/filesystem/emscripten/SDL_sysfilesystem.c
  19. 2 2
      src/filesystem/haiku/SDL_sysfilesystem.cc
  20. 8 8
      src/filesystem/unix/SDL_sysfilesystem.c
  21. 1 1
      src/gpu/vulkan/SDL_gpu_vulkan.c
  22. 1 1
      src/haptic/SDL_haptic.c
  23. 2 2
      src/locale/unix/SDL_syslocale.c
  24. 1 1
      src/main/emscripten/SDL_sysmain_runapp.c
  25. 2 2
      src/misc/unix/SDL_sysurl.c
  26. 9 10
      src/process/posix/SDL_posixprocess.c
  27. 6 0
      src/process/windows/SDL_windowsprocess.c
  28. 266 12
      src/stdlib/SDL_getenv.c
  29. 4 4
      src/stdlib/SDL_iconv.c
  30. 1 1
      src/test/SDL_test_memory.c
  31. 2 2
      src/video/SDL_egl.c
  32. 3 3
      src/video/wayland/SDL_waylandevents.c
  33. 2 2
      src/video/wayland/SDL_waylandmessagebox.c
  34. 2 2
      src/video/wayland/SDL_waylandmouse.c
  35. 1 1
      src/video/wayland/SDL_waylandshmbuffer.c
  36. 2 2
      src/video/wayland/SDL_waylandvideo.c
  37. 2 2
      src/video/wayland/SDL_waylandwindow.c
  38. 1 1
      src/video/x11/SDL_x11keyboard.c
  39. 1 1
      src/video/x11/SDL_x11modes.c
  40. 10 26
      test/childprocess.c
  41. 1 1
      test/testatomic.c
  42. 1 1
      test/testautomation_hints.c
  43. 58 57
      test/testautomation_stdlib.c
  44. 2 2
      test/testautomation_video.c
  45. 1 1
      test/testerror.c
  46. 23 62
      test/testprocess.c
  47. 1 1
      test/testsurround.c
  48. 1 1
      test/testthread.c
  49. 1 1
      test/testtimer.c

+ 3 - 3
docs/README-vita.md

@@ -21,13 +21,13 @@ Notes
 -----
 * gles1/gles2 support and renderers are disabled by default and can be enabled by configuring with `-DVIDEO_VITA_PVR=ON`
   These renderers support 720p and 1080i resolutions. These can be specified with:
-  `SDL_setenv("VITA_RESOLUTION", "720", 1);` and `SDL_setenv("VITA_RESOLUTION", "1080", 1);`
+  `SDL_SetHint(SDL_HINT_VITA_RESOLUTION, "720");` and `SDL_SetHint(SDL_HINT_VITA_RESOLUTION, "1080");`
 * Desktop GL 1.X and 2.X support and renderers are also disabled by default and also can be enabled with `-DVIDEO_VITA_PVR=ON` as long as gl4es4vita is present in your SDK.
-  They support the same resolutions as the gles1/gles2 backends and require specifying `SDL_setenv("VITA_PVR_OGL", "1", 1);`
+  They support the same resolutions as the gles1/gles2 backends and require specifying `SDL_SetHint(SDL_HINT_VITA_PVR_OPENGL, "1");`
   anytime before video subsystem initialization.
 * gles2 support via PIB is disabled by default and can be enabled by configuring with `-DVIDEO_VITA_PIB=ON`
 * By default SDL emits mouse events for touch events on every touchscreen.
   Vita has two touchscreens, so it's recommended to use `SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");` and handle touch events instead.
   Individual touchscreens can be disabled with:
-  `SDL_setenv("VITA_DISABLE_TOUCH_FRONT", "1", 1);` and `SDL_setenv("VITA_DISABLE_TOUCH_BACK", "1", 1);`
+  `SDL_SetHint(SDL_HINT_VITA_ENABLE_FRONT_TOUCH, "0");` and `SDL_SetHint(SDL_HINT_VITA_ENABLE_BACK_TOUCH, "0");`
 * Support for L2/R2/R3/R3 buttons, haptic feedback and gamepad led only available on PSTV, or when using external ds4 gamepad on vita.

+ 207 - 3
include/SDL3/SDL_stdinc.h

@@ -978,9 +978,213 @@ extern SDL_DECLSPEC void SDLCALL SDL_aligned_free(void *mem);
  */
 extern SDL_DECLSPEC int SDLCALL SDL_GetNumAllocations(void);
 
-extern SDL_DECLSPEC const char * SDLCALL SDL_getenv(const char *name);
-extern SDL_DECLSPEC int SDLCALL SDL_setenv(const char *name, const char *value, int overwrite);
-extern SDL_DECLSPEC int SDLCALL SDL_unsetenv(const char *name);
+/**
+ * A thread-safe set of environment variables
+ *
+ * \since This struct is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetEnvironment
+ * \sa SDL_CleanupEnvironment
+ * \sa SDL_CreateEnvironment
+ * \sa SDL_GetEnvironmentVariable
+ * \sa SDL_GetEnvironmentVariables
+ * \sa SDL_SetEnvironmentVariable
+ * \sa SDL_UnsetEnvironmentVariable
+ * \sa SDL_DestroyEnvironment
+ */
+typedef struct SDL_Environment SDL_Environment;
+
+/**
+ * Get the process environment.
+ *
+ * This is initialized at application start and is not affected by setenv() and unsetenv() calls after that point. Use SDL_SetEnvironmentVariable() and SDL_UnsetEnvironmentVariable() if you want to modify this environment.
+ *
+ * \returns a pointer to the environment for the process or NULL on failure; call SDL_GetError()
+ *          for more information.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_CleanupEnvironment
+ * \sa SDL_GetEnvironmentVariable
+ * \sa SDL_GetEnvironmentVariables
+ * \sa SDL_SetEnvironmentVariable
+ * \sa SDL_UnsetEnvironmentVariable
+ */
+extern SDL_DECLSPEC SDL_Environment * SDLCALL SDL_GetEnvironment(void);
+
+/**
+ * Cleanup the process environment.
+ *
+ * This is called during SDL_Quit() to free the process environment. If SDL_GetEnvironment() is called afterwards, it will automatically create a new environment copied from the C runtime environment.
+ *
+ * \threadsafety This function is not thread-safe.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetEnvironment
+ */
+extern SDL_DECLSPEC void SDLCALL SDL_CleanupEnvironment(void);
+
+/**
+ * Create a set of environment variables
+ *
+ * \param empty SDL_TRUE to create an empty environment, SDL_FALSE to initialize it from the C runtime environment.
+ * \returns a pointer to the new environment or NULL on failure; call SDL_GetError()
+ *          for more information.
+ *
+ * \threadsafety If `empty` is SDL_TRUE, it is safe to call this function from any thread, otherwise it is safe if no other threads are calling setenv() or unsetenv()
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetEnvironmentVariable
+ * \sa SDL_GetEnvironmentVariables
+ * \sa SDL_SetEnvironmentVariable
+ * \sa SDL_UnsetEnvironmentVariable
+ * \sa SDL_DestroyEnvironment
+ */
+extern SDL_DECLSPEC SDL_Environment * SDLCALL SDL_CreateEnvironment(SDL_bool empty);
+
+/**
+ * Get the value of a variable in the environment.
+ *
+ * \param env the environment to query.
+ * \param name the name of the variable to get.
+ * \returns a pointer to the value of the variable or NULL if it can't be found.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetEnvironment
+ * \sa SDL_CreateEnvironment
+ * \sa SDL_GetEnvironmentVariables
+ * \sa SDL_SetEnvironmentVariable
+ * \sa SDL_UnsetEnvironmentVariable
+ */
+extern SDL_DECLSPEC const char * SDLCALL SDL_GetEnvironmentVariable(SDL_Environment *env, const char *name);
+
+/**
+ * Get all variables in the environment.
+ *
+ * \param env the environment to query.
+ * \returns a NULL terminated array of pointers to environment variables in the form "variable=value" or NULL on
+ *          failure; call SDL_GetError() for more information. This is a
+ *          single allocation that should be freed with SDL_free() when it is
+ *          no longer needed.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetEnvironment
+ * \sa SDL_CreateEnvironment
+ * \sa SDL_GetEnvironmentVariables
+ * \sa SDL_SetEnvironmentVariable
+ * \sa SDL_UnsetEnvironmentVariable
+ */
+extern SDL_DECLSPEC char ** SDLCALL SDL_GetEnvironmentVariables(SDL_Environment *env);
+
+/**
+ * Set the value of a variable in the environment.
+ *
+ * \param env the environment to modify.
+ * \param name the name of the variable to set.
+ * \param value the value of the variable to set.
+ * \param overwrite SDL_TRUE to overwrite the variable if it exists, SDL_FALSE to return success without setting the variable if it already exists.
+ * \returns SDL_TRUE on success or SDL_FALSE on failure; call SDL_GetError()
+ *          for more information.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetEnvironment
+ * \sa SDL_CreateEnvironment
+ * \sa SDL_GetEnvironmentVariable
+ * \sa SDL_GetEnvironmentVariables
+ * \sa SDL_UnsetEnvironmentVariable
+ */
+extern SDL_DECLSPEC SDL_bool SDLCALL SDL_SetEnvironmentVariable(SDL_Environment *env, const char *name, const char *value, SDL_bool overwrite);
+
+/**
+ * Clear a variable from the environment.
+ *
+ * \param env the environment to modify.
+ * \param name the name of the variable to unset.
+ * \returns SDL_TRUE on success or SDL_FALSE on failure; call SDL_GetError()
+ *          for more information.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetEnvironment
+ * \sa SDL_CreateEnvironment
+ * \sa SDL_GetEnvironmentVariable
+ * \sa SDL_GetEnvironmentVariables
+ * \sa SDL_SetEnvironmentVariable
+ * \sa SDL_UnsetEnvironmentVariable
+ */
+extern SDL_DECLSPEC SDL_bool SDLCALL SDL_UnsetEnvironmentVariable(SDL_Environment *env, const char *name);
+
+/**
+ * Destroy a set of environment variables.
+ *
+ * \param env the environment to destroy.
+ *
+ * \threadsafety It is safe to call this function from any thread, as long as the environment is no longer in use.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_CreateEnvironment
+ */
+extern SDL_DECLSPEC void SDLCALL SDL_DestroyEnvironment(SDL_Environment *env);
+
+/**
+ * Get the value of a variable in the environment.
+ *
+ * \param name the name of the variable to get.
+ * \returns a pointer to the value of the variable or NULL if it can't be found.
+ *
+ * \threadsafety This function is not thread safe, consider using SDL_GetEnvironmentVariable() instead.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetEnvironmentVariable
+ */
+extern SDL_DECLSPEC const char * SDLCALL SDL_getenv_unsafe(const char *name);
+
+/**
+ * Set the value of a variable in the environment.
+ *
+ * \param name the name of the variable to set.
+ * \param value the value of the variable to set.
+ * \param overwrite 1 to overwrite the variable if it exists, 0 to return success without setting the variable if it already exists.
+ * \returns 0 on success, -1 on error.
+ *
+ * \threadsafety This function is not thread safe, consider using SDL_SetEnvironmentVariable() instead.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_SetEnvironmentVariable
+ */
+extern SDL_DECLSPEC int SDLCALL SDL_setenv_unsafe(const char *name, const char *value, int overwrite);
+
+/**
+ * Clear a variable from the environment.
+ *
+ * \param name the name of the variable to unset.
+ * \returns 0 on success, -1 on error.
+ *
+ * \threadsafety This function is not thread safe, consider using SDL_UnsetEnvironmentVariable() instead..
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_UnsetEnvironmentVariable
+ */
+extern SDL_DECLSPEC int SDLCALL SDL_unsetenv_unsafe(const char *name);
 
 typedef int (SDLCALL *SDL_CompareCallback)(const void *a, const void *b);
 extern SDL_DECLSPEC void SDLCALL SDL_qsort(void *base, size_t nmemb, size_t size, SDL_CompareCallback compare);

+ 2 - 0
src/SDL.c

@@ -255,12 +255,14 @@ void SDL_InitMainThread(void)
     SDL_InitLog();
     SDL_InitProperties();
     SDL_GetGlobalProperties();
+    SDL_GetEnvironment();
     SDL_InitHints();
 }
 
 static void SDL_QuitMainThread(void)
 {
     SDL_QuitHints();
+    SDL_CleanupEnvironment();
     SDL_QuitProperties();
     SDL_QuitLog();
     SDL_QuitFilesystem();

+ 5 - 0
src/SDL_hashtable.c

@@ -318,6 +318,11 @@ bool SDL_KeyMatchID(const void *a, const void *b, void *unused)
     return false;
 }
 
+void SDL_NukeFreeKey(const void *key, const void *value, void *unused)
+{
+    SDL_free((void *)key);
+}
+
 void SDL_NukeFreeValue(const void *key, const void *value, void *unused)
 {
     SDL_free((void *)value);

+ 1 - 0
src/SDL_hashtable.h

@@ -55,6 +55,7 @@ extern bool SDL_KeyMatchString(const void *a, const void *b, void *unused);
 extern Uint32 SDL_HashID(const void *key, void *unused);
 extern bool SDL_KeyMatchID(const void *a, const void *b, void *unused);
 
+extern void SDL_NukeFreeKey(const void *key, const void *value, void *unused);
 extern void SDL_NukeFreeValue(const void *key, const void *value, void *unused);
 
 #endif // SDL_hashtable_h_

+ 4 - 4
src/SDL_hints.c

@@ -72,7 +72,7 @@ SDL_bool SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPr
         return SDL_InvalidParamError("name");
     }
 
-    const char *env = SDL_getenv(name);
+    const char *env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), name);
     if (env && (priority < SDL_HINT_OVERRIDE)) {
         return SDL_SetError("An environment variable is taking priority");
     }
@@ -126,7 +126,7 @@ SDL_bool SDL_ResetHint(const char *name)
         return SDL_InvalidParamError("name");
     }
 
-    const char *env = SDL_getenv(name);
+    const char *env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), name);
 
     const SDL_PropertiesID hints = GetHintProperties(false);
     if (!hints) {
@@ -165,7 +165,7 @@ static void SDLCALL ResetHintsCallback(void *userdata, SDL_PropertiesID hints, c
         return;  // uh...okay.
     }
 
-    const char *env = SDL_getenv(name);
+    const char *env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), name);
     if ((!env && hint->value) || (env && !hint->value) || (env && SDL_strcmp(env, hint->value) != 0)) {
         SDL_HintWatch *entry = hint->callbacks;
         while (entry) {
@@ -196,7 +196,7 @@ const char *SDL_GetHint(const char *name)
         return NULL;
     }
 
-    const char *result = SDL_getenv(name);
+    const char *result = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), name);
 
     const SDL_PropertiesID hints = GetHintProperties(false);
     if (hints) {

+ 1 - 1
src/audio/SDL_audiodev.c

@@ -87,7 +87,7 @@ static void SDL_EnumUnixAudioDevices_Internal(const bool recording, const bool c
     }
 
     // Figure out what our audio device is
-    audiodev = SDL_getenv("AUDIODEV");
+    audiodev = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "AUDIODEV");
     if (!audiodev) {
         if (classic) {
             audiodev = SDL_PATH_DEV_AUDIO;

+ 2 - 1
src/core/android/SDL_android.c

@@ -1524,7 +1524,8 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
     const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
     const char *utfvalue = (*env)->GetStringUTFChars(env, value, NULL);
 
-    SDL_setenv(utfname, utfvalue, 1);
+    // This is only called at startup, to initialize the environment
+    SDL_setenv_unsafe(utfname, utfvalue, 1);
 
     (*env)->ReleaseStringUTFChars(env, name, utfname);
     (*env)->ReleaseStringUTFChars(env, value, utfvalue);

+ 5 - 5
src/core/linux/SDL_ibus.c

@@ -331,14 +331,14 @@ static char *IBus_GetDBusAddressFilename(void)
     }
 
     // Use this environment variable if it exists.
-    addr = SDL_getenv("IBUS_ADDRESS");
+    addr = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "IBUS_ADDRESS");
     if (addr && *addr) {
         return SDL_strdup(addr);
     }
 
     /* Otherwise, we have to get the hostname, display, machine id, config dir
        and look up the address from a filepath using all those bits, eek. */
-    disp_env = SDL_getenv("DISPLAY");
+    disp_env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "DISPLAY");
 
     if (!disp_env || !*disp_env) {
         display = SDL_strdup(":0.0");
@@ -363,7 +363,7 @@ static char *IBus_GetDBusAddressFilename(void)
     }
 
     if (!*host) {
-        const char *session = SDL_getenv("XDG_SESSION_TYPE");
+        const char *session = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_SESSION_TYPE");
         if (session && SDL_strcmp(session, "wayland") == 0) {
             host = "unix-wayland";
         } else {
@@ -373,11 +373,11 @@ static char *IBus_GetDBusAddressFilename(void)
 
     SDL_memset(config_dir, 0, sizeof(config_dir));
 
-    conf_env = SDL_getenv("XDG_CONFIG_HOME");
+    conf_env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_CONFIG_HOME");
     if (conf_env && *conf_env) {
         SDL_strlcpy(config_dir, conf_env, sizeof(config_dir));
     } else {
-        const char *home_env = SDL_getenv("HOME");
+        const char *home_env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
         if (!home_env || !*home_env) {
             SDL_free(display);
             return NULL;

+ 2 - 2
src/core/linux/SDL_ime.c

@@ -44,8 +44,8 @@ static void InitIME(void)
 {
     static bool inited = false;
 #ifdef HAVE_FCITX
-    const char *im_module = SDL_getenv("SDL_IM_MODULE");
-    const char *xmodifiers = SDL_getenv("XMODIFIERS");
+    const char *im_module = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_IM_MODULE");
+    const char *xmodifiers = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XMODIFIERS");
 #endif
 
     if (inited == true) {

+ 3 - 1
src/core/linux/SDL_sandbox.c

@@ -33,7 +33,9 @@ SDL_Sandbox SDL_DetectSandbox(void)
 
     /* For Snap, we check multiple variables because they might be set for
      * unrelated reasons. This is the same thing WebKitGTK does. */
-    if (SDL_getenv("SNAP") != NULL && SDL_getenv("SNAP_NAME") != NULL && SDL_getenv("SNAP_REVISION") != NULL) {
+    if (SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SNAP") != NULL &&
+        SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SNAP_NAME") != NULL &&
+        SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SNAP_REVISION") != NULL) {
         return SDL_SANDBOX_SNAP;
     }
 

+ 6 - 6
src/dialog/unix/SDL_zenitydialog.c

@@ -229,12 +229,12 @@ static void run_zenity(zenityArgs* arg_struct)
 
         /* Recent versions of Zenity have different exit codes, but picks up
           different codes from the environment */
-        SDL_setenv("ZENITY_OK", "0", 1);
-        SDL_setenv("ZENITY_CANCEL", "1", 1);
-        SDL_setenv("ZENITY_ESC", "1", 1);
-        SDL_setenv("ZENITY_EXTRA", "2", 1);
-        SDL_setenv("ZENITY_ERROR", "2", 1);
-        SDL_setenv("ZENITY_TIMEOUT", "2", 1);
+        SDL_setenv_unsafe("ZENITY_OK", "0", 1);
+        SDL_setenv_unsafe("ZENITY_CANCEL", "1", 1);
+        SDL_setenv_unsafe("ZENITY_ESC", "1", 1);
+        SDL_setenv_unsafe("ZENITY_EXTRA", "2", 1);
+        SDL_setenv_unsafe("ZENITY_ERROR", "2", 1);
+        SDL_setenv_unsafe("ZENITY_TIMEOUT", "2", 1);
 
         execv(args[0], args);
 

+ 2 - 2
src/dynapi/SDL_dynapi.c

@@ -351,7 +351,7 @@ static Sint32 initialize_jumptable(Uint32 apiver, void *table, Uint32 tablesize)
 // Init our jump table first.
 #if ENABLE_SDL_CALL_LOGGING
     {
-        const char *env = SDL_getenv_REAL("SDL_DYNAPI_LOG_CALLS");
+        const char *env = SDL_getenv_unsafe_REAL("SDL_DYNAPI_LOG_CALLS");
         const SDL_bool log_calls = (env && SDL_atoi_REAL(env));
         if (log_calls) {
 #define SDL_DYNAPI_PROC(rc, fn, params, args, ret) jump_table.fn = fn##_LOGSDLCALLS;
@@ -461,7 +461,7 @@ extern SDL_NORETURN void SDL_ExitProcess(int exitcode);
 
 static void SDL_InitDynamicAPILocked(void)
 {
-    const char *libname = SDL_getenv_REAL(SDL_DYNAMIC_API_ENVVAR);
+    const char *libname = SDL_getenv_unsafe_REAL(SDL_DYNAMIC_API_ENVVAR);
     SDL_DYNAPI_ENTRYFN entry = NULL; // funcs from here by default.
     SDL_bool use_internal = SDL_TRUE;
 

+ 11 - 3
src/dynapi/SDL_dynapi.sym

@@ -52,6 +52,7 @@ SDL3_0.0.0 {
     SDL_BroadcastCondition;
     SDL_CaptureMouse;
     SDL_ClaimWindowForGPUDevice;
+    SDL_CleanupEnvironment;
     SDL_CleanupTLS;
     SDL_ClearAudioStream;
     SDL_ClearClipboardData;
@@ -84,6 +85,7 @@ SDL3_0.0.0 {
     SDL_CreateCondition;
     SDL_CreateCursor;
     SDL_CreateDirectory;
+    SDL_CreateEnvironment;
     SDL_CreateGPUBuffer;
     SDL_CreateGPUComputePipeline;
     SDL_CreateGPUDevice;
@@ -125,6 +127,7 @@ SDL3_0.0.0 {
     SDL_DestroyAudioStream;
     SDL_DestroyCondition;
     SDL_DestroyCursor;
+    SDL_DestroyEnvironment;
     SDL_DestroyGPUDevice;
     SDL_DestroyHapticEffect;
     SDL_DestroyMutex;
@@ -276,6 +279,9 @@ SDL3_0.0.0 {
     SDL_GetDisplayProperties;
     SDL_GetDisplayUsableBounds;
     SDL_GetDisplays;
+    SDL_GetEnvironment;
+    SDL_GetEnvironmentVariable;
+    SDL_GetEnvironmentVariables;
     SDL_GetError;
     SDL_GetEventFilter;
     SDL_GetFloatProperty;
@@ -785,6 +791,7 @@ SDL3_0.0.0 {
     SDL_SetClipboardData;
     SDL_SetClipboardText;
     SDL_SetCursor;
+    SDL_SetEnvironmentVariable;
     SDL_SetError;
     SDL_SetEventEnabled;
     SDL_SetEventFilter;
@@ -933,6 +940,7 @@ SDL3_0.0.0 {
     SDL_UnlockTexture;
     SDL_UnmapGPUTransferBuffer;
     SDL_UnregisterApp;
+    SDL_UnsetEnvironmentVariable;
     SDL_UpdateGamepads;
     SDL_UpdateHapticEffect;
     SDL_UpdateJoysticks;
@@ -1020,7 +1028,7 @@ SDL3_0.0.0 {
     SDL_fmod;
     SDL_fmodf;
     SDL_free;
-    SDL_getenv;
+    SDL_getenv_unsafe;
     SDL_hid_ble_scan;
     SDL_hid_close;
     SDL_hid_device_change_count;
@@ -1095,7 +1103,7 @@ SDL3_0.0.0 {
     SDL_roundf;
     SDL_scalbn;
     SDL_scalbnf;
-    SDL_setenv;
+    SDL_setenv_unsafe;
     SDL_sin;
     SDL_sinf;
     SDL_snprintf;
@@ -1138,7 +1146,7 @@ SDL3_0.0.0 {
     SDL_uitoa;
     SDL_ulltoa;
     SDL_ultoa;
-    SDL_unsetenv;
+    SDL_unsetenv_unsafe;
     SDL_utf8strlcpy;
     SDL_utf8strlen;
     SDL_utf8strnlen;

+ 11 - 3
src/dynapi/SDL_dynapi_overrides.h

@@ -77,6 +77,7 @@
 #define SDL_BroadcastCondition SDL_BroadcastCondition_REAL
 #define SDL_CaptureMouse SDL_CaptureMouse_REAL
 #define SDL_ClaimWindowForGPUDevice SDL_ClaimWindowForGPUDevice_REAL
+#define SDL_CleanupEnvironment SDL_CleanupEnvironment_REAL
 #define SDL_CleanupTLS SDL_CleanupTLS_REAL
 #define SDL_ClearAudioStream SDL_ClearAudioStream_REAL
 #define SDL_ClearClipboardData SDL_ClearClipboardData_REAL
@@ -109,6 +110,7 @@
 #define SDL_CreateCondition SDL_CreateCondition_REAL
 #define SDL_CreateCursor SDL_CreateCursor_REAL
 #define SDL_CreateDirectory SDL_CreateDirectory_REAL
+#define SDL_CreateEnvironment SDL_CreateEnvironment_REAL
 #define SDL_CreateGPUBuffer SDL_CreateGPUBuffer_REAL
 #define SDL_CreateGPUComputePipeline SDL_CreateGPUComputePipeline_REAL
 #define SDL_CreateGPUDevice SDL_CreateGPUDevice_REAL
@@ -150,6 +152,7 @@
 #define SDL_DestroyAudioStream SDL_DestroyAudioStream_REAL
 #define SDL_DestroyCondition SDL_DestroyCondition_REAL
 #define SDL_DestroyCursor SDL_DestroyCursor_REAL
+#define SDL_DestroyEnvironment SDL_DestroyEnvironment_REAL
 #define SDL_DestroyGPUDevice SDL_DestroyGPUDevice_REAL
 #define SDL_DestroyHapticEffect SDL_DestroyHapticEffect_REAL
 #define SDL_DestroyMutex SDL_DestroyMutex_REAL
@@ -301,6 +304,9 @@
 #define SDL_GetDisplayProperties SDL_GetDisplayProperties_REAL
 #define SDL_GetDisplayUsableBounds SDL_GetDisplayUsableBounds_REAL
 #define SDL_GetDisplays SDL_GetDisplays_REAL
+#define SDL_GetEnvironment SDL_GetEnvironment_REAL
+#define SDL_GetEnvironmentVariable SDL_GetEnvironmentVariable_REAL
+#define SDL_GetEnvironmentVariables SDL_GetEnvironmentVariables_REAL
 #define SDL_GetError SDL_GetError_REAL
 #define SDL_GetEventFilter SDL_GetEventFilter_REAL
 #define SDL_GetFloatProperty SDL_GetFloatProperty_REAL
@@ -810,6 +816,7 @@
 #define SDL_SetClipboardData SDL_SetClipboardData_REAL
 #define SDL_SetClipboardText SDL_SetClipboardText_REAL
 #define SDL_SetCursor SDL_SetCursor_REAL
+#define SDL_SetEnvironmentVariable SDL_SetEnvironmentVariable_REAL
 #define SDL_SetError    SDL_SetError_REAL
 #define SDL_SetEventEnabled SDL_SetEventEnabled_REAL
 #define SDL_SetEventFilter SDL_SetEventFilter_REAL
@@ -958,6 +965,7 @@
 #define SDL_UnlockTexture SDL_UnlockTexture_REAL
 #define SDL_UnmapGPUTransferBuffer SDL_UnmapGPUTransferBuffer_REAL
 #define SDL_UnregisterApp SDL_UnregisterApp_REAL
+#define SDL_UnsetEnvironmentVariable SDL_UnsetEnvironmentVariable_REAL
 #define SDL_UpdateGamepads SDL_UpdateGamepads_REAL
 #define SDL_UpdateHapticEffect SDL_UpdateHapticEffect_REAL
 #define SDL_UpdateJoysticks SDL_UpdateJoysticks_REAL
@@ -1045,7 +1053,7 @@
 #define SDL_fmod SDL_fmod_REAL
 #define SDL_fmodf SDL_fmodf_REAL
 #define SDL_free SDL_free_REAL
-#define SDL_getenv SDL_getenv_REAL
+#define SDL_getenv_unsafe SDL_getenv_unsafe_REAL
 #define SDL_hid_ble_scan SDL_hid_ble_scan_REAL
 #define SDL_hid_close SDL_hid_close_REAL
 #define SDL_hid_device_change_count SDL_hid_device_change_count_REAL
@@ -1120,7 +1128,7 @@
 #define SDL_roundf SDL_roundf_REAL
 #define SDL_scalbn SDL_scalbn_REAL
 #define SDL_scalbnf SDL_scalbnf_REAL
-#define SDL_setenv SDL_setenv_REAL
+#define SDL_setenv_unsafe SDL_setenv_unsafe_REAL
 #define SDL_sin SDL_sin_REAL
 #define SDL_sinf SDL_sinf_REAL
 #define SDL_snprintf    SDL_snprintf_REAL
@@ -1163,7 +1171,7 @@
 #define SDL_uitoa SDL_uitoa_REAL
 #define SDL_ulltoa SDL_ulltoa_REAL
 #define SDL_ultoa SDL_ultoa_REAL
-#define SDL_unsetenv SDL_unsetenv_REAL
+#define SDL_unsetenv_unsafe SDL_unsetenv_unsafe_REAL
 #define SDL_utf8strlcpy SDL_utf8strlcpy_REAL
 #define SDL_utf8strlen SDL_utf8strlen_REAL
 #define SDL_utf8strnlen SDL_utf8strnlen_REAL

+ 11 - 3
src/dynapi/SDL_dynapi_procs.h

@@ -97,6 +97,7 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_BlitSurfaceUncheckedScaled,(SDL_Surface *a, const S
 SDL_DYNAPI_PROC(void,SDL_BroadcastCondition,(SDL_Condition *a),(a),)
 SDL_DYNAPI_PROC(SDL_bool,SDL_CaptureMouse,(SDL_bool a),(a),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_ClaimWindowForGPUDevice,(SDL_GPUDevice *a, SDL_Window *b),(a,b),return)
+SDL_DYNAPI_PROC(void,SDL_CleanupEnvironment,(void),(),)
 SDL_DYNAPI_PROC(void,SDL_CleanupTLS,(void),(),)
 SDL_DYNAPI_PROC(SDL_bool,SDL_ClearAudioStream,(SDL_AudioStream *a),(a),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_ClearClipboardData,(void),(),return)
@@ -129,6 +130,7 @@ SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateColorCursor,(SDL_Surface *a, int b, int c)
 SDL_DYNAPI_PROC(SDL_Condition*,SDL_CreateCondition,(void),(),return)
 SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateCursor,(const Uint8 *a, const Uint8 *b, int c, int d, int e, int f),(a,b,c,d,e,f),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_CreateDirectory,(const char *a),(a),return)
+SDL_DYNAPI_PROC(SDL_Environment*,SDL_CreateEnvironment,(SDL_bool a),(a),return)
 SDL_DYNAPI_PROC(SDL_GPUBuffer*,SDL_CreateGPUBuffer,(SDL_GPUDevice *a, const SDL_GPUBufferCreateInfo* b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_GPUComputePipeline*,SDL_CreateGPUComputePipeline,(SDL_GPUDevice *a, const SDL_GPUComputePipelineCreateInfo *b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_GPUDevice*,SDL_CreateGPUDevice,(SDL_GPUShaderFormat a, SDL_bool b, const char *c),(a,b,c),return)
@@ -170,6 +172,7 @@ SDL_DYNAPI_PROC(void,SDL_DelayNS,(Uint64 a),(a),)
 SDL_DYNAPI_PROC(void,SDL_DestroyAudioStream,(SDL_AudioStream *a),(a),)
 SDL_DYNAPI_PROC(void,SDL_DestroyCondition,(SDL_Condition *a),(a),)
 SDL_DYNAPI_PROC(void,SDL_DestroyCursor,(SDL_Cursor *a),(a),)
+SDL_DYNAPI_PROC(void,SDL_DestroyEnvironment,(SDL_Environment *a),(a),)
 SDL_DYNAPI_PROC(void,SDL_DestroyGPUDevice,(SDL_GPUDevice *a),(a),)
 SDL_DYNAPI_PROC(void,SDL_DestroyHapticEffect,(SDL_Haptic *a, int b),(a,b),)
 SDL_DYNAPI_PROC(void,SDL_DestroyMutex,(SDL_Mutex *a),(a),)
@@ -321,6 +324,9 @@ SDL_DYNAPI_PROC(const char*,SDL_GetDisplayName,(SDL_DisplayID a),(a),return)
 SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetDisplayProperties,(SDL_DisplayID a),(a),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_GetDisplayUsableBounds,(SDL_DisplayID a, SDL_Rect *b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_DisplayID*,SDL_GetDisplays,(int *a),(a),return)
+SDL_DYNAPI_PROC(SDL_Environment*,SDL_GetEnvironment,(void),(),return)
+SDL_DYNAPI_PROC(const char*,SDL_GetEnvironmentVariable,(SDL_Environment *a, const char *b),(a,b),return)
+SDL_DYNAPI_PROC(char**,SDL_GetEnvironmentVariables,(SDL_Environment *a),(a),return)
 SDL_DYNAPI_PROC(const char*,SDL_GetError,(void),(),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_GetEventFilter,(SDL_EventFilter *a, void **b),(a,b),return)
 SDL_DYNAPI_PROC(float,SDL_GetFloatProperty,(SDL_PropertiesID a, const char *b, float c),(a,b,c),return)
@@ -821,6 +827,7 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_SetBooleanProperty,(SDL_PropertiesID a, const char
 SDL_DYNAPI_PROC(SDL_bool,SDL_SetClipboardData,(SDL_ClipboardDataCallback a, SDL_ClipboardCleanupCallback b, void *c, const char **d, size_t e),(a,b,c,d,e),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_SetClipboardText,(const char *a),(a),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_SetCursor,(SDL_Cursor *a),(a),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_SetEnvironmentVariable,(SDL_Environment *a, const char *b, const char *c, SDL_bool d),(a,b,c,d),return)
 SDL_DYNAPI_PROC(void,SDL_SetEventEnabled,(Uint32 a, SDL_bool b),(a,b),)
 SDL_DYNAPI_PROC(void,SDL_SetEventFilter,(SDL_EventFilter a, void *b),(a,b),)
 SDL_DYNAPI_PROC(SDL_bool,SDL_SetFloatProperty,(SDL_PropertiesID a, const char *b, float c),(a,b,c),return)
@@ -968,6 +975,7 @@ SDL_DYNAPI_PROC(void,SDL_UnlockSurface,(SDL_Surface *a),(a),)
 SDL_DYNAPI_PROC(void,SDL_UnlockTexture,(SDL_Texture *a),(a),)
 SDL_DYNAPI_PROC(void,SDL_UnmapGPUTransferBuffer,(SDL_GPUDevice *a, SDL_GPUTransferBuffer *b),(a,b),)
 SDL_DYNAPI_PROC(void,SDL_UnregisterApp,(void),(),)
+SDL_DYNAPI_PROC(SDL_bool,SDL_UnsetEnvironmentVariable,(SDL_Environment *a, const char *b),(a,b),return)
 SDL_DYNAPI_PROC(void,SDL_UpdateGamepads,(void),(),)
 SDL_DYNAPI_PROC(SDL_bool,SDL_UpdateHapticEffect,(SDL_Haptic *a, int b, const SDL_HapticEffect *c),(a,b,c),return)
 SDL_DYNAPI_PROC(void,SDL_UpdateJoysticks,(void),(),)
@@ -1054,7 +1062,7 @@ SDL_DYNAPI_PROC(float,SDL_floorf,(float a),(a),return)
 SDL_DYNAPI_PROC(double,SDL_fmod,(double a, double b),(a,b),return)
 SDL_DYNAPI_PROC(float,SDL_fmodf,(float a, float b),(a,b),return)
 SDL_DYNAPI_PROC(void,SDL_free,(void *a),(a),)
-SDL_DYNAPI_PROC(const char*,SDL_getenv,(const char *a),(a),return)
+SDL_DYNAPI_PROC(const char*,SDL_getenv_unsafe,(const char *a),(a),return)
 SDL_DYNAPI_PROC(void,SDL_hid_ble_scan,(SDL_bool a),(a),)
 SDL_DYNAPI_PROC(int,SDL_hid_close,(SDL_hid_device *a),(a),return)
 SDL_DYNAPI_PROC(Uint32,SDL_hid_device_change_count,(void),(),return)
@@ -1129,7 +1137,7 @@ SDL_DYNAPI_PROC(double,SDL_round,(double a),(a),return)
 SDL_DYNAPI_PROC(float,SDL_roundf,(float a),(a),return)
 SDL_DYNAPI_PROC(double,SDL_scalbn,(double a, int b),(a,b),return)
 SDL_DYNAPI_PROC(float,SDL_scalbnf,(float a, int b),(a,b),return)
-SDL_DYNAPI_PROC(int,SDL_setenv,(const char *a, const char *b, int c),(a,b,c),return)
+SDL_DYNAPI_PROC(int,SDL_setenv_unsafe,(const char *a, const char *b, int c),(a,b,c),return)
 SDL_DYNAPI_PROC(double,SDL_sin,(double a),(a),return)
 SDL_DYNAPI_PROC(float,SDL_sinf,(float a),(a),return)
 SDL_DYNAPI_PROC(double,SDL_sqrt,(double a),(a),return)
@@ -1169,7 +1177,7 @@ SDL_DYNAPI_PROC(float,SDL_truncf,(float a),(a),return)
 SDL_DYNAPI_PROC(char*,SDL_uitoa,(unsigned int a, char *b, int c),(a,b,c),return)
 SDL_DYNAPI_PROC(char*,SDL_ulltoa,(unsigned long long a, char *b, int c),(a,b,c),return)
 SDL_DYNAPI_PROC(char*,SDL_ultoa,(unsigned long a, char *b, int c),(a,b,c),return)
-SDL_DYNAPI_PROC(int,SDL_unsetenv,(const char *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_unsetenv_unsafe,(const char *a),(a),return)
 SDL_DYNAPI_PROC(size_t,SDL_utf8strlcpy,(SDL_OUT_Z_CAP(c) char *a, const char *b, size_t c),(a,b,c),return)
 SDL_DYNAPI_PROC(size_t,SDL_utf8strlen,(const char *a),(a),return)
 SDL_DYNAPI_PROC(size_t,SDL_utf8strnlen,(const char *a, size_t b),(a,b),return)

+ 1 - 1
src/filesystem/cocoa/SDL_sysfilesystem.m

@@ -144,7 +144,7 @@ char *SDL_SYS_GetUserFolder(SDL_Folder folder)
 
         switch (folder) {
         case SDL_FOLDER_HOME:
-            base = SDL_getenv("HOME");
+            base = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
 
             if (!base) {
                 SDL_SetError("No $HOME environment variable available");

+ 1 - 1
src/filesystem/emscripten/SDL_sysfilesystem.c

@@ -93,7 +93,7 @@ char *SDL_SYS_GetUserFolder(SDL_Folder folder)
         return NULL;
     }
 
-    home = SDL_getenv("HOME");
+    home = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
     if (!home) {
         SDL_SetError("No $HOME environment variable available");
         return NULL;

+ 2 - 2
src/filesystem/haiku/SDL_sysfilesystem.cc

@@ -68,7 +68,7 @@ char *SDL_SYS_GetBasePath(void)
 char *SDL_SYS_GetPrefPath(const char *org, const char *app)
 {
     // !!! FIXME: is there a better way to do this?
-    const char *home = SDL_getenv("HOME");
+    const char *home = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
     const char *append = "/config/settings/";
     size_t len = SDL_strlen(home);
 
@@ -102,7 +102,7 @@ char *SDL_SYS_GetUserFolder(SDL_Folder folder)
     const char *home = NULL;
     char *result;
 
-    home = SDL_getenv("HOME");
+    home = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
     if (!home) {
         SDL_SetError("No $HOME environment variable available");
         return NULL;

+ 8 - 8
src/filesystem/unix/SDL_sysfilesystem.c

@@ -74,7 +74,7 @@ static char *readSymLink(const char *path)
 #ifdef SDL_PLATFORM_OPENBSD
 static char *search_path_for_binary(const char *bin)
 {
-    const char *envr_real = SDL_getenv("PATH");
+    const char *envr_real = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "PATH");
     char *envr;
     size_t alloc_size;
     char *exe = NULL;
@@ -163,7 +163,7 @@ char *SDL_SYS_GetBasePath(void)
             exe = search_path_for_binary(cmdline[0]);
         } else {
             if (exe && *exe == '.') {
-                const char *pwd = SDL_getenv("PWD");
+                const char *pwd = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "PWD");
                 if (pwd && *pwd) {
                     SDL_asprintf(&pwddst, "%s/%s", pwd, exe);
                 }
@@ -265,7 +265,7 @@ char *SDL_SYS_GetPrefPath(const char *org, const char *app)
      *
      * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
      */
-    const char *envr = SDL_getenv("XDG_DATA_HOME");
+    const char *envr = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_DATA_HOME");
     const char *append;
     char *result = NULL;
     char *ptr = NULL;
@@ -281,7 +281,7 @@ char *SDL_SYS_GetPrefPath(const char *org, const char *app)
 
     if (!envr) {
         // You end up with "$HOME/.local/share/Game Name 2"
-        envr = SDL_getenv("HOME");
+        envr = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
         if (!envr) {
             // we could take heroic measures with /etc/passwd, but oh well.
             SDL_SetError("neither XDG_DATA_HOME nor HOME environment is set");
@@ -368,12 +368,12 @@ static char *xdg_user_dir_lookup_with_fallback (const char *type, const char *fa
   int relative;
   size_t l;
 
-  home_dir = SDL_getenv ("HOME");
+  home_dir = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
 
   if (!home_dir)
     goto error;
 
-  config_home = SDL_getenv ("XDG_CONFIG_HOME");
+  config_home = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_CONFIG_HOME");
   if (!config_home || config_home[0] == 0)
     {
       l = SDL_strlen (home_dir) + SDL_strlen ("/.config/user-dirs.dirs") + 1;
@@ -495,7 +495,7 @@ static char *xdg_user_dir_lookup (const char *type)
     if (dir)
         return dir;
 
-    home_dir = SDL_getenv("HOME");
+    home_dir = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
 
     if (!home_dir)
         return NULL;
@@ -533,7 +533,7 @@ char *SDL_SYS_GetUserFolder(SDL_Folder folder)
     */
     switch(folder) {
     case SDL_FOLDER_HOME:
-        param = SDL_getenv("HOME");
+        param = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "HOME");
 
         if (!param) {
             SDL_SetError("No $HOME environment variable available");

+ 1 - 1
src/gpu/vulkan/SDL_gpu_vulkan.c

@@ -11591,7 +11591,7 @@ static Uint8 VULKAN_INTERNAL_CreateLogicalDevice(
 static void VULKAN_INTERNAL_LoadEntryPoints(void)
 {
     // Required for MoltenVK support
-    SDL_setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
+    SDL_setenv_unsafe("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
 
     // Load Vulkan entry points
     if (!SDL_Vulkan_LoadLibrary(NULL)) {

+ 1 - 1
src/haptic/SDL_haptic.c

@@ -537,7 +537,7 @@ SDL_bool SDL_SetHapticGain(SDL_Haptic *haptic, int gain)
     }
 
     // The user can use an environment variable to override the max gain.
-    env = SDL_getenv("SDL_HAPTIC_GAIN_MAX");
+    env = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_HAPTIC_GAIN_MAX");
     if (env) {
         max_gain = SDL_atoi(env);
 

+ 2 - 2
src/locale/unix/SDL_syslocale.c

@@ -78,13 +78,13 @@ bool SDL_SYS_GetPreferredLocales(char *buf, size_t buflen)
     *tmp = '\0';
 
     // LANG is the primary locale (maybe)
-    envr = SDL_getenv("LANG");
+    envr = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "LANG");
     if (envr) {
         SDL_strlcpy(tmp, envr, buflen);
     }
 
     // fallback languages
-    envr = SDL_getenv("LANGUAGE");
+    envr = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "LANGUAGE");
     if (envr) {
         if (*tmp) {
             SDL_strlcat(tmp, ":", buflen);

+ 1 - 1
src/main/emscripten/SDL_sysmain_runapp.c

@@ -48,7 +48,7 @@ int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserv
                 _free(cvalue);
             }
         }
-    }, SDL_setenv);
+    }, SDL_setenv_unsafe);
 
     return mainFunction(argc, argv);
 }

+ 2 - 2
src/misc/unix/SDL_sysurl.c

@@ -41,7 +41,7 @@ bool SDL_SYS_OpenURL(const char *url)
         pid_t pid2;
         const char *args[] = { "xdg-open", url, NULL };
         // Clear LD_PRELOAD so Chrome opens correctly when this application is launched by Steam
-        SDL_unsetenv("LD_PRELOAD");
+        SDL_unsetenv_unsafe("LD_PRELOAD");
         if (posix_spawnp(&pid2, args[0], NULL, NULL, (char **)args, environ) == 0) {
             // Child process doesn't wait for possibly-blocking grandchild.
             _exit(EXIT_SUCCESS);
@@ -51,7 +51,7 @@ bool SDL_SYS_OpenURL(const char *url)
 #else
         pid_t pid2;
         // Clear LD_PRELOAD so Chrome opens correctly when this application is launched by Steam
-        SDL_unsetenv("LD_PRELOAD");
+        SDL_unsetenv_unsafe("LD_PRELOAD");
         // Notice this is vfork and not fork!
         pid2 = vfork();
         if (pid2 == 0) { // Grandchild process will try to launch the url

+ 9 - 10
src/process/posix/SDL_posixprocess.c

@@ -35,15 +35,6 @@
 #include "../SDL_sysprocess.h"
 #include "../../file/SDL_iostream_c.h"
 
-#if defined(SDL_PLATFORM_MACOS)
-#include <crt_externs.h>
-#define environ (*_NSGetEnviron())
-#elif defined(SDL_PLATFORM_FREEBSD)
-#include <dlfcn.h>
-#define environ ((char **)dlsym(RTLD_DEFAULT, "environ"))
-#else
-extern char **environ;
-#endif
 
 #define READ_END 0
 #define WRITE_END 1
@@ -114,7 +105,7 @@ static bool GetStreamFD(SDL_PropertiesID props, const char *property, int *resul
 bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID props)
 {
     char * const *args = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, NULL);
-    char * const *env = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, environ);
+    char * const *env = SDL_GetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, NULL);
     SDL_ProcessIO stdin_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_NULL);
     SDL_ProcessIO stdout_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_INHERITED);
     SDL_ProcessIO stderr_option = (SDL_ProcessIO)SDL_GetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDERR_NUMBER, SDL_PROCESS_STDIO_INHERITED);
@@ -124,6 +115,7 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
     int stdout_pipe[2] = { -1, -1 };
     int stderr_pipe[2] = { -1, -1 };
     int fd = -1;
+    char **env_copy = NULL;
 
     // Keep the malloc() before exec() so that an OOM won't run a process at all
     SDL_ProcessData *data = SDL_calloc(1, sizeof(*data));
@@ -278,6 +270,11 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
         }
     }
 
+    if (!env) {
+        env_copy = SDL_GetEnvironmentVariables(SDL_GetEnvironment());
+        env = env_copy;
+    }
+
     // Spawn the new process
     if (posix_spawnp(&data->pid, args[0], &fa, &attr, args, env) != 0) {
         SDL_SetError("posix_spawn failed: %s", strerror(errno));
@@ -308,6 +305,7 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
 
     posix_spawn_file_actions_destroy(&fa);
     posix_spawnattr_destroy(&attr);
+    SDL_free(env_copy);
 
     return true;
 
@@ -338,6 +336,7 @@ posix_spawn_fail_none:
     if (stderr_pipe[WRITE_END] >= 0) {
         close(stderr_pipe[WRITE_END]);
     }
+    SDL_free(env_copy);
     return false;
 }
 

+ 6 - 0
src/process/windows/SDL_windowsprocess.c

@@ -186,6 +186,7 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
     HANDLE stdin_pipe[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
     HANDLE stdout_pipe[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
     HANDLE stderr_pipe[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
+    char **env_copy = NULL;
     bool result = false;
 
     // Keep the malloc() before exec() so that an OOM won't run a process at all
@@ -199,6 +200,10 @@ bool SDL_SYS_CreateProcessWithProperties(SDL_Process *process, SDL_PropertiesID
         goto done;
     }
 
+    if (!env) {
+        env_copy = SDL_GetEnvironmentVariables(SDL_GetEnvironment());
+        env = (const char * const *)env_copy;
+    }
     if (!join_env(env, &createprocess_env)) {
         goto done;
     }
@@ -381,6 +386,7 @@ done:
     }
     SDL_free(createprocess_cmdline);
     SDL_free(createprocess_env);
+    SDL_free(env_copy);
 
     if (!result) {
         if (stdin_pipe[WRITE_END] != INVALID_HANDLE_VALUE) {

+ 266 - 12
src/stdlib/SDL_getenv.c

@@ -21,6 +21,7 @@
 #include "SDL_internal.h"
 
 #include "SDL_getenv_c.h"
+#include "../SDL_hashtable.h"
 
 #if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
 #include "../core/windows/SDL_windows.h"
@@ -40,11 +41,23 @@
 #define HAVE_LOCAL_ENVIRONMENT
 #endif
 
+#if !defined(SDL_PLATFORM_WINDOWS)
+#if defined(SDL_PLATFORM_MACOS)
+#include <crt_externs.h>
+#define environ (*_NSGetEnviron())
+#elif defined(SDL_PLATFORM_FREEBSD)
+#include <dlfcn.h>
+#define environ ((char **)dlsym(RTLD_DEFAULT, "environ"))
+#else
+extern char **environ;
+#endif
+#endif // !SDL_PLATFORM_WINDOWS
+
 // Put a variable into the environment
 // Note: Name may not contain a '=' character. (Reference: http://www.unix.com/man-page/Linux/3/setenv/)
 #ifdef HAVE_LIBC_ENVIRONMENT
 #if defined(HAVE_SETENV)
-int SDL_setenv(const char *name, const char *value, int overwrite)
+int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
 {
     // Input validation
     if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) {
@@ -55,7 +68,7 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
 }
 // We have a real environment table, but no real setenv? Fake it w/ putenv.
 #else
-int SDL_setenv(const char *name, const char *value, int overwrite)
+int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
 {
     char *new_variable;
 
@@ -79,7 +92,7 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
 }
 #endif
 #elif defined(HAVE_WIN32_ENVIRONMENT)
-int SDL_setenv(const char *name, const char *value, int overwrite)
+int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
 {
     // Input validation
     if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL || !value) {
@@ -101,7 +114,7 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
 // We'll leak this, as environment variables are intended to persist past SDL_Quit()
 static char **SDL_env;
 
-int SDL_setenv(const char *name, const char *value, int overwrite)
+int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)
 {
     int added;
     size_t len, i;
@@ -114,7 +127,7 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
     }
 
     // See if it already exists
-    if (!overwrite && SDL_getenv(name)) {
+    if (!overwrite && SDL_getenv_unsafe(name)) {
         return 0;
     }
 
@@ -164,7 +177,7 @@ int SDL_setenv(const char *name, const char *value, int overwrite)
 
 #ifdef HAVE_LIBC_ENVIRONMENT
 #if defined(HAVE_UNSETENV)
-int SDL_unsetenv(const char *name)
+int SDL_unsetenv_unsafe(const char *name)
 {
     // Input validation
     if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) {
@@ -175,7 +188,7 @@ int SDL_unsetenv(const char *name)
 }
 // We have a real environment table, but no unsetenv? Fake it w/ putenv.
 #else
-int SDL_unsetenv(const char *name)
+int SDL_unsetenv_unsafe(const char *name)
 {
     // Input validation
     if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) {
@@ -187,7 +200,7 @@ int SDL_unsetenv(const char *name)
 }
 #endif
 #elif defined(HAVE_WIN32_ENVIRONMENT)
-int SDL_unsetenv(const char *name)
+int SDL_unsetenv_unsafe(const char *name)
 {
     // Input validation
     if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) {
@@ -200,7 +213,7 @@ int SDL_unsetenv(const char *name)
     return 0;
 }
 #else
-int SDL_unsetenv(const char *name)
+int SDL_unsetenv_unsafe(const char *name)
 {
     size_t len, i;
 
@@ -226,7 +239,7 @@ int SDL_unsetenv(const char *name)
 
 // Retrieve a variable named "name" from the environment
 #ifdef HAVE_LIBC_ENVIRONMENT
-const char *SDL_getenv(const char *name)
+const char *SDL_getenv_unsafe(const char *name)
 {
 #ifdef SDL_PLATFORM_ANDROID
     // Make sure variables from the application manifest are available
@@ -241,7 +254,7 @@ const char *SDL_getenv(const char *name)
     return getenv(name);
 }
 #elif defined(HAVE_WIN32_ENVIRONMENT)
-const char *SDL_getenv(const char *name)
+const char *SDL_getenv_unsafe(const char *name)
 {
     DWORD length, maxlen = 0;
     char *string = NULL;
@@ -280,7 +293,7 @@ const char *SDL_getenv(const char *name)
     return result;
 }
 #else
-const char *SDL_getenv(const char *name)
+const char *SDL_getenv_unsafe(const char *name)
 {
     size_t len, i;
     char *value;
@@ -304,3 +317,244 @@ const char *SDL_getenv(const char *name)
     return value;
 }
 #endif // HAVE_LIBC_ENVIRONMENT
+
+
+struct SDL_Environment
+{
+    SDL_Mutex *lock;
+    SDL_HashTable *strings;
+};
+static SDL_Environment *SDL_environment;
+
+SDL_Environment *SDL_GetEnvironment(void)
+{
+    if (!SDL_environment) {
+        SDL_environment = SDL_CreateEnvironment(false);
+    }
+    return SDL_environment;
+}
+
+void SDL_CleanupEnvironment(void)
+{
+    SDL_Environment *env = SDL_environment;
+
+    if (env) {
+        SDL_environment = NULL;
+        SDL_DestroyEnvironment(env);
+    }
+}
+
+SDL_Environment *SDL_CreateEnvironment(SDL_bool empty)
+{
+    SDL_Environment *env = SDL_calloc(1, sizeof(*env));
+    if (!env) {
+        return NULL;
+    }
+
+    env->strings = SDL_CreateHashTable(NULL, 16, SDL_HashString, SDL_KeyMatchString, SDL_NukeFreeKey, false);
+    if (!env->strings) {
+        SDL_free(env);
+        return NULL;
+    }
+
+    // Don't fail if we can't create a mutex (e.g. on a single-thread environment)
+    env->lock = SDL_CreateMutex();
+
+    if (!empty) {
+#ifdef SDL_PLATFORM_WINDOWS
+        LPWCH strings = GetEnvironmentStringsW();
+        if (strings) {
+            for (LPWCH string = strings; *string; string += SDL_wcslen(string) + 1) {
+                char *variable = WIN_StringToUTF8W(string);
+                if (!variable) {
+                    continue;
+                }
+
+                char *value = SDL_strchr(variable, '=');
+                if (!value || value == variable) {
+                    SDL_free(variable);
+                    continue;
+                }
+                *value++ = '\0';
+
+                SDL_InsertIntoHashTable(env->strings, variable, value);
+            }
+            FreeEnvironmentStringsW(strings);
+        }
+#else
+#ifdef SDL_PLATFORM_ANDROID
+        // Make sure variables from the application manifest are available
+        Android_JNI_GetManifestEnvironmentVariables();
+#endif
+        char **strings = environ;
+        for (int i = 0; strings[i]; ++i) {
+            char *variable = SDL_strdup(strings[i]);
+            if (!variable) {
+                continue;
+            }
+
+            char *value = SDL_strchr(variable, '=');
+            if (!value || value == variable) {
+                SDL_free(variable);
+                continue;
+            }
+            *value++ = '\0';
+
+            SDL_InsertIntoHashTable(env->strings, variable, value);
+        }
+#endif // SDL_PLATFORM_WINDOWS
+    }
+
+    return env;
+}
+
+const char *SDL_GetEnvironmentVariable(SDL_Environment *env, const char *name)
+{
+    const char *result = NULL;
+
+    if (!env) {
+        return NULL;
+    } else if (!name || *name == '\0') {
+        return NULL;
+    }
+
+    SDL_LockMutex(env->lock);
+    {
+        const char *value;
+
+        if (SDL_FindInHashTable(env->strings, name, (const void **)&value)) {
+            result = SDL_GetPersistentString(value);
+        }
+    }
+    SDL_UnlockMutex(env->lock);
+
+    return result;
+}
+
+char **SDL_GetEnvironmentVariables(SDL_Environment *env)
+{
+    char **result = NULL;
+
+    if (!env) {
+        SDL_InvalidParamError("env");
+        return NULL;
+    }
+
+    SDL_LockMutex(env->lock);
+    {
+        size_t count, length = 0;
+        void *iter;
+        const char *key, *value;
+
+        // First pass, get the size we need for all the strings
+        count = 0;
+        iter = NULL;
+        while (SDL_IterateHashTable(env->strings, (const void **)&key, (const void **)&value, &iter)) {
+            length += SDL_strlen(key) + 1 + SDL_strlen(value) + 1;
+            ++count;
+        }
+
+        // Allocate memory for the strings
+        result = (char **)SDL_malloc((count + 1) * sizeof(*result) + length);
+        char *string = (char *)(result + count + 1);
+
+        // Second pass, copy the strings
+        count = 0;
+        iter = NULL;
+        while (SDL_IterateHashTable(env->strings, (const void **)&key, (const void **)&value, &iter)) {
+            size_t len;
+
+            result[count] = string;
+            len = SDL_strlen(key);
+            SDL_memcpy(string, key, len);
+            string += len;
+            *string++ = '=';
+            len = SDL_strlen(value);
+            SDL_memcpy(string, value, len);
+            string += len;
+            *string++ = '\0';
+            ++count;
+        }
+        result[count] = NULL;
+    }
+    SDL_UnlockMutex(env->lock);
+
+    return result;
+}
+
+SDL_bool SDL_SetEnvironmentVariable(SDL_Environment *env, const char *name, const char *value, SDL_bool overwrite)
+{
+    bool result = false;
+
+    if (!env) {
+        return SDL_InvalidParamError("env");
+    } else if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) {
+        return SDL_InvalidParamError("name");
+    } else if (!value) {
+        return SDL_InvalidParamError("value");
+    }
+
+    SDL_LockMutex(env->lock);
+    {
+        const void *existing_value;
+        bool insert = true;
+
+        if (SDL_FindInHashTable(env->strings, name, &existing_value)) {
+            if (!overwrite) {
+                result = true;
+                insert = false;
+            } else {
+                SDL_RemoveFromHashTable(env->strings, name);
+            }
+        }
+
+        if (insert) {
+            char *string = NULL;
+            if (SDL_asprintf(&string, "%s=%s", name, value) > 0) {
+                size_t len = SDL_strlen(name);
+                string[len] = '\0';
+                name = string;
+                value = string + len + 1;
+                result = SDL_InsertIntoHashTable(env->strings, name, value);
+            }
+        }
+    }
+    SDL_UnlockMutex(env->lock);
+
+    return result;
+}
+
+SDL_bool SDL_UnsetEnvironmentVariable(SDL_Environment *env, const char *name)
+{
+    bool result = false;
+
+    if (!env) {
+        return SDL_InvalidParamError("env");
+    } else if (!name || *name == '\0' || SDL_strchr(name, '=') != NULL) {
+        return SDL_InvalidParamError("name");
+    }
+
+    SDL_LockMutex(env->lock);
+    {
+        const void *value;
+        if (SDL_FindInHashTable(env->strings, name, &value)) {
+            result = SDL_RemoveFromHashTable(env->strings, name);
+        } else {
+            result = true;
+        }
+    }
+    SDL_UnlockMutex(env->lock);
+
+    return result;
+}
+
+void SDL_DestroyEnvironment(SDL_Environment *env)
+{
+    if (!env || env == SDL_environment) {
+        return;
+    }
+
+    SDL_DestroyMutex(env->lock);
+    SDL_DestroyHashTable(env->strings);
+    SDL_free(env);
+}

+ 4 - 4
src/stdlib/SDL_iconv.c

@@ -163,15 +163,15 @@ static const char *getlocale(char *buffer, size_t bufsize)
     const char *lang;
     char *ptr;
 
-    lang = SDL_getenv("LC_ALL");
+    lang = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "LC_ALL");
     if (!lang) {
-        lang = SDL_getenv("LC_CTYPE");
+        lang = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "LC_CTYPE");
     }
     if (!lang) {
-        lang = SDL_getenv("LC_MESSAGES");
+        lang = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "LC_MESSAGES");
     }
     if (!lang) {
-        lang = SDL_getenv("LANG");
+        lang = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "LANG");
     }
     if (!lang || !*lang || SDL_strcmp(lang, "C") == 0) {
         lang = "ASCII";

+ 1 - 1
src/test/SDL_test_memory.c

@@ -299,7 +299,7 @@ void SDLTest_TrackAllocations(void)
 #ifdef SDLTEST_UNWIND_NO_PROC_NAME_BY_IP
     do {
         /* Don't use SDL_GetHint: SDL_malloc is off limits. */
-        const char *env_trackmem = SDL_getenv("SDL_TRACKMEM_SYMBOL_NAMES");
+        const char *env_trackmem = SDL_getenv_unsafe("SDL_TRACKMEM_SYMBOL_NAMES");
         if (env_trackmem) {
             if (SDL_strcasecmp(env_trackmem, "1") == 0 || SDL_strcasecmp(env_trackmem, "yes") == 0 || SDL_strcasecmp(env_trackmem, "true") == 0) {
                 s_unwind_symbol_names = SDL_TRUE;

+ 2 - 2
src/video/SDL_egl.c

@@ -341,7 +341,7 @@ static bool SDL_EGL_LoadLibraryInternal(SDL_VideoDevice *_this, const char *egl_
 
 #if !defined(SDL_VIDEO_STATIC_ANGLE) && !defined(SDL_VIDEO_DRIVER_VITA)
     /* A funny thing, loading EGL.so first does not work on the Raspberry, so we load libGL* first */
-    path = SDL_getenv("SDL_VIDEO_GL_DRIVER");
+    path = SDL_getenv_unsafe("SDL_VIDEO_GL_DRIVER");
     if (path) {
         opengl_dll_handle = SDL_LoadObject(path);
     }
@@ -401,7 +401,7 @@ static bool SDL_EGL_LoadLibraryInternal(SDL_VideoDevice *_this, const char *egl_
         if (egl_dll_handle) {
             SDL_UnloadObject(egl_dll_handle);
         }
-        path = SDL_getenv("SDL_VIDEO_EGL_DRIVER");
+        path = SDL_getenv_unsafe("SDL_VIDEO_EGL_DRIVER");
         if (!path) {
             path = DEFAULT_EGL;
         }

+ 3 - 3
src/video/wayland/SDL_waylandevents.c

@@ -1292,11 +1292,11 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
      */
 
     // Look up the preferred locale, falling back to "C" as default
-    locale = SDL_getenv("LC_ALL");
+    locale = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "LC_ALL");
     if (!locale) {
-        locale = SDL_getenv("LC_CTYPE");
+        locale = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "LC_CTYPE");
         if (!locale) {
-            locale = SDL_getenv("LANG");
+            locale = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "LANG");
             if (!locale) {
                 locale = "C";
             }

+ 2 - 2
src/video/wayland/SDL_waylandmessagebox.c

@@ -141,8 +141,8 @@ bool Wayland_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *butto
     };
 
     // Are we trying to connect to or are currently in a Wayland session?
-    if (!SDL_getenv("WAYLAND_DISPLAY")) {
-        const char *session = SDL_getenv("XDG_SESSION_TYPE");
+    if (!SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "WAYLAND_DISPLAY")) {
+        const char *session = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_SESSION_TYPE");
         if (session && SDL_strcasecmp(session, "wayland") != 0) {
             return SDL_SetError("Not on a wayland display");
         }

+ 2 - 2
src/video/wayland/SDL_waylandmouse.c

@@ -345,7 +345,7 @@ static bool wayland_get_system_cursor(SDL_VideoData *vdata, SDL_CursorData *cdat
 
     // Fallback envvar if the DBus properties don't exist
     if (size <= 0) {
-        const char *xcursor_size = SDL_getenv("XCURSOR_SIZE");
+        const char *xcursor_size = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XCURSOR_SIZE");
         if (xcursor_size) {
             size = SDL_atoi(xcursor_size);
         }
@@ -381,7 +381,7 @@ static bool wayland_get_system_cursor(SDL_VideoData *vdata, SDL_CursorData *cdat
 
         // Fallback envvar if the DBus properties don't exist
         if (!xcursor_theme) {
-            xcursor_theme = SDL_getenv("XCURSOR_THEME");
+            xcursor_theme = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XCURSOR_THEME");
         }
 
         theme = WAYLAND_wl_cursor_theme_load(xcursor_theme, size, vdata->shm);

+ 1 - 1
src/video/wayland/SDL_waylandshmbuffer.c

@@ -80,7 +80,7 @@ static int CreateTempFD(off_t size)
         const char *xdg_path;
         char tmp_path[PATH_MAX];
 
-        xdg_path = SDL_getenv("XDG_RUNTIME_DIR");
+        xdg_path = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_RUNTIME_DIR");
         if (!xdg_path) {
             return -1;
         }

+ 2 - 2
src/video/wayland/SDL_waylandvideo.c

@@ -429,8 +429,8 @@ static SDL_VideoDevice *Wayland_CreateDevice(bool require_preferred_protocols)
     bool display_is_external = !!display;
 
     // Are we trying to connect to or are currently in a Wayland session?
-    if (!SDL_getenv("WAYLAND_DISPLAY")) {
-        const char *session = SDL_getenv("XDG_SESSION_TYPE");
+    if (!SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "WAYLAND_DISPLAY")) {
+        const char *session = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_SESSION_TYPE");
         if (session && SDL_strcasecmp(session, "wayland") != 0) {
             return NULL;
         }

+ 2 - 2
src/video/wayland/SDL_waylandwindow.c

@@ -1914,14 +1914,14 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
         /* Note that we don't check for empty strings, as that is still
          * considered a valid activation token!
          */
-        const char *activation_token = SDL_getenv("XDG_ACTIVATION_TOKEN");
+        const char *activation_token = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_ACTIVATION_TOKEN");
         if (activation_token) {
             xdg_activation_v1_activate(c->activation_manager,
                                        activation_token,
                                        data->surface);
 
             // Clear this variable, per the protocol's request
-            SDL_unsetenv("XDG_ACTIVATION_TOKEN");
+            SDL_unsetenv_unsafe("XDG_ACTIVATION_TOKEN");
         }
     }
 

+ 1 - 1
src/video/x11/SDL_x11keyboard.c

@@ -169,7 +169,7 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this)
         char *prev_locale = setlocale(LC_ALL, NULL);
         char *prev_xmods = X11_XSetLocaleModifiers(NULL);
         const char *new_xmods = "";
-        const char *env_xmods = SDL_getenv("XMODIFIERS");
+        const char *env_xmods = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XMODIFIERS");
         bool has_dbus_ime_support = false;
 
         if (prev_locale) {

+ 1 - 1
src/video/x11/SDL_x11modes.c

@@ -248,7 +248,7 @@ static float GetGlobalContentScale(SDL_VideoDevice *_this)
 
         // If that failed, try the GDK_SCALE envvar...
         if (scale_factor <= 0.0) {
-            const char *scale_str = SDL_getenv("GDK_SCALE");
+            const char *scale_str = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "GDK_SCALE");
             if (scale_str) {
                 scale_factor = SDL_atoi(scale_str);
             }

+ 10 - 26
test/childprocess.c

@@ -5,14 +5,6 @@
 #include <stdio.h>
 #include <errno.h>
 
-#if defined(SDL_PLATFORM_WINDOWS)
-#include <windows.h>
-#elif defined(SDL_PLATFORM_MACOS)
-#include <crt_externs.h>
-#define environ (*_NSGetEnviron())
-#else
-extern char **environ;
-#endif
 
 int main(int argc, char *argv[]) {
     SDLTest_CommonState *state;
@@ -95,26 +87,18 @@ int main(int argc, char *argv[]) {
     }
 
     if (print_environment || expect_environment) {
-
-#if defined(SDL_PLATFORM_WINDOWS)
-        char *original_env = GetEnvironmentStrings();
-        const char *env = original_env;
-        for (; env[0]; env += SDL_strlen(env) + 1) {
-#else
-        char **envp = environ;
-        for (; *envp; envp++) {
-            const char *env = *envp;
-#endif
-            if (print_environment) {
-                fprintf(stdout, "%s\n", env);
-            }
-            if (expect_environment) {
-                expect_environment_match |= SDL_strcmp(env, expect_environment) == 0;
+        char **env = SDL_GetEnvironmentVariables(SDL_GetEnvironment());
+        if (env) {
+            for (i = 0; env[i]; ++i) {
+                if (print_environment) {
+                    fprintf(stdout, "%s\n", env[i]);
+                }
+                if (expect_environment) {
+                    expect_environment_match |= SDL_strcmp(env[i], expect_environment) == 0;
+                }
             }
+            SDL_free(env);
         }
-#ifdef SDL_PLATFORM_WINDOWS
-        FreeEnvironmentStringsA(original_env);
-#endif
     }
 
     if (stdin_to_stdout || stdin_to_stderr) {

+ 1 - 1
test/testatomic.c

@@ -740,7 +740,7 @@ int main(int argc, char *argv[])
 
     RunBasicTest();
 
-    if (SDL_getenv("SDL_TESTS_QUICK") != NULL) {
+    if (SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_TESTS_QUICK") != NULL) {
         SDL_Log("Not running slower tests");
         return 0;
     }

+ 1 - 1
test/testautomation_hints.c

@@ -130,7 +130,7 @@ static int SDLCALL hints_setHint(void *arg)
     SDL_free(value);
 
     /* Set default value in environment */
-    SDL_setenv(testHint, "original", 1);
+    SDL_SetEnvironmentVariable(SDL_GetEnvironment(), testHint, "original", 1);
 
     SDLTest_AssertPass("Call to SDL_GetHint() after saving and restoring hint");
     originalValue = SDL_GetHint(testHint);

+ 58 - 57
test/testautomation_stdlib.c

@@ -542,10 +542,11 @@ static int SDLCALL stdlib_swprintf(void *arg)
 #endif
 
 /**
- * Call to SDL_getenv and SDL_setenv
+ * Call to SDL_GetEnvironmentVariable() and SDL_SetEnvironmentVariable()
  */
 static int SDLCALL stdlib_getsetenv(void *arg)
 {
+    SDL_Environment *env = SDL_GetEnvironment();
     const int nameLen = 16;
     char name[17];
     int counter;
@@ -556,7 +557,7 @@ static int SDLCALL stdlib_getsetenv(void *arg)
     int overwrite;
     const char *text;
 
-    /* Create a random name. This tests SDL_getenv, since we need to */
+    /* Create a random name. This tests SDL_GetEnvironmentVariable, since we need to */
     /* make sure the variable is not set yet (it shouldn't). */
     do {
         for (counter = 0; counter < nameLen; counter++) {
@@ -564,8 +565,8 @@ static int SDLCALL stdlib_getsetenv(void *arg)
         }
         name[nameLen] = '\0';
 
-        text = SDL_getenv(name);
-        SDLTest_AssertPass("Call to SDL_getenv('%s')", name);
+        text = SDL_GetEnvironmentVariable(env, name);
+        SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, '%s')", name);
         if (text) {
             SDLTest_Log("Expected: NULL, Got: '%s' (%i)", text, (int)SDL_strlen(text));
         }
@@ -578,13 +579,13 @@ static int SDLCALL stdlib_getsetenv(void *arg)
     /* Set value 1 without overwrite */
     overwrite = 0;
     expected = value1;
-    result = SDL_setenv(name, value1, overwrite);
-    SDLTest_AssertPass("Call to SDL_setenv('%s','%s', %i)", name, value1, overwrite);
-    SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
+    result = SDL_SetEnvironmentVariable(env, name, value1, overwrite);
+    SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '%s','%s', %i)", name, value1, overwrite);
+    SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
 
     /* Check value */
-    text = SDL_getenv(name);
-    SDLTest_AssertPass("Call to SDL_getenv('%s')", name);
+    text = SDL_GetEnvironmentVariable(env, name);
+    SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, '%s')", name);
     SDLTest_AssertCheck(text != NULL, "Verify returned text is not NULL");
     if (text != NULL) {
         SDLTest_AssertCheck(
@@ -597,13 +598,13 @@ static int SDLCALL stdlib_getsetenv(void *arg)
     /* Set value 2 with overwrite */
     overwrite = 1;
     expected = value2;
-    result = SDL_setenv(name, value2, overwrite);
-    SDLTest_AssertPass("Call to SDL_setenv('%s','%s', %i)", name, value2, overwrite);
-    SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
+    result = SDL_SetEnvironmentVariable(env, name, value2, overwrite);
+    SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '%s','%s', %i)", name, value2, overwrite);
+    SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
 
     /* Check value */
-    text = SDL_getenv(name);
-    SDLTest_AssertPass("Call to SDL_getenv('%s')", name);
+    text = SDL_GetEnvironmentVariable(env, name);
+    SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, '%s')", name);
     SDLTest_AssertCheck(text != NULL, "Verify returned text is not NULL");
     if (text != NULL) {
         SDLTest_AssertCheck(
@@ -616,13 +617,13 @@ static int SDLCALL stdlib_getsetenv(void *arg)
     /* Set value 1 without overwrite */
     overwrite = 0;
     expected = value2;
-    result = SDL_setenv(name, value1, overwrite);
-    SDLTest_AssertPass("Call to SDL_setenv('%s','%s', %i)", name, value1, overwrite);
-    SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
+    result = SDL_SetEnvironmentVariable(env, name, value1, overwrite);
+    SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '%s','%s', %i)", name, value1, overwrite);
+    SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
 
     /* Check value */
-    text = SDL_getenv(name);
-    SDLTest_AssertPass("Call to SDL_getenv('%s')", name);
+    text = SDL_GetEnvironmentVariable(env, name);
+    SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, '%s')", name);
     SDLTest_AssertCheck(text != NULL, "Verify returned text is not NULL");
     if (text != NULL) {
         SDLTest_AssertCheck(
@@ -635,13 +636,13 @@ static int SDLCALL stdlib_getsetenv(void *arg)
     /* Set value 1 with overwrite */
     overwrite = 1;
     expected = value1;
-    result = SDL_setenv(name, value1, overwrite);
-    SDLTest_AssertPass("Call to SDL_setenv('%s','%s', %i)", name, value1, overwrite);
-    SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
+    result = SDL_SetEnvironmentVariable(env, name, value1, overwrite);
+    SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '%s','%s', %i)", name, value1, overwrite);
+    SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
 
     /* Check value */
-    text = SDL_getenv(name);
-    SDLTest_AssertPass("Call to SDL_getenv('%s')", name);
+    text = SDL_GetEnvironmentVariable(env, name);
+    SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, '%s')", name);
     SDLTest_AssertCheck(text != NULL, "Verify returned text is not NULL");
     if (text != NULL) {
         SDLTest_AssertCheck(
@@ -652,48 +653,48 @@ static int SDLCALL stdlib_getsetenv(void *arg)
     }
 
     /* Verify setenv() with empty string vs unsetenv() */
-    result = SDL_setenv("FOO", "1", 1);
-    SDLTest_AssertPass("Call to SDL_setenv('FOO','1', 1)");
-    SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
+    result = SDL_SetEnvironmentVariable(env, "FOO", "1", 1);
+    SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, 'FOO','1', 1)");
+    SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
     expected = "1";
-    text = SDL_getenv("FOO");
-    SDLTest_AssertPass("Call to SDL_getenv('FOO')");
+    text = SDL_GetEnvironmentVariable(env, "FOO");
+    SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, 'FOO')");
     SDLTest_AssertCheck(text && SDL_strcmp(text, expected) == 0, "Verify returned text, expected: %s, got: %s", expected, text);
-    result = SDL_setenv("FOO", "", 1);
-    SDLTest_AssertPass("Call to SDL_setenv('FOO','', 1)");
-    SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
+    result = SDL_SetEnvironmentVariable(env, "FOO", "", 1);
+    SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, 'FOO','', 1)");
+    SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
     expected = "";
-    text = SDL_getenv("FOO");
-    SDLTest_AssertPass("Call to SDL_getenv('FOO')");
+    text = SDL_GetEnvironmentVariable(env, "FOO");
+    SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, 'FOO')");
     SDLTest_AssertCheck(text && SDL_strcmp(text, expected) == 0, "Verify returned text, expected: '%s', got: '%s'", expected, text);
-    result = SDL_unsetenv("FOO");
-    SDLTest_AssertPass("Call to SDL_unsetenv('FOO')");
-    SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
-    text = SDL_getenv("FOO");
-    SDLTest_AssertPass("Call to SDL_getenv('FOO')");
+    result = SDL_UnsetEnvironmentVariable(env, "FOO");
+    SDLTest_AssertPass("Call to SDL_UnsetEnvironmentVariable(env, 'FOO')");
+    SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
+    text = SDL_GetEnvironmentVariable(env, "FOO");
+    SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, 'FOO')");
     SDLTest_AssertCheck(text == NULL, "Verify returned text, expected: (null), got: %s", text);
-    result = SDL_setenv("FOO", "0", 0);
-    SDLTest_AssertPass("Call to SDL_setenv('FOO','0', 0)");
-    SDLTest_AssertCheck(result == 0, "Check result, expected: 0, got: %i", result);
+    result = SDL_SetEnvironmentVariable(env, "FOO", "0", 0);
+    SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, 'FOO','0', 0)");
+    SDLTest_AssertCheck(result == SDL_TRUE, "Check result, expected: 1, got: %i", result);
     expected = "0";
-    text = SDL_getenv("FOO");
-    SDLTest_AssertPass("Call to SDL_getenv('FOO')");
+    text = SDL_GetEnvironmentVariable(env, "FOO");
+    SDLTest_AssertPass("Call to SDL_GetEnvironmentVariable(env, 'FOO')");
     SDLTest_AssertCheck(text && SDL_strcmp(text, expected) == 0, "Verify returned text, expected: %s, got: %s", expected, text);
 
     /* Negative cases */
     for (overwrite = 0; overwrite <= 1; overwrite++) {
-        result = SDL_setenv(NULL, value1, overwrite);
-        SDLTest_AssertPass("Call to SDL_setenv(NULL,'%s', %i)", value1, overwrite);
-        SDLTest_AssertCheck(result == -1, "Check result, expected: -1, got: %i", result);
-        result = SDL_setenv("", value1, overwrite);
-        SDLTest_AssertPass("Call to SDL_setenv('','%s', %i)", value1, overwrite);
-        SDLTest_AssertCheck(result == -1, "Check result, expected: -1, got: %i", result);
-        result = SDL_setenv("=", value1, overwrite);
-        SDLTest_AssertPass("Call to SDL_setenv('=','%s', %i)", value1, overwrite);
-        SDLTest_AssertCheck(result == -1, "Check result, expected: -1, got: %i", result);
-        result = SDL_setenv(name, NULL, overwrite);
-        SDLTest_AssertPass("Call to SDL_setenv('%s', NULL, %i)", name, overwrite);
-        SDLTest_AssertCheck(result == -1, "Check result, expected: -1, got: %i", result);
+        result = SDL_SetEnvironmentVariable(env, NULL, value1, overwrite);
+        SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, NULL,'%s', %i)", value1, overwrite);
+        SDLTest_AssertCheck(result == SDL_FALSE, "Check result, expected: 0, got: %i", result);
+        result = SDL_SetEnvironmentVariable(env, "", value1, overwrite);
+        SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '','%s', %i)", value1, overwrite);
+        SDLTest_AssertCheck(result == SDL_FALSE, "Check result, expected: 0, got: %i", result);
+        result = SDL_SetEnvironmentVariable(env, "=", value1, overwrite);
+        SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '=','%s', %i)", value1, overwrite);
+        SDLTest_AssertCheck(result == SDL_FALSE, "Check result, expected: 0, got: %i", result);
+        result = SDL_SetEnvironmentVariable(env, name, NULL, overwrite);
+        SDLTest_AssertPass("Call to SDL_SetEnvironmentVariable(env, '%s', NULL, %i)", name, overwrite);
+        SDLTest_AssertCheck(result == SDL_FALSE, "Check result, expected: 0, got: %i", result);
     }
 
     /* Clean up */
@@ -1402,7 +1403,7 @@ static const SDLTest_TestCaseReference stdlibTest_swprintf = {
 };
 
 static const SDLTest_TestCaseReference stdlibTest_getsetenv = {
-    stdlib_getsetenv, "stdlib_getsetenv", "Call to SDL_getenv and SDL_setenv", TEST_ENABLED
+    stdlib_getsetenv, "stdlib_getsetenv", "Call to SDL_GetEnvironmentVariable and SDL_SetEnvironmentVariable", TEST_ENABLED
 };
 
 static const SDLTest_TestCaseReference stdlibTest_sscanf = {

+ 2 - 2
test/testautomation_video.c

@@ -43,7 +43,7 @@ static SDL_Window *createVideoSuiteTestWindow(const char *title)
         needs_renderer = SDL_TRUE;
     } else if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0) {
         /* Try to detect if the x11 driver is running under XWayland */
-        const char *session_type = SDL_getenv("XDG_SESSION_TYPE");
+        const char *session_type = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_SESSION_TYPE");
         if (session_type && SDL_strcasecmp(session_type, "wayland") == 0) {
             needs_renderer = SDL_TRUE;
         }
@@ -1929,7 +1929,7 @@ static int SDLCALL video_getSetWindowState(void *arg)
      * Other desktops can be enabled in the future as required.
      */
     if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0 || SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0) {
-        const char *desktop = SDL_getenv("XDG_CURRENT_DESKTOP");
+        const char *desktop = SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "XDG_CURRENT_DESKTOP");
         if (SDL_strcmp(desktop, "GNOME") != 0 && SDL_strcmp(desktop, "KDE") != 0) {
             SDLTest_Log("Skipping test video_getSetWindowState: desktop environment %s not supported", desktop);
             return TEST_SKIPPED;

+ 1 - 1
test/testerror.c

@@ -93,7 +93,7 @@ int main(int argc, char *argv[])
     /* Set the error value for the main thread */
     SDL_SetError("No worries");
 
-    if (SDL_getenv("SDL_TESTS_QUICK") != NULL) {
+    if (SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_TESTS_QUICK") != NULL) {
         SDL_Log("Not running slower tests");
         SDL_Quit();
         return 0;

+ 23 - 62
test/testprocess.c

@@ -35,39 +35,35 @@ static void SDLCALL setUpProcess(void **arg) {
 
 static const char *options[] = { "/path/to/childprocess" EXE, NULL };
 
-static char *env_key_val_string(const char *key) {
-    const char *env = SDL_getenv(key);
-    size_t size_result;
-    char *result;
-
-    if (env == NULL) {
-        return NULL;
-    }
-    size_result = SDL_strlen(key) + SDL_strlen(env) + 2;
-    result = SDL_malloc(size_result);
-    SDL_snprintf(result, size_result, "%s=%s", key, env);
-    return result;
-}
-
 static char **DuplicateEnvironment(const char *key0, ...)
 {
     va_list ap;
-    size_t count = 1;
-    size_t i;
     const char *keyN;
+    SDL_Environment *env = SDL_GetEnvironment();
+    SDL_Environment *new_env = SDL_CreateEnvironment(SDL_TRUE);
     char **result;
 
     if (key0) {
-        if (SDL_strchr(key0, '=') || SDL_getenv(key0)) {
-            count += 1;
+        char *sep = SDL_strchr(key0, '=');
+        if (sep) {
+            *sep = '\0';
+            SDL_SetEnvironmentVariable(new_env, key0, sep + 1, SDL_TRUE);
+            *sep = '=';
+            SDL_SetEnvironmentVariable(new_env, key0, sep, SDL_TRUE);
+        } else {
+            SDL_SetEnvironmentVariable(new_env, key0, SDL_GetEnvironmentVariable(env, key0), SDL_TRUE);
         }
-
         va_start(ap, key0);
         for (;;) {
             keyN = va_arg(ap, const char *);
             if (keyN) {
-                if (SDL_strchr(keyN, '=') || SDL_getenv(keyN)) {
-                    count += 1;
+                sep = SDL_strchr(keyN, '=');
+                if (sep) {
+                    *sep = '\0';
+                    SDL_SetEnvironmentVariable(new_env, keyN, sep + 1, SDL_TRUE);
+                    *sep = '=';
+                } else {
+                    SDL_SetEnvironmentVariable(new_env, keyN, SDL_GetEnvironmentVariable(env, keyN), SDL_TRUE);
                 }
             } else {
                 break;
@@ -76,45 +72,11 @@ static char **DuplicateEnvironment(const char *key0, ...)
         va_end(ap);
     }
 
-    result = SDL_calloc(count, sizeof(char *));
-
-    i = 0;
-    if (key0) {
-        if (SDL_strchr(key0, '=')) {
-            result[i++] = SDL_strdup(key0);
-        } else if (SDL_getenv(key0)) {
-            result[i++] = env_key_val_string(key0);
-        }
-        va_start(ap, key0);
-        for (;;) {
-            keyN = va_arg(ap, const char *);
-            if (keyN) {
-                if (SDL_strchr(keyN, '=')) {
-                    result[i++] = SDL_strdup(keyN);
-                } else if (SDL_getenv(keyN)) {
-                    result[i++] = env_key_val_string(keyN);
-                }
-            } else {
-                break;
-            }
-        }
-        va_end(ap);
-    }
+    result = SDL_GetEnvironmentVariables(new_env);
+    SDL_DestroyEnvironment(new_env);
     return result;
 }
 
-static void DestroyEnvironment(char **environment) {
-    char **envp;
-
-    if (!environment) {
-        return;
-    }
-    for (envp = environment; *envp; envp++) {
-        SDL_free(*envp);
-    }
-    SDL_free(environment);
-}
-
 static int SDLCALL process_testArguments(void *arg)
 {
     TestProcessData *data = (TestProcessData *)arg;
@@ -187,7 +149,7 @@ static int SDLCALL process_testInheritedEnv(void *arg)
 
     test_env_val = SDLTest_RandomAsciiStringOfSize(32);
     SDLTest_AssertPass("Setting parent environment variable %s=%s", TEST_ENV_KEY, test_env_val);
-    SDL_setenv(TEST_ENV_KEY, test_env_val, 1);
+    SDL_SetEnvironmentVariable(SDL_GetEnvironment(), TEST_ENV_KEY, test_env_val, SDL_TRUE);
     SDL_snprintf(buffer, sizeof(buffer), "%s=%s", TEST_ENV_KEY, test_env_val);
     process_args[3] = buffer;
 
@@ -312,13 +274,14 @@ static int SDLCALL process_testNewEnv(void *arg)
     SDLTest_AssertCheck(exit_code == 0, "Exit code should be 0, is %d", exit_code);
     SDLTest_AssertPass("About to destroy process");
     SDL_DestroyProcess(process);
-    DestroyEnvironment(process_env);
+    SDL_free(process_env);
     SDL_free(test_env_val);
     return TEST_COMPLETED;
+
 failed:
     SDL_free(test_env_val);
     SDL_DestroyProcess(process);
-    DestroyEnvironment(process_env);
+    SDL_free(process_env);
     return TEST_ABORTED;
 }
 
@@ -330,7 +293,6 @@ static int process_testStdinToStdout(void *arg)
         "--stdin-to-stdout",
         NULL,
     };
-    const char **process_env = NULL;
     SDL_PropertiesID props;
     SDL_Process *process = NULL;
     Sint64 pid;
@@ -346,7 +308,6 @@ static int process_testStdinToStdout(void *arg)
 
     props = SDL_CreateProperties();
     SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, (void *)process_args);
-    SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, (void *)process_env);
     SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDIN_NUMBER, SDL_PROCESS_STDIO_APP);
     SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);
     process = SDL_CreateProcessWithProperties(props);

+ 1 - 1
test/testsurround.c

@@ -223,7 +223,7 @@ int main(int argc, char *argv[])
             SDL_Log("Playing %d Hz test tone on channel: %s\n", sine_freq, get_channel_name(j, total_channels));
 
             /* fill_buffer() will increment the active channel */
-            if (SDL_getenv("SDL_TESTS_QUICK") != NULL) {
+            if (SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_TESTS_QUICK") != NULL) {
                 SDL_Delay(QUICK_TEST_TIME_MSEC);
             } else {
                 SDL_Delay(CHANNEL_TEST_TIME_SEC * 1000);

+ 1 - 1
test/testthread.c

@@ -127,7 +127,7 @@ int main(int argc, char *argv[])
         return 1;
     }
 
-    if (SDL_getenv("SDL_TESTS_QUICK") != NULL) {
+    if (SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_TESTS_QUICK") != NULL) {
         SDL_Log("Not running slower tests");
         SDL_Quit();
         return 0;

+ 1 - 1
test/testtimer.c

@@ -124,7 +124,7 @@ int main(int argc, char *argv[])
         return 1;
     }
 
-    if (SDL_getenv("SDL_TESTS_QUICK") != NULL) {
+    if (SDL_GetEnvironmentVariable(SDL_GetEnvironment(), "SDL_TESTS_QUICK") != NULL) {
         SDL_Log("Not running slower tests");
         SDL_Quit();
         return 0;