On X11, replay modifiers consumed by XIM
This commit is contained in:
parent
010787a430
commit
7e28d7615e
4 changed files with 170 additions and 16 deletions
|
|
@ -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<xproto::Window>,
|
||||
/// Latest modifiers we've sent for the user to trigger change in event.
|
||||
pub modifiers: Cell<ModifiersState>,
|
||||
pub xfiltered_modifiers: VecDeque<c_ulong>,
|
||||
pub xmodmap: util::ModifierKeymap,
|
||||
pub is_composing: bool,
|
||||
}
|
||||
|
||||
|
|
@ -138,11 +143,25 @@ impl EventProcessor {
|
|||
where
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
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),
|
||||
|
|
@ -940,10 +959,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 {
|
||||
|
|
@ -957,6 +1001,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;
|
||||
}
|
||||
|
||||
|
|
@ -986,6 +1035,41 @@ impl EventProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
fn send_synthic_modifier_from_core<T: 'static, F>(
|
||||
&mut self,
|
||||
window_id: crate::window::WindowId,
|
||||
state: u16,
|
||||
mut callback: F,
|
||||
) where
|
||||
F: FnMut(&RootAEL, Event<T>),
|
||||
{
|
||||
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<T: 'static, F>(
|
||||
&self,
|
||||
event: &XIDeviceEvent,
|
||||
|
|
@ -1571,6 +1655,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,
|
||||
|
|
@ -1586,6 +1671,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,
|
||||
|
|
@ -1717,14 +1803,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(),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -51,16 +51,16 @@ mod window;
|
|||
mod xdisplay;
|
||||
mod xsettings;
|
||||
|
||||
pub use util::CustomCursor;
|
||||
|
||||
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, VideoModeHandle};
|
||||
use window::UnownedWindow;
|
||||
pub(crate) use xdisplay::{XConnection, XError, XNotSupported};
|
||||
|
||||
pub use util::CustomCursor;
|
||||
|
||||
// Xinput constants not defined in x11rb
|
||||
const ALL_DEVICES: u16 = 0;
|
||||
const ALL_MASTER_DEVICES: u16 = 1;
|
||||
|
|
@ -285,6 +285,9 @@ impl<T: 'static> EventLoop<T> {
|
|||
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 = ActiveEventLoop {
|
||||
ime,
|
||||
root,
|
||||
|
|
@ -322,6 +325,8 @@ impl<T: 'static> EventLoop<T> {
|
|||
ime_receiver,
|
||||
ime_event_receiver,
|
||||
xi2ext,
|
||||
xfiltered_modifiers: VecDeque::with_capacity(MAX_MOD_REPLAY_LEN),
|
||||
xmodmap,
|
||||
xkbext,
|
||||
xkb_context,
|
||||
num_touch: 0,
|
||||
|
|
|
|||
|
|
@ -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::{cursor::*, geometry::*, hint::*, input::*, window_property::*, wm::*};
|
||||
|
||||
use std::{
|
||||
mem::{self, MaybeUninit},
|
||||
ops::BitAnd,
|
||||
os::raw::*,
|
||||
pub use self::{
|
||||
cursor::*, geometry::*, hint::*, input::*, window_property::*, wm::*, xmodmap::ModifierKeymap,
|
||||
};
|
||||
|
||||
use super::{atoms::*, ffi, VoidCookie, X11Error, XConnection, XError};
|
||||
|
|
|
|||
56
src/platform_impl/linux/x11/util/xmodmap.rs
Normal file
56
src/platform_impl/linux/x11/util/xmodmap.rs
Normal file
|
|
@ -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<XKeyCode>,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue