SDL: Add full high DPI information to SDL_DisplayMode

From 24fec13ac19a7ae1c885caf70f9dbddd49bf4a9f Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 27 Jan 2023 10:46:51 -0800
Subject: [PATCH] Add full high DPI information to SDL_DisplayMode

SDL_DisplayMode now includes the pixel size, the screen size and the relationship between the two. For example, a 4K display at 200% scale could have a pixel size of 3840x2160, a screen size of 1920x1080, and a display scale of 2.0.
---
 docs/README-migration.md                   |   2 +
 include/SDL3/SDL_video.h                   |   8 +-
 src/core/winrt/SDL_winrtapp_direct3d.cpp   |  12 +-
 src/test/SDL_test_common.c                 |  10 +-
 src/video/SDL_video.c                      | 212 ++++++++++++---------
 src/video/android/SDL_androidvideo.c       |  38 ++--
 src/video/cocoa/SDL_cocoamodes.m           |  29 +--
 src/video/dummy/SDL_nullvideo.c            |   4 +-
 src/video/emscripten/SDL_emscriptenvideo.c |   3 +-
 src/video/haiku/SDL_bmodes.cc              |  42 ++--
 src/video/kmsdrm/SDL_kmsdrmvideo.c         |  24 +--
 src/video/n3ds/SDL_n3dsvideo.c             |   8 +-
 src/video/ngage/SDL_ngagevideo.cpp         |   4 +-
 src/video/offscreen/SDL_offscreenvideo.c   |   4 +-
 src/video/ps2/SDL_ps2video.c               |  20 +-
 src/video/psp/SDL_pspvideo.c               |  25 ++-
 src/video/raspberry/SDL_rpivideo.c         |  16 +-
 src/video/riscos/SDL_riscosmodes.c         |   4 +-
 src/video/uikit/SDL_uikitmodes.m           |  34 ++--
 src/video/uikit/SDL_uikitwindow.m          |   2 +-
 src/video/vita/SDL_vitavideo.c             |  26 ++-
 src/video/vivante/SDL_vivantevideo.c       |  18 +-
 src/video/wayland/SDL_waylandvideo.c       |  34 ++--
 src/video/windows/SDL_windowsmodes.c       |  10 +-
 src/video/windows/SDL_windowsvideo.c       |  17 +-
 src/video/winrt/SDL_winrtvideo.cpp         |  38 ++--
 src/video/x11/SDL_x11messagebox.c          |   4 +-
 src/video/x11/SDL_x11modes.c               |  38 ++--
 test/testautomation_video.c                |  27 +--
 test/testdisplayinfo.c                     |   4 +-
 test/testwm.c                              |   2 +-
 31 files changed, 372 insertions(+), 347 deletions(-)

diff --git a/docs/README-migration.md b/docs/README-migration.md
index 65d2a88703e7..723a0cda5124 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -954,6 +954,8 @@ The SDL_WINDOW_SHOWN flag has been removed. Windows are shown by default and can
 
 The SDL_WINDOW_ALLOW_HIGHDPI flag has been removed. Windows are automatically high DPI aware and their coordinates are in screen space, which may differ from physical pixels on displays using display scaling.
 
+SDL_DisplayMode now includes the pixel size, the screen size and the relationship between the two. For example, a 4K display at 200% scale could have a pixel size of 3840x2160, a screen size of 1920x1080, and a display scale of 2.0.
+
 The refresh rate in SDL_DisplayMode is now a float.
 
 SDL_SetWindowBrightness and SDL_SetWindowGammaRamp have been removed from the API, because they interact poorly with modern operating systems and aren't able to limit their effects to the SDL window.
diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h
index ba71957f4bed..7d7066b9941b 100644
--- a/include/SDL3/SDL_video.h
+++ b/include/SDL3/SDL_video.h
@@ -56,9 +56,11 @@ typedef Uint32 SDL_WindowID;
 typedef struct
 {
     Uint32 format;              /**< pixel format */
-    int w;                      /**< width in pixels */
-    int h;                      /**< height in pixels */
-    float display_scale;        /**< scale converting screen coordinates to pixels (e.g. a 3840x2160 mode with 1.5 scale would have a screen size of 2560x1440) */
+    int pixel_w;                /**< width in pixels (used for creating back buffers) */
+    int pixel_h;                /**< height in pixels (used for creating back buffers) */
+    int screen_w;               /**< width in screen coordinates (used for creating windows) */
+    int screen_h;               /**< height in screen coordinates (used for creating windows) */
+    float display_scale;        /**< scale converting screen coordinates to pixels (e.g. a 2560x1440 screen size mode with 1.5 scale would have 3840x2160 pixels) */
     float refresh_rate;         /**< refresh rate (or zero for unspecified) */
     void *driverdata;           /**< driver-specific data, initialize to 0 */
 } SDL_DisplayMode;
