On X11, fix ModifiersChanged from xdotool

xdotool will update modifiers before Xkb will actually send event
updating them, thus the modifiers will be updating even before the
actual update, which is unfortunate.

Links: https://github.com/alacritty/alacritty/issues/7502
This commit is contained in:
Kirill Chibisov
2023-12-30 09:05:03 +04:00
parent 2e610111b0
commit 3f82a6a90d
3 changed files with 32 additions and 37 deletions

View File

@@ -11,6 +11,7 @@ Unreleased` header.
# Unreleased # Unreleased
- On X11, fix `ModifiersChanged` not sent from xdotool-like input
- On X11, keymap not updated from xmodmap. - On X11, keymap not updated from xmodmap.
- On X11, reduce the amount of time spent fetching screen resources. - On X11, reduce the amount of time spent fetching screen resources.
- On Wayland, fix `Window::request_inner_size` being overwritten by resize. - On Wayland, fix `Window::request_inner_size` being overwritten by resize.

View File

@@ -1,5 +1,5 @@
use std::{ use std::{
cell::RefCell, cell::{Cell, RefCell},
collections::HashMap, collections::HashMap,
os::raw::{c_char, c_int, c_long, c_ulong}, os::raw::{c_char, c_int, c_long, c_ulong},
rc::Rc, rc::Rc,
@@ -56,6 +56,8 @@ pub(super) struct EventProcessor<T: 'static> {
pub(super) first_touch: Option<u64>, pub(super) first_touch: Option<u64>,
// Currently focused window belonging to this process // Currently focused window belonging to this process
pub(super) active_window: Option<xproto::Window>, pub(super) active_window: Option<xproto::Window>,
/// Latest modifiers we've sent for the user to trigger change in event.
pub(super) modifiers: Cell<ModifiersState>,
pub(super) is_composing: bool, pub(super) is_composing: bool,
} }
@@ -994,12 +996,7 @@ impl<T: 'static> EventProcessor<T> {
let modifiers: crate::keyboard::ModifiersState = let modifiers: crate::keyboard::ModifiersState =
self.kb_state.mods_state().into(); self.kb_state.mods_state().into();
if !modifiers.is_empty() { self.send_modifiers(modifiers, &mut callback);
callback(Event::WindowEvent {
window_id,
event: WindowEvent::ModifiersChanged(modifiers.into()),
});
}
// The deviceid for this event is for a keyboard instead of a pointer, // The deviceid for this event is for a keyboard instead of a pointer,
// so we have to do a little extra work. // so we have to do a little extra work.
@@ -1061,12 +1058,7 @@ impl<T: 'static> EventProcessor<T> {
// window regains focus. // window regains focus.
self.held_key_press = None; self.held_key_press = None;
callback(Event::WindowEvent { self.send_modifiers(ModifiersState::empty(), &mut callback);
window_id,
event: WindowEvent::ModifiersChanged(
ModifiersState::empty().into(),
),
});
if let Some(window) = self.with_window(window, Arc::clone) { if let Some(window) = self.with_window(window, Arc::clone) {
window.shared_state_lock().has_focus = false; window.shared_state_lock().has_focus = false;
@@ -1280,22 +1272,13 @@ impl<T: 'static> EventProcessor<T> {
&& (keycodes_changed || geometry_changed) && (keycodes_changed || geometry_changed)
{ {
unsafe { self.kb_state.init_with_x11_keymap() }; unsafe { self.kb_state.init_with_x11_keymap() };
let modifiers = self.kb_state.mods_state();
self.send_modifiers(modifiers.into(), &mut callback);
} }
} }
ffi::XkbMapNotify => { ffi::XkbMapNotify => {
let prev_mods = self.kb_state.mods_state();
unsafe { self.kb_state.init_with_x11_keymap() }; unsafe { self.kb_state.init_with_x11_keymap() };
let new_mods = self.kb_state.mods_state(); self.send_modifiers(self.kb_state.mods_state().into(), &mut callback);
if prev_mods != new_mods {
if let Some(window) = self.active_window {
callback(Event::WindowEvent {
window_id: mkwid(window),
event: WindowEvent::ModifiersChanged(
Into::<ModifiersState>::into(new_mods).into(),
),
});
}
}
} }
ffi::XkbStateNotify => { ffi::XkbStateNotify => {
let xev = let xev =
@@ -1304,7 +1287,9 @@ impl<T: 'static> EventProcessor<T> {
// Set the timestamp. // Set the timestamp.
wt.xconn.set_timestamp(xev.time as xproto::Timestamp); wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
let prev_mods = self.kb_state.mods_state(); // NOTE: Modifiers could update without a prior event updating them,
// thus diffing the state before and after is not reliable.
self.kb_state.update_modifiers( self.kb_state.update_modifiers(
xev.base_mods, xev.base_mods,
xev.latched_mods, xev.latched_mods,
@@ -1313,17 +1298,8 @@ impl<T: 'static> EventProcessor<T> {
xev.latched_group as u32, xev.latched_group as u32,
xev.locked_group as u32, xev.locked_group as u32,
); );
let new_mods = self.kb_state.mods_state();
if prev_mods != new_mods { self.send_modifiers(self.kb_state.mods_state().into(), &mut callback);
if let Some(window) = self.active_window {
callback(Event::WindowEvent {
window_id: mkwid(window),
event: WindowEvent::ModifiersChanged(
Into::<ModifiersState>::into(new_mods).into(),
),
});
}
}
} }
_ => {} _ => {}
} }
@@ -1392,6 +1368,23 @@ impl<T: 'static> EventProcessor<T> {
} }
} }
/// Send modifiers for the active window.
///
/// The event won't be send when the `modifiers` match the previosly `sent` modifiers value.
fn send_modifiers<F: FnMut(Event<T>)>(&self, modifiers: ModifiersState, callback: &mut F) {
let window_id = match self.active_window {
Some(window) => mkwid(window),
None => return,
};
if self.modifiers.replace(modifiers) != modifiers {
callback(Event::WindowEvent {
window_id,
event: WindowEvent::ModifiersChanged(self.modifiers.get().into()),
});
}
}
fn handle_pressed_keys<F>( fn handle_pressed_keys<F>(
wt: &super::EventLoopWindowTarget<T>, wt: &super::EventLoopWindowTarget<T>,
window_id: crate::window::WindowId, window_id: crate::window::WindowId,

View File

@@ -346,6 +346,7 @@ impl<T: 'static> EventLoop<T> {
held_key_press: None, held_key_press: None,
first_touch: None, first_touch: None,
active_window: None, active_window: None,
modifiers: Default::default(),
is_composing: false, is_composing: false,
}; };