SDL: Add SDL_Process subsystem

From 9eea8234e67d4b12b8d83b5d129ef58fb96f34c4 Mon Sep 17 00:00:00 2001
From: Semphris <[EMAIL REDACTED]>
Date: Thu, 29 Aug 2024 13:06:25 -0400
Subject: [PATCH] Add SDL_Process subsystem

---
 Android.mk                                    |   2 +
 CMakeLists.txt                                |  53 ++
 VisualC-GDK/SDL/SDL.vcxproj                   |   5 +
 VisualC-GDK/SDL/SDL.vcxproj.filters           |   5 +
 VisualC/SDL/SDL.vcxproj                       |   3 +
 VisualC/SDL/SDL.vcxproj.filters               |  11 +-
 Xcode/SDL/SDL.xcodeproj/project.pbxproj       |  44 ++
 include/SDL3/SDL.h                            |   1 +
 include/SDL3/SDL_process.h                    | 305 +++++++++
 include/build_config/SDL_build_config.h.cmake |   5 +
 .../build_config/SDL_build_config_android.h   |   3 +
 .../SDL_build_config_emscripten.h             |   3 +
 include/build_config/SDL_build_config_ios.h   |   3 +
 include/build_config/SDL_build_config_macos.h |   3 +
 .../build_config/SDL_build_config_minimal.h   |   3 +
 include/build_config/SDL_build_config_ngage.h |   3 +
 .../build_config/SDL_build_config_windows.h   |   3 +
 .../build_config/SDL_build_config_wingdk.h    |   3 +
 include/build_config/SDL_build_config_xbox.h  |   3 +
 src/dynapi/SDL_dynapi.sym                     |   8 +
 src/dynapi/SDL_dynapi_overrides.h             |   8 +
 src/dynapi/SDL_dynapi_procs.h                 |   8 +
 src/process/SDL_process.c                     | 204 ++++++
 src/process/SDL_sysprocess.h                  |  36 +
 src/process/dummy/SDL_dummyprocess.c          |  48 ++
 src/process/posix/SDL_posixprocess.c          | 401 +++++++++++
 src/process/windows/SDL_windowsprocess.c      | 452 +++++++++++++
 test/CMakeLists.txt                           |   3 +
 test/childprocess.c                           | 149 ++++
 test/testprocess.c                            | 637 ++++++++++++++++++
 30 files changed, 2414 insertions(+), 1 deletion(-)
 create mode 100644 include/SDL3/SDL_process.h
 create mode 100644 src/process/SDL_process.c
 create mode 100644 src/process/SDL_sysprocess.h
 create mode 100644 src/process/dummy/SDL_dummyprocess.c
 create mode 100644 src/process/posix/SDL_posixprocess.c
 create mode 100644 src/process/windows/SDL_windowsprocess.c
 create mode 100644 test/childprocess.c
 create mode 100644 test/testprocess.c

