SDL: Removed duplicated window size events, and added SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED

From bf4095359c5f071cf0d0d5bd32fa8d900d3ef290 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 28 Jan 2023 15:22:16 -0800
Subject: [PATCH] Removed duplicated window size events, and added
 SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED

---
 docs/README-migration.md                 |  4 ++
 include/SDL3/SDL_events.h                | 46 +++++++-------
 src/core/winrt/SDL_winrtapp_direct3d.cpp |  2 +-
 src/events/SDL_events.c                  |  2 +-
 src/events/SDL_windowevents.c            | 80 +++++++-----------------
 src/render/SDL_render.c                  |  4 +-
 src/render/direct3d/SDL_render_d3d.c     |  2 +-
 src/render/direct3d11/SDL_render_d3d11.c |  2 +-
 src/render/direct3d12/SDL_render_d3d12.c |  2 +-
 src/render/opengl/SDL_render_gl.c        |  2 +-
 src/render/software/SDL_render_sw.c      |  2 +-
 src/test/SDL_test_common.c               |  8 +--
 src/video/SDL_sysvideo.h                 |  3 +
 src/video/SDL_video.c                    | 63 ++++++++++---------
 src/video/cocoa/SDL_cocoametalview.m     |  2 +-
 src/video/windows/SDL_windowsevents.c    | 15 +----
 test/testdrawchessboard.c                |  4 +-
 17 files changed, 101 insertions(+), 142 deletions(-)

diff --git a/docs/README-migration.md b/docs/README-migration.md
index ad4c1d38ef48..39079543b490 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -124,6 +124,10 @@ The SDL_DISPLAYEVENT_* events have been moved to top level events, and SDL_DISPL
 
 The SDL_WINDOWEVENT_* events have been moved to top level events, and SDL_WINDOWEVENT has been removed. In general, handling this change just means checking for the individual events instead of first checking for SDL_WINDOWEVENT and then checking for window events. You can compare the event >= SDL_EVENT_WINDOW_FIRST and <= SDL_EVENT_WINDOW_LAST if you need to see whether it's a window event.
 
+The SDL_EVENT_WINDOW_RESIZED event is always sent, even in response to SDL_SetWindowSize().
+
+The SDL_EVENT_WINDOW_SIZE_CHANGED event has been removed, and you can use SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED to detect window backbuffer size changes.
+
 SDL_QUERY, SDL_IGNORE, SDL_ENABLE, and SDL_DISABLE have been removed. You can use the functions SDL_SetEventEnabled() and SDL_EventEnabled() to set and query event processing state.
 
 The following symbols have been renamed:
diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h
index b719417fae72..ff50789013db 100644
--- a/include/SDL3/SDL_events.h
+++ b/include/SDL3/SDL_events.h
@@ -98,32 +98,28 @@ typedef enum
 
     /* Window events */
     /* 0x200 was SDL_WINDOWEVENT, reserve the number for sdl2-compat */
