SDL: Fixed creating a window with both software and hardware renderer attached

From 48c00bfe6c1588e5982a7be9fe2677d280976692 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 6 Feb 2025 15:22:10 -0800
Subject: [PATCH] Fixed creating a window with both software and hardware
 renderer attached

---
 src/events/SDL_windowevents.c |  4 ++--
 src/render/SDL_render.c       |  5 +++++
 src/render/SDL_sysrender.h    |  3 ---
 src/video/SDL_sysvideo.h      |  3 +++
 src/video/SDL_video.c         | 26 ++++++++++++++++++++++++++
 src/video/SDL_video_c.h       |  3 +++
 6 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/src/events/SDL_windowevents.c b/src/events/SDL_windowevents.c
index fe02450b2102f..e0364f8ee0333 100644
--- a/src/events/SDL_windowevents.c
+++ b/src/events/SDL_windowevents.c
@@ -191,8 +191,8 @@ bool SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent, int data
     event.window.data2 = data2;
     event.window.windowID = window->id;
 
-    SDL_Renderer *renderer = (SDL_Renderer *)SDL_GetPointerProperty(window->props, SDL_PROP_WINDOW_RENDERER_POINTER, NULL);
-    if (renderer) {
+    for (int i = 0; i < window->num_renderers; ++i) {
+        SDL_Renderer *renderer = window->renderers[i];
         SDL_RendererEventWatch(renderer, &event);
     }
 
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index 4051eab5b901b..b8e66532ef618 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -45,6 +45,9 @@ this should probably be removed at some point in the future.  --ryan. */
 #define DONT_DRAW_WHILE_HIDDEN 0
 #endif
 
+#define SDL_PROP_WINDOW_RENDERER_POINTER "SDL.internal.window.renderer"
+#define SDL_PROP_TEXTURE_PARENT_POINTER "SDL.internal.texture.parent"
+
 #define CHECK_RENDERER_MAGIC_BUT_NOT_DESTROYED_FLAG(renderer, result)   \
     if (!SDL_ObjectValid(renderer, SDL_OBJECT_TYPE_RENDERER)) {         \
         SDL_InvalidParamError("renderer");                              \
@@ -1100,6 +1103,7 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
 
     if (window) {
         SDL_SetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_RENDERER_POINTER, renderer);
+        SDL_AddWindowRenderer(window, renderer);
     }
 
     SDL_SetRenderViewport(renderer, NULL);
@@ -5207,6 +5211,7 @@ void SDL_DestroyRendererWithoutFreeing(SDL_Renderer *renderer)
         if (SDL_GetPointerProperty(props, SDL_PROP_WINDOW_RENDERER_POINTER, NULL) == renderer) {
             SDL_ClearProperty(props, SDL_PROP_WINDOW_RENDERER_POINTER);
         }
+        SDL_RemoveWindowRenderer(renderer->window, renderer);
     }
 
     SDL_DiscardAllCommands(renderer);
diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
index 980c8ffd146df..375419a06acaf 100644
--- a/src/render/SDL_sysrender.h
+++ b/src/render/SDL_sysrender.h
@@ -32,9 +32,6 @@
 extern "C" {
 #endif
 
-#define SDL_PROP_WINDOW_RENDERER_POINTER "SDL.internal.window.renderer"
-#define SDL_PROP_TEXTURE_PARENT_POINTER "SDL.internal.texture.parent"
-
 typedef enum SDL_TextureAddressMode
 {
     SDL_TEXTURE_ADDRESS_AUTO,
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index 418b8239f168d..e043305aa06b8 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -124,6 +124,9 @@ struct SDL_Window
 
     SDL_PropertiesID props;
 
+    int num_renderers;
+    SDL_Renderer **renderers;
+
     SDL_WindowData *internal;
 
     SDL_Window *prev;
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 37fd8bb28ad00..e773969183f01 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -4132,6 +4132,31 @@ SDL_Window *SDL_GetToplevelForKeyboardFocus(void)
     return focus;
 }
 
+bool SDL_AddWindowRenderer(SDL_Window *window, SDL_Renderer *renderer)
+{
+    SDL_Renderer **renderers = (SDL_Renderer **)SDL_realloc(window->renderers, (window->num_renderers + 1) * sizeof(*renderers));
+    if (!renderers) {
+        return false;
+    }
+
+    window->renderers = renderers;
+    window->renderers[window->num_renderers++] = renderer;
+    return true;
+}
+
+void SDL_RemoveWindowRenderer(SDL_Window *window, SDL_Renderer *renderer)
+{
+    for (int i = 0; i < window->num_renderers; ++i) {
+        if (window->renderers[i] == renderer) {
+            if (i < (window->num_renderers - 1)) {
+                SDL_memmove(&window->renderers[i], &window->renderers[i + 1], (window->num_renderers - i - 1) * sizeof(window->renderers[i]));
+            }
+            --window->num_renderers;
+            break;
+        }
+    }
+}
+
 void SDL_DestroyWindow(SDL_Window *window)
 {
     CHECK_WINDOW_MAGIC(window,);
@@ -4238,6 +4263,7 @@ void SDL_DestroyWindow(SDL_Window *window)
         _this->windows = window->next;
     }
 
+    SDL_free(window->renderers);
     SDL_free(window);
 
 #ifdef SDL_VIDEO_DRIVER_UIKIT
diff --git a/src/video/SDL_video_c.h b/src/video/SDL_video_c.h
index b3f4350a926ad..fc0843b168afe 100644
--- a/src/video/SDL_video_c.h
+++ b/src/video/SDL_video_c.h
@@ -63,4 +63,7 @@ extern bool SDL_SetWindowTextureVSync(struct SDL_VideoDevice *_this, SDL_Window
 const char *SDL_GetCSSCursorName(SDL_SystemCursor id, const char **fallback_name);
 #endif
 
+extern bool SDL_AddWindowRenderer(SDL_Window *window, SDL_Renderer *renderer);
+extern void SDL_RemoveWindowRenderer(SDL_Window *window, SDL_Renderer *renderer);
+
 #endif // SDL_video_c_h_