diff options
| author | srdusr <[email protected]> | 2026-08-05 00:55:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2026-08-05 00:55:00 +0200 |
| commit | 9c1673fa73bb49433370a60a7b4bb16abee98a9b (patch) | |
| tree | d67c4e6187aee0229944900de9c1bf5c6095d20c /crates/core | |
| parent | 3c47912ae0a2b81d06e4e9101b5b4e2f1e2997d9 (diff) | |
| download | srdwm-9c1673fa73bb49433370a60a7b4bb16abee98a9b.tar.gz srdwm-9c1673fa73bb49433370a60a7b4bb16abee98a9b.zip | |
Show a keybinding the way a person writes it, and let bind_repeat be described
Measured with dotfiles-1a, who own the launcher that displays these: `srd
keybindings` returned 84 bindings and 84 empty descriptions - 100% - so
every entry their launcher showed was a bare key combo with nothing to say
what it does. Two causes, one on each side of the boundary.
`srd.bind_repeat` never accepted a description. `srd.bind` has taken an
optional third argument all along, but its repeating sibling took only two,
and mlua drops a surplus argument silently rather than raising - so a
config that documented its repeat bindings got no error and no description.
It now takes one exactly like `bind`.
The combos themselves were reported in their internal dispatch form:
`Shift+Mod4+h`. `Mod4` is the X11 modifier's name, not a key's; nothing on
a keyboard is labelled Mod4, and the canonical Ctrl/Shift/Alt/Mod4 ordering
renders the owner's own `Super+Shift+h` binding back to them inside out.
`srd keybindings` now reports a display form - Super, and the order people
write - while everything internal keeps the canonical form it dispatches
on. The display form parses back to the same binding, so it can be pasted
into a config, and a test pins that round trip rather than trusting it.
The owner's own config now describes all 84 bindings; verified through the
real path, in a nested compositor running that config: 84 of 84 described,
zero occurrences of "Mod4".
Diffstat (limited to 'crates/core')
| -rw-r--r-- | crates/core/src/event.rs | 52 | ||||
| -rw-r--r-- | crates/core/src/lib.rs | 2 |
2 files changed, 53 insertions, 1 deletions
diff --git a/crates/core/src/event.rs b/crates/core/src/event.rs index 0bfadc0..f171f06 100644 --- a/crates/core/src/event.rs +++ b/crates/core/src/event.rs @@ -104,6 +104,37 @@ pub fn parse_key_combo(combo: &str) -> Option<(Modifiers, &str)> { Some((modifiers, key_name)) } +/// The form of a combo to *show a person*, as distinct from the canonical +/// form everything internal is keyed by. +/// +/// Two differences, both of which matter only on screen: +/// +/// - `Mod4` is written `Super`. `Mod4` is the X11 modifier's name, not the +/// key's; nothing on a keyboard is labelled Mod4, and a launcher listing +/// "Mod4+Return" is showing an implementation detail. +/// - Modifiers come out in the order people write them - Super, Ctrl, Alt, +/// Shift - rather than the canonical Ctrl/Shift/Alt/Mod4 order, which +/// renders the owner's own `Super+Shift+h` binding as `Shift+Mod4+h`. +/// +/// Round-trips: [`parse_key_combo`] accepts `Super` and takes modifiers in +/// any order, so a string from here can be pasted straight into a config. +/// Nothing dispatches on this form - [`canonicalize_key_combo`] still owns +/// what a binding is stored and looked up as. +pub fn display_key_combo(combo: &str) -> String { + let Some((modifiers, key_name)) = parse_key_combo(combo) else { return combo.to_string() }; + let mut out = String::new(); + for (flag, name) in + [(Modifiers::SUPER, "Super"), (Modifiers::CTRL, "Ctrl"), (Modifiers::ALT, "Alt"), (Modifiers::SHIFT, "Shift")] + { + if modifiers.contains(flag) { + out.push_str(name); + out.push('+'); + } + } + out.push_str(key_name); + out +} + /// Re-orders a combo string into the canonical form [`key_combo_string`] /// produces, regardless of what order its modifiers were written in, *and* /// normalizes the key name to the exact casing [`crate::keysyms:: @@ -180,6 +211,27 @@ pub enum Event { #[cfg(test)] mod tests { + /// A launcher shows this string to a person, so it has to read like the + /// key on the keyboard and like what the config author wrote. + #[test] + fn display_form_uses_super_and_the_order_people_write() { + assert_eq!(display_key_combo("Shift+Mod4+h"), "Super+Shift+h"); + assert_eq!(display_key_combo("Ctrl+Mod4+k"), "Super+Ctrl+k"); + assert_eq!(display_key_combo("Shift+Alt+Tab"), "Alt+Shift+Tab"); + assert_eq!(display_key_combo("Mod4+Return"), "Super+Return"); + assert_eq!(display_key_combo("Print"), "Print"); + } + + /// The display form must be pasteable back into a config, or telling + /// someone their binding is "Super+Shift+h" sends them to a string that + /// does not work. + #[test] + fn display_form_parses_back_to_the_same_binding() { + for combo in ["Shift+Mod4+h", "Ctrl+Mod4+k", "Shift+Alt+Tab", "Mod4+Return", "Mod4+1"] { + assert_eq!(canonicalize_key_combo(&display_key_combo(combo)), canonicalize_key_combo(combo)); + } + } + use super::*; #[test] diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 9a2c3bc..755a00e 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -13,7 +13,7 @@ pub mod window; pub mod workspace; pub use context_menu::{ContextMenu, MenuAction}; -pub use event::{canonicalize_key_combo, key_combo_string, parse_key_combo, Event, MouseButton, Modifiers}; +pub use event::{canonicalize_key_combo, display_key_combo, key_combo_string, parse_key_combo, Event, MouseButton, Modifiers}; pub use geometry::Rect; pub use layout::{Layout, MasterStackLayout, NoOpLayout, TilingConfig}; pub use lock_config::LockConfig; |