-    SDL_EVENT_SYSWM     = 0x201,     /**< System specific event */
-    SDL_EVENT_WINDOW_SHOWN,          /**< Window has been shown */
-    SDL_EVENT_WINDOW_HIDDEN,         /**< Window has been hidden */
-    SDL_EVENT_WINDOW_EXPOSED,        /**< Window has been exposed and should be
-                                         redrawn */
-    SDL_EVENT_WINDOW_MOVED,          /**< Window has been moved to data1, data2
-                                     */
-    SDL_EVENT_WINDOW_RESIZED,        /**< Window has been resized to data1xdata2 */
-    SDL_EVENT_WINDOW_SIZE_CHANGED,   /**< The window size has changed, either as
-                                         a result of an API call or through the
-                                         system or user changing the window size. */
-    SDL_EVENT_WINDOW_MINIMIZED,      /**< Window has been minimized */
-    SDL_EVENT_WINDOW_MAXIMIZED,      /**< Window has been maximized */
-    SDL_EVENT_WINDOW_RESTORED,       /**< Window has been restored to normal size
-                                         and position */
-    SDL_EVENT_WINDOW_MOUSE_ENTER,          /**< Window has gained mouse focus */
-    SDL_EVENT_WINDOW_MOUSE_LEAVE,          /**< Window has lost mouse focus */
-    SDL_EVENT_WINDOW_FOCUS_GAINED,   /**< Window has gained keyboard focus */
-    SDL_EVENT_WINDOW_FOCUS_LOST,     /**< Window has lost keyboard focus */
-    SDL_EVENT_WINDOW_CLOSE_REQUESTED,          /**< The window manager requests that the window be closed */
-    SDL_EVENT_WINDOW_TAKE_FOCUS,     /**< Window is being offered a focus (should SetWindowInputFocus() on itself or a subwindow, or ignore) */
-    SDL_EVENT_WINDOW_HIT_TEST,       /**< Window had a hit test that wasn't SDL_HITTEST_NORMAL. */
-    SDL_EVENT_WINDOW_ICCPROF_CHANGED,/**< The ICC profile of the window's display has changed. */
-    SDL_EVENT_WINDOW_DISPLAY_CHANGED,/**< Window has been moved to display data1. */
+    SDL_EVENT_SYSWM     = 0x201,        /**< System specific event */
+    SDL_EVENT_WINDOW_SHOWN,             /**< Window has been shown */
+    SDL_EVENT_WINDOW_HIDDEN,            /**< Window has been hidden */
+    SDL_EVENT_WINDOW_EXPOSED,           /**< Window has been exposed and should be redrawn */
+    SDL_EVENT_WINDOW_MOVED,             /**< Window has been moved to data1, data2 */
+    SDL_EVENT_WINDOW_RESIZED,           /**< Window has been resized to data1xdata2 */
+    /* 0x207 was SDL_EVENT_WINDOW_SIZE_CHANGED, reserve the number for sdl2-compat */
+    SDL_EVENT_WINDOW_MINIMIZED = 0x208, /**< Window has been minimized */
+    SDL_EVENT_WINDOW_MAXIMIZED,         /**< Window has been maximized */
+    SDL_EVENT_WINDOW_RESTORED,          /**< Window has been restored to normal size and position */
+    SDL_EVENT_WINDOW_MOUSE_ENTER,       /**< Window has gained mouse focus */
+    SDL_EVENT_WINDOW_MOUSE_LEAVE,       /**< Window has lost mouse focus */
+    SDL_EVENT_WINDOW_FOCUS_GAINED,      /**< Window has gained keyboard focus */
+    SDL_EVENT_WINDOW_FOCUS_LOST,        /**< Window has lost keyboard focus */
+    SDL_EVENT_WINDOW_CLOSE_REQUESTED,   /**< The window manager requests that the window be closed */
+    SDL_EVENT_WINDOW_TAKE_FOCUS,        /**< Window is being offered a focus (should SetWindowInputFocus() on itself or a subwindow, or ignore) */
+    SDL_EVENT_WINDOW_HIT_TEST,          /**< Window had a hit test that wasn't SDL_HITTEST_NORMAL */
+    SDL_EVENT_WINDOW_ICCPROF_CHANGED,   /**< The ICC profile of the window's display has changed */
+    SDL_EVENT_WINDOW_DISPLAY_CHANGED,   /**< Window has been moved to display data1 */
+    SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED,/**< The pixel size of the window has changed to data1xdata2 */
     SDL_EVENT_WINDOW_FIRST = SDL_EVENT_WINDOW_SHOWN,
-    SDL_EVENT_WINDOW_LAST = SDL_EVENT_WINDOW_DISPLAY_CHANGED,
+    SDL_EVENT_WINDOW_LAST = SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED,
 
     /* Keyboard events */
     SDL_EVENT_KEY_DOWN        = 0x300, /**< Key pressed */
diff --git a/src/core/winrt/SDL_winrtapp_direct3d.cpp b/src/core/winrt/SDL_winrtapp_direct3d.cpp
index 15e21d7874da..9c450d345dfe 100644
--- a/src/core/winrt/SDL_winrtapp_direct3d.cpp
+++ b/src/core/winrt/SDL_winrtapp_direct3d.cpp
@@ -237,7 +237,7 @@ void SDL_WinRTApp::OnOrientationChanged(Object ^ sender)
         SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
         int w = (int)SDL_floorf(data->coreWindow->Bounds.Width);
         int h = (int)SDL_floorf(data->coreWindow->Bounds.Height);
