SDL: Add convenience functions to get process IOStreams

From f6f49de1342ad723686143c83492a3b132d4f3d5 Mon Sep 17 00:00:00 2001
From: ritalat <[EMAIL REDACTED]>
Date: Sat, 14 Sep 2024 03:55:53 +0300
Subject: [PATCH] Add convenience functions to get process IOStreams

---
 include/SDL3/SDL_process.h        | 40 +++++++++++++++++++++++++++++++
 src/dynapi/SDL_dynapi.sym         |  2 ++
 src/dynapi/SDL_dynapi_overrides.h |  2 ++
 src/dynapi/SDL_dynapi_procs.h     |  2 ++
 src/process/SDL_process.c         | 32 +++++++++++++++++++++++++
 test/testprocess.c                | 10 ++++----
 6 files changed, 83 insertions(+), 5 deletions(-)

diff --git a/include/SDL3/SDL_process.h b/include/SDL3/SDL_process.h
index 2af0bdcc91ad4..17b727c5405a8 100644
--- a/include/SDL3/SDL_process.h
+++ b/include/SDL3/SDL_process.h
@@ -310,6 +310,46 @@ extern SDL_DECLSPEC void * SDLCALL SDL_ReadProcess(SDL_Process *process, size_t
  */
 extern SDL_DECLSPEC SDL_bool SDLCALL SDL_WriteProcess(SDL_Process *process, const void *ptr, size_t size, SDL_bool closeio);
 
+/**
+ * Get the SDL_IOStream associated with process standard output.
+ *
+ * The process must have been created with I/O enabled.
+ *
+ * This is just a convenience function that retrieves the SDL_IOStream from the process `SDL_PROP_PROCESS_STDOUT_POINTER` property.
+ *
+ * \param process The process to get the output stream for.
+ * \returns the output stream 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_CreateProcess
+ * \sa SDL_CreateProcessWithProperties
+ */
+extern SDL_DECLSPEC SDL_IOStream *SDLCALL SDL_GetProcessOutputStream(SDL_Process *process);
+
+/**
+ * Get the SDL_IOStream associated with process standard input.
+ *
+ * The process must have been created with I/O enabled.
+ *
+ * This is just a convenience function that retrieves the SDL_IOStream from the process `SDL_PROP_PROCESS_STDIN_POINTER` property.
+ *
+ * \param process The process to get the input stream for.
+ * \returns the input stream 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_CreateProcess
+ * \sa SDL_CreateProcessWithProperties
+ */
+extern SDL_DECLSPEC SDL_IOStream *SDLCALL SDL_GetProcessInputStream(SDL_Process *process);
+
 /**
  * Stop a process.
  *
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 21b51d2db3966..3067f0f4c446a 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -431,6 +431,8 @@ SDL3_0.0.0 {
     SDL_GetPreferredLocales;
     SDL_GetPrimaryDisplay;
     SDL_GetPrimarySelectionText;
+    SDL_GetProcessInputStream;
+    SDL_GetProcessOutputStream;
     SDL_GetProcessProperties;
     SDL_GetPropertyType;
     SDL_GetRGB;
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 7aa3c9bdc0d9a..f8ad7502a70ab 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -456,6 +456,8 @@
 #define SDL_GetPreferredLocales SDL_GetPreferredLocales_REAL
 #define SDL_GetPrimaryDisplay SDL_GetPrimaryDisplay_REAL
 #define SDL_GetPrimarySelectionText SDL_GetPrimarySelectionText_REAL
+#define SDL_GetProcessInputStream SDL_GetProcessInputStream_REAL
+#define SDL_GetProcessOutputStream SDL_GetProcessOutputStream_REAL
 #define SDL_GetProcessProperties SDL_GetProcessProperties_REAL
 #define SDL_GetPropertyType SDL_GetPropertyType_REAL
 #define SDL_GetRGB SDL_GetRGB_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 069d04ccc59d2..1395c28a7cc37 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -476,6 +476,8 @@ SDL_DYNAPI_PROC(char*,SDL_GetPrefPath,(const char *a, const char *b),(a,b),retur
 SDL_DYNAPI_PROC(SDL_Locale**,SDL_GetPreferredLocales,(int *a),(a),return)
 SDL_DYNAPI_PROC(SDL_DisplayID,SDL_GetPrimaryDisplay,(void),(),return)
 SDL_DYNAPI_PROC(char*,SDL_GetPrimarySelectionText,(void),(),return)
+SDL_DYNAPI_PROC(SDL_IOStream*,SDL_GetProcessInputStream,(SDL_Process *a),(a),return)
+SDL_DYNAPI_PROC(SDL_IOStream*,SDL_GetProcessOutputStream,(SDL_Process *a),(a),return)
 SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetProcessProperties,(SDL_Process *a),(a),return)
 SDL_DYNAPI_PROC(SDL_PropertyType,SDL_GetPropertyType,(SDL_PropertiesID a, const char *b),(a,b),return)
 SDL_DYNAPI_PROC(void,SDL_GetRGB,(Uint32 a, const SDL_PixelFormatDetails *b, const SDL_Palette *c, Uint8 *d, Uint8 *e, Uint8 *f),(a,b,c,d,e,f),)
diff --git a/src/process/SDL_process.c b/src/process/SDL_process.c
index 4f0ca27120f60..6521b1296def5 100644
--- a/src/process/SDL_process.c
+++ b/src/process/SDL_process.c
@@ -153,6 +153,38 @@ SDL_bool SDL_WriteProcess(SDL_Process *process, const void *ptr, size_t size, SD
     return result;
 }
 
+SDL_IOStream *SDL_GetProcessOutputStream(SDL_Process *process)
+{
+    if (!process) {
+        SDL_InvalidParamError("process");
+        return NULL;
+    }
+
+    SDL_IOStream *io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDOUT_POINTER, NULL);
+    if (!io) {
+        SDL_SetError("Process not created with I/O enabled");
+        return NULL;
+    }
+
+    return io;
+}
+
+SDL_IOStream *SDL_GetProcessInputStream(SDL_Process *process)
+{
+    if (!process) {
+        SDL_InvalidParamError("process");
+        return NULL;
+    }
+
+    SDL_IOStream *io = (SDL_IOStream *)SDL_GetPointerProperty(process->props, SDL_PROP_PROCESS_STDIN_POINTER, NULL);
+    if (!io) {
+        SDL_SetError("Process not created with I/O enabled");
+        return NULL;
+    }
+
+    return io;
+}
+
 SDL_bool SDL_KillProcess(SDL_Process *process, SDL_bool force)
 {
     if (!process) {
diff --git a/test/testprocess.c b/test/testprocess.c
index 0c6a13886b6f7..cc14fe780ec2e 100644
--- a/test/testprocess.c
+++ b/test/testprocess.c
@@ -172,7 +172,7 @@ static int SDLCALL process_testInheritedEnv(void *arg)
     pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0);
     SDLTest_AssertCheck(pid != 0, "Checking process ID, expected non-zero, got %" SDL_PRIs64, pid);
 
-    process_stdout = (SDL_IOStream *)SDL_GetPointerProperty(props, SDL_PROP_PROCESS_STDOUT_POINTER, NULL);
+    process_stdout = SDL_GetProcessOutputStream(process);
     SDLTest_AssertCheck(process_stdout != NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDOUT_POINTER) returns a valid IO stream");
     if (!process_stdout) {
         goto failed;
@@ -250,7 +250,7 @@ static int SDLCALL process_testNewEnv(void *arg)
     pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0);
     SDLTest_AssertCheck(pid != 0, "Checking process ID, expected non-zero, got %" SDL_PRIs64, pid);
 
-    process_stdout = (SDL_IOStream *)SDL_GetPointerProperty(props, SDL_PROP_PROCESS_STDOUT_POINTER, NULL);
+    process_stdout = SDL_GetProcessOutputStream(process);
     SDLTest_AssertCheck(process_stdout != NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDOUT_POINTER) returns a valid IO stream");
     if (!process_stdout) {
         goto failed;
@@ -326,9 +326,9 @@ static int process_testStdinToStdout(void *arg)
     pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0);
     SDLTest_AssertCheck(pid != 0, "Checking process ID, expected non-zero, got %" SDL_PRIs64, pid);
 
-    process_stdin = (SDL_IOStream *)SDL_GetPointerProperty(props, SDL_PROP_PROCESS_STDIN_POINTER, NULL);
+    process_stdin = SDL_GetProcessInputStream(process);
     SDLTest_AssertCheck(process_stdin != NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDIN_POINTER) returns a valid IO stream");
-    process_stdout = (SDL_IOStream *)SDL_GetPointerProperty(props, SDL_PROP_PROCESS_STDOUT_POINTER, NULL);
+    process_stdout = SDL_GetProcessOutputStream(process);
     SDLTest_AssertCheck(process_stdout != NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDOUT_POINTER) returns a valid IO stream");
     if (!process_stdin || !process_stdout) {
         goto failed;
@@ -370,7 +370,7 @@ static int process_testStdinToStdout(void *arg)
     /* Closing stdin of `subprocessstdin --stdin-to-stdout` should close the process */
     SDL_CloseIO(process_stdin);
 
-    process_stdin = (SDL_IOStream *)SDL_GetPointerProperty(props, SDL_PROP_PROCESS_STDIN_POINTER, NULL);
+    process_stdin = SDL_GetProcessInputStream(process);
     SDLTest_AssertCheck(process_stdin == NULL, "SDL_GetPointerProperty(SDL_PROP_PROCESS_STDIN_POINTER) is cleared after close");
 
     SDLTest_AssertPass("About to wait on process");