srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/wayland/src/state/geometry.rs19
-rw-r--r--crates/wayland/src/udev/capture.rs7
-rw-r--r--crates/wayland/src/udev/render.rs25
-rw-r--r--crates/wayland/src/winit/render.rs10
4 files changed, 57 insertions, 4 deletions
diff --git a/crates/wayland/src/state/geometry.rs b/crates/wayland/src/state/geometry.rs
index a0d10fa..21cac8d 100644
--- a/crates/wayland/src/state/geometry.rs
+++ b/crates/wayland/src/state/geometry.rs
@@ -55,7 +55,24 @@ impl CompState {
let size_changed = self.last_synced_size.insert(id, size) != Some(size);
let mut moved = false;
if let Some(w) = self.id_to_window.get(&id) {
- self.space.map_element(w.clone(), (geom.x, geom.y + band), false);
+ // `w.geometry().loc` is the client's own `xdg_surface::
+ // set_window_geometry` offset - a CSD client (GTK4/Firefox
+ // concretely) declares its real visible content as a sub-rect
+ // inset within a larger buffer that also reserves an invisible
+ // shadow margin, even once the tiled-state hint below has told
+ // it to skip drawing that shadow. `render_udev_frame`/
+ // `winit/render.rs` both subtract this same offset from where
+ // they draw the window's content, specifically so the client's
+ // visible content lands at `geom.x, geom.y` instead of a
+ // shadow-margin's width/height short of it - `space` has to
+ // agree with that adjustment, not just rendering, or every
+ // click computed via `win_relative = pos - space_loc` would
+ // land `content_offset` short of whatever the user actually
+ // clicked on: rendering moves the content, hit-testing keeps
+ // routing against where the client's raw, unshifted buffer
+ // origin used to be.
+ let content_offset = w.geometry().loc;
+ self.space.map_element(w.clone(), (geom.x - content_offset.x, geom.y + band - content_offset.y), false);
moved = true;
if let Some(top) = w.toplevel() {
// xdg-shell position is a purely compositor-side concept --
diff --git a/crates/wayland/src/udev/capture.rs b/crates/wayland/src/udev/capture.rs
index 0f87d27..bcf2aa4 100644
--- a/crates/wayland/src/udev/capture.rs
+++ b/crates/wayland/src/udev/capture.rs
@@ -62,7 +62,12 @@ impl CompState {
let Some(w) = self.id_to_window.get(&id) else { continue };
let Some(surface) = crate::input::dwindow_wl_surface(w) else { continue };
let Some(geom) = self.wm.borrow().window(id).map(|w| w.geometry) else { continue };
- let loc = (geom.x - origin.0, geom.y - origin.1);
+ // Same `set_window_geometry` offset every other render path
+ // subtracts (see `udev/render.rs`'s matching fix) - without
+ // it, a CSD window's invisible shadow margin would show up as
+ // a gap in the capture too.
+ let content_offset = w.geometry().loc;
+ let loc = (geom.x - origin.0 - content_offset.x, geom.y - origin.1 - content_offset.y);
elements.extend(render_elements_from_surface_tree(&mut udev.renderer, &surface, loc, 1.0, 1.0, Kind::Unspecified));
}
diff --git a/crates/wayland/src/udev/render.rs b/crates/wayland/src/udev/render.rs
index 9642603..6c9c7d2 100644
--- a/crates/wayland/src/udev/render.rs
+++ b/crates/wayland/src/udev/render.rs
@@ -329,7 +329,30 @@ impl CompState {
if let Some(dwindow) = self.id_to_window.get(&id) {
if let Some(surface) = crate::elements::window_wl_surface(dwindow) {
let band = if w.decorated { srdwm_core::TITLEBAR_HEIGHT as i32 } else { 0 };
- let pos = (geom.x - origin.x, geom.y + band - origin.y);
+ // `dwindow.geometry().loc` is the client's own
+ // `xdg_surface.set_window_geometry` offset --
+ // GTK4 CSD clients (Firefox concretely) declare
+ // their real visible content as a sub-rect
+ // inset within a larger buffer that also holds
+ // an invisible shadow-reservation margin, even
+ // once the tiled-state hint (`sync_geometry`,
+ // `xdg_toplevel::State::Tiled*`) has told them
+ // to skip drawing that shadow - the margin
+ // itself, not just its decoration, stays
+ // reserved in the buffer. Never subtracting
+ // this meant every such client's buffer origin
+ // (0,0) - the *outer* edge of that invisible
+ // margin - landed exactly at `geom.x,geom.y`,
+ // leaving the margin's width/height of genuine
+ // gap (wallpaper visible through it) between
+ // the border this compositor draws and where
+ // the client's actual visible content begins.
+ // Reported live as "an extra layer or border
+ // over each window" - confirmed by diffing a
+ // corner crop against the real wallpaper at
+ // that exact screen position, pixel for pixel.
+ let content_offset = dwindow.geometry().loc;
+ let pos = (geom.x - origin.x - content_offset.x, geom.y + band - origin.y - content_offset.y);
let mut rounded_elem = None;
if rounded_corners_enabled {
let epoch = self.content_epoch.get(&id).copied().unwrap_or(0);
diff --git a/crates/wayland/src/winit/render.rs b/crates/wayland/src/winit/render.rs
index 735ff34..ec4fd69 100644
--- a/crates/wayland/src/winit/render.rs
+++ b/crates/wayland/src/winit/render.rs
@@ -247,7 +247,15 @@ impl WaylandPlatform {
if let Some(dwindow) = self.state.id_to_window.get(&id) {
if let Some(surface) = crate::elements::window_wl_surface(dwindow) {
let band = if w.decorated { srdwm_core::TITLEBAR_HEIGHT as i32 } else { 0 };
- let pos = (geom.x, geom.y + band);
+ // See the matching fix in `udev/render.rs`'s render loop
+ // for the full explanation: a CSD client's own
+ // `set_window_geometry` offset (its declared visible
+ // content within a larger buffer that also reserves an
+ // invisible shadow margin) was never subtracted, so
+ // that margin's worth of gap showed through at this
+ // window's top-left corner.
+ let content_offset = dwindow.geometry().loc;
+ let pos = (geom.x - content_offset.x, geom.y + band - content_offset.y);
let rounded = rounded_corners_enabled.then_some(self.state.rounded_corners_program.as_ref()).flatten().and_then(|program| {
let corners = if w.decorated { crate::rounded_corners::RoundedCorners::BOTTOM_ONLY } else { crate::rounded_corners::RoundedCorners::ALL };
crate::rounded_corners::rounded_content_element(renderer, program, &surface, pos, w.opacity, w.corner_radius as f32, corners)