srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/x11/src/keysyms.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-04-02 00:58:00 +0200
committersrdusr <[email protected]>2024-04-02 00:58:00 +0200
commit8110bb2773b6c841029a51eca7971f42a36f480c (patch)
treeb3b364a6c0231fe408229e94dc9e25ce4562ad02 /crates/x11/src/keysyms.rs
parentd1f856edc516047575cf46f12fad2b59006363c8 (diff)
downloadsrdwm-8110bb2773b6c841029a51eca7971f42a36f480c.tar.gz
srdwm-8110bb2773b6c841029a51eca7971f42a36f480c.zip
Rewrite srdwm in Rust: working X11 and Wayland backends, Lua config
The C++ prototype (moved to legacy-cpp/) was mostly a design skeleton: X11 and Windows backends were partially real, Wayland created the wlroots object graph but never wired a single event listener, macOS was stub except monitor enumeration, and the Lua engine's srd.bind() stored a key-combo string but never the actual closure. See docs/PRIOR_ART.md for the full audit. This replaces it with a Cargo workspace: - srdwm-core: platform-independent window/workspace/monitor state, a real master-stack tiling layout, and SmartPlacement grid/cascade/ snap-to-edge placement - fixing several bugs in the C++ version (hardcoded 2-column grid, cascade that never cascaded, snap-to-edge that always returned a fixed rect). 35 unit tests. - srdwm-config: the srd Lua API via mlua, implementing the surface docs/DEFAULTS.md always documented but the C++ engine never actually built (srd.window.close()/focus(direction), srd.workspace.next(), real keybinding closures, require("srd") support). 10 unit tests. - srdwm-x11: a real reparenting WM with a drawn title bar (buttons, drag, resize), verified live under Xephyr - frame placement and client offset match srdwm-core's computed geometry exactly, and the decoration renders correctly on screen. - srdwm-wayland: a from-scratch smithay compositor (the C++ version had nothing working to port from) - runs via the winit backend, tracks xdg-shell toplevels through the same WindowManager and hit-testing code X11 uses, verified to start/render/run without crashing. Decorations are solid-color (no text yet); see docs/IMPLEMENTATION_STATUS.md for exact scope. - srdwm-windows / srdwm-macos: structured, cfg-gated designs informed by komorebi/glazewm and yabai/AeroSpace respectively (see docs/PRIOR_ART.md), honestly marked as unbuilt/unverified since this sandbox has no Windows or macOS target.
Diffstat (limited to 'crates/x11/src/keysyms.rs')
-rw-r--r--crates/x11/src/keysyms.rs112
1 files changed, 112 insertions, 0 deletions
diff --git a/crates/x11/src/keysyms.rs b/crates/x11/src/keysyms.rs
new file mode 100644
index 0000000..0baa6bc
--- /dev/null
+++ b/crates/x11/src/keysyms.rs
@@ -0,0 +1,112 @@
+//! A small, hand-maintained keysym <-> name table covering the keys used in
+//! practice by window-manager keybindings (letters, digits, navigation,
+//! function keys). Not a full xkbcommon-level keymap - see module docs in
+//! `lib.rs` for why that trade-off was made for this pass.
+
+pub fn keysym_to_name(keysym: u32) -> Option<String> {
+ Some(match keysym {
+ 0x0020 => "Space".to_string(),
+ 0x0061..=0x007a => (((keysym - 0x0061) as u8 + b'a') as char).to_string(),
+ 0x0030..=0x0039 => (((keysym - 0x0030) as u8 + b'0') as char).to_string(),
+ 0xff0d => "Return".to_string(),
+ 0xff1b => "Escape".to_string(),
+ 0xff09 => "Tab".to_string(),
+ 0xff08 => "BackSpace".to_string(),
+ 0xffff => "Delete".to_string(),
+ 0xff51 => "Left".to_string(),
+ 0xff52 => "Up".to_string(),
+ 0xff53 => "Right".to_string(),
+ 0xff54 => "Down".to_string(),
+ 0xff55 => "Prior".to_string(),
+ 0xff56 => "Next".to_string(),
+ 0xff50 => "Home".to_string(),
+ 0xff57 => "End".to_string(),
+ 0xffbe..=0xffc9 => format!("F{}", keysym - 0xffbe + 1),
+ 0x1008ff13 => "XF86AudioRaiseVolume".to_string(),
+ 0x1008ff11 => "XF86AudioLowerVolume".to_string(),
+ 0x1008ff12 => "XF86AudioMute".to_string(),
+ 0x1008ff02 => "XF86MonBrightnessUp".to_string(),
+ 0x1008ff03 => "XF86MonBrightnessDown".to_string(),
+ _ => return None,
+ })
+}
+
+/// Case-insensitive: config authors reasonably write both `"Space"` and
+/// `"space"`, and X11 key names aren't consistently capitalized in the wild.
+pub fn name_to_keysym(name: &str) -> Option<u32> {
+ match name.to_ascii_lowercase().as_str() {
+ "space" => return Some(0x0020),
+ "return" | "enter" => return Some(0xff0d),
+ "escape" => return Some(0xff1b),
+ "tab" => return Some(0xff09),
+ "backspace" => return Some(0xff08),
+ "delete" => return Some(0xffff),
+ "left" => return Some(0xff51),
+ "up" => return Some(0xff52),
+ "right" => return Some(0xff53),
+ "down" => return Some(0xff54),
+ "prior" | "pageup" => return Some(0xff55),
+ "next" | "pagedown" => return Some(0xff56),
+ "home" => return Some(0xff50),
+ "end" => return Some(0xff57),
+ "xf86audioraisevolume" => return Some(0x1008ff13),
+ "xf86audiolowervolume" => return Some(0x1008ff11),
+ "xf86audiomute" => return Some(0x1008ff12),
+ "xf86monbrightnessup" => return Some(0x1008ff02),
+ "xf86monbrightnessdown" => return Some(0x1008ff03),
+ _ => {}
+ }
+ if name.len() == 1 {
+ let c = name.chars().next().unwrap().to_ascii_lowercase();
+ if c.is_ascii_lowercase() {
+ return Some(0x0061 + (c as u32 - 'a' as u32));
+ }
+ if c.is_ascii_digit() {
+ return Some(0x0030 + (c as u32 - '0' as u32));
+ }
+ }
+ if let Some(rest) = name.to_ascii_lowercase().strip_prefix('f') {
+ if let Ok(n) = rest.parse::<u32>() {
+ if (1..=12).contains(&n) {
+ return Some(0xffbe + n - 1);
+ }
+ }
+ }
+ None
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn letters_and_digits_roundtrip() {
+ for c in 'a'..='z' {
+ let name = c.to_string();
+ let ks = name_to_keysym(&name).unwrap();
+ assert_eq!(keysym_to_name(ks), Some(name));
+ }
+ for c in '0'..='9' {
+ let name = c.to_string();
+ let ks = name_to_keysym(&name).unwrap();
+ assert_eq!(keysym_to_name(ks), Some(name));
+ }
+ }
+
+ #[test]
+ fn lowercase_names_resolve_case_insensitively() {
+ // Config authors reasonably write "Mod4+Shift+space"; this must
+ // resolve to the same keysym as "Space".
+ assert_eq!(name_to_keysym("space"), name_to_keysym("Space"));
+ assert_eq!(name_to_keysym("return"), name_to_keysym("Return"));
+ assert_eq!(name_to_keysym("f5"), name_to_keysym("F5"));
+ }
+
+ #[test]
+ fn named_keys_roundtrip() {
+ for name in ["Return", "Escape", "Tab", "Left", "Right", "Up", "Down", "F1", "F12"] {
+ let ks = name_to_keysym(name).unwrap();
+ assert_eq!(keysym_to_name(ks), Some(name.to_string()));
+ }
+ }
+}