From a9ce3a13cffcba29bb99afa6b3be820fed228759 Mon Sep 17 00:00:00 2001 From: funman300 Date: Sat, 18 Jul 2026 10:12:55 -0700 Subject: [PATCH] niri-scratchpad: refresh patch with multi-monitor tests Adds two synthetic multi-output tests (niri's layout harness supports several outputs) proving scratchpad_show targets the FOCUSED output, plus a control that it returns to the origin output when focus is unchanged. This is the substitute for manual multi-monitor verification on a single-display machine. 117 layout tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) --- niri-patches/0001-scratchpad.patch | 80 +++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/niri-patches/0001-scratchpad.patch b/niri-patches/0001-scratchpad.patch index d8a771d..9fc3ae3 100644 --- a/niri-patches/0001-scratchpad.patch +++ b/niri-patches/0001-scratchpad.patch @@ -302,7 +302,7 @@ index 5c4dd639..a90d9e6e 100644 let mut seen_workspace_name = Vec::::new(); diff --git a/src/layout/tests.rs b/src/layout/tests.rs -index 31265dd9..65e02f2b 100644 +index 31265dd9..9037ff08 100644 --- a/src/layout/tests.rs +++ b/src/layout/tests.rs @@ -753,6 +753,8 @@ enum Op { @@ -327,7 +327,7 @@ index 31265dd9..65e02f2b 100644 } } } -@@ -3929,3 +3937,93 @@ proptest! { +@@ -3929,3 +3937,169 @@ proptest! { check_ops_with_options(options, ops); } } @@ -421,3 +421,79 @@ index 31265dd9..65e02f2b 100644 + assert_eq!(layout.scratchpad.len(), 1, "closed window evicted from stash"); + assert_eq!(layout.scratchpad.front().unwrap().tile.window().id(), &1); +} ++ ++#[test] ++fn scratchpad_shows_on_the_focused_output() { ++ // First, confirm that while stashed the window lives on no monitor at all (checked against ++ // a standalone run so `check_ops`'s post-op invariant checks still see a fully-formed op ++ // sequence). ++ let stash_only_ops = [ ++ Op::AddOutput(1), ++ Op::AddOutput(2), ++ Op::FocusOutput(1), ++ Op::AddWindow { params: TestWindowParams::new(0) }, // lands on output 1 ++ Op::ScratchpadStash(0), ++ ]; ++ let stashed_layout = check_ops(stash_only_ops); ++ { ++ let MonitorSet::Normal { monitors, .. } = &stashed_layout.monitor_set else { ++ unreachable!() ++ }; ++ assert!( ++ monitors.iter().all(|m| !m.windows().any(|w| *w.id() == 0)), ++ "stashed window must not be present on any monitor" ++ ); ++ } ++ ++ // Stash on output 1, then switch focus to output 2 before showing. ++ let ops = [ ++ Op::AddOutput(1), ++ Op::AddOutput(2), ++ Op::FocusOutput(1), ++ Op::AddWindow { params: TestWindowParams::new(0) }, // lands on output 1 ++ Op::ScratchpadStash(0), ++ Op::FocusOutput(2), ++ Op::ScratchpadShow, ++ ]; ++ let layout = check_ops(ops); ++ ++ let MonitorSet::Normal { monitors, active_monitor_idx, .. } = &layout.monitor_set else { ++ unreachable!() ++ }; ++ ++ // The window must appear on whichever output is currently focused (output 2), not on the ++ // output it was originally stashed from (output 1). ++ for (idx, monitor) in monitors.iter().enumerate() { ++ let has_window = monitor.windows().any(|w| *w.id() == 0); ++ if idx == *active_monitor_idx { ++ assert!(has_window, "shown window should be on the focused output"); ++ } else { ++ assert!(!has_window, "shown window should not be on a non-focused output"); ++ } ++ } ++} ++ ++#[test] ++fn scratchpad_shows_on_origin_output_when_focus_unchanged() { ++ let ops = [ ++ Op::AddOutput(1), ++ Op::AddOutput(2), ++ Op::FocusOutput(1), ++ Op::AddWindow { params: TestWindowParams::new(0) }, // lands on output 1 ++ Op::ScratchpadStash(0), ++ // no focus change here ++ Op::ScratchpadShow, ++ ]; ++ let layout = check_ops(ops); ++ ++ let MonitorSet::Normal { monitors, active_monitor_idx, .. } = &layout.monitor_set else { ++ unreachable!() ++ }; ++ ++ // Focus never left output 1, so the window should reappear there. ++ assert_eq!(*active_monitor_idx, 0, "output 1 should still be active"); ++ assert!( ++ monitors[*active_monitor_idx].windows().any(|w| *w.id() == 0), ++ "shown window should be on the still-focused origin output" ++ ); ++}