srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/core/src/keysyms.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-05-14 00:42:00 +0200
committersrdusr <[email protected]>2024-05-14 00:42:00 +0200
commitdd31bf5ac2a692617d478478d36c733586b93cf8 (patch)
tree2adb42ad8b24172cbbea3bf362a0d284acd54439 /crates/core/src/keysyms.rs
parenta9dd8a6d4947cb537f7919c5a66ba4624af61800 (diff)
downloadsrdwm-dd31bf5ac2a692617d478478d36c733586b93cf8.tar.gz
srdwm-dd31bf5ac2a692617d478478d36c733586b93cf8.zip
Draw a cursor, handle the lid, and everything a real config needs
Groundwork for actually daily-driving this: porting the user's Hyprland config exposed what srdwm couldn't yet express, and testing on a bare TTY exposed something worse. A visible mouse cursor. Nothing drew a pointer at all - on a bare TTY the mouse was simply invisible. It hid because the nested backend runs inside another compositor, which draws a cursor over srdwm's window; only the DRM backend, i.e. the actual session path, was affected. A built-in arrow is now composited above everything on the output the pointer is on. It's a reviewable ASCII bitmap rather than an XCursor theme: a cursor that is always present beats a prettier one that sometimes isn't, the same reasoning as decoration.rs's font fallback. Client-set cursor surfaces and named shapes are still not rendered, so an app asking for an I-beam gets the arrow. Lid switch. libinput switch events are handled and surfaced to config as srd.on("lid_closed"/"lid_open", fn), so closing the lid can lock and suspend instead of doing nothing. Config-driven additions, each needed by a binding in the ported config and none of which existed: fullscreen (Window.fullscreen was a dead field -- declared, never read or written), directional window move that swaps with the neighbour and reorders the stack so tiling follows, focus cycling, modifier+drag to move/resize anywhere in a window rather than only by the titlebar, modifier+scroll to change workspace, and 8 XF86 media/power keysyms taken from the system's own XF86keysym.h. The keysym tables are hand-maintained in both directions and a key missing from either fails silently, so a round-trip test now covers every one the configs bind. Verified in the QEMU VM: a bare-TTY screendump shows a recognisable arrow at the pointer position (113 white fill + 58 black outline pixels at screen centre, where the pointer starts).
Diffstat (limited to 'crates/core/src/keysyms.rs')
-rw-r--r--crates/core/src/keysyms.rs61
1 files changed, 55 insertions, 6 deletions
diff --git a/crates/core/src/keysyms.rs b/crates/core/src/keysyms.rs
index 0baa6bc..b166159 100644
--- a/crates/core/src/keysyms.rs
+++ b/crates/core/src/keysyms.rs
@@ -22,11 +22,23 @@ pub fn keysym_to_name(keysym: u32) -> Option<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(),
+ // Laptop/media keys. Values taken from the system's own
+ // <X11/XF86keysym.h>, not guessed - a wrong constant here fails
+ // silently, as an unrecognised keysym simply never matches a
+ // binding.
0x1008ff02 => "XF86MonBrightnessUp".to_string(),
0x1008ff03 => "XF86MonBrightnessDown".to_string(),
+ 0x1008ff11 => "XF86AudioLowerVolume".to_string(),
+ 0x1008ff12 => "XF86AudioMute".to_string(),
+ 0x1008ff13 => "XF86AudioRaiseVolume".to_string(),
+ 0x1008ff14 => "XF86AudioPlay".to_string(),
+ 0x1008ff15 => "XF86AudioStop".to_string(),
+ 0x1008ff16 => "XF86AudioPrev".to_string(),
+ 0x1008ff17 => "XF86AudioNext".to_string(),
+ 0x1008ff2a => "XF86PowerOff".to_string(),
+ 0x1008ff2d => "XF86ScreenSaver".to_string(),
+ 0x1008ff32 => "XF86AudioMedia".to_string(),
+ 0x1008ffb2 => "XF86AudioMicMute".to_string(),
_ => return None,
})
}
@@ -49,11 +61,22 @@ pub fn name_to_keysym(name: &str) -> Option<u32> {
"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),
+ // Must stay in sync with `keysym_to_name` above: the X11 backend
+ // resolves names through here to pass to `XGrabKey`, so a key
+ // missing from *this* direction can be pressed but never grabbed.
"xf86monbrightnessup" => return Some(0x1008ff02),
"xf86monbrightnessdown" => return Some(0x1008ff03),
+ "xf86audiolowervolume" => return Some(0x1008ff11),
+ "xf86audiomute" => return Some(0x1008ff12),
+ "xf86audioraisevolume" => return Some(0x1008ff13),
+ "xf86audioplay" => return Some(0x1008ff14),
+ "xf86audiostop" => return Some(0x1008ff15),
+ "xf86audioprev" => return Some(0x1008ff16),
+ "xf86audionext" => return Some(0x1008ff17),
+ "xf86poweroff" => return Some(0x1008ff2a),
+ "xf86screensaver" => return Some(0x1008ff2d),
+ "xf86audiomedia" => return Some(0x1008ff32),
+ "xf86audiomicmute" => return Some(0x1008ffb2),
_ => {}
}
if name.len() == 1 {
@@ -109,4 +132,30 @@ mod tests {
assert_eq!(keysym_to_name(ks), Some(name.to_string()));
}
}
+
+ #[test]
+ fn every_media_key_roundtrips_in_both_directions() {
+ // The two tables are hand-maintained and independent, so a key can
+ // easily be added to one and forgotten in the other - which fails
+ // silently (the binding just never fires, or never gets grabbed).
+ // Covers every XF86 key the shipped/ported configs actually bind.
+ for name in [
+ "XF86MonBrightnessUp",
+ "XF86MonBrightnessDown",
+ "XF86AudioRaiseVolume",
+ "XF86AudioLowerVolume",
+ "XF86AudioMute",
+ "XF86AudioMicMute",
+ "XF86AudioPlay",
+ "XF86AudioStop",
+ "XF86AudioPrev",
+ "XF86AudioNext",
+ "XF86AudioMedia",
+ "XF86PowerOff",
+ "XF86ScreenSaver",
+ ] {
+ let ks = name_to_keysym(name).unwrap_or_else(|| panic!("{name} missing from name_to_keysym"));
+ assert_eq!(keysym_to_name(ks), Some(name.to_string()), "{name} missing from keysym_to_name");
+ }
+ }
}