From 1d72227a30144c96cd2cd1583fd726b9c9dd717d Mon Sep 17 00:00:00 2001 From: littlezabi Date: Thu, 16 Jul 2026 23:36:13 +0500 Subject: [PATCH] fix: release active popup grabs on toplevel destruction If a window with an active popup grab is closed or crashes unexpectedly, the input grab state can get stuck, freezing keyboard and mouse clicks. This patch iterates over all seats and releases the active seat grab if it is owned by the destroyed window or one of its child popups. Signed-off-by: littlezabi --- src/wayland/handlers/xdg_shell/mod.rs | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/wayland/handlers/xdg_shell/mod.rs b/src/wayland/handlers/xdg_shell/mod.rs index 98b8acf6..b8288019 100644 --- a/src/wayland/handlers/xdg_shell/mod.rs +++ b/src/wayland/handlers/xdg_shell/mod.rs @@ -317,8 +317,45 @@ impl XdgShellHandler for State { } fn toplevel_destroyed(&mut self, surface: ToplevelSurface) { + for (popup, _) in smithay::desktop::PopupManager::popups_for_surface(surface.wl_surface()) { + if let smithay::desktop::PopupKind::Xdg(ref xdg_popup) = popup { + xdg_popup.send_popup_done(); + } + } + let (output, clients) = { let mut shell = self.common.shell.write(); + + for seat in shell.seats.iter() { + if let Some(data) = seat.user_data().get::() { + let mut should_ungrab = false; + let grab = data.take(); + if let Some(ref grab) = grab { + if grab.has_ended() { + should_ungrab = true; + } else if let Some(target) = grab.current_grab() { + if let Some(wl_surface) = target.wl_surface() { + if wl_surface.as_ref() == surface.wl_surface() + || smithay::desktop::PopupManager::popups_for_surface( + surface.wl_surface(), + ) + .any(|(p, _)| p.wl_surface() == wl_surface.as_ref()) + { + should_ungrab = true; + } + } + } + } + if should_ungrab { + if let Some(mut grab) = grab { + grab.ungrab(PopupUngrabStrategy::All); + } + } else { + data.set(grab); + } + } + } + let seat = shell.seats.last_active().clone(); // Clean up pending_windows for surfaces that were never mapped.