Implement WindowEvent ModifiersChanged for X11 and Wayland (#1132)
* Implement WindowEvent ModifiersChanged for X11 and Wayland * Fix modifier key state desync on X11 * Run cargo fmt
This commit is contained in:
parent
bfcd85ab15
commit
206c3c246c
6 changed files with 258 additions and 19 deletions
|
|
@ -8,6 +8,8 @@ use super::{
|
|||
XExtension,
|
||||
};
|
||||
|
||||
use util::modifiers::{ModifierKeyState, ModifierKeymap};
|
||||
|
||||
use crate::{
|
||||
dpi::{LogicalPosition, LogicalSize},
|
||||
event::{DeviceEvent, Event, KeyboardInput, ModifiersState, WindowEvent},
|
||||
|
|
@ -21,6 +23,9 @@ pub(super) struct EventProcessor<T: 'static> {
|
|||
pub(super) devices: RefCell<HashMap<DeviceId, Device>>,
|
||||
pub(super) xi2ext: XExtension,
|
||||
pub(super) target: Rc<RootELW<T>>,
|
||||
pub(super) mod_keymap: ModifierKeymap,
|
||||
pub(super) device_mod_state: ModifierKeyState,
|
||||
pub(super) window_mod_state: ModifierKeyState,
|
||||
}
|
||||
|
||||
impl<T: 'static> EventProcessor<T> {
|
||||
|
|
@ -112,12 +117,22 @@ impl<T: 'static> EventProcessor<T> {
|
|||
let event_type = xev.get_type();
|
||||
match event_type {
|
||||
ffi::MappingNotify => {
|
||||
unsafe {
|
||||
(wt.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut());
|
||||
let mapping: &ffi::XMappingEvent = xev.as_ref();
|
||||
|
||||
if mapping.request == ffi::MappingModifier
|
||||
|| mapping.request == ffi::MappingKeyboard
|
||||
{
|
||||
unsafe {
|
||||
(wt.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut());
|
||||
}
|
||||
wt.xconn
|
||||
.check_errors()
|
||||
.expect("Failed to call XRefreshKeyboardMapping");
|
||||
|
||||
self.mod_keymap.reset_from_x_connection(&wt.xconn);
|
||||
self.device_mod_state.update(&self.mod_keymap);
|
||||
self.window_mod_state.update(&self.mod_keymap);
|
||||
}
|
||||
wt.xconn
|
||||
.check_errors()
|
||||
.expect("Failed to call XRefreshKeyboardMapping");
|
||||
}
|
||||
|
||||
ffi::ClientMessage => {
|
||||
|
|
@ -514,13 +529,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
// When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with
|
||||
// a keycode of 0.
|
||||
if xkev.keycode != 0 {
|
||||
let modifiers = ModifiersState {
|
||||
alt: xkev.state & ffi::Mod1Mask != 0,
|
||||
shift: xkev.state & ffi::ShiftMask != 0,
|
||||
ctrl: xkev.state & ffi::ControlMask != 0,
|
||||
logo: xkev.state & ffi::Mod4Mask != 0,
|
||||
};
|
||||
|
||||
let keysym = unsafe {
|
||||
let mut keysym = 0;
|
||||
(wt.xconn.xlib.XLookupString)(
|
||||
|
|
@ -535,6 +543,8 @@ impl<T: 'static> EventProcessor<T> {
|
|||
};
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
let modifiers = self.window_mod_state.modifiers();
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
|
|
@ -547,6 +557,27 @@ impl<T: 'static> EventProcessor<T> {
|
|||
},
|
||||
},
|
||||
});
|
||||
|
||||
if let Some(modifier) =
|
||||
self.mod_keymap.get_modifier(xkev.keycode as ffi::KeyCode)
|
||||
{
|
||||
self.window_mod_state.key_event(
|
||||
state,
|
||||
xkev.keycode as ffi::KeyCode,
|
||||
modifier,
|
||||
);
|
||||
|
||||
let new_modifiers = self.window_mod_state.modifiers();
|
||||
|
||||
if modifiers != new_modifiers {
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ModifiersChanged {
|
||||
modifiers: new_modifiers,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if state == Pressed {
|
||||
|
|
@ -859,6 +890,21 @@ impl<T: 'static> EventProcessor<T> {
|
|||
event: Focused(true),
|
||||
});
|
||||
|
||||
// When focus is gained, send any existing modifiers
|
||||
// to the window in a ModifiersChanged event. This is
|
||||
// done to compensate for modifier keys that may be
|
||||
// changed while a window is out of focus.
|
||||
if !self.device_mod_state.is_empty() {
|
||||
self.window_mod_state = self.device_mod_state.clone();
|
||||
|
||||
let modifiers = self.window_mod_state.modifiers();
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ModifiersChanged { modifiers },
|
||||
});
|
||||
}
|
||||
|
||||
// The deviceid for this event is for a keyboard instead of a pointer,
|
||||
// so we have to do a little extra work.
|
||||
let pointer_id = self
|
||||
|
|
@ -890,6 +936,22 @@ impl<T: 'static> EventProcessor<T> {
|
|||
.borrow_mut()
|
||||
.unfocus(xev.event)
|
||||
.expect("Failed to unfocus input context");
|
||||
|
||||
// When focus is lost, send a ModifiersChanged event
|
||||
// containing no modifiers set. This is done to compensate
|
||||
// for modifier keys that may be changed while a window
|
||||
// is out of focus.
|
||||
if !self.window_mod_state.is_empty() {
|
||||
self.window_mod_state.clear();
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(xev.event),
|
||||
event: WindowEvent::ModifiersChanged {
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(xev.event),
|
||||
event: Focused(false),
|
||||
|
|
@ -1022,18 +1084,25 @@ impl<T: 'static> EventProcessor<T> {
|
|||
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
if let Some(modifier) =
|
||||
self.mod_keymap.get_modifier(keycode as ffi::KeyCode)
|
||||
{
|
||||
self.device_mod_state.key_event(
|
||||
state,
|
||||
keycode as ffi::KeyCode,
|
||||
modifier,
|
||||
);
|
||||
}
|
||||
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
|
||||
callback(Event::DeviceEvent {
|
||||
device_id: mkdid(device_id),
|
||||
event: DeviceEvent::Key(KeyboardInput {
|
||||
scancode,
|
||||
virtual_keycode,
|
||||
state,
|
||||
// So, in an ideal world we can use libxkbcommon to get modifiers.
|
||||
// However, libxkbcommon-x11 isn't as commonly installed as one
|
||||
// would hope. We can still use the Xkb extension to get
|
||||
// comprehensive keyboard state updates, but interpreting that
|
||||
// info manually is going to be involved.
|
||||
modifiers: ModifiersState::default(),
|
||||
modifiers,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue