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::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::os::raw::{c_char, c_int, c_long, c_ulong};
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
@ -38,6 +38,9 @@ use crate::platform_impl::x11::{
|
||||||
GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId,
|
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]`".
|
/// The X11 documentation states: "Keycodes lie in the inclusive range `[8, 255]`".
|
||||||
const KEYCODE_OFFSET: u8 = 8;
|
const KEYCODE_OFFSET: u8 = 8;
|
||||||
|
|
||||||
|
|
@ -63,6 +66,8 @@ pub struct EventProcessor {
|
||||||
pub active_window: Option<xproto::Window>,
|
pub active_window: Option<xproto::Window>,
|
||||||
/// Latest modifiers we've sent for the user to trigger change in event.
|
/// Latest modifiers we've sent for the user to trigger change in event.
|
||||||
pub modifiers: Cell<ModifiersState>,
|
pub modifiers: Cell<ModifiersState>,
|
||||||
|
pub xfiltered_modifiers: VecDeque<c_ulong>,
|
||||||
|
pub xmodmap: util::ModifierKeymap,
|
||||||
pub is_composing: bool,
|
pub is_composing: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,11 +143,25 @@ impl EventProcessor {
|
||||||
where
|
where
|
||||||
F: FnMut(&RootAEL, Event<T>),
|
F: FnMut(&RootAEL, Event<T>),
|
||||||
{
|
{
|
||||||
|
let event_type = xev.get_type();
|
||||||
|
|
||||||
if self.filter_event(xev) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let event_type = xev.get_type();
|
|
||||||
match event_type {
|
match event_type {
|
||||||
xlib::ClientMessage => self.client_message(xev.as_ref(), &mut callback),
|
xlib::ClientMessage => self.client_message(xev.as_ref(), &mut callback),
|
||||||
xlib::SelectionNotify => self.selection_notify(xev.as_ref(), &mut callback),
|
xlib::SelectionNotify => self.selection_notify(xev.as_ref(), &mut callback),
|
||||||
|
|
@ -940,10 +959,35 @@ impl EventProcessor {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
// Always update the modifiers.
|
// NOTE: When the modifier was captured by the XFilterEvents the modifiers for the modifier
|
||||||
self.udpate_mods_from_core_event(window_id, xev.state as u16, &mut callback);
|
// 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 {
|
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() {
|
if let Some(mut key_processor) = self.xkb_context.key_context() {
|
||||||
let event = key_processor.process_key_event(keycode, state, repeat);
|
let event = key_processor.process_key_event(keycode, state, repeat);
|
||||||
let event = Event::WindowEvent {
|
let event = Event::WindowEvent {
|
||||||
|
|
@ -957,6 +1001,11 @@ impl EventProcessor {
|
||||||
callback(&self.target, event);
|
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;
|
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>(
|
fn xinput2_button_input<T: 'static, F>(
|
||||||
&self,
|
&self,
|
||||||
event: &XIDeviceEvent,
|
event: &XIDeviceEvent,
|
||||||
|
|
@ -1571,6 +1655,7 @@ impl EventProcessor {
|
||||||
{
|
{
|
||||||
let xcb = wt.xconn.xcb_connection().get_raw_xcb_connection();
|
let xcb = wt.xconn.xcb_connection().get_raw_xcb_connection();
|
||||||
self.xkb_context.set_keymap_from_x11(xcb);
|
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) {
|
let window_id = match self.active_window.map(super::mkwid) {
|
||||||
Some(window_id) => window_id,
|
Some(window_id) => window_id,
|
||||||
|
|
@ -1586,6 +1671,7 @@ impl EventProcessor {
|
||||||
xlib::XkbMapNotify => {
|
xlib::XkbMapNotify => {
|
||||||
let xcb = wt.xconn.xcb_connection().get_raw_xcb_connection();
|
let xcb = wt.xconn.xcb_connection().get_raw_xcb_connection();
|
||||||
self.xkb_context.set_keymap_from_x11(xcb);
|
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) {
|
let window_id = match self.active_window.map(super::mkwid) {
|
||||||
Some(window_id) => window_id,
|
Some(window_id) => window_id,
|
||||||
None => return,
|
None => return,
|
||||||
|
|
@ -1717,14 +1803,18 @@ impl EventProcessor {
|
||||||
locked,
|
locked,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
// Bits 13 and 14 report the state keyboard group.
|
Self::core_keyboard_group(state),
|
||||||
((state >> 13) & 3) as u32,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let mods = xkb_state.modifiers();
|
let mods = xkb_state.modifiers();
|
||||||
self.send_modifiers(window_id, mods.into(), false, &mut callback);
|
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 {
|
pub fn xkb_mod_mask_from_core(&mut self, state: u16) -> xkb_mod_mask_t {
|
||||||
let mods_indices = match self.xkb_context.keymap_mut() {
|
let mods_indices = match self.xkb_context.keymap_mut() {
|
||||||
Some(keymap) => keymap.mods_indices(),
|
Some(keymap) => keymap.mods_indices(),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#![cfg(x11_platform)]
|
#![cfg(x11_platform)]
|
||||||
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
@ -51,16 +51,16 @@ mod window;
|
||||||
mod xdisplay;
|
mod xdisplay;
|
||||||
mod xsettings;
|
mod xsettings;
|
||||||
|
|
||||||
|
pub use util::CustomCursor;
|
||||||
|
|
||||||
use atoms::*;
|
use atoms::*;
|
||||||
use dnd::{Dnd, DndState};
|
use dnd::{Dnd, DndState};
|
||||||
use event_processor::EventProcessor;
|
use event_processor::{EventProcessor, MAX_MOD_REPLAY_LEN};
|
||||||
use ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender};
|
use ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender};
|
||||||
pub(crate) use monitor::{MonitorHandle, VideoModeHandle};
|
pub(crate) use monitor::{MonitorHandle, VideoModeHandle};
|
||||||
use window::UnownedWindow;
|
use window::UnownedWindow;
|
||||||
pub(crate) use xdisplay::{XConnection, XError, XNotSupported};
|
pub(crate) use xdisplay::{XConnection, XError, XNotSupported};
|
||||||
|
|
||||||
pub use util::CustomCursor;
|
|
||||||
|
|
||||||
// Xinput constants not defined in x11rb
|
// Xinput constants not defined in x11rb
|
||||||
const ALL_DEVICES: u16 = 0;
|
const ALL_DEVICES: u16 = 0;
|
||||||
const ALL_MASTER_DEVICES: u16 = 1;
|
const ALL_MASTER_DEVICES: u16 = 1;
|
||||||
|
|
@ -285,6 +285,9 @@ impl<T: 'static> EventLoop<T> {
|
||||||
let xkb_context =
|
let xkb_context =
|
||||||
Context::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection()).unwrap();
|
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 {
|
let window_target = ActiveEventLoop {
|
||||||
ime,
|
ime,
|
||||||
root,
|
root,
|
||||||
|
|
@ -322,6 +325,8 @@ impl<T: 'static> EventLoop<T> {
|
||||||
ime_receiver,
|
ime_receiver,
|
||||||
ime_event_receiver,
|
ime_event_receiver,
|
||||||
xi2ext,
|
xi2ext,
|
||||||
|
xfiltered_modifiers: VecDeque::with_capacity(MAX_MOD_REPLAY_LEN),
|
||||||
|
xmodmap,
|
||||||
xkbext,
|
xkbext,
|
||||||
xkb_context,
|
xkb_context,
|
||||||
num_touch: 0,
|
num_touch: 0,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
// Welcome to the util module, where we try to keep you from shooting yourself in the foot.
|
// Welcome to the util module, where we try to keep you from shooting yourself in the foot.
|
||||||
// *results may vary
|
// *results may vary
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
mem::{self, MaybeUninit},
|
||||||
|
ops::BitAnd,
|
||||||
|
os::raw::*,
|
||||||
|
};
|
||||||
|
|
||||||
mod client_msg;
|
mod client_msg;
|
||||||
mod cursor;
|
mod cursor;
|
||||||
mod geometry;
|
mod geometry;
|
||||||
|
|
@ -12,13 +18,10 @@ pub(crate) mod memory;
|
||||||
mod randr;
|
mod randr;
|
||||||
mod window_property;
|
mod window_property;
|
||||||
mod wm;
|
mod wm;
|
||||||
|
mod xmodmap;
|
||||||
|
|
||||||
pub use self::{cursor::*, geometry::*, hint::*, input::*, window_property::*, wm::*};
|
pub use self::{
|
||||||
|
cursor::*, geometry::*, hint::*, input::*, window_property::*, wm::*, xmodmap::ModifierKeymap,
|
||||||
use std::{
|
|
||||||
mem::{self, MaybeUninit},
|
|
||||||
ops::BitAnd,
|
|
||||||
os::raw::*,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{atoms::*, ffi, VoidCookie, X11Error, XConnection, XError};
|
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