-        SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_EVENT_WINDOW_SIZE_CHANGED, w, h);
+        SDL_SendWindowEvent(WINRT_GlobalSDLWindow, SDL_EVENT_WINDOW_RESIZED, w, h);
     }
 #endif
 }
diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c
index 0584d5e28236..74b332599d00 100644
--- a/src/events/SDL_events.c
+++ b/src/events/SDL_events.c
@@ -236,7 +236,6 @@ static void SDL_LogEvent(const SDL_Event *event)
         SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_EXPOSED);
         SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_MOVED);
         SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_RESIZED);
-        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_SIZE_CHANGED);
         SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_MINIMIZED);
         SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_MAXIMIZED);
         SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_RESTORED);
@@ -249,6 +248,7 @@ static void SDL_LogEvent(const SDL_Event *event)
         SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_HIT_TEST);
         SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_ICCPROF_CHANGED);
         SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_DISPLAY_CHANGED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED);
 #undef SDL_WINDOWEVENT_CASE
 
         SDL_EVENT_CASE(SDL_EVENT_SYSWM)
diff --git a/src/events/SDL_windowevents.c b/src/events/SDL_windowevents.c
index d6adace54e52..d50e75e964ab 100644
--- a/src/events/SDL_windowevents.c
+++ b/src/events/SDL_windowevents.c
@@ -25,36 +25,12 @@
 #include "SDL_events_c.h"
 #include "SDL_mouse_c.h"
 
-typedef struct RemovePendingSizeChangedAndResizedEvents_Data
-{
-    const SDL_Event *new_event;
-    SDL_bool saw_resized;
-} RemovePendingSizeChangedAndResizedEvents_Data;
 