diff --git a/Android.mk b/Android.mk
index dd034a5f94464..b352cb3186ee5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -57,6 +57,8 @@ LOCAL_SRC_FILES := \
 	$(wildcard $(LOCAL_PATH)/src/misc/android/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/power/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/power/android/*.c) \
+	$(wildcard $(LOCAL_PATH)/src/process/*.c) \
+	$(wildcard $(LOCAL_PATH)/src/process/dummy/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/filesystem/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/filesystem/android/*.c) \
 	$(wildcard $(LOCAL_PATH)/src/filesystem/posix/*.c) \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9660786a50ebc..01cc974662c5e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2860,6 +2860,55 @@ if (SDL_DIALOG)
   endif()
 endif()
 
+sdl_sources("${SDL3_SOURCE_DIR}/src/process/SDL_process.c")
+if(WINDOWS)
+  sdl_glob_sources("${SDL3_SOURCE_DIR}/src/process/windows/*.c")
+  set(SDL_PROCESS_WINDOWS 1)
+  set(HAVE_SDL_PROCESS TRUE)
+else()
+  check_c_source_compiles("
+#include <spawn.h>
+#include <unistd.h>
+
+int main(void)
+{
+    int pipes[2];
+    int pid;
+
+    const char * args[] = {
+      \"/bin/false\",
+      NULL
+    };
+
+    const char * env[] = { NULL };
+
+    pipe(pipes);
+
+    posix_spawnattr_t attr;
+    posix_spawn_file_actions_t fa;
+
+    posix_spawnattr_init(&attr);
+    posix_spawn_file_actions_init(&fa);
+
+    posix_spawn_file_actions_addclose(&fa, pipes[0]);
+    posix_spawn_file_actions_adddup2(&fa, pipes[1], STDOUT_FILENO);
+
+    posix_spawn(&pid, args[0], &fa, &attr, (char * const *) args, (char * const *) env);
+    posix_spawnp(&pid, args[0], &fa, &attr, (char * const *) args, (char * const *) env);
+
+    posix_spawn_file_actions_destroy(&fa);
+    posix_spawnattr_destroy(&attr);
+
+    return 0;
+}
+" HAVE_POSIX_SPAWN)
+  if(HAVE_POSIX_SPAWN)
+    sdl_glob_sources("${SDL3_SOURCE_DIR}/src/process/posix/*.c")
+    set(SDL_PROCESS_POSIX 1)
+    set(HAVE_SDL_PROCESS TRUE)
+  endif()
+endif()
+
 # Platform-independent options
 
 if(SDL_VIDEO)
@@ -2949,6 +2998,10 @@ if(NOT HAVE_SDL_DIALOG)
   set(SDL_DIALOG_DUMMY 1)
   sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/dummy/SDL_dummydialog.c)
 endif()
+if(NOT HAVE_SDL_PROCESS)
+  set(SDL_PROCESS_DUMMY 1)
+  sdl_glob_sources(${SDL3_SOURCE_DIR}/src/process/dummy/*.c)
+endif()
 if(NOT HAVE_CAMERA)
   set(SDL_CAMERA_DRIVER_DUMMY 1)
   sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/dummy/*.c")
diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index 4041d532a9edf..c6ab14168f87d 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -364,6 +364,8 @@
     <ClInclude Include="..\..\include\SDL3\SDL_platform.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_platform_defines.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_power.h" />
+    <ClInclude Include="..\..\include\SDL3\SDL_process.h" />
+    <ClInclude Include="..\..\include\SDL3\SDL_properties.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_rect.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_render.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_revision.h" />
@@ -754,6 +756,9 @@
     <ClCompile Include="..\..\src\misc\windows\SDL_sysurl.c" />
     <ClCompile Include="..\..\src\power\SDL_power.c" />
     <ClCompile Include="..\..\src\power\windows\SDL_syspower.c" />
+    <ClCompile Include="..\..\src\process\SDL_process.c" />
+    <ClCompile Include="..\..\src\process\dummy\SDL_dummyprocess.c" />
+    <ClCompile Include="..\..\src\process\windows\SDL_windowsprocess.c" />
     <ClCompile Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.c" />
     <ClCompile Include="..\..\src\render\direct3d12\SDL_render_d3d12.c">
       <CompileAs Condition="'$(Configuration)|$(Platform)'=='Debug|Gaming.Xbox.Scarlett.x64'">CompileAsCpp</CompileAs>
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index 61caf6d2492eb..017b9bafc96f7 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -126,6 +126,9 @@
     <ClCompile Include="..\..\src\misc\SDL_url.c" />
     <ClCompile Include="..\..\src\misc\windows\SDL_sysurl.c" />
     <ClCompile Include="..\..\src\power\SDL_power.c" />
+    <ClCompile Include="..\..\src\process\SDL_process.c" />
+    <ClCompile Include="..\..\src\process\dummy\SDL_dummyprocess.c" />
+    <ClCompile Include="..\..\src\process\windows\SDL_windowsprocess.c" />
     <ClCompile Include="..\..\src\power\windows\SDL_syspower.c" />
     <ClCompile Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.c" />
     <ClCompile Include="..\..\src\render\direct3d12\SDL_render_d3d12.c" />
@@ -283,6 +286,8 @@
     <ClInclude Include="..\..\include\SDL3\SDL_platform.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_platform_defines.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_power.h" />
+    <ClInclude Include="..\..\include\SDL3\SDL_process.h" />
+    <ClInclude Include="..\..\include\SDL3\SDL_properties.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_rect.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_render.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_revision.h" />
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index d1e3a2bb5e408..27883a0c4c799 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -284,6 +284,7 @@
     <ClInclude Include="..\..\include\SDL3\SDL_platform.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_platform_defines.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_power.h" />
+    <ClInclude Include="..\..\include\SDL3\SDL_process.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_properties.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_rect.h" />
     <ClInclude Include="..\..\include\SDL3\SDL_render.h" />
@@ -615,6 +616,8 @@
     <ClCompile Include="..\..\src\misc\windows\SDL_sysurl.c" />
     <ClCompile Include="..\..\src\power\SDL_power.c" />
     <ClCompile Include="..\..\src\power\windows\SDL_syspower.c" />
+    <ClCompile Include="..\..\src\process\SDL_process.c" />
+    <ClCompile Include="..\..\src\process\windows\SDL_windowsprocess.c" />
     <ClCompile Include="..\..\src\render\direct3d11\SDL_shaders_d3d11.c" />
     <ClCompile Include="..\..\src\render\direct3d12\SDL_render_d3d12.c" />
     <ClCompile Include="..\..\src\render\direct3d12\SDL_shaders_d3d12.c" />
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index 20a50bda4b202..34f05bb440dfa 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -348,6 +348,9 @@
     <ClInclude Include="..\..\include\SDL3\SDL_power.h">
       <Filter>API Headers</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\include\SDL3\SDL_process.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\include\SDL3\SDL_properties.h">
       <Filter>API Headers</Filter>
     </ClInclude>
@@ -1538,10 +1541,16 @@
     <ClCompile Include="..\..\src\power\SDL_power.c">
       <Filter>power</Filter>
     </ClCompile>
-    <ClCompile Include="..\..\src\SDL_log.c" />
     <ClCompile Include="..\..\src\power\windows\SDL_syspower.c">
       <Filter>power\windows</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\process\SDL_process.c">
+      <Filter>process</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\process\windows\SDL_windowsprocess.c">
+      <Filter>process\windows</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\SDL_log.c" />
     <ClCompile Include="..\..\src\render\direct3d12\SDL_render_d3d12.c">
       <Filter>render\direct3d12</Filter>
     </ClCompile>
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
index fc0651eab06f1..23cf854b46de2 100644
--- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj
+++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
@@ -457,6 +457,11 @@
 		F3B38CD7296E2E52005DA6D3 /* SDL_init.h in Headers */ = {isa = PBXBuildFile; fileRef = F3B38CCC296E2E52005DA6D3 /* SDL_init.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		F3B38CDB296E2E52005DA6D3 /* SDL_oldnames.h in Headers */ = {isa = PBXBuildFile; fileRef = F3B38CCD296E2E52005DA6D3 /* SDL_oldnames.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		F3B38CDF296E2E52005DA6D3 /* SDL_intrin.h in Headers */ = {isa = PBXBuildFile; fileRef = F3B38CCE296E2E52005DA6D3 /* SDL_intrin.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		F3B439482C93595900792030 /* SDL_process.h in Headers */ = {isa = PBXBuildFile; fileRef = F3B439472C93595900792030 /* SDL_process.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		F3B439512C935C2400792030 /* SDL_dummyprocess.c in Sources */ = {isa = PBXBuildFile; fileRef = F3B439502C935C2400792030 /* SDL_dummyprocess.c */; };
+		F3B439532C935C2C00792030 /* SDL_posixprocess.c in Sources */ = {isa = PBXBuildFile; fileRef = F3B439522C935C2C00792030 /* SDL_posixprocess.c */; };
+		F3B439562C937DAB00792030 /* SDL_process.c in Sources */ = {isa = PBXBuildFile; fileRef = F3B439542C937DAB00792030 /* SDL_process.c */; };
+		F3B439572C937DAB00792030 /* SDL_sysprocess.h in Headers */ = {isa = PBXBuildFile; fileRef = F3B439552C937DAB00792030 /* SDL_sysprocess.h */; };
 		F3C2CB222C5DDDB2004D7998 /* SDL_categories_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3C2CB202C5DDDB2004D7998 /* SDL_categories_c.h */; };
 		F3C2CB232C5DDDB2004D7998 /* SDL_categories.c in Sources */ = {isa = PBXBuildFile; fileRef = F3C2CB212C5DDDB2004D7998 /* SDL_categories.c */; };
 		F3D60A8328C16A1900788A3A /* SDL_hidapi_wii.c in Sources */ = {isa = PBXBuildFile; fileRef = F3D60A8228C16A1800788A3A /* SDL_hidapi_wii.c */; };
@@ -1023,6 +1028,11 @@
 		F3B38CCC296E2E52005DA6D3 /* SDL_init.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_init.h; path = SDL3/SDL_init.h; sourceTree = "<group>"; };
 		F3B38CCD296E2E52005DA6D3 /* SDL_oldnames.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_oldnames.h; path = SDL3/SDL_oldnames.h; sourceTree = "<group>"; };
 		F3B38CCE296E2E52005DA6D3 /* SDL_intrin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_intrin.h; path = SDL3/SDL_intrin.h; sourceTree = "<group>"; };
+		F3B439472C93595900792030 /* SDL_process.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_process.h; path = SDL3/SDL_process.h; sourceTree = "<group>"; };
+		F3B439502C935C2400792030 /* SDL_dummyprocess.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_dummyprocess.c; sourceTree = "<group>"; };
+		F3B439522C935C2C00792030 /* SDL_posixprocess.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_posixprocess.c; sourceTree = "<group>"; };
+		F3B439542C937DAB00792030 /* SDL_process.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_process.c; sourceTree = "<group>"; };
+		F3B439552C937DAB00792030 /* SDL_sysprocess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sysprocess.h; sourceTree = "<group>"; };
 		F3C2CB202C5DDDB2004D7998 /* SDL_categories_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_categories_c.h; sourceTree = "<group>"; };
 		F3C2CB212C5DDDB2004D7998 /* SDL_categories.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_categories.c; sourceTree = "<group>"; };
 		F3D60A8228C16A1800788A3A /* SDL_hidapi_wii.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_wii.c; sourceTree = "<group>"; };
@@ -1265,6 +1275,7 @@
 				F3B38CCB296E2E52005DA6D3 /* SDL_platform_defines.h */,
 				F3F7D8AB2933074900816151 /* SDL_platform.h */,
 				F3F7D8DB2933074D00816151 /* SDL_power.h */,
+				F3B439472C93595900792030 /* SDL_process.h */,
 				F3E5A6EC2AD5E10800293D83 /* SDL_properties.h */,
 				F3F7D8E22933074D00816151 /* SDL_rect.h */,
 				F3F7D8DE2933074D00816151 /* SDL_render.h */,
@@ -1338,6 +1349,7 @@
 				000082EF09C89B62BD840000 /* main */,
 				5616CA47252BB278005D5928 /* misc */,
 				A7D8A7DF23E2513F00DCD162 /* power */,
+				F3B439492C93597500792030 /* process */,
 				A7D8A8DA23E2514000DCD162 /* render */,
 				A7D8A57623E2513D00DCD162 /* sensor */,
 				A7D8A8D223E2514000DCD162 /* stdlib */,
@@ -2368,6 +2380,33 @@
 			path = ios;
 			sourceTree = "<group>";
 		};
+		F3B439492C93597500792030 /* process */ = {
+			isa = PBXGroup;
+			children = (
+				F3B4394A2C93599900792030 /* dummy */,
+				F3B4394B2C9359A500792030 /* posix */,
+				F3B439542C937DAB00792030 /* SDL_process.c */,
+				F3B439552C937DAB00792030 /* SDL_sysprocess.h */,
+			);
+			path = process;
+			sourceTree = "<group>";
+		};
+		F3B4394A2C93599900792030 /* dummy */ = {
+			isa = PBXGroup;
+			children = (
+				F3B439502C935C2400792030 /* SDL_dummyprocess.c */,
+			);
+			path = dummy;
+			sourceTree = "<group>";
+		};
+		F3B4394B2C9359A500792030 /* posix */ = {
+			isa = PBXGroup;
+			children = (
+				F3B439522C935C2C00792030 /* SDL_posixprocess.c */,
+			);
+			path = posix;
+			sourceTree = "<group>";
+		};
 		F59C70FC00D5CB5801000001 /* pkg-support */ = {
 			isa = PBXGroup;
 			children = (
@@ -2442,6 +2481,7 @@
 				F3F7D9B92933074E00816151 /* SDL_cpuinfo.h in Headers */,
 				A7D8B98023E2514400DCD162 /* SDL_d3dmath.h in Headers */,
 				A7D8B8A223E2514400DCD162 /* SDL_diskaudio.h in Headers */,
+				F3B439482C93595900792030 /* SDL_process.h in Headers */,
 				A7D8BB3F23E2514500DCD162 /* SDL_displayevents_c.h in Headers */,
 				A7D8BA1923E2514400DCD162 /* SDL_draw.h in Headers */,
 				F3C2CB222C5DDDB2004D7998 /* SDL_categories_c.h in Headers */,
@@ -2572,6 +2612,7 @@
 				A7D8AC3F23E2514100DCD162 /* SDL_sysvideo.h in Headers */,
 				F3F7D9792933074E00816151 /* SDL_thread.h in Headers */,
 				A7D8B3EC23E2514300DCD162 /* SDL_thread_c.h in Headers */,
+				F3B439572C937DAB00792030 /* SDL_sysprocess.h in Headers */,
 				E4F257912C81903800FCEAFC /* Metal_Blit.h in Headers */,
 				F3F7D90D2933074E00816151 /* SDL_timer.h in Headers */,
 				A7D8AB3123E2514100DCD162 /* SDL_timer_c.h in Headers */,
@@ -2800,6 +2841,7 @@
 				A7D8B9F523E2514400DCD162 /* SDL_rotate.c in Sources */,
 				A7D8BBE323E2574800DCD162 /* SDL_uikitvideo.m in Sources */,
 				5616CA4E252BB2A6005D5928 /* SDL_sysurl.m in Sources */,
+				F3B439562C937DAB00792030 /* SDL_process.c in Sources */,
 				A7D8A97523E2514000DCD162 /* SDL_coremotionsensor.m in Sources */,
 				F382071D284F362F004DD584 /* SDL_guid.c in Sources */,
 				A7D8BB8D23E2514500DCD162 /* SDL_touch.c in Sources */,
@@ -2862,6 +2904,7 @@
 				A7D8BA8B23E2514400DCD162 /* s_sin.c in Sources */,
 				F3F528CE2C29E1C300E6CC26 /* s_modf.c in Sources */,
 				A7D8BBEB23E2574800DCD162 /* SDL_uikitwindow.m in Sources */,
+				F3B439532C935C2C00792030 /* SDL_posixprocess.c in Sources */,
 				F395BF6525633B2400942BFF /* SDL_crc32.c in Sources */,
 				A7D8B5E723E2514300DCD162 /* SDL_power.c in Sources */,
 				A7D8AED623E2514100DCD162 /* SDL_cocoakeyboard.m in Sources */,
@@ -2905,6 +2948,7 @@
 				A7D8BADF23E2514500DCD162 /* e_fmod.c in Sources */,
 				A7D8B5CF23E2514300DCD162 /* SDL_syspower.m in Sources */,
 				A7D8BAEB23E2514500DCD162 /* e_log10.c in Sources */,
+				F3B439512C935C2400792030 /* SDL_dummyprocess.c in Sources */,
 				A7D8B76423E2514300DCD162 /* SDL_mixer.c in Sources */,
 				A7D8BB5723E2514500DCD162 /* SDL_events.c in Sources */,
 				A7D8ADE623E2514100DCD162 /* SDL_blit_0.c in Sources */,
diff --git a/include/SDL3/SDL.h b/include/SDL3/SDL.h
index c9633941dcc5a..3698f636244f1 100644
--- a/include/SDL3/SDL.h
+++ b/include/SDL3/SDL.h
@@ -65,6 +65,7 @@
 #include <SDL3/SDL_pixels.h>
 #include <SDL3/SDL_platform.h>
 #include <SDL3/SDL_power.h>
+#include <SDL3/SDL_process.h>
 #include <SDL3/SDL_properties.h>
 #include <SDL3/SDL_rect.h>
 #include <SDL3/SDL_render.h>
diff --git a/include/SDL3/SDL_process.h b/include/SDL3/SDL_process.h
new file mode 100644
index 0000000000000..fc0926333fd48
--- /dev/null
+++ b/include/SDL3/SDL_process.h
@@ -0,0 +1,305 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+/**
+ * # CategoryProcess
+ *
+ * Process control support.
+ *
+ * These functions provide a cross-platform way to spawn and manage OS-level
+ * processes.
+ *
+ * You can create a new subprocess with SDL_CreateProcess() and optionally read and write to it using SDL_ReadProcess() and SDL_WriteProcess(). If more advanced functionality like chaining input between processes is necessary, you can use SDL_CreateProcessWithProperties().
+ *
+ * You can get the status of a created process with SDL_WaitProcess(), or terminate the process with SDL_KillProcess().
+ *
+ * Don't forget to call SDL_DestroyProcess() to clean up, whether the process
+ * process was killed, terminated on its own, or is still running!
+ */
+
+#ifndef SDL_process_h_
+#define SDL_process_h_
+
+#include <SDL3/SDL_error.h>
+
+#include <SDL3/SDL_begin_code.h>
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct SDL_Process SDL_Process;
+
+/**
+ * Create a new process.
+ *
+ * The path to the executable is supplied in args[0]. args[1..N] are additional arguments passed on the command line of the new process, and the argument list should be terminated with a NULL, e.g.:
+ *
+ * ```c
+ * const char *args[] = { "myprogram", "argument", NULL };
+ * ```
+ *
+ * Setting pipe_stdio to SDL_TRUE is equivalent to setting `SDL_PROP_PROCESS_CREATE_STDIN_NUMBER` and `SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER` to `SDL_PROCESS_STDIO_APP`, and will allow the use of SDL_ReadProcess() and SDL_WriteProcess().
+ *
+ * See SDL_CreateProcessWithProperties() for more details.
+ *
+ * \param args the path and arguments for the new process.
+ * \param pipe_stdio SDL_TRUE to create pipes to the process's standard input and from the process's standard output, SDL_FALSE for the process to have no input and inherit the application's standard output.
+ * \returns the newly created and running process, or NULL if the process couldn't be created.
+ *
+ * \sa SDL_CreateProcessWithProperties
+ * \sa SDL_GetProcessProperties
+ * \sa SDL_ReadProcess
+ * \sa SDL_WriteProcess
+ * \sa SDL_KillProcess
+ * \sa SDL_WaitProcess
+ * \sa SDL_DestroyProcess
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern SDL_DECLSPEC SDL_Process *SDLCALL SDL_CreateProcess(const char * const *args, SDL_bool pipe_stdio);
+
+/**
+ * Description of where standard I/O should be directed when creating a process.
+ *
+ * If a standard I/O stream is set to SDL_PROCESS_STDIO_INHERIT, it will go to the same place as the application's I/O stream. This is the default for standard output and standard error.
+ *
+ * If a standard I/O stream is set to SDL_PROCESS_STDIO_NULL, it is connected to `NUL:` on Windows and `/dev/null` on POSIX systems. This is the default for standard input.
+ *
+ * If a standard I/O stream is set to SDL_PROCESS_STDIO_APP, it is connected to a new SDL_IOStream that is available to the application. Standard input will be available as `SDL_PROP_PROCESS_STDIN_POINTER` and allows SDL_WriteProcess(), standard output will be available as `SDL_PROP_PROCESS_STDOUT_POINTER` and allows SDL_ReadProcess(), and standard error will be available as `SDL_PROP_PROCESS_STDERR_POINTER` in the properties for the created process.
+ *
+ * If a standard I/O stream is set to SDL_PROCESS_STDIO_REDIRECT, it is connected to an existing SDL_IOStream provided by the application. Standard input is provided using `SDL_PROP_PROCESS_CREATE_STDIN_POINTER`, standard output is provided using `SDL_PROP_PROCESS_CREATE_STDOUT_POINTER`, and standard error is provided using `SDL_PROP_PROCESS_CREATE_STDERR_POINTER` in the creation properties. These existing streams should be closed by the application once the new process is created.
+ *
+ * In order to use an SDL_IOStream with SDL_PROCESS_STDIO_REDIRECT, it must have `SDL_PROP_IOSTREAM_WINDOWS_HANDLE_POINTER` or `SDL_PROP_IOSTREAM_FILE_DESCRIPTOR_NUMBER` set. This is true for streams representing files and process I/O.
+ *
+ * \sa SDL_CreateProcessWithProperties
+ * \sa SDL_GetProcessProperties
+ * \sa SDL_ReadProcess
+ * \sa SDL_WriteProcess
+ *
+ * \since This enum is available since SDL 3.0.0.
+ */
+typedef enum SDL_ProcessIO
+{
+    SDL_PROCESS_STDIO_INHERITED,    /**< The I/O stream is inherited from the application. */
+    SDL_PROCESS_STDIO_NULL,         /**< The I/O stream is ignored. */
+    SDL_PROCESS_STDIO_APP,          /**< The I/O stream is connected to a new SDL_IOStream that the application can read or write */
+    SDL_PROCESS_STDIO_REDIRECT,     /**< The I/O stream is redirected to an existing SDL_IOStream. */
+} SDL_ProcessIO;
+
+/**
+ * Create a new process with the specified properties.
+ *
+ * These are the supported properties:
+ *
+ * - `SDL_PROP_PROCESS_CREATE_ARGS_POINTER`: an array of strings containing the program to run, any arguments, and a NULL pointer, e.g. const char *args[] = { "myprogram", "argument", NULL }. This is a required property.
+ * - `SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER`: an array of strings containing variable=value, and a NULL pointer, e.g. const char *env[] = { "PATH=/bin:/usr/bin", NULL }. If this property is set, it will be the entire environment for the process, otherwise the current environment is used.
+ * - `SDL_PROP_PROCESS_CREATE_STDIN_NUMBER`: an SDL_ProcessIO value describing where standard input for the process comes from, defaults to `SDL_PROCESS_STDIO_NULL`.
+ * - `SDL_PROP_PROCESS_CREATE_STDIN_POINTER`: an SDL_IOStream pointer used for standard input when `SDL_PROP_PROCESS_CREATE_STDIN_NUMBER` is set to `SDL_PROCESS_STDIO_REDIRECT`.
+ * - `SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER`: an SDL_ProcessIO value describing where standard output for the process goes go, defaults to `SDL_PROCESS_STDIO_INHERITED`.
+ * - `SDL_PROP_PROCESS_CREATE_STDOUT_POINTER`: an SDL_IOStream pointer used for standard output when `SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER` is set to `SDL_PROCESS_STDIO_REDIRECT`.
+ * - `SDL_PROP_PROCESS_CREATE_STDERR_NUMBER`: an SDL_ProcessIO value describing where standard error for the process goes go, defaults to `SDL_PROCESS_STDIO_INHERITED`.
+ * - `SDL_PROP_PROCESS_CREATE_STDERR_POINTER`: an SDL_IOStream pointer used for standard error when `SDL_PROP_PROCESS_CREATE_STDERR_NUMBER` is set to `SDL_PROCESS_STDIO_REDIRECT`.
+ * - `SDL_PROP_PROCESS_CREATE_STDERR_TO_STDOUT_BOOLEAN`: true if the error output of the process should be redirected into the standard output of the process. This property has no effect if `SDL_PROP_PROCESS_CREATE_STDERR_NUMBER` is set.
+ *
+ * On POSIX platforms, wait() and waitpid(-1, ...) should not be called, and SIGCHLD should not be ignored or handled because those would prevent SDL from properly tracking the lifetime of the underlying process. You should use SDL_WaitProcess() instead.
+ *
+ * \param props the properties to use.
+ * \returns the newly created and running process, or NULL if the process couldn't be created.
+ *
+ * \sa SDL_CreateProcess
+ * \sa SDL_GetProcessProperties
+ * \sa SDL_ReadProcess
+ * \sa SDL_WriteProcess
+ * \sa SDL_KillProcess
+ * \sa SDL_WaitProcess
+ * \sa SDL_DestroyProcess
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern SDL_DECLSPEC SDL_Process *SDLCALL SDL_CreateProcessWithProperties(SDL_PropertiesID props);
+
+#define SDL_PROP_PROCESS_CREATE_ARGS_POINTER                "SDL.process.create.args"
+#define SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER         "SDL.process.create.environment"
+#define SDL_PROP_PROCESS_CREATE_STDIN_NUMBER                "SDL.process.create.stdin_option"
+#define SDL_PROP_PROCESS_CREATE_STDIN_POINTER               "SDL.process.create.stdin_source"
+#define SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER               "SDL.process.create.stdout_option"
+#define SDL_PROP_PROCESS_CREATE_STDOUT_POINTER              "SDL.process.create.stdout_source"
+#define SDL_PROP_PROCESS_CREATE_STDERR_NUMBER               "SDL.process.create.stderr_option"
+#define SDL_PROP_PROCESS_CREATE_STDERR_POINTER              "SDL.process.create.stderr_source"
+#define SDL_PROP_PROCESS_CREATE_STDERR_TO_STDOUT_BOOLEAN    "SDL.process.create.stderr_to_stdout"
+
+/**
+ * Get the properties associated with a process.
+ *
+ * The following read-only properties are provided by SDL:
+ *
+ * - `SDL_PROP_PROCESS_PID_NUMBER`: the process ID of the process.
+ * - `SDL_PROP_PROCESS_STDIN_POINTER`: an SDL_IOStream that can be used to write input to the process, if it was created with `SDL_PROP_PROCESS_CREATE_STDIN_NUMBER` set to `SDL_PROCESS_STDIO_APP`.
+ * - `SDL_PROP_PROCESS_STDOUT_POINTER`: a non-blocking SDL_IOStream that can be used to read output from the process, if it was created with `SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER` set to `SDL_PROCESS_STDIO_APP`.
+ * - `SDL_PROP_PROCESS_STDERR_POINTER`: a non-blocking SDL_IOStream that can be used to read error output from the process, if it was created with `SDL_PROP_PROCESS_CREATE_STDERR_NUMBER` set to `SDL_PROCESS_STDIO_APP`.
+ *
+ * \param process the process to query.
+ * \returns a valid property ID on success or 0 on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \sa SDL_CreateProcess
+ * \sa SDL_CreateProcessWithProperties
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern SDL_DECLSPEC SDL_PropertiesID SDL_GetProcessProperties(SDL_Process *process);
+
+#define SDL_PROP_PROCESS_PID_NUMBER     "SDL.process.pid"
+#define SDL_PROP_PROCESS_STDIN_POINTER  "SDL.process.stdin"
+#define SDL_PROP_PROCESS_STDOUT_POINTER "SDL.process.stdout"
+#define SDL_PROP_PROCESS_STDERR_POINTER "SDL.process.stderr"
+
+/**
+ * Read all the output from a process.
+ *
+ * If a process was created with I/O enabled, you can use this function to read the output. This function blocks until the process is complete, capturing all output, and providing the process exit code.
+ *
+ * This is just a convenience function. If you need more control over the process, you can get the output stream from the process properties and read it directly.
+ *
+ * The data is allocated with a zero byte at the end (null terminated) for
+ * convenience. This extra byte is not included in the value reported via
+ * `datasize`.
+ *
+ * The data should be freed with SDL_free().
+ *
+ * \param process The process to read.
+ * \param datasize a pointer filled in with the number of bytes read, may be NULL.
+ * \param exitcode a pointer filled in with the process exit code if the process has exited, may be NULL.
+ * \returns the data or NULL on failure; call SDL_GetError() for more
+ *          information.
+ *
+ * \sa SDL_CreateProcess
+ * \sa SDL_CreateProcessWithProperties
+ * \sa SDL_GetProcessProperties
+ * \sa SDL_WriteProcess
+ * \sa SDL_DestroyProcess
+ *
+ * \threadsafety This function is not thread safe.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern SDL_DECLSPEC void * SDLCALL SDL_ReadProcess(SDL_Process *process, size_t *datasize, int *exitcode);
+
+/**
+ * Write to a process.
+ *
+ * If a process was created with I/O enabled, you can use this function to send data as input to the process. This function blocks until the data is written.
+ *
+ * This is just a convenience function. If the process is structured so it takes large amounts of input and generates lots of output, you should get the input and output streams from the process properties and handle them simultaneously to prevent the process from being blocked waiting for I/O.
+ *
+ * \param process The process to write.
+ * \param ptr a pointer to a buffer containing data to write.
+ * \param size the number of bytes to write.
+ * \param closeio if SDL_TRUE, closes the process input before returning,
+ *                even in the case of an error.
+ * \returns SDL_TRUE on success or SDL_FALSE on failure; call SDL_GetError()
+ *          for more information.
+ *
+ * \sa SDL_CreateProcess
+ * \sa SDL_CreateProcessWithProperties
+ * \sa SDL_GetProcessProperties
+ * \sa 

(Patch may be truncated, please check the link at the top of this post.)