From cc206d31b7307980960df979f1edec3626fe2d32 Mon Sep 17 00:00:00 2001 From: Osspial Date: Fri, 27 Dec 2019 16:26:23 -0500 Subject: [PATCH] Implement windows focus key press/release on Windows (#1307) * 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 * Implement windows focus key press/release on Windows * Docs Co-authored-by: Murarth --- CHANGELOG.md | 3 ++ src/event.rs | 3 +- src/platform_impl/windows/event.rs | 10 ++++++ src/platform_impl/windows/event_loop.rs | 44 +++++++++++++++++++++++-- 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7210605..d4ac8993 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,9 @@ - On X11, generate synthetic key events for keys held when a window gains or loses focus. - On X11, issue a `CursorMoved` event when a `Touch` event occurs, as X11 implicitly moves the cursor for such events. +- Add `is_synthetic` field to `WindowEvent` variant `KeyboardInput`, + indicating that the event is generated by winit. +- On X11 and Windows, generate synthetic key events for keys held when a window gains or loses focus. # 0.20.0 Alpha 4 (2019-10-18) diff --git a/src/event.rs b/src/event.rs index c2bb0eef..a9be124a 100644 --- a/src/event.rs +++ b/src/event.rs @@ -150,9 +150,10 @@ pub enum WindowEvent { /// 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 + /// * 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. + /// ***Currently, this is only functional on X11 and Windows*** /// /// Otherwise, this value is always `false`. is_synthetic: bool, diff --git a/src/platform_impl/windows/event.rs b/src/platform_impl/windows/event.rs index 96487ed7..60f6a925 100644 --- a/src/platform_impl/windows/event.rs +++ b/src/platform_impl/windows/event.rs @@ -27,6 +27,16 @@ pub fn get_key_mods() -> ModifiersState { mods } +pub fn get_pressed_keys() -> impl Iterator { + let mut keyboard_state = vec![0u8; 256]; + unsafe { winuser::GetKeyboardState(keyboard_state.as_mut_ptr()) }; + keyboard_state + .into_iter() + .enumerate() + .filter(|(_, p)| (*p & (1 << 7)) != 0) // whether or not a key is pressed is communicated via the high-order bit + .map(|(i, _)| i as c_int) +} + unsafe fn get_char(keyboard_state: &[u8; 256], v_key: u32, hkl: HKL) -> Option { let mut unicode_bytes = [0u16; 5]; let len = winuser::ToUnicodeEx( diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index c1abdcc7..9d6b7eff 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1421,7 +1421,27 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_SETFOCUS => { - use crate::event::WindowEvent::Focused; + use crate::event::{ElementState::Released, WindowEvent::Focused}; + for windows_keycode in event::get_pressed_keys() { + let scancode = + winuser::MapVirtualKeyA(windows_keycode as _, winuser::MAPVK_VK_TO_VSC); + let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode); + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: WindowEvent::KeyboardInput { + device_id: DEVICE_ID, + input: KeyboardInput { + scancode, + virtual_keycode, + state: Released, + modifiers: event::get_key_mods(), + }, + is_synthetic: true, + }, + }) + } + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: Focused(true), @@ -1431,7 +1451,27 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_KILLFOCUS => { - use crate::event::WindowEvent::Focused; + use crate::event::{ElementState::Released, WindowEvent::Focused}; + for windows_keycode in event::get_pressed_keys() { + let scancode = + winuser::MapVirtualKeyA(windows_keycode as _, winuser::MAPVK_VK_TO_VSC); + let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode); + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: WindowEvent::KeyboardInput { + device_id: DEVICE_ID, + input: KeyboardInput { + scancode, + virtual_keycode, + state: Released, + modifiers: event::get_key_mods(), + }, + is_synthetic: true, + }, + }) + } + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: Focused(false),