SDL: wayland: Correct mode values to use pixels instead of screen units

From ba74e76e56f9b2a6f0af70fc672ebd6a26c24fe1 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Thu, 26 Jan 2023 22:29:38 -0500
Subject: [PATCH] wayland: Correct mode values to use pixels instead of screen
 units

Fixes the Wayland backend to report the desktop mode dimensions in pixels instead of screen units, adjusts enumerated fullscreen resolutions to use the correct pixel values and scaling, and changes some nomenclature to reflect the terminology used in the new DPI system.
---
 src/video/wayland/SDL_waylandvideo.c  | 74 +++++++++++++++------------
 src/video/wayland/SDL_waylandvideo.h  |  4 +-
 src/video/wayland/SDL_waylandwindow.c | 63 +++++++++++++----------
 3 files changed, 79 insertions(+), 62 deletions(-)

diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index 4ab593966bf5..c2db92e5b1f8 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -298,7 +298,7 @@ static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xd
 {
     SDL_WaylandOutputData *driverdata = data;
 
-    if (driverdata->width != 0 && driverdata->height != 0) {
+    if (driverdata->screen_width != 0 && driverdata->screen_height != 0) {
         /* FIXME: GNOME has a bug where the logical size does not account for
          * scale, resulting in bogus viewport sizes.
          *
@@ -307,7 +307,7 @@ static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xd
          * detected otherwise), then override if necessary.
          * -flibit
          */
-        const float scale = (float)driverdata->width / (float)width;
+        const float scale = (float)driverdata->screen_width / (float)width;
         if ((scale == 1.0f) && (driverdata->scale_factor != 1.0f)) {
             SDL_LogWarn(
                 SDL_LOG_CATEGORY_VIDEO,
@@ -316,8 +316,8 @@ static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xd
         }
     }
 
-    driverdata->width = width;
-    driverdata->height = height;
+    driverdata->screen_width = width;
+    driverdata->screen_height = height;
     driverdata->has_logical_size = SDL_TRUE;
 }
 
@@ -414,12 +414,15 @@ static void AddEmulatedModes(SDL_VideoDisplay *dpy, SDL_bool rot_90)
 
     int i;
     SDL_DisplayMode mode;
-    const int native_width = dpy->display_modes->w;
-    const int native_height = dpy->display_modes->h;
+    const int native_width = dpy->display_modes->pixel_w;
+    const int native_height = dpy->display_modes->pixel_h;
 
     for (i = 0; i < SDL_arraysize(mode_list); ++i) {
-        mode = *dpy->display_modes;
+        SDL_zero(mode);
+        mode.format = dpy->display_modes->format;
         mode.display_scale = 1.0f;
+        mode.refresh_rate = dpy->display_modes->refresh_rate;
+        mode.driverdata = dpy->display_modes->driverdata;
 
         if (rot_90) {
             mode.pixel_w = mode_list[i].h;
@@ -521,16 +524,16 @@ static void display_handle_mode(void *data,
     SDL_WaylandOutputData *driverdata = data;
 
     if (flags & WL_OUTPUT_MODE_CURRENT) {
-        driverdata->native_width = width;
-        driverdata->native_height = height;
+        driverdata->pixel_width = width;
+        driverdata->pixel_height = height;
 
         /*
          * Don't rotate this yet, wl-output coordinates are transformed in
          * handle_done and xdg-output coordinates are pre-transformed.
          */
         if (!driverdata->has_logical_size) {
-            driverdata->width = width;
-            driverdata->height = height;
+            driverdata->screen_width = width;
+            driverdata->screen_height = height;
         }
 
         driverdata->refresh = refresh;
@@ -565,11 +568,11 @@ static void display_handle_done(void *data,
     native_mode.format = SDL_PIXELFORMAT_RGB888;
 
     if (driverdata->transform & WL_OUTPUT_TRANSFORM_90) {
-        native_mode.pixel_w = driverdata->native_height;
-        native_mode.pixel_h = driverdata->native_width;
+        native_mode.pixel_w = driverdata->pixel_height;
+        native_mode.pixel_h = driverdata->pixel_width;
     } else {
-        native_mode.pixel_w = driverdata->native_width;
-        native_mode.pixel_h = driverdata->native_height;
+        native_mode.pixel_w = driverdata->pixel_width;
+        native_mode.pixel_h = driverdata->pixel_height;
     }
     native_mode.display_scale = 1.0f;
     native_mode.refresh_rate = ((100 * driverdata->refresh) / 1000) / 100.0f; /* mHz to Hz */
@@ -581,20 +584,20 @@ static void display_handle_done(void *data,
 
     if (driverdata->has_logical_size) { /* If xdg-output is present, calculate the true scale of the desktop */
         if (video->viewporter) {
-            driverdata->scale_factor = (float)native_mode.w / (float)driverdata->width;
+            driverdata->scale_factor = (float)native_mode.pixel_w / (float)driverdata->screen_width;
         }
     } else { /* Scale the desktop coordinates, if xdg-output isn't present */
-        driverdata->width /= driverdata->scale_factor;
-        driverdata->height /= driverdata->scale_factor;
+        driverdata->screen_width /= driverdata->scale_factor;
+        driverdata->screen_height /= driverdata->scale_factor;
     }
 
     /* xdg-output dimensions are already transformed, so no need to rotate. */
     if (driverdata->has_logical_size || !(driverdata->transform & WL_OUTPUT_TRANSFORM_90)) {
-        desktop_mode.pixel_w = driverdata->width;
-        desktop_mode.pixel_h = driverdata->height;
+        desktop_mode.pixel_w = driverdata->pixel_width;
+        desktop_mode.pixel_h = driverdata->pixel_height;
     } else {
-        desktop_mode.pixel_w = driverdata->height;
-        desktop_mode.pixel_h = driverdata->width;
+        desktop_mode.pixel_w = driverdata->pixel_height;
+        desktop_mode.pixel_h = driverdata->pixel_width;
     }
     desktop_mode.display_scale = driverdata->scale_factor;
     desktop_mode.refresh_rate = ((100 * driverdata->refresh) / 1000) / 100.0f; /* mHz to Hz */
@@ -617,10 +620,15 @@ static void display_handle_done(void *data,
         if (video->viewporter != NULL) {
             SDL_AddDisplayMode(dpy, &native_mode);
         } else {
-            /* ...if not, expose the integer scale desktop modes down to 1.0. */
+            /* ...if not, expose some smaller, integer scaled resolutions. */
             int i;
-            for (i = (int)driverdata->scale_factor - 1; i > 0; --i) {
+            const int base_pixel_w = desktop_mode.pixel_w / (int)desktop_mode.display_scale;
+            const int base_pixel_h = desktop_mode.pixel_h / (int)desktop_mode.display_scale;
+            for (i = 1; i < (int)desktop_mode.display_scale; ++i) {
+                desktop_mode.pixel_w = base_pixel_w * i;
+                desktop_mode.pixel_h = base_pixel_h * i;
                 desktop_mode.display_scale = (float)i;
+
                 SDL_AddDisplayMode(dpy, &desktop_mode);
             }
         }
@@ -629,23 +637,23 @@ static void display_handle_done(void *data,
     /* Add emulated modes if wp_viewporter is supported and mode emulation is enabled. */
     if (video->viewporter && mode_emulation_enabled) {
         const SDL_bool rot_90 = ((driverdata->transform & WL_OUTPUT_TRANSFORM_90) != 0) ||
-                                (driverdata->width < driverdata->height);
+                                (driverdata->screen_width < driverdata->screen_height);
         AddEmulatedModes(dpy, rot_90);
     }
 
     /* Calculate the display DPI */
     if (driverdata->transform & WL_OUTPUT_TRANSFORM_90) {
-        driverdata->hdpi = driverdata->physical_height ? (((float)driverdata->height) * 25.4f / driverdata->physical_height) : 0.0f;
-        driverdata->vdpi = driverdata->physical_width ? (((float)driverdata->width) * 25.4f / driverdata->physical_width) : 0.0f;
-        driverdata->ddpi = SDL_ComputeDiagonalDPI(driverdata->height,
-                                                  driverdata->width,
+        driverdata->hdpi = driverdata->physical_height ? (((float)driverdata->pixel_height) * 25.4f / driverdata->physical_height) : 0.0f;
+        driverdata->vdpi = driverdata->physical_width ? (((float)driverdata->pixel_width) * 25.4f / driverdata->physical_width) : 0.0f;
+        driverdata->ddpi = SDL_ComputeDiagonalDPI(driverdata->pixel_height,
+                                                  driverdata->pixel_width,
                                                   ((float)driverdata->physical_height) / 25.4f,
                                                   ((float)driverdata->physical_width) / 25.4f);
     } else {
-        driverdata->hdpi = driverdata->physical_width ? (((float)driverdata->width) * 25.4f / driverdata->physical_width) : 0.0f;
-        driverdata->vdpi = driverdata->physical_height ? (((float)driverdata->height) * 25.4f / driverdata->physical_height) : 0.0f;
-        driverdata->ddpi = SDL_ComputeDiagonalDPI(driverdata->width,
-                                                  driverdata->height,
+        driverdata->hdpi = driverdata->physical_width ? (((float)driverdata->pixel_width) * 25.4f / driverdata->physical_width) : 0.0f;
+        driverdata->vdpi = driverdata->physical_height ? (((float)driverdata->pixel_height) * 25.4f / driverdata->physical_height) : 0.0f;
+        driverdata->ddpi = SDL_ComputeDiagonalDPI(driverdata->pixel_width,
+                                                  driverdata->pixel_height,
                                                   ((float)driverdata->physical_width) / 25.4f,
                                                   ((float)driverdata->physical_height) / 25.4f);
     }
diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h
index 0054490b39c1..62f1f6a1f9a9 100644
--- a/src/video/wayland/SDL_waylandvideo.h
+++ b/src/video/wayland/SDL_waylandvideo.h
@@ -109,8 +109,8 @@ struct SDL_WaylandOutputData
     struct zxdg_output_v1 *xdg_output;
     uint32_t registry_id;
     float scale_factor;
-    int native_width, native_height;
-    int x, y, width, height, refresh, transform;
+    int pixel_width, pixel_height;
+    int x, y, screen_width, screen_height, refresh, transform;
     SDL_DisplayOrientation orientation;
     int physical_width, physical_height;
     float ddpi, hdpi, vdpi;
diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index b9afc46dde38..78fe3829f5d1 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -62,8 +62,8 @@ static void GetFullScreenDimensions(SDL_Window *window, int *width, int *height,
 
     int fs_width, fs_height;
     int buf_width, buf_height;
-    const int output_width = wind->fs_output_width ? wind->fs_output_width : output->width;
-    const int output_height = wind->fs_output_height ? wind->fs_output_height : output->height;
+    const int output_width = wind->fs_output_width ? wind->fs_output_width : output->screen_width;
+    const int output_height = wind->fs_output_height ? wind->fs_output_height : output->screen_height;
 
     /*
      * Fullscreen desktop mandates a desktop sized window, so that's what applications will get.
@@ -76,24 +76,24 @@ static void GetFullScreenDimensions(SDL_Window *window, int *width, int *height,
 
         /* If the application is DPI aware, we can expose the true backbuffer size */
         if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
-            buf_width = output->native_width;
-            buf_height = output->native_height;
+            buf_width = output->pixel_width;
+            buf_height = output->pixel_height;
         } else {
             buf_width = fs_width;
             buf_height = fs_height;
         }
     } else {
         /* If a mode was set, use it, otherwise use the native resolution. */
-        if (window->fullscreen_mode.w != 0 && window->fullscreen_mode.h != 0) {
-            fs_width = window->fullscreen_mode.w;
-            fs_height = window->fullscreen_mode.h;
-            buf_width = (int)SDL_lroundf((float)fs_width * window->fullscreen_mode.display_scale);
-            buf_height = (int)SDL_lroundf((float)fs_height * window->fullscreen_mode.display_scale);
+        if (window->fullscreen_mode.pixel_w != 0 && window->fullscreen_mode.pixel_h != 0) {
+            fs_width = window->fullscreen_mode.screen_w;
+            fs_height = window->fullscreen_mode.screen_h;
+            buf_width = window->fullscreen_mode.pixel_w;
+            buf_height = window->fullscreen_mode.pixel_h;
         } else {
-            fs_width = disp->display_modes[0].w;
-            fs_height = disp->display_modes[0].h;
-            buf_width = (int)SDL_lroundf((float)fs_width * disp->display_modes[0].display_scale);
-            buf_height = (int)SDL_lroundf((float)fs_width * disp->display_modes[0].display_scale);
+            fs_width = disp->display_modes[0].screen_w;
+            fs_height = disp->display_modes[0].screen_h;
+            buf_width = disp->display_modes[0].pixel_w;
+            buf_height = disp->display_modes[0].pixel_h;
         }
     }
 
@@ -129,21 +129,23 @@ static SDL_bool WindowNeedsViewport(SDL_Window *window)
     SDL_WindowData *wind = window->driverdata;
     SDL_VideoData *video = wind->waylandData;
     SDL_WaylandOutputData *output = ((SDL_WaylandOutputData *)SDL_GetDisplayForWindow(window)->driverdata);
+    const int output_width = wind->fs_output_width ? wind->fs_output_width : output->screen_width;
+    const int output_height = wind->fs_output_height ? wind->fs_output_height : output->screen_height;
     int fs_width, fs_height;
 
     /*
      * A viewport is only required when scaling is enabled and:
+     *  - The surface scale is fractional.
      *  - A fullscreen mode is being emulated and the mode does not match the logical desktop dimensions.
-     *  - The desktop uses fractional scaling and the high-DPI flag is set.
      */
     if (video->viewporter != NULL) {
-        if (FullscreenModeEmulation(window)) {
+        if (SurfaceScaleIsFractional(window)) {
+            return SDL_TRUE;
+        } else if (FullscreenModeEmulation(window)) {
             GetFullScreenDimensions(window, &fs_width, &fs_height, NULL, NULL);
-            if (fs_width != output->width || fs_height != output->height) {
+            if (fs_width != output_width || fs_height != output_height) {
                 return SDL_TRUE;
             }
-        } else if (SurfaceScaleIsFractional(window) && (window->flags & SDL_WINDOW_ALLOW_HIGHDPI)) {
-            return SDL_TRUE;
         }
     }
 
@@ -225,8 +227,8 @@ static void ConfigureWindowGeometry(SDL_Window *window)
 
     if (FullscreenModeEmulation(window)) {
         int fs_width, fs_height;
-        const int output_width = data->fs_output_width ? data->fs_output_width : output->width;
-        const int output_height = data->fs_output_height ? data->fs_output_height : output->height;
+        const int output_width = data->fs_output_width ? data->fs_output_width : output->screen_width;
+        const int output_height = data->fs_output_height ? data->fs_output_height : output->screen_height;
 
         window_size_changed = data->window_width != output_width || data->window_height != output_height;
 
@@ -238,14 +240,21 @@ static void ConfigureWindowGeometry(SDL_Window *window)
                 wl_surface_set_buffer_scale(data->surface, 1);
                 SetDrawSurfaceViewport(window, data->drawable_width, data->drawable_height,
                                        output_width, output_height);
+
+                data->window_width = output_width;
+                data->window_height = output_height;
+
+                data->pointer_scale_x = (float)fs_width / (float)output_width;
+                data->pointer_scale_y = (float)fs_height / (float)output_height;
             } else {
                 wl_surface_set_buffer_scale(data->surface, (int32_t)window->fullscreen_mode.display_scale);
-            }
-            data->window_width = output_width;
-            data->window_height = output_height;
 
-            data->pointer_scale_x = (float)fs_width / (float)output_width;
-            data->pointer_scale_y = (float)fs_height / (float)output_height;
+                data->window_width = fs_width;
+                data->window_height = fs_height;
+
+                data->pointer_scale_x = 1.0f;
+                data->pointer_scale_y = 1.0f;
+            }
         }
     } else {
         window_size_changed = data->window_width != window->w || data->window_height != window->h;
@@ -444,13 +453,13 @@ static void UpdateWindowFullscreen(SDL_Window *window, SDL_bool fullscreen)
              * otherwise, fall back to SDL_WINDOW_FULLSCREEN_DESKTOP.
              */
             if (!wind->fullscreen_flags) {
-                if (window->fullscreen_mode.w && window->fullscreen_mode.h) {
+                if (window->fullscreen_mode.pixel_w && window->fullscreen_mode.pixel_h) {
                     wind->fullscreen_flags = SDL_WINDOW_FULLSCREEN;
                 } else {
                     wind->fullscreen_flags = SDL_WINDOW_FULLSCREEN_DESKTOP;
                 }
             } else if (wind->fullscreen_flags != SDL_WINDOW_FULLSCREEN_DESKTOP &&
-                       (!window->fullscreen_mode.w || !window->fullscreen_mode.h)) {
+                       (!window->fullscreen_mode.pixel_w || !window->fullscreen_mode.pixel_h)) {
                 wind->fullscreen_flags = SDL_WINDOW_FULLSCREEN_DESKTOP;
             }