diff options
| author | srdusr <[email protected]> | 2024-05-14 00:42:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2024-05-14 00:42:00 +0200 |
| commit | dd31bf5ac2a692617d478478d36c733586b93cf8 (patch) | |
| tree | 2adb42ad8b24172cbbea3bf362a0d284acd54439 /crates/core/src/keysyms.rs | |
| parent | a9dd8a6d4947cb537f7919c5a66ba4624af61800 (diff) | |
| download | srdwm-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.rs | 61 |
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"); + } + } } |