From a5f2a570cd6052d6a71a7d58a4e3c19bd6e4bbd3 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sun, 20 Apr 2025 10:26:34 +0900 Subject: [PATCH] wayland/fix: crash due consequent calls to set_cursor_grab Only mark that the grab was applied when it actually got applied. Previously there was an issue with grab being marked as applied without a pointer over the window, when in reality it wasn't. Fixes #4073. --- src/changelog/unreleased.md | 1 + .../linux/wayland/window/state.rs | 37 ++++++++++++++----- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 6cb7a0361d..2970b76808 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -249,3 +249,4 @@ changelog entry. - On macos, `WindowExtMacOS::set_simple_fullscreen` now honors `WindowExtMacOS::set_borderless_game` - On X11 and Wayland, fixed pump_events with `Some(Duration::Zero)` blocking with `Wait` polling mode - On macOS, fixed `run_app_on_demand` returning without closing open windows. +- On Wayland, fixed a crash when consequently calling `set_cursor_grab` without pointer focus. diff --git a/src/platform_impl/linux/wayland/window/state.rs b/src/platform_impl/linux/wayland/window/state.rs index 5e74f7c83a..3650a182c7 100644 --- a/src/platform_impl/linux/wayland/window/state.rs +++ b/src/platform_impl/linux/wayland/window/state.rs @@ -222,9 +222,9 @@ impl WindowState { } /// Apply closure on the given pointer. - fn apply_on_pointer, &WinitPointerData)>( + fn apply_on_pointer, &WinitPointerData)>( &self, - callback: F, + mut callback: F, ) { self.pointers.iter().filter_map(Weak::upgrade).for_each(|pointer| { let data = pointer.pointer().winit_data(); @@ -830,34 +830,51 @@ impl WindowState { }, }; - // Replace the current mode. - let old_mode = std::mem::replace(&mut self.cursor_grab_mode.current_grab_mode, mode); - - match old_mode { - CursorGrabMode::None => (), + let mut unset_old = false; + match self.cursor_grab_mode.current_grab_mode { + CursorGrabMode::None => unset_old = true, CursorGrabMode::Confined => self.apply_on_pointer(|_, data| { data.unconfine_pointer(); + unset_old = true; }), CursorGrabMode::Locked => { - self.apply_on_pointer(|_, data| data.unlock_pointer()); + self.apply_on_pointer(|_, data| { + data.unlock_pointer(); + unset_old = true; + }); }, } + // In case we haven't unset the old mode, it means that we don't have a cursor above + // the window, thus just wait for it to re-appear. + if !unset_old { + return Ok(()); + } + + let mut set_mode = false; let surface = self.window.wl_surface(); match mode { CursorGrabMode::Locked => self.apply_on_pointer(|pointer, data| { let pointer = pointer.pointer(); - data.lock_pointer(pointer_constraints, surface, pointer, &self.queue_handle) + data.lock_pointer(pointer_constraints, surface, pointer, &self.queue_handle); + set_mode = true; }), CursorGrabMode::Confined => self.apply_on_pointer(|pointer, data| { let pointer = pointer.pointer(); - data.confine_pointer(pointer_constraints, surface, pointer, &self.queue_handle) + data.confine_pointer(pointer_constraints, surface, pointer, &self.queue_handle); + set_mode = true; }), CursorGrabMode::None => { // Current lock/confine was already removed. + set_mode = true; }, } + // Replace the current grab mode after we've ensure that it got updated. + if set_mode { + self.cursor_grab_mode.current_grab_mode = mode; + } + Ok(()) }