-static int SDLCALL RemovePendingSizeChangedAndResizedEvents(void *_userdata, SDL_Event *event)
-{
-    RemovePendingSizeChangedAndResizedEvents_Data *userdata = (RemovePendingSizeChangedAndResizedEvents_Data *)_userdata;
-    const SDL_Event *new_event = userdata->new_event;
-
-    if ((event->type == SDL_EVENT_WINDOW_SIZE_CHANGED ||
-         event->type == SDL_EVENT_WINDOW_RESIZED) &&
-        event->window.windowID == new_event->window.windowID) {
-
-        if (event->type == SDL_EVENT_WINDOW_RESIZED) {
-            userdata->saw_resized = SDL_TRUE;
-        }
-
-        /* We're about to post a new size event, drop the old one */
-        return 0;
-    }
-    return 1;
-}
-
-static int SDLCALL RemovePendingMoveEvents(void *userdata, SDL_Event *event)
+static int SDLCALL RemoveSupercededWindowEvents(void *userdata, SDL_Event *event)
 {
     SDL_Event *new_event = (SDL_Event *)userdata;
 
-    if (event->type == SDL_EVENT_WINDOW_MOVED &&
+    if (event->type == new_event->type &&
         event->window.windowID == new_event->window.windowID) {
         /* We're about to post a new move event, drop the old one */
         return 0;
@@ -62,18 +38,6 @@ static int SDLCALL RemovePendingMoveEvents(void *userdata, SDL_Event *event)
     return 1;
 }
 
-static int SDLCALL RemovePendingExposedEvents(void *userdata, SDL_Event *event)
-{
-    SDL_Event *new_event = (SDL_Event *)userdata;
-
-    if (event->type == SDL_EVENT_WINDOW_EXPOSED &&
-        event->window.windowID == new_event->window.windowID) {
-        /* We're about to post a new exposed event, drop the old one */
-        return 0;
-    }
-    return 1;
-}
-
 int SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent,
                         int data1, int data2)
 {
@@ -119,12 +83,21 @@ int SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent,
             window->windowed.h = data2;
         }
         if (data1 == window->w && data2 == window->h) {
+            SDL_CheckWindowPixelSizeChanged(window);
             return 0;
         }
         window->w = data1;
         window->h = data2;
         SDL_OnWindowResized(window);
         break;
+    case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
+        if (data1 == window->last_pixel_w && data2 == window->last_pixel_h) {
+            return 0;
+        }
+        window->last_pixel_w = data1;
+        window->last_pixel_h = data2;
+        SDL_OnWindowPixelSizeChanged(window);
+        break;
     case SDL_EVENT_WINDOW_MINIMIZED:
         if (window->flags & SDL_WINDOW_MINIMIZED) {
             return 0;
@@ -176,7 +149,10 @@ int SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent,
         SDL_OnWindowFocusLost(window);
         break;
     case SDL_EVENT_WINDOW_DISPLAY_CHANGED:
-        SDL_assert(data1 == window->display_index);
+        if (data1 < 0 || data1 == window->display_index) {
+            return 0;
+        }
+        window->display_index = data1;
         SDL_OnWindowDisplayChanged(window);
         break;
     default:
@@ -193,26 +169,12 @@ int SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent,
         event.window.data2 = data2;
         event.window.windowID = window->id;
 
-        /* Fixes queue overflow with resize events that aren't processed */
-        if (windowevent == SDL_EVENT_WINDOW_SIZE_CHANGED) {
-            /* !!! FIXME: in SDL3, let's make RESIZED/SIZE_CHANGED into one event with a flag to distinguish between them, and remove all this tapdancing. */
-            RemovePendingSizeChangedAndResizedEvents_Data userdata;
-            userdata.new_event = &event;
-            userdata.saw_resized = SDL_FALSE;
-            SDL_FilterEvents(RemovePendingSizeChangedAndResizedEvents, &userdata);
-            if (userdata.saw_resized) { /* if there was a pending resize, make sure one at the new dimensions remains. */
-                event.type = SDL_EVENT_WINDOW_RESIZED;
-                if (SDL_PushEvent(&event) <= 0) {
-                    return 0; /* oh well. */
-                }
-                event.type = SDL_EVENT_WINDOW_SIZE_CHANGED; /* then push the actual event next. */
-            }
-        }
-        if (windowevent == SDL_EVENT_WINDOW_MOVED) {
-            SDL_FilterEvents(RemovePendingMoveEvents, &event);
-        }
-        if (windowevent == SDL_EVENT_WINDOW_EXPOSED) {
-            SDL_FilterEvents(RemovePendingExposedEvents, &event);
+        /* Fixes queue overflow with move/resize events that aren't processed */
+        if (windowevent == SDL_EVENT_WINDOW_MOVED ||
+            windowevent == SDL_EVENT_WINDOW_RESIZED ||
+            windowevent == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED ||
+            windowevent == SDL_EVENT_WINDOW_EXPOSED) {
+            SDL_FilterEvents(RemoveSupercededWindowEvents, &event);
         }
         posted = (SDL_PushEvent(&event) > 0);
     }
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index b53f23b0916b..c891f20f2ee0 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -683,8 +683,8 @@ static int SDLCALL SDL_RendererEventWatch(void *userdata, SDL_Event *event)
              * window display changes as well! If the new display has a new DPI,
              * we need to update the viewport for the new window/drawable ratio.
              */
-            if (event->type == SDL_EVENT_WINDOW_SIZE_CHANGED ||
-                event->type == SDL_EVENT_WINDOW_DISPLAY_CHANGED) {
+            if (event->type == SDL_EVENT_WINDOW_RESIZED ||
+                event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
                 /* Make sure we're operating on the default render target */
                 SDL_Texture *saved_target = SDL_GetRenderTarget(renderer);
                 if (saved_target) {
diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c
index 5d5ee4ed2459..d5f7a71ff80b 100644
--- a/src/render/direct3d/SDL_render_d3d.c
+++ b/src/render/direct3d/SDL_render_d3d.c
@@ -331,7 +331,7 @@ static void D3D_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event
 {
     D3D_RenderData *data = (D3D_RenderData *)renderer->driverdata;
 
-    if (event->type == SDL_EVENT_WINDOW_SIZE_CHANGED) {
+    if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
         data->updateSize = SDL_TRUE;
     }
 }
diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c
index 275440e56a3d..2e1121b96552 100644
--- a/src/render/direct3d11/SDL_render_d3d11.c
+++ b/src/render/direct3d11/SDL_render_d3d11.c
@@ -1028,7 +1028,7 @@ void D3D11_Trim(SDL_Renderer *renderer)
 
 static void D3D11_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event)
 {
-    if (event->type == SDL_EVENT_WINDOW_SIZE_CHANGED) {
+    if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
         D3D11_UpdateForWindowSizeChange(renderer);
     }
 }
diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c
index 581dbae98c4d..b681a7051966 100644
--- a/src/render/direct3d12/SDL_render_d3d12.c
+++ b/src/render/direct3d12/SDL_render_d3d12.c
@@ -1376,7 +1376,7 @@ static HRESULT D3D12_UpdateForWindowSizeChange(SDL_Renderer *renderer)
 
 static void D3D12_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event)
 {
-    if (event->type == SDL_EVENT_WINDOW_SIZE_CHANGED) {
+    if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
         D3D12_UpdateForWindowSizeChange(renderer);
     }
 }
diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c
index 2dd36ac61e24..1b14ea34ae03 100644
--- a/src/render/opengl/SDL_render_gl.c
+++ b/src/render/opengl/SDL_render_gl.c
@@ -324,7 +324,7 @@ static void GL_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event)
      * changed behind our backs. x/y changes might seem weird but viewport
      * resets have been observed on macOS at minimum!
      */
-    if (event->type == SDL_EVENT_WINDOW_SIZE_CHANGED ||
+    if (event->type == SDL_EVENT_WINDOW_RESIZED ||
         event->type == SDL_EVENT_WINDOW_MOVED) {
         GL_RenderData *data = (GL_RenderData *)renderer->driverdata;
         data->drawstate.viewport_dirty = SDL_TRUE;
diff --git a/src/render/software/SDL_render_sw.c b/src/render/software/SDL_render_sw.c
index f65c2e5f5bce..fe9ad247f713 100644
--- a/src/render/software/SDL_render_sw.c
+++ b/src/render/software/SDL_render_sw.c
@@ -69,7 +69,7 @@ static void SW_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event)
 {
     SW_RenderData *data = (SW_RenderData *)renderer->driverdata;
 
-    if (event->type == SDL_EVENT_WINDOW_SIZE_CHANGED) {
+    if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
         data->surface = NULL;
         data->window = NULL;
     }
diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c
index 4b988f041ffd..8a56e3d7ad18 100644
--- a/src/test/SDL_test_common.c
+++ b/src/test/SDL_test_common.c
@@ -1452,10 +1452,6 @@ static void SDLTest_PrintEvent(SDL_Event *event)
         SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " resized to %" SDL_PRIs32 "x%" SDL_PRIs32,
                 event->window.windowID, event->window.data1, event->window.data2);
         break;
-    case SDL_EVENT_WINDOW_SIZE_CHANGED:
-        SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " changed size to %" SDL_PRIs32 "x%" SDL_PRIs32,
-                event->window.windowID, event->window.data1, event->window.data2);
-        break;
     case SDL_EVENT_WINDOW_MINIMIZED:
         SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " minimized", event->window.windowID);
         break;
@@ -1495,6 +1491,10 @@ static void SDLTest_PrintEvent(SDL_Event *event)
     case SDL_EVENT_WINDOW_DISPLAY_CHANGED:
         SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " display changed to %" SDL_PRIs32 "", event->window.windowID, event->window.data1);
         break;
