Skip to content

Commit

Permalink
On X11, fix ModifiersChanged from xdotool
Browse files Browse the repository at this point in the history
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: alacritty/alacritty#7502
  • Loading branch information
kchibisov committed Dec 31, 2023
1 parent 2e61011 commit 3f82a6a
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 37 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Unreleased` header.

# Unreleased

- On X11, fix `ModifiersChanged` not sent from xdotool-like input
- On X11, keymap not updated from xmodmap.
- On X11, reduce the amount of time spent fetching screen resources.
- On Wayland, fix `Window::request_inner_size` being overwritten by resize.
Expand Down
67 changes: 30 additions & 37 deletions src/platform_impl/linux/x11/event_processor.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::{
cell::RefCell,
cell::{Cell, RefCell},
collections::HashMap,
os::raw::{c_char, c_int, c_long, c_ulong},
rc::Rc,
Expand Down Expand Up @@ -56,6 +56,8 @@ pub(super) struct EventProcessor<T: 'static> {
pub(super) first_touch: Option<u64>,
// Currently focused window belonging to this process
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,
}

Expand Down Expand Up @@ -994,12 +996,7 @@ impl<T: 'static> EventProcessor<T> {

let modifiers: crate::keyboard::ModifiersState =
self.kb_state.mods_state().into();
if !modifiers.is_empty() {
callback(Event::WindowEvent {
window_id,
event: WindowEvent::ModifiersChanged(modifiers.into()),
});
}
self.send_modifiers(modifiers, &mut callback);

// The deviceid for this event is for a keyboard instead of a pointer,
// so we have to do a little extra work.
Expand Down Expand Up @@ -1061,12 +1058,7 @@ impl<T: 'static> EventProcessor<T> {
// window regains focus.
self.held_key_press = None;

callback(Event::WindowEvent {
window_id,
event: WindowEvent::ModifiersChanged(
ModifiersState::empty().into(),
),
});
self.send_modifiers(ModifiersState::empty(), &mut callback);

if let Some(window) = self.with_window(window, Arc::clone) {
window.shared_state_lock().has_focus = false;
Expand Down Expand Up @@ -1280,22 +1272,13 @@ impl<T: 'static> EventProcessor<T> {
&& (keycodes_changed || geometry_changed)
{
unsafe { self.kb_state.init_with_x11_keymap() };
let modifiers = self.kb_state.mods_state();
self.send_modifiers(modifiers.into(), &mut callback);
}
}
ffi::XkbMapNotify => {
let prev_mods = self.kb_state.mods_state();
unsafe { self.kb_state.init_with_x11_keymap() };
let new_mods = self.kb_state.mods_state();
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(),
),
});
}
}
self.send_modifiers(self.kb_state.mods_state().into(), &mut callback);
}
ffi::XkbStateNotify => {
let xev =
Expand All @@ -1304,7 +1287,9 @@ impl<T: 'static> EventProcessor<T> {
// Set the 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(
xev.base_mods,
xev.latched_mods,
Expand All @@ -1313,17 +1298,8 @@ impl<T: 'static> EventProcessor<T> {
xev.latched_group as u32,
xev.locked_group as u32,
);
let new_mods = self.kb_state.mods_state();
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(),
),
});
}
}

self.send_modifiers(self.kb_state.mods_state().into(), &mut callback);
}
_ => {}
}
Expand Down Expand Up @@ -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>(
wt: &super::EventLoopWindowTarget<T>,
window_id: crate::window::WindowId,
Expand Down
1 change: 1 addition & 0 deletions src/platform_impl/linux/x11/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ impl<T: 'static> EventLoop<T> {
held_key_press: None,
first_touch: None,
active_window: None,
modifiers: Default::default(),
is_composing: false,
};

Expand Down

0 comments on commit 3f82a6a

Please sign in to comment.