From cbd12e730af55d16dfcf3c9aa8ff5f3e6bc8d0ec Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 4 Jan 2023 15:57:38 +1100 Subject: [PATCH] Fix T103586: Crash removing monitor under Wayland & WLROOTS compositors WLROOTS compositors don't run surface leave callbacks, while this may be considered a bug in WLROOTS, neither GTK/SDL crash so workaround the crash too. This also fixes a minor glitch where the cursor scale wasn't updated when changing monitor scale at run-time. --- intern/ghost/intern/GHOST_SystemWayland.cpp | 70 ++++++++++++++++----- intern/ghost/intern/GHOST_SystemWayland.h | 2 + 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 8cd7cde79b9..42e56aa74ab 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -4535,18 +4535,7 @@ static void output_handle_scale(void *data, struct wl_output * /*wl_output*/, co CLOG_INFO(LOG, 2, "scale"); GWL_Output *output = static_cast(data); output->scale = factor; - - GHOST_WindowManager *window_manager = output->system->getWindowManager(); - if (window_manager) { - for (GHOST_IWindow *iwin : window_manager->getWindows()) { - GHOST_WindowWayland *win = static_cast(iwin); - const std::vector &outputs = win->outputs(); - if (std::find(outputs.begin(), outputs.end(), output) == outputs.cend()) { - continue; - } - win->outputs_changed_update_scale(); - } - } + output->system->output_scale_update_maybe_leave(output, false); } static const struct wl_output_listener output_listener = { @@ -4736,11 +4725,21 @@ static void gwl_registry_wl_output_update(GWL_Display *display, } static void gwl_registry_wl_output_remove(GWL_Display *display, void *user_data, - const bool /*on_exit*/) + const bool on_exit) { /* While windows & cursors hold references to outputs, there is no need to manually remove - * these references as the compositor will remove references via #wl_surface_listener.leave. */ + * these references as the compositor will remove references via #wl_surface_listener.leave. + * + * WARNING: this is not the case for WLROOTS based compositors which have a (bug?) + * where surface leave events don't run. So `system->output_leave(..)` is needed + * until the issue is resolved in WLROOTS. */ GWL_Output *output = static_cast(user_data); + + if (!on_exit) { + /* Needed for WLROOTS, does nothing if surface leave callbacks have already run. */ + output->system->output_scale_update_maybe_leave(output, true); + } + if (output->xdg_output) { zxdg_output_v1_destroy(output->xdg_output); } @@ -6765,6 +6764,49 @@ void GHOST_SystemWayland::window_surface_unref(const wl_surface *wl_surface) #undef SURFACE_CLEAR_PTR } +void GHOST_SystemWayland::output_scale_update_maybe_leave(GWL_Output *output, bool leave) +{ + /* Update scale, optionally leaving the outputs beforehand. */ + GHOST_WindowManager *window_manager = output->system->getWindowManager(); + if (window_manager) { + for (GHOST_IWindow *iwin : window_manager->getWindows()) { + GHOST_WindowWayland *win = static_cast(iwin); + const std::vector &outputs = win->outputs(); + bool found = leave ? win->outputs_leave(output) : + !(std::find(outputs.begin(), outputs.end(), output) == outputs.cend()); + if (found) { + win->outputs_changed_update_scale(); + } + } + } + + for (GWL_Seat *seat : display_->seats) { + bool found; + + found = leave ? seat->pointer.outputs.erase(output) : seat->pointer.outputs.count(output); + if (found) { + if (seat->cursor.wl_surface_cursor != nullptr) { + update_cursor_scale( + seat->cursor, seat->system->wl_shm(), &seat->pointer, seat->cursor.wl_surface_cursor); + } + } + + found = leave ? seat->tablet.outputs.erase(output) : seat->tablet.outputs.count(output); + if (found) { + for (struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2 : seat->tablet_tools) { + GWL_TabletTool *tablet_tool = static_cast( + zwp_tablet_tool_v2_get_user_data(zwp_tablet_tool_v2)); + if (tablet_tool->wl_surface_cursor != nullptr) { + update_cursor_scale(seat->cursor, + seat->system->wl_shm(), + &seat->pointer, + tablet_tool->wl_surface_cursor); + } + } + } + } +} + bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mode, const GHOST_TGrabCursorMode mode_current, int32_t init_grab_xy[2], diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h index c762b4817c6..c102a3d7a12 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.h +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -194,6 +194,8 @@ class GHOST_SystemWayland : public GHOST_System { /** Set this seat to be active. */ void seat_active_set(const struct GWL_Seat *seat); + void output_scale_update_maybe_leave(GWL_Output *output, bool leave); + /** Clear all references to this surface to prevent accessing NULL pointers. */ void window_surface_unref(const wl_surface *wl_surface);