diff --git a/src/core/winrt/SDL_winrtapp_direct3d.cpp b/src/core/winrt/SDL_winrtapp_direct3d.cpp
index fd728fc8b0dd..01d3a8afa344 100644
--- a/src/core/winrt/SDL_winrtapp_direct3d.cpp
+++ b/src/core/winrt/SDL_winrtapp_direct3d.cpp
@@ -116,10 +116,10 @@ static void WINRT_ProcessWindowSizeChange() // TODO: Pass an SDL_Window-identify
             SDL_Window *window = WINRT_GlobalSDLWindow;
             SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
 
-            int x = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Left);
-            int y = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Top);
-            int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width);
-            int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height);
+            int x = (int)SDL_lroundf(data->coreWindow->Bounds.Left);
+            int y = (int)SDL_lroundf(data->coreWindow->Bounds.Top);
+            int w = (int)SDL_floorf(data->coreWindow->Bounds.Width);
+            int h = (int)SDL_floorf(data->coreWindow->Bounds.Height);
 
 #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) && (NTDDI_VERSION == NTDDI_WIN8)
             /* WinPhone 8.0 always keeps its native window size in portrait,
@@ -235,8 +235,8 @@ void SDL_WinRTApp::OnOrientationChanged(Object ^ sender)
     SDL_Window *window = WINRT_GlobalSDLWindow;
     if (window) {
         SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
-        int w = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Width);
-        int h = WINRT_DIPS_TO_PHYSICAL_PIXELS(data->coreWindow->Bounds.Height);
+        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);
     }
 #endif
diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c
index 9bf5a85e5e56..8b961ec86c51 100644
--- a/src/test/SDL_test_common.c
+++ b/src/test/SDL_test_common.c
@@ -1130,7 +1130,7 @@ SDLTest_CommonInit(SDLTest_CommonState *state)
                 SDL_GetMasksForPixelFormatEnum(mode.format, &bpp, &Rmask, &Gmask,
                                            &Bmask, &Amask);
                 SDL_Log("  Current mode: %dx%d@%gHz, %d%% scale, %d bits-per-pixel (%s)\n",
-                        mode.w, mode.h, mode.refresh_rate, (int)(mode.display_scale * 100.0f), bpp,
+                        mode.pixel_w, mode.pixel_h, mode.refresh_rate, (int)(mode.display_scale * 100.0f), bpp,
                         SDL_GetPixelFormatName(mode.format));
                 if (Rmask || Gmask || Bmask) {
                     SDL_Log("      Red Mask   = 0x%.8" SDL_PRIx32 "\n", Rmask);
@@ -1152,7 +1152,7 @@ SDLTest_CommonInit(SDLTest_CommonState *state)
                         SDL_GetMasksForPixelFormatEnum(mode.format, &bpp, &Rmask,
                                                    &Gmask, &Bmask, &Amask);
                         SDL_Log("    Mode %d: %dx%d@%gHz, %d%% scale, %d bits-per-pixel (%s)\n",
-                                j, mode.w, mode.h, mode.refresh_rate, (int)(mode.display_scale * 100.0f), bpp,
+                                j, mode.pixel_w, mode.pixel_h, mode.refresh_rate, (int)(mode.display_scale * 100.0f), bpp,
                                 SDL_GetPixelFormatName(mode.format));
                         if (Rmask || Gmask || Bmask) {
                             SDL_Log("        Red Mask   = 0x%.8" SDL_PRIx32 "\n",
@@ -2246,7 +2246,7 @@ void SDLTest_CommonDrawWindowInfo(SDL_Renderer *renderer, SDL_Window *window, fl
 
     if (0 == SDL_GetWindowDisplayMode(window, &mode)) {
         (void)SDL_snprintf(text, sizeof text, "SDL_GetWindowDisplayMode: %dx%d@%gHz %d%% scale, (%s)",
-                           mode.w, mode.h, mode.refresh_rate, (int)(mode.display_scale * 100.0f), SDL_GetPixelFormatName(mode.format));
+                           mode.pixel_w, mode.pixel_h, mode.refresh_rate, (int)(mode.display_scale * 100.0f), SDL_GetPixelFormatName(mode.format));
         SDLTest_DrawString(renderer, 0.0f, textY, text);
         textY += lineHeight;
     }
@@ -2276,14 +2276,14 @@ void SDLTest_CommonDrawWindowInfo(SDL_Renderer *renderer, SDL_Window *window, fl
 
     if (0 == SDL_GetCurrentDisplayMode(windowDisplayIndex, &mode)) {
         (void)SDL_snprintf(text, sizeof text, "SDL_GetCurrentDisplayMode: %dx%d@%gHz %d%% scale, (%s)",
-                           mode.w, mode.h, mode.refresh_rate, (int)(mode.display_scale * 100.0f), SDL_GetPixelFormatName(mode.format));
+                           mode.pixel_w, mode.pixel_h, mode.refresh_rate, (int)(mode.display_scale * 100.0f), SDL_GetPixelFormatName(mode.format));
         SDLTest_DrawString(renderer, 0.0f, textY, text);
         textY += lineHeight;
     }
 
     if (0 == SDL_GetDesktopDisplayMode(windowDisplayIndex, &mode)) {
         (void)SDL_snprintf(text, sizeof text, "SDL_GetDesktopDisplayMode: %dx%d@%gHz %d%% scale, (%s)",
-                           mode.w, mode.h, mode.refresh_rate, (int)(mode.display_scale * 100.0f), SDL_GetPixelFormatName(mode.format));
+                           mode.pixel_w, mode.pixel_h, mode.refresh_rate, (int)(mode.display_scale * 100.0f), SDL_GetPixelFormatName(mode.format));
         SDLTest_DrawString(renderer, 0.0f, textY, text);
         textY += lineHeight;
     }
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 9b8f2f9311b4..8d4288d88f5b 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -382,16 +382,16 @@ static int SDLCALL cmpmodes(const void *A, const void *B)
 {
     const SDL_DisplayMode *a = (const SDL_DisplayMode *)A;
     const SDL_DisplayMode *b = (const SDL_DisplayMode *)B;
-    float a_display_scale = (a->display_scale == 0.0f) ? 1.0f : a->display_scale;
-    float b_display_scale = (b->display_scale == 0.0f) ? 1.0f : b->display_scale;
     if (a == b) {
         return 0;
-    } else if (a->w != b->w) {
-        return b->w - a->w;
-    } else if (a->h != b->h) {
-        return b->h - a->h;
-    } else if (a_display_scale != b_display_scale) {
-        return (int)(a_display_scale * 100) - (int)(b_display_scale * 100);
+    } else if (a->screen_w != b->screen_w) {
+        return b->screen_w - a->screen_w;
+    } else if (a->screen_h != b->screen_h) {
+        return b->screen_h - a->screen_h;
+    } else if (a->pixel_w != b->pixel_w) {
+        return b->pixel_w - a->pixel_w;
+    } else if (a->pixel_h != b->pixel_h) {
+        return b->pixel_h - a->pixel_h;
     } else if (SDL_BITSPERPIXEL(a->format) != SDL_BITSPERPIXEL(b->format)) {
         return SDL_BITSPERPIXEL(b->format) - SDL_BITSPERPIXEL(a->format);
     } else if (SDL_PIXELLAYOUT(a->format) != SDL_PIXELLAYOUT(b->format)) {
@@ -585,6 +585,40 @@ SDL_bool SDL_OnVideoThread()
     return (_this && SDL_ThreadID() == _this->thread) ? SDL_TRUE : SDL_FALSE;
 }
 
+static void SDL_FinalizeDisplayMode(SDL_DisplayMode *mode)
+{
+    /* Make sure all the fields are set up correctly */
+    if (mode->display_scale <= 0.0f) {
+        if (mode->screen_w == 0 && mode->screen_h == 0) {
+            mode->screen_w = mode->pixel_w;
+            mode->screen_h = mode->pixel_h;
+        }
+        if (mode->pixel_w == 0 && mode->pixel_h == 0) {
+            mode->pixel_w = mode->screen_w;
+            mode->pixel_h = mode->screen_h;
+        }
+        if (mode->screen_w > 0) {
+            mode->display_scale = (float)mode->pixel_w / mode->screen_w;
+        }
+    } else {
+        if (mode->screen_w == 0 && mode->screen_h == 0) {
+            mode->screen_w = (int)SDL_floorf(mode->pixel_w / mode->display_scale);
+            mode->screen_h = (int)SDL_floorf(mode->pixel_h / mode->display_scale);
+        }
+        if (mode->pixel_w == 0 && mode->pixel_h == 0) {
+            mode->pixel_w = (int)SDL_ceilf(mode->screen_w * mode->display_scale);
+            mode->pixel_h = (int)SDL_ceilf(mode->screen_h * mode->display_scale);
+        }
+    }
+
+    /* Make sure the screen width, pixel width, and display scale all match */
+    if (mode->display_scale != 0.0f) {
+        SDL_assert(mode->display_scale > 0.0f);
+        SDL_assert(SDL_fabsf(mode->screen_w - (mode->pixel_w / mode->display_scale)) < 1.0f);
+        SDL_assert(SDL_fabsf(mode->screen_h - (mode->pixel_h / mode->display_scale)) < 1.0f);
+    }
+}
+
 int SDL_AddBasicVideoDisplay(const SDL_DisplayMode *desktop_mode)
 {
     SDL_VideoDisplay display;
@@ -592,9 +626,7 @@ int SDL_AddBasicVideoDisplay(const SDL_DisplayMode *desktop_mode)
     SDL_zero(display);
     if (desktop_mode) {
         display.desktop_mode = *desktop_mode;
-        if (display.desktop_mode.display_scale == 0.0f) {
-            display.desktop_mode.display_scale = 1.0f;
-        }
+        SDL_FinalizeDisplayMode(&display.desktop_mode);
     }
     display.current_mode = display.desktop_mode;
 
@@ -624,12 +656,9 @@ int SDL_AddVideoDisplay(const SDL_VideoDisplay *display, SDL_bool send_event)
             displays[index].name = SDL_strdup(name);
         }
 
