srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2026-07-15 16:37:00 +0200
committersrdusr <[email protected]>2026-07-15 16:37:00 +0200
commit0091bcd39406d88ceb075f03de4f3bfe12fed316 (patch)
tree5768ae67e2a69fe2bb4dfd50840ac4f75d4f0fda /crates
parentaa7b7278b9e758985197170bcee4f0d2d4cb4590 (diff)
downloadsrdwm-0091bcd39406d88ceb075f03de4f3bfe12fed316.tar.gz
srdwm-0091bcd39406d88ceb075f03de4f3bfe12fed316.zip
Draw a resizing window's decoration from the same rect as its content
Reported as the titlebar not resizing at the same time as the window, and as resizing feeling cheap. effective_frame_of returned the live drag target while a resize was active, so the titlebar and border tracked the pointer while the client's actual pixels were still whatever it last committed. The two disagreed for the whole drag, and the decoration leading its own content is what reads as broken. It now returns the committed size anchored to whichever edge the drag is holding still - exactly the rect the content occupies, since sync_geometry positions it the same way, so the two agree by construction rather than by timing. The consequence is that the frame sits one commit behind the pointer instead of ahead of its own content. That is the trade every other compositor makes, and it is the right way round: a frame glued to its content and slightly behind the cursor reads as solid. The committed-size correction was extracted into committed_frame so the resize path and the ordinary path share it rather than having two versions that can drift, and so the resize path is no longer short-circuited by the pending-configure branch, which fires constantly during a drag precisely because every tick sends a configure. 533 tests pass, clippy clean. The arithmetic is covered where it is testable; how it feels mid-drag is a judgement only real hardware can make, and a nested drag did not reproduce a clean enough scenario to claim it.
Diffstat (limited to 'crates')
-rw-r--r--crates/wayland/src/state/geometry.rs54
1 files changed, 48 insertions, 6 deletions
diff --git a/crates/wayland/src/state/geometry.rs b/crates/wayland/src/state/geometry.rs
index b8f610e..5cbece9 100644
--- a/crates/wayland/src/state/geometry.rs
+++ b/crates/wayland/src/state/geometry.rs
@@ -85,6 +85,28 @@ impl CompState {
// branch existing is what keeps that gap small in practice
// rather than a full commit-cycle wide.
if wm.borrow().resizing_window() == Some(id) {
+ // Reinstated once more, but no longer as the *live drag
+ // target*. Returning that made the decoration race ahead of
+ // the content it frames: the titlebar and border tracked the
+ // pointer while the client's actual pixels were still whatever
+ // it last committed, so the two visibly disagreed for the whole
+ // drag - reported as the titlebar not resizing at the same
+ // time as the window, and as the resize feeling cheap.
+ //
+ // Now it returns the committed size, anchored to whichever edge
+ // the drag is holding still, which is exactly the rect the
+ // content actually occupies (`sync_geometry` positions it the
+ // same way). Decoration and content therefore move as one, one
+ // commit behind the pointer rather than out of step with each
+ // other. Every other compositor makes this same trade: a frame
+ // glued to its content and slightly behind the cursor reads as
+ // solid, one that leads its own content reads as broken.
+ //
+ // Falling back to the live target when there is no usable
+ // committed size yet keeps the pre-first-commit case working.
+ if let Some(rect) = Self::committed_frame(wm, id_to_window, id, geom) {
+ return rect;
+ }
return geom;
}
// Same reasoning as the active-resize branch just above, for a gap
@@ -117,8 +139,24 @@ impl CompState {
}) {
return geom;
}
- let Some(w) = wm.borrow().window(id).cloned() else { return geom };
- let Some(dwindow) = id_to_window.get(&id) else { return geom };
+ match Self::committed_frame(wm, id_to_window, id, geom) {
+ Some(rect) => rect,
+ None => geom,
+ }
+ }
+
+ /// `geom` with its size replaced by what the client actually committed,
+ /// and - during a resize from a left or top edge - its origin moved so
+ /// the opposite edge stays put. `None` when there is nothing committed
+ /// to correct against.
+ fn committed_frame(
+ wm: &Rc<RefCell<WindowManager>>,
+ id_to_window: &HashMap<WindowId, DWindow>,
+ id: WindowId,
+ geom: srdwm_core::Rect,
+ ) -> Option<srdwm_core::Rect> {
+ let w = wm.borrow().window(id).cloned()?;
+ let dwindow = id_to_window.get(&id)?;
// `dwindow.geometry()` - `xdg_surface::set_window_geometry` - is,
// per smithay's own implementation, that cached hint *intersected*
// with `bbox()`, falling back to `bbox()` only if the client never
@@ -180,9 +218,9 @@ impl CompState {
if content.size.w <= 0 || content.size.h <= 0 {
// No real committed content yet - racing the first commit
// right after creation, most likely. Nothing to correct
- // against, so fall back to the requested rect rather than
- // collapsing every dimension down to (near) zero.
- return geom;
+ // against, so let the caller fall back to the requested rect
+ // rather than collapsing every dimension down to (near) zero.
+ return None;
}
// `content` (`bbox()`) is in the same *logical* points as
// `xdg_surface::set_window_geometry` would have been, same as
@@ -203,7 +241,11 @@ impl CompState {
let scale = wm.borrow().monitors().iter().find(|m| m.id == w.monitor).map(|m| m.scale).unwrap_or(1.0);
let content_physical = ((content.size.w as f64 * scale).round() as i32, (content.size.h as f64 * scale).round() as i32);
let band = if w.decorated { TITLEBAR_HEIGHT as i32 } else { 0 };
- srdwm_core::Rect { x: geom.x, y: geom.y, width: content_physical.0.max(0) as u32, height: (band + content_physical.1.max(0)) as u32 }
+ // Anchored so a left/top drag holds its opposite edge - the same
+ // correction `sync_geometry` applies when positioning the content
+ // itself, so the two agree by construction rather than by luck.
+ let (x, y) = Self::anchor_resizing_origin(wm, id, geom, content.size, scale);
+ Some(srdwm_core::Rect { x, y, width: content_physical.0.max(0) as u32, height: (band + content_physical.1.max(0)) as u32 })
}
/// The rect a window's border, shadow, occlusion test, and resize-