Browse Source

wayland: Remove all references to destroyed outputs from windows

The removal of a wl_output may not be accompanied by leave events for the surfaces present on it. Ensure that no window continues to hold a reference to a removed output.
Frank Praznik 1 year ago
parent
commit
9eaf7d8cc8

+ 9 - 0
src/video/wayland/SDL_waylandvideo.c

@@ -732,6 +732,7 @@ static void Wayland_free_display(SDL_VideoData *d, uint32_t id)
     int num_displays = SDL_GetNumVideoDisplays();
     SDL_VideoDisplay *display;
     SDL_WaylandOutputData *data;
+    SDL_Window *window;
     int i;
 
     for (i = 0; i < num_displays; i += 1) {
@@ -751,6 +752,14 @@ static void Wayland_free_display(SDL_VideoData *d, uint32_t id)
                     }
                 }
             }
+
+            /* Surface leave events may be implicit when an output is destroyed, so make sure that
+             * no windows retain a reference to a destroyed output.
+             */
+            for (window = SDL_GetVideoDevice()->windows; window; window = window->next) {
+                Wayland_RemoveOutputFromWindow(window->driverdata, data->output);
+            }
+
             SDL_DelVideoDisplay(i);
             if (data->xdg_output) {
                 zxdg_output_v1_destroy(data->xdg_output);

+ 35 - 29
src/video/wayland/SDL_waylandwindow.c

@@ -1098,39 +1098,11 @@ static void Wayland_move_window(SDL_Window *window,
     }
 }
 
-static void handle_surface_enter(void *data, struct wl_surface *surface,
-                                 struct wl_output *output)
+void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, struct wl_output *output)
 {
-    SDL_WindowData *window = data;
-    SDL_WaylandOutputData *driverdata = wl_output_get_user_data(output);
-
-    if (!SDL_WAYLAND_own_output(output) || !SDL_WAYLAND_own_surface(surface)) {
-        return;
-    }
-
-    window->outputs = SDL_realloc(window->outputs,
-                                  sizeof(SDL_WaylandOutputData *) * (window->num_outputs + 1));
-    window->outputs[window->num_outputs++] = driverdata;
-
-    /* Update the scale factor after the move so that fullscreen outputs are updated. */
-    Wayland_move_window(window->sdlwindow, driverdata);
-
-    if (!window->fractional_scale) {
-        update_scale_factor(window);
-    }
-}
-
-static void handle_surface_leave(void *data, struct wl_surface *surface,
-                                 struct wl_output *output)
-{
-    SDL_WindowData *window = data;
     int i, send_move_event = 0;
     SDL_WaylandOutputData *driverdata = wl_output_get_user_data(output);
 
-    if (!SDL_WAYLAND_own_output(output) || !SDL_WAYLAND_own_surface(surface)) {
-        return;
-    }
-
     for (i = 0; i < window->num_outputs; i++) {
         if (window->outputs[i] == driverdata) { /* remove this one */
             if (i == (window->num_outputs - 1)) {
@@ -1159,6 +1131,40 @@ static void handle_surface_leave(void *data, struct wl_surface *surface,
     }
 }
 
+static void 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_output(output) || !SDL_WAYLAND_own_surface(surface)) {
+        return;
+    }
+
+    window->outputs = SDL_realloc(window->outputs,
+                                  sizeof(SDL_WaylandOutputData *) * (window->num_outputs + 1));
+    window->outputs[window->num_outputs++] = driverdata;
+
+    /* Update the scale factor after the move so that fullscreen outputs are updated. */
+    Wayland_move_window(window->sdlwindow, driverdata);
+
+    if (!window->fractional_scale) {
+        update_scale_factor(window);
+    }
+}
+
+static void handle_surface_leave(void *data, struct wl_surface *surface,
+                          struct wl_output *output)
+{
+    SDL_WindowData *window = (SDL_WindowData *)data;
+
+    if (!SDL_WAYLAND_own_output(output) || !SDL_WAYLAND_own_surface(surface)) {
+        return;
+    }
+
+    Wayland_RemoveOutputFromWindow(window, output);
+}
+
 static const struct wl_surface_listener surface_listener = {
     handle_surface_enter,
     handle_surface_leave

+ 1 - 0
src/video/wayland/SDL_waylandwindow.h

@@ -146,6 +146,7 @@ extern SDL_bool
 Wayland_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info);
 extern int Wayland_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
 extern int Wayland_FlashWindow(_THIS, SDL_Window *window, SDL_FlashOperation operation);
+extern void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, struct wl_output *output);
 
 extern void Wayland_InitWin(SDL_VideoData *data);
 extern void Wayland_QuitWin(SDL_VideoData *data);