-        if (displays[index].desktop_mode.display_scale == 0.0f) {
-            displays[index].desktop_mode.display_scale = 1.0f;
-        }
-        if (displays[index].current_mode.display_scale == 0.0f) {
-            displays[index].current_mode.display_scale = 1.0f;
-        }
+        SDL_FinalizeDisplayMode(&displays[index].desktop_mode);
+        SDL_FinalizeDisplayMode(&displays[index].current_mode);
+
         if (send_event) {
             SDL_SendDisplayEvent(&_this->displays[index], SDL_EVENT_DISPLAY_CONNECTED, 0);
         }
@@ -722,8 +751,8 @@ int SDL_GetDisplayBounds(int displayIndex, SDL_Rect *rect)
         SDL_GetDisplayBounds(displayIndex - 1, rect);
         rect->x += rect->w;
     }
-    rect->w = (int)(display->current_mode.w / display->current_mode.display_scale);
-    rect->h = (int)(display->current_mode.h / display->current_mode.display_scale);
+    rect->w = display->current_mode.screen_w;
+    rect->h = display->current_mode.screen_h;
     return 0;
 }
 
@@ -812,9 +841,7 @@ SDL_bool SDL_AddDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mo
         display->max_display_modes += 32;
     }
     modes[nmodes] = *mode;
