SDL: Cache window manipulation calls while an SDL_Window is hidden and replay them when the window is set visible

From 4e9cfad5587b19786b7746fc8697d6131e319438 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 19 May 2023 10:23:16 -0700
Subject: [PATCH] Cache window manipulation calls while an SDL_Window is hidden
 and replay them when the window is set visible

- SDL_MaximizeWindow, MinimizeWindow, SetFullScreenWindow, etc are not meant to show the window if it isn't currently visible, but on some platforms
  (e.g. Windows) it isn't possible to set this state without also showing the window so cache the flags in a pending_flags field until SDL_ShowWindow is
  called. Then replay the pending flags through ApplyPendingFlags (hoisted out from SDL_FinishWindowCreation).
---
 src/video/SDL_sysvideo.h |  1 +
 src/video/SDL_video.c    | 53 +++++++++++++++++++++++++++++++++++++---
 2 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index c92dd195dd28..5590e941cd08 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -78,6 +78,7 @@ struct SDL_Window
     int max_w, max_h;
     int last_pixel_w, last_pixel_h;
     Uint32 flags;
+    Uint32 pending_flags;
     float display_scale;
     SDL_bool fullscreen_exclusive;  /* The window is currently fullscreen exclusive */
     SDL_DisplayID last_fullscreen_exclusive_display;  /* The last fullscreen_exclusive display */
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 974ac30b5df9..e7dc22635bb8 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -1728,10 +1728,8 @@ void SDL_ToggleDragAndDropSupport(void)
     }
 }
 
-static void SDL_FinishWindowCreation(SDL_Window *window, Uint32 flags)
+static void ApplyWindowFlags(SDL_Window *window, Uint32 flags)
 {
-    PrepareDragAndDropSupport(window);
-
     if (flags & SDL_WINDOW_MAXIMIZED) {
         SDL_MaximizeWindow(window);
     }
@@ -1752,6 +1750,12 @@ static void SDL_FinishWindowCreation(SDL_Window *window, Uint32 flags)
     if (flags & SDL_WINDOW_KEYBOARD_GRABBED) {
         SDL_SetWindowKeyboardGrab(window, SDL_TRUE);
     }
+}
+
+static void SDL_FinishWindowCreation(SDL_Window *window, Uint32 flags)
+{
+    PrepareDragAndDropSupport(window);
+    ApplyWindowFlags(window, flags);
     if (!(flags & SDL_WINDOW_HIDDEN)) {
         SDL_ShowWindow(window);
     }
@@ -2764,6 +2768,12 @@ int SDL_ShowWindow(SDL_Window *window)
     }
     SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0);
 
+    /* Set window state if we have pending window flags cached */
+    if (window->pending_flags) {
+        ApplyWindowFlags(window, window->pending_flags);
+        window->pending_flags = 0;
+    }
+
     /* Restore child windows */
     for (child = window->first_child; child != NULL; child = child->next_sibling) {
         if (!child->restore_on_show && (child->flags & SDL_WINDOW_HIDDEN)) {
@@ -2825,6 +2835,11 @@ int SDL_MaximizeWindow(SDL_Window *window)
         return 0;
     }
 
+    if (window->flags & SDL_WINDOW_HIDDEN) {
+        window->pending_flags |= SDL_WINDOW_MAXIMIZED;
+        return 0;
+    }
+
     /* !!! FIXME: should this check if the window is resizable? */
 
     if (_this->MaximizeWindow) {
@@ -2850,6 +2865,11 @@ int SDL_MinimizeWindow(SDL_Window *window)
         return 0;
     }
 
+    if (window->flags & SDL_WINDOW_HIDDEN) {
+        window->pending_flags |= SDL_WINDOW_MINIMIZED;
+        return 0;
+    }
+
     if (!SDL_CanMinimizeWindow(window)) {
         return 0;
     }
@@ -2887,6 +2907,15 @@ int SDL_SetWindowFullscreen(SDL_Window *window, SDL_bool fullscreen)
     CHECK_WINDOW_MAGIC(window, -1);
     CHECK_WINDOW_NOT_POPUP(window, -1);
 
+    if (window->flags & SDL_WINDOW_HIDDEN) {
+        if (fullscreen) {
+            window->pending_flags |= SDL_WINDOW_FULLSCREEN;
+        } else {
+            window->pending_flags &= ~SDL_WINDOW_FULLSCREEN;
+        }
+        return 0;
+    }
+
     if (flags == (window->flags & SDL_WINDOW_FULLSCREEN)) {
         return 0;
     }
@@ -3164,6 +3193,15 @@ int SDL_SetWindowKeyboardGrab(SDL_Window *window, SDL_bool grabbed)
     CHECK_WINDOW_MAGIC(window, -1);
     CHECK_WINDOW_NOT_POPUP(window, -1);
 
+    if (window->flags & SDL_WINDOW_HIDDEN) {
+        if (grabbed) {
+            window->pending_flags |= SDL_WINDOW_KEYBOARD_GRABBED;
+        } else {
+            window->pending_flags &= ~SDL_WINDOW_KEYBOARD_GRABBED;
+        }
+        return 0;
+    }
+
     if (!!grabbed == !!(window->flags & SDL_WINDOW_KEYBOARD_GRABBED)) {
         return 0;
     }
@@ -3181,6 +3219,15 @@ int SDL_SetWindowMouseGrab(SDL_Window *window, SDL_bool grabbed)
     CHECK_WINDOW_MAGIC(window, -1);
     CHECK_WINDOW_NOT_POPUP(window, -1);
 
+    if (window->flags & SDL_WINDOW_HIDDEN) {
+        if (grabbed) {
+            window->pending_flags |= SDL_WINDOW_MOUSE_GRABBED;
+        } else {
+            window->pending_flags &= ~SDL_WINDOW_MOUSE_GRABBED;
+        }
+        return 0;
+    }
+
     if (!!grabbed == !!(window->flags & SDL_WINDOW_MOUSE_GRABBED)) {
         return 0;
     }