From bd6aadf4d166c05f22f1ab6cdfe2e815e071f162 Mon Sep 17 00:00:00 2001 From: srdusr <99972264+srdusr@users.noreply.github.com> Date: Fri, 15 Aug 2025 23:53:00 +0200 Subject: Fix a dragged/resized window rendering wrong on a different-scale monitor mid-gesture Reported live: moving a window onto the other monitor "looks very messed up". This machine's two real monitors have genuinely different scales (eDP-1 at 1.0, HDMI-A-1 at ~0.843) - the exact condition needed to expose this. WindowManager::update_drag/update_resize only corrected w.monitor once, at end_drag (update_resize never corrected it at all, not even at the end) - but state/geometry.rs::sync_geometry reads that field on every motion tick to pick which monitor's scale converts the client's physical size into the logical points xdg_toplevel::configure sends it. Crossing onto a different-scale monitor mid-drag kept every configure computed against the origin monitor's stale scale for the gesture's whole remaining duration, only self-correcting once the button came up. Both functions now re-derive w.monitor from which monitor the window's live geometry actually overlaps, every motion tick - the same Rect::overlaps lookup end_drag already used once at the end, now run continuously instead. end_drag's own fixup stays as a final-word safety net for a drag that starts and ends between two motion ticks. Does not close the related, already-documented gap where a client that doesn't speak wp-fractional-scale-v1 still mismatches once settled on a sub-1.0-scaled monitor - this only fixes the stale-during-the-gesture half. Two new tests, full workspace suite and clippy clean. --- crates/core/src/manager/tests.rs | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'crates/core/src/manager/tests.rs') diff --git a/crates/core/src/manager/tests.rs b/crates/core/src/manager/tests.rs index 88699de..4739278 100644 --- a/crates/core/src/manager/tests.rs +++ b/crates/core/src/manager/tests.rs @@ -122,6 +122,33 @@ assert!(!wm.is_dragging()); } + #[test] + fn dragging_across_a_monitor_boundary_updates_monitor_live_not_just_at_end() { + // Reported live: a window dragged onto a second monitor with a + // different scale (confirmed live: 1.0 and ~0.84) "looks very + // messed up" - `state/geometry.rs::sync_geometry` reads `w. + // monitor` on every drag motion tick to pick which scale converts + // the client's physical size into the logical points `xdg_ + // toplevel::configure` sends it, and `w.monitor` used to only get + // corrected once, at `end_drag`, leaving every mid-drag configure + // computed against the wrong monitor's scale for the drag's whole + // remaining duration. + let mut wm = WindowManager::new(); + wm.set_monitors(two_monitors()); + wm.set_layout(wm.current_workspace(), "tiling"); + let a = wm.alloc_window_id(); + let mut w = Window::new(a, "a"); + w.geometry = Rect::new(1000, 100, 200, 150); // fully on monitor 0 + wm.add_window(w); + wm.window_mut(a).unwrap().monitor = 0; + wm.start_drag(a, 1010, 110); + assert_eq!(wm.window(a).unwrap().monitor, 0); + // Dragged fully onto monitor 1 - checked immediately, before + // `end_drag` runs at all. + wm.update_drag(1600, 110); + assert_eq!(wm.window(a).unwrap().monitor, 1, "monitor must update live during the drag, not only once it ends"); + } + #[test] fn drag_ending_near_edge_snaps_to_half_screen() { let mut wm = wm_with_monitor(); @@ -153,6 +180,32 @@ assert!(!wm.is_resizing()); } + #[test] + fn resizing_across_a_monitor_boundary_updates_monitor_live() { + // Same fix as `dragging_across_a_monitor_boundary_updates_monitor_ + // live_not_just_at_end`'s own doc comment - a resize can carry the + // edge being dragged onto a different monitor just as easily as a + // move can carry the whole window, and `update_resize` never + // corrected `w.monitor` at all before this fix, not even at the + // end. + let mut wm = WindowManager::new(); + wm.set_monitors(two_monitors()); + wm.set_layout(wm.current_workspace(), "tiling"); + let a = wm.alloc_window_id(); + let mut w = Window::new(a, "a"); + w.geometry = Rect::new(1000, 100, 500, 150); // fully on monitor 0, right edge at 1500 + wm.add_window(w); + wm.window_mut(a).unwrap().monitor = 0; + wm.start_resize(a, ResizeEdge::Left, 1050, 100); + // Drags the left edge from 1000 to 1290 (right edge anchored at + // 1500, final width 210 - comfortably above MIN_WINDOW_WIDTH), + // landing the whole window past the 1280 boundary on monitor 1. + wm.update_resize(1340, 100); + let g = wm.window(a).unwrap().geometry; + assert_eq!(g, Rect::new(1290, 100, 210, 150), "sanity: resize must actually have cleared the boundary"); + assert_eq!(wm.window(a).unwrap().monitor, 1, "monitor must update live during the resize"); + } + #[test] fn ending_a_resize_remembers_the_new_size_for_the_apps_next_window() { let mut wm = wm_with_monitor(); -- cgit v1.2.3