SDL: SDL_DisplayMode now represents physical pixels and has added a display scaling factor

From 6a27188023832f666c81ec340738a5f73e01e50d Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 25 Jan 2023 03:36:35 -0800
Subject: [PATCH] SDL_DisplayMode now represents physical pixels and has added
 a display scaling factor

Work in progress on https://github.com/libsdl-org/SDL/issues/7134
---
 include/SDL3/SDL_video.h                   | 10 +++--
 src/test/SDL_test_common.c                 | 24 +++++------
 src/video/SDL_video.c                      | 48 +++++++++++++++++++---
 src/video/android/SDL_androidvideo.c       |  2 +
 src/video/cocoa/SDL_cocoamodes.m           | 16 +++++---
 src/video/dummy/SDL_nullvideo.c            |  2 -
 src/video/emscripten/SDL_emscriptenvideo.c |  3 +-
 src/video/haiku/SDL_bmodes.cc              |  1 +
 src/video/kmsdrm/SDL_kmsdrmvideo.c         |  1 +
 src/video/n3ds/SDL_n3dsvideo.c             |  1 -
 src/video/ngage/SDL_ngagevideo.cpp         |  3 +-
 src/video/offscreen/SDL_offscreenvideo.c   |  4 +-
 src/video/ps2/SDL_ps2video.c               |  3 --
 src/video/psp/SDL_pspvideo.c               |  5 +--
 src/video/raspberry/SDL_rpivideo.c         |  3 +-
 src/video/riscos/SDL_riscosmodes.c         |  1 +
 src/video/uikit/SDL_uikitmodes.m           | 36 ++++++----------
 src/video/vita/SDL_vitavideo.c             |  4 +-
 src/video/wayland/SDL_waylandvideo.c       |  1 +
 src/video/windows/SDL_windowsmodes.c       | 29 ++++++-------
 src/video/x11/SDL_x11modes.c               |  3 +-
 21 files changed, 109 insertions(+), 91 deletions(-)

diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h
index a2318246d492..d8d5c52c9d51 100644
--- a/include/SDL3/SDL_video.h
+++ b/include/SDL3/SDL_video.h
@@ -56,8 +56,9 @@ typedef Uint32 SDL_WindowID;
 typedef struct
 {
     Uint32 format;              /**< pixel format */
-    int w;                      /**< width, in screen coordinates */
-    int h;                      /**< height, in screen coordinates */
+    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) */
     float refresh_rate;         /**< refresh rate (or zero for unspecified) */
     void *driverdata;           /**< driver-specific data, initialize to 0 */
 } SDL_DisplayMode;
