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.
This commit is contained in:
Imbris 2023-07-09 13:05:49 -04:00 committed by GitHub
parent bca57ed0b4
commit bd890e69aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 8 deletions

View file

@ -8,6 +8,8 @@ And please only add new entries to the top of this list, right below the `# Unre
# Unreleased # 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`. - Implement `PartialOrd` and `Ord` for `KeyCode` and `NativeKeyCode`.
# 0.29.0-beta.0 # 0.29.0-beta.0

View file

@ -354,7 +354,6 @@ impl KbdState {
self.post_init(state, keymap); self.post_init(state, keymap);
} }
#[cfg(feature = "wayland")]
pub fn key_repeats(&mut self, keycode: ffi::xkb_keycode_t) -> bool { pub fn key_repeats(&mut self, keycode: ffi::xkb_keycode_t) -> bool {
unsafe { (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, keycode) == 1 } unsafe { (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, keycode) == 1 }
} }

View file

@ -31,8 +31,11 @@ pub(super) struct EventProcessor<T: 'static> {
pub(super) kb_state: KbdState, pub(super) kb_state: KbdState,
// Number of touch events currently in progress // Number of touch events currently in progress
pub(super) num_touch: u32, pub(super) num_touch: u32,
// Whether we've got a key release for the key press. // This is the last pressed key that is repeatable (if it hasn't been
pub(super) got_key_release: bool, // released).
//
// Used to detect key repeats.
pub(super) held_key_press: Option<u32>,
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<ffi::Window>, pub(super) active_window: Option<ffi::Window>,
@ -548,13 +551,39 @@ impl<T: 'static> EventProcessor<T> {
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD); let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
let keycode = xkev.keycode as _; 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 { let state = if ty == ffi::KeyPress {
self.got_key_release = false;
ElementState::Pressed ElementState::Pressed
} else { } else {
self.got_key_release = true;
ElementState::Released ElementState::Released
}; };
@ -926,6 +955,9 @@ impl<T: 'static> EventProcessor<T> {
&mut self.kb_state, &mut self.kb_state,
&mut callback, &mut callback,
); );
// Clear this so detecting key repeats is consistently handled when the
// window regains focus.
self.held_key_press = None;
callback(Event::WindowEvent { callback(Event::WindowEvent {
window_id, window_id,

View file

@ -289,10 +289,10 @@ impl<T: 'static> EventLoop<T> {
xkbext, xkbext,
kb_state, kb_state,
num_touch: 0, num_touch: 0,
held_key_press: None,
first_touch: None, first_touch: None,
active_window: None, active_window: None,
is_composing: false, is_composing: false,
got_key_release: true,
}; };
// Register for device hotplug events // Register for device hotplug events