+    case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
+        SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " changed pixel size to %" SDL_PRIs32 "x%" SDL_PRIs32,
+                event->window.windowID, event->window.data1, event->window.data2);
+        break;
     case SDL_EVENT_KEY_DOWN:
         SDL_Log("SDL EVENT: Keyboard: key pressed  in window %" SDL_PRIu32 ": scancode 0x%08X = %s, keycode 0x%08" SDL_PRIX32 " = %s",
                 event->key.windowID,
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index b76b6faeffa8..cb8e573ba57a 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -76,6 +76,7 @@ struct SDL_Window
     int w, h;
     int min_w, min_h;
     int max_w, max_h;
+    int last_pixel_w, last_pixel_h;
     Uint32 flags;
     Uint32 last_fullscreen_flags;
     Uint32 display_index;
@@ -498,6 +499,8 @@ extern void SDL_OnWindowShown(SDL_Window *window);
 extern void SDL_OnWindowHidden(SDL_Window *window);
 extern void SDL_OnWindowMoved(SDL_Window *window);
 extern void SDL_OnWindowResized(SDL_Window *window);
+extern void SDL_CheckWindowPixelSizeChanged(SDL_Window *window);
+extern void SDL_OnWindowPixelSizeChanged(SDL_Window *window);
 extern void SDL_OnWindowMinimized(SDL_Window *window);
 extern void SDL_OnWindowRestored(SDL_Window *window);
 extern void SDL_OnWindowEnter(SDL_Window *window);
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 3dc6ca9d8f95..d23f0a6687dc 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -2311,7 +2311,6 @@ void SDL_SetWindowAlwaysOnTop(SDL_Window *window, SDL_bool on_top)
             } else {
                 window->flags &= ~SDL_WINDOW_ALWAYS_ON_TOP;
             }
