From aa7b7278b9e758985197170bcee4f0d2d4cb4590 Mon Sep 17 00:00:00 2001 From: srdusr <99972264+srdusr@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:34:00 +0200 Subject: Workspace capture writes a readable image, and left-edge resize holds its anchor Two things, and the first is smaller than I told anyone. WORKSPACE CAPTURE. I said off-screen workspace capture did not exist and would need building. It already did: udev/capture.rs renders a workspace that is not on screen, at the target monitor's native size, downscaled to a requested size, wallpaper included. Verified on the live DRM session rather than from the source - capturing the active workspace and a non-visible one gave 320x180 images with mean luminance 0.067 and 0.137, so the second is genuinely a different render and not a copy of what is presented. The only thing missing was the container. It wrote PPM, which the shells that want thumbnails cannot decode, so the file was written successfully, returned successfully, and silently not drawn - the same failure class as a capture pass that omits a tier. encode_capture now picks the format from the destination's extension: .ppm still writes PPM so existing callers keep working, .jpg/.jpeg write JPEG, anything else writes PNG. Four tests check the actual magic bytes rather than trusting the call, plus the unfamiliar extension fallback and a size-mismatch error. LEFT-EDGE RESIZE. Reported as content resizing "from the right side even when i resize from left". The window's origin moves the instant the pointer does, but the client only commits a matching buffer some frames later, so its still-old content was being placed at the new origin - which slides the whole window rather than growing it, and leaves the edge that should be nailed down drifting. sync_geometry now derives the origin from the size the client has actually committed when the drag is from a left or top edge, so the opposite edge stays exactly where the drag started and the dragged edge catches up as commits arrive. A right or bottom drag is untouched: its origin never moves. 533 tests pass, clippy clean. --- crates/wayland/src/state/geometry.rs | 45 +++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) (limited to 'crates/wayland/src/state/geometry.rs') diff --git a/crates/wayland/src/state/geometry.rs b/crates/wayland/src/state/geometry.rs index 1910334..b8f610e 100644 --- a/crates/wayland/src/state/geometry.rs +++ b/crates/wayland/src/state/geometry.rs @@ -241,6 +241,37 @@ impl CompState { /// reading `Window.geometry` unchanged - those are about this /// compositor's own bookkeeping staying self-consistent, not about /// matching a client's real pixels. + /// `geom`'s origin, corrected so an in-progress left/top resize keeps + /// its opposite edge fixed. Returns `geom`'s own origin unchanged when + /// this window is not being resized, or is being resized from an edge + /// whose origin does not move. + /// + /// `committed` is the client's own last-committed content size, in + /// logical points; `scale` converts it to the physical space every rect + /// here uses. + fn anchor_resizing_origin( + wm: &Rc>, + id: WindowId, + geom: srdwm_core::Rect, + committed: smithay::utils::Size, + scale: f64, + ) -> (i32, i32) { + let Some((resizing, edge, orig)) = wm.borrow().resize_anchor() else { return (geom.x, geom.y) }; + if resizing != id { + return (geom.x, geom.y); + } + let (mut x, mut y) = (geom.x, geom.y); + let physical_w = (committed.w as f64 * scale).round() as i32; + let physical_h = (committed.h as f64 * scale).round() as i32; + if physical_w > 0 && matches!(edge, srdwm_core::ResizeEdge::Left | srdwm_core::ResizeEdge::TopLeft | srdwm_core::ResizeEdge::BottomLeft) { + x = orig.right() - physical_w; + } + if physical_h > 0 && matches!(edge, srdwm_core::ResizeEdge::Top | srdwm_core::ResizeEdge::TopLeft | srdwm_core::ResizeEdge::TopRight) { + y = orig.bottom() - physical_h; + } + (x, y) + } + pub(crate) fn effective_frame(&self, id: WindowId, geom: srdwm_core::Rect) -> srdwm_core::Rect { Self::effective_frame_of(&self.wm, &self.id_to_window, &self.pending_size_configure, id, geom) } @@ -362,7 +393,19 @@ impl CompState { // calls already never did this (X11 windows have no equivalent // shadow-margin geometry), which in hindsight was the correct // pattern being followed there all along. - self.space.map_element(w.clone(), (geom.x, geom.y + band), false); + // While resizing from a left or top edge, hold the opposite + // edge still. `geom` moves the instant the pointer does, but + // the client only commits a matching buffer some frames later, + // so placing its still-old content at the new origin slides the + // whole window sideways rather than growing it - reported as + // content resizing "from the right side even when i resize from + // left". Deriving the origin from the size the client has + // actually committed keeps the anchored edge exactly where the + // drag started, and the dragged edge catches up as commits + // arrive. A right/bottom drag needs none of this: its origin + // does not move at all. + let placed = Self::anchor_resizing_origin(&self.wm, id, geom, w.geometry().size, scale); + self.space.map_element(w.clone(), (placed.0, placed.1 + band), false); moved = true; if let Some(top) = w.toplevel() { // xdg-shell position is a purely compositor-side concept -- -- cgit v1.2.3