From ee85011906ced513b043021fcbae5b5dfe177ab8 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 14 Nov 2020 12:21:50 +0100 Subject: [PATCH 01/75] Introducing the new `KeyEvent` and renaming old stuff --- Cargo.toml | 1 + examples/control_flow.rs | 2 +- examples/cursor.rs | 2 +- examples/cursor_grab.rs | 2 +- examples/fullscreen.rs | 2 +- examples/handling_close.rs | 2 +- examples/minimize.rs | 2 +- examples/multiwindow.rs | 2 +- examples/resizable.rs | 2 +- examples/window_debug.rs | 4 +- src/event.rs | 99 ++++++++++++++++++++++--- src/platform_impl/windows/event.rs | 13 +++- src/platform_impl/windows/event_loop.rs | 12 +-- src/platform_impl/windows/mod.rs | 1 + 14 files changed, 119 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6a316d0cff..5e6c4dcd6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ log = "0.4" serde = { version = "1", optional = true, features = ["serde_derive"] } raw-window-handle = "0.3" bitflags = "1" +keyboard-types = "0.5" [dev-dependencies] image = "0.23" diff --git a/examples/control_flow.rs b/examples/control_flow.rs index 64372a9bc8..2d3c6911e6 100644 --- a/examples/control_flow.rs +++ b/examples/control_flow.rs @@ -51,7 +51,7 @@ fn main() { WindowEvent::CloseRequested => { close_requested = true; } - WindowEvent::KeyboardInput { + WindowEvent::KeyboardInput_DEPRECATED { input: KeyboardInput { virtual_keycode: Some(virtual_code), diff --git a/examples/cursor.rs b/examples/cursor.rs index a466e889a8..92f4736da4 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -20,7 +20,7 @@ fn main() { match event { Event::WindowEvent { event: - WindowEvent::KeyboardInput { + WindowEvent::KeyboardInput_DEPRECATED { input: KeyboardInput { state: ElementState::Pressed, diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index 90a94764de..60f93ade68 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -22,7 +22,7 @@ fn main() { match event { Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput { + WindowEvent::KeyboardInput_DEPRECATED { input: KeyboardInput { state: ElementState::Released, diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 4ed157f042..d6be08df0d 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -38,7 +38,7 @@ fn main() { match event { Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput { + WindowEvent::KeyboardInput_DEPRECATED { input: KeyboardInput { virtual_keycode: Some(virtual_code), diff --git a/examples/handling_close.rs b/examples/handling_close.rs index 8334c1773f..5c40453eb8 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -43,7 +43,7 @@ fn main() { // closing the window. How to close the window is detailed in the handler for // the Y key. } - WindowEvent::KeyboardInput { + WindowEvent::KeyboardInput_DEPRECATED { input: KeyboardInput { virtual_keycode: Some(virtual_code), diff --git a/examples/minimize.rs b/examples/minimize.rs index eb02a752c9..a818a43014 100644 --- a/examples/minimize.rs +++ b/examples/minimize.rs @@ -25,7 +25,7 @@ fn main() { // Keyboard input event to handle minimize via a hotkey Event::WindowEvent { - event: WindowEvent::KeyboardInput { input, .. }, + event: WindowEvent::KeyboardInput_DEPRECATED { input, .. }, window_id, } => { if window_id == window.id() { diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index ec97eee097..7c53ed2abc 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -33,7 +33,7 @@ fn main() { *control_flow = ControlFlow::Exit; } } - WindowEvent::KeyboardInput { + WindowEvent::KeyboardInput_DEPRECATED { input: KeyboardInput { state: ElementState::Pressed, diff --git a/examples/resizable.rs b/examples/resizable.rs index 17892d8741..b312e74cc8 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -25,7 +25,7 @@ fn main() { match event { Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput { + WindowEvent::KeyboardInput_DEPRECATED { input: KeyboardInput { virtual_keycode: Some(VirtualKeyCode::Space), diff --git a/examples/window_debug.rs b/examples/window_debug.rs index dd4007f324..b224951742 100644 --- a/examples/window_debug.rs +++ b/examples/window_debug.rs @@ -37,7 +37,7 @@ fn main() { match event { Event::DeviceEvent { event: - DeviceEvent::Key(KeyboardInput { + DeviceEvent::Key_DEPRECATED(KeyboardInput { virtual_keycode: Some(key), state: ElementState::Pressed, .. @@ -59,7 +59,7 @@ fn main() { _ => (), }, Event::WindowEvent { - event: WindowEvent::KeyboardInput { input, .. }, + event: WindowEvent::KeyboardInput_DEPRECATED { input, .. }, .. } => match input { KeyboardInput { diff --git a/src/event.rs b/src/event.rs index 5d89c7db0b..4f3f0cc3f8 100644 --- a/src/event.rs +++ b/src/event.rs @@ -248,7 +248,7 @@ pub enum WindowEvent<'a> { Focused(bool), /// An event from the keyboard has been received. - KeyboardInput { + KeyboardInput_DEPRECATED { device_id: DeviceId, input: KeyboardInput, /// If `true`, the event was generated synthetically by winit @@ -263,6 +263,21 @@ pub enum WindowEvent<'a> { is_synthetic: bool, }, + KeyboardInput { + device_id: DeviceId, + event: KeyEvent, + /// If `true`, the event was generated synthetically by winit + /// in one of the following circumstances: + /// + /// * 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, + }, + /// The keyboard modifiers have changed. /// /// Platform-specific behavior: @@ -367,16 +382,24 @@ impl Clone for WindowEvent<'static> { HoveredFileCancelled => HoveredFileCancelled, ReceivedCharacter(c) => ReceivedCharacter(*c), Focused(f) => Focused(*f), - KeyboardInput { + KeyboardInput_DEPRECATED { device_id, input, is_synthetic, - } => KeyboardInput { + } => KeyboardInput_DEPRECATED { device_id: *device_id, input: *input, is_synthetic: *is_synthetic, }, - + KeyboardInput { + device_id, + event, + is_synthetic, + } => KeyboardInput { + device_id: *device_id, + event: event.clone(), + is_synthetic: *is_synthetic, + }, ModifiersChanged(modifiers) => ModifiersChanged(modifiers.clone()), #[allow(deprecated)] CursorMoved { @@ -458,15 +481,24 @@ impl<'a> WindowEvent<'a> { HoveredFileCancelled => Some(HoveredFileCancelled), ReceivedCharacter(c) => Some(ReceivedCharacter(c)), Focused(focused) => Some(Focused(focused)), - KeyboardInput { + KeyboardInput_DEPRECATED { device_id, input, is_synthetic, - } => Some(KeyboardInput { + } => Some(KeyboardInput_DEPRECATED { device_id, input, is_synthetic, }), + KeyboardInput { + device_id, + event, + is_synthetic, + } => Some(KeyboardInput { + device_id: device_id, + event: event, + is_synthetic: is_synthetic, + }), ModifiersChanged(modifiers) => Some(ModifiersChanged(modifiers)), #[allow(deprecated)] CursorMoved { @@ -589,7 +621,7 @@ pub enum DeviceEvent { state: ElementState, }, - Key(KeyboardInput), + Key_DEPRECATED(KeyboardInput), Text { codepoint: char, @@ -605,7 +637,7 @@ pub struct KeyboardInput { /// This should not change if the user adjusts the host's keyboard map. Use when the physical location of the /// key is more important than the key's host GUI semantics, such as for movement controls in a first-person /// game. - pub scancode: ScanCode, + pub scancode: ScanCode_DEPRECATED, pub state: ElementState, @@ -623,6 +655,52 @@ pub struct KeyboardInput { pub modifiers: ModifiersState, } +//TODO Implement (de)serialization +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct KeyEvent { + pub scancode: ScanCode, + + /// Represents the position of a key independent of the + /// currently active layout. + /// Conforms to https://www.w3.org/TR/uievents-code/ + /// + /// Note that `Fn` and `FnLock` key events are not emmited by `winit`. + /// These keys are usually handled at the hardware or at the OS level. + pub physical_key: keyboard_types::Code, + + /// This value is affected by all modifiers except Ctrl. + /// + /// This is suitable for text input in a GUI application. + /// + /// Note that the `Unicode` variant may contain multiple characters. + /// For example on Windows when pressing ^ using + /// a US-International layout, this will be `Dead` for the first + /// keypress and will be `Unicode("^^")` for the second keypress. + /// It's important that this behaviour might be different on + /// other platforms. For example Linux systems may emit a + /// `Unicode("^")` on the second keypress. + /// + /// ## Platform-specific + /// - **Web:** Dead keys might be reported as the real key instead + /// of `Dead` depending on the browser/OS. + pub logical_key: keyboard_types::Key, + + pub location: keyboard_types::Location, + pub state: keyboard_types::KeyState, + pub repeat: bool, + + platform_specific: platform_impl::KeyEventExtra, +} + +// impl std::fmt::Debug for KeyEvent { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// f.debug_struct("KeyEvent") +// .field("scancode", &()) +// .field("y", &self.y) +// .finish() +// } +// } + /// Describes touch-screen input state. #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -722,7 +800,10 @@ impl Force { } /// Hardware-dependent keyboard scan code. -pub type ScanCode = u32; +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] +pub struct ScanCode (platform_impl::PlatformScanCode); + +pub type ScanCode_DEPRECATED = u32; /// Identifier for a specific analog axis on some device. pub type AxisId = u32; diff --git a/src/platform_impl/windows/event.rs b/src/platform_impl/windows/event.rs index 161cf6b2de..40de8e2951 100644 --- a/src/platform_impl/windows/event.rs +++ b/src/platform_impl/windows/event.rs @@ -5,13 +5,22 @@ use std::{ sync::atomic::{AtomicBool, AtomicPtr, Ordering}, }; -use crate::event::{ModifiersState, ScanCode, VirtualKeyCode}; +use crate::event::{ModifiersState, ScanCode, ScanCode_DEPRECATED, VirtualKeyCode}; use winapi::{ shared::minwindef::{HKL, HKL__, LPARAM, UINT, WPARAM}, um::winuser, }; +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct KeyEventExtra { + pub char_with_all_modifers: Option, + pub key_without_modifers: keyboard_types::Key, +} + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] +pub struct PlatformScanCode(pub u32); + fn key_pressed(vkey: c_int) -> bool { unsafe { (winuser::GetKeyState(vkey) & (1 << 15)) == (1 << 15) } } @@ -391,7 +400,7 @@ pub fn handle_extended_keys( pub fn process_key_params( wparam: WPARAM, lparam: LPARAM, -) -> Option<(ScanCode, Option)> { +) -> Option<(ScanCode_DEPRECATED, Option)> { let scancode = ((lparam >> 16) & 0xff) as UINT; let extended = (lparam & 0x01000000) != 0; handle_extended_keys(wparam as _, scancode, extended) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index a6794e4e2a..ca55dcf802 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1121,7 +1121,7 @@ unsafe extern "system" fn public_window_callback( #[allow(deprecated)] subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { + event: WindowEvent::KeyboardInput_DEPRECATED { device_id: DEVICE_ID, input: KeyboardInput { state: Pressed, @@ -1153,7 +1153,7 @@ unsafe extern "system" fn public_window_callback( #[allow(deprecated)] subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { + event: WindowEvent::KeyboardInput_DEPRECATED { device_id: DEVICE_ID, input: KeyboardInput { state: Released, @@ -1537,7 +1537,7 @@ unsafe extern "system" fn public_window_callback( #[allow(deprecated)] subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { + event: WindowEvent::KeyboardInput_DEPRECATED { device_id: DEVICE_ID, input: KeyboardInput { scancode, @@ -1572,7 +1572,7 @@ unsafe extern "system" fn public_window_callback( #[allow(deprecated)] subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { + event: WindowEvent::KeyboardInput_DEPRECATED { device_id: DEVICE_ID, input: KeyboardInput { scancode, @@ -1994,7 +1994,7 @@ unsafe extern "system" fn thread_event_target_callback( winuser::WM_INPUT => { use crate::event::{ - DeviceEvent::{Button, Key, Motion, MouseMotion, MouseWheel}, + DeviceEvent::{Button, Key_DEPRECATED, Motion, MouseMotion, MouseWheel}, ElementState::{Pressed, Released}, MouseScrollDelta::LineDelta, }; @@ -2078,7 +2078,7 @@ unsafe extern "system" fn thread_event_target_callback( #[allow(deprecated)] subclass_input.send_event(Event::DeviceEvent { device_id, - event: Key(KeyboardInput { + event: Key_DEPRECATED(KeyboardInput { scancode, state, virtual_keycode, diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 5b3f4e1174..fee338378b 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -3,6 +3,7 @@ use winapi::{self, shared::windef::HWND}; pub use self::{ + event::{KeyEventExtra, PlatformScanCode}, event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}, icon::WinIcon, monitor::{MonitorHandle, VideoMode}, From 4b41fbde4c26b2d383832783759b70c676cb517a Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 15 Nov 2020 15:43:09 +0100 Subject: [PATCH 02/75] Implemented physical_key on Windows --- src/event.rs | 5 +- src/platform_impl/windows/event.rs | 7 +- src/platform_impl/windows/event_loop.rs | 14 ++ src/platform_impl/windows/keyboard.rs | 263 ++++++++++++++++++++++++ src/platform_impl/windows/mod.rs | 1 + 5 files changed, 284 insertions(+), 6 deletions(-) create mode 100644 src/platform_impl/windows/keyboard.rs diff --git a/src/event.rs b/src/event.rs index 4f3f0cc3f8..90a0ad0391 100644 --- a/src/event.rs +++ b/src/event.rs @@ -248,6 +248,7 @@ pub enum WindowEvent<'a> { Focused(bool), /// An event from the keyboard has been received. + // TODO: Remove this KeyboardInput_DEPRECATED { device_id: DeviceId, input: KeyboardInput, @@ -689,7 +690,7 @@ pub struct KeyEvent { pub state: keyboard_types::KeyState, pub repeat: bool, - platform_specific: platform_impl::KeyEventExtra, + pub(crate) platform_specific: platform_impl::KeyEventExtra, } // impl std::fmt::Debug for KeyEvent { @@ -801,7 +802,7 @@ impl Force { /// Hardware-dependent keyboard scan code. #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] -pub struct ScanCode (platform_impl::PlatformScanCode); +pub struct ScanCode (pub(crate) platform_impl::PlatformScanCode); pub type ScanCode_DEPRECATED = u32; diff --git a/src/platform_impl/windows/event.rs b/src/platform_impl/windows/event.rs index 40de8e2951..d6a453cfed 100644 --- a/src/platform_impl/windows/event.rs +++ b/src/platform_impl/windows/event.rs @@ -5,22 +5,21 @@ use std::{ sync::atomic::{AtomicBool, AtomicPtr, Ordering}, }; -use crate::event::{ModifiersState, ScanCode, ScanCode_DEPRECATED, VirtualKeyCode}; +use crate::event::{ModifiersState, ScanCode_DEPRECATED, VirtualKeyCode}; use winapi::{ shared::minwindef::{HKL, HKL__, LPARAM, UINT, WPARAM}, um::winuser, }; +pub use super::keyboard::PlatformScanCode; + #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEventExtra { pub char_with_all_modifers: Option, pub key_without_modifers: keyboard_types::Key, } -#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] -pub struct PlatformScanCode(pub u32); - fn key_pressed(vkey: c_int) -> bool { unsafe { (winuser::GetKeyState(vkey) & (1 << 15)) == (1 << 15) } } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index ca55dcf802..657fe98e99 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -40,6 +40,7 @@ use crate::{ dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling}, drop_handler::FileDropHandler, event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, + keyboard::{build_key_event, destructure_key_lparam}, monitor::{self, MonitorHandle}, raw_input, util, window_state::{CursorFlags, WindowFlags, WindowState}, @@ -1132,6 +1133,19 @@ unsafe extern "system" fn public_window_callback( is_synthetic: false, }, }); + + let lparam_struct = destructure_key_lparam(lparam); + let key_event = build_key_event(wparam as _, lparam_struct, keyboard_types::KeyState::Down); + #[allow(deprecated)] + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: WindowEvent::KeyboardInput { + device_id: DEVICE_ID, + event: key_event, + is_synthetic: false, + }, + }); + // Windows doesn't emit a delete character by default, but in order to make it // consistent with the other platforms we'll emit a delete character here. if vkey == Some(VirtualKeyCode::Delete) { diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs new file mode 100644 index 0000000000..5481eb53ab --- /dev/null +++ b/src/platform_impl/windows/keyboard.rs @@ -0,0 +1,263 @@ + +use std::os::raw::c_int; + +use winapi::{ + shared::minwindef::LPARAM, + um::winuser, +}; + +use crate::{ + event::{KeyEvent, ScanCode}, + platform_impl::platform::event::KeyEventExtra, +}; + +#[derive(Debug, Copy, Clone)] +pub struct KeyLParam { + pub scancode: u8, + pub extended: bool, + + /// This is `previous_state XOR transition_state` see the lParam for WM_KEYDOWN and WM_KEYUP. + pub is_repeat: bool, +} + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] +pub struct PlatformScanCode(pub u16); + +impl PlatformScanCode { + pub fn new(scancode: u8, extended: bool) -> PlatformScanCode { + let ex_scancode = (scancode as u16) | (if extended { 0xE000 } else { 0 }); + PlatformScanCode(ex_scancode) + } +} + +pub fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { + let previous_state = (lparam >> 30) & 0x01; + let transition_state = (lparam >> 31) & 0x01; + KeyLParam { + scancode: ((lparam >> 16) & 0xFF) as u8, + extended: ((lparam >> 24) & 0x01) != 0, + is_repeat: (previous_state ^ transition_state) != 0, + } +} + +pub fn build_key_event( + vkey: i32, + lparam: KeyLParam, + state: keyboard_types::KeyState +) -> KeyEvent { + let scancode = PlatformScanCode::new(lparam.scancode, lparam.extended); + + let physical_key = native_key_to_code(scancode); + + KeyEvent { + scancode: ScanCode(scancode), + location: get_location(vkey, lparam.extended, physical_key), + physical_key, + logical_key: keyboard_types::Key::Unidentified, + state, + repeat: lparam.is_repeat, + platform_specific: KeyEventExtra { + char_with_all_modifers: None, + key_without_modifers: keyboard_types::Key::Unidentified + }, + } +} + +pub fn native_key_to_code(scancode: PlatformScanCode) -> keyboard_types::Code { + // See: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html + // and: https://www.w3.org/TR/uievents-code/ + // and: The widget/NativeKeyToDOMCodeName.h file in the firefox source + + use keyboard_types::Code; + + match scancode.0 { + 0x0029 => Code::Backquote, + 0x002B => Code::Backslash, + 0x000E => Code::Backspace, + 0x001A => Code::BracketLeft, + 0x001B => Code::BracketRight, + 0x0033 => Code::Comma, + 0x000B => Code::Digit0, + 0x0002 => Code::Digit1, + 0x0003 => Code::Digit2, + 0x0004 => Code::Digit3, + 0x0005 => Code::Digit4, + 0x0006 => Code::Digit5, + 0x0007 => Code::Digit6, + 0x0008 => Code::Digit7, + 0x0009 => Code::Digit8, + 0x000A => Code::Digit9, + 0x000D => Code::Equal, + 0x0056 => Code::IntlBackslash, + 0x0073 => Code::IntlRo, + 0x007D => Code::IntlYen, + 0x001E => Code::KeyA, + 0x0030 => Code::KeyB, + 0x002E => Code::KeyC, + 0x0020 => Code::KeyD, + 0x0012 => Code::KeyE, + 0x0021 => Code::KeyF, + 0x0022 => Code::KeyG, + 0x0023 => Code::KeyH, + 0x0017 => Code::KeyI, + 0x0024 => Code::KeyJ, + 0x0025 => Code::KeyK, + 0x0026 => Code::KeyL, + 0x0032 => Code::KeyM, + 0x0031 => Code::KeyN, + 0x0018 => Code::KeyO, + 0x0019 => Code::KeyP, + 0x0010 => Code::KeyQ, + 0x0013 => Code::KeyR, + 0x001F => Code::KeyS, + 0x0014 => Code::KeyT, + 0x0016 => Code::KeyU, + 0x002F => Code::KeyV, + 0x0011 => Code::KeyW, + 0x002D => Code::KeyX, + 0x0015 => Code::KeyY, + 0x002C => Code::KeyZ, + 0x000C => Code::Minus, + 0x0034 => Code::Period, + 0x0028 => Code::Quote, + 0x0027 => Code::Semicolon, + 0x0035 => Code::Slash, + 0x0038 => Code::AltLeft, + 0xE038 => Code::AltRight, + 0x003A => Code::CapsLock, + 0xE05D => Code::ContextMenu, + 0x001D => Code::ControlLeft, + 0xE01D => Code::ControlRight, + 0x001C => Code::Enter, + //0xE05B => Code::OSLeft, + //0xE05C => Code::OSRight, + 0x002A => Code::ShiftLeft, + 0x0036 => Code::ShiftRight, + 0x0039 => Code::Space, + 0x000F => Code::Tab, + 0x0079 => Code::Convert, + 0x0072 => Code::Lang1, // for non-Korean layout + 0xE0F2 => Code::Lang1, // for Korean layout + 0x0071 => Code::Lang2, // for non-Korean layout + 0xE0F1 => Code::Lang2, // for Korean layout + 0x0070 => Code::KanaMode, + 0x007B => Code::NonConvert, + 0xE053 => Code::Delete, + 0xE04F => Code::End, + 0xE047 => Code::Home, + 0xE052 => Code::Insert, + 0xE051 => Code::PageDown, + 0xE049 => Code::PageUp, + 0xE050 => Code::ArrowDown, + 0xE04B => Code::ArrowLeft, + 0xE04D => Code::ArrowRight, + 0xE048 => Code::ArrowUp, + 0xE045 => Code::NumLock, + 0x0052 => Code::Numpad0, + 0x004F => Code::Numpad1, + 0x0050 => Code::Numpad2, + 0x0051 => Code::Numpad3, + 0x004B => Code::Numpad4, + 0x004C => Code::Numpad5, + 0x004D => Code::Numpad6, + 0x0047 => Code::Numpad7, + 0x0048 => Code::Numpad8, + 0x0049 => Code::Numpad9, + 0x004E => Code::NumpadAdd, + 0x007E => Code::NumpadComma, + 0x0053 => Code::NumpadDecimal, + 0xE035 => Code::NumpadDivide, + 0xE01C => Code::NumpadEnter, + 0x0059 => Code::NumpadEqual, + 0x0037 => Code::NumpadMultiply, + 0x004A => Code::NumpadSubtract, + 0x0001 => Code::Escape, + 0x003B => Code::F1, + 0x003C => Code::F2, + 0x003D => Code::F3, + 0x003E => Code::F4, + 0x003F => Code::F5, + 0x0040 => Code::F6, + 0x0041 => Code::F7, + 0x0042 => Code::F8, + 0x0043 => Code::F9, + 0x0044 => Code::F10, + 0x0057 => Code::F11, + 0x0058 => Code::F12, + // TODO: Add these when included in keyboard-types + // 0x0064 => Code::F13, + // 0x0065 => Code::F14, + // 0x0066 => Code::F15, + // 0x0067 => Code::F16, + // 0x0068 => Code::F17, + // 0x0069 => Code::F18, + // 0x006A => Code::F19, + // 0x006B => Code::F20, + // 0x006C => Code::F21, + // 0x006D => Code::F22, + // 0x006E => Code::F23, + // 0x0076 => Code::F24, + 0xE037 => Code::PrintScreen, + 0x0054 => Code::PrintScreen, // Alt + PrintScreen + 0x0046 => Code::ScrollLock, + 0x0045 => Code::Pause, + 0xE046 => Code::Pause, // Ctrl + Pause + 0xE06A => Code::BrowserBack, + 0xE066 => Code::BrowserFavorites, + 0xE069 => Code::BrowserForward, + 0xE032 => Code::BrowserHome, + 0xE067 => Code::BrowserRefresh, + 0xE065 => Code::BrowserSearch, + 0xE068 => Code::BrowserStop, + 0xE06B => Code::LaunchApp1, + 0xE021 => Code::LaunchApp2, + 0xE06C => Code::LaunchMail, + 0xE022 => Code::MediaPlayPause, + 0xE06D => Code::MediaSelect, + 0xE024 => Code::MediaStop, + 0xE019 => Code::MediaTrackNext, + 0xE010 => Code::MediaTrackPrevious, + 0xE05E => Code::Power, + 0xE02E => Code::AudioVolumeDown, + 0xE020 => Code::AudioVolumeMute, + 0xE030 => Code::AudioVolumeUp, + _ => Code::Unidentified, + } +} + +pub fn get_location(vkey: c_int, extended: bool, code: keyboard_types::Code) -> keyboard_types::Location { + use winuser::*; + use keyboard_types::{Code, Location}; + const VK_ABNT_C2: c_int = 0xc2; + + // Use the native VKEY and the extended flag to cover most cases + // This is taken from the `druid` software within + // druid-shell/src/platform/windows/keyboard.rs + match vkey { + VK_LSHIFT | VK_LCONTROL | VK_LMENU | VK_LWIN => return Location::Left, + VK_RSHIFT | VK_RCONTROL | VK_RMENU | VK_RWIN => return Location::Right, + VK_RETURN if extended => return Location::Numpad, + VK_INSERT | VK_DELETE | VK_END | VK_DOWN | VK_NEXT | VK_LEFT | VK_CLEAR | VK_RIGHT + | VK_HOME | VK_UP | VK_PRIOR => { + if extended { + return Location::Standard; + } else { + return Location::Numpad; + } + } + VK_NUMPAD0 | VK_NUMPAD1 | VK_NUMPAD2 | VK_NUMPAD3 | VK_NUMPAD4 | VK_NUMPAD5 + | VK_NUMPAD6 | VK_NUMPAD7 | VK_NUMPAD8 | VK_NUMPAD9 | VK_DECIMAL | VK_DIVIDE + | VK_MULTIPLY | VK_SUBTRACT | VK_ADD | VK_ABNT_C2 => return Location::Numpad, + _ => (), + } + + match code { + Code::NumpadAdd => Location::Numpad, + Code::NumpadSubtract => Location::Numpad, + Code::NumpadMultiply => Location::Numpad, + Code::NumpadDivide => Location::Numpad, + Code::NumpadComma => Location::Numpad, + Code::NumpadDecimal => Location::Numpad, + _ => Location::Standard, + } +} diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index fee338378b..2198e49ec1 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -95,6 +95,7 @@ mod drop_handler; mod event; mod event_loop; mod icon; +mod keyboard; mod monitor; mod raw_input; mod window; From 2d9e144b91ad766d546fe434d6a13c21f3608075 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 15 Nov 2020 15:45:11 +0100 Subject: [PATCH 03/75] Ran cargo fmt --- src/event.rs | 20 ++++++------- src/platform_impl/windows/event_loop.rs | 3 +- src/platform_impl/windows/keyboard.rs | 38 +++++++++++-------------- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/event.rs b/src/event.rs index 90a0ad0391..1dbd254e5b 100644 --- a/src/event.rs +++ b/src/event.rs @@ -496,9 +496,9 @@ impl<'a> WindowEvent<'a> { event, is_synthetic, } => Some(KeyboardInput { - device_id: device_id, - event: event, - is_synthetic: is_synthetic, + device_id, + event, + is_synthetic, }), ModifiersChanged(modifiers) => Some(ModifiersChanged(modifiers)), #[allow(deprecated)] @@ -664,15 +664,15 @@ pub struct KeyEvent { /// Represents the position of a key independent of the /// currently active layout. /// Conforms to https://www.w3.org/TR/uievents-code/ - /// + /// /// Note that `Fn` and `FnLock` key events are not emmited by `winit`. /// These keys are usually handled at the hardware or at the OS level. pub physical_key: keyboard_types::Code, - + /// This value is affected by all modifiers except Ctrl. - /// + /// /// This is suitable for text input in a GUI application. - /// + /// /// Note that the `Unicode` variant may contain multiple characters. /// For example on Windows when pressing ^ using /// a US-International layout, this will be `Dead` for the first @@ -685,11 +685,11 @@ pub struct KeyEvent { /// - **Web:** Dead keys might be reported as the real key instead /// of `Dead` depending on the browser/OS. pub logical_key: keyboard_types::Key, - + pub location: keyboard_types::Location, pub state: keyboard_types::KeyState, pub repeat: bool, - + pub(crate) platform_specific: platform_impl::KeyEventExtra, } @@ -802,7 +802,7 @@ impl Force { /// Hardware-dependent keyboard scan code. #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] -pub struct ScanCode (pub(crate) platform_impl::PlatformScanCode); +pub struct ScanCode(pub(crate) platform_impl::PlatformScanCode); pub type ScanCode_DEPRECATED = u32; diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 657fe98e99..283c355c89 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1135,7 +1135,8 @@ unsafe extern "system" fn public_window_callback( }); let lparam_struct = destructure_key_lparam(lparam); - let key_event = build_key_event(wparam as _, lparam_struct, keyboard_types::KeyState::Down); + let key_event = + build_key_event(wparam as _, lparam_struct, keyboard_types::KeyState::Down); #[allow(deprecated)] subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 5481eb53ab..a5aa6c18d7 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -1,10 +1,6 @@ - use std::os::raw::c_int; -use winapi::{ - shared::minwindef::LPARAM, - um::winuser, -}; +use winapi::{shared::minwindef::LPARAM, um::winuser}; use crate::{ event::{KeyEvent, ScanCode}, @@ -40,11 +36,7 @@ pub fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { } } -pub fn build_key_event( - vkey: i32, - lparam: KeyLParam, - state: keyboard_types::KeyState -) -> KeyEvent { +pub fn build_key_event(vkey: i32, lparam: KeyLParam, state: keyboard_types::KeyState) -> KeyEvent { let scancode = PlatformScanCode::new(lparam.scancode, lparam.extended); let physical_key = native_key_to_code(scancode); @@ -58,7 +50,7 @@ pub fn build_key_event( repeat: lparam.is_repeat, platform_specific: KeyEventExtra { char_with_all_modifers: None, - key_without_modifers: keyboard_types::Key::Unidentified + key_without_modifers: keyboard_types::Key::Unidentified, }, } } @@ -69,7 +61,7 @@ pub fn native_key_to_code(scancode: PlatformScanCode) -> keyboard_types::Code { // and: The widget/NativeKeyToDOMCodeName.h file in the firefox source use keyboard_types::Code; - + match scancode.0 { 0x0029 => Code::Backquote, 0x002B => Code::Backslash, @@ -136,10 +128,10 @@ pub fn native_key_to_code(scancode: PlatformScanCode) -> keyboard_types::Code { 0x0039 => Code::Space, 0x000F => Code::Tab, 0x0079 => Code::Convert, - 0x0072 => Code::Lang1, // for non-Korean layout - 0xE0F2 => Code::Lang1, // for Korean layout - 0x0071 => Code::Lang2, // for non-Korean layout - 0xE0F1 => Code::Lang2, // for Korean layout + 0x0072 => Code::Lang1, // for non-Korean layout + 0xE0F2 => Code::Lang1, // for Korean layout + 0x0071 => Code::Lang2, // for non-Korean layout + 0xE0F1 => Code::Lang2, // for Korean layout 0x0070 => Code::KanaMode, 0x007B => Code::NonConvert, 0xE053 => Code::Delete, @@ -198,10 +190,10 @@ pub fn native_key_to_code(scancode: PlatformScanCode) -> keyboard_types::Code { // 0x006E => Code::F23, // 0x0076 => Code::F24, 0xE037 => Code::PrintScreen, - 0x0054 => Code::PrintScreen, // Alt + PrintScreen + 0x0054 => Code::PrintScreen, // Alt + PrintScreen 0x0046 => Code::ScrollLock, 0x0045 => Code::Pause, - 0xE046 => Code::Pause, // Ctrl + Pause + 0xE046 => Code::Pause, // Ctrl + Pause 0xE06A => Code::BrowserBack, 0xE066 => Code::BrowserFavorites, 0xE069 => Code::BrowserForward, @@ -225,13 +217,17 @@ pub fn native_key_to_code(scancode: PlatformScanCode) -> keyboard_types::Code { } } -pub fn get_location(vkey: c_int, extended: bool, code: keyboard_types::Code) -> keyboard_types::Location { - use winuser::*; +pub fn get_location( + vkey: c_int, + extended: bool, + code: keyboard_types::Code, +) -> keyboard_types::Location { use keyboard_types::{Code, Location}; + use winuser::*; const VK_ABNT_C2: c_int = 0xc2; // Use the native VKEY and the extended flag to cover most cases - // This is taken from the `druid` software within + // This is taken from the `druid` software within // druid-shell/src/platform/windows/keyboard.rs match vkey { VK_LSHIFT | VK_LCONTROL | VK_LMENU | VK_LWIN => return Location::Left, From 355692fc8aef2d143d81379f81c3d1c1a4a7575d Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 5 Dec 2020 15:10:17 +0100 Subject: [PATCH 04/75] Progress with the keyboard's windows implementation --- Cargo.toml | 1 + src/platform_impl/windows/event_loop.rs | 40 +- src/platform_impl/windows/keyboard.rs | 597 +++++++++++++++++++++- src/platform_impl/windows/window_state.rs | 6 +- 4 files changed, 610 insertions(+), 34 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5e6c4dcd6f..b66e65bc20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,6 +80,7 @@ features = [ "winerror", "wingdi", "winnt", + "winnls", "winuser", ] diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 283c355c89..4b943b54f7 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -763,6 +763,33 @@ unsafe extern "system" fn public_window_callback( winuser::RDW_INTERNALPAINT, ); + let keyboard_callback = || { + use crate::event::WindowEvent::KeyboardInput; + let mut retval = 0; + let mut window_state = subclass_input.window_state.lock(); + let event = window_state.key_event_builder.process_message(window, msg, wparam, lparam, &mut retval); + if let Some(event) = event { + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: KeyboardInput { + device_id: DEVICE_ID, + event: event, + is_synthetic: false, + }, + }); + } + retval + }; + + let retval = subclass_input + .event_loop_runner + .catch_unwind(keyboard_callback) + .unwrap_or(-1); + + if retval == -1 { + return retval; + } + // I decided to bind the closure to `callback` and pass it to catch_unwind rather than passing // the closure to catch_unwind directly so that the match body indendation wouldn't change and // the git blame and history would be preserved. @@ -1134,19 +1161,6 @@ unsafe extern "system" fn public_window_callback( }, }); - let lparam_struct = destructure_key_lparam(lparam); - let key_event = - build_key_event(wparam as _, lparam_struct, keyboard_types::KeyState::Down); - #[allow(deprecated)] - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - event: key_event, - is_synthetic: false, - }, - }); - // Windows doesn't emit a delete character by default, but in order to make it // consistent with the other platforms we'll emit a delete character here. if vkey == Some(VirtualKeyCode::Delete) { diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index a5aa6c18d7..f33888682c 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -1,12 +1,209 @@ -use std::os::raw::c_int; +use std::{os::raw::c_int, fmt, collections::HashMap}; -use winapi::{shared::minwindef::LPARAM, um::winuser}; +use winapi::{shared::{minwindef::{LRESULT, LPARAM, WPARAM}, windef::HWND}, um::winuser, um::winnls}; + +use std::{char, mem::MaybeUninit, ffi::OsString, os::windows::ffi::OsStringExt}; use crate::{ event::{KeyEvent, ScanCode}, platform_impl::platform::event::KeyEventExtra, }; +const VIRTUAL_KEYS: &'static [i32] = &[ + winuser::VK_LBUTTON, + winuser::VK_RBUTTON, + winuser::VK_CANCEL, + winuser::VK_MBUTTON, + winuser::VK_XBUTTON1, + winuser::VK_XBUTTON2, + winuser::VK_BACK, + winuser::VK_TAB, + winuser::VK_CLEAR, + winuser::VK_RETURN, + winuser::VK_SHIFT, + winuser::VK_CONTROL, + winuser::VK_MENU, + winuser::VK_PAUSE, + winuser::VK_CAPITAL, + winuser::VK_KANA, + winuser::VK_HANGEUL, + winuser::VK_HANGUL, + winuser::VK_JUNJA, + winuser::VK_FINAL, + winuser::VK_HANJA, + winuser::VK_KANJI, + winuser::VK_ESCAPE, + winuser::VK_CONVERT, + winuser::VK_NONCONVERT, + winuser::VK_ACCEPT, + winuser::VK_MODECHANGE, + winuser::VK_SPACE, + winuser::VK_PRIOR, + winuser::VK_NEXT, + winuser::VK_END, + winuser::VK_HOME, + winuser::VK_LEFT, + winuser::VK_UP, + winuser::VK_RIGHT, + winuser::VK_DOWN, + winuser::VK_SELECT, + winuser::VK_PRINT, + winuser::VK_EXECUTE, + winuser::VK_SNAPSHOT, + winuser::VK_INSERT, + winuser::VK_DELETE, + winuser::VK_HELP, + winuser::VK_LWIN, + winuser::VK_RWIN, + winuser::VK_APPS, + winuser::VK_SLEEP, + winuser::VK_NUMPAD0, + winuser::VK_NUMPAD1, + winuser::VK_NUMPAD2, + winuser::VK_NUMPAD3, + winuser::VK_NUMPAD4, + winuser::VK_NUMPAD5, + winuser::VK_NUMPAD6, + winuser::VK_NUMPAD7, + winuser::VK_NUMPAD8, + winuser::VK_NUMPAD9, + winuser::VK_MULTIPLY, + winuser::VK_ADD, + winuser::VK_SEPARATOR, + winuser::VK_SUBTRACT, + winuser::VK_DECIMAL, + winuser::VK_DIVIDE, + winuser::VK_F1, + winuser::VK_F2, + winuser::VK_F3, + winuser::VK_F4, + winuser::VK_F5, + winuser::VK_F6, + winuser::VK_F7, + winuser::VK_F8, + winuser::VK_F9, + winuser::VK_F10, + winuser::VK_F11, + winuser::VK_F12, + winuser::VK_F13, + winuser::VK_F14, + winuser::VK_F15, + winuser::VK_F16, + winuser::VK_F17, + winuser::VK_F18, + winuser::VK_F19, + winuser::VK_F20, + winuser::VK_F21, + winuser::VK_F22, + winuser::VK_F23, + winuser::VK_F24, + winuser::VK_NAVIGATION_VIEW, + winuser::VK_NAVIGATION_MENU, + winuser::VK_NAVIGATION_UP, + winuser::VK_NAVIGATION_DOWN, + winuser::VK_NAVIGATION_LEFT, + winuser::VK_NAVIGATION_RIGHT, + winuser::VK_NAVIGATION_ACCEPT, + winuser::VK_NAVIGATION_CANCEL, + winuser::VK_NUMLOCK, + winuser::VK_SCROLL, + winuser::VK_OEM_NEC_EQUAL, + winuser::VK_OEM_FJ_JISHO, + winuser::VK_OEM_FJ_MASSHOU, + winuser::VK_OEM_FJ_TOUROKU, + winuser::VK_OEM_FJ_LOYA, + winuser::VK_OEM_FJ_ROYA, + winuser::VK_LSHIFT, + winuser::VK_RSHIFT, + winuser::VK_LCONTROL, + winuser::VK_RCONTROL, + winuser::VK_LMENU, + winuser::VK_RMENU, + winuser::VK_BROWSER_BACK, + winuser::VK_BROWSER_FORWARD, + winuser::VK_BROWSER_REFRESH, + winuser::VK_BROWSER_STOP, + winuser::VK_BROWSER_SEARCH, + winuser::VK_BROWSER_FAVORITES, + winuser::VK_BROWSER_HOME, + winuser::VK_VOLUME_MUTE, + winuser::VK_VOLUME_DOWN, + winuser::VK_VOLUME_UP, + winuser::VK_MEDIA_NEXT_TRACK, + winuser::VK_MEDIA_PREV_TRACK, + winuser::VK_MEDIA_STOP, + winuser::VK_MEDIA_PLAY_PAUSE, + winuser::VK_LAUNCH_MAIL, + winuser::VK_LAUNCH_MEDIA_SELECT, + winuser::VK_LAUNCH_APP1, + winuser::VK_LAUNCH_APP2, + winuser::VK_OEM_1, + winuser::VK_OEM_PLUS, + winuser::VK_OEM_COMMA, + winuser::VK_OEM_MINUS, + winuser::VK_OEM_PERIOD, + winuser::VK_OEM_2, + winuser::VK_OEM_3, + winuser::VK_GAMEPAD_A, + winuser::VK_GAMEPAD_B, + winuser::VK_GAMEPAD_X, + winuser::VK_GAMEPAD_Y, + winuser::VK_GAMEPAD_RIGHT_SHOULDER, + winuser::VK_GAMEPAD_LEFT_SHOULDER, + winuser::VK_GAMEPAD_LEFT_TRIGGER, + winuser::VK_GAMEPAD_RIGHT_TRIGGER, + winuser::VK_GAMEPAD_DPAD_UP, + winuser::VK_GAMEPAD_DPAD_DOWN, + winuser::VK_GAMEPAD_DPAD_LEFT, + winuser::VK_GAMEPAD_DPAD_RIGHT, + winuser::VK_GAMEPAD_MENU, + winuser::VK_GAMEPAD_VIEW, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_UP, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_DOWN, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_LEFT, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_UP, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT, + winuser::VK_OEM_4, + winuser::VK_OEM_5, + winuser::VK_OEM_6, + winuser::VK_OEM_7, + winuser::VK_OEM_8, + winuser::VK_OEM_AX, + winuser::VK_OEM_102, + winuser::VK_ICO_HELP, + winuser::VK_ICO_00, + winuser::VK_PROCESSKEY, + winuser::VK_ICO_CLEAR, + winuser::VK_PACKET, + winuser::VK_OEM_RESET, + winuser::VK_OEM_JUMP, + winuser::VK_OEM_PA1, + winuser::VK_OEM_PA2, + winuser::VK_OEM_PA3, + winuser::VK_OEM_WSCTRL, + winuser::VK_OEM_CUSEL, + winuser::VK_OEM_ATTN, + winuser::VK_OEM_FINISH, + winuser::VK_OEM_COPY, + winuser::VK_OEM_AUTO, + winuser::VK_OEM_ENLW, + winuser::VK_OEM_BACKTAB, + winuser::VK_ATTN, + winuser::VK_CRSEL, + winuser::VK_EXSEL, + winuser::VK_EREOF, + winuser::VK_PLAY, + winuser::VK_ZOOM, + winuser::VK_NONAME, + winuser::VK_PA1, + winuser::VK_OEM_CLEAR, +]; + #[derive(Debug, Copy, Clone)] pub struct KeyLParam { pub scancode: u8, @@ -16,9 +213,13 @@ pub struct KeyLParam { pub is_repeat: bool, } -#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct PlatformScanCode(pub u16); - +impl fmt::Debug for PlatformScanCode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("PlatformScanCode").field(&format_args!("0x{:04x}", self.0)).finish() + } +} impl PlatformScanCode { pub fn new(scancode: u8, extended: bool) -> PlatformScanCode { let ex_scancode = (scancode as u16) | (if extended { 0xE000 } else { 0 }); @@ -26,6 +227,372 @@ impl PlatformScanCode { } } +/// A single winint `KeyEvent` contains information which the windows API passes to the application +/// in multiple window messages. In other words a winit `KeyEvent` cannot be build from a single +/// window message. Therefore this type keeps track of certain information from previous events so +/// that a `KeyEvent` can be constructed when the last event related to a keypress is received. +/// +/// `PeekMessage` is used to determine wheter the next window message still belongs to the current +/// keypress. If it doesn't and the current state represents a key event waiting to be dispatched, +/// than said event is dispatched. +/// +/// The sequence of window messages for a key press event is the following: +/// - Exactly one WM_KEYDOWN / WM_SYSKEYDOWN +/// - Zero or one WM_DEADCHAR / WM_SYSDEADCHAR +/// - Zero or more WM_CHAR / WM_SYSCHAR. These messages each come with a UTF-16 code unit which when +/// put together in the sequence they arrived in, forms the text which is the result of pressing the +/// key. +/// +/// Key release messages are a bit different due to the fact that they don't contribute to +/// text input. The "sequence" only consists of one WM_KEYUP / WM_SYSKEYUP event. +pub struct KeyEventBuilder { + event_info: Option, + + /// This map shouldn't need to exist. + /// However currently this seems to be the only good way + /// way of getting the label for the pressed key. Note that calling `ToUnicode` + /// just when the key is pressed/released would be enough if `ToUnicode` wouldn't + /// change the keyboard state (it removes the dead key). There is a flag to prevent + /// changing the state but that flag requires Windows 10, version 1607 or newer) + key_labels: HashMap, + + /// The locale identifier (HKL) of the layout for which the `key_labels` was generated + known_locale_id: usize, + + /// The keyup event needs to call `ToUnicode` to determine key with all modifiers except CTRL + /// (the `logical_key`). + /// + /// But `ToUnicode` without the non-modifying flag (see `key_labels`), resets the dead key + /// state which would be incorrect during every keyup event. Therefore this variable is used + /// to determine whether the last keydown event produced a dead key. + /// + /// Note that this variable is not always correct because it does + /// not track key presses outside of this window. However the ONLY situation where this + /// doesn't work as intended is when the user presses a dead key outside of this window, and + /// switched to this window BEFORE releasing it then releases the dead key. In this case + /// the `ToUnicode` function will be called, incorrectly clearing the dead key state. Having + /// an inccorect behaviour in this case seems acceptable. + prev_down_was_dead: bool, +} +impl Default for KeyEventBuilder { + fn default() -> Self { + KeyEventBuilder { + event_info: None, + key_labels: HashMap::default(), + known_locale_id: 0, + prev_down_was_dead: false, + } + } +} +impl KeyEventBuilder { + /// Call this function for every window message. + /// Returns Some() if this window message completes a KeyEvent. + /// Returns None otherwise. + pub fn process_message( + &mut self, + hwnd: HWND, + msg_kind: u32, + wparam: WPARAM, + lparam: LPARAM, + retval: &mut LRESULT + ) -> Option { + match msg_kind { + winuser::WM_INPUTLANGCHANGE => { + self.generate_labels(lparam as usize); + *retval = 1; + } + winuser::WM_SETFOCUS => { + // We do not know the dead key, state so assuing that there wasn't a deadkey press + // outside of this window. + self.prev_down_was_dead = false; + + let locale_id = unsafe { winuser::GetKeyboardLayout(0) }; + self.generate_labels(locale_id as usize); + } + winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { + self.prev_down_was_dead = false; + let vkey = wparam as i32; + let lparam_struct = destructure_key_lparam(lparam); + let scancode = PlatformScanCode::new( + lparam_struct.scancode, + lparam_struct.extended + ); + let code = native_key_to_code(scancode); + let location = get_location(vkey, lparam_struct.extended, code); + self.event_info = Some(PartialKeyEventInfo { + key_state: keyboard_types::KeyState::Down, + vkey: wparam as i32, + scancode: scancode, + is_repeat: lparam_struct.is_repeat, + code: code, + location: location, + is_dead: false, + label: self.key_labels.get(&scancode).map(|s| s.clone()), + utf16parts: Vec::with_capacity(4), + utf16parts_without_ctrl: Vec::with_capacity(4), + }); + } + winuser::WM_DEADCHAR | winuser::WM_SYSDEADCHAR => { + self.prev_down_was_dead = true; + // At this point we know that there isn't going to be any more events related to + // this key press + let mut event_info = self.event_info.take().unwrap(); + event_info.is_dead = true; + return Some(event_info.finalize()); + } + winuser::WM_CHAR | winuser::WM_SYSCHAR => { + if msg_kind == winuser::WM_SYSCHAR { + *retval = 0; + } + let is_high_surrogate = 0xD800 <= wparam && wparam <= 0xDBFF; + let is_low_surrogate = 0xDC00 <= wparam && wparam <= 0xDFFF; + + let is_utf16 = is_high_surrogate || is_low_surrogate; + + let more_char_coming; + unsafe { + let mut next_msg = MaybeUninit::uninit(); + let has_message = winuser::PeekMessageW( + next_msg.as_mut_ptr(), + hwnd, + winuser::WM_KEYFIRST, + winuser::WM_KEYLAST, + winuser::PM_NOREMOVE + ); + let has_message = has_message != 0; + if !has_message { + more_char_coming = false; + } else { + let next_msg = next_msg.assume_init().message; + if next_msg == winuser::WM_CHAR || next_msg == winuser::WM_SYSCHAR { + more_char_coming = true; + } else { + more_char_coming = false; + } + } + } + + if is_utf16 { + self.event_info.as_mut().unwrap().utf16parts.push(wparam as u16); + } else { + let utf16parts = &mut self.event_info.as_mut().unwrap().utf16parts; + let start_offset = utf16parts.len(); + let new_size = utf16parts.len() + 2; + utf16parts.resize(new_size, 0); + if let Some(ch) = char::from_u32(wparam as u32) { + let encode_len = + ch.encode_utf16(&mut utf16parts[start_offset..]).len(); + let new_size = start_offset + encode_len; + utf16parts.resize(new_size, 0); + } + } + if !more_char_coming { + let event_info = self.event_info.take().unwrap(); + return Some(event_info.finalize()); + } + } + winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { + let vkey = wparam as i32; + let lparam_struct = destructure_key_lparam(lparam); + let scancode = PlatformScanCode::new( + lparam_struct.scancode, + lparam_struct.extended + ); + let code = native_key_to_code(scancode); + let location = get_location(vkey, lparam_struct.extended, code); + let mut utf16parts = Vec::with_capacity(8); + let mut utf16parts_without_ctrl = Vec::with_capacity(8); + if !self.prev_down_was_dead { + unsafe { + //let locale_id = winuser::GetKeyboardLayout(0); + let mut key_state: [MaybeUninit; 256] = [MaybeUninit::uninit(); 256]; + winuser::GetKeyboardState(key_state[0].as_mut_ptr()); + let mut key_state = std::mem::transmute::<_, [u8; 256]>(key_state); + let unicode_len = winuser::ToUnicode( + wparam as u32, + scancode.0 as u32, + (&mut key_state[0]) as *mut _, + utf16parts.as_mut_ptr(), + utf16parts.capacity() as i32, + 0 + ); + utf16parts.set_len(unicode_len as usize); + + // Now remove all CTRL stuff from the keyboard state + key_state[winuser::VK_LCONTROL as usize] = 0; + key_state[winuser::VK_RCONTROL as usize] = 0; + let unicode_len = winuser::ToUnicode( + wparam as u32, + scancode.0 as u32, + (&mut key_state[0]) as *mut _, + utf16parts_without_ctrl.as_mut_ptr(), + utf16parts_without_ctrl.capacity() as i32, + 0 + ); + utf16parts_without_ctrl.set_len(unicode_len as usize); + } + } + + let event_info = PartialKeyEventInfo { + key_state: keyboard_types::KeyState::Up, + vkey: wparam as i32, + scancode: scancode, + is_repeat: false, + code: code, + location: location, + is_dead: self.prev_down_was_dead, + label: self.key_labels.get(&scancode).map(|s| s.clone()), + utf16parts: utf16parts, + utf16parts_without_ctrl: utf16parts_without_ctrl, + }; + return Some(event_info.finalize()); + } + _ => () + } + + None + } + + /// Returns true if succeeded. + fn generate_labels(&mut self, locale_identifier: usize) -> bool { + if self.known_locale_id == locale_identifier { + return true; + } + + let mut key_state = [0u8; 256]; + self.key_labels.clear(); + let target_capacity = 128; + if self.key_labels.capacity() < target_capacity { + self.key_labels.reserve(target_capacity - self.key_labels.capacity()); + } + unsafe { + let result = winuser::GetKeyboardState((&mut key_state[0]) as *mut _); + assert!(result != 0, "`GetKeyboardState` failed"); + for &vk in VIRTUAL_KEYS { + let scancode = winuser::MapVirtualKeyExW( + vk as u32, + winuser::MAPVK_VK_TO_VSC_EX, + locale_identifier as _ + ); + let platform_scancode = PlatformScanCode(scancode as u16); + + let mut label_wide = [0u16; 8]; + let wide_len = winuser::ToUnicodeEx( + vk as u32, + scancode, + (&mut key_state[0]) as *mut _, + (&mut label_wide[0]) as *mut _, + label_wide.len() as i32, + 0, + locale_identifier as _ + ); + if wide_len > 0 { + let label_str = OsString::from_wide(&label_wide[0..wide_len as usize]).into_string().unwrap(); + self.key_labels.insert(platform_scancode, label_str); + } + } + } + true + } +} + +struct PartialKeyEventInfo { + key_state: keyboard_types::KeyState, + /// The native Virtual Key + vkey: i32, + scancode: PlatformScanCode, + is_repeat: bool, + code: keyboard_types::Code, + location: keyboard_types::Location, + /// True if the key event corresponds to a dead key input + is_dead: bool, + label: Option, + + /// The utf16 code units of the text that was produced by the keypress event. + /// This take all modifiers into account. Including CTRL + utf16parts: Vec, + + utf16parts_without_ctrl: Vec, +} + +impl PartialKeyEventInfo { + fn finalize(self) -> KeyEvent { + use keyboard_types::Key; + + let logical_key; + if self.is_dead { + logical_key = Key::Dead; + } else { + // TODO: Check if the vkey indicates a printable key. If it does AND if there's no dead + // key active (this can be detected by passing `MAPVK_VK_TO_CHAR` to `MapVirtualKeyExW`) + // than we are safe to call ToUnicodeEx because it only resets the dead key state but + // that does not matter if there are no active dead keys anyways. + if self.utf16parts_without_ctrl.len() > 0 { + logical_key = + Key::Character(String::from_utf16(&self.utf16parts_without_ctrl).unwrap()); + } else { + logical_key = Key::Unidentified; + } + } + + let key_without_modifers; + match &logical_key { + Key::Character(_) => { + if let Some(label) = self.label { + key_without_modifers = Key::Character(label); + } else { + key_without_modifers = Key::Unidentified; + } + } + _ => { + key_without_modifers = logical_key.clone(); + } + } + + let mut char_with_all_modifiers = None; + if !self.utf16parts.is_empty() { + char_with_all_modifiers = Some(String::from_utf16(&self.utf16parts).unwrap()); + } + + KeyEvent { + scancode: ScanCode(self.scancode), + physical_key: self.code, + logical_key: logical_key, + location: self.location, + state: self.key_state, + repeat: self.is_repeat, + platform_specific: KeyEventExtra { + char_with_all_modifers: char_with_all_modifiers, + key_without_modifers: key_without_modifers, + }, + } + } +} + +fn ToUnicodeString( + vkey: u32, + scancode: u16, + key_state: *mut u8, + locale_identifier: usize +) -> Option { + let mut label_wide = [0u16; 8]; + let wide_len = unsafe { winuser::ToUnicodeEx( + vkey, + scancode as u32, + key_state, + (&mut label_wide[0]) as *mut _, + label_wide.len() as i32, + 0, + locale_identifier as _ + ) }; + if wide_len > 0 { + let label_str = OsString::from_wide(&label_wide[0..wide_len as usize]).into_string().unwrap(); + Some(label_str) + } else { + None + } +} + pub fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { let previous_state = (lparam >> 30) & 0x01; let transition_state = (lparam >> 31) & 0x01; @@ -230,30 +797,20 @@ pub fn get_location( // This is taken from the `druid` software within // druid-shell/src/platform/windows/keyboard.rs match vkey { - VK_LSHIFT | VK_LCONTROL | VK_LMENU | VK_LWIN => return Location::Left, - VK_RSHIFT | VK_RCONTROL | VK_RMENU | VK_RWIN => return Location::Right, - VK_RETURN if extended => return Location::Numpad, + VK_LSHIFT | VK_LCONTROL | VK_LMENU | VK_LWIN => Location::Left, + VK_RSHIFT | VK_RCONTROL | VK_RMENU | VK_RWIN => Location::Right, + VK_RETURN if extended => Location::Numpad, VK_INSERT | VK_DELETE | VK_END | VK_DOWN | VK_NEXT | VK_LEFT | VK_CLEAR | VK_RIGHT | VK_HOME | VK_UP | VK_PRIOR => { if extended { - return Location::Standard; + Location::Standard } else { - return Location::Numpad; + Location::Numpad } } VK_NUMPAD0 | VK_NUMPAD1 | VK_NUMPAD2 | VK_NUMPAD3 | VK_NUMPAD4 | VK_NUMPAD5 | VK_NUMPAD6 | VK_NUMPAD7 | VK_NUMPAD8 | VK_NUMPAD9 | VK_DECIMAL | VK_DIVIDE - | VK_MULTIPLY | VK_SUBTRACT | VK_ADD | VK_ABNT_C2 => return Location::Numpad, - _ => (), - } - - match code { - Code::NumpadAdd => Location::Numpad, - Code::NumpadSubtract => Location::Numpad, - Code::NumpadMultiply => Location::Numpad, - Code::NumpadDivide => Location::Numpad, - Code::NumpadComma => Location::Numpad, - Code::NumpadDecimal => Location::Numpad, + | VK_MULTIPLY | VK_SUBTRACT | VK_ADD | VK_ABNT_C2 => Location::Numpad, _ => Location::Standard, } } diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 1369fe7210..f04cae351c 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -2,7 +2,7 @@ use crate::{ dpi::{PhysicalPosition, Size}, event::ModifiersState, icon::Icon, - platform_impl::platform::{event_loop, util}, + platform_impl::platform::{event_loop, util, keyboard::KeyEventBuilder}, window::{CursorIcon, Fullscreen, Theme, WindowAttributes}, }; use parking_lot::MutexGuard; @@ -34,6 +34,9 @@ pub struct WindowState { pub current_theme: Theme, pub preferred_theme: Option, pub high_surrogate: Option, + + pub key_event_builder: KeyEventBuilder, + window_flags: WindowFlags, } @@ -127,6 +130,7 @@ impl WindowState { current_theme, preferred_theme, high_surrogate: None, + key_event_builder: KeyEventBuilder::default(), window_flags: WindowFlags::empty(), } } From 62bbb46ba3f0b647dfbdaeb2c41993862329be1f Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Thu, 26 Nov 2020 23:20:27 +0100 Subject: [PATCH 05/75] Add proper handling of dead keys --- src/platform_impl/windows/event_loop.rs | 13 +- src/platform_impl/windows/keyboard.rs | 665 +++++++++++++----------- 2 files changed, 372 insertions(+), 306 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 4b943b54f7..829c51eea2 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -40,7 +40,7 @@ use crate::{ dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling}, drop_handler::FileDropHandler, event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, - keyboard::{build_key_event, destructure_key_lparam}, + keyboard::is_msg_keyboard_related, monitor::{self, MonitorHandle}, raw_input, util, window_state::{CursorFlags, WindowFlags, WindowState}, @@ -765,6 +765,12 @@ unsafe extern "system" fn public_window_callback( let keyboard_callback = || { use crate::event::WindowEvent::KeyboardInput; + let is_keyboard_related = is_msg_keyboard_related(msg); + if !is_keyboard_related { + // We return early to avoid a deadlock from locking the window state + // when not appropriate. + return -1; + } let mut retval = 0; let mut window_state = subclass_input.window_state.lock(); let event = window_state.key_event_builder.process_message(window, msg, wparam, lparam, &mut retval); @@ -781,15 +787,12 @@ unsafe extern "system" fn public_window_callback( retval }; + // TODO: merge this retval with whatever is done in the following. let retval = subclass_input .event_loop_runner .catch_unwind(keyboard_callback) .unwrap_or(-1); - if retval == -1 { - return retval; - } - // I decided to bind the closure to `callback` and pass it to catch_unwind rather than passing // the closure to catch_unwind directly so that the match body indendation wouldn't change and // the git blame and history would be preserved. diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index f33888682c..5f5e5b008a 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -9,200 +9,12 @@ use crate::{ platform_impl::platform::event::KeyEventExtra, }; -const VIRTUAL_KEYS: &'static [i32] = &[ - winuser::VK_LBUTTON, - winuser::VK_RBUTTON, - winuser::VK_CANCEL, - winuser::VK_MBUTTON, - winuser::VK_XBUTTON1, - winuser::VK_XBUTTON2, - winuser::VK_BACK, - winuser::VK_TAB, - winuser::VK_CLEAR, - winuser::VK_RETURN, - winuser::VK_SHIFT, - winuser::VK_CONTROL, - winuser::VK_MENU, - winuser::VK_PAUSE, - winuser::VK_CAPITAL, - winuser::VK_KANA, - winuser::VK_HANGEUL, - winuser::VK_HANGUL, - winuser::VK_JUNJA, - winuser::VK_FINAL, - winuser::VK_HANJA, - winuser::VK_KANJI, - winuser::VK_ESCAPE, - winuser::VK_CONVERT, - winuser::VK_NONCONVERT, - winuser::VK_ACCEPT, - winuser::VK_MODECHANGE, - winuser::VK_SPACE, - winuser::VK_PRIOR, - winuser::VK_NEXT, - winuser::VK_END, - winuser::VK_HOME, - winuser::VK_LEFT, - winuser::VK_UP, - winuser::VK_RIGHT, - winuser::VK_DOWN, - winuser::VK_SELECT, - winuser::VK_PRINT, - winuser::VK_EXECUTE, - winuser::VK_SNAPSHOT, - winuser::VK_INSERT, - winuser::VK_DELETE, - winuser::VK_HELP, - winuser::VK_LWIN, - winuser::VK_RWIN, - winuser::VK_APPS, - winuser::VK_SLEEP, - winuser::VK_NUMPAD0, - winuser::VK_NUMPAD1, - winuser::VK_NUMPAD2, - winuser::VK_NUMPAD3, - winuser::VK_NUMPAD4, - winuser::VK_NUMPAD5, - winuser::VK_NUMPAD6, - winuser::VK_NUMPAD7, - winuser::VK_NUMPAD8, - winuser::VK_NUMPAD9, - winuser::VK_MULTIPLY, - winuser::VK_ADD, - winuser::VK_SEPARATOR, - winuser::VK_SUBTRACT, - winuser::VK_DECIMAL, - winuser::VK_DIVIDE, - winuser::VK_F1, - winuser::VK_F2, - winuser::VK_F3, - winuser::VK_F4, - winuser::VK_F5, - winuser::VK_F6, - winuser::VK_F7, - winuser::VK_F8, - winuser::VK_F9, - winuser::VK_F10, - winuser::VK_F11, - winuser::VK_F12, - winuser::VK_F13, - winuser::VK_F14, - winuser::VK_F15, - winuser::VK_F16, - winuser::VK_F17, - winuser::VK_F18, - winuser::VK_F19, - winuser::VK_F20, - winuser::VK_F21, - winuser::VK_F22, - winuser::VK_F23, - winuser::VK_F24, - winuser::VK_NAVIGATION_VIEW, - winuser::VK_NAVIGATION_MENU, - winuser::VK_NAVIGATION_UP, - winuser::VK_NAVIGATION_DOWN, - winuser::VK_NAVIGATION_LEFT, - winuser::VK_NAVIGATION_RIGHT, - winuser::VK_NAVIGATION_ACCEPT, - winuser::VK_NAVIGATION_CANCEL, - winuser::VK_NUMLOCK, - winuser::VK_SCROLL, - winuser::VK_OEM_NEC_EQUAL, - winuser::VK_OEM_FJ_JISHO, - winuser::VK_OEM_FJ_MASSHOU, - winuser::VK_OEM_FJ_TOUROKU, - winuser::VK_OEM_FJ_LOYA, - winuser::VK_OEM_FJ_ROYA, - winuser::VK_LSHIFT, - winuser::VK_RSHIFT, - winuser::VK_LCONTROL, - winuser::VK_RCONTROL, - winuser::VK_LMENU, - winuser::VK_RMENU, - winuser::VK_BROWSER_BACK, - winuser::VK_BROWSER_FORWARD, - winuser::VK_BROWSER_REFRESH, - winuser::VK_BROWSER_STOP, - winuser::VK_BROWSER_SEARCH, - winuser::VK_BROWSER_FAVORITES, - winuser::VK_BROWSER_HOME, - winuser::VK_VOLUME_MUTE, - winuser::VK_VOLUME_DOWN, - winuser::VK_VOLUME_UP, - winuser::VK_MEDIA_NEXT_TRACK, - winuser::VK_MEDIA_PREV_TRACK, - winuser::VK_MEDIA_STOP, - winuser::VK_MEDIA_PLAY_PAUSE, - winuser::VK_LAUNCH_MAIL, - winuser::VK_LAUNCH_MEDIA_SELECT, - winuser::VK_LAUNCH_APP1, - winuser::VK_LAUNCH_APP2, - winuser::VK_OEM_1, - winuser::VK_OEM_PLUS, - winuser::VK_OEM_COMMA, - winuser::VK_OEM_MINUS, - winuser::VK_OEM_PERIOD, - winuser::VK_OEM_2, - winuser::VK_OEM_3, - winuser::VK_GAMEPAD_A, - winuser::VK_GAMEPAD_B, - winuser::VK_GAMEPAD_X, - winuser::VK_GAMEPAD_Y, - winuser::VK_GAMEPAD_RIGHT_SHOULDER, - winuser::VK_GAMEPAD_LEFT_SHOULDER, - winuser::VK_GAMEPAD_LEFT_TRIGGER, - winuser::VK_GAMEPAD_RIGHT_TRIGGER, - winuser::VK_GAMEPAD_DPAD_UP, - winuser::VK_GAMEPAD_DPAD_DOWN, - winuser::VK_GAMEPAD_DPAD_LEFT, - winuser::VK_GAMEPAD_DPAD_RIGHT, - winuser::VK_GAMEPAD_MENU, - winuser::VK_GAMEPAD_VIEW, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_UP, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_DOWN, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_LEFT, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_UP, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT, - winuser::VK_OEM_4, - winuser::VK_OEM_5, - winuser::VK_OEM_6, - winuser::VK_OEM_7, - winuser::VK_OEM_8, - winuser::VK_OEM_AX, - winuser::VK_OEM_102, - winuser::VK_ICO_HELP, - winuser::VK_ICO_00, - winuser::VK_PROCESSKEY, - winuser::VK_ICO_CLEAR, - winuser::VK_PACKET, - winuser::VK_OEM_RESET, - winuser::VK_OEM_JUMP, - winuser::VK_OEM_PA1, - winuser::VK_OEM_PA2, - winuser::VK_OEM_PA3, - winuser::VK_OEM_WSCTRL, - winuser::VK_OEM_CUSEL, - winuser::VK_OEM_ATTN, - winuser::VK_OEM_FINISH, - winuser::VK_OEM_COPY, - winuser::VK_OEM_AUTO, - winuser::VK_OEM_ENLW, - winuser::VK_OEM_BACKTAB, - winuser::VK_ATTN, - winuser::VK_CRSEL, - winuser::VK_EXSEL, - winuser::VK_EREOF, - winuser::VK_PLAY, - winuser::VK_ZOOM, - winuser::VK_NONAME, - winuser::VK_PA1, - winuser::VK_OEM_CLEAR, -]; +pub fn is_msg_keyboard_related(msg: u32) -> bool { + use winuser::{WM_KEYFIRST, WM_KEYLAST, WM_SETFOCUS, WM_KILLFOCUS, WM_INPUTLANGCHANGE, WM_SHOWWINDOW}; + let is_keyboard_msg = WM_KEYFIRST <= msg && msg <= WM_KEYLAST; + + is_keyboard_msg +} #[derive(Debug, Copy, Clone)] pub struct KeyLParam { @@ -227,14 +39,16 @@ impl PlatformScanCode { } } +/// Stores information required to make `KeyEvent`s. +/// /// A single winint `KeyEvent` contains information which the windows API passes to the application /// in multiple window messages. In other words a winit `KeyEvent` cannot be build from a single /// window message. Therefore this type keeps track of certain information from previous events so /// that a `KeyEvent` can be constructed when the last event related to a keypress is received. /// -/// `PeekMessage` is used to determine wheter the next window message still belongs to the current -/// keypress. If it doesn't and the current state represents a key event waiting to be dispatched, -/// than said event is dispatched. +/// `PeekMessage` is sometimes used to determine wheter the next window message still belongs to the +/// current keypress. If it doesn't and the current state represents a key event waiting to be +/// dispatched, than said event is considered complete and is dispatched. /// /// The sequence of window messages for a key press event is the following: /// - Exactly one WM_KEYDOWN / WM_SYSKEYDOWN @@ -250,7 +64,7 @@ pub struct KeyEventBuilder { /// This map shouldn't need to exist. /// However currently this seems to be the only good way - /// way of getting the label for the pressed key. Note that calling `ToUnicode` + /// of getting the label for the pressed key. Note that calling `ToUnicode` /// just when the key is pressed/released would be enough if `ToUnicode` wouldn't /// change the keyboard state (it removes the dead key). There is a flag to prevent /// changing the state but that flag requires Windows 10, version 1607 or newer) @@ -278,7 +92,7 @@ impl Default for KeyEventBuilder { fn default() -> Self { KeyEventBuilder { event_info: None, - key_labels: HashMap::default(), + key_labels: HashMap::with_capacity(128), known_locale_id: 0, prev_down_was_dead: false, } @@ -297,20 +111,16 @@ impl KeyEventBuilder { retval: &mut LRESULT ) -> Option { match msg_kind { - winuser::WM_INPUTLANGCHANGE => { - self.generate_labels(lparam as usize); - *retval = 1; - } - winuser::WM_SETFOCUS => { - // We do not know the dead key, state so assuing that there wasn't a deadkey press - // outside of this window. + winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { + println!("{}, {}", file!(), line!()); self.prev_down_was_dead = false; + // When the labels are already generated for this locale, + // the `generate_labels` function returns without calling + // `ToUnicode` so it keeps the dead key state intact. let locale_id = unsafe { winuser::GetKeyboardLayout(0) }; self.generate_labels(locale_id as usize); - } - winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { - self.prev_down_was_dead = false; + let vkey = wparam as i32; let lparam_struct = destructure_key_lparam(lparam); let scancode = PlatformScanCode::new( @@ -318,7 +128,8 @@ impl KeyEventBuilder { lparam_struct.extended ); let code = native_key_to_code(scancode); - let location = get_location(vkey, lparam_struct.extended, code); + let location = get_location(vkey, lparam_struct.extended); + let label = self.key_labels.get(&scancode).map(|s| s.clone()); self.event_info = Some(PartialKeyEventInfo { key_state: keyboard_types::KeyState::Down, vkey: wparam as i32, @@ -327,10 +138,11 @@ impl KeyEventBuilder { code: code, location: location, is_dead: false, - label: self.key_labels.get(&scancode).map(|s| s.clone()), - utf16parts: Vec::with_capacity(4), - utf16parts_without_ctrl: Vec::with_capacity(4), + label: label, + utf16parts: Vec::with_capacity(8), + utf16parts_without_ctrl: Vec::with_capacity(8), }); + *retval = 0; } winuser::WM_DEADCHAR | winuser::WM_SYSDEADCHAR => { self.prev_down_was_dead = true; @@ -338,12 +150,12 @@ impl KeyEventBuilder { // this key press let mut event_info = self.event_info.take().unwrap(); event_info.is_dead = true; + *retval = 0; return Some(event_info.finalize()); } winuser::WM_CHAR | winuser::WM_SYSCHAR => { - if msg_kind == winuser::WM_SYSCHAR { - *retval = 0; - } + println!("{}, {}", file!(), line!()); + *retval = 0; let is_high_surrogate = 0xD800 <= wparam && wparam <= 0xDBFF; let is_low_surrogate = 0xDC00 <= wparam && wparam <= 0xDFFF; @@ -387,11 +199,40 @@ impl KeyEventBuilder { } } if !more_char_coming { - let event_info = self.event_info.take().unwrap(); + let mut event_info = self.event_info.take().unwrap(); + + // Here it's okay to call `ToUnicode` because at this point the dead key + // is already consumed by the character. + unsafe { + let mut key_state: [MaybeUninit; 256] = [MaybeUninit::uninit(); 256]; + winuser::GetKeyboardState(key_state[0].as_mut_ptr()); + let mut key_state = std::mem::transmute::<_, [u8; 256]>(key_state); + + let has_ctrl = + key_state[winuser::VK_CONTROL as usize] != 0 || + key_state[winuser::VK_LCONTROL as usize] != 0 || + key_state[winuser::VK_RCONTROL as usize] != 0; + + // If neither of the CTRL keys is pressed, just use the text with all + // modifiers because that already consumed the dead key and otherwise + // we would interpret the character incorretly, missing the dead key. + if !has_ctrl { + event_info.utf16parts_without_ctrl = event_info.utf16parts.clone(); + } else { + get_utf16_without_ctrl( + event_info.vkey as u32, + event_info.scancode, + &mut key_state, + &mut event_info.utf16parts_without_ctrl + ); + } + } + return Some(event_info.finalize()); } } winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { + *retval = 0; let vkey = wparam as i32; let lparam_struct = destructure_key_lparam(lparam); let scancode = PlatformScanCode::new( @@ -399,10 +240,17 @@ impl KeyEventBuilder { lparam_struct.extended ); let code = native_key_to_code(scancode); - let location = get_location(vkey, lparam_struct.extended, code); + let location = get_location(vkey, lparam_struct.extended); let mut utf16parts = Vec::with_capacity(8); let mut utf16parts_without_ctrl = Vec::with_capacity(8); - if !self.prev_down_was_dead { + + // Avoid calling `ToUnicode` (which resets dead keys) if either the event + // belongs to the key-down which just produced the dead key or if + // the current key would not otherwise reset the dead key state. + // + // This logic relies on the assuption that keys which don't consume + // dead keys, also do not produce text input. + if !self.prev_down_was_dead && does_vkey_consume_dead_key(wparam as u32) { unsafe { //let locale_id = winuser::GetKeyboardLayout(0); let mut key_state: [MaybeUninit; 256] = [MaybeUninit::uninit(); 256]; @@ -418,21 +266,15 @@ impl KeyEventBuilder { ); utf16parts.set_len(unicode_len as usize); - // Now remove all CTRL stuff from the keyboard state - key_state[winuser::VK_LCONTROL as usize] = 0; - key_state[winuser::VK_RCONTROL as usize] = 0; - let unicode_len = winuser::ToUnicode( + get_utf16_without_ctrl( wparam as u32, - scancode.0 as u32, - (&mut key_state[0]) as *mut _, - utf16parts_without_ctrl.as_mut_ptr(), - utf16parts_without_ctrl.capacity() as i32, - 0 + scancode, + &mut key_state, + &mut utf16parts_without_ctrl ); - utf16parts_without_ctrl.set_len(unicode_len as usize); } } - + let label = self.key_labels.get(&scancode).map(|s| s.clone()); let event_info = PartialKeyEventInfo { key_state: keyboard_types::KeyState::Up, vkey: wparam as i32, @@ -441,7 +283,7 @@ impl KeyEventBuilder { code: code, location: location, is_dead: self.prev_down_was_dead, - label: self.key_labels.get(&scancode).map(|s| s.clone()), + label: label, utf16parts: utf16parts, utf16parts_without_ctrl: utf16parts_without_ctrl, }; @@ -455,30 +297,31 @@ impl KeyEventBuilder { /// Returns true if succeeded. fn generate_labels(&mut self, locale_identifier: usize) -> bool { + println!("Generating labels"); if self.known_locale_id == locale_identifier { + println!("Skipping generation because locales are identical"); return true; } + // We initialize the keyboard state with all zeros to + // simulate a scenario when no modifier is active. let mut key_state = [0u8; 256]; self.key_labels.clear(); - let target_capacity = 128; - if self.key_labels.capacity() < target_capacity { - self.key_labels.reserve(target_capacity - self.key_labels.capacity()); - } unsafe { - let result = winuser::GetKeyboardState((&mut key_state[0]) as *mut _); - assert!(result != 0, "`GetKeyboardState` failed"); - for &vk in VIRTUAL_KEYS { + let mut add_key_label = |vkey: u32| { let scancode = winuser::MapVirtualKeyExW( - vk as u32, + vkey, winuser::MAPVK_VK_TO_VSC_EX, locale_identifier as _ ); + if scancode == 0 { + return; + } let platform_scancode = PlatformScanCode(scancode as u16); let mut label_wide = [0u16; 8]; let wide_len = winuser::ToUnicodeEx( - vk as u32, + vkey, scancode, (&mut key_state[0]) as *mut _, (&mut label_wide[0]) as *mut _, @@ -487,11 +330,28 @@ impl KeyEventBuilder { locale_identifier as _ ); if wide_len > 0 { - let label_str = OsString::from_wide(&label_wide[0..wide_len as usize]).into_string().unwrap(); - self.key_labels.insert(platform_scancode, label_str); + let os_string = OsString::from_wide(&label_wide[0..wide_len as usize]); + if let Ok(label_str) = os_string.into_string() { + self.key_labels.insert(platform_scancode, label_str); + } else { + println!("Could not transform {:?}", label_wide); + } } + }; + for &vk in VIRTUAL_KEY_ENUMS { + add_key_label(vk as u32); + } + for ch in 'A'..='Z' { + let vk = ch as u32; + add_key_label(vk); + } + for ch in '0'..='9' { + let vk = ch as u32; + add_key_label(vk); } } + self.known_locale_id = locale_identifier; + println!("{}, {}", file!(), line!()); true } } @@ -523,10 +383,7 @@ impl PartialKeyEventInfo { if self.is_dead { logical_key = Key::Dead; } else { - // TODO: Check if the vkey indicates a printable key. If it does AND if there's no dead - // key active (this can be detected by passing `MAPVK_VK_TO_CHAR` to `MapVirtualKeyExW`) - // than we are safe to call ToUnicodeEx because it only resets the dead key state but - // that does not matter if there are no active dead keys anyways. + // TODO: translate non-printable keys to `Key` if self.utf16parts_without_ctrl.len() > 0 { logical_key = Key::Character(String::from_utf16(&self.utf16parts_without_ctrl).unwrap()); @@ -536,17 +393,10 @@ impl PartialKeyEventInfo { } let key_without_modifers; - match &logical_key { - Key::Character(_) => { - if let Some(label) = self.label { - key_without_modifers = Key::Character(label); - } else { - key_without_modifers = Key::Unidentified; - } - } - _ => { - key_without_modifers = logical_key.clone(); - } + if let Some(label) = &self.label { + key_without_modifers = Key::Character(label.clone()); + } else { + key_without_modifers = logical_key.clone(); } let mut char_with_all_modifiers = None; @@ -569,30 +419,6 @@ impl PartialKeyEventInfo { } } -fn ToUnicodeString( - vkey: u32, - scancode: u16, - key_state: *mut u8, - locale_identifier: usize -) -> Option { - let mut label_wide = [0u16; 8]; - let wide_len = unsafe { winuser::ToUnicodeEx( - vkey, - scancode as u32, - key_state, - (&mut label_wide[0]) as *mut _, - label_wide.len() as i32, - 0, - locale_identifier as _ - ) }; - if wide_len > 0 { - let label_str = OsString::from_wide(&label_wide[0..wide_len as usize]).into_string().unwrap(); - Some(label_str) - } else { - None - } -} - pub fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { let previous_state = (lparam >> 30) & 0x01; let transition_state = (lparam >> 31) & 0x01; @@ -603,25 +429,6 @@ pub fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { } } -pub fn build_key_event(vkey: i32, lparam: KeyLParam, state: keyboard_types::KeyState) -> KeyEvent { - let scancode = PlatformScanCode::new(lparam.scancode, lparam.extended); - - let physical_key = native_key_to_code(scancode); - - KeyEvent { - scancode: ScanCode(scancode), - location: get_location(vkey, lparam.extended, physical_key), - physical_key, - logical_key: keyboard_types::Key::Unidentified, - state, - repeat: lparam.is_repeat, - platform_specific: KeyEventExtra { - char_with_all_modifers: None, - key_without_modifers: keyboard_types::Key::Unidentified, - }, - } -} - pub fn native_key_to_code(scancode: PlatformScanCode) -> keyboard_types::Code { // See: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html // and: https://www.w3.org/TR/uievents-code/ @@ -787,7 +594,6 @@ pub fn native_key_to_code(scancode: PlatformScanCode) -> keyboard_types::Code { pub fn get_location( vkey: c_int, extended: bool, - code: keyboard_types::Code, ) -> keyboard_types::Location { use keyboard_types::{Code, Location}; use winuser::*; @@ -814,3 +620,260 @@ pub fn get_location( _ => Location::Standard, } } + +unsafe fn get_utf16_without_ctrl( + vkey: u32, + scancode: PlatformScanCode, + key_state: &mut [u8; 256], + utf16parts_without_ctrl: &mut Vec +) { + let target_capacity = 8; + let curr_cap = utf16parts_without_ctrl.capacity(); + if curr_cap < target_capacity { + utf16parts_without_ctrl.reserve(target_capacity - curr_cap); + } + // Now remove all CTRL stuff from the keyboard state + key_state[winuser::VK_CONTROL as usize] = 0; + key_state[winuser::VK_LCONTROL as usize] = 0; + key_state[winuser::VK_RCONTROL as usize] = 0; + let unicode_len = winuser::ToUnicode( + vkey, + scancode.0 as u32, + (&mut key_state[0]) as *mut _, + utf16parts_without_ctrl.as_mut_ptr(), + utf16parts_without_ctrl.capacity() as i32, + 0 + ); + if unicode_len < 0 { + utf16parts_without_ctrl.set_len(0); + } else { + utf16parts_without_ctrl.set_len(unicode_len as usize); + } +} + +// TODO: This list might not be complete +fn does_vkey_consume_dead_key(vkey: u32) -> bool { + const A: u32 = 'A' as u32; + const Z: u32 = 'Z' as u32; + const ZERO: u32 = '0' as u32; + const NINE: u32 = '9' as u32; + match vkey { + A..=Z | ZERO..=NINE => return true, + _ => () + } + match vkey as i32 { + // OEM keys + winuser::VK_OEM_1 | winuser::VK_OEM_2 | winuser::VK_OEM_3 | winuser::VK_OEM_4 | + winuser::VK_OEM_5 | winuser::VK_OEM_6 | winuser::VK_OEM_7 | winuser::VK_OEM_8 | + winuser::VK_OEM_PLUS | winuser::VK_OEM_COMMA | winuser::VK_OEM_MINUS | + winuser::VK_OEM_PERIOD => { + true + } + // Other keys + winuser::VK_TAB | winuser::VK_BACK | winuser::VK_RETURN | winuser::VK_SPACE | + winuser::VK_NUMPAD0..=winuser::VK_NUMPAD9 | winuser::VK_MULTIPLY | winuser::VK_ADD | + winuser::VK_SUBTRACT | winuser::VK_DECIMAL | winuser::VK_DIVIDE => { + true + }, + _ => false, + } +} + +/// Warning: this does not cover all possible virtual keys. +/// Most notably it does not cover [A, Z] and [0, 9] +/// Those each have the value of ther corresponding uppercase `char`. +/// E.g. the virtual key A has the value `'A'` +const VIRTUAL_KEY_ENUMS: &'static [i32] = &[ + winuser::VK_LBUTTON, + winuser::VK_RBUTTON, + winuser::VK_CANCEL, + winuser::VK_MBUTTON, + winuser::VK_XBUTTON1, + winuser::VK_XBUTTON2, + winuser::VK_BACK, + winuser::VK_TAB, + winuser::VK_CLEAR, + winuser::VK_RETURN, + winuser::VK_SHIFT, + winuser::VK_CONTROL, + winuser::VK_MENU, + winuser::VK_PAUSE, + winuser::VK_CAPITAL, + winuser::VK_KANA, + winuser::VK_HANGEUL, + winuser::VK_HANGUL, + winuser::VK_JUNJA, + winuser::VK_FINAL, + winuser::VK_HANJA, + winuser::VK_KANJI, + winuser::VK_ESCAPE, + winuser::VK_CONVERT, + winuser::VK_NONCONVERT, + winuser::VK_ACCEPT, + winuser::VK_MODECHANGE, + winuser::VK_SPACE, + winuser::VK_PRIOR, + winuser::VK_NEXT, + winuser::VK_END, + winuser::VK_HOME, + winuser::VK_LEFT, + winuser::VK_UP, + winuser::VK_RIGHT, + winuser::VK_DOWN, + winuser::VK_SELECT, + winuser::VK_PRINT, + winuser::VK_EXECUTE, + winuser::VK_SNAPSHOT, + winuser::VK_INSERT, + winuser::VK_DELETE, + winuser::VK_HELP, + winuser::VK_LWIN, + winuser::VK_RWIN, + winuser::VK_APPS, + winuser::VK_SLEEP, + winuser::VK_NUMPAD0, + winuser::VK_NUMPAD1, + winuser::VK_NUMPAD2, + winuser::VK_NUMPAD3, + winuser::VK_NUMPAD4, + winuser::VK_NUMPAD5, + winuser::VK_NUMPAD6, + winuser::VK_NUMPAD7, + winuser::VK_NUMPAD8, + winuser::VK_NUMPAD9, + winuser::VK_MULTIPLY, + winuser::VK_ADD, + winuser::VK_SEPARATOR, + winuser::VK_SUBTRACT, + winuser::VK_DECIMAL, + winuser::VK_DIVIDE, + winuser::VK_F1, + winuser::VK_F2, + winuser::VK_F3, + winuser::VK_F4, + winuser::VK_F5, + winuser::VK_F6, + winuser::VK_F7, + winuser::VK_F8, + winuser::VK_F9, + winuser::VK_F10, + winuser::VK_F11, + winuser::VK_F12, + winuser::VK_F13, + winuser::VK_F14, + winuser::VK_F15, + winuser::VK_F16, + winuser::VK_F17, + winuser::VK_F18, + winuser::VK_F19, + winuser::VK_F20, + winuser::VK_F21, + winuser::VK_F22, + winuser::VK_F23, + winuser::VK_F24, + winuser::VK_NAVIGATION_VIEW, + winuser::VK_NAVIGATION_MENU, + winuser::VK_NAVIGATION_UP, + winuser::VK_NAVIGATION_DOWN, + winuser::VK_NAVIGATION_LEFT, + winuser::VK_NAVIGATION_RIGHT, + winuser::VK_NAVIGATION_ACCEPT, + winuser::VK_NAVIGATION_CANCEL, + winuser::VK_NUMLOCK, + winuser::VK_SCROLL, + winuser::VK_OEM_NEC_EQUAL, + winuser::VK_OEM_FJ_JISHO, + winuser::VK_OEM_FJ_MASSHOU, + winuser::VK_OEM_FJ_TOUROKU, + winuser::VK_OEM_FJ_LOYA, + winuser::VK_OEM_FJ_ROYA, + winuser::VK_LSHIFT, + winuser::VK_RSHIFT, + winuser::VK_LCONTROL, + winuser::VK_RCONTROL, + winuser::VK_LMENU, + winuser::VK_RMENU, + winuser::VK_BROWSER_BACK, + winuser::VK_BROWSER_FORWARD, + winuser::VK_BROWSER_REFRESH, + winuser::VK_BROWSER_STOP, + winuser::VK_BROWSER_SEARCH, + winuser::VK_BROWSER_FAVORITES, + winuser::VK_BROWSER_HOME, + winuser::VK_VOLUME_MUTE, + winuser::VK_VOLUME_DOWN, + winuser::VK_VOLUME_UP, + winuser::VK_MEDIA_NEXT_TRACK, + winuser::VK_MEDIA_PREV_TRACK, + winuser::VK_MEDIA_STOP, + winuser::VK_MEDIA_PLAY_PAUSE, + winuser::VK_LAUNCH_MAIL, + winuser::VK_LAUNCH_MEDIA_SELECT, + winuser::VK_LAUNCH_APP1, + winuser::VK_LAUNCH_APP2, + winuser::VK_OEM_1, + winuser::VK_OEM_PLUS, + winuser::VK_OEM_COMMA, + winuser::VK_OEM_MINUS, + winuser::VK_OEM_PERIOD, + winuser::VK_OEM_2, + winuser::VK_OEM_3, + winuser::VK_GAMEPAD_A, + winuser::VK_GAMEPAD_B, + winuser::VK_GAMEPAD_X, + winuser::VK_GAMEPAD_Y, + winuser::VK_GAMEPAD_RIGHT_SHOULDER, + winuser::VK_GAMEPAD_LEFT_SHOULDER, + winuser::VK_GAMEPAD_LEFT_TRIGGER, + winuser::VK_GAMEPAD_RIGHT_TRIGGER, + winuser::VK_GAMEPAD_DPAD_UP, + winuser::VK_GAMEPAD_DPAD_DOWN, + winuser::VK_GAMEPAD_DPAD_LEFT, + winuser::VK_GAMEPAD_DPAD_RIGHT, + winuser::VK_GAMEPAD_MENU, + winuser::VK_GAMEPAD_VIEW, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_UP, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_DOWN, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_LEFT, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_UP, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT, + winuser::VK_OEM_4, + winuser::VK_OEM_5, + winuser::VK_OEM_6, + winuser::VK_OEM_7, + winuser::VK_OEM_8, + winuser::VK_OEM_AX, + winuser::VK_OEM_102, + winuser::VK_ICO_HELP, + winuser::VK_ICO_00, + winuser::VK_PROCESSKEY, + winuser::VK_ICO_CLEAR, + winuser::VK_PACKET, + winuser::VK_OEM_RESET, + winuser::VK_OEM_JUMP, + winuser::VK_OEM_PA1, + winuser::VK_OEM_PA2, + winuser::VK_OEM_PA3, + winuser::VK_OEM_WSCTRL, + winuser::VK_OEM_CUSEL, + winuser::VK_OEM_ATTN, + winuser::VK_OEM_FINISH, + winuser::VK_OEM_COPY, + winuser::VK_OEM_AUTO, + winuser::VK_OEM_ENLW, + winuser::VK_OEM_BACKTAB, + winuser::VK_ATTN, + winuser::VK_CRSEL, + winuser::VK_EXSEL, + winuser::VK_EREOF, + winuser::VK_PLAY, + winuser::VK_ZOOM, + winuser::VK_NONAME, + winuser::VK_PA1, + winuser::VK_OEM_CLEAR, +]; From e6f4f3799c46827e6c6498ecd86f3f44cb28893b Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 28 Nov 2020 17:52:49 +0100 Subject: [PATCH 06/75] Add translation for non-printable virtual keys --- src/event.rs | 4 +- src/platform_impl/windows/keyboard.rs | 838 +++++++++++++++----------- 2 files changed, 495 insertions(+), 347 deletions(-) diff --git a/src/event.rs b/src/event.rs index 1dbd254e5b..7061efa506 100644 --- a/src/event.rs +++ b/src/event.rs @@ -656,7 +656,9 @@ pub struct KeyboardInput { pub modifiers: ModifiersState, } -//TODO Implement (de)serialization +// TODO: implement minimal IME API acting as a stopgap until #1497 gets completed. + +// TODO: Implement (de)serialization #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEvent { pub scancode: ScanCode, diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 5f5e5b008a..a38389a614 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -1,8 +1,15 @@ -use std::{os::raw::c_int, fmt, collections::HashMap}; +use std::{ + os::raw::c_int, fmt, collections::HashMap, + char, mem::MaybeUninit, ffi::OsString, os::windows::ffi::OsStringExt +}; + +use keyboard_types::Key; -use winapi::{shared::{minwindef::{LRESULT, LPARAM, WPARAM}, windef::HWND}, um::winuser, um::winnls}; +use winapi::{ + shared::{minwindef::{LRESULT, LPARAM, WPARAM, HKL, LOWORD}, windef::HWND}, + um::{winuser, winnt::{PRIMARYLANGID, LANG_KOREAN, LANG_JAPANESE}} +}; -use std::{char, mem::MaybeUninit, ffi::OsString, os::windows::ffi::OsStringExt}; use crate::{ event::{KeyEvent, ScanCode}, @@ -25,6 +32,26 @@ pub struct KeyLParam { pub is_repeat: bool, } +#[derive(Eq, PartialEq)] +enum ToUnicodeResult { + Str(String), + Dead, + None, +} + +impl ToUnicodeResult { + fn is_none(&self) -> bool { + match self { + ToUnicodeResult::None => true, + _ => false + } + } + + fn is_something(&self) -> bool { + !self.is_none() + } +} + #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct PlatformScanCode(pub u16); impl fmt::Debug for PlatformScanCode { @@ -66,15 +93,19 @@ pub struct KeyEventBuilder { /// However currently this seems to be the only good way /// of getting the label for the pressed key. Note that calling `ToUnicode` /// just when the key is pressed/released would be enough if `ToUnicode` wouldn't - /// change the keyboard state (it removes the dead key). There is a flag to prevent + /// change the keyboard state (it clears the dead key). There is a flag to prevent /// changing the state but that flag requires Windows 10, version 1607 or newer) key_labels: HashMap, - /// The locale identifier (HKL) of the layout for which the `key_labels` was generated + /// True if the keyboard layout belonging to `known_locale_id` has an AltGr key. + has_alt_graph: bool, + + /// The locale identifier (HKL) of the layout for which the `key_labels` and `has_alt_graph` was + /// generated known_locale_id: usize, - /// The keyup event needs to call `ToUnicode` to determine key with all modifiers except CTRL - /// (the `logical_key`). + /// The keyup event needs to call `ToUnicode` to determine what's the text produced by the + /// key with all modifiers except CTRL (the `logical_key`). /// /// But `ToUnicode` without the non-modifying flag (see `key_labels`), resets the dead key /// state which would be incorrect during every keyup event. Therefore this variable is used @@ -83,9 +114,9 @@ pub struct KeyEventBuilder { /// Note that this variable is not always correct because it does /// not track key presses outside of this window. However the ONLY situation where this /// doesn't work as intended is when the user presses a dead key outside of this window, and - /// switched to this window BEFORE releasing it then releases the dead key. In this case + /// switches to this window BEFORE releasing it then releases the dead key. In this case /// the `ToUnicode` function will be called, incorrectly clearing the dead key state. Having - /// an inccorect behaviour in this case seems acceptable. + /// an inccorect behaviour only in this case seems acceptable. prev_down_was_dead: bool, } impl Default for KeyEventBuilder { @@ -93,12 +124,18 @@ impl Default for KeyEventBuilder { KeyEventBuilder { event_info: None, key_labels: HashMap::with_capacity(128), + has_alt_graph: false, known_locale_id: 0, prev_down_was_dead: false, } } } impl KeyEventBuilder { + const SHIFT_FLAG: u8 = 1 << 0; + const CONTROL_FLAG: u8 = 1 << 1; + const ALT_FLAG: u8 = 1 << 2; + const CAPS_LOCK_FLAG: u8 = 1 << 3; + /// Call this function for every window message. /// Returns Some() if this window message completes a KeyEvent. /// Returns None otherwise. @@ -119,7 +156,7 @@ impl KeyEventBuilder { // the `generate_labels` function returns without calling // `ToUnicode` so it keeps the dead key state intact. let locale_id = unsafe { winuser::GetKeyboardLayout(0) }; - self.generate_labels(locale_id as usize); + self.prepare_layout(locale_id as usize); let vkey = wparam as i32; let lparam_struct = destructure_key_lparam(lparam); @@ -151,7 +188,7 @@ impl KeyEventBuilder { let mut event_info = self.event_info.take().unwrap(); event_info.is_dead = true; *retval = 0; - return Some(event_info.finalize()); + return Some(event_info.finalize(self.known_locale_id, self.has_alt_graph)); } winuser::WM_CHAR | winuser::WM_SYSCHAR => { println!("{}, {}", file!(), line!()); @@ -228,7 +265,7 @@ impl KeyEventBuilder { } } - return Some(event_info.finalize()); + return Some(event_info.finalize(self.known_locale_id, self.has_alt_graph)); } } winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { @@ -287,7 +324,7 @@ impl KeyEventBuilder { utf16parts: utf16parts, utf16parts_without_ctrl: utf16parts_without_ctrl, }; - return Some(event_info.finalize()); + return Some(event_info.finalize(self.known_locale_id, self.has_alt_graph)); } _ => () } @@ -296,10 +333,8 @@ impl KeyEventBuilder { } /// Returns true if succeeded. - fn generate_labels(&mut self, locale_identifier: usize) -> bool { - println!("Generating labels"); + fn prepare_layout(&mut self, locale_identifier: usize) -> bool { if self.known_locale_id == locale_identifier { - println!("Skipping generation because locales are identical"); return true; } @@ -307,52 +342,114 @@ impl KeyEventBuilder { // simulate a scenario when no modifier is active. let mut key_state = [0u8; 256]; self.key_labels.clear(); - unsafe { - let mut add_key_label = |vkey: u32| { - let scancode = winuser::MapVirtualKeyExW( - vkey, - winuser::MAPVK_VK_TO_VSC_EX, - locale_identifier as _ + // Virtual key values are in the domain [0, 255]. + // This is reinforced by the fact that the keyboard state array has 256 + // elements. This array is allowed to be indexed by virtual key values + // giving the key state for the virtual key used for indexing. + for vk in 0..256 { + let scancode = unsafe { winuser::MapVirtualKeyExW( + vk, + winuser::MAPVK_VK_TO_VSC_EX, + locale_identifier as HKL + ) }; + if scancode == 0 { + continue; + } + Self::apply_mod_state(&mut key_state, 0); + let unicode = Self::ToUnicodeString(&key_state, vk, scancode, locale_identifier); + let unicode_str = match unicode { + ToUnicodeResult::Str(str) => str, + _ => continue, + }; + let platform_scancode = PlatformScanCode(scancode as u16); + self.key_labels.insert(platform_scancode, unicode_str); + + // Check for alt graph. + // The logic is that if a key pressed with the CTRL modifier produces + // a different result from when it's pressed with CTRL+ALT then the layout + // has AltGr. + if !self.has_alt_graph { + Self::apply_mod_state(&mut key_state, Self::CONTROL_FLAG); + let key_with_ctrl = Self::ToUnicodeString( + &key_state, vk, scancode, locale_identifier + ); + Self::apply_mod_state(&mut key_state, Self::CONTROL_FLAG | Self::ALT_FLAG); + let key_with_ctrl_alt = Self::ToUnicodeString( + &key_state, vk, scancode, locale_identifier ); - if scancode == 0 { - return; + if key_with_ctrl.is_something() && key_with_ctrl_alt.is_something() { + self.has_alt_graph = key_with_ctrl != key_with_ctrl_alt; } - let platform_scancode = PlatformScanCode(scancode as u16); + } + } + self.known_locale_id = locale_identifier; + true + } - let mut label_wide = [0u16; 8]; - let wide_len = winuser::ToUnicodeEx( + fn ToUnicodeString(key_state: &[u8; 256], vkey: u32, scancode: u32, locale_identifier: usize) -> ToUnicodeResult { + unsafe { + let mut label_wide = [0u16; 8]; + let wide_len = winuser::ToUnicodeEx( + vkey, + scancode, + (&key_state[0]) as *const _, + (&mut label_wide[0]) as *mut _, + label_wide.len() as i32, + 0, + locale_identifier as _ + ); + if wide_len < 0 { + // If it's dead, let's run `ToUnicode` again, to consume the dead-key + winuser::ToUnicodeEx( vkey, scancode, - (&mut key_state[0]) as *mut _, + (&key_state[0]) as *const _, (&mut label_wide[0]) as *mut _, label_wide.len() as i32, 0, locale_identifier as _ ); - if wide_len > 0 { - let os_string = OsString::from_wide(&label_wide[0..wide_len as usize]); - if let Ok(label_str) = os_string.into_string() { - self.key_labels.insert(platform_scancode, label_str); - } else { - println!("Could not transform {:?}", label_wide); - } - } - }; - for &vk in VIRTUAL_KEY_ENUMS { - add_key_label(vk as u32); + return ToUnicodeResult::Dead; } - for ch in 'A'..='Z' { - let vk = ch as u32; - add_key_label(vk); - } - for ch in '0'..='9' { - let vk = ch as u32; - add_key_label(vk); + if wide_len > 0 { + let os_string = OsString::from_wide(&label_wide[0..wide_len as usize]); + if let Ok(label_str) = os_string.into_string() { + return ToUnicodeResult::Str(label_str); + } else { + println!("Could not transform {:?}", label_wide); + } } } - self.known_locale_id = locale_identifier; - println!("{}, {}", file!(), line!()); - true + ToUnicodeResult::None + } + + fn apply_mod_state(key_state: &mut [u8; 256], mod_state: u8) { + if mod_state & Self::SHIFT_FLAG != 0 { + key_state[winuser::VK_SHIFT as usize] |= 0x80; + } else { + key_state[winuser::VK_SHIFT as usize] &= !0x80; + key_state[winuser::VK_LSHIFT as usize] &= !0x80; + key_state[winuser::VK_RSHIFT as usize] &= !0x80; + } + if mod_state & Self::CONTROL_FLAG != 0 { + key_state[winuser::VK_CONTROL as usize] |= 0x80; + } else { + key_state[winuser::VK_CONTROL as usize] &= !0x80; + key_state[winuser::VK_LCONTROL as usize] &= !0x80; + key_state[winuser::VK_RCONTROL as usize] &= !0x80; + } + if mod_state & Self::ALT_FLAG != 0 { + key_state[winuser::VK_MENU as usize] |= 0x80; + } else { + key_state[winuser::VK_MENU as usize] &= !0x80; + key_state[winuser::VK_LMENU as usize] &= !0x80; + key_state[winuser::VK_RMENU as usize] &= !0x80; + } + if mod_state & Self::CAPS_LOCK_FLAG != 0 { + key_state[winuser::VK_CAPITAL as usize] |= 0x80; + } else { + key_state[winuser::VK_CAPITAL as usize] &= !0x80; + } } } @@ -376,19 +473,25 @@ struct PartialKeyEventInfo { } impl PartialKeyEventInfo { - fn finalize(self) -> KeyEvent { - use keyboard_types::Key; - + fn finalize(self, locale_id: usize, has_alt_gr: bool) -> KeyEvent { let logical_key; if self.is_dead { logical_key = Key::Dead; } else { - // TODO: translate non-printable keys to `Key` - if self.utf16parts_without_ctrl.len() > 0 { - logical_key = - Key::Character(String::from_utf16(&self.utf16parts_without_ctrl).unwrap()); - } else { - logical_key = Key::Unidentified; + let key = vkey_to_non_printable(self.vkey, locale_id, has_alt_gr); + match key { + Key::Unidentified => { + if self.utf16parts_without_ctrl.len() > 0 { + logical_key = Key::Character( + String::from_utf16(&self.utf16parts_without_ctrl).unwrap() + ); + } else { + logical_key = Key::Unidentified; + } + } + key @ _ => { + logical_key = key; + } } } @@ -429,7 +532,337 @@ pub fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { } } -pub fn native_key_to_code(scancode: PlatformScanCode) -> keyboard_types::Code { +pub fn get_location( + vkey: c_int, + extended: bool, +) -> keyboard_types::Location { + use keyboard_types::{Code, Location}; + use winuser::*; + const VK_ABNT_C2: c_int = 0xc2; + + // Use the native VKEY and the extended flag to cover most cases + // This is taken from the `druid` software within + // druid-shell/src/platform/windows/keyboard.rs + match vkey { + VK_LSHIFT | VK_LCONTROL | VK_LMENU | VK_LWIN => Location::Left, + VK_RSHIFT | VK_RCONTROL | VK_RMENU | VK_RWIN => Location::Right, + VK_RETURN if extended => Location::Numpad, + VK_INSERT | VK_DELETE | VK_END | VK_DOWN | VK_NEXT | VK_LEFT | VK_CLEAR | VK_RIGHT + | VK_HOME | VK_UP | VK_PRIOR => { + if extended { + Location::Standard + } else { + Location::Numpad + } + } + VK_NUMPAD0 | VK_NUMPAD1 | VK_NUMPAD2 | VK_NUMPAD3 | VK_NUMPAD4 | VK_NUMPAD5 + | VK_NUMPAD6 | VK_NUMPAD7 | VK_NUMPAD8 | VK_NUMPAD9 | VK_DECIMAL | VK_DIVIDE + | VK_MULTIPLY | VK_SUBTRACT | VK_ADD | VK_ABNT_C2 => Location::Numpad, + _ => Location::Standard, + } +} + +unsafe fn get_utf16_without_ctrl( + vkey: u32, + scancode: PlatformScanCode, + key_state: &mut [u8; 256], + utf16parts_without_ctrl: &mut Vec +) { + let target_capacity = 8; + let curr_cap = utf16parts_without_ctrl.capacity(); + if curr_cap < target_capacity { + utf16parts_without_ctrl.reserve(target_capacity - curr_cap); + } + // Now remove all CTRL stuff from the keyboard state + key_state[winuser::VK_CONTROL as usize] = 0; + key_state[winuser::VK_LCONTROL as usize] = 0; + key_state[winuser::VK_RCONTROL as usize] = 0; + let unicode_len = winuser::ToUnicode( + vkey, + scancode.0 as u32, + (&mut key_state[0]) as *mut _, + utf16parts_without_ctrl.as_mut_ptr(), + utf16parts_without_ctrl.capacity() as i32, + 0 + ); + if unicode_len < 0 { + utf16parts_without_ctrl.set_len(0); + } else { + utf16parts_without_ctrl.set_len(unicode_len as usize); + } +} + +// TODO: This list might not be complete +fn does_vkey_consume_dead_key(vkey: u32) -> bool { + const A: u32 = 'A' as u32; + const Z: u32 = 'Z' as u32; + const ZERO: u32 = '0' as u32; + const NINE: u32 = '9' as u32; + match vkey { + A..=Z | ZERO..=NINE => return true, + _ => () + } + match vkey as i32 { + // OEM keys + winuser::VK_OEM_1 | winuser::VK_OEM_2 | winuser::VK_OEM_3 | winuser::VK_OEM_4 | + winuser::VK_OEM_5 | winuser::VK_OEM_6 | winuser::VK_OEM_7 | winuser::VK_OEM_8 | + winuser::VK_OEM_PLUS | winuser::VK_OEM_COMMA | winuser::VK_OEM_MINUS | + winuser::VK_OEM_PERIOD => { + true + } + // Other keys + winuser::VK_TAB | winuser::VK_BACK | winuser::VK_RETURN | winuser::VK_SPACE | + winuser::VK_NUMPAD0..=winuser::VK_NUMPAD9 | winuser::VK_MULTIPLY | winuser::VK_ADD | + winuser::VK_SUBTRACT | winuser::VK_DECIMAL | winuser::VK_DIVIDE => { + true + }, + _ => false, + } +} + +/// This includes all non-character keys defined within `Key` so for example +/// backspace and tab are included. +fn vkey_to_non_printable(vkey: i32, hkl: usize, has_alt_graph: bool) -> Key { + // List of the Web key names and their corresponding platform-native key names: + // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values + + let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); + let is_korean = primary_lang_id == LANG_KOREAN; + let is_japanese = primary_lang_id == LANG_JAPANESE; + + match vkey { + winuser::VK_LBUTTON => Key::Unidentified, // Mouse + winuser::VK_RBUTTON => Key::Unidentified, // Mouse + winuser::VK_CANCEL => Key::Unidentified, // I don't think this can be represented with a Key + winuser::VK_MBUTTON => Key::Unidentified, // Mouse + winuser::VK_XBUTTON1 => Key::Unidentified, // Mouse + winuser::VK_XBUTTON2 => Key::Unidentified, // Mouse + winuser::VK_BACK => Key::Backspace, + winuser::VK_TAB => Key::Tab, + winuser::VK_CLEAR => Key::Clear, + winuser::VK_RETURN => Key::Enter, + winuser::VK_SHIFT => Key::Shift, + winuser::VK_CONTROL => Key::Control, + winuser::VK_MENU => Key::Alt, + winuser::VK_PAUSE => Key::Pause, + winuser::VK_CAPITAL => Key::CapsLock, + + //winuser::VK_HANGEUL => Key::HangulMode, // Deprecated in favour of VK_HANGUL + + // VK_HANGUL and VK_KANA are defined as the same constant therefore + // we use appropriate conditions to differentate between them + winuser::VK_HANGUL if is_korean => Key::HangulMode, + winuser::VK_KANA if is_japanese => Key::KanaMode, + + winuser::VK_JUNJA => Key::JunjaMode, + winuser::VK_FINAL => Key::FinalMode, + + // VK_HANJA and VK_KANJI are defined as the same constant therefore + // we use appropriate conditions to differentate between them + winuser::VK_HANJA if is_korean => Key::HanjaMode, + winuser::VK_KANJI if is_japanese => Key::KanjiMode, + + winuser::VK_ESCAPE => Key::Escape, + winuser::VK_CONVERT => Key::Convert, + winuser::VK_NONCONVERT => Key::NonConvert, + winuser::VK_ACCEPT => Key::Accept, + winuser::VK_MODECHANGE => Key::ModeChange, + winuser::VK_SPACE => Key::Unidentified, // This function only converts "non-printable" + winuser::VK_PRIOR => Key::PageUp, + winuser::VK_NEXT => Key::PageDown, + winuser::VK_END => Key::End, + winuser::VK_HOME => Key::Home, + winuser::VK_LEFT => Key::ArrowLeft, + winuser::VK_UP => Key::ArrowUp, + winuser::VK_RIGHT => Key::ArrowRight, + winuser::VK_DOWN => Key::ArrowDown, + winuser::VK_SELECT => Key::Select, + winuser::VK_PRINT => Key::Print, + winuser::VK_EXECUTE => Key::Execute, + winuser::VK_SNAPSHOT => Key::PrintScreen, + winuser::VK_INSERT => Key::Insert, + winuser::VK_DELETE => Key::Delete, + winuser::VK_HELP => Key::Help, + winuser::VK_LWIN => Key::Meta, + winuser::VK_RWIN => Key::Meta, + winuser::VK_APPS => Key::ContextMenu, + winuser::VK_SLEEP => Key::Standby, + + // This function only converts "non-printable" + winuser::VK_NUMPAD0 => Key::Unidentified, + winuser::VK_NUMPAD1 => Key::Unidentified, + winuser::VK_NUMPAD2 => Key::Unidentified, + winuser::VK_NUMPAD3 => Key::Unidentified, + winuser::VK_NUMPAD4 => Key::Unidentified, + winuser::VK_NUMPAD5 => Key::Unidentified, + winuser::VK_NUMPAD6 => Key::Unidentified, + winuser::VK_NUMPAD7 => Key::Unidentified, + winuser::VK_NUMPAD8 => Key::Unidentified, + winuser::VK_NUMPAD9 => Key::Unidentified, + winuser::VK_MULTIPLY => Key::Unidentified, + winuser::VK_ADD => Key::Unidentified, + winuser::VK_SEPARATOR => Key::Unidentified, + winuser::VK_SUBTRACT => Key::Unidentified, + winuser::VK_DECIMAL => Key::Unidentified, + winuser::VK_DIVIDE => Key::Unidentified, + + winuser::VK_F1 => Key::F1, + winuser::VK_F2 => Key::F2, + winuser::VK_F3 => Key::F3, + winuser::VK_F4 => Key::F4, + winuser::VK_F5 => Key::F5, + winuser::VK_F6 => Key::F6, + winuser::VK_F7 => Key::F7, + winuser::VK_F8 => Key::F8, + winuser::VK_F9 => Key::F9, + winuser::VK_F10 => Key::F10, + winuser::VK_F11 => Key::F11, + winuser::VK_F12 => Key::F12, + + // TODO: Uncomment when these are added to `keyboard_types` + // winuser::VK_F13 => Key::F13, + // winuser::VK_F14 => Key::F14, + // winuser::VK_F15 => Key::F15, + // winuser::VK_F16 => Key::F16, + // winuser::VK_F17 => Key::F17, + // winuser::VK_F18 => Key::F18, + // winuser::VK_F19 => Key::F19, + // winuser::VK_F20 => Key::F20, + // winuser::VK_F21 => Key::F21, + // winuser::VK_F22 => Key::F22, + // winuser::VK_F23 => Key::F23, + // winuser::VK_F24 => Key::F24, + + winuser::VK_NAVIGATION_VIEW => Key::Unidentified, + winuser::VK_NAVIGATION_MENU => Key::Unidentified, + winuser::VK_NAVIGATION_UP => Key::Unidentified, + winuser::VK_NAVIGATION_DOWN => Key::Unidentified, + winuser::VK_NAVIGATION_LEFT => Key::Unidentified, + winuser::VK_NAVIGATION_RIGHT => Key::Unidentified, + winuser::VK_NAVIGATION_ACCEPT => Key::Unidentified, + winuser::VK_NAVIGATION_CANCEL => Key::Unidentified, + winuser::VK_NUMLOCK => Key::NumLock, + winuser::VK_SCROLL => Key::ScrollLock, + winuser::VK_OEM_NEC_EQUAL => Key::Unidentified, + //winuser::VK_OEM_FJ_JISHO => Key::Unidentified, // Conflicts with `VK_OEM_NEC_EQUAL` + winuser::VK_OEM_FJ_MASSHOU => Key::Unidentified, + winuser::VK_OEM_FJ_TOUROKU => Key::Unidentified, + winuser::VK_OEM_FJ_LOYA => Key::Unidentified, + winuser::VK_OEM_FJ_ROYA => Key::Unidentified, + winuser::VK_LSHIFT => Key::Shift, + winuser::VK_RSHIFT => Key::Shift, + winuser::VK_LCONTROL => Key::Control, + winuser::VK_RCONTROL => Key::Control, + winuser::VK_LMENU => Key::Alt, + winuser::VK_RMENU => { + if has_alt_graph { Key::AltGraph } + else { Key::Alt } + } + winuser::VK_BROWSER_BACK => Key::BrowserBack, + winuser::VK_BROWSER_FORWARD => Key::BrowserForward, + winuser::VK_BROWSER_REFRESH => Key::BrowserRefresh, + winuser::VK_BROWSER_STOP => Key::BrowserStop, + winuser::VK_BROWSER_SEARCH => Key::BrowserSearch, + winuser::VK_BROWSER_FAVORITES => Key::BrowserFavorites, + winuser::VK_BROWSER_HOME => Key::BrowserHome, + winuser::VK_VOLUME_MUTE => Key::AudioVolumeMute, + winuser::VK_VOLUME_DOWN => Key::AudioVolumeDown, + winuser::VK_VOLUME_UP => Key::AudioVolumeUp, + winuser::VK_MEDIA_NEXT_TRACK => Key::MediaTrackNext, + winuser::VK_MEDIA_PREV_TRACK => Key::MediaTrackPrevious, + winuser::VK_MEDIA_STOP => Key::MediaStop, + winuser::VK_MEDIA_PLAY_PAUSE => Key::MediaPlayPause, + winuser::VK_LAUNCH_MAIL => Key::LaunchMail, + winuser::VK_LAUNCH_MEDIA_SELECT => Key::LaunchMediaPlayer, + winuser::VK_LAUNCH_APP1 => Key::LaunchApplication1, + winuser::VK_LAUNCH_APP2 => Key::LaunchApplication2, + + // This function only converts "non-printable" + winuser::VK_OEM_1 => Key::Unidentified, + winuser::VK_OEM_PLUS => Key::Unidentified, + winuser::VK_OEM_COMMA => Key::Unidentified, + winuser::VK_OEM_MINUS => Key::Unidentified, + winuser::VK_OEM_PERIOD => Key::Unidentified, + winuser::VK_OEM_2 => Key::Unidentified, + winuser::VK_OEM_3 => Key::Unidentified, + + winuser::VK_GAMEPAD_A => Key::Unidentified, + winuser::VK_GAMEPAD_B => Key::Unidentified, + winuser::VK_GAMEPAD_X => Key::Unidentified, + winuser::VK_GAMEPAD_Y => Key::Unidentified, + winuser::VK_GAMEPAD_RIGHT_SHOULDER => Key::Unidentified, + winuser::VK_GAMEPAD_LEFT_SHOULDER => Key::Unidentified, + winuser::VK_GAMEPAD_LEFT_TRIGGER => Key::Unidentified, + winuser::VK_GAMEPAD_RIGHT_TRIGGER => Key::Unidentified, + winuser::VK_GAMEPAD_DPAD_UP => Key::Unidentified, + winuser::VK_GAMEPAD_DPAD_DOWN => Key::Unidentified, + winuser::VK_GAMEPAD_DPAD_LEFT => Key::Unidentified, + winuser::VK_GAMEPAD_DPAD_RIGHT => Key::Unidentified, + winuser::VK_GAMEPAD_MENU => Key::Unidentified, + winuser::VK_GAMEPAD_VIEW => Key::Unidentified, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON => Key::Unidentified, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON => Key::Unidentified, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_UP => Key::Unidentified, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_DOWN => Key::Unidentified, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT => Key::Unidentified, + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_LEFT => Key::Unidentified, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_UP => Key::Unidentified, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN => Key::Unidentified, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT => Key::Unidentified, + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT => Key::Unidentified, + + // This function only converts "non-printable" + winuser::VK_OEM_4 => Key::Unidentified, + winuser::VK_OEM_5 => Key::Unidentified, + winuser::VK_OEM_6 => Key::Unidentified, + winuser::VK_OEM_7 => Key::Unidentified, + winuser::VK_OEM_8 => Key::Unidentified, + winuser::VK_OEM_AX => Key::Unidentified, + winuser::VK_OEM_102 => Key::Unidentified, + + winuser::VK_ICO_HELP => Key::Unidentified, + winuser::VK_ICO_00 => Key::Unidentified, + + winuser::VK_PROCESSKEY => Key::Process, + + winuser::VK_ICO_CLEAR => Key::Unidentified, + winuser::VK_PACKET => Key::Unidentified, + winuser::VK_OEM_RESET => Key::Unidentified, + winuser::VK_OEM_JUMP => Key::Unidentified, + winuser::VK_OEM_PA1 => Key::Unidentified, + winuser::VK_OEM_PA2 => Key::Unidentified, + winuser::VK_OEM_PA3 => Key::Unidentified, + winuser::VK_OEM_WSCTRL => Key::Unidentified, + winuser::VK_OEM_CUSEL => Key::Unidentified, + + winuser::VK_OEM_ATTN => Key::Attn, + winuser::VK_OEM_FINISH => { + if is_japanese { + Key::Katakana + } else { + // TODO: use Finish once that gets added to Key + // Key::Finish + Key::Unidentified + } + } + winuser::VK_OEM_COPY => Key::Copy, + winuser::VK_OEM_AUTO => Key::Hankaku, + winuser::VK_OEM_ENLW => Key::Zenkaku, + winuser::VK_OEM_BACKTAB => Key::Romaji, + winuser::VK_ATTN => Key::KanaMode, + winuser::VK_CRSEL => Key::CrSel, + winuser::VK_EXSEL => Key::ExSel, + winuser::VK_EREOF => Key::EraseEof, + winuser::VK_PLAY => Key::Play, + winuser::VK_ZOOM => Key::ZoomToggle, + winuser::VK_NONAME => Key::Unidentified, + winuser::VK_PA1 => Key::Unidentified, + winuser::VK_OEM_CLEAR => Key::Clear, + _ => Key::Unidentified + } +} + +fn native_key_to_code(scancode: PlatformScanCode) -> keyboard_types::Code { // See: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html // and: https://www.w3.org/TR/uievents-code/ // and: The widget/NativeKeyToDOMCodeName.h file in the firefox source @@ -590,290 +1023,3 @@ pub fn native_key_to_code(scancode: PlatformScanCode) -> keyboard_types::Code { _ => Code::Unidentified, } } - -pub fn get_location( - vkey: c_int, - extended: bool, -) -> keyboard_types::Location { - use keyboard_types::{Code, Location}; - use winuser::*; - const VK_ABNT_C2: c_int = 0xc2; - - // Use the native VKEY and the extended flag to cover most cases - // This is taken from the `druid` software within - // druid-shell/src/platform/windows/keyboard.rs - match vkey { - VK_LSHIFT | VK_LCONTROL | VK_LMENU | VK_LWIN => Location::Left, - VK_RSHIFT | VK_RCONTROL | VK_RMENU | VK_RWIN => Location::Right, - VK_RETURN if extended => Location::Numpad, - VK_INSERT | VK_DELETE | VK_END | VK_DOWN | VK_NEXT | VK_LEFT | VK_CLEAR | VK_RIGHT - | VK_HOME | VK_UP | VK_PRIOR => { - if extended { - Location::Standard - } else { - Location::Numpad - } - } - VK_NUMPAD0 | VK_NUMPAD1 | VK_NUMPAD2 | VK_NUMPAD3 | VK_NUMPAD4 | VK_NUMPAD5 - | VK_NUMPAD6 | VK_NUMPAD7 | VK_NUMPAD8 | VK_NUMPAD9 | VK_DECIMAL | VK_DIVIDE - | VK_MULTIPLY | VK_SUBTRACT | VK_ADD | VK_ABNT_C2 => Location::Numpad, - _ => Location::Standard, - } -} - -unsafe fn get_utf16_without_ctrl( - vkey: u32, - scancode: PlatformScanCode, - key_state: &mut [u8; 256], - utf16parts_without_ctrl: &mut Vec -) { - let target_capacity = 8; - let curr_cap = utf16parts_without_ctrl.capacity(); - if curr_cap < target_capacity { - utf16parts_without_ctrl.reserve(target_capacity - curr_cap); - } - // Now remove all CTRL stuff from the keyboard state - key_state[winuser::VK_CONTROL as usize] = 0; - key_state[winuser::VK_LCONTROL as usize] = 0; - key_state[winuser::VK_RCONTROL as usize] = 0; - let unicode_len = winuser::ToUnicode( - vkey, - scancode.0 as u32, - (&mut key_state[0]) as *mut _, - utf16parts_without_ctrl.as_mut_ptr(), - utf16parts_without_ctrl.capacity() as i32, - 0 - ); - if unicode_len < 0 { - utf16parts_without_ctrl.set_len(0); - } else { - utf16parts_without_ctrl.set_len(unicode_len as usize); - } -} - -// TODO: This list might not be complete -fn does_vkey_consume_dead_key(vkey: u32) -> bool { - const A: u32 = 'A' as u32; - const Z: u32 = 'Z' as u32; - const ZERO: u32 = '0' as u32; - const NINE: u32 = '9' as u32; - match vkey { - A..=Z | ZERO..=NINE => return true, - _ => () - } - match vkey as i32 { - // OEM keys - winuser::VK_OEM_1 | winuser::VK_OEM_2 | winuser::VK_OEM_3 | winuser::VK_OEM_4 | - winuser::VK_OEM_5 | winuser::VK_OEM_6 | winuser::VK_OEM_7 | winuser::VK_OEM_8 | - winuser::VK_OEM_PLUS | winuser::VK_OEM_COMMA | winuser::VK_OEM_MINUS | - winuser::VK_OEM_PERIOD => { - true - } - // Other keys - winuser::VK_TAB | winuser::VK_BACK | winuser::VK_RETURN | winuser::VK_SPACE | - winuser::VK_NUMPAD0..=winuser::VK_NUMPAD9 | winuser::VK_MULTIPLY | winuser::VK_ADD | - winuser::VK_SUBTRACT | winuser::VK_DECIMAL | winuser::VK_DIVIDE => { - true - }, - _ => false, - } -} - -/// Warning: this does not cover all possible virtual keys. -/// Most notably it does not cover [A, Z] and [0, 9] -/// Those each have the value of ther corresponding uppercase `char`. -/// E.g. the virtual key A has the value `'A'` -const VIRTUAL_KEY_ENUMS: &'static [i32] = &[ - winuser::VK_LBUTTON, - winuser::VK_RBUTTON, - winuser::VK_CANCEL, - winuser::VK_MBUTTON, - winuser::VK_XBUTTON1, - winuser::VK_XBUTTON2, - winuser::VK_BACK, - winuser::VK_TAB, - winuser::VK_CLEAR, - winuser::VK_RETURN, - winuser::VK_SHIFT, - winuser::VK_CONTROL, - winuser::VK_MENU, - winuser::VK_PAUSE, - winuser::VK_CAPITAL, - winuser::VK_KANA, - winuser::VK_HANGEUL, - winuser::VK_HANGUL, - winuser::VK_JUNJA, - winuser::VK_FINAL, - winuser::VK_HANJA, - winuser::VK_KANJI, - winuser::VK_ESCAPE, - winuser::VK_CONVERT, - winuser::VK_NONCONVERT, - winuser::VK_ACCEPT, - winuser::VK_MODECHANGE, - winuser::VK_SPACE, - winuser::VK_PRIOR, - winuser::VK_NEXT, - winuser::VK_END, - winuser::VK_HOME, - winuser::VK_LEFT, - winuser::VK_UP, - winuser::VK_RIGHT, - winuser::VK_DOWN, - winuser::VK_SELECT, - winuser::VK_PRINT, - winuser::VK_EXECUTE, - winuser::VK_SNAPSHOT, - winuser::VK_INSERT, - winuser::VK_DELETE, - winuser::VK_HELP, - winuser::VK_LWIN, - winuser::VK_RWIN, - winuser::VK_APPS, - winuser::VK_SLEEP, - winuser::VK_NUMPAD0, - winuser::VK_NUMPAD1, - winuser::VK_NUMPAD2, - winuser::VK_NUMPAD3, - winuser::VK_NUMPAD4, - winuser::VK_NUMPAD5, - winuser::VK_NUMPAD6, - winuser::VK_NUMPAD7, - winuser::VK_NUMPAD8, - winuser::VK_NUMPAD9, - winuser::VK_MULTIPLY, - winuser::VK_ADD, - winuser::VK_SEPARATOR, - winuser::VK_SUBTRACT, - winuser::VK_DECIMAL, - winuser::VK_DIVIDE, - winuser::VK_F1, - winuser::VK_F2, - winuser::VK_F3, - winuser::VK_F4, - winuser::VK_F5, - winuser::VK_F6, - winuser::VK_F7, - winuser::VK_F8, - winuser::VK_F9, - winuser::VK_F10, - winuser::VK_F11, - winuser::VK_F12, - winuser::VK_F13, - winuser::VK_F14, - winuser::VK_F15, - winuser::VK_F16, - winuser::VK_F17, - winuser::VK_F18, - winuser::VK_F19, - winuser::VK_F20, - winuser::VK_F21, - winuser::VK_F22, - winuser::VK_F23, - winuser::VK_F24, - winuser::VK_NAVIGATION_VIEW, - winuser::VK_NAVIGATION_MENU, - winuser::VK_NAVIGATION_UP, - winuser::VK_NAVIGATION_DOWN, - winuser::VK_NAVIGATION_LEFT, - winuser::VK_NAVIGATION_RIGHT, - winuser::VK_NAVIGATION_ACCEPT, - winuser::VK_NAVIGATION_CANCEL, - winuser::VK_NUMLOCK, - winuser::VK_SCROLL, - winuser::VK_OEM_NEC_EQUAL, - winuser::VK_OEM_FJ_JISHO, - winuser::VK_OEM_FJ_MASSHOU, - winuser::VK_OEM_FJ_TOUROKU, - winuser::VK_OEM_FJ_LOYA, - winuser::VK_OEM_FJ_ROYA, - winuser::VK_LSHIFT, - winuser::VK_RSHIFT, - winuser::VK_LCONTROL, - winuser::VK_RCONTROL, - winuser::VK_LMENU, - winuser::VK_RMENU, - winuser::VK_BROWSER_BACK, - winuser::VK_BROWSER_FORWARD, - winuser::VK_BROWSER_REFRESH, - winuser::VK_BROWSER_STOP, - winuser::VK_BROWSER_SEARCH, - winuser::VK_BROWSER_FAVORITES, - winuser::VK_BROWSER_HOME, - winuser::VK_VOLUME_MUTE, - winuser::VK_VOLUME_DOWN, - winuser::VK_VOLUME_UP, - winuser::VK_MEDIA_NEXT_TRACK, - winuser::VK_MEDIA_PREV_TRACK, - winuser::VK_MEDIA_STOP, - winuser::VK_MEDIA_PLAY_PAUSE, - winuser::VK_LAUNCH_MAIL, - winuser::VK_LAUNCH_MEDIA_SELECT, - winuser::VK_LAUNCH_APP1, - winuser::VK_LAUNCH_APP2, - winuser::VK_OEM_1, - winuser::VK_OEM_PLUS, - winuser::VK_OEM_COMMA, - winuser::VK_OEM_MINUS, - winuser::VK_OEM_PERIOD, - winuser::VK_OEM_2, - winuser::VK_OEM_3, - winuser::VK_GAMEPAD_A, - winuser::VK_GAMEPAD_B, - winuser::VK_GAMEPAD_X, - winuser::VK_GAMEPAD_Y, - winuser::VK_GAMEPAD_RIGHT_SHOULDER, - winuser::VK_GAMEPAD_LEFT_SHOULDER, - winuser::VK_GAMEPAD_LEFT_TRIGGER, - winuser::VK_GAMEPAD_RIGHT_TRIGGER, - winuser::VK_GAMEPAD_DPAD_UP, - winuser::VK_GAMEPAD_DPAD_DOWN, - winuser::VK_GAMEPAD_DPAD_LEFT, - winuser::VK_GAMEPAD_DPAD_RIGHT, - winuser::VK_GAMEPAD_MENU, - winuser::VK_GAMEPAD_VIEW, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_UP, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_DOWN, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_LEFT, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_UP, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT, - winuser::VK_OEM_4, - winuser::VK_OEM_5, - winuser::VK_OEM_6, - winuser::VK_OEM_7, - winuser::VK_OEM_8, - winuser::VK_OEM_AX, - winuser::VK_OEM_102, - winuser::VK_ICO_HELP, - winuser::VK_ICO_00, - winuser::VK_PROCESSKEY, - winuser::VK_ICO_CLEAR, - winuser::VK_PACKET, - winuser::VK_OEM_RESET, - winuser::VK_OEM_JUMP, - winuser::VK_OEM_PA1, - winuser::VK_OEM_PA2, - winuser::VK_OEM_PA3, - winuser::VK_OEM_WSCTRL, - winuser::VK_OEM_CUSEL, - winuser::VK_OEM_ATTN, - winuser::VK_OEM_FINISH, - winuser::VK_OEM_COPY, - winuser::VK_OEM_AUTO, - winuser::VK_OEM_ENLW, - winuser::VK_OEM_BACKTAB, - winuser::VK_ATTN, - winuser::VK_CRSEL, - winuser::VK_EXSEL, - winuser::VK_EREOF, - winuser::VK_PLAY, - winuser::VK_ZOOM, - winuser::VK_NONAME, - winuser::VK_PA1, - winuser::VK_OEM_CLEAR, -]; From edde42f5e177d8b05638dba16f595a5c28adb81e Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 28 Nov 2020 17:53:25 +0100 Subject: [PATCH 07/75] Run `cargo fmt` --- src/platform_impl/windows/event_loop.rs | 10 +- src/platform_impl/windows/keyboard.rs | 194 ++++++++++++---------- src/platform_impl/windows/window_state.rs | 2 +- 3 files changed, 115 insertions(+), 91 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 829c51eea2..4e51538195 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -773,13 +773,19 @@ unsafe extern "system" fn public_window_callback( } let mut retval = 0; let mut window_state = subclass_input.window_state.lock(); - let event = window_state.key_event_builder.process_message(window, msg, wparam, lparam, &mut retval); + let event = window_state.key_event_builder.process_message( + window, + msg, + wparam, + lparam, + &mut retval, + ); if let Some(event) = event { subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: KeyboardInput { device_id: DEVICE_ID, - event: event, + event, is_synthetic: false, }, }); diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index a38389a614..496d5f51a7 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -1,23 +1,30 @@ use std::{ - os::raw::c_int, fmt, collections::HashMap, - char, mem::MaybeUninit, ffi::OsString, os::windows::ffi::OsStringExt + char, collections::HashMap, ffi::OsString, fmt, mem::MaybeUninit, os::raw::c_int, + os::windows::ffi::OsStringExt, }; use keyboard_types::Key; use winapi::{ - shared::{minwindef::{LRESULT, LPARAM, WPARAM, HKL, LOWORD}, windef::HWND}, - um::{winuser, winnt::{PRIMARYLANGID, LANG_KOREAN, LANG_JAPANESE}} + shared::{ + minwindef::{HKL, LOWORD, LPARAM, LRESULT, WPARAM}, + windef::HWND, + }, + um::{ + winnt::{LANG_JAPANESE, LANG_KOREAN, PRIMARYLANGID}, + winuser, + }, }; - use crate::{ event::{KeyEvent, ScanCode}, platform_impl::platform::event::KeyEventExtra, }; pub fn is_msg_keyboard_related(msg: u32) -> bool { - use winuser::{WM_KEYFIRST, WM_KEYLAST, WM_SETFOCUS, WM_KILLFOCUS, WM_INPUTLANGCHANGE, WM_SHOWWINDOW}; + use winuser::{ + WM_INPUTLANGCHANGE, WM_KEYFIRST, WM_KEYLAST, WM_KILLFOCUS, WM_SETFOCUS, WM_SHOWWINDOW, + }; let is_keyboard_msg = WM_KEYFIRST <= msg && msg <= WM_KEYLAST; is_keyboard_msg @@ -43,7 +50,7 @@ impl ToUnicodeResult { fn is_none(&self) -> bool { match self { ToUnicodeResult::None => true, - _ => false + _ => false, } } @@ -56,7 +63,9 @@ impl ToUnicodeResult { pub struct PlatformScanCode(pub u16); impl fmt::Debug for PlatformScanCode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("PlatformScanCode").field(&format_args!("0x{:04x}", self.0)).finish() + f.debug_tuple("PlatformScanCode") + .field(&format_args!("0x{:04x}", self.0)) + .finish() } } impl PlatformScanCode { @@ -67,11 +76,11 @@ impl PlatformScanCode { } /// Stores information required to make `KeyEvent`s. -/// +/// /// A single winint `KeyEvent` contains information which the windows API passes to the application /// in multiple window messages. In other words a winit `KeyEvent` cannot be build from a single /// window message. Therefore this type keeps track of certain information from previous events so -/// that a `KeyEvent` can be constructed when the last event related to a keypress is received. +/// that a `KeyEvent` can be constructed when the last event related to a keypress is received. /// /// `PeekMessage` is sometimes used to determine wheter the next window message still belongs to the /// current keypress. If it doesn't and the current state represents a key event waiting to be @@ -83,7 +92,7 @@ impl PlatformScanCode { /// - Zero or more WM_CHAR / WM_SYSCHAR. These messages each come with a UTF-16 code unit which when /// put together in the sequence they arrived in, forms the text which is the result of pressing the /// key. -/// +/// /// Key release messages are a bit different due to the fact that they don't contribute to /// text input. The "sequence" only consists of one WM_KEYUP / WM_SYSKEYUP event. pub struct KeyEventBuilder { @@ -106,7 +115,7 @@ pub struct KeyEventBuilder { /// The keyup event needs to call `ToUnicode` to determine what's the text produced by the /// key with all modifiers except CTRL (the `logical_key`). - /// + /// /// But `ToUnicode` without the non-modifying flag (see `key_labels`), resets the dead key /// state which would be incorrect during every keyup event. Therefore this variable is used /// to determine whether the last keydown event produced a dead key. @@ -145,7 +154,7 @@ impl KeyEventBuilder { msg_kind: u32, wparam: WPARAM, lparam: LPARAM, - retval: &mut LRESULT + retval: &mut LRESULT, ) -> Option { match msg_kind { winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { @@ -160,22 +169,20 @@ impl KeyEventBuilder { let vkey = wparam as i32; let lparam_struct = destructure_key_lparam(lparam); - let scancode = PlatformScanCode::new( - lparam_struct.scancode, - lparam_struct.extended - ); + let scancode = + PlatformScanCode::new(lparam_struct.scancode, lparam_struct.extended); let code = native_key_to_code(scancode); let location = get_location(vkey, lparam_struct.extended); let label = self.key_labels.get(&scancode).map(|s| s.clone()); self.event_info = Some(PartialKeyEventInfo { key_state: keyboard_types::KeyState::Down, vkey: wparam as i32, - scancode: scancode, + scancode, is_repeat: lparam_struct.is_repeat, - code: code, - location: location, + code, + location, is_dead: false, - label: label, + label, utf16parts: Vec::with_capacity(8), utf16parts_without_ctrl: Vec::with_capacity(8), }); @@ -206,7 +213,7 @@ impl KeyEventBuilder { hwnd, winuser::WM_KEYFIRST, winuser::WM_KEYLAST, - winuser::PM_NOREMOVE + winuser::PM_NOREMOVE, ); let has_message = has_message != 0; if !has_message { @@ -220,17 +227,20 @@ impl KeyEventBuilder { } } } - + if is_utf16 { - self.event_info.as_mut().unwrap().utf16parts.push(wparam as u16); + self.event_info + .as_mut() + .unwrap() + .utf16parts + .push(wparam as u16); } else { let utf16parts = &mut self.event_info.as_mut().unwrap().utf16parts; let start_offset = utf16parts.len(); let new_size = utf16parts.len() + 2; utf16parts.resize(new_size, 0); if let Some(ch) = char::from_u32(wparam as u32) { - let encode_len = - ch.encode_utf16(&mut utf16parts[start_offset..]).len(); + let encode_len = ch.encode_utf16(&mut utf16parts[start_offset..]).len(); let new_size = start_offset + encode_len; utf16parts.resize(new_size, 0); } @@ -245,10 +255,9 @@ impl KeyEventBuilder { winuser::GetKeyboardState(key_state[0].as_mut_ptr()); let mut key_state = std::mem::transmute::<_, [u8; 256]>(key_state); - let has_ctrl = - key_state[winuser::VK_CONTROL as usize] != 0 || - key_state[winuser::VK_LCONTROL as usize] != 0 || - key_state[winuser::VK_RCONTROL as usize] != 0; + let has_ctrl = key_state[winuser::VK_CONTROL as usize] != 0 + || key_state[winuser::VK_LCONTROL as usize] != 0 + || key_state[winuser::VK_RCONTROL as usize] != 0; // If neither of the CTRL keys is pressed, just use the text with all // modifiers because that already consumed the dead key and otherwise @@ -260,7 +269,7 @@ impl KeyEventBuilder { event_info.vkey as u32, event_info.scancode, &mut key_state, - &mut event_info.utf16parts_without_ctrl + &mut event_info.utf16parts_without_ctrl, ); } } @@ -272,10 +281,8 @@ impl KeyEventBuilder { *retval = 0; let vkey = wparam as i32; let lparam_struct = destructure_key_lparam(lparam); - let scancode = PlatformScanCode::new( - lparam_struct.scancode, - lparam_struct.extended - ); + let scancode = + PlatformScanCode::new(lparam_struct.scancode, lparam_struct.extended); let code = native_key_to_code(scancode); let location = get_location(vkey, lparam_struct.extended); let mut utf16parts = Vec::with_capacity(8); @@ -299,15 +306,15 @@ impl KeyEventBuilder { (&mut key_state[0]) as *mut _, utf16parts.as_mut_ptr(), utf16parts.capacity() as i32, - 0 + 0, ); utf16parts.set_len(unicode_len as usize); - + get_utf16_without_ctrl( wparam as u32, scancode, &mut key_state, - &mut utf16parts_without_ctrl + &mut utf16parts_without_ctrl, ); } } @@ -315,18 +322,18 @@ impl KeyEventBuilder { let event_info = PartialKeyEventInfo { key_state: keyboard_types::KeyState::Up, vkey: wparam as i32, - scancode: scancode, + scancode, is_repeat: false, - code: code, - location: location, + code, + location, is_dead: self.prev_down_was_dead, - label: label, - utf16parts: utf16parts, - utf16parts_without_ctrl: utf16parts_without_ctrl, + label, + utf16parts, + utf16parts_without_ctrl, }; return Some(event_info.finalize(self.known_locale_id, self.has_alt_graph)); } - _ => () + _ => (), } None @@ -347,11 +354,9 @@ impl KeyEventBuilder { // elements. This array is allowed to be indexed by virtual key values // giving the key state for the virtual key used for indexing. for vk in 0..256 { - let scancode = unsafe { winuser::MapVirtualKeyExW( - vk, - winuser::MAPVK_VK_TO_VSC_EX, - locale_identifier as HKL - ) }; + let scancode = unsafe { + winuser::MapVirtualKeyExW(vk, winuser::MAPVK_VK_TO_VSC_EX, locale_identifier as HKL) + }; if scancode == 0 { continue; } @@ -370,13 +375,11 @@ impl KeyEventBuilder { // has AltGr. if !self.has_alt_graph { Self::apply_mod_state(&mut key_state, Self::CONTROL_FLAG); - let key_with_ctrl = Self::ToUnicodeString( - &key_state, vk, scancode, locale_identifier - ); + let key_with_ctrl = + Self::ToUnicodeString(&key_state, vk, scancode, locale_identifier); Self::apply_mod_state(&mut key_state, Self::CONTROL_FLAG | Self::ALT_FLAG); - let key_with_ctrl_alt = Self::ToUnicodeString( - &key_state, vk, scancode, locale_identifier - ); + let key_with_ctrl_alt = + Self::ToUnicodeString(&key_state, vk, scancode, locale_identifier); if key_with_ctrl.is_something() && key_with_ctrl_alt.is_something() { self.has_alt_graph = key_with_ctrl != key_with_ctrl_alt; } @@ -386,7 +389,12 @@ impl KeyEventBuilder { true } - fn ToUnicodeString(key_state: &[u8; 256], vkey: u32, scancode: u32, locale_identifier: usize) -> ToUnicodeResult { + fn ToUnicodeString( + key_state: &[u8; 256], + vkey: u32, + scancode: u32, + locale_identifier: usize, + ) -> ToUnicodeResult { unsafe { let mut label_wide = [0u16; 8]; let wide_len = winuser::ToUnicodeEx( @@ -396,7 +404,7 @@ impl KeyEventBuilder { (&mut label_wide[0]) as *mut _, label_wide.len() as i32, 0, - locale_identifier as _ + locale_identifier as _, ); if wide_len < 0 { // If it's dead, let's run `ToUnicode` again, to consume the dead-key @@ -407,7 +415,7 @@ impl KeyEventBuilder { (&mut label_wide[0]) as *mut _, label_wide.len() as i32, 0, - locale_identifier as _ + locale_identifier as _, ); return ToUnicodeResult::Dead; } @@ -483,7 +491,7 @@ impl PartialKeyEventInfo { Key::Unidentified => { if self.utf16parts_without_ctrl.len() > 0 { logical_key = Key::Character( - String::from_utf16(&self.utf16parts_without_ctrl).unwrap() + String::from_utf16(&self.utf16parts_without_ctrl).unwrap(), ); } else { logical_key = Key::Unidentified; @@ -510,13 +518,13 @@ impl PartialKeyEventInfo { KeyEvent { scancode: ScanCode(self.scancode), physical_key: self.code, - logical_key: logical_key, + logical_key, location: self.location, state: self.key_state, repeat: self.is_repeat, platform_specific: KeyEventExtra { char_with_all_modifers: char_with_all_modifiers, - key_without_modifers: key_without_modifers, + key_without_modifers, }, } } @@ -532,10 +540,7 @@ pub fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { } } -pub fn get_location( - vkey: c_int, - extended: bool, -) -> keyboard_types::Location { +pub fn get_location(vkey: c_int, extended: bool) -> keyboard_types::Location { use keyboard_types::{Code, Location}; use winuser::*; const VK_ABNT_C2: c_int = 0xc2; @@ -566,7 +571,7 @@ unsafe fn get_utf16_without_ctrl( vkey: u32, scancode: PlatformScanCode, key_state: &mut [u8; 256], - utf16parts_without_ctrl: &mut Vec + utf16parts_without_ctrl: &mut Vec, ) { let target_capacity = 8; let curr_cap = utf16parts_without_ctrl.capacity(); @@ -583,7 +588,7 @@ unsafe fn get_utf16_without_ctrl( (&mut key_state[0]) as *mut _, utf16parts_without_ctrl.as_mut_ptr(), utf16parts_without_ctrl.capacity() as i32, - 0 + 0, ); if unicode_len < 0 { utf16parts_without_ctrl.set_len(0); @@ -600,22 +605,33 @@ fn does_vkey_consume_dead_key(vkey: u32) -> bool { const NINE: u32 = '9' as u32; match vkey { A..=Z | ZERO..=NINE => return true, - _ => () + _ => (), } match vkey as i32 { // OEM keys - winuser::VK_OEM_1 | winuser::VK_OEM_2 | winuser::VK_OEM_3 | winuser::VK_OEM_4 | - winuser::VK_OEM_5 | winuser::VK_OEM_6 | winuser::VK_OEM_7 | winuser::VK_OEM_8 | - winuser::VK_OEM_PLUS | winuser::VK_OEM_COMMA | winuser::VK_OEM_MINUS | - winuser::VK_OEM_PERIOD => { - true - } + winuser::VK_OEM_1 + | winuser::VK_OEM_2 + | winuser::VK_OEM_3 + | winuser::VK_OEM_4 + | winuser::VK_OEM_5 + | winuser::VK_OEM_6 + | winuser::VK_OEM_7 + | winuser::VK_OEM_8 + | winuser::VK_OEM_PLUS + | winuser::VK_OEM_COMMA + | winuser::VK_OEM_MINUS + | winuser::VK_OEM_PERIOD => true, // Other keys - winuser::VK_TAB | winuser::VK_BACK | winuser::VK_RETURN | winuser::VK_SPACE | - winuser::VK_NUMPAD0..=winuser::VK_NUMPAD9 | winuser::VK_MULTIPLY | winuser::VK_ADD | - winuser::VK_SUBTRACT | winuser::VK_DECIMAL | winuser::VK_DIVIDE => { - true - }, + winuser::VK_TAB + | winuser::VK_BACK + | winuser::VK_RETURN + | winuser::VK_SPACE + | winuser::VK_NUMPAD0..=winuser::VK_NUMPAD9 + | winuser::VK_MULTIPLY + | winuser::VK_ADD + | winuser::VK_SUBTRACT + | winuser::VK_DECIMAL + | winuser::VK_DIVIDE => true, _ => false, } } @@ -629,15 +645,15 @@ fn vkey_to_non_printable(vkey: i32, hkl: usize, has_alt_graph: bool) -> Key { let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); let is_korean = primary_lang_id == LANG_KOREAN; let is_japanese = primary_lang_id == LANG_JAPANESE; - + match vkey { - winuser::VK_LBUTTON => Key::Unidentified, // Mouse - winuser::VK_RBUTTON => Key::Unidentified, // Mouse + winuser::VK_LBUTTON => Key::Unidentified, // Mouse + winuser::VK_RBUTTON => Key::Unidentified, // Mouse winuser::VK_CANCEL => Key::Unidentified, // I don't think this can be represented with a Key winuser::VK_MBUTTON => Key::Unidentified, // Mouse winuser::VK_XBUTTON1 => Key::Unidentified, // Mouse winuser::VK_XBUTTON2 => Key::Unidentified, // Mouse - winuser::VK_BACK => Key::Backspace, + winuser::VK_BACK => Key::Backspace, winuser::VK_TAB => Key::Tab, winuser::VK_CLEAR => Key::Clear, winuser::VK_RETURN => Key::Enter, @@ -732,7 +748,6 @@ fn vkey_to_non_printable(vkey: i32, hkl: usize, has_alt_graph: bool) -> Key { // winuser::VK_F22 => Key::F22, // winuser::VK_F23 => Key::F23, // winuser::VK_F24 => Key::F24, - winuser::VK_NAVIGATION_VIEW => Key::Unidentified, winuser::VK_NAVIGATION_MENU => Key::Unidentified, winuser::VK_NAVIGATION_UP => Key::Unidentified, @@ -755,8 +770,11 @@ fn vkey_to_non_printable(vkey: i32, hkl: usize, has_alt_graph: bool) -> Key { winuser::VK_RCONTROL => Key::Control, winuser::VK_LMENU => Key::Alt, winuser::VK_RMENU => { - if has_alt_graph { Key::AltGraph } - else { Key::Alt } + if has_alt_graph { + Key::AltGraph + } else { + Key::Alt + } } winuser::VK_BROWSER_BACK => Key::BrowserBack, winuser::VK_BROWSER_FORWARD => Key::BrowserForward, @@ -858,7 +876,7 @@ fn vkey_to_non_printable(vkey: i32, hkl: usize, has_alt_graph: bool) -> Key { winuser::VK_NONAME => Key::Unidentified, winuser::VK_PA1 => Key::Unidentified, winuser::VK_OEM_CLEAR => Key::Clear, - _ => Key::Unidentified + _ => Key::Unidentified, } } diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index f04cae351c..abb9e7b355 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -2,7 +2,7 @@ use crate::{ dpi::{PhysicalPosition, Size}, event::ModifiersState, icon::Icon, - platform_impl::platform::{event_loop, util, keyboard::KeyEventBuilder}, + platform_impl::platform::{event_loop, keyboard::KeyEventBuilder, util}, window::{CursorIcon, Fullscreen, Theme, WindowAttributes}, }; use parking_lot::MutexGuard; From d851d17ac267cfd6d1024a65f52a2eba12629334 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 28 Nov 2020 19:10:08 +0100 Subject: [PATCH 08/75] Fix for AltGraph not being reported --- src/platform_impl/windows/keyboard.rs | 85 ++++++++++++++++----------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 496d5f51a7..44344e3766 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -39,7 +39,7 @@ pub struct KeyLParam { pub is_repeat: bool, } -#[derive(Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq)] enum ToUnicodeResult { Str(String), Dead, @@ -158,7 +158,6 @@ impl KeyEventBuilder { ) -> Option { match msg_kind { winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { - println!("{}, {}", file!(), line!()); self.prev_down_was_dead = false; // When the labels are already generated for this locale, @@ -167,16 +166,20 @@ impl KeyEventBuilder { let locale_id = unsafe { winuser::GetKeyboardLayout(0) }; self.prepare_layout(locale_id as usize); - let vkey = wparam as i32; + //let vkey = wparam as i32; + let lparam_struct = destructure_key_lparam(lparam); let scancode = PlatformScanCode::new(lparam_struct.scancode, lparam_struct.extended); let code = native_key_to_code(scancode); + let vkey = unsafe { + winuser::MapVirtualKeyW(scancode.0 as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 + }; let location = get_location(vkey, lparam_struct.extended); let label = self.key_labels.get(&scancode).map(|s| s.clone()); - self.event_info = Some(PartialKeyEventInfo { + let mut event_info = Some(PartialKeyEventInfo { key_state: keyboard_types::KeyState::Down, - vkey: wparam as i32, + vkey, scancode, is_repeat: lparam_struct.is_repeat, code, @@ -186,7 +189,31 @@ impl KeyEventBuilder { utf16parts: Vec::with_capacity(8), utf16parts_without_ctrl: Vec::with_capacity(8), }); + + let mut next_msg = MaybeUninit::uninit(); + let peek_retval = unsafe { + winuser::PeekMessageW( + next_msg.as_mut_ptr(), + hwnd, + winuser::WM_KEYFIRST, + winuser::WM_KEYLAST, + winuser::PM_NOREMOVE, + ) + }; + let has_next_key_message = peek_retval != 0; *retval = 0; + self.event_info = None; + if has_next_key_message { + let next_msg = unsafe { next_msg.assume_init().message }; + let is_next_keydown = + next_msg == winuser::WM_KEYDOWN || next_msg == winuser::WM_SYSKEYDOWN; + if !is_next_keydown { + self.event_info = event_info.take(); + } + } + if let Some(event_info) = event_info { + return Some(event_info.finalize(locale_id as usize, self.has_alt_graph)); + } } winuser::WM_DEADCHAR | winuser::WM_SYSDEADCHAR => { self.prev_down_was_dead = true; @@ -198,7 +225,6 @@ impl KeyEventBuilder { return Some(event_info.finalize(self.known_locale_id, self.has_alt_graph)); } winuser::WM_CHAR | winuser::WM_SYSCHAR => { - println!("{}, {}", file!(), line!()); *retval = 0; let is_high_surrogate = 0xD800 <= wparam && wparam <= 0xDBFF; let is_low_surrogate = 0xDC00 <= wparam && wparam <= 0xDFFF; @@ -279,11 +305,13 @@ impl KeyEventBuilder { } winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { *retval = 0; - let vkey = wparam as i32; let lparam_struct = destructure_key_lparam(lparam); let scancode = PlatformScanCode::new(lparam_struct.scancode, lparam_struct.extended); let code = native_key_to_code(scancode); + let vkey = unsafe { + winuser::MapVirtualKeyW(scancode.0 as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 + }; let location = get_location(vkey, lparam_struct.extended); let mut utf16parts = Vec::with_capacity(8); let mut utf16parts_without_ctrl = Vec::with_capacity(8); @@ -321,7 +349,7 @@ impl KeyEventBuilder { let label = self.key_labels.get(&scancode).map(|s| s.clone()); let event_info = PartialKeyEventInfo { key_state: keyboard_types::KeyState::Up, - vkey: wparam as i32, + vkey, scancode, is_repeat: false, code, @@ -349,6 +377,7 @@ impl KeyEventBuilder { // simulate a scenario when no modifier is active. let mut key_state = [0u8; 256]; self.key_labels.clear(); + self.has_alt_graph = false; // Virtual key values are in the domain [0, 255]. // This is reinforced by the fact that the keyboard state array has 256 // elements. This array is allowed to be indexed by virtual key values @@ -361,8 +390,8 @@ impl KeyEventBuilder { continue; } Self::apply_mod_state(&mut key_state, 0); - let unicode = Self::ToUnicodeString(&key_state, vk, scancode, locale_identifier); - let unicode_str = match unicode { + let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_identifier); + let unicode_str = match unicode.clone() { ToUnicodeResult::Str(str) => str, _ => continue, }; @@ -374,14 +403,11 @@ impl KeyEventBuilder { // a different result from when it's pressed with CTRL+ALT then the layout // has AltGr. if !self.has_alt_graph { - Self::apply_mod_state(&mut key_state, Self::CONTROL_FLAG); - let key_with_ctrl = - Self::ToUnicodeString(&key_state, vk, scancode, locale_identifier); Self::apply_mod_state(&mut key_state, Self::CONTROL_FLAG | Self::ALT_FLAG); - let key_with_ctrl_alt = - Self::ToUnicodeString(&key_state, vk, scancode, locale_identifier); - if key_with_ctrl.is_something() && key_with_ctrl_alt.is_something() { - self.has_alt_graph = key_with_ctrl != key_with_ctrl_alt; + let unicode_with_ctrl_alt = + Self::to_unicode_string(&key_state, vk, scancode, locale_identifier); + if unicode_with_ctrl_alt.is_something() { + self.has_alt_graph = unicode != unicode_with_ctrl_alt; } } } @@ -389,7 +415,7 @@ impl KeyEventBuilder { true } - fn ToUnicodeString( + fn to_unicode_string( key_state: &[u8; 256], vkey: u32, scancode: u32, @@ -423,8 +449,6 @@ impl KeyEventBuilder { let os_string = OsString::from_wide(&label_wide[0..wide_len as usize]); if let Ok(label_str) = os_string.into_string() { return ToUnicodeResult::Str(label_str); - } else { - println!("Could not transform {:?}", label_wide); } } } @@ -487,22 +511,17 @@ impl PartialKeyEventInfo { logical_key = Key::Dead; } else { let key = vkey_to_non_printable(self.vkey, locale_id, has_alt_gr); - match key { - Key::Unidentified => { - if self.utf16parts_without_ctrl.len() > 0 { - logical_key = Key::Character( - String::from_utf16(&self.utf16parts_without_ctrl).unwrap(), - ); - } else { - logical_key = Key::Unidentified; - } - } - key @ _ => { - logical_key = key; + if key == Key::Unidentified { + if self.utf16parts_without_ctrl.len() > 0 { + logical_key = + Key::Character(String::from_utf16(&self.utf16parts_without_ctrl).unwrap()); + } else { + logical_key = Key::Unidentified; } + } else { + logical_key = key; } } - let key_without_modifers; if let Some(label) = &self.label { key_without_modifers = Key::Character(label.clone()); From c9c648bf1339c1df3b556cc1cb82b3b6cabcbef0 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 5 Dec 2020 00:32:56 +0100 Subject: [PATCH 09/75] Synthesize key events when focus enters or leaves --- src/event.rs | 8 + src/platform_impl/windows/event_loop.rs | 73 +++++-- src/platform_impl/windows/keyboard.rs | 248 ++++++++++++++++++++-- src/platform_impl/windows/minimal_ime.rs | 94 ++++++++ src/platform_impl/windows/mod.rs | 1 + src/platform_impl/windows/window_state.rs | 6 +- 6 files changed, 384 insertions(+), 46 deletions(-) create mode 100644 src/platform_impl/windows/minimal_ime.rs diff --git a/src/event.rs b/src/event.rs index 7061efa506..6c76556b0e 100644 --- a/src/event.rs +++ b/src/event.rs @@ -242,6 +242,12 @@ pub enum WindowEvent<'a> { /// The window received a unicode character. ReceivedCharacter(char), + /// The user commited an IME string for this window. + /// + /// This is a temporary API until #1497 gets completed. See: + /// https://github.com/rust-windowing/winit/issues/1497 + ReceivedImeText(String), + /// The window gained or lost focus. /// /// The parameter is true if the window has gained focus, and false if it has lost focus. @@ -382,6 +388,7 @@ impl Clone for WindowEvent<'static> { HoveredFile(file) => HoveredFile(file.clone()), HoveredFileCancelled => HoveredFileCancelled, ReceivedCharacter(c) => ReceivedCharacter(*c), + ReceivedImeText(s) => ReceivedImeText(s.clone()), Focused(f) => Focused(*f), KeyboardInput_DEPRECATED { device_id, @@ -481,6 +488,7 @@ impl<'a> WindowEvent<'a> { HoveredFile(file) => Some(HoveredFile(file)), HoveredFileCancelled => Some(HoveredFileCancelled), ReceivedCharacter(c) => Some(ReceivedCharacter(c)), + ReceivedImeText(s) => Some(ReceivedImeText(s)), Focused(focused) => Some(Focused(focused)), KeyboardInput_DEPRECATED { device_id, diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 4e51538195..5025534f5d 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -41,6 +41,7 @@ use crate::{ drop_handler::FileDropHandler, event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, keyboard::is_msg_keyboard_related, + minimal_ime::is_msg_ime_related, monitor::{self, MonitorHandle}, raw_input, util, window_state::{CursorFlags, WindowFlags, WindowState}, @@ -763,41 +764,65 @@ unsafe extern "system" fn public_window_callback( winuser::RDW_INTERNALPAINT, ); + let mut retval = None; let keyboard_callback = || { use crate::event::WindowEvent::KeyboardInput; let is_keyboard_related = is_msg_keyboard_related(msg); if !is_keyboard_related { // We return early to avoid a deadlock from locking the window state // when not appropriate. - return -1; + return retval; } - let mut retval = 0; - let mut window_state = subclass_input.window_state.lock(); - let event = window_state.key_event_builder.process_message( - window, - msg, - wparam, - lparam, - &mut retval, - ); - if let Some(event) = event { + let events = { + let mut window_state = subclass_input.window_state.lock(); + window_state + .key_event_builder + .process_message(window, msg, wparam, lparam, &mut retval) + }; + if let Some(events) = events { + for event in events { + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: KeyboardInput { + device_id: DEVICE_ID, + event: event.event, + is_synthetic: event.is_synthetic, + }, + }); + } + } + retval + }; + + retval = subclass_input + .event_loop_runner + .catch_unwind(keyboard_callback) + .unwrap_or(Some(-1)); + + let ime_callback = || { + use crate::event::WindowEvent::ReceivedImeText; + let is_ime_related = is_msg_ime_related(msg); + if !is_ime_related { + return retval; + } + let text = { + let mut window_state = subclass_input.window_state.lock(); + window_state + .ime_handler + .process_message(window, msg, wparam, lparam, &mut retval) + }; + if let Some(str) = text { subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), - event: KeyboardInput { - device_id: DEVICE_ID, - event, - is_synthetic: false, - }, + event: ReceivedImeText(str), }); } retval }; - - // TODO: merge this retval with whatever is done in the following. - let retval = subclass_input + retval = subclass_input .event_loop_runner - .catch_unwind(keyboard_callback) - .unwrap_or(-1); + .catch_unwind(ime_callback) + .unwrap_or(Some(-1)); // I decided to bind the closure to `callback` and pass it to catch_unwind rather than passing // the closure to catch_unwind directly so that the match body indendation wouldn't change and @@ -1942,7 +1967,11 @@ unsafe extern "system" fn public_window_callback( }); 0 } else { - commctrl::DefSubclassProc(window, msg, wparam, lparam) + if let Some(retval) = retval { + retval + } else { + commctrl::DefSubclassProc(window, msg, wparam, lparam) + } } } }; diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 44344e3766..9b860d0e70 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -7,7 +7,7 @@ use keyboard_types::Key; use winapi::{ shared::{ - minwindef::{HKL, LOWORD, LPARAM, LRESULT, WPARAM}, + minwindef::{HKL, LOWORD, LPARAM, LRESULT, UINT, WPARAM}, windef::HWND, }, um::{ @@ -22,12 +22,10 @@ use crate::{ }; pub fn is_msg_keyboard_related(msg: u32) -> bool { - use winuser::{ - WM_INPUTLANGCHANGE, WM_KEYFIRST, WM_KEYLAST, WM_KILLFOCUS, WM_SETFOCUS, WM_SHOWWINDOW, - }; + use winuser::{WM_KEYFIRST, WM_KEYLAST, WM_KILLFOCUS, WM_SETFOCUS}; let is_keyboard_msg = WM_KEYFIRST <= msg && msg <= WM_KEYLAST; - is_keyboard_msg + is_keyboard_msg || msg == WM_SETFOCUS || msg == WM_KILLFOCUS } #[derive(Debug, Copy, Clone)] @@ -75,6 +73,11 @@ impl PlatformScanCode { } } +pub struct MessageAsKeyEvent { + pub event: KeyEvent, + pub is_synthetic: bool, +} + /// Stores information required to make `KeyEvent`s. /// /// A single winint `KeyEvent` contains information which the windows API passes to the application @@ -104,7 +107,10 @@ pub struct KeyEventBuilder { /// just when the key is pressed/released would be enough if `ToUnicode` wouldn't /// change the keyboard state (it clears the dead key). There is a flag to prevent /// changing the state but that flag requires Windows 10, version 1607 or newer) - key_labels: HashMap, + key_text: HashMap, + + /// Same as `key_text` but as if caps-lock was pressed. + key_text_with_caps: HashMap, /// True if the keyboard layout belonging to `known_locale_id` has an AltGr key. has_alt_graph: bool, @@ -132,7 +138,8 @@ impl Default for KeyEventBuilder { fn default() -> Self { KeyEventBuilder { event_info: None, - key_labels: HashMap::with_capacity(128), + key_text: HashMap::with_capacity(128), + key_text_with_caps: HashMap::with_capacity(128), has_alt_graph: false, known_locale_id: 0, prev_down_was_dead: false, @@ -154,9 +161,23 @@ impl KeyEventBuilder { msg_kind: u32, wparam: WPARAM, lparam: LPARAM, - retval: &mut LRESULT, - ) -> Option { + retval: &mut Option, + ) -> Option> { match msg_kind { + winuser::WM_SETFOCUS => { + // synthesize keydown events + let key_events = self.synthesize_kbd_state(keyboard_types::KeyState::Down); + if !key_events.is_empty() { + return Some(key_events); + } + } + winuser::WM_KILLFOCUS => { + // sythesize keyup events + let key_events = self.synthesize_kbd_state(keyboard_types::KeyState::Up); + if !key_events.is_empty() { + return Some(key_events); + } + } winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { self.prev_down_was_dead = false; @@ -176,7 +197,7 @@ impl KeyEventBuilder { winuser::MapVirtualKeyW(scancode.0 as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 }; let location = get_location(vkey, lparam_struct.extended); - let label = self.key_labels.get(&scancode).map(|s| s.clone()); + let label = self.key_text.get(&scancode).map(|s| s.clone()); let mut event_info = Some(PartialKeyEventInfo { key_state: keyboard_types::KeyState::Down, vkey, @@ -201,7 +222,7 @@ impl KeyEventBuilder { ) }; let has_next_key_message = peek_retval != 0; - *retval = 0; + *retval = Some(0); self.event_info = None; if has_next_key_message { let next_msg = unsafe { next_msg.assume_init().message }; @@ -212,7 +233,11 @@ impl KeyEventBuilder { } } if let Some(event_info) = event_info { - return Some(event_info.finalize(locale_id as usize, self.has_alt_graph)); + let ev = event_info.finalize(locale_id as usize, self.has_alt_graph); + return Some(vec![MessageAsKeyEvent { + event: ev, + is_synthetic: false, + }]); } } winuser::WM_DEADCHAR | winuser::WM_SYSDEADCHAR => { @@ -221,11 +246,15 @@ impl KeyEventBuilder { // this key press let mut event_info = self.event_info.take().unwrap(); event_info.is_dead = true; - *retval = 0; - return Some(event_info.finalize(self.known_locale_id, self.has_alt_graph)); + *retval = Some(0); + let ev = event_info.finalize(self.known_locale_id, self.has_alt_graph); + return Some(vec![MessageAsKeyEvent { + event: ev, + is_synthetic: false, + }]); } winuser::WM_CHAR | winuser::WM_SYSCHAR => { - *retval = 0; + *retval = Some(0); let is_high_surrogate = 0xD800 <= wparam && wparam <= 0xDBFF; let is_low_surrogate = 0xDC00 <= wparam && wparam <= 0xDFFF; @@ -300,11 +329,15 @@ impl KeyEventBuilder { } } - return Some(event_info.finalize(self.known_locale_id, self.has_alt_graph)); + let ev = event_info.finalize(self.known_locale_id, self.has_alt_graph); + return Some(vec![MessageAsKeyEvent { + event: ev, + is_synthetic: false, + }]); } } winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { - *retval = 0; + *retval = Some(0); let lparam_struct = destructure_key_lparam(lparam); let scancode = PlatformScanCode::new(lparam_struct.scancode, lparam_struct.extended); @@ -346,7 +379,7 @@ impl KeyEventBuilder { ); } } - let label = self.key_labels.get(&scancode).map(|s| s.clone()); + let label = self.key_text.get(&scancode).map(|s| s.clone()); let event_info = PartialKeyEventInfo { key_state: keyboard_types::KeyState::Up, vkey, @@ -359,7 +392,11 @@ impl KeyEventBuilder { utf16parts, utf16parts_without_ctrl, }; - return Some(event_info.finalize(self.known_locale_id, self.has_alt_graph)); + let ev = event_info.finalize(self.known_locale_id, self.has_alt_graph); + return Some(vec![MessageAsKeyEvent { + event: ev, + is_synthetic: false, + }]); } _ => (), } @@ -376,7 +413,8 @@ impl KeyEventBuilder { // We initialize the keyboard state with all zeros to // simulate a scenario when no modifier is active. let mut key_state = [0u8; 256]; - self.key_labels.clear(); + self.key_text.clear(); + self.key_text_with_caps.clear(); self.has_alt_graph = false; // Virtual key values are in the domain [0, 255]. // This is reinforced by the fact that the keyboard state array has 256 @@ -389,14 +427,24 @@ impl KeyEventBuilder { if scancode == 0 { continue; } + let platform_scancode = PlatformScanCode(scancode as u16); + Self::apply_mod_state(&mut key_state, 0); + + // Caps lock is not pressed but it's on. + key_state[winuser::VK_CAPITAL as usize] = 1; + let unicode_caps = Self::to_unicode_string(&key_state, vk, scancode, locale_identifier); + if let ToUnicodeResult::Str(str) = unicode_caps { + self.key_text_with_caps.insert(platform_scancode, str); + } + key_state[winuser::VK_CAPITAL as usize] = 0; + let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_identifier); let unicode_str = match unicode.clone() { ToUnicodeResult::Str(str) => str, _ => continue, }; - let platform_scancode = PlatformScanCode(scancode as u16); - self.key_labels.insert(platform_scancode, unicode_str); + self.key_text.insert(platform_scancode, unicode_str); // Check for alt graph. // The logic is that if a key pressed with the CTRL modifier produces @@ -483,6 +531,160 @@ impl KeyEventBuilder { key_state[winuser::VK_CAPITAL as usize] &= !0x80; } } + + fn synthesize_kbd_state( + &mut self, + key_state: keyboard_types::KeyState, + ) -> Vec { + let mut key_events = Vec::new(); + let locale_id = unsafe { winuser::GetKeyboardLayout(0) }; + self.prepare_layout(locale_id as usize); + + let kbd_state = unsafe { + let mut kbd_state: [MaybeUninit; 256] = [MaybeUninit::uninit(); 256]; + winuser::GetKeyboardState(kbd_state[0].as_mut_ptr()); + std::mem::transmute::<_, [u8; 256]>(kbd_state) + }; + macro_rules! is_key_pressed { + ($vk:expr) => { + kbd_state[$vk as usize] & 0x80 != 0 + }; + } + + // Is caps-lock active? Be careful that this is different from caps-lock + // being held down. + let caps_lock_on = kbd_state[winuser::VK_CAPITAL as usize] & 1 != 0; + + // We are synthesizing the press event for caps-lock first. The reason: + // 1, if caps-lock is *not* held down but it's active, than we have to + // synthesize all printable keys, respecting the calps-lock state + // 2, if caps-lock is held down, we could choose to sythesize it's + // keypress after every other key, in which case all other keys *must* + // be sythesized as if the caps-lock state would be the opposite + // of what it currently is. + // -- + // For the sake of simplicity we are choosing to always sythesize + // caps-lock first, and always use the current caps-lock state + // to determine the produced text + if is_key_pressed!(winuser::VK_CAPITAL) { + let event = + self.create_synthetic(winuser::VK_CAPITAL, key_state, locale_id, caps_lock_on); + if let Some(event) = event { + key_events.push(event); + } + } + let do_non_modifier = |key_events: &mut Vec<_>| { + for vk in 0..256 { + match vk { + winuser::VK_CONTROL + | winuser::VK_LCONTROL + | winuser::VK_RCONTROL + | winuser::VK_SHIFT + | winuser::VK_LSHIFT + | winuser::VK_RSHIFT + | winuser::VK_MENU + | winuser::VK_LMENU + | winuser::VK_RMENU + | winuser::VK_CAPITAL => continue, + _ => (), + } + if !is_key_pressed!(vk) { + continue; + } + if let Some(event) = self.create_synthetic(vk, key_state, locale_id, caps_lock_on) { + key_events.push(event); + } + } + }; + let do_modifier = |key_events: &mut Vec<_>| { + const CLEAR_MODIFIER_VKS: [i32; 6] = [ + winuser::VK_LCONTROL, + winuser::VK_LSHIFT, + winuser::VK_LMENU, + winuser::VK_RCONTROL, + winuser::VK_RSHIFT, + winuser::VK_RMENU, + ]; + for vk in CLEAR_MODIFIER_VKS.iter() { + if is_key_pressed!(*vk) { + let event = self.create_synthetic(*vk, key_state, locale_id, caps_lock_on); + if let Some(event) = event { + key_events.push(event); + } + } + } + }; + + // Be cheeky and sythesize sequence modifier and non-modifier + // key events such that non-modifier keys are not affected + // by modifiers (except for caps-lock) + match key_state { + keyboard_types::KeyState::Down => { + do_non_modifier(&mut key_events); + do_modifier(&mut key_events); + } + keyboard_types::KeyState::Up => { + do_modifier(&mut key_events); + do_non_modifier(&mut key_events); + } + } + + key_events + } + + fn create_synthetic( + &self, + vk: i32, + key_state: keyboard_types::KeyState, + locale_id: HKL, + caps_lock_on: bool, + ) -> Option { + let scancode = unsafe { + winuser::MapVirtualKeyExW(vk as UINT, winuser::MAPVK_VK_TO_VSC_EX, locale_id) + }; + if scancode == 0 { + return None; + } + let is_extended = (scancode & 0xE000) == 0xE000; + let platform_scancode = PlatformScanCode(scancode as u16); + + let key_text = self.key_text.get(&platform_scancode).cloned(); + let key_text_with_caps = self.key_text_with_caps.get(&platform_scancode).cloned(); + let logical_key = match &key_text { + Some(str) => { + if caps_lock_on { + match key_text_with_caps.clone() { + Some(str) => keyboard_types::Key::Character(str), + None => keyboard_types::Key::Unidentified, + } + } else { + keyboard_types::Key::Character(str.clone()) + } + } + None => vkey_to_non_printable(vk, locale_id as usize, self.has_alt_graph), + }; + + let event_info = PartialKeyEventInfo { + key_state, + vkey: vk, + scancode: platform_scancode, + is_repeat: false, + code: native_key_to_code(platform_scancode), + location: get_location(vk, is_extended), + is_dead: false, + label: key_text, + utf16parts: Vec::with_capacity(8), + utf16parts_without_ctrl: Vec::with_capacity(8), + }; + + let mut event = event_info.finalize(locale_id as usize, self.has_alt_graph); + event.logical_key = logical_key; + event.platform_specific.char_with_all_modifers = key_text_with_caps; + Some(MessageAsKeyEvent { + event, + is_synthetic: true, + }) + } } struct PartialKeyEventInfo { @@ -560,7 +762,7 @@ pub fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { } pub fn get_location(vkey: c_int, extended: bool) -> keyboard_types::Location { - use keyboard_types::{Code, Location}; + use keyboard_types::Location; use winuser::*; const VK_ABNT_C2: c_int = 0xc2; diff --git a/src/platform_impl/windows/minimal_ime.rs b/src/platform_impl/windows/minimal_ime.rs new file mode 100644 index 0000000000..e1d3ee78b2 --- /dev/null +++ b/src/platform_impl/windows/minimal_ime.rs @@ -0,0 +1,94 @@ +use std::mem::MaybeUninit; + +use winapi::{ + shared::{ + minwindef::{HKL, LOWORD, LPARAM, LRESULT, WPARAM}, + windef::HWND, + }, + um::{ + winnt::{LANG_JAPANESE, LANG_KOREAN, PRIMARYLANGID}, + winuser, + }, +}; + +pub fn is_msg_ime_related(msg_kind: u32) -> bool { + match msg_kind { + winuser::WM_IME_COMPOSITION + | winuser::WM_IME_COMPOSITIONFULL + | winuser::WM_IME_STARTCOMPOSITION + | winuser::WM_IME_ENDCOMPOSITION + | winuser::WM_IME_CHAR + | winuser::WM_CHAR + | winuser::WM_SYSCHAR => true, + _ => false, + } +} + +pub struct MinimalIme { + // True if currently receiving messages belonging to finished IME session. + getting_ime_text: bool, + + utf16parts: Vec, +} +impl Default for MinimalIme { + fn default() -> Self { + MinimalIme { + getting_ime_text: false, + utf16parts: Vec::with_capacity(16), + } + } +} +impl MinimalIme { + pub fn process_message( + &mut self, + hwnd: HWND, + msg_kind: u32, + wparam: WPARAM, + _lparam: LPARAM, + retval: &mut Option, + ) -> Option { + match msg_kind { + winuser::WM_IME_ENDCOMPOSITION => { + self.getting_ime_text = true; + } + winuser::WM_CHAR | winuser::WM_SYSCHAR => { + if self.getting_ime_text { + *retval = Some(0); + self.utf16parts.push(wparam as u16); + + let more_char_coming; + unsafe { + let mut next_msg = MaybeUninit::uninit(); + let has_message = winuser::PeekMessageW( + next_msg.as_mut_ptr(), + hwnd, + winuser::WM_KEYFIRST, + winuser::WM_KEYLAST, + winuser::PM_NOREMOVE, + ); + let has_message = has_message != 0; + if !has_message { + more_char_coming = false; + } else { + let next_msg = next_msg.assume_init().message; + if next_msg == winuser::WM_CHAR || next_msg == winuser::WM_SYSCHAR { + more_char_coming = true; + } else { + more_char_coming = false; + } + } + } + if !more_char_coming { + let result = String::from_utf16(&self.utf16parts).unwrap(); + self.utf16parts.clear(); + self.getting_ime_text = false; + return Some(result); + } + } + } + _ => (), + } + + None + } +} diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 2198e49ec1..d17b887347 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -96,6 +96,7 @@ mod event; mod event_loop; mod icon; mod keyboard; +mod minimal_ime; mod monitor; mod raw_input; mod window; diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index abb9e7b355..a281b9ab0b 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -2,7 +2,9 @@ use crate::{ dpi::{PhysicalPosition, Size}, event::ModifiersState, icon::Icon, - platform_impl::platform::{event_loop, keyboard::KeyEventBuilder, util}, + platform_impl::platform::{ + event_loop, keyboard::KeyEventBuilder, minimal_ime::MinimalIme, util, + }, window::{CursorIcon, Fullscreen, Theme, WindowAttributes}, }; use parking_lot::MutexGuard; @@ -36,6 +38,7 @@ pub struct WindowState { pub high_surrogate: Option, pub key_event_builder: KeyEventBuilder, + pub ime_handler: MinimalIme, window_flags: WindowFlags, } @@ -131,6 +134,7 @@ impl WindowState { preferred_theme, high_surrogate: None, key_event_builder: KeyEventBuilder::default(), + ime_handler: MinimalIme::default(), window_flags: WindowFlags::empty(), } } From 3ea6d1475b9e29519501bb3c1d626419a95fbac6 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 5 Dec 2020 11:25:35 +0100 Subject: [PATCH 10/75] Minor improvements --- src/platform_impl/windows/event_loop.rs | 20 +++++++--------- src/platform_impl/windows/keyboard.rs | 31 ++++++++++++++----------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 5025534f5d..c147492d54 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -779,17 +779,15 @@ unsafe extern "system" fn public_window_callback( .key_event_builder .process_message(window, msg, wparam, lparam, &mut retval) }; - if let Some(events) = events { - for event in events { - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: KeyboardInput { - device_id: DEVICE_ID, - event: event.event, - is_synthetic: event.is_synthetic, - }, - }); - } + for event in events { + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: KeyboardInput { + device_id: DEVICE_ID, + event: event.event, + is_synthetic: event.is_synthetic, + }, + }); } retval }; diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 9b860d0e70..1d4a35b723 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -162,20 +162,20 @@ impl KeyEventBuilder { wparam: WPARAM, lparam: LPARAM, retval: &mut Option, - ) -> Option> { + ) -> Vec { match msg_kind { winuser::WM_SETFOCUS => { // synthesize keydown events let key_events = self.synthesize_kbd_state(keyboard_types::KeyState::Down); if !key_events.is_empty() { - return Some(key_events); + return key_events; } } winuser::WM_KILLFOCUS => { // sythesize keyup events let key_events = self.synthesize_kbd_state(keyboard_types::KeyState::Up); if !key_events.is_empty() { - return Some(key_events); + return key_events; } } winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { @@ -234,10 +234,10 @@ impl KeyEventBuilder { } if let Some(event_info) = event_info { let ev = event_info.finalize(locale_id as usize, self.has_alt_graph); - return Some(vec![MessageAsKeyEvent { + return vec![MessageAsKeyEvent { event: ev, is_synthetic: false, - }]); + }]; } } winuser::WM_DEADCHAR | winuser::WM_SYSDEADCHAR => { @@ -248,10 +248,10 @@ impl KeyEventBuilder { event_info.is_dead = true; *retval = Some(0); let ev = event_info.finalize(self.known_locale_id, self.has_alt_graph); - return Some(vec![MessageAsKeyEvent { + return vec![MessageAsKeyEvent { event: ev, is_synthetic: false, - }]); + }]; } winuser::WM_CHAR | winuser::WM_SYSCHAR => { *retval = Some(0); @@ -330,10 +330,10 @@ impl KeyEventBuilder { } let ev = event_info.finalize(self.known_locale_id, self.has_alt_graph); - return Some(vec![MessageAsKeyEvent { + return vec![MessageAsKeyEvent { event: ev, is_synthetic: false, - }]); + }]; } } winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { @@ -393,15 +393,15 @@ impl KeyEventBuilder { utf16parts_without_ctrl, }; let ev = event_info.finalize(self.known_locale_id, self.has_alt_graph); - return Some(vec![MessageAsKeyEvent { + return vec![MessageAsKeyEvent { event: ev, is_synthetic: false, - }]); + }]; } _ => (), } - None + Vec::new() } /// Returns true if succeeded. @@ -1079,8 +1079,11 @@ fn vkey_to_non_printable(vkey: i32, hkl: usize, has_alt_graph: bool) -> Key { if is_japanese { Key::Katakana } else { - // TODO: use Finish once that gets added to Key - // Key::Finish + // This matches IE and Firefox behaviour according to + // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values + // At the time of writing there is no `Key::Finish` variant as + // Finish is not mentionned at https://w3c.github.io/uievents-key/ + // Also see: https://github.com/pyfisch/keyboard-types/issues/9 Key::Unidentified } } From 3442c5dff5b7da180858b01b14d91779f2de8f81 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 5 Dec 2020 14:43:12 +0100 Subject: [PATCH 11/75] Remove obsolete API --- src/event.rs | 284 +---------------------- src/platform_impl/windows/event.rs | 270 +-------------------- src/platform_impl/windows/event_loop.rs | 190 +++------------ src/platform_impl/windows/keyboard.rs | 2 +- src/platform_impl/windows/minimal_ime.rs | 7 +- 5 files changed, 51 insertions(+), 702 deletions(-) diff --git a/src/event.rs b/src/event.rs index 6c76556b0e..5342ebf5d4 100644 --- a/src/event.rs +++ b/src/event.rs @@ -239,9 +239,6 @@ pub enum WindowEvent<'a> { /// hovered. HoveredFileCancelled, - /// The window received a unicode character. - ReceivedCharacter(char), - /// The user commited an IME string for this window. /// /// This is a temporary API until #1497 gets completed. See: @@ -254,22 +251,6 @@ pub enum WindowEvent<'a> { Focused(bool), /// An event from the keyboard has been received. - // TODO: Remove this - KeyboardInput_DEPRECATED { - device_id: DeviceId, - input: KeyboardInput, - /// If `true`, the event was generated synthetically by winit - /// in one of the following circumstances: - /// - /// * 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, - }, - KeyboardInput { device_id: DeviceId, event: KeyEvent, @@ -387,18 +368,8 @@ impl Clone for WindowEvent<'static> { DroppedFile(file) => DroppedFile(file.clone()), HoveredFile(file) => HoveredFile(file.clone()), HoveredFileCancelled => HoveredFileCancelled, - ReceivedCharacter(c) => ReceivedCharacter(*c), ReceivedImeText(s) => ReceivedImeText(s.clone()), Focused(f) => Focused(*f), - KeyboardInput_DEPRECATED { - device_id, - input, - is_synthetic, - } => KeyboardInput_DEPRECATED { - device_id: *device_id, - input: *input, - is_synthetic: *is_synthetic, - }, KeyboardInput { device_id, event, @@ -487,18 +458,8 @@ impl<'a> WindowEvent<'a> { DroppedFile(file) => Some(DroppedFile(file)), HoveredFile(file) => Some(HoveredFile(file)), HoveredFileCancelled => Some(HoveredFileCancelled), - ReceivedCharacter(c) => Some(ReceivedCharacter(c)), ReceivedImeText(s) => Some(ReceivedImeText(s)), Focused(focused) => Some(Focused(focused)), - KeyboardInput_DEPRECATED { - device_id, - input, - is_synthetic, - } => Some(KeyboardInput_DEPRECATED { - device_id, - input, - is_synthetic, - }), KeyboardInput { device_id, event, @@ -630,43 +591,25 @@ pub enum DeviceEvent { state: ElementState, }, - Key_DEPRECATED(KeyboardInput), + Key(RawKeyEvent), Text { codepoint: char, }, } -/// Describes a keyboard input event. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct KeyboardInput { - /// Identifies the physical key pressed - /// - /// This should not change if the user adjusts the host's keyboard map. Use when the physical location of the - /// key is more important than the key's host GUI semantics, such as for movement controls in a first-person - /// game. - pub scancode: ScanCode_DEPRECATED, - - pub state: ElementState, - - /// Identifies the semantic meaning of the key - /// - /// Use when the semantics of the key are more important than the physical location of the key, such as when - /// implementing appropriate behavior for "page up." - pub virtual_keycode: Option, - - /// Modifier keys active at the time of this input. - /// - /// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from - /// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere. - #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"] - pub modifiers: ModifiersState, +/// Describes a keyboard input as a raw device event. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct RawKeyEvent { + pub scancode: ScanCode, + pub physical_key: keyboard_types::Code, + pub state: keyboard_types::KeyState, } -// TODO: implement minimal IME API acting as a stopgap until #1497 gets completed. - -// TODO: Implement (de)serialization +/// Describes a keyboard input targeting a window. +// TODO: Implement (de)serialization. +// (This struct cannot be serialized in its entirety because `ScanCode` +// contains platform dependent data so that value cannot be serialized) #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEvent { pub scancode: ScanCode, @@ -814,8 +757,6 @@ impl Force { #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct ScanCode(pub(crate) platform_impl::PlatformScanCode); -pub type ScanCode_DEPRECATED = u32; - /// Identifier for a specific analog axis on some device. pub type AxisId = u32; @@ -859,209 +800,6 @@ pub enum MouseScrollDelta { PixelDelta(PhysicalPosition), } -/// Symbolic name for a keyboard key. -#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)] -#[repr(u32)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum VirtualKeyCode { - /// The '1' key over the letters. - Key1, - /// The '2' key over the letters. - Key2, - /// The '3' key over the letters. - Key3, - /// The '4' key over the letters. - Key4, - /// The '5' key over the letters. - Key5, - /// The '6' key over the letters. - Key6, - /// The '7' key over the letters. - Key7, - /// The '8' key over the letters. - Key8, - /// The '9' key over the letters. - Key9, - /// The '0' key over the 'O' and 'P' keys. - Key0, - - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - - /// The Escape key, next to F1. - Escape, - - F1, - F2, - F3, - F4, - F5, - F6, - F7, - F8, - F9, - F10, - F11, - F12, - F13, - F14, - F15, - F16, - F17, - F18, - F19, - F20, - F21, - F22, - F23, - F24, - - /// Print Screen/SysRq. - Snapshot, - /// Scroll Lock. - Scroll, - /// Pause/Break key, next to Scroll lock. - Pause, - - /// `Insert`, next to Backspace. - Insert, - Home, - Delete, - End, - PageDown, - PageUp, - - Left, - Up, - Right, - Down, - - /// The Backspace key, right over Enter. - // TODO: rename - Back, - /// The Enter key. - Return, - /// The space bar. - Space, - - /// The "Compose" key on Linux. - Compose, - - Caret, - - Numlock, - Numpad0, - Numpad1, - Numpad2, - Numpad3, - Numpad4, - Numpad5, - Numpad6, - Numpad7, - Numpad8, - Numpad9, - NumpadAdd, - NumpadDivide, - NumpadDecimal, - NumpadComma, - NumpadEnter, - NumpadEquals, - NumpadMultiply, - NumpadSubtract, - - AbntC1, - AbntC2, - Apostrophe, - Apps, - Asterisk, - At, - Ax, - Backslash, - Calculator, - Capital, - Colon, - Comma, - Convert, - Equals, - Grave, - Kana, - Kanji, - LAlt, - LBracket, - LControl, - LShift, - LWin, - Mail, - MediaSelect, - MediaStop, - Minus, - Mute, - MyComputer, - // also called "Next" - NavigateForward, - // also called "Prior" - NavigateBackward, - NextTrack, - NoConvert, - OEM102, - Period, - PlayPause, - Plus, - Power, - PrevTrack, - RAlt, - RBracket, - RControl, - RShift, - RWin, - Semicolon, - Slash, - Sleep, - Stop, - Sysrq, - Tab, - Underline, - Unlabeled, - VolumeDown, - VolumeUp, - Wake, - WebBack, - WebFavorites, - WebForward, - WebHome, - WebRefresh, - WebSearch, - WebStop, - Yen, - Copy, - Paste, - Cut, -} - impl ModifiersState { /// Returns `true` if the shift key is pressed. pub fn shift(&self) -> bool { diff --git a/src/platform_impl/windows/event.rs b/src/platform_impl/windows/event.rs index d6a453cfed..07e5d788e4 100644 --- a/src/platform_impl/windows/event.rs +++ b/src/platform_impl/windows/event.rs @@ -5,10 +5,10 @@ use std::{ sync::atomic::{AtomicBool, AtomicPtr, Ordering}, }; -use crate::event::{ModifiersState, ScanCode_DEPRECATED, VirtualKeyCode}; +use crate::event::ModifiersState; use winapi::{ - shared::minwindef::{HKL, HKL__, LPARAM, UINT, WPARAM}, + shared::minwindef::{HKL, HKL__}, um::winuser, }; @@ -93,16 +93,6 @@ impl From for ModifiersState { } } -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( @@ -167,259 +157,3 @@ fn layout_uses_altgr() -> bool { false } } - -pub fn vkey_to_winit_vkey(vkey: c_int) -> Option { - // VK_* codes are documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx - match vkey { - //winuser::VK_LBUTTON => Some(VirtualKeyCode::Lbutton), - //winuser::VK_RBUTTON => Some(VirtualKeyCode::Rbutton), - //winuser::VK_CANCEL => Some(VirtualKeyCode::Cancel), - //winuser::VK_MBUTTON => Some(VirtualKeyCode::Mbutton), - //winuser::VK_XBUTTON1 => Some(VirtualKeyCode::Xbutton1), - //winuser::VK_XBUTTON2 => Some(VirtualKeyCode::Xbutton2), - winuser::VK_BACK => Some(VirtualKeyCode::Back), - winuser::VK_TAB => Some(VirtualKeyCode::Tab), - //winuser::VK_CLEAR => Some(VirtualKeyCode::Clear), - winuser::VK_RETURN => Some(VirtualKeyCode::Return), - winuser::VK_LSHIFT => Some(VirtualKeyCode::LShift), - winuser::VK_RSHIFT => Some(VirtualKeyCode::RShift), - winuser::VK_LCONTROL => Some(VirtualKeyCode::LControl), - winuser::VK_RCONTROL => Some(VirtualKeyCode::RControl), - winuser::VK_LMENU => Some(VirtualKeyCode::LAlt), - winuser::VK_RMENU => Some(VirtualKeyCode::RAlt), - winuser::VK_PAUSE => Some(VirtualKeyCode::Pause), - winuser::VK_CAPITAL => Some(VirtualKeyCode::Capital), - winuser::VK_KANA => Some(VirtualKeyCode::Kana), - //winuser::VK_HANGUEL => Some(VirtualKeyCode::Hanguel), - //winuser::VK_HANGUL => Some(VirtualKeyCode::Hangul), - //winuser::VK_JUNJA => Some(VirtualKeyCode::Junja), - //winuser::VK_FINAL => Some(VirtualKeyCode::Final), - //winuser::VK_HANJA => Some(VirtualKeyCode::Hanja), - winuser::VK_KANJI => Some(VirtualKeyCode::Kanji), - winuser::VK_ESCAPE => Some(VirtualKeyCode::Escape), - winuser::VK_CONVERT => Some(VirtualKeyCode::Convert), - winuser::VK_NONCONVERT => Some(VirtualKeyCode::NoConvert), - //winuser::VK_ACCEPT => Some(VirtualKeyCode::Accept), - //winuser::VK_MODECHANGE => Some(VirtualKeyCode::Modechange), - winuser::VK_SPACE => Some(VirtualKeyCode::Space), - winuser::VK_PRIOR => Some(VirtualKeyCode::PageUp), - winuser::VK_NEXT => Some(VirtualKeyCode::PageDown), - winuser::VK_END => Some(VirtualKeyCode::End), - winuser::VK_HOME => Some(VirtualKeyCode::Home), - winuser::VK_LEFT => Some(VirtualKeyCode::Left), - winuser::VK_UP => Some(VirtualKeyCode::Up), - winuser::VK_RIGHT => Some(VirtualKeyCode::Right), - winuser::VK_DOWN => Some(VirtualKeyCode::Down), - //winuser::VK_SELECT => Some(VirtualKeyCode::Select), - //winuser::VK_PRINT => Some(VirtualKeyCode::Print), - //winuser::VK_EXECUTE => Some(VirtualKeyCode::Execute), - winuser::VK_SNAPSHOT => Some(VirtualKeyCode::Snapshot), - winuser::VK_INSERT => Some(VirtualKeyCode::Insert), - winuser::VK_DELETE => Some(VirtualKeyCode::Delete), - //winuser::VK_HELP => Some(VirtualKeyCode::Help), - 0x30 => Some(VirtualKeyCode::Key0), - 0x31 => Some(VirtualKeyCode::Key1), - 0x32 => Some(VirtualKeyCode::Key2), - 0x33 => Some(VirtualKeyCode::Key3), - 0x34 => Some(VirtualKeyCode::Key4), - 0x35 => Some(VirtualKeyCode::Key5), - 0x36 => Some(VirtualKeyCode::Key6), - 0x37 => Some(VirtualKeyCode::Key7), - 0x38 => Some(VirtualKeyCode::Key8), - 0x39 => Some(VirtualKeyCode::Key9), - 0x41 => Some(VirtualKeyCode::A), - 0x42 => Some(VirtualKeyCode::B), - 0x43 => Some(VirtualKeyCode::C), - 0x44 => Some(VirtualKeyCode::D), - 0x45 => Some(VirtualKeyCode::E), - 0x46 => Some(VirtualKeyCode::F), - 0x47 => Some(VirtualKeyCode::G), - 0x48 => Some(VirtualKeyCode::H), - 0x49 => Some(VirtualKeyCode::I), - 0x4A => Some(VirtualKeyCode::J), - 0x4B => Some(VirtualKeyCode::K), - 0x4C => Some(VirtualKeyCode::L), - 0x4D => Some(VirtualKeyCode::M), - 0x4E => Some(VirtualKeyCode::N), - 0x4F => Some(VirtualKeyCode::O), - 0x50 => Some(VirtualKeyCode::P), - 0x51 => Some(VirtualKeyCode::Q), - 0x52 => Some(VirtualKeyCode::R), - 0x53 => Some(VirtualKeyCode::S), - 0x54 => Some(VirtualKeyCode::T), - 0x55 => Some(VirtualKeyCode::U), - 0x56 => Some(VirtualKeyCode::V), - 0x57 => Some(VirtualKeyCode::W), - 0x58 => Some(VirtualKeyCode::X), - 0x59 => Some(VirtualKeyCode::Y), - 0x5A => Some(VirtualKeyCode::Z), - winuser::VK_LWIN => Some(VirtualKeyCode::LWin), - winuser::VK_RWIN => Some(VirtualKeyCode::RWin), - winuser::VK_APPS => Some(VirtualKeyCode::Apps), - winuser::VK_SLEEP => Some(VirtualKeyCode::Sleep), - winuser::VK_NUMPAD0 => Some(VirtualKeyCode::Numpad0), - winuser::VK_NUMPAD1 => Some(VirtualKeyCode::Numpad1), - winuser::VK_NUMPAD2 => Some(VirtualKeyCode::Numpad2), - winuser::VK_NUMPAD3 => Some(VirtualKeyCode::Numpad3), - winuser::VK_NUMPAD4 => Some(VirtualKeyCode::Numpad4), - winuser::VK_NUMPAD5 => Some(VirtualKeyCode::Numpad5), - winuser::VK_NUMPAD6 => Some(VirtualKeyCode::Numpad6), - winuser::VK_NUMPAD7 => Some(VirtualKeyCode::Numpad7), - winuser::VK_NUMPAD8 => Some(VirtualKeyCode::Numpad8), - winuser::VK_NUMPAD9 => Some(VirtualKeyCode::Numpad9), - winuser::VK_MULTIPLY => Some(VirtualKeyCode::NumpadMultiply), - winuser::VK_ADD => Some(VirtualKeyCode::NumpadAdd), - //winuser::VK_SEPARATOR => Some(VirtualKeyCode::Separator), - winuser::VK_SUBTRACT => Some(VirtualKeyCode::NumpadSubtract), - winuser::VK_DECIMAL => Some(VirtualKeyCode::NumpadDecimal), - winuser::VK_DIVIDE => Some(VirtualKeyCode::NumpadDivide), - winuser::VK_F1 => Some(VirtualKeyCode::F1), - winuser::VK_F2 => Some(VirtualKeyCode::F2), - winuser::VK_F3 => Some(VirtualKeyCode::F3), - winuser::VK_F4 => Some(VirtualKeyCode::F4), - winuser::VK_F5 => Some(VirtualKeyCode::F5), - winuser::VK_F6 => Some(VirtualKeyCode::F6), - winuser::VK_F7 => Some(VirtualKeyCode::F7), - winuser::VK_F8 => Some(VirtualKeyCode::F8), - winuser::VK_F9 => Some(VirtualKeyCode::F9), - winuser::VK_F10 => Some(VirtualKeyCode::F10), - winuser::VK_F11 => Some(VirtualKeyCode::F11), - winuser::VK_F12 => Some(VirtualKeyCode::F12), - winuser::VK_F13 => Some(VirtualKeyCode::F13), - winuser::VK_F14 => Some(VirtualKeyCode::F14), - winuser::VK_F15 => Some(VirtualKeyCode::F15), - winuser::VK_F16 => Some(VirtualKeyCode::F16), - winuser::VK_F17 => Some(VirtualKeyCode::F17), - winuser::VK_F18 => Some(VirtualKeyCode::F18), - winuser::VK_F19 => Some(VirtualKeyCode::F19), - winuser::VK_F20 => Some(VirtualKeyCode::F20), - winuser::VK_F21 => Some(VirtualKeyCode::F21), - winuser::VK_F22 => Some(VirtualKeyCode::F22), - winuser::VK_F23 => Some(VirtualKeyCode::F23), - winuser::VK_F24 => Some(VirtualKeyCode::F24), - winuser::VK_NUMLOCK => Some(VirtualKeyCode::Numlock), - winuser::VK_SCROLL => Some(VirtualKeyCode::Scroll), - winuser::VK_BROWSER_BACK => Some(VirtualKeyCode::NavigateBackward), - winuser::VK_BROWSER_FORWARD => Some(VirtualKeyCode::NavigateForward), - winuser::VK_BROWSER_REFRESH => Some(VirtualKeyCode::WebRefresh), - winuser::VK_BROWSER_STOP => Some(VirtualKeyCode::WebStop), - winuser::VK_BROWSER_SEARCH => Some(VirtualKeyCode::WebSearch), - winuser::VK_BROWSER_FAVORITES => Some(VirtualKeyCode::WebFavorites), - winuser::VK_BROWSER_HOME => Some(VirtualKeyCode::WebHome), - winuser::VK_VOLUME_MUTE => Some(VirtualKeyCode::Mute), - winuser::VK_VOLUME_DOWN => Some(VirtualKeyCode::VolumeDown), - winuser::VK_VOLUME_UP => Some(VirtualKeyCode::VolumeUp), - winuser::VK_MEDIA_NEXT_TRACK => Some(VirtualKeyCode::NextTrack), - winuser::VK_MEDIA_PREV_TRACK => Some(VirtualKeyCode::PrevTrack), - winuser::VK_MEDIA_STOP => Some(VirtualKeyCode::MediaStop), - winuser::VK_MEDIA_PLAY_PAUSE => Some(VirtualKeyCode::PlayPause), - winuser::VK_LAUNCH_MAIL => Some(VirtualKeyCode::Mail), - winuser::VK_LAUNCH_MEDIA_SELECT => Some(VirtualKeyCode::MediaSelect), - /*winuser::VK_LAUNCH_APP1 => Some(VirtualKeyCode::Launch_app1), - winuser::VK_LAUNCH_APP2 => Some(VirtualKeyCode::Launch_app2),*/ - winuser::VK_OEM_PLUS => Some(VirtualKeyCode::Equals), - winuser::VK_OEM_COMMA => Some(VirtualKeyCode::Comma), - winuser::VK_OEM_MINUS => Some(VirtualKeyCode::Minus), - winuser::VK_OEM_PERIOD => Some(VirtualKeyCode::Period), - winuser::VK_OEM_1 => map_text_keys(vkey), - winuser::VK_OEM_2 => map_text_keys(vkey), - winuser::VK_OEM_3 => map_text_keys(vkey), - winuser::VK_OEM_4 => map_text_keys(vkey), - winuser::VK_OEM_5 => map_text_keys(vkey), - winuser::VK_OEM_6 => map_text_keys(vkey), - winuser::VK_OEM_7 => map_text_keys(vkey), - /* winuser::VK_OEM_8 => Some(VirtualKeyCode::Oem_8), */ - winuser::VK_OEM_102 => Some(VirtualKeyCode::OEM102), - /*winuser::VK_PROCESSKEY => Some(VirtualKeyCode::Processkey), - winuser::VK_PACKET => Some(VirtualKeyCode::Packet), - winuser::VK_ATTN => Some(VirtualKeyCode::Attn), - winuser::VK_CRSEL => Some(VirtualKeyCode::Crsel), - winuser::VK_EXSEL => Some(VirtualKeyCode::Exsel), - winuser::VK_EREOF => Some(VirtualKeyCode::Ereof), - winuser::VK_PLAY => Some(VirtualKeyCode::Play), - winuser::VK_ZOOM => Some(VirtualKeyCode::Zoom), - winuser::VK_NONAME => Some(VirtualKeyCode::Noname), - winuser::VK_PA1 => Some(VirtualKeyCode::Pa1), - winuser::VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/ - _ => None, - } -} - -pub fn handle_extended_keys( - vkey: c_int, - mut scancode: UINT, - extended: bool, -) -> Option<(c_int, UINT)> { - // Welcome to hell https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input/ - scancode = if extended { 0xE000 } else { 0x0000 } | scancode; - let vkey = match vkey { - winuser::VK_SHIFT => unsafe { - winuser::MapVirtualKeyA(scancode, winuser::MAPVK_VSC_TO_VK_EX) as _ - }, - winuser::VK_CONTROL => { - if extended { - winuser::VK_RCONTROL - } else { - winuser::VK_LCONTROL - } - } - winuser::VK_MENU => { - if extended { - winuser::VK_RMENU - } else { - winuser::VK_LMENU - } - } - _ => { - match scancode { - // When VK_PAUSE is pressed it emits a LeftControl + NumLock scancode event sequence, but reports VK_PAUSE - // as the virtual key on both events, or VK_PAUSE on the first event or 0xFF when using raw input. - // Don't emit anything for the LeftControl event in the pair... - 0xE01D if vkey == winuser::VK_PAUSE => return None, - // ...and emit the Pause event for the second event in the pair. - 0x45 if vkey == winuser::VK_PAUSE || vkey == 0xFF as _ => { - scancode = 0xE059; - winuser::VK_PAUSE - } - // VK_PAUSE has an incorrect vkey value when used with modifiers. VK_PAUSE also reports a different - // scancode when used with modifiers than when used without - 0xE046 => { - scancode = 0xE059; - winuser::VK_PAUSE - } - // VK_SCROLL has an incorrect vkey value when used with modifiers. - 0x46 => winuser::VK_SCROLL, - _ => vkey, - } - } - }; - Some((vkey, scancode)) -} - -pub fn process_key_params( - wparam: WPARAM, - lparam: LPARAM, -) -> Option<(ScanCode_DEPRECATED, Option)> { - let scancode = ((lparam >> 16) & 0xff) as UINT; - let extended = (lparam & 0x01000000) != 0; - handle_extended_keys(wparam as _, scancode, extended) - .map(|(vkey, scancode)| (scancode, vkey_to_winit_vkey(vkey))) -} - -// This is needed as windows doesn't properly distinguish -// some virtual key codes for different keyboard layouts -fn map_text_keys(win_virtual_key: i32) -> Option { - let char_key = - unsafe { winuser::MapVirtualKeyA(win_virtual_key as u32, winuser::MAPVK_VK_TO_CHAR) } - & 0x7FFF; - match char::from_u32(char_key) { - Some(';') => Some(VirtualKeyCode::Semicolon), - Some('/') => Some(VirtualKeyCode::Slash), - Some('`') => Some(VirtualKeyCode::Grave), - Some('[') => Some(VirtualKeyCode::LBracket), - Some(']') => Some(VirtualKeyCode::RBracket), - Some('\'') => Some(VirtualKeyCode::Apostrophe), - Some('\\') => Some(VirtualKeyCode::Backslash), - _ => None, - } -} diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index c147492d54..42cdf01aab 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -32,15 +32,15 @@ use winapi::{ use crate::{ dpi::{PhysicalPosition, PhysicalSize}, - event::{DeviceEvent, Event, Force, KeyboardInput, Touch, TouchPhase, WindowEvent}, + event::{DeviceEvent, Event, Force, Touch, TouchPhase, WindowEvent, RawKeyEvent, ScanCode}, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, monitor::MonitorHandle as RootMonitorHandle, platform_impl::platform::{ dark_mode::try_theme, dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling}, drop_handler::FileDropHandler, - event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, - keyboard::is_msg_keyboard_related, + event, + keyboard::{is_msg_keyboard_related, native_key_to_code, PlatformScanCode}, minimal_ime::is_msg_ime_related, monitor::{self, MonitorHandle}, raw_input, util, @@ -995,39 +995,6 @@ unsafe extern "system" fn public_window_callback( 0 } - winuser::WM_CHAR | winuser::WM_SYSCHAR => { - use crate::event::WindowEvent::ReceivedCharacter; - use std::char; - let is_high_surrogate = 0xD800 <= wparam && wparam <= 0xDBFF; - let is_low_surrogate = 0xDC00 <= wparam && wparam <= 0xDFFF; - - if is_high_surrogate { - subclass_input.window_state.lock().high_surrogate = Some(wparam as u16); - } else if is_low_surrogate { - let high_surrogate = subclass_input.window_state.lock().high_surrogate.take(); - - if let Some(high_surrogate) = high_surrogate { - let pair = [high_surrogate, wparam as u16]; - if let Some(Ok(chr)) = char::decode_utf16(pair.iter().copied()).next() { - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: ReceivedCharacter(chr), - }); - } - } - } else { - subclass_input.window_state.lock().high_surrogate = None; - - if let Some(chr) = char::from_u32(wparam as u32) { - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: ReceivedCharacter(chr), - }); - } - } - 0 - } - // this is necessary for us to maintain minimize/restore state winuser::WM_SYSCOMMAND => { if wparam == winuser::SC_RESTORE { @@ -1171,64 +1138,17 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { - use crate::event::{ElementState::Pressed, VirtualKeyCode}; if msg == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 { commctrl::DefSubclassProc(window, msg, wparam, lparam) } else { - if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { - update_modifiers(window, subclass_input); - - #[allow(deprecated)] - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput_DEPRECATED { - device_id: DEVICE_ID, - input: KeyboardInput { - state: Pressed, - scancode, - 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 - // consistent with the other platforms we'll emit a delete character here. - if vkey == Some(VirtualKeyCode::Delete) { - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::ReceivedCharacter('\u{7F}'), - }); - } + if let Some(retval) = retval { + retval + } else { + commctrl::DefSubclassProc(window, msg, wparam, lparam) } - 0 } } - winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { - use crate::event::ElementState::Released; - if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { - update_modifiers(window, subclass_input); - - #[allow(deprecated)] - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput_DEPRECATED { - device_id: DEVICE_ID, - input: KeyboardInput { - state: Released, - scancode, - virtual_keycode: vkey, - modifiers: event::get_key_mods(), - }, - is_synthetic: false, - }, - }); - } - 0 - } - winuser::WM_LBUTTONDOWN => { use crate::event::{ElementState::Pressed, MouseButton::Left, WindowEvent::MouseInput}; @@ -1587,29 +1507,8 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_SETFOCUS => { - 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); - - update_modifiers(window, subclass_input); - - #[allow(deprecated)] - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput_DEPRECATED { - device_id: DEVICE_ID, - input: KeyboardInput { - scancode, - virtual_keycode, - state: Released, - modifiers: event::get_key_mods(), - }, - is_synthetic: true, - }, - }) - } + use crate::event::WindowEvent::Focused; + update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -1621,30 +1520,9 @@ unsafe extern "system" fn public_window_callback( winuser::WM_KILLFOCUS => { use crate::event::{ - ElementState::Released, ModifiersState, WindowEvent::{Focused, ModifiersChanged}, }; - 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); - - #[allow(deprecated)] - subclass_input.send_event(Event::WindowEvent { - window_id: RootWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput_DEPRECATED { - device_id: DEVICE_ID, - input: KeyboardInput { - scancode, - virtual_keycode, - state: Released, - modifiers: event::get_key_mods(), - }, - is_synthetic: true, - }, - }) - } subclass_input.window_state.lock().modifiers_state = ModifiersState::empty(); subclass_input.send_event(Event::WindowEvent { @@ -2059,8 +1937,7 @@ unsafe extern "system" fn thread_event_target_callback( winuser::WM_INPUT => { use crate::event::{ - DeviceEvent::{Button, Key_DEPRECATED, Motion, MouseMotion, MouseWheel}, - ElementState::{Pressed, Released}, + DeviceEvent::{Button, Motion, MouseMotion, MouseWheel, Key}, MouseScrollDelta::LineDelta, }; @@ -2129,28 +2006,31 @@ unsafe extern "system" fn thread_event_target_callback( || keyboard.Message == winuser::WM_SYSKEYUP; if pressed || released { - let state = if pressed { Pressed } else { Released }; - - let scancode = keyboard.MakeCode as _; - let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _) - | util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _); - - if let Some((vkey, scancode)) = - handle_extended_keys(keyboard.VKey as _, scancode, extended) - { - let virtual_keycode = vkey_to_winit_vkey(vkey); - - #[allow(deprecated)] - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Key_DEPRECATED(KeyboardInput { - scancode, - state, - virtual_keycode, - modifiers: event::get_key_mods(), - }), - }); - } + let state = if pressed { + keyboard_types::KeyState::Down + } else { + keyboard_types::KeyState::Up + }; + let extension = { + if util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _) { + 0xE000 + } else if util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _) { + 0xE100 + } else { + 0x0000 + } + }; + let scancode = keyboard.MakeCode | extension; + let platform_scancode = PlatformScanCode(scancode); + let code = native_key_to_code(platform_scancode); + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Key(RawKeyEvent { + scancode: ScanCode(platform_scancode), + physical_key: code, + state, + }), + }); } } } diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 1d4a35b723..6de8798c59 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -1104,7 +1104,7 @@ fn vkey_to_non_printable(vkey: i32, hkl: usize, has_alt_graph: bool) -> Key { } } -fn native_key_to_code(scancode: PlatformScanCode) -> keyboard_types::Code { +pub fn native_key_to_code(scancode: PlatformScanCode) -> keyboard_types::Code { // See: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html // and: https://www.w3.org/TR/uievents-code/ // and: The widget/NativeKeyToDOMCodeName.h file in the firefox source diff --git a/src/platform_impl/windows/minimal_ime.rs b/src/platform_impl/windows/minimal_ime.rs index e1d3ee78b2..2c0a869999 100644 --- a/src/platform_impl/windows/minimal_ime.rs +++ b/src/platform_impl/windows/minimal_ime.rs @@ -2,13 +2,10 @@ use std::mem::MaybeUninit; use winapi::{ shared::{ - minwindef::{HKL, LOWORD, LPARAM, LRESULT, WPARAM}, + minwindef::{LPARAM, LRESULT, WPARAM}, windef::HWND, }, - um::{ - winnt::{LANG_JAPANESE, LANG_KOREAN, PRIMARYLANGID}, - winuser, - }, + um::winuser, }; pub fn is_msg_ime_related(msg_kind: u32) -> bool { From 68d229ca75f26c188cfa771cc0c8edfe3cd2a601 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 5 Dec 2020 14:57:33 +0100 Subject: [PATCH 12/75] Fix numlock and pause not being reported correctly --- src/platform_impl/windows/keyboard.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 6de8798c59..12180071cb 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -647,7 +647,7 @@ impl KeyEventBuilder { } let is_extended = (scancode & 0xE000) == 0xE000; let platform_scancode = PlatformScanCode(scancode as u16); - + let code = native_key_to_code(platform_scancode); let key_text = self.key_text.get(&platform_scancode).cloned(); let key_text_with_caps = self.key_text_with_caps.get(&platform_scancode).cloned(); let logical_key = match &key_text { @@ -661,7 +661,7 @@ impl KeyEventBuilder { keyboard_types::Key::Character(str.clone()) } } - None => vkey_to_non_printable(vk, locale_id as usize, self.has_alt_graph), + None => vkey_to_non_printable(vk, code, locale_id as usize, self.has_alt_graph), }; let event_info = PartialKeyEventInfo { @@ -669,7 +669,7 @@ impl KeyEventBuilder { vkey: vk, scancode: platform_scancode, is_repeat: false, - code: native_key_to_code(platform_scancode), + code, location: get_location(vk, is_extended), is_dead: false, label: key_text, @@ -712,7 +712,7 @@ impl PartialKeyEventInfo { if self.is_dead { logical_key = Key::Dead; } else { - let key = vkey_to_non_printable(self.vkey, locale_id, has_alt_gr); + let key = vkey_to_non_printable(self.vkey, self.code, locale_id, has_alt_gr); if key == Key::Unidentified { if self.utf16parts_without_ctrl.len() > 0 { logical_key = @@ -859,10 +859,19 @@ fn does_vkey_consume_dead_key(vkey: u32) -> bool { /// This includes all non-character keys defined within `Key` so for example /// backspace and tab are included. -fn vkey_to_non_printable(vkey: i32, hkl: usize, has_alt_graph: bool) -> Key { +fn vkey_to_non_printable(vkey: i32, code: keyboard_types::Code, hkl: usize, has_alt_graph: bool) -> Key { + use keyboard_types::Code; // List of the Web key names and their corresponding platform-native key names: // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values + // Some keys cannot be correctly determined based on the virtual key. + // Therefore we use the `code` to translate those keys. + match code { + Code::NumLock => return Key::NumLock, + Code::Pause => return Key::Pause, + _ => () + } + let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); let is_korean = primary_lang_id == LANG_KOREAN; let is_japanese = primary_lang_id == LANG_JAPANESE; From e25f35ebea62d1bd0b5555c3557836ba39b26649 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 5 Dec 2020 14:57:53 +0100 Subject: [PATCH 13/75] Ran `cargo fmt` --- src/platform_impl/windows/event_loop.rs | 4 ++-- src/platform_impl/windows/keyboard.rs | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 42cdf01aab..fe520e599f 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -32,7 +32,7 @@ use winapi::{ use crate::{ dpi::{PhysicalPosition, PhysicalSize}, - event::{DeviceEvent, Event, Force, Touch, TouchPhase, WindowEvent, RawKeyEvent, ScanCode}, + event::{DeviceEvent, Event, Force, RawKeyEvent, ScanCode, Touch, TouchPhase, WindowEvent}, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, monitor::MonitorHandle as RootMonitorHandle, platform_impl::platform::{ @@ -1937,7 +1937,7 @@ unsafe extern "system" fn thread_event_target_callback( winuser::WM_INPUT => { use crate::event::{ - DeviceEvent::{Button, Motion, MouseMotion, MouseWheel, Key}, + DeviceEvent::{Button, Key, Motion, MouseMotion, MouseWheel}, MouseScrollDelta::LineDelta, }; diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 12180071cb..f164a77c94 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -859,7 +859,12 @@ fn does_vkey_consume_dead_key(vkey: u32) -> bool { /// This includes all non-character keys defined within `Key` so for example /// backspace and tab are included. -fn vkey_to_non_printable(vkey: i32, code: keyboard_types::Code, hkl: usize, has_alt_graph: bool) -> Key { +fn vkey_to_non_printable( + vkey: i32, + code: keyboard_types::Code, + hkl: usize, + has_alt_graph: bool, +) -> Key { use keyboard_types::Code; // List of the Web key names and their corresponding platform-native key names: // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values @@ -869,7 +874,7 @@ fn vkey_to_non_printable(vkey: i32, code: keyboard_types::Code, hkl: usize, has_ match code { Code::NumLock => return Key::NumLock, Code::Pause => return Key::Pause, - _ => () + _ => (), } let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); From ad9bf98cbad066096bdbd9712550ec6b48acd07f Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 5 Dec 2020 15:30:26 +0100 Subject: [PATCH 14/75] Fix numpad keys being reported incorrectly --- src/platform_impl/windows/keyboard.rs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index f164a77c94..32ab2cc654 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -310,9 +310,9 @@ impl KeyEventBuilder { winuser::GetKeyboardState(key_state[0].as_mut_ptr()); let mut key_state = std::mem::transmute::<_, [u8; 256]>(key_state); - let has_ctrl = key_state[winuser::VK_CONTROL as usize] != 0 - || key_state[winuser::VK_LCONTROL as usize] != 0 - || key_state[winuser::VK_RCONTROL as usize] != 0; + let has_ctrl = key_state[winuser::VK_CONTROL as usize] & 0x80 != 0 + || key_state[winuser::VK_LCONTROL as usize] & 0x80 != 0 + || key_state[winuser::VK_RCONTROL as usize] & 0x80 != 0; // If neither of the CTRL keys is pressed, just use the text with all // modifiers because that already consumed the dead key and otherwise @@ -712,16 +712,11 @@ impl PartialKeyEventInfo { if self.is_dead { logical_key = Key::Dead; } else { - let key = vkey_to_non_printable(self.vkey, self.code, locale_id, has_alt_gr); - if key == Key::Unidentified { - if self.utf16parts_without_ctrl.len() > 0 { - logical_key = - Key::Character(String::from_utf16(&self.utf16parts_without_ctrl).unwrap()); - } else { - logical_key = Key::Unidentified; - } + if !self.utf16parts_without_ctrl.is_empty() { + logical_key = + Key::Character(String::from_utf16(&self.utf16parts_without_ctrl).unwrap()); } else { - logical_key = key; + logical_key = vkey_to_non_printable(self.vkey, self.code, locale_id, has_alt_gr); } } let key_without_modifers; From 458c1befaf85d4ebb07b4cc4353e4404ebdd60c8 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 5 Dec 2020 21:31:47 +0100 Subject: [PATCH 15/75] Update examples --- examples/control_flow.rs | 51 ++++---- examples/cursor.rs | 10 +- examples/cursor_grab.rs | 25 ++-- examples/fullscreen.rs | 51 ++++---- examples/handling_close.rs | 54 ++++---- examples/minimize.rs | 10 +- examples/multithreaded.rs | 164 ++++++++++++------------ examples/multiwindow.rs | 10 +- examples/resizable.rs | 12 +- examples/window_debug.rs | 116 +++++++++-------- examples/window_icon.rs | 2 +- src/event.rs | 2 + src/platform_impl/windows/event_loop.rs | 15 ++- src/platform_impl/windows/keyboard.rs | 5 + 14 files changed, 282 insertions(+), 245 deletions(-) diff --git a/examples/control_flow.rs b/examples/control_flow.rs index 2d3c6911e6..60f903b127 100644 --- a/examples/control_flow.rs +++ b/examples/control_flow.rs @@ -2,7 +2,7 @@ use std::{thread, time}; use simple_logger::SimpleLogger; use winit::{ - event::{Event, KeyboardInput, WindowEvent}, + event::{Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -38,7 +38,7 @@ fn main() { let mut close_requested = false; event_loop.run(move |event, _, control_flow| { - use winit::event::{ElementState, StartCause, VirtualKeyCode}; + use winit::event::{StartCause, keyboard_types::Key}; println!("{:?}", event); match event { Event::NewEvents(start_cause) => { @@ -51,32 +51,35 @@ fn main() { WindowEvent::CloseRequested => { close_requested = true; } - WindowEvent::KeyboardInput_DEPRECATED { - input: - KeyboardInput { - virtual_keycode: Some(virtual_code), - state: ElementState::Pressed, + WindowEvent::KeyboardInput { + event: + KeyEvent { + logical_key: key, + state: keyboard_types::KeyState::Down, .. }, .. - } => match virtual_code { - VirtualKeyCode::Key1 => { - mode = Mode::Wait; - println!("\nmode: {:?}\n", mode); - } - VirtualKeyCode::Key2 => { - mode = Mode::WaitUntil; - println!("\nmode: {:?}\n", mode); - } - VirtualKeyCode::Key3 => { - mode = Mode::Poll; - println!("\nmode: {:?}\n", mode); - } - VirtualKeyCode::R => { - request_redraw = !request_redraw; - println!("\nrequest_redraw: {}\n", request_redraw); + } => match key { + Key::Character(string) => match string.to_lowercase().as_str() { + "1" => { + mode = Mode::Wait; + println!("\nmode: {:?}\n", mode); + } + "2" => { + mode = Mode::WaitUntil; + println!("\nmode: {:?}\n", mode); + } + "3" => { + mode = Mode::Poll; + println!("\nmode: {:?}\n", mode); + } + "r" => { + request_redraw = !request_redraw; + println!("\nrequest_redraw: {}\n", request_redraw); + } + _ => () } - VirtualKeyCode::Escape => { + Key::Escape => { close_requested = true; } _ => (), diff --git a/examples/cursor.rs b/examples/cursor.rs index 92f4736da4..4402838200 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -1,6 +1,6 @@ use simple_logger::SimpleLogger; use winit::{ - event::{ElementState, Event, KeyboardInput, WindowEvent}, + event::{Event, KeyEvent, keyboard_types::{KeyState}, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, WindowBuilder}, }; @@ -20,10 +20,10 @@ fn main() { match event { Event::WindowEvent { event: - WindowEvent::KeyboardInput_DEPRECATED { - input: - KeyboardInput { - state: ElementState::Pressed, + WindowEvent::KeyboardInput { + event: + KeyEvent { + state: KeyState::Down, .. }, .. diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index 60f93ade68..10953d6016 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -1,6 +1,8 @@ use simple_logger::SimpleLogger; use winit::{ - event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent}, + event::{ + keyboard_types, DeviceEvent, ElementState, Event, KeyEvent, ModifiersState, WindowEvent, + }, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -22,20 +24,23 @@ fn main() { match event { Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput_DEPRECATED { - input: - KeyboardInput { - state: ElementState::Released, - virtual_keycode: Some(key), + WindowEvent::KeyboardInput { + event: + KeyEvent { + logical_key: key, + state: keyboard_types::KeyState::Up, .. }, .. } => { - use winit::event::VirtualKeyCode::*; + use winit::event::keyboard_types::Key; match key { - Escape => *control_flow = ControlFlow::Exit, - G => window.set_cursor_grab(!modifiers.shift()).unwrap(), - H => window.set_cursor_visible(modifiers.shift()), + Key::Escape => *control_flow = ControlFlow::Exit, + Key::Character(string) => match string.to_lowercase().as_str() { + "g" => window.set_cursor_grab(!modifiers.shift()).unwrap(), + "h" => window.set_cursor_visible(modifiers.shift()), + _ => (), + }, _ => (), } } diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index d6be08df0d..33de596d14 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -1,7 +1,7 @@ use std::io::{stdin, stdout, Write}; use simple_logger::SimpleLogger; -use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; +use winit::event::{Event, KeyEvent, keyboard_types::{KeyState, Key}, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::monitor::{MonitorHandle, VideoMode}; use winit::window::{Fullscreen, WindowBuilder}; @@ -38,33 +38,36 @@ fn main() { match event { Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput_DEPRECATED { - input: - KeyboardInput { - virtual_keycode: Some(virtual_code), - state, + WindowEvent::KeyboardInput { + event: + KeyEvent { + logical_key: key, + state: KeyState::Down, .. }, .. - } => match (virtual_code, state) { - (VirtualKeyCode::Escape, _) => *control_flow = ControlFlow::Exit, - (VirtualKeyCode::F, ElementState::Pressed) => { - if window.fullscreen().is_some() { - window.set_fullscreen(None); - } else { - window.set_fullscreen(fullscreen.clone()); + } => match key { + Key::Escape => *control_flow = ControlFlow::Exit, + Key::Character(string) => match string.to_lowercase().as_str() { + "f" => { + if window.fullscreen().is_some() { + window.set_fullscreen(None); + } else { + window.set_fullscreen(fullscreen.clone()); + } } - } - (VirtualKeyCode::S, ElementState::Pressed) => { - println!("window.fullscreen {:?}", window.fullscreen()); - } - (VirtualKeyCode::M, ElementState::Pressed) => { - is_maximized = !is_maximized; - window.set_maximized(is_maximized); - } - (VirtualKeyCode::D, ElementState::Pressed) => { - decorations = !decorations; - window.set_decorations(decorations); + "s" => { + println!("window.fullscreen {:?}", window.fullscreen()); + } + "m" => { + is_maximized = !is_maximized; + window.set_maximized(is_maximized); + } + "d" => { + decorations = !decorations; + window.set_decorations(decorations); + } + _ => (), } _ => (), }, diff --git a/examples/handling_close.rs b/examples/handling_close.rs index 5c40453eb8..c194b8c90b 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -1,6 +1,6 @@ use simple_logger::SimpleLogger; use winit::{ - event::{Event, KeyboardInput, WindowEvent}, + event::{keyboard_types, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -17,10 +17,6 @@ fn main() { let mut close_requested = false; event_loop.run(move |event, _, control_flow| { - use winit::event::{ - ElementState::Released, - VirtualKeyCode::{N, Y}, - }; *control_flow = ControlFlow::Wait; match event { @@ -43,36 +39,38 @@ fn main() { // closing the window. How to close the window is detailed in the handler for // the Y key. } - WindowEvent::KeyboardInput_DEPRECATED { - input: - KeyboardInput { - virtual_keycode: Some(virtual_code), - state: Released, + WindowEvent::KeyboardInput { + event: + KeyEvent { + logical_key: key, + state: keyboard_types::KeyState::Up, .. }, .. } => { - match virtual_code { - Y => { - if close_requested { - // This is where you'll want to do any cleanup you need. - println!("Buh-bye!"); - - // For a single-window application like this, you'd normally just - // break out of the event loop here. If you wanted to keep running the - // event loop (i.e. if it's a multi-window application), you need to - // drop the window. That closes it, and results in `Destroyed` being - // sent. - *control_flow = ControlFlow::Exit; + if let keyboard_types::Key::Character(string) = key { + match string.to_lowercase().as_str() { + "y" => { + if close_requested { + // This is where you'll want to do any cleanup you need. + println!("Buh-bye!"); + + // For a single-window application like this, you'd normally just + // break out of the event loop here. If you wanted to keep running the + // event loop (i.e. if it's a multi-window application), you need to + // drop the window. That closes it, and results in `Destroyed` being + // sent. + *control_flow = ControlFlow::Exit; + } } - } - N => { - if close_requested { - println!("Your window will continue to stay by your side."); - close_requested = false; + "n" => { + if close_requested { + println!("Your window will continue to stay by your side."); + close_requested = false; + } } + _ => (), } - _ => (), } } _ => (), diff --git a/examples/minimize.rs b/examples/minimize.rs index a818a43014..b782c6da10 100644 --- a/examples/minimize.rs +++ b/examples/minimize.rs @@ -1,7 +1,7 @@ extern crate winit; use simple_logger::SimpleLogger; -use winit::event::{Event, VirtualKeyCode, WindowEvent}; +use winit::event::{Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; @@ -25,13 +25,15 @@ fn main() { // Keyboard input event to handle minimize via a hotkey Event::WindowEvent { - event: WindowEvent::KeyboardInput_DEPRECATED { input, .. }, + event: WindowEvent::KeyboardInput { event, .. }, window_id, } => { if window_id == window.id() { // Pressing the 'M' key will minimize the window - if input.virtual_keycode == Some(VirtualKeyCode::M) { - window.set_minimized(true); + if let keyboard_types::Key::Character(string) = event.logical_key { + if string.to_lowercase() == "m" { + window.set_minimized(true); + } } } } diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 68cdb60b78..17350648d3 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -5,7 +5,7 @@ fn main() { use simple_logger::SimpleLogger; use winit::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event::{Event, KeyEvent, WindowEvent, ModifiersState}, event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, Fullscreen, WindowBuilder}, }; @@ -27,6 +27,7 @@ fn main() { let (tx, rx) = mpsc::channel(); window_senders.insert(window.id(), tx); + let mut modifiers = ModifiersState::default(); thread::spawn(move || { while let Ok(event) = rx.recv() { match event { @@ -49,94 +50,97 @@ fn main() { ); } } - #[allow(deprecated)] + WindowEvent::ModifiersChanged(mod_state) => { + modifiers = mod_state; + } WindowEvent::KeyboardInput { - input: - KeyboardInput { - state: ElementState::Released, - virtual_keycode: Some(key), - modifiers, + event: + KeyEvent { + state: keyboard_types::KeyState::Up, + logical_key: key, .. }, .. } => { + use keyboard_types::Key::{Character, ArrowLeft, ArrowRight}; window.set_title(&format!("{:?}", key)); let state = !modifiers.shift(); - use VirtualKeyCode::*; - match key { - A => window.set_always_on_top(state), - C => window.set_cursor_icon(match state { - true => CursorIcon::Progress, - false => CursorIcon::Default, - }), - D => window.set_decorations(!state), - // Cycle through video modes - Right | Left => { - video_mode_id = match key { - Left => video_mode_id.saturating_sub(1), - Right => (video_modes.len() - 1).min(video_mode_id + 1), + match &key { + Character(string) => match string.to_lowercase().as_str() { + "a" => window.set_always_on_top(state), + "c" => window.set_cursor_icon(match state { + true => CursorIcon::Progress, + false => CursorIcon::Default, + }), + "d" => window.set_decorations(!state), + "f" => window.set_fullscreen(match (state, modifiers.alt()) { + (true, false) => Some(Fullscreen::Borderless(None)), + (true, true) => Some(Fullscreen::Exclusive( + video_modes.iter().nth(video_mode_id).unwrap().clone(), + )), + (false, _) => None, + }), + "g" => window.set_cursor_grab(state).unwrap(), + "h" => window.set_cursor_visible(!state), + "i" => { + println!("Info:"); + println!("-> outer_position : {:?}", window.outer_position()); + println!("-> inner_position : {:?}", window.inner_position()); + println!("-> outer_size : {:?}", window.outer_size()); + println!("-> inner_size : {:?}", window.inner_size()); + println!("-> fullscreen : {:?}", window.fullscreen()); + } + "l" => window.set_min_inner_size(match state { + true => Some(WINDOW_SIZE), + false => None, + }), + "m" => window.set_maximized(state), + "p" => window.set_outer_position({ + let mut position = window.outer_position().unwrap(); + let sign = if state { 1 } else { -1 }; + position.x += 10 * sign; + position.y += 10 * sign; + position + }), + "q" => window.request_redraw(), + "r" => window.set_resizable(state), + "s" => window.set_inner_size(match state { + true => PhysicalSize::new( + WINDOW_SIZE.width + 100, + WINDOW_SIZE.height + 100, + ), + false => WINDOW_SIZE, + }), + "w" => { + if let Size::Physical(size) = WINDOW_SIZE.into() { + window + .set_cursor_position(Position::Physical( + PhysicalPosition::new( + size.width as i32 / 2, + size.height as i32 / 2, + ), + )) + .unwrap() + } + } + "z" => { + window.set_visible(false); + thread::sleep(Duration::from_secs(1)); + window.set_visible(true); + } + _ => () + } + ArrowRight | ArrowLeft => { + video_mode_id = match &key { + ArrowLeft => video_mode_id.saturating_sub(1), + ArrowRight => (video_modes.len() - 1).min(video_mode_id + 1), _ => unreachable!(), }; println!( "Picking video mode: {}", video_modes.iter().nth(video_mode_id).unwrap() ); - } - F => window.set_fullscreen(match (state, modifiers.alt()) { - (true, false) => Some(Fullscreen::Borderless(None)), - (true, true) => Some(Fullscreen::Exclusive( - video_modes.iter().nth(video_mode_id).unwrap().clone(), - )), - (false, _) => None, - }), - G => window.set_cursor_grab(state).unwrap(), - H => window.set_cursor_visible(!state), - I => { - println!("Info:"); - println!("-> outer_position : {:?}", window.outer_position()); - println!("-> inner_position : {:?}", window.inner_position()); - println!("-> outer_size : {:?}", window.outer_size()); - println!("-> inner_size : {:?}", window.inner_size()); - println!("-> fullscreen : {:?}", window.fullscreen()); - } - L => window.set_min_inner_size(match state { - true => Some(WINDOW_SIZE), - false => None, - }), - M => window.set_maximized(state), - P => window.set_outer_position({ - let mut position = window.outer_position().unwrap(); - let sign = if state { 1 } else { -1 }; - position.x += 10 * sign; - position.y += 10 * sign; - position - }), - Q => window.request_redraw(), - R => window.set_resizable(state), - S => window.set_inner_size(match state { - true => PhysicalSize::new( - WINDOW_SIZE.width + 100, - WINDOW_SIZE.height + 100, - ), - false => WINDOW_SIZE, - }), - W => { - if let Size::Physical(size) = WINDOW_SIZE.into() { - window - .set_cursor_position(Position::Physical( - PhysicalPosition::new( - size.width as i32 / 2, - size.height as i32 / 2, - ), - )) - .unwrap() - } - } - Z => { - window.set_visible(false); - thread::sleep(Duration::from_secs(1)); - window.set_visible(true); - } + }, _ => (), } } @@ -155,10 +159,10 @@ fn main() { WindowEvent::CloseRequested | WindowEvent::Destroyed | WindowEvent::KeyboardInput { - input: - KeyboardInput { - state: ElementState::Released, - virtual_keycode: Some(VirtualKeyCode::Escape), + event: + KeyEvent { + state: keyboard_types::KeyState::Up, + logical_key: keyboard_types::Key::Escape, .. }, .. diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 7c53ed2abc..f681312f8b 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use simple_logger::SimpleLogger; use winit::{ - event::{ElementState, Event, KeyboardInput, WindowEvent}, + event::{Event, KeyEvent, WindowEvent, keyboard_types::{KeyState}}, event_loop::{ControlFlow, EventLoop}, window::Window, }; @@ -33,10 +33,10 @@ fn main() { *control_flow = ControlFlow::Exit; } } - WindowEvent::KeyboardInput_DEPRECATED { - input: - KeyboardInput { - state: ElementState::Pressed, + WindowEvent::KeyboardInput { + event: + KeyEvent { + state: KeyState::Up, .. }, .. diff --git a/examples/resizable.rs b/examples/resizable.rs index b312e74cc8..ffdab3f517 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -1,7 +1,7 @@ use simple_logger::SimpleLogger; use winit::{ dpi::LogicalSize, - event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event::{Event, KeyEvent, keyboard_types::{Code, KeyState}, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -25,11 +25,11 @@ fn main() { match event { Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, - WindowEvent::KeyboardInput_DEPRECATED { - input: - KeyboardInput { - virtual_keycode: Some(VirtualKeyCode::Space), - state: ElementState::Released, + WindowEvent::KeyboardInput { + event: + KeyEvent { + physical_key: Code::Space, + state: KeyState::Up, .. }, .. diff --git a/examples/window_debug.rs b/examples/window_debug.rs index b224951742..855a8d6057 100644 --- a/examples/window_debug.rs +++ b/examples/window_debug.rs @@ -3,7 +3,7 @@ use simple_logger::SimpleLogger; use winit::{ dpi::{LogicalSize, PhysicalSize}, - event::{DeviceEvent, ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event::{DeviceEvent, Event, RawKeyEvent, KeyEvent, keyboard_types::{KeyState, Key, Code}, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{Fullscreen, WindowBuilder}, }; @@ -35,22 +35,24 @@ fn main() { *control_flow = ControlFlow::Wait; match event { + // This used to use the virtual key, but the new API + // only provides the `physical_key` (`Code`). Event::DeviceEvent { event: - DeviceEvent::Key_DEPRECATED(KeyboardInput { - virtual_keycode: Some(key), - state: ElementState::Pressed, + DeviceEvent::Key(RawKeyEvent { + physical_key, + state: KeyState::Up, .. }), .. - } => match key { - VirtualKeyCode::M => { + } => match physical_key { + Code::KeyM => { if minimized { minimized = !minimized; window.set_minimized(minimized); } } - VirtualKeyCode::V => { + Code::KeyV => { if !visible { visible = !visible; window.set_visible(visible); @@ -59,61 +61,61 @@ fn main() { _ => (), }, Event::WindowEvent { - event: WindowEvent::KeyboardInput_DEPRECATED { input, .. }, + event: WindowEvent::KeyboardInput { + event: KeyEvent { + logical_key: Key::Character(key_str), + state: KeyState::Up, + .. + }, + .. + }, .. - } => match input { - KeyboardInput { - virtual_keycode: Some(key), - state: ElementState::Pressed, - .. - } => match key { - VirtualKeyCode::E => { - fn area(size: PhysicalSize) -> u32 { - size.width * size.height - } - - let monitor = window.current_monitor().unwrap(); - if let Some(mode) = monitor - .video_modes() - .max_by(|a, b| area(a.size()).cmp(&area(b.size()))) - { - window.set_fullscreen(Some(Fullscreen::Exclusive(mode))); - } else { - eprintln!("no video modes available"); - } - } - VirtualKeyCode::F => { - if window.fullscreen().is_some() { - window.set_fullscreen(None); - } else { - let monitor = window.current_monitor(); - window.set_fullscreen(Some(Fullscreen::Borderless(monitor))); - } - } - VirtualKeyCode::P => { - if window.fullscreen().is_some() { - window.set_fullscreen(None); - } else { - window.set_fullscreen(Some(Fullscreen::Borderless(None))); - } + } => match key_str.to_lowercase().as_str() { + "e" => { + fn area(size: PhysicalSize) -> u32 { + size.width * size.height } - VirtualKeyCode::M => { - minimized = !minimized; - window.set_minimized(minimized); - } - VirtualKeyCode::Q => { - *control_flow = ControlFlow::Exit; + + let monitor = window.current_monitor().unwrap(); + if let Some(mode) = monitor + .video_modes() + .max_by(|a, b| area(a.size()).cmp(&area(b.size()))) + { + window.set_fullscreen(Some(Fullscreen::Exclusive(mode))); + } else { + eprintln!("no video modes available"); } - VirtualKeyCode::V => { - visible = !visible; - window.set_visible(visible); + } + "f" => { + if window.fullscreen().is_some() { + window.set_fullscreen(None); + } else { + let monitor = window.current_monitor(); + window.set_fullscreen(Some(Fullscreen::Borderless(monitor))); } - VirtualKeyCode::X => { - maximized = !maximized; - window.set_maximized(maximized); + } + "p" => { + if window.fullscreen().is_some() { + window.set_fullscreen(None); + } else { + window.set_fullscreen(Some(Fullscreen::Borderless(None))); } - _ => (), - }, + } + "m" => { + minimized = !minimized; + window.set_minimized(minimized); + } + "q" => { + *control_flow = ControlFlow::Exit; + } + "v" => { + visible = !visible; + window.set_visible(visible); + } + "x" => { + maximized = !maximized; + window.set_maximized(maximized); + } _ => (), }, Event::WindowEvent { diff --git a/examples/window_icon.rs b/examples/window_icon.rs index 6c79625505..aaa6bc0ba7 100644 --- a/examples/window_icon.rs +++ b/examples/window_icon.rs @@ -49,7 +49,7 @@ fn load_icon(path: &Path) -> Icon { let (icon_rgba, icon_width, icon_height) = { let image = image::open(path) .expect("Failed to open icon path") - .into_rgba8(); + .into_rgba(); let (width, height) = image.dimensions(); let rgba = image.into_raw(); (rgba, width, height) diff --git a/src/event.rs b/src/event.rs index 5342ebf5d4..b72e5a6603 100644 --- a/src/event.rs +++ b/src/event.rs @@ -36,6 +36,8 @@ use instant::Instant; use std::path::PathBuf; +pub use keyboard_types; + use crate::{ dpi::{PhysicalPosition, PhysicalSize}, platform_impl, diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index fe520e599f..4cdee6cfef 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -765,6 +765,20 @@ unsafe extern "system" fn public_window_callback( ); let mut retval = None; + + // Send new modifiers before sending key events. + let mods_changed_callback = || match msg { + winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN | winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { + update_modifiers(window, subclass_input); + Some(0) + } + _ => retval, + }; + retval = subclass_input + .event_loop_runner + .catch_unwind(mods_changed_callback) + .unwrap_or(Some(-1)); + let keyboard_callback = || { use crate::event::WindowEvent::KeyboardInput; let is_keyboard_related = is_msg_keyboard_related(msg); @@ -791,7 +805,6 @@ unsafe extern "system" fn public_window_callback( } retval }; - retval = subclass_input .event_loop_runner .catch_unwind(keyboard_callback) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 32ab2cc654..a6a4dc83c6 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -179,6 +179,11 @@ impl KeyEventBuilder { } } winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { + if msg_kind == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 { + // Don't dispatch Alt+F4 to the application. + // This is handled in `event_loop.rs` + return vec![]; + } self.prev_down_was_dead = false; // When the labels are already generated for this locale, From 217789a8655a725016eed24f39228052d90f4910 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 5 Dec 2020 21:33:55 +0100 Subject: [PATCH 16/75] Ran `cargo fmt` --- examples/control_flow.rs | 6 +++--- examples/cursor.rs | 2 +- examples/fullscreen.rs | 7 +++++-- examples/handling_close.rs | 2 +- examples/multithreaded.rs | 10 +++++----- examples/multiwindow.rs | 4 ++-- examples/resizable.rs | 5 ++++- examples/window_debug.rs | 19 ++++++++++++------- 8 files changed, 33 insertions(+), 22 deletions(-) diff --git a/examples/control_flow.rs b/examples/control_flow.rs index 60f903b127..1007f6eab7 100644 --- a/examples/control_flow.rs +++ b/examples/control_flow.rs @@ -38,7 +38,7 @@ fn main() { let mut close_requested = false; event_loop.run(move |event, _, control_flow| { - use winit::event::{StartCause, keyboard_types::Key}; + use winit::event::{keyboard_types::Key, StartCause}; println!("{:?}", event); match event { Event::NewEvents(start_cause) => { @@ -77,8 +77,8 @@ fn main() { request_redraw = !request_redraw; println!("\nrequest_redraw: {}\n", request_redraw); } - _ => () - } + _ => (), + }, Key::Escape => { close_requested = true; } diff --git a/examples/cursor.rs b/examples/cursor.rs index 4402838200..81f16fdcce 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -1,6 +1,6 @@ use simple_logger::SimpleLogger; use winit::{ - event::{Event, KeyEvent, keyboard_types::{KeyState}, WindowEvent}, + event::{keyboard_types::KeyState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, WindowBuilder}, }; diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 33de596d14..6e5acd92cd 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -1,7 +1,10 @@ use std::io::{stdin, stdout, Write}; use simple_logger::SimpleLogger; -use winit::event::{Event, KeyEvent, keyboard_types::{KeyState, Key}, WindowEvent}; +use winit::event::{ + keyboard_types::{Key, KeyState}, + Event, KeyEvent, WindowEvent, +}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::monitor::{MonitorHandle, VideoMode}; use winit::window::{Fullscreen, WindowBuilder}; @@ -68,7 +71,7 @@ fn main() { window.set_decorations(decorations); } _ => (), - } + }, _ => (), }, _ => (), diff --git a/examples/handling_close.rs b/examples/handling_close.rs index c194b8c90b..3f4ce00292 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -54,7 +54,7 @@ fn main() { if close_requested { // This is where you'll want to do any cleanup you need. println!("Buh-bye!"); - + // For a single-window application like this, you'd normally just // break out of the event loop here. If you wanted to keep running the // event loop (i.e. if it's a multi-window application), you need to diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 17350648d3..706e87ed1a 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -5,7 +5,7 @@ fn main() { use simple_logger::SimpleLogger; use winit::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - event::{Event, KeyEvent, WindowEvent, ModifiersState}, + event::{Event, KeyEvent, ModifiersState, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, Fullscreen, WindowBuilder}, }; @@ -62,7 +62,7 @@ fn main() { }, .. } => { - use keyboard_types::Key::{Character, ArrowLeft, ArrowRight}; + use keyboard_types::Key::{ArrowLeft, ArrowRight, Character}; window.set_title(&format!("{:?}", key)); let state = !modifiers.shift(); match &key { @@ -128,8 +128,8 @@ fn main() { thread::sleep(Duration::from_secs(1)); window.set_visible(true); } - _ => () - } + _ => (), + }, ArrowRight | ArrowLeft => { video_mode_id = match &key { ArrowLeft => video_mode_id.saturating_sub(1), @@ -140,7 +140,7 @@ fn main() { "Picking video mode: {}", video_modes.iter().nth(video_mode_id).unwrap() ); - }, + } _ => (), } } diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index f681312f8b..24b58370a0 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use simple_logger::SimpleLogger; use winit::{ - event::{Event, KeyEvent, WindowEvent, keyboard_types::{KeyState}}, + event::{keyboard_types::KeyState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::Window, }; @@ -35,7 +35,7 @@ fn main() { } WindowEvent::KeyboardInput { event: - KeyEvent { + KeyEvent { state: KeyState::Up, .. }, diff --git a/examples/resizable.rs b/examples/resizable.rs index ffdab3f517..2c1a3851aa 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -1,7 +1,10 @@ use simple_logger::SimpleLogger; use winit::{ dpi::LogicalSize, - event::{Event, KeyEvent, keyboard_types::{Code, KeyState}, WindowEvent}, + event::{ + keyboard_types::{Code, KeyState}, + Event, KeyEvent, WindowEvent, + }, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; diff --git a/examples/window_debug.rs b/examples/window_debug.rs index 855a8d6057..6e807568d2 100644 --- a/examples/window_debug.rs +++ b/examples/window_debug.rs @@ -3,7 +3,10 @@ use simple_logger::SimpleLogger; use winit::{ dpi::{LogicalSize, PhysicalSize}, - event::{DeviceEvent, Event, RawKeyEvent, KeyEvent, keyboard_types::{KeyState, Key, Code}, WindowEvent}, + event::{ + keyboard_types::{Code, Key, KeyState}, + DeviceEvent, Event, KeyEvent, RawKeyEvent, WindowEvent, + }, event_loop::{ControlFlow, EventLoop}, window::{Fullscreen, WindowBuilder}, }; @@ -61,14 +64,16 @@ fn main() { _ => (), }, Event::WindowEvent { - event: WindowEvent::KeyboardInput { - event: KeyEvent { - logical_key: Key::Character(key_str), - state: KeyState::Up, + event: + WindowEvent::KeyboardInput { + event: + KeyEvent { + logical_key: Key::Character(key_str), + state: KeyState::Up, + .. + }, .. }, - .. - }, .. } => match key_str.to_lowercase().as_str() { "e" => { From 936dba8fd695fda17082bf454eb77d2ec156b679 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 20 Dec 2020 19:09:45 +0100 Subject: [PATCH 17/75] Add documentation for `ScanCode` --- src/event.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/event.rs b/src/event.rs index b72e5a6603..9d5ae66c03 100644 --- a/src/event.rs +++ b/src/event.rs @@ -755,7 +755,18 @@ impl Force { } } -/// Hardware-dependent keyboard scan code. +/// An opaque struct that (mostly) uniquely identifies a single physical key +/// on the current platform. +/// +/// This is distinct from `keyboard_types::Code` because this uses +/// the platform specific identifier for keys, while +/// `keyboard_types::Code` may be `Unidentified` for multiple keys +/// with different `ScanCode`. +/// +/// Furthermore this struct may store a value that cannot be ported +/// to another platform, hence it is opaque. To retreive the underlying +/// value, use one of the platform-dependent extension traits like +/// `XkbScanCodeExt` #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct ScanCode(pub(crate) platform_impl::PlatformScanCode); From 75b16a9a28c20a40c96240e0b23c1ac7aafd71fa Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 20 Dec 2020 22:40:47 +0100 Subject: [PATCH 18/75] Add key binding example --- examples/key_binding.rs | 60 +++++++++++++++++++++++++++++ src/event.rs | 4 +- src/platform/mod.rs | 1 + src/platform/modifier_supplement.rs | 46 ++++++++++++++++++++++ src/platform/windows.rs | 13 ++++++- 5 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 examples/key_binding.rs create mode 100644 src/platform/modifier_supplement.rs diff --git a/examples/key_binding.rs b/examples/key_binding.rs new file mode 100644 index 0000000000..a344853a5a --- /dev/null +++ b/examples/key_binding.rs @@ -0,0 +1,60 @@ +use simple_logger::SimpleLogger; +use winit::{ + dpi::LogicalSize, + event::{keyboard_types::KeyState, Event, KeyEvent, ModifiersState, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; + +///////////////////////////////////////////////////////////////////////////// +// WARNING: This is not available on all platforms (for example on the web). +use winit::platform::modifier_supplement::KeyEventExtModifierSupplement; +///////////////////////////////////////////////////////////////////////////// + +fn main() { + SimpleLogger::new().init().unwrap(); + let event_loop = EventLoop::new(); + + let _window = WindowBuilder::new() + .with_inner_size(LogicalSize::new(400.0, 200.0)) + .build(&event_loop) + .unwrap(); + + let mut modifiers = ModifiersState::default(); + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; + + match event { + Event::WindowEvent { event, .. } => match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::ModifiersChanged(new_state) => { + modifiers = new_state; + } + WindowEvent::KeyboardInput { event, .. } => { + handle_key_event(modifiers, event); + } + _ => (), + }, + _ => (), + }; + }); +} + +fn handle_key_event(modifiers: ModifiersState, event: KeyEvent) { + if event.state == KeyState::Down && !event.repeat { + match event.key_without_modifers() { + keyboard_types::Key::Character(c) => match c.as_str() { + "1" => { + if modifiers.shift() { + println!("Shift + 1 | logical_key: {:?}", event.logical_key); + } else { + println!("1"); + } + } + _ => (), + }, + _ => (), + } + } +} diff --git a/src/event.rs b/src/event.rs index 9d5ae66c03..ec66551c7b 100644 --- a/src/event.rs +++ b/src/event.rs @@ -757,12 +757,12 @@ impl Force { /// An opaque struct that (mostly) uniquely identifies a single physical key /// on the current platform. -/// +/// /// This is distinct from `keyboard_types::Code` because this uses /// the platform specific identifier for keys, while /// `keyboard_types::Code` may be `Unidentified` for multiple keys /// with different `ScanCode`. -/// +/// /// Furthermore this struct may store a value that cannot be ported /// to another platform, hence it is opaque. To retreive the underlying /// value, use one of the platform-dependent extension traits like diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 1ee5fce2bb..7ef041a025 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -21,5 +21,6 @@ pub mod macos; pub mod unix; pub mod windows; +pub mod modifier_supplement; pub mod run_return; pub mod web; diff --git a/src/platform/modifier_supplement.rs b/src/platform/modifier_supplement.rs new file mode 100644 index 0000000000..e5fb28a782 --- /dev/null +++ b/src/platform/modifier_supplement.rs @@ -0,0 +1,46 @@ +#![cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] + +use keyboard_types; + +/// Additional methods for the `KeyEvent` which cannot be implemented on all +/// platforms. +pub trait KeyEventExtModifierSupplement { + /// This value is affected by all modifiers including but not + /// limited to Shift, Ctrl, and Num Lock. + /// + /// This is suitable for text input in a terminal application. + /// + /// `None` is returned if the input cannot be translated to a string. + /// For example dead key input as well as F1 and + /// Home among others produce `None`. + /// + /// Note that the resulting string may contain multiple characters. + /// For example on Windows when pressing ' using + /// a US-International layout, this will be `None` for the first + /// keypress and will be `Some("''")` for the second keypress. + /// It's important that this behaviour might be different on + /// other platforms. For example Linux systems may emit a + /// `Some("'")` on the second keypress. + fn char_with_all_modifers(&self) -> &Option; + + /// This value ignores all modifiers including + /// but not limited to Shift, Caps Lock, + /// and Ctrl. In most cases this means that the + /// unicode character in the resulting string is lowercase. + /// + /// This is useful for key-bindings / shortcut key combinations. + /// + /// In case `logical_key` reports `Dead`, this will still report the + /// real key according to the current keyboard layout. This value + /// cannot be `Dead`. Furthermore the `Character` variant will always + /// contain a single-character String. + fn key_without_modifers(&self) -> &keyboard_types::Key; +} diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 05839d89c1..d46a2a31ed 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -9,9 +9,10 @@ use winapi::shared::windef::HWND; use crate::{ dpi::PhysicalSize, - event::DeviceId, + event::{DeviceId, KeyEvent}, event_loop::EventLoop, monitor::MonitorHandle, + platform::modifier_supplement::KeyEventExtModifierSupplement, platform_impl::{EventLoop as WindowsEventLoop, WinIcon}, window::{BadIcon, Icon, Theme, Window, WindowBuilder}, }; @@ -234,3 +235,13 @@ impl IconExtWindows for Icon { Ok(Icon { inner: win_icon }) } } + +impl KeyEventExtModifierSupplement for KeyEvent { + fn char_with_all_modifers(&self) -> &Option { + &self.platform_specific.char_with_all_modifers + } + + fn key_without_modifers(&self) -> &keyboard_types::Key { + &self.platform_specific.key_without_modifers + } +} From 7bbd122ff7a8b2f66f827f5773416d21eac28da1 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 2 Jan 2021 11:17:10 +0100 Subject: [PATCH 19/75] Use consistent modifier key names #1343 --- src/event.rs | 8 ++++---- src/platform_impl/windows/event.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/event.rs b/src/event.rs index ec66551c7b..ee32ce4d25 100644 --- a/src/event.rs +++ b/src/event.rs @@ -820,7 +820,7 @@ impl ModifiersState { } /// Returns `true` if the control key is pressed. pub fn ctrl(&self) -> bool { - self.intersects(Self::CTRL) + self.intersects(Self::CONTROL) } /// Returns `true` if the alt key is pressed. pub fn alt(&self) -> bool { @@ -828,7 +828,7 @@ impl ModifiersState { } /// Returns `true` if the logo key is pressed. pub fn logo(&self) -> bool { - self.intersects(Self::LOGO) + self.intersects(Self::META) } } @@ -845,7 +845,7 @@ bitflags! { // const LSHIFT = 0b010 << 0; // const RSHIFT = 0b001 << 0; /// The "control" key. - const CTRL = 0b100 << 3; + const CONTROL = 0b100 << 3; // const LCTRL = 0b010 << 3; // const RCTRL = 0b001 << 3; /// The "alt" key. @@ -853,7 +853,7 @@ bitflags! { // const LALT = 0b010 << 6; // const RALT = 0b001 << 6; /// This is the "windows" key on PC and "command" key on Mac. - const LOGO = 0b100 << 9; + const META = 0b100 << 9; // const LLOGO = 0b010 << 9; // const RLOGO = 0b001 << 9; } diff --git a/src/platform_impl/windows/event.rs b/src/platform_impl/windows/event.rs index 07e5d788e4..4ce978cc4d 100644 --- a/src/platform_impl/windows/event.rs +++ b/src/platform_impl/windows/event.rs @@ -30,7 +30,7 @@ pub fn get_key_mods() -> ModifiersState { let mut mods = ModifiersState::empty(); mods.set(ModifiersState::SHIFT, key_pressed(winuser::VK_SHIFT)); mods.set( - ModifiersState::CTRL, + ModifiersState::CONTROL, key_pressed(winuser::VK_CONTROL) && !filter_out_altgr, ); mods.set( @@ -38,7 +38,7 @@ pub fn get_key_mods() -> ModifiersState { key_pressed(winuser::VK_MENU) && !filter_out_altgr, ); mods.set( - ModifiersState::LOGO, + ModifiersState::META, key_pressed(winuser::VK_LWIN) || key_pressed(winuser::VK_RWIN), ); mods @@ -78,7 +78,7 @@ impl From for ModifiersState { side.intersects(ModifiersStateSide::LSHIFT | ModifiersStateSide::RSHIFT), ); state.set( - Self::CTRL, + Self::CONTROL, side.intersects(ModifiersStateSide::LCTRL | ModifiersStateSide::RCTRL), ); state.set( @@ -86,7 +86,7 @@ impl From for ModifiersState { side.intersects(ModifiersStateSide::LALT | ModifiersStateSide::RALT), ); state.set( - Self::LOGO, + Self::META, side.intersects(ModifiersStateSide::LLOGO | ModifiersStateSide::RLOGO), ); state From e469a9726127a7409e9eaae6142daa23c76d2eb1 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 3 Jan 2021 22:57:37 +0100 Subject: [PATCH 20/75] WONT COMPILE transitioning to new keyboard API --- Cargo.toml | 1 - src/event.rs | 82 +- src/keyboard.rs | 1099 +++++++++++++++++++++++++ src/lib.rs | 1 + src/platform_impl/windows/keyboard.rs | 626 +++++++------- 5 files changed, 1442 insertions(+), 367 deletions(-) create mode 100644 src/keyboard.rs diff --git a/Cargo.toml b/Cargo.toml index b66e65bc20..d220fb2197 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ log = "0.4" serde = { version = "1", optional = true, features = ["serde_derive"] } raw-window-handle = "0.3" bitflags = "1" -keyboard-types = "0.5" [dev-dependencies] image = "0.23" diff --git a/src/event.rs b/src/event.rs index ee32ce4d25..e19166211d 100644 --- a/src/event.rs +++ b/src/event.rs @@ -40,6 +40,7 @@ pub use keyboard_types; use crate::{ dpi::{PhysicalPosition, PhysicalSize}, + keyboard, platform_impl, window::{Theme, WindowId}, }; @@ -603,60 +604,62 @@ pub enum DeviceEvent { /// Describes a keyboard input as a raw device event. #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct RawKeyEvent { - pub scancode: ScanCode, pub physical_key: keyboard_types::Code, pub state: keyboard_types::KeyState, } /// Describes a keyboard input targeting a window. -// TODO: Implement (de)serialization. -// (This struct cannot be serialized in its entirety because `ScanCode` -// contains platform dependent data so that value cannot be serialized) #[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct KeyEvent { - pub scancode: ScanCode, - /// Represents the position of a key independent of the /// currently active layout. /// Conforms to https://www.w3.org/TR/uievents-code/ /// /// Note that `Fn` and `FnLock` key events are not emmited by `winit`. /// These keys are usually handled at the hardware or at the OS level. - pub physical_key: keyboard_types::Code, - + pub physical_key: keyboard::KeyCode, + /// This value is affected by all modifiers except Ctrl. - /// - /// This is suitable for text input in a GUI application. - /// - /// Note that the `Unicode` variant may contain multiple characters. - /// For example on Windows when pressing ^ using - /// a US-International layout, this will be `Dead` for the first - /// keypress and will be `Unicode("^^")` for the second keypress. - /// It's important that this behaviour might be different on - /// other platforms. For example Linux systems may emit a - /// `Unicode("^")` on the second keypress. - /// + /// + /// This has two use cases: + /// - Allows querying whether the current input is a Dead key + /// - Allows handling key-bindings on platforms which don't + /// support `KeyEventExtModifierSupplement::key_without_modifiers`. + /// /// ## Platform-specific /// - **Web:** Dead keys might be reported as the real key instead /// of `Dead` depending on the browser/OS. - pub logical_key: keyboard_types::Key, - - pub location: keyboard_types::Location, - pub state: keyboard_types::KeyState, + pub logical_key: keyboard::Key<'static>, + + /// Contains the text produced by this keypress. + /// + /// In most cases this is identical to the content + /// of the `Character` variant of `logical_key`. + /// However, on Windows when a dead key was pressed earlier + /// but cannot be combined with the character from this + /// keypress, the produced text will consist of two characters: + /// the dead-key-character followed by the character resulting + /// from this keypress. + /// + /// An additional difference from `logical_key` is that + /// this field stores the text representation of any key + /// that has such a representation. For example when + /// `logical_key` is `Key::Enter`, this field is `Some("\r")`. + /// + /// This is `None` if the current keypress cannot + /// be interpreted as text. + /// + /// See also: `text_with_all_modifiers()` + pub text: Option<&'static str>, + + pub location: keyboard::KeyLocation, + pub state: ElementState, pub repeat: bool, pub(crate) platform_specific: platform_impl::KeyEventExtra, } -// impl std::fmt::Debug for KeyEvent { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// f.debug_struct("KeyEvent") -// .field("scancode", &()) -// .field("y", &self.y) -// .finish() -// } -// } - /// Describes touch-screen input state. #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -755,21 +758,6 @@ impl Force { } } -/// An opaque struct that (mostly) uniquely identifies a single physical key -/// on the current platform. -/// -/// This is distinct from `keyboard_types::Code` because this uses -/// the platform specific identifier for keys, while -/// `keyboard_types::Code` may be `Unidentified` for multiple keys -/// with different `ScanCode`. -/// -/// Furthermore this struct may store a value that cannot be ported -/// to another platform, hence it is opaque. To retreive the underlying -/// value, use one of the platform-dependent extension traits like -/// `XkbScanCodeExt` -#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] -pub struct ScanCode(pub(crate) platform_impl::PlatformScanCode); - /// Identifier for a specific analog axis on some device. pub type AxisId = u32; diff --git a/src/keyboard.rs b/src/keyboard.rs new file mode 100644 index 0000000000..97e821df09 --- /dev/null +++ b/src/keyboard.rs @@ -0,0 +1,1099 @@ + + +/// Contains the platform-native physical key identifier (aka scancode) +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum NativeKeyCode { + Unidentified, + Windows(u16), + MacOS(u32), + XKB(u32), +} + +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum KeyCode { + /// This variant is used when the key cannot be translated to any + /// other variant. + /// + /// The native scancode is provided (if available) in order + /// to allow the user to specify keybindings for keys which + /// are not defined by this API. + Unidentified(NativeKeyCode), + /// ` on a US keyboard. This is the 半角/全角/漢字 (hankaku/zenkaku/kanji) key on Japanese keyboards + Backquote, + /// Used for both the US \ (on the 101-key layout) and also for the key + /// located between the " and Enter keys on row C of the 102-, + /// 104- and 106-key layouts. + /// Labelled # on a UK (102) keyboard. + Backslash, + /// [ on a US keyboard. + BracketLeft, + /// ] on a US keyboard. + BracketRight, + /// , on a US keyboard. + Comma, + /// 0 on a US keyboard. + Digit0, + /// 1 on a US keyboard. + Digit1, + /// 2 on a US keyboard. + Digit2, + /// 3 on a US keyboard. + Digit3, + /// 4 on a US keyboard. + Digit4, + /// 5 on a US keyboard. + Digit5, + /// 6 on a US keyboard. + Digit6, + /// 7& on a US keyboard. + Digit7, + /// 8 on a US keyboard. + Digit8, + /// 9 on a US keyboard. + Digit9, + /// = on a US keyboard. + Equal, + /// Located between the left Shift and Z keys. + /// Labelled \ on a UK keyboard. + IntlBackslash, + /// Located between the / and right Shift keys. + /// Labelled \ (ro) on a Japanese keyboard. + IntlRo, + /// Located between the = and Backspace keys. + /// Labelled ¥ (yen) on a Japanese keyboard. \ on a + /// Russian keyboard. + IntlYen, + /// a on a US keyboard. + /// Labelled q on an AZERTY (e.g., French) keyboard. + KeyA, + /// b on a US keyboard. + KeyB, + /// c on a US keyboard. + KeyC, + /// d on a US keyboard. + KeyD, + /// e on a US keyboard. + KeyE, + /// f on a US keyboard. + KeyF, + /// g on a US keyboard. + KeyG, + /// h on a US keyboard. + KeyH, + /// i on a US keyboard. + KeyI, + /// j on a US keyboard. + KeyJ, + /// k on a US keyboard. + KeyK, + /// l on a US keyboard. + KeyL, + /// m on a US keyboard. + KeyM, + /// n on a US keyboard. + KeyN, + /// o on a US keyboard. + KeyO, + /// p on a US keyboard. + KeyP, + /// q on a US keyboard. + /// Labelled a on an AZERTY (e.g., French) keyboard. + KeyQ, + /// r on a US keyboard. + KeyR, + /// s on a US keyboard. + KeyS, + /// t on a US keyboard. + KeyT, + /// u on a US keyboard. + KeyU, + /// v on a US keyboard. + KeyV, + /// w on a US keyboard. + /// Labelled z on an AZERTY (e.g., French) keyboard. + KeyW, + /// x on a US keyboard. + KeyX, + /// y on a US keyboard. + /// Labelled z on a QWERTZ (e.g., German) keyboard. + KeyY, + /// z on a US keyboard. + /// Labelled w on an AZERTY (e.g., French) keyboard, and y on a + /// QWERTZ (e.g., German) keyboard. + KeyZ, + /// - on a US keyboard. + Minus, + /// . on a US keyboard. + Period, + /// ' on a US keyboard. + Quote, + /// ; on a US keyboard. + Semicolon, + /// / on a US keyboard. + Slash, + /// Alt Option or . + AltLeft, + /// Alt Option or . + /// This is labelled AltGr key on many keyboard layouts. + AltRight, + /// Backspace or . + /// Labelled Delete on Apple keyboards. + Backspace, + /// CapsLock or + CapsLock, + /// The application context menu key, which is typically found between the right Meta key and the right Control key. + ContextMenu, + /// Control or + ControlLeft, + /// Control or + ControlRight, + /// Enter or . Labelled Return on Apple keyboards. + Enter, + /// The Windows, Command or other OS symbol key. + MetaLeft, + /// The Windows, Command or other OS symbol key. + MetaRight, + /// Shift or + ShiftLeft, + /// Shift or + ShiftRight, + ///   (space) + Space, + /// Tab or + Tab, + /// Japanese: (henkan) + Convert, + /// Japanese: カタカナ/ひらがな/ローマ字 (katakana/hiragana/romaji) + KanaMode, + /// Korean: HangulMode 한/영 (han/yeong)
Japanese (Mac keyboard): (kana) + Lang1, + /// Korean: Hanja (hanja)
Japanese (Mac keyboard): (eisu) + Lang2, + /// Japanese (word-processing keyboard): Katakana + Lang3, + /// Japanese (word-processing keyboard): Hiragana + Lang4, + /// Japanese (word-processing keyboard): Zenkaku/Hankaku + Lang5, + /// Japanese: 無変換 (muhenkan) + NonConvert, + /// . The forward delete key. + /// Note that on Apple keyboards, the key labelled Delete on the main part of + /// the keyboard should be encoded as "Backspace". + Delete, + /// Page Down End or + End, + /// Help. Not present on standard PC keyboards. + Help, + /// Home or + Home, + /// Insert or Ins. Not present on Apple keyboards. + Insert, + /// Page Down PgDn or + PageDown, + /// Page Up PgUp or + PageUp, + /// + ArrowDown, + /// + ArrowLeft, + /// + ArrowRight, + /// + ArrowUp, + /// On the Mac, the "NumLock" code should be used for the numpad Clear key. + NumLock, + /// 0 Ins on a keyboard
0 on a phone or remote control + Numpad0, + /// 1 End on a keyboard
1 or 1 QZ on a phone or + /// remote control + Numpad1, + /// 2 ↓ on a keyboard
2 ABC on a phone or remote control + Numpad2, + /// 3 PgDn on a keyboard
3 DEF on a phone or remote control + Numpad3, + /// 4 ← on a keyboard
4 GHI on a phone or remote control + Numpad4, + /// 5 on a keyboard
5 JKL on a phone or remote control + Numpad5, + /// 6 → on a keyboard
6 MNO on a phone or remote control + Numpad6, + /// 7 Home on a keyboard
7 PQRS or 7 PRS on a phone + /// or remote control + Numpad7, + /// 8 ↑ on a keyboard
8 TUV on a phone or remote control + Numpad8, + /// 9 PgUp on a keyboard
9 WXYZ or 9 WXY on a phone + /// or remote control + Numpad9, + /// + + NumpadAdd, + /// Found on the Microsoft Natural Keyboard. + NumpadBackspace, + /// C or A (All Clear). Also for use with numpads that have a Clear key that is separate from the NumLock key. On the Mac, the numpad Clear key should always + /// be encoded as "NumLock". + NumpadClear, + /// C (Clear Entry) + NumpadClearEntry, + /// , (thousands separator). For locales where the thousands separator + /// is a "." (e.g., Brazil), this key may generate a .. + NumpadComma, + /// . Del. For locales where the decimal separator is "," (e.g., + /// Brazil), this key may generate a ,. + NumpadDecimal, + /// / + NumpadDivide, + NumpadEnter, + /// = + NumpadEqual, + /// # on a phone or remote control device. This key is typically found + /// below the 9 key and to the right of the 0 key. + NumpadHash, + /// M Add current entry to the value stored in memory. + NumpadMemoryAdd, + /// M Clear the value stored in memory. + NumpadMemoryClear, + /// M Replace the current entry with the value stored in memory. + NumpadMemoryRecall, + /// M Replace the value stored in memory with the current entry. + NumpadMemoryStore, + /// M Subtract current entry from the value stored in memory. + NumpadMemorySubtract, + /// * on a keyboard. For use with numpads that provide mathematical + /// operations (+, - * and /).
Use "NumpadStar" for the * key on phones and remote controls. + NumpadMultiply, + /// ( Found on the Microsoft Natural Keyboard. + NumpadParenLeft, + /// ) Found on the Microsoft Natural Keyboard. + NumpadParenRight, + /// * on a phone or remote control device. + /// This key is typically found below the 7 key and to the left of + /// the 0 key.
Use "NumpadMultiply" for the * key on + /// numeric keypads. + NumpadStar, + /// - + NumpadSubtract, + /// Esc or + Escape, + /// F This is typically a hardware key that does not generate a separate + /// code. Most keyboards do not place this key in the function section, but it is + /// included here to keep it with related keys. + Fn, + /// FLock or FnLock. Function Lock key. Found on the Microsoft + /// Natural Keyboard. + FnLock, + /// PrtScr SysRq or Print Screen + PrintScreen, + /// Scroll Lock + ScrollLock, + /// Pause Break + Pause, + /// Some laptops place this key to the left of the key. + BrowserBack, + BrowserFavorites, + /// Some laptops place this key to the right of the key. + BrowserForward, + BrowserHome, + BrowserRefresh, + BrowserSearch, + BrowserStop, + /// Eject or . This key is placed in the function + /// section on some Apple keyboards. + Eject, + /// Sometimes labelled My Computer on the keyboard + LaunchApp1, + /// Sometimes labelled Calculator on the keyboard + LaunchApp2, + LaunchMail, + MediaPlayPause, + MediaSelect, + MediaStop, + MediaTrackNext, + MediaTrackPrevious, + /// This key is placed in the function section on some Apple keyboards, + /// replacing the Eject key. + Power, + Sleep, + AudioVolumeDown, + AudioVolumeMute, + AudioVolumeUp, + WakeUp, + Hyper, + Super, + Turbo, + Abort, + Resume, + Suspend, + /// Found on Sun’s USB keyboard. + Again, + /// Found on Sun’s USB keyboard. + Copy, + /// Found on Sun’s USB keyboard. + Cut, + /// Found on Sun’s USB keyboard. + Find, + /// Found on Sun’s USB keyboard. + Open, + /// Found on Sun’s USB keyboard. + Paste, + /// Found on Sun’s USB keyboard. + Props, + /// Found on Sun’s USB keyboard. + Select, + /// Found on Sun’s USB keyboard. + Undo, + /// Use for dedicated ひらがな key found on some Japanese word processing keyboards. + Hiragana, + /// Use for dedicated カタカナ key found on some Japanese word processing keyboards. + Katakana, + /// F + F1, + /// F + F2, + /// F + F3, + /// F + F4, + /// F + F5, + /// F + F6, + /// F + F7, + /// F + F8, + /// F + F9, + /// F10 + F10, + /// F11 + F11, + /// F12 + F12, + /// F13 + F13, + /// F14 + F14, + /// F15 + F15, + /// F16 + F16, + /// F17 + F17, + /// F18 + F18, + /// F19 + F19, + /// F20 + F20, + /// F21 + F21, + /// F22 + F22, + /// F23 + F23, + /// F24 + F24, + /// F25 + F25, + /// F26 + F26, + /// F27 + F27, + /// F28 + F28, + /// F29 + F29, + /// F30 + F30, + /// F31 + F31, + /// F32 + F32, + /// F33 + F33, + /// F34 + F34, + /// F35 + F35, +} + +/// Key represents the meaning of a keypress. +/// +/// Specification: +/// https://w3c.github.io/uievents-key/ +#[non_exhaustive] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Key<'a> { + /// A key string that corresponds to the character typed by the user, + /// taking into account the user’s current locale setting, modifier state, + /// and any system-level keyboard mapping overrides that are in effect. + Character(&'a str), + + /// This variant is used when the key cannot be translated to any + /// other variant. + /// + /// The native scancode is provided (if available) in order + /// to allow the user to specify keybindings for keys which + /// are not defined by this API. + Unidentified(NativeKeyCode), + + /// Contains the text representation of the dead-key + /// when available. + /// + /// ## Platform-specific + /// - **Web:** Always contains `None` + Dead(Option), + + /// The `Alt` (Alternative) key.
This key enables the alternate modifier function for interpreting concurrent or subsequent keyboard input.
This key value is also used for the Apple `Option` key. + Alt, + /// The Alternate Graphics (`AltGr` or `AltGraph`) key. + /// This key is used enable the ISO Level 3 shift modifier (the standard `Shift` key is the level 2 modifier). + /// See [ISO9995-1]. + AltGraph, + /// The `Caps Lock` (Capital) key. + /// Toggle capital character lock function for interpreting subsequent keyboard input event. + CapsLock, + /// The `Control` or `Ctrl` key, to enable control modifier function for interpreting concurrent or subsequent keyboard input. + Control, + /// The Function switch `Fn` key.
Activating this key simultaneously with another key changes that key’s value to an alternate character or function. + /// This key is often handled directly in the keyboard hardware and does not usually generate key events. + Fn, + /// The Function-Lock (`FnLock` or `F-Lock`) key. + /// Activating this key switches the mode of the keyboard to changes some keys' values to an alternate character or function. + /// This key is often handled directly in the keyboard hardware and does not usually generate key events. + FnLock, + /// The `Meta` key, to enable meta modifier function for interpreting concurrent or subsequent keyboard input. + /// This key value is used for the Windows Logo key and the Apple `Command` or `⌘` key. + Meta, + /// The `NumLock` or Number Lock key, to toggle numpad mode function for interpreting subsequent keyboard input. + NumLock, + /// The `Scroll Lock` key, to toggle between scrolling and cursor movement modes. + ScrollLock, + /// The `Shift` key, to enable shift modifier function for interpreting concurrent or subsequent keyboard input. + Shift, + /// The Symbol modifier key (used on some virtual keyboards). + Symbol, + /// The Symbol Lock key. + SymbolLock, + /// The `Hyper` key. + Hyper, + /// The `Super` key. + Super, + /// The `Enter` or `↵` key, to activate current selection or accept current input.
This key value is also used for the `Return` (Macintosh numpad) key.
This key value is also used for the Android `KEYCODE_DPAD_CENTER`. + Enter, + /// The Horizontal Tabulation `Tab` key. + Tab, + /// The down arrow key, to navigate or traverse downward. (`KEYCODE_DPAD_DOWN`) + ArrowDown, + /// The left arrow key, to navigate or traverse leftward. (`KEYCODE_DPAD_LEFT`) + ArrowLeft, + /// The right arrow key, to navigate or traverse rightward. (`KEYCODE_DPAD_RIGHT`) + ArrowRight, + /// The up arrow key, to navigate or traverse upward. (`KEYCODE_DPAD_UP`) + ArrowUp, + /// The End key, used with keyboard entry to go to the end of content (`KEYCODE_MOVE_END`). + End, + /// The Home key, used with keyboard entry, to go to start of content (`KEYCODE_MOVE_HOME`).
For the mobile phone `Home` key (which goes to the phone’s main screen), use `"GoHome"`. + Home, + /// The Page Down key, to scroll down or display next page of content. + PageDown, + /// The Page Up key, to scroll up or display previous page of content. + PageUp, + /// The Backspace key. This key value is also used for the key labeled `Delete` on MacOS keyboards. + Backspace, + /// Remove the currently selected input. + Clear, + /// Copy the current selection. (`APPCOMMAND_COPY`) + Copy, + /// The Cursor Select (Crsel) key. + CrSel, + /// Cut the current selection. (`APPCOMMAND_CUT`) + Cut, + /// The Delete (Del) Key. + /// This key value is also used for the key labeled `Delete` on MacOS keyboards when modified by the `Fn` key. + Delete, + /// The Erase to End of Field key. + /// This key deletes all characters from the current cursor position to the end of the current field. + EraseEof, + /// The Extend Selection (Exsel) key. + ExSel, + /// The Insert (Ins) key, to toggle between text modes for insertion or overtyping. (`KEYCODE_INSERT`) + Insert, + /// The Paste key. (`APPCOMMAND_PASTE`) + Paste, + /// Redo the last action. (`APPCOMMAND_REDO`) + Redo, + /// Undo the last action. (`APPCOMMAND_UNDO`) + Undo, + /// The Accept (Commit, OK) key. Accept current option or input method sequence conversion. + Accept, + /// The Again key, to redo or repeat an action. + Again, + /// The Attention (Attn) key. + Attn, + /// The Cancel key. + Cancel, + /// Show the application’s context menu. + /// This key is commonly found between the right `Meta` key and the right `Control` key. + ContextMenu, + /// The `Esc` key. This key was originally used to initiate an escape sequence, but is + /// now more generally used to exit or "escape" the current context, such as closing a dialog + /// or exiting full screen mode. + Escape, + /// The Execute key. + Execute, + /// Open the Find dialog. (`APPCOMMAND_FIND`) + Find, + /// Open a help dialog or toggle display of help information. (`APPCOMMAND_HELP`, `KEYCODE_HELP`) + Help, + /// Pause the current state or application (as appropriate). + ///

Do not use this value for the `Pause` button on media controllers. Use `"MediaPause"` instead.

+ Pause, + /// Play or resume the current state or application (as appropriate). + ///

Do not use this value for the `Play` button on media controllers. Use `"MediaPlay"` instead.

+ Play, + /// The properties (Props) key. + Props, + /// The Select key. + Select, + /// The ZoomIn key. (`KEYCODE_ZOOM_IN`) + ZoomIn, + /// The ZoomOut key. (`KEYCODE_ZOOM_OUT`) + ZoomOut, + /// The Brightness Down key. Typically controls the display brightness. (`KEYCODE_BRIGHTNESS_DOWN`) + BrightnessDown, + /// The Brightness Up key. Typically controls the display brightness. (`KEYCODE_BRIGHTNESS_UP`) + BrightnessUp, + /// Toggle removable media to eject (open) and insert (close) state. (`KEYCODE_MEDIA_EJECT`) + Eject, + /// The LogOff key. + LogOff, + /// Toggle power state. (`KEYCODE_POWER`) + ///

Note: Some devices might not expose this key to the operating environment.

+ Power, + /// The `PowerOff` key. Sometime called `PowerDown`. + PowerOff, + /// The `Print Screen` or `SnapShot` key, to initiate print-screen function. + PrintScreen, + /// The Hibernate key. + /// This key saves the current state of the computer to disk so that it can be restored. The computer will then shutdown. + Hibernate, + /// The Standby key. + /// This key turns off the display and places the computer into a low-power mode without completely shutting down. + /// It is sometimes labelled `Suspend` or `Sleep` key. (`KEYCODE_SLEEP`) + Standby, + /// The WakeUp key. (`KEYCODE_WAKEUP`) + WakeUp, + /// The All Candidates key, to initate the multi-candidate mode. + AllCandidates, + /// The Alphanumeric key. + Alphanumeric, + /// The Code Input key, to initiate the Code Input mode to allow characters to be entered by their code points. + CodeInput, + /// The Compose key, also known as Multi_key on the X Window System. + /// This key acts in a manner similar to a + /// dead key, triggering a mode where subsequent key presses are combined to produce a different character. + Compose, + /// The Convert key, to convert the current input method sequence. + Convert, + /// The Final Mode `Final` key used on some Asian keyboards, to enable the final mode for IMEs. + FinalMode, + /// Switch to the first character group. (ISO/IEC 9995) + GroupFirst, + /// Switch to the last character group. (ISO/IEC 9995) + GroupLast, + /// Switch to the next character group. (ISO/IEC 9995) + GroupNext, + /// Switch to the previous character group. (ISO/IEC 9995) + GroupPrevious, + /// The Mode Change key, to toggle between or cycle through input modes of IMEs. + ModeChange, + /// The Next Candidate function key. + NextCandidate, + /// The NonConvert ("Don’t Convert") key, to accept current input method sequence without conversion in IMEs. + NonConvert, + /// The Previous Candidate function key. + PreviousCandidate, + /// The Process key. + Process, + /// The Single Candidate function key. + SingleCandidate, + /// The Hangul (Korean characters) Mode key, to toggle between Hangul and English modes. + HangulMode, + /// The Hanja (Korean characters) Mode key. + HanjaMode, + /// The Junja (Korean characters) Mode key. + JunjaMode, + /// The Eisu key. This key may close the IME, but its purpose + /// is defined by the current IME. (`KEYCODE_EISU`) + Eisu, + /// The (Half-Width) Characters key. + Hankaku, + /// The Hiragana (Japanese Kana characters) key. + Hiragana, + /// The Hiragana/Katakana toggle key. (`KEYCODE_KATAKANA_HIRAGANA`) + HiraganaKatakana, + /// The Kana Mode (Kana Lock) key. This key is used to enter + /// hiragana mode (typically from romaji mode). + KanaMode, + /// The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key. + /// This key is typically used to switch to a hiragana keyboard for + /// the purpose of converting input into kanji. (`KEYCODE_KANA`) + KanjiMode, + /// The Katakana (Japanese Kana characters) key. + Katakana, + /// The Roman characters function key. + Romaji, + /// The Zenkaku (Full-Width) Characters key. + Zenkaku, + /// The Zenkaku/Hankaku (full-width/half-width) toggle key. (`KEYCODE_ZENKAKU_HANKAKU`) + ZenkakuHankaku, + /// General purpose virtual function key, as index 1. + Soft1, + /// General purpose virtual function key, as index 2. + Soft2, + /// General purpose virtual function key, as index 3. + Soft3, + /// General purpose virtual function key, as index 4. + Soft4, + /// Select next (numerically or logically) lower channel. (`APPCOMMAND_MEDIA_CHANNEL_DOWN`, `KEYCODE_CHANNEL_DOWN`) + ChannelDown, + /// Select next (numerically or logically) higher channel. (`APPCOMMAND_MEDIA_CHANNEL_UP`, `KEYCODE_CHANNEL_UP`) + ChannelUp, + /// Close the current document or message (Note: This doesn’t close the application). (`APPCOMMAND_CLOSE`) + Close, + /// Open an editor to forward the current message. (`APPCOMMAND_FORWARD_MAIL`) + MailForward, + /// Open an editor to reply to the current message. (`APPCOMMAND_REPLY_TO_MAIL`) + MailReply, + /// Send the current message. (`APPCOMMAND_SEND_MAIL`) + MailSend, + /// Close the current media, for example to close a CD or DVD tray. (`KEYCODE_MEDIA_CLOSE`) + MediaClose, + /// Initiate or continue forward playback at faster than normal speed, or increase speed if already fast forwarding. (`APPCOMMAND_MEDIA_FAST_FORWARD`, `KEYCODE_MEDIA_FAST_FORWARD`) + MediaFastForward, + /// Pause the currently playing media. (`APPCOMMAND_MEDIA_PAUSE`, `KEYCODE_MEDIA_PAUSE`) + ///

Media controller devices should use this value rather than `"Pause"` for their pause keys.

+ MediaPause, + /// Initiate or continue media playback at normal speed, if not currently playing at normal speed. (`APPCOMMAND_MEDIA_PLAY`, `KEYCODE_MEDIA_PLAY`) + MediaPlay, + /// Toggle media between play and pause states. (`APPCOMMAND_MEDIA_PLAY_PAUSE`, `KEYCODE_MEDIA_PLAY_PAUSE`) + MediaPlayPause, + /// Initiate or resume recording of currently selected media. (`APPCOMMAND_MEDIA_RECORD`, `KEYCODE_MEDIA_RECORD`) + MediaRecord, + /// Initiate or continue reverse playback at faster than normal speed, or increase speed if already rewinding. (`APPCOMMAND_MEDIA_REWIND`, `KEYCODE_MEDIA_REWIND`) + MediaRewind, + /// Stop media playing, pausing, forwarding, rewinding, or recording, if not already stopped. (`APPCOMMAND_MEDIA_STOP`, `KEYCODE_MEDIA_STOP`) + MediaStop, + /// Seek to next media or program track. (`APPCOMMAND_MEDIA_NEXTTRACK`, `KEYCODE_MEDIA_NEXT`) + MediaTrackNext, + /// Seek to previous media or program track. (`APPCOMMAND_MEDIA_PREVIOUSTRACK`, `KEYCODE_MEDIA_PREVIOUS`) + MediaTrackPrevious, + /// Open a new document or message. (`APPCOMMAND_NEW`) + New, + /// Open an existing document or message. (`APPCOMMAND_OPEN`) + Open, + /// Print the current document or message. (`APPCOMMAND_PRINT`) + Print, + /// Save the current document or message. (`APPCOMMAND_SAVE`) + Save, + /// Spellcheck the current document or selection. (`APPCOMMAND_SPELL_CHECK`) + SpellCheck, + /// The `11` key found on media numpads that + /// have buttons from `1` ... `12`. + Key11, + /// The `12` key found on media numpads that + /// have buttons from `1` ... `12`. + Key12, + /// Adjust audio balance leftward. (`VK_AUDIO_BALANCE_LEFT`) + AudioBalanceLeft, + /// Adjust audio balance rightward. (`VK_AUDIO_BALANCE_RIGHT`) + AudioBalanceRight, + /// Decrease audio bass boost or cycle down through bass boost states. (`APPCOMMAND_BASS_DOWN`, `VK_BASS_BOOST_DOWN`) + AudioBassBoostDown, + /// Toggle bass boost on/off. (`APPCOMMAND_BASS_BOOST`) + AudioBassBoostToggle, + /// Increase audio bass boost or cycle up through bass boost states. (`APPCOMMAND_BASS_UP`, `VK_BASS_BOOST_UP`) + AudioBassBoostUp, + /// Adjust audio fader towards front. (`VK_FADER_FRONT`) + AudioFaderFront, + /// Adjust audio fader towards rear. (`VK_FADER_REAR`) + AudioFaderRear, + /// Advance surround audio mode to next available mode. (`VK_SURROUND_MODE_NEXT`) + AudioSurroundModeNext, + /// Decrease treble. (`APPCOMMAND_TREBLE_DOWN`) + AudioTrebleDown, + /// Increase treble. (`APPCOMMAND_TREBLE_UP`) + AudioTrebleUp, + /// Decrease audio volume. (`APPCOMMAND_VOLUME_DOWN`, `KEYCODE_VOLUME_DOWN`) + AudioVolumeDown, + /// Increase audio volume. (`APPCOMMAND_VOLUME_UP`, `KEYCODE_VOLUME_UP`) + AudioVolumeUp, + /// Toggle between muted state and prior volume level. (`APPCOMMAND_VOLUME_MUTE`, `KEYCODE_VOLUME_MUTE`) + AudioVolumeMute, + /// Toggle the microphone on/off. (`APPCOMMAND_MIC_ON_OFF_TOGGLE`) + MicrophoneToggle, + /// Decrease microphone volume. (`APPCOMMAND_MICROPHONE_VOLUME_DOWN`) + MicrophoneVolumeDown, + /// Increase microphone volume. (`APPCOMMAND_MICROPHONE_VOLUME_UP`) + MicrophoneVolumeUp, + /// Mute the microphone. (`APPCOMMAND_MICROPHONE_VOLUME_MUTE`, `KEYCODE_MUTE`) + MicrophoneVolumeMute, + /// Show correction list when a word is incorrectly identified. (`APPCOMMAND_CORRECTION_LIST`) + SpeechCorrectionList, + /// Toggle between dictation mode and command/control mode. (`APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE`) + SpeechInputToggle, + /// The first generic "LaunchApplication" key. This is commonly associated with launching "My Computer", and may have a computer symbol on the key. (`APPCOMMAND_LAUNCH_APP1`) + LaunchApplication1, + /// The second generic "LaunchApplication" key. This is commonly associated with launching "Calculator", and may have a calculator symbol on the key. (`APPCOMMAND_LAUNCH_APP2`, `KEYCODE_CALCULATOR`) + LaunchApplication2, + /// The "Calendar" key. (`KEYCODE_CALENDAR`) + LaunchCalendar, + /// The "Contacts" key. (`KEYCODE_CONTACTS`) + LaunchContacts, + /// The "Mail" key. (`APPCOMMAND_LAUNCH_MAIL`) + LaunchMail, + /// The "Media Player" key. (`APPCOMMAND_LAUNCH_MEDIA_SELECT`) + LaunchMediaPlayer, + /// The "Music Player" key. + LaunchMusicPlayer, + /// The "Phone" key. + LaunchPhone, + /// The "Screen Saver" key. + LaunchScreenSaver, + /// The "Spreadsheet" key. + LaunchSpreadsheet, + /// The "Web Browser" key. + LaunchWebBrowser, + /// The "WebCam" key. + LaunchWebCam, + /// The "Word Processor" key. + LaunchWordProcessor, + /// Navigate to previous content or page in current history. (`APPCOMMAND_BROWSER_BACKWARD`) + BrowserBack, + /// Open the list of browser favorites. (`APPCOMMAND_BROWSER_FAVORITES`) + BrowserFavorites, + /// Navigate to next content or page in current history. (`APPCOMMAND_BROWSER_FORWARD`) + BrowserForward, + /// Go to the user’s preferred home page. (`APPCOMMAND_BROWSER_HOME`) + BrowserHome, + /// Refresh the current page or content. (`APPCOMMAND_BROWSER_REFRESH`) + BrowserRefresh, + /// Call up the user’s preferred search page. (`APPCOMMAND_BROWSER_SEARCH`) + BrowserSearch, + /// Stop loading the current page or content. (`APPCOMMAND_BROWSER_STOP`) + BrowserStop, + /// The Application switch key, which provides a list of recent apps to switch between. (`KEYCODE_APP_SWITCH`) + AppSwitch, + /// The Call key. (`KEYCODE_CALL`) + Call, + /// The Camera key. (`KEYCODE_CAMERA`) + Camera, + /// The Camera focus key. (`KEYCODE_FOCUS`) + CameraFocus, + /// The End Call key. (`KEYCODE_ENDCALL`) + EndCall, + /// The Back key. (`KEYCODE_BACK`) + GoBack, + /// The Home key, which goes to the phone’s main screen. (`KEYCODE_HOME`) + GoHome, + /// The Headset Hook key. (`KEYCODE_HEADSETHOOK`) + HeadsetHook, + /// The Last Number Redial key. + LastNumberRedial, + /// The Notification key. (`KEYCODE_NOTIFICATION`) + Notification, + /// Toggle between manner mode state: silent, vibrate, ring, ... (`KEYCODE_MANNER_MODE`) + MannerMode, + /// The Voice Dial key. + VoiceDial, + /// Switch to viewing TV. (`KEYCODE_TV`) + TV, + /// TV 3D Mode. (`KEYCODE_3D_MODE`) + TV3DMode, + /// Toggle between antenna and cable input. (`KEYCODE_TV_ANTENNA_CABLE`) + TVAntennaCable, + /// Audio description. (`KEYCODE_TV_AUDIO_DESCRIPTION`) + TVAudioDescription, + /// Audio description mixing volume down. (`KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN`) + TVAudioDescriptionMixDown, + /// Audio description mixing volume up. (`KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP`) + TVAudioDescriptionMixUp, + /// Contents menu. (`KEYCODE_TV_CONTENTS_MENU`) + TVContentsMenu, + /// Contents menu. (`KEYCODE_TV_DATA_SERVICE`) + TVDataService, + /// Switch the input mode on an external TV. (`KEYCODE_TV_INPUT`) + TVInput, + /// Switch to component input #1. (`KEYCODE_TV_INPUT_COMPONENT_1`) + TVInputComponent1, + /// Switch to component input #2. (`KEYCODE_TV_INPUT_COMPONENT_2`) + TVInputComponent2, + /// Switch to composite input #1. (`KEYCODE_TV_INPUT_COMPOSITE_1`) + TVInputComposite1, + /// Switch to composite input #2. (`KEYCODE_TV_INPUT_COMPOSITE_2`) + TVInputComposite2, + /// Switch to HDMI input #1. (`KEYCODE_TV_INPUT_HDMI_1`) + TVInputHDMI1, + /// Switch to HDMI input #2. (`KEYCODE_TV_INPUT_HDMI_2`) + TVInputHDMI2, + /// Switch to HDMI input #3. (`KEYCODE_TV_INPUT_HDMI_3`) + TVInputHDMI3, + /// Switch to HDMI input #4. (`KEYCODE_TV_INPUT_HDMI_4`) + TVInputHDMI4, + /// Switch to VGA input #1. (`KEYCODE_TV_INPUT_VGA_1`) + TVInputVGA1, + /// Media context menu. (`KEYCODE_TV_MEDIA_CONTEXT_MENU`) + TVMediaContext, + /// Toggle network. (`KEYCODE_TV_NETWORK`) + TVNetwork, + /// Number entry. (`KEYCODE_TV_NUMBER_ENTRY`) + TVNumberEntry, + /// Toggle the power on an external TV. (`KEYCODE_TV_POWER`) + TVPower, + /// Radio. (`KEYCODE_TV_RADIO_SERVICE`) + TVRadioService, + /// Satellite. (`KEYCODE_TV_SATELLITE`) + TVSatellite, + /// Broadcast Satellite. (`KEYCODE_TV_SATELLITE_BS`) + TVSatelliteBS, + /// Communication Satellite. (`KEYCODE_TV_SATELLITE_CS`) + TVSatelliteCS, + /// Toggle between available satellites. (`KEYCODE_TV_SATELLITE_SERVICE`) + TVSatelliteToggle, + /// Analog Terrestrial. (`KEYCODE_TV_TERRESTRIAL_ANALOG`) + TVTerrestrialAnalog, + /// Digital Terrestrial. (`KEYCODE_TV_TERRESTRIAL_DIGITAL`) + TVTerrestrialDigital, + /// Timer programming. (`KEYCODE_TV_TIMER_PROGRAMMING`) + TVTimer, + /// Switch the input mode on an external AVR (audio/video receiver). (`KEYCODE_AVR_INPUT`) + AVRInput, + /// Toggle the power on an external AVR (audio/video receiver). (`KEYCODE_AVR_POWER`) + AVRPower, + /// General purpose color-coded media function key, as index 0 (red). (`VK_COLORED_KEY_0`, `KEYCODE_PROG_RED`) + ColorF0Red, + /// General purpose color-coded media function key, as index 1 (green). (`VK_COLORED_KEY_1`, `KEYCODE_PROG_GREEN`) + ColorF1Green, + /// General purpose color-coded media function key, as index 2 (yellow). (`VK_COLORED_KEY_2`, `KEYCODE_PROG_YELLOW`) + ColorF2Yellow, + /// General purpose color-coded media function key, as index 3 (blue). (`VK_COLORED_KEY_3`, `KEYCODE_PROG_BLUE`) + ColorF3Blue, + /// General purpose color-coded media function key, as index 4 (grey). (`VK_COLORED_KEY_4`) + ColorF4Grey, + /// General purpose color-coded media function key, as index 5 (brown). (`VK_COLORED_KEY_5`) + ColorF5Brown, + /// Toggle the display of Closed Captions. (`VK_CC`, `KEYCODE_CAPTIONS`) + ClosedCaptionToggle, + /// Adjust brightness of device, by toggling between or cycling through states. (`VK_DIMMER`) + Dimmer, + /// Swap video sources. (`VK_DISPLAY_SWAP`) + DisplaySwap, + /// Select Digital Video Rrecorder. (`KEYCODE_DVR`) + DVR, + /// Exit the current application. (`VK_EXIT`) + Exit, + /// Clear program or content stored as favorite 0. (`VK_CLEAR_FAVORITE_0`) + FavoriteClear0, + /// Clear program or content stored as favorite 1. (`VK_CLEAR_FAVORITE_1`) + FavoriteClear1, + /// Clear program or content stored as favorite 2. (`VK_CLEAR_FAVORITE_2`) + FavoriteClear2, + /// Clear program or content stored as favorite 3. (`VK_CLEAR_FAVORITE_3`) + FavoriteClear3, + /// Select (recall) program or content stored as favorite 0. (`VK_RECALL_FAVORITE_0`) + FavoriteRecall0, + /// Select (recall) program or content stored as favorite 1. (`VK_RECALL_FAVORITE_1`) + FavoriteRecall1, + /// Select (recall) program or content stored as favorite 2. (`VK_RECALL_FAVORITE_2`) + FavoriteRecall2, + /// Select (recall) program or content stored as favorite 3. (`VK_RECALL_FAVORITE_3`) + FavoriteRecall3, + /// Store current program or content as favorite 0. (`VK_STORE_FAVORITE_0`) + FavoriteStore0, + /// Store current program or content as favorite 1. (`VK_STORE_FAVORITE_1`) + FavoriteStore1, + /// Store current program or content as favorite 2. (`VK_STORE_FAVORITE_2`) + FavoriteStore2, + /// Store current program or content as favorite 3. (`VK_STORE_FAVORITE_3`) + FavoriteStore3, + /// Toggle display of program or content guide. (`VK_GUIDE`, `KEYCODE_GUIDE`) + Guide, + /// If guide is active and displayed, then display next day’s content. (`VK_NEXT_DAY`) + GuideNextDay, + /// If guide is active and displayed, then display previous day’s content. (`VK_PREV_DAY`) + GuidePreviousDay, + /// Toggle display of information about currently selected context or media. (`VK_INFO`, `KEYCODE_INFO`) + Info, + /// Toggle instant replay. (`VK_INSTANT_REPLAY`) + InstantReplay, + /// Launch linked content, if available and appropriate. (`VK_LINK`) + Link, + /// List the current program. (`VK_LIST`) + ListProgram, + /// Toggle display listing of currently available live content or programs. (`VK_LIVE`) + LiveContent, + /// Lock or unlock current content or program. (`VK_LOCK`) + Lock, + /// Show a list of media applications: audio/video players and image viewers. (`VK_APPS`) + ///

Do not confuse this key value with the Windows' `VK_APPS` / `VK_CONTEXT_MENU` key, which is encoded as `"ContextMenu"`.

+ MediaApps, + /// Audio track key. (`KEYCODE_MEDIA_AUDIO_TRACK`) + MediaAudioTrack, + /// Select previously selected channel or media. (`VK_LAST`, `KEYCODE_LAST_CHANNEL`) + MediaLast, + /// Skip backward to next content or program. (`KEYCODE_MEDIA_SKIP_BACKWARD`) + MediaSkipBackward, + /// Skip forward to next content or program. (`VK_SKIP`, `KEYCODE_MEDIA_SKIP_FORWARD`) + MediaSkipForward, + /// Step backward to next content or program. (`KEYCODE_MEDIA_STEP_BACKWARD`) + MediaStepBackward, + /// Step forward to next content or program. (`KEYCODE_MEDIA_STEP_FORWARD`) + MediaStepForward, + /// Media top menu. (`KEYCODE_MEDIA_TOP_MENU`) + MediaTopMenu, + /// Navigate in. (`KEYCODE_NAVIGATE_IN`) + NavigateIn, + /// Navigate to next key. (`KEYCODE_NAVIGATE_NEXT`) + NavigateNext, + /// Navigate out. (`KEYCODE_NAVIGATE_OUT`) + NavigateOut, + /// Navigate to previous key. (`KEYCODE_NAVIGATE_PREVIOUS`) + NavigatePrevious, + /// Cycle to next favorite channel (in favorites list). (`VK_NEXT_FAVORITE_CHANNEL`) + NextFavoriteChannel, + /// Cycle to next user profile (if there are multiple user profiles). (`VK_USER`) + NextUserProfile, + /// Access on-demand content or programs. (`VK_ON_DEMAND`) + OnDemand, + /// Pairing key to pair devices. (`KEYCODE_PAIRING`) + Pairing, + /// Move picture-in-picture window down. (`VK_PINP_DOWN`) + PinPDown, + /// Move picture-in-picture window. (`VK_PINP_MOVE`) + PinPMove, + /// Toggle display of picture-in-picture window. (`VK_PINP_TOGGLE`) + PinPToggle, + /// Move picture-in-picture window up. (`VK_PINP_UP`) + PinPUp, + /// Decrease media playback speed. (`VK_PLAY_SPEED_DOWN`) + PlaySpeedDown, + /// Reset playback to normal speed. (`VK_PLAY_SPEED_RESET`) + PlaySpeedReset, + /// Increase media playback speed. (`VK_PLAY_SPEED_UP`) + PlaySpeedUp, + /// Toggle random media or content shuffle mode. (`VK_RANDOM_TOGGLE`) + RandomToggle, + /// Not a physical key, but this key code is sent when the remote control battery is low. (`VK_RC_LOW_BATTERY`) + RcLowBattery, + /// Toggle or cycle between media recording speeds. (`VK_RECORD_SPEED_NEXT`) + RecordSpeedNext, + /// Toggle RF (radio frequency) input bypass mode (pass RF input directly to the RF output). (`VK_RF_BYPASS`) + RfBypass, + /// Toggle scan channels mode. (`VK_SCAN_CHANNELS_TOGGLE`) + ScanChannelsToggle, + /// Advance display screen mode to next available mode. (`VK_SCREEN_MODE_NEXT`) + ScreenModeNext, + /// Toggle display of device settings screen. (`VK_SETTINGS`, `KEYCODE_SETTINGS`) + Settings, + /// Toggle split screen mode. (`VK_SPLIT_SCREEN_TOGGLE`) + SplitScreenToggle, + /// Switch the input mode on an external STB (set top box). (`KEYCODE_STB_INPUT`) + STBInput, + /// Toggle the power on an external STB (set top box). (`KEYCODE_STB_POWER`) + STBPower, + /// Toggle display of subtitles, if available. (`VK_SUBTITLE`) + Subtitle, + /// Toggle display of teletext, if available (`VK_TELETEXT`, `KEYCODE_TV_TELETEXT`). + Teletext, + /// Advance video mode to next available mode. (`VK_VIDEO_MODE_NEXT`) + VideoModeNext, + /// Cause device to identify itself in some manner, e.g., audibly or visibly. (`VK_WINK`) + Wink, + /// Toggle between full-screen and scaled content, or alter magnification level. (`VK_ZOOM`, `KEYCODE_TV_ZOOM_MODE`) + ZoomToggle, + /// The F1 key, a general purpose function key, as index 1. + F1, + /// The F2 key, a general purpose function key, as index 2. + F2, + /// The F3 key, a general purpose function key, as index 3. + F3, + /// The F4 key, a general purpose function key, as index 4. + F4, + /// The F5 key, a general purpose function key, as index 5. + F5, + /// The F6 key, a general purpose function key, as index 6. + F6, + /// The F7 key, a general purpose function key, as index 7. + F7, + /// The F8 key, a general purpose function key, as index 8. + F8, + /// The F9 key, a general purpose function key, as index 9. + F9, + /// The F10 key, a general purpose function key, as index 10. + F10, + /// The F11 key, a general purpose function key, as index 11. + F11, + /// The F12 key, a general purpose function key, as index 12. + F12, + /// The F13 key, a general purpose function key, as index 13. + F13, + /// The F14 key, a general purpose function key, as index 14. + F14, + /// The F15 key, a general purpose function key, as index 15. + F15, + /// The F16 key, a general purpose function key, as index 16. + F16, + /// The F17 key, a general purpose function key, as index 17. + F17, + /// The F18 key, a general purpose function key, as index 18. + F18, + /// The F19 key, a general purpose function key, as index 19. + F19, + /// The F20 key, a general purpose function key, as index 20. + F20, + /// The F21 key, a general purpose function key, as index 21. + F21, + /// The F22 key, a general purpose function key, as index 22. + F22, + /// The F23 key, a general purpose function key, as index 23. + F23, + /// The F24 key, a general purpose function key, as index 24. + F24, + /// The F25 key, a general purpose function key, as index 25. + F25, + /// The F26 key, a general purpose function key, as index 26. + F26, + /// The F27 key, a general purpose function key, as index 27. + F27, + /// The F28 key, a general purpose function key, as index 28. + F28, + /// The F29 key, a general purpose function key, as index 29. + F29, + /// The F30 key, a general purpose function key, as index 30. + F30, + /// The F31 key, a general purpose function key, as index 31. + F31, + /// The F32 key, a general purpose function key, as index 32. + F32, + /// The F33 key, a general purpose function key, as index 33. + F33, + /// The F34 key, a general purpose function key, as index 34. + F34, + /// The F35 key, a general purpose function key, as index 35. + F35, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum KeyLocation { + Standard, + Left, + Right, + Numpad, +} diff --git a/src/lib.rs b/src/lib.rs index 51f4a8634a..06556e88f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -155,6 +155,7 @@ pub mod error; pub mod event; pub mod event_loop; mod icon; +pub mod keyboard; pub mod monitor; mod platform_impl; pub mod window; diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index a6a4dc83c6..4ee0dcdaaa 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -3,8 +3,6 @@ use std::{ os::windows::ffi::OsStringExt, }; -use keyboard_types::Key; - use winapi::{ shared::{ minwindef::{HKL, LOWORD, LPARAM, LRESULT, UINT, WPARAM}, @@ -17,7 +15,8 @@ use winapi::{ }; use crate::{ - event::{KeyEvent, ScanCode}, + event::{KeyEvent, ElementState}, + keyboard::{Key, KeyCode, NativeKeyCode, KeyLocation}, platform_impl::platform::event::KeyEventExtra, }; @@ -57,20 +56,10 @@ impl ToUnicodeResult { } } -#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] -pub struct PlatformScanCode(pub u16); -impl fmt::Debug for PlatformScanCode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("PlatformScanCode") - .field(&format_args!("0x{:04x}", self.0)) - .finish() - } -} -impl PlatformScanCode { - pub fn new(scancode: u8, extended: bool) -> PlatformScanCode { - let ex_scancode = (scancode as u16) | (if extended { 0xE000 } else { 0 }); - PlatformScanCode(ex_scancode) - } +type ExScancode = u16; + +fn new_ex_scancode(scancode: u8, extended: bool) -> ExScancode { + (scancode as u16) | (if extended { 0xE000 } else { 0 }) } pub struct MessageAsKeyEvent { @@ -107,10 +96,10 @@ pub struct KeyEventBuilder { /// just when the key is pressed/released would be enough if `ToUnicode` wouldn't /// change the keyboard state (it clears the dead key). There is a flag to prevent /// changing the state but that flag requires Windows 10, version 1607 or newer) - key_text: HashMap, + key_text: HashMap, /// Same as `key_text` but as if caps-lock was pressed. - key_text_with_caps: HashMap, + key_text_with_caps: HashMap, /// True if the keyboard layout belonging to `known_locale_id` has an AltGr key. has_alt_graph: bool, @@ -166,14 +155,14 @@ impl KeyEventBuilder { match msg_kind { winuser::WM_SETFOCUS => { // synthesize keydown events - let key_events = self.synthesize_kbd_state(keyboard_types::KeyState::Down); + let key_events = self.synthesize_kbd_state(ElementState::Pressed); if !key_events.is_empty() { return key_events; } } winuser::WM_KILLFOCUS => { // sythesize keyup events - let key_events = self.synthesize_kbd_state(keyboard_types::KeyState::Up); + let key_events = self.synthesize_kbd_state(ElementState::Released); if !key_events.is_empty() { return key_events; } @@ -196,7 +185,7 @@ impl KeyEventBuilder { let lparam_struct = destructure_key_lparam(lparam); let scancode = - PlatformScanCode::new(lparam_struct.scancode, lparam_struct.extended); + new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); let code = native_key_to_code(scancode); let vkey = unsafe { winuser::MapVirtualKeyW(scancode.0 as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 @@ -204,7 +193,7 @@ impl KeyEventBuilder { let location = get_location(vkey, lparam_struct.extended); let label = self.key_text.get(&scancode).map(|s| s.clone()); let mut event_info = Some(PartialKeyEventInfo { - key_state: keyboard_types::KeyState::Down, + key_state: ElementState::Pressed, vkey, scancode, is_repeat: lparam_struct.is_repeat, @@ -360,7 +349,7 @@ impl KeyEventBuilder { // // This logic relies on the assuption that keys which don't consume // dead keys, also do not produce text input. - if !self.prev_down_was_dead && does_vkey_consume_dead_key(wparam as u32) { + if !self.prev_down_was_dead && vkey_consumes_dead_key(wparam as u32) { unsafe { //let locale_id = winuser::GetKeyboardLayout(0); let mut key_state: [MaybeUninit; 256] = [MaybeUninit::uninit(); 256]; @@ -386,7 +375,7 @@ impl KeyEventBuilder { } let label = self.key_text.get(&scancode).map(|s| s.clone()); let event_info = PartialKeyEventInfo { - key_state: keyboard_types::KeyState::Up, + key_state: ElementState::Released, vkey, scancode, is_repeat: false, @@ -539,7 +528,7 @@ impl KeyEventBuilder { fn synthesize_kbd_state( &mut self, - key_state: keyboard_types::KeyState, + key_state: ElementState, ) -> Vec { let mut key_events = Vec::new(); let locale_id = unsafe { winuser::GetKeyboardLayout(0) }; @@ -620,15 +609,15 @@ impl KeyEventBuilder { } }; - // Be cheeky and sythesize sequence modifier and non-modifier + // Be cheeky and sequence modifier and non-modifier // key events such that non-modifier keys are not affected // by modifiers (except for caps-lock) match key_state { - keyboard_types::KeyState::Down => { + ElementState::Pressed => { do_non_modifier(&mut key_events); do_modifier(&mut key_events); } - keyboard_types::KeyState::Up => { + ElementState::Released => { do_modifier(&mut key_events); do_non_modifier(&mut key_events); } @@ -640,7 +629,7 @@ impl KeyEventBuilder { fn create_synthetic( &self, vk: i32, - key_state: keyboard_types::KeyState, + key_state: ElementState, locale_id: HKL, caps_lock_on: bool, ) -> Option { @@ -650,17 +639,17 @@ impl KeyEventBuilder { if scancode == 0 { return None; } + let scancode = scancode as ExScancode; let is_extended = (scancode & 0xE000) == 0xE000; - let platform_scancode = PlatformScanCode(scancode as u16); - let code = native_key_to_code(platform_scancode); - let key_text = self.key_text.get(&platform_scancode).cloned(); - let key_text_with_caps = self.key_text_with_caps.get(&platform_scancode).cloned(); + let code = native_key_to_code(scancode); + let key_text = self.key_text.get(&scancode).cloned(); + let key_text_with_caps = self.key_text_with_caps.get(&scancode).cloned(); let logical_key = match &key_text { Some(str) => { if caps_lock_on { match key_text_with_caps.clone() { Some(str) => keyboard_types::Key::Character(str), - None => keyboard_types::Key::Unidentified, + None => keyboard_types::Key::Unidentified(native_code), } } else { keyboard_types::Key::Character(str.clone()) @@ -693,13 +682,13 @@ impl KeyEventBuilder { } struct PartialKeyEventInfo { - key_state: keyboard_types::KeyState, + key_state: ElementState, /// The native Virtual Key vkey: i32, scancode: PlatformScanCode, is_repeat: bool, - code: keyboard_types::Code, - location: keyboard_types::Location, + code: KeyCode, + location: KeyLocation, /// True if the key event corresponds to a dead key input is_dead: bool, label: Option, @@ -715,11 +704,14 @@ impl PartialKeyEventInfo { fn finalize(self, locale_id: usize, has_alt_gr: bool) -> KeyEvent { let logical_key; if self.is_dead { - logical_key = Key::Dead; + // TODO: dispatch the dead-key char here + logical_key = Key::Dead(None); } else { if !self.utf16parts_without_ctrl.is_empty() { - logical_key = - Key::Character(String::from_utf16(&self.utf16parts_without_ctrl).unwrap()); + let string = String::from_utf16(&self.utf16parts_without_ctrl).unwrap(); + // TODO: cache these in a global map + let leaked = Box::leak(Box::::from(string)); + logical_key = Key::Character(leaked); } else { logical_key = vkey_to_non_printable(self.vkey, self.code, locale_id, has_alt_gr); } @@ -737,7 +729,6 @@ impl PartialKeyEventInfo { } KeyEvent { - scancode: ScanCode(self.scancode), physical_key: self.code, logical_key, location: self.location, @@ -761,8 +752,7 @@ pub fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { } } -pub fn get_location(vkey: c_int, extended: bool) -> keyboard_types::Location { - use keyboard_types::Location; +pub fn get_location(vkey: c_int, extended: bool) -> KeyLocation { use winuser::*; const VK_ABNT_C2: c_int = 0xc2; @@ -770,27 +760,27 @@ pub fn get_location(vkey: c_int, extended: bool) -> keyboard_types::Location { // This is taken from the `druid` software within // druid-shell/src/platform/windows/keyboard.rs match vkey { - VK_LSHIFT | VK_LCONTROL | VK_LMENU | VK_LWIN => Location::Left, - VK_RSHIFT | VK_RCONTROL | VK_RMENU | VK_RWIN => Location::Right, - VK_RETURN if extended => Location::Numpad, + VK_LSHIFT | VK_LCONTROL | VK_LMENU | VK_LWIN => KeyLocation::Left, + VK_RSHIFT | VK_RCONTROL | VK_RMENU | VK_RWIN => KeyLocation::Right, + VK_RETURN if extended => KeyLocation::Numpad, VK_INSERT | VK_DELETE | VK_END | VK_DOWN | VK_NEXT | VK_LEFT | VK_CLEAR | VK_RIGHT | VK_HOME | VK_UP | VK_PRIOR => { if extended { - Location::Standard + KeyLocation::Standard } else { - Location::Numpad + KeyLocation::Numpad } } VK_NUMPAD0 | VK_NUMPAD1 | VK_NUMPAD2 | VK_NUMPAD3 | VK_NUMPAD4 | VK_NUMPAD5 | VK_NUMPAD6 | VK_NUMPAD7 | VK_NUMPAD8 | VK_NUMPAD9 | VK_DECIMAL | VK_DIVIDE - | VK_MULTIPLY | VK_SUBTRACT | VK_ADD | VK_ABNT_C2 => Location::Numpad, - _ => Location::Standard, + | VK_MULTIPLY | VK_SUBTRACT | VK_ADD | VK_ABNT_C2 => KeyLocation::Numpad, + _ => KeyLocation::Standard, } } unsafe fn get_utf16_without_ctrl( vkey: u32, - scancode: PlatformScanCode, + scancode: ExScancode, key_state: &mut [u8; 256], utf16parts_without_ctrl: &mut Vec, ) { @@ -805,7 +795,7 @@ unsafe fn get_utf16_without_ctrl( key_state[winuser::VK_RCONTROL as usize] = 0; let unicode_len = winuser::ToUnicode( vkey, - scancode.0 as u32, + scancode as u32, (&mut key_state[0]) as *mut _, utf16parts_without_ctrl.as_mut_ptr(), utf16parts_without_ctrl.capacity() as i32, @@ -819,7 +809,7 @@ unsafe fn get_utf16_without_ctrl( } // TODO: This list might not be complete -fn does_vkey_consume_dead_key(vkey: u32) -> bool { +fn vkey_consumes_dead_key(vkey: u32) -> bool { const A: u32 = 'A' as u32; const Z: u32 = 'Z' as u32; const ZERO: u32 = '0' as u32; @@ -861,19 +851,19 @@ fn does_vkey_consume_dead_key(vkey: u32) -> bool { /// backspace and tab are included. fn vkey_to_non_printable( vkey: i32, - code: keyboard_types::Code, + native_code: NativeKeyCode, + code: KeyCode, hkl: usize, has_alt_graph: bool, -) -> Key { - use keyboard_types::Code; +) -> Key<'static> { // List of the Web key names and their corresponding platform-native key names: // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values // Some keys cannot be correctly determined based on the virtual key. // Therefore we use the `code` to translate those keys. match code { - Code::NumLock => return Key::NumLock, - Code::Pause => return Key::Pause, + KeyCode::NumLock => return Key::NumLock, + KeyCode::Pause => return Key::Pause, _ => (), } @@ -882,12 +872,15 @@ fn vkey_to_non_printable( let is_japanese = primary_lang_id == LANG_JAPANESE; match vkey { - winuser::VK_LBUTTON => Key::Unidentified, // Mouse - winuser::VK_RBUTTON => Key::Unidentified, // Mouse - winuser::VK_CANCEL => Key::Unidentified, // I don't think this can be represented with a Key - winuser::VK_MBUTTON => Key::Unidentified, // Mouse - winuser::VK_XBUTTON1 => Key::Unidentified, // Mouse - winuser::VK_XBUTTON2 => Key::Unidentified, // Mouse + winuser::VK_LBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + winuser::VK_RBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + + // I don't think this can be represented with a Key + winuser::VK_CANCEL => Key::Unidentified(native_code), + + winuser::VK_MBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + winuser::VK_XBUTTON1 => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + winuser::VK_XBUTTON2 => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse winuser::VK_BACK => Key::Backspace, winuser::VK_TAB => Key::Tab, winuser::VK_CLEAR => Key::Clear, @@ -918,7 +911,7 @@ fn vkey_to_non_printable( winuser::VK_NONCONVERT => Key::NonConvert, winuser::VK_ACCEPT => Key::Accept, winuser::VK_MODECHANGE => Key::ModeChange, - winuser::VK_SPACE => Key::Unidentified, // This function only converts "non-printable" + winuser::VK_SPACE => Key::Unidentified(native_code), // This function only converts "non-printable" winuser::VK_PRIOR => Key::PageUp, winuser::VK_NEXT => Key::PageDown, winuser::VK_END => Key::End, @@ -940,22 +933,22 @@ fn vkey_to_non_printable( winuser::VK_SLEEP => Key::Standby, // This function only converts "non-printable" - winuser::VK_NUMPAD0 => Key::Unidentified, - winuser::VK_NUMPAD1 => Key::Unidentified, - winuser::VK_NUMPAD2 => Key::Unidentified, - winuser::VK_NUMPAD3 => Key::Unidentified, - winuser::VK_NUMPAD4 => Key::Unidentified, - winuser::VK_NUMPAD5 => Key::Unidentified, - winuser::VK_NUMPAD6 => Key::Unidentified, - winuser::VK_NUMPAD7 => Key::Unidentified, - winuser::VK_NUMPAD8 => Key::Unidentified, - winuser::VK_NUMPAD9 => Key::Unidentified, - winuser::VK_MULTIPLY => Key::Unidentified, - winuser::VK_ADD => Key::Unidentified, - winuser::VK_SEPARATOR => Key::Unidentified, - winuser::VK_SUBTRACT => Key::Unidentified, - winuser::VK_DECIMAL => Key::Unidentified, - winuser::VK_DIVIDE => Key::Unidentified, + winuser::VK_NUMPAD0 => Key::Unidentified(native_code), + winuser::VK_NUMPAD1 => Key::Unidentified(native_code), + winuser::VK_NUMPAD2 => Key::Unidentified(native_code), + winuser::VK_NUMPAD3 => Key::Unidentified(native_code), + winuser::VK_NUMPAD4 => Key::Unidentified(native_code), + winuser::VK_NUMPAD5 => Key::Unidentified(native_code), + winuser::VK_NUMPAD6 => Key::Unidentified(native_code), + winuser::VK_NUMPAD7 => Key::Unidentified(native_code), + winuser::VK_NUMPAD8 => Key::Unidentified(native_code), + winuser::VK_NUMPAD9 => Key::Unidentified(native_code), + winuser::VK_MULTIPLY => Key::Unidentified(native_code), + winuser::VK_ADD => Key::Unidentified(native_code), + winuser::VK_SEPARATOR => Key::Unidentified(native_code), + winuser::VK_SUBTRACT => Key::Unidentified(native_code), + winuser::VK_DECIMAL => Key::Unidentified(native_code), + winuser::VK_DIVIDE => Key::Unidentified(native_code), winuser::VK_F1 => Key::F1, winuser::VK_F2 => Key::F2, @@ -969,36 +962,34 @@ fn vkey_to_non_printable( winuser::VK_F10 => Key::F10, winuser::VK_F11 => Key::F11, winuser::VK_F12 => Key::F12, - - // TODO: Uncomment when these are added to `keyboard_types` - // winuser::VK_F13 => Key::F13, - // winuser::VK_F14 => Key::F14, - // winuser::VK_F15 => Key::F15, - // winuser::VK_F16 => Key::F16, - // winuser::VK_F17 => Key::F17, - // winuser::VK_F18 => Key::F18, - // winuser::VK_F19 => Key::F19, - // winuser::VK_F20 => Key::F20, - // winuser::VK_F21 => Key::F21, - // winuser::VK_F22 => Key::F22, - // winuser::VK_F23 => Key::F23, - // winuser::VK_F24 => Key::F24, - winuser::VK_NAVIGATION_VIEW => Key::Unidentified, - winuser::VK_NAVIGATION_MENU => Key::Unidentified, - winuser::VK_NAVIGATION_UP => Key::Unidentified, - winuser::VK_NAVIGATION_DOWN => Key::Unidentified, - winuser::VK_NAVIGATION_LEFT => Key::Unidentified, - winuser::VK_NAVIGATION_RIGHT => Key::Unidentified, - winuser::VK_NAVIGATION_ACCEPT => Key::Unidentified, - winuser::VK_NAVIGATION_CANCEL => Key::Unidentified, + winuser::VK_F13 => Key::F13, + winuser::VK_F14 => Key::F14, + winuser::VK_F15 => Key::F15, + winuser::VK_F16 => Key::F16, + winuser::VK_F17 => Key::F17, + winuser::VK_F18 => Key::F18, + winuser::VK_F19 => Key::F19, + winuser::VK_F20 => Key::F20, + winuser::VK_F21 => Key::F21, + winuser::VK_F22 => Key::F22, + winuser::VK_F23 => Key::F23, + winuser::VK_F24 => Key::F24, + winuser::VK_NAVIGATION_VIEW => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_MENU => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_UP => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_DOWN => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_LEFT => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_RIGHT => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_ACCEPT => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_CANCEL => Key::Unidentified(native_code), winuser::VK_NUMLOCK => Key::NumLock, winuser::VK_SCROLL => Key::ScrollLock, - winuser::VK_OEM_NEC_EQUAL => Key::Unidentified, - //winuser::VK_OEM_FJ_JISHO => Key::Unidentified, // Conflicts with `VK_OEM_NEC_EQUAL` - winuser::VK_OEM_FJ_MASSHOU => Key::Unidentified, - winuser::VK_OEM_FJ_TOUROKU => Key::Unidentified, - winuser::VK_OEM_FJ_LOYA => Key::Unidentified, - winuser::VK_OEM_FJ_ROYA => Key::Unidentified, + winuser::VK_OEM_NEC_EQUAL => Key::Unidentified(native_code), + //winuser::VK_OEM_FJ_JISHO => Key::Unidentified(native_code), // Conflicts with `VK_OEM_NEC_EQUAL` + winuser::VK_OEM_FJ_MASSHOU => Key::Unidentified(native_code), + winuser::VK_OEM_FJ_TOUROKU => Key::Unidentified(native_code), + winuser::VK_OEM_FJ_LOYA => Key::Unidentified(native_code), + winuser::VK_OEM_FJ_ROYA => Key::Unidentified(native_code), winuser::VK_LSHIFT => Key::Shift, winuser::VK_RSHIFT => Key::Shift, winuser::VK_LCONTROL => Key::Control, @@ -1031,62 +1022,62 @@ fn vkey_to_non_printable( winuser::VK_LAUNCH_APP2 => Key::LaunchApplication2, // This function only converts "non-printable" - winuser::VK_OEM_1 => Key::Unidentified, - winuser::VK_OEM_PLUS => Key::Unidentified, - winuser::VK_OEM_COMMA => Key::Unidentified, - winuser::VK_OEM_MINUS => Key::Unidentified, - winuser::VK_OEM_PERIOD => Key::Unidentified, - winuser::VK_OEM_2 => Key::Unidentified, - winuser::VK_OEM_3 => Key::Unidentified, - - winuser::VK_GAMEPAD_A => Key::Unidentified, - winuser::VK_GAMEPAD_B => Key::Unidentified, - winuser::VK_GAMEPAD_X => Key::Unidentified, - winuser::VK_GAMEPAD_Y => Key::Unidentified, - winuser::VK_GAMEPAD_RIGHT_SHOULDER => Key::Unidentified, - winuser::VK_GAMEPAD_LEFT_SHOULDER => Key::Unidentified, - winuser::VK_GAMEPAD_LEFT_TRIGGER => Key::Unidentified, - winuser::VK_GAMEPAD_RIGHT_TRIGGER => Key::Unidentified, - winuser::VK_GAMEPAD_DPAD_UP => Key::Unidentified, - winuser::VK_GAMEPAD_DPAD_DOWN => Key::Unidentified, - winuser::VK_GAMEPAD_DPAD_LEFT => Key::Unidentified, - winuser::VK_GAMEPAD_DPAD_RIGHT => Key::Unidentified, - winuser::VK_GAMEPAD_MENU => Key::Unidentified, - winuser::VK_GAMEPAD_VIEW => Key::Unidentified, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON => Key::Unidentified, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON => Key::Unidentified, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_UP => Key::Unidentified, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_DOWN => Key::Unidentified, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT => Key::Unidentified, - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_LEFT => Key::Unidentified, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_UP => Key::Unidentified, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN => Key::Unidentified, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT => Key::Unidentified, - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT => Key::Unidentified, + winuser::VK_OEM_1 => Key::Unidentified(native_code), + winuser::VK_OEM_PLUS => Key::Unidentified(native_code), + winuser::VK_OEM_COMMA => Key::Unidentified(native_code), + winuser::VK_OEM_MINUS => Key::Unidentified(native_code), + winuser::VK_OEM_PERIOD => Key::Unidentified(native_code), + winuser::VK_OEM_2 => Key::Unidentified(native_code), + winuser::VK_OEM_3 => Key::Unidentified(native_code), + + winuser::VK_GAMEPAD_A => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_B => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_X => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_Y => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_SHOULDER => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_SHOULDER => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_TRIGGER => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_TRIGGER => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_DPAD_UP => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_DPAD_DOWN => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_DPAD_LEFT => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_DPAD_RIGHT => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_MENU => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_VIEW => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_UP => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_DOWN => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_LEFT => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_UP => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT => Key::Unidentified(native_code), // This function only converts "non-printable" - winuser::VK_OEM_4 => Key::Unidentified, - winuser::VK_OEM_5 => Key::Unidentified, - winuser::VK_OEM_6 => Key::Unidentified, - winuser::VK_OEM_7 => Key::Unidentified, - winuser::VK_OEM_8 => Key::Unidentified, - winuser::VK_OEM_AX => Key::Unidentified, - winuser::VK_OEM_102 => Key::Unidentified, + winuser::VK_OEM_4 => Key::Unidentified(native_code), + winuser::VK_OEM_5 => Key::Unidentified(native_code), + winuser::VK_OEM_6 => Key::Unidentified(native_code), + winuser::VK_OEM_7 => Key::Unidentified(native_code), + winuser::VK_OEM_8 => Key::Unidentified(native_code), + winuser::VK_OEM_AX => Key::Unidentified(native_code), + winuser::VK_OEM_102 => Key::Unidentified(native_code), - winuser::VK_ICO_HELP => Key::Unidentified, - winuser::VK_ICO_00 => Key::Unidentified, + winuser::VK_ICO_HELP => Key::Unidentified(native_code), + winuser::VK_ICO_00 => Key::Unidentified(native_code), winuser::VK_PROCESSKEY => Key::Process, - winuser::VK_ICO_CLEAR => Key::Unidentified, - winuser::VK_PACKET => Key::Unidentified, - winuser::VK_OEM_RESET => Key::Unidentified, - winuser::VK_OEM_JUMP => Key::Unidentified, - winuser::VK_OEM_PA1 => Key::Unidentified, - winuser::VK_OEM_PA2 => Key::Unidentified, - winuser::VK_OEM_PA3 => Key::Unidentified, - winuser::VK_OEM_WSCTRL => Key::Unidentified, - winuser::VK_OEM_CUSEL => Key::Unidentified, + winuser::VK_ICO_CLEAR => Key::Unidentified(native_code), + winuser::VK_PACKET => Key::Unidentified(native_code), + winuser::VK_OEM_RESET => Key::Unidentified(native_code), + winuser::VK_OEM_JUMP => Key::Unidentified(native_code), + winuser::VK_OEM_PA1 => Key::Unidentified(native_code), + winuser::VK_OEM_PA2 => Key::Unidentified(native_code), + winuser::VK_OEM_PA3 => Key::Unidentified(native_code), + winuser::VK_OEM_WSCTRL => Key::Unidentified(native_code), + winuser::VK_OEM_CUSEL => Key::Unidentified(native_code), winuser::VK_OEM_ATTN => Key::Attn, winuser::VK_OEM_FINISH => { @@ -1098,7 +1089,7 @@ fn vkey_to_non_printable( // At the time of writing there is no `Key::Finish` variant as // Finish is not mentionned at https://w3c.github.io/uievents-key/ // Also see: https://github.com/pyfisch/keyboard-types/issues/9 - Key::Unidentified + Key::Unidentified(native_code) } } winuser::VK_OEM_COPY => Key::Copy, @@ -1111,171 +1102,168 @@ fn vkey_to_non_printable( winuser::VK_EREOF => Key::EraseEof, winuser::VK_PLAY => Key::Play, winuser::VK_ZOOM => Key::ZoomToggle, - winuser::VK_NONAME => Key::Unidentified, - winuser::VK_PA1 => Key::Unidentified, + winuser::VK_NONAME => Key::Unidentified(native_code), + winuser::VK_PA1 => Key::Unidentified(native_code), winuser::VK_OEM_CLEAR => Key::Clear, - _ => Key::Unidentified, + _ => Key::Unidentified(native_code), } } -pub fn native_key_to_code(scancode: PlatformScanCode) -> keyboard_types::Code { +pub fn native_key_to_code(scancode: ExScancode) -> KeyCode { // See: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html // and: https://www.w3.org/TR/uievents-code/ // and: The widget/NativeKeyToDOMCodeName.h file in the firefox source - use keyboard_types::Code; - - match scancode.0 { - 0x0029 => Code::Backquote, - 0x002B => Code::Backslash, - 0x000E => Code::Backspace, - 0x001A => Code::BracketLeft, - 0x001B => Code::BracketRight, - 0x0033 => Code::Comma, - 0x000B => Code::Digit0, - 0x0002 => Code::Digit1, - 0x0003 => Code::Digit2, - 0x0004 => Code::Digit3, - 0x0005 => Code::Digit4, - 0x0006 => Code::Digit5, - 0x0007 => Code::Digit6, - 0x0008 => Code::Digit7, - 0x0009 => Code::Digit8, - 0x000A => Code::Digit9, - 0x000D => Code::Equal, - 0x0056 => Code::IntlBackslash, - 0x0073 => Code::IntlRo, - 0x007D => Code::IntlYen, - 0x001E => Code::KeyA, - 0x0030 => Code::KeyB, - 0x002E => Code::KeyC, - 0x0020 => Code::KeyD, - 0x0012 => Code::KeyE, - 0x0021 => Code::KeyF, - 0x0022 => Code::KeyG, - 0x0023 => Code::KeyH, - 0x0017 => Code::KeyI, - 0x0024 => Code::KeyJ, - 0x0025 => Code::KeyK, - 0x0026 => Code::KeyL, - 0x0032 => Code::KeyM, - 0x0031 => Code::KeyN, - 0x0018 => Code::KeyO, - 0x0019 => Code::KeyP, - 0x0010 => Code::KeyQ, - 0x0013 => Code::KeyR, - 0x001F => Code::KeyS, - 0x0014 => Code::KeyT, - 0x0016 => Code::KeyU, - 0x002F => Code::KeyV, - 0x0011 => Code::KeyW, - 0x002D => Code::KeyX, - 0x0015 => Code::KeyY, - 0x002C => Code::KeyZ, - 0x000C => Code::Minus, - 0x0034 => Code::Period, - 0x0028 => Code::Quote, - 0x0027 => Code::Semicolon, - 0x0035 => Code::Slash, - 0x0038 => Code::AltLeft, - 0xE038 => Code::AltRight, - 0x003A => Code::CapsLock, - 0xE05D => Code::ContextMenu, - 0x001D => Code::ControlLeft, - 0xE01D => Code::ControlRight, - 0x001C => Code::Enter, - //0xE05B => Code::OSLeft, - //0xE05C => Code::OSRight, - 0x002A => Code::ShiftLeft, - 0x0036 => Code::ShiftRight, - 0x0039 => Code::Space, - 0x000F => Code::Tab, - 0x0079 => Code::Convert, - 0x0072 => Code::Lang1, // for non-Korean layout - 0xE0F2 => Code::Lang1, // for Korean layout - 0x0071 => Code::Lang2, // for non-Korean layout - 0xE0F1 => Code::Lang2, // for Korean layout - 0x0070 => Code::KanaMode, - 0x007B => Code::NonConvert, - 0xE053 => Code::Delete, - 0xE04F => Code::End, - 0xE047 => Code::Home, - 0xE052 => Code::Insert, - 0xE051 => Code::PageDown, - 0xE049 => Code::PageUp, - 0xE050 => Code::ArrowDown, - 0xE04B => Code::ArrowLeft, - 0xE04D => Code::ArrowRight, - 0xE048 => Code::ArrowUp, - 0xE045 => Code::NumLock, - 0x0052 => Code::Numpad0, - 0x004F => Code::Numpad1, - 0x0050 => Code::Numpad2, - 0x0051 => Code::Numpad3, - 0x004B => Code::Numpad4, - 0x004C => Code::Numpad5, - 0x004D => Code::Numpad6, - 0x0047 => Code::Numpad7, - 0x0048 => Code::Numpad8, - 0x0049 => Code::Numpad9, - 0x004E => Code::NumpadAdd, - 0x007E => Code::NumpadComma, - 0x0053 => Code::NumpadDecimal, - 0xE035 => Code::NumpadDivide, - 0xE01C => Code::NumpadEnter, - 0x0059 => Code::NumpadEqual, - 0x0037 => Code::NumpadMultiply, - 0x004A => Code::NumpadSubtract, - 0x0001 => Code::Escape, - 0x003B => Code::F1, - 0x003C => Code::F2, - 0x003D => Code::F3, - 0x003E => Code::F4, - 0x003F => Code::F5, - 0x0040 => Code::F6, - 0x0041 => Code::F7, - 0x0042 => Code::F8, - 0x0043 => Code::F9, - 0x0044 => Code::F10, - 0x0057 => Code::F11, - 0x0058 => Code::F12, - // TODO: Add these when included in keyboard-types - // 0x0064 => Code::F13, - // 0x0065 => Code::F14, - // 0x0066 => Code::F15, - // 0x0067 => Code::F16, - // 0x0068 => Code::F17, - // 0x0069 => Code::F18, - // 0x006A => Code::F19, - // 0x006B => Code::F20, - // 0x006C => Code::F21, - // 0x006D => Code::F22, - // 0x006E => Code::F23, - // 0x0076 => Code::F24, - 0xE037 => Code::PrintScreen, - 0x0054 => Code::PrintScreen, // Alt + PrintScreen - 0x0046 => Code::ScrollLock, - 0x0045 => Code::Pause, - 0xE046 => Code::Pause, // Ctrl + Pause - 0xE06A => Code::BrowserBack, - 0xE066 => Code::BrowserFavorites, - 0xE069 => Code::BrowserForward, - 0xE032 => Code::BrowserHome, - 0xE067 => Code::BrowserRefresh, - 0xE065 => Code::BrowserSearch, - 0xE068 => Code::BrowserStop, - 0xE06B => Code::LaunchApp1, - 0xE021 => Code::LaunchApp2, - 0xE06C => Code::LaunchMail, - 0xE022 => Code::MediaPlayPause, - 0xE06D => Code::MediaSelect, - 0xE024 => Code::MediaStop, - 0xE019 => Code::MediaTrackNext, - 0xE010 => Code::MediaTrackPrevious, - 0xE05E => Code::Power, - 0xE02E => Code::AudioVolumeDown, - 0xE020 => Code::AudioVolumeMute, - 0xE030 => Code::AudioVolumeUp, - _ => Code::Unidentified, + match scancode { + 0x0029 => KeyCode::Backquote, + 0x002B => KeyCode::Backslash, + 0x000E => KeyCode::Backspace, + 0x001A => KeyCode::BracketLeft, + 0x001B => KeyCode::BracketRight, + 0x0033 => KeyCode::Comma, + 0x000B => KeyCode::Digit0, + 0x0002 => KeyCode::Digit1, + 0x0003 => KeyCode::Digit2, + 0x0004 => KeyCode::Digit3, + 0x0005 => KeyCode::Digit4, + 0x0006 => KeyCode::Digit5, + 0x0007 => KeyCode::Digit6, + 0x0008 => KeyCode::Digit7, + 0x0009 => KeyCode::Digit8, + 0x000A => KeyCode::Digit9, + 0x000D => KeyCode::Equal, + 0x0056 => KeyCode::IntlBackslash, + 0x0073 => KeyCode::IntlRo, + 0x007D => KeyCode::IntlYen, + 0x001E => KeyCode::KeyA, + 0x0030 => KeyCode::KeyB, + 0x002E => KeyCode::KeyC, + 0x0020 => KeyCode::KeyD, + 0x0012 => KeyCode::KeyE, + 0x0021 => KeyCode::KeyF, + 0x0022 => KeyCode::KeyG, + 0x0023 => KeyCode::KeyH, + 0x0017 => KeyCode::KeyI, + 0x0024 => KeyCode::KeyJ, + 0x0025 => KeyCode::KeyK, + 0x0026 => KeyCode::KeyL, + 0x0032 => KeyCode::KeyM, + 0x0031 => KeyCode::KeyN, + 0x0018 => KeyCode::KeyO, + 0x0019 => KeyCode::KeyP, + 0x0010 => KeyCode::KeyQ, + 0x0013 => KeyCode::KeyR, + 0x001F => KeyCode::KeyS, + 0x0014 => KeyCode::KeyT, + 0x0016 => KeyCode::KeyU, + 0x002F => KeyCode::KeyV, + 0x0011 => KeyCode::KeyW, + 0x002D => KeyCode::KeyX, + 0x0015 => KeyCode::KeyY, + 0x002C => KeyCode::KeyZ, + 0x000C => KeyCode::Minus, + 0x0034 => KeyCode::Period, + 0x0028 => KeyCode::Quote, + 0x0027 => KeyCode::Semicolon, + 0x0035 => KeyCode::Slash, + 0x0038 => KeyCode::AltLeft, + 0xE038 => KeyCode::AltRight, + 0x003A => KeyCode::CapsLock, + 0xE05D => KeyCode::ContextMenu, + 0x001D => KeyCode::ControlLeft, + 0xE01D => KeyCode::ControlRight, + 0x001C => KeyCode::Enter, + //0xE05B => KeyCode::OSLeft, + //0xE05C => KeyCode::OSRight, + 0x002A => KeyCode::ShiftLeft, + 0x0036 => KeyCode::ShiftRight, + 0x0039 => KeyCode::Space, + 0x000F => KeyCode::Tab, + 0x0079 => KeyCode::Convert, + 0x0072 => KeyCode::Lang1, // for non-Korean layout + 0xE0F2 => KeyCode::Lang1, // for Korean layout + 0x0071 => KeyCode::Lang2, // for non-Korean layout + 0xE0F1 => KeyCode::Lang2, // for Korean layout + 0x0070 => KeyCode::KanaMode, + 0x007B => KeyCode::NonConvert, + 0xE053 => KeyCode::Delete, + 0xE04F => KeyCode::End, + 0xE047 => KeyCode::Home, + 0xE052 => KeyCode::Insert, + 0xE051 => KeyCode::PageDown, + 0xE049 => KeyCode::PageUp, + 0xE050 => KeyCode::ArrowDown, + 0xE04B => KeyCode::ArrowLeft, + 0xE04D => KeyCode::ArrowRight, + 0xE048 => KeyCode::ArrowUp, + 0xE045 => KeyCode::NumLock, + 0x0052 => KeyCode::Numpad0, + 0x004F => KeyCode::Numpad1, + 0x0050 => KeyCode::Numpad2, + 0x0051 => KeyCode::Numpad3, + 0x004B => KeyCode::Numpad4, + 0x004C => KeyCode::Numpad5, + 0x004D => KeyCode::Numpad6, + 0x0047 => KeyCode::Numpad7, + 0x0048 => KeyCode::Numpad8, + 0x0049 => KeyCode::Numpad9, + 0x004E => KeyCode::NumpadAdd, + 0x007E => KeyCode::NumpadComma, + 0x0053 => KeyCode::NumpadDecimal, + 0xE035 => KeyCode::NumpadDivide, + 0xE01C => KeyCode::NumpadEnter, + 0x0059 => KeyCode::NumpadEqual, + 0x0037 => KeyCode::NumpadMultiply, + 0x004A => KeyCode::NumpadSubtract, + 0x0001 => KeyCode::Escape, + 0x003B => KeyCode::F1, + 0x003C => KeyCode::F2, + 0x003D => KeyCode::F3, + 0x003E => KeyCode::F4, + 0x003F => KeyCode::F5, + 0x0040 => KeyCode::F6, + 0x0041 => KeyCode::F7, + 0x0042 => KeyCode::F8, + 0x0043 => KeyCode::F9, + 0x0044 => KeyCode::F10, + 0x0057 => KeyCode::F11, + 0x0058 => KeyCode::F12, + 0x0064 => KeyCode::F13, + 0x0065 => KeyCode::F14, + 0x0066 => KeyCode::F15, + 0x0067 => KeyCode::F16, + 0x0068 => KeyCode::F17, + 0x0069 => KeyCode::F18, + 0x006A => KeyCode::F19, + 0x006B => KeyCode::F20, + 0x006C => KeyCode::F21, + 0x006D => KeyCode::F22, + 0x006E => KeyCode::F23, + 0x0076 => KeyCode::F24, + 0xE037 => KeyCode::PrintScreen, + 0x0054 => KeyCode::PrintScreen, // Alt + PrintScreen + 0x0046 => KeyCode::ScrollLock, + 0x0045 => KeyCode::Pause, + 0xE046 => KeyCode::Pause, // Ctrl + Pause + 0xE06A => KeyCode::BrowserBack, + 0xE066 => KeyCode::BrowserFavorites, + 0xE069 => KeyCode::BrowserForward, + 0xE032 => KeyCode::BrowserHome, + 0xE067 => KeyCode::BrowserRefresh, + 0xE065 => KeyCode::BrowserSearch, + 0xE068 => KeyCode::BrowserStop, + 0xE06B => KeyCode::LaunchApp1, + 0xE021 => KeyCode::LaunchApp2, + 0xE06C => KeyCode::LaunchMail, + 0xE022 => KeyCode::MediaPlayPause, + 0xE06D => KeyCode::MediaSelect, + 0xE024 => KeyCode::MediaStop, + 0xE019 => KeyCode::MediaTrackNext, + 0xE010 => KeyCode::MediaTrackPrevious, + 0xE05E => KeyCode::Power, + 0xE02E => KeyCode::AudioVolumeDown, + 0xE020 => KeyCode::AudioVolumeMute, + 0xE030 => KeyCode::AudioVolumeUp, + _ => KeyCode::Unidentified(NativeKeyCode::Windows(scancode)), } } From 2cb256df3de8973afd91c69ebece6c19d7eaabfe Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Mon, 4 Jan 2021 23:18:39 +0100 Subject: [PATCH 21/75] WONT COMPILE Implement new keyboard layout preparation --- src/event.rs | 99 ------- src/keyboard.rs | 100 +++++++ src/platform_impl/windows/event.rs | 4 +- src/platform_impl/windows/event_loop.rs | 4 +- src/platform_impl/windows/keyboard.rs | 202 +------------- src/platform_impl/windows/keyboard_layout.rs | 275 +++++++++++++++++++ src/platform_impl/windows/mod.rs | 3 +- 7 files changed, 393 insertions(+), 294 deletions(-) create mode 100644 src/platform_impl/windows/keyboard_layout.rs diff --git a/src/event.rs b/src/event.rs index e19166211d..86ea6414e0 100644 --- a/src/event.rs +++ b/src/event.rs @@ -36,8 +36,6 @@ use instant::Instant; use std::path::PathBuf; -pub use keyboard_types; - use crate::{ dpi::{PhysicalPosition, PhysicalSize}, keyboard, @@ -800,100 +798,3 @@ pub enum MouseScrollDelta { /// platform. PixelDelta(PhysicalPosition), } - -impl ModifiersState { - /// Returns `true` if the shift key is pressed. - pub fn shift(&self) -> bool { - self.intersects(Self::SHIFT) - } - /// Returns `true` if the control key is pressed. - pub fn ctrl(&self) -> bool { - self.intersects(Self::CONTROL) - } - /// Returns `true` if the alt key is pressed. - pub fn alt(&self) -> bool { - self.intersects(Self::ALT) - } - /// Returns `true` if the logo key is pressed. - pub fn logo(&self) -> bool { - self.intersects(Self::META) - } -} - -bitflags! { - /// Represents the current state of the keyboard modifiers - /// - /// Each flag represents a modifier and is set if this modifier is active. - #[derive(Default)] - pub struct ModifiersState: u32 { - // left and right modifiers are currently commented out, but we should be able to support - // them in a future release - /// The "shift" key. - const SHIFT = 0b100 << 0; - // const LSHIFT = 0b010 << 0; - // const RSHIFT = 0b001 << 0; - /// The "control" key. - const CONTROL = 0b100 << 3; - // const LCTRL = 0b010 << 3; - // const RCTRL = 0b001 << 3; - /// The "alt" key. - const ALT = 0b100 << 6; - // const LALT = 0b010 << 6; - // const RALT = 0b001 << 6; - /// This is the "windows" key on PC and "command" key on Mac. - const META = 0b100 << 9; - // const LLOGO = 0b010 << 9; - // const RLOGO = 0b001 << 9; - } -} - -#[cfg(feature = "serde")] -mod modifiers_serde { - use super::ModifiersState; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - #[derive(Default, Serialize, Deserialize)] - #[serde(default)] - #[serde(rename = "ModifiersState")] - pub struct ModifiersStateSerialize { - pub shift: bool, - pub ctrl: bool, - pub alt: bool, - pub logo: bool, - } - - impl Serialize for ModifiersState { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let s = ModifiersStateSerialize { - shift: self.shift(), - ctrl: self.ctrl(), - alt: self.alt(), - logo: self.logo(), - }; - s.serialize(serializer) - } - } - - impl<'de> Deserialize<'de> for ModifiersState { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let ModifiersStateSerialize { - shift, - ctrl, - alt, - logo, - } = ModifiersStateSerialize::deserialize(deserializer)?; - let mut m = ModifiersState::empty(); - m.set(ModifiersState::SHIFT, shift); - m.set(ModifiersState::CTRL, ctrl); - m.set(ModifiersState::ALT, alt); - m.set(ModifiersState::LOGO, logo); - Ok(m) - } - } -} diff --git a/src/keyboard.rs b/src/keyboard.rs index 97e821df09..7c4ff0a1e8 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,5 +1,105 @@ + +impl ModifiersState { + /// Returns `true` if the shift key is pressed. + pub fn shift(&self) -> bool { + self.intersects(Self::SHIFT) + } + /// Returns `true` if the control key is pressed. + pub fn control(&self) -> bool { + self.intersects(Self::CONTROL) + } + /// Returns `true` if the alt key is pressed. + pub fn alt(&self) -> bool { + self.intersects(Self::ALT) + } + /// Returns `true` if the meta key is pressed. + pub fn meta(&self) -> bool { + self.intersects(Self::META) + } +} + +bitflags! { + /// Represents the current state of the keyboard modifiers + /// + /// Each flag represents a modifier and is set if this modifier is active. + #[derive(Default)] + pub struct ModifiersState: u32 { + // left and right modifiers are currently commented out, but we should be able to support + // them in a future release + /// The "shift" key. + const SHIFT = 0b100 << 0; + // const LSHIFT = 0b010 << 0; + // const RSHIFT = 0b001 << 0; + /// The "control" key. + const CONTROL = 0b100 << 3; + // const LCTRL = 0b010 << 3; + // const RCTRL = 0b001 << 3; + /// The "alt" key. + const ALT = 0b100 << 6; + // const LALT = 0b010 << 6; + // const RALT = 0b001 << 6; + /// This is the "windows" key on PC and "command" key on Mac. + const META = 0b100 << 9; + // const LLOGO = 0b010 << 9; + // const RLOGO = 0b001 << 9; + } +} + +#[cfg(feature = "serde")] +mod modifiers_serde { + use super::ModifiersState; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + #[derive(Default, Serialize, Deserialize)] + #[serde(default)] + #[serde(rename = "ModifiersState")] + pub struct ModifiersStateSerialize { + pub shift: bool, + pub control: bool, + pub alt: bool, + pub meta: bool, + } + + impl Serialize for ModifiersState { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = ModifiersStateSerialize { + shift: self.shift(), + control: self.control(), + alt: self.alt(), + meta: self.meta(), + }; + s.serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for ModifiersState { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let ModifiersStateSerialize { + shift, + control, + alt, + meta, + } = ModifiersStateSerialize::deserialize(deserializer)?; + let mut m = ModifiersState::empty(); + m.set(ModifiersState::SHIFT, shift); + m.set(ModifiersState::CONTROL, control); + m.set(ModifiersState::ALT, alt); + m.set(ModifiersState::META, meta); + Ok(m) + } + } +} + + + /// Contains the platform-native physical key identifier (aka scancode) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/src/platform_impl/windows/event.rs b/src/platform_impl/windows/event.rs index 4ce978cc4d..772e9fcd0e 100644 --- a/src/platform_impl/windows/event.rs +++ b/src/platform_impl/windows/event.rs @@ -5,15 +5,13 @@ use std::{ sync::atomic::{AtomicBool, AtomicPtr, Ordering}, }; -use crate::event::ModifiersState; +use crate::keyboard::ModifiersState; use winapi::{ shared::minwindef::{HKL, HKL__}, um::winuser, }; -pub use super::keyboard::PlatformScanCode; - #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEventExtra { pub char_with_all_modifers: Option, diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 4cdee6cfef..9a9797fec2 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -32,7 +32,7 @@ use winapi::{ use crate::{ dpi::{PhysicalPosition, PhysicalSize}, - event::{DeviceEvent, Event, Force, RawKeyEvent, ScanCode, Touch, TouchPhase, WindowEvent}, + event::{DeviceEvent, Event, Force, RawKeyEvent, Touch, TouchPhase, WindowEvent}, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, monitor::MonitorHandle as RootMonitorHandle, platform_impl::platform::{ @@ -40,7 +40,7 @@ use crate::{ dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling}, drop_handler::FileDropHandler, event, - keyboard::{is_msg_keyboard_related, native_key_to_code, PlatformScanCode}, + keyboard::{is_msg_keyboard_related, native_key_to_code}, minimal_ime::is_msg_ime_related, monitor::{self, MonitorHandle}, raw_input, util, diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 4ee0dcdaaa..b87d0338d9 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -17,7 +17,10 @@ use winapi::{ use crate::{ event::{KeyEvent, ElementState}, keyboard::{Key, KeyCode, NativeKeyCode, KeyLocation}, - platform_impl::platform::event::KeyEventExtra, + platform_impl::platform::{ + keyboard_layout::LAYOUT_CACHE, + event::KeyEventExtra + }, }; pub fn is_msg_keyboard_related(msg: u32) -> bool { @@ -36,27 +39,7 @@ pub struct KeyLParam { pub is_repeat: bool, } -#[derive(Clone, Eq, PartialEq)] -enum ToUnicodeResult { - Str(String), - Dead, - None, -} - -impl ToUnicodeResult { - fn is_none(&self) -> bool { - match self { - ToUnicodeResult::None => true, - _ => false, - } - } - - fn is_something(&self) -> bool { - !self.is_none() - } -} - -type ExScancode = u16; +pub type ExScancode = u16; fn new_ex_scancode(scancode: u8, extended: bool) -> ExScancode { (scancode as u16) | (if extended { 0xE000 } else { 0 }) @@ -90,24 +73,6 @@ pub struct MessageAsKeyEvent { pub struct KeyEventBuilder { event_info: Option, - /// This map shouldn't need to exist. - /// However currently this seems to be the only good way - /// of getting the label for the pressed key. Note that calling `ToUnicode` - /// just when the key is pressed/released would be enough if `ToUnicode` wouldn't - /// change the keyboard state (it clears the dead key). There is a flag to prevent - /// changing the state but that flag requires Windows 10, version 1607 or newer) - key_text: HashMap, - - /// Same as `key_text` but as if caps-lock was pressed. - key_text_with_caps: HashMap, - - /// True if the keyboard layout belonging to `known_locale_id` has an AltGr key. - has_alt_graph: bool, - - /// The locale identifier (HKL) of the layout for which the `key_labels` and `has_alt_graph` was - /// generated - known_locale_id: usize, - /// The keyup event needs to call `ToUnicode` to determine what's the text produced by the /// key with all modifiers except CTRL (the `logical_key`). /// @@ -127,20 +92,11 @@ impl Default for KeyEventBuilder { fn default() -> Self { KeyEventBuilder { event_info: None, - key_text: HashMap::with_capacity(128), - key_text_with_caps: HashMap::with_capacity(128), - has_alt_graph: false, - known_locale_id: 0, prev_down_was_dead: false, } } } impl KeyEventBuilder { - const SHIFT_FLAG: u8 = 1 << 0; - const CONTROL_FLAG: u8 = 1 << 1; - const ALT_FLAG: u8 = 1 << 2; - const CAPS_LOCK_FLAG: u8 = 1 << 3; - /// Call this function for every window message. /// Returns Some() if this window message completes a KeyEvent. /// Returns None otherwise. @@ -175,11 +131,8 @@ impl KeyEventBuilder { } self.prev_down_was_dead = false; - // When the labels are already generated for this locale, - // the `generate_labels` function returns without calling - // `ToUnicode` so it keeps the dead key state intact. - let locale_id = unsafe { winuser::GetKeyboardLayout(0) }; - self.prepare_layout(locale_id as usize); + let layouts = LAYOUT_CACHE.lock().unwrap(); + let (locale_id, layout) = layouts.get_current_layout(); //let vkey = wparam as i32; @@ -188,7 +141,7 @@ impl KeyEventBuilder { new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); let code = native_key_to_code(scancode); let vkey = unsafe { - winuser::MapVirtualKeyW(scancode.0 as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 + winuser::MapVirtualKeyW(scancode as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 }; let location = get_location(vkey, lparam_struct.extended); let label = self.key_text.get(&scancode).map(|s| s.clone()); @@ -398,134 +351,6 @@ impl KeyEventBuilder { Vec::new() } - /// Returns true if succeeded. - fn prepare_layout(&mut self, locale_identifier: usize) -> bool { - if self.known_locale_id == locale_identifier { - return true; - } - - // We initialize the keyboard state with all zeros to - // simulate a scenario when no modifier is active. - let mut key_state = [0u8; 256]; - self.key_text.clear(); - self.key_text_with_caps.clear(); - self.has_alt_graph = false; - // Virtual key values are in the domain [0, 255]. - // This is reinforced by the fact that the keyboard state array has 256 - // elements. This array is allowed to be indexed by virtual key values - // giving the key state for the virtual key used for indexing. - for vk in 0..256 { - let scancode = unsafe { - winuser::MapVirtualKeyExW(vk, winuser::MAPVK_VK_TO_VSC_EX, locale_identifier as HKL) - }; - if scancode == 0 { - continue; - } - let platform_scancode = PlatformScanCode(scancode as u16); - - Self::apply_mod_state(&mut key_state, 0); - - // Caps lock is not pressed but it's on. - key_state[winuser::VK_CAPITAL as usize] = 1; - let unicode_caps = Self::to_unicode_string(&key_state, vk, scancode, locale_identifier); - if let ToUnicodeResult::Str(str) = unicode_caps { - self.key_text_with_caps.insert(platform_scancode, str); - } - key_state[winuser::VK_CAPITAL as usize] = 0; - - let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_identifier); - let unicode_str = match unicode.clone() { - ToUnicodeResult::Str(str) => str, - _ => continue, - }; - self.key_text.insert(platform_scancode, unicode_str); - - // Check for alt graph. - // The logic is that if a key pressed with the CTRL modifier produces - // a different result from when it's pressed with CTRL+ALT then the layout - // has AltGr. - if !self.has_alt_graph { - Self::apply_mod_state(&mut key_state, Self::CONTROL_FLAG | Self::ALT_FLAG); - let unicode_with_ctrl_alt = - Self::to_unicode_string(&key_state, vk, scancode, locale_identifier); - if unicode_with_ctrl_alt.is_something() { - self.has_alt_graph = unicode != unicode_with_ctrl_alt; - } - } - } - self.known_locale_id = locale_identifier; - true - } - - fn to_unicode_string( - key_state: &[u8; 256], - vkey: u32, - scancode: u32, - locale_identifier: usize, - ) -> ToUnicodeResult { - unsafe { - let mut label_wide = [0u16; 8]; - let wide_len = winuser::ToUnicodeEx( - vkey, - scancode, - (&key_state[0]) as *const _, - (&mut label_wide[0]) as *mut _, - label_wide.len() as i32, - 0, - locale_identifier as _, - ); - if wide_len < 0 { - // If it's dead, let's run `ToUnicode` again, to consume the dead-key - winuser::ToUnicodeEx( - vkey, - scancode, - (&key_state[0]) as *const _, - (&mut label_wide[0]) as *mut _, - label_wide.len() as i32, - 0, - locale_identifier as _, - ); - return ToUnicodeResult::Dead; - } - if wide_len > 0 { - let os_string = OsString::from_wide(&label_wide[0..wide_len as usize]); - if let Ok(label_str) = os_string.into_string() { - return ToUnicodeResult::Str(label_str); - } - } - } - ToUnicodeResult::None - } - - fn apply_mod_state(key_state: &mut [u8; 256], mod_state: u8) { - if mod_state & Self::SHIFT_FLAG != 0 { - key_state[winuser::VK_SHIFT as usize] |= 0x80; - } else { - key_state[winuser::VK_SHIFT as usize] &= !0x80; - key_state[winuser::VK_LSHIFT as usize] &= !0x80; - key_state[winuser::VK_RSHIFT as usize] &= !0x80; - } - if mod_state & Self::CONTROL_FLAG != 0 { - key_state[winuser::VK_CONTROL as usize] |= 0x80; - } else { - key_state[winuser::VK_CONTROL as usize] &= !0x80; - key_state[winuser::VK_LCONTROL as usize] &= !0x80; - key_state[winuser::VK_RCONTROL as usize] &= !0x80; - } - if mod_state & Self::ALT_FLAG != 0 { - key_state[winuser::VK_MENU as usize] |= 0x80; - } else { - key_state[winuser::VK_MENU as usize] &= !0x80; - key_state[winuser::VK_LMENU as usize] &= !0x80; - key_state[winuser::VK_RMENU as usize] &= !0x80; - } - if mod_state & Self::CAPS_LOCK_FLAG != 0 { - key_state[winuser::VK_CAPITAL as usize] |= 0x80; - } else { - key_state[winuser::VK_CAPITAL as usize] &= !0x80; - } - } - fn synthesize_kbd_state( &mut self, key_state: ElementState, @@ -685,13 +510,11 @@ struct PartialKeyEventInfo { key_state: ElementState, /// The native Virtual Key vkey: i32, - scancode: PlatformScanCode, + scancode: ExScancode, is_repeat: bool, code: KeyCode, location: KeyLocation, - /// True if the key event corresponds to a dead key input - is_dead: bool, - label: Option, + logical_key: Key<'static>, /// The utf16 code units of the text that was produced by the keypress event. /// This take all modifiers into account. Including CTRL @@ -731,6 +554,7 @@ impl PartialKeyEventInfo { KeyEvent { physical_key: self.code, logical_key, + text: None, location: self.location, state: self.key_state, repeat: self.is_repeat, @@ -849,11 +673,11 @@ fn vkey_consumes_dead_key(vkey: u32) -> bool { /// This includes all non-character keys defined within `Key` so for example /// backspace and tab are included. -fn vkey_to_non_printable( +pub fn vkey_to_non_printable( vkey: i32, native_code: NativeKeyCode, code: KeyCode, - hkl: usize, + hkl: u64, has_alt_graph: bool, ) -> Key<'static> { // List of the Web key names and their corresponding platform-native key names: diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs new file mode 100644 index 0000000000..3984d7235d --- /dev/null +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -0,0 +1,275 @@ + +use std::{ + collections::{HashMap, HashSet, hash_map::Entry}, + sync::Mutex, + os::windows::ffi::OsStringExt, +}; + +use lazy_static::lazy_static; + + +use winapi::{ + shared::{ + minwindef::{HKL, LOWORD, LPARAM, LRESULT, UINT, WPARAM}, + }, + um::{ + winnt::{LANG_JAPANESE, LANG_KOREAN, PRIMARYLANGID}, + winuser, + }, +}; + +use crate::{ + keyboard::{ModifiersState, Key, KeyCode, NativeKeyCode}, + platform_impl::platform::keyboard::{ExScancode, vkey_to_non_printable, native_key_to_code}, +}; + +lazy_static!{ + pub static ref LAYOUT_CACHE: Mutex = Mutex::new(LayoutCache::default()); +} + +pub struct Layout { + /// Maps a modifier state to group of key strings + /// Not using `ModifiersState` here because that object cannot express caps lock + /// but we need to handle caps lock too. + /// + /// This map shouldn't need to exist. + /// However currently this seems to be the only good way + /// of getting the label for the pressed key. Note that calling `ToUnicode` + /// just when the key is pressed/released would be enough if `ToUnicode` wouldn't + /// change the keyboard state (it clears the dead key). There is a flag to prevent + /// changing the state but that flag requires Windows 10, version 1607 or newer) + pub keys: HashMap>>, + pub has_alt_graph: bool, +} + +#[derive(Default)] +pub struct LayoutCache { + /// Maps locale identifiers (HKL) to layouts + pub layouts: HashMap, + pub strings: HashSet<&'static str>, +} + +impl LayoutCache { + const SHIFT_FLAG: u8 = 1 << 0; + const CONTROL_FLAG: u8 = 1 << 1; + const ALT_FLAG: u8 = 1 << 2; + const CAPS_LOCK_FLAG: u8 = 1 << 3; + const MOD_FLAGS_END: u8 = 1 << 4; + + /// Checks whether the current layout is already known and + /// prepares the layout if it isn't known. + /// The current layout is then returned. + pub fn get_current_layout(&mut self) -> (u64, &Layout) { + let locale_id = unsafe { winuser::GetKeyboardLayout(0) } as u64; + match self.layouts.entry(locale_id) { + Entry::Occupied(entry) => { + (locale_id, entry.get()) + } + Entry::Vacant(entry) => { + let layout = self.prepare_layout(locale_id); + (locale_id, entry.insert(layout)) + } + } + } + + /// Returns Some if succeeded + fn prepare_layout(&mut self, locale_identifier: u64) -> Layout { + let mut layout = Layout { + keys: Default::default(), + has_alt_graph: false, + }; + + // We initialize the keyboard state with all zeros to + // simulate a scenario when no modifier is active. + let mut key_state = [0u8; 256]; + + // Iterate through every combination of modifiers + for mod_state in 0..Self::MOD_FLAGS_END { + let mut keys_for_this_mod = HashMap::with_capacity(256); + + Self::apply_mod_state(&mut key_state, mod_state); + + // Virtual key values are in the domain [0, 255]. + // This is reinforced by the fact that the keyboard state array has 256 + // elements. This array is allowed to be indexed by virtual key values + // giving the key state for the virtual key used for indexing. + for vk in 0..256 { + let scancode = unsafe { + winuser::MapVirtualKeyExW(vk, winuser::MAPVK_VK_TO_VSC_EX, locale_identifier as HKL) + }; + if scancode == 0 { + continue; + } + + let native_code = NativeKeyCode::Windows(scancode as ExScancode); + let key_code = native_key_to_code(scancode as ExScancode); + // Let's try to get the key from just the scancode and vk + // We don't necessarily know yet if AltGraph is present on this layout so we'll + // assume it isn't. Then we'll do a second pass where we set the "AltRight" keys to + // "AltGr" in case we find out that there's an AltGraph. + let preliminary_key = vkey_to_non_printable( + vk as i32, native_code, key_code, locale_identifier, false + ); + match preliminary_key { + Key::Unidentified(_) => (), + _ => { + keys_for_this_mod.insert(key_code, preliminary_key); + continue; + } + } + + let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_identifier); + let key = match unicode { + ToUnicodeResult::Str(str) => { + let static_str = self.get_or_insert_str(str); + Key::Character(static_str) + } + ToUnicodeResult::Dead(dead_char) => { + Key::Dead(dead_char) + } + ToUnicodeResult::None => { + // Just use the unidentified key, we got earlier + preliminary_key + } + }; + + // Check for alt graph. + // The logic is that if a key pressed with the CTRL modifier produces + // a different result from when it's pressed with CTRL+ALT then the layout + // has AltGr. + const CTRL_ALT_FLAG: u8 = Self::ALT_FLAG | Self::CONTROL_FLAG; + let is_in_ctrl_alt = (mod_state & CTRL_ALT_FLAG) == CTRL_ALT_FLAG; + if !layout.has_alt_graph && is_in_ctrl_alt { + // Unwrapping here because if we are in the ctrl+alt modifier state + // then the alt modifier state must have come before. + let alt_keys = layout.keys.get(&Self::ALT_FLAG).unwrap(); + if let Some(key_without_ctrl_alt) = alt_keys.get(&key_code) { + layout.has_alt_graph = key != *key_without_ctrl_alt; + } + } + + keys_for_this_mod.insert(key_code, key); + } + + layout.keys.insert(mod_state, keys_for_this_mod); + } + + // Second pass: replace right alt keys with AltGr if the layout has alt graph + if layout.has_alt_graph { + for mod_state in 0..Self::MOD_FLAGS_END { + if let Some(keys) = layout.keys.get_mut(&mod_state) { + if let Some(key) = keys.get_mut(&KeyCode::AltRight) { + *key = Key::AltGraph; + } + } + } + } + + layout + } + + fn get_or_insert_str(&mut self, string: String) -> &'static str { + { + let str_ref = string.as_str(); + if let Some(&existing) = self.strings.get(&str_ref) { + return existing; + } + } + let leaked = Box::leak(Box::from(string)); + self.strings.insert(leaked); + leaked + } + + fn to_unicode_string( + key_state: &[u8; 256], + vkey: u32, + scancode: u32, + locale_identifier: u64, + ) -> ToUnicodeResult { + unsafe { + let mut label_wide = [0u16; 8]; + let mut wide_len = winuser::ToUnicodeEx( + vkey, + scancode, + (&key_state[0]) as *const _, + (&mut label_wide[0]) as *mut _, + label_wide.len() as i32, + 0, + locale_identifier as _, + ); + if wide_len < 0 { + // If it's dead, let's run `ToUnicode` again, to consume the dead-key + wide_len = winuser::ToUnicodeEx( + vkey, + scancode, + (&key_state[0]) as *const _, + (&mut label_wide[0]) as *mut _, + label_wide.len() as i32, + 0, + locale_identifier as _, + ); + if wide_len > 0 { + let os_string = OsString::from_wide(&label_wide[0..wide_len as usize]); + if let Ok(label_str) = os_string.into_string() { + if let Some(ch) = label_str.chars().next() { + return ToUnicodeResult::Dead(Some(ch)); + } + } + } + return ToUnicodeResult::Dead(None); + } + if wide_len > 0 { + let os_string = OsString::from_wide(&label_wide[0..wide_len as usize]); + if let Ok(label_str) = os_string.into_string() { + return ToUnicodeResult::Str(label_str); + } + } + } + ToUnicodeResult::None + } + + fn apply_mod_state(key_state: &mut [u8; 256], mod_state: u8) { + if mod_state & Self::SHIFT_FLAG != 0 { + key_state[winuser::VK_SHIFT as usize] |= 0x80; + } else { + key_state[winuser::VK_SHIFT as usize] &= !0x80; + key_state[winuser::VK_LSHIFT as usize] &= !0x80; + key_state[winuser::VK_RSHIFT as usize] &= !0x80; + } + if mod_state & Self::CONTROL_FLAG != 0 { + key_state[winuser::VK_CONTROL as usize] |= 0x80; + } else { + key_state[winuser::VK_CONTROL as usize] &= !0x80; + key_state[winuser::VK_LCONTROL as usize] &= !0x80; + key_state[winuser::VK_RCONTROL as usize] &= !0x80; + } + if mod_state & Self::ALT_FLAG != 0 { + key_state[winuser::VK_MENU as usize] |= 0x80; + } else { + key_state[winuser::VK_MENU as usize] &= !0x80; + key_state[winuser::VK_LMENU as usize] &= !0x80; + key_state[winuser::VK_RMENU as usize] &= !0x80; + } + if mod_state & Self::CAPS_LOCK_FLAG != 0 { + key_state[winuser::VK_CAPITAL as usize] |= 0x80; + } else { + key_state[winuser::VK_CAPITAL as usize] &= !0x80; + } + } +} + +#[derive(Clone, Eq, PartialEq)] +enum ToUnicodeResult { + Str(String), + Dead(Option), + None, +} + +impl ToUnicodeResult { + fn is_none(&self) -> bool { + match self { + ToUnicodeResult::None => true, + _ => false, + } + } +} diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index d17b887347..4723c439ee 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -3,7 +3,7 @@ use winapi::{self, shared::windef::HWND}; pub use self::{ - event::{KeyEventExtra, PlatformScanCode}, + event::{KeyEventExtra}, event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}, icon::WinIcon, monitor::{MonitorHandle, VideoMode}, @@ -96,6 +96,7 @@ mod event; mod event_loop; mod icon; mod keyboard; +mod keyboard_layout; mod minimal_ime; mod monitor; mod raw_input; From a94c3d8db22865080b3e6a70218ca4a0e695c582 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Wed, 6 Jan 2021 00:49:19 +0100 Subject: [PATCH 22/75] WONT COMPILE new keyboard API progress --- src/keyboard.rs | 13 ++ src/platform_impl/windows/event.rs | 6 +- src/platform_impl/windows/keyboard.rs | 190 ++++++++++--------- src/platform_impl/windows/keyboard_layout.rs | 133 +++++++++---- 4 files changed, 208 insertions(+), 134 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 7c4ff0a1e8..771be724ae 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1189,6 +1189,19 @@ pub enum Key<'a> { F35, } +impl<'a> Key<'a> { + pub fn to_text(&self) -> Option<&'a str> { + match self { + Key::Character(ch) => Some(*ch), + Key::Enter => Some("\r"), + Key::Backspace => Some("\x08"), + Key::Tab => Some("\t"), + Key::Escape => Some("\x1b"), + _ => None, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum KeyLocation { diff --git a/src/platform_impl/windows/event.rs b/src/platform_impl/windows/event.rs index 772e9fcd0e..3eafbbc82c 100644 --- a/src/platform_impl/windows/event.rs +++ b/src/platform_impl/windows/event.rs @@ -5,7 +5,7 @@ use std::{ sync::atomic::{AtomicBool, AtomicPtr, Ordering}, }; -use crate::keyboard::ModifiersState; +use crate::keyboard::{ModifiersState, Key}; use winapi::{ shared::minwindef::{HKL, HKL__}, @@ -14,8 +14,8 @@ use winapi::{ #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEventExtra { - pub char_with_all_modifers: Option, - pub key_without_modifers: keyboard_types::Key, + pub char_with_all_modifers: Option<&'static str>, + pub key_without_modifers: Key<'static>, } fn key_pressed(vkey: c_int) -> bool { diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index b87d0338d9..1a4b07a380 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -1,7 +1,4 @@ -use std::{ - char, collections::HashMap, ffi::OsString, fmt, mem::MaybeUninit, os::raw::c_int, - os::windows::ffi::OsStringExt, -}; +use std::{char, collections::HashMap, ffi::OsString, fmt, mem::MaybeUninit, os::raw::c_int, os::windows::ffi::OsStringExt, sync::MutexGuard}; use winapi::{ shared::{ @@ -18,7 +15,7 @@ use crate::{ event::{KeyEvent, ElementState}, keyboard::{Key, KeyCode, NativeKeyCode, KeyLocation}, platform_impl::platform::{ - keyboard_layout::LAYOUT_CACHE, + keyboard_layout::{LAYOUT_CACHE, WindowsModifiers, Layout, LayoutCache}, event::KeyEventExtra }, }; @@ -131,11 +128,6 @@ impl KeyEventBuilder { } self.prev_down_was_dead = false; - let layouts = LAYOUT_CACHE.lock().unwrap(); - let (locale_id, layout) = layouts.get_current_layout(); - - //let vkey = wparam as i32; - let lparam_struct = destructure_key_lparam(lparam); let scancode = new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); @@ -144,16 +136,24 @@ impl KeyEventBuilder { winuser::MapVirtualKeyW(scancode as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 }; let location = get_location(vkey, lparam_struct.extended); - let label = self.key_text.get(&scancode).map(|s| s.clone()); + + let kbd_state = unsafe { Self::get_kbd_state() }; + let mods = WindowsModifiers::active_modifiers(&kbd_state); + let layouts = LAYOUT_CACHE.lock().unwrap(); + let (locale_id, layout) = layouts.get_current_layout(); + let logical_key = layout.get_key(mods, scancode, code); + let key_without_modifers = layout.get_key( + WindowsModifiers::empty(), scancode, code + ); let mut event_info = Some(PartialKeyEventInfo { key_state: ElementState::Pressed, + logical_key, + key_without_modifers, vkey, scancode, is_repeat: lparam_struct.is_repeat, code, location, - is_dead: false, - label, utf16parts: Vec::with_capacity(8), utf16parts_without_ctrl: Vec::with_capacity(8), }); @@ -180,7 +180,7 @@ impl KeyEventBuilder { } } if let Some(event_info) = event_info { - let ev = event_info.finalize(locale_id as usize, self.has_alt_graph); + let ev = event_info.finalize(); return vec![MessageAsKeyEvent { event: ev, is_synthetic: false, @@ -192,9 +192,8 @@ impl KeyEventBuilder { // At this point we know that there isn't going to be any more events related to // this key press let mut event_info = self.event_info.take().unwrap(); - event_info.is_dead = true; *retval = Some(0); - let ev = event_info.finalize(self.known_locale_id, self.has_alt_graph); + let ev = event_info.finalize(); return vec![MessageAsKeyEvent { event: ev, is_synthetic: false, @@ -237,6 +236,8 @@ impl KeyEventBuilder { .utf16parts .push(wparam as u16); } else { + // Otherwise wparam holds a utf32 character. + // Let's encode it as utf16 appending it to the end of utf16parts let utf16parts = &mut self.event_info.as_mut().unwrap().utf16parts; let start_offset = utf16parts.len(); let new_size = utf16parts.len() + 2; @@ -253,13 +254,11 @@ impl KeyEventBuilder { // Here it's okay to call `ToUnicode` because at this point the dead key // is already consumed by the character. unsafe { - let mut key_state: [MaybeUninit; 256] = [MaybeUninit::uninit(); 256]; - winuser::GetKeyboardState(key_state[0].as_mut_ptr()); - let mut key_state = std::mem::transmute::<_, [u8; 256]>(key_state); + let kbd_state = Self::get_kbd_state(); - let has_ctrl = key_state[winuser::VK_CONTROL as usize] & 0x80 != 0 - || key_state[winuser::VK_LCONTROL as usize] & 0x80 != 0 - || key_state[winuser::VK_RCONTROL as usize] & 0x80 != 0; + let has_ctrl = kbd_state[winuser::VK_CONTROL as usize] & 0x80 != 0 + || kbd_state[winuser::VK_LCONTROL as usize] & 0x80 != 0 + || kbd_state[winuser::VK_RCONTROL as usize] & 0x80 != 0; // If neither of the CTRL keys is pressed, just use the text with all // modifiers because that already consumed the dead key and otherwise @@ -267,16 +266,16 @@ impl KeyEventBuilder { if !has_ctrl { event_info.utf16parts_without_ctrl = event_info.utf16parts.clone(); } else { + // TODO: This should probably be done through the layout cahce get_utf16_without_ctrl( event_info.vkey as u32, event_info.scancode, - &mut key_state, + &mut kbd_state, &mut event_info.utf16parts_without_ctrl, ); } } - - let ev = event_info.finalize(self.known_locale_id, self.has_alt_graph); + let ev = event_info.finalize(); return vec![MessageAsKeyEvent { event: ev, is_synthetic: false, @@ -287,59 +286,72 @@ impl KeyEventBuilder { *retval = Some(0); let lparam_struct = destructure_key_lparam(lparam); let scancode = - PlatformScanCode::new(lparam_struct.scancode, lparam_struct.extended); + new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); let code = native_key_to_code(scancode); let vkey = unsafe { - winuser::MapVirtualKeyW(scancode.0 as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 + winuser::MapVirtualKeyW(scancode as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 }; let location = get_location(vkey, lparam_struct.extended); let mut utf16parts = Vec::with_capacity(8); let mut utf16parts_without_ctrl = Vec::with_capacity(8); + let kbd_state = unsafe { Self::get_kbd_state() }; + let mods = WindowsModifiers::active_modifiers(&kbd_state); + let mods_without_ctrl = { + let mut mods_copy = mods; + mods_copy.remove(WindowsModifiers::CONTROL); + mods_copy + }; + // Avoid calling `ToUnicode` (which resets dead keys) if either the event // belongs to the key-down which just produced the dead key or if // the current key would not otherwise reset the dead key state. // // This logic relies on the assuption that keys which don't consume // dead keys, also do not produce text input. - if !self.prev_down_was_dead && vkey_consumes_dead_key(wparam as u32) { - unsafe { - //let locale_id = winuser::GetKeyboardLayout(0); - let mut key_state: [MaybeUninit; 256] = [MaybeUninit::uninit(); 256]; - winuser::GetKeyboardState(key_state[0].as_mut_ptr()); - let mut key_state = std::mem::transmute::<_, [u8; 256]>(key_state); - let unicode_len = winuser::ToUnicode( - wparam as u32, - scancode.0 as u32, - (&mut key_state[0]) as *mut _, - utf16parts.as_mut_ptr(), - utf16parts.capacity() as i32, - 0, - ); - utf16parts.set_len(unicode_len as usize); - - get_utf16_without_ctrl( - wparam as u32, - scancode, - &mut key_state, - &mut utf16parts_without_ctrl, - ); - } - } - let label = self.key_text.get(&scancode).map(|s| s.clone()); + // if !self.prev_down_was_dead && vkey_consumes_dead_key(wparam as u32) { + // unsafe { + // let unicode_len = winuser::ToUnicode( + // wparam as u32, + // scancode as u32, + // (&mut kbd_state[0]) as *mut _, + // utf16parts.as_mut_ptr(), + // utf16parts.capacity() as i32, + // 0, + // ); + // utf16parts.set_len(unicode_len as usize); + + // get_utf16_without_ctrl( + // wparam as u32, + // scancode, + // &mut kbd_state, + // &mut utf16parts_without_ctrl, + // ); + // } + // } + + let layouts = LAYOUT_CACHE.lock().unwrap(); + let (locale_id, layout) = layouts.get_current_layout(); + let logical_key = layout.get_key(mods_without_ctrl, scancode, code); + let key_without_modifers = layout.get_key( + WindowsModifiers::empty(), scancode, code + ); + let key_with_all_modifiers = layout.get_key(mods, scancode, code); let event_info = PartialKeyEventInfo { key_state: ElementState::Released, + logical_key, + key_without_modifers, vkey, scancode, is_repeat: false, code, location, - is_dead: self.prev_down_was_dead, - label, - utf16parts, - utf16parts_without_ctrl, + utf16parts: Vec::new(), + utf16parts_without_ctrl: Vec::new(), }; - let ev = event_info.finalize(self.known_locale_id, self.has_alt_graph); + let mut ev = event_info.finalize(); + ev.text = logical_key.to_text(); + ev.platform_specific.char_with_all_modifers = key_with_all_modifiers.to_text(); return vec![MessageAsKeyEvent { event: ev, is_synthetic: false, @@ -356,14 +368,13 @@ impl KeyEventBuilder { key_state: ElementState, ) -> Vec { let mut key_events = Vec::new(); - let locale_id = unsafe { winuser::GetKeyboardLayout(0) }; - self.prepare_layout(locale_id as usize); - - let kbd_state = unsafe { - let mut kbd_state: [MaybeUninit; 256] = [MaybeUninit::uninit(); 256]; - winuser::GetKeyboardState(kbd_state[0].as_mut_ptr()); - std::mem::transmute::<_, [u8; 256]>(kbd_state) + + let locale_id = { + let mut layout_cache = LAYOUT_CACHE.lock().unwrap(); + layout_cache.get_current_layout().0 }; + + let kbd_state = unsafe { Self::get_kbd_state() }; macro_rules! is_key_pressed { ($vk:expr) => { kbd_state[$vk as usize] & 0x80 != 0 @@ -504,6 +515,12 @@ impl KeyEventBuilder { is_synthetic: true, }) } + + unsafe fn get_kbd_state() -> [u8; 256] { + let mut kbd_state: [MaybeUninit; 256] = [MaybeUninit::uninit(); 256]; + winuser::GetKeyboardState(kbd_state[0].as_mut_ptr()); + std::mem::transmute::<_, [u8; 256]>(kbd_state) + } } struct PartialKeyEventInfo { @@ -516,6 +533,8 @@ struct PartialKeyEventInfo { location: KeyLocation, logical_key: Key<'static>, + key_without_modifers: Key<'static>, + /// The utf16 code units of the text that was produced by the keypress event. /// This take all modifiers into account. Including CTRL utf16parts: Vec, @@ -524,43 +543,26 @@ struct PartialKeyEventInfo { } impl PartialKeyEventInfo { - fn finalize(self, locale_id: usize, has_alt_gr: bool) -> KeyEvent { - let logical_key; - if self.is_dead { - // TODO: dispatch the dead-key char here - logical_key = Key::Dead(None); - } else { - if !self.utf16parts_without_ctrl.is_empty() { - let string = String::from_utf16(&self.utf16parts_without_ctrl).unwrap(); - // TODO: cache these in a global map - let leaked = Box::leak(Box::::from(string)); - logical_key = Key::Character(leaked); - } else { - logical_key = vkey_to_non_printable(self.vkey, self.code, locale_id, has_alt_gr); - } - } - let key_without_modifers; - if let Some(label) = &self.label { - key_without_modifers = Key::Character(label.clone()); - } else { - key_without_modifers = logical_key.clone(); - } - + fn finalize(self, mut layout_cache: MutexGuard<'_, LayoutCache>) -> KeyEvent { let mut char_with_all_modifiers = None; if !self.utf16parts.is_empty() { - char_with_all_modifiers = Some(String::from_utf16(&self.utf16parts).unwrap()); + let os_string = OsString::from_wide(&self.utf16parts); + if let Ok(string) = os_string.into_string() { + let static_str = layout_cache.get_or_insert_str(string); + char_with_all_modifiers = Some(static_str); + } } KeyEvent { physical_key: self.code, - logical_key, + logical_key: self.logical_key, text: None, location: self.location, state: self.key_state, repeat: self.is_repeat, platform_specific: KeyEventExtra { char_with_all_modifers: char_with_all_modifiers, - key_without_modifers, + key_without_modifers: self.key_without_modifers, }, } } @@ -605,7 +607,7 @@ pub fn get_location(vkey: c_int, extended: bool) -> KeyLocation { unsafe fn get_utf16_without_ctrl( vkey: u32, scancode: ExScancode, - key_state: &mut [u8; 256], + kbd_state: &mut [u8; 256], utf16parts_without_ctrl: &mut Vec, ) { let target_capacity = 8; @@ -614,13 +616,13 @@ unsafe fn get_utf16_without_ctrl( utf16parts_without_ctrl.reserve(target_capacity - curr_cap); } // Now remove all CTRL stuff from the keyboard state - key_state[winuser::VK_CONTROL as usize] = 0; - key_state[winuser::VK_LCONTROL as usize] = 0; - key_state[winuser::VK_RCONTROL as usize] = 0; + kbd_state[winuser::VK_CONTROL as usize] = 0; + kbd_state[winuser::VK_LCONTROL as usize] = 0; + kbd_state[winuser::VK_RCONTROL as usize] = 0; let unicode_len = winuser::ToUnicode( vkey, scancode as u32, - (&mut key_state[0]) as *mut _, + (&mut kbd_state[0]) as *mut _, utf16parts_without_ctrl.as_mut_ptr(), utf16parts_without_ctrl.capacity() as i32, 0, diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 3984d7235d..d2a3df9c7f 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -27,6 +27,78 @@ lazy_static!{ pub static ref LAYOUT_CACHE: Mutex = Mutex::new(LayoutCache::default()); } +bitflags! { + pub struct WindowsModifiers : u8 { + const SHIFT = 1 << 0; + const CONTROL = 1 << 1; + const ALT = 1 << 2; + const CAPS_LOCK = 1 << 3; + const FLAGS_END = 1 << 4; + } +} + +impl WindowsModifiers { + pub fn active_modifiers(key_state: &[u8; 256]) -> WindowsModifiers { + let shift = key_state[winuser::VK_SHIFT as usize] & 0x80 != 0; + let lshift = key_state[winuser::VK_LSHIFT as usize] & 0x80 != 0; + let rshift = key_state[winuser::VK_RSHIFT as usize] & 0x80 != 0; + + let control = key_state[winuser::VK_CONTROL as usize] & 0x80 != 0; + let lcontrol = key_state[winuser::VK_LCONTROL as usize] & 0x80 != 0; + let rcontrol = key_state[winuser::VK_RCONTROL as usize] & 0x80 != 0; + + let alt = key_state[winuser::VK_MENU as usize] & 0x80 != 0; + let lalt = key_state[winuser::VK_LMENU as usize] & 0x80 != 0; + let ralt = key_state[winuser::VK_RMENU as usize] & 0x80 != 0; + + let caps = key_state[winuser::VK_CAPITAL as usize] & 0x01 != 0; + + let mut result = WindowsModifiers::empty(); + if shift || lshift || rshift { + result.insert(WindowsModifiers::SHIFT); + } + if control || lcontrol || rcontrol { + result.insert(WindowsModifiers::CONTROL); + } + if alt || lalt || ralt { + result.insert(WindowsModifiers::ALT); + } + if caps { + result.insert(WindowsModifiers::CAPS_LOCK); + } + result + } + + pub fn apply_to_key_state(self, key_state: &mut [u8; 256]) { + if self.intersects(Self::SHIFT) { + key_state[winuser::VK_SHIFT as usize] |= 0x80; + } else { + key_state[winuser::VK_SHIFT as usize] &= !0x80; + key_state[winuser::VK_LSHIFT as usize] &= !0x80; + key_state[winuser::VK_RSHIFT as usize] &= !0x80; + } + if self.intersects(Self::CONTROL) { + key_state[winuser::VK_CONTROL as usize] |= 0x80; + } else { + key_state[winuser::VK_CONTROL as usize] &= !0x80; + key_state[winuser::VK_LCONTROL as usize] &= !0x80; + key_state[winuser::VK_RCONTROL as usize] &= !0x80; + } + if self.intersects(Self::ALT) { + key_state[winuser::VK_MENU as usize] |= 0x80; + } else { + key_state[winuser::VK_MENU as usize] &= !0x80; + key_state[winuser::VK_LMENU as usize] &= !0x80; + key_state[winuser::VK_RMENU as usize] &= !0x80; + } + if self.intersects(Self::CAPS_LOCK) { + key_state[winuser::VK_CAPITAL as usize] |= 0x80; + } else { + key_state[winuser::VK_CAPITAL as usize] &= !0x80; + } + } +} + pub struct Layout { /// Maps a modifier state to group of key strings /// Not using `ModifiersState` here because that object cannot express caps lock @@ -38,10 +110,21 @@ pub struct Layout { /// just when the key is pressed/released would be enough if `ToUnicode` wouldn't /// change the keyboard state (it clears the dead key). There is a flag to prevent /// changing the state but that flag requires Windows 10, version 1607 or newer) - pub keys: HashMap>>, + pub keys: HashMap>>, pub has_alt_graph: bool, } +impl Layout { + pub fn get_key(&self, mods: WindowsModifiers, scancode: ExScancode, keycode: KeyCode) -> Key<'static> { + if let Some(keys) = self.keys.get(&mods) { + if let Some(key) = keys.get(&keycode) { + return *key; + } + } + Key::Unidentified(NativeKeyCode::Windows(scancode)) + } +} + #[derive(Default)] pub struct LayoutCache { /// Maps locale identifiers (HKL) to layouts @@ -84,10 +167,13 @@ impl LayoutCache { let mut key_state = [0u8; 256]; // Iterate through every combination of modifiers - for mod_state in 0..Self::MOD_FLAGS_END { + let mods_end = WindowsModifiers::FLAGS_END.bits; + for mod_state in 0..mods_end { let mut keys_for_this_mod = HashMap::with_capacity(256); - Self::apply_mod_state(&mut key_state, mod_state); + let mod_state = unsafe { WindowsModifiers::from_bits_unchecked(mod_state) }; + + //Self::apply_mod_state(&mut key_state, mod_state); // Virtual key values are in the domain [0, 255]. // This is reinforced by the fact that the keyboard state array has 256 @@ -137,12 +223,13 @@ impl LayoutCache { // The logic is that if a key pressed with the CTRL modifier produces // a different result from when it's pressed with CTRL+ALT then the layout // has AltGr. - const CTRL_ALT_FLAG: u8 = Self::ALT_FLAG | Self::CONTROL_FLAG; - let is_in_ctrl_alt = (mod_state & CTRL_ALT_FLAG) == CTRL_ALT_FLAG; + const CTRL_ALT: WindowsModifiers = + WindowsModifiers::CONTROL | WindowsModifiers::ALT; + let is_in_ctrl_alt = (mod_state & CTRL_ALT) == CTRL_ALT; if !layout.has_alt_graph && is_in_ctrl_alt { // Unwrapping here because if we are in the ctrl+alt modifier state // then the alt modifier state must have come before. - let alt_keys = layout.keys.get(&Self::ALT_FLAG).unwrap(); + let alt_keys = layout.keys.get(&WindowsModifiers::ALT).unwrap(); if let Some(key_without_ctrl_alt) = alt_keys.get(&key_code) { layout.has_alt_graph = key != *key_without_ctrl_alt; } @@ -156,7 +243,8 @@ impl LayoutCache { // Second pass: replace right alt keys with AltGr if the layout has alt graph if layout.has_alt_graph { - for mod_state in 0..Self::MOD_FLAGS_END { + for mod_state in 0..mods_end { + let mod_state = unsafe { WindowsModifiers::from_bits_unchecked(mod_state) }; if let Some(keys) = layout.keys.get_mut(&mod_state) { if let Some(key) = keys.get_mut(&KeyCode::AltRight) { *key = Key::AltGraph; @@ -168,7 +256,7 @@ impl LayoutCache { layout } - fn get_or_insert_str(&mut self, string: String) -> &'static str { + pub fn get_or_insert_str(&mut self, string: String) -> &'static str { { let str_ref = string.as_str(); if let Some(&existing) = self.strings.get(&str_ref) { @@ -227,35 +315,6 @@ impl LayoutCache { } ToUnicodeResult::None } - - fn apply_mod_state(key_state: &mut [u8; 256], mod_state: u8) { - if mod_state & Self::SHIFT_FLAG != 0 { - key_state[winuser::VK_SHIFT as usize] |= 0x80; - } else { - key_state[winuser::VK_SHIFT as usize] &= !0x80; - key_state[winuser::VK_LSHIFT as usize] &= !0x80; - key_state[winuser::VK_RSHIFT as usize] &= !0x80; - } - if mod_state & Self::CONTROL_FLAG != 0 { - key_state[winuser::VK_CONTROL as usize] |= 0x80; - } else { - key_state[winuser::VK_CONTROL as usize] &= !0x80; - key_state[winuser::VK_LCONTROL as usize] &= !0x80; - key_state[winuser::VK_RCONTROL as usize] &= !0x80; - } - if mod_state & Self::ALT_FLAG != 0 { - key_state[winuser::VK_MENU as usize] |= 0x80; - } else { - key_state[winuser::VK_MENU as usize] &= !0x80; - key_state[winuser::VK_LMENU as usize] &= !0x80; - key_state[winuser::VK_RMENU as usize] &= !0x80; - } - if mod_state & Self::CAPS_LOCK_FLAG != 0 { - key_state[winuser::VK_CAPITAL as usize] |= 0x80; - } else { - key_state[winuser::VK_CAPITAL as usize] &= !0x80; - } - } } #[derive(Clone, Eq, PartialEq)] From ff2d7aae154db93635e5b908be56bbbd19b1c785 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Thu, 7 Jan 2021 23:07:25 +0100 Subject: [PATCH 23/75] Main compile errors fixed for keyboard --- src/event.rs | 24 +- src/keyboard.rs | 11 +- src/platform/modifier_supplement.rs | 6 +- src/platform/windows.rs | 11 +- src/platform_impl/windows/event.rs | 2 +- src/platform_impl/windows/event_loop.rs | 17 +- src/platform_impl/windows/keyboard.rs | 221 +++++++------------ src/platform_impl/windows/keyboard_layout.rs | 142 ++++-------- src/platform_impl/windows/mod.rs | 2 +- src/platform_impl/windows/window_state.rs | 2 +- 10 files changed, 164 insertions(+), 274 deletions(-) diff --git a/src/event.rs b/src/event.rs index 86ea6414e0..5236d7f00a 100644 --- a/src/event.rs +++ b/src/event.rs @@ -38,7 +38,7 @@ use std::path::PathBuf; use crate::{ dpi::{PhysicalPosition, PhysicalSize}, - keyboard, + keyboard::{self, ModifiersState}, platform_impl, window::{Theme, WindowId}, }; @@ -601,14 +601,14 @@ pub enum DeviceEvent { /// Describes a keyboard input as a raw device event. #[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct RawKeyEvent { - pub physical_key: keyboard_types::Code, - pub state: keyboard_types::KeyState, + pub physical_key: keyboard::KeyCode, + pub state: ElementState, } /// Describes a keyboard input targeting a window. #[derive(Debug, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct KeyEvent { /// Represents the position of a key independent of the /// currently active layout. @@ -617,19 +617,19 @@ pub struct KeyEvent { /// Note that `Fn` and `FnLock` key events are not emmited by `winit`. /// These keys are usually handled at the hardware or at the OS level. pub physical_key: keyboard::KeyCode, - + /// This value is affected by all modifiers except Ctrl. - /// + /// /// This has two use cases: /// - Allows querying whether the current input is a Dead key /// - Allows handling key-bindings on platforms which don't /// support `KeyEventExtModifierSupplement::key_without_modifiers`. - /// + /// /// ## Platform-specific /// - **Web:** Dead keys might be reported as the real key instead /// of `Dead` depending on the browser/OS. pub logical_key: keyboard::Key<'static>, - + /// Contains the text produced by this keypress. /// /// In most cases this is identical to the content @@ -642,15 +642,15 @@ pub struct KeyEvent { /// /// An additional difference from `logical_key` is that /// this field stores the text representation of any key - /// that has such a representation. For example when + /// that has such a representation. For example when /// `logical_key` is `Key::Enter`, this field is `Some("\r")`. - /// + /// /// This is `None` if the current keypress cannot /// be interpreted as text. - /// + /// /// See also: `text_with_all_modifiers()` pub text: Option<&'static str>, - + pub location: keyboard::KeyLocation, pub state: ElementState, pub repeat: bool, diff --git a/src/keyboard.rs b/src/keyboard.rs index 771be724ae..71b07aa136 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,6 +1,3 @@ - - - impl ModifiersState { /// Returns `true` if the shift key is pressed. pub fn shift(&self) -> bool { @@ -98,8 +95,6 @@ mod modifiers_serde { } } - - /// Contains the platform-native physical key identifier (aka scancode) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -526,14 +521,14 @@ pub enum KeyCode { /// Specification: /// https://w3c.github.io/uievents-key/ #[non_exhaustive] -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Key<'a> { /// A key string that corresponds to the character typed by the user, /// taking into account the user’s current locale setting, modifier state, /// and any system-level keyboard mapping overrides that are in effect. Character(&'a str), - + /// This variant is used when the key cannot be translated to any /// other variant. /// @@ -544,7 +539,7 @@ pub enum Key<'a> { /// Contains the text representation of the dead-key /// when available. - /// + /// /// ## Platform-specific /// - **Web:** Always contains `None` Dead(Option), diff --git a/src/platform/modifier_supplement.rs b/src/platform/modifier_supplement.rs index e5fb28a782..b91f56affc 100644 --- a/src/platform/modifier_supplement.rs +++ b/src/platform/modifier_supplement.rs @@ -8,7 +8,7 @@ target_os = "openbsd" ))] -use keyboard_types; +use crate::keyboard::Key; /// Additional methods for the `KeyEvent` which cannot be implemented on all /// platforms. @@ -29,7 +29,7 @@ pub trait KeyEventExtModifierSupplement { /// It's important that this behaviour might be different on /// other platforms. For example Linux systems may emit a /// `Some("'")` on the second keypress. - fn char_with_all_modifers(&self) -> &Option; + fn char_with_all_modifers(&self) -> Option<&str>; /// This value ignores all modifiers including /// but not limited to Shift, Caps Lock, @@ -42,5 +42,5 @@ pub trait KeyEventExtModifierSupplement { /// real key according to the current keyboard layout. This value /// cannot be `Dead`. Furthermore the `Character` variant will always /// contain a single-character String. - fn key_without_modifers(&self) -> &keyboard_types::Key; + fn key_without_modifers(&self) -> Key<'static>; } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index d46a2a31ed..4de5631d87 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -11,6 +11,7 @@ use crate::{ dpi::PhysicalSize, event::{DeviceId, KeyEvent}, event_loop::EventLoop, + keyboard::Key, monitor::MonitorHandle, platform::modifier_supplement::KeyEventExtModifierSupplement, platform_impl::{EventLoop as WindowsEventLoop, WinIcon}, @@ -237,11 +238,13 @@ impl IconExtWindows for Icon { } impl KeyEventExtModifierSupplement for KeyEvent { - fn char_with_all_modifers(&self) -> &Option { - &self.platform_specific.char_with_all_modifers + #[inline] + fn char_with_all_modifers(&self) -> Option<&str> { + self.platform_specific.char_with_all_modifers } - fn key_without_modifers(&self) -> &keyboard_types::Key { - &self.platform_specific.key_without_modifers + #[inline] + fn key_without_modifers(&self) -> Key<'static> { + self.platform_specific.key_without_modifers } } diff --git a/src/platform_impl/windows/event.rs b/src/platform_impl/windows/event.rs index 3eafbbc82c..a3434e673e 100644 --- a/src/platform_impl/windows/event.rs +++ b/src/platform_impl/windows/event.rs @@ -5,7 +5,7 @@ use std::{ sync::atomic::{AtomicBool, AtomicPtr, Ordering}, }; -use crate::keyboard::{ModifiersState, Key}; +use crate::keyboard::{Key, ModifiersState}; use winapi::{ shared::minwindef::{HKL, HKL__}, diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 9a9797fec2..e680dcc61e 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -34,6 +34,7 @@ use crate::{ dpi::{PhysicalPosition, PhysicalSize}, event::{DeviceEvent, Event, Force, RawKeyEvent, Touch, TouchPhase, WindowEvent}, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, + keyboard::ModifiersState, monitor::MonitorHandle as RootMonitorHandle, platform_impl::platform::{ dark_mode::try_theme, @@ -1532,10 +1533,7 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_KILLFOCUS => { - use crate::event::{ - ModifiersState, - WindowEvent::{Focused, ModifiersChanged}, - }; + use crate::event::WindowEvent::{Focused, ModifiersChanged}; subclass_input.window_state.lock().modifiers_state = ModifiersState::empty(); subclass_input.send_event(Event::WindowEvent { @@ -1951,6 +1949,7 @@ unsafe extern "system" fn thread_event_target_callback( winuser::WM_INPUT => { use crate::event::{ DeviceEvent::{Button, Key, Motion, MouseMotion, MouseWheel}, + ElementState::{Pressed, Released}, MouseScrollDelta::LineDelta, }; @@ -2019,11 +2018,7 @@ unsafe extern "system" fn thread_event_target_callback( || keyboard.Message == winuser::WM_SYSKEYUP; if pressed || released { - let state = if pressed { - keyboard_types::KeyState::Down - } else { - keyboard_types::KeyState::Up - }; + let state = if pressed { Pressed } else { Released }; let extension = { if util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _) { 0xE000 @@ -2034,12 +2029,10 @@ unsafe extern "system" fn thread_event_target_callback( } }; let scancode = keyboard.MakeCode | extension; - let platform_scancode = PlatformScanCode(scancode); - let code = native_key_to_code(platform_scancode); + let code = native_key_to_code(scancode); subclass_input.send_event(Event::DeviceEvent { device_id, event: Key(RawKeyEvent { - scancode: ScanCode(platform_scancode), physical_key: code, state, }), diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 1a4b07a380..8eb73b6f1e 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -1,4 +1,7 @@ -use std::{char, collections::HashMap, ffi::OsString, fmt, mem::MaybeUninit, os::raw::c_int, os::windows::ffi::OsStringExt, sync::MutexGuard}; +use std::{ + char, collections::HashSet, ffi::OsString, mem::MaybeUninit, os::raw::c_int, + os::windows::ffi::OsStringExt, sync::MutexGuard, +}; use winapi::{ shared::{ @@ -12,11 +15,11 @@ use winapi::{ }; use crate::{ - event::{KeyEvent, ElementState}, - keyboard::{Key, KeyCode, NativeKeyCode, KeyLocation}, + event::{ElementState, KeyEvent}, + keyboard::{Key, KeyCode, KeyLocation, NativeKeyCode}, platform_impl::platform::{ - keyboard_layout::{LAYOUT_CACHE, WindowsModifiers, Layout, LayoutCache}, - event::KeyEventExtra + event::KeyEventExtra, + keyboard_layout::{get_or_insert_str, LayoutCache, WindowsModifiers, LAYOUT_CACHE}, }, }; @@ -129,8 +132,7 @@ impl KeyEventBuilder { self.prev_down_was_dead = false; let lparam_struct = destructure_key_lparam(lparam); - let scancode = - new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); + let scancode = new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); let code = native_key_to_code(scancode); let vkey = unsafe { winuser::MapVirtualKeyW(scancode as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 @@ -139,12 +141,16 @@ impl KeyEventBuilder { let kbd_state = unsafe { Self::get_kbd_state() }; let mods = WindowsModifiers::active_modifiers(&kbd_state); - let layouts = LAYOUT_CACHE.lock().unwrap(); - let (locale_id, layout) = layouts.get_current_layout(); - let logical_key = layout.get_key(mods, scancode, code); - let key_without_modifers = layout.get_key( - WindowsModifiers::empty(), scancode, code - ); + let mods_without_ctrl = { + let mut mods_copy = mods; + mods_copy.remove(WindowsModifiers::CONTROL); + mods_copy + }; + let mut layouts = LAYOUT_CACHE.lock().unwrap(); + let (_, layout) = layouts.get_current_layout(); + let logical_key = layout.get_key(mods_without_ctrl, scancode, code); + let key_without_modifers = + layout.get_key(WindowsModifiers::empty(), scancode, code); let mut event_info = Some(PartialKeyEventInfo { key_state: ElementState::Pressed, logical_key, @@ -180,7 +186,7 @@ impl KeyEventBuilder { } } if let Some(event_info) = event_info { - let ev = event_info.finalize(); + let ev = event_info.finalize(&mut layouts.strings); return vec![MessageAsKeyEvent { event: ev, is_synthetic: false, @@ -191,9 +197,10 @@ impl KeyEventBuilder { self.prev_down_was_dead = true; // At this point we know that there isn't going to be any more events related to // this key press - let mut event_info = self.event_info.take().unwrap(); + let event_info = self.event_info.take().unwrap(); *retval = Some(0); - let ev = event_info.finalize(); + let mut layouts = LAYOUT_CACHE.lock().unwrap(); + let ev = event_info.finalize(&mut layouts.strings); return vec![MessageAsKeyEvent { event: ev, is_synthetic: false, @@ -254,7 +261,7 @@ impl KeyEventBuilder { // Here it's okay to call `ToUnicode` because at this point the dead key // is already consumed by the character. unsafe { - let kbd_state = Self::get_kbd_state(); + let mut kbd_state = Self::get_kbd_state(); let has_ctrl = kbd_state[winuser::VK_CONTROL as usize] & 0x80 != 0 || kbd_state[winuser::VK_LCONTROL as usize] & 0x80 != 0 @@ -275,7 +282,8 @@ impl KeyEventBuilder { ); } } - let ev = event_info.finalize(); + let mut layouts = LAYOUT_CACHE.lock().unwrap(); + let ev = event_info.finalize(&mut layouts.strings); return vec![MessageAsKeyEvent { event: ev, is_synthetic: false, @@ -285,15 +293,14 @@ impl KeyEventBuilder { winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { *retval = Some(0); let lparam_struct = destructure_key_lparam(lparam); - let scancode = - new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); + let scancode = new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); let code = native_key_to_code(scancode); let vkey = unsafe { winuser::MapVirtualKeyW(scancode as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 }; let location = get_location(vkey, lparam_struct.extended); - let mut utf16parts = Vec::with_capacity(8); - let mut utf16parts_without_ctrl = Vec::with_capacity(8); + //let mut utf16parts = Vec::with_capacity(8); + //let mut utf16parts_without_ctrl = Vec::with_capacity(8); let kbd_state = unsafe { Self::get_kbd_state() }; let mods = WindowsModifiers::active_modifiers(&kbd_state); @@ -303,39 +310,11 @@ impl KeyEventBuilder { mods_copy }; - // Avoid calling `ToUnicode` (which resets dead keys) if either the event - // belongs to the key-down which just produced the dead key or if - // the current key would not otherwise reset the dead key state. - // - // This logic relies on the assuption that keys which don't consume - // dead keys, also do not produce text input. - // if !self.prev_down_was_dead && vkey_consumes_dead_key(wparam as u32) { - // unsafe { - // let unicode_len = winuser::ToUnicode( - // wparam as u32, - // scancode as u32, - // (&mut kbd_state[0]) as *mut _, - // utf16parts.as_mut_ptr(), - // utf16parts.capacity() as i32, - // 0, - // ); - // utf16parts.set_len(unicode_len as usize); - - // get_utf16_without_ctrl( - // wparam as u32, - // scancode, - // &mut kbd_state, - // &mut utf16parts_without_ctrl, - // ); - // } - // } - - let layouts = LAYOUT_CACHE.lock().unwrap(); - let (locale_id, layout) = layouts.get_current_layout(); + let mut layouts = LAYOUT_CACHE.lock().unwrap(); + let (_, layout) = layouts.get_current_layout(); let logical_key = layout.get_key(mods_without_ctrl, scancode, code); - let key_without_modifers = layout.get_key( - WindowsModifiers::empty(), scancode, code - ); + let key_without_modifers = + layout.get_key(WindowsModifiers::empty(), scancode, code); let key_with_all_modifiers = layout.get_key(mods, scancode, code); let event_info = PartialKeyEventInfo { key_state: ElementState::Released, @@ -349,7 +328,7 @@ impl KeyEventBuilder { utf16parts: Vec::new(), utf16parts_without_ctrl: Vec::new(), }; - let mut ev = event_info.finalize(); + let mut ev = event_info.finalize(&mut layouts.strings); ev.text = logical_key.to_text(); ev.platform_specific.char_with_all_modifers = key_with_all_modifiers.to_text(); return vec![MessageAsKeyEvent { @@ -363,16 +342,11 @@ impl KeyEventBuilder { Vec::new() } - fn synthesize_kbd_state( - &mut self, - key_state: ElementState, - ) -> Vec { + fn synthesize_kbd_state(&mut self, key_state: ElementState) -> Vec { let mut key_events = Vec::new(); - - let locale_id = { - let mut layout_cache = LAYOUT_CACHE.lock().unwrap(); - layout_cache.get_current_layout().0 - }; + + let mut layouts = LAYOUT_CACHE.lock().unwrap(); + let (locale_id, _) = layouts.get_current_layout(); let kbd_state = unsafe { Self::get_kbd_state() }; macro_rules! is_key_pressed { @@ -397,13 +371,18 @@ impl KeyEventBuilder { // caps-lock first, and always use the current caps-lock state // to determine the produced text if is_key_pressed!(winuser::VK_CAPITAL) { - let event = - self.create_synthetic(winuser::VK_CAPITAL, key_state, locale_id, caps_lock_on); + let event = self.create_synthetic( + winuser::VK_CAPITAL, + key_state, + caps_lock_on, + locale_id as HKL, + &mut layouts, + ); if let Some(event) = event { key_events.push(event); } } - let do_non_modifier = |key_events: &mut Vec<_>| { + let do_non_modifier = |key_events: &mut Vec<_>, layouts: &mut _| { for vk in 0..256 { match vk { winuser::VK_CONTROL @@ -421,12 +400,14 @@ impl KeyEventBuilder { if !is_key_pressed!(vk) { continue; } - if let Some(event) = self.create_synthetic(vk, key_state, locale_id, caps_lock_on) { + let event = + self.create_synthetic(vk, key_state, caps_lock_on, locale_id as HKL, layouts); + if let Some(event) = event { key_events.push(event); } } }; - let do_modifier = |key_events: &mut Vec<_>| { + let do_modifier = |key_events: &mut Vec<_>, layouts: &mut _| { const CLEAR_MODIFIER_VKS: [i32; 6] = [ winuser::VK_LCONTROL, winuser::VK_LSHIFT, @@ -437,7 +418,13 @@ impl KeyEventBuilder { ]; for vk in CLEAR_MODIFIER_VKS.iter() { if is_key_pressed!(*vk) { - let event = self.create_synthetic(*vk, key_state, locale_id, caps_lock_on); + let event = self.create_synthetic( + *vk, + key_state, + caps_lock_on, + locale_id as HKL, + layouts, + ); if let Some(event) = event { key_events.push(event); } @@ -450,12 +437,12 @@ impl KeyEventBuilder { // by modifiers (except for caps-lock) match key_state { ElementState::Pressed => { - do_non_modifier(&mut key_events); - do_modifier(&mut key_events); + do_non_modifier(&mut key_events, &mut layouts); + do_modifier(&mut key_events, &mut layouts); } ElementState::Released => { - do_modifier(&mut key_events); - do_non_modifier(&mut key_events); + do_modifier(&mut key_events, &mut layouts); + do_non_modifier(&mut key_events, &mut layouts); } } @@ -466,8 +453,9 @@ impl KeyEventBuilder { &self, vk: i32, key_state: ElementState, - locale_id: HKL, caps_lock_on: bool, + locale_id: HKL, + layouts: &mut MutexGuard<'_, LayoutCache>, ) -> Option { let scancode = unsafe { winuser::MapVirtualKeyExW(vk as UINT, winuser::MAPVK_VK_TO_VSC_EX, locale_id) @@ -478,38 +466,34 @@ impl KeyEventBuilder { let scancode = scancode as ExScancode; let is_extended = (scancode & 0xE000) == 0xE000; let code = native_key_to_code(scancode); - let key_text = self.key_text.get(&scancode).cloned(); - let key_text_with_caps = self.key_text_with_caps.get(&scancode).cloned(); - let logical_key = match &key_text { - Some(str) => { - if caps_lock_on { - match key_text_with_caps.clone() { - Some(str) => keyboard_types::Key::Character(str), - None => keyboard_types::Key::Unidentified(native_code), - } - } else { - keyboard_types::Key::Character(str.clone()) - } - } - None => vkey_to_non_printable(vk, code, locale_id as usize, self.has_alt_graph), + let mods = if caps_lock_on { + WindowsModifiers::CAPS_LOCK + } else { + WindowsModifiers::empty() }; - + let logical_key; + let key_without_modifers; + { + let layout = layouts.layouts.get(&(locale_id as u64)).unwrap(); + logical_key = layout.get_key(mods, scancode, code); + key_without_modifers = layout.get_key(WindowsModifiers::empty(), scancode, code); + } let event_info = PartialKeyEventInfo { + logical_key, + key_without_modifers, key_state, vkey: vk, - scancode: platform_scancode, + scancode, is_repeat: false, code, location: get_location(vk, is_extended), - is_dead: false, - label: key_text, utf16parts: Vec::with_capacity(8), utf16parts_without_ctrl: Vec::with_capacity(8), }; - let mut event = event_info.finalize(locale_id as usize, self.has_alt_graph); + let mut event = event_info.finalize(&mut layouts.strings); event.logical_key = logical_key; - event.platform_specific.char_with_all_modifers = key_text_with_caps; + event.platform_specific.char_with_all_modifers = logical_key.to_text(); Some(MessageAsKeyEvent { event, is_synthetic: true, @@ -543,12 +527,12 @@ struct PartialKeyEventInfo { } impl PartialKeyEventInfo { - fn finalize(self, mut layout_cache: MutexGuard<'_, LayoutCache>) -> KeyEvent { + fn finalize(self, strings: &mut HashSet<&'static str>) -> KeyEvent { let mut char_with_all_modifiers = None; if !self.utf16parts.is_empty() { let os_string = OsString::from_wide(&self.utf16parts); if let Ok(string) = os_string.into_string() { - let static_str = layout_cache.get_or_insert_str(string); + let static_str = get_or_insert_str(strings, string); char_with_all_modifiers = Some(static_str); } } @@ -634,45 +618,6 @@ unsafe fn get_utf16_without_ctrl( } } -// TODO: This list might not be complete -fn vkey_consumes_dead_key(vkey: u32) -> bool { - const A: u32 = 'A' as u32; - const Z: u32 = 'Z' as u32; - const ZERO: u32 = '0' as u32; - const NINE: u32 = '9' as u32; - match vkey { - A..=Z | ZERO..=NINE => return true, - _ => (), - } - match vkey as i32 { - // OEM keys - winuser::VK_OEM_1 - | winuser::VK_OEM_2 - | winuser::VK_OEM_3 - | winuser::VK_OEM_4 - | winuser::VK_OEM_5 - | winuser::VK_OEM_6 - | winuser::VK_OEM_7 - | winuser::VK_OEM_8 - | winuser::VK_OEM_PLUS - | winuser::VK_OEM_COMMA - | winuser::VK_OEM_MINUS - | winuser::VK_OEM_PERIOD => true, - // Other keys - winuser::VK_TAB - | winuser::VK_BACK - | winuser::VK_RETURN - | winuser::VK_SPACE - | winuser::VK_NUMPAD0..=winuser::VK_NUMPAD9 - | winuser::VK_MULTIPLY - | winuser::VK_ADD - | winuser::VK_SUBTRACT - | winuser::VK_DECIMAL - | winuser::VK_DIVIDE => true, - _ => false, - } -} - /// This includes all non-character keys defined within `Key` so for example /// backspace and tab are included. pub fn vkey_to_non_printable( @@ -698,8 +643,8 @@ pub fn vkey_to_non_printable( let is_japanese = primary_lang_id == LANG_JAPANESE; match vkey { - winuser::VK_LBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse - winuser::VK_RBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + winuser::VK_LBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + winuser::VK_RBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse // I don't think this can be represented with a Key winuser::VK_CANCEL => Key::Unidentified(native_code), diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index d2a3df9c7f..0066389a4e 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -1,29 +1,20 @@ - use std::{ - collections::{HashMap, HashSet, hash_map::Entry}, - sync::Mutex, + collections::{hash_map::Entry, HashMap, HashSet}, + ffi::OsString, os::windows::ffi::OsStringExt, + sync::Mutex, }; use lazy_static::lazy_static; - -use winapi::{ - shared::{ - minwindef::{HKL, LOWORD, LPARAM, LRESULT, UINT, WPARAM}, - }, - um::{ - winnt::{LANG_JAPANESE, LANG_KOREAN, PRIMARYLANGID}, - winuser, - }, -}; +use winapi::{shared::minwindef::HKL, um::winuser}; use crate::{ - keyboard::{ModifiersState, Key, KeyCode, NativeKeyCode}, - platform_impl::platform::keyboard::{ExScancode, vkey_to_non_printable, native_key_to_code}, + keyboard::{Key, KeyCode, NativeKeyCode}, + platform_impl::platform::keyboard::{native_key_to_code, vkey_to_non_printable, ExScancode}, }; -lazy_static!{ +lazy_static! { pub static ref LAYOUT_CACHE: Mutex = Mutex::new(LayoutCache::default()); } @@ -42,17 +33,17 @@ impl WindowsModifiers { let shift = key_state[winuser::VK_SHIFT as usize] & 0x80 != 0; let lshift = key_state[winuser::VK_LSHIFT as usize] & 0x80 != 0; let rshift = key_state[winuser::VK_RSHIFT as usize] & 0x80 != 0; - + let control = key_state[winuser::VK_CONTROL as usize] & 0x80 != 0; let lcontrol = key_state[winuser::VK_LCONTROL as usize] & 0x80 != 0; let rcontrol = key_state[winuser::VK_RCONTROL as usize] & 0x80 != 0; - + let alt = key_state[winuser::VK_MENU as usize] & 0x80 != 0; let lalt = key_state[winuser::VK_LMENU as usize] & 0x80 != 0; let ralt = key_state[winuser::VK_RMENU as usize] & 0x80 != 0; - + let caps = key_state[winuser::VK_CAPITAL as usize] & 0x01 != 0; - + let mut result = WindowsModifiers::empty(); if shift || lshift || rshift { result.insert(WindowsModifiers::SHIFT); @@ -68,35 +59,6 @@ impl WindowsModifiers { } result } - - pub fn apply_to_key_state(self, key_state: &mut [u8; 256]) { - if self.intersects(Self::SHIFT) { - key_state[winuser::VK_SHIFT as usize] |= 0x80; - } else { - key_state[winuser::VK_SHIFT as usize] &= !0x80; - key_state[winuser::VK_LSHIFT as usize] &= !0x80; - key_state[winuser::VK_RSHIFT as usize] &= !0x80; - } - if self.intersects(Self::CONTROL) { - key_state[winuser::VK_CONTROL as usize] |= 0x80; - } else { - key_state[winuser::VK_CONTROL as usize] &= !0x80; - key_state[winuser::VK_LCONTROL as usize] &= !0x80; - key_state[winuser::VK_RCONTROL as usize] &= !0x80; - } - if self.intersects(Self::ALT) { - key_state[winuser::VK_MENU as usize] |= 0x80; - } else { - key_state[winuser::VK_MENU as usize] &= !0x80; - key_state[winuser::VK_LMENU as usize] &= !0x80; - key_state[winuser::VK_RMENU as usize] &= !0x80; - } - if self.intersects(Self::CAPS_LOCK) { - key_state[winuser::VK_CAPITAL as usize] |= 0x80; - } else { - key_state[winuser::VK_CAPITAL as usize] &= !0x80; - } - } } pub struct Layout { @@ -115,7 +77,12 @@ pub struct Layout { } impl Layout { - pub fn get_key(&self, mods: WindowsModifiers, scancode: ExScancode, keycode: KeyCode) -> Key<'static> { + pub fn get_key( + &self, + mods: WindowsModifiers, + scancode: ExScancode, + keycode: KeyCode, + ) -> Key<'static> { if let Some(keys) = self.keys.get(&mods) { if let Some(key) = keys.get(&keycode) { return *key; @@ -133,30 +100,21 @@ pub struct LayoutCache { } impl LayoutCache { - const SHIFT_FLAG: u8 = 1 << 0; - const CONTROL_FLAG: u8 = 1 << 1; - const ALT_FLAG: u8 = 1 << 2; - const CAPS_LOCK_FLAG: u8 = 1 << 3; - const MOD_FLAGS_END: u8 = 1 << 4; - /// Checks whether the current layout is already known and /// prepares the layout if it isn't known. /// The current layout is then returned. - pub fn get_current_layout(&mut self) -> (u64, &Layout) { + pub fn get_current_layout<'a>(&'a mut self) -> (u64, &'a Layout) { let locale_id = unsafe { winuser::GetKeyboardLayout(0) } as u64; match self.layouts.entry(locale_id) { - Entry::Occupied(entry) => { - (locale_id, entry.get()) - } + Entry::Occupied(entry) => (locale_id, entry.into_mut()), Entry::Vacant(entry) => { - let layout = self.prepare_layout(locale_id); + let layout = Self::prepare_layout(&mut self.strings, locale_id); (locale_id, entry.insert(layout)) } } } - /// Returns Some if succeeded - fn prepare_layout(&mut self, locale_identifier: u64) -> Layout { + fn prepare_layout(strings: &mut HashSet<&'static str>, locale_identifier: u64) -> Layout { let mut layout = Layout { keys: Default::default(), has_alt_graph: false, @@ -164,7 +122,7 @@ impl LayoutCache { // We initialize the keyboard state with all zeros to // simulate a scenario when no modifier is active. - let mut key_state = [0u8; 256]; + let key_state = [0u8; 256]; // Iterate through every combination of modifiers let mods_end = WindowsModifiers::FLAGS_END.bits; @@ -181,7 +139,11 @@ impl LayoutCache { // giving the key state for the virtual key used for indexing. for vk in 0..256 { let scancode = unsafe { - winuser::MapVirtualKeyExW(vk, winuser::MAPVK_VK_TO_VSC_EX, locale_identifier as HKL) + winuser::MapVirtualKeyExW( + vk, + winuser::MAPVK_VK_TO_VSC_EX, + locale_identifier as HKL, + ) }; if scancode == 0 { continue; @@ -194,7 +156,11 @@ impl LayoutCache { // assume it isn't. Then we'll do a second pass where we set the "AltRight" keys to // "AltGr" in case we find out that there's an AltGraph. let preliminary_key = vkey_to_non_printable( - vk as i32, native_code, key_code, locale_identifier, false + vk as i32, + native_code, + key_code, + locale_identifier, + false, ); match preliminary_key { Key::Unidentified(_) => (), @@ -207,12 +173,10 @@ impl LayoutCache { let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_identifier); let key = match unicode { ToUnicodeResult::Str(str) => { - let static_str = self.get_or_insert_str(str); + let static_str = get_or_insert_str(strings, str); Key::Character(static_str) } - ToUnicodeResult::Dead(dead_char) => { - Key::Dead(dead_char) - } + ToUnicodeResult::Dead(dead_char) => Key::Dead(dead_char), ToUnicodeResult::None => { // Just use the unidentified key, we got earlier preliminary_key @@ -223,9 +187,8 @@ impl LayoutCache { // The logic is that if a key pressed with the CTRL modifier produces // a different result from when it's pressed with CTRL+ALT then the layout // has AltGr. - const CTRL_ALT: WindowsModifiers = - WindowsModifiers::CONTROL | WindowsModifiers::ALT; - let is_in_ctrl_alt = (mod_state & CTRL_ALT) == CTRL_ALT; + let ctrl_alt: WindowsModifiers = WindowsModifiers::CONTROL | WindowsModifiers::ALT; + let is_in_ctrl_alt = (mod_state & ctrl_alt) == ctrl_alt; if !layout.has_alt_graph && is_in_ctrl_alt { // Unwrapping here because if we are in the ctrl+alt modifier state // then the alt modifier state must have come before. @@ -256,18 +219,6 @@ impl LayoutCache { layout } - pub fn get_or_insert_str(&mut self, string: String) -> &'static str { - { - let str_ref = string.as_str(); - if let Some(&existing) = self.strings.get(&str_ref) { - return existing; - } - } - let leaked = Box::leak(Box::from(string)); - self.strings.insert(leaked); - leaked - } - fn to_unicode_string( key_state: &[u8; 256], vkey: u32, @@ -317,18 +268,21 @@ impl LayoutCache { } } +pub fn get_or_insert_str(strings: &mut HashSet<&'static str>, string: String) -> &'static str { + { + let str_ref = string.as_str(); + if let Some(&existing) = strings.get(str_ref) { + return existing; + } + } + let leaked = Box::leak(Box::from(string)); + strings.insert(leaked); + leaked +} + #[derive(Clone, Eq, PartialEq)] enum ToUnicodeResult { Str(String), Dead(Option), None, } - -impl ToUnicodeResult { - fn is_none(&self) -> bool { - match self { - ToUnicodeResult::None => true, - _ => false, - } - } -} diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 4723c439ee..c3979d7060 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -3,7 +3,7 @@ use winapi::{self, shared::windef::HWND}; pub use self::{ - event::{KeyEventExtra}, + event::KeyEventExtra, event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}, icon::WinIcon, monitor::{MonitorHandle, VideoMode}, diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index a281b9ab0b..c2d0523adc 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -1,7 +1,7 @@ use crate::{ dpi::{PhysicalPosition, Size}, - event::ModifiersState, icon::Icon, + keyboard::ModifiersState, platform_impl::platform::{ event_loop, keyboard::KeyEventBuilder, minimal_ime::MinimalIme, util, }, From eedf78e956b48d7d2d82f75e63f2eb07d5a2b6ec Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Fri, 8 Jan 2021 19:03:09 +0100 Subject: [PATCH 24/75] Fix bugs in new keyboard handling --- src/platform_impl/windows/keyboard.rs | 121 ++++++++----------- src/platform_impl/windows/keyboard_layout.rs | 98 ++++++++++++--- 2 files changed, 131 insertions(+), 88 deletions(-) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 8eb73b6f1e..c7d7c0debf 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -141,11 +141,8 @@ impl KeyEventBuilder { let kbd_state = unsafe { Self::get_kbd_state() }; let mods = WindowsModifiers::active_modifiers(&kbd_state); - let mods_without_ctrl = { - let mut mods_copy = mods; - mods_copy.remove(WindowsModifiers::CONTROL); - mods_copy - }; + let mods_without_ctrl = mods.remove_only_ctrl(); + let mut layouts = LAYOUT_CACHE.lock().unwrap(); let (_, layout) = layouts.get_current_layout(); let logical_key = layout.get_key(mods_without_ctrl, scancode, code); @@ -155,13 +152,12 @@ impl KeyEventBuilder { key_state: ElementState::Pressed, logical_key, key_without_modifers, - vkey, scancode, is_repeat: lparam_struct.is_repeat, code, location, utf16parts: Vec::with_capacity(8), - utf16parts_without_ctrl: Vec::with_capacity(8), + text: PartialText::System(Vec::new()), }); let mut next_msg = MaybeUninit::uninit(); @@ -258,31 +254,35 @@ impl KeyEventBuilder { if !more_char_coming { let mut event_info = self.event_info.take().unwrap(); + let mut layouts = LAYOUT_CACHE.lock().unwrap(); // Here it's okay to call `ToUnicode` because at this point the dead key // is already consumed by the character. unsafe { - let mut kbd_state = Self::get_kbd_state(); - - let has_ctrl = kbd_state[winuser::VK_CONTROL as usize] & 0x80 != 0 - || kbd_state[winuser::VK_LCONTROL as usize] & 0x80 != 0 - || kbd_state[winuser::VK_RCONTROL as usize] & 0x80 != 0; + let kbd_state = Self::get_kbd_state(); + let mod_state = WindowsModifiers::active_modifiers(&kbd_state); + + let (_, layout) = layouts.get_current_layout(); + let ctrl_on; + if layout.has_alt_graph { + let alt_on = mod_state.contains(WindowsModifiers::ALT); + ctrl_on = !alt_on && mod_state.contains(WindowsModifiers::CONTROL) + } else { + ctrl_on = mod_state.contains(WindowsModifiers::CONTROL) + } - // If neither of the CTRL keys is pressed, just use the text with all + // If CTRL is not pressed, just use the text with all // modifiers because that already consumed the dead key and otherwise // we would interpret the character incorretly, missing the dead key. - if !has_ctrl { - event_info.utf16parts_without_ctrl = event_info.utf16parts.clone(); + if !ctrl_on { + event_info.text = PartialText::System(event_info.utf16parts.clone()); } else { - // TODO: This should probably be done through the layout cahce - get_utf16_without_ctrl( - event_info.vkey as u32, - event_info.scancode, - &mut kbd_state, - &mut event_info.utf16parts_without_ctrl, - ); + let mod_no_ctrl = mod_state.remove_only_ctrl(); + let scancode = event_info.scancode; + let keycode = event_info.code; + let key = layout.get_key(mod_no_ctrl, scancode, keycode); + event_info.text = PartialText::Text(key.to_text()); } } - let mut layouts = LAYOUT_CACHE.lock().unwrap(); let ev = event_info.finalize(&mut layouts.strings); return vec![MessageAsKeyEvent { event: ev, @@ -304,11 +304,7 @@ impl KeyEventBuilder { let kbd_state = unsafe { Self::get_kbd_state() }; let mods = WindowsModifiers::active_modifiers(&kbd_state); - let mods_without_ctrl = { - let mut mods_copy = mods; - mods_copy.remove(WindowsModifiers::CONTROL); - mods_copy - }; + let mods_without_ctrl = mods.remove_only_ctrl(); let mut layouts = LAYOUT_CACHE.lock().unwrap(); let (_, layout) = layouts.get_current_layout(); @@ -320,13 +316,12 @@ impl KeyEventBuilder { key_state: ElementState::Released, logical_key, key_without_modifers, - vkey, scancode, is_repeat: false, code, location, utf16parts: Vec::new(), - utf16parts_without_ctrl: Vec::new(), + text: PartialText::System(Vec::new()), }; let mut ev = event_info.finalize(&mut layouts.strings); ev.text = logical_key.to_text(); @@ -482,13 +477,12 @@ impl KeyEventBuilder { logical_key, key_without_modifers, key_state, - vkey: vk, scancode, is_repeat: false, code, location: get_location(vk, is_extended), utf16parts: Vec::with_capacity(8), - utf16parts_without_ctrl: Vec::with_capacity(8), + text: PartialText::System(Vec::new()), }; let mut event = event_info.finalize(&mut layouts.strings); @@ -507,10 +501,14 @@ impl KeyEventBuilder { } } +enum PartialText { + // Unicode + System(Vec), + Text(Option<&'static str>), +} + struct PartialKeyEventInfo { key_state: ElementState, - /// The native Virtual Key - vkey: i32, scancode: ExScancode, is_repeat: bool, code: KeyCode, @@ -523,7 +521,7 @@ struct PartialKeyEventInfo { /// This take all modifiers into account. Including CTRL utf16parts: Vec, - utf16parts_without_ctrl: Vec, + text: PartialText, } impl PartialKeyEventInfo { @@ -537,10 +535,27 @@ impl PartialKeyEventInfo { } } + // The text without ctrl + let mut text = None; + match self.text { + PartialText::System(wide) => { + if !wide.is_empty() { + let os_string = OsString::from_wide(&wide); + if let Ok(string) = os_string.into_string() { + let static_str = get_or_insert_str(strings, string); + text = Some(static_str); + } + } + } + PartialText::Text(s) => { + text = s; + } + } + KeyEvent { physical_key: self.code, logical_key: self.logical_key, - text: None, + text, location: self.location, state: self.key_state, repeat: self.is_repeat, @@ -588,36 +603,6 @@ pub fn get_location(vkey: c_int, extended: bool) -> KeyLocation { } } -unsafe fn get_utf16_without_ctrl( - vkey: u32, - scancode: ExScancode, - kbd_state: &mut [u8; 256], - utf16parts_without_ctrl: &mut Vec, -) { - let target_capacity = 8; - let curr_cap = utf16parts_without_ctrl.capacity(); - if curr_cap < target_capacity { - utf16parts_without_ctrl.reserve(target_capacity - curr_cap); - } - // Now remove all CTRL stuff from the keyboard state - kbd_state[winuser::VK_CONTROL as usize] = 0; - kbd_state[winuser::VK_LCONTROL as usize] = 0; - kbd_state[winuser::VK_RCONTROL as usize] = 0; - let unicode_len = winuser::ToUnicode( - vkey, - scancode as u32, - (&mut kbd_state[0]) as *mut _, - utf16parts_without_ctrl.as_mut_ptr(), - utf16parts_without_ctrl.capacity() as i32, - 0, - ); - if unicode_len < 0 { - utf16parts_without_ctrl.set_len(0); - } else { - utf16parts_without_ctrl.set_len(unicode_len as usize); - } -} - /// This includes all non-character keys defined within `Key` so for example /// backspace and tab are included. pub fn vkey_to_non_printable( @@ -944,8 +929,8 @@ pub fn native_key_to_code(scancode: ExScancode) -> KeyCode { 0x001D => KeyCode::ControlLeft, 0xE01D => KeyCode::ControlRight, 0x001C => KeyCode::Enter, - //0xE05B => KeyCode::OSLeft, - //0xE05C => KeyCode::OSRight, + 0xE05B => KeyCode::MetaLeft, + 0xE05C => KeyCode::MetaRight, 0x002A => KeyCode::ShiftLeft, 0x0036 => KeyCode::ShiftRight, 0x0039 => KeyCode::Space, diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 0066389a4e..df61147a32 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -57,8 +57,50 @@ impl WindowsModifiers { if caps { result.insert(WindowsModifiers::CAPS_LOCK); } + + println!("Active modifiers: {:?}", result); + result } + + pub fn apply_to_kbd_state(self, key_state: &mut [u8; 256]) { + if self.intersects(Self::SHIFT) { + key_state[winuser::VK_SHIFT as usize] |= 0x80; + } else { + key_state[winuser::VK_SHIFT as usize] &= !0x80; + key_state[winuser::VK_LSHIFT as usize] &= !0x80; + key_state[winuser::VK_RSHIFT as usize] &= !0x80; + } + if self.intersects(Self::CONTROL) { + key_state[winuser::VK_CONTROL as usize] |= 0x80; + } else { + key_state[winuser::VK_CONTROL as usize] &= !0x80; + key_state[winuser::VK_LCONTROL as usize] &= !0x80; + key_state[winuser::VK_RCONTROL as usize] &= !0x80; + } + if self.intersects(Self::ALT) { + key_state[winuser::VK_MENU as usize] |= 0x80; + } else { + key_state[winuser::VK_MENU as usize] &= !0x80; + key_state[winuser::VK_LMENU as usize] &= !0x80; + key_state[winuser::VK_RMENU as usize] &= !0x80; + } + if self.intersects(Self::CAPS_LOCK) { + key_state[winuser::VK_CAPITAL as usize] |= 0x80; + } else { + key_state[winuser::VK_CAPITAL as usize] &= !0x80; + } + } + + /// Removes the control modifier if the alt modifier is not present. + /// This is useful because on Windows: (Control + Alt) == AltGr + /// but we don't want to interfere with the AltGr state. + pub fn remove_only_ctrl(mut self) -> WindowsModifiers { + if !self.contains(WindowsModifiers::ALT) { + self.remove(WindowsModifiers::CONTROL); + } + self + } } pub struct Layout { @@ -83,6 +125,11 @@ impl Layout { scancode: ExScancode, keycode: KeyCode, ) -> Key<'static> { + // let ctrl_alt: WindowsModifiers = WindowsModifiers::CONTROL | WindowsModifiers::ALT; + // if self.has_alt_graph && mods.contains(ctrl_alt) { + + // } + if let Some(keys) = self.keys.get(&mods) { if let Some(key) = keys.get(&keycode) { return *key; @@ -114,7 +161,7 @@ impl LayoutCache { } } - fn prepare_layout(strings: &mut HashSet<&'static str>, locale_identifier: u64) -> Layout { + fn prepare_layout(strings: &mut HashSet<&'static str>, locale_id: u64) -> Layout { let mut layout = Layout { keys: Default::default(), has_alt_graph: false, @@ -122,7 +169,7 @@ impl LayoutCache { // We initialize the keyboard state with all zeros to // simulate a scenario when no modifier is active. - let key_state = [0u8; 256]; + let mut key_state = [0u8; 256]; // Iterate through every combination of modifiers let mods_end = WindowsModifiers::FLAGS_END.bits; @@ -130,8 +177,7 @@ impl LayoutCache { let mut keys_for_this_mod = HashMap::with_capacity(256); let mod_state = unsafe { WindowsModifiers::from_bits_unchecked(mod_state) }; - - //Self::apply_mod_state(&mut key_state, mod_state); + mod_state.apply_to_kbd_state(&mut key_state); // Virtual key values are in the domain [0, 255]. // This is reinforced by the fact that the keyboard state array has 256 @@ -142,7 +188,7 @@ impl LayoutCache { winuser::MapVirtualKeyExW( vk, winuser::MAPVK_VK_TO_VSC_EX, - locale_identifier as HKL, + locale_id as HKL, ) }; if scancode == 0 { @@ -159,7 +205,7 @@ impl LayoutCache { vk as i32, native_code, key_code, - locale_identifier, + locale_id, false, ); match preliminary_key { @@ -170,37 +216,49 @@ impl LayoutCache { } } - let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_identifier); + let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_id); let key = match unicode { ToUnicodeResult::Str(str) => { let static_str = get_or_insert_str(strings, str); Key::Character(static_str) } - ToUnicodeResult::Dead(dead_char) => Key::Dead(dead_char), + ToUnicodeResult::Dead(dead_char) => { + //println!("{:?} - {:?} produced dead {:?}", key_code, mod_state, dead_char); + Key::Dead(dead_char) + }, ToUnicodeResult::None => { - // Just use the unidentified key, we got earlier - preliminary_key + let has_alt = mod_state.contains(WindowsModifiers::ALT); + let has_ctrl = mod_state.contains(WindowsModifiers::CONTROL); + // HACK: `ToUnicodeEx` seems to fail getting the string for the numpad + // divide key, so we handle that explicitly here + if !has_alt && !has_ctrl && key_code == KeyCode::NumpadDivide { + Key::Character("/") + } else { + // Just use the unidentified key, we got earlier + preliminary_key + } } }; // Check for alt graph. - // The logic is that if a key pressed with the CTRL modifier produces - // a different result from when it's pressed with CTRL+ALT then the layout + // The logic is that if a key pressed with no modifier produces + // a different `Character` from when it's pressed with CTRL+ALT then the layout // has AltGr. let ctrl_alt: WindowsModifiers = WindowsModifiers::CONTROL | WindowsModifiers::ALT; - let is_in_ctrl_alt = (mod_state & ctrl_alt) == ctrl_alt; + let is_in_ctrl_alt = mod_state == ctrl_alt; if !layout.has_alt_graph && is_in_ctrl_alt { // Unwrapping here because if we are in the ctrl+alt modifier state // then the alt modifier state must have come before. - let alt_keys = layout.keys.get(&WindowsModifiers::ALT).unwrap(); - if let Some(key_without_ctrl_alt) = alt_keys.get(&key_code) { - layout.has_alt_graph = key != *key_without_ctrl_alt; + let simple_keys = layout.keys.get(&WindowsModifiers::empty()).unwrap(); + if let Some(Key::Character(key_no_altgr)) = simple_keys.get(&key_code) { + if let Key::Character(key) = key { + layout.has_alt_graph = key != *key_no_altgr; + } } } keys_for_this_mod.insert(key_code, key); } - layout.keys.insert(mod_state, keys_for_this_mod); } @@ -223,7 +281,7 @@ impl LayoutCache { key_state: &[u8; 256], vkey: u32, scancode: u32, - locale_identifier: u64, + locale_id: u64, ) -> ToUnicodeResult { unsafe { let mut label_wide = [0u16; 8]; @@ -234,7 +292,7 @@ impl LayoutCache { (&mut label_wide[0]) as *mut _, label_wide.len() as i32, 0, - locale_identifier as _, + locale_id as HKL, ); if wide_len < 0 { // If it's dead, let's run `ToUnicode` again, to consume the dead-key @@ -245,7 +303,7 @@ impl LayoutCache { (&mut label_wide[0]) as *mut _, label_wide.len() as i32, 0, - locale_identifier as _, + locale_id as HKL, ); if wide_len > 0 { let os_string = OsString::from_wide(&label_wide[0..wide_len as usize]); From 7b67bbd42740f18c103a7bb60539ff071928a6fe Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 9 Jan 2021 15:23:30 +0100 Subject: [PATCH 25/75] Remove obsolete code --- src/platform_impl/windows/event.rs | 156 ------------------- src/platform_impl/windows/event_loop.rs | 57 +++---- src/platform_impl/windows/keyboard.rs | 22 +-- src/platform_impl/windows/keyboard_layout.rs | 45 ++++-- src/platform_impl/windows/mod.rs | 8 +- 5 files changed, 79 insertions(+), 209 deletions(-) diff --git a/src/platform_impl/windows/event.rs b/src/platform_impl/windows/event.rs index a3434e673e..8b13789179 100644 --- a/src/platform_impl/windows/event.rs +++ b/src/platform_impl/windows/event.rs @@ -1,157 +1 @@ -use std::{ - char, - os::raw::c_int, - ptr, - sync::atomic::{AtomicBool, AtomicPtr, Ordering}, -}; -use crate::keyboard::{Key, ModifiersState}; - -use winapi::{ - shared::minwindef::{HKL, HKL__}, - um::winuser, -}; - -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct KeyEventExtra { - pub char_with_all_modifers: Option<&'static str>, - pub key_without_modifers: Key<'static>, -} - -fn key_pressed(vkey: c_int) -> bool { - unsafe { (winuser::GetKeyState(vkey) & (1 << 15)) == (1 << 15) } -} - -pub fn get_key_mods() -> ModifiersState { - let filter_out_altgr = layout_uses_altgr() && key_pressed(winuser::VK_RMENU); - - let mut mods = ModifiersState::empty(); - mods.set(ModifiersState::SHIFT, key_pressed(winuser::VK_SHIFT)); - mods.set( - ModifiersState::CONTROL, - key_pressed(winuser::VK_CONTROL) && !filter_out_altgr, - ); - mods.set( - ModifiersState::ALT, - key_pressed(winuser::VK_MENU) && !filter_out_altgr, - ); - mods.set( - ModifiersState::META, - key_pressed(winuser::VK_LWIN) || key_pressed(winuser::VK_RWIN), - ); - mods -} - -bitflags! { - #[derive(Default)] - pub struct ModifiersStateSide: u32 { - const LSHIFT = 0b010 << 0; - const RSHIFT = 0b001 << 0; - - const LCTRL = 0b010 << 3; - const RCTRL = 0b001 << 3; - - const LALT = 0b010 << 6; - const RALT = 0b001 << 6; - - const LLOGO = 0b010 << 9; - const RLOGO = 0b001 << 9; - } -} - -impl ModifiersStateSide { - pub fn filter_out_altgr(&self) -> ModifiersStateSide { - match layout_uses_altgr() && self.contains(Self::RALT) { - false => *self, - true => *self & !(Self::LCTRL | Self::RCTRL | Self::LALT | Self::RALT), - } - } -} - -impl From for ModifiersState { - fn from(side: ModifiersStateSide) -> Self { - let mut state = ModifiersState::default(); - state.set( - Self::SHIFT, - side.intersects(ModifiersStateSide::LSHIFT | ModifiersStateSide::RSHIFT), - ); - state.set( - Self::CONTROL, - side.intersects(ModifiersStateSide::LCTRL | ModifiersStateSide::RCTRL), - ); - state.set( - Self::ALT, - side.intersects(ModifiersStateSide::LALT | ModifiersStateSide::RALT), - ); - state.set( - Self::META, - side.intersects(ModifiersStateSide::LLOGO | ModifiersStateSide::RLOGO), - ); - state - } -} - -unsafe fn get_char(keyboard_state: &[u8; 256], v_key: u32, hkl: HKL) -> Option { - let mut unicode_bytes = [0u16; 5]; - let len = winuser::ToUnicodeEx( - v_key, - 0, - keyboard_state.as_ptr(), - unicode_bytes.as_mut_ptr(), - unicode_bytes.len() as _, - 0, - hkl, - ); - if len >= 1 { - char::decode_utf16(unicode_bytes.iter().cloned()) - .next() - .and_then(|c| c.ok()) - } else { - None - } -} - -/// Figures out if the keyboard layout has an AltGr key instead of an Alt key. -/// -/// Unfortunately, the Windows API doesn't give a way for us to conveniently figure that out. So, -/// we use a technique blatantly stolen from [the Firefox source code][source]: iterate over every -/// possible virtual key and compare the `char` output when AltGr is pressed vs when it isn't. If -/// pressing AltGr outputs characters that are different from the standard characters, the layout -/// uses AltGr. Otherwise, it doesn't. -/// -/// [source]: https://github.com/mozilla/gecko-dev/blob/265e6721798a455604328ed5262f430cfcc37c2f/widget/windows/KeyboardLayout.cpp#L4356-L4416 -fn layout_uses_altgr() -> bool { - unsafe { - static ACTIVE_LAYOUT: AtomicPtr = AtomicPtr::new(ptr::null_mut()); - static USES_ALTGR: AtomicBool = AtomicBool::new(false); - - let hkl = winuser::GetKeyboardLayout(0); - let old_hkl = ACTIVE_LAYOUT.swap(hkl, Ordering::SeqCst); - - if hkl == old_hkl { - return USES_ALTGR.load(Ordering::SeqCst); - } - - let mut keyboard_state_altgr = [0u8; 256]; - // AltGr is an alias for Ctrl+Alt for... some reason. Whatever it is, those are the keypresses - // we have to emulate to do an AltGr test. - keyboard_state_altgr[winuser::VK_MENU as usize] = 0x80; - keyboard_state_altgr[winuser::VK_CONTROL as usize] = 0x80; - - let keyboard_state_empty = [0u8; 256]; - - for v_key in 0..=255 { - let key_noaltgr = get_char(&keyboard_state_empty, v_key, hkl); - let key_altgr = get_char(&keyboard_state_altgr, v_key, hkl); - if let (Some(noaltgr), Some(altgr)) = (key_noaltgr, key_altgr) { - if noaltgr != altgr { - USES_ALTGR.store(true, Ordering::SeqCst); - return true; - } - } - } - - USES_ALTGR.store(false, Ordering::SeqCst); - false - } -} diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index e680dcc61e..9be7911834 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -40,8 +40,8 @@ use crate::{ dark_mode::try_theme, dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling}, drop_handler::FileDropHandler, - event, keyboard::{is_msg_keyboard_related, native_key_to_code}, + keyboard_layout::LAYOUT_CACHE, minimal_ime::is_msg_ime_related, monitor::{self, MonitorHandle}, raw_input, util, @@ -721,10 +721,15 @@ unsafe fn process_control_flow(runner: &EventLoopRunner) { } /// Emit a `ModifiersChanged` event whenever modifiers have changed. -fn update_modifiers(window: HWND, subclass_input: &SubclassInput) { +/// Returns the current modifier state +fn update_modifiers(window: HWND, subclass_input: &SubclassInput) -> ModifiersState { use crate::event::WindowEvent::ModifiersChanged; - let modifiers = event::get_key_mods(); + let modifiers = { + let mut layouts = LAYOUT_CACHE.lock().unwrap(); + layouts.get_agnostic_mods() + }; + let mut window_state = subclass_input.window_state.lock(); if window_state.modifiers_state != modifiers { window_state.modifiers_state = modifiers; @@ -739,6 +744,7 @@ fn update_modifiers(window: HWND, subclass_input: &SubclassInput) { }); } } + modifiers } /// Any window whose callback is configured to this function will have its events propagated @@ -1073,14 +1079,13 @@ unsafe extern "system" fn public_window_callback( w.mouse.last_position = Some(position); } if cursor_moved { - update_modifiers(window, subclass_input); - + let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: CursorMoved { device_id: DEVICE_ID, position, - modifiers: event::get_key_mods(), + modifiers, }, }); } @@ -1114,7 +1119,7 @@ unsafe extern "system" fn public_window_callback( let value = value as i32; let value = value as f32 / winuser::WHEEL_DELTA as f32; - update_modifiers(window, subclass_input); + let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -1122,7 +1127,7 @@ unsafe extern "system" fn public_window_callback( device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved, - modifiers: event::get_key_mods(), + modifiers, }, }); @@ -1136,7 +1141,7 @@ unsafe extern "system" fn public_window_callback( let value = value as i32; let value = value as f32 / winuser::WHEEL_DELTA as f32; - update_modifiers(window, subclass_input); + let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -1144,7 +1149,7 @@ unsafe extern "system" fn public_window_callback( device_id: DEVICE_ID, delta: LineDelta(value, 0.0), phase: TouchPhase::Moved, - modifiers: event::get_key_mods(), + modifiers, }, }); @@ -1168,7 +1173,7 @@ unsafe extern "system" fn public_window_callback( capture_mouse(window, &mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); + let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -1176,7 +1181,7 @@ unsafe extern "system" fn public_window_callback( device_id: DEVICE_ID, state: Pressed, button: Left, - modifiers: event::get_key_mods(), + modifiers, }, }); 0 @@ -1189,7 +1194,7 @@ unsafe extern "system" fn public_window_callback( release_mouse(&mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); + let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -1197,7 +1202,7 @@ unsafe extern "system" fn public_window_callback( device_id: DEVICE_ID, state: Released, button: Left, - modifiers: event::get_key_mods(), + modifiers, }, }); 0 @@ -1210,7 +1215,7 @@ unsafe extern "system" fn public_window_callback( capture_mouse(window, &mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); + let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -1218,7 +1223,7 @@ unsafe extern "system" fn public_window_callback( device_id: DEVICE_ID, state: Pressed, button: Right, - modifiers: event::get_key_mods(), + modifiers, }, }); 0 @@ -1231,7 +1236,7 @@ unsafe extern "system" fn public_window_callback( release_mouse(&mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); + let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -1239,7 +1244,7 @@ unsafe extern "system" fn public_window_callback( device_id: DEVICE_ID, state: Released, button: Right, - modifiers: event::get_key_mods(), + modifiers, }, }); 0 @@ -1252,7 +1257,7 @@ unsafe extern "system" fn public_window_callback( capture_mouse(window, &mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); + let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -1260,7 +1265,7 @@ unsafe extern "system" fn public_window_callback( device_id: DEVICE_ID, state: Pressed, button: Middle, - modifiers: event::get_key_mods(), + modifiers, }, }); 0 @@ -1273,7 +1278,7 @@ unsafe extern "system" fn public_window_callback( release_mouse(&mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); + let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -1281,7 +1286,7 @@ unsafe extern "system" fn public_window_callback( device_id: DEVICE_ID, state: Released, button: Middle, - modifiers: event::get_key_mods(), + modifiers, }, }); 0 @@ -1295,7 +1300,7 @@ unsafe extern "system" fn public_window_callback( capture_mouse(window, &mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); + let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -1303,7 +1308,7 @@ unsafe extern "system" fn public_window_callback( device_id: DEVICE_ID, state: Pressed, button: Other(xbutton as u8), - modifiers: event::get_key_mods(), + modifiers, }, }); 0 @@ -1317,7 +1322,7 @@ unsafe extern "system" fn public_window_callback( release_mouse(&mut *subclass_input.window_state.lock()); - update_modifiers(window, subclass_input); + let modifiers = update_modifiers(window, subclass_input); subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -1325,7 +1330,7 @@ unsafe extern "system" fn public_window_callback( device_id: DEVICE_ID, state: Released, button: Other(xbutton as u8), - modifiers: event::get_key_mods(), + modifiers, }, }); 0 diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index c7d7c0debf..7b46155f05 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -18,8 +18,8 @@ use crate::{ event::{ElementState, KeyEvent}, keyboard::{Key, KeyCode, KeyLocation, NativeKeyCode}, platform_impl::platform::{ - event::KeyEventExtra, keyboard_layout::{get_or_insert_str, LayoutCache, WindowsModifiers, LAYOUT_CACHE}, + KeyEventExtra, }, }; @@ -45,6 +45,12 @@ fn new_ex_scancode(scancode: u8, extended: bool) -> ExScancode { (scancode as u16) | (if extended { 0xE000 } else { 0 }) } +unsafe fn get_kbd_state() -> [u8; 256] { + let mut kbd_state: [MaybeUninit; 256] = [MaybeUninit::uninit(); 256]; + winuser::GetKeyboardState(kbd_state[0].as_mut_ptr()); + std::mem::transmute::<_, [u8; 256]>(kbd_state) +} + pub struct MessageAsKeyEvent { pub event: KeyEvent, pub is_synthetic: bool, @@ -139,7 +145,7 @@ impl KeyEventBuilder { }; let location = get_location(vkey, lparam_struct.extended); - let kbd_state = unsafe { Self::get_kbd_state() }; + let kbd_state = unsafe { get_kbd_state() }; let mods = WindowsModifiers::active_modifiers(&kbd_state); let mods_without_ctrl = mods.remove_only_ctrl(); @@ -258,7 +264,7 @@ impl KeyEventBuilder { // Here it's okay to call `ToUnicode` because at this point the dead key // is already consumed by the character. unsafe { - let kbd_state = Self::get_kbd_state(); + let kbd_state = get_kbd_state(); let mod_state = WindowsModifiers::active_modifiers(&kbd_state); let (_, layout) = layouts.get_current_layout(); @@ -302,7 +308,7 @@ impl KeyEventBuilder { //let mut utf16parts = Vec::with_capacity(8); //let mut utf16parts_without_ctrl = Vec::with_capacity(8); - let kbd_state = unsafe { Self::get_kbd_state() }; + let kbd_state = unsafe { get_kbd_state() }; let mods = WindowsModifiers::active_modifiers(&kbd_state); let mods_without_ctrl = mods.remove_only_ctrl(); @@ -343,7 +349,7 @@ impl KeyEventBuilder { let mut layouts = LAYOUT_CACHE.lock().unwrap(); let (locale_id, _) = layouts.get_current_layout(); - let kbd_state = unsafe { Self::get_kbd_state() }; + let kbd_state = unsafe { get_kbd_state() }; macro_rules! is_key_pressed { ($vk:expr) => { kbd_state[$vk as usize] & 0x80 != 0 @@ -493,12 +499,6 @@ impl KeyEventBuilder { is_synthetic: true, }) } - - unsafe fn get_kbd_state() -> [u8; 256] { - let mut kbd_state: [MaybeUninit; 256] = [MaybeUninit::uninit(); 256]; - winuser::GetKeyboardState(kbd_state[0].as_mut_ptr()); - std::mem::transmute::<_, [u8; 256]>(kbd_state) - } } enum PartialText { diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index df61147a32..d1f2520d46 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -7,10 +7,10 @@ use std::{ use lazy_static::lazy_static; -use winapi::{shared::minwindef::HKL, um::winuser}; +use winapi::{ctypes::c_int, shared::minwindef::HKL, um::winuser}; use crate::{ - keyboard::{Key, KeyCode, NativeKeyCode}, + keyboard::{Key, KeyCode, ModifiersState, NativeKeyCode}, platform_impl::platform::keyboard::{native_key_to_code, vkey_to_non_printable, ExScancode}, }; @@ -18,6 +18,10 @@ lazy_static! { pub static ref LAYOUT_CACHE: Mutex = Mutex::new(LayoutCache::default()); } +fn key_pressed(vkey: c_int) -> bool { + unsafe { (winuser::GetKeyState(vkey) & (1 << 15)) == (1 << 15) } +} + bitflags! { pub struct WindowsModifiers : u8 { const SHIFT = 1 << 0; @@ -161,6 +165,26 @@ impl LayoutCache { } } + pub fn get_agnostic_mods(&mut self) -> ModifiersState { + let (_, layout) = self.get_current_layout(); + let filter_out_altgr = layout.has_alt_graph && key_pressed(winuser::VK_RMENU); + let mut mods = ModifiersState::empty(); + mods.set(ModifiersState::SHIFT, key_pressed(winuser::VK_SHIFT)); + mods.set( + ModifiersState::CONTROL, + key_pressed(winuser::VK_CONTROL) && !filter_out_altgr, + ); + mods.set( + ModifiersState::ALT, + key_pressed(winuser::VK_MENU) && !filter_out_altgr, + ); + mods.set( + ModifiersState::META, + key_pressed(winuser::VK_LWIN) || key_pressed(winuser::VK_RWIN), + ); + mods + } + fn prepare_layout(strings: &mut HashSet<&'static str>, locale_id: u64) -> Layout { let mut layout = Layout { keys: Default::default(), @@ -185,11 +209,7 @@ impl LayoutCache { // giving the key state for the virtual key used for indexing. for vk in 0..256 { let scancode = unsafe { - winuser::MapVirtualKeyExW( - vk, - winuser::MAPVK_VK_TO_VSC_EX, - locale_id as HKL, - ) + winuser::MapVirtualKeyExW(vk, winuser::MAPVK_VK_TO_VSC_EX, locale_id as HKL) }; if scancode == 0 { continue; @@ -201,13 +221,8 @@ impl LayoutCache { // We don't necessarily know yet if AltGraph is present on this layout so we'll // assume it isn't. Then we'll do a second pass where we set the "AltRight" keys to // "AltGr" in case we find out that there's an AltGraph. - let preliminary_key = vkey_to_non_printable( - vk as i32, - native_code, - key_code, - locale_id, - false, - ); + let preliminary_key = + vkey_to_non_printable(vk as i32, native_code, key_code, locale_id, false); match preliminary_key { Key::Unidentified(_) => (), _ => { @@ -225,7 +240,7 @@ impl LayoutCache { ToUnicodeResult::Dead(dead_char) => { //println!("{:?} - {:?} produced dead {:?}", key_code, mod_state, dead_char); Key::Dead(dead_char) - }, + } ToUnicodeResult::None => { let has_alt = mod_state.contains(WindowsModifiers::ALT); let has_ctrl = mod_state.contains(WindowsModifiers::CONTROL); diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index c3979d7060..6d655bc686 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -3,7 +3,6 @@ use winapi::{self, shared::windef::HWND}; pub use self::{ - event::KeyEventExtra, event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}, icon::WinIcon, monitor::{MonitorHandle, VideoMode}, @@ -14,6 +13,7 @@ pub use self::icon::WinIcon as PlatformIcon; use crate::event::DeviceId as RootDeviceId; use crate::icon::Icon; +use crate::keyboard::Key; use crate::window::Theme; #[derive(Clone)] @@ -74,6 +74,12 @@ fn wrap_device_id(id: u32) -> RootDeviceId { pub type OsError = std::io::Error; +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct KeyEventExtra { + pub char_with_all_modifers: Option<&'static str>, + pub key_without_modifers: Key<'static>, +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct WindowId(HWND); unsafe impl Send for WindowId {} From 86f0da80ad6fba4e256fa14c39a2463e314efdee Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 9 Jan 2021 16:20:17 +0100 Subject: [PATCH 26/75] Fix examples --- examples/control_flow.rs | 43 ++++++++++++++++++----------------- examples/cursor.rs | 4 ++-- examples/cursor_grab.rs | 14 +++++++----- examples/fullscreen.rs | 46 +++++++++++++++++++------------------- examples/handling_close.rs | 43 ++++++++++++++++++----------------- examples/key_binding.rs | 20 ++++++++--------- examples/minimize.rs | 12 +++++----- examples/multithreaded.rs | 13 ++++++----- examples/multiwindow.rs | 4 ++-- examples/resizable.rs | 8 +++---- examples/window_debug.rs | 16 +++++++------ 11 files changed, 116 insertions(+), 107 deletions(-) diff --git a/examples/control_flow.rs b/examples/control_flow.rs index 1007f6eab7..9fdab39c45 100644 --- a/examples/control_flow.rs +++ b/examples/control_flow.rs @@ -2,9 +2,10 @@ use std::{thread, time}; use simple_logger::SimpleLogger; use winit::{ - event::{Event, KeyEvent, WindowEvent}, + event::{Event, KeyEvent, WindowEvent, ElementState}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, + keyboard::Key }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -38,7 +39,7 @@ fn main() { let mut close_requested = false; event_loop.run(move |event, _, control_flow| { - use winit::event::{keyboard_types::Key, StartCause}; + use winit::event::StartCause; println!("{:?}", event); match event { Event::NewEvents(start_cause) => { @@ -55,29 +56,29 @@ fn main() { event: KeyEvent { logical_key: key, - state: keyboard_types::KeyState::Down, + state: ElementState::Pressed, .. }, .. } => match key { - Key::Character(string) => match string.to_lowercase().as_str() { - "1" => { - mode = Mode::Wait; - println!("\nmode: {:?}\n", mode); - } - "2" => { - mode = Mode::WaitUntil; - println!("\nmode: {:?}\n", mode); - } - "3" => { - mode = Mode::Poll; - println!("\nmode: {:?}\n", mode); - } - "r" => { - request_redraw = !request_redraw; - println!("\nrequest_redraw: {}\n", request_redraw); - } - _ => (), + // WARNING: Consider using `key_without_modifers()` if available on your platform. + // See the `key_binding` example + + Key::Character("1") => { + mode = Mode::Wait; + println!("\nmode: {:?}\n", mode); + } + Key::Character("2") => { + mode = Mode::WaitUntil; + println!("\nmode: {:?}\n", mode); + } + Key::Character("3") => { + mode = Mode::Poll; + println!("\nmode: {:?}\n", mode); + } + Key::Character("r") => { + request_redraw = !request_redraw; + println!("\nrequest_redraw: {}\n", request_redraw); }, Key::Escape => { close_requested = true; diff --git a/examples/cursor.rs b/examples/cursor.rs index 81f16fdcce..943e59b087 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -1,6 +1,6 @@ use simple_logger::SimpleLogger; use winit::{ - event::{keyboard_types::KeyState, Event, KeyEvent, WindowEvent}, + event::{Event, KeyEvent, WindowEvent, ElementState}, event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, WindowBuilder}, }; @@ -23,7 +23,7 @@ fn main() { WindowEvent::KeyboardInput { event: KeyEvent { - state: KeyState::Down, + state: ElementState::Pressed, .. }, .. diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index 10953d6016..b94f921fac 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -1,9 +1,10 @@ use simple_logger::SimpleLogger; use winit::{ event::{ - keyboard_types, DeviceEvent, ElementState, Event, KeyEvent, ModifiersState, WindowEvent, + DeviceEvent, ElementState, Event, KeyEvent, WindowEvent, }, event_loop::{ControlFlow, EventLoop}, + keyboard::{Key, ModifiersState}, window::WindowBuilder, }; @@ -28,19 +29,20 @@ fn main() { event: KeyEvent { logical_key: key, - state: keyboard_types::KeyState::Up, + state: ElementState::Released, .. }, .. } => { - use winit::event::keyboard_types::Key; + // WARNING: Consider using `key_without_modifers()` if available on your platform. + // See the `key_binding` example match key { Key::Escape => *control_flow = ControlFlow::Exit, - Key::Character(string) => match string.to_lowercase().as_str() { + Key::Character(ch) => match ch.to_lowercase().as_str() { "g" => window.set_cursor_grab(!modifiers.shift()).unwrap(), "h" => window.set_cursor_visible(modifiers.shift()), - _ => (), - }, + _ => () + } _ => (), } } diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 6e5acd92cd..c0997fa1d0 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -2,10 +2,10 @@ use std::io::{stdin, stdout, Write}; use simple_logger::SimpleLogger; use winit::event::{ - keyboard_types::{Key, KeyState}, - Event, KeyEvent, WindowEvent, + Event, KeyEvent, WindowEvent, ElementState, }; use winit::event_loop::{ControlFlow, EventLoop}; +use winit::keyboard::Key; use winit::monitor::{MonitorHandle, VideoMode}; use winit::window::{Fullscreen, WindowBuilder}; @@ -45,33 +45,33 @@ fn main() { event: KeyEvent { logical_key: key, - state: KeyState::Down, + state: ElementState::Pressed, .. }, .. } => match key { Key::Escape => *control_flow = ControlFlow::Exit, - Key::Character(string) => match string.to_lowercase().as_str() { - "f" => { - if window.fullscreen().is_some() { - window.set_fullscreen(None); - } else { - window.set_fullscreen(fullscreen.clone()); - } - } - "s" => { - println!("window.fullscreen {:?}", window.fullscreen()); - } - "m" => { - is_maximized = !is_maximized; - window.set_maximized(is_maximized); - } - "d" => { - decorations = !decorations; - window.set_decorations(decorations); + + // WARNING: Consider using `key_without_modifers()` if available on your platform. + // See the `key_binding` example + Key::Character("f") => { + if window.fullscreen().is_some() { + window.set_fullscreen(None); + } else { + window.set_fullscreen(fullscreen.clone()); } - _ => (), - }, + } + Key::Character("s") => { + println!("window.fullscreen {:?}", window.fullscreen()); + } + Key::Character("m") => { + is_maximized = !is_maximized; + window.set_maximized(is_maximized); + } + Key::Character("d") => { + decorations = !decorations; + window.set_decorations(decorations); + } _ => (), }, _ => (), diff --git a/examples/handling_close.rs b/examples/handling_close.rs index 3f4ce00292..66b966ec6d 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -1,7 +1,8 @@ use simple_logger::SimpleLogger; use winit::{ - event::{keyboard_types, Event, KeyEvent, WindowEvent}, + event::{Event, KeyEvent, WindowEvent, ElementState}, event_loop::{ControlFlow, EventLoop}, + keyboard::Key, window::WindowBuilder, }; @@ -43,34 +44,34 @@ fn main() { event: KeyEvent { logical_key: key, - state: keyboard_types::KeyState::Up, + state: ElementState::Released, .. }, .. } => { - if let keyboard_types::Key::Character(string) = key { - match string.to_lowercase().as_str() { - "y" => { - if close_requested { - // This is where you'll want to do any cleanup you need. - println!("Buh-bye!"); + // WARNING: Consider using `key_without_modifers()` if available on your platform. + // See the `key_binding` example + match key { + Key::Character("y") => { + if close_requested { + // This is where you'll want to do any cleanup you need. + println!("Buh-bye!"); - // For a single-window application like this, you'd normally just - // break out of the event loop here. If you wanted to keep running the - // event loop (i.e. if it's a multi-window application), you need to - // drop the window. That closes it, and results in `Destroyed` being - // sent. - *control_flow = ControlFlow::Exit; - } + // For a single-window application like this, you'd normally just + // break out of the event loop here. If you wanted to keep running the + // event loop (i.e. if it's a multi-window application), you need to + // drop the window. That closes it, and results in `Destroyed` being + // sent. + *control_flow = ControlFlow::Exit; } - "n" => { - if close_requested { - println!("Your window will continue to stay by your side."); - close_requested = false; - } + } + Key::Character("n") => { + if close_requested { + println!("Your window will continue to stay by your side."); + close_requested = false; } - _ => (), } + _ => (), } } _ => (), diff --git a/examples/key_binding.rs b/examples/key_binding.rs index a344853a5a..3adf5789dc 100644 --- a/examples/key_binding.rs +++ b/examples/key_binding.rs @@ -1,8 +1,9 @@ use simple_logger::SimpleLogger; use winit::{ dpi::LogicalSize, - event::{keyboard_types::KeyState, Event, KeyEvent, ModifiersState, WindowEvent}, + event::{Event, KeyEvent, WindowEvent, ElementState}, event_loop::{ControlFlow, EventLoop}, + keyboard::{Key, ModifiersState}, window::WindowBuilder, }; @@ -42,18 +43,15 @@ fn main() { } fn handle_key_event(modifiers: ModifiersState, event: KeyEvent) { - if event.state == KeyState::Down && !event.repeat { + if event.state == ElementState::Pressed && !event.repeat { match event.key_without_modifers() { - keyboard_types::Key::Character(c) => match c.as_str() { - "1" => { - if modifiers.shift() { - println!("Shift + 1 | logical_key: {:?}", event.logical_key); - } else { - println!("1"); - } + Key::Character("1") => { + if modifiers.shift() { + println!("Shift + 1 | logical_key: {:?}", event.logical_key); + } else { + println!("1"); } - _ => (), - }, + } _ => (), } } diff --git a/examples/minimize.rs b/examples/minimize.rs index b782c6da10..3c8b7f84ed 100644 --- a/examples/minimize.rs +++ b/examples/minimize.rs @@ -1,8 +1,10 @@ extern crate winit; use simple_logger::SimpleLogger; + use winit::event::{Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; +use winit::keyboard::Key; use winit::window::WindowBuilder; fn main() { @@ -29,11 +31,11 @@ fn main() { window_id, } => { if window_id == window.id() { - // Pressing the 'M' key will minimize the window - if let keyboard_types::Key::Character(string) = event.logical_key { - if string.to_lowercase() == "m" { - window.set_minimized(true); - } + // Pressing the 'm' key will minimize the window + // WARNING: Consider using `key_without_modifers()` if available on your platform. + // See the `key_binding` example + if let Key::Character("m") = event.logical_key { + window.set_minimized(true); } } } diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 706e87ed1a..c0c4f946ad 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -5,8 +5,9 @@ fn main() { use simple_logger::SimpleLogger; use winit::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - event::{Event, KeyEvent, ModifiersState, WindowEvent}, + event::{Event, KeyEvent, WindowEvent, ElementState}, event_loop::{ControlFlow, EventLoop}, + keyboard::{Key, ModifiersState}, window::{CursorIcon, Fullscreen, WindowBuilder}, }; @@ -56,16 +57,18 @@ fn main() { WindowEvent::KeyboardInput { event: KeyEvent { - state: keyboard_types::KeyState::Up, + state: ElementState::Released, logical_key: key, .. }, .. } => { - use keyboard_types::Key::{ArrowLeft, ArrowRight, Character}; + use Key::{ArrowLeft, ArrowRight, Character}; window.set_title(&format!("{:?}", key)); let state = !modifiers.shift(); match &key { + // WARNING: Consider using `key_without_modifers()` if available on your platform. + // See the `key_binding` example Character(string) => match string.to_lowercase().as_str() { "a" => window.set_always_on_top(state), "c" => window.set_cursor_icon(match state { @@ -161,8 +164,8 @@ fn main() { | WindowEvent::KeyboardInput { event: KeyEvent { - state: keyboard_types::KeyState::Up, - logical_key: keyboard_types::Key::Escape, + state: ElementState::Released, + logical_key: Key::Escape, .. }, .. diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 24b58370a0..5a33ee2f5c 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use simple_logger::SimpleLogger; use winit::{ - event::{keyboard_types::KeyState, Event, KeyEvent, WindowEvent}, + event::{Event, KeyEvent, WindowEvent, ElementState}, event_loop::{ControlFlow, EventLoop}, window::Window, }; @@ -36,7 +36,7 @@ fn main() { WindowEvent::KeyboardInput { event: KeyEvent { - state: KeyState::Up, + state: ElementState::Released, .. }, .. diff --git a/examples/resizable.rs b/examples/resizable.rs index 2c1a3851aa..26d355662e 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -2,10 +2,10 @@ use simple_logger::SimpleLogger; use winit::{ dpi::LogicalSize, event::{ - keyboard_types::{Code, KeyState}, - Event, KeyEvent, WindowEvent, + Event, KeyEvent, WindowEvent, ElementState }, event_loop::{ControlFlow, EventLoop}, + keyboard::KeyCode, window::WindowBuilder, }; @@ -31,8 +31,8 @@ fn main() { WindowEvent::KeyboardInput { event: KeyEvent { - physical_key: Code::Space, - state: KeyState::Up, + physical_key: KeyCode::Space, + state: ElementState::Released, .. }, .. diff --git a/examples/window_debug.rs b/examples/window_debug.rs index 6e807568d2..fb3a36fcc2 100644 --- a/examples/window_debug.rs +++ b/examples/window_debug.rs @@ -4,10 +4,10 @@ use simple_logger::SimpleLogger; use winit::{ dpi::{LogicalSize, PhysicalSize}, event::{ - keyboard_types::{Code, Key, KeyState}, - DeviceEvent, Event, KeyEvent, RawKeyEvent, WindowEvent, + DeviceEvent, Event, KeyEvent, RawKeyEvent, WindowEvent, ElementState, }, event_loop::{ControlFlow, EventLoop}, + keyboard::{Key, KeyCode}, window::{Fullscreen, WindowBuilder}, }; @@ -44,18 +44,18 @@ fn main() { event: DeviceEvent::Key(RawKeyEvent { physical_key, - state: KeyState::Up, + state: ElementState::Released, .. }), .. } => match physical_key { - Code::KeyM => { + KeyCode::KeyM => { if minimized { minimized = !minimized; window.set_minimized(minimized); } } - Code::KeyV => { + KeyCode::KeyV => { if !visible { visible = !visible; window.set_visible(visible); @@ -69,13 +69,15 @@ fn main() { event: KeyEvent { logical_key: Key::Character(key_str), - state: KeyState::Up, + state: ElementState::Released, .. }, .. }, .. - } => match key_str.to_lowercase().as_str() { + } => match key_str { + // WARNING: Consider using `key_without_modifers()` if available on your platform. + // See the `key_binding` example "e" => { fn area(size: PhysicalSize) -> u32 { size.width * size.height From f4a3ee92dffca28f09cefdc4e007bd8acdf67452 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 9 Jan 2021 17:03:56 +0100 Subject: [PATCH 27/75] Ran `cargo fmt` --- examples/control_flow.rs | 7 +++---- examples/cursor.rs | 2 +- examples/cursor_grab.rs | 8 +++----- examples/fullscreen.rs | 4 +--- examples/handling_close.rs | 2 +- examples/key_binding.rs | 2 +- examples/multithreaded.rs | 2 +- examples/multiwindow.rs | 2 +- examples/resizable.rs | 4 +--- examples/window_debug.rs | 4 +--- 10 files changed, 14 insertions(+), 23 deletions(-) diff --git a/examples/control_flow.rs b/examples/control_flow.rs index 9fdab39c45..6e876a3eaf 100644 --- a/examples/control_flow.rs +++ b/examples/control_flow.rs @@ -2,10 +2,10 @@ use std::{thread, time}; use simple_logger::SimpleLogger; use winit::{ - event::{Event, KeyEvent, WindowEvent, ElementState}, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, + keyboard::Key, window::WindowBuilder, - keyboard::Key }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -63,7 +63,6 @@ fn main() { } => match key { // WARNING: Consider using `key_without_modifers()` if available on your platform. // See the `key_binding` example - Key::Character("1") => { mode = Mode::Wait; println!("\nmode: {:?}\n", mode); @@ -79,7 +78,7 @@ fn main() { Key::Character("r") => { request_redraw = !request_redraw; println!("\nrequest_redraw: {}\n", request_redraw); - }, + } Key::Escape => { close_requested = true; } diff --git a/examples/cursor.rs b/examples/cursor.rs index 943e59b087..2b6fe5b207 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -1,6 +1,6 @@ use simple_logger::SimpleLogger; use winit::{ - event::{Event, KeyEvent, WindowEvent, ElementState}, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, WindowBuilder}, }; diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index b94f921fac..48879dadf4 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -1,8 +1,6 @@ use simple_logger::SimpleLogger; use winit::{ - event::{ - DeviceEvent, ElementState, Event, KeyEvent, WindowEvent, - }, + event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, keyboard::{Key, ModifiersState}, window::WindowBuilder, @@ -41,8 +39,8 @@ fn main() { Key::Character(ch) => match ch.to_lowercase().as_str() { "g" => window.set_cursor_grab(!modifiers.shift()).unwrap(), "h" => window.set_cursor_visible(modifiers.shift()), - _ => () - } + _ => (), + }, _ => (), } } diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index c0997fa1d0..bc61537c98 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -1,9 +1,7 @@ use std::io::{stdin, stdout, Write}; use simple_logger::SimpleLogger; -use winit::event::{ - Event, KeyEvent, WindowEvent, ElementState, -}; +use winit::event::{ElementState, Event, KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::keyboard::Key; use winit::monitor::{MonitorHandle, VideoMode}; diff --git a/examples/handling_close.rs b/examples/handling_close.rs index 66b966ec6d..283cf48195 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -1,6 +1,6 @@ use simple_logger::SimpleLogger; use winit::{ - event::{Event, KeyEvent, WindowEvent, ElementState}, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, keyboard::Key, window::WindowBuilder, diff --git a/examples/key_binding.rs b/examples/key_binding.rs index 3adf5789dc..66ca751aba 100644 --- a/examples/key_binding.rs +++ b/examples/key_binding.rs @@ -1,7 +1,7 @@ use simple_logger::SimpleLogger; use winit::{ dpi::LogicalSize, - event::{Event, KeyEvent, WindowEvent, ElementState}, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, keyboard::{Key, ModifiersState}, window::WindowBuilder, diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index c0c4f946ad..629aff408a 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -5,7 +5,7 @@ fn main() { use simple_logger::SimpleLogger; use winit::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - event::{Event, KeyEvent, WindowEvent, ElementState}, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, keyboard::{Key, ModifiersState}, window::{CursorIcon, Fullscreen, WindowBuilder}, diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index 5a33ee2f5c..924a55296b 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use simple_logger::SimpleLogger; use winit::{ - event::{Event, KeyEvent, WindowEvent, ElementState}, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::Window, }; diff --git a/examples/resizable.rs b/examples/resizable.rs index 26d355662e..8d3387cabb 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -1,9 +1,7 @@ use simple_logger::SimpleLogger; use winit::{ dpi::LogicalSize, - event::{ - Event, KeyEvent, WindowEvent, ElementState - }, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, keyboard::KeyCode, window::WindowBuilder, diff --git a/examples/window_debug.rs b/examples/window_debug.rs index fb3a36fcc2..59a49b935b 100644 --- a/examples/window_debug.rs +++ b/examples/window_debug.rs @@ -3,9 +3,7 @@ use simple_logger::SimpleLogger; use winit::{ dpi::{LogicalSize, PhysicalSize}, - event::{ - DeviceEvent, Event, KeyEvent, RawKeyEvent, WindowEvent, ElementState, - }, + event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, keyboard::{Key, KeyCode}, window::{Fullscreen, WindowBuilder}, From 0d4397742501d4f1d8b04329ac1fed4fd0076880 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 9 Jan 2021 17:15:54 +0100 Subject: [PATCH 28/75] Fix build error with serde --- tests/serde_objects.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/serde_objects.rs b/tests/serde_objects.rs index ad729dcd1b..1941beae8e 100644 --- a/tests/serde_objects.rs +++ b/tests/serde_objects.rs @@ -4,9 +4,9 @@ use serde::{Deserialize, Serialize}; use winit::{ dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}, event::{ - ElementState, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase, - VirtualKeyCode, + ElementState, MouseButton, MouseScrollDelta, TouchPhase, }, + keyboard::{ModifiersState, Key, KeyCode, KeyLocation}, window::CursorIcon, }; @@ -20,12 +20,13 @@ fn window_serde() { #[test] fn events_serde() { - needs_serde::(); needs_serde::(); needs_serde::(); needs_serde::(); needs_serde::(); - needs_serde::(); + needs_serde::>(); + needs_serde::(); + needs_serde::(); needs_serde::(); } From 5d3b7b65a8566e83b47c81d5326882a94102d6a5 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 9 Jan 2021 17:16:39 +0100 Subject: [PATCH 29/75] Ran `cargo fmt` --- tests/serde_objects.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/serde_objects.rs b/tests/serde_objects.rs index 1941beae8e..b0333fa410 100644 --- a/tests/serde_objects.rs +++ b/tests/serde_objects.rs @@ -3,10 +3,8 @@ use serde::{Deserialize, Serialize}; use winit::{ dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}, - event::{ - ElementState, MouseButton, MouseScrollDelta, TouchPhase, - }, - keyboard::{ModifiersState, Key, KeyCode, KeyLocation}, + event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase}, + keyboard::{Key, KeyCode, KeyLocation, ModifiersState}, window::CursorIcon, }; From 1edbb634e7cd1bb4a12e47838da891cd84dc6360 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 10 Jan 2021 10:59:11 +0100 Subject: [PATCH 30/75] Tweaks in the Windows keyboard implementation --- src/platform/modifier_supplement.rs | 24 +--- src/platform/windows.rs | 2 +- src/platform_impl/windows/keyboard.rs | 136 +++++++++++-------- src/platform_impl/windows/keyboard_layout.rs | 12 +- 4 files changed, 90 insertions(+), 84 deletions(-) diff --git a/src/platform/modifier_supplement.rs b/src/platform/modifier_supplement.rs index b91f56affc..629a47ae45 100644 --- a/src/platform/modifier_supplement.rs +++ b/src/platform/modifier_supplement.rs @@ -13,23 +13,10 @@ use crate::keyboard::Key; /// Additional methods for the `KeyEvent` which cannot be implemented on all /// platforms. pub trait KeyEventExtModifierSupplement { - /// This value is affected by all modifiers including but not - /// limited to Shift, Ctrl, and Num Lock. + /// Identical to `KeyEvent::text` but this is affected by Ctrl. /// - /// This is suitable for text input in a terminal application. - /// - /// `None` is returned if the input cannot be translated to a string. - /// For example dead key input as well as F1 and - /// Home among others produce `None`. - /// - /// Note that the resulting string may contain multiple characters. - /// For example on Windows when pressing ' using - /// a US-International layout, this will be `None` for the first - /// keypress and will be `Some("''")` for the second keypress. - /// It's important that this behaviour might be different on - /// other platforms. For example Linux systems may emit a - /// `Some("'")` on the second keypress. - fn char_with_all_modifers(&self) -> Option<&str>; + /// For example, pressing Ctrl+a produces `Some("\x01")`. + fn text_with_all_modifers(&self) -> Option<&str>; /// This value ignores all modifiers including /// but not limited to Shift, Caps Lock, @@ -39,8 +26,7 @@ pub trait KeyEventExtModifierSupplement { /// This is useful for key-bindings / shortcut key combinations. /// /// In case `logical_key` reports `Dead`, this will still report the - /// real key according to the current keyboard layout. This value - /// cannot be `Dead`. Furthermore the `Character` variant will always - /// contain a single-character String. + /// key as `Characcter` according to the current keyboard layout. This value + /// cannot be `Dead`. fn key_without_modifers(&self) -> Key<'static>; } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 4de5631d87..3a0b5b5d95 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -239,7 +239,7 @@ impl IconExtWindows for Icon { impl KeyEventExtModifierSupplement for KeyEvent { #[inline] - fn char_with_all_modifers(&self) -> Option<&str> { + fn text_with_all_modifers(&self) -> Option<&str> { self.platform_specific.char_with_all_modifers } diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 7b46155f05..5b78013340 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -137,34 +137,11 @@ impl KeyEventBuilder { } self.prev_down_was_dead = false; - let lparam_struct = destructure_key_lparam(lparam); - let scancode = new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); - let code = native_key_to_code(scancode); - let vkey = unsafe { - winuser::MapVirtualKeyW(scancode as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 - }; - let location = get_location(vkey, lparam_struct.extended); - - let kbd_state = unsafe { get_kbd_state() }; - let mods = WindowsModifiers::active_modifiers(&kbd_state); - let mods_without_ctrl = mods.remove_only_ctrl(); - let mut layouts = LAYOUT_CACHE.lock().unwrap(); - let (_, layout) = layouts.get_current_layout(); - let logical_key = layout.get_key(mods_without_ctrl, scancode, code); - let key_without_modifers = - layout.get_key(WindowsModifiers::empty(), scancode, code); - let mut event_info = Some(PartialKeyEventInfo { - key_state: ElementState::Pressed, - logical_key, - key_without_modifers, - scancode, - is_repeat: lparam_struct.is_repeat, - code, - location, - utf16parts: Vec::with_capacity(8), - text: PartialText::System(Vec::new()), - }); + let mut event_info = Some( + PartialKeyEventInfo::from_message(lparam, ElementState::Pressed, &mut layouts) + .0, + ); let mut next_msg = MaybeUninit::uninit(); let peek_retval = unsafe { @@ -298,38 +275,15 @@ impl KeyEventBuilder { } winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { *retval = Some(0); - let lparam_struct = destructure_key_lparam(lparam); - let scancode = new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); - let code = native_key_to_code(scancode); - let vkey = unsafe { - winuser::MapVirtualKeyW(scancode as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 - }; - let location = get_location(vkey, lparam_struct.extended); - //let mut utf16parts = Vec::with_capacity(8); - //let mut utf16parts_without_ctrl = Vec::with_capacity(8); - - let kbd_state = unsafe { get_kbd_state() }; - let mods = WindowsModifiers::active_modifiers(&kbd_state); - let mods_without_ctrl = mods.remove_only_ctrl(); let mut layouts = LAYOUT_CACHE.lock().unwrap(); - let (_, layout) = layouts.get_current_layout(); - let logical_key = layout.get_key(mods_without_ctrl, scancode, code); - let key_without_modifers = - layout.get_key(WindowsModifiers::empty(), scancode, code); - let key_with_all_modifiers = layout.get_key(mods, scancode, code); - let event_info = PartialKeyEventInfo { - key_state: ElementState::Released, - logical_key, - key_without_modifers, - scancode, - is_repeat: false, - code, - location, - utf16parts: Vec::new(), - text: PartialText::System(Vec::new()), - }; + let (event_info, aux_info) = + PartialKeyEventInfo::from_message(lparam, ElementState::Released, &mut layouts); + let logical_key = event_info.logical_key; let mut ev = event_info.finalize(&mut layouts.strings); + let (_, layout) = layouts.get_current_layout(); + let key_with_all_modifiers = + layout.get_key(aux_info.modifiers, aux_info.scancode, aux_info.key_code); ev.text = logical_key.to_text(); ev.platform_specific.char_with_all_modifers = key_with_all_modifiers.to_text(); return vec![MessageAsKeyEvent { @@ -481,7 +435,7 @@ impl KeyEventBuilder { } let event_info = PartialKeyEventInfo { logical_key, - key_without_modifers, + key_without_modifiers: key_without_modifers, key_state, scancode, is_repeat: false, @@ -515,7 +469,7 @@ struct PartialKeyEventInfo { location: KeyLocation, logical_key: Key<'static>, - key_without_modifers: Key<'static>, + key_without_modifiers: Key<'static>, /// The utf16 code units of the text that was produced by the keypress event. /// This take all modifiers into account. Including CTRL @@ -524,7 +478,71 @@ struct PartialKeyEventInfo { text: PartialText, } +struct AuxKeyInfo { + scancode: ExScancode, + key_code: KeyCode, + modifiers: WindowsModifiers, +} + impl PartialKeyEventInfo { + fn from_message( + lparam: LPARAM, + state: ElementState, + layouts: &mut MutexGuard<'_, LayoutCache>, + ) -> (Self, AuxKeyInfo) { + const NO_MODS: WindowsModifiers = WindowsModifiers::empty(); + + let lparam_struct = destructure_key_lparam(lparam); + let scancode = new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); + let code = native_key_to_code(scancode); + let vkey = + unsafe { winuser::MapVirtualKeyW(scancode as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 }; + let location = get_location(vkey, lparam_struct.extended); + + let kbd_state = unsafe { get_kbd_state() }; + let mods = WindowsModifiers::active_modifiers(&kbd_state); + let mods_without_ctrl = mods.remove_only_ctrl(); + + let (_, layout) = layouts.get_current_layout(); + let logical_key = layout.get_key(mods_without_ctrl, scancode, code); + let key_without_modifiers = match layout.get_key(NO_MODS, scancode, code) { + // We convert dead keys into their character. + // The reason for this is that `key_without_modifiers` is designed for key-bindings + // but for example the US International treats `'` (apostrophe) as a dead key and + // reguar US keyboard treats it a character. In order for a single binding configuration + // to work with both layouts we forward each dead key as a character. + Key::Dead(k) => { + if let Some(ch) = k { + // I'm avoiding the heap allocation. I don't want to talk about it :( + let mut utf8 = [0; 4]; + let s = ch.encode_utf8(&mut utf8); + let static_str = get_or_insert_str(&mut layouts.strings, s); + Key::Character(static_str) + } else { + Key::Unidentified(NativeKeyCode::Unidentified) + } + } + key => key, + }; + let aux_info = AuxKeyInfo { + scancode, + key_code: code, + modifiers: mods, + }; + let partial_key_event_info = PartialKeyEventInfo { + key_state: state, + logical_key, + key_without_modifiers, + scancode, + is_repeat: lparam_struct.is_repeat, + code, + location, + utf16parts: Vec::with_capacity(8), + text: PartialText::System(Vec::new()), + }; + (partial_key_event_info, aux_info) + } + fn finalize(self, strings: &mut HashSet<&'static str>) -> KeyEvent { let mut char_with_all_modifiers = None; if !self.utf16parts.is_empty() { @@ -561,7 +579,7 @@ impl PartialKeyEventInfo { repeat: self.is_repeat, platform_specific: KeyEventExtra { char_with_all_modifers: char_with_all_modifiers, - key_without_modifers: self.key_without_modifers, + key_without_modifers: self.key_without_modifiers, }, } } diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index d1f2520d46..09beb5bd83 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -62,8 +62,6 @@ impl WindowsModifiers { result.insert(WindowsModifiers::CAPS_LOCK); } - println!("Active modifiers: {:?}", result); - result } @@ -341,14 +339,18 @@ impl LayoutCache { } } -pub fn get_or_insert_str(strings: &mut HashSet<&'static str>, string: String) -> &'static str { +pub fn get_or_insert_str(strings: &mut HashSet<&'static str>, string: T) -> &'static str +where + T: AsRef, + String: From, +{ { - let str_ref = string.as_str(); + let str_ref = string.as_ref(); if let Some(&existing) = strings.get(str_ref) { return existing; } } - let leaked = Box::leak(Box::from(string)); + let leaked = Box::leak(Box::from(String::from(string))); strings.insert(leaked); leaked } From 884e67329d7399e35fc88d5292daec3673858bd9 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 10 Jan 2021 12:24:55 +0100 Subject: [PATCH 31/75] Add `KeyCodeExtScancode` --- src/platform/mod.rs | 1 + src/platform/scancode.rs | 31 ++ src/platform/windows.rs | 349 ++++++++++++++++++- src/platform_impl/windows/event_loop.rs | 7 +- src/platform_impl/windows/keyboard.rs | 171 +-------- src/platform_impl/windows/keyboard_layout.rs | 5 +- 6 files changed, 390 insertions(+), 174 deletions(-) create mode 100644 src/platform/scancode.rs diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 7ef041a025..84f016c2d2 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -23,4 +23,5 @@ pub mod windows; pub mod modifier_supplement; pub mod run_return; +pub mod scancode; pub mod web; diff --git a/src/platform/scancode.rs b/src/platform/scancode.rs new file mode 100644 index 0000000000..7617622b62 --- /dev/null +++ b/src/platform/scancode.rs @@ -0,0 +1,31 @@ +#![cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] + +// TODO: Maybe merge this with `modifier_supplement` if the two are indeed supported on the same +// set of platforms + +use crate::keyboard::KeyCode; + +pub trait KeyCodeExtScancode { + /// The raw value of the platform specific physical key identifier. + /// + /// Returns `Some(key_id)` if the conversion was succesful; returns `None` otherwise. + /// + /// ## Platform-specific + /// - **Windows:** A 16bit extended scancode + // TODO: Describe what this value contains for each platform + fn to_scancode(self) -> Option; + + /// Constructs a `KeyCode` from a platform specific physical key identifier. + /// + /// Note that this conversion may be lossy, i.e. converting the returned `KeyCode` back, + /// using `to_scancode` might not yield the original value. + fn from_scancode(scancode: u32) -> KeyCode; +} diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 3a0b5b5d95..c3f65702ae 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -4,16 +4,24 @@ use std::os::raw::c_void; use std::path::Path; use libc; -use winapi::shared::minwindef::WORD; -use winapi::shared::windef::HWND; +use winapi::{ + shared::{ + minwindef::{LOWORD, WORD}, + windef::HWND, + }, + um::{ + winnt::{LANG_KOREAN, PRIMARYLANGID}, + winuser::GetKeyboardLayout, + }, +}; use crate::{ dpi::PhysicalSize, event::{DeviceId, KeyEvent}, event_loop::EventLoop, - keyboard::Key, + keyboard::{Key, KeyCode, NativeKeyCode}, monitor::MonitorHandle, - platform::modifier_supplement::KeyEventExtModifierSupplement, + platform::{modifier_supplement::KeyEventExtModifierSupplement, scancode::KeyCodeExtScancode}, platform_impl::{EventLoop as WindowsEventLoop, WinIcon}, window::{BadIcon, Icon, Theme, Window, WindowBuilder}, }; @@ -248,3 +256,336 @@ impl KeyEventExtModifierSupplement for KeyEvent { self.platform_specific.key_without_modifers } } + +impl KeyCodeExtScancode for KeyCode { + fn to_scancode(self) -> Option { + // See `from_scancode` for more info + + let hkl = unsafe { GetKeyboardLayout(0) }; + + let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); + let is_korean = primary_lang_id == LANG_KOREAN; + + match self { + KeyCode::Backquote => Some(0x0029), + KeyCode::Backslash => Some(0x002B), + KeyCode::Backspace => Some(0x000E), + KeyCode::BracketLeft => Some(0x001A), + KeyCode::BracketRight => Some(0x001B), + KeyCode::Comma => Some(0x0033), + KeyCode::Digit0 => Some(0x000B), + KeyCode::Digit1 => Some(0x0002), + KeyCode::Digit2 => Some(0x0003), + KeyCode::Digit3 => Some(0x0004), + KeyCode::Digit4 => Some(0x0005), + KeyCode::Digit5 => Some(0x0006), + KeyCode::Digit6 => Some(0x0007), + KeyCode::Digit7 => Some(0x0008), + KeyCode::Digit8 => Some(0x0009), + KeyCode::Digit9 => Some(0x000A), + KeyCode::Equal => Some(0x000D), + KeyCode::IntlBackslash => Some(0x0056), + KeyCode::IntlRo => Some(0x0073), + KeyCode::IntlYen => Some(0x007D), + KeyCode::KeyA => Some(0x001E), + KeyCode::KeyB => Some(0x0030), + KeyCode::KeyC => Some(0x002E), + KeyCode::KeyD => Some(0x0020), + KeyCode::KeyE => Some(0x0012), + KeyCode::KeyF => Some(0x0021), + KeyCode::KeyG => Some(0x0022), + KeyCode::KeyH => Some(0x0023), + KeyCode::KeyI => Some(0x0017), + KeyCode::KeyJ => Some(0x0024), + KeyCode::KeyK => Some(0x0025), + KeyCode::KeyL => Some(0x0026), + KeyCode::KeyM => Some(0x0032), + KeyCode::KeyN => Some(0x0031), + KeyCode::KeyO => Some(0x0018), + KeyCode::KeyP => Some(0x0019), + KeyCode::KeyQ => Some(0x0010), + KeyCode::KeyR => Some(0x0013), + KeyCode::KeyS => Some(0x001F), + KeyCode::KeyT => Some(0x0014), + KeyCode::KeyU => Some(0x0016), + KeyCode::KeyV => Some(0x002F), + KeyCode::KeyW => Some(0x0011), + KeyCode::KeyX => Some(0x002D), + KeyCode::KeyY => Some(0x0015), + KeyCode::KeyZ => Some(0x002C), + KeyCode::Minus => Some(0x000C), + KeyCode::Period => Some(0x0034), + KeyCode::Quote => Some(0x0028), + KeyCode::Semicolon => Some(0x0027), + KeyCode::Slash => Some(0x0035), + KeyCode::AltLeft => Some(0x0038), + KeyCode::AltRight => Some(0xE038), + KeyCode::CapsLock => Some(0x003A), + KeyCode::ContextMenu => Some(0xE05D), + KeyCode::ControlLeft => Some(0x001D), + KeyCode::ControlRight => Some(0xE01D), + KeyCode::Enter => Some(0x001C), + KeyCode::MetaLeft => Some(0xE05B), + KeyCode::MetaRight => Some(0xE05C), + KeyCode::ShiftLeft => Some(0x002A), + KeyCode::ShiftRight => Some(0x0036), + KeyCode::Space => Some(0x0039), + KeyCode::Tab => Some(0x000F), + KeyCode::Convert => Some(0x0079), + KeyCode::Lang1 => { + if is_korean { + Some(0xE0F2) + } else { + Some(0x0072) + } + } + KeyCode::Lang2 => { + if is_korean { + Some(0xE0F1) + } else { + Some(0x0071) + } + } + KeyCode::KanaMode => Some(0x0070), + KeyCode::NonConvert => Some(0x007B), + KeyCode::Delete => Some(0xE053), + KeyCode::End => Some(0xE04F), + KeyCode::Home => Some(0xE047), + KeyCode::Insert => Some(0xE052), + KeyCode::PageDown => Some(0xE051), + KeyCode::PageUp => Some(0xE049), + KeyCode::ArrowDown => Some(0xE050), + KeyCode::ArrowLeft => Some(0xE04B), + KeyCode::ArrowRight => Some(0xE04D), + KeyCode::ArrowUp => Some(0xE048), + KeyCode::NumLock => Some(0xE045), + KeyCode::Numpad0 => Some(0x0052), + KeyCode::Numpad1 => Some(0x004F), + KeyCode::Numpad2 => Some(0x0050), + KeyCode::Numpad3 => Some(0x0051), + KeyCode::Numpad4 => Some(0x004B), + KeyCode::Numpad5 => Some(0x004C), + KeyCode::Numpad6 => Some(0x004D), + KeyCode::Numpad7 => Some(0x0047), + KeyCode::Numpad8 => Some(0x0048), + KeyCode::Numpad9 => Some(0x0049), + KeyCode::NumpadAdd => Some(0x004E), + KeyCode::NumpadComma => Some(0x007E), + KeyCode::NumpadDecimal => Some(0x0053), + KeyCode::NumpadDivide => Some(0xE035), + KeyCode::NumpadEnter => Some(0xE01C), + KeyCode::NumpadEqual => Some(0x0059), + KeyCode::NumpadMultiply => Some(0x0037), + KeyCode::NumpadSubtract => Some(0x004A), + KeyCode::Escape => Some(0x0001), + KeyCode::F1 => Some(0x003B), + KeyCode::F2 => Some(0x003C), + KeyCode::F3 => Some(0x003D), + KeyCode::F4 => Some(0x003E), + KeyCode::F5 => Some(0x003F), + KeyCode::F6 => Some(0x0040), + KeyCode::F7 => Some(0x0041), + KeyCode::F8 => Some(0x0042), + KeyCode::F9 => Some(0x0043), + KeyCode::F10 => Some(0x0044), + KeyCode::F11 => Some(0x0057), + KeyCode::F12 => Some(0x0058), + KeyCode::F13 => Some(0x0064), + KeyCode::F14 => Some(0x0065), + KeyCode::F15 => Some(0x0066), + KeyCode::F16 => Some(0x0067), + KeyCode::F17 => Some(0x0068), + KeyCode::F18 => Some(0x0069), + KeyCode::F19 => Some(0x006A), + KeyCode::F20 => Some(0x006B), + KeyCode::F21 => Some(0x006C), + KeyCode::F22 => Some(0x006D), + KeyCode::F23 => Some(0x006E), + KeyCode::F24 => Some(0x0076), + KeyCode::PrintScreen => Some(0xE037), + //KeyCode::PrintScreen => Some(0x0054), // Alt + PrintScreen + KeyCode::ScrollLock => Some(0x0046), + KeyCode::Pause => Some(0x0045), + //KeyCode::Pause => Some(0xE046), // Ctrl + Pause + KeyCode::BrowserBack => Some(0xE06A), + KeyCode::BrowserFavorites => Some(0xE066), + KeyCode::BrowserForward => Some(0xE069), + KeyCode::BrowserHome => Some(0xE032), + KeyCode::BrowserRefresh => Some(0xE067), + KeyCode::BrowserSearch => Some(0xE065), + KeyCode::BrowserStop => Some(0xE068), + KeyCode::LaunchApp1 => Some(0xE06B), + KeyCode::LaunchApp2 => Some(0xE021), + KeyCode::LaunchMail => Some(0xE06C), + KeyCode::MediaPlayPause => Some(0xE022), + KeyCode::MediaSelect => Some(0xE06D), + KeyCode::MediaStop => Some(0xE024), + KeyCode::MediaTrackNext => Some(0xE019), + KeyCode::MediaTrackPrevious => Some(0xE010), + KeyCode::Power => Some(0xE05E), + KeyCode::AudioVolumeDown => Some(0xE02E), + KeyCode::AudioVolumeMute => Some(0xE020), + KeyCode::AudioVolumeUp => Some(0xE030), + _ => None, + } + } + + fn from_scancode(scancode: u32) -> KeyCode { + // See: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html + // and: https://www.w3.org/TR/uievents-code/ + // and: The widget/NativeKeyToDOMCodeName.h file in the firefox source + + match scancode { + 0x0029 => KeyCode::Backquote, + 0x002B => KeyCode::Backslash, + 0x000E => KeyCode::Backspace, + 0x001A => KeyCode::BracketLeft, + 0x001B => KeyCode::BracketRight, + 0x0033 => KeyCode::Comma, + 0x000B => KeyCode::Digit0, + 0x0002 => KeyCode::Digit1, + 0x0003 => KeyCode::Digit2, + 0x0004 => KeyCode::Digit3, + 0x0005 => KeyCode::Digit4, + 0x0006 => KeyCode::Digit5, + 0x0007 => KeyCode::Digit6, + 0x0008 => KeyCode::Digit7, + 0x0009 => KeyCode::Digit8, + 0x000A => KeyCode::Digit9, + 0x000D => KeyCode::Equal, + 0x0056 => KeyCode::IntlBackslash, + 0x0073 => KeyCode::IntlRo, + 0x007D => KeyCode::IntlYen, + 0x001E => KeyCode::KeyA, + 0x0030 => KeyCode::KeyB, + 0x002E => KeyCode::KeyC, + 0x0020 => KeyCode::KeyD, + 0x0012 => KeyCode::KeyE, + 0x0021 => KeyCode::KeyF, + 0x0022 => KeyCode::KeyG, + 0x0023 => KeyCode::KeyH, + 0x0017 => KeyCode::KeyI, + 0x0024 => KeyCode::KeyJ, + 0x0025 => KeyCode::KeyK, + 0x0026 => KeyCode::KeyL, + 0x0032 => KeyCode::KeyM, + 0x0031 => KeyCode::KeyN, + 0x0018 => KeyCode::KeyO, + 0x0019 => KeyCode::KeyP, + 0x0010 => KeyCode::KeyQ, + 0x0013 => KeyCode::KeyR, + 0x001F => KeyCode::KeyS, + 0x0014 => KeyCode::KeyT, + 0x0016 => KeyCode::KeyU, + 0x002F => KeyCode::KeyV, + 0x0011 => KeyCode::KeyW, + 0x002D => KeyCode::KeyX, + 0x0015 => KeyCode::KeyY, + 0x002C => KeyCode::KeyZ, + 0x000C => KeyCode::Minus, + 0x0034 => KeyCode::Period, + 0x0028 => KeyCode::Quote, + 0x0027 => KeyCode::Semicolon, + 0x0035 => KeyCode::Slash, + 0x0038 => KeyCode::AltLeft, + 0xE038 => KeyCode::AltRight, + 0x003A => KeyCode::CapsLock, + 0xE05D => KeyCode::ContextMenu, + 0x001D => KeyCode::ControlLeft, + 0xE01D => KeyCode::ControlRight, + 0x001C => KeyCode::Enter, + 0xE05B => KeyCode::MetaLeft, + 0xE05C => KeyCode::MetaRight, + 0x002A => KeyCode::ShiftLeft, + 0x0036 => KeyCode::ShiftRight, + 0x0039 => KeyCode::Space, + 0x000F => KeyCode::Tab, + 0x0079 => KeyCode::Convert, + 0x0072 => KeyCode::Lang1, // for non-Korean layout + 0xE0F2 => KeyCode::Lang1, // for Korean layout + 0x0071 => KeyCode::Lang2, // for non-Korean layout + 0xE0F1 => KeyCode::Lang2, // for Korean layout + 0x0070 => KeyCode::KanaMode, + 0x007B => KeyCode::NonConvert, + 0xE053 => KeyCode::Delete, + 0xE04F => KeyCode::End, + 0xE047 => KeyCode::Home, + 0xE052 => KeyCode::Insert, + 0xE051 => KeyCode::PageDown, + 0xE049 => KeyCode::PageUp, + 0xE050 => KeyCode::ArrowDown, + 0xE04B => KeyCode::ArrowLeft, + 0xE04D => KeyCode::ArrowRight, + 0xE048 => KeyCode::ArrowUp, + 0xE045 => KeyCode::NumLock, + 0x0052 => KeyCode::Numpad0, + 0x004F => KeyCode::Numpad1, + 0x0050 => KeyCode::Numpad2, + 0x0051 => KeyCode::Numpad3, + 0x004B => KeyCode::Numpad4, + 0x004C => KeyCode::Numpad5, + 0x004D => KeyCode::Numpad6, + 0x0047 => KeyCode::Numpad7, + 0x0048 => KeyCode::Numpad8, + 0x0049 => KeyCode::Numpad9, + 0x004E => KeyCode::NumpadAdd, + 0x007E => KeyCode::NumpadComma, + 0x0053 => KeyCode::NumpadDecimal, + 0xE035 => KeyCode::NumpadDivide, + 0xE01C => KeyCode::NumpadEnter, + 0x0059 => KeyCode::NumpadEqual, + 0x0037 => KeyCode::NumpadMultiply, + 0x004A => KeyCode::NumpadSubtract, + 0x0001 => KeyCode::Escape, + 0x003B => KeyCode::F1, + 0x003C => KeyCode::F2, + 0x003D => KeyCode::F3, + 0x003E => KeyCode::F4, + 0x003F => KeyCode::F5, + 0x0040 => KeyCode::F6, + 0x0041 => KeyCode::F7, + 0x0042 => KeyCode::F8, + 0x0043 => KeyCode::F9, + 0x0044 => KeyCode::F10, + 0x0057 => KeyCode::F11, + 0x0058 => KeyCode::F12, + 0x0064 => KeyCode::F13, + 0x0065 => KeyCode::F14, + 0x0066 => KeyCode::F15, + 0x0067 => KeyCode::F16, + 0x0068 => KeyCode::F17, + 0x0069 => KeyCode::F18, + 0x006A => KeyCode::F19, + 0x006B => KeyCode::F20, + 0x006C => KeyCode::F21, + 0x006D => KeyCode::F22, + 0x006E => KeyCode::F23, + 0x0076 => KeyCode::F24, + 0xE037 => KeyCode::PrintScreen, + 0x0054 => KeyCode::PrintScreen, // Alt + PrintScreen + 0x0046 => KeyCode::ScrollLock, + 0x0045 => KeyCode::Pause, + 0xE046 => KeyCode::Pause, // Ctrl + Pause + 0xE06A => KeyCode::BrowserBack, + 0xE066 => KeyCode::BrowserFavorites, + 0xE069 => KeyCode::BrowserForward, + 0xE032 => KeyCode::BrowserHome, + 0xE067 => KeyCode::BrowserRefresh, + 0xE065 => KeyCode::BrowserSearch, + 0xE068 => KeyCode::BrowserStop, + 0xE06B => KeyCode::LaunchApp1, + 0xE021 => KeyCode::LaunchApp2, + 0xE06C => KeyCode::LaunchMail, + 0xE022 => KeyCode::MediaPlayPause, + 0xE06D => KeyCode::MediaSelect, + 0xE024 => KeyCode::MediaStop, + 0xE019 => KeyCode::MediaTrackNext, + 0xE010 => KeyCode::MediaTrackPrevious, + 0xE05E => KeyCode::Power, + 0xE02E => KeyCode::AudioVolumeDown, + 0xE020 => KeyCode::AudioVolumeMute, + 0xE030 => KeyCode::AudioVolumeUp, + _ => KeyCode::Unidentified(NativeKeyCode::Windows(scancode as u16)), + } + } +} diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 374b46b3b5..86f8b6178e 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -35,13 +35,14 @@ use crate::{ dpi::{PhysicalPosition, PhysicalSize}, event::{DeviceEvent, Event, Force, RawKeyEvent, Touch, TouchPhase, WindowEvent}, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, - keyboard::ModifiersState, + keyboard::{KeyCode, ModifiersState}, monitor::MonitorHandle as RootMonitorHandle, + platform::scancode::KeyCodeExtScancode, platform_impl::platform::{ dark_mode::try_theme, dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling}, drop_handler::FileDropHandler, - keyboard::{is_msg_keyboard_related, native_key_to_code}, + keyboard::is_msg_keyboard_related, keyboard_layout::LAYOUT_CACHE, minimal_ime::is_msg_ime_related, monitor::{self, MonitorHandle}, @@ -2098,7 +2099,7 @@ unsafe extern "system" fn thread_event_target_callback( } }; let scancode = keyboard.MakeCode | extension; - let code = native_key_to_code(scancode); + let code = KeyCode::from_scancode(scancode as u32); subclass_input.send_event(Event::DeviceEvent { device_id, event: Key(RawKeyEvent { diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 5b78013340..250c0a6a36 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -17,6 +17,7 @@ use winapi::{ use crate::{ event::{ElementState, KeyEvent}, keyboard::{Key, KeyCode, KeyLocation, NativeKeyCode}, + platform::scancode::KeyCodeExtScancode, platform_impl::platform::{ keyboard_layout::{get_or_insert_str, LayoutCache, WindowsModifiers, LAYOUT_CACHE}, KeyEventExtra, @@ -138,10 +139,9 @@ impl KeyEventBuilder { self.prev_down_was_dead = false; let mut layouts = LAYOUT_CACHE.lock().unwrap(); - let mut event_info = Some( - PartialKeyEventInfo::from_message(lparam, ElementState::Pressed, &mut layouts) - .0, - ); + let (event_info, _) = + PartialKeyEventInfo::from_message(lparam, ElementState::Pressed, &mut layouts); + let mut event_info = Some(event_info); let mut next_msg = MaybeUninit::uninit(); let peek_retval = unsafe { @@ -420,7 +420,7 @@ impl KeyEventBuilder { } let scancode = scancode as ExScancode; let is_extended = (scancode & 0xE000) == 0xE000; - let code = native_key_to_code(scancode); + let code = KeyCode::from_scancode(scancode as u32); let mods = if caps_lock_on { WindowsModifiers::CAPS_LOCK } else { @@ -494,7 +494,7 @@ impl PartialKeyEventInfo { let lparam_struct = destructure_key_lparam(lparam); let scancode = new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); - let code = native_key_to_code(scancode); + let code = KeyCode::from_scancode(scancode as u32); let vkey = unsafe { winuser::MapVirtualKeyW(scancode as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 }; let location = get_location(vkey, lparam_struct.extended); @@ -882,162 +882,3 @@ pub fn vkey_to_non_printable( _ => Key::Unidentified(native_code), } } - -pub fn native_key_to_code(scancode: ExScancode) -> KeyCode { - // See: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html - // and: https://www.w3.org/TR/uievents-code/ - // and: The widget/NativeKeyToDOMCodeName.h file in the firefox source - - match scancode { - 0x0029 => KeyCode::Backquote, - 0x002B => KeyCode::Backslash, - 0x000E => KeyCode::Backspace, - 0x001A => KeyCode::BracketLeft, - 0x001B => KeyCode::BracketRight, - 0x0033 => KeyCode::Comma, - 0x000B => KeyCode::Digit0, - 0x0002 => KeyCode::Digit1, - 0x0003 => KeyCode::Digit2, - 0x0004 => KeyCode::Digit3, - 0x0005 => KeyCode::Digit4, - 0x0006 => KeyCode::Digit5, - 0x0007 => KeyCode::Digit6, - 0x0008 => KeyCode::Digit7, - 0x0009 => KeyCode::Digit8, - 0x000A => KeyCode::Digit9, - 0x000D => KeyCode::Equal, - 0x0056 => KeyCode::IntlBackslash, - 0x0073 => KeyCode::IntlRo, - 0x007D => KeyCode::IntlYen, - 0x001E => KeyCode::KeyA, - 0x0030 => KeyCode::KeyB, - 0x002E => KeyCode::KeyC, - 0x0020 => KeyCode::KeyD, - 0x0012 => KeyCode::KeyE, - 0x0021 => KeyCode::KeyF, - 0x0022 => KeyCode::KeyG, - 0x0023 => KeyCode::KeyH, - 0x0017 => KeyCode::KeyI, - 0x0024 => KeyCode::KeyJ, - 0x0025 => KeyCode::KeyK, - 0x0026 => KeyCode::KeyL, - 0x0032 => KeyCode::KeyM, - 0x0031 => KeyCode::KeyN, - 0x0018 => KeyCode::KeyO, - 0x0019 => KeyCode::KeyP, - 0x0010 => KeyCode::KeyQ, - 0x0013 => KeyCode::KeyR, - 0x001F => KeyCode::KeyS, - 0x0014 => KeyCode::KeyT, - 0x0016 => KeyCode::KeyU, - 0x002F => KeyCode::KeyV, - 0x0011 => KeyCode::KeyW, - 0x002D => KeyCode::KeyX, - 0x0015 => KeyCode::KeyY, - 0x002C => KeyCode::KeyZ, - 0x000C => KeyCode::Minus, - 0x0034 => KeyCode::Period, - 0x0028 => KeyCode::Quote, - 0x0027 => KeyCode::Semicolon, - 0x0035 => KeyCode::Slash, - 0x0038 => KeyCode::AltLeft, - 0xE038 => KeyCode::AltRight, - 0x003A => KeyCode::CapsLock, - 0xE05D => KeyCode::ContextMenu, - 0x001D => KeyCode::ControlLeft, - 0xE01D => KeyCode::ControlRight, - 0x001C => KeyCode::Enter, - 0xE05B => KeyCode::MetaLeft, - 0xE05C => KeyCode::MetaRight, - 0x002A => KeyCode::ShiftLeft, - 0x0036 => KeyCode::ShiftRight, - 0x0039 => KeyCode::Space, - 0x000F => KeyCode::Tab, - 0x0079 => KeyCode::Convert, - 0x0072 => KeyCode::Lang1, // for non-Korean layout - 0xE0F2 => KeyCode::Lang1, // for Korean layout - 0x0071 => KeyCode::Lang2, // for non-Korean layout - 0xE0F1 => KeyCode::Lang2, // for Korean layout - 0x0070 => KeyCode::KanaMode, - 0x007B => KeyCode::NonConvert, - 0xE053 => KeyCode::Delete, - 0xE04F => KeyCode::End, - 0xE047 => KeyCode::Home, - 0xE052 => KeyCode::Insert, - 0xE051 => KeyCode::PageDown, - 0xE049 => KeyCode::PageUp, - 0xE050 => KeyCode::ArrowDown, - 0xE04B => KeyCode::ArrowLeft, - 0xE04D => KeyCode::ArrowRight, - 0xE048 => KeyCode::ArrowUp, - 0xE045 => KeyCode::NumLock, - 0x0052 => KeyCode::Numpad0, - 0x004F => KeyCode::Numpad1, - 0x0050 => KeyCode::Numpad2, - 0x0051 => KeyCode::Numpad3, - 0x004B => KeyCode::Numpad4, - 0x004C => KeyCode::Numpad5, - 0x004D => KeyCode::Numpad6, - 0x0047 => KeyCode::Numpad7, - 0x0048 => KeyCode::Numpad8, - 0x0049 => KeyCode::Numpad9, - 0x004E => KeyCode::NumpadAdd, - 0x007E => KeyCode::NumpadComma, - 0x0053 => KeyCode::NumpadDecimal, - 0xE035 => KeyCode::NumpadDivide, - 0xE01C => KeyCode::NumpadEnter, - 0x0059 => KeyCode::NumpadEqual, - 0x0037 => KeyCode::NumpadMultiply, - 0x004A => KeyCode::NumpadSubtract, - 0x0001 => KeyCode::Escape, - 0x003B => KeyCode::F1, - 0x003C => KeyCode::F2, - 0x003D => KeyCode::F3, - 0x003E => KeyCode::F4, - 0x003F => KeyCode::F5, - 0x0040 => KeyCode::F6, - 0x0041 => KeyCode::F7, - 0x0042 => KeyCode::F8, - 0x0043 => KeyCode::F9, - 0x0044 => KeyCode::F10, - 0x0057 => KeyCode::F11, - 0x0058 => KeyCode::F12, - 0x0064 => KeyCode::F13, - 0x0065 => KeyCode::F14, - 0x0066 => KeyCode::F15, - 0x0067 => KeyCode::F16, - 0x0068 => KeyCode::F17, - 0x0069 => KeyCode::F18, - 0x006A => KeyCode::F19, - 0x006B => KeyCode::F20, - 0x006C => KeyCode::F21, - 0x006D => KeyCode::F22, - 0x006E => KeyCode::F23, - 0x0076 => KeyCode::F24, - 0xE037 => KeyCode::PrintScreen, - 0x0054 => KeyCode::PrintScreen, // Alt + PrintScreen - 0x0046 => KeyCode::ScrollLock, - 0x0045 => KeyCode::Pause, - 0xE046 => KeyCode::Pause, // Ctrl + Pause - 0xE06A => KeyCode::BrowserBack, - 0xE066 => KeyCode::BrowserFavorites, - 0xE069 => KeyCode::BrowserForward, - 0xE032 => KeyCode::BrowserHome, - 0xE067 => KeyCode::BrowserRefresh, - 0xE065 => KeyCode::BrowserSearch, - 0xE068 => KeyCode::BrowserStop, - 0xE06B => KeyCode::LaunchApp1, - 0xE021 => KeyCode::LaunchApp2, - 0xE06C => KeyCode::LaunchMail, - 0xE022 => KeyCode::MediaPlayPause, - 0xE06D => KeyCode::MediaSelect, - 0xE024 => KeyCode::MediaStop, - 0xE019 => KeyCode::MediaTrackNext, - 0xE010 => KeyCode::MediaTrackPrevious, - 0xE05E => KeyCode::Power, - 0xE02E => KeyCode::AudioVolumeDown, - 0xE020 => KeyCode::AudioVolumeMute, - 0xE030 => KeyCode::AudioVolumeUp, - _ => KeyCode::Unidentified(NativeKeyCode::Windows(scancode)), - } -} diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 09beb5bd83..67a8244431 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -11,7 +11,8 @@ use winapi::{ctypes::c_int, shared::minwindef::HKL, um::winuser}; use crate::{ keyboard::{Key, KeyCode, ModifiersState, NativeKeyCode}, - platform_impl::platform::keyboard::{native_key_to_code, vkey_to_non_printable, ExScancode}, + platform::scancode::KeyCodeExtScancode, + platform_impl::platform::keyboard::{vkey_to_non_printable, ExScancode}, }; lazy_static! { @@ -214,7 +215,7 @@ impl LayoutCache { } let native_code = NativeKeyCode::Windows(scancode as ExScancode); - let key_code = native_key_to_code(scancode as ExScancode); + let key_code = KeyCode::from_scancode(scancode); // Let's try to get the key from just the scancode and vk // We don't necessarily know yet if AltGraph is present on this layout so we'll // assume it isn't. Then we'll do a second pass where we set the "AltRight" keys to From 5f5d87ce9487ce1691f181c09923de27398919fd Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 10 Jan 2021 12:58:00 +0100 Subject: [PATCH 32/75] Add `reset_dead_keys` --- src/platform_impl/windows/window.rs | 21 +++++++++++++++++++++ src/window.rs | 16 ++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 0eb7616094..2358d400c5 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -1,5 +1,6 @@ #![cfg(target_os = "windows")] +use mem::MaybeUninit; use parking_lot::Mutex; use raw_window_handle::{windows::WindowsHandle, RawWindowHandle}; use std::{ @@ -654,6 +655,26 @@ impl Window { pub fn theme(&self) -> Theme { self.window_state.lock().current_theme } + + #[inline] + pub fn reset_dead_keys(&self) { + // `ToUnicode` consumes the dead-key by default so we are constructing a fake (but valid) + // key input which we can call `ToUnicode` with. + unsafe { + let vk = 'a' as u32; + let scancode = winuser::MapVirtualKeyW(vk, winuser::MAPVK_VK_TO_VSC); + let kbd_state = [0; 256]; + let mut char_buff = [MaybeUninit::uninit(); 8]; + winuser::ToUnicode( + 'a' as u32, + scancode, + kbd_state.as_ptr(), + char_buff[0].as_mut_ptr(), + char_buff.len() as i32, + 0, + ); + } + } } impl Drop for Window { diff --git a/src/window.rs b/src/window.rs index 9ed2de06a9..33b2980008 100644 --- a/src/window.rs +++ b/src/window.rs @@ -406,6 +406,22 @@ impl Window { pub fn request_redraw(&self) { self.window.request_redraw() } + + /// Reset the dead key state of the keyboard. + /// + /// This is useful when a dead key is bound to trigger an action. Then + /// this function can be called to reset the dead key state so that + /// follow-up text input won't be affected by the dead key. + /// + /// ## Platform-specific + /// - **Web:** Does nothing + // --------------------------- + // Developers' Note: If this cannot be implemented on every desktop platform + // at least, then this function should be provided through a platform specific + // extension trait + pub fn reset_dead_keys(&self) { + self.window.reset_dead_keys(); + } } /// Position and size functions. From 3226db14cbc4411a09d6e76f9f1ba261b277bb1c Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 10 Jan 2021 17:40:22 +0100 Subject: [PATCH 33/75] Improve the documentation for `Key` and `KeyCode` --- src/keyboard.rs | 521 ++++++++++++++++++++++++++++-------------------- 1 file changed, 305 insertions(+), 216 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 71b07aa136..279c19152b 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -116,7 +116,8 @@ pub enum KeyCode { /// to allow the user to specify keybindings for keys which /// are not defined by this API. Unidentified(NativeKeyCode), - /// ` on a US keyboard. This is the 半角/全角/漢字 (hankaku/zenkaku/kanji) key on Japanese keyboards + /// ` on a US keyboard. This is the 半角/全角/漢字 + /// (hankaku/zenkaku/kanji) key on Japanese keyboards Backquote, /// Used for both the US \ (on the 101-key layout) and also for the key /// located between the " and Enter keys on row C of the 102-, @@ -155,10 +156,10 @@ pub enum KeyCode { /// Labelled \ on a UK keyboard. IntlBackslash, /// Located between the / and right Shift keys. - /// Labelled \ (ro) on a Japanese keyboard. + /// Labelled \ (ro) on a Japanese keyboard. IntlRo, /// Located between the = and Backspace keys. - /// Labelled ¥ (yen) on a Japanese keyboard. \ on a + /// Labelled ¥ (yen) on a Japanese keyboard. \ on a /// Russian keyboard. IntlYen, /// a on a US keyboard. @@ -259,13 +260,17 @@ pub enum KeyCode { Space, /// Tab or Tab, - /// Japanese: (henkan) + /// Japanese: (henkan) Convert, - /// Japanese: カタカナ/ひらがな/ローマ字 (katakana/hiragana/romaji) + /// Japanese: カタカナ/ひらがな/ローマ字 (katakana/hiragana/romaji) KanaMode, - /// Korean: HangulMode 한/영 (han/yeong)
Japanese (Mac keyboard): (kana) + /// Korean: HangulMode 한/영 (han/yeong) + /// + /// Japanese (Mac keyboard): (kana) Lang1, - /// Korean: Hanja (hanja)
Japanese (Mac keyboard): (eisu) + /// Korean: Hanja (hanja) + /// + /// Japanese (Mac keyboard): (eisu) Lang2, /// Japanese (word-processing keyboard): Katakana Lang3, @@ -273,11 +278,11 @@ pub enum KeyCode { Lang4, /// Japanese (word-processing keyboard): Zenkaku/Hankaku Lang5, - /// Japanese: 無変換 (muhenkan) + /// Japanese: 無変換 (muhenkan) NonConvert, /// . The forward delete key. /// Note that on Apple keyboards, the key labelled Delete on the main part of - /// the keyboard should be encoded as "Backspace". + /// the keyboard should be encoded as "Backspace". Delete, /// Page Down End or End, @@ -299,37 +304,38 @@ pub enum KeyCode { ArrowRight, /// ArrowUp, - /// On the Mac, the "NumLock" code should be used for the numpad Clear key. + /// On the Mac, the "NumLock" code should be used for the numpad Clear + /// key. NumLock, - /// 0 Ins on a keyboard
0 on a phone or remote control + /// 0 Ins on a keyboard. 0 on a phone or remote control Numpad0, - /// 1 End on a keyboard
1 or 1 QZ on a phone or - /// remote control + /// 1 End on a keyboard. 1 or 1 QZ on a phone or remote control Numpad1, - /// 2 ↓ on a keyboard
2 ABC on a phone or remote control + /// 2 ↓ on a keyboard. 2 ABC on a phone or remote control Numpad2, - /// 3 PgDn on a keyboard
3 DEF on a phone or remote control + /// 3 PgDn on a keyboard. 3 DEF on a phone or remote control Numpad3, - /// 4 ← on a keyboard
4 GHI on a phone or remote control + /// 4 ← on a keyboard. 4 GHI on a phone or remote control Numpad4, - /// 5 on a keyboard
5 JKL on a phone or remote control + /// 5 on a keyboard. 5 JKL on a phone or remote control Numpad5, - /// 6 → on a keyboard
6 MNO on a phone or remote control + /// 6 → on a keyboard. 6 MNO on a phone or remote control Numpad6, - /// 7 Home on a keyboard
7 PQRS or 7 PRS on a phone + /// 7 Home on a keyboard. 7 PQRS or 7 PRS on a phone /// or remote control Numpad7, - /// 8 ↑ on a keyboard
8 TUV on a phone or remote control + /// 8 ↑ on a keyboard. 8 TUV on a phone or remote control Numpad8, - /// 9 PgUp on a keyboard
9 WXYZ or 9 WXY on a phone + /// 9 PgUp on a keyboard. 9 WXYZ or 9 WXY on a phone /// or remote control Numpad9, /// + NumpadAdd, /// Found on the Microsoft Natural Keyboard. NumpadBackspace, - /// C or A (All Clear). Also for use with numpads that have a Clear key that is separate from the NumLock key. On the Mac, the numpad Clear key should always - /// be encoded as "NumLock". + /// C or A (All Clear). Also for use with numpads that have a + /// Clear key that is separate from the NumLock key. On the Mac, the + /// numpad Clear key should always be encoded as "NumLock". NumpadClear, /// C (Clear Entry) NumpadClearEntry, @@ -358,7 +364,9 @@ pub enum KeyCode { /// M Subtract current entry from the value stored in memory. NumpadMemorySubtract, /// * on a keyboard. For use with numpads that provide mathematical - /// operations (+, - * and /).
Use "NumpadStar" for the * key on phones and remote controls. + /// operations (+, - * and /). + /// + /// Use "NumpadStar" for the * key on phones and remote controls. NumpadMultiply, /// ( Found on the Microsoft Natural Keyboard. NumpadParenLeft, @@ -366,7 +374,9 @@ pub enum KeyCode { NumpadParenRight, /// * on a phone or remote control device. /// This key is typically found below the 7 key and to the left of - /// the 0 key.
Use "NumpadMultiply" for the * key on + /// the 0 key. + /// + /// Use "NumpadMultiply" for the * key on /// numeric keypads. NumpadStar, /// - @@ -395,8 +405,8 @@ pub enum KeyCode { BrowserRefresh, BrowserSearch, BrowserStop, - /// Eject or . This key is placed in the function - /// section on some Apple keyboards. + /// Eject or . This key is placed in the function section on some Apple + /// keyboards. Eject, /// Sometimes labelled My Computer on the keyboard LaunchApp1, @@ -408,8 +418,8 @@ pub enum KeyCode { MediaStop, MediaTrackNext, MediaTrackPrevious, - /// This key is placed in the function section on some Apple keyboards, - /// replacing the Eject key. + /// This key is placed in the function section on some Apple keyboards, replacing the + /// Eject key. Power, Sleep, AudioVolumeDown, @@ -444,75 +454,99 @@ pub enum KeyCode { Hiragana, /// Use for dedicated カタカナ key found on some Japanese word processing keyboards. Katakana, - /// F + /// General-purpose function key. + /// Usually found at the top of the keyboard. F1, - /// F + /// General-purpose function key. + /// Usually found at the top of the keyboard. F2, - /// F + /// General-purpose function key. + /// Usually found at the top of the keyboard. F3, - /// F + /// General-purpose function key. + /// Usually found at the top of the keyboard. F4, - /// F + /// General-purpose function key. + /// Usually found at the top of the keyboard. F5, - /// F + /// General-purpose function key. + /// Usually found at the top of the keyboard. F6, - /// F + /// General-purpose function key. + /// Usually found at the top of the keyboard. F7, - /// F + /// General-purpose function key. + /// Usually found at the top of the keyboard. F8, - /// F + /// General-purpose function key. + /// Usually found at the top of the keyboard. F9, - /// F10 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F10, - /// F11 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F11, - /// F12 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F12, - /// F13 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F13, - /// F14 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F14, - /// F15 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F15, - /// F16 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F16, - /// F17 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F17, - /// F18 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F18, - /// F19 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F19, - /// F20 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F20, - /// F21 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F21, - /// F22 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F22, - /// F23 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F23, - /// F24 + /// General-purpose function key. + /// Usually found at the top of the keyboard. F24, - /// F25 + /// General-purpose function key. F25, - /// F26 + /// General-purpose function key. F26, - /// F27 + /// General-purpose function key. F27, - /// F28 + /// General-purpose function key. F28, - /// F29 + /// General-purpose function key. F29, - /// F30 + /// General-purpose function key. F30, - /// F31 + /// General-purpose function key. F31, - /// F32 + /// General-purpose function key. F32, - /// F33 + /// General-purpose function key. F33, - /// F34 + /// General-purpose function key. F34, - /// F35 + /// General-purpose function key. F35, } @@ -524,100 +558,113 @@ pub enum KeyCode { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Key<'a> { - /// A key string that corresponds to the character typed by the user, - /// taking into account the user’s current locale setting, modifier state, - /// and any system-level keyboard mapping overrides that are in effect. + /// A key string that corresponds to the character typed by the user, taking into account the + /// user’s current locale setting, and any system-level keyboard mapping overrides that are in + /// effect. Character(&'a str), - /// This variant is used when the key cannot be translated to any - /// other variant. + /// This variant is used when the key cannot be translated to any other variant. /// - /// The native scancode is provided (if available) in order - /// to allow the user to specify keybindings for keys which - /// are not defined by this API. + /// The native scancode is provided (if available) in order to allow the user to specify + /// keybindings for keys which are not defined by this API. Unidentified(NativeKeyCode), - /// Contains the text representation of the dead-key - /// when available. + /// Contains the text representation of the dead-key when available. /// /// ## Platform-specific /// - **Web:** Always contains `None` Dead(Option), - /// The `Alt` (Alternative) key.
This key enables the alternate modifier function for interpreting concurrent or subsequent keyboard input.
This key value is also used for the Apple `Option` key. + /// The `Alt` (Alternative) key. + /// + /// This key enables the alternate modifier function for interpreting concurrent or subsequent + /// keyboard input. This key value is also used for the Apple `Option` key. Alt, /// The Alternate Graphics (`AltGr` or `AltGraph`) key. - /// This key is used enable the ISO Level 3 shift modifier (the standard `Shift` key is the level 2 modifier). - /// See [ISO9995-1]. + /// + /// This key is used enable the ISO Level 3 shift modifier (the standard `Shift` key is the + /// level 2 modifier). See [ISO9995-1]. AltGraph, /// The `Caps Lock` (Capital) key. + /// /// Toggle capital character lock function for interpreting subsequent keyboard input event. CapsLock, - /// The `Control` or `Ctrl` key, to enable control modifier function for interpreting concurrent or subsequent keyboard input. + /// The `Control` or `Ctrl` key. + /// + /// Used to enable control modifier function for interpreting concurrent or subsequent keyboard + /// input. Control, - /// The Function switch `Fn` key.
Activating this key simultaneously with another key changes that key’s value to an alternate character or function. - /// This key is often handled directly in the keyboard hardware and does not usually generate key events. + /// The Function switch `Fn` key. Activating this key simultaneously with another key changes + /// that key’s value to an alternate character or function. This key is often handled directly + /// in the keyboard hardware and does not usually generate key events. Fn, - /// The Function-Lock (`FnLock` or `F-Lock`) key. - /// Activating this key switches the mode of the keyboard to changes some keys' values to an alternate character or function. - /// This key is often handled directly in the keyboard hardware and does not usually generate key events. + /// The Function-Lock (`FnLock` or `F-Lock`) key. Activating this key switches the mode of the + /// keyboard to changes some keys' values to an alternate character or function. This key is + /// often handled directly in the keyboard hardware and does not usually generate key events. FnLock, - /// The `Meta` key, to enable meta modifier function for interpreting concurrent or subsequent keyboard input. - /// This key value is used for the Windows Logo key and the Apple `Command` or `⌘` key. + /// The `Meta` key. Used to enable meta modifier function for interpreting concurrent or + /// subsequent keyboard input. This key value is used for the "Windows Logo" key and the Apple + /// `Command` or `⌘` key. Meta, - /// The `NumLock` or Number Lock key, to toggle numpad mode function for interpreting subsequent keyboard input. + /// The `NumLock` or Number Lock key. Used to toggle numpad mode function for interpreting + /// subsequent keyboard input. NumLock, - /// The `Scroll Lock` key, to toggle between scrolling and cursor movement modes. + /// Toggle between scrolling and cursor movement modes. ScrollLock, - /// The `Shift` key, to enable shift modifier function for interpreting concurrent or subsequent keyboard input. + /// The `Shift` key + /// + /// Used to enable shift modifier function for interpreting concurrent or subsequent keyboard + /// input. Shift, /// The Symbol modifier key (used on some virtual keyboards). Symbol, - /// The Symbol Lock key. SymbolLock, - /// The `Hyper` key. Hyper, - /// The `Super` key. Super, - /// The `Enter` or `↵` key, to activate current selection or accept current input.
This key value is also used for the `Return` (Macintosh numpad) key.
This key value is also used for the Android `KEYCODE_DPAD_CENTER`. + /// The `Enter` or `↵` key. Used to activate current selection or accept current input. This key + /// value is also used for the `Return` (Macintosh numpad) key. This key value is also used for + /// the Android `KEYCODE_DPAD_CENTER`. Enter, /// The Horizontal Tabulation `Tab` key. Tab, - /// The down arrow key, to navigate or traverse downward. (`KEYCODE_DPAD_DOWN`) + /// Navigate or traverse downward. (`KEYCODE_DPAD_DOWN`) ArrowDown, - /// The left arrow key, to navigate or traverse leftward. (`KEYCODE_DPAD_LEFT`) + /// Navigate or traverse leftward. (`KEYCODE_DPAD_LEFT`) ArrowLeft, - /// The right arrow key, to navigate or traverse rightward. (`KEYCODE_DPAD_RIGHT`) + /// Navigate or traverse rightward. (`KEYCODE_DPAD_RIGHT`) ArrowRight, - /// The up arrow key, to navigate or traverse upward. (`KEYCODE_DPAD_UP`) + /// Navigate or traverse upward. (`KEYCODE_DPAD_UP`) ArrowUp, /// The End key, used with keyboard entry to go to the end of content (`KEYCODE_MOVE_END`). End, - /// The Home key, used with keyboard entry, to go to start of content (`KEYCODE_MOVE_HOME`).
For the mobile phone `Home` key (which goes to the phone’s main screen), use `"GoHome"`. + /// The Home key, used with keyboard entry, to go to start of content (`KEYCODE_MOVE_HOME`). + /// For the mobile phone `Home` key (which goes to the phone’s main screen), use `"GoHome"`. Home, - /// The Page Down key, to scroll down or display next page of content. + /// Scroll down or display next page of content. PageDown, - /// The Page Up key, to scroll up or display previous page of content. + /// Scroll up or display previous page of content. PageUp, - /// The Backspace key. This key value is also used for the key labeled `Delete` on MacOS keyboards. + /// Used to remove the character to the left of the cursor. This key value is also used for + /// the key labeled `Delete` on MacOS keyboards. Backspace, /// Remove the currently selected input. Clear, /// Copy the current selection. (`APPCOMMAND_COPY`) Copy, - /// The Cursor Select (Crsel) key. + /// The Cursor Select key. CrSel, /// Cut the current selection. (`APPCOMMAND_CUT`) Cut, - /// The Delete (Del) Key. - /// This key value is also used for the key labeled `Delete` on MacOS keyboards when modified by the `Fn` key. + /// Used to delete the character to the right of the cursor. This key value is also used for the + /// key labeled `Delete` on MacOS keyboards when `Fn` is active. Delete, - /// The Erase to End of Field key. - /// This key deletes all characters from the current cursor position to the end of the current field. + /// The Erase to End of Field key. This key deletes all characters from the current cursor + /// position to the end of the current field. EraseEof, /// The Extend Selection (Exsel) key. ExSel, - /// The Insert (Ins) key, to toggle between text modes for insertion or overtyping. (`KEYCODE_INSERT`) + /// Toggle between text modes for insertion or overtyping. + /// (`KEYCODE_INSERT`) Insert, /// The Paste key. (`APPCOMMAND_PASTE`) Paste, @@ -627,11 +674,10 @@ pub enum Key<'a> { Undo, /// The Accept (Commit, OK) key. Accept current option or input method sequence conversion. Accept, - /// The Again key, to redo or repeat an action. + /// Redo or repeat an action. Again, /// The Attention (Attn) key. Attn, - /// The Cancel key. Cancel, /// Show the application’s context menu. /// This key is commonly found between the right `Meta` key and the right `Control` key. @@ -640,61 +686,64 @@ pub enum Key<'a> { /// now more generally used to exit or "escape" the current context, such as closing a dialog /// or exiting full screen mode. Escape, - /// The Execute key. Execute, /// Open the Find dialog. (`APPCOMMAND_FIND`) Find, - /// Open a help dialog or toggle display of help information. (`APPCOMMAND_HELP`, `KEYCODE_HELP`) + /// Open a help dialog or toggle display of help information. (`APPCOMMAND_HELP`, + /// `KEYCODE_HELP`) Help, /// Pause the current state or application (as appropriate). - ///

Do not use this value for the `Pause` button on media controllers. Use `"MediaPause"` instead.

+ /// + /// Note: Do not use this value for the `Pause` button on media controllers. Use `"MediaPause"` + /// instead. Pause, /// Play or resume the current state or application (as appropriate). - ///

Do not use this value for the `Play` button on media controllers. Use `"MediaPlay"` instead.

+ /// + /// Note: Do not use this value for the `Play` button on media controllers. Use `"MediaPlay"` + /// instead. Play, /// The properties (Props) key. Props, - /// The Select key. Select, /// The ZoomIn key. (`KEYCODE_ZOOM_IN`) ZoomIn, /// The ZoomOut key. (`KEYCODE_ZOOM_OUT`) ZoomOut, - /// The Brightness Down key. Typically controls the display brightness. (`KEYCODE_BRIGHTNESS_DOWN`) + /// The Brightness Down key. Typically controls the display brightness. + /// (`KEYCODE_BRIGHTNESS_DOWN`) BrightnessDown, /// The Brightness Up key. Typically controls the display brightness. (`KEYCODE_BRIGHTNESS_UP`) BrightnessUp, /// Toggle removable media to eject (open) and insert (close) state. (`KEYCODE_MEDIA_EJECT`) Eject, - /// The LogOff key. LogOff, /// Toggle power state. (`KEYCODE_POWER`) - ///

Note: Some devices might not expose this key to the operating environment.

+ /// Note: Note: Some devices might not expose this key to the operating environment. Power, /// The `PowerOff` key. Sometime called `PowerDown`. PowerOff, - /// The `Print Screen` or `SnapShot` key, to initiate print-screen function. + /// Initiate print-screen function. PrintScreen, - /// The Hibernate key. - /// This key saves the current state of the computer to disk so that it can be restored. The computer will then shutdown. + /// The Hibernate key. This key saves the current state of the computer to disk so that it can + /// be restored. The computer will then shutdown. Hibernate, - /// The Standby key. - /// This key turns off the display and places the computer into a low-power mode without completely shutting down. - /// It is sometimes labelled `Suspend` or `Sleep` key. (`KEYCODE_SLEEP`) + /// The Standby key. This key turns off the display and places the computer into a low-power + /// mode without completely shutting down. It is sometimes labelled `Suspend` or `Sleep` key. + /// (`KEYCODE_SLEEP`) Standby, /// The WakeUp key. (`KEYCODE_WAKEUP`) WakeUp, - /// The All Candidates key, to initate the multi-candidate mode. + /// Initate the multi-candidate mode. AllCandidates, - /// The Alphanumeric key. Alphanumeric, - /// The Code Input key, to initiate the Code Input mode to allow characters to be entered by their code points. + /// Initiate the Code Input mode to allow characters to be entered by + /// their code points. CodeInput, - /// The Compose key, also known as Multi_key on the X Window System. - /// This key acts in a manner similar to a - /// dead key, triggering a mode where subsequent key presses are combined to produce a different character. + /// The Compose key, also known as "Multi_key" on the X Window System. This key acts in a + /// manner similar to a dead key, triggering a mode where subsequent key presses are combined to + /// produce a different character. Compose, - /// The Convert key, to convert the current input method sequence. + /// Convert the current input method sequence. Convert, /// The Final Mode `Final` key used on some Asian keyboards, to enable the final mode for IMEs. FinalMode, @@ -706,26 +755,21 @@ pub enum Key<'a> { GroupNext, /// Switch to the previous character group. (ISO/IEC 9995) GroupPrevious, - /// The Mode Change key, to toggle between or cycle through input modes of IMEs. + /// Toggle between or cycle through input modes of IMEs. ModeChange, - /// The Next Candidate function key. NextCandidate, - /// The NonConvert ("Don’t Convert") key, to accept current input method sequence without conversion in IMEs. + /// Accept current input method sequence without + /// conversion in IMEs. NonConvert, - /// The Previous Candidate function key. PreviousCandidate, - /// The Process key. Process, - /// The Single Candidate function key. SingleCandidate, - /// The Hangul (Korean characters) Mode key, to toggle between Hangul and English modes. + /// Toggle between Hangul and English modes. HangulMode, - /// The Hanja (Korean characters) Mode key. HanjaMode, - /// The Junja (Korean characters) Mode key. JunjaMode, - /// The Eisu key. This key may close the IME, but its purpose - /// is defined by the current IME. (`KEYCODE_EISU`) + /// The Eisu key. This key may close the IME, but its purpose is defined by the current IME. + /// (`KEYCODE_EISU`) Eisu, /// The (Half-Width) Characters key. Hankaku, @@ -733,12 +777,12 @@ pub enum Key<'a> { Hiragana, /// The Hiragana/Katakana toggle key. (`KEYCODE_KATAKANA_HIRAGANA`) HiraganaKatakana, - /// The Kana Mode (Kana Lock) key. This key is used to enter - /// hiragana mode (typically from romaji mode). + /// The Kana Mode (Kana Lock) key. This key is used to enter hiragana mode (typically from + /// romaji mode). KanaMode, - /// The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key. - /// This key is typically used to switch to a hiragana keyboard for - /// the purpose of converting input into kanji. (`KEYCODE_KANA`) + /// The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key. This key is + /// typically used to switch to a hiragana keyboard for the purpose of converting input into + /// kanji. (`KEYCODE_KANA`) KanjiMode, /// The Katakana (Japanese Kana characters) key. Katakana, @@ -756,11 +800,14 @@ pub enum Key<'a> { Soft3, /// General purpose virtual function key, as index 4. Soft4, - /// Select next (numerically or logically) lower channel. (`APPCOMMAND_MEDIA_CHANNEL_DOWN`, `KEYCODE_CHANNEL_DOWN`) + /// Select next (numerically or logically) lower channel. (`APPCOMMAND_MEDIA_CHANNEL_DOWN`, + /// `KEYCODE_CHANNEL_DOWN`) ChannelDown, - /// Select next (numerically or logically) higher channel. (`APPCOMMAND_MEDIA_CHANNEL_UP`, `KEYCODE_CHANNEL_UP`) + /// Select next (numerically or logically) higher channel. (`APPCOMMAND_MEDIA_CHANNEL_UP`, + /// `KEYCODE_CHANNEL_UP`) ChannelUp, - /// Close the current document or message (Note: This doesn’t close the application). (`APPCOMMAND_CLOSE`) + /// Close the current document or message (Note: This doesn’t close the application). + /// (`APPCOMMAND_CLOSE`) Close, /// Open an editor to forward the current message. (`APPCOMMAND_FORWARD_MAIL`) MailForward, @@ -770,24 +817,33 @@ pub enum Key<'a> { MailSend, /// Close the current media, for example to close a CD or DVD tray. (`KEYCODE_MEDIA_CLOSE`) MediaClose, - /// Initiate or continue forward playback at faster than normal speed, or increase speed if already fast forwarding. (`APPCOMMAND_MEDIA_FAST_FORWARD`, `KEYCODE_MEDIA_FAST_FORWARD`) + /// Initiate or continue forward playback at faster than normal speed, or increase speed if + /// already fast forwarding. (`APPCOMMAND_MEDIA_FAST_FORWARD`, `KEYCODE_MEDIA_FAST_FORWARD`) MediaFastForward, /// Pause the currently playing media. (`APPCOMMAND_MEDIA_PAUSE`, `KEYCODE_MEDIA_PAUSE`) - ///

Media controller devices should use this value rather than `"Pause"` for their pause keys.

+ /// + /// Note: Media controller devices should use this value rather than `"Pause"` for their pause + /// keys. MediaPause, - /// Initiate or continue media playback at normal speed, if not currently playing at normal speed. (`APPCOMMAND_MEDIA_PLAY`, `KEYCODE_MEDIA_PLAY`) + /// Initiate or continue media playback at normal speed, if not currently playing at normal + /// speed. (`APPCOMMAND_MEDIA_PLAY`, `KEYCODE_MEDIA_PLAY`) MediaPlay, - /// Toggle media between play and pause states. (`APPCOMMAND_MEDIA_PLAY_PAUSE`, `KEYCODE_MEDIA_PLAY_PAUSE`) + /// Toggle media between play and pause states. (`APPCOMMAND_MEDIA_PLAY_PAUSE`, + /// `KEYCODE_MEDIA_PLAY_PAUSE`) MediaPlayPause, - /// Initiate or resume recording of currently selected media. (`APPCOMMAND_MEDIA_RECORD`, `KEYCODE_MEDIA_RECORD`) + /// Initiate or resume recording of currently selected media. (`APPCOMMAND_MEDIA_RECORD`, + /// `KEYCODE_MEDIA_RECORD`) MediaRecord, - /// Initiate or continue reverse playback at faster than normal speed, or increase speed if already rewinding. (`APPCOMMAND_MEDIA_REWIND`, `KEYCODE_MEDIA_REWIND`) + /// Initiate or continue reverse playback at faster than normal speed, or increase speed if + /// already rewinding. (`APPCOMMAND_MEDIA_REWIND`, `KEYCODE_MEDIA_REWIND`) MediaRewind, - /// Stop media playing, pausing, forwarding, rewinding, or recording, if not already stopped. (`APPCOMMAND_MEDIA_STOP`, `KEYCODE_MEDIA_STOP`) + /// Stop media playing, pausing, forwarding, rewinding, or recording, if not already stopped. + /// (`APPCOMMAND_MEDIA_STOP`, `KEYCODE_MEDIA_STOP`) MediaStop, /// Seek to next media or program track. (`APPCOMMAND_MEDIA_NEXTTRACK`, `KEYCODE_MEDIA_NEXT`) MediaTrackNext, - /// Seek to previous media or program track. (`APPCOMMAND_MEDIA_PREVIOUSTRACK`, `KEYCODE_MEDIA_PREVIOUS`) + /// Seek to previous media or program track. (`APPCOMMAND_MEDIA_PREVIOUSTRACK`, + /// `KEYCODE_MEDIA_PREVIOUS`) MediaTrackPrevious, /// Open a new document or message. (`APPCOMMAND_NEW`) New, @@ -809,11 +865,13 @@ pub enum Key<'a> { AudioBalanceLeft, /// Adjust audio balance rightward. (`VK_AUDIO_BALANCE_RIGHT`) AudioBalanceRight, - /// Decrease audio bass boost or cycle down through bass boost states. (`APPCOMMAND_BASS_DOWN`, `VK_BASS_BOOST_DOWN`) + /// Decrease audio bass boost or cycle down through bass boost states. (`APPCOMMAND_BASS_DOWN`, + /// `VK_BASS_BOOST_DOWN`) AudioBassBoostDown, /// Toggle bass boost on/off. (`APPCOMMAND_BASS_BOOST`) AudioBassBoostToggle, - /// Increase audio bass boost or cycle up through bass boost states. (`APPCOMMAND_BASS_UP`, `VK_BASS_BOOST_UP`) + /// Increase audio bass boost or cycle up through bass boost states. (`APPCOMMAND_BASS_UP`, + /// `VK_BASS_BOOST_UP`) AudioBassBoostUp, /// Adjust audio fader towards front. (`VK_FADER_FRONT`) AudioFaderFront, @@ -829,7 +887,8 @@ pub enum Key<'a> { AudioVolumeDown, /// Increase audio volume. (`APPCOMMAND_VOLUME_UP`, `KEYCODE_VOLUME_UP`) AudioVolumeUp, - /// Toggle between muted state and prior volume level. (`APPCOMMAND_VOLUME_MUTE`, `KEYCODE_VOLUME_MUTE`) + /// Toggle between muted state and prior volume level. (`APPCOMMAND_VOLUME_MUTE`, + /// `KEYCODE_VOLUME_MUTE`) AudioVolumeMute, /// Toggle the microphone on/off. (`APPCOMMAND_MIC_ON_OFF_TOGGLE`) MicrophoneToggle, @@ -841,11 +900,15 @@ pub enum Key<'a> { MicrophoneVolumeMute, /// Show correction list when a word is incorrectly identified. (`APPCOMMAND_CORRECTION_LIST`) SpeechCorrectionList, - /// Toggle between dictation mode and command/control mode. (`APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE`) + /// Toggle between dictation mode and command/control mode. + /// (`APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE`) SpeechInputToggle, - /// The first generic "LaunchApplication" key. This is commonly associated with launching "My Computer", and may have a computer symbol on the key. (`APPCOMMAND_LAUNCH_APP1`) + /// The first generic "LaunchApplication" key. This is commonly associated with launching "My + /// Computer", and may have a computer symbol on the key. (`APPCOMMAND_LAUNCH_APP1`) LaunchApplication1, - /// The second generic "LaunchApplication" key. This is commonly associated with launching "Calculator", and may have a calculator symbol on the key. (`APPCOMMAND_LAUNCH_APP2`, `KEYCODE_CALCULATOR`) + /// The second generic "LaunchApplication" key. This is commonly associated with launching + /// "Calculator", and may have a calculator symbol on the key. (`APPCOMMAND_LAUNCH_APP2`, + /// `KEYCODE_CALCULATOR`) LaunchApplication2, /// The "Calendar" key. (`KEYCODE_CALENDAR`) LaunchCalendar, @@ -855,19 +918,12 @@ pub enum Key<'a> { LaunchMail, /// The "Media Player" key. (`APPCOMMAND_LAUNCH_MEDIA_SELECT`) LaunchMediaPlayer, - /// The "Music Player" key. LaunchMusicPlayer, - /// The "Phone" key. LaunchPhone, - /// The "Screen Saver" key. LaunchScreenSaver, - /// The "Spreadsheet" key. LaunchSpreadsheet, - /// The "Web Browser" key. LaunchWebBrowser, - /// The "WebCam" key. LaunchWebCam, - /// The "Word Processor" key. LaunchWordProcessor, /// Navigate to previous content or page in current history. (`APPCOMMAND_BROWSER_BACKWARD`) BrowserBack, @@ -883,7 +939,8 @@ pub enum Key<'a> { BrowserSearch, /// Stop loading the current page or content. (`APPCOMMAND_BROWSER_STOP`) BrowserStop, - /// The Application switch key, which provides a list of recent apps to switch between. (`KEYCODE_APP_SWITCH`) + /// The Application switch key, which provides a list of recent apps to switch between. + /// (`KEYCODE_APP_SWITCH`) AppSwitch, /// The Call key. (`KEYCODE_CALL`) Call, @@ -899,13 +956,11 @@ pub enum Key<'a> { GoHome, /// The Headset Hook key. (`KEYCODE_HEADSETHOOK`) HeadsetHook, - /// The Last Number Redial key. LastNumberRedial, /// The Notification key. (`KEYCODE_NOTIFICATION`) Notification, /// Toggle between manner mode state: silent, vibrate, ring, ... (`KEYCODE_MANNER_MODE`) MannerMode, - /// The Voice Dial key. VoiceDial, /// Switch to viewing TV. (`KEYCODE_TV`) TV, @@ -971,13 +1026,17 @@ pub enum Key<'a> { AVRInput, /// Toggle the power on an external AVR (audio/video receiver). (`KEYCODE_AVR_POWER`) AVRPower, - /// General purpose color-coded media function key, as index 0 (red). (`VK_COLORED_KEY_0`, `KEYCODE_PROG_RED`) + /// General purpose color-coded media function key, as index 0 (red). (`VK_COLORED_KEY_0`, + /// `KEYCODE_PROG_RED`) ColorF0Red, - /// General purpose color-coded media function key, as index 1 (green). (`VK_COLORED_KEY_1`, `KEYCODE_PROG_GREEN`) + /// General purpose color-coded media function key, as index 1 (green). (`VK_COLORED_KEY_1`, + /// `KEYCODE_PROG_GREEN`) ColorF1Green, - /// General purpose color-coded media function key, as index 2 (yellow). (`VK_COLORED_KEY_2`, `KEYCODE_PROG_YELLOW`) + /// General purpose color-coded media function key, as index 2 (yellow). (`VK_COLORED_KEY_2`, + /// `KEYCODE_PROG_YELLOW`) ColorF2Yellow, - /// General purpose color-coded media function key, as index 3 (blue). (`VK_COLORED_KEY_3`, `KEYCODE_PROG_BLUE`) + /// General purpose color-coded media function key, as index 3 (blue). (`VK_COLORED_KEY_3`, + /// `KEYCODE_PROG_BLUE`) ColorF3Blue, /// General purpose color-coded media function key, as index 4 (grey). (`VK_COLORED_KEY_4`) ColorF4Grey, @@ -1023,7 +1082,8 @@ pub enum Key<'a> { GuideNextDay, /// If guide is active and displayed, then display previous day’s content. (`VK_PREV_DAY`) GuidePreviousDay, - /// Toggle display of information about currently selected context or media. (`VK_INFO`, `KEYCODE_INFO`) + /// Toggle display of information about currently selected context or media. (`VK_INFO`, + /// `KEYCODE_INFO`) Info, /// Toggle instant replay. (`VK_INSTANT_REPLAY`) InstantReplay, @@ -1036,7 +1096,9 @@ pub enum Key<'a> { /// Lock or unlock current content or program. (`VK_LOCK`) Lock, /// Show a list of media applications: audio/video players and image viewers. (`VK_APPS`) - ///

Do not confuse this key value with the Windows' `VK_APPS` / `VK_CONTEXT_MENU` key, which is encoded as `"ContextMenu"`.

+ /// + /// Note: Do not confuse this key value with the Windows' `VK_APPS` / `VK_CONTEXT_MENU` key, + /// which is encoded as `"ContextMenu"`. MediaApps, /// Audio track key. (`KEYCODE_MEDIA_AUDIO_TRACK`) MediaAudioTrack, @@ -1084,11 +1146,13 @@ pub enum Key<'a> { PlaySpeedUp, /// Toggle random media or content shuffle mode. (`VK_RANDOM_TOGGLE`) RandomToggle, - /// Not a physical key, but this key code is sent when the remote control battery is low. (`VK_RC_LOW_BATTERY`) + /// Not a physical key, but this key code is sent when the remote control battery is low. + /// (`VK_RC_LOW_BATTERY`) RcLowBattery, /// Toggle or cycle between media recording speeds. (`VK_RECORD_SPEED_NEXT`) RecordSpeedNext, - /// Toggle RF (radio frequency) input bypass mode (pass RF input directly to the RF output). (`VK_RF_BYPASS`) + /// Toggle RF (radio frequency) input bypass mode (pass RF input directly to the RF output). + /// (`VK_RF_BYPASS`) RfBypass, /// Toggle scan channels mode. (`VK_SCAN_CHANNELS_TOGGLE`) ScanChannelsToggle, @@ -1110,77 +1174,102 @@ pub enum Key<'a> { VideoModeNext, /// Cause device to identify itself in some manner, e.g., audibly or visibly. (`VK_WINK`) Wink, - /// Toggle between full-screen and scaled content, or alter magnification level. (`VK_ZOOM`, `KEYCODE_TV_ZOOM_MODE`) + /// Toggle between full-screen and scaled content, or alter magnification level. (`VK_ZOOM`, + /// `KEYCODE_TV_ZOOM_MODE`) ZoomToggle, - /// The F1 key, a general purpose function key, as index 1. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F1, - /// The F2 key, a general purpose function key, as index 2. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F2, - /// The F3 key, a general purpose function key, as index 3. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F3, - /// The F4 key, a general purpose function key, as index 4. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F4, - /// The F5 key, a general purpose function key, as index 5. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F5, - /// The F6 key, a general purpose function key, as index 6. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F6, - /// The F7 key, a general purpose function key, as index 7. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F7, - /// The F8 key, a general purpose function key, as index 8. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F8, - /// The F9 key, a general purpose function key, as index 9. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F9, - /// The F10 key, a general purpose function key, as index 10. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F10, - /// The F11 key, a general purpose function key, as index 11. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F11, - /// The F12 key, a general purpose function key, as index 12. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F12, - /// The F13 key, a general purpose function key, as index 13. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F13, - /// The F14 key, a general purpose function key, as index 14. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F14, - /// The F15 key, a general purpose function key, as index 15. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F15, - /// The F16 key, a general purpose function key, as index 16. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F16, - /// The F17 key, a general purpose function key, as index 17. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F17, - /// The F18 key, a general purpose function key, as index 18. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F18, - /// The F19 key, a general purpose function key, as index 19. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F19, - /// The F20 key, a general purpose function key, as index 20. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F20, - /// The F21 key, a general purpose function key, as index 21. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F21, - /// The F22 key, a general purpose function key, as index 22. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F22, - /// The F23 key, a general purpose function key, as index 23. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F23, - /// The F24 key, a general purpose function key, as index 24. + /// General-purpose function key. + /// Usually found at the top of the keyboard. F24, - /// The F25 key, a general purpose function key, as index 25. + /// General-purpose function key. F25, - /// The F26 key, a general purpose function key, as index 26. + /// General-purpose function key. F26, - /// The F27 key, a general purpose function key, as index 27. + /// General-purpose function key. F27, - /// The F28 key, a general purpose function key, as index 28. + /// General-purpose function key. F28, - /// The F29 key, a general purpose function key, as index 29. + /// General-purpose function key. F29, - /// The F30 key, a general purpose function key, as index 30. + /// General-purpose function key. F30, - /// The F31 key, a general purpose function key, as index 31. + /// General-purpose function key. F31, - /// The F32 key, a general purpose function key, as index 32. + /// General-purpose function key. F32, - /// The F33 key, a general purpose function key, as index 33. + /// General-purpose function key. F33, - /// The F34 key, a general purpose function key, as index 34. + /// General-purpose function key. F34, - /// The F35 key, a general purpose function key, as index 35. + /// General-purpose function key. F35, } From b56abf11f1114fcaf72c396b775895cc2823644d Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 10 Jan 2021 19:26:37 +0100 Subject: [PATCH 34/75] Rename the meta key to super --- examples/cursor_grab.rs | 4 +- examples/key_binding.rs | 2 +- examples/multithreaded.rs | 4 +- src/keyboard.rs | 84 ++++++++++++-------- src/platform/windows.rs | 8 +- src/platform_impl/windows/keyboard.rs | 4 +- src/platform_impl/windows/keyboard_layout.rs | 2 +- 7 files changed, 61 insertions(+), 47 deletions(-) diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index 48879dadf4..746bf426ba 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -37,8 +37,8 @@ fn main() { match key { Key::Escape => *control_flow = ControlFlow::Exit, Key::Character(ch) => match ch.to_lowercase().as_str() { - "g" => window.set_cursor_grab(!modifiers.shift()).unwrap(), - "h" => window.set_cursor_visible(modifiers.shift()), + "g" => window.set_cursor_grab(!modifiers.shift_key()).unwrap(), + "h" => window.set_cursor_visible(modifiers.shift_key()), _ => (), }, _ => (), diff --git a/examples/key_binding.rs b/examples/key_binding.rs index 66ca751aba..0083ff311e 100644 --- a/examples/key_binding.rs +++ b/examples/key_binding.rs @@ -46,7 +46,7 @@ fn handle_key_event(modifiers: ModifiersState, event: KeyEvent) { if event.state == ElementState::Pressed && !event.repeat { match event.key_without_modifers() { Key::Character("1") => { - if modifiers.shift() { + if modifiers.shift_key() { println!("Shift + 1 | logical_key: {:?}", event.logical_key); } else { println!("1"); diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 629aff408a..00235b114c 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -65,7 +65,7 @@ fn main() { } => { use Key::{ArrowLeft, ArrowRight, Character}; window.set_title(&format!("{:?}", key)); - let state = !modifiers.shift(); + let state = !modifiers.shift_key(); match &key { // WARNING: Consider using `key_without_modifers()` if available on your platform. // See the `key_binding` example @@ -76,7 +76,7 @@ fn main() { false => CursorIcon::Default, }), "d" => window.set_decorations(!state), - "f" => window.set_fullscreen(match (state, modifiers.alt()) { + "f" => window.set_fullscreen(match (state, modifiers.alt_key()) { (true, false) => Some(Fullscreen::Borderless(None)), (true, true) => Some(Fullscreen::Exclusive( video_modes.iter().nth(video_mode_id).unwrap().clone(), diff --git a/src/keyboard.rs b/src/keyboard.rs index 279c19152b..7136a4366e 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,19 +1,19 @@ impl ModifiersState { /// Returns `true` if the shift key is pressed. - pub fn shift(&self) -> bool { + pub fn shift_key(&self) -> bool { self.intersects(Self::SHIFT) } /// Returns `true` if the control key is pressed. - pub fn control(&self) -> bool { + pub fn control_key(&self) -> bool { self.intersects(Self::CONTROL) } /// Returns `true` if the alt key is pressed. - pub fn alt(&self) -> bool { + pub fn alt_key(&self) -> bool { self.intersects(Self::ALT) } - /// Returns `true` if the meta key is pressed. - pub fn meta(&self) -> bool { - self.intersects(Self::META) + /// Returns `true` if the super key is pressed. + pub fn super_key(&self) -> bool { + self.intersects(Self::SUPER) } } @@ -38,7 +38,7 @@ bitflags! { // const LALT = 0b010 << 6; // const RALT = 0b001 << 6; /// This is the "windows" key on PC and "command" key on Mac. - const META = 0b100 << 9; + const SUPER = 0b100 << 9; // const LLOGO = 0b010 << 9; // const RLOGO = 0b001 << 9; } @@ -53,10 +53,10 @@ mod modifiers_serde { #[serde(default)] #[serde(rename = "ModifiersState")] pub struct ModifiersStateSerialize { - pub shift: bool, - pub control: bool, - pub alt: bool, - pub meta: bool, + pub shift_key: bool, + pub control_key: bool, + pub alt_key: bool, + pub super_key: bool, } impl Serialize for ModifiersState { @@ -65,10 +65,10 @@ mod modifiers_serde { S: Serializer, { let s = ModifiersStateSerialize { - shift: self.shift(), - control: self.control(), - alt: self.alt(), - meta: self.meta(), + shift_key: self.shift_key(), + control_key: self.control_key(), + alt_key: self.alt_key(), + super_key: self.super_key(), }; s.serialize(serializer) } @@ -80,16 +80,16 @@ mod modifiers_serde { D: Deserializer<'de>, { let ModifiersStateSerialize { - shift, - control, - alt, - meta, + shift_key, + control_key, + alt_key, + super_key, } = ModifiersStateSerialize::deserialize(deserializer)?; let mut m = ModifiersState::empty(); - m.set(ModifiersState::SHIFT, shift); - m.set(ModifiersState::CONTROL, control); - m.set(ModifiersState::ALT, alt); - m.set(ModifiersState::META, meta); + m.set(ModifiersState::SHIFT, shift_key); + m.set(ModifiersState::CONTROL, control_key); + m.set(ModifiersState::ALT, alt_key); + m.set(ModifiersState::SUPER, super_key); Ok(m) } } @@ -105,6 +105,15 @@ pub enum NativeKeyCode { XKB(u32), } +/// Represents the location of a physical key. +/// +/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.code`] with a few +/// exceptions: +/// - The keys that the specification calls "MetaLeft" and "MetaRight" are named "SuperLeft" and +/// "SuperRight" here. +/// - The `Unidentified` variant here, can still identifiy a key through it's `NativeKeyCode`. +/// +/// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -240,7 +249,8 @@ pub enum KeyCode { Backspace, /// CapsLock or CapsLock, - /// The application context menu key, which is typically found between the right Meta key and the right Control key. + /// The application context menu key, which is typically found between the right + /// Super key and the right Control key. ContextMenu, /// Control or ControlLeft, @@ -249,9 +259,9 @@ pub enum KeyCode { /// Enter or . Labelled Return on Apple keyboards. Enter, /// The Windows, Command or other OS symbol key. - MetaLeft, + SuperLeft, /// The Windows, Command or other OS symbol key. - MetaRight, + SuperRight, /// Shift or ShiftLeft, /// Shift or @@ -552,8 +562,14 @@ pub enum KeyCode { /// Key represents the meaning of a keypress. /// -/// Specification: -/// https://w3c.github.io/uievents-key/ +/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.key`] with a few +/// exceptions: +/// - The key that the specification calls "Meta" is named "Super" here. +/// - The `Unidentified` variant here, can still identifiy a key through it's `NativeKeyCode`. +/// - The `Dead` variant here, can specify the character which is inserted when pressing the +/// dead-key twice. +/// +/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/ #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -602,17 +618,11 @@ pub enum Key<'a> { /// keyboard to changes some keys' values to an alternate character or function. This key is /// often handled directly in the keyboard hardware and does not usually generate key events. FnLock, - /// The `Meta` key. Used to enable meta modifier function for interpreting concurrent or - /// subsequent keyboard input. This key value is used for the "Windows Logo" key and the Apple - /// `Command` or `⌘` key. - Meta, /// The `NumLock` or Number Lock key. Used to toggle numpad mode function for interpreting /// subsequent keyboard input. NumLock, /// Toggle between scrolling and cursor movement modes. ScrollLock, - /// The `Shift` key - /// /// Used to enable shift modifier function for interpreting concurrent or subsequent keyboard /// input. Shift, @@ -620,6 +630,10 @@ pub enum Key<'a> { Symbol, SymbolLock, Hyper, + /// Used to enable "super" modifier function for interpreting concurrent or subsequent keyboard + /// input. This key value is used for the "Windows Logo" key and the Apple `Command` or `⌘` key. + /// + /// Note: In some contexts (e.g. the Web) this is referred to as the "Meta" key. Super, /// The `Enter` or `↵` key. Used to activate current selection or accept current input. This key /// value is also used for the `Return` (Macintosh numpad) key. This key value is also used for @@ -680,7 +694,7 @@ pub enum Key<'a> { Attn, Cancel, /// Show the application’s context menu. - /// This key is commonly found between the right `Meta` key and the right `Control` key. + /// This key is commonly found between the right `Super` key and the right `Control` key. ContextMenu, /// The `Esc` key. This key was originally used to initiate an escape sequence, but is /// now more generally used to exit or "escape" the current context, such as closing a dialog diff --git a/src/platform/windows.rs b/src/platform/windows.rs index c3f65702ae..e84a17ecdd 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -325,8 +325,8 @@ impl KeyCodeExtScancode for KeyCode { KeyCode::ControlLeft => Some(0x001D), KeyCode::ControlRight => Some(0xE01D), KeyCode::Enter => Some(0x001C), - KeyCode::MetaLeft => Some(0xE05B), - KeyCode::MetaRight => Some(0xE05C), + KeyCode::SuperLeft => Some(0xE05B), + KeyCode::SuperRight => Some(0xE05C), KeyCode::ShiftLeft => Some(0x002A), KeyCode::ShiftRight => Some(0x0036), KeyCode::Space => Some(0x0039), @@ -494,8 +494,8 @@ impl KeyCodeExtScancode for KeyCode { 0x001D => KeyCode::ControlLeft, 0xE01D => KeyCode::ControlRight, 0x001C => KeyCode::Enter, - 0xE05B => KeyCode::MetaLeft, - 0xE05C => KeyCode::MetaRight, + 0xE05B => KeyCode::SuperLeft, + 0xE05C => KeyCode::SuperRight, 0x002A => KeyCode::ShiftLeft, 0x0036 => KeyCode::ShiftRight, 0x0039 => KeyCode::Space, diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 250c0a6a36..5177df5e5e 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -701,8 +701,8 @@ pub fn vkey_to_non_printable( winuser::VK_INSERT => Key::Insert, winuser::VK_DELETE => Key::Delete, winuser::VK_HELP => Key::Help, - winuser::VK_LWIN => Key::Meta, - winuser::VK_RWIN => Key::Meta, + winuser::VK_LWIN => Key::Super, + winuser::VK_RWIN => Key::Super, winuser::VK_APPS => Key::ContextMenu, winuser::VK_SLEEP => Key::Standby, diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 67a8244431..4280298876 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -178,7 +178,7 @@ impl LayoutCache { key_pressed(winuser::VK_MENU) && !filter_out_altgr, ); mods.set( - ModifiersState::META, + ModifiersState::SUPER, key_pressed(winuser::VK_LWIN) || key_pressed(winuser::VK_RWIN), ); mods From 05270954ddddb5ef87188845d86d5075a7bdacd8 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Mon, 11 Jan 2021 21:54:08 +0100 Subject: [PATCH 35/75] Address feedback for the keyboard API --- examples/key_binding.rs | 2 +- src/event.rs | 14 ++++++++------ src/platform/modifier_supplement.rs | 2 +- src/platform/windows.rs | 3 ++- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/examples/key_binding.rs b/examples/key_binding.rs index 0083ff311e..6f9bb1fe83 100644 --- a/examples/key_binding.rs +++ b/examples/key_binding.rs @@ -44,7 +44,7 @@ fn main() { fn handle_key_event(modifiers: ModifiersState, event: KeyEvent) { if event.state == ElementState::Pressed && !event.repeat { - match event.key_without_modifers() { + match event.key_without_modifiers() { Key::Character("1") => { if modifiers.shift_key() { println!("Shift + 1 | logical_key: {:?}", event.logical_key); diff --git a/src/event.rs b/src/event.rs index 97aacf9892..30ec8127fb 100644 --- a/src/event.rs +++ b/src/event.rs @@ -610,12 +610,12 @@ pub struct RawKeyEvent { /// Describes a keyboard input targeting a window. #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEvent { - /// Represents the position of a key independent of the - /// currently active layout. - /// Conforms to https://www.w3.org/TR/uievents-code/ + /// Represents the position of a key independent of the currently active layout. /// - /// Note that `Fn` and `FnLock` key events are not emmited by `winit`. - /// These keys are usually handled at the hardware or at the OS level. + /// It also uniquely identifies the physical key (i.e. it's synonymus with a scancode). + /// + /// Note that `Fn` and `FnLock` key events are not guaranteed to be emmited by `winit`. These + /// keys are usually handled at the hardware or at the OS level. pub physical_key: keyboard::KeyCode, /// This value is affected by all modifiers except Ctrl. @@ -623,11 +623,13 @@ pub struct KeyEvent { /// This has two use cases: /// - Allows querying whether the current input is a Dead key /// - Allows handling key-bindings on platforms which don't - /// support `KeyEventExtModifierSupplement::key_without_modifiers`. + /// support [`key_without_modifiers`]. /// /// ## Platform-specific /// - **Web:** Dead keys might be reported as the real key instead /// of `Dead` depending on the browser/OS. + /// + /// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers pub logical_key: keyboard::Key<'static>, /// Contains the text produced by this keypress. diff --git a/src/platform/modifier_supplement.rs b/src/platform/modifier_supplement.rs index 629a47ae45..26aa956e16 100644 --- a/src/platform/modifier_supplement.rs +++ b/src/platform/modifier_supplement.rs @@ -28,5 +28,5 @@ pub trait KeyEventExtModifierSupplement { /// In case `logical_key` reports `Dead`, this will still report the /// key as `Characcter` according to the current keyboard layout. This value /// cannot be `Dead`. - fn key_without_modifers(&self) -> Key<'static>; + fn key_without_modifiers(&self) -> Key<'static>; } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index e84a17ecdd..21daf0dec4 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -252,7 +252,7 @@ impl KeyEventExtModifierSupplement for KeyEvent { } #[inline] - fn key_without_modifers(&self) -> Key<'static> { + fn key_without_modifiers(&self) -> Key<'static> { self.platform_specific.key_without_modifers } } @@ -426,6 +426,7 @@ impl KeyCodeExtScancode for KeyCode { KeyCode::AudioVolumeDown => Some(0xE02E), KeyCode::AudioVolumeMute => Some(0xE020), KeyCode::AudioVolumeUp => Some(0xE030), + KeyCode::Unidentified(NativeKeyCode::Windows(scancode)) => Some(scancode as u32), _ => None, } } From 1ea30a78dd55b48083b7c1b6c2308ef2bf044033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Art=C3=BAr=20Kov=C3=A1cs?= Date: Sat, 23 Jan 2021 10:27:33 +0100 Subject: [PATCH 36/75] Fix for rustdoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Markus Røyset --- src/keyboard.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 7136a4366e..fefba05b9f 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -128,7 +128,7 @@ pub enum KeyCode { /// ` on a US keyboard. This is the 半角/全角/漢字 /// (hankaku/zenkaku/kanji) key on Japanese keyboards Backquote, - /// Used for both the US \ (on the 101-key layout) and also for the key + /// Used for both the US \\ (on the 101-key layout) and also for the key /// located between the " and Enter keys on row C of the 102-, /// 104- and 106-key layouts. /// Labelled # on a UK (102) keyboard. @@ -162,13 +162,13 @@ pub enum KeyCode { /// = on a US keyboard. Equal, /// Located between the left Shift and Z keys. - /// Labelled \ on a UK keyboard. + /// Labelled \\ on a UK keyboard. IntlBackslash, /// Located between the / and right Shift keys. - /// Labelled \ (ro) on a Japanese keyboard. + /// Labelled \\ (ro) on a Japanese keyboard. IntlRo, /// Located between the = and Backspace keys. - /// Labelled ¥ (yen) on a Japanese keyboard. \ on a + /// Labelled ¥ (yen) on a Japanese keyboard. \\ on a /// Russian keyboard. IntlYen, /// a on a US keyboard. From 94eb458065077a0703869683485dcb8a11671b37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Art=C3=BAr=20Kov=C3=A1cs?= Date: Sat, 23 Jan 2021 10:44:04 +0100 Subject: [PATCH 37/75] Improve documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Markus Røyset --- src/event.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/event.rs b/src/event.rs index 30ec8127fb..ed7582528f 100644 --- a/src/event.rs +++ b/src/event.rs @@ -242,8 +242,9 @@ pub enum WindowEvent<'a> { /// The user commited an IME string for this window. /// - /// This is a temporary API until #1497 gets completed. See: - /// https://github.com/rust-windowing/winit/issues/1497 + /// This is a temporary API until [#1497] gets completed. + /// + /// [#1497]: https://github.com/rust-windowing/winit/issues/1497 ReceivedImeText(String), /// The window gained or lost focus. From 9c3025db5345d3cdf21ad557bc3236f4f88e73b1 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 23 Jan 2021 13:01:45 +0100 Subject: [PATCH 38/75] Fix for arrow keys being reported as unidentified. And minor improvements --- Cargo.toml | 1 + src/keyboard.rs | 30 +- src/platform_impl/windows/keyboard.rs | 269 +----------- src/platform_impl/windows/keyboard_layout.rs | 427 ++++++++++++++++++- 4 files changed, 453 insertions(+), 274 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c33f533f95..eeb63a56df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ log = "0.4" serde = { version = "1", optional = true, features = ["serde_derive"] } raw-window-handle = "0.3" bitflags = "1" +nameof = "1" [dev-dependencies] image = "0.23.12" diff --git a/src/keyboard.rs b/src/keyboard.rs index fefba05b9f..7eb4d19918 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,3 +1,7 @@ +//! Types related to the keyboard. + +use nameof::name_of; + impl ModifiersState { /// Returns `true` if the shift key is pressed. pub fn shift_key(&self) -> bool { @@ -96,7 +100,7 @@ mod modifiers_serde { } /// Contains the platform-native physical key identifier (aka scancode) -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum NativeKeyCode { Unidentified, @@ -104,6 +108,30 @@ pub enum NativeKeyCode { MacOS(u32), XKB(u32), } +impl std::fmt::Debug for NativeKeyCode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use NativeKeyCode::{MacOS, Unidentified, Windows, XKB}; + let mut debug_tuple; + match self { + Unidentified => { + debug_tuple = f.debug_tuple(name_of!(Unidentified)); + } + Windows(v) => { + debug_tuple = f.debug_tuple(name_of!(Windows)); + debug_tuple.field(&format_args!("0x{:04X}", v)); + } + MacOS(v) => { + debug_tuple = f.debug_tuple(name_of!(MacOS)); + debug_tuple.field(v); + } + XKB(v) => { + debug_tuple = f.debug_tuple(name_of!(XKB)); + debug_tuple.field(v); + } + } + debug_tuple.finish() + } +} /// Represents the location of a physical key. /// diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 5177df5e5e..1727a43594 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -5,13 +5,10 @@ use std::{ use winapi::{ shared::{ - minwindef::{HKL, LOWORD, LPARAM, LRESULT, UINT, WPARAM}, + minwindef::{HKL, LPARAM, LRESULT, UINT, WPARAM}, windef::HWND, }, - um::{ - winnt::{LANG_JAPANESE, LANG_KOREAN, PRIMARYLANGID}, - winuser, - }, + um::winuser, }; use crate::{ @@ -620,265 +617,3 @@ pub fn get_location(vkey: c_int, extended: bool) -> KeyLocation { _ => KeyLocation::Standard, } } - -/// This includes all non-character keys defined within `Key` so for example -/// backspace and tab are included. -pub fn vkey_to_non_printable( - vkey: i32, - native_code: NativeKeyCode, - code: KeyCode, - hkl: u64, - has_alt_graph: bool, -) -> Key<'static> { - // List of the Web key names and their corresponding platform-native key names: - // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values - - // Some keys cannot be correctly determined based on the virtual key. - // Therefore we use the `code` to translate those keys. - match code { - KeyCode::NumLock => return Key::NumLock, - KeyCode::Pause => return Key::Pause, - _ => (), - } - - let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); - let is_korean = primary_lang_id == LANG_KOREAN; - let is_japanese = primary_lang_id == LANG_JAPANESE; - - match vkey { - winuser::VK_LBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse - winuser::VK_RBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse - - // I don't think this can be represented with a Key - winuser::VK_CANCEL => Key::Unidentified(native_code), - - winuser::VK_MBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse - winuser::VK_XBUTTON1 => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse - winuser::VK_XBUTTON2 => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse - winuser::VK_BACK => Key::Backspace, - winuser::VK_TAB => Key::Tab, - winuser::VK_CLEAR => Key::Clear, - winuser::VK_RETURN => Key::Enter, - winuser::VK_SHIFT => Key::Shift, - winuser::VK_CONTROL => Key::Control, - winuser::VK_MENU => Key::Alt, - winuser::VK_PAUSE => Key::Pause, - winuser::VK_CAPITAL => Key::CapsLock, - - //winuser::VK_HANGEUL => Key::HangulMode, // Deprecated in favour of VK_HANGUL - - // VK_HANGUL and VK_KANA are defined as the same constant therefore - // we use appropriate conditions to differentate between them - winuser::VK_HANGUL if is_korean => Key::HangulMode, - winuser::VK_KANA if is_japanese => Key::KanaMode, - - winuser::VK_JUNJA => Key::JunjaMode, - winuser::VK_FINAL => Key::FinalMode, - - // VK_HANJA and VK_KANJI are defined as the same constant therefore - // we use appropriate conditions to differentate between them - winuser::VK_HANJA if is_korean => Key::HanjaMode, - winuser::VK_KANJI if is_japanese => Key::KanjiMode, - - winuser::VK_ESCAPE => Key::Escape, - winuser::VK_CONVERT => Key::Convert, - winuser::VK_NONCONVERT => Key::NonConvert, - winuser::VK_ACCEPT => Key::Accept, - winuser::VK_MODECHANGE => Key::ModeChange, - winuser::VK_SPACE => Key::Unidentified(native_code), // This function only converts "non-printable" - winuser::VK_PRIOR => Key::PageUp, - winuser::VK_NEXT => Key::PageDown, - winuser::VK_END => Key::End, - winuser::VK_HOME => Key::Home, - winuser::VK_LEFT => Key::ArrowLeft, - winuser::VK_UP => Key::ArrowUp, - winuser::VK_RIGHT => Key::ArrowRight, - winuser::VK_DOWN => Key::ArrowDown, - winuser::VK_SELECT => Key::Select, - winuser::VK_PRINT => Key::Print, - winuser::VK_EXECUTE => Key::Execute, - winuser::VK_SNAPSHOT => Key::PrintScreen, - winuser::VK_INSERT => Key::Insert, - winuser::VK_DELETE => Key::Delete, - winuser::VK_HELP => Key::Help, - winuser::VK_LWIN => Key::Super, - winuser::VK_RWIN => Key::Super, - winuser::VK_APPS => Key::ContextMenu, - winuser::VK_SLEEP => Key::Standby, - - // This function only converts "non-printable" - winuser::VK_NUMPAD0 => Key::Unidentified(native_code), - winuser::VK_NUMPAD1 => Key::Unidentified(native_code), - winuser::VK_NUMPAD2 => Key::Unidentified(native_code), - winuser::VK_NUMPAD3 => Key::Unidentified(native_code), - winuser::VK_NUMPAD4 => Key::Unidentified(native_code), - winuser::VK_NUMPAD5 => Key::Unidentified(native_code), - winuser::VK_NUMPAD6 => Key::Unidentified(native_code), - winuser::VK_NUMPAD7 => Key::Unidentified(native_code), - winuser::VK_NUMPAD8 => Key::Unidentified(native_code), - winuser::VK_NUMPAD9 => Key::Unidentified(native_code), - winuser::VK_MULTIPLY => Key::Unidentified(native_code), - winuser::VK_ADD => Key::Unidentified(native_code), - winuser::VK_SEPARATOR => Key::Unidentified(native_code), - winuser::VK_SUBTRACT => Key::Unidentified(native_code), - winuser::VK_DECIMAL => Key::Unidentified(native_code), - winuser::VK_DIVIDE => Key::Unidentified(native_code), - - winuser::VK_F1 => Key::F1, - winuser::VK_F2 => Key::F2, - winuser::VK_F3 => Key::F3, - winuser::VK_F4 => Key::F4, - winuser::VK_F5 => Key::F5, - winuser::VK_F6 => Key::F6, - winuser::VK_F7 => Key::F7, - winuser::VK_F8 => Key::F8, - winuser::VK_F9 => Key::F9, - winuser::VK_F10 => Key::F10, - winuser::VK_F11 => Key::F11, - winuser::VK_F12 => Key::F12, - winuser::VK_F13 => Key::F13, - winuser::VK_F14 => Key::F14, - winuser::VK_F15 => Key::F15, - winuser::VK_F16 => Key::F16, - winuser::VK_F17 => Key::F17, - winuser::VK_F18 => Key::F18, - winuser::VK_F19 => Key::F19, - winuser::VK_F20 => Key::F20, - winuser::VK_F21 => Key::F21, - winuser::VK_F22 => Key::F22, - winuser::VK_F23 => Key::F23, - winuser::VK_F24 => Key::F24, - winuser::VK_NAVIGATION_VIEW => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_MENU => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_UP => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_DOWN => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_LEFT => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_RIGHT => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_ACCEPT => Key::Unidentified(native_code), - winuser::VK_NAVIGATION_CANCEL => Key::Unidentified(native_code), - winuser::VK_NUMLOCK => Key::NumLock, - winuser::VK_SCROLL => Key::ScrollLock, - winuser::VK_OEM_NEC_EQUAL => Key::Unidentified(native_code), - //winuser::VK_OEM_FJ_JISHO => Key::Unidentified(native_code), // Conflicts with `VK_OEM_NEC_EQUAL` - winuser::VK_OEM_FJ_MASSHOU => Key::Unidentified(native_code), - winuser::VK_OEM_FJ_TOUROKU => Key::Unidentified(native_code), - winuser::VK_OEM_FJ_LOYA => Key::Unidentified(native_code), - winuser::VK_OEM_FJ_ROYA => Key::Unidentified(native_code), - winuser::VK_LSHIFT => Key::Shift, - winuser::VK_RSHIFT => Key::Shift, - winuser::VK_LCONTROL => Key::Control, - winuser::VK_RCONTROL => Key::Control, - winuser::VK_LMENU => Key::Alt, - winuser::VK_RMENU => { - if has_alt_graph { - Key::AltGraph - } else { - Key::Alt - } - } - winuser::VK_BROWSER_BACK => Key::BrowserBack, - winuser::VK_BROWSER_FORWARD => Key::BrowserForward, - winuser::VK_BROWSER_REFRESH => Key::BrowserRefresh, - winuser::VK_BROWSER_STOP => Key::BrowserStop, - winuser::VK_BROWSER_SEARCH => Key::BrowserSearch, - winuser::VK_BROWSER_FAVORITES => Key::BrowserFavorites, - winuser::VK_BROWSER_HOME => Key::BrowserHome, - winuser::VK_VOLUME_MUTE => Key::AudioVolumeMute, - winuser::VK_VOLUME_DOWN => Key::AudioVolumeDown, - winuser::VK_VOLUME_UP => Key::AudioVolumeUp, - winuser::VK_MEDIA_NEXT_TRACK => Key::MediaTrackNext, - winuser::VK_MEDIA_PREV_TRACK => Key::MediaTrackPrevious, - winuser::VK_MEDIA_STOP => Key::MediaStop, - winuser::VK_MEDIA_PLAY_PAUSE => Key::MediaPlayPause, - winuser::VK_LAUNCH_MAIL => Key::LaunchMail, - winuser::VK_LAUNCH_MEDIA_SELECT => Key::LaunchMediaPlayer, - winuser::VK_LAUNCH_APP1 => Key::LaunchApplication1, - winuser::VK_LAUNCH_APP2 => Key::LaunchApplication2, - - // This function only converts "non-printable" - winuser::VK_OEM_1 => Key::Unidentified(native_code), - winuser::VK_OEM_PLUS => Key::Unidentified(native_code), - winuser::VK_OEM_COMMA => Key::Unidentified(native_code), - winuser::VK_OEM_MINUS => Key::Unidentified(native_code), - winuser::VK_OEM_PERIOD => Key::Unidentified(native_code), - winuser::VK_OEM_2 => Key::Unidentified(native_code), - winuser::VK_OEM_3 => Key::Unidentified(native_code), - - winuser::VK_GAMEPAD_A => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_B => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_X => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_Y => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_SHOULDER => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_SHOULDER => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_TRIGGER => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_TRIGGER => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_DPAD_UP => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_DPAD_DOWN => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_DPAD_LEFT => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_DPAD_RIGHT => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_MENU => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_VIEW => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_UP => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_DOWN => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_LEFT_THUMBSTICK_LEFT => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_UP => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT => Key::Unidentified(native_code), - winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT => Key::Unidentified(native_code), - - // This function only converts "non-printable" - winuser::VK_OEM_4 => Key::Unidentified(native_code), - winuser::VK_OEM_5 => Key::Unidentified(native_code), - winuser::VK_OEM_6 => Key::Unidentified(native_code), - winuser::VK_OEM_7 => Key::Unidentified(native_code), - winuser::VK_OEM_8 => Key::Unidentified(native_code), - winuser::VK_OEM_AX => Key::Unidentified(native_code), - winuser::VK_OEM_102 => Key::Unidentified(native_code), - - winuser::VK_ICO_HELP => Key::Unidentified(native_code), - winuser::VK_ICO_00 => Key::Unidentified(native_code), - - winuser::VK_PROCESSKEY => Key::Process, - - winuser::VK_ICO_CLEAR => Key::Unidentified(native_code), - winuser::VK_PACKET => Key::Unidentified(native_code), - winuser::VK_OEM_RESET => Key::Unidentified(native_code), - winuser::VK_OEM_JUMP => Key::Unidentified(native_code), - winuser::VK_OEM_PA1 => Key::Unidentified(native_code), - winuser::VK_OEM_PA2 => Key::Unidentified(native_code), - winuser::VK_OEM_PA3 => Key::Unidentified(native_code), - winuser::VK_OEM_WSCTRL => Key::Unidentified(native_code), - winuser::VK_OEM_CUSEL => Key::Unidentified(native_code), - - winuser::VK_OEM_ATTN => Key::Attn, - winuser::VK_OEM_FINISH => { - if is_japanese { - Key::Katakana - } else { - // This matches IE and Firefox behaviour according to - // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values - // At the time of writing there is no `Key::Finish` variant as - // Finish is not mentionned at https://w3c.github.io/uievents-key/ - // Also see: https://github.com/pyfisch/keyboard-types/issues/9 - Key::Unidentified(native_code) - } - } - winuser::VK_OEM_COPY => Key::Copy, - winuser::VK_OEM_AUTO => Key::Hankaku, - winuser::VK_OEM_ENLW => Key::Zenkaku, - winuser::VK_OEM_BACKTAB => Key::Romaji, - winuser::VK_ATTN => Key::KanaMode, - winuser::VK_CRSEL => Key::CrSel, - winuser::VK_EXSEL => Key::ExSel, - winuser::VK_EREOF => Key::EraseEof, - winuser::VK_PLAY => Key::Play, - winuser::VK_ZOOM => Key::ZoomToggle, - winuser::VK_NONAME => Key::Unidentified(native_code), - winuser::VK_PA1 => Key::Unidentified(native_code), - winuser::VK_OEM_CLEAR => Key::Clear, - _ => Key::Unidentified(native_code), - } -} diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 4280298876..a30a40fbad 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -7,12 +7,19 @@ use std::{ use lazy_static::lazy_static; -use winapi::{ctypes::c_int, shared::minwindef::HKL, um::winuser}; +use winapi::{ + ctypes::c_int, + shared::minwindef::{HKL, LOWORD}, + um::{ + winnt::{LANG_JAPANESE, LANG_KOREAN, PRIMARYLANGID}, + winuser, + }, +}; use crate::{ keyboard::{Key, KeyCode, ModifiersState, NativeKeyCode}, platform::scancode::KeyCodeExtScancode, - platform_impl::platform::keyboard::{vkey_to_non_printable, ExScancode}, + platform_impl::platform::keyboard::ExScancode, }; lazy_static! { @@ -107,6 +114,7 @@ impl WindowsModifiers { } pub struct Layout { + pub hkl: u64, /// Maps a modifier state to group of key strings /// Not using `ModifiersState` here because that object cannot express caps lock /// but we need to handle caps lock too. @@ -128,10 +136,14 @@ impl Layout { scancode: ExScancode, keycode: KeyCode, ) -> Key<'static> { - // let ctrl_alt: WindowsModifiers = WindowsModifiers::CONTROL | WindowsModifiers::ALT; - // if self.has_alt_graph && mods.contains(ctrl_alt) { - - // } + // This feels much like a hack as one would assume that the `keys` map contains every key + // mapping. However `MapVirtualKeyExW` sometimes maps virtual keys to odd scancodes that + // don't match the scancode coming from the KEYDOWN message for the same key. + // For example: + // `VK_LEFT` is mapped to `0x004B`, but the scancode for the left arrow is `0xE04B`. + if let Some(key) = keycode_to_non_character_key(keycode, self.has_alt_graph, self.hkl) { + return key; + } if let Some(keys) = self.keys.get(&mods) { if let Some(key) = keys.get(&keycode) { @@ -186,6 +198,7 @@ impl LayoutCache { fn prepare_layout(strings: &mut HashSet<&'static str>, locale_id: u64) -> Layout { let mut layout = Layout { + hkl: locale_id, keys: Default::default(), has_alt_graph: false, }; @@ -362,3 +375,405 @@ enum ToUnicodeResult { Dead(Option), None, } + +fn keycode_to_non_character_key( + keycode: KeyCode, + has_alt_graph: bool, + hkl: u64, +) -> Option> { + let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); + let is_korean = primary_lang_id == LANG_KOREAN; + match keycode { + KeyCode::AltLeft => Some(Key::Alt), + KeyCode::AltRight => { + if has_alt_graph { + Some(Key::AltGraph) + } else { + Some(Key::Alt) + } + } + KeyCode::Backspace => Some(Key::Backspace), + KeyCode::CapsLock => Some(Key::CapsLock), + KeyCode::ContextMenu => Some(Key::ContextMenu), + KeyCode::ControlLeft => Some(Key::Control), + KeyCode::ControlRight => Some(Key::Control), + KeyCode::Enter => Some(Key::Enter), + KeyCode::SuperLeft => Some(Key::Super), + KeyCode::SuperRight => Some(Key::Super), + KeyCode::ShiftLeft => Some(Key::Shift), + KeyCode::ShiftRight => Some(Key::Shift), + KeyCode::Tab => Some(Key::Tab), + KeyCode::Convert => Some(Key::Convert), + KeyCode::KanaMode => Some(Key::KanaMode), + KeyCode::Lang1 => { + if is_korean { + Some(Key::HangulMode) + } else { + Some(Key::KanaMode) + } + } + KeyCode::Lang2 => { + if is_korean { + Some(Key::HanjaMode) + } else { + Some(Key::Eisu) + } + } + KeyCode::Lang3 => Some(Key::Katakana), + KeyCode::Lang4 => Some(Key::Hiragana), + KeyCode::Lang5 => Some(Key::ZenkakuHankaku), + KeyCode::NonConvert => Some(Key::NonConvert), + KeyCode::Delete => Some(Key::Delete), + KeyCode::End => Some(Key::End), + KeyCode::Help => Some(Key::Help), + KeyCode::Home => Some(Key::Home), + KeyCode::Insert => Some(Key::Insert), + KeyCode::PageDown => Some(Key::PageDown), + KeyCode::PageUp => Some(Key::PageUp), + KeyCode::ArrowDown => Some(Key::ArrowDown), + KeyCode::ArrowLeft => Some(Key::ArrowLeft), + KeyCode::ArrowRight => Some(Key::ArrowRight), + KeyCode::ArrowUp => Some(Key::ArrowUp), + KeyCode::NumLock => Some(Key::NumLock), + KeyCode::NumpadClear => Some(Key::Clear), + KeyCode::NumpadEnter => Some(Key::Enter), + KeyCode::Escape => Some(Key::Escape), + KeyCode::Fn => Some(Key::Fn), + KeyCode::FnLock => Some(Key::FnLock), + KeyCode::PrintScreen => Some(Key::PrintScreen), + KeyCode::ScrollLock => Some(Key::ScrollLock), + KeyCode::Pause => Some(Key::Pause), + KeyCode::BrowserBack => Some(Key::BrowserBack), + KeyCode::BrowserFavorites => Some(Key::BrowserFavorites), + KeyCode::BrowserForward => Some(Key::BrowserForward), + KeyCode::BrowserHome => Some(Key::BrowserHome), + KeyCode::BrowserRefresh => Some(Key::BrowserRefresh), + KeyCode::BrowserSearch => Some(Key::BrowserSearch), + KeyCode::BrowserStop => Some(Key::BrowserStop), + KeyCode::Eject => Some(Key::Eject), + KeyCode::LaunchApp1 => Some(Key::LaunchApplication1), + KeyCode::LaunchApp2 => Some(Key::LaunchApplication2), + KeyCode::LaunchMail => Some(Key::LaunchMail), + KeyCode::MediaPlayPause => Some(Key::MediaPlayPause), + KeyCode::MediaStop => Some(Key::MediaStop), + KeyCode::MediaTrackNext => Some(Key::MediaTrackNext), + KeyCode::MediaTrackPrevious => Some(Key::MediaTrackPrevious), + KeyCode::Power => Some(Key::Power), + KeyCode::Sleep => Some(Key::Standby), + KeyCode::AudioVolumeDown => Some(Key::AudioVolumeDown), + KeyCode::AudioVolumeMute => Some(Key::AudioVolumeMute), + KeyCode::AudioVolumeUp => Some(Key::AudioVolumeUp), + KeyCode::WakeUp => Some(Key::WakeUp), + KeyCode::Hyper => Some(Key::Hyper), + KeyCode::Super => Some(Key::Super), + KeyCode::Again => Some(Key::Again), + KeyCode::Copy => Some(Key::Copy), + KeyCode::Cut => Some(Key::Cut), + KeyCode::Find => Some(Key::Find), + KeyCode::Open => Some(Key::Open), + KeyCode::Paste => Some(Key::Paste), + KeyCode::Props => Some(Key::Props), + KeyCode::Select => Some(Key::Select), + KeyCode::Undo => Some(Key::Undo), + KeyCode::Hiragana => Some(Key::Hiragana), + KeyCode::Katakana => Some(Key::Katakana), + KeyCode::F1 => Some(Key::F1), + KeyCode::F2 => Some(Key::F2), + KeyCode::F3 => Some(Key::F3), + KeyCode::F4 => Some(Key::F4), + KeyCode::F5 => Some(Key::F5), + KeyCode::F6 => Some(Key::F6), + KeyCode::F7 => Some(Key::F7), + KeyCode::F8 => Some(Key::F8), + KeyCode::F9 => Some(Key::F9), + KeyCode::F10 => Some(Key::F10), + KeyCode::F11 => Some(Key::F11), + KeyCode::F12 => Some(Key::F12), + KeyCode::F13 => Some(Key::F13), + KeyCode::F14 => Some(Key::F14), + KeyCode::F15 => Some(Key::F15), + KeyCode::F16 => Some(Key::F16), + KeyCode::F17 => Some(Key::F17), + KeyCode::F18 => Some(Key::F18), + KeyCode::F19 => Some(Key::F19), + KeyCode::F20 => Some(Key::F20), + KeyCode::F21 => Some(Key::F21), + KeyCode::F22 => Some(Key::F22), + KeyCode::F23 => Some(Key::F23), + KeyCode::F24 => Some(Key::F24), + KeyCode::F25 => Some(Key::F25), + KeyCode::F26 => Some(Key::F26), + KeyCode::F27 => Some(Key::F27), + KeyCode::F28 => Some(Key::F28), + KeyCode::F29 => Some(Key::F29), + KeyCode::F30 => Some(Key::F30), + KeyCode::F31 => Some(Key::F31), + KeyCode::F32 => Some(Key::F32), + KeyCode::F33 => Some(Key::F33), + KeyCode::F34 => Some(Key::F34), + KeyCode::F35 => Some(Key::F35), + _ => None, + } +} + +/// This includes all non-character keys defined within `Key` so for example +/// backspace and tab are included. +fn vkey_to_non_printable( + vkey: i32, + native_code: NativeKeyCode, + code: KeyCode, + hkl: u64, + has_alt_graph: bool, +) -> Key<'static> { + // List of the Web key names and their corresponding platform-native key names: + // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values + + // Some keys cannot be correctly determined based on the virtual key. + // Therefore we use the `code` to translate those keys. + match code { + KeyCode::NumLock => return Key::NumLock, + KeyCode::Pause => return Key::Pause, + _ => (), + } + + let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); + let is_korean = primary_lang_id == LANG_KOREAN; + let is_japanese = primary_lang_id == LANG_JAPANESE; + + match vkey { + winuser::VK_LBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + winuser::VK_RBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + + // I don't think this can be represented with a Key + winuser::VK_CANCEL => Key::Unidentified(native_code), + + winuser::VK_MBUTTON => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + winuser::VK_XBUTTON1 => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + winuser::VK_XBUTTON2 => Key::Unidentified(NativeKeyCode::Unidentified), // Mouse + winuser::VK_BACK => Key::Backspace, + winuser::VK_TAB => Key::Tab, + winuser::VK_CLEAR => Key::Clear, + winuser::VK_RETURN => Key::Enter, + winuser::VK_SHIFT => Key::Shift, + winuser::VK_CONTROL => Key::Control, + winuser::VK_MENU => Key::Alt, + winuser::VK_PAUSE => Key::Pause, + winuser::VK_CAPITAL => Key::CapsLock, + + //winuser::VK_HANGEUL => Key::HangulMode, // Deprecated in favour of VK_HANGUL + + // VK_HANGUL and VK_KANA are defined as the same constant therefore + // we use appropriate conditions to differentate between them + winuser::VK_HANGUL if is_korean => Key::HangulMode, + winuser::VK_KANA if is_japanese => Key::KanaMode, + + winuser::VK_JUNJA => Key::JunjaMode, + winuser::VK_FINAL => Key::FinalMode, + + // VK_HANJA and VK_KANJI are defined as the same constant therefore + // we use appropriate conditions to differentate between them + winuser::VK_HANJA if is_korean => Key::HanjaMode, + winuser::VK_KANJI if is_japanese => Key::KanjiMode, + + winuser::VK_ESCAPE => Key::Escape, + winuser::VK_CONVERT => Key::Convert, + winuser::VK_NONCONVERT => Key::NonConvert, + winuser::VK_ACCEPT => Key::Accept, + winuser::VK_MODECHANGE => Key::ModeChange, + winuser::VK_SPACE => Key::Unidentified(native_code), // This function only converts "non-printable" + winuser::VK_PRIOR => Key::PageUp, + winuser::VK_NEXT => Key::PageDown, + winuser::VK_END => Key::End, + winuser::VK_HOME => Key::Home, + winuser::VK_LEFT => Key::ArrowLeft, + winuser::VK_UP => Key::ArrowUp, + winuser::VK_RIGHT => Key::ArrowRight, + winuser::VK_DOWN => Key::ArrowDown, + winuser::VK_SELECT => Key::Select, + winuser::VK_PRINT => Key::Print, + winuser::VK_EXECUTE => Key::Execute, + winuser::VK_SNAPSHOT => Key::PrintScreen, + winuser::VK_INSERT => Key::Insert, + winuser::VK_DELETE => Key::Delete, + winuser::VK_HELP => Key::Help, + winuser::VK_LWIN => Key::Super, + winuser::VK_RWIN => Key::Super, + winuser::VK_APPS => Key::ContextMenu, + winuser::VK_SLEEP => Key::Standby, + + // This function only converts "non-printable" + winuser::VK_NUMPAD0 => Key::Unidentified(native_code), + winuser::VK_NUMPAD1 => Key::Unidentified(native_code), + winuser::VK_NUMPAD2 => Key::Unidentified(native_code), + winuser::VK_NUMPAD3 => Key::Unidentified(native_code), + winuser::VK_NUMPAD4 => Key::Unidentified(native_code), + winuser::VK_NUMPAD5 => Key::Unidentified(native_code), + winuser::VK_NUMPAD6 => Key::Unidentified(native_code), + winuser::VK_NUMPAD7 => Key::Unidentified(native_code), + winuser::VK_NUMPAD8 => Key::Unidentified(native_code), + winuser::VK_NUMPAD9 => Key::Unidentified(native_code), + winuser::VK_MULTIPLY => Key::Unidentified(native_code), + winuser::VK_ADD => Key::Unidentified(native_code), + winuser::VK_SEPARATOR => Key::Unidentified(native_code), + winuser::VK_SUBTRACT => Key::Unidentified(native_code), + winuser::VK_DECIMAL => Key::Unidentified(native_code), + winuser::VK_DIVIDE => Key::Unidentified(native_code), + + winuser::VK_F1 => Key::F1, + winuser::VK_F2 => Key::F2, + winuser::VK_F3 => Key::F3, + winuser::VK_F4 => Key::F4, + winuser::VK_F5 => Key::F5, + winuser::VK_F6 => Key::F6, + winuser::VK_F7 => Key::F7, + winuser::VK_F8 => Key::F8, + winuser::VK_F9 => Key::F9, + winuser::VK_F10 => Key::F10, + winuser::VK_F11 => Key::F11, + winuser::VK_F12 => Key::F12, + winuser::VK_F13 => Key::F13, + winuser::VK_F14 => Key::F14, + winuser::VK_F15 => Key::F15, + winuser::VK_F16 => Key::F16, + winuser::VK_F17 => Key::F17, + winuser::VK_F18 => Key::F18, + winuser::VK_F19 => Key::F19, + winuser::VK_F20 => Key::F20, + winuser::VK_F21 => Key::F21, + winuser::VK_F22 => Key::F22, + winuser::VK_F23 => Key::F23, + winuser::VK_F24 => Key::F24, + winuser::VK_NAVIGATION_VIEW => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_MENU => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_UP => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_DOWN => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_LEFT => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_RIGHT => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_ACCEPT => Key::Unidentified(native_code), + winuser::VK_NAVIGATION_CANCEL => Key::Unidentified(native_code), + winuser::VK_NUMLOCK => Key::NumLock, + winuser::VK_SCROLL => Key::ScrollLock, + winuser::VK_OEM_NEC_EQUAL => Key::Unidentified(native_code), + //winuser::VK_OEM_FJ_JISHO => Key::Unidentified(native_code), // Conflicts with `VK_OEM_NEC_EQUAL` + winuser::VK_OEM_FJ_MASSHOU => Key::Unidentified(native_code), + winuser::VK_OEM_FJ_TOUROKU => Key::Unidentified(native_code), + winuser::VK_OEM_FJ_LOYA => Key::Unidentified(native_code), + winuser::VK_OEM_FJ_ROYA => Key::Unidentified(native_code), + winuser::VK_LSHIFT => Key::Shift, + winuser::VK_RSHIFT => Key::Shift, + winuser::VK_LCONTROL => Key::Control, + winuser::VK_RCONTROL => Key::Control, + winuser::VK_LMENU => Key::Alt, + winuser::VK_RMENU => { + if has_alt_graph { + Key::AltGraph + } else { + Key::Alt + } + } + winuser::VK_BROWSER_BACK => Key::BrowserBack, + winuser::VK_BROWSER_FORWARD => Key::BrowserForward, + winuser::VK_BROWSER_REFRESH => Key::BrowserRefresh, + winuser::VK_BROWSER_STOP => Key::BrowserStop, + winuser::VK_BROWSER_SEARCH => Key::BrowserSearch, + winuser::VK_BROWSER_FAVORITES => Key::BrowserFavorites, + winuser::VK_BROWSER_HOME => Key::BrowserHome, + winuser::VK_VOLUME_MUTE => Key::AudioVolumeMute, + winuser::VK_VOLUME_DOWN => Key::AudioVolumeDown, + winuser::VK_VOLUME_UP => Key::AudioVolumeUp, + winuser::VK_MEDIA_NEXT_TRACK => Key::MediaTrackNext, + winuser::VK_MEDIA_PREV_TRACK => Key::MediaTrackPrevious, + winuser::VK_MEDIA_STOP => Key::MediaStop, + winuser::VK_MEDIA_PLAY_PAUSE => Key::MediaPlayPause, + winuser::VK_LAUNCH_MAIL => Key::LaunchMail, + winuser::VK_LAUNCH_MEDIA_SELECT => Key::LaunchMediaPlayer, + winuser::VK_LAUNCH_APP1 => Key::LaunchApplication1, + winuser::VK_LAUNCH_APP2 => Key::LaunchApplication2, + + // This function only converts "non-printable" + winuser::VK_OEM_1 => Key::Unidentified(native_code), + winuser::VK_OEM_PLUS => Key::Unidentified(native_code), + winuser::VK_OEM_COMMA => Key::Unidentified(native_code), + winuser::VK_OEM_MINUS => Key::Unidentified(native_code), + winuser::VK_OEM_PERIOD => Key::Unidentified(native_code), + winuser::VK_OEM_2 => Key::Unidentified(native_code), + winuser::VK_OEM_3 => Key::Unidentified(native_code), + + winuser::VK_GAMEPAD_A => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_B => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_X => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_Y => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_SHOULDER => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_SHOULDER => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_TRIGGER => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_TRIGGER => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_DPAD_UP => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_DPAD_DOWN => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_DPAD_LEFT => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_DPAD_RIGHT => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_MENU => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_VIEW => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_UP => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_DOWN => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_LEFT_THUMBSTICK_LEFT => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_UP => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT => Key::Unidentified(native_code), + winuser::VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT => Key::Unidentified(native_code), + + // This function only converts "non-printable" + winuser::VK_OEM_4 => Key::Unidentified(native_code), + winuser::VK_OEM_5 => Key::Unidentified(native_code), + winuser::VK_OEM_6 => Key::Unidentified(native_code), + winuser::VK_OEM_7 => Key::Unidentified(native_code), + winuser::VK_OEM_8 => Key::Unidentified(native_code), + winuser::VK_OEM_AX => Key::Unidentified(native_code), + winuser::VK_OEM_102 => Key::Unidentified(native_code), + + winuser::VK_ICO_HELP => Key::Unidentified(native_code), + winuser::VK_ICO_00 => Key::Unidentified(native_code), + + winuser::VK_PROCESSKEY => Key::Process, + + winuser::VK_ICO_CLEAR => Key::Unidentified(native_code), + winuser::VK_PACKET => Key::Unidentified(native_code), + winuser::VK_OEM_RESET => Key::Unidentified(native_code), + winuser::VK_OEM_JUMP => Key::Unidentified(native_code), + winuser::VK_OEM_PA1 => Key::Unidentified(native_code), + winuser::VK_OEM_PA2 => Key::Unidentified(native_code), + winuser::VK_OEM_PA3 => Key::Unidentified(native_code), + winuser::VK_OEM_WSCTRL => Key::Unidentified(native_code), + winuser::VK_OEM_CUSEL => Key::Unidentified(native_code), + + winuser::VK_OEM_ATTN => Key::Attn, + winuser::VK_OEM_FINISH => { + if is_japanese { + Key::Katakana + } else { + // This matches IE and Firefox behaviour according to + // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values + // At the time of writing there is no `Key::Finish` variant as + // Finish is not mentionned at https://w3c.github.io/uievents-key/ + // Also see: https://github.com/pyfisch/keyboard-types/issues/9 + Key::Unidentified(native_code) + } + } + winuser::VK_OEM_COPY => Key::Copy, + winuser::VK_OEM_AUTO => Key::Hankaku, + winuser::VK_OEM_ENLW => Key::Zenkaku, + winuser::VK_OEM_BACKTAB => Key::Romaji, + winuser::VK_ATTN => Key::KanaMode, + winuser::VK_CRSEL => Key::CrSel, + winuser::VK_EXSEL => Key::ExSel, + winuser::VK_EREOF => Key::EraseEof, + winuser::VK_PLAY => Key::Play, + winuser::VK_ZOOM => Key::ZoomToggle, + winuser::VK_NONAME => Key::Unidentified(native_code), + winuser::VK_PA1 => Key::Unidentified(native_code), + winuser::VK_OEM_CLEAR => Key::Clear, + _ => Key::Unidentified(native_code), + } +} From d92a942940e8b5f7aadf61fa50706fba0c35649c Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 23 Jan 2021 14:16:24 +0100 Subject: [PATCH 39/75] Fix media keys reporting Unidentified --- src/platform_impl/windows/event_loop.rs | 12 ++++++- src/platform_impl/windows/keyboard.rs | 45 ++++++++++++++++++++----- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 86f8b6178e..6549ab94f8 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2098,7 +2098,17 @@ unsafe extern "system" fn thread_event_target_callback( 0x0000 } }; - let scancode = keyboard.MakeCode | extension; + let scancode; + if keyboard.MakeCode == 0 { + // In some cases (often with media keys) the device reports a scancode of 0 but a + // valid virtual key. In these cases we obtain the scancode from the virtual key. + scancode = winuser::MapVirtualKeyW( + keyboard.VKey as u32, + winuser::MAPVK_VK_TO_VSC_EX, + ) as u16; + } else { + scancode = keyboard.MakeCode | extension; + } let code = KeyCode::from_scancode(scancode as u32); subclass_input.send_event(Event::DeviceEvent { device_id, diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 1727a43594..a7f69b7661 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -136,8 +136,12 @@ impl KeyEventBuilder { self.prev_down_was_dead = false; let mut layouts = LAYOUT_CACHE.lock().unwrap(); - let (event_info, _) = - PartialKeyEventInfo::from_message(lparam, ElementState::Pressed, &mut layouts); + let (event_info, _) = PartialKeyEventInfo::from_message( + wparam, + lparam, + ElementState::Pressed, + &mut layouts, + ); let mut event_info = Some(event_info); let mut next_msg = MaybeUninit::uninit(); @@ -274,8 +278,12 @@ impl KeyEventBuilder { *retval = Some(0); let mut layouts = LAYOUT_CACHE.lock().unwrap(); - let (event_info, aux_info) = - PartialKeyEventInfo::from_message(lparam, ElementState::Released, &mut layouts); + let (event_info, aux_info) = PartialKeyEventInfo::from_message( + wparam, + lparam, + ElementState::Released, + &mut layouts, + ); let logical_key = event_info.logical_key; let mut ev = event_info.finalize(&mut layouts.strings); let (_, layout) = layouts.get_current_layout(); @@ -483,24 +491,45 @@ struct AuxKeyInfo { impl PartialKeyEventInfo { fn from_message( + wparam: WPARAM, lparam: LPARAM, state: ElementState, layouts: &mut MutexGuard<'_, LayoutCache>, ) -> (Self, AuxKeyInfo) { const NO_MODS: WindowsModifiers = WindowsModifiers::empty(); + let (_, layout) = layouts.get_current_layout(); let lparam_struct = destructure_key_lparam(lparam); - let scancode = new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); + let scancode; + let vkey; + if lparam_struct.scancode == 0 { + // In some cases (often with media keys) the device reports a scancode of 0 but a + // valid virtual key. In these cases we obtain the scancode from the virtual key. + vkey = wparam as c_int; + scancode = unsafe { + winuser::MapVirtualKeyExW( + vkey as u32, + winuser::MAPVK_VK_TO_VSC_EX, + layout.hkl as HKL, + ) as u16 + }; + } else { + scancode = new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); + vkey = unsafe { + winuser::MapVirtualKeyExW( + scancode as u32, + winuser::MAPVK_VSC_TO_VK_EX, + layout.hkl as HKL, + ) as i32 + }; + } let code = KeyCode::from_scancode(scancode as u32); - let vkey = - unsafe { winuser::MapVirtualKeyW(scancode as u32, winuser::MAPVK_VSC_TO_VK_EX) as i32 }; let location = get_location(vkey, lparam_struct.extended); let kbd_state = unsafe { get_kbd_state() }; let mods = WindowsModifiers::active_modifiers(&kbd_state); let mods_without_ctrl = mods.remove_only_ctrl(); - let (_, layout) = layouts.get_current_layout(); let logical_key = layout.get_key(mods_without_ctrl, scancode, code); let key_without_modifiers = match layout.get_key(NO_MODS, scancode, code) { // We convert dead keys into their character. From 84a7ceb70cc51bdb9322dc28ce12ffb2b24fda79 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 23 Jan 2021 14:28:26 +0100 Subject: [PATCH 40/75] Don't report text on key release events --- src/platform/windows.rs | 2 +- src/platform_impl/windows/keyboard.rs | 36 +++++++-------------------- src/platform_impl/windows/mod.rs | 2 +- 3 files changed, 11 insertions(+), 29 deletions(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 21daf0dec4..97a1402229 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -248,7 +248,7 @@ impl IconExtWindows for Icon { impl KeyEventExtModifierSupplement for KeyEvent { #[inline] fn text_with_all_modifers(&self) -> Option<&str> { - self.platform_specific.char_with_all_modifers + self.platform_specific.text_with_all_modifers } #[inline] diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index a7f69b7661..347e85e79b 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -136,7 +136,7 @@ impl KeyEventBuilder { self.prev_down_was_dead = false; let mut layouts = LAYOUT_CACHE.lock().unwrap(); - let (event_info, _) = PartialKeyEventInfo::from_message( + let event_info = PartialKeyEventInfo::from_message( wparam, lparam, ElementState::Pressed, @@ -278,21 +278,15 @@ impl KeyEventBuilder { *retval = Some(0); let mut layouts = LAYOUT_CACHE.lock().unwrap(); - let (event_info, aux_info) = PartialKeyEventInfo::from_message( + let event_info = PartialKeyEventInfo::from_message( wparam, lparam, ElementState::Released, &mut layouts, ); - let logical_key = event_info.logical_key; - let mut ev = event_info.finalize(&mut layouts.strings); - let (_, layout) = layouts.get_current_layout(); - let key_with_all_modifiers = - layout.get_key(aux_info.modifiers, aux_info.scancode, aux_info.key_code); - ev.text = logical_key.to_text(); - ev.platform_specific.char_with_all_modifers = key_with_all_modifiers.to_text(); + let event = event_info.finalize(&mut layouts.strings); return vec![MessageAsKeyEvent { - event: ev, + event, is_synthetic: false, }]; } @@ -452,7 +446,7 @@ impl KeyEventBuilder { let mut event = event_info.finalize(&mut layouts.strings); event.logical_key = logical_key; - event.platform_specific.char_with_all_modifers = logical_key.to_text(); + event.platform_specific.text_with_all_modifers = logical_key.to_text(); Some(MessageAsKeyEvent { event, is_synthetic: true, @@ -483,19 +477,13 @@ struct PartialKeyEventInfo { text: PartialText, } -struct AuxKeyInfo { - scancode: ExScancode, - key_code: KeyCode, - modifiers: WindowsModifiers, -} - impl PartialKeyEventInfo { fn from_message( wparam: WPARAM, lparam: LPARAM, state: ElementState, layouts: &mut MutexGuard<'_, LayoutCache>, - ) -> (Self, AuxKeyInfo) { + ) -> Self { const NO_MODS: WindowsModifiers = WindowsModifiers::empty(); let (_, layout) = layouts.get_current_layout(); @@ -550,12 +538,7 @@ impl PartialKeyEventInfo { } key => key, }; - let aux_info = AuxKeyInfo { - scancode, - key_code: code, - modifiers: mods, - }; - let partial_key_event_info = PartialKeyEventInfo { + PartialKeyEventInfo { key_state: state, logical_key, key_without_modifiers, @@ -565,8 +548,7 @@ impl PartialKeyEventInfo { location, utf16parts: Vec::with_capacity(8), text: PartialText::System(Vec::new()), - }; - (partial_key_event_info, aux_info) + } } fn finalize(self, strings: &mut HashSet<&'static str>) -> KeyEvent { @@ -604,7 +586,7 @@ impl PartialKeyEventInfo { state: self.key_state, repeat: self.is_repeat, platform_specific: KeyEventExtra { - char_with_all_modifers: char_with_all_modifiers, + text_with_all_modifers: char_with_all_modifiers, key_without_modifers: self.key_without_modifiers, }, } diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 6d655bc686..bc579860fc 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -76,7 +76,7 @@ pub type OsError = std::io::Error; #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEventExtra { - pub char_with_all_modifers: Option<&'static str>, + pub text_with_all_modifers: Option<&'static str>, pub key_without_modifers: Key<'static>, } From d3178b3ff3f0a6b237d04790cfd805641db560be Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 23 Jan 2021 18:37:08 +0100 Subject: [PATCH 41/75] Fix for NumLock being reported as Pause in raw input --- src/platform_impl/windows/event_loop.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 6549ab94f8..41e8882cc4 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -19,6 +19,7 @@ use std::{ use winapi::shared::basetsd::{DWORD_PTR, UINT_PTR}; use winapi::{ + ctypes::c_int, shared::{ minwindef::{BOOL, DWORD, HIWORD, INT, LOWORD, LPARAM, LRESULT, UINT, WPARAM}, windef::{HWND, POINT, RECT}, @@ -2109,7 +2110,17 @@ unsafe extern "system" fn thread_event_target_callback( } else { scancode = keyboard.MakeCode | extension; } - let code = KeyCode::from_scancode(scancode as u32); + let code; + if keyboard.VKey as c_int == winuser::VK_NUMLOCK { + // For some reason the scancode for numlock reports the `Pause` key. + // This is beyond my comprehension but it's probably related to what's + // described in the article by Raymond Chen, titled: + // "Why does Ctrl+ScrollLock cancel dialogs?" + // https://devblogs.microsoft.com/oldnewthing/20080211-00/?p=23503 + code = KeyCode::NumLock; + } else { + code = KeyCode::from_scancode(scancode as u32); + } subclass_input.send_event(Event::DeviceEvent { device_id, event: Key(RawKeyEvent { From 76e7bca35a14399fd2be9f4153e75189e17c2500 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 23 Jan 2021 20:19:56 +0100 Subject: [PATCH 42/75] Fix for strange behaviour around NumLock and Pause --- src/platform_impl/windows/event_loop.rs | 254 +++++++++++++----------- 1 file changed, 142 insertions(+), 112 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 41e8882cc4..82cffb7af8 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -28,7 +28,7 @@ use winapi::{ um::{ commctrl, libloaderapi, ole2, processthreadsapi, winbase, winnt::{HANDLE, LONG, LPCSTR, SHORT}, - winuser, + winuser::{self, RAWINPUT}, }, }; @@ -2018,118 +2018,8 @@ unsafe extern "system" fn thread_event_target_callback( } winuser::WM_INPUT => { - use crate::event::{ - DeviceEvent::{Button, Key, Motion, MouseMotion, MouseWheel}, - ElementState::{Pressed, Released}, - MouseScrollDelta::LineDelta, - }; - if let Some(data) = raw_input::get_raw_input_data(lparam as _) { - let device_id = wrap_device_id(data.header.hDevice as _); - - if data.header.dwType == winuser::RIM_TYPEMOUSE { - let mouse = data.data.mouse(); - - if util::has_flag(mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) { - let x = mouse.lLastX as f64; - let y = mouse.lLastY as f64; - - if x != 0.0 { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Motion { axis: 0, value: x }, - }); - } - - if y != 0.0 { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Motion { axis: 1, value: y }, - }); - } - - if x != 0.0 || y != 0.0 { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: MouseMotion { delta: (x, y) }, - }); - } - } - - if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { - let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: MouseWheel { - delta: LineDelta(0.0, delta as f32), - }, - }); - } - - let button_state = raw_input::get_raw_mouse_button_state(mouse.usButtonFlags); - // Left, middle, and right, respectively. - for (index, state) in button_state.iter().enumerate() { - if let Some(state) = *state { - // This gives us consistency with X11, since there doesn't - // seem to be anything else reasonable to do for a mouse - // button ID. - let button = (index + 1) as _; - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Button { button, state }, - }); - } - } - } else if data.header.dwType == winuser::RIM_TYPEKEYBOARD { - let keyboard = data.data.keyboard(); - - let pressed = keyboard.Message == winuser::WM_KEYDOWN - || keyboard.Message == winuser::WM_SYSKEYDOWN; - let released = keyboard.Message == winuser::WM_KEYUP - || keyboard.Message == winuser::WM_SYSKEYUP; - - if pressed || released { - let state = if pressed { Pressed } else { Released }; - let extension = { - if util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _) { - 0xE000 - } else if util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _) { - 0xE100 - } else { - 0x0000 - } - }; - let scancode; - if keyboard.MakeCode == 0 { - // In some cases (often with media keys) the device reports a scancode of 0 but a - // valid virtual key. In these cases we obtain the scancode from the virtual key. - scancode = winuser::MapVirtualKeyW( - keyboard.VKey as u32, - winuser::MAPVK_VK_TO_VSC_EX, - ) as u16; - } else { - scancode = keyboard.MakeCode | extension; - } - let code; - if keyboard.VKey as c_int == winuser::VK_NUMLOCK { - // For some reason the scancode for numlock reports the `Pause` key. - // This is beyond my comprehension but it's probably related to what's - // described in the article by Raymond Chen, titled: - // "Why does Ctrl+ScrollLock cancel dialogs?" - // https://devblogs.microsoft.com/oldnewthing/20080211-00/?p=23503 - code = KeyCode::NumLock; - } else { - code = KeyCode::from_scancode(scancode as u32); - } - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Key(RawKeyEvent { - physical_key: code, - state, - }), - }); - } - } + handle_raw_input(&subclass_input, data); } commctrl::DefSubclassProc(window, msg, wparam, lparam) @@ -2201,3 +2091,143 @@ unsafe extern "system" fn thread_event_target_callback( } result } + +unsafe fn handle_raw_input( + subclass_input: &Box>, + data: RAWINPUT, +) { + use crate::event::{ + DeviceEvent::{Button, Key, Motion, MouseMotion, MouseWheel}, + ElementState::{Pressed, Released}, + MouseScrollDelta::LineDelta, + }; + + let device_id = wrap_device_id(data.header.hDevice as _); + + if data.header.dwType == winuser::RIM_TYPEMOUSE { + let mouse = data.data.mouse(); + + if util::has_flag(mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) { + let x = mouse.lLastX as f64; + let y = mouse.lLastY as f64; + + if x != 0.0 { + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Motion { axis: 0, value: x }, + }); + } + + if y != 0.0 { + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Motion { axis: 1, value: y }, + }); + } + + if x != 0.0 || y != 0.0 { + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: MouseMotion { delta: (x, y) }, + }); + } + } + + if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { + let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: MouseWheel { + delta: LineDelta(0.0, delta as f32), + }, + }); + } + + let button_state = raw_input::get_raw_mouse_button_state(mouse.usButtonFlags); + // Left, middle, and right, respectively. + for (index, state) in button_state.iter().enumerate() { + if let Some(state) = *state { + // This gives us consistency with X11, since there doesn't + // seem to be anything else reasonable to do for a mouse + // button ID. + let button = (index + 1) as _; + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Button { button, state }, + }); + } + } + } else if data.header.dwType == winuser::RIM_TYPEKEYBOARD { + let keyboard = data.data.keyboard(); + + let pressed = + keyboard.Message == winuser::WM_KEYDOWN || keyboard.Message == winuser::WM_SYSKEYDOWN; + let released = + keyboard.Message == winuser::WM_KEYUP || keyboard.Message == winuser::WM_SYSKEYUP; + + if !pressed && !released { + return; + } + + let state = if pressed { Pressed } else { Released }; + let extension = { + if util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _) { + 0xE000 + } else if util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _) { + 0xE100 + } else { + 0x0000 + } + }; + let scancode; + if keyboard.MakeCode == 0 { + // In some cases (often with media keys) the device reports a scancode of 0 but a + // valid virtual key. In these cases we obtain the scancode from the virtual key. + scancode = + winuser::MapVirtualKeyW(keyboard.VKey as u32, winuser::MAPVK_VK_TO_VSC_EX) as u16; + } else { + scancode = keyboard.MakeCode | extension; + } + if scancode == 0xE11D { + // Some keys are equivalent to a combination of keys at the hardware (or driver?) level. + // For example Pause = Ctrl+NumLock. + // This equvalence means that if the user presses Pause, the keyboard will emmit two + // subsequent keypresses: + // 1, 0xE11D - Which is a left ctrl (0x1D) with an extension flag (0xE100) + // 2, 0x0045 - Which on its own can be interpreted as Pause + // + // For this reason if we encounter the first keypress, we simply ignore it, trusting + // that there's going to be another event coming, from which we can extract the + // appropriate key. + // For more on this, read the article by Raymond Chen, titled: + // "Why does Ctrl+ScrollLock cancel dialogs?" + // https://devblogs.microsoft.com/oldnewthing/20080211-00/?p=23503 + return; + } + let code; + if keyboard.VKey as c_int == winuser::VK_NUMLOCK { + // Historically the NumLock and the Pause key were one and the same physical key. + // The user could trigger Pause by pressing Ctrl+NumLock. + // Now these are often physically separate and the two keys can be differentiated by + // checking the extension flag of the scancode. NumLock is 0xE045, Pause is 0x0045. + // + // However in this event, both keys are reported as 0x0045 even on modern hardware. + // Therefore we use the virtual key instead to determine whether it's a NumLock and + // set the KeyCode accordingly. + // + // For more on this, read the article by Raymond Chen, titled: + // "Why does Ctrl+ScrollLock cancel dialogs?" + // https://devblogs.microsoft.com/oldnewthing/20080211-00/?p=23503 + code = KeyCode::NumLock; + } else { + code = KeyCode::from_scancode(scancode as u32); + } + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Key(RawKeyEvent { + physical_key: code, + state, + }), + }); + } +} From 41855c94938360bd90e9050add37d8c18cfffe0d Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 24 Jan 2021 11:54:33 +0100 Subject: [PATCH 43/75] Fix for NumLock being ineffective --- src/keyboard.rs | 7 +- src/platform_impl/windows/keyboard.rs | 28 +-- src/platform_impl/windows/keyboard_layout.rs | 235 ++++++------------- 3 files changed, 90 insertions(+), 180 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 7eb4d19918..9f9c3f8d17 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -592,7 +592,10 @@ pub enum KeyCode { /// /// This mostly conforms to the UI Events Specification's [`KeyboardEvent.key`] with a few /// exceptions: -/// - The key that the specification calls "Meta" is named "Super" here. +/// - The `Super` variant here, is named `Meta` in the aforementionned specification. (There's +/// another key which the specification calls `Super`. That does not exist here.) +/// - The `Space` variant here, can be identified by the character it generates in the +/// specificaiton. /// - The `Unidentified` variant here, can still identifiy a key through it's `NativeKeyCode`. /// - The `Dead` variant here, can specify the character which is inserted when pressing the /// dead-key twice. @@ -669,6 +672,8 @@ pub enum Key<'a> { Enter, /// The Horizontal Tabulation `Tab` key. Tab, + /// Used in text to insert a space between words. Usually located below the character keys. + Space, /// Navigate or traverse downward. (`KEYCODE_DPAD_DOWN`) ArrowDown, /// Navigate or traverse leftward. (`KEYCODE_DPAD_LEFT`) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 347e85e79b..3e608a4fbd 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -261,9 +261,10 @@ impl KeyEventBuilder { event_info.text = PartialText::System(event_info.utf16parts.clone()); } else { let mod_no_ctrl = mod_state.remove_only_ctrl(); + let vkey = event_info.vkey; let scancode = event_info.scancode; let keycode = event_info.code; - let key = layout.get_key(mod_no_ctrl, scancode, keycode); + let key = layout.get_key(mod_no_ctrl, vkey, scancode, keycode); event_info.text = PartialText::Text(key.to_text()); } } @@ -429,10 +430,11 @@ impl KeyEventBuilder { let key_without_modifers; { let layout = layouts.layouts.get(&(locale_id as u64)).unwrap(); - logical_key = layout.get_key(mods, scancode, code); - key_without_modifers = layout.get_key(WindowsModifiers::empty(), scancode, code); + logical_key = layout.get_key(mods, vk, scancode, code); + key_without_modifers = layout.get_key(WindowsModifiers::empty(), vk, scancode, code); } let event_info = PartialKeyEventInfo { + vkey: vk, logical_key, key_without_modifiers: key_without_modifers, key_state, @@ -461,8 +463,9 @@ enum PartialText { } struct PartialKeyEventInfo { - key_state: ElementState, + vkey: c_int, scancode: ExScancode, + key_state: ElementState, is_repeat: bool, code: KeyCode, location: KeyLocation, @@ -489,11 +492,10 @@ impl PartialKeyEventInfo { let (_, layout) = layouts.get_current_layout(); let lparam_struct = destructure_key_lparam(lparam); let scancode; - let vkey; + let vkey = wparam as c_int; if lparam_struct.scancode == 0 { // In some cases (often with media keys) the device reports a scancode of 0 but a // valid virtual key. In these cases we obtain the scancode from the virtual key. - vkey = wparam as c_int; scancode = unsafe { winuser::MapVirtualKeyExW( vkey as u32, @@ -503,13 +505,6 @@ impl PartialKeyEventInfo { }; } else { scancode = new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); - vkey = unsafe { - winuser::MapVirtualKeyExW( - scancode as u32, - winuser::MAPVK_VSC_TO_VK_EX, - layout.hkl as HKL, - ) as i32 - }; } let code = KeyCode::from_scancode(scancode as u32); let location = get_location(vkey, lparam_struct.extended); @@ -518,8 +513,8 @@ impl PartialKeyEventInfo { let mods = WindowsModifiers::active_modifiers(&kbd_state); let mods_without_ctrl = mods.remove_only_ctrl(); - let logical_key = layout.get_key(mods_without_ctrl, scancode, code); - let key_without_modifiers = match layout.get_key(NO_MODS, scancode, code) { + let logical_key = layout.get_key(mods_without_ctrl, vkey, scancode, code); + let key_without_modifiers = match layout.get_key(NO_MODS, vkey, scancode, code) { // We convert dead keys into their character. // The reason for this is that `key_without_modifiers` is designed for key-bindings // but for example the US International treats `'` (apostrophe) as a dead key and @@ -539,10 +534,11 @@ impl PartialKeyEventInfo { key => key, }; PartialKeyEventInfo { + vkey, + scancode, key_state: state, logical_key, key_without_modifiers, - scancode, is_repeat: lparam_struct.is_repeat, code, location, diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index a30a40fbad..703c3ac331 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -23,13 +23,32 @@ use crate::{ }; lazy_static! { - pub static ref LAYOUT_CACHE: Mutex = Mutex::new(LayoutCache::default()); + pub(crate) static ref LAYOUT_CACHE: Mutex = Mutex::new(LayoutCache::default()); } fn key_pressed(vkey: c_int) -> bool { unsafe { (winuser::GetKeyState(vkey) & (1 << 15)) == (1 << 15) } } +const NUMPAD_VKEYS: [c_int; 16] = [ + winuser::VK_NUMPAD0, + winuser::VK_NUMPAD1, + winuser::VK_NUMPAD2, + winuser::VK_NUMPAD3, + winuser::VK_NUMPAD4, + winuser::VK_NUMPAD5, + winuser::VK_NUMPAD6, + winuser::VK_NUMPAD7, + winuser::VK_NUMPAD8, + winuser::VK_NUMPAD9, + winuser::VK_MULTIPLY, + winuser::VK_ADD, + winuser::VK_SEPARATOR, + winuser::VK_SUBTRACT, + winuser::VK_DECIMAL, + winuser::VK_DIVIDE, +]; + bitflags! { pub struct WindowsModifiers : u8 { const SHIFT = 1 << 0; @@ -113,8 +132,16 @@ impl WindowsModifiers { } } -pub struct Layout { +pub(crate) struct Layout { pub hkl: u64, + + /// Maps a Windows virtual key to a `Key`. + /// + /// Only those keys are mapped for which the modifier state is not needed when mapping. Making + /// this field separate from the `keys` field, saves having to add NumLock as a modifier to + /// `WindowsModifiers`, which would double the number of items in keys. + pub simple_vkeys: HashMap>, + /// Maps a modifier state to group of key strings /// Not using `ModifiersState` here because that object cannot express caps lock /// but we need to handle caps lock too. @@ -133,29 +160,35 @@ impl Layout { pub fn get_key( &self, mods: WindowsModifiers, + vkey: c_int, scancode: ExScancode, keycode: KeyCode, ) -> Key<'static> { + let native_code = NativeKeyCode::Windows(scancode); + // This feels much like a hack as one would assume that the `keys` map contains every key // mapping. However `MapVirtualKeyExW` sometimes maps virtual keys to odd scancodes that // don't match the scancode coming from the KEYDOWN message for the same key. // For example: // `VK_LEFT` is mapped to `0x004B`, but the scancode for the left arrow is `0xE04B`. - if let Some(key) = keycode_to_non_character_key(keycode, self.has_alt_graph, self.hkl) { - return key; + let key_from_vkey = vkey_to_non_char_key(vkey, native_code, self.hkl, self.has_alt_graph); + if !matches!(key_from_vkey, Key::Unidentified(_)) { + return key_from_vkey; + } + if let Some(key) = self.simple_vkeys.get(&vkey) { + return *key; } - if let Some(keys) = self.keys.get(&mods) { if let Some(key) = keys.get(&keycode) { return *key; } } - Key::Unidentified(NativeKeyCode::Windows(scancode)) + Key::Unidentified(native_code) } } #[derive(Default)] -pub struct LayoutCache { +pub(crate) struct LayoutCache { /// Maps locale identifiers (HKL) to layouts pub layouts: HashMap, pub strings: HashSet<&'static str>, @@ -199,6 +232,7 @@ impl LayoutCache { fn prepare_layout(strings: &mut HashSet<&'static str>, locale_id: u64) -> Layout { let mut layout = Layout { hkl: locale_id, + simple_vkeys: Default::default(), keys: Default::default(), has_alt_graph: false, }; @@ -207,6 +241,24 @@ impl LayoutCache { // simulate a scenario when no modifier is active. let mut key_state = [0u8; 256]; + // First, generate all the simple vkeys + // Some numpad keys generate different charcaters based on the locale. + // For example `VK_DECIMAL` is sometimes "." and sometimes "," + layout.simple_vkeys.reserve(NUMPAD_VKEYS.len()); + for vk in NUMPAD_VKEYS.iter() { + let vk = (*vk) as u32; + let scancode = unsafe { + winuser::MapVirtualKeyExW(vk, winuser::MAPVK_VK_TO_VSC_EX, locale_id as HKL) + }; + let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_id); + if let ToUnicodeResult::Str(s) = unicode { + let static_str = get_or_insert_str(strings, s); + layout + .simple_vkeys + .insert(vk as i32, Key::Character(static_str)); + } + } + // Iterate through every combination of modifiers let mods_end = WindowsModifiers::FLAGS_END.bits; for mod_state in 0..mods_end { @@ -234,7 +286,7 @@ impl LayoutCache { // assume it isn't. Then we'll do a second pass where we set the "AltRight" keys to // "AltGr" in case we find out that there's an AltGraph. let preliminary_key = - vkey_to_non_printable(vk as i32, native_code, key_code, locale_id, false); + vkey_to_non_char_key(vk as i32, native_code, locale_id, false); match preliminary_key { Key::Unidentified(_) => (), _ => { @@ -369,173 +421,30 @@ where leaked } -#[derive(Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] enum ToUnicodeResult { Str(String), Dead(Option), None, } -fn keycode_to_non_character_key( - keycode: KeyCode, - has_alt_graph: bool, - hkl: u64, -) -> Option> { - let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); - let is_korean = primary_lang_id == LANG_KOREAN; - match keycode { - KeyCode::AltLeft => Some(Key::Alt), - KeyCode::AltRight => { - if has_alt_graph { - Some(Key::AltGraph) - } else { - Some(Key::Alt) - } - } - KeyCode::Backspace => Some(Key::Backspace), - KeyCode::CapsLock => Some(Key::CapsLock), - KeyCode::ContextMenu => Some(Key::ContextMenu), - KeyCode::ControlLeft => Some(Key::Control), - KeyCode::ControlRight => Some(Key::Control), - KeyCode::Enter => Some(Key::Enter), - KeyCode::SuperLeft => Some(Key::Super), - KeyCode::SuperRight => Some(Key::Super), - KeyCode::ShiftLeft => Some(Key::Shift), - KeyCode::ShiftRight => Some(Key::Shift), - KeyCode::Tab => Some(Key::Tab), - KeyCode::Convert => Some(Key::Convert), - KeyCode::KanaMode => Some(Key::KanaMode), - KeyCode::Lang1 => { - if is_korean { - Some(Key::HangulMode) - } else { - Some(Key::KanaMode) - } - } - KeyCode::Lang2 => { - if is_korean { - Some(Key::HanjaMode) - } else { - Some(Key::Eisu) - } - } - KeyCode::Lang3 => Some(Key::Katakana), - KeyCode::Lang4 => Some(Key::Hiragana), - KeyCode::Lang5 => Some(Key::ZenkakuHankaku), - KeyCode::NonConvert => Some(Key::NonConvert), - KeyCode::Delete => Some(Key::Delete), - KeyCode::End => Some(Key::End), - KeyCode::Help => Some(Key::Help), - KeyCode::Home => Some(Key::Home), - KeyCode::Insert => Some(Key::Insert), - KeyCode::PageDown => Some(Key::PageDown), - KeyCode::PageUp => Some(Key::PageUp), - KeyCode::ArrowDown => Some(Key::ArrowDown), - KeyCode::ArrowLeft => Some(Key::ArrowLeft), - KeyCode::ArrowRight => Some(Key::ArrowRight), - KeyCode::ArrowUp => Some(Key::ArrowUp), - KeyCode::NumLock => Some(Key::NumLock), - KeyCode::NumpadClear => Some(Key::Clear), - KeyCode::NumpadEnter => Some(Key::Enter), - KeyCode::Escape => Some(Key::Escape), - KeyCode::Fn => Some(Key::Fn), - KeyCode::FnLock => Some(Key::FnLock), - KeyCode::PrintScreen => Some(Key::PrintScreen), - KeyCode::ScrollLock => Some(Key::ScrollLock), - KeyCode::Pause => Some(Key::Pause), - KeyCode::BrowserBack => Some(Key::BrowserBack), - KeyCode::BrowserFavorites => Some(Key::BrowserFavorites), - KeyCode::BrowserForward => Some(Key::BrowserForward), - KeyCode::BrowserHome => Some(Key::BrowserHome), - KeyCode::BrowserRefresh => Some(Key::BrowserRefresh), - KeyCode::BrowserSearch => Some(Key::BrowserSearch), - KeyCode::BrowserStop => Some(Key::BrowserStop), - KeyCode::Eject => Some(Key::Eject), - KeyCode::LaunchApp1 => Some(Key::LaunchApplication1), - KeyCode::LaunchApp2 => Some(Key::LaunchApplication2), - KeyCode::LaunchMail => Some(Key::LaunchMail), - KeyCode::MediaPlayPause => Some(Key::MediaPlayPause), - KeyCode::MediaStop => Some(Key::MediaStop), - KeyCode::MediaTrackNext => Some(Key::MediaTrackNext), - KeyCode::MediaTrackPrevious => Some(Key::MediaTrackPrevious), - KeyCode::Power => Some(Key::Power), - KeyCode::Sleep => Some(Key::Standby), - KeyCode::AudioVolumeDown => Some(Key::AudioVolumeDown), - KeyCode::AudioVolumeMute => Some(Key::AudioVolumeMute), - KeyCode::AudioVolumeUp => Some(Key::AudioVolumeUp), - KeyCode::WakeUp => Some(Key::WakeUp), - KeyCode::Hyper => Some(Key::Hyper), - KeyCode::Super => Some(Key::Super), - KeyCode::Again => Some(Key::Again), - KeyCode::Copy => Some(Key::Copy), - KeyCode::Cut => Some(Key::Cut), - KeyCode::Find => Some(Key::Find), - KeyCode::Open => Some(Key::Open), - KeyCode::Paste => Some(Key::Paste), - KeyCode::Props => Some(Key::Props), - KeyCode::Select => Some(Key::Select), - KeyCode::Undo => Some(Key::Undo), - KeyCode::Hiragana => Some(Key::Hiragana), - KeyCode::Katakana => Some(Key::Katakana), - KeyCode::F1 => Some(Key::F1), - KeyCode::F2 => Some(Key::F2), - KeyCode::F3 => Some(Key::F3), - KeyCode::F4 => Some(Key::F4), - KeyCode::F5 => Some(Key::F5), - KeyCode::F6 => Some(Key::F6), - KeyCode::F7 => Some(Key::F7), - KeyCode::F8 => Some(Key::F8), - KeyCode::F9 => Some(Key::F9), - KeyCode::F10 => Some(Key::F10), - KeyCode::F11 => Some(Key::F11), - KeyCode::F12 => Some(Key::F12), - KeyCode::F13 => Some(Key::F13), - KeyCode::F14 => Some(Key::F14), - KeyCode::F15 => Some(Key::F15), - KeyCode::F16 => Some(Key::F16), - KeyCode::F17 => Some(Key::F17), - KeyCode::F18 => Some(Key::F18), - KeyCode::F19 => Some(Key::F19), - KeyCode::F20 => Some(Key::F20), - KeyCode::F21 => Some(Key::F21), - KeyCode::F22 => Some(Key::F22), - KeyCode::F23 => Some(Key::F23), - KeyCode::F24 => Some(Key::F24), - KeyCode::F25 => Some(Key::F25), - KeyCode::F26 => Some(Key::F26), - KeyCode::F27 => Some(Key::F27), - KeyCode::F28 => Some(Key::F28), - KeyCode::F29 => Some(Key::F29), - KeyCode::F30 => Some(Key::F30), - KeyCode::F31 => Some(Key::F31), - KeyCode::F32 => Some(Key::F32), - KeyCode::F33 => Some(Key::F33), - KeyCode::F34 => Some(Key::F34), - KeyCode::F35 => Some(Key::F35), - _ => None, - } -} - -/// This includes all non-character keys defined within `Key` so for example -/// backspace and tab are included. -fn vkey_to_non_printable( +/// This converts virtual keys to `Key`s. Only those virtual keys are converted which can be +/// unambigously converted to a `Key` with only information passed in as arguments. +/// +/// In other words this function does not need to "prepare" the current layout in order to do +/// the conversion but as such it cannot convert language specific character keys for example. +/// +/// The result includes all non-character keys defined within `Key` plus characters from numpad keys. +/// So for example backspace and tab are included. +fn vkey_to_non_char_key( vkey: i32, native_code: NativeKeyCode, - code: KeyCode, hkl: u64, has_alt_graph: bool, ) -> Key<'static> { // List of the Web key names and their corresponding platform-native key names: // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values - // Some keys cannot be correctly determined based on the virtual key. - // Therefore we use the `code` to translate those keys. - match code { - KeyCode::NumLock => return Key::NumLock, - KeyCode::Pause => return Key::Pause, - _ => (), - } - let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); let is_korean = primary_lang_id == LANG_KOREAN; let is_japanese = primary_lang_id == LANG_JAPANESE; @@ -580,7 +489,7 @@ fn vkey_to_non_printable( winuser::VK_NONCONVERT => Key::NonConvert, winuser::VK_ACCEPT => Key::Accept, winuser::VK_MODECHANGE => Key::ModeChange, - winuser::VK_SPACE => Key::Unidentified(native_code), // This function only converts "non-printable" + winuser::VK_SPACE => Key::Space, winuser::VK_PRIOR => Key::PageUp, winuser::VK_NEXT => Key::PageDown, winuser::VK_END => Key::End, @@ -601,7 +510,7 @@ fn vkey_to_non_printable( winuser::VK_APPS => Key::ContextMenu, winuser::VK_SLEEP => Key::Standby, - // This function only converts "non-printable" + // Numpad keys produce characters winuser::VK_NUMPAD0 => Key::Unidentified(native_code), winuser::VK_NUMPAD1 => Key::Unidentified(native_code), winuser::VK_NUMPAD2 => Key::Unidentified(native_code), From d99d6fc6af324eecf3a8d5946913ea80d8456041 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 24 Jan 2021 12:36:26 +0100 Subject: [PATCH 44/75] Fix for location not being reported correctly --- src/platform_impl/windows/keyboard.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 3e608a4fbd..05c665a11c 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -419,7 +419,6 @@ impl KeyEventBuilder { return None; } let scancode = scancode as ExScancode; - let is_extended = (scancode & 0xE000) == 0xE000; let code = KeyCode::from_scancode(scancode as u32); let mods = if caps_lock_on { WindowsModifiers::CAPS_LOCK @@ -441,7 +440,7 @@ impl KeyEventBuilder { scancode, is_repeat: false, code, - location: get_location(vk, is_extended), + location: get_location(scancode, locale_id), utf16parts: Vec::with_capacity(8), text: PartialText::System(Vec::new()), }; @@ -507,7 +506,7 @@ impl PartialKeyEventInfo { scancode = new_ex_scancode(lparam_struct.scancode, lparam_struct.extended); } let code = KeyCode::from_scancode(scancode as u32); - let location = get_location(vkey, lparam_struct.extended); + let location = get_location(scancode, layout.hkl as HKL); let kbd_state = unsafe { get_kbd_state() }; let mods = WindowsModifiers::active_modifiers(&kbd_state); @@ -599,10 +598,16 @@ pub fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { } } -pub fn get_location(vkey: c_int, extended: bool) -> KeyLocation { +fn get_location(scancode: ExScancode, hkl: HKL) -> KeyLocation { use winuser::*; const VK_ABNT_C2: c_int = 0xc2; + let extension = 0xE000; + let extended = (scancode & extension) == extension; + let vkey = unsafe { + winuser::MapVirtualKeyExW(scancode as u32, winuser::MAPVK_VSC_TO_VK_EX, hkl) as i32 + }; + // Use the native VKEY and the extended flag to cover most cases // This is taken from the `druid` software within // druid-shell/src/platform/windows/keyboard.rs From 3096e0e2953fb11bb581374a205df588dcc83b43 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 24 Jan 2021 14:03:06 +0100 Subject: [PATCH 45/75] `RawKeyEvent`s now report repeat --- src/event.rs | 3 ++- src/platform_impl/windows/event_loop.rs | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/event.rs b/src/event.rs index ed7582528f..89799e64c4 100644 --- a/src/event.rs +++ b/src/event.rs @@ -601,11 +601,12 @@ pub enum DeviceEvent { } /// Describes a keyboard input as a raw device event. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct RawKeyEvent { pub physical_key: keyboard::KeyCode, pub state: ElementState, + pub repeat: bool, } /// Describes a keyboard input targeting a window. diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 82cffb7af8..493dab9056 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -5,7 +5,10 @@ mod runner; use parking_lot::Mutex; use std::{ cell::Cell, - collections::VecDeque, + collections::{ + hash_map::{Entry, HashMap}, + VecDeque, + }, marker::PhantomData, mem, panic, ptr, rc::Rc, @@ -86,6 +89,7 @@ lazy_static! { get_function!("user32.dll", GetPointerTouchInfo); static ref GET_POINTER_PEN_INFO: Option = get_function!("user32.dll", GetPointerPenInfo); + static ref RAW_KEYS_PRESSED: Mutex> = Mutex::new(HashMap::new()); } pub(crate) struct SubclassInput { @@ -2222,11 +2226,25 @@ unsafe fn handle_raw_input( } else { code = KeyCode::from_scancode(scancode as u32); } + let repeat; + let mut keys_pressed = RAW_KEYS_PRESSED.lock(); + match keys_pressed.entry(code) { + Entry::Occupied(mut e) => { + let prev_pressed = e.get_mut(); + repeat = *prev_pressed && pressed; + *prev_pressed = pressed; + } + Entry::Vacant(e) => { + e.insert(pressed); + repeat = false; + } + } subclass_input.send_event(Event::DeviceEvent { device_id, event: Key(RawKeyEvent { physical_key: code, state, + repeat, }), }); } From 37da87b394947a55dd0b02a32fa602d1122b454f Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Thu, 4 Feb 2021 07:39:58 +0100 Subject: [PATCH 46/75] Don't report text for synthetic key releases --- src/platform_impl/windows/keyboard.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 05c665a11c..5ece786e6f 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -432,6 +432,12 @@ impl KeyEventBuilder { logical_key = layout.get_key(mods, vk, scancode, code); key_without_modifers = layout.get_key(WindowsModifiers::empty(), vk, scancode, code); } + let text; + if key_state == ElementState::Pressed { + text = logical_key.to_text(); + } else { + text = None; + } let event_info = PartialKeyEventInfo { vkey: vk, logical_key, @@ -442,12 +448,12 @@ impl KeyEventBuilder { code, location: get_location(scancode, locale_id), utf16parts: Vec::with_capacity(8), - text: PartialText::System(Vec::new()), + text: PartialText::Text(text), }; let mut event = event_info.finalize(&mut layouts.strings); event.logical_key = logical_key; - event.platform_specific.text_with_all_modifers = logical_key.to_text(); + event.platform_specific.text_with_all_modifers = text; Some(MessageAsKeyEvent { event, is_synthetic: true, From 1fbd967bd3d28b3791104ae22877adf0863673c8 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Thu, 4 Feb 2021 08:25:52 +0100 Subject: [PATCH 47/75] Address feedback - Add the `Space` variant to the `to_text` function. - Mark `get_kbd_state` safe. - Change `[MaybeUninit; 256]` to `MaybeUninit<[u8; 256]>` --- src/keyboard.rs | 1 + src/platform_impl/windows/keyboard.rs | 62 +++++++++++++-------------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 9f9c3f8d17..53399161b0 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1327,6 +1327,7 @@ impl<'a> Key<'a> { Key::Enter => Some("\r"), Key::Backspace => Some("\x08"), Key::Tab => Some("\t"), + Key::Space => Some(" "), Key::Escape => Some("\x1b"), _ => None, } diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 5ece786e6f..728d699faa 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -43,10 +43,12 @@ fn new_ex_scancode(scancode: u8, extended: bool) -> ExScancode { (scancode as u16) | (if extended { 0xE000 } else { 0 }) } -unsafe fn get_kbd_state() -> [u8; 256] { - let mut kbd_state: [MaybeUninit; 256] = [MaybeUninit::uninit(); 256]; - winuser::GetKeyboardState(kbd_state[0].as_mut_ptr()); - std::mem::transmute::<_, [u8; 256]>(kbd_state) +fn get_kbd_state() -> [u8; 256] { + unsafe { + let mut kbd_state: MaybeUninit<[u8; 256]> = MaybeUninit::uninit(); + winuser::GetKeyboardState(kbd_state.as_mut_ptr() as *mut u8); + kbd_state.assume_init() + } } pub struct MessageAsKeyEvent { @@ -241,32 +243,30 @@ impl KeyEventBuilder { let mut layouts = LAYOUT_CACHE.lock().unwrap(); // Here it's okay to call `ToUnicode` because at this point the dead key // is already consumed by the character. - unsafe { - let kbd_state = get_kbd_state(); - let mod_state = WindowsModifiers::active_modifiers(&kbd_state); - - let (_, layout) = layouts.get_current_layout(); - let ctrl_on; - if layout.has_alt_graph { - let alt_on = mod_state.contains(WindowsModifiers::ALT); - ctrl_on = !alt_on && mod_state.contains(WindowsModifiers::CONTROL) - } else { - ctrl_on = mod_state.contains(WindowsModifiers::CONTROL) - } + let kbd_state = get_kbd_state(); + let mod_state = WindowsModifiers::active_modifiers(&kbd_state); + + let (_, layout) = layouts.get_current_layout(); + let ctrl_on; + if layout.has_alt_graph { + let alt_on = mod_state.contains(WindowsModifiers::ALT); + ctrl_on = !alt_on && mod_state.contains(WindowsModifiers::CONTROL) + } else { + ctrl_on = mod_state.contains(WindowsModifiers::CONTROL) + } - // If CTRL is not pressed, just use the text with all - // modifiers because that already consumed the dead key and otherwise - // we would interpret the character incorretly, missing the dead key. - if !ctrl_on { - event_info.text = PartialText::System(event_info.utf16parts.clone()); - } else { - let mod_no_ctrl = mod_state.remove_only_ctrl(); - let vkey = event_info.vkey; - let scancode = event_info.scancode; - let keycode = event_info.code; - let key = layout.get_key(mod_no_ctrl, vkey, scancode, keycode); - event_info.text = PartialText::Text(key.to_text()); - } + // If CTRL is not pressed, just use the text with all + // modifiers because that already consumed the dead key and otherwise + // we would interpret the character incorretly, missing the dead key. + if !ctrl_on { + event_info.text = PartialText::System(event_info.utf16parts.clone()); + } else { + let mod_no_ctrl = mod_state.remove_only_ctrl(); + let vkey = event_info.vkey; + let scancode = event_info.scancode; + let keycode = event_info.code; + let key = layout.get_key(mod_no_ctrl, vkey, scancode, keycode); + event_info.text = PartialText::Text(key.to_text()); } let ev = event_info.finalize(&mut layouts.strings); return vec![MessageAsKeyEvent { @@ -303,7 +303,7 @@ impl KeyEventBuilder { let mut layouts = LAYOUT_CACHE.lock().unwrap(); let (locale_id, _) = layouts.get_current_layout(); - let kbd_state = unsafe { get_kbd_state() }; + let kbd_state = get_kbd_state(); macro_rules! is_key_pressed { ($vk:expr) => { kbd_state[$vk as usize] & 0x80 != 0 @@ -514,7 +514,7 @@ impl PartialKeyEventInfo { let code = KeyCode::from_scancode(scancode as u32); let location = get_location(scancode, layout.hkl as HKL); - let kbd_state = unsafe { get_kbd_state() }; + let kbd_state = get_kbd_state(); let mods = WindowsModifiers::active_modifiers(&kbd_state); let mods_without_ctrl = mods.remove_only_ctrl(); From f8571901752ea4cc9d13f5ebb2168354d35fbf9a Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 6 Feb 2021 10:33:41 +0100 Subject: [PATCH 48/75] Filter `Unidentified` from PrtSc key device events --- src/platform_impl/windows/event_loop.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 493dab9056..d41deb814b 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2192,14 +2192,21 @@ unsafe fn handle_raw_input( } else { scancode = keyboard.MakeCode | extension; } - if scancode == 0xE11D { - // Some keys are equivalent to a combination of keys at the hardware (or driver?) level. - // For example Pause = Ctrl+NumLock. + if scancode == 0xE11D || scancode == 0xE02A { + // At the hardware (or driver?) level, pressing the Pause key is equivalent to pressing + // Ctrl+NumLock. // This equvalence means that if the user presses Pause, the keyboard will emmit two // subsequent keypresses: // 1, 0xE11D - Which is a left ctrl (0x1D) with an extension flag (0xE100) // 2, 0x0045 - Which on its own can be interpreted as Pause // + // There's another combination which isn't quite an equivalence: + // PrtSc used to be Shift+Asterisk. This means that on some keyboards, presssing + // PrtSc (print screen) produces the following sequence: + // 1, 0xE02A - Which is a left shift (0x2A) with an exteion flag (0xE000) + // 2, 0xE037 - Which is a numpad multiply (0x37) with an exteion flag (0xE000). This on + // its own it can be interpreted as PrtSc + // // For this reason if we encounter the first keypress, we simply ignore it, trusting // that there's going to be another event coming, from which we can extract the // appropriate key. From ee21163a01c1fd7b416aeb92dcf78efc89ef8668 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 6 Feb 2021 11:32:03 +0100 Subject: [PATCH 49/75] Don't report incorrect `RawKeyEvent` for shift + numpad --- src/event.rs | 4 +-- src/platform_impl/windows/event_loop.rs | 33 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/event.rs b/src/event.rs index 89799e64c4..093b840dd6 100644 --- a/src/event.rs +++ b/src/event.rs @@ -614,7 +614,7 @@ pub struct RawKeyEvent { pub struct KeyEvent { /// Represents the position of a key independent of the currently active layout. /// - /// It also uniquely identifies the physical key (i.e. it's synonymus with a scancode). + /// It also uniquely identifies the physical key (i.e. it's synonymous with a scancode). /// /// Note that `Fn` and `FnLock` key events are not guaranteed to be emmited by `winit`. These /// keys are usually handled at the hardware or at the OS level. @@ -623,7 +623,7 @@ pub struct KeyEvent { /// This value is affected by all modifiers except Ctrl. /// /// This has two use cases: - /// - Allows querying whether the current input is a Dead key + /// - Allows querying whether the current input is a Dead key. /// - Allows handling key-bindings on platforms which don't /// support [`key_without_modifiers`]. /// diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index d41deb814b..2fe3056abb 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2233,6 +2233,39 @@ unsafe fn handle_raw_input( } else { code = KeyCode::from_scancode(scancode as u32); } + if keyboard.VKey as c_int == winuser::VK_SHIFT { + match code { + KeyCode::NumpadDecimal + | KeyCode::Numpad0 + | KeyCode::Numpad1 + | KeyCode::Numpad2 + | KeyCode::Numpad3 + | KeyCode::Numpad4 + | KeyCode::Numpad5 + | KeyCode::Numpad6 + | KeyCode::Numpad7 + | KeyCode::Numpad8 + | KeyCode::Numpad9 => { + // On Windows, holding the Shift key makes numpad keys behave as if NumLock + // wasn't active. The way this is exposed to applications by the system is that + // the application receives a fake key release event for the shift key at the + // moment when the numpad key is pressed, just before receiving the numpad key + // as well. + // + // The issue is that in the raw device event (here), the fake shift release + // event reports the numpad key as the scancode. Unfortunately the event doesn't + // have any information to tell whether it's the left shift or the right shift + // that needs to get the fake release (or press) event so we don't forward this + // event to the application at all. + // + // For more on this, read the article by Raymond Chen, titled: + // "The shift key overrides NumLock" + // https://devblogs.microsoft.com/oldnewthing/20040906-00/?p=37953 + return; + } + _ => (), + } + } let repeat; let mut keys_pressed = RAW_KEYS_PRESSED.lock(); match keys_pressed.entry(code) { From df1a48d66f62f3f2e942db40e7894b56999fc086 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 6 Feb 2021 12:30:14 +0100 Subject: [PATCH 50/75] AltGraph is not reported again --- src/platform_impl/windows/keyboard_layout.rs | 24 +++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 703c3ac331..0a7783c3f6 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -166,14 +166,22 @@ impl Layout { ) -> Key<'static> { let native_code = NativeKeyCode::Windows(scancode); - // This feels much like a hack as one would assume that the `keys` map contains every key - // mapping. However `MapVirtualKeyExW` sometimes maps virtual keys to odd scancodes that - // don't match the scancode coming from the KEYDOWN message for the same key. - // For example: - // `VK_LEFT` is mapped to `0x004B`, but the scancode for the left arrow is `0xE04B`. - let key_from_vkey = vkey_to_non_char_key(vkey, native_code, self.hkl, self.has_alt_graph); - if !matches!(key_from_vkey, Key::Unidentified(_)) { - return key_from_vkey; + let unknown_alt = vkey == winuser::VK_MENU; + if !unknown_alt { + // Here we try using the virtual key directly but if the virtual key doesn't distinguish + // between left and right alt, we can't report AltGr. Therefore we only do this if the + // key is not the "unknown alt" key. + // + // The reason for using the virtual key directly is that `MapVirtualKeyExW` (used when + // building the keys map) sometimes maps virtual keys to odd scancodes that don't match + // the scancode coming from the KEYDOWN message for the same key. For example: `VK_LEFT` + // is mapped to `0x004B`, but the scancode for the left arrow is `0xE04B`. + let key_from_vkey = + vkey_to_non_char_key(vkey, native_code, self.hkl, self.has_alt_graph); + + if !matches!(key_from_vkey, Key::Unidentified(_)) { + return key_from_vkey; + } } if let Some(key) = self.simple_vkeys.get(&vkey) { return *key; From c3e4f486ac27cf9470e8113da5fea90a6e05ac05 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 6 Feb 2021 12:45:18 +0100 Subject: [PATCH 51/75] Document Windows specific behaviour for shift+numpad --- src/event.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/event.rs b/src/event.rs index 093b840dd6..ecedf0d5e8 100644 --- a/src/event.rs +++ b/src/event.rs @@ -253,9 +253,15 @@ pub enum WindowEvent<'a> { Focused(bool), /// An event from the keyboard has been received. + /// + /// ## Platform-specific + /// - **Windows:** The shift key overrides NumLock. In other words, while shift is held down, + /// numpad keys act as if NumLock wasn't active. When this is used the OS sends fake key + /// events which are not marked as `is_synthetic`. KeyboardInput { device_id: DeviceId, event: KeyEvent, + /// If `true`, the event was generated synthetically by winit /// in one of the following circumstances: /// From 76c1ab35940f2b9e808a22f65148461bda6804ef Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 14 Feb 2021 16:49:03 +0100 Subject: [PATCH 52/75] Fix typo --- src/platform/modifier_supplement.rs | 2 +- src/platform/windows.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/modifier_supplement.rs b/src/platform/modifier_supplement.rs index 26aa956e16..fec1187768 100644 --- a/src/platform/modifier_supplement.rs +++ b/src/platform/modifier_supplement.rs @@ -16,7 +16,7 @@ pub trait KeyEventExtModifierSupplement { /// Identical to `KeyEvent::text` but this is affected by Ctrl. /// /// For example, pressing Ctrl+a produces `Some("\x01")`. - fn text_with_all_modifers(&self) -> Option<&str>; + fn text_with_all_modifiers(&self) -> Option<&str>; /// This value ignores all modifiers including /// but not limited to Shift, Caps Lock, diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 97a1402229..ba799d2124 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -247,7 +247,7 @@ impl IconExtWindows for Icon { impl KeyEventExtModifierSupplement for KeyEvent { #[inline] - fn text_with_all_modifers(&self) -> Option<&str> { + fn text_with_all_modifiers(&self) -> Option<&str> { self.platform_specific.text_with_all_modifers } From 61775c1c87ff57f2bca16a5e3c5c9188a55a569c Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 14 Feb 2021 18:01:29 +0100 Subject: [PATCH 53/75] Dead keys now affect characters from logical_key --- src/platform_impl/windows/keyboard.rs | 36 +++++++++++++++++--- src/platform_impl/windows/keyboard_layout.rs | 4 +-- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 728d699faa..3507d77aca 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -440,7 +440,7 @@ impl KeyEventBuilder { } let event_info = PartialKeyEventInfo { vkey: vk, - logical_key, + logical_key: PartialLogicalKey::This(logical_key), key_without_modifiers: key_without_modifers, key_state, scancode, @@ -467,6 +467,15 @@ enum PartialText { Text(Option<&'static str>), } +enum PartialLogicalKey { + /// Use the text provided by the WM_UNICHAR messages and report that as + /// a `Character` variant + Text, + + /// Use the value directly provided by this variant + This(Key<'static>), +} + struct PartialKeyEventInfo { vkey: c_int, scancode: ExScancode, @@ -474,7 +483,7 @@ struct PartialKeyEventInfo { is_repeat: bool, code: KeyCode, location: KeyLocation, - logical_key: Key<'static>, + logical_key: PartialLogicalKey, key_without_modifiers: Key<'static>, @@ -518,7 +527,18 @@ impl PartialKeyEventInfo { let mods = WindowsModifiers::active_modifiers(&kbd_state); let mods_without_ctrl = mods.remove_only_ctrl(); - let logical_key = layout.get_key(mods_without_ctrl, vkey, scancode, code); + let preliminary_logical_key = layout.get_key(mods_without_ctrl, vkey, scancode, code); + let key_is_char = matches!(preliminary_logical_key, Key::Character(_)); + let is_pressed = state == ElementState::Pressed; + + // In some cases we want to use the UNICHAR text for logical_key in order to allow + // dead keys to have an effect on the character reported by `logical_key`. + let logical_key; + if is_pressed && key_is_char && !mods.contains(WindowsModifiers::CONTROL) { + logical_key = PartialLogicalKey::Text; + } else { + logical_key = PartialLogicalKey::This(preliminary_logical_key); + } let key_without_modifiers = match layout.get_key(NO_MODS, vkey, scancode, code) { // We convert dead keys into their character. // The reason for this is that `key_without_modifiers` is designed for key-bindings @@ -579,9 +599,17 @@ impl PartialKeyEventInfo { } } + let logical_key = match self.logical_key { + PartialLogicalKey::Text => match text { + Some(s) => Key::Character(s), + None => Key::Unidentified(NativeKeyCode::Windows(self.scancode)), + }, + PartialLogicalKey::This(v) => v, + }; + KeyEvent { physical_key: self.code, - logical_key: self.logical_key, + logical_key, text, location: self.location, state: self.key_state, diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 0a7783c3f6..6a5f38258e 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -115,9 +115,9 @@ impl WindowsModifiers { key_state[winuser::VK_RMENU as usize] &= !0x80; } if self.intersects(Self::CAPS_LOCK) { - key_state[winuser::VK_CAPITAL as usize] |= 0x80; + key_state[winuser::VK_CAPITAL as usize] |= 0x01; } else { - key_state[winuser::VK_CAPITAL as usize] &= !0x80; + key_state[winuser::VK_CAPITAL as usize] &= !0x01; } } From 72e36b565720659943315125cf42e861d78fc072 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 14 Feb 2021 23:14:14 +0100 Subject: [PATCH 54/75] Prevent Pause and NumLock mappings in window events --- src/platform_impl/windows/keyboard.rs | 68 ++++++++++++++++++--------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 3507d77aca..11ba1a04a1 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -527,37 +527,59 @@ impl PartialKeyEventInfo { let mods = WindowsModifiers::active_modifiers(&kbd_state); let mods_without_ctrl = mods.remove_only_ctrl(); + // On Windows Ctrl+NumLock = Pause (and apparently Ctrl+Pause -> NumLock). In these cases + // the KeyCode still stores the real key, so in the name of consistency across platforms, we + // circumvent this mapping and force the key values to match the keycode. + // For more on this, read the article by Raymond Chen, titled: + // "Why does Ctrl+ScrollLock cancel dialogs?" + // https://devblogs.microsoft.com/oldnewthing/20080211-00/?p=23503 + let code_as_key = if mods.contains(WindowsModifiers::CONTROL) { + match code { + KeyCode::NumLock => Some(Key::NumLock), + KeyCode::Pause => Some(Key::Pause), + _ => None, + } + } else { + None + }; + let preliminary_logical_key = layout.get_key(mods_without_ctrl, vkey, scancode, code); let key_is_char = matches!(preliminary_logical_key, Key::Character(_)); let is_pressed = state == ElementState::Pressed; - // In some cases we want to use the UNICHAR text for logical_key in order to allow - // dead keys to have an effect on the character reported by `logical_key`. - let logical_key; - if is_pressed && key_is_char && !mods.contains(WindowsModifiers::CONTROL) { - logical_key = PartialLogicalKey::Text; + let logical_key = if let Some(key) = code_as_key { + PartialLogicalKey::This(key) + } else if is_pressed && key_is_char && !mods.contains(WindowsModifiers::CONTROL) { + // In some cases we want to use the UNICHAR text for logical_key in order to allow + // dead keys to have an effect on the character reported by `logical_key`. + PartialLogicalKey::Text } else { - logical_key = PartialLogicalKey::This(preliminary_logical_key); - } - let key_without_modifiers = match layout.get_key(NO_MODS, vkey, scancode, code) { - // We convert dead keys into their character. - // The reason for this is that `key_without_modifiers` is designed for key-bindings - // but for example the US International treats `'` (apostrophe) as a dead key and - // reguar US keyboard treats it a character. In order for a single binding configuration - // to work with both layouts we forward each dead key as a character. - Key::Dead(k) => { - if let Some(ch) = k { - // I'm avoiding the heap allocation. I don't want to talk about it :( - let mut utf8 = [0; 4]; - let s = ch.encode_utf8(&mut utf8); - let static_str = get_or_insert_str(&mut layouts.strings, s); - Key::Character(static_str) - } else { - Key::Unidentified(NativeKeyCode::Unidentified) + PartialLogicalKey::This(preliminary_logical_key) + }; + let key_without_modifiers = if let Some(key) = code_as_key { + key + } else { + match layout.get_key(NO_MODS, vkey, scancode, code) { + // We convert dead keys into their character. + // The reason for this is that `key_without_modifiers` is designed for key-bindings + // but for example the US International treats `'` (apostrophe) as a dead key and + // reguar US keyboard treats it a character. In order for a single binding configuration + // to work with both layouts we forward each dead key as a character. + Key::Dead(k) => { + if let Some(ch) = k { + // I'm avoiding the heap allocation. I don't want to talk about it :( + let mut utf8 = [0; 4]; + let s = ch.encode_utf8(&mut utf8); + let static_str = get_or_insert_str(&mut layouts.strings, s); + Key::Character(static_str) + } else { + Key::Unidentified(NativeKeyCode::Unidentified) + } } + key => key, } - key => key, }; + PartialKeyEventInfo { vkey, scancode, From 98246b9072b0210b0af92a543cfc5348196389ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Art=C3=BAr=20Kov=C3=A1cs?= Date: Mon, 15 Feb 2021 19:22:06 +0100 Subject: [PATCH 55/75] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Markus Røyset --- src/event.rs | 8 +-- src/keyboard.rs | 74 +++++++++++--------- src/platform/modifier_supplement.rs | 4 +- src/platform/scancode.rs | 6 +- src/platform_impl/windows/event_loop.rs | 10 +-- src/platform_impl/windows/keyboard.rs | 66 ++++++++--------- src/platform_impl/windows/keyboard_layout.rs | 32 ++++----- src/platform_impl/windows/minimal_ime.rs | 2 +- src/platform_impl/windows/window.rs | 2 +- 9 files changed, 104 insertions(+), 100 deletions(-) diff --git a/src/event.rs b/src/event.rs index ecedf0d5e8..3848dc89ef 100644 --- a/src/event.rs +++ b/src/event.rs @@ -256,7 +256,7 @@ pub enum WindowEvent<'a> { /// /// ## Platform-specific /// - **Windows:** The shift key overrides NumLock. In other words, while shift is held down, - /// numpad keys act as if NumLock wasn't active. When this is used the OS sends fake key + /// numpad keys act as if NumLock wasn't active. When this is used, the OS sends fake key /// events which are not marked as `is_synthetic`. KeyboardInput { device_id: DeviceId, @@ -620,10 +620,10 @@ pub struct RawKeyEvent { pub struct KeyEvent { /// Represents the position of a key independent of the currently active layout. /// - /// It also uniquely identifies the physical key (i.e. it's synonymous with a scancode). + /// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode). /// - /// Note that `Fn` and `FnLock` key events are not guaranteed to be emmited by `winit`. These - /// keys are usually handled at the hardware or at the OS level. + /// Note that `Fn` and `FnLock` key events are not guaranteed to be emitted by `winit`. These + /// keys are usually handled at the hardware or OS level. pub physical_key: keyboard::KeyCode, /// This value is affected by all modifiers except Ctrl. diff --git a/src/keyboard.rs b/src/keyboard.rs index 53399161b0..4f33a07cf6 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -43,8 +43,8 @@ bitflags! { // const RALT = 0b001 << 6; /// This is the "windows" key on PC and "command" key on Mac. const SUPER = 0b100 << 9; - // const LLOGO = 0b010 << 9; - // const RLOGO = 0b001 << 9; + // const LSUPER = 0b010 << 9; + // const RSUPER = 0b001 << 9; } } @@ -159,7 +159,7 @@ pub enum KeyCode { /// Used for both the US \\ (on the 101-key layout) and also for the key /// located between the " and Enter keys on row C of the 102-, /// 104- and 106-key layouts. - /// Labelled # on a UK (102) keyboard. + /// Labeled # on a UK (102) keyboard. Backslash, /// [ on a US keyboard. BracketLeft, @@ -181,7 +181,7 @@ pub enum KeyCode { Digit5, /// 6 on a US keyboard. Digit6, - /// 7& on a US keyboard. + /// 7 on a US keyboard. Digit7, /// 8 on a US keyboard. Digit8, @@ -190,17 +190,17 @@ pub enum KeyCode { /// = on a US keyboard. Equal, /// Located between the left Shift and Z keys. - /// Labelled \\ on a UK keyboard. + /// Labeled \\ on a UK keyboard. IntlBackslash, /// Located between the / and right Shift keys. - /// Labelled \\ (ro) on a Japanese keyboard. + /// Labeled \\ (ro) on a Japanese keyboard. IntlRo, /// Located between the = and Backspace keys. - /// Labelled ¥ (yen) on a Japanese keyboard. \\ on a + /// Labeled ¥ (yen) on a Japanese keyboard. \\ on a /// Russian keyboard. IntlYen, /// a on a US keyboard. - /// Labelled q on an AZERTY (e.g., French) keyboard. + /// Labeled q on an AZERTY (e.g., French) keyboard. KeyA, /// b on a US keyboard. KeyB, @@ -233,7 +233,7 @@ pub enum KeyCode { /// p on a US keyboard. KeyP, /// q on a US keyboard. - /// Labelled a on an AZERTY (e.g., French) keyboard. + /// Labeled a on an AZERTY (e.g., French) keyboard. KeyQ, /// r on a US keyboard. KeyR, @@ -246,15 +246,15 @@ pub enum KeyCode { /// v on a US keyboard. KeyV, /// w on a US keyboard. - /// Labelled z on an AZERTY (e.g., French) keyboard. + /// Labeled z on an AZERTY (e.g., French) keyboard. KeyW, /// x on a US keyboard. KeyX, /// y on a US keyboard. - /// Labelled z on a QWERTZ (e.g., German) keyboard. + /// Labeled z on a QWERTZ (e.g., German) keyboard. KeyY, /// z on a US keyboard. - /// Labelled w on an AZERTY (e.g., French) keyboard, and y on a + /// Labeled w on an AZERTY (e.g., French) keyboard, and y on a /// QWERTZ (e.g., German) keyboard. KeyZ, /// - on a US keyboard. @@ -267,13 +267,13 @@ pub enum KeyCode { Semicolon, /// / on a US keyboard. Slash, - /// Alt Option or . + /// Alt, Option, or . AltLeft, - /// Alt Option or . - /// This is labelled AltGr key on many keyboard layouts. + /// Alt, Option, or . + /// This is labeled AltGr on many keyboard layouts. AltRight, /// Backspace or . - /// Labelled Delete on Apple keyboards. + /// Labeled Delete on Apple keyboards. Backspace, /// CapsLock or CapsLock, @@ -284,11 +284,11 @@ pub enum KeyCode { ControlLeft, /// Control or ControlRight, - /// Enter or . Labelled Return on Apple keyboards. + /// Enter or . Labeled Return on Apple keyboards. Enter, - /// The Windows, Command or other OS symbol key. + /// The Windows, , Command, or other OS symbol key. SuperLeft, - /// The Windows, Command or other OS symbol key. + /// The Windows, , Command, or other OS symbol key. SuperRight, /// Shift or ShiftLeft, @@ -320,9 +320,11 @@ pub enum KeyCode { NonConvert, /// . The forward delete key. /// Note that on Apple keyboards, the key labelled Delete on the main part of - /// the keyboard should be encoded as "Backspace". + /// the keyboard is encoded as [`Backspace`]. + /// + /// [`Backspace`]: Self::Backspace Delete, - /// Page Down End or + /// Page Down, End, or End, /// Help. Not present on standard PC keyboards. Help, @@ -330,9 +332,9 @@ pub enum KeyCode { Home, /// Insert or Ins. Not present on Apple keyboards. Insert, - /// Page Down PgDn or + /// Page Down, PgDn, or PageDown, - /// Page Up PgUp or + /// Page Up, PgUp, or PageUp, /// ArrowDown, @@ -342,8 +344,7 @@ pub enum KeyCode { ArrowRight, /// ArrowUp, - /// On the Mac, the "NumLock" code should be used for the numpad Clear - /// key. + /// On the Mac, this is used for the numpad Clear key. NumLock, /// 0 Ins on a keyboard. 0 on a phone or remote control Numpad0, @@ -373,7 +374,9 @@ pub enum KeyCode { NumpadBackspace, /// C or A (All Clear). Also for use with numpads that have a /// Clear key that is separate from the NumLock key. On the Mac, the - /// numpad Clear key should always be encoded as "NumLock". + /// numpad Clear key is encoded as [`NumLock`]. + /// + /// [`NumLock`]: Self::NumLock NumpadClear, /// C (Clear Entry) NumpadClearEntry, @@ -404,13 +407,14 @@ pub enum KeyCode { /// * on a keyboard. For use with numpads that provide mathematical /// operations (+, - * and /). /// - /// Use "NumpadStar" for the * key on phones and remote controls. + /// Use `NumpadStar` for the * key on phones and remote controls. NumpadMultiply, /// ( Found on the Microsoft Natural Keyboard. NumpadParenLeft, /// ) Found on the Microsoft Natural Keyboard. NumpadParenRight, /// * on a phone or remote control device. + /// /// This key is typically found below the 7 key and to the left of /// the 0 key. /// @@ -421,9 +425,7 @@ pub enum KeyCode { NumpadSubtract, /// Esc or Escape, - /// F This is typically a hardware key that does not generate a separate - /// code. Most keyboards do not place this key in the function section, but it is - /// included here to keep it with related keys. + /// Fn This is typically a hardware key that does not generate a separate code. Fn, /// FLock or FnLock. Function Lock key. Found on the Microsoft /// Natural Keyboard. @@ -592,7 +594,7 @@ pub enum KeyCode { /// /// This mostly conforms to the UI Events Specification's [`KeyboardEvent.key`] with a few /// exceptions: -/// - The `Super` variant here, is named `Meta` in the aforementionned specification. (There's +/// - The `Super` variant here, is named `Meta` in the aforementioned specification. (There's /// another key which the specification calls `Super`. That does not exist here.) /// - The `Space` variant here, can be identified by the character it generates in the /// specificaiton. @@ -625,12 +627,12 @@ pub enum Key<'a> { /// The `Alt` (Alternative) key. /// /// This key enables the alternate modifier function for interpreting concurrent or subsequent - /// keyboard input. This key value is also used for the Apple `Option` key. + /// keyboard input. This key value is also used for the Apple Option key. Alt, - /// The Alternate Graphics (`AltGr` or `AltGraph`) key. + /// The Alternate Graphics (AltGr or AltGraph) key. /// /// This key is used enable the ISO Level 3 shift modifier (the standard `Shift` key is the - /// level 2 modifier). See [ISO9995-1]. + /// level 2 modifier). AltGraph, /// The `Caps Lock` (Capital) key. /// @@ -685,7 +687,9 @@ pub enum Key<'a> { /// The End key, used with keyboard entry to go to the end of content (`KEYCODE_MOVE_END`). End, /// The Home key, used with keyboard entry, to go to start of content (`KEYCODE_MOVE_HOME`). - /// For the mobile phone `Home` key (which goes to the phone’s main screen), use `"GoHome"`. + /// For the mobile phone `Home` key (which goes to the phone’s main screen), use [`GoHome`]. + /// + /// [`GoHome`]: Self::GoHome Home, /// Scroll down or display next page of content. PageDown, diff --git a/src/platform/modifier_supplement.rs b/src/platform/modifier_supplement.rs index fec1187768..361ed05d04 100644 --- a/src/platform/modifier_supplement.rs +++ b/src/platform/modifier_supplement.rs @@ -18,7 +18,7 @@ pub trait KeyEventExtModifierSupplement { /// For example, pressing Ctrl+a produces `Some("\x01")`. fn text_with_all_modifiers(&self) -> Option<&str>; - /// This value ignores all modifiers including + /// This value ignores all modifiers including, /// but not limited to Shift, Caps Lock, /// and Ctrl. In most cases this means that the /// unicode character in the resulting string is lowercase. @@ -26,7 +26,7 @@ pub trait KeyEventExtModifierSupplement { /// This is useful for key-bindings / shortcut key combinations. /// /// In case `logical_key` reports `Dead`, this will still report the - /// key as `Characcter` according to the current keyboard layout. This value + /// key as `Character` according to the current keyboard layout. This value /// cannot be `Dead`. fn key_without_modifiers(&self) -> Key<'static>; } diff --git a/src/platform/scancode.rs b/src/platform/scancode.rs index 7617622b62..078fe74dea 100644 --- a/src/platform/scancode.rs +++ b/src/platform/scancode.rs @@ -14,7 +14,7 @@ use crate::keyboard::KeyCode; pub trait KeyCodeExtScancode { - /// The raw value of the platform specific physical key identifier. + /// The raw value of the platform-specific physical key identifier. /// /// Returns `Some(key_id)` if the conversion was succesful; returns `None` otherwise. /// @@ -23,9 +23,9 @@ pub trait KeyCodeExtScancode { // TODO: Describe what this value contains for each platform fn to_scancode(self) -> Option; - /// Constructs a `KeyCode` from a platform specific physical key identifier. + /// Constructs a `KeyCode` from a platform-specific physical key identifier. /// - /// Note that this conversion may be lossy, i.e. converting the returned `KeyCode` back, + /// Note that this conversion may be lossy, i.e. converting the returned `KeyCode` back /// using `to_scancode` might not yield the original value. fn from_scancode(scancode: u32) -> KeyCode; } diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 2fe3056abb..da43d9b7da 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2195,9 +2195,9 @@ unsafe fn handle_raw_input( if scancode == 0xE11D || scancode == 0xE02A { // At the hardware (or driver?) level, pressing the Pause key is equivalent to pressing // Ctrl+NumLock. - // This equvalence means that if the user presses Pause, the keyboard will emmit two + // This equvalence means that if the user presses Pause, the keyboard will emit two // subsequent keypresses: - // 1, 0xE11D - Which is a left ctrl (0x1D) with an extension flag (0xE100) + // 1, 0xE11D - Which is a left Ctrl (0x1D) with an extension flag (0xE100) // 2, 0x0045 - Which on its own can be interpreted as Pause // // There's another combination which isn't quite an equivalence: @@ -2207,7 +2207,7 @@ unsafe fn handle_raw_input( // 2, 0xE037 - Which is a numpad multiply (0x37) with an exteion flag (0xE000). This on // its own it can be interpreted as PrtSc // - // For this reason if we encounter the first keypress, we simply ignore it, trusting + // For this reason, if we encounter the first keypress, we simply ignore it, trusting // that there's going to be another event coming, from which we can extract the // appropriate key. // For more on this, read the article by Raymond Chen, titled: @@ -2217,7 +2217,7 @@ unsafe fn handle_raw_input( } let code; if keyboard.VKey as c_int == winuser::VK_NUMLOCK { - // Historically the NumLock and the Pause key were one and the same physical key. + // Historically, the NumLock and the Pause key were one and the same physical key. // The user could trigger Pause by pressing Ctrl+NumLock. // Now these are often physically separate and the two keys can be differentiated by // checking the extension flag of the scancode. NumLock is 0xE045, Pause is 0x0045. @@ -2253,7 +2253,7 @@ unsafe fn handle_raw_input( // as well. // // The issue is that in the raw device event (here), the fake shift release - // event reports the numpad key as the scancode. Unfortunately the event doesn't + // event reports the numpad key as the scancode. Unfortunately, the event doesn't // have any information to tell whether it's the left shift or the right shift // that needs to get the fake release (or press) event so we don't forward this // event to the application at all. diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 3507d77aca..4f7199df4d 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -33,7 +33,7 @@ pub struct KeyLParam { pub scancode: u8, pub extended: bool, - /// This is `previous_state XOR transition_state` see the lParam for WM_KEYDOWN and WM_KEYUP. + /// This is `previous_state XOR transition_state`. See the lParam for WM_KEYDOWN and WM_KEYUP for further details. pub is_repeat: bool, } @@ -58,36 +58,36 @@ pub struct MessageAsKeyEvent { /// Stores information required to make `KeyEvent`s. /// -/// A single winint `KeyEvent` contains information which the windows API passes to the application -/// in multiple window messages. In other words a winit `KeyEvent` cannot be build from a single -/// window message. Therefore this type keeps track of certain information from previous events so +/// A single Winit `KeyEvent` contains information which the Windows API passes to the application +/// in multiple window messages. In other words: a Winit `KeyEvent` cannot be built from a single +/// window message. Therefore, this type keeps track of certain information from previous events so /// that a `KeyEvent` can be constructed when the last event related to a keypress is received. /// -/// `PeekMessage` is sometimes used to determine wheter the next window message still belongs to the +/// `PeekMessage` is sometimes used to determine whether the next window message still belongs to the /// current keypress. If it doesn't and the current state represents a key event waiting to be -/// dispatched, than said event is considered complete and is dispatched. +/// dispatched, then said event is considered complete and is dispatched. /// /// The sequence of window messages for a key press event is the following: /// - Exactly one WM_KEYDOWN / WM_SYSKEYDOWN /// - Zero or one WM_DEADCHAR / WM_SYSDEADCHAR /// - Zero or more WM_CHAR / WM_SYSCHAR. These messages each come with a UTF-16 code unit which when -/// put together in the sequence they arrived in, forms the text which is the result of pressing the -/// key. +/// put together in the sequence they arrived in, forms the text which is the result of pressing the +/// key. /// /// Key release messages are a bit different due to the fact that they don't contribute to /// text input. The "sequence" only consists of one WM_KEYUP / WM_SYSKEYUP event. pub struct KeyEventBuilder { event_info: Option, - /// The keyup event needs to call `ToUnicode` to determine what's the text produced by the - /// key with all modifiers except CTRL (the `logical_key`). + /// The keyup event needs to call `ToUnicode` to determine what the text produced by the + /// key with all modifiers except CTRL (the `logical_key`) is. /// /// But `ToUnicode` without the non-modifying flag (see `key_labels`), resets the dead key /// state which would be incorrect during every keyup event. Therefore this variable is used /// to determine whether the last keydown event produced a dead key. /// /// Note that this variable is not always correct because it does - /// not track key presses outside of this window. However the ONLY situation where this + /// not track key presses outside of this window. However, the ONLY situation where this /// doesn't work as intended is when the user presses a dead key outside of this window, and /// switches to this window BEFORE releasing it then releases the dead key. In this case /// the `ToUnicode` function will be called, incorrectly clearing the dead key state. Having @@ -177,7 +177,7 @@ impl KeyEventBuilder { } winuser::WM_DEADCHAR | winuser::WM_SYSDEADCHAR => { self.prev_down_was_dead = true; - // At this point we know that there isn't going to be any more events related to + // At this point, we know that there isn't going to be any more events related to // this key press let event_info = self.event_info.take().unwrap(); *retval = Some(0); @@ -225,8 +225,8 @@ impl KeyEventBuilder { .utf16parts .push(wparam as u16); } else { - // Otherwise wparam holds a utf32 character. - // Let's encode it as utf16 appending it to the end of utf16parts + // In this case, wparam holds a UTF-32 character. + // Let's encode it as UTF-16 and append it to the end of `utf16parts` let utf16parts = &mut self.event_info.as_mut().unwrap().utf16parts; let start_offset = utf16parts.len(); let new_size = utf16parts.len() + 2; @@ -241,7 +241,7 @@ impl KeyEventBuilder { let mut event_info = self.event_info.take().unwrap(); let mut layouts = LAYOUT_CACHE.lock().unwrap(); - // Here it's okay to call `ToUnicode` because at this point the dead key + // It's okay to call `ToUnicode` here, because at this point the dead key // is already consumed by the character. let kbd_state = get_kbd_state(); let mod_state = WindowsModifiers::active_modifiers(&kbd_state); @@ -255,9 +255,9 @@ impl KeyEventBuilder { ctrl_on = mod_state.contains(WindowsModifiers::CONTROL) } - // If CTRL is not pressed, just use the text with all - // modifiers because that already consumed the dead key and otherwise - // we would interpret the character incorretly, missing the dead key. + // If Ctrl is not pressed, just use the text with all + // modifiers because that already consumed the dead key. Otherwise, + // we would interpret the character incorrectly, missing the dead key. if !ctrl_on { event_info.text = PartialText::System(event_info.utf16parts.clone()); } else { @@ -310,17 +310,17 @@ impl KeyEventBuilder { }; } - // Is caps-lock active? Be careful that this is different from caps-lock + // Is caps-lock active? Note that this is different from caps-lock // being held down. let caps_lock_on = kbd_state[winuser::VK_CAPITAL as usize] & 1 != 0; - // We are synthesizing the press event for caps-lock first. The reason: - // 1, if caps-lock is *not* held down but it's active, than we have to - // synthesize all printable keys, respecting the calps-lock state - // 2, if caps-lock is held down, we could choose to sythesize it's - // keypress after every other key, in which case all other keys *must* - // be sythesized as if the caps-lock state would be the opposite - // of what it currently is. + // We are synthesizing the press event for caps-lock first for the following reasons: + // 1. If caps-lock is *not* held down but *is* active, then we have to + // synthesize all printable keys, respecting the caps-lock state. + // 2. If caps-lock is held down, we could choose to sythesize its + // keypress after every other key, in which case all other keys *must* + // be sythesized as if the caps-lock state was be the opposite + // of what it currently is. // -- // For the sake of simplicity we are choosing to always sythesize // caps-lock first, and always use the current caps-lock state @@ -487,7 +487,7 @@ struct PartialKeyEventInfo { key_without_modifiers: Key<'static>, - /// The utf16 code units of the text that was produced by the keypress event. + /// The UTF-16 code units of the text that was produced by the keypress event. /// This take all modifiers into account. Including CTRL utf16parts: Vec, @@ -541,10 +541,10 @@ impl PartialKeyEventInfo { } let key_without_modifiers = match layout.get_key(NO_MODS, vkey, scancode, code) { // We convert dead keys into their character. - // The reason for this is that `key_without_modifiers` is designed for key-bindings - // but for example the US International treats `'` (apostrophe) as a dead key and - // reguar US keyboard treats it a character. In order for a single binding configuration - // to work with both layouts we forward each dead key as a character. + // The reason for this is that `key_without_modifiers` is designed for key-bindings, + // but the US International layout treats `'` (apostrophe) as a dead key and the + // reguar US layout treats it a character. In order for a single binding configuration + // to work with both layouts, we forward each dead key as a character. Key::Dead(k) => { if let Some(ch) = k { // I'm avoiding the heap allocation. I don't want to talk about it :( @@ -582,7 +582,7 @@ impl PartialKeyEventInfo { } } - // The text without ctrl + // The text without Ctrl let mut text = None; match self.text { PartialText::System(wide) => { @@ -643,7 +643,7 @@ fn get_location(scancode: ExScancode, hkl: HKL) -> KeyLocation { }; // Use the native VKEY and the extended flag to cover most cases - // This is taken from the `druid` software within + // This is taken from the `druid` GUI library, specifically // druid-shell/src/platform/windows/keyboard.rs match vkey { VK_LSHIFT | VK_LCONTROL | VK_LMENU | VK_LWIN => KeyLocation::Left, diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 6a5f38258e..6612db580f 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -137,13 +137,13 @@ pub(crate) struct Layout { /// Maps a Windows virtual key to a `Key`. /// - /// Only those keys are mapped for which the modifier state is not needed when mapping. Making - /// this field separate from the `keys` field, saves having to add NumLock as a modifier to - /// `WindowsModifiers`, which would double the number of items in keys. + /// The only keys that are mapped are ones which don't require knowing the modifier state when + /// mapping. Making this field separate from the `keys` field saves having to add NumLock as a + /// modifier to `WindowsModifiers`, which would double the number of items in keys. pub simple_vkeys: HashMap>, /// Maps a modifier state to group of key strings - /// Not using `ModifiersState` here because that object cannot express caps lock + /// We're not using `ModifiersState` here because that object cannot express caps lock, /// but we need to handle caps lock too. /// /// This map shouldn't need to exist. @@ -151,7 +151,7 @@ pub(crate) struct Layout { /// of getting the label for the pressed key. Note that calling `ToUnicode` /// just when the key is pressed/released would be enough if `ToUnicode` wouldn't /// change the keyboard state (it clears the dead key). There is a flag to prevent - /// changing the state but that flag requires Windows 10, version 1607 or newer) + /// changing the state, but that flag requires Windows 10, version 1607 or newer) pub keys: HashMap>>, pub has_alt_graph: bool, } @@ -169,7 +169,7 @@ impl Layout { let unknown_alt = vkey == winuser::VK_MENU; if !unknown_alt { // Here we try using the virtual key directly but if the virtual key doesn't distinguish - // between left and right alt, we can't report AltGr. Therefore we only do this if the + // between left and right alt, we can't report AltGr. Therefore, we only do this if the // key is not the "unknown alt" key. // // The reason for using the virtual key directly is that `MapVirtualKeyExW` (used when @@ -249,7 +249,7 @@ impl LayoutCache { // simulate a scenario when no modifier is active. let mut key_state = [0u8; 256]; - // First, generate all the simple vkeys + // First, generate all the simple vkeys. // Some numpad keys generate different charcaters based on the locale. // For example `VK_DECIMAL` is sometimes "." and sometimes "," layout.simple_vkeys.reserve(NUMPAD_VKEYS.len()); @@ -382,7 +382,7 @@ impl LayoutCache { locale_id as HKL, ); if wide_len < 0 { - // If it's dead, let's run `ToUnicode` again, to consume the dead-key + // If it's dead, we run `ToUnicode` again to consume the dead-key wide_len = winuser::ToUnicodeEx( vkey, scancode, @@ -436,14 +436,14 @@ enum ToUnicodeResult { None, } -/// This converts virtual keys to `Key`s. Only those virtual keys are converted which can be -/// unambigously converted to a `Key` with only information passed in as arguments. +/// This converts virtual keys to `Key`s. Only virtual keys which can be unambiguously converted to +/// a `Key`, with only the information passed in as arguments, are converted. /// -/// In other words this function does not need to "prepare" the current layout in order to do -/// the conversion but as such it cannot convert language specific character keys for example. +/// In other words: this function does not need to "prepare" the current layout in order to do +/// the conversion, but as such it cannot convert certain keys, like language-specific character keys. /// /// The result includes all non-character keys defined within `Key` plus characters from numpad keys. -/// So for example backspace and tab are included. +/// For example, backspace and tab are included. fn vkey_to_non_char_key( vkey: i32, native_code: NativeKeyCode, @@ -479,7 +479,7 @@ fn vkey_to_non_char_key( //winuser::VK_HANGEUL => Key::HangulMode, // Deprecated in favour of VK_HANGUL - // VK_HANGUL and VK_KANA are defined as the same constant therefore + // VK_HANGUL and VK_KANA are defined as the same constant, therefore // we use appropriate conditions to differentate between them winuser::VK_HANGUL if is_korean => Key::HangulMode, winuser::VK_KANA if is_japanese => Key::KanaMode, @@ -487,7 +487,7 @@ fn vkey_to_non_char_key( winuser::VK_JUNJA => Key::JunjaMode, winuser::VK_FINAL => Key::FinalMode, - // VK_HANJA and VK_KANJI are defined as the same constant therefore + // VK_HANJA and VK_KANJI are defined as the same constant, therefore // we use appropriate conditions to differentate between them winuser::VK_HANJA if is_korean => Key::HanjaMode, winuser::VK_KANJI if is_japanese => Key::KanjiMode, @@ -672,7 +672,7 @@ fn vkey_to_non_char_key( } else { // This matches IE and Firefox behaviour according to // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values - // At the time of writing there is no `Key::Finish` variant as + // At the time of writing, there is no `Key::Finish` variant as // Finish is not mentionned at https://w3c.github.io/uievents-key/ // Also see: https://github.com/pyfisch/keyboard-types/issues/9 Key::Unidentified(native_code) diff --git a/src/platform_impl/windows/minimal_ime.rs b/src/platform_impl/windows/minimal_ime.rs index 2c0a869999..8591e4f565 100644 --- a/src/platform_impl/windows/minimal_ime.rs +++ b/src/platform_impl/windows/minimal_ime.rs @@ -22,7 +22,7 @@ pub fn is_msg_ime_related(msg_kind: u32) -> bool { } pub struct MinimalIme { - // True if currently receiving messages belonging to finished IME session. + // True if we're currently receiving messages belonging to a finished IME session. getting_ime_text: bool, utf16parts: Vec, diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 2358d400c5..5ee098ac0b 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -658,7 +658,7 @@ impl Window { #[inline] pub fn reset_dead_keys(&self) { - // `ToUnicode` consumes the dead-key by default so we are constructing a fake (but valid) + // `ToUnicode` consumes the dead-key by default, so we are constructing a fake (but valid) // key input which we can call `ToUnicode` with. unsafe { let vk = 'a' as u32; From 0aa46fde4d6ca3ff3702bba4273ad8113e520a48 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Mon, 15 Feb 2021 20:22:43 +0100 Subject: [PATCH 56/75] Ran `cargo fmt` --- src/keyboard.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 4f33a07cf6..40d3a0f6f7 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -425,7 +425,7 @@ pub enum KeyCode { NumpadSubtract, /// Esc or Escape, - /// Fn This is typically a hardware key that does not generate a separate code. + /// Fn This is typically a hardware key that does not generate a separate code. Fn, /// FLock or FnLock. Function Lock key. Found on the Microsoft /// Natural Keyboard. From ea98471d2d978aab3935e83bbdd190d30541583f Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Mon, 15 Feb 2021 20:24:12 +0100 Subject: [PATCH 57/75] Add W3C license for `Key` and `KeyCode` --- src/keyboard.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/keyboard.rs b/src/keyboard.rs index 40d3a0f6f7..8823727ad6 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,5 +1,74 @@ //! Types related to the keyboard. +// This file contains a substantial portion of the UI Events Specification by the W3C. In +// particular, the variant names within `Key` and `KeyCode` and their documentation are modified +// versions of contents of the aforementioned specification. +// +// The original documents are: +// +// ### For `Key` +// UI Events KeyboardEvent key Values +// https://www.w3.org/TR/2017/CR-uievents-key-20170601/ +// Copyright © 2017 W3C® (MIT, ERCIM, Keio, Beihang). +// +// ### For `KeyCode` +// UI Events KeyboardEvent code Values +// https://www.w3.org/TR/2017/CR-uievents-code-20170601/ +// Copyright © 2017 W3C® (MIT, ERCIM, Keio, Beihang). +// +// These documents were used under the terms of the following license. This W3C license as well as +// the W3C short notice apply to the `Key` and `KeyCode` enums and their variants and the +// documentation attached to their variants. + +// --------- BEGGINING OF W3C LICENSE -------------------------------------------------------------- +// +// License +// +// By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, +// and will comply with the following terms and conditions. +// +// Permission to copy, modify, and distribute this work, with or without modification, for any +// purpose and without fee or royalty is hereby granted, provided that you include the following on +// ALL copies of the work or portions thereof, including modifications: +// +// - The full text of this NOTICE in a location viewable to users of the redistributed or derivative +// work. +// - Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none +// exist, the W3C Software and Document Short Notice should be included. +// - Notice of any changes or modifications, through a copyright statement on the new code or +// document such as "This software or document includes material copied from or derived from +// [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." +// +// Disclaimers +// +// THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR +// ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD +// PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. +// +// COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES +// ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. +// +// The name and trademarks of copyright holders may NOT be used in advertising or publicity +// pertaining to the work without specific, written prior permission. Title to copyright in this +// work will at all times remain with copyright holders. +// +// --------- END OF W3C LICENSE -------------------------------------------------------------------- + +// --------- BEGGINING OF W3C SHORT NOTICE --------------------------------------------------------- +// +// winit: https://github.com/rust-windowing/winit +// +// Copyright © 2021 World Wide Web Consortium, (Massachusetts Institute of Technology, European +// Research Consortium for Informatics and Mathematics, Keio University, Beihang). All Rights +// Reserved. This work is distributed under the W3C® Software License [1] in the hope that it will +// be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// [1] http://www.w3.org/Consortium/Legal/copyright-software +// +// --------- END OF W3C SHORT NOTICE --------------------------------------------------------------- + use nameof::name_of; impl ModifiersState { From 3a9473516b07a71896535b78a2ab7bc2392a8c4e Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Tue, 16 Feb 2021 20:13:19 +0100 Subject: [PATCH 58/75] Extend documentation according to feedback --- src/event.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/event.rs b/src/event.rs index 3848dc89ef..2662c841d5 100644 --- a/src/event.rs +++ b/src/event.rs @@ -621,6 +621,10 @@ pub struct KeyEvent { /// Represents the position of a key independent of the currently active layout. /// /// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode). + /// The most prevalent use case for this is games. For example the default keys for the player + /// to move around might be the W, A, S, and D keys on a US layout. The position of these keys + /// is more important than their label, so they should map to Z, Q, S, and D on an "AZERTY" + /// layout. (This value is `KeyCode::KeyW` for the Z key on an AZERTY layout.) /// /// Note that `Fn` and `FnLock` key events are not guaranteed to be emitted by `winit`. These /// keys are usually handled at the hardware or OS level. From 70b2d6e951fdad45b5bc4d97bbe210fd456a4cb4 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 20 Feb 2021 13:59:17 +0100 Subject: [PATCH 59/75] Ignore NumLock in `key_without_modifiers` --- src/keyboard.rs | 2 +- src/platform/windows.rs | 2 +- src/platform_impl/windows/keyboard.rs | 38 +- src/platform_impl/windows/keyboard_layout.rs | 532 ++++++++++++++++++- src/platform_impl/windows/mod.rs | 2 +- 5 files changed, 546 insertions(+), 30 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 8823727ad6..28983cfaf4 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -208,6 +208,7 @@ impl std::fmt::Debug for NativeKeyCode { /// exceptions: /// - The keys that the specification calls "MetaLeft" and "MetaRight" are named "SuperLeft" and /// "SuperRight" here. +/// - The key that the specification calls "Super" is reported as `Unidentified` here. /// - The `Unidentified` variant here, can still identifiy a key through it's `NativeKeyCode`. /// /// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables @@ -536,7 +537,6 @@ pub enum KeyCode { AudioVolumeUp, WakeUp, Hyper, - Super, Turbo, Abort, Resume, diff --git a/src/platform/windows.rs b/src/platform/windows.rs index ba799d2124..c5605fd448 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -253,7 +253,7 @@ impl KeyEventExtModifierSupplement for KeyEvent { #[inline] fn key_without_modifiers(&self) -> Key<'static> { - self.platform_specific.key_without_modifers + self.platform_specific.key_without_modifiers } } diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 96458cddae..59a310c669 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -262,10 +262,11 @@ impl KeyEventBuilder { event_info.text = PartialText::System(event_info.utf16parts.clone()); } else { let mod_no_ctrl = mod_state.remove_only_ctrl(); + let num_lock_on = kbd_state[winuser::VK_NUMLOCK as usize] & 1 != 0; let vkey = event_info.vkey; let scancode = event_info.scancode; let keycode = event_info.code; - let key = layout.get_key(mod_no_ctrl, vkey, scancode, keycode); + let key = layout.get_key(mod_no_ctrl, num_lock_on, vkey, scancode, keycode); event_info.text = PartialText::Text(key.to_text()); } let ev = event_info.finalize(&mut layouts.strings); @@ -313,6 +314,7 @@ impl KeyEventBuilder { // Is caps-lock active? Note that this is different from caps-lock // being held down. let caps_lock_on = kbd_state[winuser::VK_CAPITAL as usize] & 1 != 0; + let num_lock_on = kbd_state[winuser::VK_NUMLOCK as usize] & 1 != 0; // We are synthesizing the press event for caps-lock first for the following reasons: // 1. If caps-lock is *not* held down but *is* active, then we have to @@ -330,6 +332,7 @@ impl KeyEventBuilder { winuser::VK_CAPITAL, key_state, caps_lock_on, + num_lock_on, locale_id as HKL, &mut layouts, ); @@ -355,8 +358,14 @@ impl KeyEventBuilder { if !is_key_pressed!(vk) { continue; } - let event = - self.create_synthetic(vk, key_state, caps_lock_on, locale_id as HKL, layouts); + let event = self.create_synthetic( + vk, + key_state, + caps_lock_on, + num_lock_on, + locale_id as HKL, + layouts, + ); if let Some(event) = event { key_events.push(event); } @@ -377,6 +386,7 @@ impl KeyEventBuilder { *vk, key_state, caps_lock_on, + num_lock_on, locale_id as HKL, layouts, ); @@ -409,6 +419,7 @@ impl KeyEventBuilder { vk: i32, key_state: ElementState, caps_lock_on: bool, + num_lock_on: bool, locale_id: HKL, layouts: &mut MutexGuard<'_, LayoutCache>, ) -> Option { @@ -425,13 +436,10 @@ impl KeyEventBuilder { } else { WindowsModifiers::empty() }; - let logical_key; - let key_without_modifers; - { - let layout = layouts.layouts.get(&(locale_id as u64)).unwrap(); - logical_key = layout.get_key(mods, vk, scancode, code); - key_without_modifers = layout.get_key(WindowsModifiers::empty(), vk, scancode, code); - } + let layout = layouts.layouts.get(&(locale_id as u64)).unwrap(); + let logical_key = layout.get_key(mods, num_lock_on, vk, scancode, code); + let key_without_modifiers = + layout.get_key(WindowsModifiers::empty(), false, vk, scancode, code); let text; if key_state == ElementState::Pressed { text = logical_key.to_text(); @@ -441,7 +449,7 @@ impl KeyEventBuilder { let event_info = PartialKeyEventInfo { vkey: vk, logical_key: PartialLogicalKey::This(logical_key), - key_without_modifiers: key_without_modifers, + key_without_modifiers, key_state, scancode, is_repeat: false, @@ -526,6 +534,7 @@ impl PartialKeyEventInfo { let kbd_state = get_kbd_state(); let mods = WindowsModifiers::active_modifiers(&kbd_state); let mods_without_ctrl = mods.remove_only_ctrl(); + let num_lock_on = kbd_state[winuser::VK_NUMLOCK as usize] & 1 != 0; // On Windows Ctrl+NumLock = Pause (and apparently Ctrl+Pause -> NumLock). In these cases // the KeyCode still stores the real key, so in the name of consistency across platforms, we @@ -543,7 +552,8 @@ impl PartialKeyEventInfo { None }; - let preliminary_logical_key = layout.get_key(mods_without_ctrl, vkey, scancode, code); + let preliminary_logical_key = + layout.get_key(mods_without_ctrl, num_lock_on, vkey, scancode, code); let key_is_char = matches!(preliminary_logical_key, Key::Character(_)); let is_pressed = state == ElementState::Pressed; @@ -559,7 +569,7 @@ impl PartialKeyEventInfo { let key_without_modifiers = if let Some(key) = code_as_key { key } else { - match layout.get_key(NO_MODS, vkey, scancode, code) { + match layout.get_key(NO_MODS, false, vkey, scancode, code) { // We convert dead keys into their character. // The reason for this is that `key_without_modifiers` is designed for key-bindings, // but the US International layout treats `'` (apostrophe) as a dead key and the @@ -638,7 +648,7 @@ impl PartialKeyEventInfo { repeat: self.is_repeat, platform_specific: KeyEventExtra { text_with_all_modifers: char_with_all_modifiers, - key_without_modifers: self.key_without_modifiers, + key_without_modifiers: self.key_without_modifiers, }, } } diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 6612db580f..75624b1ac9 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -49,6 +49,29 @@ const NUMPAD_VKEYS: [c_int; 16] = [ winuser::VK_DIVIDE, ]; +lazy_static! { + static ref NUMPAD_KEYCODES: HashSet = { + let mut keycodes = HashSet::new(); + keycodes.insert(KeyCode::Numpad0); + keycodes.insert(KeyCode::Numpad1); + keycodes.insert(KeyCode::Numpad2); + keycodes.insert(KeyCode::Numpad3); + keycodes.insert(KeyCode::Numpad4); + keycodes.insert(KeyCode::Numpad5); + keycodes.insert(KeyCode::Numpad6); + keycodes.insert(KeyCode::Numpad7); + keycodes.insert(KeyCode::Numpad8); + keycodes.insert(KeyCode::Numpad9); + keycodes.insert(KeyCode::NumpadMultiply); + keycodes.insert(KeyCode::NumpadAdd); + keycodes.insert(KeyCode::NumpadComma); + keycodes.insert(KeyCode::NumpadSubtract); + keycodes.insert(KeyCode::NumpadDecimal); + keycodes.insert(KeyCode::NumpadDivide); + keycodes + }; +} + bitflags! { pub struct WindowsModifiers : u8 { const SHIFT = 1 << 0; @@ -135,12 +158,18 @@ impl WindowsModifiers { pub(crate) struct Layout { pub hkl: u64, - /// Maps a Windows virtual key to a `Key`. + /// Maps numpad keys from Windows virtual key to a `Key`. /// - /// The only keys that are mapped are ones which don't require knowing the modifier state when - /// mapping. Making this field separate from the `keys` field saves having to add NumLock as a - /// modifier to `WindowsModifiers`, which would double the number of items in keys. - pub simple_vkeys: HashMap>, + /// This is useful because some numpad keys generate different charcaters based on the locale. + /// For example `VK_DECIMAL` is sometimes "." and sometimes ",". Note: numpad-specific virtual + /// keys are only produced by Windows when the NumLock is active. + /// + /// Making this field separate from the `keys` field saves having to add NumLock as a modifier + /// to `WindowsModifiers`, which would double the number of items in keys. + pub numlock_on_keys: HashMap>, + /// Like `numlock_on_keys` but this will map to the key that would be produced if numlock was + /// off. The keys of this map are identical to the keys of `numlock_on_keys`. + pub numlock_off_keys: HashMap>, /// Maps a modifier state to group of key strings /// We're not using `ModifiersState` here because that object cannot express caps lock, @@ -160,6 +189,7 @@ impl Layout { pub fn get_key( &self, mods: WindowsModifiers, + num_lock_on: bool, vkey: c_int, scancode: ExScancode, keycode: KeyCode, @@ -183,8 +213,14 @@ impl Layout { return key_from_vkey; } } - if let Some(key) = self.simple_vkeys.get(&vkey) { - return *key; + if num_lock_on { + if let Some(key) = self.numlock_on_keys.get(&vkey) { + return *key; + } + } else { + if let Some(key) = self.numlock_off_keys.get(&vkey) { + return *key; + } } if let Some(keys) = self.keys.get(&mods) { if let Some(key) = keys.get(&keycode) { @@ -240,7 +276,8 @@ impl LayoutCache { fn prepare_layout(strings: &mut HashSet<&'static str>, locale_id: u64) -> Layout { let mut layout = Layout { hkl: locale_id, - simple_vkeys: Default::default(), + numlock_on_keys: Default::default(), + numlock_off_keys: Default::default(), keys: Default::default(), has_alt_graph: false, }; @@ -249,10 +286,43 @@ impl LayoutCache { // simulate a scenario when no modifier is active. let mut key_state = [0u8; 256]; - // First, generate all the simple vkeys. - // Some numpad keys generate different charcaters based on the locale. - // For example `VK_DECIMAL` is sometimes "." and sometimes "," - layout.simple_vkeys.reserve(NUMPAD_VKEYS.len()); + // `MapVirtualKeyExW` maps (non-numpad-specific) virtual keys to scancodes as if numlock + // was off. We rely on this behavior to find all virtual keys which are not numpad-specific + // but map to the numpad. + // + // src_vkey: VK ==> scancode: u16 (on the numpad) + // + // Then we convert the source virtual key into a `Key` and the scancode into a virtual key + // to get the reverse mapping. + // + // src_vkey: VK ==> scancode: u16 (on the numpad) + // || || + // \/ \/ + // map_value: Key <- map_vkey: VK + layout.numlock_off_keys.reserve(NUMPAD_KEYCODES.len()); + for vk in 0..256 { + let scancode = unsafe { + winuser::MapVirtualKeyExW(vk, winuser::MAPVK_VK_TO_VSC_EX, locale_id as HKL) + }; + if scancode == 0 { + continue; + } + let keycode = KeyCode::from_scancode(scancode); + if !is_numpad_specific(vk as i32) && NUMPAD_KEYCODES.contains(&keycode) { + let native_code = NativeKeyCode::Windows(scancode as u16); + let map_vkey = keycode_to_vkey(keycode, locale_id); + if map_vkey == 0 { + continue; + } + let map_value = vkey_to_non_char_key(vk as i32, native_code, locale_id, false); + if matches!(map_value, Key::Unidentified(_)) { + continue; + } + layout.numlock_off_keys.insert(map_vkey, map_value); + } + } + + layout.numlock_on_keys.reserve(NUMPAD_VKEYS.len()); for vk in NUMPAD_VKEYS.iter() { let vk = (*vk) as u32; let scancode = unsafe { @@ -262,7 +332,7 @@ impl LayoutCache { if let ToUnicodeResult::Str(s) = unicode { let static_str = get_or_insert_str(strings, s); layout - .simple_vkeys + .numlock_on_keys .insert(vk as i32, Key::Character(static_str)); } } @@ -436,6 +506,442 @@ enum ToUnicodeResult { None, } +fn is_numpad_specific(vk: i32) -> bool { + match vk { + winuser::VK_NUMPAD0 => true, + winuser::VK_NUMPAD1 => true, + winuser::VK_NUMPAD2 => true, + winuser::VK_NUMPAD3 => true, + winuser::VK_NUMPAD4 => true, + winuser::VK_NUMPAD5 => true, + winuser::VK_NUMPAD6 => true, + winuser::VK_NUMPAD7 => true, + winuser::VK_NUMPAD8 => true, + winuser::VK_NUMPAD9 => true, + winuser::VK_ADD => true, + winuser::VK_SUBTRACT => true, + winuser::VK_DIVIDE => true, + winuser::VK_DECIMAL => true, + winuser::VK_SEPARATOR => true, + _ => false, + } +} + +fn keycode_to_vkey(keycode: KeyCode, hkl: u64) -> i32 { + let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); + let is_korean = primary_lang_id == LANG_KOREAN; + let is_japanese = primary_lang_id == LANG_JAPANESE; + + match keycode { + KeyCode::Backquote => 0, + KeyCode::Backslash => 0, + KeyCode::BracketLeft => 0, + KeyCode::BracketRight => 0, + KeyCode::Comma => 0, + KeyCode::Digit0 => 0, + KeyCode::Digit1 => 0, + KeyCode::Digit2 => 0, + KeyCode::Digit3 => 0, + KeyCode::Digit4 => 0, + KeyCode::Digit5 => 0, + KeyCode::Digit6 => 0, + KeyCode::Digit7 => 0, + KeyCode::Digit8 => 0, + KeyCode::Digit9 => 0, + KeyCode::Equal => 0, + KeyCode::IntlBackslash => 0, + KeyCode::IntlRo => 0, + KeyCode::IntlYen => 0, + KeyCode::KeyA => 0, + KeyCode::KeyB => 0, + KeyCode::KeyC => 0, + KeyCode::KeyD => 0, + KeyCode::KeyE => 0, + KeyCode::KeyF => 0, + KeyCode::KeyG => 0, + KeyCode::KeyH => 0, + KeyCode::KeyI => 0, + KeyCode::KeyJ => 0, + KeyCode::KeyK => 0, + KeyCode::KeyL => 0, + KeyCode::KeyM => 0, + KeyCode::KeyN => 0, + KeyCode::KeyO => 0, + KeyCode::KeyP => 0, + KeyCode::KeyQ => 0, + KeyCode::KeyR => 0, + KeyCode::KeyS => 0, + KeyCode::KeyT => 0, + KeyCode::KeyU => 0, + KeyCode::KeyV => 0, + KeyCode::KeyW => 0, + KeyCode::KeyX => 0, + KeyCode::KeyY => 0, + KeyCode::KeyZ => 0, + KeyCode::Minus => 0, + KeyCode::Period => 0, + KeyCode::Quote => 0, + KeyCode::Semicolon => 0, + KeyCode::Slash => 0, + KeyCode::AltLeft => winuser::VK_LMENU, + KeyCode::AltRight => winuser::VK_RMENU, + KeyCode::Backspace => winuser::VK_BACK, + KeyCode::CapsLock => winuser::VK_CAPITAL, + KeyCode::ContextMenu => winuser::VK_APPS, + KeyCode::ControlLeft => winuser::VK_LCONTROL, + KeyCode::ControlRight => winuser::VK_RCONTROL, + KeyCode::Enter => winuser::VK_RETURN, + KeyCode::SuperLeft => winuser::VK_LWIN, + KeyCode::SuperRight => winuser::VK_RWIN, + KeyCode::ShiftLeft => winuser::VK_RSHIFT, + KeyCode::ShiftRight => winuser::VK_LSHIFT, + KeyCode::Space => winuser::VK_SPACE, + KeyCode::Tab => winuser::VK_TAB, + KeyCode::Convert => winuser::VK_CONVERT, + KeyCode::KanaMode => winuser::VK_KANA, + KeyCode::Lang1 if is_korean => winuser::VK_HANGUL, + KeyCode::Lang1 if is_japanese => winuser::VK_KANA, + KeyCode::Lang2 if is_korean => winuser::VK_HANJA, + KeyCode::Lang2 if is_japanese => 0, + KeyCode::Lang3 if is_japanese => winuser::VK_OEM_FINISH, + KeyCode::Lang4 if is_japanese => 0, + KeyCode::Lang5 if is_japanese => 0, + KeyCode::NonConvert => winuser::VK_NONCONVERT, + KeyCode::Delete => winuser::VK_DELETE, + KeyCode::End => winuser::VK_END, + KeyCode::Help => winuser::VK_HELP, + KeyCode::Home => winuser::VK_HOME, + KeyCode::Insert => winuser::VK_INSERT, + KeyCode::PageDown => winuser::VK_NEXT, + KeyCode::PageUp => winuser::VK_PRIOR, + KeyCode::ArrowDown => winuser::VK_DOWN, + KeyCode::ArrowLeft => winuser::VK_LEFT, + KeyCode::ArrowRight => winuser::VK_RIGHT, + KeyCode::ArrowUp => winuser::VK_UP, + KeyCode::NumLock => winuser::VK_NUMLOCK, + KeyCode::Numpad0 => winuser::VK_NUMPAD0, + KeyCode::Numpad1 => winuser::VK_NUMPAD1, + KeyCode::Numpad2 => winuser::VK_NUMPAD2, + KeyCode::Numpad3 => winuser::VK_NUMPAD3, + KeyCode::Numpad4 => winuser::VK_NUMPAD4, + KeyCode::Numpad5 => winuser::VK_NUMPAD5, + KeyCode::Numpad6 => winuser::VK_NUMPAD6, + KeyCode::Numpad7 => winuser::VK_NUMPAD7, + KeyCode::Numpad8 => winuser::VK_NUMPAD8, + KeyCode::Numpad9 => winuser::VK_NUMPAD9, + KeyCode::NumpadAdd => winuser::VK_ADD, + KeyCode::NumpadBackspace => winuser::VK_BACK, + KeyCode::NumpadClear => winuser::VK_CLEAR, + KeyCode::NumpadClearEntry => 0, + KeyCode::NumpadComma => winuser::VK_SEPARATOR, + KeyCode::NumpadDecimal => winuser::VK_DECIMAL, + KeyCode::NumpadDivide => winuser::VK_DIVIDE, + KeyCode::NumpadEnter => winuser::VK_RETURN, + KeyCode::NumpadEqual => 0, + KeyCode::NumpadHash => 0, + KeyCode::NumpadMemoryAdd => 0, + KeyCode::NumpadMemoryClear => 0, + KeyCode::NumpadMemoryRecall => 0, + KeyCode::NumpadMemoryStore => 0, + KeyCode::NumpadMemorySubtract => 0, + KeyCode::NumpadMultiply => winuser::VK_MULTIPLY, + KeyCode::NumpadParenLeft => 0, + KeyCode::NumpadParenRight => 0, + KeyCode::NumpadStar => 0, + KeyCode::NumpadSubtract => winuser::VK_SUBTRACT, + KeyCode::Escape => winuser::VK_ESCAPE, + KeyCode::Fn => 0, + KeyCode::FnLock => 0, + KeyCode::PrintScreen => winuser::VK_SNAPSHOT, + KeyCode::ScrollLock => winuser::VK_SCROLL, + KeyCode::Pause => winuser::VK_PAUSE, + KeyCode::BrowserBack => winuser::VK_BROWSER_BACK, + KeyCode::BrowserFavorites => winuser::VK_BROWSER_FAVORITES, + KeyCode::BrowserForward => winuser::VK_BROWSER_FORWARD, + KeyCode::BrowserHome => winuser::VK_BROWSER_HOME, + KeyCode::BrowserRefresh => winuser::VK_BROWSER_REFRESH, + KeyCode::BrowserSearch => winuser::VK_BROWSER_SEARCH, + KeyCode::BrowserStop => winuser::VK_BROWSER_STOP, + KeyCode::Eject => 0, + KeyCode::LaunchApp1 => winuser::VK_LAUNCH_APP1, + KeyCode::LaunchApp2 => winuser::VK_LAUNCH_APP2, + KeyCode::LaunchMail => winuser::VK_LAUNCH_MAIL, + KeyCode::MediaPlayPause => winuser::VK_MEDIA_PLAY_PAUSE, + KeyCode::MediaSelect => winuser::VK_LAUNCH_MEDIA_SELECT, + KeyCode::MediaStop => winuser::VK_MEDIA_STOP, + KeyCode::MediaTrackNext => winuser::VK_MEDIA_NEXT_TRACK, + KeyCode::MediaTrackPrevious => winuser::VK_MEDIA_PREV_TRACK, + KeyCode::Power => 0, + KeyCode::Sleep => 0, + KeyCode::AudioVolumeDown => winuser::VK_VOLUME_DOWN, + KeyCode::AudioVolumeMute => winuser::VK_VOLUME_MUTE, + KeyCode::AudioVolumeUp => winuser::VK_VOLUME_UP, + KeyCode::WakeUp => 0, + KeyCode::Hyper => 0, + KeyCode::Turbo => 0, + KeyCode::Abort => 0, + KeyCode::Resume => 0, + KeyCode::Suspend => 0, + KeyCode::Again => 0, + KeyCode::Copy => 0, + KeyCode::Cut => 0, + KeyCode::Find => 0, + KeyCode::Open => 0, + KeyCode::Paste => 0, + KeyCode::Props => 0, + KeyCode::Select => winuser::VK_SELECT, + KeyCode::Undo => 0, + KeyCode::Hiragana => 0, + KeyCode::Katakana => 0, + KeyCode::F1 => winuser::VK_F1, + KeyCode::F2 => winuser::VK_F2, + KeyCode::F3 => winuser::VK_F3, + KeyCode::F4 => winuser::VK_F4, + KeyCode::F5 => winuser::VK_F5, + KeyCode::F6 => winuser::VK_F6, + KeyCode::F7 => winuser::VK_F7, + KeyCode::F8 => winuser::VK_F8, + KeyCode::F9 => winuser::VK_F9, + KeyCode::F10 => winuser::VK_F10, + KeyCode::F11 => winuser::VK_F11, + KeyCode::F12 => winuser::VK_F12, + KeyCode::F13 => winuser::VK_F13, + KeyCode::F14 => winuser::VK_F14, + KeyCode::F15 => winuser::VK_F15, + KeyCode::F16 => winuser::VK_F16, + KeyCode::F17 => winuser::VK_F17, + KeyCode::F18 => winuser::VK_F18, + KeyCode::F19 => winuser::VK_F19, + KeyCode::F20 => winuser::VK_F20, + KeyCode::F21 => winuser::VK_F21, + KeyCode::F22 => winuser::VK_F22, + KeyCode::F23 => winuser::VK_F23, + KeyCode::F24 => winuser::VK_F24, + KeyCode::F25 => 0, + KeyCode::F26 => 0, + KeyCode::F27 => 0, + KeyCode::F28 => 0, + KeyCode::F29 => 0, + KeyCode::F30 => 0, + KeyCode::F31 => 0, + KeyCode::F32 => 0, + KeyCode::F33 => 0, + KeyCode::F34 => 0, + KeyCode::F35 => 0, + KeyCode::Unidentified(_) => 0, + _ => 0, + } +} + +fn key_code_to_non_char_key( + key_code: KeyCode, + native_code: NativeKeyCode, + hkl: u64, +) -> Key<'static> { + let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); + let is_korean = primary_lang_id == LANG_KOREAN; + let is_japanese = primary_lang_id == LANG_JAPANESE; + + match key_code { + KeyCode::Backquote => Key::Unidentified(native_code), + KeyCode::Backslash => Key::Unidentified(native_code), + KeyCode::BracketLeft => Key::Unidentified(native_code), + KeyCode::BracketRight => Key::Unidentified(native_code), + KeyCode::Comma => Key::Unidentified(native_code), + KeyCode::Digit0 => Key::Unidentified(native_code), + KeyCode::Digit1 => Key::Unidentified(native_code), + KeyCode::Digit2 => Key::Unidentified(native_code), + KeyCode::Digit3 => Key::Unidentified(native_code), + KeyCode::Digit4 => Key::Unidentified(native_code), + KeyCode::Digit5 => Key::Unidentified(native_code), + KeyCode::Digit6 => Key::Unidentified(native_code), + KeyCode::Digit7 => Key::Unidentified(native_code), + KeyCode::Digit8 => Key::Unidentified(native_code), + KeyCode::Digit9 => Key::Unidentified(native_code), + KeyCode::Equal => Key::Unidentified(native_code), + KeyCode::IntlBackslash => Key::Unidentified(native_code), + KeyCode::IntlRo => Key::Unidentified(native_code), + KeyCode::IntlYen => Key::Unidentified(native_code), + KeyCode::KeyA => Key::Unidentified(native_code), + KeyCode::KeyB => Key::Unidentified(native_code), + KeyCode::KeyC => Key::Unidentified(native_code), + KeyCode::KeyD => Key::Unidentified(native_code), + KeyCode::KeyE => Key::Unidentified(native_code), + KeyCode::KeyF => Key::Unidentified(native_code), + KeyCode::KeyG => Key::Unidentified(native_code), + KeyCode::KeyH => Key::Unidentified(native_code), + KeyCode::KeyI => Key::Unidentified(native_code), + KeyCode::KeyJ => Key::Unidentified(native_code), + KeyCode::KeyK => Key::Unidentified(native_code), + KeyCode::KeyL => Key::Unidentified(native_code), + KeyCode::KeyM => Key::Unidentified(native_code), + KeyCode::KeyN => Key::Unidentified(native_code), + KeyCode::KeyO => Key::Unidentified(native_code), + KeyCode::KeyP => Key::Unidentified(native_code), + KeyCode::KeyQ => Key::Unidentified(native_code), + KeyCode::KeyR => Key::Unidentified(native_code), + KeyCode::KeyS => Key::Unidentified(native_code), + KeyCode::KeyT => Key::Unidentified(native_code), + KeyCode::KeyU => Key::Unidentified(native_code), + KeyCode::KeyV => Key::Unidentified(native_code), + KeyCode::KeyW => Key::Unidentified(native_code), + KeyCode::KeyX => Key::Unidentified(native_code), + KeyCode::KeyY => Key::Unidentified(native_code), + KeyCode::KeyZ => Key::Unidentified(native_code), + KeyCode::Minus => Key::Unidentified(native_code), + KeyCode::Period => Key::Unidentified(native_code), + KeyCode::Quote => Key::Unidentified(native_code), + KeyCode::Semicolon => Key::Unidentified(native_code), + KeyCode::Slash => Key::Unidentified(native_code), + KeyCode::AltLeft => Key::Alt, + KeyCode::AltRight => Key::Alt, + KeyCode::Backspace => Key::Backspace, + KeyCode::CapsLock => Key::CapsLock, + KeyCode::ContextMenu => Key::ContextMenu, + KeyCode::ControlLeft => Key::Control, + KeyCode::ControlRight => Key::Control, + KeyCode::Enter => Key::Enter, + KeyCode::SuperLeft => Key::Super, + KeyCode::SuperRight => Key::Super, + KeyCode::ShiftLeft => Key::Shift, + KeyCode::ShiftRight => Key::Shift, + KeyCode::Space => Key::Space, + KeyCode::Tab => Key::Tab, + KeyCode::Convert => Key::Convert, + KeyCode::KanaMode => Key::KanaMode, + KeyCode::Lang1 if is_korean => Key::HangulMode, + KeyCode::Lang1 if is_japanese => Key::KanaMode, + KeyCode::Lang2 if is_korean => Key::HanjaMode, + KeyCode::Lang2 if is_japanese => Key::Eisu, + KeyCode::Lang3 if is_japanese => Key::Katakana, + KeyCode::Lang4 if is_japanese => Key::Hiragana, + KeyCode::Lang5 if is_japanese => Key::ZenkakuHankaku, + KeyCode::NonConvert => Key::NonConvert, + KeyCode::Delete => Key::Delete, + KeyCode::End => Key::End, + KeyCode::Help => Key::Help, + KeyCode::Home => Key::Home, + KeyCode::Insert => Key::Insert, + KeyCode::PageDown => Key::PageDown, + KeyCode::PageUp => Key::PageUp, + KeyCode::ArrowDown => Key::ArrowDown, + KeyCode::ArrowLeft => Key::ArrowLeft, + KeyCode::ArrowRight => Key::ArrowRight, + KeyCode::ArrowUp => Key::ArrowUp, + KeyCode::NumLock => Key::NumLock, + KeyCode::Numpad0 => Key::Unidentified(native_code), + KeyCode::Numpad1 => Key::Unidentified(native_code), + KeyCode::Numpad2 => Key::Unidentified(native_code), + KeyCode::Numpad3 => Key::Unidentified(native_code), + KeyCode::Numpad4 => Key::Unidentified(native_code), + KeyCode::Numpad5 => Key::Unidentified(native_code), + KeyCode::Numpad6 => Key::Unidentified(native_code), + KeyCode::Numpad7 => Key::Unidentified(native_code), + KeyCode::Numpad8 => Key::Unidentified(native_code), + KeyCode::Numpad9 => Key::Unidentified(native_code), + KeyCode::NumpadAdd => Key::Unidentified(native_code), + KeyCode::NumpadBackspace => Key::Unidentified(native_code), + KeyCode::NumpadClear => Key::Clear, + KeyCode::NumpadClearEntry => Key::Unidentified(native_code), + KeyCode::NumpadComma => Key::Unidentified(native_code), + KeyCode::NumpadDecimal => Key::Unidentified(native_code), + KeyCode::NumpadDivide => Key::Unidentified(native_code), + KeyCode::NumpadEnter => Key::Enter, + KeyCode::NumpadEqual => Key::Unidentified(native_code), + KeyCode::NumpadHash => Key::Unidentified(native_code), + KeyCode::NumpadMemoryAdd => Key::Unidentified(native_code), + KeyCode::NumpadMemoryClear => Key::Unidentified(native_code), + KeyCode::NumpadMemoryRecall => Key::Unidentified(native_code), + KeyCode::NumpadMemoryStore => Key::Unidentified(native_code), + KeyCode::NumpadMemorySubtract => Key::Unidentified(native_code), + KeyCode::NumpadMultiply => Key::Unidentified(native_code), + KeyCode::NumpadParenLeft => Key::Unidentified(native_code), + KeyCode::NumpadParenRight => Key::Unidentified(native_code), + KeyCode::NumpadStar => Key::Unidentified(native_code), + KeyCode::NumpadSubtract => Key::Unidentified(native_code), + KeyCode::Escape => Key::Escape, + KeyCode::Fn => Key::Fn, + KeyCode::FnLock => Key::FnLock, + KeyCode::PrintScreen => Key::PrintScreen, + KeyCode::ScrollLock => Key::ScrollLock, + KeyCode::Pause => Key::Pause, + KeyCode::BrowserBack => Key::BrowserBack, + KeyCode::BrowserFavorites => Key::BrowserFavorites, + KeyCode::BrowserForward => Key::BrowserForward, + KeyCode::BrowserHome => Key::BrowserHome, + KeyCode::BrowserRefresh => Key::BrowserRefresh, + KeyCode::BrowserSearch => Key::BrowserSearch, + KeyCode::BrowserStop => Key::BrowserStop, + KeyCode::Eject => Key::Eject, + KeyCode::LaunchApp1 => Key::LaunchApplication1, + KeyCode::LaunchApp2 => Key::LaunchApplication2, + KeyCode::LaunchMail => Key::LaunchMail, + KeyCode::MediaPlayPause => Key::MediaPlayPause, + KeyCode::MediaSelect => Key::Unidentified(native_code), + KeyCode::MediaStop => Key::MediaStop, + KeyCode::MediaTrackNext => Key::MediaTrackNext, + KeyCode::MediaTrackPrevious => Key::MediaTrackPrevious, + KeyCode::Power => Key::Power, + KeyCode::Sleep => Key::Standby, + KeyCode::AudioVolumeDown => Key::AudioVolumeDown, + KeyCode::AudioVolumeMute => Key::AudioVolumeMute, + KeyCode::AudioVolumeUp => Key::AudioVolumeUp, + KeyCode::WakeUp => Key::WakeUp, + KeyCode::Hyper => Key::Hyper, + KeyCode::Turbo => Key::Unidentified(native_code), + KeyCode::Abort => Key::Unidentified(native_code), + KeyCode::Resume => Key::Unidentified(native_code), + KeyCode::Suspend => Key::Unidentified(native_code), + KeyCode::Again => Key::Again, + KeyCode::Copy => Key::Copy, + KeyCode::Cut => Key::Cut, + KeyCode::Find => Key::Find, + KeyCode::Open => Key::Open, + KeyCode::Paste => Key::Paste, + KeyCode::Props => Key::Props, + KeyCode::Select => Key::Select, + KeyCode::Undo => Key::Undo, + KeyCode::Hiragana => Key::Hiragana, + KeyCode::Katakana => Key::Katakana, + KeyCode::F1 => Key::F1, + KeyCode::F2 => Key::F2, + KeyCode::F3 => Key::F3, + KeyCode::F4 => Key::F4, + KeyCode::F5 => Key::F5, + KeyCode::F6 => Key::F6, + KeyCode::F7 => Key::F7, + KeyCode::F8 => Key::F8, + KeyCode::F9 => Key::F9, + KeyCode::F10 => Key::F10, + KeyCode::F11 => Key::F11, + KeyCode::F12 => Key::F12, + KeyCode::F13 => Key::F13, + KeyCode::F14 => Key::F14, + KeyCode::F15 => Key::F15, + KeyCode::F16 => Key::F16, + KeyCode::F17 => Key::F17, + KeyCode::F18 => Key::F18, + KeyCode::F19 => Key::F19, + KeyCode::F20 => Key::F20, + KeyCode::F21 => Key::F21, + KeyCode::F22 => Key::F22, + KeyCode::F23 => Key::F23, + KeyCode::F24 => Key::F24, + KeyCode::F25 => Key::F25, + KeyCode::F26 => Key::F26, + KeyCode::F27 => Key::F27, + KeyCode::F28 => Key::F28, + KeyCode::F29 => Key::F29, + KeyCode::F30 => Key::F30, + KeyCode::F31 => Key::F31, + KeyCode::F32 => Key::F32, + KeyCode::F33 => Key::F33, + KeyCode::F34 => Key::F34, + KeyCode::F35 => Key::F35, + _ => Key::Unidentified(native_code), + } +} + /// This converts virtual keys to `Key`s. Only virtual keys which can be unambiguously converted to /// a `Key`, with only the information passed in as arguments, are converted. /// diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index bc579860fc..dc680baca8 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -77,7 +77,7 @@ pub type OsError = std::io::Error; #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEventExtra { pub text_with_all_modifers: Option<&'static str>, - pub key_without_modifers: Key<'static>, + pub key_without_modifiers: Key<'static>, } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] From 0717b087e3de905b35416104a60eeb0b7ed7b5c8 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 20 Feb 2021 14:00:02 +0100 Subject: [PATCH 60/75] Remove unused `key_code_to_non_char_key` --- src/platform_impl/windows/keyboard_layout.rs | 209 ------------------- 1 file changed, 209 deletions(-) diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 75624b1ac9..3f1100b9b4 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -733,215 +733,6 @@ fn keycode_to_vkey(keycode: KeyCode, hkl: u64) -> i32 { } } -fn key_code_to_non_char_key( - key_code: KeyCode, - native_code: NativeKeyCode, - hkl: u64, -) -> Key<'static> { - let primary_lang_id = PRIMARYLANGID(LOWORD(hkl as u32)); - let is_korean = primary_lang_id == LANG_KOREAN; - let is_japanese = primary_lang_id == LANG_JAPANESE; - - match key_code { - KeyCode::Backquote => Key::Unidentified(native_code), - KeyCode::Backslash => Key::Unidentified(native_code), - KeyCode::BracketLeft => Key::Unidentified(native_code), - KeyCode::BracketRight => Key::Unidentified(native_code), - KeyCode::Comma => Key::Unidentified(native_code), - KeyCode::Digit0 => Key::Unidentified(native_code), - KeyCode::Digit1 => Key::Unidentified(native_code), - KeyCode::Digit2 => Key::Unidentified(native_code), - KeyCode::Digit3 => Key::Unidentified(native_code), - KeyCode::Digit4 => Key::Unidentified(native_code), - KeyCode::Digit5 => Key::Unidentified(native_code), - KeyCode::Digit6 => Key::Unidentified(native_code), - KeyCode::Digit7 => Key::Unidentified(native_code), - KeyCode::Digit8 => Key::Unidentified(native_code), - KeyCode::Digit9 => Key::Unidentified(native_code), - KeyCode::Equal => Key::Unidentified(native_code), - KeyCode::IntlBackslash => Key::Unidentified(native_code), - KeyCode::IntlRo => Key::Unidentified(native_code), - KeyCode::IntlYen => Key::Unidentified(native_code), - KeyCode::KeyA => Key::Unidentified(native_code), - KeyCode::KeyB => Key::Unidentified(native_code), - KeyCode::KeyC => Key::Unidentified(native_code), - KeyCode::KeyD => Key::Unidentified(native_code), - KeyCode::KeyE => Key::Unidentified(native_code), - KeyCode::KeyF => Key::Unidentified(native_code), - KeyCode::KeyG => Key::Unidentified(native_code), - KeyCode::KeyH => Key::Unidentified(native_code), - KeyCode::KeyI => Key::Unidentified(native_code), - KeyCode::KeyJ => Key::Unidentified(native_code), - KeyCode::KeyK => Key::Unidentified(native_code), - KeyCode::KeyL => Key::Unidentified(native_code), - KeyCode::KeyM => Key::Unidentified(native_code), - KeyCode::KeyN => Key::Unidentified(native_code), - KeyCode::KeyO => Key::Unidentified(native_code), - KeyCode::KeyP => Key::Unidentified(native_code), - KeyCode::KeyQ => Key::Unidentified(native_code), - KeyCode::KeyR => Key::Unidentified(native_code), - KeyCode::KeyS => Key::Unidentified(native_code), - KeyCode::KeyT => Key::Unidentified(native_code), - KeyCode::KeyU => Key::Unidentified(native_code), - KeyCode::KeyV => Key::Unidentified(native_code), - KeyCode::KeyW => Key::Unidentified(native_code), - KeyCode::KeyX => Key::Unidentified(native_code), - KeyCode::KeyY => Key::Unidentified(native_code), - KeyCode::KeyZ => Key::Unidentified(native_code), - KeyCode::Minus => Key::Unidentified(native_code), - KeyCode::Period => Key::Unidentified(native_code), - KeyCode::Quote => Key::Unidentified(native_code), - KeyCode::Semicolon => Key::Unidentified(native_code), - KeyCode::Slash => Key::Unidentified(native_code), - KeyCode::AltLeft => Key::Alt, - KeyCode::AltRight => Key::Alt, - KeyCode::Backspace => Key::Backspace, - KeyCode::CapsLock => Key::CapsLock, - KeyCode::ContextMenu => Key::ContextMenu, - KeyCode::ControlLeft => Key::Control, - KeyCode::ControlRight => Key::Control, - KeyCode::Enter => Key::Enter, - KeyCode::SuperLeft => Key::Super, - KeyCode::SuperRight => Key::Super, - KeyCode::ShiftLeft => Key::Shift, - KeyCode::ShiftRight => Key::Shift, - KeyCode::Space => Key::Space, - KeyCode::Tab => Key::Tab, - KeyCode::Convert => Key::Convert, - KeyCode::KanaMode => Key::KanaMode, - KeyCode::Lang1 if is_korean => Key::HangulMode, - KeyCode::Lang1 if is_japanese => Key::KanaMode, - KeyCode::Lang2 if is_korean => Key::HanjaMode, - KeyCode::Lang2 if is_japanese => Key::Eisu, - KeyCode::Lang3 if is_japanese => Key::Katakana, - KeyCode::Lang4 if is_japanese => Key::Hiragana, - KeyCode::Lang5 if is_japanese => Key::ZenkakuHankaku, - KeyCode::NonConvert => Key::NonConvert, - KeyCode::Delete => Key::Delete, - KeyCode::End => Key::End, - KeyCode::Help => Key::Help, - KeyCode::Home => Key::Home, - KeyCode::Insert => Key::Insert, - KeyCode::PageDown => Key::PageDown, - KeyCode::PageUp => Key::PageUp, - KeyCode::ArrowDown => Key::ArrowDown, - KeyCode::ArrowLeft => Key::ArrowLeft, - KeyCode::ArrowRight => Key::ArrowRight, - KeyCode::ArrowUp => Key::ArrowUp, - KeyCode::NumLock => Key::NumLock, - KeyCode::Numpad0 => Key::Unidentified(native_code), - KeyCode::Numpad1 => Key::Unidentified(native_code), - KeyCode::Numpad2 => Key::Unidentified(native_code), - KeyCode::Numpad3 => Key::Unidentified(native_code), - KeyCode::Numpad4 => Key::Unidentified(native_code), - KeyCode::Numpad5 => Key::Unidentified(native_code), - KeyCode::Numpad6 => Key::Unidentified(native_code), - KeyCode::Numpad7 => Key::Unidentified(native_code), - KeyCode::Numpad8 => Key::Unidentified(native_code), - KeyCode::Numpad9 => Key::Unidentified(native_code), - KeyCode::NumpadAdd => Key::Unidentified(native_code), - KeyCode::NumpadBackspace => Key::Unidentified(native_code), - KeyCode::NumpadClear => Key::Clear, - KeyCode::NumpadClearEntry => Key::Unidentified(native_code), - KeyCode::NumpadComma => Key::Unidentified(native_code), - KeyCode::NumpadDecimal => Key::Unidentified(native_code), - KeyCode::NumpadDivide => Key::Unidentified(native_code), - KeyCode::NumpadEnter => Key::Enter, - KeyCode::NumpadEqual => Key::Unidentified(native_code), - KeyCode::NumpadHash => Key::Unidentified(native_code), - KeyCode::NumpadMemoryAdd => Key::Unidentified(native_code), - KeyCode::NumpadMemoryClear => Key::Unidentified(native_code), - KeyCode::NumpadMemoryRecall => Key::Unidentified(native_code), - KeyCode::NumpadMemoryStore => Key::Unidentified(native_code), - KeyCode::NumpadMemorySubtract => Key::Unidentified(native_code), - KeyCode::NumpadMultiply => Key::Unidentified(native_code), - KeyCode::NumpadParenLeft => Key::Unidentified(native_code), - KeyCode::NumpadParenRight => Key::Unidentified(native_code), - KeyCode::NumpadStar => Key::Unidentified(native_code), - KeyCode::NumpadSubtract => Key::Unidentified(native_code), - KeyCode::Escape => Key::Escape, - KeyCode::Fn => Key::Fn, - KeyCode::FnLock => Key::FnLock, - KeyCode::PrintScreen => Key::PrintScreen, - KeyCode::ScrollLock => Key::ScrollLock, - KeyCode::Pause => Key::Pause, - KeyCode::BrowserBack => Key::BrowserBack, - KeyCode::BrowserFavorites => Key::BrowserFavorites, - KeyCode::BrowserForward => Key::BrowserForward, - KeyCode::BrowserHome => Key::BrowserHome, - KeyCode::BrowserRefresh => Key::BrowserRefresh, - KeyCode::BrowserSearch => Key::BrowserSearch, - KeyCode::BrowserStop => Key::BrowserStop, - KeyCode::Eject => Key::Eject, - KeyCode::LaunchApp1 => Key::LaunchApplication1, - KeyCode::LaunchApp2 => Key::LaunchApplication2, - KeyCode::LaunchMail => Key::LaunchMail, - KeyCode::MediaPlayPause => Key::MediaPlayPause, - KeyCode::MediaSelect => Key::Unidentified(native_code), - KeyCode::MediaStop => Key::MediaStop, - KeyCode::MediaTrackNext => Key::MediaTrackNext, - KeyCode::MediaTrackPrevious => Key::MediaTrackPrevious, - KeyCode::Power => Key::Power, - KeyCode::Sleep => Key::Standby, - KeyCode::AudioVolumeDown => Key::AudioVolumeDown, - KeyCode::AudioVolumeMute => Key::AudioVolumeMute, - KeyCode::AudioVolumeUp => Key::AudioVolumeUp, - KeyCode::WakeUp => Key::WakeUp, - KeyCode::Hyper => Key::Hyper, - KeyCode::Turbo => Key::Unidentified(native_code), - KeyCode::Abort => Key::Unidentified(native_code), - KeyCode::Resume => Key::Unidentified(native_code), - KeyCode::Suspend => Key::Unidentified(native_code), - KeyCode::Again => Key::Again, - KeyCode::Copy => Key::Copy, - KeyCode::Cut => Key::Cut, - KeyCode::Find => Key::Find, - KeyCode::Open => Key::Open, - KeyCode::Paste => Key::Paste, - KeyCode::Props => Key::Props, - KeyCode::Select => Key::Select, - KeyCode::Undo => Key::Undo, - KeyCode::Hiragana => Key::Hiragana, - KeyCode::Katakana => Key::Katakana, - KeyCode::F1 => Key::F1, - KeyCode::F2 => Key::F2, - KeyCode::F3 => Key::F3, - KeyCode::F4 => Key::F4, - KeyCode::F5 => Key::F5, - KeyCode::F6 => Key::F6, - KeyCode::F7 => Key::F7, - KeyCode::F8 => Key::F8, - KeyCode::F9 => Key::F9, - KeyCode::F10 => Key::F10, - KeyCode::F11 => Key::F11, - KeyCode::F12 => Key::F12, - KeyCode::F13 => Key::F13, - KeyCode::F14 => Key::F14, - KeyCode::F15 => Key::F15, - KeyCode::F16 => Key::F16, - KeyCode::F17 => Key::F17, - KeyCode::F18 => Key::F18, - KeyCode::F19 => Key::F19, - KeyCode::F20 => Key::F20, - KeyCode::F21 => Key::F21, - KeyCode::F22 => Key::F22, - KeyCode::F23 => Key::F23, - KeyCode::F24 => Key::F24, - KeyCode::F25 => Key::F25, - KeyCode::F26 => Key::F26, - KeyCode::F27 => Key::F27, - KeyCode::F28 => Key::F28, - KeyCode::F29 => Key::F29, - KeyCode::F30 => Key::F30, - KeyCode::F31 => Key::F31, - KeyCode::F32 => Key::F32, - KeyCode::F33 => Key::F33, - KeyCode::F34 => Key::F34, - KeyCode::F35 => Key::F35, - _ => Key::Unidentified(native_code), - } -} - /// This converts virtual keys to `Key`s. Only virtual keys which can be unambiguously converted to /// a `Key`, with only the information passed in as arguments, are converted. /// From c88bf3dd81d7c68c5d9c14a3c4feff57a7adfd2d Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 20 Feb 2021 16:26:55 +0100 Subject: [PATCH 61/75] Remove empty event.rs file --- src/platform_impl/windows/event.rs | 1 - src/platform_impl/windows/mod.rs | 1 - 2 files changed, 2 deletions(-) delete mode 100644 src/platform_impl/windows/event.rs diff --git a/src/platform_impl/windows/event.rs b/src/platform_impl/windows/event.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/src/platform_impl/windows/event.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index e59af25c68..a72099db88 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -100,7 +100,6 @@ mod util; mod dark_mode; mod dpi; mod drop_handler; -mod event; mod event_loop; mod icon; mod keyboard; From 8538486947051df795735e5dc8dfa3fe5f4b23e8 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 20 Feb 2021 17:24:21 +0100 Subject: [PATCH 62/75] Use space for resetting dead keys --- src/platform_impl/windows/window.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index fc16d6485a..e56a472a02 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -668,12 +668,12 @@ impl Window { // `ToUnicode` consumes the dead-key by default, so we are constructing a fake (but valid) // key input which we can call `ToUnicode` with. unsafe { - let vk = 'a' as u32; + let vk = winuser::VK_SPACE as u32; let scancode = winuser::MapVirtualKeyW(vk, winuser::MAPVK_VK_TO_VSC); let kbd_state = [0; 256]; let mut char_buff = [MaybeUninit::uninit(); 8]; winuser::ToUnicode( - 'a' as u32, + vk, scancode, kbd_state.as_ptr(), char_buff[0].as_mut_ptr(), From 15cfca113b3f9c002d68a718978c1939d754ebf6 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 20 Feb 2021 17:43:47 +0100 Subject: [PATCH 63/75] Fix reporting multiple graphemes in logical_key --- Cargo.toml | 1 + src/platform_impl/windows/keyboard.rs | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c0d3eb7150..807fa9c109 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ features = ["display_link"] [target.'cfg(target_os = "windows")'.dependencies] parking_lot = "0.11" +unicode-segmentation = "1.7.1" [target.'cfg(target_os = "windows")'.dependencies.winapi] version = "0.3.6" diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 59a310c669..f0c050ed44 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -11,6 +11,8 @@ use winapi::{ um::winuser, }; +use unicode_segmentation::UnicodeSegmentation; + use crate::{ event::{ElementState, KeyEvent}, keyboard::{Key, KeyCode, KeyLocation, NativeKeyCode}, @@ -476,9 +478,11 @@ enum PartialText { } enum PartialLogicalKey { - /// Use the text provided by the WM_UNICHAR messages and report that as - /// a `Character` variant - Text, + /// Use the text provided by the WM_CHAR messages and report that as a `Character` variant. If + /// the text consists of multiple grapheme clusters (user-precieved characters) that means that + /// dead key could not be combined with the second input, and in that case we should fall back + /// to using what would have without a dead-key input. + TextOr(Key<'static>), /// Use the value directly provided by this variant This(Key<'static>), @@ -562,7 +566,7 @@ impl PartialKeyEventInfo { } else if is_pressed && key_is_char && !mods.contains(WindowsModifiers::CONTROL) { // In some cases we want to use the UNICHAR text for logical_key in order to allow // dead keys to have an effect on the character reported by `logical_key`. - PartialLogicalKey::Text + PartialLogicalKey::TextOr(preliminary_logical_key) } else { PartialLogicalKey::This(preliminary_logical_key) }; @@ -632,8 +636,14 @@ impl PartialKeyEventInfo { } let logical_key = match self.logical_key { - PartialLogicalKey::Text => match text { - Some(s) => Key::Character(s), + PartialLogicalKey::TextOr(fallback) => match text { + Some(s) => { + if s.grapheme_indices(true).count() > 1 { + fallback + } else { + Key::Character(s) + } + } None => Key::Unidentified(NativeKeyCode::Windows(self.scancode)), }, PartialLogicalKey::This(v) => v, From 13bf3cd7142f56e1abf7e90f881891a44ee45525 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 28 Feb 2021 13:56:24 +0100 Subject: [PATCH 64/75] Avoid incorrect synthetic keypress during setfocus --- src/platform_impl/windows/keyboard.rs | 40 ++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index f0c050ed44..35e6106348 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -45,6 +45,8 @@ fn new_ex_scancode(scancode: u8, extended: bool) -> ExScancode { (scancode as u16) | (if extended { 0xE000 } else { 0 }) } +/// Gets the keyboard state as reported by messages that have been removed from the event queue. +/// See also: get_async_kbd_state fn get_kbd_state() -> [u8; 256] { unsafe { let mut kbd_state: MaybeUninit<[u8; 256]> = MaybeUninit::uninit(); @@ -53,6 +55,31 @@ fn get_kbd_state() -> [u8; 256] { } } +/// Gets the current keyboard state regardless of whether the corresponding keyboard events have +/// been removed from the event queue. See also: get_kbd_state +fn get_async_kbd_state() -> [u8; 256] { + unsafe { + let mut kbd_state: [u8; 256] = MaybeUninit::uninit().assume_init(); + for (vk, state) in kbd_state.iter_mut().enumerate() { + let vk = vk as c_int; + let async_state = winuser::GetAsyncKeyState(vk as c_int); + let is_down = (async_state & (1 << 15)) != 0; + *state = if is_down { 0x80 } else { 0 }; + + if matches!( + vk, + winuser::VK_CAPITAL | winuser::VK_NUMLOCK | winuser::VK_SCROLL + ) { + // Toggle states aren't reported by `GetAsyncKeyState` + let toggle_state = winuser::GetKeyState(vk); + let is_active = (toggle_state & 1) != 0; + *state |= if is_active { 1 } else { 0 }; + } + } + kbd_state + } +} + pub struct MessageAsKeyEvent { pub event: KeyEvent, pub is_synthetic: bool, @@ -119,14 +146,16 @@ impl KeyEventBuilder { match msg_kind { winuser::WM_SETFOCUS => { // synthesize keydown events - let key_events = self.synthesize_kbd_state(ElementState::Pressed); + let kbd_state = get_async_kbd_state(); + let key_events = self.synthesize_kbd_state(ElementState::Pressed, &kbd_state); if !key_events.is_empty() { return key_events; } } winuser::WM_KILLFOCUS => { // sythesize keyup events - let key_events = self.synthesize_kbd_state(ElementState::Released); + let kbd_state = get_kbd_state(); + let key_events = self.synthesize_kbd_state(ElementState::Released, &kbd_state); if !key_events.is_empty() { return key_events; } @@ -300,13 +329,16 @@ impl KeyEventBuilder { Vec::new() } - fn synthesize_kbd_state(&mut self, key_state: ElementState) -> Vec { + fn synthesize_kbd_state( + &mut self, + key_state: ElementState, + kbd_state: &[u8; 256], + ) -> Vec { let mut key_events = Vec::new(); let mut layouts = LAYOUT_CACHE.lock().unwrap(); let (locale_id, _) = layouts.get_current_layout(); - let kbd_state = get_kbd_state(); macro_rules! is_key_pressed { ($vk:expr) => { kbd_state[$vk as usize] & 0x80 != 0 From 57d550ad0c9902d8f93575310ee57781bda3ee16 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 28 Feb 2021 18:22:34 +0100 Subject: [PATCH 65/75] Fixed the AltGr keypress not being reported when the AltGr key is pressed and released in a very quick succession --- src/platform_impl/windows/keyboard.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 35e6106348..765c175e1e 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -192,9 +192,14 @@ impl KeyEventBuilder { self.event_info = None; if has_next_key_message { let next_msg = unsafe { next_msg.assume_init().message }; - let is_next_keydown = - next_msg == winuser::WM_KEYDOWN || next_msg == winuser::WM_SYSKEYDOWN; - if !is_next_keydown { + let next_belongs_to_this = !matches!( + next_msg, + winuser::WM_KEYDOWN + | winuser::WM_SYSKEYDOWN + | winuser::WM_KEYUP + | winuser::WM_SYSKEYUP + ); + if next_belongs_to_this { self.event_info = event_info.take(); } } From 0926ec70edec85779214c824a99952e22223d31b Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 28 Feb 2021 19:36:10 +0100 Subject: [PATCH 66/75] Filter fake Ctrl events when pressing AltGr --- src/platform_impl/windows/keyboard.rs | 184 +++++++++++++++++--------- 1 file changed, 124 insertions(+), 60 deletions(-) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 765c175e1e..dbce72ce38 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -18,7 +18,7 @@ use crate::{ keyboard::{Key, KeyCode, KeyLocation, NativeKeyCode}, platform::scancode::KeyCodeExtScancode, platform_impl::platform::{ - keyboard_layout::{get_or_insert_str, LayoutCache, WindowsModifiers, LAYOUT_CACHE}, + keyboard_layout::{get_or_insert_str, Layout, LayoutCache, WindowsModifiers, LAYOUT_CACHE}, KeyEventExtra, }, }; @@ -30,56 +30,8 @@ pub fn is_msg_keyboard_related(msg: u32) -> bool { is_keyboard_msg || msg == WM_SETFOCUS || msg == WM_KILLFOCUS } -#[derive(Debug, Copy, Clone)] -pub struct KeyLParam { - pub scancode: u8, - pub extended: bool, - - /// This is `previous_state XOR transition_state`. See the lParam for WM_KEYDOWN and WM_KEYUP for further details. - pub is_repeat: bool, -} - pub type ExScancode = u16; -fn new_ex_scancode(scancode: u8, extended: bool) -> ExScancode { - (scancode as u16) | (if extended { 0xE000 } else { 0 }) -} - -/// Gets the keyboard state as reported by messages that have been removed from the event queue. -/// See also: get_async_kbd_state -fn get_kbd_state() -> [u8; 256] { - unsafe { - let mut kbd_state: MaybeUninit<[u8; 256]> = MaybeUninit::uninit(); - winuser::GetKeyboardState(kbd_state.as_mut_ptr() as *mut u8); - kbd_state.assume_init() - } -} - -/// Gets the current keyboard state regardless of whether the corresponding keyboard events have -/// been removed from the event queue. See also: get_kbd_state -fn get_async_kbd_state() -> [u8; 256] { - unsafe { - let mut kbd_state: [u8; 256] = MaybeUninit::uninit().assume_init(); - for (vk, state) in kbd_state.iter_mut().enumerate() { - let vk = vk as c_int; - let async_state = winuser::GetAsyncKeyState(vk as c_int); - let is_down = (async_state & (1 << 15)) != 0; - *state = if is_down { 0x80 } else { 0 }; - - if matches!( - vk, - winuser::VK_CAPITAL | winuser::VK_NUMLOCK | winuser::VK_SCROLL - ) { - // Toggle states aren't reported by `GetAsyncKeyState` - let toggle_state = winuser::GetKeyState(vk); - let is_active = (toggle_state & 1) != 0; - *state |= if is_active { 1 } else { 0 }; - } - } - kbd_state - } -} - pub struct MessageAsKeyEvent { pub event: KeyEvent, pub is_synthetic: bool, @@ -175,7 +127,6 @@ impl KeyEventBuilder { ElementState::Pressed, &mut layouts, ); - let mut event_info = Some(event_info); let mut next_msg = MaybeUninit::uninit(); let peek_retval = unsafe { @@ -190,20 +141,31 @@ impl KeyEventBuilder { let has_next_key_message = peek_retval != 0; *retval = Some(0); self.event_info = None; + let mut finished_event_info = Some(event_info); if has_next_key_message { - let next_msg = unsafe { next_msg.assume_init().message }; + let next_msg = unsafe { next_msg.assume_init() }; + let next_msg_kind = next_msg.message; let next_belongs_to_this = !matches!( - next_msg, + next_msg_kind, winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN | winuser::WM_KEYUP | winuser::WM_SYSKEYUP ); if next_belongs_to_this { - self.event_info = event_info.take(); + self.event_info = finished_event_info.take(); + } else { + let (_, layout) = layouts.get_current_layout(); + let is_fake = { + let curr_event = finished_event_info.as_ref().unwrap(); + is_current_fake(curr_event, next_msg, layout) + }; + if is_fake { + finished_event_info = None; + } } } - if let Some(event_info) = event_info { + if let Some(event_info) = finished_event_info { let ev = event_info.finalize(&mut layouts.strings); return vec![MessageAsKeyEvent { event: ev, @@ -322,11 +284,36 @@ impl KeyEventBuilder { ElementState::Released, &mut layouts, ); - let event = event_info.finalize(&mut layouts.strings); - return vec![MessageAsKeyEvent { - event, - is_synthetic: false, - }]; + let mut next_msg = MaybeUninit::uninit(); + let peek_retval = unsafe { + winuser::PeekMessageW( + next_msg.as_mut_ptr(), + hwnd, + winuser::WM_KEYFIRST, + winuser::WM_KEYLAST, + winuser::PM_NOREMOVE, + ) + }; + let has_next_key_message = peek_retval != 0; + let mut valid_event_info = Some(event_info); + if has_next_key_message { + let next_msg = unsafe { next_msg.assume_init() }; + let (_, layout) = layouts.get_current_layout(); + let is_fake = { + let event_info = valid_event_info.as_ref().unwrap(); + is_current_fake(&event_info, next_msg, layout) + }; + if is_fake { + valid_event_info = None; + } + } + if let Some(event_info) = valid_event_info { + let event = event_info.finalize(&mut layouts.strings); + return vec![MessageAsKeyEvent { + event, + is_synthetic: false, + }]; + } } _ => (), } @@ -701,7 +688,16 @@ impl PartialKeyEventInfo { } } -pub fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { +#[derive(Debug, Copy, Clone)] +struct KeyLParam { + pub scancode: u8, + pub extended: bool, + + /// This is `previous_state XOR transition_state`. See the lParam for WM_KEYDOWN and WM_KEYUP for further details. + pub is_repeat: bool, +} + +fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { let previous_state = (lparam >> 30) & 0x01; let transition_state = (lparam >> 31) & 0x01; KeyLParam { @@ -711,6 +707,74 @@ pub fn destructure_key_lparam(lparam: LPARAM) -> KeyLParam { } } +#[inline] +fn new_ex_scancode(scancode: u8, extended: bool) -> ExScancode { + (scancode as u16) | (if extended { 0xE000 } else { 0 }) +} + +#[inline] +fn ex_scancode_from_lparam(lparam: LPARAM) -> ExScancode { + let lparam = destructure_key_lparam(lparam); + new_ex_scancode(lparam.scancode, lparam.extended) +} + +/// Gets the keyboard state as reported by messages that have been removed from the event queue. +/// See also: get_async_kbd_state +fn get_kbd_state() -> [u8; 256] { + unsafe { + let mut kbd_state: MaybeUninit<[u8; 256]> = MaybeUninit::uninit(); + winuser::GetKeyboardState(kbd_state.as_mut_ptr() as *mut u8); + kbd_state.assume_init() + } +} + +/// Gets the current keyboard state regardless of whether the corresponding keyboard events have +/// been removed from the event queue. See also: get_kbd_state +fn get_async_kbd_state() -> [u8; 256] { + unsafe { + let mut kbd_state: [u8; 256] = MaybeUninit::uninit().assume_init(); + for (vk, state) in kbd_state.iter_mut().enumerate() { + let vk = vk as c_int; + let async_state = winuser::GetAsyncKeyState(vk as c_int); + let is_down = (async_state & (1 << 15)) != 0; + *state = if is_down { 0x80 } else { 0 }; + + if matches!( + vk, + winuser::VK_CAPITAL | winuser::VK_NUMLOCK | winuser::VK_SCROLL + ) { + // Toggle states aren't reported by `GetAsyncKeyState` + let toggle_state = winuser::GetKeyState(vk); + let is_active = (toggle_state & 1) != 0; + *state |= if is_active { 1 } else { 0 }; + } + } + kbd_state + } +} + +/// On windows, AltGr == Ctrl + Alt +/// +/// Due to this equivalence, the system generates a fake Ctrl key-press (and key-release) preceeding +/// every AltGr key-press (and key-release). We check if the current event is a Ctrl event and if +/// the next event is a right Alt (AltGr) event. If this is the case, the current event must be the +/// fake Ctrl event. +fn is_current_fake( + curr_info: &PartialKeyEventInfo, + next_msg: winuser::MSG, + layout: &Layout, +) -> bool { + let curr_is_ctrl = matches!(curr_info.logical_key, PartialLogicalKey::This(Key::Control)); + if layout.has_alt_graph { + let next_code = ex_scancode_from_lparam(next_msg.lParam); + let next_is_altgr = next_code == 0xE038; // 0xE038 is right alt + if curr_is_ctrl && next_is_altgr { + return true; + } + } + false +} + fn get_location(scancode: ExScancode, hkl: HKL) -> KeyLocation { use winuser::*; const VK_ABNT_C2: c_int = 0xc2; From 42055e397ef6e678016d5465762fd22a3a01fa25 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Wed, 3 Mar 2021 20:56:42 +0100 Subject: [PATCH 67/75] Improve code quality --- src/platform_impl/windows/event_loop.rs | 144 +++++++++++------------ src/platform_impl/windows/keyboard.rs | 15 +-- src/platform_impl/windows/minimal_ime.rs | 10 +- 3 files changed, 82 insertions(+), 87 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 248675db9d..c8931eb5fe 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -117,6 +117,13 @@ impl ThreadMsgTargetSubclassInput { } } +/// The result of a subclass procedure (the message handling callback) +pub(crate) enum ProcResult { + DefSubclassProc, // <- this should be the default value + DefWindowProc, + Value(isize), +} + pub struct EventLoop { thread_msg_sender: Sender, window_target: RootELW, @@ -834,20 +841,20 @@ unsafe fn public_window_callback_inner( winuser::RDW_INTERNALPAINT, ); - let mut retval = None; + let mut result = ProcResult::DefSubclassProc; // Send new modifiers before sending key events. let mods_changed_callback = || match msg { winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN | winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { update_modifiers(window, subclass_input); - Some(0) + result = ProcResult::Value(0); } - _ => retval, + _ => (), }; - retval = subclass_input + subclass_input .event_loop_runner .catch_unwind(mods_changed_callback) - .unwrap_or(Some(-1)); + .unwrap_or_else(|| result = ProcResult::Value(-1)); let keyboard_callback = || { use crate::event::WindowEvent::KeyboardInput; @@ -855,13 +862,13 @@ unsafe fn public_window_callback_inner( if !is_keyboard_related { // We return early to avoid a deadlock from locking the window state // when not appropriate. - return retval; + return; } let events = { let mut window_state = subclass_input.window_state.lock(); window_state .key_event_builder - .process_message(window, msg, wparam, lparam, &mut retval) + .process_message(window, msg, wparam, lparam, &mut result) }; for event in events { subclass_input.send_event(Event::WindowEvent { @@ -873,24 +880,23 @@ unsafe fn public_window_callback_inner( }, }); } - retval }; - retval = subclass_input + subclass_input .event_loop_runner .catch_unwind(keyboard_callback) - .unwrap_or(Some(-1)); + .unwrap_or_else(|| result = ProcResult::Value(-1)); let ime_callback = || { use crate::event::WindowEvent::ReceivedImeText; let is_ime_related = is_msg_ime_related(msg); if !is_ime_related { - return retval; + return; } let text = { let mut window_state = subclass_input.window_state.lock(); window_state .ime_handler - .process_message(window, msg, wparam, lparam, &mut retval) + .process_message(window, msg, wparam, lparam, &mut result) }; if let Some(str) = text { subclass_input.send_event(Event::WindowEvent { @@ -898,12 +904,11 @@ unsafe fn public_window_callback_inner( event: ReceivedImeText(str), }); } - retval }; - retval = subclass_input + subclass_input .event_loop_runner .catch_unwind(ime_callback) - .unwrap_or(Some(-1)); + .unwrap_or_else(|| result = ProcResult::Value(-1)); // I decided to bind the closure to `callback` and pass it to catch_unwind rather than passing // the closure to catch_unwind directly so that the match body indendation wouldn't change and @@ -914,7 +919,7 @@ unsafe fn public_window_callback_inner( .window_state .lock() .set_window_flags_in_place(|f| f.insert(WindowFlags::MARKER_IN_SIZE_MOVE)); - 0 + result = ProcResult::Value(0); } winuser::WM_EXITSIZEMOVE => { @@ -922,18 +927,16 @@ unsafe fn public_window_callback_inner( .window_state .lock() .set_window_flags_in_place(|f| f.remove(WindowFlags::MARKER_IN_SIZE_MOVE)); - 0 + result = ProcResult::Value(0); } winuser::WM_NCCREATE => { enable_non_client_dpi_scaling(window); - commctrl::DefSubclassProc(window, msg, wparam, lparam) } winuser::WM_NCLBUTTONDOWN => { if wparam == winuser::HTCAPTION as _ { winuser::PostMessageW(window, winuser::WM_MOUSEMOVE, 0, lparam); } - commctrl::DefSubclassProc(window, msg, wparam, lparam) } winuser::WM_CLOSE => { @@ -942,7 +945,7 @@ unsafe fn public_window_callback_inner( window_id: RootWindowId(WindowId(window)), event: CloseRequested, }); - 0 + result = ProcResult::Value(0); } winuser::WM_DESTROY => { @@ -953,13 +956,13 @@ unsafe fn public_window_callback_inner( event: Destroyed, }); subclass_input.event_loop_runner.remove_window(window); - 0 + result = ProcResult::Value(0); } winuser::WM_NCDESTROY => { remove_window_subclass::(window); subclass_input.subclass_removed.set(true); - 0 + result = ProcResult::Value(0); } winuser::WM_PAINT => { @@ -981,8 +984,6 @@ unsafe fn public_window_callback_inner( process_control_flow(&subclass_input.event_loop_runner); } } - - commctrl::DefSubclassProc(window, msg, wparam, lparam) } winuser::WM_WINDOWPOSCHANGING => { @@ -1030,7 +1031,7 @@ unsafe fn public_window_callback_inner( } } - 0 + result = ProcResult::Value(0); } // WM_MOVE supplies client area positions, so we send Moved here instead. @@ -1048,7 +1049,7 @@ unsafe fn public_window_callback_inner( } // This is necessary for us to still get sent WM_SIZE. - commctrl::DefSubclassProc(window, msg, wparam, lparam) + result = ProcResult::DefSubclassProc; } winuser::WM_SIZE => { @@ -1075,7 +1076,7 @@ unsafe fn public_window_callback_inner( } subclass_input.send_event(event); - 0 + result = ProcResult::Value(0); } // this is necessary for us to maintain minimize/restore state @@ -1093,11 +1094,12 @@ unsafe fn public_window_callback_inner( if wparam == winuser::SC_SCREENSAVE { let window_state = subclass_input.window_state.lock(); if window_state.fullscreen.is_some() { - return 0; + result = ProcResult::Value(0); + return; } } - winuser::DefWindowProcW(window, msg, wparam, lparam) + result = ProcResult::DefWindowProc; } winuser::WM_MOUSEMOVE => { @@ -1153,7 +1155,7 @@ unsafe fn public_window_callback_inner( }); } - 0 + result = ProcResult::Value(0); } winuser::WM_MOUSELEAVE => { @@ -1172,7 +1174,7 @@ unsafe fn public_window_callback_inner( }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_MOUSEWHEEL => { @@ -1194,7 +1196,7 @@ unsafe fn public_window_callback_inner( }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_MOUSEHWHEEL => { @@ -1216,18 +1218,12 @@ unsafe fn public_window_callback_inner( }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { if msg == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 { - commctrl::DefSubclassProc(window, msg, wparam, lparam) - } else { - if let Some(retval) = retval { - retval - } else { - commctrl::DefSubclassProc(window, msg, wparam, lparam) - } + result = ProcResult::DefSubclassProc; } } @@ -1247,7 +1243,7 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_LBUTTONUP => { @@ -1268,7 +1264,7 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_RBUTTONDOWN => { @@ -1289,7 +1285,7 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_RBUTTONUP => { @@ -1310,7 +1306,7 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_MBUTTONDOWN => { @@ -1331,7 +1327,7 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_MBUTTONUP => { @@ -1352,7 +1348,7 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_XBUTTONDOWN => { @@ -1374,7 +1370,7 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_XBUTTONUP => { @@ -1396,7 +1392,7 @@ unsafe fn public_window_callback_inner( modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_CAPTURECHANGED => { @@ -1407,7 +1403,7 @@ unsafe fn public_window_callback_inner( if lparam != window as isize { subclass_input.window_state.lock().mouse.capture_count = 0; } - 0 + result = ProcResult::Value(0); } winuser::WM_TOUCH => { @@ -1456,7 +1452,7 @@ unsafe fn public_window_callback_inner( } } winuser::CloseTouchInputHandle(htouch); - 0 + result = ProcResult::Value(0); } winuser::WM_POINTERDOWN | winuser::WM_POINTERUPDATE | winuser::WM_POINTERUP => { @@ -1479,7 +1475,8 @@ unsafe fn public_window_callback_inner( std::ptr::null_mut(), ) == 0 { - return 0; + result = ProcResult::Value(0); + return; } let pointer_info_count = (entries_count * pointers_count) as usize; @@ -1492,7 +1489,8 @@ unsafe fn public_window_callback_inner( pointer_infos.as_mut_ptr(), ) == 0 { - return 0; + result = ProcResult::Value(0); + return; } // https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getpointerframeinfohistory @@ -1596,7 +1594,7 @@ unsafe fn public_window_callback_inner( SkipPointerFrameMessages(pointer_id); } - 0 + result = ProcResult::Value(0); } winuser::WM_SETFOCUS => { @@ -1608,7 +1606,7 @@ unsafe fn public_window_callback_inner( event: Focused(true), }); - 0 + result = ProcResult::Value(0); } winuser::WM_KILLFOCUS => { @@ -1624,7 +1622,7 @@ unsafe fn public_window_callback_inner( window_id: RootWindowId(WindowId(window)), event: Focused(false), }); - 0 + result = ProcResult::Value(0); } winuser::WM_SETCURSOR => { @@ -1645,17 +1643,12 @@ unsafe fn public_window_callback_inner( Some(cursor) => { let cursor = winuser::LoadCursorW(ptr::null_mut(), cursor.to_windows_cursor()); winuser::SetCursor(cursor); - 0 + result = ProcResult::Value(0); } - None => winuser::DefWindowProcW(window, msg, wparam, lparam), + None => result = ProcResult::DefWindowProc, } } - winuser::WM_DROPFILES => { - // See `FileDropHandler` for implementation. - 0 - } - winuser::WM_GETMINMAXINFO => { let mmi = lparam as *mut winuser::MINMAXINFO; @@ -1680,7 +1673,7 @@ unsafe fn public_window_callback_inner( } } - 0 + result = ProcResult::Value(0); } // Only sent on Windows 8.1 or newer. On Windows 7 and older user has to log out to change @@ -1702,7 +1695,8 @@ unsafe fn public_window_callback_inner( window_state.scale_factor = new_scale_factor; if new_scale_factor == old_scale_factor { - return 0; + result = ProcResult::Value(0); + return; } window_state.fullscreen.is_none() @@ -1897,7 +1891,7 @@ unsafe fn public_window_callback_inner( winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE, ); - 0 + result = ProcResult::Value(0); } winuser::WM_SETTINGCHANGE => { @@ -1918,26 +1912,18 @@ unsafe fn public_window_callback_inner( }); } } - - commctrl::DefSubclassProc(window, msg, wparam, lparam) } _ => { if msg == *DESTROY_MSG_ID { winuser::DestroyWindow(window); - 0 + result = ProcResult::Value(0); } else if msg == *SET_RETAIN_STATE_ON_SIZE_MSG_ID { let mut window_state = subclass_input.window_state.lock(); window_state.set_window_flags_in_place(|f| { f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam != 0) }); - 0 - } else { - if let Some(retval) = retval { - retval - } else { - commctrl::DefSubclassProc(window, msg, wparam, lparam) - } + result = ProcResult::Value(0); } } }; @@ -1945,7 +1931,13 @@ unsafe fn public_window_callback_inner( subclass_input .event_loop_runner .catch_unwind(callback) - .unwrap_or(-1) + .unwrap_or_else(|| result = ProcResult::Value(-1)); + + match result { + ProcResult::DefSubclassProc => commctrl::DefSubclassProc(window, msg, wparam, lparam), + ProcResult::DefWindowProc => winuser::DefWindowProcW(window, msg, wparam, lparam), + ProcResult::Value(val) => val, + } } unsafe extern "system" fn thread_event_target_callback( diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index dbce72ce38..cba5099f52 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -5,7 +5,7 @@ use std::{ use winapi::{ shared::{ - minwindef::{HKL, LPARAM, LRESULT, UINT, WPARAM}, + minwindef::{HKL, LPARAM, UINT, WPARAM}, windef::HWND, }, um::winuser, @@ -18,6 +18,7 @@ use crate::{ keyboard::{Key, KeyCode, KeyLocation, NativeKeyCode}, platform::scancode::KeyCodeExtScancode, platform_impl::platform::{ + event_loop::ProcResult, keyboard_layout::{get_or_insert_str, Layout, LayoutCache, WindowsModifiers, LAYOUT_CACHE}, KeyEventExtra, }, @@ -87,13 +88,13 @@ impl KeyEventBuilder { /// Call this function for every window message. /// Returns Some() if this window message completes a KeyEvent. /// Returns None otherwise. - pub fn process_message( + pub(crate) fn process_message( &mut self, hwnd: HWND, msg_kind: u32, wparam: WPARAM, lparam: LPARAM, - retval: &mut Option, + result: &mut ProcResult, ) -> Vec { match msg_kind { winuser::WM_SETFOCUS => { @@ -118,6 +119,7 @@ impl KeyEventBuilder { // This is handled in `event_loop.rs` return vec![]; } + *result = ProcResult::Value(0); self.prev_down_was_dead = false; let mut layouts = LAYOUT_CACHE.lock().unwrap(); @@ -139,7 +141,6 @@ impl KeyEventBuilder { ) }; let has_next_key_message = peek_retval != 0; - *retval = Some(0); self.event_info = None; let mut finished_event_info = Some(event_info); if has_next_key_message { @@ -174,11 +175,11 @@ impl KeyEventBuilder { } } winuser::WM_DEADCHAR | winuser::WM_SYSDEADCHAR => { + *result = ProcResult::Value(0); self.prev_down_was_dead = true; // At this point, we know that there isn't going to be any more events related to // this key press let event_info = self.event_info.take().unwrap(); - *retval = Some(0); let mut layouts = LAYOUT_CACHE.lock().unwrap(); let ev = event_info.finalize(&mut layouts.strings); return vec![MessageAsKeyEvent { @@ -187,7 +188,7 @@ impl KeyEventBuilder { }]; } winuser::WM_CHAR | winuser::WM_SYSCHAR => { - *retval = Some(0); + *result = ProcResult::Value(0); let is_high_surrogate = 0xD800 <= wparam && wparam <= 0xDBFF; let is_low_surrogate = 0xDC00 <= wparam && wparam <= 0xDFFF; @@ -275,7 +276,7 @@ impl KeyEventBuilder { } } winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { - *retval = Some(0); + *result = ProcResult::Value(0); let mut layouts = LAYOUT_CACHE.lock().unwrap(); let event_info = PartialKeyEventInfo::from_message( diff --git a/src/platform_impl/windows/minimal_ime.rs b/src/platform_impl/windows/minimal_ime.rs index 8591e4f565..2d51efdf37 100644 --- a/src/platform_impl/windows/minimal_ime.rs +++ b/src/platform_impl/windows/minimal_ime.rs @@ -2,12 +2,14 @@ use std::mem::MaybeUninit; use winapi::{ shared::{ - minwindef::{LPARAM, LRESULT, WPARAM}, + minwindef::{LPARAM, WPARAM}, windef::HWND, }, um::winuser, }; +use crate::platform_impl::platform::event_loop::ProcResult; + pub fn is_msg_ime_related(msg_kind: u32) -> bool { match msg_kind { winuser::WM_IME_COMPOSITION @@ -36,13 +38,13 @@ impl Default for MinimalIme { } } impl MinimalIme { - pub fn process_message( + pub(crate) fn process_message( &mut self, hwnd: HWND, msg_kind: u32, wparam: WPARAM, _lparam: LPARAM, - retval: &mut Option, + result: &mut ProcResult, ) -> Option { match msg_kind { winuser::WM_IME_ENDCOMPOSITION => { @@ -50,7 +52,7 @@ impl MinimalIme { } winuser::WM_CHAR | winuser::WM_SYSCHAR => { if self.getting_ime_text { - *retval = Some(0); + *result = ProcResult::Value(0); self.utf16parts.push(wparam as u16); let more_char_coming; From 03c8b0dc289c34d95fce3fea434cc846b959127d Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Thu, 1 Apr 2021 15:09:40 +0200 Subject: [PATCH 68/75] Remove `repeat` from `RawKeyEvent` --- src/event.rs | 7 ++++++- src/platform_impl/windows/event_loop.rs | 15 --------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/event.rs b/src/event.rs index 2662c841d5..6bd9009a0b 100644 --- a/src/event.rs +++ b/src/event.rs @@ -607,12 +607,17 @@ pub enum DeviceEvent { } /// Describes a keyboard input as a raw device event. +/// +/// Note that holding down a key may produce repeated `RawKeyEvent`s. The +/// operating system doesn't provide information whether such an event is a +/// repeat or the initial keypress. An application may emulate this by, for +/// example keeping a Map/Set of pressed keys and determining whether a keypress +/// corresponds to an already pressed key. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct RawKeyEvent { pub physical_key: keyboard::KeyCode, pub state: ElementState, - pub repeat: bool, } /// Describes a keyboard input targeting a window. diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index c8931eb5fe..b5f6eebc44 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -89,7 +89,6 @@ lazy_static! { get_function!("user32.dll", GetPointerTouchInfo); static ref GET_POINTER_PEN_INFO: Option = get_function!("user32.dll", GetPointerPenInfo); - static ref RAW_KEYS_PRESSED: Mutex> = Mutex::new(HashMap::new()); } pub(crate) struct SubclassInput { @@ -2263,25 +2262,11 @@ unsafe fn handle_raw_input( _ => (), } } - let repeat; - let mut keys_pressed = RAW_KEYS_PRESSED.lock(); - match keys_pressed.entry(code) { - Entry::Occupied(mut e) => { - let prev_pressed = e.get_mut(); - repeat = *prev_pressed && pressed; - *prev_pressed = pressed; - } - Entry::Vacant(e) => { - e.insert(pressed); - repeat = false; - } - } subclass_input.send_event(Event::DeviceEvent { device_id, event: Key(RawKeyEvent { physical_key: code, state, - repeat, }), }); } From 166d79eea0c22a21626628f8c02c3c8fb371000c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Art=C3=BAr=20Kov=C3=A1cs?= Date: Mon, 12 Apr 2021 20:04:35 +0200 Subject: [PATCH 69/75] Allow fractional scroll in raw mouse events --- src/platform_impl/windows/event_loop.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index b5f6eebc44..0c7cc4a9f2 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2134,11 +2134,12 @@ unsafe fn handle_raw_input( } if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { - let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; + // We must cast to SHORT first, becaues `usButtonData` must be interpreted as signed. + let delta = mouse.usButtonData as SHORT as f32 / winuser::WHEEL_DELTA as f32; subclass_input.send_event(Event::DeviceEvent { device_id, event: MouseWheel { - delta: LineDelta(0.0, delta as f32), + delta: LineDelta(0.0, delta), }, }); } From 766c7dc6af655a9cbfd422f0defe2e91bc598932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Art=C3=BAr=20Kov=C3=A1cs?= Date: Mon, 12 Apr 2021 20:06:04 +0200 Subject: [PATCH 70/75] Fix typo Co-authored-by: Markus Siglreithmaier --- src/platform_impl/windows/event_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index b5f6eebc44..6786d933f6 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -2199,7 +2199,7 @@ unsafe fn handle_raw_input( // There's another combination which isn't quite an equivalence: // PrtSc used to be Shift+Asterisk. This means that on some keyboards, presssing // PrtSc (print screen) produces the following sequence: - // 1, 0xE02A - Which is a left shift (0x2A) with an exteion flag (0xE000) + // 1, 0xE02A - Which is a left shift (0x2A) with an extension flag (0xE000) // 2, 0xE037 - Which is a numpad multiply (0x37) with an exteion flag (0xE000). This on // its own it can be interpreted as PrtSc // From 37dae30e20662ac7322598c01e9827093e46a2b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Art=C3=BAr=20Kov=C3=A1cs?= Date: Mon, 12 Apr 2021 20:06:41 +0200 Subject: [PATCH 71/75] Remove unused imports --- src/platform_impl/windows/event_loop.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 0c7cc4a9f2..af2984b86a 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -6,7 +6,6 @@ use parking_lot::Mutex; use std::{ cell::Cell, collections::{ - hash_map::{Entry, HashMap}, VecDeque, }, marker::PhantomData, From f0474f2cb8e66d176132ec89a6bb992c4740a0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Art=C3=BAr=20Kov=C3=A1cs?= Date: Mon, 12 Apr 2021 20:13:40 +0200 Subject: [PATCH 72/75] Remove unused variable --- src/platform_impl/windows/event_loop.rs | 4 +--- src/platform_impl/windows/keyboard.rs | 22 +--------------------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index c09ebec266..b9726a4e2c 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -5,9 +5,7 @@ mod runner; use parking_lot::Mutex; use std::{ cell::Cell, - collections::{ - VecDeque, - }, + collections::VecDeque, marker::PhantomData, mem, panic, ptr, rc::Rc, diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index cba5099f52..2db86872a6 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -60,28 +60,10 @@ pub struct MessageAsKeyEvent { /// text input. The "sequence" only consists of one WM_KEYUP / WM_SYSKEYUP event. pub struct KeyEventBuilder { event_info: Option, - - /// The keyup event needs to call `ToUnicode` to determine what the text produced by the - /// key with all modifiers except CTRL (the `logical_key`) is. - /// - /// But `ToUnicode` without the non-modifying flag (see `key_labels`), resets the dead key - /// state which would be incorrect during every keyup event. Therefore this variable is used - /// to determine whether the last keydown event produced a dead key. - /// - /// Note that this variable is not always correct because it does - /// not track key presses outside of this window. However, the ONLY situation where this - /// doesn't work as intended is when the user presses a dead key outside of this window, and - /// switches to this window BEFORE releasing it then releases the dead key. In this case - /// the `ToUnicode` function will be called, incorrectly clearing the dead key state. Having - /// an inccorect behaviour only in this case seems acceptable. - prev_down_was_dead: bool, } impl Default for KeyEventBuilder { fn default() -> Self { - KeyEventBuilder { - event_info: None, - prev_down_was_dead: false, - } + KeyEventBuilder { event_info: None } } } impl KeyEventBuilder { @@ -120,7 +102,6 @@ impl KeyEventBuilder { return vec![]; } *result = ProcResult::Value(0); - self.prev_down_was_dead = false; let mut layouts = LAYOUT_CACHE.lock().unwrap(); let event_info = PartialKeyEventInfo::from_message( @@ -176,7 +157,6 @@ impl KeyEventBuilder { } winuser::WM_DEADCHAR | winuser::WM_SYSDEADCHAR => { *result = ProcResult::Value(0); - self.prev_down_was_dead = true; // At this point, we know that there isn't going to be any more events related to // this key press let event_info = self.event_info.take().unwrap(); From f72d2f1081f0444c90ad2df697cd058aa788ad4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Art=C3=BAr=20Kov=C3=A1cs?= Date: Sun, 25 Apr 2021 14:24:39 +0200 Subject: [PATCH 73/75] Remove unnecessary `unwrap()` Co-authored-by: Markus Siglreithmaier --- src/platform_impl/windows/minimal_ime.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/windows/minimal_ime.rs b/src/platform_impl/windows/minimal_ime.rs index 2d51efdf37..cbf754eecc 100644 --- a/src/platform_impl/windows/minimal_ime.rs +++ b/src/platform_impl/windows/minimal_ime.rs @@ -78,10 +78,10 @@ impl MinimalIme { } } if !more_char_coming { - let result = String::from_utf16(&self.utf16parts).unwrap(); + let result = String::from_utf16(&self.utf16parts).ok(); self.utf16parts.clear(); self.getting_ime_text = false; - return Some(result); + return result; } } } From b0ddbdbf2ab0349d0688e024ff65d96581bad501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Art=C3=BAr=20Kov=C3=A1cs?= Date: Sun, 25 Apr 2021 14:31:18 +0200 Subject: [PATCH 74/75] Avoid using the deprecated `into_rgba()` --- examples/window_icon.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/window_icon.rs b/examples/window_icon.rs index aaa6bc0ba7..6c79625505 100644 --- a/examples/window_icon.rs +++ b/examples/window_icon.rs @@ -49,7 +49,7 @@ fn load_icon(path: &Path) -> Icon { let (icon_rgba, icon_width, icon_height) = { let image = image::open(path) .expect("Failed to open icon path") - .into_rgba(); + .into_rgba8(); let (width, height) = image.dimensions(); let rgba = image.into_raw(); (rgba, width, height) From d05ac2ec77316a1370ebe0a6dc1608e3a7009a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Art=C3=BAr=20Kov=C3=A1cs?= Date: Sun, 25 Apr 2021 15:32:53 +0200 Subject: [PATCH 75/75] Fix IME crash --- src/platform_impl/windows/keyboard.rs | 29 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs index 2db86872a6..00235ed018 100644 --- a/src/platform_impl/windows/keyboard.rs +++ b/src/platform_impl/windows/keyboard.rs @@ -168,6 +168,10 @@ impl KeyEventBuilder { }]; } winuser::WM_CHAR | winuser::WM_SYSCHAR => { + if self.event_info.is_none() { + trace!("Received a CHAR message but no `event_info` was available. The message is probably IME, returning."); + return vec![]; + } *result = ProcResult::Value(0); let is_high_surrogate = 0xD800 <= wparam && wparam <= 0xDBFF; let is_low_surrogate = 0xDC00 <= wparam && wparam <= 0xDFFF; @@ -198,15 +202,19 @@ impl KeyEventBuilder { } if is_utf16 { - self.event_info - .as_mut() - .unwrap() - .utf16parts - .push(wparam as u16); + if let Some(ev_info) = self.event_info.as_mut() { + ev_info.utf16parts.push(wparam as u16); + } } else { // In this case, wparam holds a UTF-32 character. // Let's encode it as UTF-16 and append it to the end of `utf16parts` - let utf16parts = &mut self.event_info.as_mut().unwrap().utf16parts; + let utf16parts = match self.event_info.as_mut() { + Some(ev_info) => &mut ev_info.utf16parts, + None => { + warn!("The event_info was None when it was expected to be some"); + return vec![]; + } + }; let start_offset = utf16parts.len(); let new_size = utf16parts.len() + 2; utf16parts.resize(new_size, 0); @@ -217,8 +225,13 @@ impl KeyEventBuilder { } } if !more_char_coming { - let mut event_info = self.event_info.take().unwrap(); - + let mut event_info = match self.event_info.take() { + Some(ev_info) => ev_info, + None => { + warn!("The event_info was None when it was expected to be some"); + return vec![]; + } + }; let mut layouts = LAYOUT_CACHE.lock().unwrap(); // It's okay to call `ToUnicode` here, because at this point the dead key // is already consumed by the character.