niri-scratchpad: stop screencasts on stash; harden acceptance spawn wait
Stashing a window now stops an active screencast of it, rather than leaving the consumer on a frozen last frame (a stashed window is never rendered). Covers both stash paths - Mod+M and Mod+Shift+M's hide branch - by diffing which windows left the layout across the action, so scratchpad_show's branch logic isn't duplicated in the caller. Dynamic-target casts are retargeted to Nothing rather than ended; that carve-out is documented inline. Also widen the acceptance harness spawn wait to 30s: each test launches its own nested niri, and a cold alacritty start occasionally exceeded the old 10s. Deliberately not a retry, which could double-spawn and corrupt window counts. 117 unit tests and 8/8 live acceptance pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -168,29 +168,81 @@ index 38440c90..fe16901f 100644
|
||||
// involving laptop going to sleep and resuming.
|
||||
error!("toplevel missing from both unmapped_windows and layout");
|
||||
diff --git a/src/input/mod.rs b/src/input/mod.rs
|
||||
index f1ad4932..91bc8942 100644
|
||||
index f1ad4932..87b5ea7a 100644
|
||||
--- a/src/input/mod.rs
|
||||
+++ b/src/input/mod.rs
|
||||
@@ -1376,6 +1376,13 @@ impl State {
|
||||
@@ -54,6 +54,7 @@ use crate::ui::mru::{WindowMru, WindowMruUi};
|
||||
use crate::ui::screenshot_ui::ScreenshotUi;
|
||||
use crate::utils::spawning::{spawn, spawn_sh};
|
||||
use crate::utils::{center, get_monotonic_time, CastSessionId, ResizeEdge};
|
||||
+use crate::window::mapped::MappedId;
|
||||
|
||||
pub mod backend_ext;
|
||||
pub mod move_grab;
|
||||
@@ -647,6 +648,38 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
+ /// Ids of all windows currently in the layout. Excludes the scratchpad stash,
|
||||
+ /// which is not part of any workspace.
|
||||
+ fn layout_window_ids(&self) -> HashSet<u64> {
|
||||
+ self.niri
|
||||
+ .layout
|
||||
+ .windows()
|
||||
+ .map(|(_, mapped)| {
|
||||
+ // Annotated to pin the inherent Mapped::id() -> MappedId; the
|
||||
+ // LayoutElement trait also has an id(), returning &Window.
|
||||
+ let id: MappedId = mapped.id();
|
||||
+ id.get()
|
||||
+ })
|
||||
+ .collect()
|
||||
+ }
|
||||
+
|
||||
+ /// Stop screencasts of windows that were in the layout before an action and are
|
||||
+ /// gone after it. A stashed window is never rendered, so its cast would otherwise
|
||||
+ /// sit frozen on its last frame indefinitely; stopping is the chosen policy over
|
||||
+ /// rendering stashed tiles.
|
||||
+ ///
|
||||
+ /// Note the carve-out inherited from `stop_casts_for_target`: a cast with a
|
||||
+ /// *dynamic* target is not ended, it is retargeted to `CastTarget::Nothing`. So a
|
||||
+ /// follow-focus cast survives a stash and goes blank rather than stopping. That is
|
||||
+ /// shared behaviour with every other stop site, not specific to the scratchpad.
|
||||
+ fn stop_casts_for_windows_gone(&mut self, before: HashSet<u64>) {
|
||||
+ let after = self.layout_window_ids();
|
||||
+ for id in before.difference(&after) {
|
||||
+ self.niri
|
||||
+ .stop_casts_for_target(CastTarget::Window { id: *id });
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
pub fn do_action(&mut self, action: Action, allow_when_locked: bool) {
|
||||
if self.niri.is_locked() && !(allow_when_locked || allowed_when_locked(&action)) {
|
||||
return;
|
||||
@@ -1376,6 +1409,15 @@ impl State {
|
||||
}
|
||||
}
|
||||
}
|
||||
+ Action::MoveWindowToScratchpad => {
|
||||
+ let focus = self.niri.layout.focus().map(|m| m.window.clone());
|
||||
+ if let Some(window) = focus {
|
||||
+ let before = self.layout_window_ids();
|
||||
+ self.niri.layout.move_to_scratchpad(&window);
|
||||
+ self.stop_casts_for_windows_gone(before);
|
||||
+ self.niri.queue_redraw_all();
|
||||
+ }
|
||||
+ }
|
||||
Action::MoveColumnToWorkspaceDown(focus) => {
|
||||
self.niri.layout.move_column_to_workspace_down(focus);
|
||||
self.maybe_warp_cursor_to_focus();
|
||||
@@ -2245,6 +2252,10 @@ impl State {
|
||||
@@ -2245,6 +2287,13 @@ impl State {
|
||||
Action::StopCast(session_id) => {
|
||||
self.niri.stop_cast(CastSessionId::from(session_id));
|
||||
}
|
||||
+ Action::ScratchpadShow => {
|
||||
+ // The hide branch of scratchpad_show also stashes a window.
|
||||
+ let before = self.layout_window_ids();
|
||||
+ self.niri.layout.scratchpad_show();
|
||||
+ self.stop_casts_for_windows_gone(before);
|
||||
+ self.niri.queue_redraw_all();
|
||||
+ }
|
||||
Action::ToggleOverview => {
|
||||
|
||||
Reference in New Issue
Block a user