From c259a20f9674610f9537fa90b8e9d42de49fb5c0 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Tue, 27 Feb 2024 12:01:19 -0500 Subject: [PATCH] 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. --- src/video/wayland/SDL_waylandvideo.c | 7 ++++ src/video/wayland/SDL_waylandwindow.c | 60 +++++++++++++++------------ src/video/wayland/SDL_waylandwindow.h | 2 + 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 0ba46f43e..bd7fd44f4 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -947,6 +947,13 @@ static void Wayland_free_display(SDL_VideoDisplay *display) if (display) { SDL_DisplayData *display_data = display->driverdata; + /* A preceding surface leave event is not guaranteed when an output is removed, + * so ensure that no window continues to hold a reference to a removed output. + */ + for (SDL_Window *window = SDL_GetVideoDevice()->windows; window; window = window->next) { + Wayland_RemoveOutputFromWindow(window->driverdata, display_data->output); + } + SDL_free(display_data->wl_output_name); if (display_data->xdg_output) { diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index 09f1963d1..1aa64b22a 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -1244,7 +1244,7 @@ static void Wayland_MaybeUpdateScaleFactor(SDL_WindowData *window) factor = SDL_max(factor, driverdata->scale_factor); } } else { - /* No monitor (somehow)? Just fall back. */ + /* All outputs removed, just fall back. */ factor = window->windowed_scale_factor; } @@ -1304,6 +1304,37 @@ static void Wayland_move_window(SDL_Window *window, SDL_DisplayData *driverdata) } } +void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, struct wl_output *output) +{ + int i, send_move_event = 0; + SDL_DisplayData *driverdata = wl_output_get_user_data(output); + + for (i = 0; i < window->num_outputs; i++) { + 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(SDL_DisplayData *) * ((window->num_outputs - i) - 1)); + } + window->num_outputs--; + i--; + } + } + + if (window->num_outputs == 0) { + SDL_free(window->outputs); + window->outputs = NULL; + } else if (send_move_event) { + Wayland_move_window(window->sdlwindow, + window->outputs[window->num_outputs - 1]); + } + + Wayland_MaybeUpdateScaleFactor(window); +} + static void handle_surface_enter(void *data, struct wl_surface *surface, struct wl_output *output) { @@ -1332,37 +1363,12 @@ 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_DisplayData *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)) { - window->outputs[i] = NULL; - send_move_event = 1; - } else { - SDL_memmove(&window->outputs[i], - &window->outputs[i + 1], - sizeof(SDL_DisplayData *) * ((window->num_outputs - i) - 1)); - } - window->num_outputs--; - i--; - } - } - - if (window->num_outputs == 0) { - SDL_free(window->outputs); - window->outputs = NULL; - } else if (send_move_event) { - Wayland_move_window(window->sdlwindow, - window->outputs[window->num_outputs - 1]); - } - - Wayland_MaybeUpdateScaleFactor(window); + Wayland_RemoveOutputFromWindow(window, output); } static void handle_preferred_buffer_scale(void *data, struct wl_surface *wl_surface, int32_t factor) diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index 1d045f3e9..cf8e7ea2a 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -206,4 +206,6 @@ extern int Wayland_SetWindowHitTest(SDL_Window *window, SDL_bool enabled); extern int Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation); extern int Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window); +extern void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, struct wl_output *output); + #endif /* SDL_waylandwindow_h_ */