mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-24 06:11:30 +11:00
X11: Sync key press/release with window focus (#1296)
* X11: Sync key press/release with window focus * When a window loses focus, key release events are issued for all pressed keys * When a window gains focus, key press events are issued for all pressed keys * Adds `is_synthetic` field to `WindowEvent` variant `KeyboardInput` to indicate that these events are synthetic. * Adds `is_synthetic: false` to `WindowEvent::KeyboardInput` events issued on all other platforms * Clarify code with comments
This commit is contained in:
parent
830d47a5f7
commit
35505a3114
|
@ -10,6 +10,9 @@
|
|||
- On Windows, fix focusing unfocused windows when switching from fullscreen to windowed.
|
||||
- On X11, fix reporting incorrect DPI factor when waking from suspend.
|
||||
- Change `EventLoopClosed` to contain the original event.
|
||||
- Add `is_synthetic` field to `WindowEvent` variant `KeyboardInput`,
|
||||
indicating that the event is generated by winit.
|
||||
- On X11, generate synthetic key events for keys held when a window gains or loses focus.
|
||||
|
||||
# 0.20.0 Alpha 4 (2019-10-18)
|
||||
|
||||
|
|
|
@ -132,6 +132,15 @@ pub enum WindowEvent {
|
|||
KeyboardInput {
|
||||
device_id: DeviceId,
|
||||
input: KeyboardInput,
|
||||
/// If `true`, the event was generated synthetically by winit
|
||||
/// in one of the following circumstances:
|
||||
///
|
||||
/// * **X11**: Synthetic key press events are generated for all keys pressed
|
||||
/// when a window gains focus. Likewise, synthetic key release events
|
||||
/// are generated for all keys pressed when a window goes out of focus.
|
||||
///
|
||||
/// Otherwise, this value is always `false`.
|
||||
is_synthetic: bool,
|
||||
},
|
||||
|
||||
/// The cursor has moved on the window.
|
||||
|
|
|
@ -74,6 +74,7 @@ pub fn init_keyboard(
|
|||
virtual_keycode: vkcode,
|
||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
})
|
||||
.unwrap();
|
||||
|
@ -125,6 +126,7 @@ pub fn init_keyboard(
|
|||
virtual_keycode: vkcode,
|
||||
modifiers: my_modifiers.lock().unwrap().clone(),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
})
|
||||
.unwrap();
|
||||
|
@ -198,6 +200,7 @@ pub fn init_keyboard(
|
|||
virtual_keycode: None,
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
})
|
||||
.unwrap();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{cell::RefCell, collections::HashMap, ptr, rc::Rc, slice};
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc, slice};
|
||||
|
||||
use libc::{c_char, c_int, c_long, c_uint, c_ulong};
|
||||
|
||||
|
@ -12,7 +12,7 @@ use util::modifiers::{ModifierKeyState, ModifierKeymap};
|
|||
|
||||
use crate::{
|
||||
dpi::{LogicalPosition, LogicalSize},
|
||||
event::{DeviceEvent, Event, KeyboardInput, ModifiersState, WindowEvent},
|
||||
event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent},
|
||||
event_loop::EventLoopWindowTarget as RootELW,
|
||||
};
|
||||
|
||||
|
@ -557,22 +557,13 @@ impl<T: 'static> EventProcessor<T> {
|
|||
// value, though this should only be an issue under multiseat configurations.
|
||||
let device = util::VIRTUAL_CORE_KEYBOARD;
|
||||
let device_id = mkdid(device);
|
||||
let keycode = xkev.keycode;
|
||||
|
||||
// When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with
|
||||
// a keycode of 0.
|
||||
if xkev.keycode != 0 {
|
||||
let keysym = unsafe {
|
||||
let mut keysym = 0;
|
||||
(wt.xconn.xlib.XLookupString)(
|
||||
xkev,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
&mut keysym,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
wt.xconn.check_errors().expect("Failed to lookup keysym");
|
||||
keysym
|
||||
};
|
||||
if keycode != 0 {
|
||||
let scancode = keycode - 8;
|
||||
let keysym = wt.xconn.lookup_keysym(xkev);
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
update_modifiers!(
|
||||
|
@ -588,10 +579,11 @@ impl<T: 'static> EventProcessor<T> {
|
|||
device_id,
|
||||
input: KeyboardInput {
|
||||
state,
|
||||
scancode: xkev.keycode - 8,
|
||||
scancode,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -908,6 +900,10 @@ impl<T: 'static> EventProcessor<T> {
|
|||
event: Focused(true),
|
||||
});
|
||||
|
||||
let modifiers = ModifiersState::from_x11(&xev.mods);
|
||||
|
||||
update_modifiers!(modifiers, None);
|
||||
|
||||
// The deviceid for this event is for a keyboard instead of a pointer,
|
||||
// so we have to do a little extra work.
|
||||
let pointer_id = self
|
||||
|
@ -926,9 +922,12 @@ impl<T: 'static> EventProcessor<T> {
|
|||
event: CursorMoved {
|
||||
device_id: mkdid(pointer_id),
|
||||
position,
|
||||
modifiers: ModifiersState::from_x11(&xev.mods),
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
|
||||
// Issue key press events for all pressed keys
|
||||
self.handle_pressed_keys(window_id, ElementState::Pressed, &mut callback);
|
||||
}
|
||||
ffi::XI_FocusOut => {
|
||||
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
|
||||
|
@ -940,8 +939,13 @@ impl<T: 'static> EventProcessor<T> {
|
|||
.unfocus(xev.event)
|
||||
.expect("Failed to unfocus input context");
|
||||
|
||||
let window_id = mkwid(xev.event);
|
||||
|
||||
// Issue key release events for all pressed keys
|
||||
self.handle_pressed_keys(window_id, ElementState::Released, &mut callback);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(xev.event),
|
||||
window_id,
|
||||
event: Focused(false),
|
||||
})
|
||||
}
|
||||
|
@ -1058,20 +1062,8 @@ impl<T: 'static> EventProcessor<T> {
|
|||
return;
|
||||
}
|
||||
let scancode = (keycode - 8) as u32;
|
||||
|
||||
let keysym = unsafe {
|
||||
(wt.xconn.xlib.XKeycodeToKeysym)(
|
||||
wt.xconn.display,
|
||||
xev.detail as ffi::KeyCode,
|
||||
0,
|
||||
)
|
||||
};
|
||||
wt.xconn
|
||||
.check_errors()
|
||||
.expect("Failed to lookup raw keysym");
|
||||
|
||||
let keysym = wt.xconn.keycode_to_keysym(keycode as ffi::KeyCode);
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
|
||||
callback(Event::DeviceEvent {
|
||||
|
@ -1182,4 +1174,45 @@ impl<T: 'static> EventProcessor<T> {
|
|||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_pressed_keys<F>(
|
||||
&self,
|
||||
window_id: crate::window::WindowId,
|
||||
state: ElementState,
|
||||
callback: &mut F,
|
||||
) where
|
||||
F: FnMut(Event<T>),
|
||||
{
|
||||
let wt = get_xtarget(&self.target);
|
||||
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
|
||||
// Get the set of keys currently pressed and apply Key events to each
|
||||
let keys = wt.xconn.query_keymap();
|
||||
|
||||
for keycode in &keys {
|
||||
if keycode < 8 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let scancode = (keycode - 8) as u32;
|
||||
let keysym = wt.xconn.keycode_to_keysym(keycode);
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id,
|
||||
input: KeyboardInput {
|
||||
scancode,
|
||||
state,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
is_synthetic: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
92
src/platform_impl/linux/x11/util/keys.rs
Normal file
92
src/platform_impl/linux/x11/util/keys.rs
Normal file
|
@ -0,0 +1,92 @@
|
|||
use std::{iter::Enumerate, ptr, slice::Iter};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct Keymap {
|
||||
keys: [u8; 32],
|
||||
}
|
||||
|
||||
pub struct KeymapIter<'a> {
|
||||
iter: Enumerate<Iter<'a, u8>>,
|
||||
index: usize,
|
||||
item: Option<u8>,
|
||||
}
|
||||
|
||||
impl Keymap {
|
||||
pub fn iter(&self) -> KeymapIter<'_> {
|
||||
KeymapIter {
|
||||
iter: self.keys.iter().enumerate(),
|
||||
index: 0,
|
||||
item: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Keymap {
|
||||
type Item = ffi::KeyCode;
|
||||
type IntoIter = KeymapIter<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for KeymapIter<'_> {
|
||||
type Item = ffi::KeyCode;
|
||||
|
||||
fn next(&mut self) -> Option<ffi::KeyCode> {
|
||||
if self.item.is_none() {
|
||||
while let Some((index, &item)) = self.iter.next() {
|
||||
if item != 0 {
|
||||
self.index = index;
|
||||
self.item = Some(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.item.take().map(|item| {
|
||||
debug_assert!(item != 0);
|
||||
|
||||
let bit = first_bit(item);
|
||||
|
||||
if item != bit {
|
||||
// Remove the first bit; save the rest for further iterations
|
||||
self.item = Some(item ^ bit);
|
||||
}
|
||||
|
||||
let shift = bit.trailing_zeros() + (self.index * 8) as u32;
|
||||
shift as ffi::KeyCode
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl XConnection {
|
||||
pub fn keycode_to_keysym(&self, keycode: ffi::KeyCode) -> ffi::KeySym {
|
||||
unsafe { (self.xlib.XKeycodeToKeysym)(self.display, keycode, 0) }
|
||||
}
|
||||
|
||||
pub fn lookup_keysym(&self, xkev: &mut ffi::XKeyEvent) -> ffi::KeySym {
|
||||
let mut keysym = 0;
|
||||
|
||||
unsafe {
|
||||
(self.xlib.XLookupString)(xkev, ptr::null_mut(), 0, &mut keysym, ptr::null_mut());
|
||||
}
|
||||
|
||||
keysym
|
||||
}
|
||||
|
||||
pub fn query_keymap(&self) -> Keymap {
|
||||
let mut keys = [0; 32];
|
||||
|
||||
unsafe {
|
||||
(self.xlib.XQueryKeymap)(self.display, keys.as_mut_ptr() as *mut c_char);
|
||||
}
|
||||
|
||||
Keymap { keys }
|
||||
}
|
||||
}
|
||||
|
||||
fn first_bit(b: u8) -> u8 {
|
||||
1 << b.trailing_zeros()
|
||||
}
|
|
@ -9,6 +9,7 @@ mod geometry;
|
|||
mod hint;
|
||||
mod icon;
|
||||
mod input;
|
||||
pub mod keys;
|
||||
mod memory;
|
||||
pub mod modifiers;
|
||||
mod randr;
|
||||
|
|
|
@ -264,6 +264,7 @@ pub unsafe fn modifier_event(
|
|||
virtual_keycode,
|
||||
modifiers: event_mods(ns_event),
|
||||
},
|
||||
is_synthetic: false,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -582,6 +582,7 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
|
|||
virtual_keycode,
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -633,6 +634,7 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
|
|||
virtual_keycode,
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -741,6 +743,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
|
|||
virtual_keycode,
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ impl<T> WindowTarget<T> {
|
|||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -83,6 +84,7 @@ impl<T> WindowTarget<T> {
|
|||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1277,6 +1277,7 @@ unsafe extern "system" fn public_window_callback<T>(
|
|||
virtual_keycode: vkey,
|
||||
modifiers: event::get_key_mods(),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
// Windows doesn't emit a delete character by default, but in order to make it
|
||||
|
@ -1305,6 +1306,7 @@ unsafe extern "system" fn public_window_callback<T>(
|
|||
virtual_keycode: vkey,
|
||||
modifiers: event::get_key_mods(),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue