use crate::{error::*, WM}; 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<()> { dbg!(e); Ok(()) } pub(crate) fn handle_mapping_notify(&mut self, e: MappingNotifyEvent) -> Result<()> { 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); } }