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.
This commit is contained in:
Victor Brekenfeld 2021-09-17 16:55:01 +02:00
parent d0ee7d831e
commit 90dd28c910
3 changed files with 55 additions and 9 deletions

View File

@ -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

View File

@ -45,7 +45,9 @@ impl<Backend> AnvilState<Backend> {
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),

View File

@ -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<F>(&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();
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.
let sym = guard.state.key_get_one_sym(keycode + 8);
let mods_changed = guard.key_input(keycode, state);
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;