From e993ab6e41738c50eb64fc644be1bf49ca069f78 Mon Sep 17 00:00:00 2001 From: tcmal Date: Tue, 4 Jun 2024 22:06:07 +0100 Subject: grab all keys when needed --- src/keys.rs | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/main.rs | 2 +- 2 files changed, 117 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/keys.rs b/src/keys.rs index 6cf28a6..08dd056 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -1,12 +1,125 @@ use crate::{error::*, WM}; -use xcb::x::{KeyPressEvent, MappingNotifyEvent}; +use xcb::x::{ + GetKeyboardMapping, GetModifierMapping, GrabKey, GrabMode, KeyPressEvent, Keycode, Keysym, + Mapping, MappingNotifyEvent, ModMask, UngrabKey, GRAB_ANY, +}; + +// https://github.com/D-Programming-Deimos/libX11/blob/master/c/X11/keysymdef.h +const KEYSYM_NUMLOCK: Keysym = 0xff7f; impl WM<'_> { pub(crate) fn handle_key_press(&mut self, e: KeyPressEvent) -> Result<()> { - todo!() + dbg!(e); + + Ok(()) } pub(crate) fn handle_mapping_notify(&mut self, e: MappingNotifyEvent) -> Result<()> { - todo!() + if e.request() == Mapping::Keyboard { + self.grabkeys()?; + } + + Ok(()) + } + + pub(crate) fn grabkeys(&self) -> Result<()> { + let numlock_mask = self.get_numlock_mask()?; + + // ungrab all keys + self.conn.send_request(&UngrabKey { + key: GRAB_ANY, + grab_window: self.root, + modifiers: ModMask::ANY, + }); + + // go through keymap and grab what we need + let (min_keycode, max_keycode) = ( + self.conn.get_setup().min_keycode(), + self.conn.get_setup().max_keycode(), + ); + let mapping = self + .conn + .wait_for_reply(self.conn.send_request(&GetKeyboardMapping { + first_keycode: min_keycode, + count: max_keycode - min_keycode + 1, + }))?; + + for shift in 0..mapping.keysyms_per_keycode() { + for keycode in min_keycode..max_keycode { + let keysym = mapping.keysyms() + [(shift as usize * mapping.keysyms_per_keycode() as usize + keycode as usize)]; + if true { + // todo: is bound + // grab key with any combination of modifiers + for modmask in [ + ModMask::empty(), + ModMask::LOCK, + numlock_mask, + numlock_mask | ModMask::LOCK, + ] { + self.conn.send_request(&GrabKey { + grab_window: self.root, + key: keycode, + modifiers: modmask, + owner_events: true, + pointer_mode: GrabMode::Async, + keyboard_mode: GrabMode::Async, + }); + } + } + } + } + + // ensure all requests succeeded + self.conn.flush()?; + + Ok(()) + } + + pub(crate) fn get_numlock_mask(&self) -> Result { + let map = self + .conn + .wait_for_reply(self.conn.send_request(&GetModifierMapping {}))?; + let numlock_keycode = self.keysym_to_keycode(KEYSYM_NUMLOCK)?; + + let keypermod = map.keycodes().len() / 8; + for i in 0..8 { + for j in 0..keypermod { + if map.keycodes()[(i * keypermod + j) as usize] == numlock_keycode { + return Ok(ModMask::from_bits(1 << i).expect("x11 has unrecognised modifier")); + } + } + } + + // todo: is this ok? + return Ok(ModMask::empty()); + } + + pub(crate) fn keysym_to_keycode(&self, keysym: Keysym) -> Result { + let (min_keycode, max_keycode) = ( + self.conn.get_setup().min_keycode(), + self.conn.get_setup().max_keycode(), + ); + let mapping = self + .conn + .wait_for_reply(self.conn.send_request(&GetKeyboardMapping { + first_keycode: min_keycode, + count: max_keycode - min_keycode + 1, + }))?; + + // todo: apparently there's an optimisation here https://github.com/ArcherPergande/xcbkeys/blob/main/xcbkeys.h + for shift in 0..mapping.keysyms_per_keycode() { + for keycode in min_keycode..max_keycode { + if mapping.keysyms() + [keycode as usize * mapping.keysyms_per_keycode() as usize + shift as usize] + == keysym + { + return Ok(keycode); + } + } + } + + // todo: is this right? + return Ok(0); } } diff --git a/src/main.rs b/src/main.rs index c2b32a0..f61f422 100644 --- a/src/main.rs +++ b/src/main.rs @@ -89,7 +89,7 @@ impl WM<'_> { ], }))?; - // TODO: grab keys + self.grabkeys()?; // TODO: reset focus -- cgit v1.2.3