-
             _this->SetWindowAlwaysOnTop(_this, window, (SDL_bool)want);
         }
     }
@@ -2353,17 +2352,9 @@ void SDL_SetWindowSize(SDL_Window *window, int w, int h)
             SDL_UpdateFullscreenMode(window, SDL_TRUE);
         }
     } else {
-        int old_w = window->w;
-        int old_h = window->h;
-        window->w = w;
-        window->h = h;
         if (_this->SetWindowSize) {
             _this->SetWindowSize(_this, window);
         }
-        if (window->w != old_w || window->h != old_h) {
-            /* We didn't get a SDL_EVENT_WINDOW_RESIZED event (by design) */
-            SDL_OnWindowResized(window);
-        }
     }
 }
 
@@ -3006,20 +2997,33 @@ void SDL_OnWindowHidden(SDL_Window *window)
     SDL_UpdateFullscreenMode(window, SDL_FALSE);
 }
 
+void SDL_CheckWindowDisplayChanged(SDL_Window *window)
+{
+    int display_index;
+
+    if (window->is_destroying) {
+        return;
+    }
+
+    display_index = SDL_GetWindowDisplayIndex(window);
+    SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_DISPLAY_CHANGED, display_index, 0);
+}
+
 void SDL_OnWindowDisplayChanged(SDL_Window *window)
 {
     if ((window->flags & SDL_WINDOW_FULLSCREEN_MASK) != 0) {
-        SDL_Rect rect;
-
         if (SDL_WINDOW_FULLSCREEN_VISIBLE(window) && (window->flags & SDL_WINDOW_FULLSCREEN_EXCLUSIVE) != 0) {
             window->last_fullscreen_flags = 0;
 
             if (SDL_UpdateFullscreenMode(window, SDL_TRUE) != 0) {
                 /* Something went wrong and the window is no longer fullscreen. */
-                window->flags &= ~SDL_WINDOW_FULLSCREEN_MASK;
-                return;
+                window->flags &= ~SDL_WINDOW_FULLSCREEN_EXCLUSIVE;
             }
         }
+    }
+
+    if ((window->flags & SDL_WINDOW_FULLSCREEN_MASK) != 0) {
+        SDL_Rect rect;
 
         /*
          * If mode switching is being emulated, the display bounds don't necessarily reflect the
@@ -3044,31 +3048,32 @@ void SDL_OnWindowDisplayChanged(SDL_Window *window)
             }
         }
     }
+
+    SDL_CheckWindowPixelSizeChanged(window);
+}
+
+void SDL_OnWindowMoved(SDL_Window *window)
+{
+    SDL_CheckWindowDisplayChanged(window);
 }
 
 void SDL_OnWindowResized(SDL_Window *window)
 {
-    int display_index = SDL_GetWindowDisplayIndex(window);
-    window->surface_valid = SDL_FALSE;
+    SDL_CheckWindowDisplayChanged(window);
+    SDL_CheckWindowPixelSizeChanged(window);
+}
 
-    if (!window->is_destroying) {
-        SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SIZE_CHANGED, window->w, window->h);
+void SDL_CheckWindowPixelSizeChanged(SDL_Window *window)
+{
+    int pixel_w = 0, pixel_h = 0;
 
-        if (display_index != window->display_index && display_index != -1) {
-            window->display_index = display_index;
-            SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_DISPLAY_CHANGED, window->display_index, 0);
-        }
-    }
+    SDL_GetWindowSizeInPixels(window, &pixel_w, &pixel_h);
+    SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED, pixel_w, pixel_h);
 }
 
-void SDL_OnWindowMoved(SDL_Window *window)
+void SDL_OnWindowPixelSizeChanged(SDL_Window *window)
 {
-    int display_index = SDL_GetWindowDisplayIndex(window);
-
-    if (!window->is_destroying && display_index != window->display_index && display_index != -1) {
-        window->display_index = display_index;
-        SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_DISPLAY_CHANGED, window->display_index, 0);
-    }
+    window->surface_valid = SDL_FALSE;
 }
 
 void SDL_OnWindowMinimized(SDL_Window *window)
diff --git a/src/video/cocoa/SDL_cocoametalview.m b/src/video/cocoa/SDL_cocoametalview.m
index eb4d5920d6f7..841d6eb37ab6 100644
--- a/src/video/cocoa/SDL_cocoametalview.m
+++ b/src/video/cocoa/SDL_cocoametalview.m
@@ -41,7 +41,7 @@ static int SDLCALL SDL_MetalViewEventWatch(void *userdata, SDL_Event *event)
      * events don't always happen in the same frame (for example when a
      * resizable window exits a fullscreen Space via the user pressing the OS
      * exit-space button). */
-    if (event->type == SDL_EVENT_WINDOW_SIZE_CHANGED) {
+    if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
         @autoreleasepool {
             SDL_cocoametalview *view = (__bridge SDL_cocoametalview *)userdata;
             if (view.sdlWindowID == event->window.windowID) {
diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 0bdaf9b6bf12..8eac1acb864e 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -1652,16 +1652,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
                     /* Update the cached DPI value for this window */
                     data->scaling_dpi = newDPI;
 
-                    /* Send a SDL_EVENT_WINDOW_SIZE_CHANGED saying that the client size (in dpi-scaled points) is unchanged.
-                       Renderers need to get this to know that the framebuffer size changed.
-
-                       We clear the window size to force the event to be delivered, but what we really
-                       want for SDL3 is a new event to notify that the DPI changed and then watch for
-                       that in the renderer directly.
-                     */
-                    data->window->w = 0;
-                    data->window->h = 0;
-                    SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_SIZE_CHANGED, data->window->w, data->window->h);
+                    SDL_CheckWindowPixelSizeChanged(data->window);
                 }
 
 #ifdef HIGHDPI_DEBUG
@@ -1725,9 +1716,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
                 /* Update the cached DPI value for this window */
                 data->scaling_dpi = newDPI;
 
-                /* Send a SDL_EVENT_WINDOW_SIZE_CHANGED saying that the client size (in dpi-scaled points) is unchanged.
-                   Renderers need to get this to know that the framebuffer size changed. */
-                SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_SIZE_CHANGED, data->window->w, data->window->h);
+                SDL_CheckWindowPixelSizeChanged(data->window);
             }
 
             return 0;
diff --git a/test/testdrawchessboard.c b/test/testdrawchessboard.c
index a0aa0444e0e6..daf078645c3c 100644
--- a/test/testdrawchessboard.c
+++ b/test/testdrawchessboard.c
@@ -62,8 +62,8 @@ void loop()
     SDL_Event e;
     while (SDL_PollEvent(&e)) {
 
-        /* Re-create when window has been resized */
-        if (e.type == SDL_EVENT_WINDOW_SIZE_CHANGED) {
+        /* Re-create when window surface has been resized */
+        if (e.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
 
             SDL_DestroyRenderer(renderer);