diff options
| -rw-r--r-- | niri-config/src/lib.rs | 43 | ||||
| -rw-r--r-- | resources/default-config.kdl | 6 | ||||
| -rw-r--r-- | src/input.rs | 18 | ||||
| -rw-r--r-- | src/ipc/server.rs | 2 | ||||
| -rw-r--r-- | wiki/Configuration:-Key-Bindings.md | 9 |
5 files changed, 63 insertions, 15 deletions
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index d2bb506d..d88ee139 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -739,6 +739,7 @@ pub struct Bind { pub key: Key, pub action: Action, pub cooldown: Option<Duration>, + pub allow_when_locked: bool, } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] @@ -1564,7 +1565,7 @@ where &val.literal, "argument", "no arguments expected for this node", - )) + )); } let key = node @@ -1573,6 +1574,8 @@ where .map_err(|e| DecodeError::conversion(&node.node_name, e.wrap_err("invalid keybind")))?; let mut cooldown = None; + let mut allow_when_locked = false; + let mut allow_when_locked_node = None; for (name, val) in &node.properties { match &***name { "cooldown-ms" => { @@ -1580,6 +1583,10 @@ where knuffel::traits::DecodeScalar::decode(val, ctx)?, )); } + "allow-when-locked" => { + allow_when_locked = knuffel::traits::DecodeScalar::decode(val, ctx)?; + allow_when_locked_node = Some(name); + } name_str => { ctx.emit_error(DecodeError::unexpected( name, @@ -1599,6 +1606,7 @@ where key, action: Action::Spawn(vec![]), cooldown: None, + allow_when_locked: false, }; if let Some(child) = children.next() { @@ -1610,11 +1618,24 @@ where )); } match Action::decode_node(child, ctx) { - Ok(action) => Ok(Self { - key, - action, - cooldown, - }), + Ok(action) => { + if !matches!(action, Action::Spawn(_)) { + if let Some(node) = allow_when_locked_node { + ctx.emit_error(DecodeError::unexpected( + node, + "property", + "allow-when-locked can only be set on spawn binds", + )); + } + } + + Ok(Self { + key, + action, + cooldown, + allow_when_locked, + }) + } Err(e) => { ctx.emit_error(e); Ok(dummy) @@ -1922,7 +1943,7 @@ mod tests { } binds { - Mod+T { spawn "alacritty"; } + Mod+T allow-when-locked=true { spawn "alacritty"; } Mod+Q { close-window; } Mod+Shift+H { focus-monitor-left; } Mod+Ctrl+Shift+L { move-window-to-monitor-right; } @@ -2131,6 +2152,7 @@ mod tests { }, action: Action::Spawn(vec!["alacritty".to_owned()]), cooldown: None, + allow_when_locked: true, }, Bind { key: Key { @@ -2139,6 +2161,7 @@ mod tests { }, action: Action::CloseWindow, cooldown: None, + allow_when_locked: false, }, Bind { key: Key { @@ -2147,6 +2170,7 @@ mod tests { }, action: Action::FocusMonitorLeft, cooldown: None, + allow_when_locked: false, }, Bind { key: Key { @@ -2155,6 +2179,7 @@ mod tests { }, action: Action::MoveWindowToMonitorRight, cooldown: None, + allow_when_locked: false, }, Bind { key: Key { @@ -2163,6 +2188,7 @@ mod tests { }, action: Action::ConsumeWindowIntoColumn, cooldown: None, + allow_when_locked: false, }, Bind { key: Key { @@ -2171,6 +2197,7 @@ mod tests { }, action: Action::FocusWorkspace(1), cooldown: None, + allow_when_locked: false, }, Bind { key: Key { @@ -2179,6 +2206,7 @@ mod tests { }, action: Action::Quit(true), cooldown: None, + allow_when_locked: false, }, Bind { key: Key { @@ -2187,6 +2215,7 @@ mod tests { }, action: Action::FocusWorkspaceDown, cooldown: Some(Duration::from_millis(150)), + allow_when_locked: false, }, ]), debug: DebugConfig { diff --git a/resources/default-config.kdl b/resources/default-config.kdl index df09bcf5..0aa78f3d 100644 --- a/resources/default-config.kdl +++ b/resources/default-config.kdl @@ -263,8 +263,10 @@ binds { // Mod+T { spawn "bash" "-c" "notify-send hello && exec alacritty"; } // Example volume keys mappings for PipeWire & WirePlumber. - XF86AudioRaiseVolume { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1+"; } - XF86AudioLowerVolume { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1-"; } + // The allow-when-locked=true property makes them work even when the session is locked. + XF86AudioRaiseVolume allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1+"; } + XF86AudioLowerVolume allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1-"; } + XF86AudioMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; } Mod+Q { close-window; } diff --git a/src/input.rs b/src/input.rs index 2fd03a64..49546faa 100644 --- a/src/input.rs +++ b/src/input.rs @@ -297,12 +297,12 @@ impl State { pub fn handle_bind(&mut self, bind: Bind) { let Some(cooldown) = bind.cooldown else { - self.do_action(bind.action); + self.do_action(bind.action, bind.allow_when_locked); return; }; // Check this first so that it doesn't trigger the cooldown. - if self.niri.is_locked() && !allowed_when_locked(&bind.action) { + if self.niri.is_locked() && !(bind.allow_when_locked || allowed_when_locked(&bind.action)) { return; } @@ -323,13 +323,13 @@ impl State { .unwrap(); entry.insert(token); - self.do_action(bind.action); + self.do_action(bind.action, bind.allow_when_locked); } } } - pub fn do_action(&mut self, action: Action) { - if self.niri.is_locked() && !allowed_when_locked(&action) { + 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; } @@ -1844,6 +1844,7 @@ fn should_intercept_key( }, action, cooldown: None, + allow_when_locked: false, }); } } @@ -1892,6 +1893,7 @@ fn find_bind( }, action, cooldown: None, + allow_when_locked: false, }); } @@ -2174,6 +2176,7 @@ mod tests { }, action: Action::CloseWindow, cooldown: None, + allow_when_locked: false, }]); let comp_mod = CompositorMod::Super; @@ -2306,6 +2309,7 @@ mod tests { }, action: Action::CloseWindow, cooldown: None, + allow_when_locked: false, }, Bind { key: Key { @@ -2314,6 +2318,7 @@ mod tests { }, action: Action::FocusColumnLeft, cooldown: None, + allow_when_locked: false, }, Bind { key: Key { @@ -2322,6 +2327,7 @@ mod tests { }, action: Action::FocusWindowDown, cooldown: None, + allow_when_locked: false, }, Bind { key: Key { @@ -2330,6 +2336,7 @@ mod tests { }, action: Action::FocusWindowUp, cooldown: None, + allow_when_locked: false, }, Bind { key: Key { @@ -2338,6 +2345,7 @@ mod tests { }, action: Action::FocusColumnRight, cooldown: None, + allow_when_locked: false, }, ]); diff --git a/src/ipc/server.rs b/src/ipc/server.rs index 38a9b3dd..528da716 100644 --- a/src/ipc/server.rs +++ b/src/ipc/server.rs @@ -156,7 +156,7 @@ fn process(ctx: &ClientCtx, buf: &str) -> anyhow::Result<Response> { Request::Action(action) => { let action = niri_config::Action::from(action); ctx.event_loop.insert_idle(move |state| { - state.do_action(action); + state.do_action(action, false); }); Response::Handled } diff --git a/wiki/Configuration:-Key-Bindings.md b/wiki/Configuration:-Key-Bindings.md index 1164f2dc..62c250bb 100644 --- a/wiki/Configuration:-Key-Bindings.md +++ b/wiki/Configuration:-Key-Bindings.md @@ -113,6 +113,15 @@ binds { } ``` +Spawn bindings have a special `allow-when-locked=true` property that makes them work even while the session is locked: + +``` +binds { + // This mute bind will work even when the session is locked. + XF86AudioMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; } +} +``` + Currently, niri *does not* use a shell to run commands, which means that you need to manually separate arguments. ``` |
