SDL: wayland: Rework enter/leave and update_scale_factor to avoid bogus wl_output data.

From a3eb297ec241fb65a7d11b047ab56144d5bb3aff Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Tue, 27 Jul 2021 17:17:19 -0400
Subject: [PATCH] wayland: Rework enter/leave and update_scale_factor to avoid
 bogus wl_output data.

Also remove get_window_scale_factor() which was just pointless indirection.
---
 src/video/wayland/SDL_waylandwindow.c | 128 +++++++++++++++-----------
 src/video/wayland/SDL_waylandwindow.h |   2 +-
 2 files changed, 76 insertions(+), 54 deletions(-)

diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index d4bbb44b9..344de837a 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -52,10 +52,6 @@ SDL_bool SDL_WAYLAND_own_surface(struct wl_surface *surface)
     return SDL_TRUE; /* For older clients we have to assume this is us... */
 }
 
-static float get_window_scale_factor(SDL_Window *window) {
-      return ((SDL_WindowData*)window->driverdata)->scale_factor;
-}
-
 static void
 CommitMinMaxDimensions(SDL_Window *window)
 {
@@ -182,9 +178,12 @@ handle_configure_xdg_shell_surface(void *data, struct xdg_surface *xdg, uint32_t
         window->w = wind->resize.width;
         window->h = wind->resize.height;
 
-        wl_surface_set_buffer_scale(wind->surface, get_window_scale_factor(window));
+        wl_surface_set_buffer_scale(wind->surface, wind->scale_factor);
         if (wind->egl_window) {
-            WAYLAND_wl_egl_window_resize(wind->egl_window, window->w * get_window_scale_factor(window), window->h * get_window_scale_factor(window), 0, 0);
+            WAYLAND_wl_egl_window_resize(wind->egl_window,
+                                         window->w * wind->scale_factor,
+                                         window->h * wind->scale_factor,
+                                         0, 0);
         }
 
         xdg_surface_ack_configure(xdg, serial);
@@ -422,42 +421,46 @@ static const struct qt_extended_surface_listener extended_surface_listener = {
 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
 
 static void
-update_scale_factor(SDL_WindowData *window) {
-   float old_factor = window->scale_factor, new_factor = 0.0;
-   int i;
-
-   if (!(window->sdlwindow->flags & SDL_WINDOW_ALLOW_HIGHDPI)) {
-       return;
-   }
-
-   if (!window->num_outputs) {
-       new_factor = old_factor;
-   }
-
-   if (FULLSCREEN_VISIBLE(window->sdlwindow)) {
-       SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window->sdlwindow);
-       SDL_WaylandOutputData* driverdata = display->driverdata;
-       new_factor = driverdata->scale_factor;
-   }
-
-   for (i = 0; i < window->num_outputs; i++) {
-       SDL_WaylandOutputData* driverdata = wl_output_get_user_data(window->outputs[i]);
-       float factor = driverdata->scale_factor;
-       if (factor > new_factor) {
-           new_factor = factor;
-       }
-   }
-
-   if (new_factor != old_factor) {
-       /* force the resize event to trigger, as the logical size didn't change */
-       window->resize.width = window->sdlwindow->w;
-       window->resize.height = window->sdlwindow->h;
-       window->resize.scale_factor = new_factor;
-       window->resize.pending = SDL_TRUE;
-       if (!(window->sdlwindow->flags & SDL_WINDOW_OPENGL)) {
-           Wayland_HandlePendingResize(window->sdlwindow);  /* OpenGL windows handle this in SwapWindow */
-       }
-   }
+update_scale_factor(SDL_WindowData *window)
+{
+    float old_factor = window->scale_factor;
+    float new_factor;
+    int i;
+
+    if (!(window->sdlwindow->flags & SDL_WINDOW_ALLOW_HIGHDPI)) {
+        /* Scale will always be 1, just ignore this */
+        return;
+    }
+
+    if (FULLSCREEN_VISIBLE(window->sdlwindow)) {
+        /* For fullscreen, use the active display's scale factor */
+        SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window->sdlwindow);
+        SDL_WaylandOutputData* driverdata = display->driverdata;
+        new_factor = driverdata->scale_factor;
+    } else if (window->num_outputs == 0) {
+        /* No monitor (somehow)? Just fall back. */
+        new_factor = old_factor;
+    } else {
+        /* Check every display's factor, use the highest */
+        new_factor = 0.0f;
+        for (i = 0; i < window->num_outputs; i++) {
+            SDL_WaylandOutputData* driverdata = window->outputs[i];
+            if (driverdata->scale_factor > new_factor) {
+                new_factor = driverdata->scale_factor;
+            }
+        }
+    }
+
+    if (new_factor != old_factor) {
+        /* force the resize event to trigger, as the logical size didn't change */
+        window->resize.width = window->sdlwindow->w;
+        window->resize.height = window->sdlwindow->h;
+        window->resize.scale_factor = new_factor;
+        window->resize.pending = SDL_TRUE;
+        if (!(window->sdlwindow->flags & SDL_WINDOW_OPENGL)) {
+            Wayland_HandlePendingResize(window->sdlwindow);  /* OpenGL windows handle this in SwapWindow */
+        }
+    }
 }
 
 /* While we can't get window position from the compositor, we do at least know
@@ -484,12 +487,18 @@ handle_surface_enter(void *data, struct wl_surface *surface,
                      struct wl_output *output)
 {
     SDL_WindowData *window = data;
+    SDL_WaylandOutputData *driverdata = wl_output_get_user_data(output);
+
+    if (!SDL_WAYLAND_own_surface(surface)) {
+        return;
+    }
 
-    window->outputs = SDL_realloc(window->outputs, (window->num_outputs + 1) * sizeof *window->outputs);
-    window->outputs[window->num_outputs++] = output;
+    window->outputs = SDL_realloc(window->outputs,
+                                  sizeof(SDL_WaylandOutputData*) * (window->num_outputs + 1));
+    window->outputs[window->num_outputs++] = driverdata;
     update_scale_factor(window);
 
-    Wayland_move_window(window->sdlwindow, wl_output_get_user_data(output));
+    Wayland_move_window(window->sdlwindow, driverdata);
 }
 
 static void
@@ -498,14 +507,21 @@ handle_surface_leave(void *data, struct wl_surface *surface,
 {
     SDL_WindowData *window = data;
     int i, send_move_event = 0;
+    SDL_WaylandOutputData *driverdata = wl_output_get_user_data(output);
+
+    if (!SDL_WAYLAND_own_surface(surface)) {
+        return;
+    }
 
     for (i = 0; i < window->num_outputs; i++) {
-        if (window->outputs[i] == output) {  /* remove this one */
+        if (window->outputs[i] == driverdata) {  /* remove this one */
             if (i == (window->num_outputs-1)) {
                 window->outputs[i] = NULL;
                 send_move_event = 1;
             } else {
-                SDL_memmove(&window->outputs[i], &window->outputs[i+1], sizeof (output) * ((window->num_outputs - i) - 1));
+                SDL_memmove(&window->outputs[i],
+                            &window->outputs[i + 1],
+                            sizeof(SDL_WaylandOutputData*) * ((window->num_outputs - i) - 1));
             }
             window->num_outputs--;
             i--;
@@ -513,11 +529,11 @@ handle_surface_leave(void *data, struct wl_surface *surface,
     }
 
     if (window->num_outputs == 0) {
-       SDL_free(window->outputs);
-       window->outputs = NULL;
+        SDL_free(window->outputs);
+        window->outputs = NULL;
     } else if (send_move_event) {
         Wayland_move_window(window->sdlwindow,
-                            wl_output_get_user_data(window->outputs[window->num_outputs - 1]));
+                            window->outputs[window->num_outputs - 1]);
     }
 
     update_scale_factor(window);
@@ -1205,7 +1221,10 @@ Wayland_HandlePendingResize(SDL_Window *window)
         data->scale_factor = data->resize.scale_factor;
         wl_surface_set_buffer_scale(data->surface, data->scale_factor);
         if (data->egl_window) {
-            WAYLAND_wl_egl_window_resize(data->egl_window, window->w * data->scale_factor, window->h * data->scale_factor, 0, 0);
+            WAYLAND_wl_egl_window_resize(data->egl_window,
+                                         window->w * data->scale_factor,
+                                         window->h * data->scale_factor,
+                                         0, 0);
         }
 
         if (data->resize.configure) {
@@ -1259,10 +1278,13 @@ void Wayland_SetWindowSize(_THIS, SDL_Window * window)
     }
 #endif
 
-    wl_surface_set_buffer_scale(wind->surface, get_window_scale_factor(window));
+    wl_surface_set_buffer_scale(wind->surface, wind->scale_factor);
 
     if (wind->egl_window) {
-        WAYLAND_wl_egl_window_resize(wind->egl_window, window->w * get_window_scale_factor(window), window->h * get_window_scale_factor(window), 0, 0);
+        WAYLAND_wl_egl_window_resize(wind->egl_window,
+                                     window->w * wind->scale_factor,
+                                     window->h * wind->scale_factor,
+                                     0, 0);
     }
 
 #ifdef HAVE_LIBDECOR_H
diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h
index fec5d9879..01f1614f2 100644
--- a/src/video/wayland/SDL_waylandwindow.h
+++ b/src/video/wayland/SDL_waylandwindow.h
@@ -81,7 +81,7 @@ typedef struct {
         float scale_factor;
     } resize;
 
-    struct wl_output **outputs;
+    SDL_WaylandOutputData **outputs;
     int num_outputs;
 
     float scale_factor;