From 90dd28c910c0d2eab0796e873ca021708966202b Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Fri, 17 Sep 2021 16:55:01 +0200 Subject: [PATCH] wayland/seat: Add `KeysymHandle` to allow for keycode conversions. Currently keycodes are always converted using xkbcommons `State::key_get_one_sym` function. This may be not what the compositor wants, e.g. if it represents keybindings with explicit modifiers. Applying Shift in this case changes the sym, making it necessary for the compositor to *undo* this transformation, which is hard or even impossible and very unnecessary, when we have all the necessary information in smithay. Therefor this commit replaces the `Keysym` argument of the filter closure with a `KeysymHandle`, which allows for different variants of keysyms to be received. Modified (as previously), unmodified or even as a raw keycode. --- CHANGELOG.md | 1 + anvil/src/input_handler.rs | 4 ++- src/wayland/seat/keyboard.rs | 59 +++++++++++++++++++++++++++++++----- 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c2e085..c1cbe4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - `XdgPositionerState` moved to `XdgPopupState` and added to `XdgRequest::NewPopup` - `PopupSurface::send_configure` now checks the protocol version and returns an `Result` +- `KeyboardHandle::input` filter closure now receives a `KeysymHandle` instead of a `Keysym`. ### Additions diff --git a/anvil/src/input_handler.rs b/anvil/src/input_handler.rs index e2f06d2..38925b9 100644 --- a/anvil/src/input_handler.rs +++ b/anvil/src/input_handler.rs @@ -45,7 +45,9 @@ impl AnvilState { let mut action = KeyAction::None; let suppressed_keys = &mut self.suppressed_keys; self.keyboard - .input(keycode, state, serial, time, |modifiers, keysym| { + .input(keycode, state, serial, time, |modifiers, handle| { + let keysym = handle.modified_sym(); + debug!(log, "keysym"; "state" => format!("{:?}", state), "mods" => format!("{:?}", modifiers), diff --git a/src/wayland/seat/keyboard.rs b/src/wayland/seat/keyboard.rs index 99fc320..b2f6e8c 100644 --- a/src/wayland/seat/keyboard.rs +++ b/src/wayland/seat/keyboard.rs @@ -282,6 +282,47 @@ struct KbdRc { logger: ::slog::Logger, } +/// Handle to the underlying keycode to allow for different conversions +pub struct KeysymHandle<'a> { + keycode: u32, + keymap: &'a xkb::Keymap, + state: &'a xkb::State, +} + +impl<'a> fmt::Debug for KeysymHandle<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.keycode) + } +} + +impl<'a> KeysymHandle<'a> { + /// Returns the sym for the underlying keycode with all modifications by the current keymap state applied. + /// + /// This function is similar to [`KeysymHandle::modified_syms`], but is intended for cases where the user + /// does not want to or cannot handle multiple keysyms. + /// + /// If the key does not have exactly one keysym, returns [`keysyms::KEY_NoSymbol`]. + pub fn modified_sym(&'a self) -> Keysym { + self.state.key_get_one_sym(self.keycode) + } + + /// Returns the syms for the underlying keycode with all modifications by the current keymap state applied. + pub fn modified_syms(&'a self) -> &'a [Keysym] { + self.state.key_get_syms(self.keycode) + } + + /// Returns the syms for the underlying keycode without any modifications by the current keymap state applied. + pub fn raw_syms(&'a self) -> &'a [Keysym] { + self.keymap + .key_get_syms_by_level(self.keycode, self.state.key_get_layout(self.keycode), 0) + } + + /// Returns the raw code in X keycode system (shifted by 8) + pub fn raw_code(&'a self) -> u32 { + self.keycode + } +} + /// An handle to a keyboard handler /// /// It can be cloned and all clones manipulate the same internal state. @@ -313,22 +354,24 @@ impl KeyboardHandle { /// to be compared against. This includes non-character keysyms, such as XF86 special keys. pub fn input(&self, keycode: u32, state: KeyState, serial: Serial, time: u32, filter: F) where - F: FnOnce(&ModifiersState, Keysym) -> bool, + F: FnOnce(&ModifiersState, KeysymHandle<'_>) -> bool, { trace!(self.arc.logger, "Handling keystroke"; "keycode" => keycode, "state" => format_args!("{:?}", state)); let mut guard = self.arc.internal.borrow_mut(); - - // Offset the keycode by 8, as the evdev XKB rules reflect X's - // broken keycode system, which starts at 8. - let sym = guard.state.key_get_one_sym(keycode + 8); - let mods_changed = guard.key_input(keycode, state); + let handle = KeysymHandle { + // Offset the keycode by 8, as the evdev XKB rules reflect X's + // broken keycode system, which starts at 8. + keycode: keycode + 8, + state: &guard.state, + keymap: &guard.keymap, + }; trace!(self.arc.logger, "Calling input filter"; - "mods_state" => format_args!("{:?}", guard.mods_state), "sym" => xkb::keysym_get_name(sym) + "mods_state" => format_args!("{:?}", guard.mods_state), "sym" => xkb::keysym_get_name(handle.modified_sym()) ); - if !filter(&guard.mods_state, sym) { + if !filter(&guard.mods_state, handle) { // the filter returned false, we do not forward to client trace!(self.arc.logger, "Input was intercepted by filter"); return;