From b28f92a6e1fa89a9090d5588fec733206c8817e7 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 15 Aug 2025 13:14:13 +0200 Subject: [PATCH] focus: Don't consider XWaylandGrab exclusive --- src/debug.rs | 1 - src/input/mod.rs | 17 ++++++----- src/shell/focus/mod.rs | 30 +++++-------------- src/shell/focus/target.rs | 5 ---- src/shell/mod.rs | 25 ---------------- .../handlers/xwayland_keyboard_grab.rs | 19 +++++++++++- 6 files changed, 36 insertions(+), 61 deletions(-) diff --git a/src/debug.rs b/src/debug.rs index f6b8e6ce..f55b8df4 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -344,7 +344,6 @@ fn format_keyboard_focus(focus: Option) -> String { Some(Popup(x)) => format!("Popup {}", x.wl_surface().id().protocol_id()), Some(Group(_)) => format!("Window Group"), Some(LockSurface(x)) => format!("LockSurface {}", x.wl_surface().id().protocol_id()), - Some(XWaylandGrab(x)) => format!("XWayland Grab {}", x.id().protocol_id()), None => format!("None"), } } diff --git a/src/input/mod.rs b/src/input/mod.rs index f79c573b..caafb9c3 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -26,7 +26,7 @@ use crate::{ }, utils::{float::NextDown, prelude::*, quirks::workspace_overview_is_open}, wayland::{ - handlers::screencopy::SessionHolder, + handlers::{screencopy::SessionHolder, xwayland_keyboard_grab::XWaylandGrabSeat}, protocols::screencopy::{BufferConstraints, CursorSessionRef}, }, }; @@ -241,12 +241,13 @@ impl State { let current_focus = seat.get_keyboard().unwrap().current_focus(); let shortcuts_inhibited = current_focus.as_ref().is_some_and(|f| { f.wl_surface() - .and_then(|surface| { + .map(|surface| { seat.keyboard_shortcuts_inhibitor_for_surface(&surface) .map(|inhibitor| inhibitor.is_active()) + .unwrap_or(false) + || seat.has_active_xwayland_grab(&surface) }) .unwrap_or(false) - || matches!(f, KeyboardFocusTarget::XWaylandGrab(_)) }); let sym = handle.modified_sym(); @@ -686,12 +687,13 @@ impl State { let current_focus = seat.get_keyboard().unwrap().current_focus(); let shortcuts_inhibited = current_focus.as_ref().is_some_and(|f| { f.wl_surface() - .and_then(|surface| { + .map(|surface| { seat.keyboard_shortcuts_inhibitor_for_surface(&surface) .map(|inhibitor| inhibitor.is_active()) + .unwrap_or(false) + || seat.has_active_xwayland_grab(&surface) }) .unwrap_or(false) - || matches!(f, KeyboardFocusTarget::XWaylandGrab(_)) }); let serial = SERIAL_COUNTER.next_serial(); @@ -1566,12 +1568,13 @@ impl State { let shortcuts_inhibited = current_focus.as_ref().is_some_and(|f| { f.wl_surface() - .and_then(|surface| { + .map(|surface| { seat.keyboard_shortcuts_inhibitor_for_surface(&surface) .map(|inhibitor| inhibitor.is_active()) + .unwrap_or(false) + || seat.has_active_xwayland_grab(&surface) }) .unwrap_or(false) - || matches!(f, KeyboardFocusTarget::XWaylandGrab(_)) }); self.common.atspi_ei.input( diff --git a/src/shell/focus/mod.rs b/src/shell/focus/mod.rs index 73469044..28719a60 100644 --- a/src/shell/focus/mod.rs +++ b/src/shell/focus/mod.rs @@ -435,6 +435,14 @@ impl Common { .cloned() .collect::>(); for seat in &seats { + let mut xwayland_grab = seat + .user_data() + .get_or_insert(XWaylandGrabSeatData::default) + .grab + .lock() + .unwrap(); + xwayland_grab.take_if(|(surface, g)| !g.grab().is_alive() || !surface.alive()); + { let shell = state.common.shell.read(); let focused_output = seat.focused_output(); @@ -559,18 +567,6 @@ fn focus_target_is_valid( return matches!(target, KeyboardFocusTarget::LockSurface(_)); } - let xwayland_grab = seat - .user_data() - .get_or_insert(XWaylandGrabSeatData::default) - .grab - .lock() - .unwrap(); - if let Some((surface, grab)) = &*xwayland_grab { - if grab.grab().is_alive() { - return target == KeyboardFocusTarget::XWaylandGrab(surface.clone()); - } - } - // If an exclusive layer shell surface exists (on any output), only exclusive // shell surfaces can have focus, on the highest layer with exclusive surfaces. if let Some(layer) = exclusive_layer_surface_layer(shell) { @@ -632,7 +628,6 @@ fn focus_target_is_valid( } KeyboardFocusTarget::Popup(_) => true, KeyboardFocusTarget::LockSurface(_) => false, - KeyboardFocusTarget::XWaylandGrab(_) => false, } } @@ -641,21 +636,12 @@ fn update_focus_target( seat: &Seat, output: &Output, ) -> Option { - let mut xwayland_grab = seat - .user_data() - .get_or_insert(XWaylandGrabSeatData::default) - .grab - .lock() - .unwrap(); - xwayland_grab.take_if(|(_, g)| !g.grab().is_alive()); if let Some(session_lock) = &shell.session_lock { session_lock .surfaces .get(output) .cloned() .map(KeyboardFocusTarget::from) - } else if let Some((surface, _)) = &*xwayland_grab { - Some(KeyboardFocusTarget::XWaylandGrab(surface.clone())) } else if let Some(layer) = exclusive_layer_surface_layer(shell) { layer_map_for_output(output) .layers() diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs index ed01e9ff..2e30d133 100644 --- a/src/shell/focus/target.rs +++ b/src/shell/focus/target.rs @@ -74,7 +74,6 @@ pub enum KeyboardFocusTarget { LayerSurface(LayerSurface), Popup(PopupKind), LockSurface(LockSurface), - XWaylandGrab(WlSurface), } // TODO: This should be TryFrom, but PopupGrab needs to be able to convert. Fix this in smithay @@ -199,7 +198,6 @@ impl KeyboardFocusTarget { KeyboardFocusTarget::LayerSurface(l) => Some(l.wl_surface()), KeyboardFocusTarget::Popup(p) => Some(p.wl_surface()), KeyboardFocusTarget::LockSurface(l) => Some(l.wl_surface()), - KeyboardFocusTarget::XWaylandGrab(g) => Some(g), } } @@ -276,7 +274,6 @@ impl IsAlive for KeyboardFocusTarget { KeyboardFocusTarget::LayerSurface(l) => l.alive(), KeyboardFocusTarget::Popup(p) => p.alive(), KeyboardFocusTarget::LockSurface(l) => l.alive(), - KeyboardFocusTarget::XWaylandGrab(g) => g.alive(), } } } @@ -526,7 +523,6 @@ impl WaylandFocus for KeyboardFocusTarget { KeyboardFocusTarget::LayerSurface(l) => Some(Cow::Borrowed(l.wl_surface())), KeyboardFocusTarget::Popup(p) => Some(Cow::Borrowed(p.wl_surface())), KeyboardFocusTarget::LockSurface(l) => Some(Cow::Borrowed(l.wl_surface())), - KeyboardFocusTarget::XWaylandGrab(g) => Some(Cow::Borrowed(g)), } } fn same_client_as(&self, object_id: &ObjectId) -> bool { @@ -537,7 +533,6 @@ impl WaylandFocus for KeyboardFocusTarget { KeyboardFocusTarget::LayerSurface(l) => l.wl_surface().id().same_client_as(object_id), KeyboardFocusTarget::Popup(p) => p.wl_surface().id().same_client_as(object_id), KeyboardFocusTarget::LockSurface(l) => l.wl_surface().id().same_client_as(object_id), - KeyboardFocusTarget::XWaylandGrab(g) => g.id().same_client_as(object_id), } } } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index bc36e178..95101dec 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1665,30 +1665,6 @@ impl Shell { }?; focus_target = KeyboardFocusTarget::Element(new_target); - } else if let KeyboardFocusTarget::XWaylandGrab(surface) = &focus_target { - if let Some(new_target) = self.element_for_surface(surface) { - focus_target = KeyboardFocusTarget::Element(new_target.clone()); - } else if let Some(new_target) = self - .workspaces - .spaces() - .find_map(|w| w.get_fullscreen().filter(|s| *s == surface)) - { - focus_target = KeyboardFocusTarget::Fullscreen(new_target.clone()); - } else if let Some(or) = self - .override_redirect_windows - .iter() - .find(|w| w.wl_surface().as_ref() == Some(surface)) - { - // Find output the override redirect window overlaps the most with - let or_geo = or.geometry().as_global(); - let (output, _) = self - .outputs() - .filter_map(|o| Some((o, o.geometry().intersection(or_geo)?))) - .max_by_key(|(_, intersection)| intersection.size.w * intersection.size.h)?; - return Some(output.clone()); - } else { - return None; - } } match focus_target { @@ -1754,7 +1730,6 @@ impl Shell { .iter() .find_map(|(output, s)| (s == &surface).then_some(output)) .cloned(), - KeyboardFocusTarget::XWaylandGrab(_) => unreachable!(), KeyboardFocusTarget::Popup(_) => unreachable!(), } } diff --git a/src/wayland/handlers/xwayland_keyboard_grab.rs b/src/wayland/handlers/xwayland_keyboard_grab.rs index 23b23af1..bdb9c06d 100644 --- a/src/wayland/handlers/xwayland_keyboard_grab.rs +++ b/src/wayland/handlers/xwayland_keyboard_grab.rs @@ -4,7 +4,7 @@ use crate::{shell::focus::target::KeyboardFocusTarget, state::State}; use smithay::{ delegate_xwayland_keyboard_grab, input::Seat, - reexports::wayland_server::protocol::wl_surface::WlSurface, + reexports::wayland_server::{protocol::wl_surface::WlSurface, Resource}, wayland::xwayland_keyboard_grab::{XWaylandKeyboardGrab, XWaylandKeyboardGrabHandler}, }; use std::sync::Mutex; @@ -33,4 +33,21 @@ impl XWaylandKeyboardGrabHandler for State { Some(KeyboardFocusTarget::Element(element)) } } + +pub trait XWaylandGrabSeat { + fn has_active_xwayland_grab(&self, surface: &WlSurface) -> bool; +} + +impl XWaylandGrabSeat for Seat { + fn has_active_xwayland_grab(&self, surface: &WlSurface) -> bool { + self.user_data() + .get_or_insert(XWaylandGrabSeatData::default) + .grab + .lock() + .unwrap() + .as_ref() + .is_some_and(|(s, g)| g.grab().is_alive() && s == surface) + } +} + delegate_xwayland_keyboard_grab!(State);