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 Windows, fix focusing unfocused windows when switching from fullscreen to windowed.
|
||||||
- On X11, fix reporting incorrect DPI factor when waking from suspend.
|
- On X11, fix reporting incorrect DPI factor when waking from suspend.
|
||||||
- Change `EventLoopClosed` to contain the original event.
|
- 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)
|
# 0.20.0 Alpha 4 (2019-10-18)
|
||||||
|
|
||||||
|
|
|
@ -132,6 +132,15 @@ pub enum WindowEvent {
|
||||||
KeyboardInput {
|
KeyboardInput {
|
||||||
device_id: DeviceId,
|
device_id: DeviceId,
|
||||||
input: KeyboardInput,
|
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.
|
/// The cursor has moved on the window.
|
||||||
|
|
|
@ -74,6 +74,7 @@ pub fn init_keyboard(
|
||||||
virtual_keycode: vkcode,
|
virtual_keycode: vkcode,
|
||||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
||||||
},
|
},
|
||||||
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -125,6 +126,7 @@ pub fn init_keyboard(
|
||||||
virtual_keycode: vkcode,
|
virtual_keycode: vkcode,
|
||||||
modifiers: my_modifiers.lock().unwrap().clone(),
|
modifiers: my_modifiers.lock().unwrap().clone(),
|
||||||
},
|
},
|
||||||
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -198,6 +200,7 @@ pub fn init_keyboard(
|
||||||
virtual_keycode: None,
|
virtual_keycode: None,
|
||||||
modifiers: ModifiersState::default(),
|
modifiers: ModifiersState::default(),
|
||||||
},
|
},
|
||||||
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.unwrap();
|
.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};
|
use libc::{c_char, c_int, c_long, c_uint, c_ulong};
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use util::modifiers::{ModifierKeyState, ModifierKeymap};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::{LogicalPosition, LogicalSize},
|
dpi::{LogicalPosition, LogicalSize},
|
||||||
event::{DeviceEvent, Event, KeyboardInput, ModifiersState, WindowEvent},
|
event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent},
|
||||||
event_loop::EventLoopWindowTarget as RootELW,
|
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.
|
// value, though this should only be an issue under multiseat configurations.
|
||||||
let device = util::VIRTUAL_CORE_KEYBOARD;
|
let device = util::VIRTUAL_CORE_KEYBOARD;
|
||||||
let device_id = mkdid(device);
|
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
|
// When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with
|
||||||
// a keycode of 0.
|
// a keycode of 0.
|
||||||
if xkev.keycode != 0 {
|
if keycode != 0 {
|
||||||
let keysym = unsafe {
|
let scancode = keycode - 8;
|
||||||
let mut keysym = 0;
|
let keysym = wt.xconn.lookup_keysym(xkev);
|
||||||
(wt.xconn.xlib.XLookupString)(
|
|
||||||
xkev,
|
|
||||||
ptr::null_mut(),
|
|
||||||
0,
|
|
||||||
&mut keysym,
|
|
||||||
ptr::null_mut(),
|
|
||||||
);
|
|
||||||
wt.xconn.check_errors().expect("Failed to lookup keysym");
|
|
||||||
keysym
|
|
||||||
};
|
|
||||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||||
|
|
||||||
update_modifiers!(
|
update_modifiers!(
|
||||||
|
@ -588,10 +579,11 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
device_id,
|
device_id,
|
||||||
input: KeyboardInput {
|
input: KeyboardInput {
|
||||||
state,
|
state,
|
||||||
scancode: xkev.keycode - 8,
|
scancode,
|
||||||
virtual_keycode,
|
virtual_keycode,
|
||||||
modifiers,
|
modifiers,
|
||||||
},
|
},
|
||||||
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -908,6 +900,10 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
event: Focused(true),
|
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,
|
// The deviceid for this event is for a keyboard instead of a pointer,
|
||||||
// so we have to do a little extra work.
|
// so we have to do a little extra work.
|
||||||
let pointer_id = self
|
let pointer_id = self
|
||||||
|
@ -926,9 +922,12 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
event: CursorMoved {
|
event: CursorMoved {
|
||||||
device_id: mkdid(pointer_id),
|
device_id: mkdid(pointer_id),
|
||||||
position,
|
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 => {
|
ffi::XI_FocusOut => {
|
||||||
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
@ -940,8 +939,13 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
.unfocus(xev.event)
|
.unfocus(xev.event)
|
||||||
.expect("Failed to unfocus input context");
|
.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 {
|
callback(Event::WindowEvent {
|
||||||
window_id: mkwid(xev.event),
|
window_id,
|
||||||
event: Focused(false),
|
event: Focused(false),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1058,20 +1062,8 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let scancode = (keycode - 8) as u32;
|
let scancode = (keycode - 8) as u32;
|
||||||
|
let keysym = wt.xconn.keycode_to_keysym(keycode as ffi::KeyCode);
|
||||||
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 virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||||
|
|
||||||
let modifiers = self.device_mod_state.modifiers();
|
let modifiers = self.device_mod_state.modifiers();
|
||||||
|
|
||||||
callback(Event::DeviceEvent {
|
callback(Event::DeviceEvent {
|
||||||
|
@ -1182,4 +1174,45 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
Err(_) => (),
|
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 hint;
|
||||||
mod icon;
|
mod icon;
|
||||||
mod input;
|
mod input;
|
||||||
|
pub mod keys;
|
||||||
mod memory;
|
mod memory;
|
||||||
pub mod modifiers;
|
pub mod modifiers;
|
||||||
mod randr;
|
mod randr;
|
||||||
|
|
|
@ -264,6 +264,7 @@ pub unsafe fn modifier_event(
|
||||||
virtual_keycode,
|
virtual_keycode,
|
||||||
modifiers: event_mods(ns_event),
|
modifiers: event_mods(ns_event),
|
||||||
},
|
},
|
||||||
|
is_synthetic: false,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -582,6 +582,7 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||||
virtual_keycode,
|
virtual_keycode,
|
||||||
modifiers: event_mods(event),
|
modifiers: event_mods(event),
|
||||||
},
|
},
|
||||||
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -633,6 +634,7 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
|
||||||
virtual_keycode,
|
virtual_keycode,
|
||||||
modifiers: event_mods(event),
|
modifiers: event_mods(event),
|
||||||
},
|
},
|
||||||
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -741,6 +743,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
|
||||||
virtual_keycode,
|
virtual_keycode,
|
||||||
modifiers: event_mods(event),
|
modifiers: event_mods(event),
|
||||||
},
|
},
|
||||||
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,7 @@ impl<T> WindowTarget<T> {
|
||||||
virtual_keycode,
|
virtual_keycode,
|
||||||
modifiers,
|
modifiers,
|
||||||
},
|
},
|
||||||
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -83,6 +84,7 @@ impl<T> WindowTarget<T> {
|
||||||
virtual_keycode,
|
virtual_keycode,
|
||||||
modifiers,
|
modifiers,
|
||||||
},
|
},
|
||||||
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1277,6 +1277,7 @@ unsafe extern "system" fn public_window_callback<T>(
|
||||||
virtual_keycode: vkey,
|
virtual_keycode: vkey,
|
||||||
modifiers: event::get_key_mods(),
|
modifiers: event::get_key_mods(),
|
||||||
},
|
},
|
||||||
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// Windows doesn't emit a delete character by default, but in order to make it
|
// 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,
|
virtual_keycode: vkey,
|
||||||
modifiers: event::get_key_mods(),
|
modifiers: event::get_key_mods(),
|
||||||
},
|
},
|
||||||
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue