diff options
| -rw-r--r-- | niri-config/src/lib.rs | 5 | ||||
| -rw-r--r-- | src/niri.rs | 25 | ||||
| -rw-r--r-- | src/window/mapped.rs | 17 | ||||
| -rw-r--r-- | src/window/mod.rs | 13 | ||||
| -rw-r--r-- | wiki/Configuration:-Window-Rules.md | 37 |
5 files changed, 94 insertions, 3 deletions
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index e20b0f49..701f5c5a 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -1210,6 +1210,8 @@ pub struct Match { #[knuffel(property)] pub is_floating: Option<bool>, #[knuffel(property)] + pub is_window_cast_target: Option<bool>, + #[knuffel(property)] pub at_startup: Option<bool>, } @@ -3948,6 +3950,7 @@ mod tests { is_focused: None, is_active_in_column: None, is_floating: None, + is_window_cast_target: None, at_startup: None, }], excludes: vec![ @@ -3958,6 +3961,7 @@ mod tests { is_focused: None, is_active_in_column: None, is_floating: None, + is_window_cast_target: None, at_startup: None, }, Match { @@ -3967,6 +3971,7 @@ mod tests { is_focused: Some(false), is_active_in_column: None, is_floating: None, + is_window_cast_target: None, at_startup: None, }, ], diff --git a/src/niri.rs b/src/niri.rs index 4dfaf525..b1b9a90c 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -642,13 +642,18 @@ impl State { self.niri.refresh_idle_inhibit(); self.refresh_pointer_contents(); foreign_toplevel::refresh(self); + + #[cfg(feature = "xdp-gnome-screencast")] + self.niri.refresh_mapped_cast_outputs(); + // Should happen before refresh_window_rules(), but after anything that can start or stop + // screencasts. + #[cfg(feature = "xdp-gnome-screencast")] + self.niri.refresh_mapped_cast_window_rules(); + self.niri.refresh_window_rules(); self.refresh_ipc_outputs(); self.ipc_refresh_layout(); self.ipc_refresh_keyboard_layout_index(); - - #[cfg(feature = "xdp-gnome-screencast")] - self.niri.refresh_mapped_cast_outputs(); } fn notify_blocker_cleared(&mut self) { @@ -3257,6 +3262,20 @@ impl Niri { } #[cfg(feature = "xdp-gnome-screencast")] + pub fn refresh_mapped_cast_window_rules(&mut self) { + // O(N^2) but should be fine since there aren't many casts usually. + self.layout.with_windows_mut(|mapped, _| { + let id = mapped.id().get(); + // Find regardless of cast.is_active. + let value = self + .casts + .iter() + .any(|cast| cast.target == (CastTarget::Window { id })); + mapped.set_is_window_cast_target(value); + }); + } + + #[cfg(feature = "xdp-gnome-screencast")] pub fn refresh_mapped_cast_outputs(&mut self) { use std::collections::hash_map::Entry; diff --git a/src/window/mapped.rs b/src/window/mapped.rs index b5bf8afb..055f2cc5 100644 --- a/src/window/mapped.rs +++ b/src/window/mapped.rs @@ -80,6 +80,9 @@ pub struct Mapped { /// Whether this window is floating. is_floating: bool, + /// Whether this window is a target of a window cast. + is_window_cast_target: bool, + /// Whether this window should ignore opacity set through window rules. ignore_opacity_window_rule: bool, @@ -188,6 +191,7 @@ impl Mapped { is_focused: false, is_active_in_column: true, is_floating: false, + is_window_cast_target: false, ignore_opacity_window_rule: false, block_out_buffer: RefCell::new(SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.])), animate_next_configure: false, @@ -260,6 +264,10 @@ impl Mapped { self.is_floating } + pub fn is_window_cast_target(&self) -> bool { + self.is_window_cast_target + } + pub fn toggle_ignore_opacity_window_rule(&mut self) { self.ignore_opacity_window_rule = !self.ignore_opacity_window_rule; } @@ -273,6 +281,15 @@ impl Mapped { self.need_to_recompute_rules = true; } + pub fn set_is_window_cast_target(&mut self, value: bool) { + if self.is_window_cast_target == value { + return; + } + + self.is_window_cast_target = value; + self.need_to_recompute_rules = true; + } + fn render_snapshot(&self, renderer: &mut GlesRenderer) -> LayoutElementRenderSnapshot { let _span = tracy_client::span!("Mapped::render_snapshot"); diff --git a/src/window/mod.rs b/src/window/mod.rs index 10aecbfe..41f2182b 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -146,6 +146,13 @@ impl<'a> WindowRef<'a> { WindowRef::Mapped(mapped) => mapped.is_floating(), } } + + pub fn is_window_cast_target(self) -> bool { + match self { + WindowRef::Unmapped(_) => false, + WindowRef::Mapped(mapped) => mapped.is_window_cast_target(), + } + } } impl ResolvedWindowRules { @@ -446,5 +453,11 @@ fn window_matches(window: WindowRef, role: &XdgToplevelSurfaceRoleAttributes, m: } } + if let Some(is_window_cast_target) = m.is_window_cast_target { + if window.is_window_cast_target() != is_window_cast_target { + return false; + } + } + true } diff --git a/wiki/Configuration:-Window-Rules.md b/wiki/Configuration:-Window-Rules.md index fe4858cf..b294a442 100644 --- a/wiki/Configuration:-Window-Rules.md +++ b/wiki/Configuration:-Window-Rules.md @@ -34,6 +34,7 @@ window-rule { match is-focused=false match is-active-in-column=true match is-floating=true + match is-window-cast-target=true match at-startup=true // Properties that apply once upon window opening. @@ -239,6 +240,42 @@ window-rule { } ``` +#### `is-window-cast-target` + +<sup>Since: next release</sup> + +Can be `true` or `false`. +Matches `true` for windows that are target of an ongoing window screencast. + +```kdl +// Indicate screencasted windows with red colors. +window-rule { + match is-window-cast-target=true + + focus-ring { + active-color "#f38ba8" + inactive-color "#7d0d2d" + } + + border { + inactive-color "#7d0d2d" + } + + shadow { + color "#7d0d2d70" + } + + tab-indicator { + active-color "#f38ba8" + inactive-color "#7d0d2d" + } +} +``` + +Example: + + + #### `at-startup` <sup>Since: 0.1.6</sup> |
