From bd890e69aa0ca9e2b3b6ef5b047a44f05102af91 Mon Sep 17 00:00:00 2001 From: Imbris <2002109+Imberflur@users.noreply.github.com> Date: Sun, 9 Jul 2023 13:05:49 -0400 Subject: [PATCH] On X11, avoid false positive key repeats Instead of a single `bool` indicating that a key press has occured and no key has been released since then, we store the scancode of the last pressed key (if it is a key that repeats when held). This fixes a bug where pressing a new key while one is already held down will be flagged as a repeat even though it is obviously not a repeat. --- CHANGELOG.md | 2 + src/platform_impl/linux/common/xkb_state.rs | 1 - .../linux/x11/event_processor.rs | 44 ++++++++++++++++--- src/platform_impl/linux/x11/mod.rs | 2 +- 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d590d39..76cb48e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- On X11, fix false positive flagging of key repeats when pressing different keys with no release + between presses. - Implement `PartialOrd` and `Ord` for `KeyCode` and `NativeKeyCode`. # 0.29.0-beta.0 diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 541c186d..b6ba73df 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -354,7 +354,6 @@ impl KbdState { self.post_init(state, keymap); } - #[cfg(feature = "wayland")] pub fn key_repeats(&mut self, keycode: ffi::xkb_keycode_t) -> bool { unsafe { (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, keycode) == 1 } } diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index d8f076be..d86216b6 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -31,8 +31,11 @@ pub(super) struct EventProcessor { pub(super) kb_state: KbdState, // Number of touch events currently in progress pub(super) num_touch: u32, - // Whether we've got a key release for the key press. - pub(super) got_key_release: bool, + // This is the last pressed key that is repeatable (if it hasn't been + // released). + // + // Used to detect key repeats. + pub(super) held_key_press: Option, pub(super) first_touch: Option, // Currently focused window belonging to this process pub(super) active_window: Option, @@ -548,13 +551,39 @@ impl EventProcessor { let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD); let keycode = xkev.keycode as _; - let repeat = ty == ffi::KeyPress && !self.got_key_release; - // Update state after the repeat setting. + + // Update state to track key repeats and determine whether this key was a repeat. + // + // Note, when a key is held before focusing on this window the first + // (non-synthetic) event will not be flagged as a repeat (also note that the + // synthetic press event that is generated before this when the window gains focus + // will also not be flagged as a repeat). + // + // Only keys that can repeat should change the held_key_press state since a + // continuously held repeatable key may continue repeating after the press of a + // non-repeatable key. + let repeat = if self.kb_state.key_repeats(keycode) { + let is_latest_held = self.held_key_press == Some(keycode); + + if ty == ffi::KeyPress { + self.held_key_press = Some(keycode); + is_latest_held + } else { + // Check that the released key is the latest repeatable key that has been + // pressed, since repeats will continue for the latest key press if a + // different previously pressed key is released. + if is_latest_held { + self.held_key_press = None; + } + false + } + } else { + false + }; + let state = if ty == ffi::KeyPress { - self.got_key_release = false; ElementState::Pressed } else { - self.got_key_release = true; ElementState::Released }; @@ -926,6 +955,9 @@ impl EventProcessor { &mut self.kb_state, &mut callback, ); + // Clear this so detecting key repeats is consistently handled when the + // window regains focus. + self.held_key_press = None; callback(Event::WindowEvent { window_id, diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 4400b36b..10bec837 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -289,10 +289,10 @@ impl EventLoop { xkbext, kb_state, num_touch: 0, + held_key_press: None, first_touch: None, active_window: None, is_composing: false, - got_key_release: true, }; // Register for device hotplug events