srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/core/src
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-07-14 01:02:00 +0200
committersrdusr <[email protected]>2024-07-14 01:02:00 +0200
commit2994f622b2998dc6aebbd38f52a6e6612ed8e7c8 (patch)
treeb181781f42cbbdf71551d09fdbc2a02d6c15d785 /crates/core/src
parent86b557a6c0baa4146b30712d8b7633f1b9b46d43 (diff)
downloadsrdwm-2994f622b2998dc6aebbd38f52a6e6612ed8e7c8.tar.gz
srdwm-2994f622b2998dc6aebbd38f52a6e6612ed8e7c8.zip
Fix keybindings silently never firing when a named key is lowercase
canonicalize_key_combo already reordered multi-modifier combos into dispatch's canonical Ctrl/Shift/Alt/Mod4 order, but passed the key name through verbatim. keysyms::keysym_to_name capitalizes every named key ("Space", "Return", "Escape", "BackSpace", ...) while leaving letters/digits alone, so srd.bind("Super+space", ...) stored "Mod4+space" while a real Space keypress dispatches as "Mod4+Space" -- never matching. Accepted silently at config-load time, so the only live symptom was the bind's own callback never running at all. Root-caused live: keybindings.lua's Super+space bind had a temporary diagnostic added (logs to /tmp/superspace.log before spawning ags) to tell "key never fired" apart from "key fired but ags failed" - the log file never existed, meaning the callback itself never ran. Fix: round-trip the key name through name_to_keysym (already case- insensitive) and back through keysym_to_name before storing, so any case the config writes normalizes to dispatch's canonical form.
Diffstat (limited to 'crates/core/src')
-rw-r--r--crates/core/src/event.rs48
1 files changed, 46 insertions, 2 deletions
diff --git a/crates/core/src/event.rs b/crates/core/src/event.rs
index 5822d3f..ad96f73 100644
--- a/crates/core/src/event.rs
+++ b/crates/core/src/event.rs
@@ -81,12 +81,36 @@ pub fn parse_key_combo(combo: &str) -> Option<(Modifiers, &str)> {
}
/// Re-orders a combo string into the canonical form [`key_combo_string`]
-/// produces, regardless of what order its modifiers were written in.
+/// produces, regardless of what order its modifiers were written in, *and*
+/// normalizes the key name to the exact casing [`crate::keysyms::
+/// keysym_to_name`] produces at dispatch time.
+///
+/// That second part matters on its own, independent of modifier order:
+/// `keysym_to_name` capitalizes every named key ("Space", "Return",
+/// "Escape", "BackSpace", ...) while leaving letters/digits as-is, but
+/// nothing constrains how a config author *writes* one - `srd.bind
+/// ("Super+space", ...)` (lowercase, as `keybindings.lua` had it) parsed to
+/// the literal key name "space" with no case change, so the string stored
+/// here never matched what a real Space keypress builds at dispatch
+/// ("Space", capitalized) even though the modifier-order fix above was
+/// already in place. The bind was accepted at config-load time (no error,
+/// nothing to notice) and then simply never fired - confirmed live: the
+/// bind's own diagnostic (spawning a command that logs to a file) never
+/// produced a log entry, meaning the keypress never even reached the
+/// callback. Round-tripping through [`crate::keysyms::name_to_keysym`] (which
+/// *is* already case-insensitive) and back fixes any such case mismatch for
+/// every key the table recognizes; an unrecognized name is left as-is
+/// (harmless - it wouldn't have matched at dispatch regardless of case).
+///
/// Unparseable input (empty string) is returned unchanged, so a caller that
/// can't do anything better with it still has *something* to store/log.
pub fn canonicalize_key_combo(combo: &str) -> String {
match parse_key_combo(combo) {
- Some((modifiers, key_name)) => key_combo_string(modifiers, key_name),
+ Some((modifiers, key_name)) => {
+ let canonical_name =
+ crate::keysyms::name_to_keysym(key_name).and_then(crate::keysyms::keysym_to_name).unwrap_or_else(|| key_name.to_string());
+ key_combo_string(modifiers, &canonical_name)
+ }
None => combo.to_string(),
}
}
@@ -161,6 +185,26 @@ mod tests {
}
#[test]
+ fn lowercase_named_key_still_reaches_the_capitalized_dispatch_form() {
+ // `srd.bind("Super+space", ...)` (lowercase, as a real config had
+ // it) must resolve to the exact same string a live Space keypress
+ // builds at dispatch time - `keysyms::keysym_to_name` always
+ // capitalizes named keys ("Space"), so without this normalization
+ // the bind is silently accepted at load time and then never fires.
+ assert_eq!(canonicalize_key_combo("Super+space"), canonicalize_key_combo("Super+Space"));
+ assert_eq!(canonicalize_key_combo("Super+space"), "Mod4+Space");
+ assert_eq!(canonicalize_key_combo("Super+Shift+return"), canonicalize_key_combo("Super+Shift+Return"));
+ }
+
+ #[test]
+ fn unrecognized_key_name_is_left_as_is() {
+ // Not in `keysyms`' table at all - round-tripping through it fails,
+ // so the original text passes through unchanged rather than being
+ // silently dropped.
+ assert_eq!(canonicalize_key_combo("Mod4+NotARealKey"), "Mod4+NotARealKey");
+ }
+
+ #[test]
fn parse_key_combo_accepts_modifiers_in_any_order() {
let (mods, key) = parse_key_combo("Mod4+Shift+m").unwrap();
assert_eq!(key, "m");