mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Add allow-when-locked=true spawn bind property
This commit is contained in:
+33
-4
@@ -739,6 +739,7 @@ pub struct Bind {
|
|||||||
pub key: Key,
|
pub key: Key,
|
||||||
pub action: Action,
|
pub action: Action,
|
||||||
pub cooldown: Option<Duration>,
|
pub cooldown: Option<Duration>,
|
||||||
|
pub allow_when_locked: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
@@ -1564,7 +1565,7 @@ where
|
|||||||
&val.literal,
|
&val.literal,
|
||||||
"argument",
|
"argument",
|
||||||
"no arguments expected for this node",
|
"no arguments expected for this node",
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let key = node
|
let key = node
|
||||||
@@ -1573,6 +1574,8 @@ where
|
|||||||
.map_err(|e| DecodeError::conversion(&node.node_name, e.wrap_err("invalid keybind")))?;
|
.map_err(|e| DecodeError::conversion(&node.node_name, e.wrap_err("invalid keybind")))?;
|
||||||
|
|
||||||
let mut cooldown = None;
|
let mut cooldown = None;
|
||||||
|
let mut allow_when_locked = false;
|
||||||
|
let mut allow_when_locked_node = None;
|
||||||
for (name, val) in &node.properties {
|
for (name, val) in &node.properties {
|
||||||
match &***name {
|
match &***name {
|
||||||
"cooldown-ms" => {
|
"cooldown-ms" => {
|
||||||
@@ -1580,6 +1583,10 @@ where
|
|||||||
knuffel::traits::DecodeScalar::decode(val, ctx)?,
|
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 => {
|
name_str => {
|
||||||
ctx.emit_error(DecodeError::unexpected(
|
ctx.emit_error(DecodeError::unexpected(
|
||||||
name,
|
name,
|
||||||
@@ -1599,6 +1606,7 @@ where
|
|||||||
key,
|
key,
|
||||||
action: Action::Spawn(vec![]),
|
action: Action::Spawn(vec![]),
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(child) = children.next() {
|
if let Some(child) = children.next() {
|
||||||
@@ -1610,11 +1618,24 @@ where
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
match Action::decode_node(child, ctx) {
|
match Action::decode_node(child, ctx) {
|
||||||
Ok(action) => Ok(Self {
|
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,
|
key,
|
||||||
action,
|
action,
|
||||||
cooldown,
|
cooldown,
|
||||||
}),
|
allow_when_locked,
|
||||||
|
})
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
ctx.emit_error(e);
|
ctx.emit_error(e);
|
||||||
Ok(dummy)
|
Ok(dummy)
|
||||||
@@ -1922,7 +1943,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
binds {
|
binds {
|
||||||
Mod+T { spawn "alacritty"; }
|
Mod+T allow-when-locked=true { spawn "alacritty"; }
|
||||||
Mod+Q { close-window; }
|
Mod+Q { close-window; }
|
||||||
Mod+Shift+H { focus-monitor-left; }
|
Mod+Shift+H { focus-monitor-left; }
|
||||||
Mod+Ctrl+Shift+L { move-window-to-monitor-right; }
|
Mod+Ctrl+Shift+L { move-window-to-monitor-right; }
|
||||||
@@ -2131,6 +2152,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::Spawn(vec!["alacritty".to_owned()]),
|
action: Action::Spawn(vec!["alacritty".to_owned()]),
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: true,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -2139,6 +2161,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::CloseWindow,
|
action: Action::CloseWindow,
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -2147,6 +2170,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::FocusMonitorLeft,
|
action: Action::FocusMonitorLeft,
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -2155,6 +2179,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::MoveWindowToMonitorRight,
|
action: Action::MoveWindowToMonitorRight,
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -2163,6 +2188,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::ConsumeWindowIntoColumn,
|
action: Action::ConsumeWindowIntoColumn,
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -2171,6 +2197,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::FocusWorkspace(1),
|
action: Action::FocusWorkspace(1),
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -2179,6 +2206,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::Quit(true),
|
action: Action::Quit(true),
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -2187,6 +2215,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::FocusWorkspaceDown,
|
action: Action::FocusWorkspaceDown,
|
||||||
cooldown: Some(Duration::from_millis(150)),
|
cooldown: Some(Duration::from_millis(150)),
|
||||||
|
allow_when_locked: false,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
debug: DebugConfig {
|
debug: DebugConfig {
|
||||||
|
|||||||
@@ -263,8 +263,10 @@ binds {
|
|||||||
// Mod+T { spawn "bash" "-c" "notify-send hello && exec alacritty"; }
|
// Mod+T { spawn "bash" "-c" "notify-send hello && exec alacritty"; }
|
||||||
|
|
||||||
// Example volume keys mappings for PipeWire & WirePlumber.
|
// Example volume keys mappings for PipeWire & WirePlumber.
|
||||||
XF86AudioRaiseVolume { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1+"; }
|
// The allow-when-locked=true property makes them work even when the session is locked.
|
||||||
XF86AudioLowerVolume { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1-"; }
|
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; }
|
Mod+Q { close-window; }
|
||||||
|
|
||||||
|
|||||||
+13
-5
@@ -297,12 +297,12 @@ impl State {
|
|||||||
|
|
||||||
pub fn handle_bind(&mut self, bind: Bind) {
|
pub fn handle_bind(&mut self, bind: Bind) {
|
||||||
let Some(cooldown) = bind.cooldown else {
|
let Some(cooldown) = bind.cooldown else {
|
||||||
self.do_action(bind.action);
|
self.do_action(bind.action, bind.allow_when_locked);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check this first so that it doesn't trigger the cooldown.
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,13 +323,13 @@ impl State {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
entry.insert(token);
|
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) {
|
pub fn do_action(&mut self, action: Action, allow_when_locked: bool) {
|
||||||
if self.niri.is_locked() && !allowed_when_locked(&action) {
|
if self.niri.is_locked() && !(allow_when_locked || allowed_when_locked(&action)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1844,6 +1844,7 @@ fn should_intercept_key(
|
|||||||
},
|
},
|
||||||
action,
|
action,
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1892,6 +1893,7 @@ fn find_bind(
|
|||||||
},
|
},
|
||||||
action,
|
action,
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2174,6 +2176,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::CloseWindow,
|
action: Action::CloseWindow,
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
let comp_mod = CompositorMod::Super;
|
let comp_mod = CompositorMod::Super;
|
||||||
@@ -2306,6 +2309,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::CloseWindow,
|
action: Action::CloseWindow,
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -2314,6 +2318,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::FocusColumnLeft,
|
action: Action::FocusColumnLeft,
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -2322,6 +2327,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::FocusWindowDown,
|
action: Action::FocusWindowDown,
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -2330,6 +2336,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::FocusWindowUp,
|
action: Action::FocusWindowUp,
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
},
|
},
|
||||||
Bind {
|
Bind {
|
||||||
key: Key {
|
key: Key {
|
||||||
@@ -2338,6 +2345,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
action: Action::FocusColumnRight,
|
action: Action::FocusColumnRight,
|
||||||
cooldown: None,
|
cooldown: None,
|
||||||
|
allow_when_locked: false,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -156,7 +156,7 @@ fn process(ctx: &ClientCtx, buf: &str) -> anyhow::Result<Response> {
|
|||||||
Request::Action(action) => {
|
Request::Action(action) => {
|
||||||
let action = niri_config::Action::from(action);
|
let action = niri_config::Action::from(action);
|
||||||
ctx.event_loop.insert_idle(move |state| {
|
ctx.event_loop.insert_idle(move |state| {
|
||||||
state.do_action(action);
|
state.do_action(action, false);
|
||||||
});
|
});
|
||||||
Response::Handled
|
Response::Handled
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
Currently, niri *does not* use a shell to run commands, which means that you need to manually separate arguments.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user