From 7dc68c73a65ae630315fad2955bdd6ca65b787ae Mon Sep 17 00:00:00 2001 From: The Zilch Date: Tue, 4 Nov 2025 14:54:39 +0000 Subject: [PATCH] feat: add new input-passthrough window rule Adds a new window rule that makes windows transparent to input events, allowing clicks to pass through to windows below. Use cases: Gaming overlays, custom HUDs, desktop-wide status displays, or watching content on top without stealing input from applications underneath (Discord/OBS/video players over games or coding tools). Especially useful when coupled with floating and opacity rules to create custom overlays with ease. --- niri-config/src/window_rule.rs | 2 ++ src/layout/tile.rs | 4 ++++ src/window/mod.rs | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/niri-config/src/window_rule.rs b/niri-config/src/window_rule.rs index 0465d28fad..27f77655cf 100644 --- a/niri-config/src/window_rule.rs +++ b/niri-config/src/window_rule.rs @@ -72,6 +72,8 @@ pub struct WindowRule { pub scroll_factor: Option>, #[knuffel(child, unwrap(argument))] pub tiled_state: Option, + #[knuffel(child, unwrap(argument))] + pub input_passthrough: Option, } #[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)] diff --git a/src/layout/tile.rs b/src/layout/tile.rs index bc39db6d4d..66d83dfaac 100644 --- a/src/layout/tile.rs +++ b/src/layout/tile.rs @@ -860,6 +860,10 @@ impl Tile { let offset = self.bob_offset(); let point = point - offset; + if self.window.rules().input_passthrough == Some(true) { + return None; + } + if self.is_in_input_region(point) { let win_pos = self.buf_loc() + offset; Some(HitType::Input { win_pos }) diff --git a/src/window/mod.rs b/src/window/mod.rs index a329edd765..ec2072e8ee 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -119,6 +119,7 @@ pub struct ResolvedWindowRules { /// Override whether to set the Tiled xdg-toplevel state on the window. pub tiled_state: Option, + pub input_passthrough: Option, } impl<'a> WindowRef<'a> { @@ -240,6 +241,7 @@ impl ResolvedWindowRules { variable_refresh_rate: None, scroll_factor: None, tiled_state: None, + input_passthrough: None, } } @@ -365,6 +367,9 @@ impl ResolvedWindowRules { if let Some(x) = rule.tiled_state { resolved.tiled_state = Some(x); } + if let Some(x) = rule.input_passthrough { + resolved.input_passthrough = Some(x); + } } resolved.open_on_output = open_on_output.map(|x| x.to_owned());