From 32b54cc31f87e93782308d43e15b4020f2fed38e Mon Sep 17 00:00:00 2001 From: Skygrango Date: Wed, 6 May 2026 00:26:00 +0800 Subject: [PATCH] shell: implement surface_offset and surface_tree_offset --- src/shell/element/mod.rs | 8 +++++ src/shell/element/surface.rs | 67 +++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index 4696aa2e..d5b79703 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -254,6 +254,14 @@ impl CosmicMapped { .any(|(w, _)| w.has_surface(surface, surface_type)) } + pub fn surface_offset(&self, surface: &WlSurface) -> Option> { + self.windows().find_map(|(window, window_offset)| { + window + .surface_offset(surface) + .map(|offset| window_offset + offset) + }) + } + /// Give the pointer target under a relative offset into this element. /// /// Returns Target + Offset relative to the target diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 1a1e214b..769ffbd9 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -45,7 +45,10 @@ use smithay::{ IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, user_data::UserDataMap, }, wayland::{ - compositor::{SurfaceData, TraversalAction, with_states, with_surface_tree_downward}, + compositor::{ + SubsurfaceCachedState, SurfaceData, TraversalAction, with_states, + with_surface_tree_downward, + }, seat::WaylandFocus, shell::xdg::{ SurfaceCachedState, ToplevelCachedState, ToplevelSurface, XdgToplevelSurfaceData, @@ -652,6 +655,68 @@ impl CosmicSurface { } } + pub fn surface_offset(&self, surface: &WlSurface) -> Option> { + match self.0.underlying_surface() { + WindowSurface::Wayland(toplevel) => { + Self::surface_tree_offset(toplevel.wl_surface(), surface) + } + WindowSurface::X11(surface_x11) => { + if surface_x11.wl_surface().as_ref() == Some(surface) { + Some(Point::default()) + } else { + None + } + } + } + } + + pub fn surface_tree_offset( + root: &WlSurface, + surface: &WlSurface, + ) -> Option> { + if root == surface { + return Some(Point::default()); + } + + let found = AtomicBool::new(false); + let mut found_offset = Point::::default(); + + with_surface_tree_downward( + root, + Point::::default(), + |s, states, parent_offset| { + let mut offset = *parent_offset; + if s != root { + offset += states + .cached_state + .get::() + .current() + .location; + } + TraversalAction::DoChildren(offset) + }, + |s, _, offset| { + if s == surface { + found_offset = *offset; + found.store(true, Ordering::SeqCst); + } + }, + |_, _, _| !found.load(Ordering::SeqCst), + ); + + if found.load(Ordering::SeqCst) { + return Some(found_offset); + } + + for (popup, popup_offset) in PopupManager::popups_for_surface(root) { + if let Some(offset) = Self::surface_tree_offset(popup.wl_surface(), surface) { + return Some(popup_offset + offset); + } + } + + None + } + pub fn focus_under( &self, relative_pos: Point,