diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index bb6ccb6103..b809768443 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1,5 +1,5 @@ use std::cell::{Cell, RefCell}; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::os::raw::{c_char, c_int, c_long, c_ulong}; use std::slice; use std::sync::{Arc, Mutex}; @@ -38,6 +38,9 @@ use crate::platform_impl::x11::{ GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId, }; +/// The maximum amount of X modifiers to replay. +pub const MAX_MOD_REPLAY_LEN: usize = 32; + /// The X11 documentation states: "Keycodes lie in the inclusive range `[8, 255]`". const KEYCODE_OFFSET: u8 = 8; @@ -63,6 +66,8 @@ pub struct EventProcessor { pub active_window: Option, /// Latest modifiers we've sent for the user to trigger change in event. pub modifiers: Cell, + pub xfiltered_modifiers: VecDeque, + pub xmodmap: util::ModifierKeymap, pub is_composing: bool, } @@ -138,11 +143,25 @@ impl EventProcessor { where F: FnMut(&RootELW, Event), { + let event_type = xev.get_type(); + if self.filter_event(xev) { + if event_type == xlib::KeyPress || event_type == xlib::KeyRelease { + let xev: &XKeyEvent = xev.as_ref(); + if self.xmodmap.is_modifier(xev.keycode as u8) { + // Don't grow the buffer past the `MAX_MOD_REPLAY_LEN`. This could happen + // when the modifiers are consumed entirely or serials are altered. + // + // Both cases shouldn't happen in well behaving clients. + if self.xfiltered_modifiers.len() == MAX_MOD_REPLAY_LEN { + self.xfiltered_modifiers.pop_back(); + } + self.xfiltered_modifiers.push_front(xev.serial); + } + } return; } - let event_type = xev.get_type(); match event_type { xlib::ClientMessage => self.client_message(xev.as_ref(), &mut callback), xlib::SelectionNotify => self.selection_notify(xev.as_ref(), &mut callback), @@ -936,10 +955,35 @@ impl EventProcessor { false }; - // Always update the modifiers. - self.udpate_mods_from_core_event(window_id, xev.state as u16, &mut callback); + // NOTE: When the modifier was captured by the XFilterEvents the modifiers for the modifier + // itself are out of sync due to XkbState being delivered before XKeyEvent, since it's + // being replayed by the XIM, thus we should replay ourselves. + let replay = if let Some(position) = self + .xfiltered_modifiers + .iter() + .rev() + .position(|&s| s == xev.serial) + { + // We don't have to replay modifiers pressed before the current event if some events + // were not forwarded to us, since their state is irrelevant. + self.xfiltered_modifiers + .resize(self.xfiltered_modifiers.len() - 1 - position, 0); + true + } else { + false + }; + + // Always update the modifiers when we're not replaying. + if !replay { + self.udpate_mods_from_core_event(window_id, xev.state as u16, &mut callback); + } if keycode != 0 && !self.is_composing { + // Don't alter the modifiers state from replaying. + if replay { + self.send_synthic_modifier_from_core(window_id, xev.state as u16, &mut callback); + } + if let Some(mut key_processor) = self.xkb_context.key_context() { let event = key_processor.process_key_event(keycode, state, repeat); let event = Event::WindowEvent { @@ -953,6 +997,11 @@ impl EventProcessor { callback(&self.target, event); } + // Restore the client's modifiers state after replay. + if replay { + self.send_modifiers(window_id, self.modifiers.get(), true, &mut callback); + } + return; } @@ -982,6 +1031,41 @@ impl EventProcessor { } } + fn send_synthic_modifier_from_core( + &mut self, + window_id: crate::window::WindowId, + state: u16, + mut callback: F, + ) where + F: FnMut(&RootELW, Event), + { + let keymap = match self.xkb_context.keymap_mut() { + Some(keymap) => keymap, + None => return, + }; + + let wt = Self::window_target(&self.target); + let xcb = wt.xconn.xcb_connection().get_raw_xcb_connection(); + + // Use synthetic state since we're replaying the modifier. The user modifier state + // will be restored later. + let mut xkb_state = match XkbState::new_x11(xcb, keymap) { + Some(xkb_state) => xkb_state, + None => return, + }; + + let mask = self.xkb_mod_mask_from_core(state); + xkb_state.update_modifiers(mask, 0, 0, 0, 0, Self::core_keyboard_group(state)); + let mods: ModifiersState = xkb_state.modifiers().into(); + + let event = Event::WindowEvent { + window_id, + event: WindowEvent::ModifiersChanged(mods.into()), + }; + + callback(&self.target, event); + } + fn xinput2_button_input(&self, event: &XIDeviceEvent, state: ElementState, mut callback: F) where F: FnMut(&RootELW, Event), @@ -1551,6 +1635,7 @@ impl EventProcessor { { let xcb = wt.xconn.xcb_connection().get_raw_xcb_connection(); self.xkb_context.set_keymap_from_x11(xcb); + self.xmodmap.reload_from_x_connection(&wt.xconn); let window_id = match self.active_window.map(super::mkwid) { Some(window_id) => window_id, @@ -1566,6 +1651,7 @@ impl EventProcessor { xlib::XkbMapNotify => { let xcb = wt.xconn.xcb_connection().get_raw_xcb_connection(); self.xkb_context.set_keymap_from_x11(xcb); + self.xmodmap.reload_from_x_connection(&wt.xconn); let window_id = match self.active_window.map(super::mkwid) { Some(window_id) => window_id, None => return, @@ -1694,14 +1780,18 @@ impl EventProcessor { locked, 0, 0, - // Bits 13 and 14 report the state keyboard group. - ((state >> 13) & 3) as u32, + Self::core_keyboard_group(state), ); let mods = xkb_state.modifiers(); self.send_modifiers(window_id, mods.into(), false, &mut callback); } + // Bits 13 and 14 report the state keyboard group. + pub fn core_keyboard_group(state: u16) -> u32 { + ((state >> 13) & 3) as u32 + } + pub fn xkb_mod_mask_from_core(&mut self, state: u16) -> xkb_mod_mask_t { let mods_indices = match self.xkb_context.keymap_mut() { Some(keymap) => keymap.mods_indices(), diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 9174101a79..aa0442069e 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -1,7 +1,7 @@ #![cfg(x11_platform)] use std::cell::{Cell, RefCell}; -use std::collections::{HashMap, HashSet}; +use std::collections::{HashMap, HashSet, VecDeque}; use std::ffi::CStr; use std::fmt; use std::marker::PhantomData; @@ -58,7 +58,7 @@ mod xsettings; use atoms::*; use dnd::{Dnd, DndState}; -use event_processor::EventProcessor; +use event_processor::{EventProcessor, MAX_MOD_REPLAY_LEN}; use ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender}; pub(crate) use monitor::{MonitorHandle, VideoMode}; use window::UnownedWindow; @@ -289,6 +289,9 @@ impl EventLoop { let xkb_context = Context::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection()).unwrap(); + let mut xmodmap = util::ModifierKeymap::new(); + xmodmap.reload_from_x_connection(&xconn); + let window_target = EventLoopWindowTarget { ime, root, @@ -327,6 +330,8 @@ impl EventLoop { ime_receiver, ime_event_receiver, xi2ext, + xfiltered_modifiers: VecDeque::with_capacity(MAX_MOD_REPLAY_LEN), + xmodmap, xkbext, xkb_context, num_touch: 0, diff --git a/src/platform_impl/linux/x11/util/mod.rs b/src/platform_impl/linux/x11/util/mod.rs index f806b0825c..937671aa58 100644 --- a/src/platform_impl/linux/x11/util/mod.rs +++ b/src/platform_impl/linux/x11/util/mod.rs @@ -1,6 +1,12 @@ // Welcome to the util module, where we try to keep you from shooting yourself in the foot. // *results may vary +use std::{ + mem::{self, MaybeUninit}, + ops::BitAnd, + os::raw::*, +}; + mod client_msg; mod cursor; mod geometry; @@ -12,13 +18,10 @@ pub(crate) mod memory; mod randr; mod window_property; mod wm; +mod xmodmap; -pub use self::{geometry::*, hint::*, input::*, window_property::*, wm::*}; - -use std::{ - mem::{self, MaybeUninit}, - ops::BitAnd, - os::raw::*, +pub use self::{ + geometry::*, hint::*, input::*, window_property::*, wm::*, xmodmap::ModifierKeymap, }; use super::{atoms::*, ffi, VoidCookie, X11Error, XConnection, XError}; diff --git a/src/platform_impl/linux/x11/util/xmodmap.rs b/src/platform_impl/linux/x11/util/xmodmap.rs new file mode 100644 index 0000000000..523a582970 --- /dev/null +++ b/src/platform_impl/linux/x11/util/xmodmap.rs @@ -0,0 +1,56 @@ +use std::collections::HashSet; +use std::slice; + +use x11_dl::xlib::{KeyCode as XKeyCode, XModifierKeymap}; + +// Offsets within XModifierKeymap to each set of keycodes. +// We are only interested in Shift, Control, Alt, and Logo. +// +// There are 8 sets total. The order of keycode sets is: +// Shift, Lock, Control, Mod1 (Alt), Mod2, Mod3, Mod4 (Logo), Mod5 +// +// https://tronche.com/gui/x/xlib/input/XSetModifierMapping.html +const NUM_MODS: usize = 8; + +/// Track which keys are modifiers, so we can properly replay them when they were filtered. +#[derive(Debug, Default)] +pub struct ModifierKeymap { + // Maps keycodes to modifiers + modifers: HashSet, +} + +impl ModifierKeymap { + pub fn new() -> ModifierKeymap { + ModifierKeymap::default() + } + + pub fn is_modifier(&self, keycode: XKeyCode) -> bool { + self.modifers.contains(&keycode) + } + + pub fn reload_from_x_connection(&mut self, xconn: &super::XConnection) { + unsafe { + let keymap = (xconn.xlib.XGetModifierMapping)(xconn.display); + + if keymap.is_null() { + return; + } + + self.reset_from_x_keymap(&*keymap); + + (xconn.xlib.XFreeModifiermap)(keymap); + } + } + + fn reset_from_x_keymap(&mut self, keymap: &XModifierKeymap) { + let keys_per_mod = keymap.max_keypermod as usize; + + let keys = unsafe { + slice::from_raw_parts(keymap.modifiermap as *const _, keys_per_mod * NUM_MODS) + }; + self.modifers.clear(); + for key in keys { + self.modifers.insert(*key); + } + } +}