diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b9ec750..05011395 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 Windows, macOS, X11, Wayland and Web, implement setting images as cursors. See the `custom_cursors.rs` example. diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 2cf597f9..8f766f06 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -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, @@ -56,6 +56,8 @@ pub(super) struct EventProcessor { pub(super) first_touch: Option, // Currently focused window belonging to this process pub(super) active_window: Option, + /// Latest modifiers we've sent for the user to trigger change in event. + pub(super) modifiers: Cell, pub(super) is_composing: bool, } @@ -994,12 +996,7 @@ impl EventProcessor { 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. @@ -1061,12 +1058,7 @@ impl EventProcessor { // 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; @@ -1280,22 +1272,13 @@ impl EventProcessor { && (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::::into(new_mods).into(), - ), - }); - } - } + self.send_modifiers(self.kb_state.mods_state().into(), &mut callback); } ffi::XkbStateNotify => { let xev = @@ -1304,7 +1287,9 @@ impl EventProcessor { // 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, @@ -1313,17 +1298,8 @@ impl EventProcessor { 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::::into(new_mods).into(), - ), - }); - } - } + + self.send_modifiers(self.kb_state.mods_state().into(), &mut callback); } _ => {} } @@ -1392,6 +1368,23 @@ impl EventProcessor { } } + /// Send modifiers for the active window. + /// + /// The event won't be send when the `modifiers` match the previosly `sent` modifiers value. + fn send_modifiers)>(&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( wt: &super::EventLoopWindowTarget, window_id: crate::window::WindowId, diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 7d6743d8..29e7ba81 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -347,6 +347,7 @@ impl EventLoop { held_key_press: None, first_touch: None, active_window: None, + modifiers: Default::default(), is_composing: false, };