-    if (modes[nmodes].display_scale == 0.0f) {
-        modes[nmodes].display_scale = 1.0f;
-    }
+    SDL_FinalizeDisplayMode(&modes[nmodes]);
     display->num_display_modes++;
 
     /* Re-sort video modes */
@@ -827,17 +854,13 @@ SDL_bool SDL_AddDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mo
 void SDL_SetCurrentDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode)
 {
     SDL_memcpy(&display->current_mode, mode, sizeof(*mode));
-    if (display->current_mode.display_scale == 0.0f) {
-        display->current_mode.display_scale = 1.0f;
-    }
+    SDL_FinalizeDisplayMode(&display->current_mode);
 }
 
 void SDL_SetDesktopDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode)
 {
     SDL_memcpy(&display->desktop_mode, mode, sizeof(*mode));
-    if (display->desktop_mode.display_scale == 0.0f) {
-        display->desktop_mode.display_scale = 1.0f;
-    }
+    SDL_FinalizeDisplayMode(&display->desktop_mode);
 }
 
 static int SDL_GetNumDisplayModesForDisplay(SDL_VideoDisplay *display)
@@ -922,9 +945,9 @@ static SDL_DisplayMode *SDL_GetClosestDisplayModeForDisplay(SDL_VideoDisplay *di
                                                             SDL_DisplayMode *closest)
 {
     Uint32 target_format;
-    float target_display_scale;
     float target_refresh_rate;
     int i;
+    SDL_DisplayMode requested_mode;
     SDL_DisplayMode *current, *match;
 
     if (mode == NULL || closest == NULL) {
@@ -932,6 +955,11 @@ static SDL_DisplayMode *SDL_GetClosestDisplayModeForDisplay(SDL_VideoDisplay *di
         return NULL;
     }
 
+    /* Make sure all the fields are filled out in the requested mode */
+    requested_mode = *mode;
+    SDL_FinalizeDisplayMode(&requested_mode);
+    mode = &requested_mode;
+
     /* Default to the desktop format */
     if (mode->format) {
         target_format = mode->format;
@@ -939,13 +967,6 @@ static SDL_DisplayMode *SDL_GetClosestDisplayModeForDisplay(SDL_VideoDisplay *di
         target_format = display->desktop_mode.format;
     }
 
-    /* Default to 1.0 scale */
-    if (mode->display_scale > 0.0f) {
-        target_display_scale = mode->display_scale;
-    } else {
-        target_display_scale = 1.0f;
-    }
-
     /* Default to the desktop refresh rate */
     if (mode->refresh_rate > 0.0f) {
         target_refresh_rate = mode->refresh_rate;
@@ -957,12 +978,12 @@ static SDL_DisplayMode *SDL_GetClosestDisplayModeForDisplay(SDL_VideoDisplay *di
     for (i = 0; i < SDL_GetNumDisplayModesForDisplay(display); ++i) {
         current = &display->display_modes[i];
 
-        if (current->w && (current->w < mode->w)) {
+        if (current->pixel_w && (current->pixel_w < mode->pixel_w)) {
             /* Out of sorted modes large enough here */
             break;
         }
-        if (current->h && (current->h < mode->h)) {
-            if (current->w && (current->w == mode->w)) {
+        if (current->pixel_h && (current->pixel_h < mode->pixel_h)) {
+            if (current->pixel_w && (current->pixel_w == mode->pixel_w)) {
                 /* Out of sorted modes large enough here */
                 break;
             }
@@ -971,7 +992,7 @@ static SDL_DisplayMode *SDL_GetClosestDisplayModeForDisplay(SDL_VideoDisplay *di
                modes may still follow. */
             continue;
         }
-        if (match == NULL || current->w < match->w || current->h < match->h) {
+        if (match == NULL || current->pixel_w < match->pixel_w || current->pixel_h < match->pixel_h) {
             match = current;
             continue;
         }
@@ -993,35 +1014,29 @@ static SDL_DisplayMode *SDL_GetClosestDisplayModeForDisplay(SDL_VideoDisplay *di
                 continue;
             }
         }
-        if (current->display_scale != match->display_scale) {
-            /* Sorted lowest display scale to highest */
-            if (current->display_scale <= target_display_scale) {
-                match = current;
-                continue;
-            }
-        }
     }
+
     if (match) {
+        SDL_zerop(closest);
         if (match->format) {
             closest->format = match->format;
         } else {
             closest->format = mode->format;
         }
-        if (match->w && match->h) {
-            closest->w = match->w;
-            closest->h = match->h;
+        if (match->screen_w && match->screen_h) {
+            closest->screen_w = match->screen_w;
+            closest->screen_h = match->screen_h;
         } else {
-            closest->w = mode->w;
-            closest->h = mode->h;
+            closest->screen_w = mode->screen_w;
+            closest->screen_h = mode->screen_h;
         }
-        if (match->display_scale > 0.0f) {
-            closest->display_scale = match->display_scale;
-        } else if (mode->display_scale > 0.0f) {
-            closest->display_scale = mode->display_scale;
+        if (match->pixel_w && match->pixel_h) {
+            closest->pixel_w = match->pixel_w;
+            closest->pixel_h = match->pixel_h;
         } else {
-            closest->display_scale = 1.0f;
+            closest->pixel_w = mode->pixel_w;
+            closest->pixel_h = mode->pixel_h;
         }
-
         if (match->refresh_rate > 0.0f) {
             closest->refresh_rate = match->refresh_rate;
         } else {
@@ -1029,19 +1044,17 @@ static SDL_DisplayMode *SDL_GetClosestDisplayModeForDisplay(SDL_VideoDisplay *di
         }
         closest->driverdata = match->driverdata;
 
-        /*
-         * Pick some reasonable defaults if the app and driver don't
-         * care
-         */
+        /* Pick some reasonable defaults if the app and driver don't care */
         if (!closest->format) {
             closest->format = SDL_PIXELFORMAT_RGB888;
         }
-        if (!closest->w) {
-            closest->w = 640;
+        if (!closest->screen_w) {
+            closest->screen_w = 640;
         }
-        if (!closest->h) {
-            closest->h = 480;
+        if (!closest->screen_h) {
+            closest->screen_h = 480;
         }
+        SDL_FinalizeDisplayMode(closest);
         return closest;
     }
     return NULL;
@@ -1075,11 +1088,17 @@ static int SDL_SetDisplayModeForDisplay(SDL_VideoDisplay *display, const SDL_Dis
         if (!display_mode.format) {
             display_mode.format = display->current_mode.format;
         }
-        if (!display_mode.w) {
-            display_mode.w = display->current_mode.w;
+        if (!display_mode.pixel_w) {
+            display_mode.pixel_w = display->current_mode.pixel_w;
+        }
+        if (!display_mode.pixel_h) {
+            display_mode.pixel_h = display->current_mode.pixel_h;
+        }
+        if (!display_mode.screen_w) {
+            display_mode.screen_w = display->current_mode.screen_w;
         }
-        if (!display_mode.h) {
-            display_mode.h = display->current_mode.h;
+        if (!display_mode.screen_h) {
+            display_mode.screen_h = display->current_mode.screen_h;
         }
         if (display_mode.refresh_rate == 0.0f) {
             display_mode.refresh_rate = display->current_mode.refresh_rate;
@@ -1087,7 +1106,9 @@ static int SDL_SetDisplayModeForDisplay(SDL_VideoDisplay *display, const SDL_Dis
 
         /* Get a good video mode, the closest one possible */
         if (!SDL_GetClosestDisplayModeForDisplay(display, &display_mode, &display_mode)) {
-            return SDL_SetError("No video mode large enough for %dx%d", display_mode.w, display_mode.h);
+            return SDL_SetError("No video mode large enough for %dx%d (%dx%d)",
+                                display_mode.pixel_w, display_mode.pixel_h,
+                                display_mode.screen_w, display_mode.screen_h);
         }
     } else {
         display_mode = display->desktop_mode;
@@ -1284,11 +1305,11 @@ int SDL_SetWindowDisplayMode(SDL_Window *window, const SDL_DisplayMode *mode)
 #ifndef __ANDROID__
                 /* Android may not resize the window to exactly what our fullscreen mode is, especially on
                  * windowed Android environments like the Chromebook or Samsung DeX.  Given this, we shouldn't
-                 * use fullscreen_mode.w and fullscreen_mode.h, but rather get our current native size.  As such,
+                 * use fullscreen_mode.screen_w and fullscreen_mode.screen_h, but rather get our current native size.  As such,
                  * Android's SetWindowFullscreen will generate the window event for us with the proper final size.
                  */
-                SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, fullscreen_mode.w, fullscreen_mode.h);
-#endif
+                SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, fullscreen_mode.screen_w, fullscreen_mode.screen_h);
+#endif /* !__ANDROID__ */
             }
         }
     }
@@ -1307,11 +1328,11 @@ int SDL_GetWindowDisplayMode(SDL_Window *window, SDL_DisplayMode *mode)
     }
 
     fullscreen_mode = window->fullscreen_mode;
-    if (!fullscreen_mode.w) {
-        fullscreen_mode.w = window->windowed.w;
+    if (!fullscreen_mode.screen_w) {
+        fullscreen_mode.screen_w = window->windowed.w;
     }
-    if (!fullscreen_mode.h) {
-        fullscreen_mode.h = window->windowed.h;
+    if (!fullscreen_mode.screen_h) {
+        fullscreen_mode.screen_h = window->windowed.h;
     }
 
     display = SDL_GetDisplayForWindow(window);
@@ -1469,7 +1490,7 @@ static int SDL_UpdateFullscreenMode(SDL_Window *window, SDL_bool fullscreen)
             if (SDL_GetWindowDisplayMode(other, &fullscreen_mode) == 0) {
                 SDL_bool resized = SDL_TRUE;
 
-                if (other->w == fullscreen_mode.w && other->h == fullscreen_mode.h) {
+                if (other->w == fullscreen_mode.screen_w && other->h == fullscreen_mode.screen_h) {
                     resized = SDL_FALSE;
                 }
 
@@ -1501,11 +1522,8 @@ static int SDL_UpdateFullscreenMode(SDL_Window *window, SDL_bool fullscreen)
                     /* This is also unnecessary on Win32 (WIN_SetWindowFullscreen calls SetWindowPos,
                      * WM_WINDOWPOSCHANGED will send SDL_EVENT_WINDOW_RESIZED).
                      */
-
-                    /* The new fullscreen window size must be sent in screen coordinates, not pixels. */
                     SDL_SendWindowEvent(other, SDL_EVENT_WINDOW_RESIZED,
-                                        (int)(fullscreen_mode.w / fullscreen_mode.display_scale),
-                                        (int)(fullscreen_mode.h / fullscreen_mode.display_scale));
+                                        fullscreen_mode.screen_w, fullscreen_mode.screen_h);
 #endif
                 } else {
                     SDL_OnWindowResized(other);
@@ -1745,15 +1763,15 @@ SDL_Window *SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint
         if ((flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP && (bounds.w != w || bounds.h != h)) {
             SDL_DisplayMode fullscreen_mode, closest_mode;
             SDL_zero(fullscreen_mode);
-            fullscreen_mode.w = w;
-            fullscreen_mode.h = h;
+            fullscreen_mode.screen_w = w;
+            fullscreen_mode.screen_h = h;
             if (SDL_GetClosestDisplayModeForDisplay(display, &fullscreen_mode, &closest_mode) != NULL) {
-                bounds.w = closest_mode.w;
-                bounds.h = closest_mode.h;
+                bounds.w = closest_mode.screen_w;
+                bounds.h = closest_mode.screen_h;
             }
         }
-        window->fullscreen_mode.w = bounds.w;
-        window->fullscreen_mode.h = bounds.h;
+        window->fullscreen_mode.screen_w = bounds.w;
+        window->fullscreen_mode.screen_h = bounds.h;
         window->x = bounds.x;
         window->y = bounds.y;
         window->w = bounds.w;
@@ -2409,8 +2427,16 @@ void SDL_GetWindowSizeInPixels(SDL_Window *window, int *w, int *h)
 
         if (display)
         {
-            *w = (int)SDL_ceilf(*w * display->current_mode.display_scale);
-            *h = (int)SDL_ceilf(*h * display->current_mode.display_scale);
+            if (*w == display->current_mode.screen_w) {
+                *w = display->current_mode.pixel_w;
+            } else {
+                *w = (int)SDL_ceilf(*w * display->current_mode.display_scale);
+            }
+            if (*h == display->current_mode.screen_h) {
+                *h = display->current_mode.pixel_h;
+            } else {
+                *h = (int)SDL_ceilf(*h * display->current_mode.display_scale);
+            }
         }
     }
 }
@@ -2999,8 +3025,8 @@ void SDL_OnWindowDisplayChanged(SDL_Window *window)
             window->y = rect.y;
             window->w = rect.w;
             window->h = rect.h;
-            window->fullscreen_mode.w = rect.w;
-            window->fullscreen_mode.h = rect.h;
+            window->fullscreen_mode.screen_w = rect.w;
+            window->fullscreen_mode.screen_h = rect.h;
             if (_this->SetWindowSize) {
                 _this->SetWindowSize(_this, window);
             }
diff --git a/src/video/android/SDL_androidvideo.c b/src/video/android/SDL_androidvideo.c
index ca1969fa2f2e..562df5ed9adb 100644
--- a/src/video/android/SDL_androidvideo.c
+++ b/src/video/android/SDL_androidvideo.c
@@ -180,8 +180,8 @@ int Android_VideoInit(_THIS)
 
     SDL_zero(mode);
     mode.format = Android_ScreenFormat;
-    mode.w = Android_DeviceWidth;
-    mode.h = Android_DeviceHeight;
+    mode.pixel_w = Android_DeviceWidth;
+    mode.pixel_h = Android_DeviceHeight;
     mode.display_scale = Android_ScreenDensity;
     mode.refresh_rate = Android_ScreenRate;
     mode.driverdata = NULL;
@@ -276,11 +276,15 @@ void Android_SendResize(SDL_Window *window)
     SDL_VideoDevice *device = SDL_GetVideoDevice();
     if (device && device->num_displays > 0) {
         SDL_VideoDisplay *display = &device->displays[0];
-        display->desktop_mode.format = Android_ScreenFormat;
-        display->desktop_mode.w = Android_DeviceWidth;
-        display->desktop_mode.h = Android_DeviceHeight;
-        display->desktop_mode.display_scale = Android_ScreenDensity;
-        display->desktop_mode.refresh_rate = Android_ScreenRate;
+        SDL_DisplayMode desktop_mode;
+
+        SDL_zero(desktop_mode);
+        desktop_mode.format = Android_ScreenFormat;
+        desktop_mode.pixel_w = Android_DeviceWidth;
+        desktop_mode.pixel_h = Android_DeviceHeight;
+        desktop_mode.display_scale = Android_ScreenDensity;
+        desktop_mode.refresh_rate = Android_ScreenRate;
+        SDL_SetDesktopDisplayMode(display, &desktop_mode);
     }
 
     if (window) {
@@ -288,15 +292,17 @@ void Android_SendResize(SDL_Window *window)
          * will fall back to the old mode */
         int w, h;
         SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
-        display->display_modes[0].format = Android_ScreenFormat;
-        display->display_modes[0].w = Android_DeviceWidth;
-        display->display_modes[0].h = Android_DeviceHeight;
-        display->display_modes[0].display_scale = Android_ScreenDensity;
-        display->display_modes[0].refresh_rate = Android_ScreenRate;
-        display->current_mode = display->display_modes[0];
-
-        w = (int)SDL_ceilf(Android_SurfaceWidth / Android_ScreenDensity);
-        h = (int)SDL_ceilf(Android_SurfaceHeight / Android_ScreenDensity);
+        SDL_DisplayMode current_mode;
+
+        current_mode.format = Android_ScreenFormat;
+        current_mode.pixel_w = Android_DeviceWidth;
+        current_mode.pixel_h = Android_DeviceHeight;
+        current_mode.display_scale = Android_ScreenDensity;
+        current_mode.refresh_rate = Android_ScreenRate;
+        SDL_SetCurrentDisplayMode(display, &current_mode);
+
+        w = (int)SDL_floorf(Android_SurfaceWidth / Android_ScreenDensity);
+        h = (int)SDL_floorf(Android_SurfaceHeight / Android_ScreenDensity);
         SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, w, h);
     }
 }
diff --git a/src/video/cocoa/SDL_cocoamodes.m b/src/video/cocoa/SDL_cocoamodes.m
index cc2afdc40bf4..ecb71d7f2096 100644
--- a/src/video/cocoa/SDL_cocoamodes.m
+++ b/src/video/cocoa/SDL_cocoamodes.m
@@ -143,10 +143,10 @@ static SDL_bool GetDisplayMode(_THIS, CGDisplayModeRef vidmode, SDL_bool vidmode
 {
     SDL_DisplayModeData *data;
     bool usableForGUI = CGDisplayModeIsUsableForDesktopGUI(vidmode);
-    int width = (int)CGDisplayModeGetWidth(vidmode);
-    int height = (int)CGDisplayModeGetHeight(vidmode);
-    int pixelW = width;
-    int pixelH = height;
+    size_t width = CGDisplayModeGetWidth(vidmode);
+    size_t height = CGDisplayModeGetHeight(vidmode);
+    size_t pixelW = width;
+    size_t pixelH = height;
     uint32_t ioflags = CGDisplayModeGetIOFlags(vidmode);
     float refreshrate = GetDisplayModeRefreshRate(vidmode, link);
     Uint32 format = GetDisplayModePixelFormat(vidmode);
@@ -173,15 +173,15 @@ static SDL_bool GetDisplayMode(_THIS, CGDisplayModeRef vidmode, SDL

(Patch may be truncated, please check the link at the top of this post.)