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.
This commit is contained in:
Frank Praznik 2024-02-27 11:16:19 -05:00
parent 9a65d123a7
commit 9eaf7d8cc8
3 changed files with 45 additions and 29 deletions

View File

@ -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);

View File

@ -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

View File

@ -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);