srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/config/src/engine
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2026-05-13 19:47:00 +0200
committersrdusr <[email protected]>2026-05-13 19:47:00 +0200
commit670c9845129fc0c4b6e0192d657eea05e1a64b49 (patch)
tree273573d7ed109af3d39e268a6d43787b831c9d47 /crates/config/src/engine
parent646b37e7e3aa5931079c6b9e804f420bb43c74d5 (diff)
downloadsrdwm-670c9845129fc0c4b6e0192d657eea05e1a64b49.tar.gz
srdwm-670c9845129fc0c4b6e0192d657eea05e1a64b49.zip
Expose key bindings over IPC so a launcher can list them
Asked whether srdwm's bindings show in the AGS launcher. They could not: srd.bind lives entirely in the Lua engine, and nothing published a binding anywhere a client could read it. There was no IPC command, no field in any response, and bound_keys() was used only by main.rs to register grabs. srd.bind now takes an optional third argument, a description, and the loaded set is copied into the WindowManager after the initial load and after every reload. Core neither owns nor interprets them - it has no Lua state and never dispatches a key - it holds them so the IPC layer, which is handed a WindowManager and nothing else, can serve them. New `srd keybindings` returns combo and description pairs, sorted so a UI listing them does not reshuffle on every refresh. Every binding in the shipped config now carries a description, so the feature is useful without the user writing any. Verified live in a nested instance: 46 bindings published, 0 without a description, and editing the config file updated the list without a restart (which also exercised reload-on-write again). Also verified, for the separate report that windows cannot be moved to another workspace from the AGS workspace pills: the compositor side works. `srd dispatch move workspace <id> 2` moved a window from workspace 1 to 2 and correctly hid it, since workspace 1 was active. Nothing to fix here; the missing piece is on the shell side. 515 tests pass, clippy clean.
Diffstat (limited to 'crates/config/src/engine')
-rw-r--r--crates/config/src/engine/general.rs8
-rw-r--r--crates/config/src/engine/mod.rs22
-rw-r--r--crates/config/src/engine/support.rs4
3 files changed, 31 insertions, 3 deletions
diff --git a/crates/config/src/engine/general.rs b/crates/config/src/engine/general.rs
index c8a136f..68b441e 100644
--- a/crates/config/src/engine/general.rs
+++ b/crates/config/src/engine/general.rs
@@ -109,7 +109,7 @@ impl Engine {
pub(super) fn fn_bind(&self) -> Result<mlua::Function<'_>> {
let state = self.state.clone();
- Ok(self.lua.create_function(move |lua, (combo, f): (String, mlua::Function)| {
+ Ok(self.lua.create_function(move |lua, (combo, f, description): (String, mlua::Function, Option<String>)| {
// `key_bindings` is keyed by whatever string dispatch builds
// from a real keypress (`srdwm_core::key_combo_string`, fixed
// Ctrl/Shift/Alt/Mod4 order) - storing the config's own
@@ -119,7 +119,11 @@ impl Engine {
// grabbed/intercepted. See `parse_key_combo`'s doc comment.
let combo = srdwm_core::canonicalize_key_combo(&combo);
let key = lua.create_registry_value(f)?;
- state.borrow_mut().key_bindings.insert(combo, key);
+ let mut s = state.borrow_mut();
+ if let Some(description) = description.filter(|d| !d.trim().is_empty()) {
+ s.key_descriptions.insert(combo.clone(), description);
+ }
+ s.key_bindings.insert(combo, key);
Ok(())
})?)
}
diff --git a/crates/config/src/engine/mod.rs b/crates/config/src/engine/mod.rs
index ac7bdb8..8facd56 100644
--- a/crates/config/src/engine/mod.rs
+++ b/crates/config/src/engine/mod.rs
@@ -26,6 +26,15 @@ struct SharedState {
wm: Rc<RefCell<WindowManager>>,
values: HashMap<String, ConfigValue>,
key_bindings: HashMap<String, RegistryKey>,
+ /// Human-readable description for a binding, from `srd.bind`'s optional
+ /// third argument. Separate from `key_bindings` so a binding without one
+ /// costs nothing, and cleared alongside it on reload.
+ ///
+ /// Exists so a launcher or cheat-sheet can list what the keys actually
+ /// do - a bare combo is close to useless in a UI. Published to
+ /// `WindowManager` by `main.rs` and served over IPC as
+ /// `srd keybindings`.
+ key_descriptions: HashMap<String, String>,
/// Combos registered with `srd.bind_repeat`, which fire repeatedly while
/// held (Hyprland's `binde`). A subset of `key_bindings`.
repeat_keys: std::collections::HashSet<String>,
@@ -67,6 +76,7 @@ impl Engine {
wm,
values: default_config(),
key_bindings: HashMap::new(),
+ key_descriptions: HashMap::new(),
repeat_keys: std::collections::HashSet::new(),
event_handlers: HashMap::new(),
config_dir: config_dir.into(),
@@ -189,6 +199,18 @@ impl Engine {
self.state.borrow().key_bindings.keys().cloned().collect()
}
+ /// Every binding as `(combo, description)`, sorted by combo so the list
+ /// is stable between calls - a UI listing them should not reshuffle on
+ /// every refresh. `description` is empty when the binding did not give
+ /// one.
+ pub fn bound_keys_with_descriptions(&self) -> Vec<(String, String)> {
+ let state = self.state.borrow();
+ let mut out: Vec<(String, String)> =
+ state.key_bindings.keys().map(|combo| (combo.clone(), state.key_descriptions.get(combo).cloned().unwrap_or_default())).collect();
+ out.sort();
+ out
+ }
+
/// Combos that should auto-repeat while held.
pub fn repeat_keys(&self) -> Vec<String> {
self.state.borrow().repeat_keys.iter().cloned().collect()
diff --git a/crates/config/src/engine/support.rs b/crates/config/src/engine/support.rs
index 76bcf5e..9d81035 100644
--- a/crates/config/src/engine/support.rs
+++ b/crates/config/src/engine/support.rs
@@ -33,16 +33,18 @@ pub(super) fn do_reload(lua: &Lua, state: &Rc<RefCell<SharedState>>) -> Result<(
std::mem::take(&mut s.key_bindings),
std::mem::take(&mut s.event_handlers),
std::mem::take(&mut s.repeat_keys),
+ std::mem::take(&mut s.key_descriptions),
);
(s.config_dir.clone(), previous)
};
- let restore = |state: &Rc<RefCell<SharedState>>, previous: (_, _, _)| {
+ let restore = |state: &Rc<RefCell<SharedState>>, previous: (_, _, _, _)| {
let mut s = state.borrow_mut();
// Whatever the failed run managed to register before erroring is
// discarded, not merged: half of a broken config is not a config.
s.key_bindings = previous.0;
s.event_handlers = previous.1;
s.repeat_keys = previous.2;
+ s.key_descriptions = previous.3;
};
let path = config_dir.join("init.lua");
let src = match std::fs::read_to_string(&path) {