SDL: win32: Retain the WS_MAXIMIZEDBOX style while in fullscreen

From 10a5b388df7102e1fc089b689c4a8a0b6b8da572 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Mon, 20 Jan 2025 14:17:39 -0500
Subject: [PATCH] win32: Retain the WS_MAXIMIZEDBOX style while in fullscreen

This needs to be preserved while in fullscreen, or leaving fullscreen for the maximized state can cause the taskbar to disappear with borderless windows.
---
 src/video/windows/SDL_windowsevents.c | 14 +++++++++-----
 src/video/windows/SDL_windowswindow.c | 16 +++++++++-------
 src/video/windows/SDL_windowswindow.h |  2 +-
 3 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 3ef492ec5679b..0fcb08b00c27e 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -1611,13 +1611,17 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
                 SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
             }
             SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0);
-            data->force_resizable = true;
+            data->force_ws_maximizebox = true;
         } else if (data->window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED)) {
             SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
 
-            // If resizable was forced on for the maximized window, clear the style flags now.
-            data->force_resizable = false;
-            WIN_SetWindowResizable(SDL_GetVideoDevice(), data->window, !!(data->window->flags & SDL_WINDOW_RESIZABLE));
+            /* If resizable was forced on for the maximized window, clear the style flags now,
+             * but not if the window is fullscreen, as this needs to be preserved in that case.
+             */
+            if (!(data->window->flags & SDL_WINDOW_FULLSCREEN)) {
+                data->force_ws_maximizebox = false;
+                WIN_SetWindowResizable(SDL_GetVideoDevice(), data->window, !!(data->window->flags & SDL_WINDOW_RESIZABLE));
+            }
         }
 
         if (windowpos->flags & SWP_HIDEWINDOW) {
@@ -2038,7 +2042,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
                         params->rgrc[0] = info.rcWork;
                     }
                 }
-            } else if (!(window_flags & SDL_WINDOW_RESIZABLE) && !data->force_resizable) {
+            } else if (!(window_flags & SDL_WINDOW_RESIZABLE) && !data->force_ws_maximizebox) {
                 int w, h;
                 if (data->window->last_size_pending) {
                     w = data->window->pending.w;
diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c
index 6ca0bfde57bc6..1711fc0a597c0 100644
--- a/src/video/windows/SDL_windowswindow.c
+++ b/src/video/windows/SDL_windowswindow.c
@@ -185,22 +185,24 @@ static DWORD GetWindowStyle(SDL_Window *window)
         /* The WS_MAXIMIZEBOX style flag needs to be retained for as long as the window is maximized,
          * or restoration from minimized can fail, and leaving maximized can result in an odd size.
          */
-        if ((window->flags & SDL_WINDOW_RESIZABLE) || (window->internal && window->internal->force_resizable)) {
+        if (window->flags & SDL_WINDOW_RESIZABLE) {
             /* You can have a borderless resizable window, but Windows doesn't always draw it correctly,
                see https://bugzilla.libsdl.org/show_bug.cgi?id=4466
              */
             if (!(window->flags & SDL_WINDOW_BORDERLESS) ||
                 SDL_GetHintBoolean("SDL_BORDERLESS_RESIZABLE_STYLE", true)) {
                 style |= STYLE_RESIZABLE;
-            } else if (window->flags & SDL_WINDOW_BORDERLESS) {
-                /* Even if the resizable style hint isn't set, WS_MAXIMIZEBOX is still needed, or
-                 * maximizing the window will make it fullscreen and cover the taskbar, instead
-                 * of entering a normal maximized state that fills the usable desktop area.
-                 */
-                style |= WS_MAXIMIZEBOX;
             }
         }
 
+        if (window->internal && window->internal->force_ws_maximizebox) {
+            /* Even if the resizable flag is cleared, WS_MAXIMIZEBOX is still needed as long
+             * as the window is maximized, or de-maximizing or minimizing and restoring the
+             * maximized window can result in the window disappearing or being the wrong size.
+             */
+            style |= WS_MAXIMIZEBOX;
+        }
+
         // Need to set initialize minimize style, or when we call ShowWindow with WS_MINIMIZE it will activate a random window
         if (window->flags & SDL_WINDOW_MINIMIZED) {
             style |= WS_MINIMIZE;
diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h
index 1430dee572b08..d8165d0b52e03 100644
--- a/src/video/windows/SDL_windowswindow.h
+++ b/src/video/windows/SDL_windowswindow.h
@@ -81,7 +81,7 @@ struct SDL_WindowData
     bool skip_update_clipcursor;
     bool windowed_mode_was_maximized;
     bool in_window_deactivation;
-    bool force_resizable;
+    bool force_ws_maximizebox;
     bool disable_move_size_events;
     RECT initial_size_rect;
     RECT cursor_clipped_rect; // last successfully committed clipping rect for this window