@@ -322,7 +323,7 @@ extern DECLSPEC int SDLCALL SDL_GetNumVideoDisplays(void);
 extern DECLSPEC const char *SDLCALL SDL_GetDisplayName(int displayIndex);
 
 /**
- * Get the desktop area represented by a display.
+ * Get the desktop area represented by a display, in screen coordinates.
  *
  * The primary display (`displayIndex` zero) is always located at 0,0.
  *
@@ -338,7 +339,7 @@ extern DECLSPEC const char *SDLCALL SDL_GetDisplayName(int displayIndex);
 extern DECLSPEC int SDLCALL SDL_GetDisplayBounds(int displayIndex, SDL_Rect *rect);
 
 /**
- * Get the usable desktop area represented by a display.
+ * Get the usable desktop area represented by a display, in screen coordinates.
  *
  * The primary display (`displayIndex` zero) is always located at 0,0.
  *
@@ -440,6 +441,7 @@ extern DECLSPEC int SDLCALL SDL_GetNumDisplayModes(int displayIndex);
  *
  * - width -> largest to smallest
  * - height -> largest to smallest
+ * - display_scale -> smallest to largest
  * - bits per pixel -> more colors to fewer colors
  * - packed pixel layout -> largest to smallest
  * - refresh rate -> highest to lowest
diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c
index 2d23c82eb542..6aeb65c9c4b4 100644
--- a/src/test/SDL_test_common.c
+++ b/src/test/SDL_test_common.c
@@ -1124,13 +1124,13 @@ SDLTest_CommonInit(SDLTest_CommonState *state)
 
                 SDL_Log("Bounds: %dx%d at %d,%d\n", bounds.w, bounds.h, bounds.x, bounds.y);
                 SDL_Log("Usable bounds: %dx%d at %d,%d\n", usablebounds.w, usablebounds.h, usablebounds.x, usablebounds.y);
-                SDL_Log("DPI: %fx%f\n", hdpi, vdpi);
+                SDL_Log("DPI: %gx%g\n", hdpi, vdpi);
 
                 SDL_GetDesktopDisplayMode(i, &mode);
                 SDL_GetMasksForPixelFormatEnum(mode.format, &bpp, &Rmask, &Gmask,
                                            &Bmask, &Amask);
-                SDL_Log("  Current mode: %dx%d@%gHz, %d bits-per-pixel (%s)\n",
-                        mode.w, mode.h, mode.refresh_rate, bpp,
+                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,
                         SDL_GetPixelFormatName(mode.format));
                 if (Rmask || Gmask || Bmask) {
                     SDL_Log("      Red Mask   = 0x%.8" SDL_PRIx32 "\n", Rmask);
@@ -1151,8 +1151,8 @@ SDLTest_CommonInit(SDLTest_CommonState *state)
                         SDL_GetDisplayMode(i, j, &mode);
                         SDL_GetMasksForPixelFormatEnum(mode.format, &bpp, &Rmask,
                                                    &Gmask, &Bmask, &Amask);
-                        SDL_Log("    Mode %d: %dx%d@%gHz, %d bits-per-pixel (%s)\n",
-                                j, mode.w, mode.h, mode.refresh_rate, bpp,
+                        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,
                                 SDL_GetPixelFormatName(mode.format));
                         if (Rmask || Gmask || Bmask) {
                             SDL_Log("        Red Mask   = 0x%.8" SDL_PRIx32 "\n",
@@ -2245,8 +2245,8 @@ void SDLTest_CommonDrawWindowInfo(SDL_Renderer *renderer, SDL_Window *window, fl
     textY += lineHeight;
 
     if (0 == SDL_GetWindowDisplayMode(window, &mode)) {
-        (void)SDL_snprintf(text, sizeof text, "SDL_GetWindowDisplayMode: %dx%d@%gHz (%s)",
-                           mode.w, mode.h, mode.refresh_rate, SDL_GetPixelFormatName(mode.format));
+        (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));
         SDLTest_DrawString(renderer, 0.0f, textY, text);
         textY += lineHeight;
     }
@@ -2275,21 +2275,21 @@ 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 (%s)",
-                           mode.w, mode.h, mode.refresh_rate, SDL_GetPixelFormatName(mode.format));
+        (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));
         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 (%s)",
-                           mode.w, mode.h, mode.refresh_rate, SDL_GetPixelFormatName(mode.format));
+        (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));
         SDLTest_DrawString(renderer, 0.0f, textY, text);
         textY += lineHeight;
     }
 
     if (0 == SDL_GetDisplayPhysicalDPI(windowDisplayIndex, &ddpi, &hdpi, &vdpi)) {
-        (void)SDL_snprintf(text, sizeof text, "SDL_GetDisplayPhysicalDPI: ddpi: %f, hdpi: %f, vdpi: %f",
+        (void)SDL_snprintf(text, sizeof text, "SDL_GetDisplayPhysicalDPI: ddpi: %g, hdpi: %g, vdpi: %g",
                            ddpi, hdpi, vdpi);
         SDLTest_DrawString(renderer, 0.0f, textY, text);
         textY += lineHeight;
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 3c0c47a37f12..b413d0394ccc 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -382,12 +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 (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)) {
@@ -588,6 +592,9 @@ 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;
+        }
     }
     display.current_mode = display.desktop_mode;
 
@@ -617,6 +624,12 @@ 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;
+        }
         if (send_event) {
             SDL_SendDisplayEvent(&_this->displays[index], SDL_EVENT_DISPLAY_CONNECTED, 0);
         }
@@ -709,8 +722,8 @@ int SDL_GetDisplayBounds(int displayIndex, SDL_Rect *rect)
         SDL_GetDisplayBounds(displayIndex - 1, rect);
         rect->x += rect->w;
     }
-    rect->w = display->current_mode.w;
-    rect->h = display->current_mode.h;
+    rect->w = (int)(display->current_mode.w / display->current_mode.display_scale);
+    rect->h = (int)(display->current_mode.h / display->current_mode.display_scale);
     return 0;
 }
 
@@ -791,9 +804,7 @@ SDL_bool SDL_AddDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mo
 
     /* Go ahead and add the new mode */
     if (nmodes == display->max_display_modes) {
-        modes =
-            SDL_realloc(modes,
-                        (display->max_display_modes + 32) * sizeof(*modes));
+        modes = SDL_realloc(modes, (display->max_display_modes + 32) * sizeof(*modes));
         if (modes == NULL) {
             return SDL_FALSE;
         }
@@ -801,6 +812,9 @@ 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;
+    }
     display->num_display_modes++;
 
     /* Re-sort video modes */
