SDL: Added SDL_GetWindows()

From 0ff5c054862caebe26e8a3febc366c4098353267 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 24 Jun 2024 12:22:12 -0700
Subject: [PATCH] Added SDL_GetWindows()

---
 include/SDL3/SDL_video.h          | 14 ++++++++++-
 src/dynapi/SDL_dynapi.sym         |  1 +
 src/dynapi/SDL_dynapi_overrides.h |  1 +
 src/dynapi/SDL_dynapi_procs.h     |  1 +
 src/video/SDL_video.c             | 42 +++++++++++++++++++++++++++++--
 test/testautomation_video.c       |  8 ++++++
 6 files changed, 64 insertions(+), 3 deletions(-)

diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h
index 6ff85cf0936dc..5943d9d2b8c76 100644
--- a/include/SDL3/SDL_video.h
+++ b/include/SDL3/SDL_video.h
@@ -384,7 +384,7 @@ extern SDL_DECLSPEC SDL_SystemTheme SDLCALL SDL_GetSystemTheme(void);
 /**
  * Get a list of currently connected displays.
  *
- * \param count a pointer filled in with the number of displays returned.
+ * \param count a pointer filled in with the number of displays returned, may be NULL.
  * \returns a 0 terminated array of display instance IDs which should be freed
  *          with SDL_free(), or NULL on error; call SDL_GetError() for more
  *          details.
@@ -783,6 +783,18 @@ extern SDL_DECLSPEC void *SDLCALL SDL_GetWindowICCProfile(SDL_Window *window, si
  */
 extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetWindowPixelFormat(SDL_Window *window);
 
+/**
+ * Get a list of valid windows.
+ *
+ * \param count a pointer filled in with the number of windows returned, may be NULL.
+ * \returns a 0 terminated array of window pointers which should be freed
+ *          with SDL_free(), or NULL on error; call SDL_GetError() for more
+ *          details.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern SDL_DECLSPEC SDL_Window **SDLCALL SDL_GetWindows(int *count);
+
 /**
  * Create a window with the specified dimensions and flags.
  *
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 02fe914686724..e3bcc9d445873 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -480,6 +480,7 @@ SDL3_0.0.0 {
     SDL_GetWindowSurface;
     SDL_GetWindowSurfaceVSync;
     SDL_GetWindowTitle;
+    SDL_GetWindows;
     SDL_GlobDirectory;
     SDL_GlobStorageDirectory;
     SDL_HapticEffectSupported;
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index ba1777a3731f7..2d71ca64a5b80 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -505,6 +505,7 @@
 #define SDL_GetWindowSurface SDL_GetWindowSurface_REAL
 #define SDL_GetWindowSurfaceVSync SDL_GetWindowSurfaceVSync_REAL
 #define SDL_GetWindowTitle SDL_GetWindowTitle_REAL
+#define SDL_GetWindows SDL_GetWindows_REAL
 #define SDL_GlobDirectory SDL_GlobDirectory_REAL
 #define SDL_GlobStorageDirectory SDL_GlobStorageDirectory_REAL
 #define SDL_HapticEffectSupported SDL_HapticEffectSupported_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 67e62d7b6bf6a..a1528faeba295 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -525,6 +525,7 @@ SDL_DYNAPI_PROC(int,SDL_GetWindowSizeInPixels,(SDL_Window *a, int *b, int *c),(a
 SDL_DYNAPI_PROC(SDL_Surface*,SDL_GetWindowSurface,(SDL_Window *a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_GetWindowSurfaceVSync,(SDL_Window *a, int *b),(a,b),return)
 SDL_DYNAPI_PROC(const char*,SDL_GetWindowTitle,(SDL_Window *a),(a),return)
+SDL_DYNAPI_PROC(SDL_Window**,SDL_GetWindows,(int *a),(a),return)
 SDL_DYNAPI_PROC(char**,SDL_GlobDirectory,(const char *a, const char *b, SDL_GlobFlags c, int *d),(a,b,c,d),return)
 SDL_DYNAPI_PROC(char**,SDL_GlobStorageDirectory,(SDL_Storage *a, const char *b, const char *c, SDL_GlobFlags d, int *e),(a,b,c,d,e),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_HapticEffectSupported,(SDL_Haptic *a, const SDL_HapticEffect *b),(a,b),return)
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 2ffb28ff4576c..37628f086084b 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -1999,6 +1999,44 @@ void SDL_ToggleDragAndDropSupport(void)
     }
 }
 
+SDL_Window **SDLCALL SDL_GetWindows(int *count)
+{
+    if (count) {
+        *count = 0;
+    }
+
+    if (!_this) {
+        SDL_UninitializedVideo();
+        return NULL;
+    }
+
+    SDL_Window *window;
+    int num_added = 0;
+    int num_windows = 0;
+    for (window = _this->windows; window; window = window->next) {
+        ++num_windows;
+    }
+
+    SDL_Window **windows = SDL_malloc((num_windows + 1) * sizeof(*windows));
+    if (!windows) {
+        return NULL;
+    }
+
+    for (window = _this->windows; window; window = window->next) {
+        windows[num_added++] = window;
+        if (num_added == num_windows) {
+            // Race condition? Multi-threading not supported, ignore it
+            break;
+        }
+    }
+    windows[num_added] = NULL;
+
+    if (count) {
+        *count = num_added;
+    }
+    return windows;
+}
+
 static void ApplyWindowFlags(SDL_Window *window, SDL_WindowFlags flags)
 {
     if (!SDL_WINDOW_IS_POPUP(window)) {
@@ -3955,7 +3993,7 @@ SDL_bool SDL_ScreenSaverEnabled(void)
 int SDL_EnableScreenSaver(void)
 {
     if (!_this) {
-        return 0;
+        return SDL_UninitializedVideo();
     }
     if (!_this->suspend_screensaver) {
         return 0;
@@ -3971,7 +4009,7 @@ int SDL_EnableScreenSaver(void)
 int SDL_DisableScreenSaver(void)
 {
     if (!_this) {
-        return 0;
+        return SDL_UninitializedVideo();
     }
     if (_this->suspend_screensaver) {
         return 0;
diff --git a/test/testautomation_video.c b/test/testautomation_video.c
index c98cab7e22616..88ae689f08bcc 100644
--- a/test/testautomation_video.c
+++ b/test/testautomation_video.c
@@ -13,8 +13,10 @@
 static SDL_Window *createVideoSuiteTestWindow(const char *title)
 {
     SDL_Window *window;
+    SDL_Window **windows;
     SDL_Event event;
     int w, h;
+    int count;
     SDL_WindowFlags flags;
     SDL_bool needs_renderer = SDL_FALSE;
     SDL_bool needs_events_pumped = SDL_FALSE;
@@ -28,6 +30,12 @@ static SDL_Window *createVideoSuiteTestWindow(const char *title)
     SDLTest_AssertPass("Call to SDL_CreateWindow('Title',%d,%d,%" SDL_PRIu64 ")", w, h, flags);
     SDLTest_AssertCheck(window != NULL, "Validate that returned window struct is not NULL");
 
+    /* Check the window is available in the window list */
+    windows = SDL_GetWindows(&count);
+    SDLTest_AssertCheck(windows != NULL, "Validate that returned window list is not NULL");
+    SDLTest_AssertCheck(windows[0] == window, "Validate that the window is first in the window list");
+    SDL_free(windows);
+
     /* Wayland and XWayland windows require that a frame be presented before they are fully mapped and visible onscreen.
      * This is required for the mouse/keyboard grab tests to pass.
      */