@@ -813,11 +827,17 @@ 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;
+    }
 }
 
 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;
+    }
 }
 
 static int SDL_GetNumDisplayModesForDisplay(SDL_VideoDisplay *display)
@@ -902,6 +922,7 @@ 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 *current, *match;
@@ -918,6 +939,13 @@ 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;
@@ -962,6 +990,14 @@ static SDL_DisplayMode *SDL_GetClosestDisplayModeForDisplay(SDL_VideoDisplay *di
             /* Sorted highest refresh to lowest */
             if (current->refresh_rate >= target_refresh_rate) {
                 match = current;
+                continue;
+            }
+        }
+        if (current->display_scale != match->display_scale) {
+            /* Sorted lowest display scale to highest */
+            if (current->display_scale <= target_display_scale) {
+                match = current;
+                continue;
             }
         }
     }
@@ -978,6 +1014,8 @@ static SDL_DisplayMode *SDL_GetClosestDisplayModeForDisplay(SDL_VideoDisplay *di
             closest->w = mode->w;
             closest->h = mode->h;
         }
+        closest->display_scale = mode->display_scale;
+
         if (match->refresh_rate > 0.0f) {
             closest->refresh_rate = match->refresh_rate;
         } else {
diff --git a/src/video/android/SDL_androidvideo.c b/src/video/android/SDL_androidvideo.c
index 9d9d69cd7e81..925f3dd10c64 100644
--- a/src/video/android/SDL_androidvideo.c
+++ b/src/video/android/SDL_androidvideo.c
@@ -177,9 +177,11 @@ int Android_VideoInit(_THIS)
     videodata->isPausing = SDL_FALSE;
     videodata->pauseAudio = SDL_GetHintBoolean(SDL_HINT_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO, SDL_TRUE);
 
+    SDL_zero(mode);
     mode.format = Android_ScreenFormat;
     mode.w = Android_DeviceWidth;
     mode.h = Android_DeviceHeight;
+    mode.display_scale = 1.0f; /* FIXME */
     mode.refresh_rate = Android_ScreenRate;
     mode.driverdata = NULL;
 
diff --git a/src/video/cocoa/SDL_cocoamodes.m b/src/video/cocoa/SDL_cocoamodes.m
index 1abb4399777a..cc2afdc40bf4 100644
--- a/src/video/cocoa/SDL_cocoamodes.m
+++ b/src/video/cocoa/SDL_cocoamodes.m
@@ -145,6 +145,8 @@ static SDL_bool GetDisplayMode(_THIS, CGDisplayModeRef vidmode, SDL_bool vidmode
     bool usableForGUI = CGDisplayModeIsUsableForDesktopGUI(vidmode);
     int width = (int)CGDisplayModeGetWidth(vidmode);
     int height = (int)CGDisplayModeGetHeight(vidmode);
+    int pixelW = width;
+    int pixelH = height;
     uint32_t ioflags = CGDisplayModeGetIOFlags(vidmode);
     float refreshrate = GetDisplayModeRefreshRate(vidmode, link);
     Uint32 format = GetDisplayModePixelFormat(vidmode);
@@ -164,17 +166,17 @@ static SDL_bool GetDisplayMode(_THIS, CGDisplayModeRef vidmode, SDL_bool vidmode
     modes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
     CFArrayAppendValue(modes, vidmode);
 
-    /* If a list of possible diplay modes is passed in, use it to filter out
+    /* If a list of possible display modes is passed in, use it to filter out
      * modes that have duplicate sizes. We don't just rely on SDL's higher level
      * duplicate filtering because this code can choose what properties are
      * prefered, and it can add CGDisplayModes to the DisplayModeData's list of
      * modes to try (see comment below for why that's necessary).
      * CGDisplayModeGetPixelWidth and friends are only available in 10.8+. */
 #ifdef MAC_OS_X_VERSION_10_8
-    if (modelist != NULL && floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_7) {
-        int pixelW = (int)CGDisplayModeGetPixelWidth(vidmode);
-        int pixelH = (int)CGDisplayModeGetPixelHeight(vidmode);
+    pixelW = (int)CGDisplayModeGetPixelWidth(vidmode);
+    pixelH = (int)CGDisplayModeGetPixelHeight(vidmode);
 
+    if (modelist != NULL && floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_7) {
         CFIndex modescount = CFArrayGetCount(modelist);
         int i;
 
@@ -254,6 +256,7 @@ static SDL_bool GetDisplayMode(_THIS, CGDisplayModeRef vidmode, SDL_bool vidmode
     }
 #endif
 
+    SDL_zerop(mode);
     data = (SDL_DisplayModeData *)SDL_malloc(sizeof(*data));
     if (!data) {
         CFRelease(modes);
@@ -261,8 +264,9 @@ static SDL_bool GetDisplayMode(_THIS, CGDisplayModeRef vidmode, SDL_bool vidmode
     }
     data->modes = modes;
     mode->format = format;
-    mode->w = width;
-    mode->h = height;
+    mode->w = pixelW;
+    mode->h = pixelH;
+    mode->display_scale = (float)pixelW / width;
     mode->refresh_rate = refreshrate;
     mode->driverdata = data;
     return SDL_TRUE;
diff --git a/src/video/dummy/SDL_nullvideo.c b/src/video/dummy/SDL_nullvideo.c
index 6c781b9afe15..1bb6171279eb 100644
--- a/src/video/dummy/SDL_nullvideo.c
+++ b/src/video/dummy/SDL_nullvideo.c
@@ -146,8 +146,6 @@ int DUMMY_VideoInit(_THIS)
     mode.format = SDL_PIXELFORMAT_RGB888;
     mode.w = 1024;
     mode.h = 768;
-    mode.refresh_rate = 0.0f;
-    mode.driverdata = NULL;
     if (SDL_AddBasicVideoDisplay(&mode) < 0) {
         return -1;
     }
diff --git a/src/video/emscripten/SDL_emscriptenvideo.c b/src/video/emscripten/SDL_emscriptenvideo.c
index 895bedca2e5e..bb22d7e08a87 100644
--- a/src/video/emscripten/SDL_emscriptenvideo.c
+++ b/src/video/emscripten/SDL_emscriptenvideo.c
@@ -127,11 +127,10 @@ int Emscripten_VideoInit(_THIS)
     SDL_DisplayMode mode;
 
     /* Use a fake 32-bpp desktop mode */
+    SDL_zero(mode);
     mode.format = SDL_PIXELFORMAT_RGB888;
     emscripten_get_screen_size(&mode.w, &mode.h);
 
-    mode.refresh_rate = 0.0f;
-    mode.driverdata = NULL;
     if (SDL_AddBasicVideoDisplay(&mode) < 0) {
         return -1;
     }
diff --git a/src/video/haiku/SDL_bmodes.cc b/src/video/haiku/SDL_bmodes.cc
index e1ef48a94523..147bae41d706 100644
--- a/src/video/haiku/SDL_bmodes.cc
+++ b/src/video/haiku/SDL_bmodes.cc
@@ -167,6 +167,7 @@ int32 HAIKU_ColorSpaceToSDLPxFormat(uint32 colorspace)
 
 static void _BDisplayModeToSdlDisplayMode(display_mode *bmode,
         SDL_DisplayMode *mode) {
+    SDL_zerop(mode);
     mode->w = bmode->virtual_width;
     mode->h = bmode->virtual_height;
     mode->refresh_rate = get_refresh_rate(*bmode);
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.c b/src/video/kmsdrm/SDL_kmsdrmvideo.c
index e996bcba42a7..1db906aa15fc 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.c
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c
@@ -1295,6 +1295,7 @@ void KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay *display)
             modedata->mode_index = i;
         }
 
+        SDL_zero(mode);
         mode.w = conn->modes[i].hdisplay;
         mode.h = conn->modes[i].vdisplay;
         mode.refresh_rate = CalculateRefreshRate(&conn->modes[i]);
diff --git a/src/video/n3ds/SDL_n3dsvideo.c b/src/video/n3ds/SDL_n3dsvideo.c
index bcdae31f2ba9..9b43206be0b2 100644
--- a/src/video/n3ds/SDL_n3dsvideo.c
+++ b/src/video/n3ds/SDL_n3dsvideo.c
@@ -122,7 +122,6 @@ AddN3DSDisplay(gfxScreen_t screen)
     mode.h = GSP_SCREEN_WIDTH;
     mode.refresh_rate = 60.0f;
     mode.format = FRAMEBUFFER_FORMAT;
-    mode.driverdata = NULL;
 
     display.name = (screen == GFX_TOP) ? "N3DS top screen" : "N3DS bottom screen";
     display.desktop_mode = mode;
diff --git a/src/video/ngage/SDL_ngagevideo.cpp b/src/video/ngage/SDL_ngagevideo.cpp
index cc2570748dc8..388b65415379 100644
--- a/src/video/ngage/SDL_ngagevideo.cpp
+++ b/src/video/ngage/SDL_ngagevideo.cpp
@@ -148,11 +148,10 @@ int NGAGE_VideoInit(_THIS)
     SDL_DisplayMode mode;
 
     /* Use 12-bpp desktop mode */
+    SDL_zero(mode);
     mode.format = SDL_PIXELFORMAT_RGB444;
     mode.w = 176;
     mode.h = 208;
-    mode.refresh_rate = 0.0f;
-    mode.driverdata = NULL;
     if (SDL_AddBasicVideoDisplay(&mode) < 0) {
         return -1;
     }
diff --git a/src/video/offscreen/SDL_offscreenvideo.c b/src/video/offscreen/SDL_offscreenvideo.c
index 5b1156f28b48..a13477f8aad3 100644
--- a/src/video/offscreen/SDL_offscreenvideo.c
+++ b/src/video/offscreen/SDL_offscreenvideo.c
@@ -101,16 +101,14 @@ int OFFSCREEN_VideoInit(_THIS)
     SDL_DisplayMode mode;
 
     /* Use a fake 32-bpp desktop mode */
+    SDL_zero(mode);
     mode.format = SDL_PIXELFORMAT_RGB888;
     mode.w = 1024;
     mode.h = 768;
-    mode.refresh_rate = 0.0f;
-    mode.driverdata = NULL;
     if (SDL_AddBasicVideoDisplay(&mode) < 0) {
         return -1;
     }
 
-    SDL_zero(mode);
     SDL_AddDisplayMode(&_this->displays[0], &mode);
 
     /* We're done! */
diff --git a/src/video/ps2/SDL_ps2video.c b/src/video/ps2/SDL_ps2video.c
index e198a6b87669..a99357da1932 100644
--- a/src/video/ps2/SDL_ps2video.c
+++ b/src/video/ps2/SDL_ps2video.c
@@ -69,19 +69,16 @@ static int PS2_VideoInit(_THIS)
     SDL_DisplayMode current_mode;
 
     SDL_zero(current_mode);
-
     current_mode.w = 640;
     current_mode.h = 480;
     current_mode.refresh_rate = 60.0f;
 
     /* 32 bpp for default */
     current_mode.format = SDL_PIXELFORMAT_ABGR8888;
-    current_mode.driverdata = NULL;
 
     SDL_zero(display);
     display.desktop_mode = current_mode;
     display.current_mode = current_mode;
-    display.driverdata = NULL;
     SDL_AddDisplayMode(&display, &current_mode);
 
     SDL_AddVideoDisplay(&display, SDL_FALSE);
diff --git a/src/video/psp/SDL_pspvideo.c b/src/video/psp/SDL_pspvideo.c
index 70c764851e92..7ab359ed8ad8 100644
--- a/src/video/psp/SDL_pspvideo.c
+++ b/src/video/psp/SDL_pspvideo.c
@@ -140,19 +140,16 @@ int PSP_VideoInit(_THIS)
     SDL_DisplayMode current_mode;
 
     SDL_zero(current_mode);
-
     current_mode.w = 480;
     current_mode.h = 272;
-
     current_mode.refresh_rate = 60.0f;
+
     /* 32 bpp for default */
     current_mode.format = SDL_PIXELFORMAT_ABGR8888;
-    current_mode.driverdata = NULL;
 
     SDL_zero(display);
     display.desktop_mode = current_mode;
     display.current_mode = current_mode;
-    display.driverdata = NULL;
 
     SDL_AddDisplayMode(&display, &current_mode);
 
diff --git a/src/video/raspberry/SDL_rpivideo.c b/src/video/raspberry/SDL_rpivideo.c
index 7dd4017efbe4..dbc1a1d288f7 100644
--- a/src/video/raspberry/SDL_rpivideo.c
+++ b/src/video/raspberry/SDL_rpivideo.c
@@ -166,11 +166,10 @@ static void AddDispManXDisplay(const int display_id)
     current_mode.w = modeinfo.width;
     current_mode.h = modeinfo.height;
     current_mode.refresh_rate = RPI_GetRefreshRate();
+
     /* 32 bpp for default */
     current_mode.format = SDL_PIXELFORMAT_ABGR8888;
 
-    current_mode.driverdata = NULL;
-
     SDL_zero(display);
     display.desktop_mode = current_mode;
     display.current_mode = current_mode;
diff --git a/src/video/riscos/SDL_riscosmodes.c b/src/video/riscos/SDL_riscosmodes.c
index 06612c55f000..0988a9451f96 100644
--- a/src/video/riscos/SDL_riscosmodes.c
+++ b/src/video/riscos/SDL_riscosmodes.c
@@ -136,6 +136,7 @@ static SDL_bool read_mode_block(int *block, SDL_DisplayMode *mode, SDL_bool exte
         modeflags = read_mode_variable(block, 0);
     }
 
+    SDL_zerop(mode);
     mode->w = xres;
     mode->h = yres;
     mode->format = RISCOS_ModeToPixelFormat(ncolour, modeflags, log2bpp);
diff --git a/src/video/uikit/SDL_uikitmodes.m b/src/video/uikit/SDL_uikitmodes.m
index 60ade1a8f305..c1a5f8c039d7 100644
--- a/src/video/uikit/SDL_uikitmodes.m
+++ b/src/video/uikit/SDL_uikitmodes.m
@@ -263,16 +263,17 @@ static int UIKit_AddSingleDisplayMode(SDL_VideoDisplay *display, int w, int h,
                                       UIScreen *uiscreen, UIScreenMode *uiscreenmode)
 {
     SDL_DisplayMode mode;
-    SDL_zero(mode);
 
+    SDL_zero(mode);
     if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode) < 0) {
         return -1;
     }
 
-    mode.format = SDL_PIXELFORMAT_ABGR8888;
-    mode.refresh_rate = UIKit_GetDisplayModeRefreshRate(uiscreen);
     mode.w = w;
     mode.h = h;
+    mode.display_scale = uiscreen.scale;
+    mode.refresh_rate = UIKit_GetDisplayModeRefreshRate(uiscreen);
+    mode.format = SDL_PIXELFORMAT_ABGR8888;
 
     if (SDL_AddDisplayMode(display, &mode)) {
         return 0;
@@ -282,8 +283,8 @@ static int UIKit_AddSingleDisplayMode(SDL_VideoDisplay *display, int w, int h,
     }
 }
 
-static int UIKit_AddDisplayMode(SDL_VideoDisplay *display, int w, int h, UIScreen *uiscreen,
-                                UIScreenMode *uiscreenmode, SDL_bool addRotation)
+static int UIKit_AddDisplayMode(SDL_VideoDisplay *display, int w, int h,
+                                UIScreen *uiscreen, UIScreenMode *uiscreenmode, SDL_bool addRotation)
 {
     if (UIKit_AddSingleDisplayMode(display, w, h, uiscreen, uiscreenmode) < 0) {
         return -1;
@@ -302,10 +303,9 @@ static int UIKit_AddDisplayMode(SDL_VideoDisplay *display, int w, int h, UIScree
 int UIKit_AddDisplay(UIScreen *uiscreen, SDL_bool send_event)
 {
     UIScreenMode *uiscreenmode = uiscreen.currentMode;
-    CGSize size = uiscreen.bounds.size;
+    CGSize size = uiscreenmode.size;
     SDL_VideoDisplay display;
     SDL_DisplayMode mode;
-    SDL_zero(mode);
 
     /* Make sure the width/height are oriented correctly */
     if (UIKit_IsDisplayLandscape(uiscreen) != (size.width > size.height)) {
@@ -314,10 +314,12 @@ int UIKit_AddDisplay(UIScreen *uiscreen, SDL_bool send_event)
         size.height = height;
     }
 
-    mode.format = SDL_PIXELFORMAT_ABGR8888;
-    mode.refresh_rate = UIKit_GetDisplayModeRefreshRate(uiscreen);
+    SDL_zero(mode);
     mode.w = (int)size.width;
     mode.h = (int)size.height;
+    mode.display_scale = uiscreen.scale;
+    mode.format = SDL_PIXELFORMAT_ABGR8888;
+    mode.refresh_rate = UIKit_GetDisplayModeRefreshRate(uiscreen);
 
     if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode) < 0) {
         return -1;
@@ -394,7 +396,6 @@ void UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay *display)
 
         SDL_bool isLandscape = UIKit_IsDisplayLandscape(data.uiscreen);
         SDL_bool addRotation = (data.uiscreen == [UIScreen mainScreen]);
-        CGFloat scale = data.uiscreen.scale;
         NSArray *availableModes = nil;
 
 #if TARGET_OS_TV
@@ -405,19 +406,8 @@ void UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay *display)
 #endif
 
         for (UIScreenMode *uimode in availableModes) {
-            /* The size of a UIScreenMode is in pixels, but we deal exclusively
-             * in points (except in SDL_GL_GetDrawableSize.)
-             *
-             * For devices such as iPhone 6/7/8 Plus, the UIScreenMode reported
-             * by iOS is not in physical pixels of the display, but rather the
-             * point size times the scale.  For example, on iOS 12.2 on iPhone 8
-             * Plus the uimode.size is 1242x2208 and the uiscreen.scale is 3
-             * thus this will give the size in points which is 414x736. The code
-             * used to use the nativeScale, assuming UIScreenMode returned raw
-             * physical pixels (as suggested by its documentation, but in
-             * practice it is returning the retina pixels). */
-            int w = (int)(uimode.size.width / scale);
-            int h = (int)(uimode.size.height / scale);
+            int w = uimode.size.width;
+            int h = uimode.size.height;
 
             /* Make sure the width/height are oriented correctly */
             if (isLandscape != (w > h)) {
diff --git a/src/video/vita/SDL_vitavideo.c b/src/video/vita/SDL_vitavideo.c
index 6732e47017fe..1d792eec9b8d 100644
--- a/src/video/vita/SDL_vitavideo.c
+++ b/src/video/vita/SDL_vitavideo.c
@@ -210,15 +210,13 @@ int VITA_VideoInit(_THIS)
 #endif
 
     current_mode.refresh_rate = 60.0f;
+
     /* 32 bpp for default */
     current_mode.format = SDL_PIXELFORMAT_ABGR8888;
 
-    current_mode.driverdata = NULL;
-
     SDL_zero(display);
     display.desktop_mode = current_mode;
     display.current_mode = current_mode;
-    display.driverdata = NULL;
 
     SDL_AddVideoDisplay(&display, SDL_FALSE);
     VITA_InitTouch();
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index ee5762f93e7d..65308674ccdd 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -570,6 +570,7 @@ static void display_handle_done(void *data,
         native_mode.w = driverdata->native_width;
         native_mode.h = driverdata->native_height;
     }
+    native_mode.display_scale = 1.0f; /* FIXME */
     native_mode.refresh_rate = ((100 * driverdata->refresh) / 1000) / 100.0f; /* mHz to Hz */
     native_mode.driverdata = driverdata->output;
 
diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c
index 1292cd2219b6..2bea542e5cf1 100644
--- a/src/video/windows/SDL_windowsmodes.c
+++ b/src/video/windows/SDL_windowsmodes.c
@@ -47,20 +47,6 @@ static void WIN_UpdateDisplayMode(_THIS, LPCWSTR deviceName, DWORD index, SDL_Di
         char bmi_data[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)];
         LPBITMAPINFO bmi;
         HBITMAP hbm;
-        int logical_width = GetDeviceCaps(hdc, HORZRES);
-        int logical_height = GetDeviceCaps(hdc, VERTRES);
-
-        /* High-DPI notes:
-
-           If DPI-unaware:
-             - GetDeviceCaps( hdc, HORZRES ) will return the monitor width in points.
-             - DeviceMode.dmPelsWidth is actual pixels (unlike almost all other Windows API's,
-               it's not virtualized when DPI unaware).
-
-           If DPI-aware:
-            - GetDeviceCaps( hdc, HORZRES ) will return pixels, same as DeviceMode.dmPelsWidth */
-        mode->w = logical_width;
-        mode->h = logical_height;
 
         SDL_zeroa(bmi_data);
         bmi = (LPBITMAPINFO)bmi_data;
@@ -172,8 +158,9 @@ static float WIN_GetRefreshRate(DEVMODE *mode)
     }
 }
 
-static SDL_bool WIN_GetDisplayMode(_THIS, LPCWSTR deviceName, DWORD index, SDL_DisplayMode *mode, SDL_DisplayOrientation *orientation)
+static SDL_bool WIN_GetDisplayMode(_THIS, HMONITOR hMonitor, LPCWSTR deviceName, DWORD index, SDL_DisplayMode *mode, SDL_DisplayOrientation *orientation)
 {
+    const SDL_VideoData *videodata = (const SDL_VideoData *)_this->driverdata;
     SDL_DisplayModeData *data;
     DEVMODE devmode;
 
@@ -188,6 +175,7 @@ static SDL_bool WIN_GetDisplayMode(_THIS, LPCWSTR deviceName, DWORD index, SDL_D
         return SDL_FALSE;
     }
 
+    SDL_zerop(mode);
     mode->driverdata = data;
     data->DeviceMode = devmode;
 
@@ -196,6 +184,13 @@ static SDL_bool WIN_GetDisplayMode(_THIS, LPCWSTR deviceName, DWORD index, SDL_D
     mode->h = data->DeviceMode.dmPelsHeight;
     mode->refresh_rate = WIN_GetRefreshRate(&data->DeviceMode);
 
+    if (index == ENUM_CURRENT_SETTINGS && videodata->GetDpiForMonitor) {
+        UINT hdpi_uint, vdpi_uint;
+        if (videodata->GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &hdpi_uint, &vdpi_uint) == S_OK) {
+            mode->display_scale = (float)hdpi_uint / 96;
+        }
+    }
+
     /* Fill in the mode information */
     WIN_UpdateDisplayMode(_this, deviceName, index, mode);
 
@@ -319,7 +314,7 @@ static void WIN_AddDisplay(_THIS, HMONITOR hMonitor, const MONITORINFOEXW *info,
     SDL_Log("Display: %s\n", WIN_StringToUTF8W(info->szDevice));
 #endif
 
-    if (!WIN_GetDispl

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