From ee85011906ced513b043021fcbae5b5dfe177ab8 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sat, 14 Nov 2020 12:21:50 +0100 Subject: [PATCH 001/131] 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 002/131] 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 003/131] 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 004/131] 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 005/131] 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 006/131] 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 007/131] 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 008/131] 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 009/131] 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 010/131] 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 011/131] 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 012/131] 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 013/131] 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 014/131] 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 015/131] 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 016/131] 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 017/131] 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 018/131] 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 019/131] 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 020/131] 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 021/131] 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 022/131] 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 023/131] 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 024/131] 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 025/131] 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 026/131] 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 027/131] 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 028/131] 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 029/131] 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 030/131] 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 031/131] 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 032/131] 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 033/131] 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 034/131] 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 035/131] 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 036/131] 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 037/131] 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 038/131] 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 039/131] 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 040/131] 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 041/131] 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 042/131] 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 043/131] 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 044/131] 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 045/131] `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 046/131] 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 047/131] 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 048/131] 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 049/131] 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 050/131] 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 051/131] 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 052/131] 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 053/131] 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 054/131] 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 055/131] 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 056/131] 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 057/131] 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 058/131] 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 059/131] 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 060/131] 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 061/131] 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 062/131] 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 063/131] 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 064/131] 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 065/131] 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 066/131] 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 067/131] 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 c02a0014401665eb39596e311ab94a365ce121d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Fri, 19 Mar 2021 20:41:14 +0100 Subject: [PATCH 068/131] Get stuff to compile --- src/platform/unix.rs | 15 + src/platform_impl/linux/mod.rs | 11 + .../linux/wayland/seat/keyboard/handlers.rs | 35 +- .../linux/wayland/seat/keyboard/keymap.rs | 411 ++-- .../linux/wayland/seat/keyboard/mod.rs | 6 +- src/platform_impl/linux/wayland/seat/mod.rs | 2 +- .../linux/wayland/seat/pointer/data.rs | 3 +- .../linux/wayland/seat/pointer/mod.rs | 2 +- .../linux/wayland/seat/text_input/handlers.rs | 2 +- .../linux/x11/event_processor.rs | 57 +- src/platform_impl/linux/x11/events.rs | 2010 ++++++++--------- src/platform_impl/linux/x11/util/input.rs | 6 +- src/platform_impl/linux/x11/util/modifiers.rs | 25 +- 13 files changed, 1329 insertions(+), 1256 deletions(-) diff --git a/src/platform/unix.rs b/src/platform/unix.rs index 8662e45690..6ec6b28f8a 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -11,8 +11,11 @@ use std::os::raw; use std::{ptr, sync::Arc}; use crate::{ + event::KeyEvent, event_loop::{EventLoop, EventLoopWindowTarget}, + keyboard::Key, monitor::MonitorHandle, + platform::modifier_supplement::KeyEventExtModifierSupplement, window::{Window, WindowBuilder}, }; @@ -529,3 +532,15 @@ pub struct ARGBColor { pub g: u8, pub b: u8, } + +impl KeyEventExtModifierSupplement for KeyEvent { + #[inline] + fn text_with_all_modifiers(&self) -> Option<&str> { + self.platform_specific.text_with_all_modifers + } + + #[inline] + fn key_without_modifiers(&self) -> Key<'static> { + self.platform_specific.key_without_modifiers + } +} diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index e845b83be3..27d15ee96f 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -29,6 +29,7 @@ use crate::{ event::Event, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, icon::Icon, + keyboard::Key, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, window::{CursorIcon, Fullscreen, UserAttentionType, WindowAttributes}, }; @@ -40,6 +41,12 @@ pub mod wayland; #[cfg(feature = "x11")] pub mod x11; +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct KeyEventExtra { + pub text_with_all_modifers: Option<&'static str>, + pub key_without_modifiers: Key<'static>, +} + /// Environment variable specifying which backend should be used on unix platform. /// /// Legal values are x11 and wayland. If this variable is set only the named backend @@ -424,6 +431,10 @@ impl Window { x11_or_wayland!(match self; Window(w) => w.set_ime_position(position)) } + pub fn reset_dead_keys(&self) { + todo!() + } + #[inline] pub fn request_user_attention(&self, _request_type: Option) { match self { diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs index 7c320973b8..d81e440cc5 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs @@ -4,7 +4,8 @@ use sctk::reexports::client::protocol::wl_keyboard::KeyState; use sctk::seat::keyboard::Event as KeyboardEvent; -use crate::event::{ElementState, KeyboardInput, ModifiersState, WindowEvent}; +use crate::event::{ElementState, KeyEvent, WindowEvent}; +use crate::keyboard::ModifiersState; use crate::platform_impl::wayland::event_loop::WinitState; use crate::platform_impl::wayland::{self, DeviceId}; @@ -68,19 +69,21 @@ pub(super) fn handle_keyboard( _ => unreachable!(), }; - let virtual_keycode = keymap::keysym_to_vkey(keysym); + // let virtual_keycode = keymap::keysym_to_vkey(keysym); event_sink.push_window_event( - #[allow(deprecated)] WindowEvent::KeyboardInput { device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( DeviceId, )), - input: KeyboardInput { + event: KeyEvent { + physical_key: todo!(), + logical_key: todo!(), + text: todo!(), + location: todo!(), state, - scancode: rawkey, - virtual_keycode, - modifiers: *inner.modifiers_state.borrow(), + repeat: false, + platform_specific: todo!(), }, is_synthetic: false, }, @@ -94,7 +97,7 @@ pub(super) fn handle_keyboard( if let Some(txt) = utf8 { for ch in txt.chars() { - event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id); + // event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id); } } } @@ -109,19 +112,21 @@ pub(super) fn handle_keyboard( None => return, }; - let virtual_keycode = keymap::keysym_to_vkey(keysym); + // let virtual_keycode = keymap::keysym_to_vkey(keysym); event_sink.push_window_event( - #[allow(deprecated)] WindowEvent::KeyboardInput { device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( DeviceId, )), - input: KeyboardInput { + event: KeyEvent { + physical_key: todo!(), + logical_key: todo!(), + text: todo!(), + location: todo!(), state: ElementState::Pressed, - scancode: rawkey, - virtual_keycode, - modifiers: *inner.modifiers_state.borrow(), + repeat: false, + platform_specific: todo!(), }, is_synthetic: false, }, @@ -130,7 +135,7 @@ pub(super) fn handle_keyboard( if let Some(txt) = utf8 { for ch in txt.chars() { - event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id); + // event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id); } } } diff --git a/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs b/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs index 991afff2c9..b674dd2945 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs @@ -1,192 +1,229 @@ -//! Convert Wayland keys to winit keys. +// //! Convert Wayland keys to winit keys. -use crate::event::VirtualKeyCode; +// use crate::keyboard::Key; -pub fn keysym_to_vkey(keysym: u32) -> Option { - use sctk::seat::keyboard::keysyms; - match keysym { - // Numbers. - keysyms::XKB_KEY_1 => Some(VirtualKeyCode::Key1), - keysyms::XKB_KEY_2 => Some(VirtualKeyCode::Key2), - keysyms::XKB_KEY_3 => Some(VirtualKeyCode::Key3), - keysyms::XKB_KEY_4 => Some(VirtualKeyCode::Key4), - keysyms::XKB_KEY_5 => Some(VirtualKeyCode::Key5), - keysyms::XKB_KEY_6 => Some(VirtualKeyCode::Key6), - keysyms::XKB_KEY_7 => Some(VirtualKeyCode::Key7), - keysyms::XKB_KEY_8 => Some(VirtualKeyCode::Key8), - keysyms::XKB_KEY_9 => Some(VirtualKeyCode::Key9), - keysyms::XKB_KEY_0 => Some(VirtualKeyCode::Key0), - // Letters. - keysyms::XKB_KEY_A | keysyms::XKB_KEY_a => Some(VirtualKeyCode::A), - keysyms::XKB_KEY_B | keysyms::XKB_KEY_b => Some(VirtualKeyCode::B), - keysyms::XKB_KEY_C | keysyms::XKB_KEY_c => Some(VirtualKeyCode::C), - keysyms::XKB_KEY_D | keysyms::XKB_KEY_d => Some(VirtualKeyCode::D), - keysyms::XKB_KEY_E | keysyms::XKB_KEY_e => Some(VirtualKeyCode::E), - keysyms::XKB_KEY_F | keysyms::XKB_KEY_f => Some(VirtualKeyCode::F), - keysyms::XKB_KEY_G | keysyms::XKB_KEY_g => Some(VirtualKeyCode::G), - keysyms::XKB_KEY_H | keysyms::XKB_KEY_h => Some(VirtualKeyCode::H), - keysyms::XKB_KEY_I | keysyms::XKB_KEY_i => Some(VirtualKeyCode::I), - keysyms::XKB_KEY_J | keysyms::XKB_KEY_j => Some(VirtualKeyCode::J), - keysyms::XKB_KEY_K | keysyms::XKB_KEY_k => Some(VirtualKeyCode::K), - keysyms::XKB_KEY_L | keysyms::XKB_KEY_l => Some(VirtualKeyCode::L), - keysyms::XKB_KEY_M | keysyms::XKB_KEY_m => Some(VirtualKeyCode::M), - keysyms::XKB_KEY_N | keysyms::XKB_KEY_n => Some(VirtualKeyCode::N), - keysyms::XKB_KEY_O | keysyms::XKB_KEY_o => Some(VirtualKeyCode::O), - keysyms::XKB_KEY_P | keysyms::XKB_KEY_p => Some(VirtualKeyCode::P), - keysyms::XKB_KEY_Q | keysyms::XKB_KEY_q => Some(VirtualKeyCode::Q), - keysyms::XKB_KEY_R | keysyms::XKB_KEY_r => Some(VirtualKeyCode::R), - keysyms::XKB_KEY_S | keysyms::XKB_KEY_s => Some(VirtualKeyCode::S), - keysyms::XKB_KEY_T | keysyms::XKB_KEY_t => Some(VirtualKeyCode::T), - keysyms::XKB_KEY_U | keysyms::XKB_KEY_u => Some(VirtualKeyCode::U), - keysyms::XKB_KEY_V | keysyms::XKB_KEY_v => Some(VirtualKeyCode::V), - keysyms::XKB_KEY_W | keysyms::XKB_KEY_w => Some(VirtualKeyCode::W), - keysyms::XKB_KEY_X | keysyms::XKB_KEY_x => Some(VirtualKeyCode::X), - keysyms::XKB_KEY_Y | keysyms::XKB_KEY_y => Some(VirtualKeyCode::Y), - keysyms::XKB_KEY_Z | keysyms::XKB_KEY_z => Some(VirtualKeyCode::Z), - // Escape. - keysyms::XKB_KEY_Escape => Some(VirtualKeyCode::Escape), - // Function keys. - keysyms::XKB_KEY_F1 => Some(VirtualKeyCode::F1), - keysyms::XKB_KEY_F2 => Some(VirtualKeyCode::F2), - keysyms::XKB_KEY_F3 => Some(VirtualKeyCode::F3), - keysyms::XKB_KEY_F4 => Some(VirtualKeyCode::F4), - keysyms::XKB_KEY_F5 => Some(VirtualKeyCode::F5), - keysyms::XKB_KEY_F6 => Some(VirtualKeyCode::F6), - keysyms::XKB_KEY_F7 => Some(VirtualKeyCode::F7), - keysyms::XKB_KEY_F8 => Some(VirtualKeyCode::F8), - keysyms::XKB_KEY_F9 => Some(VirtualKeyCode::F9), - keysyms::XKB_KEY_F10 => Some(VirtualKeyCode::F10), - keysyms::XKB_KEY_F11 => Some(VirtualKeyCode::F11), - keysyms::XKB_KEY_F12 => Some(VirtualKeyCode::F12), - keysyms::XKB_KEY_F13 => Some(VirtualKeyCode::F13), - keysyms::XKB_KEY_F14 => Some(VirtualKeyCode::F14), - keysyms::XKB_KEY_F15 => Some(VirtualKeyCode::F15), - keysyms::XKB_KEY_F16 => Some(VirtualKeyCode::F16), - keysyms::XKB_KEY_F17 => Some(VirtualKeyCode::F17), - keysyms::XKB_KEY_F18 => Some(VirtualKeyCode::F18), - keysyms::XKB_KEY_F19 => Some(VirtualKeyCode::F19), - keysyms::XKB_KEY_F20 => Some(VirtualKeyCode::F20), - keysyms::XKB_KEY_F21 => Some(VirtualKeyCode::F21), - keysyms::XKB_KEY_F22 => Some(VirtualKeyCode::F22), - keysyms::XKB_KEY_F23 => Some(VirtualKeyCode::F23), - keysyms::XKB_KEY_F24 => Some(VirtualKeyCode::F24), - // Flow control. - keysyms::XKB_KEY_Print => Some(VirtualKeyCode::Snapshot), - keysyms::XKB_KEY_Scroll_Lock => Some(VirtualKeyCode::Scroll), - keysyms::XKB_KEY_Pause => Some(VirtualKeyCode::Pause), - keysyms::XKB_KEY_Insert => Some(VirtualKeyCode::Insert), - keysyms::XKB_KEY_Home => Some(VirtualKeyCode::Home), - keysyms::XKB_KEY_Delete => Some(VirtualKeyCode::Delete), - keysyms::XKB_KEY_End => Some(VirtualKeyCode::End), - keysyms::XKB_KEY_Page_Down => Some(VirtualKeyCode::PageDown), - keysyms::XKB_KEY_Page_Up => Some(VirtualKeyCode::PageUp), - // Arrows. - keysyms::XKB_KEY_Left => Some(VirtualKeyCode::Left), - keysyms::XKB_KEY_Up => Some(VirtualKeyCode::Up), - keysyms::XKB_KEY_Right => Some(VirtualKeyCode::Right), - keysyms::XKB_KEY_Down => Some(VirtualKeyCode::Down), +// pub fn keysym_to_vkey(keysym: u32) -> Option> { +// use sctk::seat::keyboard::keysyms; +// match keysym { +// // Numbers. +// // keysyms::XKB_KEY_1 => Some(Key::Character("1")), +// // keysyms::XKB_KEY_2 => Some(Key::Character("2")), +// // keysyms::XKB_KEY_3 => Some(Key::Character("3")), +// // keysyms::XKB_KEY_4 => Some(Key::Character("4")), +// // keysyms::XKB_KEY_5 => Some(Key::Character("5")), +// // keysyms::XKB_KEY_6 => Some(Key::Character("6")), +// // keysyms::XKB_KEY_7 => Some(Key::Character("7")), +// // keysyms::XKB_KEY_8 => Some(Key::Character("8")), +// // keysyms::XKB_KEY_9 => Some(Key::Character("9")), +// // keysyms::XKB_KEY_0 => Some(Key::Character("0")), +// // Letters. +// // keysyms::XKB_KEY_A => Some(Key::Character("A")), +// // keysyms::XKB_KEY_a => Some(Key::Character("a")), +// // keysyms::XKB_KEY_B => Some(Key::Character("B")), +// // keysyms::XKB_KEY_b => Some(Key::Character("b")), +// // keysyms::XKB_KEY_C => Some(Key::Character("C")), +// // keysyms::XKB_KEY_c => Some(Key::Character("c")), +// // keysyms::XKB_KEY_D => Some(Key::Character("D")), +// // keysyms::XKB_KEY_d => Some(Key::Character("d")), +// // keysyms::XKB_KEY_E => Some(Key::Character("E")), +// // keysyms::XKB_KEY_e => Some(Key::Character("e")), +// // keysyms::XKB_KEY_F => Some(Key::Character("F")), +// // keysyms::XKB_KEY_f => Some(Key::Character("f")), +// // keysyms::XKB_KEY_G => Some(Key::Character("G")), +// // keysyms::XKB_KEY_g => Some(Key::Character("g")), +// // keysyms::XKB_KEY_H => Some(Key::Character("H")), +// // keysyms::XKB_KEY_h => Some(Key::Character("h")), +// // keysyms::XKB_KEY_I => Some(Key::Character("I")), +// // keysyms::XKB_KEY_i => Some(Key::Character("i")), +// // keysyms::XKB_KEY_J => Some(Key::Character("J")), +// // keysyms::XKB_KEY_j => Some(Key::Character("j")), +// // keysyms::XKB_KEY_K => Some(Key::Character("K")), +// // keysyms::XKB_KEY_k => Some(Key::Character("k")), +// // keysyms::XKB_KEY_L => Some(Key::Character("L")), +// // keysyms::XKB_KEY_l => Some(Key::Character("l")), +// // keysyms::XKB_KEY_M => Some(Key::Character("M")), +// // keysyms::XKB_KEY_m => Some(Key::Character("m")), +// // keysyms::XKB_KEY_N => Some(Key::Character("N")), +// // keysyms::XKB_KEY_n => Some(Key::Character("n")), +// // keysyms::XKB_KEY_O => Some(Key::Character("O")), +// // keysyms::XKB_KEY_o => Some(Key::Character("o")), +// // keysyms::XKB_KEY_P => Some(Key::Character("P")), +// // keysyms::XKB_KEY_p => Some(Key::Character("p")), +// // keysyms::XKB_KEY_Q => Some(Key::Character("Q")), +// // keysyms::XKB_KEY_q => Some(Key::Character("q")), +// // keysyms::XKB_KEY_R => Some(Key::Character("R")), +// // keysyms::XKB_KEY_r => Some(Key::Character("r")), +// // keysyms::XKB_KEY_S => Some(Key::Character("S")), +// // keysyms::XKB_KEY_s => Some(Key::Character("s")), +// // keysyms::XKB_KEY_T => Some(Key::Character("T")), +// // keysyms::XKB_KEY_t => Some(Key::Character("t")), +// // keysyms::XKB_KEY_U => Some(Key::Character("U")), +// // keysyms::XKB_KEY_u => Some(Key::Character("u")), +// // keysyms::XKB_KEY_V => Some(Key::Character("V")), +// // keysyms::XKB_KEY_v => Some(Key::Character("v")), +// // keysyms::XKB_KEY_W => Some(Key::Character("W")), +// // keysyms::XKB_KEY_w => Some(Key::Character("w")), +// // keysyms::XKB_KEY_X => Some(Key::Character("X")), +// // keysyms::XKB_KEY_x => Some(Key::Character("x")), +// // keysyms::XKB_KEY_Y => Some(Key::Character("Y")), +// // keysyms::XKB_KEY_y => Some(Key::Character("y")), +// // keysyms::XKB_KEY_Z => Some(Key::Character("Z")), +// // keysyms::XKB_KEY_z => Some(Key::Character("z")), +// // Escape. +// keysyms::XKB_KEY_Escape => Some(Key::Escape), +// // Function keys. +// keysyms::XKB_KEY_F1 => Some(Key::F1), +// keysyms::XKB_KEY_F2 => Some(Key::F2), +// keysyms::XKB_KEY_F3 => Some(Key::F3), +// keysyms::XKB_KEY_F4 => Some(Key::F4), +// keysyms::XKB_KEY_F5 => Some(Key::F5), +// keysyms::XKB_KEY_F6 => Some(Key::F6), +// keysyms::XKB_KEY_F7 => Some(Key::F7), +// keysyms::XKB_KEY_F8 => Some(Key::F8), +// keysyms::XKB_KEY_F9 => Some(Key::F9), +// keysyms::XKB_KEY_F10 => Some(Key::F10), +// keysyms::XKB_KEY_F11 => Some(Key::F11), +// keysyms::XKB_KEY_F12 => Some(Key::F12), +// keysyms::XKB_KEY_F13 => Some(Key::F13), +// keysyms::XKB_KEY_F14 => Some(Key::F14), +// keysyms::XKB_KEY_F15 => Some(Key::F15), +// keysyms::XKB_KEY_F16 => Some(Key::F16), +// keysyms::XKB_KEY_F17 => Some(Key::F17), +// keysyms::XKB_KEY_F18 => Some(Key::F18), +// keysyms::XKB_KEY_F19 => Some(Key::F19), +// keysyms::XKB_KEY_F20 => Some(Key::F20), +// keysyms::XKB_KEY_F21 => Some(Key::F21), +// keysyms::XKB_KEY_F22 => Some(Key::F22), +// keysyms::XKB_KEY_F23 => Some(Key::F23), +// keysyms::XKB_KEY_F24 => Some(Key::F24), +// keysyms::XKB_KEY_F25 => Some(Key::F25), +// keysyms::XKB_KEY_F26 => Some(Key::F26), +// keysyms::XKB_KEY_F27 => Some(Key::F27), +// keysyms::XKB_KEY_F28 => Some(Key::F28), +// keysyms::XKB_KEY_F29 => Some(Key::F29), +// keysyms::XKB_KEY_F30 => Some(Key::F30), +// keysyms::XKB_KEY_F31 => Some(Key::F31), +// keysyms::XKB_KEY_F32 => Some(Key::F32), +// keysyms::XKB_KEY_F33 => Some(Key::F33), +// keysyms::XKB_KEY_F34 => Some(Key::F34), +// keysyms::XKB_KEY_F35 => Some(Key::F35), +// // Flow control. +// keysyms::XKB_KEY_Print => Some(Key::PrintScreen), +// keysyms::XKB_KEY_Scroll_Lock => Some(Key::ScrollLock), +// keysyms::XKB_KEY_Pause => Some(Key::Pause), +// keysyms::XKB_KEY_Insert => Some(Key::Insert), +// keysyms::XKB_KEY_Home => Some(Key::Home), +// keysyms::XKB_KEY_Delete => Some(Key::Delete), +// keysyms::XKB_KEY_End => Some(Key::End), +// keysyms::XKB_KEY_Page_Down => Some(Key::PageDown), +// keysyms::XKB_KEY_Page_Up => Some(Key::PageUp), +// // Arrows. +// keysyms::XKB_KEY_Left => Some(Key::Left), +// keysyms::XKB_KEY_Up => Some(Key::Up), +// keysyms::XKB_KEY_Right => Some(Key::Right), +// keysyms::XKB_KEY_Down => Some(Key::Down), - keysyms::XKB_KEY_BackSpace => Some(VirtualKeyCode::Back), - keysyms::XKB_KEY_Return => Some(VirtualKeyCode::Return), - keysyms::XKB_KEY_space => Some(VirtualKeyCode::Space), +// keysyms::XKB_KEY_BackSpace => Some(Key::Back), +// keysyms::XKB_KEY_Return => Some(Key::Return), +// keysyms::XKB_KEY_space => Some(Key::Space), - keysyms::XKB_KEY_Multi_key => Some(VirtualKeyCode::Compose), - keysyms::XKB_KEY_caret => Some(VirtualKeyCode::Caret), +// keysyms::XKB_KEY_Multi_key => Some(Key::Compose), +// keysyms::XKB_KEY_caret => Some(Key::Caret), - // Keypad. - keysyms::XKB_KEY_Num_Lock => Some(VirtualKeyCode::Numlock), - keysyms::XKB_KEY_KP_0 => Some(VirtualKeyCode::Numpad0), - keysyms::XKB_KEY_KP_1 => Some(VirtualKeyCode::Numpad1), - keysyms::XKB_KEY_KP_2 => Some(VirtualKeyCode::Numpad2), - keysyms::XKB_KEY_KP_3 => Some(VirtualKeyCode::Numpad3), - keysyms::XKB_KEY_KP_4 => Some(VirtualKeyCode::Numpad4), - keysyms::XKB_KEY_KP_5 => Some(VirtualKeyCode::Numpad5), - keysyms::XKB_KEY_KP_6 => Some(VirtualKeyCode::Numpad6), - keysyms::XKB_KEY_KP_7 => Some(VirtualKeyCode::Numpad7), - keysyms::XKB_KEY_KP_8 => Some(VirtualKeyCode::Numpad8), - keysyms::XKB_KEY_KP_9 => Some(VirtualKeyCode::Numpad9), - // Misc. - // => Some(VirtualKeyCode::AbntC1), - // => Some(VirtualKeyCode::AbntC2), - keysyms::XKB_KEY_plus => Some(VirtualKeyCode::Plus), - keysyms::XKB_KEY_apostrophe => Some(VirtualKeyCode::Apostrophe), - // => Some(VirtualKeyCode::Apps), - keysyms::XKB_KEY_at => Some(VirtualKeyCode::At), - // => Some(VirtualKeyCode::Ax), - keysyms::XKB_KEY_backslash => Some(VirtualKeyCode::Backslash), - keysyms::XKB_KEY_XF86Calculator => Some(VirtualKeyCode::Calculator), - // => Some(VirtualKeyCode::Capital), - keysyms::XKB_KEY_colon => Some(VirtualKeyCode::Colon), - keysyms::XKB_KEY_comma => Some(VirtualKeyCode::Comma), - // => Some(VirtualKeyCode::Convert), - keysyms::XKB_KEY_equal => Some(VirtualKeyCode::Equals), - keysyms::XKB_KEY_grave => Some(VirtualKeyCode::Grave), - // => Some(VirtualKeyCode::Kana), - keysyms::XKB_KEY_Kanji => Some(VirtualKeyCode::Kanji), - keysyms::XKB_KEY_Alt_L => Some(VirtualKeyCode::LAlt), - keysyms::XKB_KEY_bracketleft => Some(VirtualKeyCode::LBracket), - keysyms::XKB_KEY_Control_L => Some(VirtualKeyCode::LControl), - keysyms::XKB_KEY_Shift_L => Some(VirtualKeyCode::LShift), - keysyms::XKB_KEY_Super_L => Some(VirtualKeyCode::LWin), - keysyms::XKB_KEY_XF86Mail => Some(VirtualKeyCode::Mail), - // => Some(VirtualKeyCode::MediaSelect), - // => Some(VirtualKeyCode::MediaStop), - keysyms::XKB_KEY_minus => Some(VirtualKeyCode::Minus), - keysyms::XKB_KEY_asterisk => Some(VirtualKeyCode::Asterisk), - keysyms::XKB_KEY_XF86AudioMute => Some(VirtualKeyCode::Mute), - // => Some(VirtualKeyCode::MyComputer), - keysyms::XKB_KEY_XF86AudioNext => Some(VirtualKeyCode::NextTrack), - // => Some(VirtualKeyCode::NoConvert), - keysyms::XKB_KEY_KP_Separator => Some(VirtualKeyCode::NumpadComma), - keysyms::XKB_KEY_KP_Enter => Some(VirtualKeyCode::NumpadEnter), - keysyms::XKB_KEY_KP_Equal => Some(VirtualKeyCode::NumpadEquals), - keysyms::XKB_KEY_KP_Add => Some(VirtualKeyCode::NumpadAdd), - keysyms::XKB_KEY_KP_Subtract => Some(VirtualKeyCode::NumpadSubtract), - keysyms::XKB_KEY_KP_Multiply => Some(VirtualKeyCode::NumpadMultiply), - keysyms::XKB_KEY_KP_Divide => Some(VirtualKeyCode::NumpadDivide), - keysyms::XKB_KEY_KP_Decimal => Some(VirtualKeyCode::NumpadDecimal), - keysyms::XKB_KEY_KP_Page_Up => Some(VirtualKeyCode::PageUp), - keysyms::XKB_KEY_KP_Page_Down => Some(VirtualKeyCode::PageDown), - keysyms::XKB_KEY_KP_Home => Some(VirtualKeyCode::Home), - keysyms::XKB_KEY_KP_End => Some(VirtualKeyCode::End), - keysyms::XKB_KEY_KP_Left => Some(VirtualKeyCode::Left), - keysyms::XKB_KEY_KP_Up => Some(VirtualKeyCode::Up), - keysyms::XKB_KEY_KP_Right => Some(VirtualKeyCode::Right), - keysyms::XKB_KEY_KP_Down => Some(VirtualKeyCode::Down), - // => Some(VirtualKeyCode::OEM102), - keysyms::XKB_KEY_period => Some(VirtualKeyCode::Period), - // => Some(VirtualKeyCode::Playpause), - keysyms::XKB_KEY_XF86PowerOff => Some(VirtualKeyCode::Power), - keysyms::XKB_KEY_XF86AudioPrev => Some(VirtualKeyCode::PrevTrack), - keysyms::XKB_KEY_Alt_R => Some(VirtualKeyCode::RAlt), - keysyms::XKB_KEY_bracketright => Some(VirtualKeyCode::RBracket), - keysyms::XKB_KEY_Control_R => Some(VirtualKeyCode::RControl), - keysyms::XKB_KEY_Shift_R => Some(VirtualKeyCode::RShift), - keysyms::XKB_KEY_Super_R => Some(VirtualKeyCode::RWin), - keysyms::XKB_KEY_semicolon => Some(VirtualKeyCode::Semicolon), - keysyms::XKB_KEY_slash => Some(VirtualKeyCode::Slash), - keysyms::XKB_KEY_XF86Sleep => Some(VirtualKeyCode::Sleep), - // => Some(VirtualKeyCode::Stop), - // => Some(VirtualKeyCode::Sysrq), - keysyms::XKB_KEY_Tab => Some(VirtualKeyCode::Tab), - keysyms::XKB_KEY_ISO_Left_Tab => Some(VirtualKeyCode::Tab), - keysyms::XKB_KEY_underscore => Some(VirtualKeyCode::Underline), - // => Some(VirtualKeyCode::Unlabeled), - keysyms::XKB_KEY_XF86AudioLowerVolume => Some(VirtualKeyCode::VolumeDown), - keysyms::XKB_KEY_XF86AudioRaiseVolume => Some(VirtualKeyCode::VolumeUp), - // => Some(VirtualKeyCode::Wake), - // => Some(VirtualKeyCode::Webback), - // => Some(VirtualKeyCode::WebFavorites), - // => Some(VirtualKeyCode::WebForward), - // => Some(VirtualKeyCode::WebHome), - // => Some(VirtualKeyCode::WebRefresh), - // => Some(VirtualKeyCode::WebSearch), - // => Some(VirtualKeyCode::WebStop), - keysyms::XKB_KEY_yen => Some(VirtualKeyCode::Yen), - keysyms::XKB_KEY_XF86Copy => Some(VirtualKeyCode::Copy), - keysyms::XKB_KEY_XF86Paste => Some(VirtualKeyCode::Paste), - keysyms::XKB_KEY_XF86Cut => Some(VirtualKeyCode::Cut), - // Fallback. - _ => None, - } -} +// // Keypad. +// keysyms::XKB_KEY_Num_Lock => Some(Key::Numlock), +// keysyms::XKB_KEY_KP_0 => Some(Key::Numpad0), +// keysyms::XKB_KEY_KP_1 => Some(Key::Numpad1), +// keysyms::XKB_KEY_KP_2 => Some(Key::Numpad2), +// keysyms::XKB_KEY_KP_3 => Some(Key::Numpad3), +// keysyms::XKB_KEY_KP_4 => Some(Key::Numpad4), +// keysyms::XKB_KEY_KP_5 => Some(Key::Numpad5), +// keysyms::XKB_KEY_KP_6 => Some(Key::Numpad6), +// keysyms::XKB_KEY_KP_7 => Some(Key::Numpad7), +// keysyms::XKB_KEY_KP_8 => Some(Key::Numpad8), +// keysyms::XKB_KEY_KP_9 => Some(Key::Numpad9), +// // Misc. +// // => Some(Key::AbntC1), +// // => Some(Key::AbntC2), +// keysyms::XKB_KEY_plus => Some(Key::Plus), +// keysyms::XKB_KEY_apostrophe => Some(Key::Apostrophe), +// // => Some(Key::Apps), +// keysyms::XKB_KEY_at => Some(Key::At), +// // => Some(Key::Ax), +// keysyms::XKB_KEY_backslash => Some(Key::Backslash), +// keysyms::XKB_KEY_XF86Calculator => Some(Key::Calculator), +// // => Some(Key::Capital), +// keysyms::XKB_KEY_colon => Some(Key::Colon), +// keysyms::XKB_KEY_comma => Some(Key::Comma), +// // => Some(Key::Convert), +// keysyms::XKB_KEY_equal => Some(Key::Equals), +// keysyms::XKB_KEY_grave => Some(Key::Grave), +// // => Some(Key::Kana), +// keysyms::XKB_KEY_Kanji => Some(Key::Kanji), +// keysyms::XKB_KEY_Alt_L => Some(Key::LAlt), +// keysyms::XKB_KEY_bracketleft => Some(Key::LBracket), +// keysyms::XKB_KEY_Control_L => Some(Key::LControl), +// keysyms::XKB_KEY_Shift_L => Some(Key::LShift), +// keysyms::XKB_KEY_Super_L => Some(Key::LWin), +// keysyms::XKB_KEY_XF86Mail => Some(Key::Mail), +// // => Some(Key::MediaSelect), +// // => Some(Key::MediaStop), +// keysyms::XKB_KEY_minus => Some(Key::Minus), +// keysyms::XKB_KEY_asterisk => Some(Key::Asterisk), +// keysyms::XKB_KEY_XF86AudioMute => Some(Key::Mute), +// // => Some(Key::MyComputer), +// keysyms::XKB_KEY_XF86AudioNext => Some(Key::NextTrack), +// // => Some(Key::NoConvert), +// keysyms::XKB_KEY_KP_Separator => Some(Key::NumpadComma), +// keysyms::XKB_KEY_KP_Enter => Some(Key::NumpadEnter), +// keysyms::XKB_KEY_KP_Equal => Some(Key::NumpadEquals), +// keysyms::XKB_KEY_KP_Add => Some(Key::NumpadAdd), +// keysyms::XKB_KEY_KP_Subtract => Some(Key::NumpadSubtract), +// keysyms::XKB_KEY_KP_Multiply => Some(Key::NumpadMultiply), +// keysyms::XKB_KEY_KP_Divide => Some(Key::NumpadDivide), +// keysyms::XKB_KEY_KP_Decimal => Some(Key::NumpadDecimal), +// keysyms::XKB_KEY_KP_Page_Up => Some(Key::PageUp), +// keysyms::XKB_KEY_KP_Page_Down => Some(Key::PageDown), +// keysyms::XKB_KEY_KP_Home => Some(Key::Home), +// keysyms::XKB_KEY_KP_End => Some(Key::End), +// keysyms::XKB_KEY_KP_Left => Some(Key::Left), +// keysyms::XKB_KEY_KP_Up => Some(Key::Up), +// keysyms::XKB_KEY_KP_Right => Some(Key::Right), +// keysyms::XKB_KEY_KP_Down => Some(Key::Down), +// // => Some(Key::OEM102), +// keysyms::XKB_KEY_period => Some(Key::Period), +// // => Some(Key::Playpause), +// keysyms::XKB_KEY_XF86PowerOff => Some(Key::Power), +// keysyms::XKB_KEY_XF86AudioPrev => Some(Key::PrevTrack), +// keysyms::XKB_KEY_Alt_R => Some(Key::RAlt), +// keysyms::XKB_KEY_bracketright => Some(Key::RBracket), +// keysyms::XKB_KEY_Control_R => Some(Key::RControl), +// keysyms::XKB_KEY_Shift_R => Some(Key::RShift), +// keysyms::XKB_KEY_Super_R => Some(Key::RWin), +// keysyms::XKB_KEY_semicolon => Some(Key::Semicolon), +// keysyms::XKB_KEY_slash => Some(Key::Slash), +// keysyms::XKB_KEY_XF86Sleep => Some(Key::Sleep), +// // => Some(Key::Stop), +// // => Some(Key::Sysrq), +// keysyms::XKB_KEY_Tab => Some(Key::Tab), +// keysyms::XKB_KEY_ISO_Left_Tab => Some(Key::Tab), +// keysyms::XKB_KEY_underscore => Some(Key::Underline), +// // => Some(Key::Unlabeled), +// keysyms::XKB_KEY_XF86AudioLowerVolume => Some(Key::VolumeDown), +// keysyms::XKB_KEY_XF86AudioRaiseVolume => Some(Key::VolumeUp), +// // => Some(Key::Wake), +// // => Some(Key::Webback), +// // => Some(Key::WebFavorites), +// // => Some(Key::WebForward), +// // => Some(Key::WebHome), +// // => Some(Key::WebRefresh), +// // => Some(Key::WebSearch), +// // => Some(Key::WebStop), +// keysyms::XKB_KEY_yen => Some(Key::Yen), +// keysyms::XKB_KEY_XF86Copy => Some(Key::Copy), +// keysyms::XKB_KEY_XF86Paste => Some(Key::Paste), +// keysyms::XKB_KEY_XF86Cut => Some(Key::Cut), +// // Fallback. +// _ => None, +// } +// } diff --git a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs b/src/platform_impl/linux/wayland/seat/keyboard/mod.rs index 1362dcf797..d71719ecd2 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/mod.rs @@ -11,7 +11,7 @@ use sctk::reexports::calloop::{LoopHandle, Source}; use sctk::seat::keyboard::{self, RepeatSource}; -use crate::event::ModifiersState; +use crate::keyboard::ModifiersState; use crate::platform_impl::wayland::event_loop::WinitState; use crate::platform_impl::wayland::WindowId; @@ -97,9 +97,9 @@ impl From for ModifiersState { fn from(mods: keyboard::ModifiersState) -> ModifiersState { let mut wl_mods = ModifiersState::empty(); wl_mods.set(ModifiersState::SHIFT, mods.shift); - wl_mods.set(ModifiersState::CTRL, mods.ctrl); + wl_mods.set(ModifiersState::CONTROL, mods.ctrl); wl_mods.set(ModifiersState::ALT, mods.alt); - wl_mods.set(ModifiersState::LOGO, mods.logo); + wl_mods.set(ModifiersState::SUPER, mods.logo); wl_mods } } diff --git a/src/platform_impl/linux/wayland/seat/mod.rs b/src/platform_impl/linux/wayland/seat/mod.rs index 23098d08c8..f4271b2b10 100644 --- a/src/platform_impl/linux/wayland/seat/mod.rs +++ b/src/platform_impl/linux/wayland/seat/mod.rs @@ -17,7 +17,7 @@ use sctk::seat::{SeatData, SeatListener}; use super::env::WinitEnv; use super::event_loop::WinitState; -use crate::event::ModifiersState; +use crate::keyboard::ModifiersState; mod keyboard; pub mod pointer; diff --git a/src/platform_impl/linux/wayland/seat/pointer/data.rs b/src/platform_impl/linux/wayland/seat/pointer/data.rs index 1da60d3526..a502a86e4c 100644 --- a/src/platform_impl/linux/wayland/seat/pointer/data.rs +++ b/src/platform_impl/linux/wayland/seat/pointer/data.rs @@ -8,7 +8,8 @@ use sctk::reexports::client::Attached; use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_pointer_constraints_v1::{ZwpPointerConstraintsV1}; use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_confined_pointer_v1::ZwpConfinedPointerV1; -use crate::event::{ModifiersState, TouchPhase}; +use crate::event::TouchPhase; +use crate::keyboard::ModifiersState; /// A data being used by pointer handlers. pub(super) struct PointerData { diff --git a/src/platform_impl/linux/wayland/seat/pointer/mod.rs b/src/platform_impl/linux/wayland/seat/pointer/mod.rs index 5debc8cbcf..2472cc5fd0 100644 --- a/src/platform_impl/linux/wayland/seat/pointer/mod.rs +++ b/src/platform_impl/linux/wayland/seat/pointer/mod.rs @@ -14,7 +14,7 @@ use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_c use sctk::seat::pointer::{ThemeManager, ThemedPointer}; -use crate::event::ModifiersState; +use crate::keyboard::ModifiersState; use crate::platform_impl::wayland::event_loop::WinitState; use crate::window::CursorIcon; diff --git a/src/platform_impl/linux/wayland/seat/text_input/handlers.rs b/src/platform_impl/linux/wayland/seat/text_input/handlers.rs index 4ba13d6715..26d56a3cb6 100644 --- a/src/platform_impl/linux/wayland/seat/text_input/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/text_input/handlers.rs @@ -70,7 +70,7 @@ pub(super) fn handle_text_input( }; for ch in text.chars() { - event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id); + // event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id); } } _ => (), diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 65ed4d9ff1..e25e08da46 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -14,10 +14,9 @@ use util::modifiers::{ModifierKeyState, ModifierKeymap}; use crate::{ dpi::{PhysicalPosition, PhysicalSize}, - event::{ - DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, TouchPhase, WindowEvent, - }, + event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, TouchPhase, WindowEvent}, event_loop::EventLoopWindowTarget as RootELW, + keyboard::ModifiersState, }; /// The X11 documentation states: "Keycodes lie in the inclusive range [8,255]". @@ -570,7 +569,7 @@ impl EventProcessor { if keycode != 0 { let scancode = keycode - KEYCODE_OFFSET as u32; let keysym = wt.xconn.lookup_keysym(xkev); - let virtual_keycode = events::keysym_to_element(keysym as c_uint); + // let virtual_keycode = events::keysym_to_element(keysym as c_uint); update_modifiers!( ModifiersState::from_x11_mask(xkev.state), @@ -579,16 +578,18 @@ impl EventProcessor { let modifiers = self.device_mod_state.modifiers(); - #[allow(deprecated)] callback(Event::WindowEvent { window_id, event: WindowEvent::KeyboardInput { device_id, - input: KeyboardInput { + event: KeyEvent { + physical_key: todo!(), + logical_key: todo!(), + text: todo!(), + location: todo!(), state, - scancode, - virtual_keycode, - modifiers, + repeat: todo!(), + platform_specific: todo!(), }, is_synthetic: false, }, @@ -603,11 +604,11 @@ impl EventProcessor { }; for chr in written.chars() { - let event = Event::WindowEvent { - window_id, - event: WindowEvent::ReceivedCharacter(chr), - }; - callback(event); + // let event = Event::WindowEvent { + // window_id, + // event: WindowEvent::ReceivedCharacter(chr), + // }; + // callback(event); } } } @@ -1095,17 +1096,15 @@ impl EventProcessor { return; } let keysym = wt.xconn.keycode_to_keysym(keycode as ffi::KeyCode); - let virtual_keycode = events::keysym_to_element(keysym as c_uint); + // let virtual_keycode = events::keysym_to_element(keysym as c_uint); let modifiers = self.device_mod_state.modifiers(); - #[allow(deprecated)] callback(Event::DeviceEvent { device_id, - event: DeviceEvent::Key(KeyboardInput { - scancode: scancode as u32, - virtual_keycode, - state, - modifiers, + event: DeviceEvent::Key(RawKeyEvent { + physical_key: todo!(), + state: todo!(), + repeat: todo!(), }), }); @@ -1251,7 +1250,7 @@ impl EventProcessor { { let scancode = (keycode - KEYCODE_OFFSET) as u32; let keysym = wt.xconn.keycode_to_keysym(keycode); - let virtual_keycode = events::keysym_to_element(keysym as c_uint); + // let virtual_keycode = events::keysym_to_element(keysym as c_uint); if let Some(modifier) = mod_keymap.get_modifier(keycode as ffi::KeyCode) { device_mod_state.key_event( @@ -1261,16 +1260,18 @@ impl EventProcessor { ); } - #[allow(deprecated)] callback(Event::WindowEvent { window_id, event: WindowEvent::KeyboardInput { device_id, - input: KeyboardInput { - scancode, - state, - virtual_keycode, - modifiers, + event: KeyEvent { + physical_key: todo!(), + logical_key: todo!(), + text: todo!(), + location: todo!(), + state: todo!(), + repeat: todo!(), + platform_specific: todo!(), }, is_synthetic: true, }, diff --git a/src/platform_impl/linux/x11/events.rs b/src/platform_impl/linux/x11/events.rs index 0c02ca0c82..e721ab5922 100644 --- a/src/platform_impl/linux/x11/events.rs +++ b/src/platform_impl/linux/x11/events.rs @@ -1,1008 +1,1008 @@ use super::ffi; -use crate::event::VirtualKeyCode; +use crate::keyboard::KeyCode; use libc; -pub fn keysym_to_element(keysym: libc::c_uint) -> Option { - Some(match keysym { - ffi::XK_BackSpace => VirtualKeyCode::Back, - ffi::XK_Tab => VirtualKeyCode::Tab, - //ffi::XK_Linefeed => VirtualKeyCode::Linefeed, - //ffi::XK_Clear => VirtualKeyCode::Clear, - ffi::XK_Return => VirtualKeyCode::Return, - //ffi::XK_Pause => VirtualKeyCode::Pause, - //ffi::XK_Scroll_Lock => VirtualKeyCode::Scroll_lock, - //ffi::XK_Sys_Req => VirtualKeyCode::Sys_req, - ffi::XK_Escape => VirtualKeyCode::Escape, - ffi::XK_Delete => VirtualKeyCode::Delete, - ffi::XK_Multi_key => VirtualKeyCode::Compose, - //ffi::XK_Kanji => VirtualKeyCode::Kanji, - //ffi::XK_Muhenkan => VirtualKeyCode::Muhenkan, - //ffi::XK_Henkan_Mode => VirtualKeyCode::Henkan_mode, - //ffi::XK_Henkan => VirtualKeyCode::Henkan, - //ffi::XK_Romaji => VirtualKeyCode::Romaji, - //ffi::XK_Hiragana => VirtualKeyCode::Hiragana, - //ffi::XK_Katakana => VirtualKeyCode::Katakana, - //ffi::XK_Hiragana_Katakana => VirtualKeyCode::Hiragana_katakana, - //ffi::XK_Zenkaku => VirtualKeyCode::Zenkaku, - //ffi::XK_Hankaku => VirtualKeyCode::Hankaku, - //ffi::XK_Zenkaku_Hankaku => VirtualKeyCode::Zenkaku_hankaku, - //ffi::XK_Touroku => VirtualKeyCode::Touroku, - //ffi::XK_Massyo => VirtualKeyCode::Massyo, - //ffi::XK_Kana_Lock => VirtualKeyCode::Kana_lock, - //ffi::XK_Kana_Shift => VirtualKeyCode::Kana_shift, - //ffi::XK_Eisu_Shift => VirtualKeyCode::Eisu_shift, - //ffi::XK_Eisu_toggle => VirtualKeyCode::Eisu_toggle, - ffi::XK_Home => VirtualKeyCode::Home, - ffi::XK_Left => VirtualKeyCode::Left, - ffi::XK_Up => VirtualKeyCode::Up, - ffi::XK_Right => VirtualKeyCode::Right, - ffi::XK_Down => VirtualKeyCode::Down, - //ffi::XK_Prior => VirtualKeyCode::Prior, - ffi::XK_Page_Up => VirtualKeyCode::PageUp, - //ffi::XK_Next => VirtualKeyCode::Next, - ffi::XK_Page_Down => VirtualKeyCode::PageDown, - ffi::XK_End => VirtualKeyCode::End, - //ffi::XK_Begin => VirtualKeyCode::Begin, - //ffi::XK_Win_L => VirtualKeyCode::Win_l, - //ffi::XK_Win_R => VirtualKeyCode::Win_r, - //ffi::XK_App => VirtualKeyCode::App, - //ffi::XK_Select => VirtualKeyCode::Select, - //ffi::XK_Print => VirtualKeyCode::Print, - //ffi::XK_Execute => VirtualKeyCode::Execute, - ffi::XK_Insert => VirtualKeyCode::Insert, - //ffi::XK_Undo => VirtualKeyCode::Undo, - //ffi::XK_Redo => VirtualKeyCode::Redo, - //ffi::XK_Menu => VirtualKeyCode::Menu, - //ffi::XK_Find => VirtualKeyCode::Find, - //ffi::XK_Cancel => VirtualKeyCode::Cancel, - //ffi::XK_Help => VirtualKeyCode::Help, - //ffi::XK_Break => VirtualKeyCode::Break, - //ffi::XK_Mode_switch => VirtualKeyCode::Mode_switch, - //ffi::XK_script_switch => VirtualKeyCode::Script_switch, - //ffi::XK_Num_Lock => VirtualKeyCode::Num_lock, - //ffi::XK_KP_Space => VirtualKeyCode::Kp_space, - //ffi::XK_KP_Tab => VirtualKeyCode::Kp_tab, - //ffi::XK_KP_Enter => VirtualKeyCode::Kp_enter, - //ffi::XK_KP_F1 => VirtualKeyCode::Kp_f1, - //ffi::XK_KP_F2 => VirtualKeyCode::Kp_f2, - //ffi::XK_KP_F3 => VirtualKeyCode::Kp_f3, - //ffi::XK_KP_F4 => VirtualKeyCode::Kp_f4, - ffi::XK_KP_Home => VirtualKeyCode::Home, - ffi::XK_KP_Left => VirtualKeyCode::Left, - ffi::XK_KP_Up => VirtualKeyCode::Up, - ffi::XK_KP_Right => VirtualKeyCode::Right, - ffi::XK_KP_Down => VirtualKeyCode::Down, - //ffi::XK_KP_Prior => VirtualKeyCode::Kp_prior, - ffi::XK_KP_Page_Up => VirtualKeyCode::PageUp, - //ffi::XK_KP_Next => VirtualKeyCode::Kp_next, - ffi::XK_KP_Page_Down => VirtualKeyCode::PageDown, - ffi::XK_KP_End => VirtualKeyCode::End, - //ffi::XK_KP_Begin => VirtualKeyCode::Kp_begin, - ffi::XK_KP_Insert => VirtualKeyCode::Insert, - ffi::XK_KP_Delete => VirtualKeyCode::Delete, - ffi::XK_KP_Equal => VirtualKeyCode::NumpadEquals, - ffi::XK_KP_Multiply => VirtualKeyCode::NumpadMultiply, - ffi::XK_KP_Add => VirtualKeyCode::NumpadAdd, - //ffi::XK_KP_Separator => VirtualKeyCode::Kp_separator, - ffi::XK_KP_Subtract => VirtualKeyCode::NumpadSubtract, - ffi::XK_KP_Decimal => VirtualKeyCode::NumpadDecimal, - ffi::XK_KP_Divide => VirtualKeyCode::NumpadDivide, - ffi::XK_KP_0 => VirtualKeyCode::Numpad0, - ffi::XK_KP_1 => VirtualKeyCode::Numpad1, - ffi::XK_KP_2 => VirtualKeyCode::Numpad2, - ffi::XK_KP_3 => VirtualKeyCode::Numpad3, - ffi::XK_KP_4 => VirtualKeyCode::Numpad4, - ffi::XK_KP_5 => VirtualKeyCode::Numpad5, - ffi::XK_KP_6 => VirtualKeyCode::Numpad6, - ffi::XK_KP_7 => VirtualKeyCode::Numpad7, - ffi::XK_KP_8 => VirtualKeyCode::Numpad8, - ffi::XK_KP_9 => VirtualKeyCode::Numpad9, - ffi::XK_F1 => VirtualKeyCode::F1, - ffi::XK_F2 => VirtualKeyCode::F2, - ffi::XK_F3 => VirtualKeyCode::F3, - ffi::XK_F4 => VirtualKeyCode::F4, - ffi::XK_F5 => VirtualKeyCode::F5, - ffi::XK_F6 => VirtualKeyCode::F6, - ffi::XK_F7 => VirtualKeyCode::F7, - ffi::XK_F8 => VirtualKeyCode::F8, - ffi::XK_F9 => VirtualKeyCode::F9, - ffi::XK_F10 => VirtualKeyCode::F10, - ffi::XK_F11 => VirtualKeyCode::F11, - //ffi::XK_L1 => VirtualKeyCode::L1, - ffi::XK_F12 => VirtualKeyCode::F12, - //ffi::XK_L2 => VirtualKeyCode::L2, - ffi::XK_F13 => VirtualKeyCode::F13, - //ffi::XK_L3 => VirtualKeyCode::L3, - ffi::XK_F14 => VirtualKeyCode::F14, - //ffi::XK_L4 => VirtualKeyCode::L4, - ffi::XK_F15 => VirtualKeyCode::F15, - //ffi::XK_L5 => VirtualKeyCode::L5, - ffi::XK_F16 => VirtualKeyCode::F16, - //ffi::XK_L6 => VirtualKeyCode::L6, - ffi::XK_F17 => VirtualKeyCode::F17, - //ffi::XK_L7 => VirtualKeyCode::L7, - ffi::XK_F18 => VirtualKeyCode::F18, - //ffi::XK_L8 => VirtualKeyCode::L8, - ffi::XK_F19 => VirtualKeyCode::F19, - //ffi::XK_L9 => VirtualKeyCode::L9, - ffi::XK_F20 => VirtualKeyCode::F20, - //ffi::XK_L10 => VirtualKeyCode::L10, - ffi::XK_F21 => VirtualKeyCode::F21, - //ffi::XK_R1 => VirtualKeyCode::R1, - ffi::XK_F22 => VirtualKeyCode::F22, - //ffi::XK_R2 => VirtualKeyCode::R2, - ffi::XK_F23 => VirtualKeyCode::F23, - //ffi::XK_R3 => VirtualKeyCode::R3, - ffi::XK_F24 => VirtualKeyCode::F24, - //ffi::XK_R4 => VirtualKeyCode::R4, - //ffi::XK_F25 => VirtualKeyCode::F25, - //ffi::XK_R5 => VirtualKeyCode::R5, - //ffi::XK_F26 => VirtualKeyCode::F26, - //ffi::XK_R6 => VirtualKeyCode::R6, - //ffi::XK_F27 => VirtualKeyCode::F27, - //ffi::XK_R7 => VirtualKeyCode::R7, - //ffi::XK_F28 => VirtualKeyCode::F28, - //ffi::XK_R8 => VirtualKeyCode::R8, - //ffi::XK_F29 => VirtualKeyCode::F29, - //ffi::XK_R9 => VirtualKeyCode::R9, - //ffi::XK_F30 => VirtualKeyCode::F30, - //ffi::XK_R10 => VirtualKeyCode::R10, - //ffi::XK_F31 => VirtualKeyCode::F31, - //ffi::XK_R11 => VirtualKeyCode::R11, - //ffi::XK_F32 => VirtualKeyCode::F32, - //ffi::XK_R12 => VirtualKeyCode::R12, - //ffi::XK_F33 => VirtualKeyCode::F33, - //ffi::XK_R13 => VirtualKeyCode::R13, - //ffi::XK_F34 => VirtualKeyCode::F34, - //ffi::XK_R14 => VirtualKeyCode::R14, - //ffi::XK_F35 => VirtualKeyCode::F35, - //ffi::XK_R15 => VirtualKeyCode::R15, - ffi::XK_Shift_L => VirtualKeyCode::LShift, - ffi::XK_Shift_R => VirtualKeyCode::RShift, - ffi::XK_Control_L => VirtualKeyCode::LControl, - ffi::XK_Control_R => VirtualKeyCode::RControl, - //ffi::XK_Caps_Lock => VirtualKeyCode::Caps_lock, - //ffi::XK_Shift_Lock => VirtualKeyCode::Shift_lock, - //ffi::XK_Meta_L => VirtualKeyCode::Meta_l, - //ffi::XK_Meta_R => VirtualKeyCode::Meta_r, - ffi::XK_Alt_L => VirtualKeyCode::LAlt, - ffi::XK_Alt_R => VirtualKeyCode::RAlt, - //ffi::XK_Super_L => VirtualKeyCode::Super_l, - //ffi::XK_Super_R => VirtualKeyCode::Super_r, - //ffi::XK_Hyper_L => VirtualKeyCode::Hyper_l, - //ffi::XK_Hyper_R => VirtualKeyCode::Hyper_r, - ffi::XK_ISO_Left_Tab => VirtualKeyCode::Tab, - ffi::XK_space => VirtualKeyCode::Space, - //ffi::XK_exclam => VirtualKeyCode::Exclam, - //ffi::XK_quotedbl => VirtualKeyCode::Quotedbl, - //ffi::XK_numbersign => VirtualKeyCode::Numbersign, - //ffi::XK_dollar => VirtualKeyCode::Dollar, - //ffi::XK_percent => VirtualKeyCode::Percent, - //ffi::XK_ampersand => VirtualKeyCode::Ampersand, - ffi::XK_apostrophe => VirtualKeyCode::Apostrophe, - //ffi::XK_quoteright => VirtualKeyCode::Quoteright, - //ffi::XK_parenleft => VirtualKeyCode::Parenleft, - //ffi::XK_parenright => VirtualKeyCode::Parenright, - ffi::XK_asterisk => VirtualKeyCode::Asterisk, - ffi::XK_plus => VirtualKeyCode::Plus, - ffi::XK_comma => VirtualKeyCode::Comma, - ffi::XK_minus => VirtualKeyCode::Minus, - ffi::XK_period => VirtualKeyCode::Period, - ffi::XK_slash => VirtualKeyCode::Slash, - ffi::XK_0 => VirtualKeyCode::Key0, - ffi::XK_1 => VirtualKeyCode::Key1, - ffi::XK_2 => VirtualKeyCode::Key2, - ffi::XK_3 => VirtualKeyCode::Key3, - ffi::XK_4 => VirtualKeyCode::Key4, - ffi::XK_5 => VirtualKeyCode::Key5, - ffi::XK_6 => VirtualKeyCode::Key6, - ffi::XK_7 => VirtualKeyCode::Key7, - ffi::XK_8 => VirtualKeyCode::Key8, - ffi::XK_9 => VirtualKeyCode::Key9, - ffi::XK_colon => VirtualKeyCode::Colon, - ffi::XK_semicolon => VirtualKeyCode::Semicolon, - //ffi::XK_less => VirtualKeyCode::Less, - ffi::XK_equal => VirtualKeyCode::Equals, - //ffi::XK_greater => VirtualKeyCode::Greater, - //ffi::XK_question => VirtualKeyCode::Question, - ffi::XK_at => VirtualKeyCode::At, - ffi::XK_A => VirtualKeyCode::A, - ffi::XK_B => VirtualKeyCode::B, - ffi::XK_C => VirtualKeyCode::C, - ffi::XK_D => VirtualKeyCode::D, - ffi::XK_E => VirtualKeyCode::E, - ffi::XK_F => VirtualKeyCode::F, - ffi::XK_G => VirtualKeyCode::G, - ffi::XK_H => VirtualKeyCode::H, - ffi::XK_I => VirtualKeyCode::I, - ffi::XK_J => VirtualKeyCode::J, - ffi::XK_K => VirtualKeyCode::K, - ffi::XK_L => VirtualKeyCode::L, - ffi::XK_M => VirtualKeyCode::M, - ffi::XK_N => VirtualKeyCode::N, - ffi::XK_O => VirtualKeyCode::O, - ffi::XK_P => VirtualKeyCode::P, - ffi::XK_Q => VirtualKeyCode::Q, - ffi::XK_R => VirtualKeyCode::R, - ffi::XK_S => VirtualKeyCode::S, - ffi::XK_T => VirtualKeyCode::T, - ffi::XK_U => VirtualKeyCode::U, - ffi::XK_V => VirtualKeyCode::V, - ffi::XK_W => VirtualKeyCode::W, - ffi::XK_X => VirtualKeyCode::X, - ffi::XK_Y => VirtualKeyCode::Y, - ffi::XK_Z => VirtualKeyCode::Z, - ffi::XK_bracketleft => VirtualKeyCode::LBracket, - ffi::XK_backslash => VirtualKeyCode::Backslash, - ffi::XK_bracketright => VirtualKeyCode::RBracket, - //ffi::XK_asciicircum => VirtualKeyCode::Asciicircum, - //ffi::XK_underscore => VirtualKeyCode::Underscore, - ffi::XK_grave => VirtualKeyCode::Grave, - //ffi::XK_quoteleft => VirtualKeyCode::Quoteleft, - ffi::XK_a => VirtualKeyCode::A, - ffi::XK_b => VirtualKeyCode::B, - ffi::XK_c => VirtualKeyCode::C, - ffi::XK_d => VirtualKeyCode::D, - ffi::XK_e => VirtualKeyCode::E, - ffi::XK_f => VirtualKeyCode::F, - ffi::XK_g => VirtualKeyCode::G, - ffi::XK_h => VirtualKeyCode::H, - ffi::XK_i => VirtualKeyCode::I, - ffi::XK_j => VirtualKeyCode::J, - ffi::XK_k => VirtualKeyCode::K, - ffi::XK_l => VirtualKeyCode::L, - ffi::XK_m => VirtualKeyCode::M, - ffi::XK_n => VirtualKeyCode::N, - ffi::XK_o => VirtualKeyCode::O, - ffi::XK_p => VirtualKeyCode::P, - ffi::XK_q => VirtualKeyCode::Q, - ffi::XK_r => VirtualKeyCode::R, - ffi::XK_s => VirtualKeyCode::S, - ffi::XK_t => VirtualKeyCode::T, - ffi::XK_u => VirtualKeyCode::U, - ffi::XK_v => VirtualKeyCode::V, - ffi::XK_w => VirtualKeyCode::W, - ffi::XK_x => VirtualKeyCode::X, - ffi::XK_y => VirtualKeyCode::Y, - ffi::XK_z => VirtualKeyCode::Z, - //ffi::XK_braceleft => VirtualKeyCode::Braceleft, - //ffi::XK_bar => VirtualKeyCode::Bar, - //ffi::XK_braceright => VirtualKeyCode::Braceright, - //ffi::XK_asciitilde => VirtualKeyCode::Asciitilde, - //ffi::XK_nobreakspace => VirtualKeyCode::Nobreakspace, - //ffi::XK_exclamdown => VirtualKeyCode::Exclamdown, - //ffi::XK_cent => VirtualKeyCode::Cent, - //ffi::XK_sterling => VirtualKeyCode::Sterling, - //ffi::XK_currency => VirtualKeyCode::Currency, - //ffi::XK_yen => VirtualKeyCode::Yen, - //ffi::XK_brokenbar => VirtualKeyCode::Brokenbar, - //ffi::XK_section => VirtualKeyCode::Section, - //ffi::XK_diaeresis => VirtualKeyCode::Diaeresis, - //ffi::XK_copyright => VirtualKeyCode::Copyright, - //ffi::XK_ordfeminine => VirtualKeyCode::Ordfeminine, - //ffi::XK_guillemotleft => VirtualKeyCode::Guillemotleft, - //ffi::XK_notsign => VirtualKeyCode::Notsign, - //ffi::XK_hyphen => VirtualKeyCode::Hyphen, - //ffi::XK_registered => VirtualKeyCode::Registered, - //ffi::XK_macron => VirtualKeyCode::Macron, - //ffi::XK_degree => VirtualKeyCode::Degree, - //ffi::XK_plusminus => VirtualKeyCode::Plusminus, - //ffi::XK_twosuperior => VirtualKeyCode::Twosuperior, - //ffi::XK_threesuperior => VirtualKeyCode::Threesuperior, - //ffi::XK_acute => VirtualKeyCode::Acute, - //ffi::XK_mu => VirtualKeyCode::Mu, - //ffi::XK_paragraph => VirtualKeyCode::Paragraph, - //ffi::XK_periodcentered => VirtualKeyCode::Periodcentered, - //ffi::XK_cedilla => VirtualKeyCode::Cedilla, - //ffi::XK_onesuperior => VirtualKeyCode::Onesuperior, - //ffi::XK_masculine => VirtualKeyCode::Masculine, - //ffi::XK_guillemotright => VirtualKeyCode::Guillemotright, - //ffi::XK_onequarter => VirtualKeyCode::Onequarter, - //ffi::XK_onehalf => VirtualKeyCode::Onehalf, - //ffi::XK_threequarters => VirtualKeyCode::Threequarters, - //ffi::XK_questiondown => VirtualKeyCode::Questiondown, - //ffi::XK_Agrave => VirtualKeyCode::Agrave, - //ffi::XK_Aacute => VirtualKeyCode::Aacute, - //ffi::XK_Acircumflex => VirtualKeyCode::Acircumflex, - //ffi::XK_Atilde => VirtualKeyCode::Atilde, - //ffi::XK_Adiaeresis => VirtualKeyCode::Adiaeresis, - //ffi::XK_Aring => VirtualKeyCode::Aring, - //ffi::XK_AE => VirtualKeyCode::Ae, - //ffi::XK_Ccedilla => VirtualKeyCode::Ccedilla, - //ffi::XK_Egrave => VirtualKeyCode::Egrave, - //ffi::XK_Eacute => VirtualKeyCode::Eacute, - //ffi::XK_Ecircumflex => VirtualKeyCode::Ecircumflex, - //ffi::XK_Ediaeresis => VirtualKeyCode::Ediaeresis, - //ffi::XK_Igrave => VirtualKeyCode::Igrave, - //ffi::XK_Iacute => VirtualKeyCode::Iacute, - //ffi::XK_Icircumflex => VirtualKeyCode::Icircumflex, - //ffi::XK_Idiaeresis => VirtualKeyCode::Idiaeresis, - //ffi::XK_ETH => VirtualKeyCode::Eth, - //ffi::XK_Eth => VirtualKeyCode::Eth, - //ffi::XK_Ntilde => VirtualKeyCode::Ntilde, - //ffi::XK_Ograve => VirtualKeyCode::Ograve, - //ffi::XK_Oacute => VirtualKeyCode::Oacute, - //ffi::XK_Ocircumflex => VirtualKeyCode::Ocircumflex, - //ffi::XK_Otilde => VirtualKeyCode::Otilde, - //ffi::XK_Odiaeresis => VirtualKeyCode::Odiaeresis, - //ffi::XK_multiply => VirtualKeyCode::Multiply, - //ffi::XK_Ooblique => VirtualKeyCode::Ooblique, - //ffi::XK_Ugrave => VirtualKeyCode::Ugrave, - //ffi::XK_Uacute => VirtualKeyCode::Uacute, - //ffi::XK_Ucircumflex => VirtualKeyCode::Ucircumflex, - //ffi::XK_Udiaeresis => VirtualKeyCode::Udiaeresis, - //ffi::XK_Yacute => VirtualKeyCode::Yacute, - //ffi::XK_THORN => VirtualKeyCode::Thorn, - //ffi::XK_Thorn => VirtualKeyCode::Thorn, - //ffi::XK_ssharp => VirtualKeyCode::Ssharp, - //ffi::XK_agrave => VirtualKeyCode::Agrave, - //ffi::XK_aacute => VirtualKeyCode::Aacute, - //ffi::XK_acircumflex => VirtualKeyCode::Acircumflex, - //ffi::XK_atilde => VirtualKeyCode::Atilde, - //ffi::XK_adiaeresis => VirtualKeyCode::Adiaeresis, - //ffi::XK_aring => VirtualKeyCode::Aring, - //ffi::XK_ae => VirtualKeyCode::Ae, - //ffi::XK_ccedilla => VirtualKeyCode::Ccedilla, - //ffi::XK_egrave => VirtualKeyCode::Egrave, - //ffi::XK_eacute => VirtualKeyCode::Eacute, - //ffi::XK_ecircumflex => VirtualKeyCode::Ecircumflex, - //ffi::XK_ediaeresis => VirtualKeyCode::Ediaeresis, - //ffi::XK_igrave => VirtualKeyCode::Igrave, - //ffi::XK_iacute => VirtualKeyCode::Iacute, - //ffi::XK_icircumflex => VirtualKeyCode::Icircumflex, - //ffi::XK_idiaeresis => VirtualKeyCode::Idiaeresis, - //ffi::XK_eth => VirtualKeyCode::Eth, - //ffi::XK_ntilde => VirtualKeyCode::Ntilde, - //ffi::XK_ograve => VirtualKeyCode::Ograve, - //ffi::XK_oacute => VirtualKeyCode::Oacute, - //ffi::XK_ocircumflex => VirtualKeyCode::Ocircumflex, - //ffi::XK_otilde => VirtualKeyCode::Otilde, - //ffi::XK_odiaeresis => VirtualKeyCode::Odiaeresis, - //ffi::XK_division => VirtualKeyCode::Division, - //ffi::XK_oslash => VirtualKeyCode::Oslash, - //ffi::XK_ugrave => VirtualKeyCode::Ugrave, - //ffi::XK_uacute => VirtualKeyCode::Uacute, - //ffi::XK_ucircumflex => VirtualKeyCode::Ucircumflex, - //ffi::XK_udiaeresis => VirtualKeyCode::Udiaeresis, - //ffi::XK_yacute => VirtualKeyCode::Yacute, - //ffi::XK_thorn => VirtualKeyCode::Thorn, - //ffi::XK_ydiaeresis => VirtualKeyCode::Ydiaeresis, - //ffi::XK_Aogonek => VirtualKeyCode::Aogonek, - //ffi::XK_breve => VirtualKeyCode::Breve, - //ffi::XK_Lstroke => VirtualKeyCode::Lstroke, - //ffi::XK_Lcaron => VirtualKeyCode::Lcaron, - //ffi::XK_Sacute => VirtualKeyCode::Sacute, - //ffi::XK_Scaron => VirtualKeyCode::Scaron, - //ffi::XK_Scedilla => VirtualKeyCode::Scedilla, - //ffi::XK_Tcaron => VirtualKeyCode::Tcaron, - //ffi::XK_Zacute => VirtualKeyCode::Zacute, - //ffi::XK_Zcaron => VirtualKeyCode::Zcaron, - //ffi::XK_Zabovedot => VirtualKeyCode::Zabovedot, - //ffi::XK_aogonek => VirtualKeyCode::Aogonek, - //ffi::XK_ogonek => VirtualKeyCode::Ogonek, - //ffi::XK_lstroke => VirtualKeyCode::Lstroke, - //ffi::XK_lcaron => VirtualKeyCode::Lcaron, - //ffi::XK_sacute => VirtualKeyCode::Sacute, - //ffi::XK_caron => VirtualKeyCode::Caron, - //ffi::XK_scaron => VirtualKeyCode::Scaron, - //ffi::XK_scedilla => VirtualKeyCode::Scedilla, - //ffi::XK_tcaron => VirtualKeyCode::Tcaron, - //ffi::XK_zacute => VirtualKeyCode::Zacute, - //ffi::XK_doubleacute => VirtualKeyCode::Doubleacute, - //ffi::XK_zcaron => VirtualKeyCode::Zcaron, - //ffi::XK_zabovedot => VirtualKeyCode::Zabovedot, - //ffi::XK_Racute => VirtualKeyCode::Racute, - //ffi::XK_Abreve => VirtualKeyCode::Abreve, - //ffi::XK_Lacute => VirtualKeyCode::Lacute, - //ffi::XK_Cacute => VirtualKeyCode::Cacute, - //ffi::XK_Ccaron => VirtualKeyCode::Ccaron, - //ffi::XK_Eogonek => VirtualKeyCode::Eogonek, - //ffi::XK_Ecaron => VirtualKeyCode::Ecaron, - //ffi::XK_Dcaron => VirtualKeyCode::Dcaron, - //ffi::XK_Dstroke => VirtualKeyCode::Dstroke, - //ffi::XK_Nacute => VirtualKeyCode::Nacute, - //ffi::XK_Ncaron => VirtualKeyCode::Ncaron, - //ffi::XK_Odoubleacute => VirtualKeyCode::Odoubleacute, - //ffi::XK_Rcaron => VirtualKeyCode::Rcaron, - //ffi::XK_Uring => VirtualKeyCode::Uring, - //ffi::XK_Udoubleacute => VirtualKeyCode::Udoubleacute, - //ffi::XK_Tcedilla => VirtualKeyCode::Tcedilla, - //ffi::XK_racute => VirtualKeyCode::Racute, - //ffi::XK_abreve => VirtualKeyCode::Abreve, - //ffi::XK_lacute => VirtualKeyCode::Lacute, - //ffi::XK_cacute => VirtualKeyCode::Cacute, - //ffi::XK_ccaron => VirtualKeyCode::Ccaron, - //ffi::XK_eogonek => VirtualKeyCode::Eogonek, - //ffi::XK_ecaron => VirtualKeyCode::Ecaron, - //ffi::XK_dcaron => VirtualKeyCode::Dcaron, - //ffi::XK_dstroke => VirtualKeyCode::Dstroke, - //ffi::XK_nacute => VirtualKeyCode::Nacute, - //ffi::XK_ncaron => VirtualKeyCode::Ncaron, - //ffi::XK_odoubleacute => VirtualKeyCode::Odoubleacute, - //ffi::XK_udoubleacute => VirtualKeyCode::Udoubleacute, - //ffi::XK_rcaron => VirtualKeyCode::Rcaron, - //ffi::XK_uring => VirtualKeyCode::Uring, - //ffi::XK_tcedilla => VirtualKeyCode::Tcedilla, - //ffi::XK_abovedot => VirtualKeyCode::Abovedot, - //ffi::XK_Hstroke => VirtualKeyCode::Hstroke, - //ffi::XK_Hcircumflex => VirtualKeyCode::Hcircumflex, - //ffi::XK_Iabovedot => VirtualKeyCode::Iabovedot, - //ffi::XK_Gbreve => VirtualKeyCode::Gbreve, - //ffi::XK_Jcircumflex => VirtualKeyCode::Jcircumflex, - //ffi::XK_hstroke => VirtualKeyCode::Hstroke, - //ffi::XK_hcircumflex => VirtualKeyCode::Hcircumflex, - //ffi::XK_idotless => VirtualKeyCode::Idotless, - //ffi::XK_gbreve => VirtualKeyCode::Gbreve, - //ffi::XK_jcircumflex => VirtualKeyCode::Jcircumflex, - //ffi::XK_Cabovedot => VirtualKeyCode::Cabovedot, - //ffi::XK_Ccircumflex => VirtualKeyCode::Ccircumflex, - //ffi::XK_Gabovedot => VirtualKeyCode::Gabovedot, - //ffi::XK_Gcircumflex => VirtualKeyCode::Gcircumflex, - //ffi::XK_Ubreve => VirtualKeyCode::Ubreve, - //ffi::XK_Scircumflex => VirtualKeyCode::Scircumflex, - //ffi::XK_cabovedot => VirtualKeyCode::Cabovedot, - //ffi::XK_ccircumflex => VirtualKeyCode::Ccircumflex, - //ffi::XK_gabovedot => VirtualKeyCode::Gabovedot, - //ffi::XK_gcircumflex => VirtualKeyCode::Gcircumflex, - //ffi::XK_ubreve => VirtualKeyCode::Ubreve, - //ffi::XK_scircumflex => VirtualKeyCode::Scircumflex, - //ffi::XK_kra => VirtualKeyCode::Kra, - //ffi::XK_kappa => VirtualKeyCode::Kappa, - //ffi::XK_Rcedilla => VirtualKeyCode::Rcedilla, - //ffi::XK_Itilde => VirtualKeyCode::Itilde, - //ffi::XK_Lcedilla => VirtualKeyCode::Lcedilla, - //ffi::XK_Emacron => VirtualKeyCode::Emacron, - //ffi::XK_Gcedilla => VirtualKeyCode::Gcedilla, - //ffi::XK_Tslash => VirtualKeyCode::Tslash, - //ffi::XK_rcedilla => VirtualKeyCode::Rcedilla, - //ffi::XK_itilde => VirtualKeyCode::Itilde, - //ffi::XK_lcedilla => VirtualKeyCode::Lcedilla, - //ffi::XK_emacron => VirtualKeyCode::Emacron, - //ffi::XK_gcedilla => VirtualKeyCode::Gcedilla, - //ffi::XK_tslash => VirtualKeyCode::Tslash, - //ffi::XK_ENG => VirtualKeyCode::Eng, - //ffi::XK_eng => VirtualKeyCode::Eng, - //ffi::XK_Amacron => VirtualKeyCode::Amacron, - //ffi::XK_Iogonek => VirtualKeyCode::Iogonek, - //ffi::XK_Eabovedot => VirtualKeyCode::Eabovedot, - //ffi::XK_Imacron => VirtualKeyCode::Imacron, - //ffi::XK_Ncedilla => VirtualKeyCode::Ncedilla, - //ffi::XK_Omacron => VirtualKeyCode::Omacron, - //ffi::XK_Kcedilla => VirtualKeyCode::Kcedilla, - //ffi::XK_Uogonek => VirtualKeyCode::Uogonek, - //ffi::XK_Utilde => VirtualKeyCode::Utilde, - //ffi::XK_Umacron => VirtualKeyCode::Umacron, - //ffi::XK_amacron => VirtualKeyCode::Amacron, - //ffi::XK_iogonek => VirtualKeyCode::Iogonek, - //ffi::XK_eabovedot => VirtualKeyCode::Eabovedot, - //ffi::XK_imacron => VirtualKeyCode::Imacron, - //ffi::XK_ncedilla => VirtualKeyCode::Ncedilla, - //ffi::XK_omacron => VirtualKeyCode::Omacron, - //ffi::XK_kcedilla => VirtualKeyCode::Kcedilla, - //ffi::XK_uogonek => VirtualKeyCode::Uogonek, - //ffi::XK_utilde => VirtualKeyCode::Utilde, - //ffi::XK_umacron => VirtualKeyCode::Umacron, - //ffi::XK_overline => VirtualKeyCode::Overline, - //ffi::XK_kana_fullstop => VirtualKeyCode::Kana_fullstop, - //ffi::XK_kana_openingbracket => VirtualKeyCode::Kana_openingbracket, - //ffi::XK_kana_closingbracket => VirtualKeyCode::Kana_closingbracket, - //ffi::XK_kana_comma => VirtualKeyCode::Kana_comma, - //ffi::XK_kana_conjunctive => VirtualKeyCode::Kana_conjunctive, - //ffi::XK_kana_middledot => VirtualKeyCode::Kana_middledot, - //ffi::XK_kana_WO => VirtualKeyCode::Kana_wo, - //ffi::XK_kana_a => VirtualKeyCode::Kana_a, - //ffi::XK_kana_i => VirtualKeyCode::Kana_i, - //ffi::XK_kana_u => VirtualKeyCode::Kana_u, - //ffi::XK_kana_e => VirtualKeyCode::Kana_e, - //ffi::XK_kana_o => VirtualKeyCode::Kana_o, - //ffi::XK_kana_ya => VirtualKeyCode::Kana_ya, - //ffi::XK_kana_yu => VirtualKeyCode::Kana_yu, - //ffi::XK_kana_yo => VirtualKeyCode::Kana_yo, - //ffi::XK_kana_tsu => VirtualKeyCode::Kana_tsu, - //ffi::XK_kana_tu => VirtualKeyCode::Kana_tu, - //ffi::XK_prolongedsound => VirtualKeyCode::Prolongedsound, - //ffi::XK_kana_A => VirtualKeyCode::Kana_a, - //ffi::XK_kana_I => VirtualKeyCode::Kana_i, - //ffi::XK_kana_U => VirtualKeyCode::Kana_u, - //ffi::XK_kana_E => VirtualKeyCode::Kana_e, - //ffi::XK_kana_O => VirtualKeyCode::Kana_o, - //ffi::XK_kana_KA => VirtualKeyCode::Kana_ka, - //ffi::XK_kana_KI => VirtualKeyCode::Kana_ki, - //ffi::XK_kana_KU => VirtualKeyCode::Kana_ku, - //ffi::XK_kana_KE => VirtualKeyCode::Kana_ke, - //ffi::XK_kana_KO => VirtualKeyCode::Kana_ko, - //ffi::XK_kana_SA => VirtualKeyCode::Kana_sa, - //ffi::XK_kana_SHI => VirtualKeyCode::Kana_shi, - //ffi::XK_kana_SU => VirtualKeyCode::Kana_su, - //ffi::XK_kana_SE => VirtualKeyCode::Kana_se, - //ffi::XK_kana_SO => VirtualKeyCode::Kana_so, - //ffi::XK_kana_TA => VirtualKeyCode::Kana_ta, - //ffi::XK_kana_CHI => VirtualKeyCode::Kana_chi, - //ffi::XK_kana_TI => VirtualKeyCode::Kana_ti, - //ffi::XK_kana_TSU => VirtualKeyCode::Kana_tsu, - //ffi::XK_kana_TU => VirtualKeyCode::Kana_tu, - //ffi::XK_kana_TE => VirtualKeyCode::Kana_te, - //ffi::XK_kana_TO => VirtualKeyCode::Kana_to, - //ffi::XK_kana_NA => VirtualKeyCode::Kana_na, - //ffi::XK_kana_NI => VirtualKeyCode::Kana_ni, - //ffi::XK_kana_NU => VirtualKeyCode::Kana_nu, - //ffi::XK_kana_NE => VirtualKeyCode::Kana_ne, - //ffi::XK_kana_NO => VirtualKeyCode::Kana_no, - //ffi::XK_kana_HA => VirtualKeyCode::Kana_ha, - //ffi::XK_kana_HI => VirtualKeyCode::Kana_hi, - //ffi::XK_kana_FU => VirtualKeyCode::Kana_fu, - //ffi::XK_kana_HU => VirtualKeyCode::Kana_hu, - //ffi::XK_kana_HE => VirtualKeyCode::Kana_he, - //ffi::XK_kana_HO => VirtualKeyCode::Kana_ho, - //ffi::XK_kana_MA => VirtualKeyCode::Kana_ma, - //ffi::XK_kana_MI => VirtualKeyCode::Kana_mi, - //ffi::XK_kana_MU => VirtualKeyCode::Kana_mu, - //ffi::XK_kana_ME => VirtualKeyCode::Kana_me, - //ffi::XK_kana_MO => VirtualKeyCode::Kana_mo, - //ffi::XK_kana_YA => VirtualKeyCode::Kana_ya, - //ffi::XK_kana_YU => VirtualKeyCode::Kana_yu, - //ffi::XK_kana_YO => VirtualKeyCode::Kana_yo, - //ffi::XK_kana_RA => VirtualKeyCode::Kana_ra, - //ffi::XK_kana_RI => VirtualKeyCode::Kana_ri, - //ffi::XK_kana_RU => VirtualKeyCode::Kana_ru, - //ffi::XK_kana_RE => VirtualKeyCode::Kana_re, - //ffi::XK_kana_RO => VirtualKeyCode::Kana_ro, - //ffi::XK_kana_WA => VirtualKeyCode::Kana_wa, - //ffi::XK_kana_N => VirtualKeyCode::Kana_n, - //ffi::XK_voicedsound => VirtualKeyCode::Voicedsound, - //ffi::XK_semivoicedsound => VirtualKeyCode::Semivoicedsound, - //ffi::XK_kana_switch => VirtualKeyCode::Kana_switch, - //ffi::XK_Arabic_comma => VirtualKeyCode::Arabic_comma, - //ffi::XK_Arabic_semicolon => VirtualKeyCode::Arabic_semicolon, - //ffi::XK_Arabic_question_mark => VirtualKeyCode::Arabic_question_mark, - //ffi::XK_Arabic_hamza => VirtualKeyCode::Arabic_hamza, - //ffi::XK_Arabic_maddaonalef => VirtualKeyCode::Arabic_maddaonalef, - //ffi::XK_Arabic_hamzaonalef => VirtualKeyCode::Arabic_hamzaonalef, - //ffi::XK_Arabic_hamzaonwaw => VirtualKeyCode::Arabic_hamzaonwaw, - //ffi::XK_Arabic_hamzaunderalef => VirtualKeyCode::Arabic_hamzaunderalef, - //ffi::XK_Arabic_hamzaonyeh => VirtualKeyCode::Arabic_hamzaonyeh, - //ffi::XK_Arabic_alef => VirtualKeyCode::Arabic_alef, - //ffi::XK_Arabic_beh => VirtualKeyCode::Arabic_beh, - //ffi::XK_Arabic_tehmarbuta => VirtualKeyCode::Arabic_tehmarbuta, - //ffi::XK_Arabic_teh => VirtualKeyCode::Arabic_teh, - //ffi::XK_Arabic_theh => VirtualKeyCode::Arabic_theh, - //ffi::XK_Arabic_jeem => VirtualKeyCode::Arabic_jeem, - //ffi::XK_Arabic_hah => VirtualKeyCode::Arabic_hah, - //ffi::XK_Arabic_khah => VirtualKeyCode::Arabic_khah, - //ffi::XK_Arabic_dal => VirtualKeyCode::Arabic_dal, - //ffi::XK_Arabic_thal => VirtualKeyCode::Arabic_thal, - //ffi::XK_Arabic_ra => VirtualKeyCode::Arabic_ra, - //ffi::XK_Arabic_zain => VirtualKeyCode::Arabic_zain, - //ffi::XK_Arabic_seen => VirtualKeyCode::Arabic_seen, - //ffi::XK_Arabic_sheen => VirtualKeyCode::Arabic_sheen, - //ffi::XK_Arabic_sad => VirtualKeyCode::Arabic_sad, - //ffi::XK_Arabic_dad => VirtualKeyCode::Arabic_dad, - //ffi::XK_Arabic_tah => VirtualKeyCode::Arabic_tah, - //ffi::XK_Arabic_zah => VirtualKeyCode::Arabic_zah, - //ffi::XK_Arabic_ain => VirtualKeyCode::Arabic_ain, - //ffi::XK_Arabic_ghain => VirtualKeyCode::Arabic_ghain, - //ffi::XK_Arabic_tatweel => VirtualKeyCode::Arabic_tatweel, - //ffi::XK_Arabic_feh => VirtualKeyCode::Arabic_feh, - //ffi::XK_Arabic_qaf => VirtualKeyCode::Arabic_qaf, - //ffi::XK_Arabic_kaf => VirtualKeyCode::Arabic_kaf, - //ffi::XK_Arabic_lam => VirtualKeyCode::Arabic_lam, - //ffi::XK_Arabic_meem => VirtualKeyCode::Arabic_meem, - //ffi::XK_Arabic_noon => VirtualKeyCode::Arabic_noon, - //ffi::XK_Arabic_ha => VirtualKeyCode::Arabic_ha, - //ffi::XK_Arabic_heh => VirtualKeyCode::Arabic_heh, - //ffi::XK_Arabic_waw => VirtualKeyCode::Arabic_waw, - //ffi::XK_Arabic_alefmaksura => VirtualKeyCode::Arabic_alefmaksura, - //ffi::XK_Arabic_yeh => VirtualKeyCode::Arabic_yeh, - //ffi::XK_Arabic_fathatan => VirtualKeyCode::Arabic_fathatan, - //ffi::XK_Arabic_dammatan => VirtualKeyCode::Arabic_dammatan, - //ffi::XK_Arabic_kasratan => VirtualKeyCode::Arabic_kasratan, - //ffi::XK_Arabic_fatha => VirtualKeyCode::Arabic_fatha, - //ffi::XK_Arabic_damma => VirtualKeyCode::Arabic_damma, - //ffi::XK_Arabic_kasra => VirtualKeyCode::Arabic_kasra, - //ffi::XK_Arabic_shadda => VirtualKeyCode::Arabic_shadda, - //ffi::XK_Arabic_sukun => VirtualKeyCode::Arabic_sukun, - //ffi::XK_Arabic_switch => VirtualKeyCode::Arabic_switch, - //ffi::XK_Serbian_dje => VirtualKeyCode::Serbian_dje, - //ffi::XK_Macedonia_gje => VirtualKeyCode::Macedonia_gje, - //ffi::XK_Cyrillic_io => VirtualKeyCode::Cyrillic_io, - //ffi::XK_Ukrainian_ie => VirtualKeyCode::Ukrainian_ie, - //ffi::XK_Ukranian_je => VirtualKeyCode::Ukranian_je, - //ffi::XK_Macedonia_dse => VirtualKeyCode::Macedonia_dse, - //ffi::XK_Ukrainian_i => VirtualKeyCode::Ukrainian_i, - //ffi::XK_Ukranian_i => VirtualKeyCode::Ukranian_i, - //ffi::XK_Ukrainian_yi => VirtualKeyCode::Ukrainian_yi, - //ffi::XK_Ukranian_yi => VirtualKeyCode::Ukranian_yi, - //ffi::XK_Cyrillic_je => VirtualKeyCode::Cyrillic_je, - //ffi::XK_Serbian_je => VirtualKeyCode::Serbian_je, - //ffi::XK_Cyrillic_lje => VirtualKeyCode::Cyrillic_lje, - //ffi::XK_Serbian_lje => VirtualKeyCode::Serbian_lje, - //ffi::XK_Cyrillic_nje => VirtualKeyCode::Cyrillic_nje, - //ffi::XK_Serbian_nje => VirtualKeyCode::Serbian_nje, - //ffi::XK_Serbian_tshe => VirtualKeyCode::Serbian_tshe, - //ffi::XK_Macedonia_kje => VirtualKeyCode::Macedonia_kje, - //ffi::XK_Byelorussian_shortu => VirtualKeyCode::Byelorussian_shortu, - //ffi::XK_Cyrillic_dzhe => VirtualKeyCode::Cyrillic_dzhe, - //ffi::XK_Serbian_dze => VirtualKeyCode::Serbian_dze, - //ffi::XK_numerosign => VirtualKeyCode::Numerosign, - //ffi::XK_Serbian_DJE => VirtualKeyCode::Serbian_dje, - //ffi::XK_Macedonia_GJE => VirtualKeyCode::Macedonia_gje, - //ffi::XK_Cyrillic_IO => VirtualKeyCode::Cyrillic_io, - //ffi::XK_Ukrainian_IE => VirtualKeyCode::Ukrainian_ie, - //ffi::XK_Ukranian_JE => VirtualKeyCode::Ukranian_je, - //ffi::XK_Macedonia_DSE => VirtualKeyCode::Macedonia_dse, - //ffi::XK_Ukrainian_I => VirtualKeyCode::Ukrainian_i, - //ffi::XK_Ukranian_I => VirtualKeyCode::Ukranian_i, - //ffi::XK_Ukrainian_YI => VirtualKeyCode::Ukrainian_yi, - //ffi::XK_Ukranian_YI => VirtualKeyCode::Ukranian_yi, - //ffi::XK_Cyrillic_JE => VirtualKeyCode::Cyrillic_je, - //ffi::XK_Serbian_JE => VirtualKeyCode::Serbian_je, - //ffi::XK_Cyrillic_LJE => VirtualKeyCode::Cyrillic_lje, - //ffi::XK_Serbian_LJE => VirtualKeyCode::Serbian_lje, - //ffi::XK_Cyrillic_NJE => VirtualKeyCode::Cyrillic_nje, - //ffi::XK_Serbian_NJE => VirtualKeyCode::Serbian_nje, - //ffi::XK_Serbian_TSHE => VirtualKeyCode::Serbian_tshe, - //ffi::XK_Macedonia_KJE => VirtualKeyCode::Macedonia_kje, - //ffi::XK_Byelorussian_SHORTU => VirtualKeyCode::Byelorussian_shortu, - //ffi::XK_Cyrillic_DZHE => VirtualKeyCode::Cyrillic_dzhe, - //ffi::XK_Serbian_DZE => VirtualKeyCode::Serbian_dze, - //ffi::XK_Cyrillic_yu => VirtualKeyCode::Cyrillic_yu, - //ffi::XK_Cyrillic_a => VirtualKeyCode::Cyrillic_a, - //ffi::XK_Cyrillic_be => VirtualKeyCode::Cyrillic_be, - //ffi::XK_Cyrillic_tse => VirtualKeyCode::Cyrillic_tse, - //ffi::XK_Cyrillic_de => VirtualKeyCode::Cyrillic_de, - //ffi::XK_Cyrillic_ie => VirtualKeyCode::Cyrillic_ie, - //ffi::XK_Cyrillic_ef => VirtualKeyCode::Cyrillic_ef, - //ffi::XK_Cyrillic_ghe => VirtualKeyCode::Cyrillic_ghe, - //ffi::XK_Cyrillic_ha => VirtualKeyCode::Cyrillic_ha, - //ffi::XK_Cyrillic_i => VirtualKeyCode::Cyrillic_i, - //ffi::XK_Cyrillic_shorti => VirtualKeyCode::Cyrillic_shorti, - //ffi::XK_Cyrillic_ka => VirtualKeyCode::Cyrillic_ka, - //ffi::XK_Cyrillic_el => VirtualKeyCode::Cyrillic_el, - //ffi::XK_Cyrillic_em => VirtualKeyCode::Cyrillic_em, - //ffi::XK_Cyrillic_en => VirtualKeyCode::Cyrillic_en, - //ffi::XK_Cyrillic_o => VirtualKeyCode::Cyrillic_o, - //ffi::XK_Cyrillic_pe => VirtualKeyCode::Cyrillic_pe, - //ffi::XK_Cyrillic_ya => VirtualKeyCode::Cyrillic_ya, - //ffi::XK_Cyrillic_er => VirtualKeyCode::Cyrillic_er, - //ffi::XK_Cyrillic_es => VirtualKeyCode::Cyrillic_es, - //ffi::XK_Cyrillic_te => VirtualKeyCode::Cyrillic_te, - //ffi::XK_Cyrillic_u => VirtualKeyCode::Cyrillic_u, - //ffi::XK_Cyrillic_zhe => VirtualKeyCode::Cyrillic_zhe, - //ffi::XK_Cyrillic_ve => VirtualKeyCode::Cyrillic_ve, - //ffi::XK_Cyrillic_softsign => VirtualKeyCode::Cyrillic_softsign, - //ffi::XK_Cyrillic_yeru => VirtualKeyCode::Cyrillic_yeru, - //ffi::XK_Cyrillic_ze => VirtualKeyCode::Cyrillic_ze, - //ffi::XK_Cyrillic_sha => VirtualKeyCode::Cyrillic_sha, - //ffi::XK_Cyrillic_e => VirtualKeyCode::Cyrillic_e, - //ffi::XK_Cyrillic_shcha => VirtualKeyCode::Cyrillic_shcha, - //ffi::XK_Cyrillic_che => VirtualKeyCode::Cyrillic_che, - //ffi::XK_Cyrillic_hardsign => VirtualKeyCode::Cyrillic_hardsign, - //ffi::XK_Cyrillic_YU => VirtualKeyCode::Cyrillic_yu, - //ffi::XK_Cyrillic_A => VirtualKeyCode::Cyrillic_a, - //ffi::XK_Cyrillic_BE => VirtualKeyCode::Cyrillic_be, - //ffi::XK_Cyrillic_TSE => VirtualKeyCode::Cyrillic_tse, - //ffi::XK_Cyrillic_DE => VirtualKeyCode::Cyrillic_de, - //ffi::XK_Cyrillic_IE => VirtualKeyCode::Cyrillic_ie, - //ffi::XK_Cyrillic_EF => VirtualKeyCode::Cyrillic_ef, - //ffi::XK_Cyrillic_GHE => VirtualKeyCode::Cyrillic_ghe, - //ffi::XK_Cyrillic_HA => VirtualKeyCode::Cyrillic_ha, - //ffi::XK_Cyrillic_I => VirtualKeyCode::Cyrillic_i, - //ffi::XK_Cyrillic_SHORTI => VirtualKeyCode::Cyrillic_shorti, - //ffi::XK_Cyrillic_KA => VirtualKeyCode::Cyrillic_ka, - //ffi::XK_Cyrillic_EL => VirtualKeyCode::Cyrillic_el, - //ffi::XK_Cyrillic_EM => VirtualKeyCode::Cyrillic_em, - //ffi::XK_Cyrillic_EN => VirtualKeyCode::Cyrillic_en, - //ffi::XK_Cyrillic_O => VirtualKeyCode::Cyrillic_o, - //ffi::XK_Cyrillic_PE => VirtualKeyCode::Cyrillic_pe, - //ffi::XK_Cyrillic_YA => VirtualKeyCode::Cyrillic_ya, - //ffi::XK_Cyrillic_ER => VirtualKeyCode::Cyrillic_er, - //ffi::XK_Cyrillic_ES => VirtualKeyCode::Cyrillic_es, - //ffi::XK_Cyrillic_TE => VirtualKeyCode::Cyrillic_te, - //ffi::XK_Cyrillic_U => VirtualKeyCode::Cyrillic_u, - //ffi::XK_Cyrillic_ZHE => VirtualKeyCode::Cyrillic_zhe, - //ffi::XK_Cyrillic_VE => VirtualKeyCode::Cyrillic_ve, - //ffi::XK_Cyrillic_SOFTSIGN => VirtualKeyCode::Cyrillic_softsign, - //ffi::XK_Cyrillic_YERU => VirtualKeyCode::Cyrillic_yeru, - //ffi::XK_Cyrillic_ZE => VirtualKeyCode::Cyrillic_ze, - //ffi::XK_Cyrillic_SHA => VirtualKeyCode::Cyrillic_sha, - //ffi::XK_Cyrillic_E => VirtualKeyCode::Cyrillic_e, - //ffi::XK_Cyrillic_SHCHA => VirtualKeyCode::Cyrillic_shcha, - //ffi::XK_Cyrillic_CHE => VirtualKeyCode::Cyrillic_che, - //ffi::XK_Cyrillic_HARDSIGN => VirtualKeyCode::Cyrillic_hardsign, - //ffi::XK_Greek_ALPHAaccent => VirtualKeyCode::Greek_alphaaccent, - //ffi::XK_Greek_EPSILONaccent => VirtualKeyCode::Greek_epsilonaccent, - //ffi::XK_Greek_ETAaccent => VirtualKeyCode::Greek_etaaccent, - //ffi::XK_Greek_IOTAaccent => VirtualKeyCode::Greek_iotaaccent, - //ffi::XK_Greek_IOTAdiaeresis => VirtualKeyCode::Greek_iotadiaeresis, - //ffi::XK_Greek_OMICRONaccent => VirtualKeyCode::Greek_omicronaccent, - //ffi::XK_Greek_UPSILONaccent => VirtualKeyCode::Greek_upsilonaccent, - //ffi::XK_Greek_UPSILONdieresis => VirtualKeyCode::Greek_upsilondieresis, - //ffi::XK_Greek_OMEGAaccent => VirtualKeyCode::Greek_omegaaccent, - //ffi::XK_Greek_accentdieresis => VirtualKeyCode::Greek_accentdieresis, - //ffi::XK_Greek_horizbar => VirtualKeyCode::Greek_horizbar, - //ffi::XK_Greek_alphaaccent => VirtualKeyCode::Greek_alphaaccent, - //ffi::XK_Greek_epsilonaccent => VirtualKeyCode::Greek_epsilonaccent, - //ffi::XK_Greek_etaaccent => VirtualKeyCode::Greek_etaaccent, - //ffi::XK_Greek_iotaaccent => VirtualKeyCode::Greek_iotaaccent, - //ffi::XK_Greek_iotadieresis => VirtualKeyCode::Greek_iotadieresis, - //ffi::XK_Greek_iotaaccentdieresis => VirtualKeyCode::Greek_iotaaccentdieresis, - //ffi::XK_Greek_omicronaccent => VirtualKeyCode::Greek_omicronaccent, - //ffi::XK_Greek_upsilonaccent => VirtualKeyCode::Greek_upsilonaccent, - //ffi::XK_Greek_upsilondieresis => VirtualKeyCode::Greek_upsilondieresis, - //ffi::XK_Greek_upsilonaccentdieresis => VirtualKeyCode::Greek_upsilonaccentdieresis, - //ffi::XK_Greek_omegaaccent => VirtualKeyCode::Greek_omegaaccent, - //ffi::XK_Greek_ALPHA => VirtualKeyCode::Greek_alpha, - //ffi::XK_Greek_BETA => VirtualKeyCode::Greek_beta, - //ffi::XK_Greek_GAMMA => VirtualKeyCode::Greek_gamma, - //ffi::XK_Greek_DELTA => VirtualKeyCode::Greek_delta, - //ffi::XK_Greek_EPSILON => VirtualKeyCode::Greek_epsilon, - //ffi::XK_Greek_ZETA => VirtualKeyCode::Greek_zeta, - //ffi::XK_Greek_ETA => VirtualKeyCode::Greek_eta, - //ffi::XK_Greek_THETA => VirtualKeyCode::Greek_theta, - //ffi::XK_Greek_IOTA => VirtualKeyCode::Greek_iota, - //ffi::XK_Greek_KAPPA => VirtualKeyCode::Greek_kappa, - //ffi::XK_Greek_LAMDA => VirtualKeyCode::Greek_lamda, - //ffi::XK_Greek_LAMBDA => VirtualKeyCode::Greek_lambda, - //ffi::XK_Greek_MU => VirtualKeyCode::Greek_mu, - //ffi::XK_Greek_NU => VirtualKeyCode::Greek_nu, - //ffi::XK_Greek_XI => VirtualKeyCode::Greek_xi, - //ffi::XK_Greek_OMICRON => VirtualKeyCode::Greek_omicron, - //ffi::XK_Greek_PI => VirtualKeyCode::Greek_pi, - //ffi::XK_Greek_RHO => VirtualKeyCode::Greek_rho, - //ffi::XK_Greek_SIGMA => VirtualKeyCode::Greek_sigma, - //ffi::XK_Greek_TAU => VirtualKeyCode::Greek_tau, - //ffi::XK_Greek_UPSILON => VirtualKeyCode::Greek_upsilon, - //ffi::XK_Greek_PHI => VirtualKeyCode::Greek_phi, - //ffi::XK_Greek_CHI => VirtualKeyCode::Greek_chi, - //ffi::XK_Greek_PSI => VirtualKeyCode::Greek_psi, - //ffi::XK_Greek_OMEGA => VirtualKeyCode::Greek_omega, - //ffi::XK_Greek_alpha => VirtualKeyCode::Greek_alpha, - //ffi::XK_Greek_beta => VirtualKeyCode::Greek_beta, - //ffi::XK_Greek_gamma => VirtualKeyCode::Greek_gamma, - //ffi::XK_Greek_delta => VirtualKeyCode::Greek_delta, - //ffi::XK_Greek_epsilon => VirtualKeyCode::Greek_epsilon, - //ffi::XK_Greek_zeta => VirtualKeyCode::Greek_zeta, - //ffi::XK_Greek_eta => VirtualKeyCode::Greek_eta, - //ffi::XK_Greek_theta => VirtualKeyCode::Greek_theta, - //ffi::XK_Greek_iota => VirtualKeyCode::Greek_iota, - //ffi::XK_Greek_kappa => VirtualKeyCode::Greek_kappa, - //ffi::XK_Greek_lamda => VirtualKeyCode::Greek_lamda, - //ffi::XK_Greek_lambda => VirtualKeyCode::Greek_lambda, - //ffi::XK_Greek_mu => VirtualKeyCode::Greek_mu, - //ffi::XK_Greek_nu => VirtualKeyCode::Greek_nu, - //ffi::XK_Greek_xi => VirtualKeyCode::Greek_xi, - //ffi::XK_Greek_omicron => VirtualKeyCode::Greek_omicron, - //ffi::XK_Greek_pi => VirtualKeyCode::Greek_pi, - //ffi::XK_Greek_rho => VirtualKeyCode::Greek_rho, - //ffi::XK_Greek_sigma => VirtualKeyCode::Greek_sigma, - //ffi::XK_Greek_finalsmallsigma => VirtualKeyCode::Greek_finalsmallsigma, - //ffi::XK_Greek_tau => VirtualKeyCode::Greek_tau, - //ffi::XK_Greek_upsilon => VirtualKeyCode::Greek_upsilon, - //ffi::XK_Greek_phi => VirtualKeyCode::Greek_phi, - //ffi::XK_Greek_chi => VirtualKeyCode::Greek_chi, - //ffi::XK_Greek_psi => VirtualKeyCode::Greek_psi, - //ffi::XK_Greek_omega => VirtualKeyCode::Greek_omega, - //ffi::XK_Greek_switch => VirtualKeyCode::Greek_switch, - //ffi::XK_leftradical => VirtualKeyCode::Leftradical, - //ffi::XK_topleftradical => VirtualKeyCode::Topleftradical, - //ffi::XK_horizconnector => VirtualKeyCode::Horizconnector, - //ffi::XK_topintegral => VirtualKeyCode::Topintegral, - //ffi::XK_botintegral => VirtualKeyCode::Botintegral, - //ffi::XK_vertconnector => VirtualKeyCode::Vertconnector, - //ffi::XK_topleftsqbracket => VirtualKeyCode::Topleftsqbracket, - //ffi::XK_botleftsqbracket => VirtualKeyCode::Botleftsqbracket, - //ffi::XK_toprightsqbracket => VirtualKeyCode::Toprightsqbracket, - //ffi::XK_botrightsqbracket => VirtualKeyCode::Botrightsqbracket, - //ffi::XK_topleftparens => VirtualKeyCode::Topleftparens, - //ffi::XK_botleftparens => VirtualKeyCode::Botleftparens, - //ffi::XK_toprightparens => VirtualKeyCode::Toprightparens, - //ffi::XK_botrightparens => VirtualKeyCode::Botrightparens, - //ffi::XK_leftmiddlecurlybrace => VirtualKeyCode::Leftmiddlecurlybrace, - //ffi::XK_rightmiddlecurlybrace => VirtualKeyCode::Rightmiddlecurlybrace, - //ffi::XK_topleftsummation => VirtualKeyCode::Topleftsummation, - //ffi::XK_botleftsummation => VirtualKeyCode::Botleftsummation, - //ffi::XK_topvertsummationconnector => VirtualKeyCode::Topvertsummationconnector, - //ffi::XK_botvertsummationconnector => VirtualKeyCode::Botvertsummationconnector, - //ffi::XK_toprightsummation => VirtualKeyCode::Toprightsummation, - //ffi::XK_botrightsummation => VirtualKeyCode::Botrightsummation, - //ffi::XK_rightmiddlesummation => VirtualKeyCode::Rightmiddlesummation, - //ffi::XK_lessthanequal => VirtualKeyCode::Lessthanequal, - //ffi::XK_notequal => VirtualKeyCode::Notequal, - //ffi::XK_greaterthanequal => VirtualKeyCode::Greaterthanequal, - //ffi::XK_integral => VirtualKeyCode::Integral, - //ffi::XK_therefore => VirtualKeyCode::Therefore, - //ffi::XK_variation => VirtualKeyCode::Variation, - //ffi::XK_infinity => VirtualKeyCode::Infinity, - //ffi::XK_nabla => VirtualKeyCode::Nabla, - //ffi::XK_approximate => VirtualKeyCode::Approximate, - //ffi::XK_similarequal => VirtualKeyCode::Similarequal, - //ffi::XK_ifonlyif => VirtualKeyCode::Ifonlyif, - //ffi::XK_implies => VirtualKeyCode::Implies, - //ffi::XK_identical => VirtualKeyCode::Identical, - //ffi::XK_radical => VirtualKeyCode::Radical, - //ffi::XK_includedin => VirtualKeyCode::Includedin, - //ffi::XK_includes => VirtualKeyCode::Includes, - //ffi::XK_intersection => VirtualKeyCode::Intersection, - //ffi::XK_union => VirtualKeyCode::Union, - //ffi::XK_logicaland => VirtualKeyCode::Logicaland, - //ffi::XK_logicalor => VirtualKeyCode::Logicalor, - //ffi::XK_partialderivative => VirtualKeyCode::Partialderivative, - //ffi::XK_function => VirtualKeyCode::Function, - //ffi::XK_leftarrow => VirtualKeyCode::Leftarrow, - //ffi::XK_uparrow => VirtualKeyCode::Uparrow, - //ffi::XK_rightarrow => VirtualKeyCode::Rightarrow, - //ffi::XK_downarrow => VirtualKeyCode::Downarrow, - //ffi::XK_blank => VirtualKeyCode::Blank, - //ffi::XK_soliddiamond => VirtualKeyCode::Soliddiamond, - //ffi::XK_checkerboard => VirtualKeyCode::Checkerboard, - //ffi::XK_ht => VirtualKeyCode::Ht, - //ffi::XK_ff => VirtualKeyCode::Ff, - //ffi::XK_cr => VirtualKeyCode::Cr, - //ffi::XK_lf => VirtualKeyCode::Lf, - //ffi::XK_nl => VirtualKeyCode::Nl, - //ffi::XK_vt => VirtualKeyCode::Vt, - //ffi::XK_lowrightcorner => VirtualKeyCode::Lowrightcorner, - //ffi::XK_uprightcorner => VirtualKeyCode::Uprightcorner, - //ffi::XK_upleftcorner => VirtualKeyCode::Upleftcorner, - //ffi::XK_lowleftcorner => VirtualKeyCode::Lowleftcorner, - //ffi::XK_crossinglines => VirtualKeyCode::Crossinglines, - //ffi::XK_horizlinescan1 => VirtualKeyCode::Horizlinescan1, - //ffi::XK_horizlinescan3 => VirtualKeyCode::Horizlinescan3, - //ffi::XK_horizlinescan5 => VirtualKeyCode::Horizlinescan5, - //ffi::XK_horizlinescan7 => VirtualKeyCode::Horizlinescan7, - //ffi::XK_horizlinescan9 => VirtualKeyCode::Horizlinescan9, - //ffi::XK_leftt => VirtualKeyCode::Leftt, - //ffi::XK_rightt => VirtualKeyCode::Rightt, - //ffi::XK_bott => VirtualKeyCode::Bott, - //ffi::XK_topt => VirtualKeyCode::Topt, - //ffi::XK_vertbar => VirtualKeyCode::Vertbar, - //ffi::XK_emspace => VirtualKeyCode::Emspace, - //ffi::XK_enspace => VirtualKeyCode::Enspace, - //ffi::XK_em3space => VirtualKeyCode::Em3space, - //ffi::XK_em4space => VirtualKeyCode::Em4space, - //ffi::XK_digitspace => VirtualKeyCode::Digitspace, - //ffi::XK_punctspace => VirtualKeyCode::Punctspace, - //ffi::XK_thinspace => VirtualKeyCode::Thinspace, - //ffi::XK_hairspace => VirtualKeyCode::Hairspace, - //ffi::XK_emdash => VirtualKeyCode::Emdash, - //ffi::XK_endash => VirtualKeyCode::Endash, - //ffi::XK_signifblank => VirtualKeyCode::Signifblank, - //ffi::XK_ellipsis => VirtualKeyCode::Ellipsis, - //ffi::XK_doubbaselinedot => VirtualKeyCode::Doubbaselinedot, - //ffi::XK_onethird => VirtualKeyCode::Onethird, - //ffi::XK_twothirds => VirtualKeyCode::Twothirds, - //ffi::XK_onefifth => VirtualKeyCode::Onefifth, - //ffi::XK_twofifths => VirtualKeyCode::Twofifths, - //ffi::XK_threefifths => VirtualKeyCode::Threefifths, - //ffi::XK_fourfifths => VirtualKeyCode::Fourfifths, - //ffi::XK_onesixth => VirtualKeyCode::Onesixth, - //ffi::XK_fivesixths => VirtualKeyCode::Fivesixths, - //ffi::XK_careof => VirtualKeyCode::Careof, - //ffi::XK_figdash => VirtualKeyCode::Figdash, - //ffi::XK_leftanglebracket => VirtualKeyCode::Leftanglebracket, - //ffi::XK_decimalpoint => VirtualKeyCode::Decimalpoint, - //ffi::XK_rightanglebracket => VirtualKeyCode::Rightanglebracket, - //ffi::XK_marker => VirtualKeyCode::Marker, - //ffi::XK_oneeighth => VirtualKeyCode::Oneeighth, - //ffi::XK_threeeighths => VirtualKeyCode::Threeeighths, - //ffi::XK_fiveeighths => VirtualKeyCode::Fiveeighths, - //ffi::XK_seveneighths => VirtualKeyCode::Seveneighths, - //ffi::XK_trademark => VirtualKeyCode::Trademark, - //ffi::XK_signaturemark => VirtualKeyCode::Signaturemark, - //ffi::XK_trademarkincircle => VirtualKeyCode::Trademarkincircle, - //ffi::XK_leftopentriangle => VirtualKeyCode::Leftopentriangle, - //ffi::XK_rightopentriangle => VirtualKeyCode::Rightopentriangle, - //ffi::XK_emopencircle => VirtualKeyCode::Emopencircle, - //ffi::XK_emopenrectangle => VirtualKeyCode::Emopenrectangle, - //ffi::XK_leftsinglequotemark => VirtualKeyCode::Leftsinglequotemark, - //ffi::XK_rightsinglequotemark => VirtualKeyCode::Rightsinglequotemark, - //ffi::XK_leftdoublequotemark => VirtualKeyCode::Leftdoublequotemark, - //ffi::XK_rightdoublequotemark => VirtualKeyCode::Rightdoublequotemark, - //ffi::XK_prescription => VirtualKeyCode::Prescription, - //ffi::XK_minutes => VirtualKeyCode::Minutes, - //ffi::XK_seconds => VirtualKeyCode::Seconds, - //ffi::XK_latincross => VirtualKeyCode::Latincross, - //ffi::XK_hexagram => VirtualKeyCode::Hexagram, - //ffi::XK_filledrectbullet => VirtualKeyCode::Filledrectbullet, - //ffi::XK_filledlefttribullet => VirtualKeyCode::Filledlefttribullet, - //ffi::XK_filledrighttribullet => VirtualKeyCode::Filledrighttribullet, - //ffi::XK_emfilledcircle => VirtualKeyCode::Emfilledcircle, - //ffi::XK_emfilledrect => VirtualKeyCode::Emfilledrect, - //ffi::XK_enopencircbullet => VirtualKeyCode::Enopencircbullet, - //ffi::XK_enopensquarebullet => VirtualKeyCode::Enopensquarebullet, - //ffi::XK_openrectbullet => VirtualKeyCode::Openrectbullet, - //ffi::XK_opentribulletup => VirtualKeyCode::Opentribulletup, - //ffi::XK_opentribulletdown => VirtualKeyCode::Opentribulletdown, - //ffi::XK_openstar => VirtualKeyCode::Openstar, - //ffi::XK_enfilledcircbullet => VirtualKeyCode::Enfilledcircbullet, - //ffi::XK_enfilledsqbullet => VirtualKeyCode::Enfilledsqbullet, - //ffi::XK_filledtribulletup => VirtualKeyCode::Filledtribulletup, - //ffi::XK_filledtribulletdown => VirtualKeyCode::Filledtribulletdown, - //ffi::XK_leftpointer => VirtualKeyCode::Leftpointer, - //ffi::XK_rightpointer => VirtualKeyCode::Rightpointer, - //ffi::XK_club => VirtualKeyCode::Club, - //ffi::XK_diamond => VirtualKeyCode::Diamond, - //ffi::XK_heart => VirtualKeyCode::Heart, - //ffi::XK_maltesecross => VirtualKeyCode::Maltesecross, - //ffi::XK_dagger => VirtualKeyCode::Dagger, - //ffi::XK_doubledagger => VirtualKeyCode::Doubledagger, - //ffi::XK_checkmark => VirtualKeyCode::Checkmark, - //ffi::XK_ballotcross => VirtualKeyCode::Ballotcross, - //ffi::XK_musicalsharp => VirtualKeyCode::Musicalsharp, - //ffi::XK_musicalflat => VirtualKeyCode::Musicalflat, - //ffi::XK_malesymbol => VirtualKeyCode::Malesymbol, - //ffi::XK_femalesymbol => VirtualKeyCode::Femalesymbol, - //ffi::XK_telephone => VirtualKeyCode::Telephone, - //ffi::XK_telephonerecorder => VirtualKeyCode::Telephonerecorder, - //ffi::XK_phonographcopyright => VirtualKeyCode::Phonographcopyright, - //ffi::XK_caret => VirtualKeyCode::Caret, - //ffi::XK_singlelowquotemark => VirtualKeyCode::Singlelowquotemark, - //ffi::XK_doublelowquotemark => VirtualKeyCode::Doublelowquotemark, - //ffi::XK_cursor => VirtualKeyCode::Cursor, - //ffi::XK_leftcaret => VirtualKeyCode::Leftcaret, - //ffi::XK_rightcaret => VirtualKeyCode::Rightcaret, - //ffi::XK_downcaret => VirtualKeyCode::Downcaret, - //ffi::XK_upcaret => VirtualKeyCode::Upcaret, - //ffi::XK_overbar => VirtualKeyCode::Overbar, - //ffi::XK_downtack => VirtualKeyCode::Downtack, - //ffi::XK_upshoe => VirtualKeyCode::Upshoe, - //ffi::XK_downstile => VirtualKeyCode::Downstile, - //ffi::XK_underbar => VirtualKeyCode::Underbar, - //ffi::XK_jot => VirtualKeyCode::Jot, - //ffi::XK_quad => VirtualKeyCode::Quad, - //ffi::XK_uptack => VirtualKeyCode::Uptack, - //ffi::XK_circle => VirtualKeyCode::Circle, - //ffi::XK_upstile => VirtualKeyCode::Upstile, - //ffi::XK_downshoe => VirtualKeyCode::Downshoe, - //ffi::XK_rightshoe => VirtualKeyCode::Rightshoe, - //ffi::XK_leftshoe => VirtualKeyCode::Leftshoe, - //ffi::XK_lefttack => VirtualKeyCode::Lefttack, - //ffi::XK_righttack => VirtualKeyCode::Righttack, - //ffi::XK_hebrew_doublelowline => VirtualKeyCode::Hebrew_doublelowline, - //ffi::XK_hebrew_aleph => VirtualKeyCode::Hebrew_aleph, - //ffi::XK_hebrew_bet => VirtualKeyCode::Hebrew_bet, - //ffi::XK_hebrew_beth => VirtualKeyCode::Hebrew_beth, - //ffi::XK_hebrew_gimel => VirtualKeyCode::Hebrew_gimel, - //ffi::XK_hebrew_gimmel => VirtualKeyCode::Hebrew_gimmel, - //ffi::XK_hebrew_dalet => VirtualKeyCode::Hebrew_dalet, - //ffi::XK_hebrew_daleth => VirtualKeyCode::Hebrew_daleth, - //ffi::XK_hebrew_he => VirtualKeyCode::Hebrew_he, - //ffi::XK_hebrew_waw => VirtualKeyCode::Hebrew_waw, - //ffi::XK_hebrew_zain => VirtualKeyCode::Hebrew_zain, - //ffi::XK_hebrew_zayin => VirtualKeyCode::Hebrew_zayin, - //ffi::XK_hebrew_chet => VirtualKeyCode::Hebrew_chet, - //ffi::XK_hebrew_het => VirtualKeyCode::Hebrew_het, - //ffi::XK_hebrew_tet => VirtualKeyCode::Hebrew_tet, - //ffi::XK_hebrew_teth => VirtualKeyCode::Hebrew_teth, - //ffi::XK_hebrew_yod => VirtualKeyCode::Hebrew_yod, - //ffi::XK_hebrew_finalkaph => VirtualKeyCode::Hebrew_finalkaph, - //ffi::XK_hebrew_kaph => VirtualKeyCode::Hebrew_kaph, - //ffi::XK_hebrew_lamed => VirtualKeyCode::Hebrew_lamed, - //ffi::XK_hebrew_finalmem => VirtualKeyCode::Hebrew_finalmem, - //ffi::XK_hebrew_mem => VirtualKeyCode::Hebrew_mem, - //ffi::XK_hebrew_finalnun => VirtualKeyCode::Hebrew_finalnun, - //ffi::XK_hebrew_nun => VirtualKeyCode::Hebrew_nun, - //ffi::XK_hebrew_samech => VirtualKeyCode::Hebrew_samech, - //ffi::XK_hebrew_samekh => VirtualKeyCode::Hebrew_samekh, - //ffi::XK_hebrew_ayin => VirtualKeyCode::Hebrew_ayin, - //ffi::XK_hebrew_finalpe => VirtualKeyCode::Hebrew_finalpe, - //ffi::XK_hebrew_pe => VirtualKeyCode::Hebrew_pe, - //ffi::XK_hebrew_finalzade => VirtualKeyCode::Hebrew_finalzade, - //ffi::XK_hebrew_finalzadi => VirtualKeyCode::Hebrew_finalzadi, - //ffi::XK_hebrew_zade => VirtualKeyCode::Hebrew_zade, - //ffi::XK_hebrew_zadi => VirtualKeyCode::Hebrew_zadi, - //ffi::XK_hebrew_qoph => VirtualKeyCode::Hebrew_qoph, - //ffi::XK_hebrew_kuf => VirtualKeyCode::Hebrew_kuf, - //ffi::XK_hebrew_resh => VirtualKeyCode::Hebrew_resh, - //ffi::XK_hebrew_shin => VirtualKeyCode::Hebrew_shin, - //ffi::XK_hebrew_taw => VirtualKeyCode::Hebrew_taw, - //ffi::XK_hebrew_taf => VirtualKeyCode::Hebrew_taf, - //ffi::XK_Hebrew_switch => VirtualKeyCode::Hebrew_switch, - ffi::XF86XK_Back => VirtualKeyCode::NavigateBackward, - ffi::XF86XK_Forward => VirtualKeyCode::NavigateForward, - ffi::XF86XK_Copy => VirtualKeyCode::Copy, - ffi::XF86XK_Paste => VirtualKeyCode::Paste, - ffi::XF86XK_Cut => VirtualKeyCode::Cut, - _ => return None, - }) -} +// pub fn keysym_to_element(keysym: libc::c_uint) -> Option { +// Some(match keysym { +// ffi::XK_BackSpace => VirtualKeyCode::Back, +// ffi::XK_Tab => VirtualKeyCode::Tab, +// //ffi::XK_Linefeed => VirtualKeyCode::Linefeed, +// //ffi::XK_Clear => VirtualKeyCode::Clear, +// ffi::XK_Return => VirtualKeyCode::Return, +// //ffi::XK_Pause => VirtualKeyCode::Pause, +// //ffi::XK_Scroll_Lock => VirtualKeyCode::Scroll_lock, +// //ffi::XK_Sys_Req => VirtualKeyCode::Sys_req, +// ffi::XK_Escape => VirtualKeyCode::Escape, +// ffi::XK_Delete => VirtualKeyCode::Delete, +// ffi::XK_Multi_key => VirtualKeyCode::Compose, +// //ffi::XK_Kanji => VirtualKeyCode::Kanji, +// //ffi::XK_Muhenkan => VirtualKeyCode::Muhenkan, +// //ffi::XK_Henkan_Mode => VirtualKeyCode::Henkan_mode, +// //ffi::XK_Henkan => VirtualKeyCode::Henkan, +// //ffi::XK_Romaji => VirtualKeyCode::Romaji, +// //ffi::XK_Hiragana => VirtualKeyCode::Hiragana, +// //ffi::XK_Katakana => VirtualKeyCode::Katakana, +// //ffi::XK_Hiragana_Katakana => VirtualKeyCode::Hiragana_katakana, +// //ffi::XK_Zenkaku => VirtualKeyCode::Zenkaku, +// //ffi::XK_Hankaku => VirtualKeyCode::Hankaku, +// //ffi::XK_Zenkaku_Hankaku => VirtualKeyCode::Zenkaku_hankaku, +// //ffi::XK_Touroku => VirtualKeyCode::Touroku, +// //ffi::XK_Massyo => VirtualKeyCode::Massyo, +// //ffi::XK_Kana_Lock => VirtualKeyCode::Kana_lock, +// //ffi::XK_Kana_Shift => VirtualKeyCode::Kana_shift, +// //ffi::XK_Eisu_Shift => VirtualKeyCode::Eisu_shift, +// //ffi::XK_Eisu_toggle => VirtualKeyCode::Eisu_toggle, +// ffi::XK_Home => VirtualKeyCode::Home, +// ffi::XK_Left => VirtualKeyCode::Left, +// ffi::XK_Up => VirtualKeyCode::Up, +// ffi::XK_Right => VirtualKeyCode::Right, +// ffi::XK_Down => VirtualKeyCode::Down, +// //ffi::XK_Prior => VirtualKeyCode::Prior, +// ffi::XK_Page_Up => VirtualKeyCode::PageUp, +// //ffi::XK_Next => VirtualKeyCode::Next, +// ffi::XK_Page_Down => VirtualKeyCode::PageDown, +// ffi::XK_End => VirtualKeyCode::End, +// //ffi::XK_Begin => VirtualKeyCode::Begin, +// //ffi::XK_Win_L => VirtualKeyCode::Win_l, +// //ffi::XK_Win_R => VirtualKeyCode::Win_r, +// //ffi::XK_App => VirtualKeyCode::App, +// //ffi::XK_Select => VirtualKeyCode::Select, +// //ffi::XK_Print => VirtualKeyCode::Print, +// //ffi::XK_Execute => VirtualKeyCode::Execute, +// ffi::XK_Insert => VirtualKeyCode::Insert, +// //ffi::XK_Undo => VirtualKeyCode::Undo, +// //ffi::XK_Redo => VirtualKeyCode::Redo, +// //ffi::XK_Menu => VirtualKeyCode::Menu, +// //ffi::XK_Find => VirtualKeyCode::Find, +// //ffi::XK_Cancel => VirtualKeyCode::Cancel, +// //ffi::XK_Help => VirtualKeyCode::Help, +// //ffi::XK_Break => VirtualKeyCode::Break, +// //ffi::XK_Mode_switch => VirtualKeyCode::Mode_switch, +// //ffi::XK_script_switch => VirtualKeyCode::Script_switch, +// //ffi::XK_Num_Lock => VirtualKeyCode::Num_lock, +// //ffi::XK_KP_Space => VirtualKeyCode::Kp_space, +// //ffi::XK_KP_Tab => VirtualKeyCode::Kp_tab, +// //ffi::XK_KP_Enter => VirtualKeyCode::Kp_enter, +// //ffi::XK_KP_F1 => VirtualKeyCode::Kp_f1, +// //ffi::XK_KP_F2 => VirtualKeyCode::Kp_f2, +// //ffi::XK_KP_F3 => VirtualKeyCode::Kp_f3, +// //ffi::XK_KP_F4 => VirtualKeyCode::Kp_f4, +// ffi::XK_KP_Home => VirtualKeyCode::Home, +// ffi::XK_KP_Left => VirtualKeyCode::Left, +// ffi::XK_KP_Up => VirtualKeyCode::Up, +// ffi::XK_KP_Right => VirtualKeyCode::Right, +// ffi::XK_KP_Down => VirtualKeyCode::Down, +// //ffi::XK_KP_Prior => VirtualKeyCode::Kp_prior, +// ffi::XK_KP_Page_Up => VirtualKeyCode::PageUp, +// //ffi::XK_KP_Next => VirtualKeyCode::Kp_next, +// ffi::XK_KP_Page_Down => VirtualKeyCode::PageDown, +// ffi::XK_KP_End => VirtualKeyCode::End, +// //ffi::XK_KP_Begin => VirtualKeyCode::Kp_begin, +// ffi::XK_KP_Insert => VirtualKeyCode::Insert, +// ffi::XK_KP_Delete => VirtualKeyCode::Delete, +// ffi::XK_KP_Equal => VirtualKeyCode::NumpadEquals, +// ffi::XK_KP_Multiply => VirtualKeyCode::NumpadMultiply, +// ffi::XK_KP_Add => VirtualKeyCode::NumpadAdd, +// //ffi::XK_KP_Separator => VirtualKeyCode::Kp_separator, +// ffi::XK_KP_Subtract => VirtualKeyCode::NumpadSubtract, +// ffi::XK_KP_Decimal => VirtualKeyCode::NumpadDecimal, +// ffi::XK_KP_Divide => VirtualKeyCode::NumpadDivide, +// ffi::XK_KP_0 => VirtualKeyCode::Numpad0, +// ffi::XK_KP_1 => VirtualKeyCode::Numpad1, +// ffi::XK_KP_2 => VirtualKeyCode::Numpad2, +// ffi::XK_KP_3 => VirtualKeyCode::Numpad3, +// ffi::XK_KP_4 => VirtualKeyCode::Numpad4, +// ffi::XK_KP_5 => VirtualKeyCode::Numpad5, +// ffi::XK_KP_6 => VirtualKeyCode::Numpad6, +// ffi::XK_KP_7 => VirtualKeyCode::Numpad7, +// ffi::XK_KP_8 => VirtualKeyCode::Numpad8, +// ffi::XK_KP_9 => VirtualKeyCode::Numpad9, +// ffi::XK_F1 => VirtualKeyCode::F1, +// ffi::XK_F2 => VirtualKeyCode::F2, +// ffi::XK_F3 => VirtualKeyCode::F3, +// ffi::XK_F4 => VirtualKeyCode::F4, +// ffi::XK_F5 => VirtualKeyCode::F5, +// ffi::XK_F6 => VirtualKeyCode::F6, +// ffi::XK_F7 => VirtualKeyCode::F7, +// ffi::XK_F8 => VirtualKeyCode::F8, +// ffi::XK_F9 => VirtualKeyCode::F9, +// ffi::XK_F10 => VirtualKeyCode::F10, +// ffi::XK_F11 => VirtualKeyCode::F11, +// //ffi::XK_L1 => VirtualKeyCode::L1, +// ffi::XK_F12 => VirtualKeyCode::F12, +// //ffi::XK_L2 => VirtualKeyCode::L2, +// ffi::XK_F13 => VirtualKeyCode::F13, +// //ffi::XK_L3 => VirtualKeyCode::L3, +// ffi::XK_F14 => VirtualKeyCode::F14, +// //ffi::XK_L4 => VirtualKeyCode::L4, +// ffi::XK_F15 => VirtualKeyCode::F15, +// //ffi::XK_L5 => VirtualKeyCode::L5, +// ffi::XK_F16 => VirtualKeyCode::F16, +// //ffi::XK_L6 => VirtualKeyCode::L6, +// ffi::XK_F17 => VirtualKeyCode::F17, +// //ffi::XK_L7 => VirtualKeyCode::L7, +// ffi::XK_F18 => VirtualKeyCode::F18, +// //ffi::XK_L8 => VirtualKeyCode::L8, +// ffi::XK_F19 => VirtualKeyCode::F19, +// //ffi::XK_L9 => VirtualKeyCode::L9, +// ffi::XK_F20 => VirtualKeyCode::F20, +// //ffi::XK_L10 => VirtualKeyCode::L10, +// ffi::XK_F21 => VirtualKeyCode::F21, +// //ffi::XK_R1 => VirtualKeyCode::R1, +// ffi::XK_F22 => VirtualKeyCode::F22, +// //ffi::XK_R2 => VirtualKeyCode::R2, +// ffi::XK_F23 => VirtualKeyCode::F23, +// //ffi::XK_R3 => VirtualKeyCode::R3, +// ffi::XK_F24 => VirtualKeyCode::F24, +// //ffi::XK_R4 => VirtualKeyCode::R4, +// //ffi::XK_F25 => VirtualKeyCode::F25, +// //ffi::XK_R5 => VirtualKeyCode::R5, +// //ffi::XK_F26 => VirtualKeyCode::F26, +// //ffi::XK_R6 => VirtualKeyCode::R6, +// //ffi::XK_F27 => VirtualKeyCode::F27, +// //ffi::XK_R7 => VirtualKeyCode::R7, +// //ffi::XK_F28 => VirtualKeyCode::F28, +// //ffi::XK_R8 => VirtualKeyCode::R8, +// //ffi::XK_F29 => VirtualKeyCode::F29, +// //ffi::XK_R9 => VirtualKeyCode::R9, +// //ffi::XK_F30 => VirtualKeyCode::F30, +// //ffi::XK_R10 => VirtualKeyCode::R10, +// //ffi::XK_F31 => VirtualKeyCode::F31, +// //ffi::XK_R11 => VirtualKeyCode::R11, +// //ffi::XK_F32 => VirtualKeyCode::F32, +// //ffi::XK_R12 => VirtualKeyCode::R12, +// //ffi::XK_F33 => VirtualKeyCode::F33, +// //ffi::XK_R13 => VirtualKeyCode::R13, +// //ffi::XK_F34 => VirtualKeyCode::F34, +// //ffi::XK_R14 => VirtualKeyCode::R14, +// //ffi::XK_F35 => VirtualKeyCode::F35, +// //ffi::XK_R15 => VirtualKeyCode::R15, +// ffi::XK_Shift_L => VirtualKeyCode::LShift, +// ffi::XK_Shift_R => VirtualKeyCode::RShift, +// ffi::XK_Control_L => VirtualKeyCode::LControl, +// ffi::XK_Control_R => VirtualKeyCode::RControl, +// //ffi::XK_Caps_Lock => VirtualKeyCode::Caps_lock, +// //ffi::XK_Shift_Lock => VirtualKeyCode::Shift_lock, +// //ffi::XK_Meta_L => VirtualKeyCode::Meta_l, +// //ffi::XK_Meta_R => VirtualKeyCode::Meta_r, +// ffi::XK_Alt_L => VirtualKeyCode::LAlt, +// ffi::XK_Alt_R => VirtualKeyCode::RAlt, +// //ffi::XK_Super_L => VirtualKeyCode::Super_l, +// //ffi::XK_Super_R => VirtualKeyCode::Super_r, +// //ffi::XK_Hyper_L => VirtualKeyCode::Hyper_l, +// //ffi::XK_Hyper_R => VirtualKeyCode::Hyper_r, +// ffi::XK_ISO_Left_Tab => VirtualKeyCode::Tab, +// ffi::XK_space => VirtualKeyCode::Space, +// //ffi::XK_exclam => VirtualKeyCode::Exclam, +// //ffi::XK_quotedbl => VirtualKeyCode::Quotedbl, +// //ffi::XK_numbersign => VirtualKeyCode::Numbersign, +// //ffi::XK_dollar => VirtualKeyCode::Dollar, +// //ffi::XK_percent => VirtualKeyCode::Percent, +// //ffi::XK_ampersand => VirtualKeyCode::Ampersand, +// ffi::XK_apostrophe => VirtualKeyCode::Apostrophe, +// //ffi::XK_quoteright => VirtualKeyCode::Quoteright, +// //ffi::XK_parenleft => VirtualKeyCode::Parenleft, +// //ffi::XK_parenright => VirtualKeyCode::Parenright, +// ffi::XK_asterisk => VirtualKeyCode::Asterisk, +// ffi::XK_plus => VirtualKeyCode::Plus, +// ffi::XK_comma => VirtualKeyCode::Comma, +// ffi::XK_minus => VirtualKeyCode::Minus, +// ffi::XK_period => VirtualKeyCode::Period, +// ffi::XK_slash => VirtualKeyCode::Slash, +// ffi::XK_0 => VirtualKeyCode::Key0, +// ffi::XK_1 => VirtualKeyCode::Key1, +// ffi::XK_2 => VirtualKeyCode::Key2, +// ffi::XK_3 => VirtualKeyCode::Key3, +// ffi::XK_4 => VirtualKeyCode::Key4, +// ffi::XK_5 => VirtualKeyCode::Key5, +// ffi::XK_6 => VirtualKeyCode::Key6, +// ffi::XK_7 => VirtualKeyCode::Key7, +// ffi::XK_8 => VirtualKeyCode::Key8, +// ffi::XK_9 => VirtualKeyCode::Key9, +// ffi::XK_colon => VirtualKeyCode::Colon, +// ffi::XK_semicolon => VirtualKeyCode::Semicolon, +// //ffi::XK_less => VirtualKeyCode::Less, +// ffi::XK_equal => VirtualKeyCode::Equals, +// //ffi::XK_greater => VirtualKeyCode::Greater, +// //ffi::XK_question => VirtualKeyCode::Question, +// ffi::XK_at => VirtualKeyCode::At, +// ffi::XK_A => VirtualKeyCode::A, +// ffi::XK_B => VirtualKeyCode::B, +// ffi::XK_C => VirtualKeyCode::C, +// ffi::XK_D => VirtualKeyCode::D, +// ffi::XK_E => VirtualKeyCode::E, +// ffi::XK_F => VirtualKeyCode::F, +// ffi::XK_G => VirtualKeyCode::G, +// ffi::XK_H => VirtualKeyCode::H, +// ffi::XK_I => VirtualKeyCode::I, +// ffi::XK_J => VirtualKeyCode::J, +// ffi::XK_K => VirtualKeyCode::K, +// ffi::XK_L => VirtualKeyCode::L, +// ffi::XK_M => VirtualKeyCode::M, +// ffi::XK_N => VirtualKeyCode::N, +// ffi::XK_O => VirtualKeyCode::O, +// ffi::XK_P => VirtualKeyCode::P, +// ffi::XK_Q => VirtualKeyCode::Q, +// ffi::XK_R => VirtualKeyCode::R, +// ffi::XK_S => VirtualKeyCode::S, +// ffi::XK_T => VirtualKeyCode::T, +// ffi::XK_U => VirtualKeyCode::U, +// ffi::XK_V => VirtualKeyCode::V, +// ffi::XK_W => VirtualKeyCode::W, +// ffi::XK_X => VirtualKeyCode::X, +// ffi::XK_Y => VirtualKeyCode::Y, +// ffi::XK_Z => VirtualKeyCode::Z, +// ffi::XK_bracketleft => VirtualKeyCode::LBracket, +// ffi::XK_backslash => VirtualKeyCode::Backslash, +// ffi::XK_bracketright => VirtualKeyCode::RBracket, +// //ffi::XK_asciicircum => VirtualKeyCode::Asciicircum, +// //ffi::XK_underscore => VirtualKeyCode::Underscore, +// ffi::XK_grave => VirtualKeyCode::Grave, +// //ffi::XK_quoteleft => VirtualKeyCode::Quoteleft, +// ffi::XK_a => VirtualKeyCode::A, +// ffi::XK_b => VirtualKeyCode::B, +// ffi::XK_c => VirtualKeyCode::C, +// ffi::XK_d => VirtualKeyCode::D, +// ffi::XK_e => VirtualKeyCode::E, +// ffi::XK_f => VirtualKeyCode::F, +// ffi::XK_g => VirtualKeyCode::G, +// ffi::XK_h => VirtualKeyCode::H, +// ffi::XK_i => VirtualKeyCode::I, +// ffi::XK_j => VirtualKeyCode::J, +// ffi::XK_k => VirtualKeyCode::K, +// ffi::XK_l => VirtualKeyCode::L, +// ffi::XK_m => VirtualKeyCode::M, +// ffi::XK_n => VirtualKeyCode::N, +// ffi::XK_o => VirtualKeyCode::O, +// ffi::XK_p => VirtualKeyCode::P, +// ffi::XK_q => VirtualKeyCode::Q, +// ffi::XK_r => VirtualKeyCode::R, +// ffi::XK_s => VirtualKeyCode::S, +// ffi::XK_t => VirtualKeyCode::T, +// ffi::XK_u => VirtualKeyCode::U, +// ffi::XK_v => VirtualKeyCode::V, +// ffi::XK_w => VirtualKeyCode::W, +// ffi::XK_x => VirtualKeyCode::X, +// ffi::XK_y => VirtualKeyCode::Y, +// ffi::XK_z => VirtualKeyCode::Z, +// //ffi::XK_braceleft => VirtualKeyCode::Braceleft, +// //ffi::XK_bar => VirtualKeyCode::Bar, +// //ffi::XK_braceright => VirtualKeyCode::Braceright, +// //ffi::XK_asciitilde => VirtualKeyCode::Asciitilde, +// //ffi::XK_nobreakspace => VirtualKeyCode::Nobreakspace, +// //ffi::XK_exclamdown => VirtualKeyCode::Exclamdown, +// //ffi::XK_cent => VirtualKeyCode::Cent, +// //ffi::XK_sterling => VirtualKeyCode::Sterling, +// //ffi::XK_currency => VirtualKeyCode::Currency, +// //ffi::XK_yen => VirtualKeyCode::Yen, +// //ffi::XK_brokenbar => VirtualKeyCode::Brokenbar, +// //ffi::XK_section => VirtualKeyCode::Section, +// //ffi::XK_diaeresis => VirtualKeyCode::Diaeresis, +// //ffi::XK_copyright => VirtualKeyCode::Copyright, +// //ffi::XK_ordfeminine => VirtualKeyCode::Ordfeminine, +// //ffi::XK_guillemotleft => VirtualKeyCode::Guillemotleft, +// //ffi::XK_notsign => VirtualKeyCode::Notsign, +// //ffi::XK_hyphen => VirtualKeyCode::Hyphen, +// //ffi::XK_registered => VirtualKeyCode::Registered, +// //ffi::XK_macron => VirtualKeyCode::Macron, +// //ffi::XK_degree => VirtualKeyCode::Degree, +// //ffi::XK_plusminus => VirtualKeyCode::Plusminus, +// //ffi::XK_twosuperior => VirtualKeyCode::Twosuperior, +// //ffi::XK_threesuperior => VirtualKeyCode::Threesuperior, +// //ffi::XK_acute => VirtualKeyCode::Acute, +// //ffi::XK_mu => VirtualKeyCode::Mu, +// //ffi::XK_paragraph => VirtualKeyCode::Paragraph, +// //ffi::XK_periodcentered => VirtualKeyCode::Periodcentered, +// //ffi::XK_cedilla => VirtualKeyCode::Cedilla, +// //ffi::XK_onesuperior => VirtualKeyCode::Onesuperior, +// //ffi::XK_masculine => VirtualKeyCode::Masculine, +// //ffi::XK_guillemotright => VirtualKeyCode::Guillemotright, +// //ffi::XK_onequarter => VirtualKeyCode::Onequarter, +// //ffi::XK_onehalf => VirtualKeyCode::Onehalf, +// //ffi::XK_threequarters => VirtualKeyCode::Threequarters, +// //ffi::XK_questiondown => VirtualKeyCode::Questiondown, +// //ffi::XK_Agrave => VirtualKeyCode::Agrave, +// //ffi::XK_Aacute => VirtualKeyCode::Aacute, +// //ffi::XK_Acircumflex => VirtualKeyCode::Acircumflex, +// //ffi::XK_Atilde => VirtualKeyCode::Atilde, +// //ffi::XK_Adiaeresis => VirtualKeyCode::Adiaeresis, +// //ffi::XK_Aring => VirtualKeyCode::Aring, +// //ffi::XK_AE => VirtualKeyCode::Ae, +// //ffi::XK_Ccedilla => VirtualKeyCode::Ccedilla, +// //ffi::XK_Egrave => VirtualKeyCode::Egrave, +// //ffi::XK_Eacute => VirtualKeyCode::Eacute, +// //ffi::XK_Ecircumflex => VirtualKeyCode::Ecircumflex, +// //ffi::XK_Ediaeresis => VirtualKeyCode::Ediaeresis, +// //ffi::XK_Igrave => VirtualKeyCode::Igrave, +// //ffi::XK_Iacute => VirtualKeyCode::Iacute, +// //ffi::XK_Icircumflex => VirtualKeyCode::Icircumflex, +// //ffi::XK_Idiaeresis => VirtualKeyCode::Idiaeresis, +// //ffi::XK_ETH => VirtualKeyCode::Eth, +// //ffi::XK_Eth => VirtualKeyCode::Eth, +// //ffi::XK_Ntilde => VirtualKeyCode::Ntilde, +// //ffi::XK_Ograve => VirtualKeyCode::Ograve, +// //ffi::XK_Oacute => VirtualKeyCode::Oacute, +// //ffi::XK_Ocircumflex => VirtualKeyCode::Ocircumflex, +// //ffi::XK_Otilde => VirtualKeyCode::Otilde, +// //ffi::XK_Odiaeresis => VirtualKeyCode::Odiaeresis, +// //ffi::XK_multiply => VirtualKeyCode::Multiply, +// //ffi::XK_Ooblique => VirtualKeyCode::Ooblique, +// //ffi::XK_Ugrave => VirtualKeyCode::Ugrave, +// //ffi::XK_Uacute => VirtualKeyCode::Uacute, +// //ffi::XK_Ucircumflex => VirtualKeyCode::Ucircumflex, +// //ffi::XK_Udiaeresis => VirtualKeyCode::Udiaeresis, +// //ffi::XK_Yacute => VirtualKeyCode::Yacute, +// //ffi::XK_THORN => VirtualKeyCode::Thorn, +// //ffi::XK_Thorn => VirtualKeyCode::Thorn, +// //ffi::XK_ssharp => VirtualKeyCode::Ssharp, +// //ffi::XK_agrave => VirtualKeyCode::Agrave, +// //ffi::XK_aacute => VirtualKeyCode::Aacute, +// //ffi::XK_acircumflex => VirtualKeyCode::Acircumflex, +// //ffi::XK_atilde => VirtualKeyCode::Atilde, +// //ffi::XK_adiaeresis => VirtualKeyCode::Adiaeresis, +// //ffi::XK_aring => VirtualKeyCode::Aring, +// //ffi::XK_ae => VirtualKeyCode::Ae, +// //ffi::XK_ccedilla => VirtualKeyCode::Ccedilla, +// //ffi::XK_egrave => VirtualKeyCode::Egrave, +// //ffi::XK_eacute => VirtualKeyCode::Eacute, +// //ffi::XK_ecircumflex => VirtualKeyCode::Ecircumflex, +// //ffi::XK_ediaeresis => VirtualKeyCode::Ediaeresis, +// //ffi::XK_igrave => VirtualKeyCode::Igrave, +// //ffi::XK_iacute => VirtualKeyCode::Iacute, +// //ffi::XK_icircumflex => VirtualKeyCode::Icircumflex, +// //ffi::XK_idiaeresis => VirtualKeyCode::Idiaeresis, +// //ffi::XK_eth => VirtualKeyCode::Eth, +// //ffi::XK_ntilde => VirtualKeyCode::Ntilde, +// //ffi::XK_ograve => VirtualKeyCode::Ograve, +// //ffi::XK_oacute => VirtualKeyCode::Oacute, +// //ffi::XK_ocircumflex => VirtualKeyCode::Ocircumflex, +// //ffi::XK_otilde => VirtualKeyCode::Otilde, +// //ffi::XK_odiaeresis => VirtualKeyCode::Odiaeresis, +// //ffi::XK_division => VirtualKeyCode::Division, +// //ffi::XK_oslash => VirtualKeyCode::Oslash, +// //ffi::XK_ugrave => VirtualKeyCode::Ugrave, +// //ffi::XK_uacute => VirtualKeyCode::Uacute, +// //ffi::XK_ucircumflex => VirtualKeyCode::Ucircumflex, +// //ffi::XK_udiaeresis => VirtualKeyCode::Udiaeresis, +// //ffi::XK_yacute => VirtualKeyCode::Yacute, +// //ffi::XK_thorn => VirtualKeyCode::Thorn, +// //ffi::XK_ydiaeresis => VirtualKeyCode::Ydiaeresis, +// //ffi::XK_Aogonek => VirtualKeyCode::Aogonek, +// //ffi::XK_breve => VirtualKeyCode::Breve, +// //ffi::XK_Lstroke => VirtualKeyCode::Lstroke, +// //ffi::XK_Lcaron => VirtualKeyCode::Lcaron, +// //ffi::XK_Sacute => VirtualKeyCode::Sacute, +// //ffi::XK_Scaron => VirtualKeyCode::Scaron, +// //ffi::XK_Scedilla => VirtualKeyCode::Scedilla, +// //ffi::XK_Tcaron => VirtualKeyCode::Tcaron, +// //ffi::XK_Zacute => VirtualKeyCode::Zacute, +// //ffi::XK_Zcaron => VirtualKeyCode::Zcaron, +// //ffi::XK_Zabovedot => VirtualKeyCode::Zabovedot, +// //ffi::XK_aogonek => VirtualKeyCode::Aogonek, +// //ffi::XK_ogonek => VirtualKeyCode::Ogonek, +// //ffi::XK_lstroke => VirtualKeyCode::Lstroke, +// //ffi::XK_lcaron => VirtualKeyCode::Lcaron, +// //ffi::XK_sacute => VirtualKeyCode::Sacute, +// //ffi::XK_caron => VirtualKeyCode::Caron, +// //ffi::XK_scaron => VirtualKeyCode::Scaron, +// //ffi::XK_scedilla => VirtualKeyCode::Scedilla, +// //ffi::XK_tcaron => VirtualKeyCode::Tcaron, +// //ffi::XK_zacute => VirtualKeyCode::Zacute, +// //ffi::XK_doubleacute => VirtualKeyCode::Doubleacute, +// //ffi::XK_zcaron => VirtualKeyCode::Zcaron, +// //ffi::XK_zabovedot => VirtualKeyCode::Zabovedot, +// //ffi::XK_Racute => VirtualKeyCode::Racute, +// //ffi::XK_Abreve => VirtualKeyCode::Abreve, +// //ffi::XK_Lacute => VirtualKeyCode::Lacute, +// //ffi::XK_Cacute => VirtualKeyCode::Cacute, +// //ffi::XK_Ccaron => VirtualKeyCode::Ccaron, +// //ffi::XK_Eogonek => VirtualKeyCode::Eogonek, +// //ffi::XK_Ecaron => VirtualKeyCode::Ecaron, +// //ffi::XK_Dcaron => VirtualKeyCode::Dcaron, +// //ffi::XK_Dstroke => VirtualKeyCode::Dstroke, +// //ffi::XK_Nacute => VirtualKeyCode::Nacute, +// //ffi::XK_Ncaron => VirtualKeyCode::Ncaron, +// //ffi::XK_Odoubleacute => VirtualKeyCode::Odoubleacute, +// //ffi::XK_Rcaron => VirtualKeyCode::Rcaron, +// //ffi::XK_Uring => VirtualKeyCode::Uring, +// //ffi::XK_Udoubleacute => VirtualKeyCode::Udoubleacute, +// //ffi::XK_Tcedilla => VirtualKeyCode::Tcedilla, +// //ffi::XK_racute => VirtualKeyCode::Racute, +// //ffi::XK_abreve => VirtualKeyCode::Abreve, +// //ffi::XK_lacute => VirtualKeyCode::Lacute, +// //ffi::XK_cacute => VirtualKeyCode::Cacute, +// //ffi::XK_ccaron => VirtualKeyCode::Ccaron, +// //ffi::XK_eogonek => VirtualKeyCode::Eogonek, +// //ffi::XK_ecaron => VirtualKeyCode::Ecaron, +// //ffi::XK_dcaron => VirtualKeyCode::Dcaron, +// //ffi::XK_dstroke => VirtualKeyCode::Dstroke, +// //ffi::XK_nacute => VirtualKeyCode::Nacute, +// //ffi::XK_ncaron => VirtualKeyCode::Ncaron, +// //ffi::XK_odoubleacute => VirtualKeyCode::Odoubleacute, +// //ffi::XK_udoubleacute => VirtualKeyCode::Udoubleacute, +// //ffi::XK_rcaron => VirtualKeyCode::Rcaron, +// //ffi::XK_uring => VirtualKeyCode::Uring, +// //ffi::XK_tcedilla => VirtualKeyCode::Tcedilla, +// //ffi::XK_abovedot => VirtualKeyCode::Abovedot, +// //ffi::XK_Hstroke => VirtualKeyCode::Hstroke, +// //ffi::XK_Hcircumflex => VirtualKeyCode::Hcircumflex, +// //ffi::XK_Iabovedot => VirtualKeyCode::Iabovedot, +// //ffi::XK_Gbreve => VirtualKeyCode::Gbreve, +// //ffi::XK_Jcircumflex => VirtualKeyCode::Jcircumflex, +// //ffi::XK_hstroke => VirtualKeyCode::Hstroke, +// //ffi::XK_hcircumflex => VirtualKeyCode::Hcircumflex, +// //ffi::XK_idotless => VirtualKeyCode::Idotless, +// //ffi::XK_gbreve => VirtualKeyCode::Gbreve, +// //ffi::XK_jcircumflex => VirtualKeyCode::Jcircumflex, +// //ffi::XK_Cabovedot => VirtualKeyCode::Cabovedot, +// //ffi::XK_Ccircumflex => VirtualKeyCode::Ccircumflex, +// //ffi::XK_Gabovedot => VirtualKeyCode::Gabovedot, +// //ffi::XK_Gcircumflex => VirtualKeyCode::Gcircumflex, +// //ffi::XK_Ubreve => VirtualKeyCode::Ubreve, +// //ffi::XK_Scircumflex => VirtualKeyCode::Scircumflex, +// //ffi::XK_cabovedot => VirtualKeyCode::Cabovedot, +// //ffi::XK_ccircumflex => VirtualKeyCode::Ccircumflex, +// //ffi::XK_gabovedot => VirtualKeyCode::Gabovedot, +// //ffi::XK_gcircumflex => VirtualKeyCode::Gcircumflex, +// //ffi::XK_ubreve => VirtualKeyCode::Ubreve, +// //ffi::XK_scircumflex => VirtualKeyCode::Scircumflex, +// //ffi::XK_kra => VirtualKeyCode::Kra, +// //ffi::XK_kappa => VirtualKeyCode::Kappa, +// //ffi::XK_Rcedilla => VirtualKeyCode::Rcedilla, +// //ffi::XK_Itilde => VirtualKeyCode::Itilde, +// //ffi::XK_Lcedilla => VirtualKeyCode::Lcedilla, +// //ffi::XK_Emacron => VirtualKeyCode::Emacron, +// //ffi::XK_Gcedilla => VirtualKeyCode::Gcedilla, +// //ffi::XK_Tslash => VirtualKeyCode::Tslash, +// //ffi::XK_rcedilla => VirtualKeyCode::Rcedilla, +// //ffi::XK_itilde => VirtualKeyCode::Itilde, +// //ffi::XK_lcedilla => VirtualKeyCode::Lcedilla, +// //ffi::XK_emacron => VirtualKeyCode::Emacron, +// //ffi::XK_gcedilla => VirtualKeyCode::Gcedilla, +// //ffi::XK_tslash => VirtualKeyCode::Tslash, +// //ffi::XK_ENG => VirtualKeyCode::Eng, +// //ffi::XK_eng => VirtualKeyCode::Eng, +// //ffi::XK_Amacron => VirtualKeyCode::Amacron, +// //ffi::XK_Iogonek => VirtualKeyCode::Iogonek, +// //ffi::XK_Eabovedot => VirtualKeyCode::Eabovedot, +// //ffi::XK_Imacron => VirtualKeyCode::Imacron, +// //ffi::XK_Ncedilla => VirtualKeyCode::Ncedilla, +// //ffi::XK_Omacron => VirtualKeyCode::Omacron, +// //ffi::XK_Kcedilla => VirtualKeyCode::Kcedilla, +// //ffi::XK_Uogonek => VirtualKeyCode::Uogonek, +// //ffi::XK_Utilde => VirtualKeyCode::Utilde, +// //ffi::XK_Umacron => VirtualKeyCode::Umacron, +// //ffi::XK_amacron => VirtualKeyCode::Amacron, +// //ffi::XK_iogonek => VirtualKeyCode::Iogonek, +// //ffi::XK_eabovedot => VirtualKeyCode::Eabovedot, +// //ffi::XK_imacron => VirtualKeyCode::Imacron, +// //ffi::XK_ncedilla => VirtualKeyCode::Ncedilla, +// //ffi::XK_omacron => VirtualKeyCode::Omacron, +// //ffi::XK_kcedilla => VirtualKeyCode::Kcedilla, +// //ffi::XK_uogonek => VirtualKeyCode::Uogonek, +// //ffi::XK_utilde => VirtualKeyCode::Utilde, +// //ffi::XK_umacron => VirtualKeyCode::Umacron, +// //ffi::XK_overline => VirtualKeyCode::Overline, +// //ffi::XK_kana_fullstop => VirtualKeyCode::Kana_fullstop, +// //ffi::XK_kana_openingbracket => VirtualKeyCode::Kana_openingbracket, +// //ffi::XK_kana_closingbracket => VirtualKeyCode::Kana_closingbracket, +// //ffi::XK_kana_comma => VirtualKeyCode::Kana_comma, +// //ffi::XK_kana_conjunctive => VirtualKeyCode::Kana_conjunctive, +// //ffi::XK_kana_middledot => VirtualKeyCode::Kana_middledot, +// //ffi::XK_kana_WO => VirtualKeyCode::Kana_wo, +// //ffi::XK_kana_a => VirtualKeyCode::Kana_a, +// //ffi::XK_kana_i => VirtualKeyCode::Kana_i, +// //ffi::XK_kana_u => VirtualKeyCode::Kana_u, +// //ffi::XK_kana_e => VirtualKeyCode::Kana_e, +// //ffi::XK_kana_o => VirtualKeyCode::Kana_o, +// //ffi::XK_kana_ya => VirtualKeyCode::Kana_ya, +// //ffi::XK_kana_yu => VirtualKeyCode::Kana_yu, +// //ffi::XK_kana_yo => VirtualKeyCode::Kana_yo, +// //ffi::XK_kana_tsu => VirtualKeyCode::Kana_tsu, +// //ffi::XK_kana_tu => VirtualKeyCode::Kana_tu, +// //ffi::XK_prolongedsound => VirtualKeyCode::Prolongedsound, +// //ffi::XK_kana_A => VirtualKeyCode::Kana_a, +// //ffi::XK_kana_I => VirtualKeyCode::Kana_i, +// //ffi::XK_kana_U => VirtualKeyCode::Kana_u, +// //ffi::XK_kana_E => VirtualKeyCode::Kana_e, +// //ffi::XK_kana_O => VirtualKeyCode::Kana_o, +// //ffi::XK_kana_KA => VirtualKeyCode::Kana_ka, +// //ffi::XK_kana_KI => VirtualKeyCode::Kana_ki, +// //ffi::XK_kana_KU => VirtualKeyCode::Kana_ku, +// //ffi::XK_kana_KE => VirtualKeyCode::Kana_ke, +// //ffi::XK_kana_KO => VirtualKeyCode::Kana_ko, +// //ffi::XK_kana_SA => VirtualKeyCode::Kana_sa, +// //ffi::XK_kana_SHI => VirtualKeyCode::Kana_shi, +// //ffi::XK_kana_SU => VirtualKeyCode::Kana_su, +// //ffi::XK_kana_SE => VirtualKeyCode::Kana_se, +// //ffi::XK_kana_SO => VirtualKeyCode::Kana_so, +// //ffi::XK_kana_TA => VirtualKeyCode::Kana_ta, +// //ffi::XK_kana_CHI => VirtualKeyCode::Kana_chi, +// //ffi::XK_kana_TI => VirtualKeyCode::Kana_ti, +// //ffi::XK_kana_TSU => VirtualKeyCode::Kana_tsu, +// //ffi::XK_kana_TU => VirtualKeyCode::Kana_tu, +// //ffi::XK_kana_TE => VirtualKeyCode::Kana_te, +// //ffi::XK_kana_TO => VirtualKeyCode::Kana_to, +// //ffi::XK_kana_NA => VirtualKeyCode::Kana_na, +// //ffi::XK_kana_NI => VirtualKeyCode::Kana_ni, +// //ffi::XK_kana_NU => VirtualKeyCode::Kana_nu, +// //ffi::XK_kana_NE => VirtualKeyCode::Kana_ne, +// //ffi::XK_kana_NO => VirtualKeyCode::Kana_no, +// //ffi::XK_kana_HA => VirtualKeyCode::Kana_ha, +// //ffi::XK_kana_HI => VirtualKeyCode::Kana_hi, +// //ffi::XK_kana_FU => VirtualKeyCode::Kana_fu, +// //ffi::XK_kana_HU => VirtualKeyCode::Kana_hu, +// //ffi::XK_kana_HE => VirtualKeyCode::Kana_he, +// //ffi::XK_kana_HO => VirtualKeyCode::Kana_ho, +// //ffi::XK_kana_MA => VirtualKeyCode::Kana_ma, +// //ffi::XK_kana_MI => VirtualKeyCode::Kana_mi, +// //ffi::XK_kana_MU => VirtualKeyCode::Kana_mu, +// //ffi::XK_kana_ME => VirtualKeyCode::Kana_me, +// //ffi::XK_kana_MO => VirtualKeyCode::Kana_mo, +// //ffi::XK_kana_YA => VirtualKeyCode::Kana_ya, +// //ffi::XK_kana_YU => VirtualKeyCode::Kana_yu, +// //ffi::XK_kana_YO => VirtualKeyCode::Kana_yo, +// //ffi::XK_kana_RA => VirtualKeyCode::Kana_ra, +// //ffi::XK_kana_RI => VirtualKeyCode::Kana_ri, +// //ffi::XK_kana_RU => VirtualKeyCode::Kana_ru, +// //ffi::XK_kana_RE => VirtualKeyCode::Kana_re, +// //ffi::XK_kana_RO => VirtualKeyCode::Kana_ro, +// //ffi::XK_kana_WA => VirtualKeyCode::Kana_wa, +// //ffi::XK_kana_N => VirtualKeyCode::Kana_n, +// //ffi::XK_voicedsound => VirtualKeyCode::Voicedsound, +// //ffi::XK_semivoicedsound => VirtualKeyCode::Semivoicedsound, +// //ffi::XK_kana_switch => VirtualKeyCode::Kana_switch, +// //ffi::XK_Arabic_comma => VirtualKeyCode::Arabic_comma, +// //ffi::XK_Arabic_semicolon => VirtualKeyCode::Arabic_semicolon, +// //ffi::XK_Arabic_question_mark => VirtualKeyCode::Arabic_question_mark, +// //ffi::XK_Arabic_hamza => VirtualKeyCode::Arabic_hamza, +// //ffi::XK_Arabic_maddaonalef => VirtualKeyCode::Arabic_maddaonalef, +// //ffi::XK_Arabic_hamzaonalef => VirtualKeyCode::Arabic_hamzaonalef, +// //ffi::XK_Arabic_hamzaonwaw => VirtualKeyCode::Arabic_hamzaonwaw, +// //ffi::XK_Arabic_hamzaunderalef => VirtualKeyCode::Arabic_hamzaunderalef, +// //ffi::XK_Arabic_hamzaonyeh => VirtualKeyCode::Arabic_hamzaonyeh, +// //ffi::XK_Arabic_alef => VirtualKeyCode::Arabic_alef, +// //ffi::XK_Arabic_beh => VirtualKeyCode::Arabic_beh, +// //ffi::XK_Arabic_tehmarbuta => VirtualKeyCode::Arabic_tehmarbuta, +// //ffi::XK_Arabic_teh => VirtualKeyCode::Arabic_teh, +// //ffi::XK_Arabic_theh => VirtualKeyCode::Arabic_theh, +// //ffi::XK_Arabic_jeem => VirtualKeyCode::Arabic_jeem, +// //ffi::XK_Arabic_hah => VirtualKeyCode::Arabic_hah, +// //ffi::XK_Arabic_khah => VirtualKeyCode::Arabic_khah, +// //ffi::XK_Arabic_dal => VirtualKeyCode::Arabic_dal, +// //ffi::XK_Arabic_thal => VirtualKeyCode::Arabic_thal, +// //ffi::XK_Arabic_ra => VirtualKeyCode::Arabic_ra, +// //ffi::XK_Arabic_zain => VirtualKeyCode::Arabic_zain, +// //ffi::XK_Arabic_seen => VirtualKeyCode::Arabic_seen, +// //ffi::XK_Arabic_sheen => VirtualKeyCode::Arabic_sheen, +// //ffi::XK_Arabic_sad => VirtualKeyCode::Arabic_sad, +// //ffi::XK_Arabic_dad => VirtualKeyCode::Arabic_dad, +// //ffi::XK_Arabic_tah => VirtualKeyCode::Arabic_tah, +// //ffi::XK_Arabic_zah => VirtualKeyCode::Arabic_zah, +// //ffi::XK_Arabic_ain => VirtualKeyCode::Arabic_ain, +// //ffi::XK_Arabic_ghain => VirtualKeyCode::Arabic_ghain, +// //ffi::XK_Arabic_tatweel => VirtualKeyCode::Arabic_tatweel, +// //ffi::XK_Arabic_feh => VirtualKeyCode::Arabic_feh, +// //ffi::XK_Arabic_qaf => VirtualKeyCode::Arabic_qaf, +// //ffi::XK_Arabic_kaf => VirtualKeyCode::Arabic_kaf, +// //ffi::XK_Arabic_lam => VirtualKeyCode::Arabic_lam, +// //ffi::XK_Arabic_meem => VirtualKeyCode::Arabic_meem, +// //ffi::XK_Arabic_noon => VirtualKeyCode::Arabic_noon, +// //ffi::XK_Arabic_ha => VirtualKeyCode::Arabic_ha, +// //ffi::XK_Arabic_heh => VirtualKeyCode::Arabic_heh, +// //ffi::XK_Arabic_waw => VirtualKeyCode::Arabic_waw, +// //ffi::XK_Arabic_alefmaksura => VirtualKeyCode::Arabic_alefmaksura, +// //ffi::XK_Arabic_yeh => VirtualKeyCode::Arabic_yeh, +// //ffi::XK_Arabic_fathatan => VirtualKeyCode::Arabic_fathatan, +// //ffi::XK_Arabic_dammatan => VirtualKeyCode::Arabic_dammatan, +// //ffi::XK_Arabic_kasratan => VirtualKeyCode::Arabic_kasratan, +// //ffi::XK_Arabic_fatha => VirtualKeyCode::Arabic_fatha, +// //ffi::XK_Arabic_damma => VirtualKeyCode::Arabic_damma, +// //ffi::XK_Arabic_kasra => VirtualKeyCode::Arabic_kasra, +// //ffi::XK_Arabic_shadda => VirtualKeyCode::Arabic_shadda, +// //ffi::XK_Arabic_sukun => VirtualKeyCode::Arabic_sukun, +// //ffi::XK_Arabic_switch => VirtualKeyCode::Arabic_switch, +// //ffi::XK_Serbian_dje => VirtualKeyCode::Serbian_dje, +// //ffi::XK_Macedonia_gje => VirtualKeyCode::Macedonia_gje, +// //ffi::XK_Cyrillic_io => VirtualKeyCode::Cyrillic_io, +// //ffi::XK_Ukrainian_ie => VirtualKeyCode::Ukrainian_ie, +// //ffi::XK_Ukranian_je => VirtualKeyCode::Ukranian_je, +// //ffi::XK_Macedonia_dse => VirtualKeyCode::Macedonia_dse, +// //ffi::XK_Ukrainian_i => VirtualKeyCode::Ukrainian_i, +// //ffi::XK_Ukranian_i => VirtualKeyCode::Ukranian_i, +// //ffi::XK_Ukrainian_yi => VirtualKeyCode::Ukrainian_yi, +// //ffi::XK_Ukranian_yi => VirtualKeyCode::Ukranian_yi, +// //ffi::XK_Cyrillic_je => VirtualKeyCode::Cyrillic_je, +// //ffi::XK_Serbian_je => VirtualKeyCode::Serbian_je, +// //ffi::XK_Cyrillic_lje => VirtualKeyCode::Cyrillic_lje, +// //ffi::XK_Serbian_lje => VirtualKeyCode::Serbian_lje, +// //ffi::XK_Cyrillic_nje => VirtualKeyCode::Cyrillic_nje, +// //ffi::XK_Serbian_nje => VirtualKeyCode::Serbian_nje, +// //ffi::XK_Serbian_tshe => VirtualKeyCode::Serbian_tshe, +// //ffi::XK_Macedonia_kje => VirtualKeyCode::Macedonia_kje, +// //ffi::XK_Byelorussian_shortu => VirtualKeyCode::Byelorussian_shortu, +// //ffi::XK_Cyrillic_dzhe => VirtualKeyCode::Cyrillic_dzhe, +// //ffi::XK_Serbian_dze => VirtualKeyCode::Serbian_dze, +// //ffi::XK_numerosign => VirtualKeyCode::Numerosign, +// //ffi::XK_Serbian_DJE => VirtualKeyCode::Serbian_dje, +// //ffi::XK_Macedonia_GJE => VirtualKeyCode::Macedonia_gje, +// //ffi::XK_Cyrillic_IO => VirtualKeyCode::Cyrillic_io, +// //ffi::XK_Ukrainian_IE => VirtualKeyCode::Ukrainian_ie, +// //ffi::XK_Ukranian_JE => VirtualKeyCode::Ukranian_je, +// //ffi::XK_Macedonia_DSE => VirtualKeyCode::Macedonia_dse, +// //ffi::XK_Ukrainian_I => VirtualKeyCode::Ukrainian_i, +// //ffi::XK_Ukranian_I => VirtualKeyCode::Ukranian_i, +// //ffi::XK_Ukrainian_YI => VirtualKeyCode::Ukrainian_yi, +// //ffi::XK_Ukranian_YI => VirtualKeyCode::Ukranian_yi, +// //ffi::XK_Cyrillic_JE => VirtualKeyCode::Cyrillic_je, +// //ffi::XK_Serbian_JE => VirtualKeyCode::Serbian_je, +// //ffi::XK_Cyrillic_LJE => VirtualKeyCode::Cyrillic_lje, +// //ffi::XK_Serbian_LJE => VirtualKeyCode::Serbian_lje, +// //ffi::XK_Cyrillic_NJE => VirtualKeyCode::Cyrillic_nje, +// //ffi::XK_Serbian_NJE => VirtualKeyCode::Serbian_nje, +// //ffi::XK_Serbian_TSHE => VirtualKeyCode::Serbian_tshe, +// //ffi::XK_Macedonia_KJE => VirtualKeyCode::Macedonia_kje, +// //ffi::XK_Byelorussian_SHORTU => VirtualKeyCode::Byelorussian_shortu, +// //ffi::XK_Cyrillic_DZHE => VirtualKeyCode::Cyrillic_dzhe, +// //ffi::XK_Serbian_DZE => VirtualKeyCode::Serbian_dze, +// //ffi::XK_Cyrillic_yu => VirtualKeyCode::Cyrillic_yu, +// //ffi::XK_Cyrillic_a => VirtualKeyCode::Cyrillic_a, +// //ffi::XK_Cyrillic_be => VirtualKeyCode::Cyrillic_be, +// //ffi::XK_Cyrillic_tse => VirtualKeyCode::Cyrillic_tse, +// //ffi::XK_Cyrillic_de => VirtualKeyCode::Cyrillic_de, +// //ffi::XK_Cyrillic_ie => VirtualKeyCode::Cyrillic_ie, +// //ffi::XK_Cyrillic_ef => VirtualKeyCode::Cyrillic_ef, +// //ffi::XK_Cyrillic_ghe => VirtualKeyCode::Cyrillic_ghe, +// //ffi::XK_Cyrillic_ha => VirtualKeyCode::Cyrillic_ha, +// //ffi::XK_Cyrillic_i => VirtualKeyCode::Cyrillic_i, +// //ffi::XK_Cyrillic_shorti => VirtualKeyCode::Cyrillic_shorti, +// //ffi::XK_Cyrillic_ka => VirtualKeyCode::Cyrillic_ka, +// //ffi::XK_Cyrillic_el => VirtualKeyCode::Cyrillic_el, +// //ffi::XK_Cyrillic_em => VirtualKeyCode::Cyrillic_em, +// //ffi::XK_Cyrillic_en => VirtualKeyCode::Cyrillic_en, +// //ffi::XK_Cyrillic_o => VirtualKeyCode::Cyrillic_o, +// //ffi::XK_Cyrillic_pe => VirtualKeyCode::Cyrillic_pe, +// //ffi::XK_Cyrillic_ya => VirtualKeyCode::Cyrillic_ya, +// //ffi::XK_Cyrillic_er => VirtualKeyCode::Cyrillic_er, +// //ffi::XK_Cyrillic_es => VirtualKeyCode::Cyrillic_es, +// //ffi::XK_Cyrillic_te => VirtualKeyCode::Cyrillic_te, +// //ffi::XK_Cyrillic_u => VirtualKeyCode::Cyrillic_u, +// //ffi::XK_Cyrillic_zhe => VirtualKeyCode::Cyrillic_zhe, +// //ffi::XK_Cyrillic_ve => VirtualKeyCode::Cyrillic_ve, +// //ffi::XK_Cyrillic_softsign => VirtualKeyCode::Cyrillic_softsign, +// //ffi::XK_Cyrillic_yeru => VirtualKeyCode::Cyrillic_yeru, +// //ffi::XK_Cyrillic_ze => VirtualKeyCode::Cyrillic_ze, +// //ffi::XK_Cyrillic_sha => VirtualKeyCode::Cyrillic_sha, +// //ffi::XK_Cyrillic_e => VirtualKeyCode::Cyrillic_e, +// //ffi::XK_Cyrillic_shcha => VirtualKeyCode::Cyrillic_shcha, +// //ffi::XK_Cyrillic_che => VirtualKeyCode::Cyrillic_che, +// //ffi::XK_Cyrillic_hardsign => VirtualKeyCode::Cyrillic_hardsign, +// //ffi::XK_Cyrillic_YU => VirtualKeyCode::Cyrillic_yu, +// //ffi::XK_Cyrillic_A => VirtualKeyCode::Cyrillic_a, +// //ffi::XK_Cyrillic_BE => VirtualKeyCode::Cyrillic_be, +// //ffi::XK_Cyrillic_TSE => VirtualKeyCode::Cyrillic_tse, +// //ffi::XK_Cyrillic_DE => VirtualKeyCode::Cyrillic_de, +// //ffi::XK_Cyrillic_IE => VirtualKeyCode::Cyrillic_ie, +// //ffi::XK_Cyrillic_EF => VirtualKeyCode::Cyrillic_ef, +// //ffi::XK_Cyrillic_GHE => VirtualKeyCode::Cyrillic_ghe, +// //ffi::XK_Cyrillic_HA => VirtualKeyCode::Cyrillic_ha, +// //ffi::XK_Cyrillic_I => VirtualKeyCode::Cyrillic_i, +// //ffi::XK_Cyrillic_SHORTI => VirtualKeyCode::Cyrillic_shorti, +// //ffi::XK_Cyrillic_KA => VirtualKeyCode::Cyrillic_ka, +// //ffi::XK_Cyrillic_EL => VirtualKeyCode::Cyrillic_el, +// //ffi::XK_Cyrillic_EM => VirtualKeyCode::Cyrillic_em, +// //ffi::XK_Cyrillic_EN => VirtualKeyCode::Cyrillic_en, +// //ffi::XK_Cyrillic_O => VirtualKeyCode::Cyrillic_o, +// //ffi::XK_Cyrillic_PE => VirtualKeyCode::Cyrillic_pe, +// //ffi::XK_Cyrillic_YA => VirtualKeyCode::Cyrillic_ya, +// //ffi::XK_Cyrillic_ER => VirtualKeyCode::Cyrillic_er, +// //ffi::XK_Cyrillic_ES => VirtualKeyCode::Cyrillic_es, +// //ffi::XK_Cyrillic_TE => VirtualKeyCode::Cyrillic_te, +// //ffi::XK_Cyrillic_U => VirtualKeyCode::Cyrillic_u, +// //ffi::XK_Cyrillic_ZHE => VirtualKeyCode::Cyrillic_zhe, +// //ffi::XK_Cyrillic_VE => VirtualKeyCode::Cyrillic_ve, +// //ffi::XK_Cyrillic_SOFTSIGN => VirtualKeyCode::Cyrillic_softsign, +// //ffi::XK_Cyrillic_YERU => VirtualKeyCode::Cyrillic_yeru, +// //ffi::XK_Cyrillic_ZE => VirtualKeyCode::Cyrillic_ze, +// //ffi::XK_Cyrillic_SHA => VirtualKeyCode::Cyrillic_sha, +// //ffi::XK_Cyrillic_E => VirtualKeyCode::Cyrillic_e, +// //ffi::XK_Cyrillic_SHCHA => VirtualKeyCode::Cyrillic_shcha, +// //ffi::XK_Cyrillic_CHE => VirtualKeyCode::Cyrillic_che, +// //ffi::XK_Cyrillic_HARDSIGN => VirtualKeyCode::Cyrillic_hardsign, +// //ffi::XK_Greek_ALPHAaccent => VirtualKeyCode::Greek_alphaaccent, +// //ffi::XK_Greek_EPSILONaccent => VirtualKeyCode::Greek_epsilonaccent, +// //ffi::XK_Greek_ETAaccent => VirtualKeyCode::Greek_etaaccent, +// //ffi::XK_Greek_IOTAaccent => VirtualKeyCode::Greek_iotaaccent, +// //ffi::XK_Greek_IOTAdiaeresis => VirtualKeyCode::Greek_iotadiaeresis, +// //ffi::XK_Greek_OMICRONaccent => VirtualKeyCode::Greek_omicronaccent, +// //ffi::XK_Greek_UPSILONaccent => VirtualKeyCode::Greek_upsilonaccent, +// //ffi::XK_Greek_UPSILONdieresis => VirtualKeyCode::Greek_upsilondieresis, +// //ffi::XK_Greek_OMEGAaccent => VirtualKeyCode::Greek_omegaaccent, +// //ffi::XK_Greek_accentdieresis => VirtualKeyCode::Greek_accentdieresis, +// //ffi::XK_Greek_horizbar => VirtualKeyCode::Greek_horizbar, +// //ffi::XK_Greek_alphaaccent => VirtualKeyCode::Greek_alphaaccent, +// //ffi::XK_Greek_epsilonaccent => VirtualKeyCode::Greek_epsilonaccent, +// //ffi::XK_Greek_etaaccent => VirtualKeyCode::Greek_etaaccent, +// //ffi::XK_Greek_iotaaccent => VirtualKeyCode::Greek_iotaaccent, +// //ffi::XK_Greek_iotadieresis => VirtualKeyCode::Greek_iotadieresis, +// //ffi::XK_Greek_iotaaccentdieresis => VirtualKeyCode::Greek_iotaaccentdieresis, +// //ffi::XK_Greek_omicronaccent => VirtualKeyCode::Greek_omicronaccent, +// //ffi::XK_Greek_upsilonaccent => VirtualKeyCode::Greek_upsilonaccent, +// //ffi::XK_Greek_upsilondieresis => VirtualKeyCode::Greek_upsilondieresis, +// //ffi::XK_Greek_upsilonaccentdieresis => VirtualKeyCode::Greek_upsilonaccentdieresis, +// //ffi::XK_Greek_omegaaccent => VirtualKeyCode::Greek_omegaaccent, +// //ffi::XK_Greek_ALPHA => VirtualKeyCode::Greek_alpha, +// //ffi::XK_Greek_BETA => VirtualKeyCode::Greek_beta, +// //ffi::XK_Greek_GAMMA => VirtualKeyCode::Greek_gamma, +// //ffi::XK_Greek_DELTA => VirtualKeyCode::Greek_delta, +// //ffi::XK_Greek_EPSILON => VirtualKeyCode::Greek_epsilon, +// //ffi::XK_Greek_ZETA => VirtualKeyCode::Greek_zeta, +// //ffi::XK_Greek_ETA => VirtualKeyCode::Greek_eta, +// //ffi::XK_Greek_THETA => VirtualKeyCode::Greek_theta, +// //ffi::XK_Greek_IOTA => VirtualKeyCode::Greek_iota, +// //ffi::XK_Greek_KAPPA => VirtualKeyCode::Greek_kappa, +// //ffi::XK_Greek_LAMDA => VirtualKeyCode::Greek_lamda, +// //ffi::XK_Greek_LAMBDA => VirtualKeyCode::Greek_lambda, +// //ffi::XK_Greek_MU => VirtualKeyCode::Greek_mu, +// //ffi::XK_Greek_NU => VirtualKeyCode::Greek_nu, +// //ffi::XK_Greek_XI => VirtualKeyCode::Greek_xi, +// //ffi::XK_Greek_OMICRON => VirtualKeyCode::Greek_omicron, +// //ffi::XK_Greek_PI => VirtualKeyCode::Greek_pi, +// //ffi::XK_Greek_RHO => VirtualKeyCode::Greek_rho, +// //ffi::XK_Greek_SIGMA => VirtualKeyCode::Greek_sigma, +// //ffi::XK_Greek_TAU => VirtualKeyCode::Greek_tau, +// //ffi::XK_Greek_UPSILON => VirtualKeyCode::Greek_upsilon, +// //ffi::XK_Greek_PHI => VirtualKeyCode::Greek_phi, +// //ffi::XK_Greek_CHI => VirtualKeyCode::Greek_chi, +// //ffi::XK_Greek_PSI => VirtualKeyCode::Greek_psi, +// //ffi::XK_Greek_OMEGA => VirtualKeyCode::Greek_omega, +// //ffi::XK_Greek_alpha => VirtualKeyCode::Greek_alpha, +// //ffi::XK_Greek_beta => VirtualKeyCode::Greek_beta, +// //ffi::XK_Greek_gamma => VirtualKeyCode::Greek_gamma, +// //ffi::XK_Greek_delta => VirtualKeyCode::Greek_delta, +// //ffi::XK_Greek_epsilon => VirtualKeyCode::Greek_epsilon, +// //ffi::XK_Greek_zeta => VirtualKeyCode::Greek_zeta, +// //ffi::XK_Greek_eta => VirtualKeyCode::Greek_eta, +// //ffi::XK_Greek_theta => VirtualKeyCode::Greek_theta, +// //ffi::XK_Greek_iota => VirtualKeyCode::Greek_iota, +// //ffi::XK_Greek_kappa => VirtualKeyCode::Greek_kappa, +// //ffi::XK_Greek_lamda => VirtualKeyCode::Greek_lamda, +// //ffi::XK_Greek_lambda => VirtualKeyCode::Greek_lambda, +// //ffi::XK_Greek_mu => VirtualKeyCode::Greek_mu, +// //ffi::XK_Greek_nu => VirtualKeyCode::Greek_nu, +// //ffi::XK_Greek_xi => VirtualKeyCode::Greek_xi, +// //ffi::XK_Greek_omicron => VirtualKeyCode::Greek_omicron, +// //ffi::XK_Greek_pi => VirtualKeyCode::Greek_pi, +// //ffi::XK_Greek_rho => VirtualKeyCode::Greek_rho, +// //ffi::XK_Greek_sigma => VirtualKeyCode::Greek_sigma, +// //ffi::XK_Greek_finalsmallsigma => VirtualKeyCode::Greek_finalsmallsigma, +// //ffi::XK_Greek_tau => VirtualKeyCode::Greek_tau, +// //ffi::XK_Greek_upsilon => VirtualKeyCode::Greek_upsilon, +// //ffi::XK_Greek_phi => VirtualKeyCode::Greek_phi, +// //ffi::XK_Greek_chi => VirtualKeyCode::Greek_chi, +// //ffi::XK_Greek_psi => VirtualKeyCode::Greek_psi, +// //ffi::XK_Greek_omega => VirtualKeyCode::Greek_omega, +// //ffi::XK_Greek_switch => VirtualKeyCode::Greek_switch, +// //ffi::XK_leftradical => VirtualKeyCode::Leftradical, +// //ffi::XK_topleftradical => VirtualKeyCode::Topleftradical, +// //ffi::XK_horizconnector => VirtualKeyCode::Horizconnector, +// //ffi::XK_topintegral => VirtualKeyCode::Topintegral, +// //ffi::XK_botintegral => VirtualKeyCode::Botintegral, +// //ffi::XK_vertconnector => VirtualKeyCode::Vertconnector, +// //ffi::XK_topleftsqbracket => VirtualKeyCode::Topleftsqbracket, +// //ffi::XK_botleftsqbracket => VirtualKeyCode::Botleftsqbracket, +// //ffi::XK_toprightsqbracket => VirtualKeyCode::Toprightsqbracket, +// //ffi::XK_botrightsqbracket => VirtualKeyCode::Botrightsqbracket, +// //ffi::XK_topleftparens => VirtualKeyCode::Topleftparens, +// //ffi::XK_botleftparens => VirtualKeyCode::Botleftparens, +// //ffi::XK_toprightparens => VirtualKeyCode::Toprightparens, +// //ffi::XK_botrightparens => VirtualKeyCode::Botrightparens, +// //ffi::XK_leftmiddlecurlybrace => VirtualKeyCode::Leftmiddlecurlybrace, +// //ffi::XK_rightmiddlecurlybrace => VirtualKeyCode::Rightmiddlecurlybrace, +// //ffi::XK_topleftsummation => VirtualKeyCode::Topleftsummation, +// //ffi::XK_botleftsummation => VirtualKeyCode::Botleftsummation, +// //ffi::XK_topvertsummationconnector => VirtualKeyCode::Topvertsummationconnector, +// //ffi::XK_botvertsummationconnector => VirtualKeyCode::Botvertsummationconnector, +// //ffi::XK_toprightsummation => VirtualKeyCode::Toprightsummation, +// //ffi::XK_botrightsummation => VirtualKeyCode::Botrightsummation, +// //ffi::XK_rightmiddlesummation => VirtualKeyCode::Rightmiddlesummation, +// //ffi::XK_lessthanequal => VirtualKeyCode::Lessthanequal, +// //ffi::XK_notequal => VirtualKeyCode::Notequal, +// //ffi::XK_greaterthanequal => VirtualKeyCode::Greaterthanequal, +// //ffi::XK_integral => VirtualKeyCode::Integral, +// //ffi::XK_therefore => VirtualKeyCode::Therefore, +// //ffi::XK_variation => VirtualKeyCode::Variation, +// //ffi::XK_infinity => VirtualKeyCode::Infinity, +// //ffi::XK_nabla => VirtualKeyCode::Nabla, +// //ffi::XK_approximate => VirtualKeyCode::Approximate, +// //ffi::XK_similarequal => VirtualKeyCode::Similarequal, +// //ffi::XK_ifonlyif => VirtualKeyCode::Ifonlyif, +// //ffi::XK_implies => VirtualKeyCode::Implies, +// //ffi::XK_identical => VirtualKeyCode::Identical, +// //ffi::XK_radical => VirtualKeyCode::Radical, +// //ffi::XK_includedin => VirtualKeyCode::Includedin, +// //ffi::XK_includes => VirtualKeyCode::Includes, +// //ffi::XK_intersection => VirtualKeyCode::Intersection, +// //ffi::XK_union => VirtualKeyCode::Union, +// //ffi::XK_logicaland => VirtualKeyCode::Logicaland, +// //ffi::XK_logicalor => VirtualKeyCode::Logicalor, +// //ffi::XK_partialderivative => VirtualKeyCode::Partialderivative, +// //ffi::XK_function => VirtualKeyCode::Function, +// //ffi::XK_leftarrow => VirtualKeyCode::Leftarrow, +// //ffi::XK_uparrow => VirtualKeyCode::Uparrow, +// //ffi::XK_rightarrow => VirtualKeyCode::Rightarrow, +// //ffi::XK_downarrow => VirtualKeyCode::Downarrow, +// //ffi::XK_blank => VirtualKeyCode::Blank, +// //ffi::XK_soliddiamond => VirtualKeyCode::Soliddiamond, +// //ffi::XK_checkerboard => VirtualKeyCode::Checkerboard, +// //ffi::XK_ht => VirtualKeyCode::Ht, +// //ffi::XK_ff => VirtualKeyCode::Ff, +// //ffi::XK_cr => VirtualKeyCode::Cr, +// //ffi::XK_lf => VirtualKeyCode::Lf, +// //ffi::XK_nl => VirtualKeyCode::Nl, +// //ffi::XK_vt => VirtualKeyCode::Vt, +// //ffi::XK_lowrightcorner => VirtualKeyCode::Lowrightcorner, +// //ffi::XK_uprightcorner => VirtualKeyCode::Uprightcorner, +// //ffi::XK_upleftcorner => VirtualKeyCode::Upleftcorner, +// //ffi::XK_lowleftcorner => VirtualKeyCode::Lowleftcorner, +// //ffi::XK_crossinglines => VirtualKeyCode::Crossinglines, +// //ffi::XK_horizlinescan1 => VirtualKeyCode::Horizlinescan1, +// //ffi::XK_horizlinescan3 => VirtualKeyCode::Horizlinescan3, +// //ffi::XK_horizlinescan5 => VirtualKeyCode::Horizlinescan5, +// //ffi::XK_horizlinescan7 => VirtualKeyCode::Horizlinescan7, +// //ffi::XK_horizlinescan9 => VirtualKeyCode::Horizlinescan9, +// //ffi::XK_leftt => VirtualKeyCode::Leftt, +// //ffi::XK_rightt => VirtualKeyCode::Rightt, +// //ffi::XK_bott => VirtualKeyCode::Bott, +// //ffi::XK_topt => VirtualKeyCode::Topt, +// //ffi::XK_vertbar => VirtualKeyCode::Vertbar, +// //ffi::XK_emspace => VirtualKeyCode::Emspace, +// //ffi::XK_enspace => VirtualKeyCode::Enspace, +// //ffi::XK_em3space => VirtualKeyCode::Em3space, +// //ffi::XK_em4space => VirtualKeyCode::Em4space, +// //ffi::XK_digitspace => VirtualKeyCode::Digitspace, +// //ffi::XK_punctspace => VirtualKeyCode::Punctspace, +// //ffi::XK_thinspace => VirtualKeyCode::Thinspace, +// //ffi::XK_hairspace => VirtualKeyCode::Hairspace, +// //ffi::XK_emdash => VirtualKeyCode::Emdash, +// //ffi::XK_endash => VirtualKeyCode::Endash, +// //ffi::XK_signifblank => VirtualKeyCode::Signifblank, +// //ffi::XK_ellipsis => VirtualKeyCode::Ellipsis, +// //ffi::XK_doubbaselinedot => VirtualKeyCode::Doubbaselinedot, +// //ffi::XK_onethird => VirtualKeyCode::Onethird, +// //ffi::XK_twothirds => VirtualKeyCode::Twothirds, +// //ffi::XK_onefifth => VirtualKeyCode::Onefifth, +// //ffi::XK_twofifths => VirtualKeyCode::Twofifths, +// //ffi::XK_threefifths => VirtualKeyCode::Threefifths, +// //ffi::XK_fourfifths => VirtualKeyCode::Fourfifths, +// //ffi::XK_onesixth => VirtualKeyCode::Onesixth, +// //ffi::XK_fivesixths => VirtualKeyCode::Fivesixths, +// //ffi::XK_careof => VirtualKeyCode::Careof, +// //ffi::XK_figdash => VirtualKeyCode::Figdash, +// //ffi::XK_leftanglebracket => VirtualKeyCode::Leftanglebracket, +// //ffi::XK_decimalpoint => VirtualKeyCode::Decimalpoint, +// //ffi::XK_rightanglebracket => VirtualKeyCode::Rightanglebracket, +// //ffi::XK_marker => VirtualKeyCode::Marker, +// //ffi::XK_oneeighth => VirtualKeyCode::Oneeighth, +// //ffi::XK_threeeighths => VirtualKeyCode::Threeeighths, +// //ffi::XK_fiveeighths => VirtualKeyCode::Fiveeighths, +// //ffi::XK_seveneighths => VirtualKeyCode::Seveneighths, +// //ffi::XK_trademark => VirtualKeyCode::Trademark, +// //ffi::XK_signaturemark => VirtualKeyCode::Signaturemark, +// //ffi::XK_trademarkincircle => VirtualKeyCode::Trademarkincircle, +// //ffi::XK_leftopentriangle => VirtualKeyCode::Leftopentriangle, +// //ffi::XK_rightopentriangle => VirtualKeyCode::Rightopentriangle, +// //ffi::XK_emopencircle => VirtualKeyCode::Emopencircle, +// //ffi::XK_emopenrectangle => VirtualKeyCode::Emopenrectangle, +// //ffi::XK_leftsinglequotemark => VirtualKeyCode::Leftsinglequotemark, +// //ffi::XK_rightsinglequotemark => VirtualKeyCode::Rightsinglequotemark, +// //ffi::XK_leftdoublequotemark => VirtualKeyCode::Leftdoublequotemark, +// //ffi::XK_rightdoublequotemark => VirtualKeyCode::Rightdoublequotemark, +// //ffi::XK_prescription => VirtualKeyCode::Prescription, +// //ffi::XK_minutes => VirtualKeyCode::Minutes, +// //ffi::XK_seconds => VirtualKeyCode::Seconds, +// //ffi::XK_latincross => VirtualKeyCode::Latincross, +// //ffi::XK_hexagram => VirtualKeyCode::Hexagram, +// //ffi::XK_filledrectbullet => VirtualKeyCode::Filledrectbullet, +// //ffi::XK_filledlefttribullet => VirtualKeyCode::Filledlefttribullet, +// //ffi::XK_filledrighttribullet => VirtualKeyCode::Filledrighttribullet, +// //ffi::XK_emfilledcircle => VirtualKeyCode::Emfilledcircle, +// //ffi::XK_emfilledrect => VirtualKeyCode::Emfilledrect, +// //ffi::XK_enopencircbullet => VirtualKeyCode::Enopencircbullet, +// //ffi::XK_enopensquarebullet => VirtualKeyCode::Enopensquarebullet, +// //ffi::XK_openrectbullet => VirtualKeyCode::Openrectbullet, +// //ffi::XK_opentribulletup => VirtualKeyCode::Opentribulletup, +// //ffi::XK_opentribulletdown => VirtualKeyCode::Opentribulletdown, +// //ffi::XK_openstar => VirtualKeyCode::Openstar, +// //ffi::XK_enfilledcircbullet => VirtualKeyCode::Enfilledcircbullet, +// //ffi::XK_enfilledsqbullet => VirtualKeyCode::Enfilledsqbullet, +// //ffi::XK_filledtribulletup => VirtualKeyCode::Filledtribulletup, +// //ffi::XK_filledtribulletdown => VirtualKeyCode::Filledtribulletdown, +// //ffi::XK_leftpointer => VirtualKeyCode::Leftpointer, +// //ffi::XK_rightpointer => VirtualKeyCode::Rightpointer, +// //ffi::XK_club => VirtualKeyCode::Club, +// //ffi::XK_diamond => VirtualKeyCode::Diamond, +// //ffi::XK_heart => VirtualKeyCode::Heart, +// //ffi::XK_maltesecross => VirtualKeyCode::Maltesecross, +// //ffi::XK_dagger => VirtualKeyCode::Dagger, +// //ffi::XK_doubledagger => VirtualKeyCode::Doubledagger, +// //ffi::XK_checkmark => VirtualKeyCode::Checkmark, +// //ffi::XK_ballotcross => VirtualKeyCode::Ballotcross, +// //ffi::XK_musicalsharp => VirtualKeyCode::Musicalsharp, +// //ffi::XK_musicalflat => VirtualKeyCode::Musicalflat, +// //ffi::XK_malesymbol => VirtualKeyCode::Malesymbol, +// //ffi::XK_femalesymbol => VirtualKeyCode::Femalesymbol, +// //ffi::XK_telephone => VirtualKeyCode::Telephone, +// //ffi::XK_telephonerecorder => VirtualKeyCode::Telephonerecorder, +// //ffi::XK_phonographcopyright => VirtualKeyCode::Phonographcopyright, +// //ffi::XK_caret => VirtualKeyCode::Caret, +// //ffi::XK_singlelowquotemark => VirtualKeyCode::Singlelowquotemark, +// //ffi::XK_doublelowquotemark => VirtualKeyCode::Doublelowquotemark, +// //ffi::XK_cursor => VirtualKeyCode::Cursor, +// //ffi::XK_leftcaret => VirtualKeyCode::Leftcaret, +// //ffi::XK_rightcaret => VirtualKeyCode::Rightcaret, +// //ffi::XK_downcaret => VirtualKeyCode::Downcaret, +// //ffi::XK_upcaret => VirtualKeyCode::Upcaret, +// //ffi::XK_overbar => VirtualKeyCode::Overbar, +// //ffi::XK_downtack => VirtualKeyCode::Downtack, +// //ffi::XK_upshoe => VirtualKeyCode::Upshoe, +// //ffi::XK_downstile => VirtualKeyCode::Downstile, +// //ffi::XK_underbar => VirtualKeyCode::Underbar, +// //ffi::XK_jot => VirtualKeyCode::Jot, +// //ffi::XK_quad => VirtualKeyCode::Quad, +// //ffi::XK_uptack => VirtualKeyCode::Uptack, +// //ffi::XK_circle => VirtualKeyCode::Circle, +// //ffi::XK_upstile => VirtualKeyCode::Upstile, +// //ffi::XK_downshoe => VirtualKeyCode::Downshoe, +// //ffi::XK_rightshoe => VirtualKeyCode::Rightshoe, +// //ffi::XK_leftshoe => VirtualKeyCode::Leftshoe, +// //ffi::XK_lefttack => VirtualKeyCode::Lefttack, +// //ffi::XK_righttack => VirtualKeyCode::Righttack, +// //ffi::XK_hebrew_doublelowline => VirtualKeyCode::Hebrew_doublelowline, +// //ffi::XK_hebrew_aleph => VirtualKeyCode::Hebrew_aleph, +// //ffi::XK_hebrew_bet => VirtualKeyCode::Hebrew_bet, +// //ffi::XK_hebrew_beth => VirtualKeyCode::Hebrew_beth, +// //ffi::XK_hebrew_gimel => VirtualKeyCode::Hebrew_gimel, +// //ffi::XK_hebrew_gimmel => VirtualKeyCode::Hebrew_gimmel, +// //ffi::XK_hebrew_dalet => VirtualKeyCode::Hebrew_dalet, +// //ffi::XK_hebrew_daleth => VirtualKeyCode::Hebrew_daleth, +// //ffi::XK_hebrew_he => VirtualKeyCode::Hebrew_he, +// //ffi::XK_hebrew_waw => VirtualKeyCode::Hebrew_waw, +// //ffi::XK_hebrew_zain => VirtualKeyCode::Hebrew_zain, +// //ffi::XK_hebrew_zayin => VirtualKeyCode::Hebrew_zayin, +// //ffi::XK_hebrew_chet => VirtualKeyCode::Hebrew_chet, +// //ffi::XK_hebrew_het => VirtualKeyCode::Hebrew_het, +// //ffi::XK_hebrew_tet => VirtualKeyCode::Hebrew_tet, +// //ffi::XK_hebrew_teth => VirtualKeyCode::Hebrew_teth, +// //ffi::XK_hebrew_yod => VirtualKeyCode::Hebrew_yod, +// //ffi::XK_hebrew_finalkaph => VirtualKeyCode::Hebrew_finalkaph, +// //ffi::XK_hebrew_kaph => VirtualKeyCode::Hebrew_kaph, +// //ffi::XK_hebrew_lamed => VirtualKeyCode::Hebrew_lamed, +// //ffi::XK_hebrew_finalmem => VirtualKeyCode::Hebrew_finalmem, +// //ffi::XK_hebrew_mem => VirtualKeyCode::Hebrew_mem, +// //ffi::XK_hebrew_finalnun => VirtualKeyCode::Hebrew_finalnun, +// //ffi::XK_hebrew_nun => VirtualKeyCode::Hebrew_nun, +// //ffi::XK_hebrew_samech => VirtualKeyCode::Hebrew_samech, +// //ffi::XK_hebrew_samekh => VirtualKeyCode::Hebrew_samekh, +// //ffi::XK_hebrew_ayin => VirtualKeyCode::Hebrew_ayin, +// //ffi::XK_hebrew_finalpe => VirtualKeyCode::Hebrew_finalpe, +// //ffi::XK_hebrew_pe => VirtualKeyCode::Hebrew_pe, +// //ffi::XK_hebrew_finalzade => VirtualKeyCode::Hebrew_finalzade, +// //ffi::XK_hebrew_finalzadi => VirtualKeyCode::Hebrew_finalzadi, +// //ffi::XK_hebrew_zade => VirtualKeyCode::Hebrew_zade, +// //ffi::XK_hebrew_zadi => VirtualKeyCode::Hebrew_zadi, +// //ffi::XK_hebrew_qoph => VirtualKeyCode::Hebrew_qoph, +// //ffi::XK_hebrew_kuf => VirtualKeyCode::Hebrew_kuf, +// //ffi::XK_hebrew_resh => VirtualKeyCode::Hebrew_resh, +// //ffi::XK_hebrew_shin => VirtualKeyCode::Hebrew_shin, +// //ffi::XK_hebrew_taw => VirtualKeyCode::Hebrew_taw, +// //ffi::XK_hebrew_taf => VirtualKeyCode::Hebrew_taf, +// //ffi::XK_Hebrew_switch => VirtualKeyCode::Hebrew_switch, +// ffi::XF86XK_Back => VirtualKeyCode::NavigateBackward, +// ffi::XF86XK_Forward => VirtualKeyCode::NavigateForward, +// ffi::XF86XK_Copy => VirtualKeyCode::Copy, +// ffi::XF86XK_Paste => VirtualKeyCode::Paste, +// ffi::XF86XK_Cut => VirtualKeyCode::Cut, +// _ => return None, +// }) +// } diff --git a/src/platform_impl/linux/x11/util/input.rs b/src/platform_impl/linux/x11/util/input.rs index e9f45aee1c..6c7c651eed 100644 --- a/src/platform_impl/linux/x11/util/input.rs +++ b/src/platform_impl/linux/x11/util/input.rs @@ -1,7 +1,7 @@ use std::{slice, str}; use super::*; -use crate::event::ModifiersState; +use crate::keyboard::ModifiersState; pub const VIRTUAL_CORE_POINTER: c_int = 2; pub const VIRTUAL_CORE_KEYBOARD: c_int = 3; @@ -20,8 +20,8 @@ impl ModifiersState { let mut m = ModifiersState::empty(); m.set(ModifiersState::ALT, mask & ffi::Mod1Mask != 0); m.set(ModifiersState::SHIFT, mask & ffi::ShiftMask != 0); - m.set(ModifiersState::CTRL, mask & ffi::ControlMask != 0); - m.set(ModifiersState::LOGO, mask & ffi::Mod4Mask != 0); + m.set(ModifiersState::CONTROL, mask & ffi::ControlMask != 0); + m.set(ModifiersState::SUPER, mask & ffi::Mod4Mask != 0); m } } diff --git a/src/platform_impl/linux/x11/util/modifiers.rs b/src/platform_impl/linux/x11/util/modifiers.rs index 7c951997ad..d7e10fa0b2 100644 --- a/src/platform_impl/linux/x11/util/modifiers.rs +++ b/src/platform_impl/linux/x11/util/modifiers.rs @@ -2,7 +2,8 @@ use std::{collections::HashMap, slice}; use super::*; -use crate::event::{ElementState, ModifiersState}; +use crate::event::ElementState; +use crate::keyboard::ModifiersState; // Offsets within XModifierKeymap to each set of keycodes. // We are only interested in Shift, Control, Alt, and Logo. @@ -116,10 +117,12 @@ impl ModifierKeyState { let mut new_state = *state; match except { - Some(Modifier::Alt) => new_state.set(ModifiersState::ALT, self.state.alt()), - Some(Modifier::Ctrl) => new_state.set(ModifiersState::CTRL, self.state.ctrl()), - Some(Modifier::Shift) => new_state.set(ModifiersState::SHIFT, self.state.shift()), - Some(Modifier::Logo) => new_state.set(ModifiersState::LOGO, self.state.logo()), + Some(Modifier::Alt) => new_state.set(ModifiersState::ALT, self.state.alt_key()), + Some(Modifier::Ctrl) => { + new_state.set(ModifiersState::CONTROL, self.state.control_key()) + } + Some(Modifier::Shift) => new_state.set(ModifiersState::SHIFT, self.state.shift_key()), + Some(Modifier::Logo) => new_state.set(ModifiersState::SUPER, self.state.super_key()), None => (), } @@ -170,18 +173,18 @@ impl ModifierKeyState { fn get_modifier(state: &ModifiersState, modifier: Modifier) -> bool { match modifier { - Modifier::Alt => state.alt(), - Modifier::Ctrl => state.ctrl(), - Modifier::Shift => state.shift(), - Modifier::Logo => state.logo(), + Modifier::Alt => state.alt_key(), + Modifier::Ctrl => state.control_key(), + Modifier::Shift => state.shift_key(), + Modifier::Logo => state.super_key(), } } fn set_modifier(state: &mut ModifiersState, modifier: Modifier, value: bool) { match modifier { Modifier::Alt => state.set(ModifiersState::ALT, value), - Modifier::Ctrl => state.set(ModifiersState::CTRL, value), + Modifier::Ctrl => state.set(ModifiersState::CONTROL, value), Modifier::Shift => state.set(ModifiersState::SHIFT, value), - Modifier::Logo => state.set(ModifiersState::LOGO, value), + Modifier::Logo => state.set(ModifiersState::SUPER, value), } } From fd2e1acb5d0ac917fee4c435687325ec99f8737e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Sat, 20 Mar 2021 00:46:14 +0100 Subject: [PATCH 069/131] WIP --- .../linux/wayland/seat/keyboard/handlers.rs | 35 +- .../linux/wayland/seat/keyboard/keymap.rs | 694 ++++++++++++------ 2 files changed, 493 insertions(+), 236 deletions(-) diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs index d81e440cc5..47cbc984ad 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs @@ -5,9 +5,10 @@ use sctk::reexports::client::protocol::wl_keyboard::KeyState; use sctk::seat::keyboard::Event as KeyboardEvent; use crate::event::{ElementState, KeyEvent, WindowEvent}; -use crate::keyboard::ModifiersState; +use crate::keyboard::{Key, KeyLocation, ModifiersState, NativeKeyCode}; use crate::platform_impl::wayland::event_loop::WinitState; use crate::platform_impl::wayland::{self, DeviceId}; +use crate::platform_impl::KeyEventExtra; use super::keymap; use super::KeyboardInner; @@ -69,7 +70,8 @@ pub(super) fn handle_keyboard( _ => unreachable!(), }; - // let virtual_keycode = keymap::keysym_to_vkey(keysym); + let physical_key = keymap::rawkey_to_keycode(rawkey); + let logical_key = keymap::keysym_to_key(keysym); event_sink.push_window_event( WindowEvent::KeyboardInput { @@ -77,13 +79,16 @@ pub(super) fn handle_keyboard( DeviceId, )), event: KeyEvent { - physical_key: todo!(), - logical_key: todo!(), - text: todo!(), - location: todo!(), + physical_key, + logical_key, + text: None, + location: KeyLocation::Standard, state, repeat: false, - platform_specific: todo!(), + platform_specific: KeyEventExtra { + key_without_modifiers: Key::Unidentified(NativeKeyCode::Unidentified), + text_with_all_modifers: None, + }, }, is_synthetic: false, }, @@ -112,7 +117,8 @@ pub(super) fn handle_keyboard( None => return, }; - // let virtual_keycode = keymap::keysym_to_vkey(keysym); + let physical_key = keymap::rawkey_to_keycode(rawkey); + let logical_key = keymap::keysym_to_key(keysym); event_sink.push_window_event( WindowEvent::KeyboardInput { @@ -120,13 +126,16 @@ pub(super) fn handle_keyboard( DeviceId, )), event: KeyEvent { - physical_key: todo!(), - logical_key: todo!(), - text: todo!(), - location: todo!(), + physical_key, + logical_key, + text: None, + location: KeyLocation::Standard, state: ElementState::Pressed, repeat: false, - platform_specific: todo!(), + platform_specific: KeyEventExtra { + key_without_modifiers: Key::Unidentified(NativeKeyCode::Unidentified), + text_with_all_modifers: None, + }, }, is_synthetic: false, }, diff --git a/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs b/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs index b674dd2945..ae48fc95cf 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs @@ -1,229 +1,477 @@ // //! Convert Wayland keys to winit keys. -// use crate::keyboard::Key; +use crate::keyboard::{Key, KeyCode, NativeKeyCode}; -// pub fn keysym_to_vkey(keysym: u32) -> Option> { -// use sctk::seat::keyboard::keysyms; -// match keysym { -// // Numbers. -// // keysyms::XKB_KEY_1 => Some(Key::Character("1")), -// // keysyms::XKB_KEY_2 => Some(Key::Character("2")), -// // keysyms::XKB_KEY_3 => Some(Key::Character("3")), -// // keysyms::XKB_KEY_4 => Some(Key::Character("4")), -// // keysyms::XKB_KEY_5 => Some(Key::Character("5")), -// // keysyms::XKB_KEY_6 => Some(Key::Character("6")), -// // keysyms::XKB_KEY_7 => Some(Key::Character("7")), -// // keysyms::XKB_KEY_8 => Some(Key::Character("8")), -// // keysyms::XKB_KEY_9 => Some(Key::Character("9")), -// // keysyms::XKB_KEY_0 => Some(Key::Character("0")), -// // Letters. -// // keysyms::XKB_KEY_A => Some(Key::Character("A")), -// // keysyms::XKB_KEY_a => Some(Key::Character("a")), -// // keysyms::XKB_KEY_B => Some(Key::Character("B")), -// // keysyms::XKB_KEY_b => Some(Key::Character("b")), -// // keysyms::XKB_KEY_C => Some(Key::Character("C")), -// // keysyms::XKB_KEY_c => Some(Key::Character("c")), -// // keysyms::XKB_KEY_D => Some(Key::Character("D")), -// // keysyms::XKB_KEY_d => Some(Key::Character("d")), -// // keysyms::XKB_KEY_E => Some(Key::Character("E")), -// // keysyms::XKB_KEY_e => Some(Key::Character("e")), -// // keysyms::XKB_KEY_F => Some(Key::Character("F")), -// // keysyms::XKB_KEY_f => Some(Key::Character("f")), -// // keysyms::XKB_KEY_G => Some(Key::Character("G")), -// // keysyms::XKB_KEY_g => Some(Key::Character("g")), -// // keysyms::XKB_KEY_H => Some(Key::Character("H")), -// // keysyms::XKB_KEY_h => Some(Key::Character("h")), -// // keysyms::XKB_KEY_I => Some(Key::Character("I")), -// // keysyms::XKB_KEY_i => Some(Key::Character("i")), -// // keysyms::XKB_KEY_J => Some(Key::Character("J")), -// // keysyms::XKB_KEY_j => Some(Key::Character("j")), -// // keysyms::XKB_KEY_K => Some(Key::Character("K")), -// // keysyms::XKB_KEY_k => Some(Key::Character("k")), -// // keysyms::XKB_KEY_L => Some(Key::Character("L")), -// // keysyms::XKB_KEY_l => Some(Key::Character("l")), -// // keysyms::XKB_KEY_M => Some(Key::Character("M")), -// // keysyms::XKB_KEY_m => Some(Key::Character("m")), -// // keysyms::XKB_KEY_N => Some(Key::Character("N")), -// // keysyms::XKB_KEY_n => Some(Key::Character("n")), -// // keysyms::XKB_KEY_O => Some(Key::Character("O")), -// // keysyms::XKB_KEY_o => Some(Key::Character("o")), -// // keysyms::XKB_KEY_P => Some(Key::Character("P")), -// // keysyms::XKB_KEY_p => Some(Key::Character("p")), -// // keysyms::XKB_KEY_Q => Some(Key::Character("Q")), -// // keysyms::XKB_KEY_q => Some(Key::Character("q")), -// // keysyms::XKB_KEY_R => Some(Key::Character("R")), -// // keysyms::XKB_KEY_r => Some(Key::Character("r")), -// // keysyms::XKB_KEY_S => Some(Key::Character("S")), -// // keysyms::XKB_KEY_s => Some(Key::Character("s")), -// // keysyms::XKB_KEY_T => Some(Key::Character("T")), -// // keysyms::XKB_KEY_t => Some(Key::Character("t")), -// // keysyms::XKB_KEY_U => Some(Key::Character("U")), -// // keysyms::XKB_KEY_u => Some(Key::Character("u")), -// // keysyms::XKB_KEY_V => Some(Key::Character("V")), -// // keysyms::XKB_KEY_v => Some(Key::Character("v")), -// // keysyms::XKB_KEY_W => Some(Key::Character("W")), -// // keysyms::XKB_KEY_w => Some(Key::Character("w")), -// // keysyms::XKB_KEY_X => Some(Key::Character("X")), -// // keysyms::XKB_KEY_x => Some(Key::Character("x")), -// // keysyms::XKB_KEY_Y => Some(Key::Character("Y")), -// // keysyms::XKB_KEY_y => Some(Key::Character("y")), -// // keysyms::XKB_KEY_Z => Some(Key::Character("Z")), -// // keysyms::XKB_KEY_z => Some(Key::Character("z")), -// // Escape. -// keysyms::XKB_KEY_Escape => Some(Key::Escape), -// // Function keys. -// keysyms::XKB_KEY_F1 => Some(Key::F1), -// keysyms::XKB_KEY_F2 => Some(Key::F2), -// keysyms::XKB_KEY_F3 => Some(Key::F3), -// keysyms::XKB_KEY_F4 => Some(Key::F4), -// keysyms::XKB_KEY_F5 => Some(Key::F5), -// keysyms::XKB_KEY_F6 => Some(Key::F6), -// keysyms::XKB_KEY_F7 => Some(Key::F7), -// keysyms::XKB_KEY_F8 => Some(Key::F8), -// keysyms::XKB_KEY_F9 => Some(Key::F9), -// keysyms::XKB_KEY_F10 => Some(Key::F10), -// keysyms::XKB_KEY_F11 => Some(Key::F11), -// keysyms::XKB_KEY_F12 => Some(Key::F12), -// keysyms::XKB_KEY_F13 => Some(Key::F13), -// keysyms::XKB_KEY_F14 => Some(Key::F14), -// keysyms::XKB_KEY_F15 => Some(Key::F15), -// keysyms::XKB_KEY_F16 => Some(Key::F16), -// keysyms::XKB_KEY_F17 => Some(Key::F17), -// keysyms::XKB_KEY_F18 => Some(Key::F18), -// keysyms::XKB_KEY_F19 => Some(Key::F19), -// keysyms::XKB_KEY_F20 => Some(Key::F20), -// keysyms::XKB_KEY_F21 => Some(Key::F21), -// keysyms::XKB_KEY_F22 => Some(Key::F22), -// keysyms::XKB_KEY_F23 => Some(Key::F23), -// keysyms::XKB_KEY_F24 => Some(Key::F24), -// keysyms::XKB_KEY_F25 => Some(Key::F25), -// keysyms::XKB_KEY_F26 => Some(Key::F26), -// keysyms::XKB_KEY_F27 => Some(Key::F27), -// keysyms::XKB_KEY_F28 => Some(Key::F28), -// keysyms::XKB_KEY_F29 => Some(Key::F29), -// keysyms::XKB_KEY_F30 => Some(Key::F30), -// keysyms::XKB_KEY_F31 => Some(Key::F31), -// keysyms::XKB_KEY_F32 => Some(Key::F32), -// keysyms::XKB_KEY_F33 => Some(Key::F33), -// keysyms::XKB_KEY_F34 => Some(Key::F34), -// keysyms::XKB_KEY_F35 => Some(Key::F35), -// // Flow control. -// keysyms::XKB_KEY_Print => Some(Key::PrintScreen), -// keysyms::XKB_KEY_Scroll_Lock => Some(Key::ScrollLock), -// keysyms::XKB_KEY_Pause => Some(Key::Pause), -// keysyms::XKB_KEY_Insert => Some(Key::Insert), -// keysyms::XKB_KEY_Home => Some(Key::Home), -// keysyms::XKB_KEY_Delete => Some(Key::Delete), -// keysyms::XKB_KEY_End => Some(Key::End), -// keysyms::XKB_KEY_Page_Down => Some(Key::PageDown), -// keysyms::XKB_KEY_Page_Up => Some(Key::PageUp), -// // Arrows. -// keysyms::XKB_KEY_Left => Some(Key::Left), -// keysyms::XKB_KEY_Up => Some(Key::Up), -// keysyms::XKB_KEY_Right => Some(Key::Right), -// keysyms::XKB_KEY_Down => Some(Key::Down), +pub fn rawkey_to_keycode(rawkey: u32) -> KeyCode { + // keycodes are taken from linux/include/uapi/linux/input-event-codes.h + match rawkey { + 0 => todo!("What should be done in this case? Return `NativeKeyCode`, or perhaps `None`?"), + 1 => KeyCode::Escape, + 2 => KeyCode::Digit1, + 3 => KeyCode::Digit2, + 4 => KeyCode::Digit3, + 5 => KeyCode::Digit4, + 6 => KeyCode::Digit5, + 7 => KeyCode::Digit6, + 8 => KeyCode::Digit7, + 9 => KeyCode::Digit8, + 10 => KeyCode::Digit9, + 11 => KeyCode::Digit0, + 12 => KeyCode::Minus, + 13 => KeyCode::Equal, + 14 => KeyCode::Backspace, + 15 => KeyCode::Tab, + 16 => KeyCode::KeyQ, + 17 => KeyCode::KeyW, + 18 => KeyCode::KeyE, + 19 => KeyCode::KeyR, + 20 => KeyCode::KeyT, + 21 => KeyCode::KeyY, + 22 => KeyCode::KeyU, + 23 => KeyCode::KeyI, + 24 => KeyCode::KeyO, + 25 => KeyCode::KeyP, + 26 => KeyCode::BracketLeft, + 27 => KeyCode::BracketRight, + 28 => KeyCode::Enter, + 29 => KeyCode::ControlLeft, + 30 => KeyCode::KeyA, + 31 => KeyCode::KeyS, + 32 => KeyCode::KeyD, + 33 => KeyCode::KeyF, + 34 => KeyCode::KeyG, + 35 => KeyCode::KeyH, + 36 => KeyCode::KeyJ, + 37 => KeyCode::KeyK, + 38 => KeyCode::KeyL, + 39 => KeyCode::Semicolon, + 40 => KeyCode::Quote, + 41 => KeyCode::Backquote, + 42 => KeyCode::ShiftLeft, + 43 => KeyCode::Backslash, + 44 => KeyCode::KeyZ, + 45 => KeyCode::KeyX, + 46 => KeyCode::KeyC, + 47 => KeyCode::KeyV, + 48 => KeyCode::KeyB, + 49 => KeyCode::KeyN, + 50 => KeyCode::KeyM, + 51 => KeyCode::Comma, + 52 => KeyCode::Period, + 53 => KeyCode::Slash, + 54 => KeyCode::ShiftRight, + 55 => KeyCode::NumpadMultiply, + 56 => KeyCode::AltLeft, + 57 => KeyCode::Space, + 58 => KeyCode::CapsLock, + 59 => KeyCode::F1, + 60 => KeyCode::F2, + 61 => KeyCode::F3, + 62 => KeyCode::F4, + 63 => KeyCode::F5, + 64 => KeyCode::F6, + 65 => KeyCode::F7, + 66 => KeyCode::F8, + 67 => KeyCode::F9, + 68 => KeyCode::F10, + 69 => KeyCode::NumLock, + 70 => KeyCode::ScrollLock, + 71 => KeyCode::Numpad7, + 72 => KeyCode::Numpad8, + 73 => KeyCode::Numpad9, + 74 => KeyCode::NumpadSubtract, + 75 => KeyCode::Numpad4, + 76 => KeyCode::Numpad4, + 77 => KeyCode::Numpad6, + 78 => KeyCode::NumpadAdd, + 79 => KeyCode::Numpad1, + 80 => KeyCode::Numpad2, + 81 => KeyCode::Numpad3, + 82 => KeyCode::Numpad0, + 83 => KeyCode::NumpadDecimal, + 85 => KeyCode::Lang5, + 86 => KeyCode::IntlBackslash, // TODO: Verify. + 87 => KeyCode::F11, + 88 => KeyCode::F12, + 89 => KeyCode::IntlRo, + 90 => KeyCode::Lang3, + 91 => KeyCode::Lang4, + 92 => KeyCode::Convert, + 93 => KeyCode::KanaMode, + 94 => KeyCode::NonConvert, + // 95 => KeyCode::KPJPCOMMA, // TODO: What the heck is this supposed to be? + 96 => KeyCode::NumpadEnter, + 97 => KeyCode::ControlRight, + 98 => KeyCode::NumpadDivide, + 99 => KeyCode::PrintScreen, // TODO: Verify. + 100 => KeyCode::AltRight, + // 101 => KeyCode::LINEFEED, // TODO: What the heck is this supposed to be? + 102 => KeyCode::Home, + 103 => KeyCode::ArrowUp, + 104 => KeyCode::PageUp, + 105 => KeyCode::ArrowLeft, + 106 => KeyCode::ArrowRight, + 107 => KeyCode::End, + 108 => KeyCode::ArrowDown, + 109 => KeyCode::PageDown, + 110 => KeyCode::Insert, + 111 => KeyCode::Delete, + // 112 => KeyCode::MACRO, // TODO: What the heck is this supposed to be? + 113 => KeyCode::AudioVolumeMute, + 114 => KeyCode::AudioVolumeDown, + 115 => KeyCode::AudioVolumeUp, + // TODO: I have no idea if this should be mapped to `KeyCode::Power` + // Neither the Linux header or the uievents-code document disambigues this. + // 116 => KeyCode::POWER, + 117 => KeyCode::NumpadEqual, + // 118 => KeyCode::KPPLUSMINUS, // TODO: What the heck is this supposed to be? + 119 => KeyCode::Pause, + // 120 => KeyCode::SCALE, // TODO: What the heck is this supposed to be? + 121 => KeyCode::NumpadComma, + 122 => KeyCode::Lang1, + 123 => KeyCode::Lang2, + 124 => KeyCode::IntlYen, + 125 => KeyCode::SuperLeft, + 126 => KeyCode::SuperRight, + 127 => KeyCode::ContextMenu, + // 128 => KeyCode::STOP, + // 129 => KeyCode::AGAIN, + // 130 => KeyCode::PROPS, + // 131 => KeyCode::UNDO, + // 132 => KeyCode::FRONT, + // 133 => KeyCode::COPY, + // 134 => KeyCode::OPEN, + // 135 => KeyCode::PASTE, + // 136 => KeyCode::FIND, + // 137 => KeyCode::CUT, + // 138 => KeyCode::HELP, + // 139 => KeyCode::MENU, + // 140 => KeyCode::CALC, + // 141 => KeyCode::SETUP, + // 142 => KeyCode::SLEEP, + // 143 => KeyCode::WAKEUP, + // 144 => KeyCode::FILE, + // 145 => KeyCode::SENDFILE, + // 146 => KeyCode::DELETEFILE, + // 147 => KeyCode::XFER, + // 148 => KeyCode::PROG1, + // 149 => KeyCode::PROG2, + // 150 => KeyCode::WWW, + // 151 => KeyCode::MSDOS, + // 152 => KeyCode::COFFEE, + // 153 => KeyCode::ROTATE_DISPLAY, + // 154 => KeyCode::CYCLEWINDOWS, + // 155 => KeyCode::MAIL, + // 156 => KeyCode::BOOKMARKS, + // 157 => KeyCode::COMPUTER, + // 158 => KeyCode::BACK, + // 159 => KeyCode::FORWARD, + // 160 => KeyCode::CLOSECD, + // 161 => KeyCode::EJECTCD, + // 162 => KeyCode::EJECTCLOSECD, + 163 => KeyCode::MediaTrackNext, + 164 => KeyCode::MediaPlayPause, + 165 => KeyCode::MediaTrackPrevious, + 166 => KeyCode::MediaStop, + // 167 => KeyCode::RECORD, + // 168 => KeyCode::REWIND, + // 169 => KeyCode::PHONE, + // 170 => KeyCode::ISO, + // 171 => KeyCode::CONFIG, + // 172 => KeyCode::HOMEPAGE, + // 173 => KeyCode::REFRESH, + // 174 => KeyCode::EXIT, + // 175 => KeyCode::MOVE, + // 176 => KeyCode::EDIT, + // 177 => KeyCode::SCROLLUP, + // 178 => KeyCode::SCROLLDOWN, + // 179 => KeyCode::KPLEFTPAREN, + // 180 => KeyCode::KPRIGHTPAREN, + // 181 => KeyCode::NEW, + // 182 => KeyCode::REDO, + 183 => KeyCode::F13, + 184 => KeyCode::F14, + 185 => KeyCode::F15, + 186 => KeyCode::F16, + 187 => KeyCode::F17, + 188 => KeyCode::F18, + 189 => KeyCode::F19, + 190 => KeyCode::F20, + 191 => KeyCode::F21, + 192 => KeyCode::F22, + 193 => KeyCode::F23, + 194 => KeyCode::F24, + // 200 => KeyCode::PLAYCD, + // 201 => KeyCode::PAUSECD, + // 202 => KeyCode::PROG3, + // 203 => KeyCode::PROG4, + // 204 => KeyCode::DASHBOARD, + // 205 => KeyCode::SUSPEND, + // 206 => KeyCode::CLOSE, + // 207 => KeyCode::PLAY, + // 208 => KeyCode::FASTFORWARD, + // 209 => KeyCode::BASSBOOST, + // 210 => KeyCode::PRINT, + // 211 => KeyCode::HP, + // 212 => KeyCode::CAMERA, + // 213 => KeyCode::SOUND, + // 214 => KeyCode::QUESTION, + // 215 => KeyCode::EMAIL, + // 216 => KeyCode::CHAT, + // 217 => KeyCode::SEARCH, + // 218 => KeyCode::CONNECT, + // 219 => KeyCode::FINANCE, + // 220 => KeyCode::SPORT, + // 221 => KeyCode::SHOP, + // 222 => KeyCode::ALTERASE, + // 223 => KeyCode::CANCEL, + // 224 => KeyCode::BRIGHTNESSDOW, + // 225 => KeyCode::BRIGHTNESSU, + // 226 => KeyCode::MEDIA, + // 227 => KeyCode::SWITCHVIDEOMODE, + // 228 => KeyCode::KBDILLUMTOGGLE, + // 229 => KeyCode::KBDILLUMDOWN, + // 230 => KeyCode::KBDILLUMUP, + // 231 => KeyCode::SEND, + // 232 => KeyCode::REPLY, + // 233 => KeyCode::FORWARDMAIL, + // 234 => KeyCode::SAVE, + // 235 => KeyCode::DOCUMENTS, + // 236 => KeyCode::BATTERY, + // 237 => KeyCode::BLUETOOTH, + // 238 => KeyCode::WLAN, + // 239 => KeyCode::UWB, + 240 => KeyCode::Unidentified(NativeKeyCode::Unidentified), + // 241 => KeyCode::VIDEO_NEXT, + // 242 => KeyCode::VIDEO_PREV, + // 243 => KeyCode::BRIGHTNESS_CYCLE, + // 244 => KeyCode::BRIGHTNESS_AUTO, + _ => KeyCode::Unidentified(NativeKeyCode::XKB(rawkey)), + } +} -// keysyms::XKB_KEY_BackSpace => Some(Key::Back), -// keysyms::XKB_KEY_Return => Some(Key::Return), -// keysyms::XKB_KEY_space => Some(Key::Space), +pub fn keysym_to_key(keysym: u32) -> Key<'static> { + use sctk::seat::keyboard::keysyms; + match keysym { + // // Numbers. + // // keysyms::XKB_KEY_1 => Some(Key::Character("1")), + // // keysyms::XKB_KEY_2 => Some(Key::Character("2")), + // // keysyms::XKB_KEY_3 => Some(Key::Character("3")), + // // keysyms::XKB_KEY_4 => Some(Key::Character("4")), + // // keysyms::XKB_KEY_5 => Some(Key::Character("5")), + // // keysyms::XKB_KEY_6 => Some(Key::Character("6")), + // // keysyms::XKB_KEY_7 => Some(Key::Character("7")), + // // keysyms::XKB_KEY_8 => Some(Key::Character("8")), + // // keysyms::XKB_KEY_9 => Some(Key::Character("9")), + // // keysyms::XKB_KEY_0 => Some(Key::Character("0")), + // // Letters. + // // keysyms::XKB_KEY_A => Some(Key::Character("A")), + // // keysyms::XKB_KEY_a => Some(Key::Character("a")), + // // keysyms::XKB_KEY_B => Some(Key::Character("B")), + // // keysyms::XKB_KEY_b => Some(Key::Character("b")), + // // keysyms::XKB_KEY_C => Some(Key::Character("C")), + // // keysyms::XKB_KEY_c => Some(Key::Character("c")), + // // keysyms::XKB_KEY_D => Some(Key::Character("D")), + // // keysyms::XKB_KEY_d => Some(Key::Character("d")), + // // keysyms::XKB_KEY_E => Some(Key::Character("E")), + // // keysyms::XKB_KEY_e => Some(Key::Character("e")), + // // keysyms::XKB_KEY_F => Some(Key::Character("F")), + // // keysyms::XKB_KEY_f => Some(Key::Character("f")), + // // keysyms::XKB_KEY_G => Some(Key::Character("G")), + // // keysyms::XKB_KEY_g => Some(Key::Character("g")), + // // keysyms::XKB_KEY_H => Some(Key::Character("H")), + // // keysyms::XKB_KEY_h => Some(Key::Character("h")), + // // keysyms::XKB_KEY_I => Some(Key::Character("I")), + // // keysyms::XKB_KEY_i => Some(Key::Character("i")), + // // keysyms::XKB_KEY_J => Some(Key::Character("J")), + // // keysyms::XKB_KEY_j => Some(Key::Character("j")), + // // keysyms::XKB_KEY_K => Some(Key::Character("K")), + // // keysyms::XKB_KEY_k => Some(Key::Character("k")), + // // keysyms::XKB_KEY_L => Some(Key::Character("L")), + // // keysyms::XKB_KEY_l => Some(Key::Character("l")), + // // keysyms::XKB_KEY_M => Some(Key::Character("M")), + // // keysyms::XKB_KEY_m => Some(Key::Character("m")), + // // keysyms::XKB_KEY_N => Some(Key::Character("N")), + // // keysyms::XKB_KEY_n => Some(Key::Character("n")), + // // keysyms::XKB_KEY_O => Some(Key::Character("O")), + // // keysyms::XKB_KEY_o => Some(Key::Character("o")), + // // keysyms::XKB_KEY_P => Some(Key::Character("P")), + // // keysyms::XKB_KEY_p => Some(Key::Character("p")), + // // keysyms::XKB_KEY_Q => Some(Key::Character("Q")), + // // keysyms::XKB_KEY_q => Some(Key::Character("q")), + // // keysyms::XKB_KEY_R => Some(Key::Character("R")), + // // keysyms::XKB_KEY_r => Some(Key::Character("r")), + // // keysyms::XKB_KEY_S => Some(Key::Character("S")), + // // keysyms::XKB_KEY_s => Some(Key::Character("s")), + // // keysyms::XKB_KEY_T => Some(Key::Character("T")), + // // keysyms::XKB_KEY_t => Some(Key::Character("t")), + // // keysyms::XKB_KEY_U => Some(Key::Character("U")), + // // keysyms::XKB_KEY_u => Some(Key::Character("u")), + // // keysyms::XKB_KEY_V => Some(Key::Character("V")), + // // keysyms::XKB_KEY_v => Some(Key::Character("v")), + // // keysyms::XKB_KEY_W => Some(Key::Character("W")), + // // keysyms::XKB_KEY_w => Some(Key::Character("w")), + // // keysyms::XKB_KEY_X => Some(Key::Character("X")), + // // keysyms::XKB_KEY_x => Some(Key::Character("x")), + // // keysyms::XKB_KEY_Y => Some(Key::Character("Y")), + // // keysyms::XKB_KEY_y => Some(Key::Character("y")), + // // keysyms::XKB_KEY_Z => Some(Key::Character("Z")), + // // keysyms::XKB_KEY_z => Some(Key::Character("z")), + // // Escape. + // keysyms::XKB_KEY_Escape => Some(Key::Escape), + // // Function keys. + // keysyms::XKB_KEY_F1 => Some(Key::F1), + // keysyms::XKB_KEY_F2 => Some(Key::F2), + // keysyms::XKB_KEY_F3 => Some(Key::F3), + // keysyms::XKB_KEY_F4 => Some(Key::F4), + // keysyms::XKB_KEY_F5 => Some(Key::F5), + // keysyms::XKB_KEY_F6 => Some(Key::F6), + // keysyms::XKB_KEY_F7 => Some(Key::F7), + // keysyms::XKB_KEY_F8 => Some(Key::F8), + // keysyms::XKB_KEY_F9 => Some(Key::F9), + // keysyms::XKB_KEY_F10 => Some(Key::F10), + // keysyms::XKB_KEY_F11 => Some(Key::F11), + // keysyms::XKB_KEY_F12 => Some(Key::F12), + // keysyms::XKB_KEY_F13 => Some(Key::F13), + // keysyms::XKB_KEY_F14 => Some(Key::F14), + // keysyms::XKB_KEY_F15 => Some(Key::F15), + // keysyms::XKB_KEY_F16 => Some(Key::F16), + // keysyms::XKB_KEY_F17 => Some(Key::F17), + // keysyms::XKB_KEY_F18 => Some(Key::F18), + // keysyms::XKB_KEY_F19 => Some(Key::F19), + // keysyms::XKB_KEY_F20 => Some(Key::F20), + // keysyms::XKB_KEY_F21 => Some(Key::F21), + // keysyms::XKB_KEY_F22 => Some(Key::F22), + // keysyms::XKB_KEY_F23 => Some(Key::F23), + // keysyms::XKB_KEY_F24 => Some(Key::F24), + // keysyms::XKB_KEY_F25 => Some(Key::F25), + // keysyms::XKB_KEY_F26 => Some(Key::F26), + // keysyms::XKB_KEY_F27 => Some(Key::F27), + // keysyms::XKB_KEY_F28 => Some(Key::F28), + // keysyms::XKB_KEY_F29 => Some(Key::F29), + // keysyms::XKB_KEY_F30 => Some(Key::F30), + // keysyms::XKB_KEY_F31 => Some(Key::F31), + // keysyms::XKB_KEY_F32 => Some(Key::F32), + // keysyms::XKB_KEY_F33 => Some(Key::F33), + // keysyms::XKB_KEY_F34 => Some(Key::F34), + // keysyms::XKB_KEY_F35 => Some(Key::F35), + // // Flow control. + // keysyms::XKB_KEY_Print => Some(Key::PrintScreen), + // keysyms::XKB_KEY_Scroll_Lock => Some(Key::ScrollLock), + // keysyms::XKB_KEY_Pause => Some(Key::Pause), + // keysyms::XKB_KEY_Insert => Some(Key::Insert), + // keysyms::XKB_KEY_Home => Some(Key::Home), + // keysyms::XKB_KEY_Delete => Some(Key::Delete), + // keysyms::XKB_KEY_End => Some(Key::End), + // keysyms::XKB_KEY_Page_Down => Some(Key::PageDown), + // keysyms::XKB_KEY_Page_Up => Some(Key::PageUp), + // // Arrows. + // keysyms::XKB_KEY_Left => Some(Key::Left), + // keysyms::XKB_KEY_Up => Some(Key::Up), + // keysyms::XKB_KEY_Right => Some(Key::Right), + // keysyms::XKB_KEY_Down => Some(Key::Down), -// keysyms::XKB_KEY_Multi_key => Some(Key::Compose), -// keysyms::XKB_KEY_caret => Some(Key::Caret), + // keysyms::XKB_KEY_BackSpace => Some(Key::Back), + // keysyms::XKB_KEY_Return => Some(Key::Return), + // keysyms::XKB_KEY_space => Some(Key::Space), -// // Keypad. -// keysyms::XKB_KEY_Num_Lock => Some(Key::Numlock), -// keysyms::XKB_KEY_KP_0 => Some(Key::Numpad0), -// keysyms::XKB_KEY_KP_1 => Some(Key::Numpad1), -// keysyms::XKB_KEY_KP_2 => Some(Key::Numpad2), -// keysyms::XKB_KEY_KP_3 => Some(Key::Numpad3), -// keysyms::XKB_KEY_KP_4 => Some(Key::Numpad4), -// keysyms::XKB_KEY_KP_5 => Some(Key::Numpad5), -// keysyms::XKB_KEY_KP_6 => Some(Key::Numpad6), -// keysyms::XKB_KEY_KP_7 => Some(Key::Numpad7), -// keysyms::XKB_KEY_KP_8 => Some(Key::Numpad8), -// keysyms::XKB_KEY_KP_9 => Some(Key::Numpad9), -// // Misc. -// // => Some(Key::AbntC1), -// // => Some(Key::AbntC2), -// keysyms::XKB_KEY_plus => Some(Key::Plus), -// keysyms::XKB_KEY_apostrophe => Some(Key::Apostrophe), -// // => Some(Key::Apps), -// keysyms::XKB_KEY_at => Some(Key::At), -// // => Some(Key::Ax), -// keysyms::XKB_KEY_backslash => Some(Key::Backslash), -// keysyms::XKB_KEY_XF86Calculator => Some(Key::Calculator), -// // => Some(Key::Capital), -// keysyms::XKB_KEY_colon => Some(Key::Colon), -// keysyms::XKB_KEY_comma => Some(Key::Comma), -// // => Some(Key::Convert), -// keysyms::XKB_KEY_equal => Some(Key::Equals), -// keysyms::XKB_KEY_grave => Some(Key::Grave), -// // => Some(Key::Kana), -// keysyms::XKB_KEY_Kanji => Some(Key::Kanji), -// keysyms::XKB_KEY_Alt_L => Some(Key::LAlt), -// keysyms::XKB_KEY_bracketleft => Some(Key::LBracket), -// keysyms::XKB_KEY_Control_L => Some(Key::LControl), -// keysyms::XKB_KEY_Shift_L => Some(Key::LShift), -// keysyms::XKB_KEY_Super_L => Some(Key::LWin), -// keysyms::XKB_KEY_XF86Mail => Some(Key::Mail), -// // => Some(Key::MediaSelect), -// // => Some(Key::MediaStop), -// keysyms::XKB_KEY_minus => Some(Key::Minus), -// keysyms::XKB_KEY_asterisk => Some(Key::Asterisk), -// keysyms::XKB_KEY_XF86AudioMute => Some(Key::Mute), -// // => Some(Key::MyComputer), -// keysyms::XKB_KEY_XF86AudioNext => Some(Key::NextTrack), -// // => Some(Key::NoConvert), -// keysyms::XKB_KEY_KP_Separator => Some(Key::NumpadComma), -// keysyms::XKB_KEY_KP_Enter => Some(Key::NumpadEnter), -// keysyms::XKB_KEY_KP_Equal => Some(Key::NumpadEquals), -// keysyms::XKB_KEY_KP_Add => Some(Key::NumpadAdd), -// keysyms::XKB_KEY_KP_Subtract => Some(Key::NumpadSubtract), -// keysyms::XKB_KEY_KP_Multiply => Some(Key::NumpadMultiply), -// keysyms::XKB_KEY_KP_Divide => Some(Key::NumpadDivide), -// keysyms::XKB_KEY_KP_Decimal => Some(Key::NumpadDecimal), -// keysyms::XKB_KEY_KP_Page_Up => Some(Key::PageUp), -// keysyms::XKB_KEY_KP_Page_Down => Some(Key::PageDown), -// keysyms::XKB_KEY_KP_Home => Some(Key::Home), -// keysyms::XKB_KEY_KP_End => Some(Key::End), -// keysyms::XKB_KEY_KP_Left => Some(Key::Left), -// keysyms::XKB_KEY_KP_Up => Some(Key::Up), -// keysyms::XKB_KEY_KP_Right => Some(Key::Right), -// keysyms::XKB_KEY_KP_Down => Some(Key::Down), -// // => Some(Key::OEM102), -// keysyms::XKB_KEY_period => Some(Key::Period), -// // => Some(Key::Playpause), -// keysyms::XKB_KEY_XF86PowerOff => Some(Key::Power), -// keysyms::XKB_KEY_XF86AudioPrev => Some(Key::PrevTrack), -// keysyms::XKB_KEY_Alt_R => Some(Key::RAlt), -// keysyms::XKB_KEY_bracketright => Some(Key::RBracket), -// keysyms::XKB_KEY_Control_R => Some(Key::RControl), -// keysyms::XKB_KEY_Shift_R => Some(Key::RShift), -// keysyms::XKB_KEY_Super_R => Some(Key::RWin), -// keysyms::XKB_KEY_semicolon => Some(Key::Semicolon), -// keysyms::XKB_KEY_slash => Some(Key::Slash), -// keysyms::XKB_KEY_XF86Sleep => Some(Key::Sleep), -// // => Some(Key::Stop), -// // => Some(Key::Sysrq), -// keysyms::XKB_KEY_Tab => Some(Key::Tab), -// keysyms::XKB_KEY_ISO_Left_Tab => Some(Key::Tab), -// keysyms::XKB_KEY_underscore => Some(Key::Underline), -// // => Some(Key::Unlabeled), -// keysyms::XKB_KEY_XF86AudioLowerVolume => Some(Key::VolumeDown), -// keysyms::XKB_KEY_XF86AudioRaiseVolume => Some(Key::VolumeUp), -// // => Some(Key::Wake), -// // => Some(Key::Webback), -// // => Some(Key::WebFavorites), -// // => Some(Key::WebForward), -// // => Some(Key::WebHome), -// // => Some(Key::WebRefresh), -// // => Some(Key::WebSearch), -// // => Some(Key::WebStop), -// keysyms::XKB_KEY_yen => Some(Key::Yen), -// keysyms::XKB_KEY_XF86Copy => Some(Key::Copy), -// keysyms::XKB_KEY_XF86Paste => Some(Key::Paste), -// keysyms::XKB_KEY_XF86Cut => Some(Key::Cut), -// // Fallback. -// _ => None, -// } -// } + // keysyms::XKB_KEY_Multi_key => Some(Key::Compose), + // keysyms::XKB_KEY_caret => Some(Key::Caret), + + // // Keypad. + // keysyms::XKB_KEY_Num_Lock => Some(Key::Numlock), + // keysyms::XKB_KEY_KP_0 => Some(Key::Numpad0), + // keysyms::XKB_KEY_KP_1 => Some(Key::Numpad1), + // keysyms::XKB_KEY_KP_2 => Some(Key::Numpad2), + // keysyms::XKB_KEY_KP_3 => Some(Key::Numpad3), + // keysyms::XKB_KEY_KP_4 => Some(Key::Numpad4), + // keysyms::XKB_KEY_KP_5 => Some(Key::Numpad5), + // keysyms::XKB_KEY_KP_6 => Some(Key::Numpad6), + // keysyms::XKB_KEY_KP_7 => Some(Key::Numpad7), + // keysyms::XKB_KEY_KP_8 => Some(Key::Numpad8), + // keysyms::XKB_KEY_KP_9 => Some(Key::Numpad9), + // // Misc. + // // => Some(Key::AbntC1), + // // => Some(Key::AbntC2), + // keysyms::XKB_KEY_plus => Some(Key::Plus), + // keysyms::XKB_KEY_apostrophe => Some(Key::Apostrophe), + // // => Some(Key::Apps), + // keysyms::XKB_KEY_at => Some(Key::At), + // // => Some(Key::Ax), + // keysyms::XKB_KEY_backslash => Some(Key::Backslash), + // keysyms::XKB_KEY_XF86Calculator => Some(Key::Calculator), + // // => Some(Key::Capital), + // keysyms::XKB_KEY_colon => Some(Key::Colon), + // keysyms::XKB_KEY_comma => Some(Key::Comma), + // // => Some(Key::Convert), + // keysyms::XKB_KEY_equal => Some(Key::Equals), + // keysyms::XKB_KEY_grave => Some(Key::Grave), + // // => Some(Key::Kana), + // keysyms::XKB_KEY_Kanji => Some(Key::Kanji), + // keysyms::XKB_KEY_Alt_L => Some(Key::LAlt), + // keysyms::XKB_KEY_bracketleft => Some(Key::LBracket), + // keysyms::XKB_KEY_Control_L => Some(Key::LControl), + // keysyms::XKB_KEY_Shift_L => Some(Key::LShift), + // keysyms::XKB_KEY_Super_L => Some(Key::LWin), + // keysyms::XKB_KEY_XF86Mail => Some(Key::Mail), + // // => Some(Key::MediaSelect), + // // => Some(Key::MediaStop), + // keysyms::XKB_KEY_minus => Some(Key::Minus), + // keysyms::XKB_KEY_asterisk => Some(Key::Asterisk), + // keysyms::XKB_KEY_XF86AudioMute => Some(Key::Mute), + // // => Some(Key::MyComputer), + // keysyms::XKB_KEY_XF86AudioNext => Some(Key::NextTrack), + // // => Some(Key::NoConvert), + // keysyms::XKB_KEY_KP_Separator => Some(Key::NumpadComma), + // keysyms::XKB_KEY_KP_Enter => Some(Key::NumpadEnter), + // keysyms::XKB_KEY_KP_Equal => Some(Key::NumpadEquals), + // keysyms::XKB_KEY_KP_Add => Some(Key::NumpadAdd), + // keysyms::XKB_KEY_KP_Subtract => Some(Key::NumpadSubtract), + // keysyms::XKB_KEY_KP_Multiply => Some(Key::NumpadMultiply), + // keysyms::XKB_KEY_KP_Divide => Some(Key::NumpadDivide), + // keysyms::XKB_KEY_KP_Decimal => Some(Key::NumpadDecimal), + // keysyms::XKB_KEY_KP_Page_Up => Some(Key::PageUp), + // keysyms::XKB_KEY_KP_Page_Down => Some(Key::PageDown), + // keysyms::XKB_KEY_KP_Home => Some(Key::Home), + // keysyms::XKB_KEY_KP_End => Some(Key::End), + // keysyms::XKB_KEY_KP_Left => Some(Key::Left), + // keysyms::XKB_KEY_KP_Up => Some(Key::Up), + // keysyms::XKB_KEY_KP_Right => Some(Key::Right), + // keysyms::XKB_KEY_KP_Down => Some(Key::Down), + // // => Some(Key::OEM102), + // keysyms::XKB_KEY_period => Some(Key::Period), + // // => Some(Key::Playpause), + // keysyms::XKB_KEY_XF86PowerOff => Some(Key::Power), + // keysyms::XKB_KEY_XF86AudioPrev => Some(Key::PrevTrack), + // keysyms::XKB_KEY_Alt_R => Some(Key::RAlt), + // keysyms::XKB_KEY_bracketright => Some(Key::RBracket), + // keysyms::XKB_KEY_Control_R => Some(Key::RControl), + // keysyms::XKB_KEY_Shift_R => Some(Key::RShift), + // keysyms::XKB_KEY_Super_R => Some(Key::RWin), + // keysyms::XKB_KEY_semicolon => Some(Key::Semicolon), + // keysyms::XKB_KEY_slash => Some(Key::Slash), + // keysyms::XKB_KEY_XF86Sleep => Some(Key::Sleep), + // // => Some(Key::Stop), + // // => Some(Key::Sysrq), + // keysyms::XKB_KEY_Tab => Some(Key::Tab), + // keysyms::XKB_KEY_ISO_Left_Tab => Some(Key::Tab), + // keysyms::XKB_KEY_underscore => Some(Key::Underline), + // // => Some(Key::Unlabeled), + // keysyms::XKB_KEY_XF86AudioLowerVolume => Some(Key::VolumeDown), + // keysyms::XKB_KEY_XF86AudioRaiseVolume => Some(Key::VolumeUp), + // // => Some(Key::Wake), + // // => Some(Key::Webback), + // // => Some(Key::WebFavorites), + // // => Some(Key::WebForward), + // // => Some(Key::WebHome), + // // => Some(Key::WebRefresh), + // // => Some(Key::WebSearch), + // // => Some(Key::WebStop), + // keysyms::XKB_KEY_yen => Some(Key::Yen), + // keysyms::XKB_KEY_XF86Copy => Some(Key::Copy), + // keysyms::XKB_KEY_XF86Paste => Some(Key::Paste), + // keysyms::XKB_KEY_XF86Cut => Some(Key::Cut), + // // Fallback. + _ => Key::Unidentified(NativeKeyCode::Unidentified), + } +} From 46aa80adac6a4790bf67853bd89e77b20f600218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Mon, 29 Mar 2021 01:49:30 +0200 Subject: [PATCH 070/131] Add some keysym mappings and move the keymap.rs file --- Cargo.toml | 1 + src/platform_impl/linux/common/keymap.rs | 685 ++++++++++++++++++ src/platform_impl/linux/common/mod.rs | 1 + src/platform_impl/linux/mod.rs | 3 + .../linux/wayland/seat/keyboard/handlers.rs | 3 +- .../linux/wayland/seat/keyboard/keymap.rs | 477 ------------ .../linux/wayland/seat/keyboard/mod.rs | 1 - 7 files changed, 692 insertions(+), 479 deletions(-) create mode 100644 src/platform_impl/linux/common/keymap.rs create mode 100644 src/platform_impl/linux/common/mod.rs delete mode 100644 src/platform_impl/linux/wayland/seat/keyboard/keymap.rs diff --git a/Cargo.toml b/Cargo.toml index 807fa9c109..b40e6679a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,6 +94,7 @@ mio-extras = { version = "2.0", optional = true } x11-dl = { version = "2.18.5", optional = true } percent-encoding = { version = "2.0", optional = true } parking_lot = { version = "0.11.0", optional = true } +xkbcommon = "0.4.0" [target.'cfg(target_arch = "wasm32")'.dependencies.web_sys] package = "web-sys" diff --git a/src/platform_impl/linux/common/keymap.rs b/src/platform_impl/linux/common/keymap.rs new file mode 100644 index 0000000000..eeccf65a97 --- /dev/null +++ b/src/platform_impl/linux/common/keymap.rs @@ -0,0 +1,685 @@ +//! Convert Wayland keys to winit keys. + +use crate::keyboard::{Key, KeyCode, KeyLocation, NativeKeyCode}; + +// TODO: Do another pass on all of this + +pub fn rawkey_to_keycode(rawkey: u32) -> KeyCode { + // The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as + // libxkbcommon's documentation indicates that the keycode values we're getting from it are + // defined by the Linux kernel. If Winit programs end up being run on other Unix-likes which + // also use libxkbcommon, then I dearly hope the keycode values mean the same thing. + // + // Some of the keycodes are likely superfluous for our purposes, and some are ones which are + // difficult to test the correctness of, or discover the purpose of. Because of this, they've + // either been commented out here, or not included at all. + // + // TODO: There are probably a couple more unproblematic keycodes to map here. + match rawkey { + 0 => KeyCode::Unidentified(NativeKeyCode::XKB(0)), // TODO: Is `NativeKeyCode::Unidentified` better? + 1 => KeyCode::Escape, + 2 => KeyCode::Digit1, + 3 => KeyCode::Digit2, + 4 => KeyCode::Digit3, + 5 => KeyCode::Digit4, + 6 => KeyCode::Digit5, + 7 => KeyCode::Digit6, + 8 => KeyCode::Digit7, + 9 => KeyCode::Digit8, + 10 => KeyCode::Digit9, + 11 => KeyCode::Digit0, + 12 => KeyCode::Minus, + 13 => KeyCode::Equal, + 14 => KeyCode::Backspace, + 15 => KeyCode::Tab, + 16 => KeyCode::KeyQ, + 17 => KeyCode::KeyW, + 18 => KeyCode::KeyE, + 19 => KeyCode::KeyR, + 20 => KeyCode::KeyT, + 21 => KeyCode::KeyY, + 22 => KeyCode::KeyU, + 23 => KeyCode::KeyI, + 24 => KeyCode::KeyO, + 25 => KeyCode::KeyP, + 26 => KeyCode::BracketLeft, + 27 => KeyCode::BracketRight, + 28 => KeyCode::Enter, + 29 => KeyCode::ControlLeft, + 30 => KeyCode::KeyA, + 31 => KeyCode::KeyS, + 32 => KeyCode::KeyD, + 33 => KeyCode::KeyF, + 34 => KeyCode::KeyG, + 35 => KeyCode::KeyH, + 36 => KeyCode::KeyJ, + 37 => KeyCode::KeyK, + 38 => KeyCode::KeyL, + 39 => KeyCode::Semicolon, + 40 => KeyCode::Quote, + 41 => KeyCode::Backquote, + 42 => KeyCode::ShiftLeft, + 43 => KeyCode::Backslash, + 44 => KeyCode::KeyZ, + 45 => KeyCode::KeyX, + 46 => KeyCode::KeyC, + 47 => KeyCode::KeyV, + 48 => KeyCode::KeyB, + 49 => KeyCode::KeyN, + 50 => KeyCode::KeyM, + 51 => KeyCode::Comma, + 52 => KeyCode::Period, + 53 => KeyCode::Slash, + 54 => KeyCode::ShiftRight, + 55 => KeyCode::NumpadMultiply, + 56 => KeyCode::AltLeft, + 57 => KeyCode::Space, + 58 => KeyCode::CapsLock, + 59 => KeyCode::F1, + 60 => KeyCode::F2, + 61 => KeyCode::F3, + 62 => KeyCode::F4, + 63 => KeyCode::F5, + 64 => KeyCode::F6, + 65 => KeyCode::F7, + 66 => KeyCode::F8, + 67 => KeyCode::F9, + 68 => KeyCode::F10, + 69 => KeyCode::NumLock, + 70 => KeyCode::ScrollLock, + 71 => KeyCode::Numpad7, + 72 => KeyCode::Numpad8, + 73 => KeyCode::Numpad9, + 74 => KeyCode::NumpadSubtract, + 75 => KeyCode::Numpad4, + 76 => KeyCode::Numpad4, + 77 => KeyCode::Numpad6, + 78 => KeyCode::NumpadAdd, + 79 => KeyCode::Numpad1, + 80 => KeyCode::Numpad2, + 81 => KeyCode::Numpad3, + 82 => KeyCode::Numpad0, + 83 => KeyCode::NumpadDecimal, + 85 => KeyCode::Lang5, + 86 => KeyCode::IntlBackslash, + 87 => KeyCode::F11, + 88 => KeyCode::F12, + 89 => KeyCode::IntlRo, + 90 => KeyCode::Lang3, + 91 => KeyCode::Lang4, + 92 => KeyCode::Convert, + 93 => KeyCode::KanaMode, + 94 => KeyCode::NonConvert, + // 95 => KeyCode::KPJPCOMMA, + 96 => KeyCode::NumpadEnter, + 97 => KeyCode::ControlRight, + 98 => KeyCode::NumpadDivide, + 99 => KeyCode::PrintScreen, + 100 => KeyCode::AltRight, + // 101 => KeyCode::LINEFEED, + 102 => KeyCode::Home, + 103 => KeyCode::ArrowUp, + 104 => KeyCode::PageUp, + 105 => KeyCode::ArrowLeft, + 106 => KeyCode::ArrowRight, + 107 => KeyCode::End, + 108 => KeyCode::ArrowDown, + 109 => KeyCode::PageDown, + 110 => KeyCode::Insert, + 111 => KeyCode::Delete, + // 112 => KeyCode::MACRO, + 113 => KeyCode::AudioVolumeMute, + 114 => KeyCode::AudioVolumeDown, + 115 => KeyCode::AudioVolumeUp, + // 116 => KeyCode::POWER, + 117 => KeyCode::NumpadEqual, + // 118 => KeyCode::KPPLUSMINUS, + 119 => KeyCode::Pause, + // 120 => KeyCode::SCALE, + 121 => KeyCode::NumpadComma, + 122 => KeyCode::Lang1, + 123 => KeyCode::Lang2, + 124 => KeyCode::IntlYen, + 125 => KeyCode::SuperLeft, + 126 => KeyCode::SuperRight, + 127 => KeyCode::ContextMenu, + // 128 => KeyCode::STOP, + // 129 => KeyCode::AGAIN, + // 130 => KeyCode::PROPS, + // 131 => KeyCode::UNDO, + // 132 => KeyCode::FRONT, + // 133 => KeyCode::COPY, + // 134 => KeyCode::OPEN, + // 135 => KeyCode::PASTE, + // 136 => KeyCode::FIND, + // 137 => KeyCode::CUT, + // 138 => KeyCode::HELP, + // 139 => KeyCode::MENU, + // 140 => KeyCode::CALC, + // 141 => KeyCode::SETUP, + // 142 => KeyCode::SLEEP, + // 143 => KeyCode::WAKEUP, + // 144 => KeyCode::FILE, + // 145 => KeyCode::SENDFILE, + // 146 => KeyCode::DELETEFILE, + // 147 => KeyCode::XFER, + // 148 => KeyCode::PROG1, + // 149 => KeyCode::PROG2, + // 150 => KeyCode::WWW, + // 151 => KeyCode::MSDOS, + // 152 => KeyCode::COFFEE, + // 153 => KeyCode::ROTATE_DISPLAY, + // 154 => KeyCode::CYCLEWINDOWS, + // 155 => KeyCode::MAIL, + // 156 => KeyCode::BOOKMARKS, + // 157 => KeyCode::COMPUTER, + // 158 => KeyCode::BACK, + // 159 => KeyCode::FORWARD, + // 160 => KeyCode::CLOSECD, + // 161 => KeyCode::EJECTCD, + // 162 => KeyCode::EJECTCLOSECD, + 163 => KeyCode::MediaTrackNext, + 164 => KeyCode::MediaPlayPause, + 165 => KeyCode::MediaTrackPrevious, + 166 => KeyCode::MediaStop, + // 167 => KeyCode::RECORD, + // 168 => KeyCode::REWIND, + // 169 => KeyCode::PHONE, + // 170 => KeyCode::ISO, + // 171 => KeyCode::CONFIG, + // 172 => KeyCode::HOMEPAGE, + // 173 => KeyCode::REFRESH, + // 174 => KeyCode::EXIT, + // 175 => KeyCode::MOVE, + // 176 => KeyCode::EDIT, + // 177 => KeyCode::SCROLLUP, + // 178 => KeyCode::SCROLLDOWN, + // 179 => KeyCode::KPLEFTPAREN, + // 180 => KeyCode::KPRIGHTPAREN, + // 181 => KeyCode::NEW, + // 182 => KeyCode::REDO, + 183 => KeyCode::F13, + 184 => KeyCode::F14, + 185 => KeyCode::F15, + 186 => KeyCode::F16, + 187 => KeyCode::F17, + 188 => KeyCode::F18, + 189 => KeyCode::F19, + 190 => KeyCode::F20, + 191 => KeyCode::F21, + 192 => KeyCode::F22, + 193 => KeyCode::F23, + 194 => KeyCode::F24, + // 200 => KeyCode::PLAYCD, + // 201 => KeyCode::PAUSECD, + // 202 => KeyCode::PROG3, + // 203 => KeyCode::PROG4, + // 204 => KeyCode::DASHBOARD, + // 205 => KeyCode::SUSPEND, + // 206 => KeyCode::CLOSE, + // 207 => KeyCode::PLAY, + // 208 => KeyCode::FASTFORWARD, + // 209 => KeyCode::BASSBOOST, + // 210 => KeyCode::PRINT, + // 211 => KeyCode::HP, + // 212 => KeyCode::CAMERA, + // 213 => KeyCode::SOUND, + // 214 => KeyCode::QUESTION, + // 215 => KeyCode::EMAIL, + // 216 => KeyCode::CHAT, + // 217 => KeyCode::SEARCH, + // 218 => KeyCode::CONNECT, + // 219 => KeyCode::FINANCE, + // 220 => KeyCode::SPORT, + // 221 => KeyCode::SHOP, + // 222 => KeyCode::ALTERASE, + // 223 => KeyCode::CANCEL, + // 224 => KeyCode::BRIGHTNESSDOW, + // 225 => KeyCode::BRIGHTNESSU, + // 226 => KeyCode::MEDIA, + // 227 => KeyCode::SWITCHVIDEOMODE, + // 228 => KeyCode::KBDILLUMTOGGLE, + // 229 => KeyCode::KBDILLUMDOWN, + // 230 => KeyCode::KBDILLUMUP, + // 231 => KeyCode::SEND, + // 232 => KeyCode::REPLY, + // 233 => KeyCode::FORWARDMAIL, + // 234 => KeyCode::SAVE, + // 235 => KeyCode::DOCUMENTS, + // 236 => KeyCode::BATTERY, + // 237 => KeyCode::BLUETOOTH, + // 238 => KeyCode::WLAN, + // 239 => KeyCode::UWB, + 240 => KeyCode::Unidentified(NativeKeyCode::Unidentified), + // 241 => KeyCode::VIDEO_NEXT, + // 242 => KeyCode::VIDEO_PREV, + // 243 => KeyCode::BRIGHTNESS_CYCLE, + // 244 => KeyCode::BRIGHTNESS_AUTO, + // 245 => KeyCode::DISPLAY_OFF, + // 246 => KeyCode::WWAN, + // 247 => KeyCode::RFKILL, + // 248 => KeyCode::KEY_MICMUTE, + _ => KeyCode::Unidentified(NativeKeyCode::XKB(rawkey)), + } +} + +pub fn keysym_to_key(keysym: u32) -> Key<'static> { + use xkbcommon::xkb; + match keysym { + // TTY function keys + xkb::KEY_BackSpace => Key::Backspace, + xkb::KEY_Tab => Key::Tab, + // xkb::KEY_Linefeed => Key::Linefeed, + xkb::KEY_Clear => Key::Clear, + xkb::KEY_Return => Key::Enter, + // xkb::KEY_Pause => Key::Pause, + xkb::KEY_Scroll_Lock => Key::ScrollLock, + xkb::KEY_Sys_Req => Key::PrintScreen, + xkb::KEY_Escape => Key::Escape, + xkb::KEY_Delete => Key::Delete, + + // IME keys + xkb::KEY_Multi_key => Key::Compose, + xkb::KEY_Codeinput => Key::CodeInput, + xkb::KEY_SingleCandidate => Key::SingleCandidate, + xkb::KEY_MultipleCandidate => Key::AllCandidates, + xkb::KEY_PreviousCandidate => Key::PreviousCandidate, + + // Japanese keys + xkb::KEY_Kanji => Key::KanjiMode, + xkb::KEY_Muhenkan => Key::NonConvert, + xkb::KEY_Henkan_Mode => Key::Convert, + xkb::KEY_Romaji => Key::Romaji, + xkb::KEY_Hiragana => Key::Hiragana, + xkb::KEY_Hiragana_Katakana => Key::HiraganaKatakana, + xkb::KEY_Zenkaku => Key::Zenkaku, + xkb::KEY_Hankaku => Key::Hankaku, + xkb::KEY_Zenkaku_Hankaku => Key::ZenkakuHankaku, + // xkb::KEY_Touroku => Key::Touroku, + // xkb::KEY_Massyo => Key::Massyo, + xkb::KEY_Kana_Lock => Key::KanaMode, + // TODO: This seems a tad perverse, but I'm not really familiar with japanese keyboards. + // MDN documents this as a valid mapping, however. + // xkb::KEY_Kana_Shift => Key::KanaMode, + // TODO: Is this the correct mapping? + // xkb::KEY_Eisu_Shift => Key::Alphanumeric, + // xkb::KEY_Eisu_toggle => Key::Alphanumeric, + // NOTE: The next three items are aliases for values we've already mapped. + // xkb::KEY_Kanji_Bangou => Key::CodeInput, + // xkb::KEY_Zen_Koho => Key::AllCandidates, + // xkb::KEY_Mae_Koho => Key::PreviousCandidate, + + // Cursor control & motion + xkb::KEY_Home => Key::Home, + xkb::KEY_Left => Key::ArrowLeft, + xkb::KEY_Up => Key::ArrowUp, + xkb::KEY_Right => Key::ArrowRight, + xkb::KEY_Down => Key::ArrowDown, + // xkb::KEY_Prior => Key::Prior, + xkb::KEY_Page_Up => Key::PageUp, + // xkb::KEY_Next => Key::Next, + xkb::KEY_End => Key::End, + // xkb::KEY_Begin => Key::Begin, + + // Misc. functions + xkb::KEY_Select => Key::Select, + xkb::KEY_Print => Key::PrintScreen, + xkb::KEY_Execute => Key::Execute, + xkb::KEY_Insert => Key::Insert, + xkb::KEY_Undo => Key::Undo, + xkb::KEY_Redo => Key::Redo, + xkb::KEY_Menu => Key::ContextMenu, + xkb::KEY_Find => Key::Find, + xkb::KEY_Cancel => Key::Cancel, + xkb::KEY_Help => Key::Help, + xkb::KEY_Break => Key::Pause, + xkb::KEY_Mode_switch => Key::ModeChange, + // xkb::KEY_script_switch => Key::ModeChange, + xkb::KEY_Num_Lock => Key::NumLock, + + // Keypad keys + // xkb::KEY_KP_Space => Key::Character(" "), + xkb::KEY_KP_Tab => Key::Tab, + xkb::KEY_KP_Enter => Key::Enter, + xkb::KEY_KP_F1 => Key::F1, + xkb::KEY_KP_F2 => Key::F2, + xkb::KEY_KP_F3 => Key::F3, + xkb::KEY_KP_F4 => Key::F4, + xkb::KEY_KP_Home => Key::Home, + xkb::KEY_KP_Left => Key::ArrowLeft, + xkb::KEY_KP_Up => Key::ArrowLeft, + xkb::KEY_KP_Right => Key::ArrowRight, + xkb::KEY_KP_Down => Key::ArrowDown, + // xkb::KEY_KP_Prior => Key::Prior, + xkb::KEY_KP_Page_Up => Key::PageUp, + // xkb::KEY_KP_Next => Key::Next, + xkb::KEY_KP_Page_Down => Key::PageDown, + xkb::KEY_KP_End => Key::End, + // xkb::KEY_KP_Begin => Key::Begin, + xkb::KEY_KP_Insert => Key::Insert, + xkb::KEY_KP_Delete => Key::Delete, + // xkb::KEY_KP_Equal => Key::Equal, + // xkb::KEY_KP_Multiply => Key::Multiply, + // xkb::KEY_KP_Add => Key::Add, + // xkb::KEY_KP_Separator => Key::Separator, + // xkb::KEY_KP_Subtract => Key::Subtract, + // xkb::KEY_KP_Decimal => Key::Decimal, + // xkb::KEY_KP_Divide => Key::Divide, + + // xkb::KEY_KP_0 => Key::Character("0"), + // xkb::KEY_KP_1 => Key::Character("1"), + // xkb::KEY_KP_2 => Key::Character("2"), + // xkb::KEY_KP_3 => Key::Character("3"), + // xkb::KEY_KP_4 => Key::Character("4"), + // xkb::KEY_KP_5 => Key::Character("5"), + // xkb::KEY_KP_6 => Key::Character("6"), + // xkb::KEY_KP_7 => Key::Character("7"), + // xkb::KEY_KP_8 => Key::Character("8"), + // xkb::KEY_KP_9 => Key::Character("9"), + + // Function keys + xkb::KEY_F1 => Key::F1, + xkb::KEY_F2 => Key::F2, + xkb::KEY_F3 => Key::F3, + xkb::KEY_F4 => Key::F4, + xkb::KEY_F5 => Key::F5, + xkb::KEY_F6 => Key::F6, + xkb::KEY_F7 => Key::F7, + xkb::KEY_F8 => Key::F8, + xkb::KEY_F9 => Key::F9, + xkb::KEY_F10 => Key::F10, + xkb::KEY_F11 => Key::F11, + xkb::KEY_F12 => Key::F12, + xkb::KEY_F13 => Key::F13, + xkb::KEY_F14 => Key::F14, + xkb::KEY_F15 => Key::F15, + xkb::KEY_F16 => Key::F16, + xkb::KEY_F17 => Key::F17, + xkb::KEY_F18 => Key::F18, + xkb::KEY_F19 => Key::F19, + xkb::KEY_F20 => Key::F20, + xkb::KEY_F21 => Key::F21, + xkb::KEY_F22 => Key::F22, + xkb::KEY_F23 => Key::F23, + xkb::KEY_F24 => Key::F24, + xkb::KEY_F25 => Key::F25, + xkb::KEY_F26 => Key::F26, + xkb::KEY_F27 => Key::F27, + xkb::KEY_F28 => Key::F28, + xkb::KEY_F29 => Key::F29, + xkb::KEY_F30 => Key::F30, + xkb::KEY_F31 => Key::F31, + xkb::KEY_F32 => Key::F32, + xkb::KEY_F33 => Key::F33, + xkb::KEY_F34 => Key::F34, + xkb::KEY_F35 => Key::F35, + + // Modifiers + xkb::KEY_Shift_L => Key::Shift, + xkb::KEY_Shift_R => Key::Shift, + xkb::KEY_Control_L => Key::Control, + xkb::KEY_Control_R => Key::Control, + xkb::KEY_Caps_Lock => Key::CapsLock, + // xkb::KEY_Shift_Lock => Key::ShiftLock, + // + xkb::KEY_Meta_L => Key::Super, + xkb::KEY_Meta_R => Key::Super, + xkb::KEY_Alt_L => Key::Alt, + xkb::KEY_Alt_R => Key::Alt, + // NOTE: The key xkb calls "Meta" is called "Super" by Winit. + // xkb::KEY_Super_L => Key::Super, + // xkb::KEY_Super_R => Key::Super, + xkb::KEY_Hyper_L => Key::Hyper, + xkb::KEY_Hyper_R => Key::Hyper, + + // XKB function and modifier keys + // xkb::KEY_ISO_Lock => Key::IsoLock, + // xkb::KEY_ISO_Level2_Latch => Key::IsoLevel2Latch, + // NOTE: I'm not quite certain if mapping the next 3 values to AltGraph is correct. + // xkb::KEY_ISO_Level3_Shift => Key::AltGraph, + // xkb::KEY_ISO_Level3_Latch => Key::AltGraph, + // xkb::KEY_ISO_Level3_Lock => Key::AltGraph, + // xkb::KEY_ISO_Level5_Shift => Key::IsoLevel5Shift, + // xkb::KEY_ISO_Level5_Latch => Key::IsoLevel5Latch, + // xkb::KEY_ISO_Level5_Lock => Key::IsoLevel5Lock, + // xkb::KEY_ISO_Group_Shift => Key::IsoGroupShift, + // xkb::KEY_ISO_Group_Latch => Key::IsoGroupLatch, + // xkb::KEY_ISO_Group_Lock => Key::IsoGroupLock, + xkb::KEY_ISO_Next_Group => Key::GroupNext, + // xkb::KEY_ISO_Next_Group_Lock => Key::GroupNextLock, + xkb::KEY_ISO_Prev_Group => Key::GroupPrevious, + // xkb::KEY_ISO_Prev_Group_Lock => Key::GroupPreviousLock, + xkb::KEY_ISO_First_Group => Key::GroupFirst, + // xkb::KEY_ISO_First_Group_Lock => Key::GroupFirstLock, + xkb::KEY_ISO_Last_Group => Key::GroupLast, + // xkb::KEY_ISO_Last_Group_Lock => Key::GroupLastLock, + // + xkb::KEY_ISO_Left_Tab => Key::Tab, + // xkb::KEY_ISO_Move_Line_Up => Key::IsoMoveLineUp, + // xkb::KEY_ISO_Move_Line_Down => Key::IsoMoveLineDown, + // xkb::KEY_ISO_Partial_Line_Up => Key::IsoPartialLineUp, + // xkb::KEY_ISO_Partial_Line_Down => Key::IsoPartialLineDown, + // xkb::KEY_ISO_Partial_Space_Left => Key::IsoPartialSpaceLeft, + // xkb::KEY_ISO_Partial_Space_Right => Key::IsoPartialSpaceRight, + // xkb::KEY_ISO_Set_Margin_Left => Key::IsoSetMarginLeft, + // xkb::KEY_ISO_Set_Margin_Right => Key::IsoSetMarginRight, + // xkb::KEY_ISO_Release_Margin_Left => Key::IsoReleaseMarginLeft, + // xkb::KEY_ISO_Release_Margin_Right => Key::IsoReleaseMarginRight, + // xkb::KEY_ISO_Release_Both_Margins => Key::IsoReleaseBothMargins, + // xkb::KEY_ISO_Fast_Cursor_Left => Key::IsoFastCursorLeft, + // xkb::KEY_ISO_Fast_Cursor_Right => Key::IsoFastCursorRight, + // xkb::KEY_ISO_Fast_Cursor_Up => Key::IsoFastCursorUp, + // xkb::KEY_ISO_Fast_Cursor_Down => Key::IsoFastCursorDown, + // xkb::KEY_ISO_Continuous_Underline => Key::IsoContinuousUnderline, + // xkb::KEY_ISO_Discontinuous_Underline => Key::IsoDiscontinuousUnderline, + // xkb::KEY_ISO_Emphasize => Key::IsoEmphasize, + // xkb::KEY_ISO_Center_Object => Key::IsoCenterObject, + xkb::KEY_ISO_Enter => Key::Enter, + + // KEY_dead_grave..KEY_dead_currency + + // KEY_dead_lowline..KEY_dead_longsolidusoverlay + + // KEY_dead_a..KEY_dead_capital_schwa + + // KEY_dead_greek + + // KEY_First_Virtual_Screen..KEY_Terminate_Server + + // KEY_AccessX_Enable..KEY_AudibleBell_Enable + + // KEY_Pointer_Left..KEY_Pointer_Drag5 + + // KEY_Pointer_EnableKeys..KEY_Pointer_DfltBtnPrev + + // KEY_ch..KEY_C_H + + // 3270 terminal keys + // xkb::KEY_3270_Duplicate => Key::Duplicate, + // xkb::KEY_3270_FieldMark => Key::FieldMark, + // xkb::KEY_3270_Right2 => Key::Right2, + // xkb::KEY_3270_Left2 => Key::Left2, + // xkb::KEY_3270_BackTab => Key::BackTab, + xkb::KEY_3270_EraseEOF => Key::EraseEof, + // xkb::KEY_3270_EraseInput => Key::EraseInput, + // xkb::KEY_3270_Reset => Key::Reset, + // xkb::KEY_3270_Quit => Key::Quit, + // xkb::KEY_3270_PA1 => Key::Pa1, + // xkb::KEY_3270_PA2 => Key::Pa2, + // xkb::KEY_3270_PA3 => Key::Pa3, + // xkb::KEY_3270_Test => Key::Test, + xkb::KEY_3270_Attn => Key::Attn, + // xkb::KEY_3270_CursorBlink => Key::CursorBlink, + // xkb::KEY_3270_AltCursor => Key::AltCursor, + // xkb::KEY_3270_KeyClick => Key::KeyClick, + // xkb::KEY_3270_Jump => Key::Jump, + // xkb::KEY_3270_Ident => Key::Ident, + // xkb::KEY_3270_Rule => Key::Rule, + // xkb::KEY_3270_Copy => Key::Copy, + xkb::KEY_3270_Play => Key::Play, + // xkb::KEY_3270_Setup => Key::Setup, + // xkb::KEY_3270_Record => Key::Record, + // xkb::KEY_3270_ChangeScreen => Key::ChangeScreen, + // xkb::KEY_3270_DeleteWord => Key::DeleteWord, + xkb::KEY_3270_ExSelect => Key::ExSel, + xkb::KEY_3270_CursorSelect => Key::CrSel, + xkb::KEY_3270_PrintScreen => Key::PrintScreen, + xkb::KEY_3270_Enter => Key::Enter, + + xkb::KEY_space => Key::Space, + // KEY_exclam..KEY_Sinh_kunddaliya + + // XFree86 + // xkb::KEY_XF86ModeLock => Key::ModeLock, + + // XFree86 - Backlight controls + xkb::KEY_XF86MonBrightnessUp => Key::BrightnessUp, + xkb::KEY_XF86MonBrightnessDown => Key::BrightnessDown, + // xkb::KEY_XF86KbdLightOnOff => Key::LightOnOff, + // xkb::KEY_XF86KbdBrightnessUp => Key::KeyboardBrightnessUp, + // xkb::KEY_XF86KbdBrightnessDown => Key::KeyboardBrightnessDown, + + // XFree86 - "Internet" + xkb::KEY_XF86Standby => Key::Standby, + xkb::KEY_XF86AudioLowerVolume => Key::AudioVolumeDown, + xkb::KEY_XF86AudioRaiseVolume => Key::AudioVolumeUp, + xkb::KEY_XF86AudioPlay => Key::MediaPlay, + xkb::KEY_XF86AudioStop => Key::MediaStop, + xkb::KEY_XF86AudioPrev => Key::MediaTrackPrevious, + xkb::KEY_XF86AudioNext => Key::MediaTrackNext, + xkb::KEY_XF86HomePage => Key::BrowserHome, + xkb::KEY_XF86Mail => Key::LaunchMail, + // xkb::KEY_XF86Start => Key::Start, + xkb::KEY_XF86Search => Key::BrowserSearch, + xkb::KEY_XF86AudioRecord => Key::MediaRecord, + + // XFree86 - PDA + xkb::KEY_XF86Calculator => Key::LaunchApplication2, + // xkb::KEY_XF86Memo => Key::Memo, + // xkb::KEY_XF86ToDoList => Key::ToDoList, + xkb::KEY_XF86Calendar => Key::LaunchCalendar, + xkb::KEY_XF86PowerDown => Key::Power, + // xkb::KEY_XF86ContrastAdjust => Key::AdjustContrast, + // xkb::KEY_XF86RockerUp => Key::RockerUp, // TODO: Use Key::ArrowUp? + // xkb::KEY_XF86RockerDown => Key::RockerDown, // TODO: Use Key::ArrowDown? + // xkb::KEY_XF86RockerEnter => Key::RockerEnter, // TODO: Use Key::Enter? + + // XFree86 - More "Internet" + xkb::KEY_XF86Back => Key::BrowserBack, + xkb::KEY_XF86Forward => Key::BrowserForward, + // xkb::KEY_XF86Stop => Key::Stop, + xkb::KEY_XF86Refresh => Key::BrowserRefresh, + xkb::KEY_XF86PowerOff => Key::Power, + xkb::KEY_XF86WakeUp => Key::WakeUp, + xkb::KEY_XF86Eject => Key::Eject, + xkb::KEY_XF86ScreenSaver => Key::LaunchScreenSaver, + xkb::KEY_XF86WWW => Key::LaunchWebBrowser, + xkb::KEY_XF86Sleep => Key::Standby, + xkb::KEY_XF86Favorites => Key::BrowserFavorites, + xkb::KEY_XF86AudioPause => Key::MediaPause, + // xkb::KEY_XF86AudioMedia => Key::AudioMedia, + xkb::KEY_XF86MyComputer => Key::LaunchApplication1, + // xkb::KEY_XF86VendorHome => Key::VendorHome, + // xkb::KEY_XF86LightBulb => Key::LightBulb, + // xkb::KEY_XF86Shop => Key::BrowserShop, + // xkb::KEY_XF86History => Key::BrowserHistory, + // xkb::KEY_XF86OpenURL => Key::OpenUrl, + // xkb::KEY_XF86AddFavorite => Key::AddFavorite, + // xkb::KEY_XF86HotLinks => Key::HotLinks, + // xkb::KEY_XF86BrightnessAdjust => Key::BrightnessAdjust, + // xkb::KEY_XF86Finance => Key::BrowserFinance, + // xkb::KEY_XF86Community => Key::BrowserCommunity, + xkb::KEY_XF86AudioRewind => Key::MediaRewind, + // xkb::KEY_XF86BackForward => Key::???, + // KEY_XF86Launch0..KEY_XF86LaunchF + + // KEY_XF86ApplicationLeft..KEY_XF86CD + xkb::KEY_XF86Calculater => Key::LaunchApplication2, // This must be a typo, right? + // KEY_XF86Clear + xkb::KEY_XF86Close => Key::Close, + xkb::KEY_XF86Copy => Key::Copy, + xkb::KEY_XF86Cut => Key::Cut, + // KEY_XF86Display..KEY_XF86Documents + xkb::KEY_XF86Excel => Key::LaunchSpreadsheet, + // KEY_XF86Explorer..KEY_XF86iTouch + xkb::KEY_XF86LogOff => Key::LogOff, + // KEY_XF86Market..KEY_XF86MenuPB + xkb::KEY_XF86MySites => Key::BrowserFavorites, + xkb::KEY_XF86New => Key::New, + // KEY_XF86News..KEY_XF86OfficeHome + xkb::KEY_XF86Open => Key::Open, + // KEY_XF86Option + xkb::KEY_XF86Paste => Key::Paste, + xkb::KEY_XF86Phone => Key::LaunchPhone, + // KEY_XF86Q + xkb::KEY_XF86Reply => Key::MailReply, + xkb::KEY_XF86Reload => Key::BrowserRefresh, + // KEY_XF86RotateWindows..KEY_XF86RotationKB + xkb::KEY_XF86Save => Key::Save, + // KEY_XF86ScrollUp..KEY_XF86ScrollClick + xkb::KEY_XF86Send => Key::MailSend, + xkb::KEY_XF86Spell => Key::SpellCheck, + xkb::KEY_XF86SplitScreen => Key::SplitScreenToggle, + // KEY_XF86Support..KEY_XF86User2KB + xkb::KEY_XF86Video => Key::LaunchMediaPlayer, + // KEY_XF86WheelButton + xkb::KEY_XF86Word => Key::LaunchWordProcessor, + // KEY_XF86Xfer + xkb::KEY_XF86ZoomIn => Key::ZoomIn, + xkb::KEY_XF86ZoomOut => Key::ZoomOut, + + // KEY_XF86Away..KEY_XF86Messenger + xkb::KEY_XF86WebCam => Key::LaunchWebCam, + xkb::KEY_XF86MailForward => Key::MailForward, + // KEY_XF86Pictures + xkb::KEY_XF86Music => Key::LaunchMusicPlayer, + + // KEY_XF86Battery..KEY_XF86UWB + // + xkb::KEY_XF86AudioForward => Key::MediaFastForward, + // KEY_XF86AudioRepeat + xkb::KEY_XF86AudioRandomPlay => Key::RandomToggle, + xkb::KEY_XF86Subtitle => Key::Subtitle, + xkb::KEY_XF86AudioCycleTrack => Key::MediaAudioTrack, + // KEY_XF86CycleAngle..KEY_XF86Blue + // + xkb::KEY_XF86Suspend => Key::Standby, + xkb::KEY_XF86Hibernate => Key::Hibernate, + // KEY_XF86TouchpadToggle..KEY_XF86TouchpadOff + // + xkb::KEY_XF86AudioMute => Key::AudioVolumeMute, + + // KEY_XF86Switch_VT_1..KEY_XF86Switch_VT_12 + + // KEY_XF86Ungrab..KEY_XF86ClearGrab + xkb::KEY_XF86Next_VMode => Key::VideoModeNext, + // xkb::KEY_XF86Prev_VMode => Key::VideoModePrevious, + // KEY_XF86LogWindowTree..KEY_XF86LogGrabInfo + + // KEY_SunFA_Grave..KEY_SunFA_Cedilla + + // xkb::KEY_SunF36 => Key::F36 | Key::F11, + // xkb::KEY_SunF37 => Key::F37 | Key::F12, + + // xkb::KEY_SunSys_Req => Key::PrintScreen, + // The next couple of xkb (until KEY_SunStop) are already handled. + // KEY_SunPrint_Screen..KEY_SunPageDown + + // KEY_SunUndo..KEY_SunFront + xkb::KEY_SunCopy => Key::Copy, + xkb::KEY_SunOpen => Key::Open, + xkb::KEY_SunPaste => Key::Paste, + xkb::KEY_SunCut => Key::Cut, + + // KEY_SunPowerSwitch + xkb::KEY_SunAudioLowerVolume => Key::AudioVolumeDown, + xkb::KEY_SunAudioMute => Key::AudioVolumeMute, + xkb::KEY_SunAudioRaiseVolume => Key::AudioVolumeUp, + // KEY_SunVideoDegauss + xkb::KEY_SunVideoLowerBrightness => Key::BrightnessDown, + xkb::KEY_SunVideoRaiseBrightness => Key::BrightnessUp, + // KEY_SunPowerSwitchShift + // + _ => Key::Unidentified(NativeKeyCode::XKB(keysym)), + } +} diff --git a/src/platform_impl/linux/common/mod.rs b/src/platform_impl/linux/common/mod.rs new file mode 100644 index 0000000000..32030aff05 --- /dev/null +++ b/src/platform_impl/linux/common/mod.rs @@ -0,0 +1 @@ +pub mod keymap; diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 27d15ee96f..1321437c3c 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -41,6 +41,9 @@ pub mod wayland; #[cfg(feature = "x11")] pub mod x11; +#[cfg(any(feature = "x11", feature = "wayland"))] +mod common; + #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEventExtra { pub text_with_all_modifers: Option<&'static str>, diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs index 47cbc984ad..91c1edf8cc 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs @@ -10,7 +10,8 @@ use crate::platform_impl::wayland::event_loop::WinitState; use crate::platform_impl::wayland::{self, DeviceId}; use crate::platform_impl::KeyEventExtra; -use super::keymap; +// TODO: This is kind of terrible +use super::super::super::super::common::keymap; use super::KeyboardInner; #[inline] diff --git a/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs b/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs deleted file mode 100644 index ae48fc95cf..0000000000 --- a/src/platform_impl/linux/wayland/seat/keyboard/keymap.rs +++ /dev/null @@ -1,477 +0,0 @@ -// //! Convert Wayland keys to winit keys. - -use crate::keyboard::{Key, KeyCode, NativeKeyCode}; - -pub fn rawkey_to_keycode(rawkey: u32) -> KeyCode { - // keycodes are taken from linux/include/uapi/linux/input-event-codes.h - match rawkey { - 0 => todo!("What should be done in this case? Return `NativeKeyCode`, or perhaps `None`?"), - 1 => KeyCode::Escape, - 2 => KeyCode::Digit1, - 3 => KeyCode::Digit2, - 4 => KeyCode::Digit3, - 5 => KeyCode::Digit4, - 6 => KeyCode::Digit5, - 7 => KeyCode::Digit6, - 8 => KeyCode::Digit7, - 9 => KeyCode::Digit8, - 10 => KeyCode::Digit9, - 11 => KeyCode::Digit0, - 12 => KeyCode::Minus, - 13 => KeyCode::Equal, - 14 => KeyCode::Backspace, - 15 => KeyCode::Tab, - 16 => KeyCode::KeyQ, - 17 => KeyCode::KeyW, - 18 => KeyCode::KeyE, - 19 => KeyCode::KeyR, - 20 => KeyCode::KeyT, - 21 => KeyCode::KeyY, - 22 => KeyCode::KeyU, - 23 => KeyCode::KeyI, - 24 => KeyCode::KeyO, - 25 => KeyCode::KeyP, - 26 => KeyCode::BracketLeft, - 27 => KeyCode::BracketRight, - 28 => KeyCode::Enter, - 29 => KeyCode::ControlLeft, - 30 => KeyCode::KeyA, - 31 => KeyCode::KeyS, - 32 => KeyCode::KeyD, - 33 => KeyCode::KeyF, - 34 => KeyCode::KeyG, - 35 => KeyCode::KeyH, - 36 => KeyCode::KeyJ, - 37 => KeyCode::KeyK, - 38 => KeyCode::KeyL, - 39 => KeyCode::Semicolon, - 40 => KeyCode::Quote, - 41 => KeyCode::Backquote, - 42 => KeyCode::ShiftLeft, - 43 => KeyCode::Backslash, - 44 => KeyCode::KeyZ, - 45 => KeyCode::KeyX, - 46 => KeyCode::KeyC, - 47 => KeyCode::KeyV, - 48 => KeyCode::KeyB, - 49 => KeyCode::KeyN, - 50 => KeyCode::KeyM, - 51 => KeyCode::Comma, - 52 => KeyCode::Period, - 53 => KeyCode::Slash, - 54 => KeyCode::ShiftRight, - 55 => KeyCode::NumpadMultiply, - 56 => KeyCode::AltLeft, - 57 => KeyCode::Space, - 58 => KeyCode::CapsLock, - 59 => KeyCode::F1, - 60 => KeyCode::F2, - 61 => KeyCode::F3, - 62 => KeyCode::F4, - 63 => KeyCode::F5, - 64 => KeyCode::F6, - 65 => KeyCode::F7, - 66 => KeyCode::F8, - 67 => KeyCode::F9, - 68 => KeyCode::F10, - 69 => KeyCode::NumLock, - 70 => KeyCode::ScrollLock, - 71 => KeyCode::Numpad7, - 72 => KeyCode::Numpad8, - 73 => KeyCode::Numpad9, - 74 => KeyCode::NumpadSubtract, - 75 => KeyCode::Numpad4, - 76 => KeyCode::Numpad4, - 77 => KeyCode::Numpad6, - 78 => KeyCode::NumpadAdd, - 79 => KeyCode::Numpad1, - 80 => KeyCode::Numpad2, - 81 => KeyCode::Numpad3, - 82 => KeyCode::Numpad0, - 83 => KeyCode::NumpadDecimal, - 85 => KeyCode::Lang5, - 86 => KeyCode::IntlBackslash, // TODO: Verify. - 87 => KeyCode::F11, - 88 => KeyCode::F12, - 89 => KeyCode::IntlRo, - 90 => KeyCode::Lang3, - 91 => KeyCode::Lang4, - 92 => KeyCode::Convert, - 93 => KeyCode::KanaMode, - 94 => KeyCode::NonConvert, - // 95 => KeyCode::KPJPCOMMA, // TODO: What the heck is this supposed to be? - 96 => KeyCode::NumpadEnter, - 97 => KeyCode::ControlRight, - 98 => KeyCode::NumpadDivide, - 99 => KeyCode::PrintScreen, // TODO: Verify. - 100 => KeyCode::AltRight, - // 101 => KeyCode::LINEFEED, // TODO: What the heck is this supposed to be? - 102 => KeyCode::Home, - 103 => KeyCode::ArrowUp, - 104 => KeyCode::PageUp, - 105 => KeyCode::ArrowLeft, - 106 => KeyCode::ArrowRight, - 107 => KeyCode::End, - 108 => KeyCode::ArrowDown, - 109 => KeyCode::PageDown, - 110 => KeyCode::Insert, - 111 => KeyCode::Delete, - // 112 => KeyCode::MACRO, // TODO: What the heck is this supposed to be? - 113 => KeyCode::AudioVolumeMute, - 114 => KeyCode::AudioVolumeDown, - 115 => KeyCode::AudioVolumeUp, - // TODO: I have no idea if this should be mapped to `KeyCode::Power` - // Neither the Linux header or the uievents-code document disambigues this. - // 116 => KeyCode::POWER, - 117 => KeyCode::NumpadEqual, - // 118 => KeyCode::KPPLUSMINUS, // TODO: What the heck is this supposed to be? - 119 => KeyCode::Pause, - // 120 => KeyCode::SCALE, // TODO: What the heck is this supposed to be? - 121 => KeyCode::NumpadComma, - 122 => KeyCode::Lang1, - 123 => KeyCode::Lang2, - 124 => KeyCode::IntlYen, - 125 => KeyCode::SuperLeft, - 126 => KeyCode::SuperRight, - 127 => KeyCode::ContextMenu, - // 128 => KeyCode::STOP, - // 129 => KeyCode::AGAIN, - // 130 => KeyCode::PROPS, - // 131 => KeyCode::UNDO, - // 132 => KeyCode::FRONT, - // 133 => KeyCode::COPY, - // 134 => KeyCode::OPEN, - // 135 => KeyCode::PASTE, - // 136 => KeyCode::FIND, - // 137 => KeyCode::CUT, - // 138 => KeyCode::HELP, - // 139 => KeyCode::MENU, - // 140 => KeyCode::CALC, - // 141 => KeyCode::SETUP, - // 142 => KeyCode::SLEEP, - // 143 => KeyCode::WAKEUP, - // 144 => KeyCode::FILE, - // 145 => KeyCode::SENDFILE, - // 146 => KeyCode::DELETEFILE, - // 147 => KeyCode::XFER, - // 148 => KeyCode::PROG1, - // 149 => KeyCode::PROG2, - // 150 => KeyCode::WWW, - // 151 => KeyCode::MSDOS, - // 152 => KeyCode::COFFEE, - // 153 => KeyCode::ROTATE_DISPLAY, - // 154 => KeyCode::CYCLEWINDOWS, - // 155 => KeyCode::MAIL, - // 156 => KeyCode::BOOKMARKS, - // 157 => KeyCode::COMPUTER, - // 158 => KeyCode::BACK, - // 159 => KeyCode::FORWARD, - // 160 => KeyCode::CLOSECD, - // 161 => KeyCode::EJECTCD, - // 162 => KeyCode::EJECTCLOSECD, - 163 => KeyCode::MediaTrackNext, - 164 => KeyCode::MediaPlayPause, - 165 => KeyCode::MediaTrackPrevious, - 166 => KeyCode::MediaStop, - // 167 => KeyCode::RECORD, - // 168 => KeyCode::REWIND, - // 169 => KeyCode::PHONE, - // 170 => KeyCode::ISO, - // 171 => KeyCode::CONFIG, - // 172 => KeyCode::HOMEPAGE, - // 173 => KeyCode::REFRESH, - // 174 => KeyCode::EXIT, - // 175 => KeyCode::MOVE, - // 176 => KeyCode::EDIT, - // 177 => KeyCode::SCROLLUP, - // 178 => KeyCode::SCROLLDOWN, - // 179 => KeyCode::KPLEFTPAREN, - // 180 => KeyCode::KPRIGHTPAREN, - // 181 => KeyCode::NEW, - // 182 => KeyCode::REDO, - 183 => KeyCode::F13, - 184 => KeyCode::F14, - 185 => KeyCode::F15, - 186 => KeyCode::F16, - 187 => KeyCode::F17, - 188 => KeyCode::F18, - 189 => KeyCode::F19, - 190 => KeyCode::F20, - 191 => KeyCode::F21, - 192 => KeyCode::F22, - 193 => KeyCode::F23, - 194 => KeyCode::F24, - // 200 => KeyCode::PLAYCD, - // 201 => KeyCode::PAUSECD, - // 202 => KeyCode::PROG3, - // 203 => KeyCode::PROG4, - // 204 => KeyCode::DASHBOARD, - // 205 => KeyCode::SUSPEND, - // 206 => KeyCode::CLOSE, - // 207 => KeyCode::PLAY, - // 208 => KeyCode::FASTFORWARD, - // 209 => KeyCode::BASSBOOST, - // 210 => KeyCode::PRINT, - // 211 => KeyCode::HP, - // 212 => KeyCode::CAMERA, - // 213 => KeyCode::SOUND, - // 214 => KeyCode::QUESTION, - // 215 => KeyCode::EMAIL, - // 216 => KeyCode::CHAT, - // 217 => KeyCode::SEARCH, - // 218 => KeyCode::CONNECT, - // 219 => KeyCode::FINANCE, - // 220 => KeyCode::SPORT, - // 221 => KeyCode::SHOP, - // 222 => KeyCode::ALTERASE, - // 223 => KeyCode::CANCEL, - // 224 => KeyCode::BRIGHTNESSDOW, - // 225 => KeyCode::BRIGHTNESSU, - // 226 => KeyCode::MEDIA, - // 227 => KeyCode::SWITCHVIDEOMODE, - // 228 => KeyCode::KBDILLUMTOGGLE, - // 229 => KeyCode::KBDILLUMDOWN, - // 230 => KeyCode::KBDILLUMUP, - // 231 => KeyCode::SEND, - // 232 => KeyCode::REPLY, - // 233 => KeyCode::FORWARDMAIL, - // 234 => KeyCode::SAVE, - // 235 => KeyCode::DOCUMENTS, - // 236 => KeyCode::BATTERY, - // 237 => KeyCode::BLUETOOTH, - // 238 => KeyCode::WLAN, - // 239 => KeyCode::UWB, - 240 => KeyCode::Unidentified(NativeKeyCode::Unidentified), - // 241 => KeyCode::VIDEO_NEXT, - // 242 => KeyCode::VIDEO_PREV, - // 243 => KeyCode::BRIGHTNESS_CYCLE, - // 244 => KeyCode::BRIGHTNESS_AUTO, - _ => KeyCode::Unidentified(NativeKeyCode::XKB(rawkey)), - } -} - -pub fn keysym_to_key(keysym: u32) -> Key<'static> { - use sctk::seat::keyboard::keysyms; - match keysym { - // // Numbers. - // // keysyms::XKB_KEY_1 => Some(Key::Character("1")), - // // keysyms::XKB_KEY_2 => Some(Key::Character("2")), - // // keysyms::XKB_KEY_3 => Some(Key::Character("3")), - // // keysyms::XKB_KEY_4 => Some(Key::Character("4")), - // // keysyms::XKB_KEY_5 => Some(Key::Character("5")), - // // keysyms::XKB_KEY_6 => Some(Key::Character("6")), - // // keysyms::XKB_KEY_7 => Some(Key::Character("7")), - // // keysyms::XKB_KEY_8 => Some(Key::Character("8")), - // // keysyms::XKB_KEY_9 => Some(Key::Character("9")), - // // keysyms::XKB_KEY_0 => Some(Key::Character("0")), - // // Letters. - // // keysyms::XKB_KEY_A => Some(Key::Character("A")), - // // keysyms::XKB_KEY_a => Some(Key::Character("a")), - // // keysyms::XKB_KEY_B => Some(Key::Character("B")), - // // keysyms::XKB_KEY_b => Some(Key::Character("b")), - // // keysyms::XKB_KEY_C => Some(Key::Character("C")), - // // keysyms::XKB_KEY_c => Some(Key::Character("c")), - // // keysyms::XKB_KEY_D => Some(Key::Character("D")), - // // keysyms::XKB_KEY_d => Some(Key::Character("d")), - // // keysyms::XKB_KEY_E => Some(Key::Character("E")), - // // keysyms::XKB_KEY_e => Some(Key::Character("e")), - // // keysyms::XKB_KEY_F => Some(Key::Character("F")), - // // keysyms::XKB_KEY_f => Some(Key::Character("f")), - // // keysyms::XKB_KEY_G => Some(Key::Character("G")), - // // keysyms::XKB_KEY_g => Some(Key::Character("g")), - // // keysyms::XKB_KEY_H => Some(Key::Character("H")), - // // keysyms::XKB_KEY_h => Some(Key::Character("h")), - // // keysyms::XKB_KEY_I => Some(Key::Character("I")), - // // keysyms::XKB_KEY_i => Some(Key::Character("i")), - // // keysyms::XKB_KEY_J => Some(Key::Character("J")), - // // keysyms::XKB_KEY_j => Some(Key::Character("j")), - // // keysyms::XKB_KEY_K => Some(Key::Character("K")), - // // keysyms::XKB_KEY_k => Some(Key::Character("k")), - // // keysyms::XKB_KEY_L => Some(Key::Character("L")), - // // keysyms::XKB_KEY_l => Some(Key::Character("l")), - // // keysyms::XKB_KEY_M => Some(Key::Character("M")), - // // keysyms::XKB_KEY_m => Some(Key::Character("m")), - // // keysyms::XKB_KEY_N => Some(Key::Character("N")), - // // keysyms::XKB_KEY_n => Some(Key::Character("n")), - // // keysyms::XKB_KEY_O => Some(Key::Character("O")), - // // keysyms::XKB_KEY_o => Some(Key::Character("o")), - // // keysyms::XKB_KEY_P => Some(Key::Character("P")), - // // keysyms::XKB_KEY_p => Some(Key::Character("p")), - // // keysyms::XKB_KEY_Q => Some(Key::Character("Q")), - // // keysyms::XKB_KEY_q => Some(Key::Character("q")), - // // keysyms::XKB_KEY_R => Some(Key::Character("R")), - // // keysyms::XKB_KEY_r => Some(Key::Character("r")), - // // keysyms::XKB_KEY_S => Some(Key::Character("S")), - // // keysyms::XKB_KEY_s => Some(Key::Character("s")), - // // keysyms::XKB_KEY_T => Some(Key::Character("T")), - // // keysyms::XKB_KEY_t => Some(Key::Character("t")), - // // keysyms::XKB_KEY_U => Some(Key::Character("U")), - // // keysyms::XKB_KEY_u => Some(Key::Character("u")), - // // keysyms::XKB_KEY_V => Some(Key::Character("V")), - // // keysyms::XKB_KEY_v => Some(Key::Character("v")), - // // keysyms::XKB_KEY_W => Some(Key::Character("W")), - // // keysyms::XKB_KEY_w => Some(Key::Character("w")), - // // keysyms::XKB_KEY_X => Some(Key::Character("X")), - // // keysyms::XKB_KEY_x => Some(Key::Character("x")), - // // keysyms::XKB_KEY_Y => Some(Key::Character("Y")), - // // keysyms::XKB_KEY_y => Some(Key::Character("y")), - // // keysyms::XKB_KEY_Z => Some(Key::Character("Z")), - // // keysyms::XKB_KEY_z => Some(Key::Character("z")), - // // Escape. - // keysyms::XKB_KEY_Escape => Some(Key::Escape), - // // Function keys. - // keysyms::XKB_KEY_F1 => Some(Key::F1), - // keysyms::XKB_KEY_F2 => Some(Key::F2), - // keysyms::XKB_KEY_F3 => Some(Key::F3), - // keysyms::XKB_KEY_F4 => Some(Key::F4), - // keysyms::XKB_KEY_F5 => Some(Key::F5), - // keysyms::XKB_KEY_F6 => Some(Key::F6), - // keysyms::XKB_KEY_F7 => Some(Key::F7), - // keysyms::XKB_KEY_F8 => Some(Key::F8), - // keysyms::XKB_KEY_F9 => Some(Key::F9), - // keysyms::XKB_KEY_F10 => Some(Key::F10), - // keysyms::XKB_KEY_F11 => Some(Key::F11), - // keysyms::XKB_KEY_F12 => Some(Key::F12), - // keysyms::XKB_KEY_F13 => Some(Key::F13), - // keysyms::XKB_KEY_F14 => Some(Key::F14), - // keysyms::XKB_KEY_F15 => Some(Key::F15), - // keysyms::XKB_KEY_F16 => Some(Key::F16), - // keysyms::XKB_KEY_F17 => Some(Key::F17), - // keysyms::XKB_KEY_F18 => Some(Key::F18), - // keysyms::XKB_KEY_F19 => Some(Key::F19), - // keysyms::XKB_KEY_F20 => Some(Key::F20), - // keysyms::XKB_KEY_F21 => Some(Key::F21), - // keysyms::XKB_KEY_F22 => Some(Key::F22), - // keysyms::XKB_KEY_F23 => Some(Key::F23), - // keysyms::XKB_KEY_F24 => Some(Key::F24), - // keysyms::XKB_KEY_F25 => Some(Key::F25), - // keysyms::XKB_KEY_F26 => Some(Key::F26), - // keysyms::XKB_KEY_F27 => Some(Key::F27), - // keysyms::XKB_KEY_F28 => Some(Key::F28), - // keysyms::XKB_KEY_F29 => Some(Key::F29), - // keysyms::XKB_KEY_F30 => Some(Key::F30), - // keysyms::XKB_KEY_F31 => Some(Key::F31), - // keysyms::XKB_KEY_F32 => Some(Key::F32), - // keysyms::XKB_KEY_F33 => Some(Key::F33), - // keysyms::XKB_KEY_F34 => Some(Key::F34), - // keysyms::XKB_KEY_F35 => Some(Key::F35), - // // Flow control. - // keysyms::XKB_KEY_Print => Some(Key::PrintScreen), - // keysyms::XKB_KEY_Scroll_Lock => Some(Key::ScrollLock), - // keysyms::XKB_KEY_Pause => Some(Key::Pause), - // keysyms::XKB_KEY_Insert => Some(Key::Insert), - // keysyms::XKB_KEY_Home => Some(Key::Home), - // keysyms::XKB_KEY_Delete => Some(Key::Delete), - // keysyms::XKB_KEY_End => Some(Key::End), - // keysyms::XKB_KEY_Page_Down => Some(Key::PageDown), - // keysyms::XKB_KEY_Page_Up => Some(Key::PageUp), - // // Arrows. - // keysyms::XKB_KEY_Left => Some(Key::Left), - // keysyms::XKB_KEY_Up => Some(Key::Up), - // keysyms::XKB_KEY_Right => Some(Key::Right), - // keysyms::XKB_KEY_Down => Some(Key::Down), - - // keysyms::XKB_KEY_BackSpace => Some(Key::Back), - // keysyms::XKB_KEY_Return => Some(Key::Return), - // keysyms::XKB_KEY_space => Some(Key::Space), - - // keysyms::XKB_KEY_Multi_key => Some(Key::Compose), - // keysyms::XKB_KEY_caret => Some(Key::Caret), - - // // Keypad. - // keysyms::XKB_KEY_Num_Lock => Some(Key::Numlock), - // keysyms::XKB_KEY_KP_0 => Some(Key::Numpad0), - // keysyms::XKB_KEY_KP_1 => Some(Key::Numpad1), - // keysyms::XKB_KEY_KP_2 => Some(Key::Numpad2), - // keysyms::XKB_KEY_KP_3 => Some(Key::Numpad3), - // keysyms::XKB_KEY_KP_4 => Some(Key::Numpad4), - // keysyms::XKB_KEY_KP_5 => Some(Key::Numpad5), - // keysyms::XKB_KEY_KP_6 => Some(Key::Numpad6), - // keysyms::XKB_KEY_KP_7 => Some(Key::Numpad7), - // keysyms::XKB_KEY_KP_8 => Some(Key::Numpad8), - // keysyms::XKB_KEY_KP_9 => Some(Key::Numpad9), - // // Misc. - // // => Some(Key::AbntC1), - // // => Some(Key::AbntC2), - // keysyms::XKB_KEY_plus => Some(Key::Plus), - // keysyms::XKB_KEY_apostrophe => Some(Key::Apostrophe), - // // => Some(Key::Apps), - // keysyms::XKB_KEY_at => Some(Key::At), - // // => Some(Key::Ax), - // keysyms::XKB_KEY_backslash => Some(Key::Backslash), - // keysyms::XKB_KEY_XF86Calculator => Some(Key::Calculator), - // // => Some(Key::Capital), - // keysyms::XKB_KEY_colon => Some(Key::Colon), - // keysyms::XKB_KEY_comma => Some(Key::Comma), - // // => Some(Key::Convert), - // keysyms::XKB_KEY_equal => Some(Key::Equals), - // keysyms::XKB_KEY_grave => Some(Key::Grave), - // // => Some(Key::Kana), - // keysyms::XKB_KEY_Kanji => Some(Key::Kanji), - // keysyms::XKB_KEY_Alt_L => Some(Key::LAlt), - // keysyms::XKB_KEY_bracketleft => Some(Key::LBracket), - // keysyms::XKB_KEY_Control_L => Some(Key::LControl), - // keysyms::XKB_KEY_Shift_L => Some(Key::LShift), - // keysyms::XKB_KEY_Super_L => Some(Key::LWin), - // keysyms::XKB_KEY_XF86Mail => Some(Key::Mail), - // // => Some(Key::MediaSelect), - // // => Some(Key::MediaStop), - // keysyms::XKB_KEY_minus => Some(Key::Minus), - // keysyms::XKB_KEY_asterisk => Some(Key::Asterisk), - // keysyms::XKB_KEY_XF86AudioMute => Some(Key::Mute), - // // => Some(Key::MyComputer), - // keysyms::XKB_KEY_XF86AudioNext => Some(Key::NextTrack), - // // => Some(Key::NoConvert), - // keysyms::XKB_KEY_KP_Separator => Some(Key::NumpadComma), - // keysyms::XKB_KEY_KP_Enter => Some(Key::NumpadEnter), - // keysyms::XKB_KEY_KP_Equal => Some(Key::NumpadEquals), - // keysyms::XKB_KEY_KP_Add => Some(Key::NumpadAdd), - // keysyms::XKB_KEY_KP_Subtract => Some(Key::NumpadSubtract), - // keysyms::XKB_KEY_KP_Multiply => Some(Key::NumpadMultiply), - // keysyms::XKB_KEY_KP_Divide => Some(Key::NumpadDivide), - // keysyms::XKB_KEY_KP_Decimal => Some(Key::NumpadDecimal), - // keysyms::XKB_KEY_KP_Page_Up => Some(Key::PageUp), - // keysyms::XKB_KEY_KP_Page_Down => Some(Key::PageDown), - // keysyms::XKB_KEY_KP_Home => Some(Key::Home), - // keysyms::XKB_KEY_KP_End => Some(Key::End), - // keysyms::XKB_KEY_KP_Left => Some(Key::Left), - // keysyms::XKB_KEY_KP_Up => Some(Key::Up), - // keysyms::XKB_KEY_KP_Right => Some(Key::Right), - // keysyms::XKB_KEY_KP_Down => Some(Key::Down), - // // => Some(Key::OEM102), - // keysyms::XKB_KEY_period => Some(Key::Period), - // // => Some(Key::Playpause), - // keysyms::XKB_KEY_XF86PowerOff => Some(Key::Power), - // keysyms::XKB_KEY_XF86AudioPrev => Some(Key::PrevTrack), - // keysyms::XKB_KEY_Alt_R => Some(Key::RAlt), - // keysyms::XKB_KEY_bracketright => Some(Key::RBracket), - // keysyms::XKB_KEY_Control_R => Some(Key::RControl), - // keysyms::XKB_KEY_Shift_R => Some(Key::RShift), - // keysyms::XKB_KEY_Super_R => Some(Key::RWin), - // keysyms::XKB_KEY_semicolon => Some(Key::Semicolon), - // keysyms::XKB_KEY_slash => Some(Key::Slash), - // keysyms::XKB_KEY_XF86Sleep => Some(Key::Sleep), - // // => Some(Key::Stop), - // // => Some(Key::Sysrq), - // keysyms::XKB_KEY_Tab => Some(Key::Tab), - // keysyms::XKB_KEY_ISO_Left_Tab => Some(Key::Tab), - // keysyms::XKB_KEY_underscore => Some(Key::Underline), - // // => Some(Key::Unlabeled), - // keysyms::XKB_KEY_XF86AudioLowerVolume => Some(Key::VolumeDown), - // keysyms::XKB_KEY_XF86AudioRaiseVolume => Some(Key::VolumeUp), - // // => Some(Key::Wake), - // // => Some(Key::Webback), - // // => Some(Key::WebFavorites), - // // => Some(Key::WebForward), - // // => Some(Key::WebHome), - // // => Some(Key::WebRefresh), - // // => Some(Key::WebSearch), - // // => Some(Key::WebStop), - // keysyms::XKB_KEY_yen => Some(Key::Yen), - // keysyms::XKB_KEY_XF86Copy => Some(Key::Copy), - // keysyms::XKB_KEY_XF86Paste => Some(Key::Paste), - // keysyms::XKB_KEY_XF86Cut => Some(Key::Cut), - // // Fallback. - _ => Key::Unidentified(NativeKeyCode::Unidentified), - } -} diff --git a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs b/src/platform_impl/linux/wayland/seat/keyboard/mod.rs index d71719ecd2..c5c090bf04 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/mod.rs @@ -16,7 +16,6 @@ use crate::platform_impl::wayland::event_loop::WinitState; use crate::platform_impl::wayland::WindowId; mod handlers; -mod keymap; pub(crate) struct Keyboard { pub keyboard: WlKeyboard, From f13a00e71c526fb2df42fd24f69644baeb0ca67e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Mon, 29 Mar 2021 02:24:03 +0200 Subject: [PATCH 071/131] Add key location and some minor corrections Also add a `Meta` key (code) value. --- src/keyboard.rs | 6 ++ src/platform_impl/linux/common/keymap.rs | 71 ++++++++++++++++--- .../linux/wayland/seat/keyboard/handlers.rs | 13 ++-- 3 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 28983cfaf4..6329695fcf 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -536,6 +536,9 @@ pub enum KeyCode { AudioVolumeMute, AudioVolumeUp, WakeUp, + // Legacy modifier key. Also called "Super" in certain places. + Meta, + // Legacy modifier key. Hyper, Turbo, Abort, @@ -731,6 +734,9 @@ pub enum Key<'a> { /// The Symbol modifier key (used on some virtual keyboards). Symbol, SymbolLock, + // Legacy modifier key. Also called "Super" in certain places. + Meta, + // Legacy modifier key. 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. diff --git a/src/platform_impl/linux/common/keymap.rs b/src/platform_impl/linux/common/keymap.rs index eeccf65a97..d3438f0ef9 100644 --- a/src/platform_impl/linux/common/keymap.rs +++ b/src/platform_impl/linux/common/keymap.rs @@ -315,9 +315,9 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { xkb::KEY_Up => Key::ArrowUp, xkb::KEY_Right => Key::ArrowRight, xkb::KEY_Down => Key::ArrowDown, - // xkb::KEY_Prior => Key::Prior, + // xkb::KEY_Prior => Key::PageUp, xkb::KEY_Page_Up => Key::PageUp, - // xkb::KEY_Next => Key::Next, + // xkb::KEY_Next => Key::PageDown, xkb::KEY_End => Key::End, // xkb::KEY_Begin => Key::Begin, @@ -350,9 +350,9 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { xkb::KEY_KP_Up => Key::ArrowLeft, xkb::KEY_KP_Right => Key::ArrowRight, xkb::KEY_KP_Down => Key::ArrowDown, - // xkb::KEY_KP_Prior => Key::Prior, + // xkb::KEY_KP_Prior => Key::PageUp, xkb::KEY_KP_Page_Up => Key::PageUp, - // xkb::KEY_KP_Next => Key::Next, + // xkb::KEY_KP_Next => Key::PageDown, xkb::KEY_KP_Page_Down => Key::PageDown, xkb::KEY_KP_End => Key::End, // xkb::KEY_KP_Begin => Key::Begin, @@ -421,14 +421,15 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { xkb::KEY_Control_R => Key::Control, xkb::KEY_Caps_Lock => Key::CapsLock, // xkb::KEY_Shift_Lock => Key::ShiftLock, - // + + // NOTE: The key xkb calls "Meta" is called "Super" by Winit, and vice versa. + // This is a tad confusing, but these keys have different names depending on who you ask. xkb::KEY_Meta_L => Key::Super, xkb::KEY_Meta_R => Key::Super, xkb::KEY_Alt_L => Key::Alt, xkb::KEY_Alt_R => Key::Alt, - // NOTE: The key xkb calls "Meta" is called "Super" by Winit. - // xkb::KEY_Super_L => Key::Super, - // xkb::KEY_Super_R => Key::Super, + xkb::KEY_Super_L => Key::Meta, + xkb::KEY_Super_R => Key::Meta, xkb::KEY_Hyper_L => Key::Hyper, xkb::KEY_Hyper_R => Key::Hyper, @@ -683,3 +684,57 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { _ => Key::Unidentified(NativeKeyCode::XKB(keysym)), } } + +pub fn keysym_location(keysym: u32) -> KeyLocation { + use xkbcommon::xkb; + match keysym { + xkb::KEY_Shift_L + | xkb::KEY_Control_L + | xkb::KEY_Meta_L + | xkb::KEY_Alt_L + | xkb::KEY_Super_L + | xkb::KEY_Hyper_L => KeyLocation::Left, + xkb::KEY_Shift_R + | xkb::KEY_Control_R + | xkb::KEY_Meta_R + | xkb::KEY_Alt_R + | xkb::KEY_Super_R + | xkb::KEY_Hyper_R => KeyLocation::Right, + xkb::KEY_KP_0 + | xkb::KEY_KP_1 + | xkb::KEY_KP_2 + | xkb::KEY_KP_3 + | xkb::KEY_KP_4 + | xkb::KEY_KP_5 + | xkb::KEY_KP_6 + | xkb::KEY_KP_7 + | xkb::KEY_KP_8 + | xkb::KEY_KP_9 + | xkb::KEY_KP_Space + | xkb::KEY_KP_Tab + | xkb::KEY_KP_Enter + | xkb::KEY_KP_F1 + | xkb::KEY_KP_F2 + | xkb::KEY_KP_F3 + | xkb::KEY_KP_F4 + | xkb::KEY_KP_Home + | xkb::KEY_KP_Left + | xkb::KEY_KP_Up + | xkb::KEY_KP_Right + | xkb::KEY_KP_Down + | xkb::KEY_KP_Page_Up + | xkb::KEY_KP_Page_Down + | xkb::KEY_KP_End + | xkb::KEY_KP_Begin + | xkb::KEY_KP_Insert + | xkb::KEY_KP_Delete + | xkb::KEY_KP_Equal + | xkb::KEY_KP_Multiply + | xkb::KEY_KP_Add + | xkb::KEY_KP_Separator + | xkb::KEY_KP_Subtract + | xkb::KEY_KP_Decimal + | xkb::KEY_KP_Divide => KeyLocation::Numpad, + _ => KeyLocation::Standard, + } +} diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs index 91c1edf8cc..268beb6fa3 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs @@ -5,13 +5,12 @@ use sctk::reexports::client::protocol::wl_keyboard::KeyState; use sctk::seat::keyboard::Event as KeyboardEvent; use crate::event::{ElementState, KeyEvent, WindowEvent}; -use crate::keyboard::{Key, KeyLocation, ModifiersState, NativeKeyCode}; +use crate::keyboard::{Key, ModifiersState, NativeKeyCode}; +use crate::platform_impl::platform::common::keymap; use crate::platform_impl::wayland::event_loop::WinitState; use crate::platform_impl::wayland::{self, DeviceId}; use crate::platform_impl::KeyEventExtra; -// TODO: This is kind of terrible -use super::super::super::super::common::keymap; use super::KeyboardInner; #[inline] @@ -73,6 +72,7 @@ pub(super) fn handle_keyboard( let physical_key = keymap::rawkey_to_keycode(rawkey); let logical_key = keymap::keysym_to_key(keysym); + let location = keymap::keysym_location(keysym); event_sink.push_window_event( WindowEvent::KeyboardInput { @@ -83,7 +83,7 @@ pub(super) fn handle_keyboard( physical_key, logical_key, text: None, - location: KeyLocation::Standard, + location, state, repeat: false, platform_specific: KeyEventExtra { @@ -120,6 +120,7 @@ pub(super) fn handle_keyboard( let physical_key = keymap::rawkey_to_keycode(rawkey); let logical_key = keymap::keysym_to_key(keysym); + let location = keymap::keysym_location(keysym); event_sink.push_window_event( WindowEvent::KeyboardInput { @@ -130,9 +131,9 @@ pub(super) fn handle_keyboard( physical_key, logical_key, text: None, - location: KeyLocation::Standard, + location, state: ElementState::Pressed, - repeat: false, + repeat: true, platform_specific: KeyEventExtra { key_without_modifiers: Key::Unidentified(NativeKeyCode::Unidentified), text_with_all_modifers: None, From fe38f4f330bf662a6616069cebcf1e060bdd2337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Tue, 30 Mar 2021 00:12:38 +0200 Subject: [PATCH 072/131] Use xkbcommon-dl rather than xkbcommon --- Cargo.toml | 2 +- src/platform_impl/linux/common/keymap.rs | 826 +++++++++++------------ 2 files changed, 414 insertions(+), 414 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b40e6679a0..38f4d49dae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,7 +94,7 @@ mio-extras = { version = "2.0", optional = true } x11-dl = { version = "2.18.5", optional = true } percent-encoding = { version = "2.0", optional = true } parking_lot = { version = "0.11.0", optional = true } -xkbcommon = "0.4.0" +xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "224646a" } [target.'cfg(target_arch = "wasm32")'.dependencies.web_sys] package = "web-sys" diff --git a/src/platform_impl/linux/common/keymap.rs b/src/platform_impl/linux/common/keymap.rs index d3438f0ef9..3016851dd0 100644 --- a/src/platform_impl/linux/common/keymap.rs +++ b/src/platform_impl/linux/common/keymap.rs @@ -264,477 +264,477 @@ pub fn rawkey_to_keycode(rawkey: u32) -> KeyCode { } pub fn keysym_to_key(keysym: u32) -> Key<'static> { - use xkbcommon::xkb; + use xkbcommon_dl::keysyms; match keysym { // TTY function keys - xkb::KEY_BackSpace => Key::Backspace, - xkb::KEY_Tab => Key::Tab, - // xkb::KEY_Linefeed => Key::Linefeed, - xkb::KEY_Clear => Key::Clear, - xkb::KEY_Return => Key::Enter, - // xkb::KEY_Pause => Key::Pause, - xkb::KEY_Scroll_Lock => Key::ScrollLock, - xkb::KEY_Sys_Req => Key::PrintScreen, - xkb::KEY_Escape => Key::Escape, - xkb::KEY_Delete => Key::Delete, + keysyms::XKB_KEY_BackSpace => Key::Backspace, + keysyms::XKB_KEY_Tab => Key::Tab, + // keysyms::XKB_KEY_Linefeed => Key::Linefeed, + keysyms::XKB_KEY_Clear => Key::Clear, + keysyms::XKB_KEY_Return => Key::Enter, + // keysyms::XKB_KEY_Pause => Key::Pause, + keysyms::XKB_KEY_Scroll_Lock => Key::ScrollLock, + keysyms::XKB_KEY_Sys_Req => Key::PrintScreen, + keysyms::XKB_KEY_Escape => Key::Escape, + keysyms::XKB_KEY_Delete => Key::Delete, // IME keys - xkb::KEY_Multi_key => Key::Compose, - xkb::KEY_Codeinput => Key::CodeInput, - xkb::KEY_SingleCandidate => Key::SingleCandidate, - xkb::KEY_MultipleCandidate => Key::AllCandidates, - xkb::KEY_PreviousCandidate => Key::PreviousCandidate, + keysyms::XKB_KEY_Multi_key => Key::Compose, + keysyms::XKB_KEY_Codeinput => Key::CodeInput, + keysyms::XKB_KEY_SingleCandidate => Key::SingleCandidate, + keysyms::XKB_KEY_MultipleCandidate => Key::AllCandidates, + keysyms::XKB_KEY_PreviousCandidate => Key::PreviousCandidate, // Japanese keys - xkb::KEY_Kanji => Key::KanjiMode, - xkb::KEY_Muhenkan => Key::NonConvert, - xkb::KEY_Henkan_Mode => Key::Convert, - xkb::KEY_Romaji => Key::Romaji, - xkb::KEY_Hiragana => Key::Hiragana, - xkb::KEY_Hiragana_Katakana => Key::HiraganaKatakana, - xkb::KEY_Zenkaku => Key::Zenkaku, - xkb::KEY_Hankaku => Key::Hankaku, - xkb::KEY_Zenkaku_Hankaku => Key::ZenkakuHankaku, - // xkb::KEY_Touroku => Key::Touroku, - // xkb::KEY_Massyo => Key::Massyo, - xkb::KEY_Kana_Lock => Key::KanaMode, + keysyms::XKB_KEY_Kanji => Key::KanjiMode, + keysyms::XKB_KEY_Muhenkan => Key::NonConvert, + keysyms::XKB_KEY_Henkan_Mode => Key::Convert, + keysyms::XKB_KEY_Romaji => Key::Romaji, + keysyms::XKB_KEY_Hiragana => Key::Hiragana, + keysyms::XKB_KEY_Hiragana_Katakana => Key::HiraganaKatakana, + keysyms::XKB_KEY_Zenkaku => Key::Zenkaku, + keysyms::XKB_KEY_Hankaku => Key::Hankaku, + keysyms::XKB_KEY_Zenkaku_Hankaku => Key::ZenkakuHankaku, + // keysyms::XKB_KEY_Touroku => Key::Touroku, + // keysyms::XKB_KEY_Massyo => Key::Massyo, + keysyms::XKB_KEY_Kana_Lock => Key::KanaMode, // TODO: This seems a tad perverse, but I'm not really familiar with japanese keyboards. // MDN documents this as a valid mapping, however. - // xkb::KEY_Kana_Shift => Key::KanaMode, + // keysyms::XKB_KEY_Kana_Shift => Key::KanaMode, // TODO: Is this the correct mapping? - // xkb::KEY_Eisu_Shift => Key::Alphanumeric, - // xkb::KEY_Eisu_toggle => Key::Alphanumeric, + // keysyms::XKB_KEY_Eisu_Shift => Key::Alphanumeric, + // keysyms::XKB_KEY_Eisu_toggle => Key::Alphanumeric, // NOTE: The next three items are aliases for values we've already mapped. - // xkb::KEY_Kanji_Bangou => Key::CodeInput, - // xkb::KEY_Zen_Koho => Key::AllCandidates, - // xkb::KEY_Mae_Koho => Key::PreviousCandidate, + // keysyms::XKB_KEY_Kanji_Bangou => Key::CodeInput, + // keysyms::XKB_KEY_Zen_Koho => Key::AllCandidates, + // keysyms::XKB_KEY_Mae_Koho => Key::PreviousCandidate, // Cursor control & motion - xkb::KEY_Home => Key::Home, - xkb::KEY_Left => Key::ArrowLeft, - xkb::KEY_Up => Key::ArrowUp, - xkb::KEY_Right => Key::ArrowRight, - xkb::KEY_Down => Key::ArrowDown, - // xkb::KEY_Prior => Key::PageUp, - xkb::KEY_Page_Up => Key::PageUp, - // xkb::KEY_Next => Key::PageDown, - xkb::KEY_End => Key::End, - // xkb::KEY_Begin => Key::Begin, + keysyms::XKB_KEY_Home => Key::Home, + keysyms::XKB_KEY_Left => Key::ArrowLeft, + keysyms::XKB_KEY_Up => Key::ArrowUp, + keysyms::XKB_KEY_Right => Key::ArrowRight, + keysyms::XKB_KEY_Down => Key::ArrowDown, + // keysyms::XKB_KEY_Prior => Key::PageUp, + keysyms::XKB_KEY_Page_Up => Key::PageUp, + // keysyms::XKB_KEY_Next => Key::PageDown, + keysyms::XKB_KEY_End => Key::End, + // keysyms::XKB_KEY_Begin => Key::Begin, // Misc. functions - xkb::KEY_Select => Key::Select, - xkb::KEY_Print => Key::PrintScreen, - xkb::KEY_Execute => Key::Execute, - xkb::KEY_Insert => Key::Insert, - xkb::KEY_Undo => Key::Undo, - xkb::KEY_Redo => Key::Redo, - xkb::KEY_Menu => Key::ContextMenu, - xkb::KEY_Find => Key::Find, - xkb::KEY_Cancel => Key::Cancel, - xkb::KEY_Help => Key::Help, - xkb::KEY_Break => Key::Pause, - xkb::KEY_Mode_switch => Key::ModeChange, - // xkb::KEY_script_switch => Key::ModeChange, - xkb::KEY_Num_Lock => Key::NumLock, + keysyms::XKB_KEY_Select => Key::Select, + keysyms::XKB_KEY_Print => Key::PrintScreen, + keysyms::XKB_KEY_Execute => Key::Execute, + keysyms::XKB_KEY_Insert => Key::Insert, + keysyms::XKB_KEY_Undo => Key::Undo, + keysyms::XKB_KEY_Redo => Key::Redo, + keysyms::XKB_KEY_Menu => Key::ContextMenu, + keysyms::XKB_KEY_Find => Key::Find, + keysyms::XKB_KEY_Cancel => Key::Cancel, + keysyms::XKB_KEY_Help => Key::Help, + keysyms::XKB_KEY_Break => Key::Pause, + keysyms::XKB_KEY_Mode_switch => Key::ModeChange, + // keysyms::XKB_KEY_script_switch => Key::ModeChange, + keysyms::XKB_KEY_Num_Lock => Key::NumLock, // Keypad keys - // xkb::KEY_KP_Space => Key::Character(" "), - xkb::KEY_KP_Tab => Key::Tab, - xkb::KEY_KP_Enter => Key::Enter, - xkb::KEY_KP_F1 => Key::F1, - xkb::KEY_KP_F2 => Key::F2, - xkb::KEY_KP_F3 => Key::F3, - xkb::KEY_KP_F4 => Key::F4, - xkb::KEY_KP_Home => Key::Home, - xkb::KEY_KP_Left => Key::ArrowLeft, - xkb::KEY_KP_Up => Key::ArrowLeft, - xkb::KEY_KP_Right => Key::ArrowRight, - xkb::KEY_KP_Down => Key::ArrowDown, - // xkb::KEY_KP_Prior => Key::PageUp, - xkb::KEY_KP_Page_Up => Key::PageUp, - // xkb::KEY_KP_Next => Key::PageDown, - xkb::KEY_KP_Page_Down => Key::PageDown, - xkb::KEY_KP_End => Key::End, - // xkb::KEY_KP_Begin => Key::Begin, - xkb::KEY_KP_Insert => Key::Insert, - xkb::KEY_KP_Delete => Key::Delete, - // xkb::KEY_KP_Equal => Key::Equal, - // xkb::KEY_KP_Multiply => Key::Multiply, - // xkb::KEY_KP_Add => Key::Add, - // xkb::KEY_KP_Separator => Key::Separator, - // xkb::KEY_KP_Subtract => Key::Subtract, - // xkb::KEY_KP_Decimal => Key::Decimal, - // xkb::KEY_KP_Divide => Key::Divide, - - // xkb::KEY_KP_0 => Key::Character("0"), - // xkb::KEY_KP_1 => Key::Character("1"), - // xkb::KEY_KP_2 => Key::Character("2"), - // xkb::KEY_KP_3 => Key::Character("3"), - // xkb::KEY_KP_4 => Key::Character("4"), - // xkb::KEY_KP_5 => Key::Character("5"), - // xkb::KEY_KP_6 => Key::Character("6"), - // xkb::KEY_KP_7 => Key::Character("7"), - // xkb::KEY_KP_8 => Key::Character("8"), - // xkb::KEY_KP_9 => Key::Character("9"), + // keysyms::XKB_KEY_KP_Space => Key::Character(" "), + keysyms::XKB_KEY_KP_Tab => Key::Tab, + keysyms::XKB_KEY_KP_Enter => Key::Enter, + keysyms::XKB_KEY_KP_F1 => Key::F1, + keysyms::XKB_KEY_KP_F2 => Key::F2, + keysyms::XKB_KEY_KP_F3 => Key::F3, + keysyms::XKB_KEY_KP_F4 => Key::F4, + keysyms::XKB_KEY_KP_Home => Key::Home, + keysyms::XKB_KEY_KP_Left => Key::ArrowLeft, + keysyms::XKB_KEY_KP_Up => Key::ArrowLeft, + keysyms::XKB_KEY_KP_Right => Key::ArrowRight, + keysyms::XKB_KEY_KP_Down => Key::ArrowDown, + // keysyms::XKB_KEY_KP_Prior => Key::PageUp, + keysyms::XKB_KEY_KP_Page_Up => Key::PageUp, + // keysyms::XKB_KEY_KP_Next => Key::PageDown, + keysyms::XKB_KEY_KP_Page_Down => Key::PageDown, + keysyms::XKB_KEY_KP_End => Key::End, + // keysyms::XKB_KEY_KP_Begin => Key::Begin, + keysyms::XKB_KEY_KP_Insert => Key::Insert, + keysyms::XKB_KEY_KP_Delete => Key::Delete, + // keysyms::XKB_KEY_KP_Equal => Key::Equal, + // keysyms::XKB_KEY_KP_Multiply => Key::Multiply, + // keysyms::XKB_KEY_KP_Add => Key::Add, + // keysyms::XKB_KEY_KP_Separator => Key::Separator, + // keysyms::XKB_KEY_KP_Subtract => Key::Subtract, + // keysyms::XKB_KEY_KP_Decimal => Key::Decimal, + // keysyms::XKB_KEY_KP_Divide => Key::Divide, + + // keysyms::XKB_KEY_KP_0 => Key::Character("0"), + // keysyms::XKB_KEY_KP_1 => Key::Character("1"), + // keysyms::XKB_KEY_KP_2 => Key::Character("2"), + // keysyms::XKB_KEY_KP_3 => Key::Character("3"), + // keysyms::XKB_KEY_KP_4 => Key::Character("4"), + // keysyms::XKB_KEY_KP_5 => Key::Character("5"), + // keysyms::XKB_KEY_KP_6 => Key::Character("6"), + // keysyms::XKB_KEY_KP_7 => Key::Character("7"), + // keysyms::XKB_KEY_KP_8 => Key::Character("8"), + // keysyms::XKB_KEY_KP_9 => Key::Character("9"), // Function keys - xkb::KEY_F1 => Key::F1, - xkb::KEY_F2 => Key::F2, - xkb::KEY_F3 => Key::F3, - xkb::KEY_F4 => Key::F4, - xkb::KEY_F5 => Key::F5, - xkb::KEY_F6 => Key::F6, - xkb::KEY_F7 => Key::F7, - xkb::KEY_F8 => Key::F8, - xkb::KEY_F9 => Key::F9, - xkb::KEY_F10 => Key::F10, - xkb::KEY_F11 => Key::F11, - xkb::KEY_F12 => Key::F12, - xkb::KEY_F13 => Key::F13, - xkb::KEY_F14 => Key::F14, - xkb::KEY_F15 => Key::F15, - xkb::KEY_F16 => Key::F16, - xkb::KEY_F17 => Key::F17, - xkb::KEY_F18 => Key::F18, - xkb::KEY_F19 => Key::F19, - xkb::KEY_F20 => Key::F20, - xkb::KEY_F21 => Key::F21, - xkb::KEY_F22 => Key::F22, - xkb::KEY_F23 => Key::F23, - xkb::KEY_F24 => Key::F24, - xkb::KEY_F25 => Key::F25, - xkb::KEY_F26 => Key::F26, - xkb::KEY_F27 => Key::F27, - xkb::KEY_F28 => Key::F28, - xkb::KEY_F29 => Key::F29, - xkb::KEY_F30 => Key::F30, - xkb::KEY_F31 => Key::F31, - xkb::KEY_F32 => Key::F32, - xkb::KEY_F33 => Key::F33, - xkb::KEY_F34 => Key::F34, - xkb::KEY_F35 => Key::F35, + keysyms::XKB_KEY_F1 => Key::F1, + keysyms::XKB_KEY_F2 => Key::F2, + keysyms::XKB_KEY_F3 => Key::F3, + keysyms::XKB_KEY_F4 => Key::F4, + keysyms::XKB_KEY_F5 => Key::F5, + keysyms::XKB_KEY_F6 => Key::F6, + keysyms::XKB_KEY_F7 => Key::F7, + keysyms::XKB_KEY_F8 => Key::F8, + keysyms::XKB_KEY_F9 => Key::F9, + keysyms::XKB_KEY_F10 => Key::F10, + keysyms::XKB_KEY_F11 => Key::F11, + keysyms::XKB_KEY_F12 => Key::F12, + keysyms::XKB_KEY_F13 => Key::F13, + keysyms::XKB_KEY_F14 => Key::F14, + keysyms::XKB_KEY_F15 => Key::F15, + keysyms::XKB_KEY_F16 => Key::F16, + keysyms::XKB_KEY_F17 => Key::F17, + keysyms::XKB_KEY_F18 => Key::F18, + keysyms::XKB_KEY_F19 => Key::F19, + keysyms::XKB_KEY_F20 => Key::F20, + keysyms::XKB_KEY_F21 => Key::F21, + keysyms::XKB_KEY_F22 => Key::F22, + keysyms::XKB_KEY_F23 => Key::F23, + keysyms::XKB_KEY_F24 => Key::F24, + keysyms::XKB_KEY_F25 => Key::F25, + keysyms::XKB_KEY_F26 => Key::F26, + keysyms::XKB_KEY_F27 => Key::F27, + keysyms::XKB_KEY_F28 => Key::F28, + keysyms::XKB_KEY_F29 => Key::F29, + keysyms::XKB_KEY_F30 => Key::F30, + keysyms::XKB_KEY_F31 => Key::F31, + keysyms::XKB_KEY_F32 => Key::F32, + keysyms::XKB_KEY_F33 => Key::F33, + keysyms::XKB_KEY_F34 => Key::F34, + keysyms::XKB_KEY_F35 => Key::F35, // Modifiers - xkb::KEY_Shift_L => Key::Shift, - xkb::KEY_Shift_R => Key::Shift, - xkb::KEY_Control_L => Key::Control, - xkb::KEY_Control_R => Key::Control, - xkb::KEY_Caps_Lock => Key::CapsLock, - // xkb::KEY_Shift_Lock => Key::ShiftLock, + keysyms::XKB_KEY_Shift_L => Key::Shift, + keysyms::XKB_KEY_Shift_R => Key::Shift, + keysyms::XKB_KEY_Control_L => Key::Control, + keysyms::XKB_KEY_Control_R => Key::Control, + keysyms::XKB_KEY_Caps_Lock => Key::CapsLock, + // keysyms::XKB_KEY_Shift_Lock => Key::ShiftLock, // NOTE: The key xkb calls "Meta" is called "Super" by Winit, and vice versa. // This is a tad confusing, but these keys have different names depending on who you ask. - xkb::KEY_Meta_L => Key::Super, - xkb::KEY_Meta_R => Key::Super, - xkb::KEY_Alt_L => Key::Alt, - xkb::KEY_Alt_R => Key::Alt, - xkb::KEY_Super_L => Key::Meta, - xkb::KEY_Super_R => Key::Meta, - xkb::KEY_Hyper_L => Key::Hyper, - xkb::KEY_Hyper_R => Key::Hyper, + keysyms::XKB_KEY_Meta_L => Key::Super, + keysyms::XKB_KEY_Meta_R => Key::Super, + keysyms::XKB_KEY_Alt_L => Key::Alt, + keysyms::XKB_KEY_Alt_R => Key::Alt, + keysyms::XKB_KEY_Super_L => Key::Meta, + keysyms::XKB_KEY_Super_R => Key::Meta, + keysyms::XKB_KEY_Hyper_L => Key::Hyper, + keysyms::XKB_KEY_Hyper_R => Key::Hyper, // XKB function and modifier keys - // xkb::KEY_ISO_Lock => Key::IsoLock, - // xkb::KEY_ISO_Level2_Latch => Key::IsoLevel2Latch, + // keysyms::XKB_KEY_ISO_Lock => Key::IsoLock, + // keysyms::XKB_KEY_ISO_Level2_Latch => Key::IsoLevel2Latch, // NOTE: I'm not quite certain if mapping the next 3 values to AltGraph is correct. - // xkb::KEY_ISO_Level3_Shift => Key::AltGraph, - // xkb::KEY_ISO_Level3_Latch => Key::AltGraph, - // xkb::KEY_ISO_Level3_Lock => Key::AltGraph, - // xkb::KEY_ISO_Level5_Shift => Key::IsoLevel5Shift, - // xkb::KEY_ISO_Level5_Latch => Key::IsoLevel5Latch, - // xkb::KEY_ISO_Level5_Lock => Key::IsoLevel5Lock, - // xkb::KEY_ISO_Group_Shift => Key::IsoGroupShift, - // xkb::KEY_ISO_Group_Latch => Key::IsoGroupLatch, - // xkb::KEY_ISO_Group_Lock => Key::IsoGroupLock, - xkb::KEY_ISO_Next_Group => Key::GroupNext, - // xkb::KEY_ISO_Next_Group_Lock => Key::GroupNextLock, - xkb::KEY_ISO_Prev_Group => Key::GroupPrevious, - // xkb::KEY_ISO_Prev_Group_Lock => Key::GroupPreviousLock, - xkb::KEY_ISO_First_Group => Key::GroupFirst, - // xkb::KEY_ISO_First_Group_Lock => Key::GroupFirstLock, - xkb::KEY_ISO_Last_Group => Key::GroupLast, - // xkb::KEY_ISO_Last_Group_Lock => Key::GroupLastLock, + // keysyms::XKB_KEY_ISO_Level3_Shift => Key::AltGraph, + // keysyms::XKB_KEY_ISO_Level3_Latch => Key::AltGraph, + // keysyms::XKB_KEY_ISO_Level3_Lock => Key::AltGraph, + // keysyms::XKB_KEY_ISO_Level5_Shift => Key::IsoLevel5Shift, + // keysyms::XKB_KEY_ISO_Level5_Latch => Key::IsoLevel5Latch, + // keysyms::XKB_KEY_ISO_Level5_Lock => Key::IsoLevel5Lock, + // keysyms::XKB_KEY_ISO_Group_Shift => Key::IsoGroupShift, + // keysyms::XKB_KEY_ISO_Group_Latch => Key::IsoGroupLatch, + // keysyms::XKB_KEY_ISO_Group_Lock => Key::IsoGroupLock, + keysyms::XKB_KEY_ISO_Next_Group => Key::GroupNext, + // keysyms::XKB_KEY_ISO_Next_Group_Lock => Key::GroupNextLock, + keysyms::XKB_KEY_ISO_Prev_Group => Key::GroupPrevious, + // keysyms::XKB_KEY_ISO_Prev_Group_Lock => Key::GroupPreviousLock, + keysyms::XKB_KEY_ISO_First_Group => Key::GroupFirst, + // keysyms::XKB_KEY_ISO_First_Group_Lock => Key::GroupFirstLock, + keysyms::XKB_KEY_ISO_Last_Group => Key::GroupLast, + // keysyms::XKB_KEY_ISO_Last_Group_Lock => Key::GroupLastLock, // - xkb::KEY_ISO_Left_Tab => Key::Tab, - // xkb::KEY_ISO_Move_Line_Up => Key::IsoMoveLineUp, - // xkb::KEY_ISO_Move_Line_Down => Key::IsoMoveLineDown, - // xkb::KEY_ISO_Partial_Line_Up => Key::IsoPartialLineUp, - // xkb::KEY_ISO_Partial_Line_Down => Key::IsoPartialLineDown, - // xkb::KEY_ISO_Partial_Space_Left => Key::IsoPartialSpaceLeft, - // xkb::KEY_ISO_Partial_Space_Right => Key::IsoPartialSpaceRight, - // xkb::KEY_ISO_Set_Margin_Left => Key::IsoSetMarginLeft, - // xkb::KEY_ISO_Set_Margin_Right => Key::IsoSetMarginRight, - // xkb::KEY_ISO_Release_Margin_Left => Key::IsoReleaseMarginLeft, - // xkb::KEY_ISO_Release_Margin_Right => Key::IsoReleaseMarginRight, - // xkb::KEY_ISO_Release_Both_Margins => Key::IsoReleaseBothMargins, - // xkb::KEY_ISO_Fast_Cursor_Left => Key::IsoFastCursorLeft, - // xkb::KEY_ISO_Fast_Cursor_Right => Key::IsoFastCursorRight, - // xkb::KEY_ISO_Fast_Cursor_Up => Key::IsoFastCursorUp, - // xkb::KEY_ISO_Fast_Cursor_Down => Key::IsoFastCursorDown, - // xkb::KEY_ISO_Continuous_Underline => Key::IsoContinuousUnderline, - // xkb::KEY_ISO_Discontinuous_Underline => Key::IsoDiscontinuousUnderline, - // xkb::KEY_ISO_Emphasize => Key::IsoEmphasize, - // xkb::KEY_ISO_Center_Object => Key::IsoCenterObject, - xkb::KEY_ISO_Enter => Key::Enter, + keysyms::XKB_KEY_ISO_Left_Tab => Key::Tab, + // keysyms::XKB_KEY_ISO_Move_Line_Up => Key::IsoMoveLineUp, + // keysyms::XKB_KEY_ISO_Move_Line_Down => Key::IsoMoveLineDown, + // keysyms::XKB_KEY_ISO_Partial_Line_Up => Key::IsoPartialLineUp, + // keysyms::XKB_KEY_ISO_Partial_Line_Down => Key::IsoPartialLineDown, + // keysyms::XKB_KEY_ISO_Partial_Space_Left => Key::IsoPartialSpaceLeft, + // keysyms::XKB_KEY_ISO_Partial_Space_Right => Key::IsoPartialSpaceRight, + // keysyms::XKB_KEY_ISO_Set_Margin_Left => Key::IsoSetMarginLeft, + // keysyms::XKB_KEY_ISO_Set_Margin_Right => Key::IsoSetMarginRight, + // keysyms::XKB_KEY_ISO_Release_Margin_Left => Key::IsoReleaseMarginLeft, + // keysyms::XKB_KEY_ISO_Release_Margin_Right => Key::IsoReleaseMarginRight, + // keysyms::XKB_KEY_ISO_Release_Both_Margins => Key::IsoReleaseBothMargins, + // keysyms::XKB_KEY_ISO_Fast_Cursor_Left => Key::IsoFastCursorLeft, + // keysyms::XKB_KEY_ISO_Fast_Cursor_Right => Key::IsoFastCursorRight, + // keysyms::XKB_KEY_ISO_Fast_Cursor_Up => Key::IsoFastCursorUp, + // keysyms::XKB_KEY_ISO_Fast_Cursor_Down => Key::IsoFastCursorDown, + // keysyms::XKB_KEY_ISO_Continuous_Underline => Key::IsoContinuousUnderline, + // keysyms::XKB_KEY_ISO_Discontinuous_Underline => Key::IsoDiscontinuousUnderline, + // keysyms::XKB_KEY_ISO_Emphasize => Key::IsoEmphasize, + // keysyms::XKB_KEY_ISO_Center_Object => Key::IsoCenterObject, + keysyms::XKB_KEY_ISO_Enter => Key::Enter, - // KEY_dead_grave..KEY_dead_currency + // XKB_KEY_dead_grave..XKB_KEY_dead_currency - // KEY_dead_lowline..KEY_dead_longsolidusoverlay + // XKB_KEY_dead_lowline..XKB_KEY_dead_longsolidusoverlay - // KEY_dead_a..KEY_dead_capital_schwa + // XKB_KEY_dead_a..XKB_KEY_dead_capital_schwa - // KEY_dead_greek + // XKB_KEY_dead_greek - // KEY_First_Virtual_Screen..KEY_Terminate_Server + // XKB_KEY_First_Virtual_Screen..XKB_KEY_Terminate_Server - // KEY_AccessX_Enable..KEY_AudibleBell_Enable + // XKB_KEY_AccessX_Enable..XKB_KEY_AudibleBell_Enable - // KEY_Pointer_Left..KEY_Pointer_Drag5 + // XKB_KEY_Pointer_Left..XKB_KEY_Pointer_Drag5 - // KEY_Pointer_EnableKeys..KEY_Pointer_DfltBtnPrev + // XKB_KEY_Pointer_EnableKeys..XKB_KEY_Pointer_DfltBtnPrev - // KEY_ch..KEY_C_H + // XKB_KEY_ch..XKB_KEY_C_H // 3270 terminal keys - // xkb::KEY_3270_Duplicate => Key::Duplicate, - // xkb::KEY_3270_FieldMark => Key::FieldMark, - // xkb::KEY_3270_Right2 => Key::Right2, - // xkb::KEY_3270_Left2 => Key::Left2, - // xkb::KEY_3270_BackTab => Key::BackTab, - xkb::KEY_3270_EraseEOF => Key::EraseEof, - // xkb::KEY_3270_EraseInput => Key::EraseInput, - // xkb::KEY_3270_Reset => Key::Reset, - // xkb::KEY_3270_Quit => Key::Quit, - // xkb::KEY_3270_PA1 => Key::Pa1, - // xkb::KEY_3270_PA2 => Key::Pa2, - // xkb::KEY_3270_PA3 => Key::Pa3, - // xkb::KEY_3270_Test => Key::Test, - xkb::KEY_3270_Attn => Key::Attn, - // xkb::KEY_3270_CursorBlink => Key::CursorBlink, - // xkb::KEY_3270_AltCursor => Key::AltCursor, - // xkb::KEY_3270_KeyClick => Key::KeyClick, - // xkb::KEY_3270_Jump => Key::Jump, - // xkb::KEY_3270_Ident => Key::Ident, - // xkb::KEY_3270_Rule => Key::Rule, - // xkb::KEY_3270_Copy => Key::Copy, - xkb::KEY_3270_Play => Key::Play, - // xkb::KEY_3270_Setup => Key::Setup, - // xkb::KEY_3270_Record => Key::Record, - // xkb::KEY_3270_ChangeScreen => Key::ChangeScreen, - // xkb::KEY_3270_DeleteWord => Key::DeleteWord, - xkb::KEY_3270_ExSelect => Key::ExSel, - xkb::KEY_3270_CursorSelect => Key::CrSel, - xkb::KEY_3270_PrintScreen => Key::PrintScreen, - xkb::KEY_3270_Enter => Key::Enter, - - xkb::KEY_space => Key::Space, - // KEY_exclam..KEY_Sinh_kunddaliya + // keysyms::XKB_KEY_3270_Duplicate => Key::Duplicate, + // keysyms::XKB_KEY_3270_FieldMark => Key::FieldMark, + // keysyms::XKB_KEY_3270_Right2 => Key::Right2, + // keysyms::XKB_KEY_3270_Left2 => Key::Left2, + // keysyms::XKB_KEY_3270_BackTab => Key::BackTab, + keysyms::XKB_KEY_3270_EraseEOF => Key::EraseEof, + // keysyms::XKB_KEY_3270_EraseInput => Key::EraseInput, + // keysyms::XKB_KEY_3270_Reset => Key::Reset, + // keysyms::XKB_KEY_3270_Quit => Key::Quit, + // keysyms::XKB_KEY_3270_PA1 => Key::Pa1, + // keysyms::XKB_KEY_3270_PA2 => Key::Pa2, + // keysyms::XKB_KEY_3270_PA3 => Key::Pa3, + // keysyms::XKB_KEY_3270_Test => Key::Test, + keysyms::XKB_KEY_3270_Attn => Key::Attn, + // keysyms::XKB_KEY_3270_CursorBlink => Key::CursorBlink, + // keysyms::XKB_KEY_3270_AltCursor => Key::AltCursor, + // keysyms::XKB_KEY_3270_KeyClick => Key::KeyClick, + // keysyms::XKB_KEY_3270_Jump => Key::Jump, + // keysyms::XKB_KEY_3270_Ident => Key::Ident, + // keysyms::XKB_KEY_3270_Rule => Key::Rule, + // keysyms::XKB_KEY_3270_Copy => Key::Copy, + keysyms::XKB_KEY_3270_Play => Key::Play, + // keysyms::XKB_KEY_3270_Setup => Key::Setup, + // keysyms::XKB_KEY_3270_Record => Key::Record, + // keysyms::XKB_KEY_3270_ChangeScreen => Key::ChangeScreen, + // keysyms::XKB_KEY_3270_DeleteWord => Key::DeleteWord, + keysyms::XKB_KEY_3270_ExSelect => Key::ExSel, + keysyms::XKB_KEY_3270_CursorSelect => Key::CrSel, + keysyms::XKB_KEY_3270_PrintScreen => Key::PrintScreen, + keysyms::XKB_KEY_3270_Enter => Key::Enter, + + keysyms::XKB_KEY_space => Key::Space, + // XKB_KEY_exclam..XKB_KEY_Sinh_kunddaliya // XFree86 - // xkb::KEY_XF86ModeLock => Key::ModeLock, + // keysyms::XKB_KEY_XF86ModeLock => Key::ModeLock, // XFree86 - Backlight controls - xkb::KEY_XF86MonBrightnessUp => Key::BrightnessUp, - xkb::KEY_XF86MonBrightnessDown => Key::BrightnessDown, - // xkb::KEY_XF86KbdLightOnOff => Key::LightOnOff, - // xkb::KEY_XF86KbdBrightnessUp => Key::KeyboardBrightnessUp, - // xkb::KEY_XF86KbdBrightnessDown => Key::KeyboardBrightnessDown, + keysyms::XKB_KEY_XF86MonBrightnessUp => Key::BrightnessUp, + keysyms::XKB_KEY_XF86MonBrightnessDown => Key::BrightnessDown, + // keysyms::XKB_KEY_XF86KbdLightOnOff => Key::LightOnOff, + // keysyms::XKB_KEY_XF86KbdBrightnessUp => Key::KeyboardBrightnessUp, + // keysyms::XKB_KEY_XF86KbdBrightnessDown => Key::KeyboardBrightnessDown, // XFree86 - "Internet" - xkb::KEY_XF86Standby => Key::Standby, - xkb::KEY_XF86AudioLowerVolume => Key::AudioVolumeDown, - xkb::KEY_XF86AudioRaiseVolume => Key::AudioVolumeUp, - xkb::KEY_XF86AudioPlay => Key::MediaPlay, - xkb::KEY_XF86AudioStop => Key::MediaStop, - xkb::KEY_XF86AudioPrev => Key::MediaTrackPrevious, - xkb::KEY_XF86AudioNext => Key::MediaTrackNext, - xkb::KEY_XF86HomePage => Key::BrowserHome, - xkb::KEY_XF86Mail => Key::LaunchMail, - // xkb::KEY_XF86Start => Key::Start, - xkb::KEY_XF86Search => Key::BrowserSearch, - xkb::KEY_XF86AudioRecord => Key::MediaRecord, + keysyms::XKB_KEY_XF86Standby => Key::Standby, + keysyms::XKB_KEY_XF86AudioLowerVolume => Key::AudioVolumeDown, + keysyms::XKB_KEY_XF86AudioRaiseVolume => Key::AudioVolumeUp, + keysyms::XKB_KEY_XF86AudioPlay => Key::MediaPlay, + keysyms::XKB_KEY_XF86AudioStop => Key::MediaStop, + keysyms::XKB_KEY_XF86AudioPrev => Key::MediaTrackPrevious, + keysyms::XKB_KEY_XF86AudioNext => Key::MediaTrackNext, + keysyms::XKB_KEY_XF86HomePage => Key::BrowserHome, + keysyms::XKB_KEY_XF86Mail => Key::LaunchMail, + // keysyms::XKB_KEY_XF86Start => Key::Start, + keysyms::XKB_KEY_XF86Search => Key::BrowserSearch, + keysyms::XKB_KEY_XF86AudioRecord => Key::MediaRecord, // XFree86 - PDA - xkb::KEY_XF86Calculator => Key::LaunchApplication2, - // xkb::KEY_XF86Memo => Key::Memo, - // xkb::KEY_XF86ToDoList => Key::ToDoList, - xkb::KEY_XF86Calendar => Key::LaunchCalendar, - xkb::KEY_XF86PowerDown => Key::Power, - // xkb::KEY_XF86ContrastAdjust => Key::AdjustContrast, - // xkb::KEY_XF86RockerUp => Key::RockerUp, // TODO: Use Key::ArrowUp? - // xkb::KEY_XF86RockerDown => Key::RockerDown, // TODO: Use Key::ArrowDown? - // xkb::KEY_XF86RockerEnter => Key::RockerEnter, // TODO: Use Key::Enter? + keysyms::XKB_KEY_XF86Calculator => Key::LaunchApplication2, + // keysyms::XKB_KEY_XF86Memo => Key::Memo, + // keysyms::XKB_KEY_XF86ToDoList => Key::ToDoList, + keysyms::XKB_KEY_XF86Calendar => Key::LaunchCalendar, + keysyms::XKB_KEY_XF86PowerDown => Key::Power, + // keysyms::XKB_KEY_XF86ContrastAdjust => Key::AdjustContrast, + // keysyms::XKB_KEY_XF86RockerUp => Key::RockerUp, // TODO: Use Key::ArrowUp? + // keysyms::XKB_KEY_XF86RockerDown => Key::RockerDown, // TODO: Use Key::ArrowDown? + // keysyms::XKB_KEY_XF86RockerEnter => Key::RockerEnter, // TODO: Use Key::Enter? // XFree86 - More "Internet" - xkb::KEY_XF86Back => Key::BrowserBack, - xkb::KEY_XF86Forward => Key::BrowserForward, - // xkb::KEY_XF86Stop => Key::Stop, - xkb::KEY_XF86Refresh => Key::BrowserRefresh, - xkb::KEY_XF86PowerOff => Key::Power, - xkb::KEY_XF86WakeUp => Key::WakeUp, - xkb::KEY_XF86Eject => Key::Eject, - xkb::KEY_XF86ScreenSaver => Key::LaunchScreenSaver, - xkb::KEY_XF86WWW => Key::LaunchWebBrowser, - xkb::KEY_XF86Sleep => Key::Standby, - xkb::KEY_XF86Favorites => Key::BrowserFavorites, - xkb::KEY_XF86AudioPause => Key::MediaPause, - // xkb::KEY_XF86AudioMedia => Key::AudioMedia, - xkb::KEY_XF86MyComputer => Key::LaunchApplication1, - // xkb::KEY_XF86VendorHome => Key::VendorHome, - // xkb::KEY_XF86LightBulb => Key::LightBulb, - // xkb::KEY_XF86Shop => Key::BrowserShop, - // xkb::KEY_XF86History => Key::BrowserHistory, - // xkb::KEY_XF86OpenURL => Key::OpenUrl, - // xkb::KEY_XF86AddFavorite => Key::AddFavorite, - // xkb::KEY_XF86HotLinks => Key::HotLinks, - // xkb::KEY_XF86BrightnessAdjust => Key::BrightnessAdjust, - // xkb::KEY_XF86Finance => Key::BrowserFinance, - // xkb::KEY_XF86Community => Key::BrowserCommunity, - xkb::KEY_XF86AudioRewind => Key::MediaRewind, - // xkb::KEY_XF86BackForward => Key::???, - // KEY_XF86Launch0..KEY_XF86LaunchF - - // KEY_XF86ApplicationLeft..KEY_XF86CD - xkb::KEY_XF86Calculater => Key::LaunchApplication2, // This must be a typo, right? - // KEY_XF86Clear - xkb::KEY_XF86Close => Key::Close, - xkb::KEY_XF86Copy => Key::Copy, - xkb::KEY_XF86Cut => Key::Cut, - // KEY_XF86Display..KEY_XF86Documents - xkb::KEY_XF86Excel => Key::LaunchSpreadsheet, - // KEY_XF86Explorer..KEY_XF86iTouch - xkb::KEY_XF86LogOff => Key::LogOff, - // KEY_XF86Market..KEY_XF86MenuPB - xkb::KEY_XF86MySites => Key::BrowserFavorites, - xkb::KEY_XF86New => Key::New, - // KEY_XF86News..KEY_XF86OfficeHome - xkb::KEY_XF86Open => Key::Open, - // KEY_XF86Option - xkb::KEY_XF86Paste => Key::Paste, - xkb::KEY_XF86Phone => Key::LaunchPhone, - // KEY_XF86Q - xkb::KEY_XF86Reply => Key::MailReply, - xkb::KEY_XF86Reload => Key::BrowserRefresh, - // KEY_XF86RotateWindows..KEY_XF86RotationKB - xkb::KEY_XF86Save => Key::Save, - // KEY_XF86ScrollUp..KEY_XF86ScrollClick - xkb::KEY_XF86Send => Key::MailSend, - xkb::KEY_XF86Spell => Key::SpellCheck, - xkb::KEY_XF86SplitScreen => Key::SplitScreenToggle, - // KEY_XF86Support..KEY_XF86User2KB - xkb::KEY_XF86Video => Key::LaunchMediaPlayer, - // KEY_XF86WheelButton - xkb::KEY_XF86Word => Key::LaunchWordProcessor, - // KEY_XF86Xfer - xkb::KEY_XF86ZoomIn => Key::ZoomIn, - xkb::KEY_XF86ZoomOut => Key::ZoomOut, - - // KEY_XF86Away..KEY_XF86Messenger - xkb::KEY_XF86WebCam => Key::LaunchWebCam, - xkb::KEY_XF86MailForward => Key::MailForward, - // KEY_XF86Pictures - xkb::KEY_XF86Music => Key::LaunchMusicPlayer, - - // KEY_XF86Battery..KEY_XF86UWB + keysyms::XKB_KEY_XF86Back => Key::BrowserBack, + keysyms::XKB_KEY_XF86Forward => Key::BrowserForward, + // keysyms::XKB_KEY_XF86Stop => Key::Stop, + keysyms::XKB_KEY_XF86Refresh => Key::BrowserRefresh, + keysyms::XKB_KEY_XF86PowerOff => Key::Power, + keysyms::XKB_KEY_XF86WakeUp => Key::WakeUp, + keysyms::XKB_KEY_XF86Eject => Key::Eject, + keysyms::XKB_KEY_XF86ScreenSaver => Key::LaunchScreenSaver, + keysyms::XKB_KEY_XF86WWW => Key::LaunchWebBrowser, + keysyms::XKB_KEY_XF86Sleep => Key::Standby, + keysyms::XKB_KEY_XF86Favorites => Key::BrowserFavorites, + keysyms::XKB_KEY_XF86AudioPause => Key::MediaPause, + // keysyms::XKB_KEY_XF86AudioMedia => Key::AudioMedia, + keysyms::XKB_KEY_XF86MyComputer => Key::LaunchApplication1, + // keysyms::XKB_KEY_XF86VendorHome => Key::VendorHome, + // keysyms::XKB_KEY_XF86LightBulb => Key::LightBulb, + // keysyms::XKB_KEY_XF86Shop => Key::BrowserShop, + // keysyms::XKB_KEY_XF86History => Key::BrowserHistory, + // keysyms::XKB_KEY_XF86OpenURL => Key::OpenUrl, + // keysyms::XKB_KEY_XF86AddFavorite => Key::AddFavorite, + // keysyms::XKB_KEY_XF86HotLinks => Key::HotLinks, + // keysyms::XKB_KEY_XF86BrightnessAdjust => Key::BrightnessAdjust, + // keysyms::XKB_KEY_XF86Finance => Key::BrowserFinance, + // keysyms::XKB_KEY_XF86Community => Key::BrowserCommunity, + keysyms::XKB_KEY_XF86AudioRewind => Key::MediaRewind, + // keysyms::XKB_KEY_XF86BackForward => Key::???, + // XKB_KEY_XF86Launch0..XKB_KEY_XF86LaunchF + + // XKB_KEY_XF86ApplicationLeft..XKB_KEY_XF86CD + keysyms::XKB_KEY_XF86Calculater => Key::LaunchApplication2, // This must be a typo, right? + // XKB_KEY_XF86Clear + keysyms::XKB_KEY_XF86Close => Key::Close, + keysyms::XKB_KEY_XF86Copy => Key::Copy, + keysyms::XKB_KEY_XF86Cut => Key::Cut, + // XKB_KEY_XF86Display..XKB_KEY_XF86Documents + keysyms::XKB_KEY_XF86Excel => Key::LaunchSpreadsheet, + // XKB_KEY_XF86Explorer..XKB_KEY_XF86iTouch + keysyms::XKB_KEY_XF86LogOff => Key::LogOff, + // XKB_KEY_XF86Market..XKB_KEY_XF86MenuPB + keysyms::XKB_KEY_XF86MySites => Key::BrowserFavorites, + keysyms::XKB_KEY_XF86New => Key::New, + // XKB_KEY_XF86News..XKB_KEY_XF86OfficeHome + keysyms::XKB_KEY_XF86Open => Key::Open, + // XKB_KEY_XF86Option + keysyms::XKB_KEY_XF86Paste => Key::Paste, + keysyms::XKB_KEY_XF86Phone => Key::LaunchPhone, + // XKB_KEY_XF86Q + keysyms::XKB_KEY_XF86Reply => Key::MailReply, + keysyms::XKB_KEY_XF86Reload => Key::BrowserRefresh, + // XKB_KEY_XF86RotateWindows..XKB_KEY_XF86RotationKB + keysyms::XKB_KEY_XF86Save => Key::Save, + // XKB_KEY_XF86ScrollUp..XKB_KEY_XF86ScrollClick + keysyms::XKB_KEY_XF86Send => Key::MailSend, + keysyms::XKB_KEY_XF86Spell => Key::SpellCheck, + keysyms::XKB_KEY_XF86SplitScreen => Key::SplitScreenToggle, + // XKB_KEY_XF86Support..XKB_KEY_XF86User2KB + keysyms::XKB_KEY_XF86Video => Key::LaunchMediaPlayer, + // XKB_KEY_XF86WheelButton + keysyms::XKB_KEY_XF86Word => Key::LaunchWordProcessor, + // XKB_KEY_XF86Xfer + keysyms::XKB_KEY_XF86ZoomIn => Key::ZoomIn, + keysyms::XKB_KEY_XF86ZoomOut => Key::ZoomOut, + + // XKB_KEY_XF86Away..XKB_KEY_XF86Messenger + keysyms::XKB_KEY_XF86WebCam => Key::LaunchWebCam, + keysyms::XKB_KEY_XF86MailForward => Key::MailForward, + // XKB_KEY_XF86Pictures + keysyms::XKB_KEY_XF86Music => Key::LaunchMusicPlayer, + + // XKB_KEY_XF86Battery..XKB_KEY_XF86UWB // - xkb::KEY_XF86AudioForward => Key::MediaFastForward, - // KEY_XF86AudioRepeat - xkb::KEY_XF86AudioRandomPlay => Key::RandomToggle, - xkb::KEY_XF86Subtitle => Key::Subtitle, - xkb::KEY_XF86AudioCycleTrack => Key::MediaAudioTrack, - // KEY_XF86CycleAngle..KEY_XF86Blue + keysyms::XKB_KEY_XF86AudioForward => Key::MediaFastForward, + // XKB_KEY_XF86AudioRepeat + keysyms::XKB_KEY_XF86AudioRandomPlay => Key::RandomToggle, + keysyms::XKB_KEY_XF86Subtitle => Key::Subtitle, + keysyms::XKB_KEY_XF86AudioCycleTrack => Key::MediaAudioTrack, + // XKB_KEY_XF86CycleAngle..XKB_KEY_XF86Blue // - xkb::KEY_XF86Suspend => Key::Standby, - xkb::KEY_XF86Hibernate => Key::Hibernate, - // KEY_XF86TouchpadToggle..KEY_XF86TouchpadOff + keysyms::XKB_KEY_XF86Suspend => Key::Standby, + keysyms::XKB_KEY_XF86Hibernate => Key::Hibernate, + // XKB_KEY_XF86TouchpadToggle..XKB_KEY_XF86TouchpadOff // - xkb::KEY_XF86AudioMute => Key::AudioVolumeMute, - - // KEY_XF86Switch_VT_1..KEY_XF86Switch_VT_12 - - // KEY_XF86Ungrab..KEY_XF86ClearGrab - xkb::KEY_XF86Next_VMode => Key::VideoModeNext, - // xkb::KEY_XF86Prev_VMode => Key::VideoModePrevious, - // KEY_XF86LogWindowTree..KEY_XF86LogGrabInfo - - // KEY_SunFA_Grave..KEY_SunFA_Cedilla - - // xkb::KEY_SunF36 => Key::F36 | Key::F11, - // xkb::KEY_SunF37 => Key::F37 | Key::F12, - - // xkb::KEY_SunSys_Req => Key::PrintScreen, - // The next couple of xkb (until KEY_SunStop) are already handled. - // KEY_SunPrint_Screen..KEY_SunPageDown - - // KEY_SunUndo..KEY_SunFront - xkb::KEY_SunCopy => Key::Copy, - xkb::KEY_SunOpen => Key::Open, - xkb::KEY_SunPaste => Key::Paste, - xkb::KEY_SunCut => Key::Cut, - - // KEY_SunPowerSwitch - xkb::KEY_SunAudioLowerVolume => Key::AudioVolumeDown, - xkb::KEY_SunAudioMute => Key::AudioVolumeMute, - xkb::KEY_SunAudioRaiseVolume => Key::AudioVolumeUp, - // KEY_SunVideoDegauss - xkb::KEY_SunVideoLowerBrightness => Key::BrightnessDown, - xkb::KEY_SunVideoRaiseBrightness => Key::BrightnessUp, - // KEY_SunPowerSwitchShift + keysyms::XKB_KEY_XF86AudioMute => Key::AudioVolumeMute, + + // XKB_KEY_XF86Switch_VT_1..XKB_KEY_XF86Switch_VT_12 + + // XKB_KEY_XF86Ungrab..XKB_KEY_XF86ClearGrab + keysyms::XKB_KEY_XF86Next_VMode => Key::VideoModeNext, + // keysyms::XKB_KEY_XF86Prev_VMode => Key::VideoModePrevious, + // XKB_KEY_XF86LogWindowTree..XKB_KEY_XF86LogGrabInfo + + // XKB_KEY_SunFA_Grave..XKB_KEY_SunFA_Cedilla + + // keysyms::XKB_KEY_SunF36 => Key::F36 | Key::F11, + // keysyms::XKB_KEY_SunF37 => Key::F37 | Key::F12, + + // keysyms::XKB_KEY_SunSys_Req => Key::PrintScreen, + // The next couple of xkb (until XKB_KEY_SunStop) are already handled. + // XKB_KEY_SunPrint_Screen..XKB_KEY_SunPageDown + + // XKB_KEY_SunUndo..XKB_KEY_SunFront + keysyms::XKB_KEY_SunCopy => Key::Copy, + keysyms::XKB_KEY_SunOpen => Key::Open, + keysyms::XKB_KEY_SunPaste => Key::Paste, + keysyms::XKB_KEY_SunCut => Key::Cut, + + // XKB_KEY_SunPowerSwitch + keysyms::XKB_KEY_SunAudioLowerVolume => Key::AudioVolumeDown, + keysyms::XKB_KEY_SunAudioMute => Key::AudioVolumeMute, + keysyms::XKB_KEY_SunAudioRaiseVolume => Key::AudioVolumeUp, + // XKB_KEY_SunVideoDegauss + keysyms::XKB_KEY_SunVideoLowerBrightness => Key::BrightnessDown, + keysyms::XKB_KEY_SunVideoRaiseBrightness => Key::BrightnessUp, + // XKB_KEY_SunPowerSwitchShift // _ => Key::Unidentified(NativeKeyCode::XKB(keysym)), } } pub fn keysym_location(keysym: u32) -> KeyLocation { - use xkbcommon::xkb; + use xkbcommon_dl::keysyms; match keysym { - xkb::KEY_Shift_L - | xkb::KEY_Control_L - | xkb::KEY_Meta_L - | xkb::KEY_Alt_L - | xkb::KEY_Super_L - | xkb::KEY_Hyper_L => KeyLocation::Left, - xkb::KEY_Shift_R - | xkb::KEY_Control_R - | xkb::KEY_Meta_R - | xkb::KEY_Alt_R - | xkb::KEY_Super_R - | xkb::KEY_Hyper_R => KeyLocation::Right, - xkb::KEY_KP_0 - | xkb::KEY_KP_1 - | xkb::KEY_KP_2 - | xkb::KEY_KP_3 - | xkb::KEY_KP_4 - | xkb::KEY_KP_5 - | xkb::KEY_KP_6 - | xkb::KEY_KP_7 - | xkb::KEY_KP_8 - | xkb::KEY_KP_9 - | xkb::KEY_KP_Space - | xkb::KEY_KP_Tab - | xkb::KEY_KP_Enter - | xkb::KEY_KP_F1 - | xkb::KEY_KP_F2 - | xkb::KEY_KP_F3 - | xkb::KEY_KP_F4 - | xkb::KEY_KP_Home - | xkb::KEY_KP_Left - | xkb::KEY_KP_Up - | xkb::KEY_KP_Right - | xkb::KEY_KP_Down - | xkb::KEY_KP_Page_Up - | xkb::KEY_KP_Page_Down - | xkb::KEY_KP_End - | xkb::KEY_KP_Begin - | xkb::KEY_KP_Insert - | xkb::KEY_KP_Delete - | xkb::KEY_KP_Equal - | xkb::KEY_KP_Multiply - | xkb::KEY_KP_Add - | xkb::KEY_KP_Separator - | xkb::KEY_KP_Subtract - | xkb::KEY_KP_Decimal - | xkb::KEY_KP_Divide => KeyLocation::Numpad, + keysyms::XKB_KEY_Shift_L + | keysyms::XKB_KEY_Control_L + | keysyms::XKB_KEY_Meta_L + | keysyms::XKB_KEY_Alt_L + | keysyms::XKB_KEY_Super_L + | keysyms::XKB_KEY_Hyper_L => KeyLocation::Left, + keysyms::XKB_KEY_Shift_R + | keysyms::XKB_KEY_Control_R + | keysyms::XKB_KEY_Meta_R + | keysyms::XKB_KEY_Alt_R + | keysyms::XKB_KEY_Super_R + | keysyms::XKB_KEY_Hyper_R => KeyLocation::Right, + keysyms::XKB_KEY_KP_0 + | keysyms::XKB_KEY_KP_1 + | keysyms::XKB_KEY_KP_2 + | keysyms::XKB_KEY_KP_3 + | keysyms::XKB_KEY_KP_4 + | keysyms::XKB_KEY_KP_5 + | keysyms::XKB_KEY_KP_6 + | keysyms::XKB_KEY_KP_7 + | keysyms::XKB_KEY_KP_8 + | keysyms::XKB_KEY_KP_9 + | keysyms::XKB_KEY_KP_Space + | keysyms::XKB_KEY_KP_Tab + | keysyms::XKB_KEY_KP_Enter + | keysyms::XKB_KEY_KP_F1 + | keysyms::XKB_KEY_KP_F2 + | keysyms::XKB_KEY_KP_F3 + | keysyms::XKB_KEY_KP_F4 + | keysyms::XKB_KEY_KP_Home + | keysyms::XKB_KEY_KP_Left + | keysyms::XKB_KEY_KP_Up + | keysyms::XKB_KEY_KP_Right + | keysyms::XKB_KEY_KP_Down + | keysyms::XKB_KEY_KP_Page_Up + | keysyms::XKB_KEY_KP_Page_Down + | keysyms::XKB_KEY_KP_End + | keysyms::XKB_KEY_KP_Begin + | keysyms::XKB_KEY_KP_Insert + | keysyms::XKB_KEY_KP_Delete + | keysyms::XKB_KEY_KP_Equal + | keysyms::XKB_KEY_KP_Multiply + | keysyms::XKB_KEY_KP_Add + | keysyms::XKB_KEY_KP_Separator + | keysyms::XKB_KEY_KP_Subtract + | keysyms::XKB_KEY_KP_Decimal + | keysyms::XKB_KEY_KP_Divide => KeyLocation::Numpad, _ => KeyLocation::Standard, } } From 0369b415fca51b518fc83be6662a330c82a446e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Wed, 31 Mar 2021 04:31:56 +0200 Subject: [PATCH 073/131] Hoist keyboard handling code from sctk into Winit --- Cargo.toml | 5 +- src/platform_impl/linux/common/mod.rs | 1 + src/platform_impl/linux/common/xkb_state.rs | 383 +++++++++++ .../linux/wayland/seat/keyboard/handlers.rs | 601 +++++++++++++++++- .../linux/wayland/seat/keyboard/mod.rs | 16 +- 5 files changed, 985 insertions(+), 21 deletions(-) create mode 100644 src/platform_impl/linux/common/xkb_state.rs diff --git a/Cargo.toml b/Cargo.toml index 38f4d49dae..8fd6314187 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ default = ["x11", "wayland"] web-sys = ["web_sys", "wasm-bindgen", "instant/wasm-bindgen"] stdweb = ["std_web", "instant/stdweb"] x11 = ["x11-dl", "mio", "mio-extras", "percent-encoding", "parking_lot"] -wayland = ["wayland-client", "sctk"] +wayland = ["wayland-client", "sctk", "memmap2"] [dependencies] instant = "0.1" @@ -94,7 +94,8 @@ mio-extras = { version = "2.0", optional = true } x11-dl = { version = "2.18.5", optional = true } percent-encoding = { version = "2.0", optional = true } parking_lot = { version = "0.11.0", optional = true } -xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "224646a" } +memmap2 = { version = "0.2.1", optional = true } +xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "e175ffe" } [target.'cfg(target_arch = "wasm32")'.dependencies.web_sys] package = "web-sys" diff --git a/src/platform_impl/linux/common/mod.rs b/src/platform_impl/linux/common/mod.rs index 32030aff05..fa23919676 100644 --- a/src/platform_impl/linux/common/mod.rs +++ b/src/platform_impl/linux/common/mod.rs @@ -1 +1,2 @@ pub mod keymap; +pub mod xkb_state; diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs new file mode 100644 index 0000000000..5bb3fd278c --- /dev/null +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -0,0 +1,383 @@ +use std::env; +use std::ffi::CString; +use std::fs::File; +use std::os::raw::c_char; +use std::os::unix::ffi::OsStringExt; +use std::ptr; + +use memmap2::MmapOptions; +use xkbcommon_dl::{ + self as ffi, xkb_state_component, XKBCOMMON_COMPOSE_HANDLE as XKBCH, XKBCOMMON_HANDLE as XKBH, +}; + +pub use sctk::seat::keyboard::RMLVO; + +pub(crate) struct KbState { + xkb_context: *mut ffi::xkb_context, + xkb_keymap: *mut ffi::xkb_keymap, + xkb_state: *mut ffi::xkb_state, + xkb_compose_table: *mut ffi::xkb_compose_table, + xkb_compose_state: *mut ffi::xkb_compose_state, + mods_state: ModifiersState, + locked: bool, +} + +/// Represents the current state of the keyboard modifiers +/// +/// Each field of this struct represents a modifier and is `true` if this modifier is active. +/// +/// For some modifiers, this means that the key is currently pressed, others are toggled +/// (like caps lock). +#[derive(Copy, Clone, Debug, Default)] +pub struct ModifiersState { + /// The "control" key + pub ctrl: bool, + /// The "alt" key + pub alt: bool, + /// The "shift" key + pub shift: bool, + /// The "Caps lock" key + pub caps_lock: bool, + /// The "logo" key + /// + /// Also known as the "windows" key on most keyboards + pub logo: bool, + /// The "Num lock" key + pub num_lock: bool, +} + +impl ModifiersState { + fn new() -> ModifiersState { + ModifiersState::default() + } + + fn update_with(&mut self, state: *mut ffi::xkb_state) { + self.ctrl = unsafe { + (XKBH.xkb_state_mod_name_is_active)( + state, + ffi::XKB_MOD_NAME_CTRL.as_ptr() as *const c_char, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0 + }; + self.alt = unsafe { + (XKBH.xkb_state_mod_name_is_active)( + state, + ffi::XKB_MOD_NAME_ALT.as_ptr() as *const c_char, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0 + }; + self.shift = unsafe { + (XKBH.xkb_state_mod_name_is_active)( + state, + ffi::XKB_MOD_NAME_SHIFT.as_ptr() as *const c_char, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0 + }; + self.caps_lock = unsafe { + (XKBH.xkb_state_mod_name_is_active)( + state, + ffi::XKB_MOD_NAME_CAPS.as_ptr() as *const c_char, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0 + }; + self.logo = unsafe { + (XKBH.xkb_state_mod_name_is_active)( + state, + ffi::XKB_MOD_NAME_LOGO.as_ptr() as *const c_char, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0 + }; + self.num_lock = unsafe { + (XKBH.xkb_state_mod_name_is_active)( + state, + ffi::XKB_MOD_NAME_NUM.as_ptr() as *const c_char, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0 + }; + } +} + +impl KbState { + pub(crate) fn update_modifiers( + &mut self, + mods_depressed: u32, + mods_latched: u32, + mods_locked: u32, + group: u32, + ) { + if !self.ready() { + return; + } + let mask = unsafe { + (XKBH.xkb_state_update_mask)( + self.xkb_state, + mods_depressed, + mods_latched, + mods_locked, + 0, + 0, + group, + ) + }; + if mask.contains(xkb_state_component::XKB_STATE_MODS_EFFECTIVE) { + // effective value of mods have changed, we need to update our state + self.mods_state.update_with(self.xkb_state); + } + } + + pub(crate) fn get_one_sym_raw(&mut self, keycode: u32) -> u32 { + if !self.ready() { + return 0; + } + unsafe { (XKBH.xkb_state_key_get_one_sym)(self.xkb_state, keycode + 8) } + } + + pub(crate) fn get_utf8_raw(&mut self, keycode: u32) -> Option { + if !self.ready() { + return None; + } + let size = unsafe { + (XKBH.xkb_state_key_get_utf8)(self.xkb_state, keycode + 8, ptr::null_mut(), 0) + } + 1; + if size <= 1 { + return None; + }; + let mut buffer = Vec::with_capacity(size as usize); + unsafe { + buffer.set_len(size as usize); + (XKBH.xkb_state_key_get_utf8)( + self.xkb_state, + keycode + 8, + buffer.as_mut_ptr() as *mut _, + size as usize, + ); + }; + // remove the final `\0` + buffer.pop(); + // libxkbcommon will always provide valid UTF8 + Some(unsafe { String::from_utf8_unchecked(buffer) }) + } + + pub(crate) fn compose_feed(&mut self, keysym: u32) -> Option { + if !self.ready() || self.xkb_compose_state.is_null() { + return None; + } + Some(unsafe { (XKBCH.xkb_compose_state_feed)(self.xkb_compose_state, keysym) }) + } + + pub(crate) fn compose_status(&mut self) -> Option { + if !self.ready() || self.xkb_compose_state.is_null() { + return None; + } + Some(unsafe { (XKBCH.xkb_compose_state_get_status)(self.xkb_compose_state) }) + } + + pub(crate) fn compose_get_utf8(&mut self) -> Option { + if !self.ready() || self.xkb_compose_state.is_null() { + return None; + } + let size = unsafe { + (XKBCH.xkb_compose_state_get_utf8)(self.xkb_compose_state, ptr::null_mut(), 0) + } + 1; + if size <= 1 { + return None; + }; + let mut buffer = Vec::with_capacity(size as usize); + unsafe { + buffer.set_len(size as usize); + (XKBCH.xkb_compose_state_get_utf8)( + self.xkb_compose_state, + buffer.as_mut_ptr() as *mut _, + size as usize, + ); + }; + // remove the final `\0` + buffer.pop(); + // libxkbcommon will always provide valid UTF8 + Some(unsafe { String::from_utf8_unchecked(buffer) }) + } + + pub(crate) fn new() -> Result { + { + if ffi::XKBCOMMON_OPTION.as_ref().is_none() { + return Err(Error::XKBNotFound); + } + } + let context = + unsafe { (XKBH.xkb_context_new)(ffi::xkb_context_flags::XKB_CONTEXT_NO_FLAGS) }; + if context.is_null() { + return Err(Error::XKBNotFound); + } + + let mut me = KbState { + xkb_context: context, + xkb_keymap: ptr::null_mut(), + xkb_state: ptr::null_mut(), + xkb_compose_table: ptr::null_mut(), + xkb_compose_state: ptr::null_mut(), + mods_state: ModifiersState::new(), + locked: false, + }; + + unsafe { + me.init_compose(); + } + + Ok(me) + } + + pub(crate) fn from_rmlvo(rmlvo: RMLVO) -> Result { + fn to_cstring(s: Option) -> Result, Error> { + s.map_or(Ok(None), |s| CString::new(s).map(Option::Some)) + .map_err(|_| Error::BadNames) + } + + let mut state = KbState::new()?; + + let rules = to_cstring(rmlvo.rules)?; + let model = to_cstring(rmlvo.model)?; + let layout = to_cstring(rmlvo.layout)?; + let variant = to_cstring(rmlvo.variant)?; + let options = to_cstring(rmlvo.options)?; + + let xkb_names = ffi::xkb_rule_names { + rules: rules.map_or(ptr::null(), |s| s.as_ptr()), + model: model.map_or(ptr::null(), |s| s.as_ptr()), + layout: layout.map_or(ptr::null(), |s| s.as_ptr()), + variant: variant.map_or(ptr::null(), |s| s.as_ptr()), + options: options.map_or(ptr::null(), |s| s.as_ptr()), + }; + + unsafe { + state.init_with_rmlvo(xkb_names)?; + } + + state.locked = true; + Ok(state) + } + + pub(crate) unsafe fn init_compose(&mut self) { + let locale = env::var_os("LC_ALL") + .and_then(|v| if v.is_empty() { None } else { Some(v) }) + .or_else(|| env::var_os("LC_CTYPE")) + .and_then(|v| if v.is_empty() { None } else { Some(v) }) + .or_else(|| env::var_os("LANG")) + .and_then(|v| if v.is_empty() { None } else { Some(v) }) + .unwrap_or_else(|| "C".into()); + let locale = CString::new(locale.into_vec()).unwrap(); + + let compose_table = (XKBCH.xkb_compose_table_new_from_locale)( + self.xkb_context, + locale.as_ptr(), + ffi::xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS, + ); + + if compose_table.is_null() { + // init of compose table failed, continue without compose + return; + } + + let compose_state = (XKBCH.xkb_compose_state_new)( + compose_table, + ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, + ); + + if compose_state.is_null() { + // init of compose state failed, continue without compose + (XKBCH.xkb_compose_table_unref)(compose_table); + return; + } + + self.xkb_compose_table = compose_table; + self.xkb_compose_state = compose_state; + } + + pub(crate) unsafe fn post_init(&mut self, keymap: *mut ffi::xkb_keymap) { + let state = (XKBH.xkb_state_new)(keymap); + self.xkb_keymap = keymap; + self.xkb_state = state; + self.mods_state.update_with(state); + } + + pub(crate) unsafe fn de_init(&mut self) { + (XKBH.xkb_state_unref)(self.xkb_state); + self.xkb_state = ptr::null_mut(); + (XKBH.xkb_keymap_unref)(self.xkb_keymap); + self.xkb_keymap = ptr::null_mut(); + } + + pub(crate) unsafe fn init_with_fd(&mut self, fd: File, size: usize) { + let map = MmapOptions::new().len(size).map(&fd).unwrap(); + + let keymap = (XKBH.xkb_keymap_new_from_string)( + self.xkb_context, + map.as_ptr() as *const _, + ffi::xkb_keymap_format::XKB_KEYMAP_FORMAT_TEXT_V1, + ffi::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS, + ); + + if keymap.is_null() { + panic!("Received invalid keymap from compositor."); + } + + self.post_init(keymap); + } + + pub(crate) unsafe fn init_with_rmlvo( + &mut self, + names: ffi::xkb_rule_names, + ) -> Result<(), Error> { + let keymap = (XKBH.xkb_keymap_new_from_names)( + self.xkb_context, + &names, + ffi::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS, + ); + + if keymap.is_null() { + return Err(Error::BadNames); + } + + self.post_init(keymap); + + Ok(()) + } + + pub(crate) unsafe fn key_repeats(&mut self, xkb_keycode_t: ffi::xkb_keycode_t) -> bool { + (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, xkb_keycode_t) == 1 + } + + #[inline] + pub(crate) fn ready(&self) -> bool { + !self.xkb_state.is_null() + } + + #[inline] + pub(crate) fn locked(&self) -> bool { + self.locked + } + + #[inline] + pub(crate) fn mods_state(&self) -> ModifiersState { + self.mods_state + } +} + +impl Drop for KbState { + fn drop(&mut self) { + unsafe { + (XKBCH.xkb_compose_state_unref)(self.xkb_compose_state); + (XKBCH.xkb_compose_table_unref)(self.xkb_compose_table); + (XKBH.xkb_state_unref)(self.xkb_state); + (XKBH.xkb_keymap_unref)(self.xkb_keymap); + (XKBH.xkb_context_unref)(self.xkb_context); + } + } +} + +#[derive(Debug)] +pub enum Error { + /// libxkbcommon is not available + XKBNotFound, + /// Provided RMLVO specified a keymap that would not be loaded + BadNames, +} diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs index 268beb6fa3..acb5dc1852 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs @@ -1,12 +1,13 @@ //! Handling of various keyboard events. +use sctk::reexports::calloop; +use sctk::reexports::client; use sctk::reexports::client::protocol::wl_keyboard::KeyState; -use sctk::seat::keyboard::Event as KeyboardEvent; - use crate::event::{ElementState, KeyEvent, WindowEvent}; use crate::keyboard::{Key, ModifiersState, NativeKeyCode}; use crate::platform_impl::platform::common::keymap; +use crate::platform_impl::platform::common::xkb_state::{self, RMLVO}; use crate::platform_impl::wayland::event_loop::WinitState; use crate::platform_impl::wayland::{self, DeviceId}; use crate::platform_impl::KeyEventExtra; @@ -15,13 +16,13 @@ use super::KeyboardInner; #[inline] pub(super) fn handle_keyboard( - event: KeyboardEvent<'_>, + event: Event<'_>, inner: &mut KeyboardInner, winit_state: &mut WinitState, ) { let event_sink = &mut winit_state.event_sink; match event { - KeyboardEvent::Enter { surface, .. } => { + Event::Enter { surface, .. } => { let window_id = wayland::make_wid(&surface); // Window gained focus. @@ -35,7 +36,7 @@ pub(super) fn handle_keyboard( inner.target_window_id = Some(window_id); } - KeyboardEvent::Leave { surface, .. } => { + Event::Leave { surface, .. } => { let window_id = wayland::make_wid(&surface); // Notify that no modifiers are being pressed. @@ -52,7 +53,7 @@ pub(super) fn handle_keyboard( // Reset the id. inner.target_window_id = None; } - KeyboardEvent::Key { + Event::Key { rawkey, keysym, state, @@ -107,7 +108,7 @@ pub(super) fn handle_keyboard( } } } - KeyboardEvent::Repeat { + Event::Repeat { rawkey, keysym, utf8, @@ -148,9 +149,7 @@ pub(super) fn handle_keyboard( for ch in txt.chars() { // event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id); } - } - } - KeyboardEvent::Modifiers { modifiers } => { + Event::Modifiers { modifiers } => { let modifiers = ModifiersState::from(modifiers); if let Some(window_id) = inner.target_window_id { *inner.modifiers_state.borrow_mut() = modifiers; @@ -165,3 +164,585 @@ pub(super) fn handle_keyboard( } } } + +// ! ====================================================================================================== ! +// ! "INSPIRED" BY SCTK ! +// ! ====================================================================================================== ! + +use std::num::NonZeroU32; +use std::time::Duration; +use std::{ + cell::RefCell, + convert::TryInto, + fs::File, + os::unix::io::{FromRawFd, RawFd}, + rc::Rc, +}; + +use sctk::reexports::client::{ + protocol::{wl_keyboard, wl_seat, wl_surface}, + Attached, +}; +use xkbcommon_dl as ffi; + +use super::super::super::super::common::xkb_state::KbState; + +const MICROS_IN_SECOND: u32 = 1000000; + +/// Possible kinds of key repetition +pub enum RepeatKind { + /// keys will be repeated at a set rate and delay + Fixed { + /// The number of repetitions per second that should occur. + rate: u32, + /// delay (in milliseconds) between a key press and the start of repetition + delay: u32, + }, + /// keys will be repeated at a rate and delay set by the wayland server + System, +} + +#[derive(Debug)] +/// An error that occurred while trying to initialize a mapped keyboard +pub enum Error { + XkbState(xkb_state::Error), + /// The provided seat does not have the keyboard capability + NoKeyboard, + /// Failed to init timers for repetition + TimerError(std::io::Error), +} + +impl From for Error { + fn from(err: xkb_state::Error) -> Self { + Self::XkbState(err) + } +} + +/// Events received from a mapped keyboard +pub enum Event<'a> { + /// The keyboard focus has entered a surface + Enter { + /// serial number of the event + serial: u32, + /// surface that was entered + surface: wl_surface::WlSurface, + /// raw values of the currently pressed keys + rawkeys: &'a [u32], + /// interpreted symbols of the currently pressed keys + keysyms: &'a [u32], + }, + /// The keyboard focus has left a surface + Leave { + /// serial number of the event + serial: u32, + /// surface that was left + surface: wl_surface::WlSurface, + }, + /// The key modifiers have changed state + Modifiers { + /// current state of the modifiers + modifiers: xkb_state::ModifiersState, + }, + /// A key event occurred + Key { + /// serial number of the event + serial: u32, + /// time at which the keypress occurred + time: u32, + /// raw value of the key + rawkey: u32, + /// interpreted symbol of the key + keysym: u32, + /// new state of the key + state: KeyState, + /// utf8 interpretation of the entered text + /// + /// will always be `None` on key release events + utf8: Option, + }, + /// A key repetition event + Repeat { + /// time at which the repetition occured + time: u32, + /// raw value of the key + rawkey: u32, + /// interpreted symbol of the key + keysym: u32, + /// utf8 interpretation of the entered text + utf8: Option, + }, +} + +/// Implement a keyboard for keymap translation with key repetition +/// +/// This requires you to provide a callback to receive the events after they +/// have been interpreted with the keymap. +/// +/// The keymap will be loaded from the provided RMLVO rules, or from the compositor +/// provided keymap if `None`. +/// +/// Returns an error if xkbcommon could not be initialized, the RMLVO specification +/// contained invalid values, or if the provided seat does not have keyboard capability. +pub fn map_keyboard_repeat( + loop_handle: calloop::LoopHandle, + seat: &Attached, + rmlvo: Option, + repeatkind: RepeatKind, + callback: F, +) -> Result<(wl_keyboard::WlKeyboard, calloop::Source), Error> +where + F: FnMut(Event<'_>, wl_keyboard::WlKeyboard, wayland_client::DispatchData<'_>) + 'static, +{ + let has_kbd = sctk::seat::with_seat_data(seat, |data| data.has_keyboard).unwrap_or(false); + let keyboard = if has_kbd { + seat.get_keyboard() + } else { + return Err(Error::NoKeyboard); + }; + + let state = Rc::new(RefCell::new( + rmlvo + .map(KbState::from_rmlvo) + .unwrap_or_else(KbState::new)?, + )); + + let callback = Rc::new(RefCell::new(callback)); + + let repeat = match repeatkind { + RepeatKind::System => RepeatDetails { + locked: false, + gap: None, + delay: 200, + }, + RepeatKind::Fixed { rate, delay } => { + let gap = rate_to_gap(rate as i32); + RepeatDetails { + locked: true, + gap, + delay, + } + } + }; + + // Prepare the repetition handling. + let (mut kbd_handler, source) = { + let current_repeat = Rc::new(RefCell::new(None)); + + let source = RepeatSource { + timer: calloop::timer::Timer::new().map_err(Error::TimerError)?, + state: state.clone(), + current_repeat: current_repeat.clone(), + }; + + let timer_handle = source.timer.handle(); + + let handler = KbdHandler { + callback: callback.clone(), + state, + repeat: Some(KbdRepeat { + timer_handle, + current_repeat, + details: repeat, + }), + }; + (handler, source) + }; + + let source = loop_handle + .insert_source(source, move |event, kbd, ddata| { + (&mut *callback.borrow_mut())( + event, + kbd.clone(), + wayland_client::DispatchData::wrap(ddata), + ) + }) + .map_err(|e| Error::TimerError(e.error))?; + + keyboard.quick_assign(move |keyboard, event, data| { + kbd_handler.event(keyboard.detach(), event, data) + }); + + Ok((keyboard.detach(), source)) +} + +fn rate_to_gap(rate: i32) -> Option { + if rate <= 0 { + None + } else if MICROS_IN_SECOND < rate as u32 { + NonZeroU32::new(1) + } else { + NonZeroU32::new(MICROS_IN_SECOND / rate as u32) + } +} + +/* + * Classic handling + */ + +type KbdCallback = dyn FnMut(Event<'_>, wl_keyboard::WlKeyboard, wayland_client::DispatchData<'_>); + +struct RepeatDetails { + locked: bool, + /// Gap between key presses in microseconds. + /// + /// If the `gap` is `None`, it means that repeat is disabled. + gap: Option, + /// Delay before starting key repeat in milliseconds. + delay: u32, +} + +struct KbdHandler { + state: Rc>, + callback: Rc>, + repeat: Option, +} + +struct KbdRepeat { + timer_handle: calloop::timer::TimerHandle<()>, + current_repeat: Rc>>, + details: RepeatDetails, +} + +impl KbdRepeat { + fn start_repeat(&self, key: u32, keyboard: wl_keyboard::WlKeyboard, time: u32) { + // Start a new repetition, overwriting the previous ones + self.timer_handle.cancel_all_timeouts(); + + // Handle disabled repeat rate. + let gap = match self.details.gap { + Some(gap) => gap.get() as u64, + None => return, + }; + + *self.current_repeat.borrow_mut() = Some(RepeatData { + keyboard, + keycode: key, + gap, + time: (time + self.details.delay) as u64 * 1000, + }); + self.timer_handle + .add_timeout(Duration::from_micros(self.details.delay as u64 * 1000), ()); + } + + fn stop_repeat(&self, key: u32) { + // only cancel if the released key is the currently repeating key + let mut guard = self.current_repeat.borrow_mut(); + let stop = (*guard).as_ref().map(|d| d.keycode == key).unwrap_or(false); + if stop { + self.timer_handle.cancel_all_timeouts(); + *guard = None; + } + } + + fn stop_all_repeat(&self) { + self.timer_handle.cancel_all_timeouts(); + *self.current_repeat.borrow_mut() = None; + } +} + +impl KbdHandler { + fn event( + &mut self, + kbd: wl_keyboard::WlKeyboard, + event: wl_keyboard::Event, + dispatch_data: client::DispatchData<'_>, + ) { + use wl_keyboard::Event; + + match event { + Event::Keymap { format, fd, size } => self.keymap(kbd, format, fd, size), + Event::Enter { + serial, + surface, + keys, + } => self.enter(kbd, serial, surface, keys, dispatch_data), + Event::Leave { serial, surface } => self.leave(kbd, serial, surface, dispatch_data), + Event::Key { + serial, + time, + key, + state, + } => self.key(kbd, serial, time, key, state, dispatch_data), + Event::Modifiers { + mods_depressed, + mods_latched, + mods_locked, + group, + .. + } => self.modifiers( + kbd, + mods_depressed, + mods_latched, + mods_locked, + group, + dispatch_data, + ), + Event::RepeatInfo { rate, delay } => self.repeat_info(kbd, rate, delay), + _ => {} + } + } + + fn keymap( + &mut self, + _: wl_keyboard::WlKeyboard, + format: wl_keyboard::KeymapFormat, + fd: RawFd, + size: u32, + ) { + let fd = unsafe { File::from_raw_fd(fd) }; + let mut state = self.state.borrow_mut(); + if state.locked() { + // state is locked, ignore keymap updates + return; + } + if state.ready() { + // new keymap, we first deinit to free resources + unsafe { + state.de_init(); + } + } + match format { + wl_keyboard::KeymapFormat::XkbV1 => unsafe { + state.init_with_fd(fd, size as usize); + }, + wl_keyboard::KeymapFormat::NoKeymap => { + // TODO: how to handle this (hopefully never occuring) case? + } + _ => unreachable!(), + } + } + + fn enter( + &mut self, + object: wl_keyboard::WlKeyboard, + serial: u32, + surface: wl_surface::WlSurface, + keys: Vec, + dispatch_data: client::DispatchData<'_>, + ) { + let mut state = self.state.borrow_mut(); + let rawkeys = keys + .chunks_exact(4) + .map(|c| u32::from_ne_bytes(c.try_into().unwrap())) + .collect::>(); + let keys: Vec = rawkeys.iter().map(|k| state.get_one_sym_raw(*k)).collect(); + (&mut *self.callback.borrow_mut())( + Event::Enter { + serial, + surface, + rawkeys: &rawkeys, + keysyms: &keys, + }, + object, + dispatch_data, + ); + } + + fn leave( + &mut self, + object: wl_keyboard::WlKeyboard, + serial: u32, + surface: wl_surface::WlSurface, + dispatch_data: client::DispatchData<'_>, + ) { + { + if let Some(ref mut repeat) = self.repeat { + repeat.stop_all_repeat(); + } + } + (&mut *self.callback.borrow_mut())(Event::Leave { serial, surface }, object, dispatch_data); + } + + fn key( + &mut self, + object: wl_keyboard::WlKeyboard, + serial: u32, + time: u32, + key: u32, + key_state: wl_keyboard::KeyState, + dispatch_data: client::DispatchData<'_>, + ) { + let (sym, utf8, repeats) = { + let mut state = self.state.borrow_mut(); + // Get the values to generate a key event + let sym = state.get_one_sym_raw(key); + let utf8 = if key_state == wl_keyboard::KeyState::Pressed { + match state.compose_feed(sym) { + Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED) => { + if let Some(status) = state.compose_status() { + match status { + ffi::xkb_compose_status::XKB_COMPOSE_COMPOSED => { + state.compose_get_utf8() + } + ffi::xkb_compose_status::XKB_COMPOSE_NOTHING => { + state.get_utf8_raw(key) + } + _ => None, + } + } else { + state.get_utf8_raw(key) + } + } + Some(_) => { + // XKB_COMPOSE_FEED_IGNORED + None + } + None => { + // XKB COMPOSE is not initialized + state.get_utf8_raw(key) + } + } + } else { + None + }; + let repeats = unsafe { state.key_repeats(key + 8) }; + (sym, utf8, repeats) + }; + + { + if let Some(ref mut repeat_handle) = self.repeat { + if repeats { + if key_state == wl_keyboard::KeyState::Pressed { + repeat_handle.start_repeat(key, object.clone(), time); + } else { + repeat_handle.stop_repeat(key); + } + } + } + } + + (&mut *self.callback.borrow_mut())( + Event::Key { + serial, + time, + rawkey: key, + keysym: sym, + state: key_state, + utf8, + }, + object, + dispatch_data, + ); + } + + fn modifiers( + &mut self, + object: wl_keyboard::WlKeyboard, + mods_depressed: u32, + mods_latched: u32, + mods_locked: u32, + group: u32, + dispatch_data: client::DispatchData<'_>, + ) { + { + let mut state = self.state.borrow_mut(); + state.update_modifiers(mods_depressed, mods_latched, mods_locked, group); + (&mut *self.callback.borrow_mut())( + Event::Modifiers { + modifiers: state.mods_state(), + }, + object, + dispatch_data, + ); + } + } + + fn repeat_info(&mut self, _: wl_keyboard::WlKeyboard, rate: i32, delay: i32) { + { + if let Some(ref mut repeat_handle) = self.repeat { + if !repeat_handle.details.locked { + repeat_handle.details.gap = rate_to_gap(rate); + repeat_handle.details.delay = delay as u32; + } + } + } + } +} + +/* + * Repeat handling + */ + +struct RepeatData { + keyboard: wl_keyboard::WlKeyboard, + keycode: u32, + /// Gap between key presses in microseconds. + gap: u64, + /// Time of the last event in microseconds. + time: u64, +} + +/// An event source managing the key repetition of a keyboard +/// +/// It is given to you from [`map_keyboard`](fn.map_keyboard.html), and you need to +/// insert it in your calloop event loop if you want to have functionning key repetition. +/// +/// If don't want key repetition you can just drop it. +/// +/// This source will not directly generate calloop events, and the callback provided to +/// `EventLoopHandle::insert_source()` will be ignored. Instead it triggers the +/// callback you provided to [`map_keyboard`](fn.map_keyboard.html). +pub struct RepeatSource { + timer: calloop::timer::Timer<()>, + state: Rc>, + current_repeat: Rc>>, +} + +impl calloop::EventSource for RepeatSource { + type Event = Event<'static>; + type Metadata = wl_keyboard::WlKeyboard; + type Ret = (); + + fn process_events( + &mut self, + readiness: calloop::Readiness, + token: calloop::Token, + mut callback: F, + ) -> std::io::Result<()> + where + F: FnMut(Event<'static>, &mut wl_keyboard::WlKeyboard), + { + let current_repeat = &self.current_repeat; + let state = &self.state; + self.timer + .process_events(readiness, token, |(), timer_handle| { + if let Some(ref mut data) = *current_repeat.borrow_mut() { + // there is something to repeat + let mut state = state.borrow_mut(); + let keysym = state.get_one_sym_raw(data.keycode); + let utf8 = state.get_utf8_raw(data.keycode); + let new_time = data.gap + data.time; + // Notify the callback. + callback( + Event::Repeat { + time: (new_time / 1000) as u32, + rawkey: data.keycode, + keysym, + utf8, + }, + &mut data.keyboard, + ); + // Update the time of last event. + data.time = new_time; + // Schedule the next timeout. + timer_handle.add_timeout(Duration::from_micros(data.gap), ()); + } + }) + } + + fn register(&mut self, poll: &mut calloop::Poll, token: calloop::Token) -> std::io::Result<()> { + self.timer.register(poll, token) + } + + fn reregister( + &mut self, + poll: &mut calloop::Poll, + token: calloop::Token, + ) -> std::io::Result<()> { + self.timer.reregister(poll, token) + } + + fn unregister(&mut self, poll: &mut calloop::Poll) -> std::io::Result<()> { + self.timer.unregister(poll) + } +} diff --git a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs b/src/platform_impl/linux/wayland/seat/keyboard/mod.rs index c5c090bf04..1a57b441b0 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/mod.rs @@ -3,15 +3,13 @@ use std::cell::RefCell; use std::rc::Rc; +use sctk::reexports::calloop::{LoopHandle, Source}; use sctk::reexports::client::protocol::wl_keyboard::WlKeyboard; use sctk::reexports::client::protocol::wl_seat::WlSeat; use sctk::reexports::client::Attached; -use sctk::reexports::calloop::{LoopHandle, Source}; - -use sctk::seat::keyboard::{self, RepeatSource}; - use crate::keyboard::ModifiersState; +use crate::platform_impl::platform::common::xkb_state; use crate::platform_impl::wayland::event_loop::WinitState; use crate::platform_impl::wayland::WindowId; @@ -21,7 +19,7 @@ pub(crate) struct Keyboard { pub keyboard: WlKeyboard, /// The source for repeat keys. - pub repeat_source: Option>, + pub repeat_source: Option>, /// LoopHandle to drop `RepeatSource`, when dropping the keyboard. pub loop_handle: LoopHandle, @@ -34,11 +32,11 @@ impl Keyboard { modifiers_state: Rc>, ) -> Option { let mut inner = KeyboardInner::new(modifiers_state); - let keyboard_data = keyboard::map_keyboard_repeat( + let keyboard_data = handlers::map_keyboard_repeat( loop_handle.clone(), &seat, None, - keyboard::RepeatKind::System, + handlers::RepeatKind::System, move |event, _, mut dispatch_data| { let winit_state = dispatch_data.get::().unwrap(); handlers::handle_keyboard(event, &mut inner, winit_state); @@ -92,8 +90,8 @@ impl KeyboardInner { } } -impl From for ModifiersState { - fn from(mods: keyboard::ModifiersState) -> ModifiersState { +impl From for ModifiersState { + fn from(mods: xkb_state::ModifiersState) -> ModifiersState { let mut wl_mods = ModifiersState::empty(); wl_mods.set(ModifiersState::SHIFT, mods.shift); wl_mods.set(ModifiersState::CONTROL, mods.ctrl); From 423e184acd163df786895ad152189c499277e95d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 1 Apr 2021 05:40:02 +0200 Subject: [PATCH 074/131] Wire up the new events properly --- Cargo.toml | 2 +- src/platform/unix.rs | 2 +- src/platform_impl/linux/common/keymap.rs | 13 +- src/platform_impl/linux/common/xkb_state.rs | 144 +++++++++++++ src/platform_impl/linux/mod.rs | 2 +- .../linux/wayland/seat/keyboard/handlers.rs | 199 +++++++++--------- 6 files changed, 247 insertions(+), 115 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8fd6314187..b80715b504 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,7 +95,7 @@ x11-dl = { version = "2.18.5", optional = true } percent-encoding = { version = "2.0", optional = true } parking_lot = { version = "0.11.0", optional = true } memmap2 = { version = "0.2.1", optional = true } -xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "e175ffe" } +xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "f335e626c" } [target.'cfg(target_arch = "wasm32")'.dependencies.web_sys] package = "web-sys" diff --git a/src/platform/unix.rs b/src/platform/unix.rs index 6ec6b28f8a..0b2a2dbe9e 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -536,7 +536,7 @@ pub struct ARGBColor { impl KeyEventExtModifierSupplement for KeyEvent { #[inline] fn text_with_all_modifiers(&self) -> Option<&str> { - self.platform_specific.text_with_all_modifers + self.platform_specific.text_with_all_modifiers } #[inline] diff --git a/src/platform_impl/linux/common/keymap.rs b/src/platform_impl/linux/common/keymap.rs index 3016851dd0..86bd90b840 100644 --- a/src/platform_impl/linux/common/keymap.rs +++ b/src/platform_impl/linux/common/keymap.rs @@ -421,15 +421,13 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { keysyms::XKB_KEY_Control_R => Key::Control, keysyms::XKB_KEY_Caps_Lock => Key::CapsLock, // keysyms::XKB_KEY_Shift_Lock => Key::ShiftLock, - - // NOTE: The key xkb calls "Meta" is called "Super" by Winit, and vice versa. - // This is a tad confusing, but these keys have different names depending on who you ask. - keysyms::XKB_KEY_Meta_L => Key::Super, - keysyms::XKB_KEY_Meta_R => Key::Super, + // + keysyms::XKB_KEY_Meta_L => Key::Meta, + keysyms::XKB_KEY_Meta_R => Key::Meta, keysyms::XKB_KEY_Alt_L => Key::Alt, keysyms::XKB_KEY_Alt_R => Key::Alt, - keysyms::XKB_KEY_Super_L => Key::Meta, - keysyms::XKB_KEY_Super_R => Key::Meta, + keysyms::XKB_KEY_Super_L => Key::Super, + keysyms::XKB_KEY_Super_R => Key::Super, keysyms::XKB_KEY_Hyper_L => Key::Hyper, keysyms::XKB_KEY_Hyper_R => Key::Hyper, @@ -681,6 +679,7 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { keysyms::XKB_KEY_SunVideoRaiseBrightness => Key::BrightnessUp, // XKB_KEY_SunPowerSwitchShift // + 0 => Key::Unidentified(NativeKeyCode::Unidentified), _ => Key::Unidentified(NativeKeyCode::XKB(keysym)), } } diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 5bb3fd278c..1fa6f34a58 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -1,3 +1,5 @@ +use std::cell::RefCell; +use std::convert::TryInto; use std::env; use std::ffi::CString; use std::fs::File; @@ -12,6 +14,11 @@ use xkbcommon_dl::{ pub use sctk::seat::keyboard::RMLVO; +use crate::{ + event::ElementState, + keyboard::{Key, KeyCode, KeyLocation}, +}; + pub(crate) struct KbState { xkb_context: *mut ffi::xkb_context, xkb_keymap: *mut ffi::xkb_keymap, @@ -381,3 +388,140 @@ pub enum Error { /// Provided RMLVO specified a keymap that would not be loaded BadNames, } + +impl KbState { + pub fn process_key_event(&mut self, keycode: u32, state: ElementState) -> KeyEventResults<'_> { + KeyEventResults::new(self, keycode, state == ElementState::Pressed) + } + + pub fn process_key_repeat_event(&mut self, keycode: u32) -> KeyEventResults<'_> { + KeyEventResults::new(self, keycode, false) + } +} + +enum XkbCompose { + Accepted(ffi::xkb_compose_status), + Ignored, + Uninitialized, +} + +pub(crate) struct KeyEventResults<'a> { + state: RefCell<&'a mut KbState>, + keycode: u32, + keysym: u32, + compose: Option, +} + +impl<'a> KeyEventResults<'a> { + fn new(state: &'a mut KbState, keycode: u32, compose: bool) -> Self { + let keysym = state.get_one_sym_raw(keycode); + + let compose = if compose { + Some(match state.compose_feed(keysym) { + Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED) => { + // Unwrapping is safe here, as `compose_feed` returns `None` when composition is uninitialized. + XkbCompose::Accepted(state.compose_status().unwrap()) + } + Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED) => XkbCompose::Ignored, + None => XkbCompose::Uninitialized, + }) + } else { + None + }; + + KeyEventResults { + state: RefCell::new(state), + keycode, + keysym, + compose, + } + } + + pub fn keycode(&mut self) -> KeyCode { + super::keymap::rawkey_to_keycode(self.keycode) + } + + pub fn key(&mut self) -> (Key<'static>, KeyLocation) { + let location = super::keymap::keysym_location(self.keysym); + let mut key = super::keymap::keysym_to_key(self.keysym); + if matches!(key, Key::Unidentified(_)) { + if let Some(string) = self.state.borrow_mut().get_utf8_raw(self.keycode) { + key = Key::Character(cached_string(string)); + } + } + (key, location) + } + + pub fn key_without_modifiers(&mut self) -> (Key<'static>, KeyLocation) { + // This will become a pointer to an array which libxkbcommon owns, so we don't need to deallocate it. + let mut keysyms = ptr::null(); + let keysym_count = unsafe { + (XKBH.xkb_keymap_key_get_syms_by_level)( + self.state.borrow_mut().xkb_keymap, + self.keycode, + 0, + 0, + &mut keysyms, + ) + }; + let keysym = if keysym_count == 1 { + unsafe { *keysyms } + } else { + 0 + }; + let key = super::keymap::keysym_to_key(keysym); + let location = super::keymap::keysym_location(keysym); + (key, location) + } + + pub fn text(&mut self) -> Option<&'static str> { + let keysym = self.keysym; + self._text(|| keysym_to_utf8_raw(keysym)) + } + + pub fn text_with_all_modifiers(&mut self) -> Option<&'static str> { + // TODO: Should Ctrl override any attempts to compose text? + // gnome-terminal agrees, but konsole disagrees. + // Should it be configurable instead? + let keycode = self.keycode; + self._text(|| self.state.borrow_mut().get_utf8_raw(keycode)) + } + + fn _text(&self, fallback: F) -> Option<&'static str> + where + F: FnOnce() -> Option, + { + if let Some(compose) = &self.compose { + match compose { + XkbCompose::Accepted(status) => match status { + ffi::xkb_compose_status::XKB_COMPOSE_COMPOSED => { + self.state.borrow_mut().compose_get_utf8() + } + ffi::xkb_compose_status::XKB_COMPOSE_NOTHING => fallback(), + _ => None, + }, + XkbCompose::Ignored | XkbCompose::Uninitialized => fallback(), + } + } else { + fallback() + } + .map(cached_string) + } +} + +fn keysym_to_utf8_raw(keysym: u32) -> Option { + let size = unsafe { (XKBH.xkb_keysym_to_utf8)(keysym, ptr::null_mut(), 0) } + 1; + if size <= 1 { + return None; + } + let mut buffer: Vec = Vec::with_capacity(size.try_into().unwrap()); + unsafe { (XKBH.xkb_keysym_to_utf8)(keysym, buffer.as_mut_ptr().cast(), buffer.len()) }; + // remove the final `\0` + buffer.pop(); + // libxkbcommon will always provide valid UTF8 + Some(unsafe { String::from_utf8_unchecked(buffer) }) +} + +fn cached_string>(string: S) -> &'static str { + Box::leak(string.into().into_boxed_str()) +} diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 1321437c3c..3f4f6e9ff6 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -46,8 +46,8 @@ mod common; #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEventExtra { - pub text_with_all_modifers: Option<&'static str>, pub key_without_modifiers: Key<'static>, + pub text_with_all_modifiers: Option<&'static str>, } /// Environment variable specifying which backend should be used on unix platform. diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs index acb5dc1852..3593e912bd 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs @@ -2,15 +2,16 @@ use sctk::reexports::calloop; use sctk::reexports::client; -use sctk::reexports::client::protocol::wl_keyboard::KeyState; -use crate::event::{ElementState, KeyEvent, WindowEvent}; -use crate::keyboard::{Key, ModifiersState, NativeKeyCode}; -use crate::platform_impl::platform::common::keymap; +use crate::keyboard::{Key, KeyLocation, ModifiersState}; use crate::platform_impl::platform::common::xkb_state::{self, RMLVO}; use crate::platform_impl::wayland::event_loop::WinitState; use crate::platform_impl::wayland::{self, DeviceId}; use crate::platform_impl::KeyEventExtra; +use crate::{ + event::{ElementState, KeyEvent, WindowEvent}, + keyboard::KeyCode, +}; use super::KeyboardInner; @@ -54,10 +55,13 @@ pub(super) fn handle_keyboard( inner.target_window_id = None; } Event::Key { - rawkey, - keysym, + physical_key, + logical_key, + text, + location, state, - utf8, + key_without_modifiers, + text_with_all_modifiers, .. } => { let window_id = match inner.target_window_id { @@ -65,16 +69,6 @@ pub(super) fn handle_keyboard( None => return, }; - let state = match state { - KeyState::Pressed => ElementState::Pressed, - KeyState::Released => ElementState::Released, - _ => unreachable!(), - }; - - let physical_key = keymap::rawkey_to_keycode(rawkey); - let logical_key = keymap::keysym_to_key(keysym); - let location = keymap::keysym_location(keysym); - event_sink.push_window_event( WindowEvent::KeyboardInput { device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( @@ -83,35 +77,27 @@ pub(super) fn handle_keyboard( event: KeyEvent { physical_key, logical_key, - text: None, + text, location, state, repeat: false, platform_specific: KeyEventExtra { - key_without_modifiers: Key::Unidentified(NativeKeyCode::Unidentified), - text_with_all_modifers: None, + key_without_modifiers, + text_with_all_modifiers, }, }, is_synthetic: false, }, window_id, ); - - // Send ReceivedCharacter event only on ElementState::Pressed. - if ElementState::Released == state { - return; - } - - if let Some(txt) = utf8 { - for ch in txt.chars() { - // event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id); - } - } } Event::Repeat { - rawkey, - keysym, - utf8, + physical_key, + logical_key, + text, + location, + key_without_modifiers, + text_with_all_modifiers, .. } => { let window_id = match inner.target_window_id { @@ -119,10 +105,6 @@ pub(super) fn handle_keyboard( None => return, }; - let physical_key = keymap::rawkey_to_keycode(rawkey); - let logical_key = keymap::keysym_to_key(keysym); - let location = keymap::keysym_location(keysym); - event_sink.push_window_event( WindowEvent::KeyboardInput { device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland( @@ -131,24 +113,20 @@ pub(super) fn handle_keyboard( event: KeyEvent { physical_key, logical_key, - text: None, + text, location, state: ElementState::Pressed, repeat: true, platform_specific: KeyEventExtra { - key_without_modifiers: Key::Unidentified(NativeKeyCode::Unidentified), - text_with_all_modifers: None, + key_without_modifiers, + text_with_all_modifiers, }, }, is_synthetic: false, }, window_id, ); - - if let Some(txt) = utf8 { - for ch in txt.chars() { - // event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id); - } + } Event::Modifiers { modifiers } => { let modifiers = ModifiersState::from(modifiers); if let Some(window_id) = inner.target_window_id { @@ -183,7 +161,6 @@ use sctk::reexports::client::{ protocol::{wl_keyboard, wl_seat, wl_surface}, Attached, }; -use xkbcommon_dl as ffi; use super::super::super::super::common::xkb_state::KbState; @@ -192,6 +169,7 @@ const MICROS_IN_SECOND: u32 = 1000000; /// Possible kinds of key repetition pub enum RepeatKind { /// keys will be repeated at a set rate and delay + #[allow(dead_code)] Fixed { /// The number of repetitions per second that should occur. rate: u32, @@ -249,27 +227,24 @@ pub enum Event<'a> { serial: u32, /// time at which the keypress occurred time: u32, - /// raw value of the key - rawkey: u32, - /// interpreted symbol of the key - keysym: u32, - /// new state of the key - state: KeyState, - /// utf8 interpretation of the entered text - /// - /// will always be `None` on key release events - utf8: Option, + physical_key: KeyCode, + logical_key: Key<'static>, + text: Option<&'static str>, + location: KeyLocation, + state: ElementState, + key_without_modifiers: Key<'static>, + text_with_all_modifiers: Option<&'static str>, }, /// A key repetition event Repeat { /// time at which the repetition occured time: u32, - /// raw value of the key - rawkey: u32, - /// interpreted symbol of the key - keysym: u32, - /// utf8 interpretation of the entered text - utf8: Option, + physical_key: KeyCode, + logical_key: Key<'static>, + text: Option<&'static str>, + location: KeyLocation, + key_without_modifiers: Key<'static>, + text_with_all_modifiers: Option<&'static str>, }, } @@ -562,47 +537,49 @@ impl KbdHandler { key_state: wl_keyboard::KeyState, dispatch_data: client::DispatchData<'_>, ) { - let (sym, utf8, repeats) = { + let ( + physical_key, + logical_key, + text, + location, + state, + key_without_modifiers, + text_with_all_modifiers, + repeats, + ) = { let mut state = self.state.borrow_mut(); - // Get the values to generate a key event - let sym = state.get_one_sym_raw(key); - let utf8 = if key_state == wl_keyboard::KeyState::Pressed { - match state.compose_feed(sym) { - Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED) => { - if let Some(status) = state.compose_status() { - match status { - ffi::xkb_compose_status::XKB_COMPOSE_COMPOSED => { - state.compose_get_utf8() - } - ffi::xkb_compose_status::XKB_COMPOSE_NOTHING => { - state.get_utf8_raw(key) - } - _ => None, - } - } else { - state.get_utf8_raw(key) - } - } - Some(_) => { - // XKB_COMPOSE_FEED_IGNORED - None - } - None => { - // XKB COMPOSE is not initialized - state.get_utf8_raw(key) - } - } - } else { - None + let key_state = match key_state { + wl_keyboard::KeyState::Pressed => ElementState::Pressed, + wl_keyboard::KeyState::Released => ElementState::Released, + _ => unreachable!(), }; + + let mut ker = state.process_key_event(key, key_state); + + let physical_key = ker.keycode(); + let (logical_key, location) = ker.key(); + let text = ker.text(); + let (key_without_modifiers, _) = ker.key_without_modifiers(); + let text_with_all_modifiers = ker.text_with_all_modifiers(); + let repeats = unsafe { state.key_repeats(key + 8) }; - (sym, utf8, repeats) + + ( + physical_key, + logical_key, + text, + location, + key_state, + key_without_modifiers, + text_with_all_modifiers, + repeats, + ) }; { if let Some(ref mut repeat_handle) = self.repeat { if repeats { - if key_state == wl_keyboard::KeyState::Pressed { + if state == ElementState::Pressed { repeat_handle.start_repeat(key, object.clone(), time); } else { repeat_handle.stop_repeat(key); @@ -615,10 +592,13 @@ impl KbdHandler { Event::Key { serial, time, - rawkey: key, - keysym: sym, - state: key_state, - utf8, + physical_key, + logical_key, + text, + location, + state, + key_without_modifiers, + text_with_all_modifiers, }, object, dispatch_data, @@ -709,16 +689,25 @@ impl calloop::EventSource for RepeatSource { if let Some(ref mut data) = *current_repeat.borrow_mut() { // there is something to repeat let mut state = state.borrow_mut(); - let keysym = state.get_one_sym_raw(data.keycode); - let utf8 = state.get_utf8_raw(data.keycode); + let mut ker = state.process_key_repeat_event(data.keycode); + + let physical_key = ker.keycode(); + let (logical_key, location) = ker.key(); + let text = ker.text(); + let (key_without_modifiers, _) = ker.key_without_modifiers(); + let text_with_all_modifiers = ker.text_with_all_modifiers(); + let new_time = data.gap + data.time; // Notify the callback. callback( Event::Repeat { time: (new_time / 1000) as u32, - rawkey: data.keycode, - keysym, - utf8, + physical_key, + logical_key, + text, + location, + key_without_modifiers, + text_with_all_modifiers, }, &mut data.keyboard, ); From 2f1114f2260609b17b7649186027e1f315fcdf35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 1 Apr 2021 06:03:36 +0200 Subject: [PATCH 075/131] Fix creation of key value strings Use `xkb_keysym_to_utf8` rather than `xkb_state_key_get_utf8` to create key value strings, as the latter performs keysym transformations, while the former doesn't. --- src/platform_impl/linux/common/xkb_state.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 1fa6f34a58..2983a503cf 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -445,7 +445,7 @@ impl<'a> KeyEventResults<'a> { let location = super::keymap::keysym_location(self.keysym); let mut key = super::keymap::keysym_to_key(self.keysym); if matches!(key, Key::Unidentified(_)) { - if let Some(string) = self.state.borrow_mut().get_utf8_raw(self.keycode) { + if let Some(string) = keysym_to_utf8_raw(self.keysym) { key = Key::Character(cached_string(string)); } } @@ -510,12 +510,21 @@ impl<'a> KeyEventResults<'a> { } fn keysym_to_utf8_raw(keysym: u32) -> Option { - let size = unsafe { (XKBH.xkb_keysym_to_utf8)(keysym, ptr::null_mut(), 0) } + 1; - if size <= 1 { - return None; + let mut buffer: Vec = Vec::with_capacity(8); + loop { + let bytes_written = unsafe { + (XKBH.xkb_keysym_to_utf8)(keysym, buffer.as_mut_ptr().cast(), buffer.capacity()) + }; + if bytes_written == 0 { + return None; + } else if bytes_written == -1 { + buffer.reserve(8); + } else { + unsafe { buffer.set_len(bytes_written.try_into().unwrap()) }; + break; + } } - let mut buffer: Vec = Vec::with_capacity(size.try_into().unwrap()); - unsafe { (XKBH.xkb_keysym_to_utf8)(keysym, buffer.as_mut_ptr().cast(), buffer.len()) }; + // remove the final `\0` buffer.pop(); // libxkbcommon will always provide valid UTF8 From 228d1ec7519634686889cd6718085a320bb70b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 1 Apr 2021 07:37:32 +0200 Subject: [PATCH 076/131] Add missing keysym mapping I accidentally skipped over `XKB_KEY_Page_Down` while initially mapping keysyms. --- src/platform_impl/linux/common/keymap.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform_impl/linux/common/keymap.rs b/src/platform_impl/linux/common/keymap.rs index 86bd90b840..dbd0431679 100644 --- a/src/platform_impl/linux/common/keymap.rs +++ b/src/platform_impl/linux/common/keymap.rs @@ -318,6 +318,7 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { // keysyms::XKB_KEY_Prior => Key::PageUp, keysyms::XKB_KEY_Page_Up => Key::PageUp, // keysyms::XKB_KEY_Next => Key::PageDown, + keysyms::XKB_KEY_Page_Down => Key::PageDown, keysyms::XKB_KEY_End => Key::End, // keysyms::XKB_KEY_Begin => Key::Begin, From 7f773592ba817bc1105fa5791f0aa20badff6a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 1 Apr 2021 08:18:20 +0200 Subject: [PATCH 077/131] Fix `key_without_modifiers` I forgot about the offset between evdev keycodes and xkb keycodes. --- src/platform_impl/linux/common/xkb_state.rs | 22 +++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 2983a503cf..cc200c3a5d 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -442,14 +442,7 @@ impl<'a> KeyEventResults<'a> { } pub fn key(&mut self) -> (Key<'static>, KeyLocation) { - let location = super::keymap::keysym_location(self.keysym); - let mut key = super::keymap::keysym_to_key(self.keysym); - if matches!(key, Key::Unidentified(_)) { - if let Some(string) = keysym_to_utf8_raw(self.keysym) { - key = Key::Character(cached_string(string)); - } - } - (key, location) + Self::keysym_to_key(self.keysym) } pub fn key_without_modifiers(&mut self) -> (Key<'static>, KeyLocation) { @@ -458,7 +451,7 @@ impl<'a> KeyEventResults<'a> { let keysym_count = unsafe { (XKBH.xkb_keymap_key_get_syms_by_level)( self.state.borrow_mut().xkb_keymap, - self.keycode, + self.keycode + 8, 0, 0, &mut keysyms, @@ -469,8 +462,17 @@ impl<'a> KeyEventResults<'a> { } else { 0 }; - let key = super::keymap::keysym_to_key(keysym); + Self::keysym_to_key(keysym) + } + + fn keysym_to_key(keysym: u32) -> (Key<'static>, KeyLocation) { let location = super::keymap::keysym_location(keysym); + let mut key = super::keymap::keysym_to_key(keysym); + if matches!(key, Key::Unidentified(_)) { + if let Some(string) = keysym_to_utf8_raw(keysym) { + key = Key::Character(cached_string(string)); + } + } (key, location) } From 61b8956551f8483f667db8618aabfdceaa8ac5db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 1 Apr 2021 08:42:24 +0200 Subject: [PATCH 078/131] Feature-gate things which depend on memmap2 --- src/platform_impl/linux/common/xkb_state.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index cc200c3a5d..147a009e31 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -7,6 +7,7 @@ use std::os::raw::c_char; use std::os::unix::ffi::OsStringExt; use std::ptr; +#[cfg(feature = "wayland")] use memmap2::MmapOptions; use xkbcommon_dl::{ self as ffi, xkb_state_component, XKBCOMMON_COMPOSE_HANDLE as XKBCH, XKBCOMMON_HANDLE as XKBH, @@ -313,6 +314,7 @@ impl KbState { self.xkb_keymap = ptr::null_mut(); } + #[cfg(feature = "wayland")] pub(crate) unsafe fn init_with_fd(&mut self, fd: File, size: usize) { let map = MmapOptions::new().len(size).map(&fd).unwrap(); From bdde8dfce24525ed2a15260c9bdd3f6295aceefb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 1 Apr 2021 08:47:01 +0200 Subject: [PATCH 079/131] Be more consistent with what `keycode` means --- src/platform_impl/linux/common/xkb_state.rs | 4 ++-- src/platform_impl/linux/wayland/seat/keyboard/handlers.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 147a009e31..fa1e3e4fda 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -351,8 +351,8 @@ impl KbState { Ok(()) } - pub(crate) unsafe fn key_repeats(&mut self, xkb_keycode_t: ffi::xkb_keycode_t) -> bool { - (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, xkb_keycode_t) == 1 + pub(crate) unsafe fn key_repeats(&mut self, keycode: ffi::xkb_keycode_t) -> bool { + (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, keycode + 8) == 1 } #[inline] diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs index 3593e912bd..9a6d704055 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs @@ -562,7 +562,7 @@ impl KbdHandler { let (key_without_modifiers, _) = ker.key_without_modifiers(); let text_with_all_modifiers = ker.text_with_all_modifiers(); - let repeats = unsafe { state.key_repeats(key + 8) }; + let repeats = unsafe { state.key_repeats(key) }; ( physical_key, From 3aea36ffbfd40042acc892c0a736bd987090669b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 1 Apr 2021 08:48:51 +0200 Subject: [PATCH 080/131] Wire up Wayland IME again --- src/platform_impl/linux/wayland/seat/text_input/handlers.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/platform_impl/linux/wayland/seat/text_input/handlers.rs b/src/platform_impl/linux/wayland/seat/text_input/handlers.rs index 26d56a3cb6..b57b5f58a3 100644 --- a/src/platform_impl/linux/wayland/seat/text_input/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/text_input/handlers.rs @@ -69,9 +69,7 @@ pub(super) fn handle_text_input( _ => return, }; - for ch in text.chars() { - // event_sink.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id); - } + event_sink.push_window_event(WindowEvent::ReceivedImeText(text), window_id); } _ => (), } From 663969cb7f6c7b02d0072b315c176551d6c52959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 1 Apr 2021 10:12:12 +0200 Subject: [PATCH 081/131] Improve key string handling --- src/platform_impl/linux/common/xkb_state.rs | 141 ++++++++++++-------- 1 file changed, 83 insertions(+), 58 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index fa1e3e4fda..ab957920f9 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -1,4 +1,3 @@ -use std::cell::RefCell; use std::convert::TryInto; use std::env; use std::ffi::CString; @@ -28,6 +27,7 @@ pub(crate) struct KbState { xkb_compose_state: *mut ffi::xkb_compose_state, mods_state: ModifiersState, locked: bool, + scratch_buffer: Vec, } /// Represents the current state of the keyboard modifiers @@ -140,7 +140,7 @@ impl KbState { unsafe { (XKBH.xkb_state_key_get_one_sym)(self.xkb_state, keycode + 8) } } - pub(crate) fn get_utf8_raw(&mut self, keycode: u32) -> Option { + pub(crate) fn get_utf8_raw(&mut self, keycode: u32) -> Option<&'static str> { if !self.ready() { return None; } @@ -150,20 +150,21 @@ impl KbState { if size <= 1 { return None; }; - let mut buffer = Vec::with_capacity(size as usize); + self.scratch_buffer.clear(); + let size = size.try_into().unwrap(); + self.scratch_buffer.reserve(size); unsafe { - buffer.set_len(size as usize); + self.scratch_buffer.set_len(size); (XKBH.xkb_state_key_get_utf8)( self.xkb_state, keycode + 8, - buffer.as_mut_ptr() as *mut _, - size as usize, + self.scratch_buffer.as_mut_ptr() as *mut _, + size, ); }; // remove the final `\0` - buffer.pop(); - // libxkbcommon will always provide valid UTF8 - Some(unsafe { String::from_utf8_unchecked(buffer) }) + self.scratch_buffer.pop(); + Some(byte_slice_to_cached_string(&self.scratch_buffer)) } pub(crate) fn compose_feed(&mut self, keysym: u32) -> Option { @@ -180,7 +181,7 @@ impl KbState { Some(unsafe { (XKBCH.xkb_compose_state_get_status)(self.xkb_compose_state) }) } - pub(crate) fn compose_get_utf8(&mut self) -> Option { + pub(crate) fn compose_get_utf8(&mut self) -> Option<&'static str> { if !self.ready() || self.xkb_compose_state.is_null() { return None; } @@ -190,19 +191,20 @@ impl KbState { if size <= 1 { return None; }; - let mut buffer = Vec::with_capacity(size as usize); + self.scratch_buffer.clear(); + let size = size.try_into().unwrap(); + self.scratch_buffer.reserve(size); unsafe { - buffer.set_len(size as usize); + self.scratch_buffer.set_len(size); (XKBCH.xkb_compose_state_get_utf8)( self.xkb_compose_state, - buffer.as_mut_ptr() as *mut _, + self.scratch_buffer.as_mut_ptr() as *mut _, size as usize, ); }; // remove the final `\0` - buffer.pop(); - // libxkbcommon will always provide valid UTF8 - Some(unsafe { String::from_utf8_unchecked(buffer) }) + self.scratch_buffer.pop(); + Some(byte_slice_to_cached_string(&self.scratch_buffer)) } pub(crate) fn new() -> Result { @@ -225,6 +227,7 @@ impl KbState { xkb_compose_state: ptr::null_mut(), mods_state: ModifiersState::new(), locked: false, + scratch_buffer: Vec::new(), }; unsafe { @@ -399,6 +402,36 @@ impl KbState { pub fn process_key_repeat_event(&mut self, keycode: u32) -> KeyEventResults<'_> { KeyEventResults::new(self, keycode, false) } + + fn keysym_to_utf8_raw(&mut self, keysym: u32) -> Option<&'static str> { + self.scratch_buffer.clear(); + self.scratch_buffer.reserve(8); + loop { + unsafe { self.scratch_buffer.set_len(8) }; + let bytes_written = unsafe { + (XKBH.xkb_keysym_to_utf8)( + keysym, + self.scratch_buffer.as_mut_ptr().cast(), + self.scratch_buffer.capacity(), + ) + }; + if bytes_written == 0 { + return None; + } else if bytes_written == -1 { + self.scratch_buffer.reserve(8); + } else { + unsafe { + self.scratch_buffer + .set_len(bytes_written.try_into().unwrap()) + }; + break; + } + } + + // remove the final `\0` + self.scratch_buffer.pop(); + Some(byte_slice_to_cached_string(&self.scratch_buffer)) + } } enum XkbCompose { @@ -408,7 +441,7 @@ enum XkbCompose { } pub(crate) struct KeyEventResults<'a> { - state: RefCell<&'a mut KbState>, + state: &'a mut KbState, keycode: u32, keysym: u32, compose: Option, @@ -432,7 +465,7 @@ impl<'a> KeyEventResults<'a> { }; KeyEventResults { - state: RefCell::new(state), + state, keycode, keysym, compose, @@ -444,7 +477,7 @@ impl<'a> KeyEventResults<'a> { } pub fn key(&mut self) -> (Key<'static>, KeyLocation) { - Self::keysym_to_key(self.keysym) + self.keysym_to_key(self.keysym) } pub fn key_without_modifiers(&mut self) -> (Key<'static>, KeyLocation) { @@ -452,7 +485,7 @@ impl<'a> KeyEventResults<'a> { let mut keysyms = ptr::null(); let keysym_count = unsafe { (XKBH.xkb_keymap_key_get_syms_by_level)( - self.state.borrow_mut().xkb_keymap, + self.state.xkb_keymap, self.keycode + 8, 0, 0, @@ -464,77 +497,69 @@ impl<'a> KeyEventResults<'a> { } else { 0 }; - Self::keysym_to_key(keysym) + self.keysym_to_key(keysym) } - fn keysym_to_key(keysym: u32) -> (Key<'static>, KeyLocation) { + fn keysym_to_key(&mut self, keysym: u32) -> (Key<'static>, KeyLocation) { let location = super::keymap::keysym_location(keysym); let mut key = super::keymap::keysym_to_key(keysym); if matches!(key, Key::Unidentified(_)) { - if let Some(string) = keysym_to_utf8_raw(keysym) { - key = Key::Character(cached_string(string)); + if let Some(string) = self.state.keysym_to_utf8_raw(keysym) { + key = Key::Character(string); } } (key, location) } pub fn text(&mut self) -> Option<&'static str> { - let keysym = self.keysym; - self._text(|| keysym_to_utf8_raw(keysym)) + self._text(|this| this.state.keysym_to_utf8_raw(this.keysym)) } pub fn text_with_all_modifiers(&mut self) -> Option<&'static str> { // TODO: Should Ctrl override any attempts to compose text? // gnome-terminal agrees, but konsole disagrees. // Should it be configurable instead? - let keycode = self.keycode; - self._text(|| self.state.borrow_mut().get_utf8_raw(keycode)) + self._text(|this| this.state.get_utf8_raw(this.keycode)) } - fn _text(&self, fallback: F) -> Option<&'static str> + fn _text(&mut self, fallback: F) -> Option<&'static str> where - F: FnOnce() -> Option, + F: FnOnce(&mut Self) -> Option<&'static str>, { if let Some(compose) = &self.compose { match compose { XkbCompose::Accepted(status) => match status { - ffi::xkb_compose_status::XKB_COMPOSE_COMPOSED => { - self.state.borrow_mut().compose_get_utf8() - } - ffi::xkb_compose_status::XKB_COMPOSE_NOTHING => fallback(), + ffi::xkb_compose_status::XKB_COMPOSE_COMPOSED => self.state.compose_get_utf8(), + ffi::xkb_compose_status::XKB_COMPOSE_NOTHING => fallback(self), _ => None, }, - XkbCompose::Ignored | XkbCompose::Uninitialized => fallback(), + XkbCompose::Ignored | XkbCompose::Uninitialized => fallback(self), } } else { - fallback() + fallback(self) } - .map(cached_string) } } -fn keysym_to_utf8_raw(keysym: u32) -> Option { - let mut buffer: Vec = Vec::with_capacity(8); - loop { - let bytes_written = unsafe { - (XKBH.xkb_keysym_to_utf8)(keysym, buffer.as_mut_ptr().cast(), buffer.capacity()) - }; - if bytes_written == 0 { - return None; - } else if bytes_written == -1 { - buffer.reserve(8); - } else { - unsafe { buffer.set_len(bytes_written.try_into().unwrap()) }; - break; - } +fn byte_slice_to_cached_string(bytes: &[u8]) -> &'static str { + use std::cell::RefCell; + use std::collections::HashSet; + + thread_local! { + static STRING_CACHE: RefCell> = RefCell::new(HashSet::new()); } - // remove the final `\0` - buffer.pop(); - // libxkbcommon will always provide valid UTF8 - Some(unsafe { String::from_utf8_unchecked(buffer) }) -} + let string = std::str::from_utf8(bytes).unwrap(); -fn cached_string>(string: S) -> &'static str { - Box::leak(string.into().into_boxed_str()) + STRING_CACHE.with(|cache| { + let mut cache = cache.borrow_mut(); + if let Some(string) = cache.get(string) { + *string + } else { + // borrowck couldn't quite figure out this one on its own + let string: &'static str = Box::leak(String::from(string).into_boxed_str()); + cache.insert(string); + string + } + }) } From 03c8b0dc289c34d95fce3fea434cc846b959127d Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Thu, 1 Apr 2021 15:09:40 +0200 Subject: [PATCH 082/131] 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 083/131] 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 084/131] 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 085/131] 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 086/131] 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 2183ea5beaa7dfd2cd5c5c519c4db0fb127038d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Fri, 23 Apr 2021 21:10:08 +0200 Subject: [PATCH 087/131] Remove usage of `RawKeyEvent.repeat` --- src/platform_impl/linux/x11/event_processor.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index e25e08da46..c0f8527ecf 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1104,7 +1104,6 @@ impl EventProcessor { event: DeviceEvent::Key(RawKeyEvent { physical_key: todo!(), state: todo!(), - repeat: todo!(), }), }); From f4eb8b3b066c875b5ac52fdeb64433f9d9b4b3af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Sat, 24 Apr 2021 18:06:34 +0200 Subject: [PATCH 088/131] Add initial X11 support --- src/platform_impl/linux/common/xkb_state.rs | 41 ++++++++++ .../linux/x11/event_processor.rs | 78 ++++++++++++------- src/platform_impl/linux/x11/mod.rs | 6 ++ 3 files changed, 96 insertions(+), 29 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index ab957920f9..59e03146f4 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -8,6 +8,11 @@ use std::ptr; #[cfg(feature = "wayland")] use memmap2::MmapOptions; +#[cfg(feature = "x11")] +use x11_dl::xlib_xcb::xcb_connection_t; +#[cfg(feature = "x11")] +use xkbcommon_dl::x11::XKBCOMMON_X11_HANDLE as XKBXH; + use xkbcommon_dl::{ self as ffi, xkb_state_component, XKBCOMMON_COMPOSE_HANDLE as XKBCH, XKBCOMMON_HANDLE as XKBH, }; @@ -237,6 +242,42 @@ impl KbState { Ok(me) } + #[cfg(feature = "x11")] + pub(crate) fn from_x11_xkb(connection: *mut xcb_connection_t) -> Result { + let mut me = Self::new()?; + + let result = unsafe { + (XKBXH.xkb_x11_setup_xkb_extension)( + connection, + 1, + 2, + xkbcommon_dl::x11::xkb_x11_setup_xkb_extension_flags::XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ) + }; + assert_eq!(result, 1, "Failed to initialize libxkbcommon"); + + // TODO: Support keyboards other than the "virtual core keyboard device". + let core_keyboard_id = unsafe { (XKBXH.xkb_x11_get_core_keyboard_device_id)(connection) }; + let keymap = unsafe { + (XKBXH.xkb_x11_keymap_new_from_device)( + me.xkb_context, + connection, + core_keyboard_id, + xkbcommon_dl::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS, + ) + }; + assert_ne!(keymap, ptr::null_mut()); + me.xkb_keymap = keymap; + + unsafe { me.post_init(keymap) }; + + Ok(me) + } + pub(crate) fn from_rmlvo(rmlvo: RMLVO) -> Result { fn to_cstring(s: Option) -> Result, Error> { s.map_or(Ok(None), |s| CString::new(s).map(Option::Some)) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index c0f8527ecf..f4da463772 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1,13 +1,12 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc}; -use libc::{c_char, c_int, c_long, c_uint, c_ulong}; +use libc::{c_char, c_int, c_long, c_ulong}; use parking_lot::MutexGuard; use super::{ - events, ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd, - DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId, - XExtension, + ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd, DndState, + GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId, XExtension, }; use util::modifiers::{ModifierKeyState, ModifierKeymap}; @@ -17,6 +16,10 @@ use crate::{ event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, TouchPhase, WindowEvent}, event_loop::EventLoopWindowTarget as RootELW, keyboard::ModifiersState, + platform_impl::platform::{ + common::{keymap, xkb_state::KbState}, + KeyEventExtra, + }, }; /// The X11 documentation states: "Keycodes lie in the inclusive range [8,255]". @@ -29,6 +32,7 @@ pub(super) struct EventProcessor { pub(super) devices: RefCell>, pub(super) xi2ext: XExtension, pub(super) target: Rc>, + pub(super) kb_state: KbState, pub(super) mod_keymap: ModifierKeymap, pub(super) device_mod_state: ModifierKeyState, // Number of touch events currently in progress @@ -567,29 +571,34 @@ impl EventProcessor { // When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with // a keycode of 0. if keycode != 0 { - let scancode = keycode - KEYCODE_OFFSET as u32; - let keysym = wt.xconn.lookup_keysym(xkev); - // let virtual_keycode = events::keysym_to_element(keysym as c_uint); + let keycode = keycode - KEYCODE_OFFSET as u32; + let mut ker = self.kb_state.process_key_event(keycode, state); + let physical_key = ker.keycode(); + let (logical_key, location) = ker.key(); + let text = ker.text(); + let (key_without_modifiers, _) = ker.key_without_modifiers(); + let text_with_all_modifiers = ker.text_with_all_modifiers(); update_modifiers!( ModifiersState::from_x11_mask(xkev.state), self.mod_keymap.get_modifier(xkev.keycode as ffi::KeyCode) ); - let modifiers = self.device_mod_state.modifiers(); - callback(Event::WindowEvent { window_id, event: WindowEvent::KeyboardInput { device_id, event: KeyEvent { - physical_key: todo!(), - logical_key: todo!(), - text: todo!(), - location: todo!(), + physical_key, + logical_key, + text, + location, state, - repeat: todo!(), - platform_specific: todo!(), + repeat: false, + platform_specific: KeyEventExtra { + key_without_modifiers, + text_with_all_modifiers, + }, }, is_synthetic: false, }, @@ -929,6 +938,7 @@ impl EventProcessor { &wt, window_id, ElementState::Pressed, + &mut self.kb_state, &self.mod_keymap, &mut self.device_mod_state, &mut callback, @@ -953,6 +963,7 @@ impl EventProcessor { &wt, window_id, ElementState::Released, + &mut self.kb_state, &self.mod_keymap, &mut self.device_mod_state, &mut callback, @@ -1095,18 +1106,19 @@ impl EventProcessor { if scancode < 0 { return; } - let keysym = wt.xconn.keycode_to_keysym(keycode as ffi::KeyCode); - // let virtual_keycode = events::keysym_to_element(keysym as c_uint); + let physical_key = keymap::rawkey_to_keycode(scancode as u32); let modifiers = self.device_mod_state.modifiers(); callback(Event::DeviceEvent { device_id, event: DeviceEvent::Key(RawKeyEvent { - physical_key: todo!(), - state: todo!(), + physical_key, + state, }), }); + // `ModifiersChanged` is dispatched here because we assume that every `KeyPress` is + // preceeded by a `RawKeyPress`. if let Some(modifier) = self.mod_keymap.get_modifier(keycode as ffi::KeyCode) { @@ -1231,6 +1243,7 @@ impl EventProcessor { wt: &super::EventLoopWindowTarget, window_id: crate::window::WindowId, state: ElementState, + kb_state: &mut KbState, mod_keymap: &ModifierKeymap, device_mod_state: &mut ModifierKeyState, callback: &mut F, @@ -1247,9 +1260,13 @@ impl EventProcessor { .into_iter() .filter(|k| *k >= KEYCODE_OFFSET) { - let scancode = (keycode - KEYCODE_OFFSET) as u32; - let keysym = wt.xconn.keycode_to_keysym(keycode); - // let virtual_keycode = events::keysym_to_element(keysym as c_uint); + let keycode = (keycode - KEYCODE_OFFSET) as u32; + let mut ker = kb_state.process_key_event(keycode, state); + let physical_key = ker.keycode(); + let (logical_key, location) = ker.key(); + let text = ker.text(); + let (key_without_modifiers, _) = ker.key_without_modifiers(); + let text_with_all_modifiers = ker.text_with_all_modifiers(); if let Some(modifier) = mod_keymap.get_modifier(keycode as ffi::KeyCode) { device_mod_state.key_event( @@ -1264,13 +1281,16 @@ impl EventProcessor { event: WindowEvent::KeyboardInput { device_id, event: KeyEvent { - physical_key: todo!(), - logical_key: todo!(), - text: todo!(), - location: todo!(), - state: todo!(), - repeat: todo!(), - platform_specific: todo!(), + physical_key, + logical_key, + text, + location, + state, + repeat: false, + platform_specific: KeyEventExtra { + key_without_modifiers, + text_with_all_modifiers, + }, }, is_synthetic: true, }, diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index d06936c55b..f520c5f2de 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -48,6 +48,7 @@ use self::{ ime::{Ime, ImeCreationError, ImeReceiver, ImeSender}, util::modifiers::ModifierKeymap, }; +use super::common::xkb_state::KbState; use crate::{ error::OsError as RootOsError, event::{Event, StartCause}, @@ -208,6 +209,10 @@ impl EventLoop { ) .unwrap(); + let kb_state = + KbState::from_x11_xkb(unsafe { (xconn.xlib_xcb.XGetXCBConnection)(xconn.display) }) + .unwrap(); + let target = Rc::new(RootELW { p: super::EventLoopWindowTarget::X(EventLoopWindowTarget { ime, @@ -230,6 +235,7 @@ impl EventLoop { randr_event_offset, ime_receiver, xi2ext, + kb_state, mod_keymap, device_mod_state: Default::default(), num_touch: 0, From 43e7faa9677e4dcb672aa5962835632716511d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Sat, 24 Apr 2021 18:06:59 +0200 Subject: [PATCH 089/131] Improve debug printing of `NativeKeyCode` --- src/keyboard.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 6329695fcf..80a5aa7922 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -195,7 +195,7 @@ impl std::fmt::Debug for NativeKeyCode { } XKB(v) => { debug_tuple = f.debug_tuple(name_of!(XKB)); - debug_tuple.field(v); + debug_tuple.field(&format_args!("0x{:04X}", v)); } } debug_tuple.finish() From ab17bc1e348a98f80230e4de0145b0763add37e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Art=C3=BAr=20Kov=C3=A1cs?= Date: Sun, 25 Apr 2021 18:29:55 +0200 Subject: [PATCH 090/131] New keyboard API for Windows (#1788) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Introducing the new `KeyEvent` and renaming old stuff * Implemented physical_key on Windows * Ran cargo fmt * Progress with the keyboard's windows implementation * Add proper handling of dead keys * Add translation for non-printable virtual keys * Run `cargo fmt` * Fix for AltGraph not being reported * Synthesize key events when focus enters or leaves * Minor improvements * Remove obsolete API * Fix numlock and pause not being reported correctly * Ran `cargo fmt` * Fix numpad keys being reported incorrectly * Update examples * Ran `cargo fmt` * Add documentation for `ScanCode` * Add key binding example * Use consistent modifier key names #1343 * WONT COMPILE transitioning to new keyboard API * WONT COMPILE Implement new keyboard layout preparation * WONT COMPILE new keyboard API progress * Main compile errors fixed for keyboard * Fix bugs in new keyboard handling * Remove obsolete code * Fix examples * Ran `cargo fmt` * Fix build error with serde * Ran `cargo fmt` * Tweaks in the Windows keyboard implementation * Add `KeyCodeExtScancode` * Add `reset_dead_keys` * Improve the documentation for `Key` and `KeyCode` * Rename the meta key to super * Address feedback for the keyboard API * Fix for rustdoc Co-authored-by: Markus Røyset * Improve documentation Co-authored-by: Markus Røyset * Fix for arrow keys being reported as unidentified. And minor improvements * Fix media keys reporting Unidentified * Don't report text on key release events * Fix for NumLock being reported as Pause in raw input * Fix for strange behaviour around NumLock and Pause * Fix for NumLock being ineffective * Fix for location not being reported correctly * `RawKeyEvent`s now report repeat * Don't report text for synthetic key releases * Address feedback - Add the `Space` variant to the `to_text` function. - Mark `get_kbd_state` safe. - Change `[MaybeUninit; 256]` to `MaybeUninit<[u8; 256]>` * Filter `Unidentified` from PrtSc key device events * Don't report incorrect `RawKeyEvent` for shift + numpad * AltGraph is not reported again * Document Windows specific behaviour for shift+numpad * Fix typo * Dead keys now affect characters from logical_key * Prevent Pause and NumLock mappings in window events * Apply suggestions from code review Co-authored-by: Markus Røyset * Ran `cargo fmt` * Add W3C license for `Key` and `KeyCode` * Extend documentation according to feedback * Ignore NumLock in `key_without_modifiers` * Remove unused `key_code_to_non_char_key` * Remove empty event.rs file * Use space for resetting dead keys * Fix reporting multiple graphemes in logical_key * Avoid incorrect synthetic keypress during setfocus * Fixed the AltGr keypress not being reported when the AltGr key is pressed and released in a very quick succession * Filter fake Ctrl events when pressing AltGr * Improve code quality * Remove `repeat` from `RawKeyEvent` * Allow fractional scroll in raw mouse events * Fix typo Co-authored-by: Markus Siglreithmaier * Remove unused imports * Remove unused variable * Remove unnecessary `unwrap()` Co-authored-by: Markus Siglreithmaier * Avoid using the deprecated `into_rgba()` * Fix IME crash Co-authored-by: Markus Røyset Co-authored-by: Markus Siglreithmaier --- Cargo.toml | 3 + examples/control_flow.rs | 25 +- examples/cursor.rs | 6 +- examples/cursor_grab.rs | 21 +- examples/fullscreen.rs | 26 +- examples/handling_close.rs | 23 +- examples/key_binding.rs | 58 + examples/minimize.rs | 12 +- examples/multithreaded.rs | 163 +- examples/multiwindow.rs | 8 +- examples/resizable.rs | 9 +- examples/window_debug.rs | 121 +- src/event.rs | 417 ++---- src/keyboard.rs | 1417 ++++++++++++++++++ src/lib.rs | 1 + src/platform/mod.rs | 2 + src/platform/modifier_supplement.rs | 32 + src/platform/scancode.rs | 31 + src/platform/windows.rs | 362 ++++- src/platform_impl/windows/event.rs | 417 ------ src/platform_impl/windows/event_loop.rs | 653 ++++---- src/platform_impl/windows/keyboard.rs | 802 ++++++++++ src/platform_impl/windows/keyboard_layout.rs | 993 ++++++++++++ src/platform_impl/windows/minimal_ime.rs | 93 ++ src/platform_impl/windows/mod.rs | 11 +- src/platform_impl/windows/window.rs | 21 + src/platform_impl/windows/window_state.rs | 12 +- src/window.rs | 16 + tests/serde_objects.rs | 11 +- 29 files changed, 4506 insertions(+), 1260 deletions(-) create mode 100644 examples/key_binding.rs create mode 100644 src/keyboard.rs create mode 100644 src/platform/modifier_supplement.rs create mode 100644 src/platform/scancode.rs delete mode 100644 src/platform_impl/windows/event.rs create mode 100644 src/platform_impl/windows/keyboard.rs create mode 100644 src/platform_impl/windows/keyboard_layout.rs create mode 100644 src/platform_impl/windows/minimal_ime.rs diff --git a/Cargo.toml b/Cargo.toml index 64b1bee10e..807fa9c109 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" @@ -57,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" @@ -80,6 +82,7 @@ features = [ "winerror", "wingdi", "winnt", + "winnls", "winuser", ] diff --git a/examples/control_flow.rs b/examples/control_flow.rs index 64372a9bc8..6e876a3eaf 100644 --- a/examples/control_flow.rs +++ b/examples/control_flow.rs @@ -2,8 +2,9 @@ use std::{thread, time}; use simple_logger::SimpleLogger; use winit::{ - event::{Event, KeyboardInput, WindowEvent}, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, + keyboard::Key, window::WindowBuilder, }; @@ -38,7 +39,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; println!("{:?}", event); match event { Event::NewEvents(start_cause) => { @@ -52,31 +53,33 @@ fn main() { close_requested = true; } WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(virtual_code), + event: + KeyEvent { + logical_key: key, state: ElementState::Pressed, .. }, .. - } => match virtual_code { - VirtualKeyCode::Key1 => { + } => 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); } - VirtualKeyCode::Key2 => { + Key::Character("2") => { mode = Mode::WaitUntil; println!("\nmode: {:?}\n", mode); } - VirtualKeyCode::Key3 => { + Key::Character("3") => { mode = Mode::Poll; println!("\nmode: {:?}\n", mode); } - VirtualKeyCode::R => { + Key::Character("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 a466e889a8..2b6fe5b207 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::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{CursorIcon, WindowBuilder}, }; @@ -21,8 +21,8 @@ fn main() { Event::WindowEvent { event: WindowEvent::KeyboardInput { - input: - KeyboardInput { + event: + KeyEvent { state: ElementState::Pressed, .. }, diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index 90a94764de..746bf426ba 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -1,7 +1,8 @@ use simple_logger::SimpleLogger; use winit::{ - event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent}, + event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, + keyboard::{Key, ModifiersState}, window::WindowBuilder, }; @@ -23,19 +24,23 @@ fn main() { Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::KeyboardInput { - input: - KeyboardInput { + event: + KeyEvent { + logical_key: key, state: ElementState::Released, - virtual_keycode: Some(key), .. }, .. } => { - use winit::event::VirtualKeyCode::*; + // WARNING: Consider using `key_without_modifers()` if available on your platform. + // See the `key_binding` example 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(ch) => match ch.to_lowercase().as_str() { + "g" => window.set_cursor_grab(!modifiers.shift_key()).unwrap(), + "h" => window.set_cursor_visible(modifiers.shift_key()), + _ => (), + }, _ => (), } } diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 83fbde30db..4566632be6 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -1,8 +1,9 @@ use std::io::{stdin, stdout, Write}; use simple_logger::SimpleLogger; -use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}; +use winit::event::{ElementState, Event, KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; +use winit::keyboard::Key; use winit::monitor::{MonitorHandle, VideoMode}; use winit::window::{Fullscreen, WindowBuilder}; @@ -38,30 +39,33 @@ fn main() { Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(virtual_code), - state, + event: + KeyEvent { + logical_key: key, + state: ElementState::Pressed, .. }, .. - } => match (virtual_code, state) { - (VirtualKeyCode::Escape, _) => *control_flow = ControlFlow::Exit, - (VirtualKeyCode::F, ElementState::Pressed) => { + } => match key { + Key::Escape => *control_flow = ControlFlow::Exit, + + // 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()); } } - (VirtualKeyCode::S, ElementState::Pressed) => { + Key::Character("s") => { println!("window.fullscreen {:?}", window.fullscreen()); } - (VirtualKeyCode::M, ElementState::Pressed) => { + Key::Character("m") => { let is_maximized = window.is_maximized(); window.set_maximized(!is_maximized); } - (VirtualKeyCode::D, ElementState::Pressed) => { + Key::Character("d") => { decorations = !decorations; window.set_decorations(decorations); } diff --git a/examples/handling_close.rs b/examples/handling_close.rs index 8334c1773f..283cf48195 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -1,7 +1,8 @@ use simple_logger::SimpleLogger; use winit::{ - event::{Event, KeyboardInput, WindowEvent}, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, + keyboard::Key, window::WindowBuilder, }; @@ -17,10 +18,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 { @@ -44,16 +41,18 @@ fn main() { // the Y key. } WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(virtual_code), - state: Released, + event: + KeyEvent { + logical_key: key, + state: ElementState::Released, .. }, .. } => { - match virtual_code { - Y => { + // 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!"); @@ -66,7 +65,7 @@ fn main() { *control_flow = ControlFlow::Exit; } } - N => { + 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 new file mode 100644 index 0000000000..6f9bb1fe83 --- /dev/null +++ b/examples/key_binding.rs @@ -0,0 +1,58 @@ +use simple_logger::SimpleLogger; +use winit::{ + dpi::LogicalSize, + event::{ElementState, Event, KeyEvent, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + keyboard::{Key, ModifiersState}, + 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 == ElementState::Pressed && !event.repeat { + match event.key_without_modifiers() { + Key::Character("1") => { + if modifiers.shift_key() { + println!("Shift + 1 | logical_key: {:?}", event.logical_key); + } else { + println!("1"); + } + } + _ => (), + } + } +} diff --git a/examples/minimize.rs b/examples/minimize.rs index eb02a752c9..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, VirtualKeyCode, WindowEvent}; + +use winit::event::{Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; +use winit::keyboard::Key; use winit::window::WindowBuilder; fn main() { @@ -25,12 +27,14 @@ fn main() { // Keyboard input event to handle minimize via a hotkey Event::WindowEvent { - event: WindowEvent::KeyboardInput { 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) { + // 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 68cdb60b78..00235b114c 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::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, + keyboard::{Key, ModifiersState}, window::{CursorIcon, Fullscreen, WindowBuilder}, }; @@ -27,6 +28,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,32 +51,92 @@ fn main() { ); } } - #[allow(deprecated)] + WindowEvent::ModifiersChanged(mod_state) => { + modifiers = mod_state; + } WindowEvent::KeyboardInput { - input: - KeyboardInput { + event: + KeyEvent { state: ElementState::Released, - virtual_keycode: Some(key), - modifiers, + logical_key: key, .. }, .. } => { + use Key::{ArrowLeft, ArrowRight, Character}; 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), + let state = !modifiers.shift_key(); + 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 { + true => CursorIcon::Progress, + false => CursorIcon::Default, + }), + "d" => window.set_decorations(!state), + "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(), + )), + (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!( @@ -82,61 +144,6 @@ fn main() { 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 +162,10 @@ fn main() { WindowEvent::CloseRequested | WindowEvent::Destroyed | WindowEvent::KeyboardInput { - input: - KeyboardInput { + event: + KeyEvent { state: ElementState::Released, - virtual_keycode: Some(VirtualKeyCode::Escape), + logical_key: Key::Escape, .. }, .. diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index ec97eee097..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::{ElementState, Event, KeyboardInput, WindowEvent}, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::Window, }; @@ -34,9 +34,9 @@ fn main() { } } WindowEvent::KeyboardInput { - input: - KeyboardInput { - state: ElementState::Pressed, + event: + KeyEvent { + state: ElementState::Released, .. }, .. diff --git a/examples/resizable.rs b/examples/resizable.rs index 17892d8741..8d3387cabb 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -1,8 +1,9 @@ use simple_logger::SimpleLogger; use winit::{ dpi::LogicalSize, - event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, + keyboard::KeyCode, window::WindowBuilder, }; @@ -26,9 +27,9 @@ fn main() { Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(VirtualKeyCode::Space), + event: + KeyEvent { + physical_key: KeyCode::Space, state: ElementState::Released, .. }, diff --git a/examples/window_debug.rs b/examples/window_debug.rs index 577ad5cd73..4a12cf47b9 100644 --- a/examples/window_debug.rs +++ b/examples/window_debug.rs @@ -3,8 +3,9 @@ use simple_logger::SimpleLogger; use winit::{ dpi::{LogicalSize, PhysicalSize}, - event::{DeviceEvent, ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, + keyboard::{Key, KeyCode}, window::{Fullscreen, WindowBuilder}, }; @@ -34,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(KeyboardInput { - virtual_keycode: Some(key), - state: ElementState::Pressed, + DeviceEvent::Key(RawKeyEvent { + physical_key, + state: ElementState::Released, .. }), .. - } => match key { - VirtualKeyCode::M => { + } => match physical_key { + KeyCode::KeyM => { if minimized { minimized = !minimized; window.set_minimized(minimized); } } - VirtualKeyCode::V => { + KeyCode::KeyV => { if !visible { visible = !visible; window.set_visible(visible); @@ -58,61 +61,65 @@ fn main() { _ => (), }, Event::WindowEvent { - event: WindowEvent::KeyboardInput { input, .. }, + event: + WindowEvent::KeyboardInput { + event: + KeyEvent { + logical_key: Key::Character(key_str), + state: ElementState::Released, + .. + }, + .. + }, .. - } => 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 { + // 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 } - 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 => { - let is_maximized = window.is_maximized(); - window.set_maximized(!is_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" => { + let is_maximized = window.is_maximized(); + window.set_maximized(!is_maximized); + } _ => (), }, Event::WindowEvent { diff --git a/src/event.rs b/src/event.rs index f39ddaac50..6bd9009a0b 100644 --- a/src/event.rs +++ b/src/event.rs @@ -38,6 +38,7 @@ use std::path::PathBuf; use crate::{ dpi::{PhysicalPosition, PhysicalSize}, + keyboard::{self, ModifiersState}, platform_impl, window::{Theme, WindowId}, }; @@ -239,8 +240,12 @@ 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. + /// + /// [#1497]: https://github.com/rust-windowing/winit/issues/1497 + ReceivedImeText(String), /// The window gained or lost focus. /// @@ -248,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, - input: KeyboardInput, + event: KeyEvent, + /// If `true`, the event was generated synthetically by winit /// in one of the following circumstances: /// @@ -365,18 +376,17 @@ 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 { device_id, - input, + event, is_synthetic, } => KeyboardInput { device_id: *device_id, - input: *input, + event: event.clone(), is_synthetic: *is_synthetic, }, - ModifiersChanged(modifiers) => ModifiersChanged(modifiers.clone()), #[allow(deprecated)] CursorMoved { @@ -456,15 +466,15 @@ 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 { device_id, - input, + event, is_synthetic, } => Some(KeyboardInput { device_id, - input, + event, is_synthetic, }), ModifiersChanged(modifiers) => Some(ModifiersChanged(modifiers)), @@ -589,38 +599,82 @@ pub enum DeviceEvent { state: ElementState, }, - Key(KeyboardInput), + Key(RawKeyEvent), Text { codepoint: char, }, } -/// Describes a keyboard input event. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +/// 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 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, - +pub struct RawKeyEvent { + pub physical_key: keyboard::KeyCode, pub state: ElementState, +} - /// Identifies the semantic meaning of the key +/// 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. + /// + /// 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.) /// - /// 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, + /// 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, - /// Modifier keys active at the time of this input. + /// 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 [`key_without_modifiers`]. /// - /// 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, + /// ## 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. + /// + /// 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, } /// Describes touch-screen input state. @@ -721,9 +775,6 @@ impl Force { } } -/// Hardware-dependent keyboard scan code. -pub type ScanCode = u32; - /// Identifier for a specific analog axis on some device. pub type AxisId = u32; @@ -766,303 +817,3 @@ pub enum MouseScrollDelta { /// platform. 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 { - self.intersects(Self::SHIFT) - } - /// Returns `true` if the control key is pressed. - pub fn ctrl(&self) -> bool { - self.intersects(Self::CTRL) - } - /// 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::LOGO) - } -} - -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 CTRL = 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 LOGO = 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 new file mode 100644 index 0000000000..28983cfaf4 --- /dev/null +++ b/src/keyboard.rs @@ -0,0 +1,1417 @@ +//! 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 { + /// Returns `true` if the shift key is pressed. + pub fn shift_key(&self) -> bool { + self.intersects(Self::SHIFT) + } + /// Returns `true` if the control key is pressed. + pub fn control_key(&self) -> bool { + self.intersects(Self::CONTROL) + } + /// Returns `true` if the alt key is pressed. + pub fn alt_key(&self) -> bool { + self.intersects(Self::ALT) + } + /// Returns `true` if the super key is pressed. + pub fn super_key(&self) -> bool { + self.intersects(Self::SUPER) + } +} + +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 SUPER = 0b100 << 9; + // const LSUPER = 0b010 << 9; + // const RSUPER = 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_key: bool, + pub control_key: bool, + pub alt_key: bool, + pub super_key: bool, + } + + impl Serialize for ModifiersState { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = ModifiersStateSerialize { + shift_key: self.shift_key(), + control_key: self.control_key(), + alt_key: self.alt_key(), + super_key: self.super_key(), + }; + s.serialize(serializer) + } + } + + impl<'de> Deserialize<'de> for ModifiersState { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let ModifiersStateSerialize { + shift_key, + control_key, + alt_key, + super_key, + } = ModifiersStateSerialize::deserialize(deserializer)?; + let mut m = ModifiersState::empty(); + 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) + } + } +} + +/// Contains the platform-native physical key identifier (aka scancode) +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum NativeKeyCode { + Unidentified, + Windows(u16), + 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. +/// +/// 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 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 +#[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. + /// Labeled # 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. + /// Labeled \\ on a UK keyboard. + IntlBackslash, + /// Located between the / and right Shift keys. + /// Labeled \\ (ro) on a Japanese keyboard. + IntlRo, + /// Located between the = and Backspace keys. + /// Labeled ¥ (yen) on a Japanese keyboard. \\ on a + /// Russian keyboard. + IntlYen, + /// a on a US keyboard. + /// Labeled 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. + /// Labeled 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. + /// Labeled z on an AZERTY (e.g., French) keyboard. + KeyW, + /// x on a US keyboard. + KeyX, + /// y on a US keyboard. + /// Labeled z on a QWERTZ (e.g., German) keyboard. + KeyY, + /// z on a US keyboard. + /// Labeled 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 labeled AltGr on many keyboard layouts. + AltRight, + /// Backspace or . + /// Labeled Delete on Apple keyboards. + Backspace, + /// CapsLock or + CapsLock, + /// The application context menu key, which is typically found between the right + /// Super key and the right Control key. + ContextMenu, + /// Control or + ControlLeft, + /// Control or + ControlRight, + /// Enter or . Labeled Return on Apple keyboards. + Enter, + /// The Windows, , Command, or other OS symbol key. + SuperLeft, + /// The Windows, , Command, or other OS symbol key. + SuperRight, + /// 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 is encoded as [`Backspace`]. + /// + /// [`Backspace`]: Self::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, this is 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 is encoded as [`NumLock`]. + /// + /// [`NumLock`]: Self::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, + /// 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. + 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, + 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, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F1, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F2, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F3, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F4, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F5, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F6, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F7, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F8, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F9, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F10, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F11, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F12, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F13, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F14, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F15, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F16, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F17, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F18, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F19, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F20, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F21, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F22, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F23, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F24, + /// General-purpose function key. + F25, + /// General-purpose function key. + F26, + /// General-purpose function key. + F27, + /// General-purpose function key. + F28, + /// General-purpose function key. + F29, + /// General-purpose function key. + F30, + /// General-purpose function key. + F31, + /// General-purpose function key. + F32, + /// General-purpose function key. + F33, + /// General-purpose function key. + F34, + /// General-purpose function key. + F35, +} + +/// Key represents the meaning of a keypress. +/// +/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.key`] with a few +/// exceptions: +/// - 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. +/// - 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))] +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, 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). + AltGraph, + /// The `Caps Lock` (Capital) key. + /// + /// Toggle capital character lock function for interpreting subsequent keyboard input event. + CapsLock, + /// 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. + 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 `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, + /// Used to enable shift modifier function for interpreting concurrent or subsequent keyboard + /// input. + Shift, + /// The Symbol modifier key (used on some virtual keyboards). + 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 + /// the Android `KEYCODE_DPAD_CENTER`. + 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`) + ArrowLeft, + /// Navigate or traverse rightward. (`KEYCODE_DPAD_RIGHT`) + ArrowRight, + /// 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`]. + /// + /// [`GoHome`]: Self::GoHome + Home, + /// Scroll down or display next page of content. + PageDown, + /// Scroll up or display previous page of content. + PageUp, + /// 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 key. + CrSel, + /// Cut the current selection. (`APPCOMMAND_CUT`) + Cut, + /// 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. + EraseEof, + /// The Extend Selection (Exsel) key. + ExSel, + /// 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, + /// Redo or repeat an action. + Again, + /// The Attention (Attn) key. + Attn, + Cancel, + /// Show the application’s context menu. + /// 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 + /// or exiting full screen mode. + Escape, + 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). + /// + /// 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). + /// + /// Note: Do not use this value for the `Play` button on media controllers. Use `"MediaPlay"` + /// instead. + Play, + /// The properties (Props) key. + Props, + 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, + LogOff, + /// Toggle power state. (`KEYCODE_POWER`) + /// Note: Note: Some devices might not expose this key to the operating environment. + Power, + /// The `PowerOff` key. Sometime called `PowerDown`. + PowerOff, + /// 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, + /// Initate the multi-candidate mode. + AllCandidates, + Alphanumeric, + /// 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, + /// 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, + /// Toggle between or cycle through input modes of IMEs. + ModeChange, + NextCandidate, + /// Accept current input method sequence without + /// conversion in IMEs. + NonConvert, + PreviousCandidate, + Process, + SingleCandidate, + /// Toggle between Hangul and English modes. + HangulMode, + HanjaMode, + 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`) + /// + /// 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`) + 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, + LaunchMusicPlayer, + LaunchPhone, + LaunchScreenSaver, + LaunchSpreadsheet, + LaunchWebBrowser, + LaunchWebCam, + 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, + LastNumberRedial, + /// The Notification key. (`KEYCODE_NOTIFICATION`) + Notification, + /// Toggle between manner mode state: silent, vibrate, ring, ... (`KEYCODE_MANNER_MODE`) + MannerMode, + 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`) + /// + /// 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, + /// 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, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F1, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F2, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F3, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F4, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F5, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F6, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F7, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F8, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F9, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F10, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F11, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F12, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F13, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F14, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F15, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F16, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F17, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F18, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F19, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F20, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F21, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F22, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F23, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F24, + /// General-purpose function key. + F25, + /// General-purpose function key. + F26, + /// General-purpose function key. + F27, + /// General-purpose function key. + F28, + /// General-purpose function key. + F29, + /// General-purpose function key. + F30, + /// General-purpose function key. + F31, + /// General-purpose function key. + F32, + /// General-purpose function key. + F33, + /// General-purpose function key. + F34, + /// General-purpose function key. + 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::Space => Some(" "), + Key::Escape => Some("\x1b"), + _ => None, + } + } +} + +#[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/mod.rs b/src/platform/mod.rs index 1ee5fce2bb..84f016c2d2 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -21,5 +21,7 @@ pub mod macos; pub mod unix; pub mod windows; +pub mod modifier_supplement; pub mod run_return; +pub mod scancode; pub mod web; diff --git a/src/platform/modifier_supplement.rs b/src/platform/modifier_supplement.rs new file mode 100644 index 0000000000..361ed05d04 --- /dev/null +++ b/src/platform/modifier_supplement.rs @@ -0,0 +1,32 @@ +#![cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] + +use crate::keyboard::Key; + +/// Additional methods for the `KeyEvent` which cannot be implemented on all +/// platforms. +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_modifiers(&self) -> Option<&str>; + + /// 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 + /// 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 new file mode 100644 index 0000000000..078fe74dea --- /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 dd58d758cd..f163a200bf 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -4,14 +4,24 @@ use std::os::raw::c_void; use std::path::Path; use libc; -use winapi::shared::minwindef::WORD; -use winapi::shared::windef::{HMENU, HWND}; +use winapi::{ + shared::{ + minwindef::{LOWORD, WORD}, + windef::{HMENU, HWND}, + }, + um::{ + winnt::{LANG_KOREAN, PRIMARYLANGID}, + winuser::GetKeyboardLayout, + }, +}; use crate::{ dpi::PhysicalSize, - event::DeviceId, + event::{DeviceId, KeyEvent}, event_loop::EventLoop, + keyboard::{Key, KeyCode, NativeKeyCode}, monitor::MonitorHandle, + platform::{modifier_supplement::KeyEventExtModifierSupplement, scancode::KeyCodeExtScancode}, platform_impl::{EventLoop as WindowsEventLoop, WinIcon}, window::{BadIcon, Icon, Theme, Window, WindowBuilder}, }; @@ -250,3 +260,349 @@ impl IconExtWindows for Icon { Ok(Icon { inner: win_icon }) } } + +impl KeyEventExtModifierSupplement for KeyEvent { + #[inline] + fn text_with_all_modifiers(&self) -> Option<&str> { + self.platform_specific.text_with_all_modifers + } + + #[inline] + fn key_without_modifiers(&self) -> Key<'static> { + self.platform_specific.key_without_modifiers + } +} + +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::SuperLeft => Some(0xE05B), + KeyCode::SuperRight => 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), + KeyCode::Unidentified(NativeKeyCode::Windows(scancode)) => Some(scancode as u32), + _ => 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::SuperLeft, + 0xE05C => KeyCode::SuperRight, + 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.rs b/src/platform_impl/windows/event.rs deleted file mode 100644 index 161cf6b2de..0000000000 --- a/src/platform_impl/windows/event.rs +++ /dev/null @@ -1,417 +0,0 @@ -use std::{ - char, - os::raw::c_int, - ptr, - sync::atomic::{AtomicBool, AtomicPtr, Ordering}, -}; - -use crate::event::{ModifiersState, ScanCode, VirtualKeyCode}; - -use winapi::{ - shared::minwindef::{HKL, HKL__, LPARAM, UINT, WPARAM}, - um::winuser, -}; - -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::CTRL, - key_pressed(winuser::VK_CONTROL) && !filter_out_altgr, - ); - mods.set( - ModifiersState::ALT, - key_pressed(winuser::VK_MENU) && !filter_out_altgr, - ); - mods.set( - ModifiersState::LOGO, - 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::CTRL, - side.intersects(ModifiersStateSide::LCTRL | ModifiersStateSide::RCTRL), - ); - state.set( - Self::ALT, - side.intersects(ModifiersStateSide::LALT | ModifiersStateSide::RALT), - ); - state.set( - Self::LOGO, - side.intersects(ModifiersStateSide::LLOGO | ModifiersStateSide::RLOGO), - ); - state - } -} - -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( - 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 - } -} - -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, 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 5b49cbe809..b9726a4e2c 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, WORD, WPARAM}, windef::{HWND, POINT, RECT}, @@ -27,20 +28,24 @@ use winapi::{ um::{ commctrl, libloaderapi, ole2, processthreadsapi, winbase, winnt::{HANDLE, LONG, LPCSTR, SHORT}, - winuser, + winuser::{self, RAWINPUT}, }, }; use crate::{ dpi::{PhysicalPosition, PhysicalSize}, - event::{DeviceEvent, Event, Force, KeyboardInput, Touch, TouchPhase, WindowEvent}, + event::{DeviceEvent, Event, Force, RawKeyEvent, Touch, TouchPhase, WindowEvent}, event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}, + 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, - event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, + keyboard::is_msg_keyboard_related, + keyboard_layout::LAYOUT_CACHE, + minimal_ime::is_msg_ime_related, monitor::{self, MonitorHandle}, raw_input, util, window_state::{CursorFlags, WindowFlags, WindowState}, @@ -108,6 +113,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, @@ -745,10 +757,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; @@ -763,6 +780,7 @@ fn update_modifiers(window: HWND, subclass_input: &SubclassInput) { }); } } + modifiers } /// Any window whose callback is configured to this function will have its events propagated @@ -819,6 +837,75 @@ unsafe fn public_window_callback_inner( winuser::RDW_INTERNALPAINT, ); + 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); + result = ProcResult::Value(0); + } + _ => (), + }; + subclass_input + .event_loop_runner + .catch_unwind(mods_changed_callback) + .unwrap_or_else(|| result = ProcResult::Value(-1)); + + 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; + } + let events = { + let mut window_state = subclass_input.window_state.lock(); + window_state + .key_event_builder + .process_message(window, msg, wparam, lparam, &mut result) + }; + 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, + }, + }); + } + }; + subclass_input + .event_loop_runner + .catch_unwind(keyboard_callback) + .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; + } + let text = { + let mut window_state = subclass_input.window_state.lock(); + window_state + .ime_handler + .process_message(window, msg, wparam, lparam, &mut result) + }; + if let Some(str) = text { + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: ReceivedImeText(str), + }); + } + }; + subclass_input + .event_loop_runner + .catch_unwind(ime_callback) + .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 // the git blame and history would be preserved. @@ -828,7 +915,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 => { @@ -836,18 +923,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 => { @@ -856,7 +941,7 @@ unsafe fn public_window_callback_inner( window_id: RootWindowId(WindowId(window)), event: CloseRequested, }); - 0 + result = ProcResult::Value(0); } winuser::WM_DESTROY => { @@ -867,13 +952,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 => { @@ -895,8 +980,6 @@ unsafe fn public_window_callback_inner( process_control_flow(&subclass_input.event_loop_runner); } } - - commctrl::DefSubclassProc(window, msg, wparam, lparam) } winuser::WM_WINDOWPOSCHANGING => { @@ -944,7 +1027,7 @@ unsafe fn public_window_callback_inner( } } - 0 + result = ProcResult::Value(0); } // WM_MOVE supplies client area positions, so we send Moved here instead. @@ -962,7 +1045,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 => { @@ -989,40 +1072,7 @@ unsafe fn public_window_callback_inner( } subclass_input.send_event(event); - 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 + result = ProcResult::Value(0); } // this is necessary for us to maintain minimize/restore state @@ -1040,11 +1090,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 => { @@ -1089,19 +1140,18 @@ unsafe fn public_window_callback_inner( 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, }, }); } - 0 + result = ProcResult::Value(0); } winuser::WM_MOUSELEAVE => { @@ -1120,7 +1170,7 @@ unsafe fn public_window_callback_inner( }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_MOUSEWHEEL => { @@ -1130,7 +1180,7 @@ unsafe fn public_window_callback_inner( 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)), @@ -1138,11 +1188,11 @@ unsafe fn public_window_callback_inner( device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved, - modifiers: event::get_key_mods(), + modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_MOUSEHWHEEL => { @@ -1152,7 +1202,7 @@ unsafe fn public_window_callback_inner( 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)), @@ -1160,77 +1210,25 @@ unsafe fn public_window_callback_inner( device_id: DEVICE_ID, delta: LineDelta(value, 0.0), phase: TouchPhase::Moved, - modifiers: event::get_key_mods(), + modifiers, }, }); - 0 + result = ProcResult::Value(0); } 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 { - 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}'), - }); - } - } - 0 + result = ProcResult::DefSubclassProc; } } - 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 { - 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}; 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)), @@ -1238,10 +1236,10 @@ unsafe fn public_window_callback_inner( device_id: DEVICE_ID, state: Pressed, button: Left, - modifiers: event::get_key_mods(), + modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_LBUTTONUP => { @@ -1251,7 +1249,7 @@ unsafe fn public_window_callback_inner( release_mouse(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)), @@ -1259,10 +1257,10 @@ unsafe fn public_window_callback_inner( device_id: DEVICE_ID, state: Released, button: Left, - modifiers: event::get_key_mods(), + modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_RBUTTONDOWN => { @@ -1272,7 +1270,7 @@ unsafe fn public_window_callback_inner( 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)), @@ -1280,10 +1278,10 @@ unsafe fn public_window_callback_inner( device_id: DEVICE_ID, state: Pressed, button: Right, - modifiers: event::get_key_mods(), + modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_RBUTTONUP => { @@ -1293,7 +1291,7 @@ unsafe fn public_window_callback_inner( release_mouse(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)), @@ -1301,10 +1299,10 @@ unsafe fn public_window_callback_inner( device_id: DEVICE_ID, state: Released, button: Right, - modifiers: event::get_key_mods(), + modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_MBUTTONDOWN => { @@ -1314,7 +1312,7 @@ unsafe fn public_window_callback_inner( 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)), @@ -1322,10 +1320,10 @@ unsafe fn public_window_callback_inner( device_id: DEVICE_ID, state: Pressed, button: Middle, - modifiers: event::get_key_mods(), + modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_MBUTTONUP => { @@ -1335,7 +1333,7 @@ unsafe fn public_window_callback_inner( release_mouse(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)), @@ -1343,10 +1341,10 @@ unsafe fn public_window_callback_inner( device_id: DEVICE_ID, state: Released, button: Middle, - modifiers: event::get_key_mods(), + modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_XBUTTONDOWN => { @@ -1357,7 +1355,7 @@ unsafe fn public_window_callback_inner( 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)), @@ -1365,10 +1363,10 @@ unsafe fn public_window_callback_inner( device_id: DEVICE_ID, state: Pressed, button: Other(xbutton), - modifiers: event::get_key_mods(), + modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_XBUTTONUP => { @@ -1379,7 +1377,7 @@ unsafe fn public_window_callback_inner( release_mouse(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)), @@ -1387,10 +1385,10 @@ unsafe fn public_window_callback_inner( device_id: DEVICE_ID, state: Released, button: Other(xbutton), - modifiers: event::get_key_mods(), + modifiers, }, }); - 0 + result = ProcResult::Value(0); } winuser::WM_CAPTURECHANGED => { @@ -1401,7 +1399,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 => { @@ -1450,7 +1448,7 @@ unsafe fn public_window_callback_inner( } } winuser::CloseTouchInputHandle(htouch); - 0 + result = ProcResult::Value(0); } winuser::WM_POINTERDOWN | winuser::WM_POINTERUPDATE | winuser::WM_POINTERUP => { @@ -1473,7 +1471,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; @@ -1486,7 +1485,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 @@ -1590,68 +1590,23 @@ unsafe fn public_window_callback_inner( SkipPointerFrameMessages(pointer_id); } - 0 + result = ProcResult::Value(0); } 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 { - 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)), event: Focused(true), }); - 0 + result = ProcResult::Value(0); } 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 { - device_id: DEVICE_ID, - input: KeyboardInput { - scancode, - virtual_keycode, - state: Released, - modifiers: event::get_key_mods(), - }, - is_synthetic: true, - }, - }) - } + use crate::event::WindowEvent::{Focused, ModifiersChanged}; subclass_input.window_state.lock().modifiers_state = ModifiersState::empty(); subclass_input.send_event(Event::WindowEvent { @@ -1663,7 +1618,7 @@ unsafe fn public_window_callback_inner( window_id: RootWindowId(WindowId(window)), event: Focused(false), }); - 0 + result = ProcResult::Value(0); } winuser::WM_SETCURSOR => { @@ -1684,17 +1639,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; @@ -1719,7 +1669,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 @@ -1741,7 +1691,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() @@ -1936,7 +1887,7 @@ unsafe fn public_window_callback_inner( winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE, ); - 0 + result = ProcResult::Value(0); } winuser::WM_SETTINGCHANGE => { @@ -1957,22 +1908,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 { - commctrl::DefSubclassProc(window, msg, wparam, lparam) + result = ProcResult::Value(0); } } }; @@ -1980,7 +1927,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( @@ -2062,101 +2015,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 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(KeyboardInput { - scancode, - state, - virtual_keycode, - modifiers: event::get_key_mods(), - }), - }); - } - } - } + handle_raw_input(&subclass_input, data); } commctrl::DefSubclassProc(window, msg, wparam, lparam) @@ -2228,3 +2088,184 @@ 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) { + // 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), + }, + }); + } + + 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 || 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 emit 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 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 + // + // 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); + } + 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; + } + _ => (), + } + } + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Key(RawKeyEvent { + physical_key: code, + state, + }), + }); + } +} diff --git a/src/platform_impl/windows/keyboard.rs b/src/platform_impl/windows/keyboard.rs new file mode 100644 index 0000000000..00235ed018 --- /dev/null +++ b/src/platform_impl/windows/keyboard.rs @@ -0,0 +1,802 @@ +use std::{ + char, collections::HashSet, ffi::OsString, mem::MaybeUninit, os::raw::c_int, + os::windows::ffi::OsStringExt, sync::MutexGuard, +}; + +use winapi::{ + shared::{ + minwindef::{HKL, LPARAM, UINT, WPARAM}, + windef::HWND, + }, + um::winuser, +}; + +use unicode_segmentation::UnicodeSegmentation; + +use crate::{ + event::{ElementState, KeyEvent}, + 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, + }, +}; + +pub fn is_msg_keyboard_related(msg: u32) -> bool { + use winuser::{WM_KEYFIRST, WM_KEYLAST, WM_KILLFOCUS, WM_SETFOCUS}; + let is_keyboard_msg = WM_KEYFIRST <= msg && msg <= WM_KEYLAST; + + is_keyboard_msg || msg == WM_SETFOCUS || msg == WM_KILLFOCUS +} + +pub type ExScancode = u16; + +pub struct MessageAsKeyEvent { + pub event: KeyEvent, + pub is_synthetic: bool, +} + +/// Stores information required to make `KeyEvent`s. +/// +/// 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 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, 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. +/// +/// 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, +} +impl Default for KeyEventBuilder { + fn default() -> Self { + KeyEventBuilder { event_info: None } + } +} +impl KeyEventBuilder { + /// Call this function for every window message. + /// Returns Some() if this window message completes a KeyEvent. + /// Returns None otherwise. + pub(crate) fn process_message( + &mut self, + hwnd: HWND, + msg_kind: u32, + wparam: WPARAM, + lparam: LPARAM, + result: &mut ProcResult, + ) -> Vec { + match msg_kind { + winuser::WM_SETFOCUS => { + // synthesize keydown events + 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 kbd_state = get_kbd_state(); + let key_events = self.synthesize_kbd_state(ElementState::Released, &kbd_state); + if !key_events.is_empty() { + return key_events; + } + } + 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![]; + } + *result = ProcResult::Value(0); + + let mut layouts = LAYOUT_CACHE.lock().unwrap(); + let event_info = PartialKeyEventInfo::from_message( + wparam, + lparam, + ElementState::Pressed, + &mut layouts, + ); + + 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; + 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() }; + let next_msg_kind = next_msg.message; + let next_belongs_to_this = !matches!( + next_msg_kind, + winuser::WM_KEYDOWN + | winuser::WM_SYSKEYDOWN + | winuser::WM_KEYUP + | winuser::WM_SYSKEYUP + ); + if next_belongs_to_this { + 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) = finished_event_info { + let ev = event_info.finalize(&mut layouts.strings); + return vec![MessageAsKeyEvent { + event: ev, + is_synthetic: false, + }]; + } + } + winuser::WM_DEADCHAR | winuser::WM_SYSDEADCHAR => { + *result = ProcResult::Value(0); + // 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(); + let mut layouts = LAYOUT_CACHE.lock().unwrap(); + let ev = event_info.finalize(&mut layouts.strings); + return vec![MessageAsKeyEvent { + event: ev, + is_synthetic: false, + }]; + } + 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; + + 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 { + 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 = 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); + 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 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. + 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. Otherwise, + // we would interpret the character incorrectly, 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 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, num_lock_on, vkey, scancode, keycode); + event_info.text = PartialText::Text(key.to_text()); + } + let ev = event_info.finalize(&mut layouts.strings); + return vec![MessageAsKeyEvent { + event: ev, + is_synthetic: false, + }]; + } + } + winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { + *result = ProcResult::Value(0); + + let mut layouts = LAYOUT_CACHE.lock().unwrap(); + let event_info = PartialKeyEventInfo::from_message( + wparam, + lparam, + ElementState::Released, + &mut layouts, + ); + 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, + }]; + } + } + _ => (), + } + + Vec::new() + } + + 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(); + + macro_rules! is_key_pressed { + ($vk:expr) => { + kbd_state[$vk as usize] & 0x80 != 0 + }; + } + + // 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 + // 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 + // to determine the produced text + if is_key_pressed!(winuser::VK_CAPITAL) { + let event = self.create_synthetic( + winuser::VK_CAPITAL, + key_state, + caps_lock_on, + num_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<_>, layouts: &mut _| { + 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; + } + 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); + } + } + }; + let do_modifier = |key_events: &mut Vec<_>, layouts: &mut _| { + 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, + caps_lock_on, + num_lock_on, + locale_id as HKL, + layouts, + ); + if let Some(event) = event { + key_events.push(event); + } + } + } + }; + + // 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 { + ElementState::Pressed => { + do_non_modifier(&mut key_events, &mut layouts); + do_modifier(&mut key_events, &mut layouts); + } + ElementState::Released => { + do_modifier(&mut key_events, &mut layouts); + do_non_modifier(&mut key_events, &mut layouts); + } + } + + key_events + } + + fn create_synthetic( + &self, + vk: i32, + key_state: ElementState, + caps_lock_on: bool, + num_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) + }; + if scancode == 0 { + return None; + } + let scancode = scancode as ExScancode; + let code = KeyCode::from_scancode(scancode as u32); + let mods = if caps_lock_on { + WindowsModifiers::CAPS_LOCK + } else { + WindowsModifiers::empty() + }; + 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(); + } else { + text = None; + } + let event_info = PartialKeyEventInfo { + vkey: vk, + logical_key: PartialLogicalKey::This(logical_key), + key_without_modifiers, + key_state, + scancode, + is_repeat: false, + code, + location: get_location(scancode, locale_id), + utf16parts: Vec::with_capacity(8), + 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 = text; + Some(MessageAsKeyEvent { + event, + is_synthetic: true, + }) + } +} + +enum PartialText { + // Unicode + System(Vec), + Text(Option<&'static str>), +} + +enum PartialLogicalKey { + /// 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>), +} + +struct PartialKeyEventInfo { + vkey: c_int, + scancode: ExScancode, + key_state: ElementState, + is_repeat: bool, + code: KeyCode, + location: KeyLocation, + logical_key: PartialLogicalKey, + + key_without_modifiers: Key<'static>, + + /// 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, + + text: PartialText, +} + +impl PartialKeyEventInfo { + fn from_message( + wparam: WPARAM, + lparam: LPARAM, + state: ElementState, + layouts: &mut MutexGuard<'_, LayoutCache>, + ) -> Self { + const NO_MODS: WindowsModifiers = WindowsModifiers::empty(); + + let (_, layout) = layouts.get_current_layout(); + let lparam_struct = destructure_key_lparam(lparam); + let scancode; + 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. + 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); + } + let code = KeyCode::from_scancode(scancode as u32); + let location = get_location(scancode, layout.hkl as HKL); + + 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 + // 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, num_lock_on, vkey, scancode, code); + let key_is_char = matches!(preliminary_logical_key, Key::Character(_)); + let is_pressed = state == ElementState::Pressed; + + 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::TextOr(preliminary_logical_key) + } else { + PartialLogicalKey::This(preliminary_logical_key) + }; + let key_without_modifiers = if let Some(key) = code_as_key { + key + } else { + 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 + // 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 :( + 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, + } + }; + + PartialKeyEventInfo { + vkey, + scancode, + key_state: state, + logical_key, + key_without_modifiers, + is_repeat: lparam_struct.is_repeat, + code, + location, + utf16parts: Vec::with_capacity(8), + text: PartialText::System(Vec::new()), + } + } + + 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 = get_or_insert_str(strings, string); + char_with_all_modifiers = Some(static_str); + } + } + + // 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; + } + } + + let logical_key = match self.logical_key { + 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, + }; + + KeyEvent { + physical_key: self.code, + logical_key, + text, + location: self.location, + state: self.key_state, + repeat: self.is_repeat, + platform_specific: KeyEventExtra { + text_with_all_modifers: char_with_all_modifiers, + key_without_modifiers: self.key_without_modifiers, + }, + } + } +} + +#[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 { + scancode: ((lparam >> 16) & 0xFF) as u8, + extended: ((lparam >> 24) & 0x01) != 0, + is_repeat: (previous_state ^ transition_state) != 0, + } +} + +#[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; + + 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` GUI library, specifically + // druid-shell/src/platform/windows/keyboard.rs + match vkey { + 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 { + KeyLocation::Standard + } else { + 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 => KeyLocation::Numpad, + _ => KeyLocation::Standard, + } +} diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs new file mode 100644 index 0000000000..3f1100b9b4 --- /dev/null +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -0,0 +1,993 @@ +use std::{ + collections::{hash_map::Entry, HashMap, HashSet}, + ffi::OsString, + os::windows::ffi::OsStringExt, + sync::Mutex, +}; + +use lazy_static::lazy_static; + +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::ExScancode, +}; + +lazy_static! { + 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, +]; + +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; + 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_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] |= 0x01; + } else { + key_state[winuser::VK_CAPITAL as usize] &= !0x01; + } + } + + /// 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(crate) struct Layout { + pub hkl: u64, + + /// Maps numpad keys from Windows virtual key to a `Key`. + /// + /// 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, + /// 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, +} + +impl Layout { + pub fn get_key( + &self, + mods: WindowsModifiers, + num_lock_on: bool, + vkey: c_int, + scancode: ExScancode, + keycode: KeyCode, + ) -> Key<'static> { + let native_code = NativeKeyCode::Windows(scancode); + + 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 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) { + return *key; + } + } + Key::Unidentified(native_code) + } +} + +#[derive(Default)] +pub(crate) struct LayoutCache { + /// Maps locale identifiers (HKL) to layouts + pub layouts: HashMap, + pub strings: HashSet<&'static str>, +} + +impl LayoutCache { + /// 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<'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.into_mut()), + Entry::Vacant(entry) => { + let layout = Self::prepare_layout(&mut self.strings, locale_id); + (locale_id, entry.insert(layout)) + } + } + } + + 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::SUPER, + 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 { + hkl: locale_id, + numlock_on_keys: Default::default(), + numlock_off_keys: Default::default(), + 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]; + + // `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 { + 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 + .numlock_on_keys + .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 { + let mut keys_for_this_mod = HashMap::with_capacity(256); + + let mod_state = unsafe { WindowsModifiers::from_bits_unchecked(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 + // 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_id as HKL) + }; + if scancode == 0 { + continue; + } + + let native_code = NativeKeyCode::Windows(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 + // "AltGr" in case we find out that there's an AltGraph. + let preliminary_key = + vkey_to_non_char_key(vk as i32, native_code, locale_id, 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_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) => { + //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); + // 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 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; + 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 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); + } + + // Second pass: replace right alt keys with AltGr if the layout has alt graph + if layout.has_alt_graph { + 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; + } + } + } + } + + layout + } + + fn to_unicode_string( + key_state: &[u8; 256], + vkey: u32, + scancode: u32, + locale_id: 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_id as HKL, + ); + if wide_len < 0 { + // If it's dead, we 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_id as HKL, + ); + 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 + } +} + +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_ref(); + if let Some(&existing) = strings.get(str_ref) { + return existing; + } + } + let leaked = Box::leak(Box::from(String::from(string))); + strings.insert(leaked); + leaked +} + +#[derive(Debug, Clone, Eq, PartialEq)] +enum ToUnicodeResult { + Str(String), + Dead(Option), + 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, + } +} + +/// 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 certain keys, like language-specific character keys. +/// +/// The result includes all non-character keys defined within `Key` plus characters from numpad keys. +/// For example, backspace and tab are included. +fn vkey_to_non_char_key( + vkey: i32, + native_code: NativeKeyCode, + 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 + + 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::Space, + 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, + + // 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), + 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/minimal_ime.rs b/src/platform_impl/windows/minimal_ime.rs new file mode 100644 index 0000000000..cbf754eecc --- /dev/null +++ b/src/platform_impl/windows/minimal_ime.rs @@ -0,0 +1,93 @@ +use std::mem::MaybeUninit; + +use winapi::{ + shared::{ + 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 + | 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 we're currently receiving messages belonging to a 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(crate) fn process_message( + &mut self, + hwnd: HWND, + msg_kind: u32, + wparam: WPARAM, + _lparam: LPARAM, + result: &mut ProcResult, + ) -> Option { + match msg_kind { + winuser::WM_IME_ENDCOMPOSITION => { + self.getting_ime_text = true; + } + winuser::WM_CHAR | winuser::WM_SYSCHAR => { + if self.getting_ime_text { + *result = ProcResult::Value(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).ok(); + self.utf16parts.clear(); + self.getting_ime_text = false; + return result; + } + } + } + _ => (), + } + + None + } +} diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index d748e6f01f..a72099db88 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -13,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)] @@ -75,6 +76,12 @@ fn wrap_device_id(id: u32) -> RootDeviceId { 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_modifiers: Key<'static>, +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct WindowId(HWND); unsafe impl Send for WindowId {} @@ -93,9 +100,11 @@ mod util; mod dark_mode; mod dpi; mod drop_handler; -mod event; mod event_loop; mod icon; +mod keyboard; +mod keyboard_layout; +mod minimal_ime; mod monitor; mod raw_input; mod window; diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index cc04b8f790..e56a472a02 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::{ @@ -661,6 +662,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 = 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( + vk, + 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/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 9ef5523239..19587d2880 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -1,8 +1,10 @@ use crate::{ dpi::{PhysicalPosition, Size}, - event::ModifiersState, icon::Icon, - platform_impl::platform::{event_loop, util}, + keyboard::ModifiersState, + platform_impl::platform::{ + event_loop, keyboard::KeyEventBuilder, minimal_ime::MinimalIme, util, + }, window::{CursorIcon, Fullscreen, Theme, WindowAttributes}, }; use parking_lot::MutexGuard; @@ -34,6 +36,10 @@ pub struct WindowState { pub current_theme: Theme, pub preferred_theme: Option, pub high_surrogate: Option, + + pub key_event_builder: KeyEventBuilder, + pub ime_handler: MinimalIme, + pub window_flags: WindowFlags, } @@ -121,6 +127,8 @@ impl WindowState { current_theme, preferred_theme, high_surrogate: None, + key_event_builder: KeyEventBuilder::default(), + ime_handler: MinimalIme::default(), window_flags: WindowFlags::empty(), } } diff --git a/src/window.rs b/src/window.rs index 052f5746e0..efa9ffa403 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. diff --git a/tests/serde_objects.rs b/tests/serde_objects.rs index ad729dcd1b..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, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase, - VirtualKeyCode, - }, + event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase}, + keyboard::{Key, KeyCode, KeyLocation, ModifiersState}, window::CursorIcon, }; @@ -20,12 +18,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 6d7cf96569c1dff0302dd6a8e35c81fbfc0bee34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Sun, 25 Apr 2021 18:55:46 +0200 Subject: [PATCH 091/131] Address compilation errors --- Cargo.toml | 4 ++-- src/platform_impl/linux/common/keymap.rs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b80715b504..3fe68b1a63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ targets = ["i686-pc-windows-msvc", "x86_64-pc-windows-msvc", "i686-unknown-linux default = ["x11", "wayland"] web-sys = ["web_sys", "wasm-bindgen", "instant/wasm-bindgen"] stdweb = ["std_web", "instant/stdweb"] -x11 = ["x11-dl", "mio", "mio-extras", "percent-encoding", "parking_lot"] +x11 = ["x11-dl", "mio", "mio-extras", "percent-encoding", "parking_lot", "xkbcommon-dl/x11"] wayland = ["wayland-client", "sctk", "memmap2"] [dependencies] @@ -95,7 +95,7 @@ x11-dl = { version = "2.18.5", optional = true } percent-encoding = { version = "2.0", optional = true } parking_lot = { version = "0.11.0", optional = true } memmap2 = { version = "0.2.1", optional = true } -xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "f335e626c" } +xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "5307184" } [target.'cfg(target_arch = "wasm32")'.dependencies.web_sys] package = "web-sys" diff --git a/src/platform_impl/linux/common/keymap.rs b/src/platform_impl/linux/common/keymap.rs index dbd0431679..4f72adc8f9 100644 --- a/src/platform_impl/linux/common/keymap.rs +++ b/src/platform_impl/linux/common/keymap.rs @@ -423,8 +423,9 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { keysyms::XKB_KEY_Caps_Lock => Key::CapsLock, // keysyms::XKB_KEY_Shift_Lock => Key::ShiftLock, // - keysyms::XKB_KEY_Meta_L => Key::Meta, - keysyms::XKB_KEY_Meta_R => Key::Meta, + // TODO: Address how renaming `Meta` to `Super` affects this set of keys. + // keysyms::XKB_KEY_Meta_L => Key::Meta, + // keysyms::XKB_KEY_Meta_R => Key::Meta, keysyms::XKB_KEY_Alt_L => Key::Alt, keysyms::XKB_KEY_Alt_R => Key::Alt, keysyms::XKB_KEY_Super_L => Key::Super, From 47f936b5185c9c51a7c6a4c5cbd68d9fd558b9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Wed, 28 Apr 2021 17:33:41 +0200 Subject: [PATCH 092/131] Hook up X11 XKB modifier keys --- src/platform_impl/linux/common/xkb_state.rs | 10 +++--- .../linux/wayland/seat/keyboard/handlers.rs | 2 +- .../linux/x11/event_processor.rs | 24 +++++++++++++- src/platform_impl/linux/x11/mod.rs | 31 +++++++++++++++++++ 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 59e03146f4..ce85501ee4 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -116,7 +116,9 @@ impl KbState { mods_depressed: u32, mods_latched: u32, mods_locked: u32, - group: u32, + depressed_group: u32, + latched_group: u32, + locked_group: u32, ) { if !self.ready() { return; @@ -127,9 +129,9 @@ impl KbState { mods_depressed, mods_latched, mods_locked, - 0, - 0, - group, + depressed_group, + latched_group, + locked_group, ) }; if mask.contains(xkb_state_component::XKB_STATE_MODS_EFFECTIVE) { diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs index 9a6d704055..d167004c6f 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs @@ -616,7 +616,7 @@ impl KbdHandler { ) { { let mut state = self.state.borrow_mut(); - state.update_modifiers(mods_depressed, mods_latched, mods_locked, group); + state.update_modifiers(mods_depressed, mods_latched, mods_locked, 0, 0, group); (&mut *self.callback.borrow_mut())( Event::Modifiers { modifiers: state.mods_state(), diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index f4da463772..459573604a 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc}; +use std::{cell::RefCell, collections::HashMap, convert::identity, rc::Rc, slice, sync::Arc}; use libc::{c_char, c_int, c_long, c_ulong}; @@ -31,6 +31,7 @@ pub(super) struct EventProcessor { pub(super) randr_event_offset: c_int, pub(super) devices: RefCell>, pub(super) xi2ext: XExtension, + pub(super) xkbext: XExtension, pub(super) target: Rc>, pub(super) kb_state: KbState, pub(super) mod_keymap: ModifierKeymap, @@ -1168,6 +1169,27 @@ impl EventProcessor { } } _ => { + if event_type == self.xkbext.first_event_id { + let xev = unsafe { &*(identity(xev) as *const _ as *const ffi::XkbAnyEvent) }; + match xev.xkb_type { + ffi::XkbStateNotify => { + let xev = + unsafe { &*(xev as *const _ as *const ffi::XkbStateNotifyEvent) }; + if matches!(xev.event_type, 2 | 3) { + // TODO: Is `as u32` fine here? + self.kb_state.update_modifiers( + xev.base_mods, + xev.latched_mods, + xev.locked_mods, + u32::from_ne_bytes(xev.base_group.to_ne_bytes()), + u32::from_ne_bytes(xev.latched_group.to_ne_bytes()), + u32::from_ne_bytes(xev.locked_group.to_ne_bytes()), + ) + } + } + _ => {} + } + } if event_type == self.randr_event_offset { // In the future, it would be quite easy to emit monitor hotplug events. let prev_list = monitor::invalidate_cached_monitor_list(); diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index f520c5f2de..825c1269a8 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -159,6 +159,27 @@ impl EventLoop { ext }; + let xkbext = { + let mut ext = XExtension::default(); + + let res = unsafe { + (xconn.xlib.XkbQueryExtension)( + xconn.display, + &mut ext.opcode, + &mut ext.first_event_id, + &mut ext.first_error_id, + &mut 1, + &mut 0, + ) + }; + + if res == ffi::False { + panic!("X server missing XKB extension"); + } + + ext + }; + unsafe { let mut xinput_major_ver = ffi::XI_2_Major; let mut xinput_minor_ver = ffi::XI_2_Minor; @@ -235,6 +256,7 @@ impl EventLoop { randr_event_offset, ime_receiver, xi2ext, + xkbext, kb_state, mod_keymap, device_mod_state: Default::default(), @@ -250,6 +272,15 @@ impl EventLoop { .select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask) .queue(); + get_xtarget(&target) + .xconn + .select_xkb_events( + 0x100, // Use the "core keyboard device" + ffi::XkbNewKeyboardNotifyMask | ffi::XkbMapNotifyMask | ffi::XkbStateNotifyMask, + ) + .unwrap() + .queue(); + event_processor.init_device(ffi::XIAllDevices); let result = EventLoop { From 299f0a8c1fd0a92b218f042112e57b8d2a58e798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Sat, 1 May 2021 19:56:51 +0200 Subject: [PATCH 093/131] Use XInput2 for regular key events --- .../linux/x11/event_processor.rs | 130 +++++++----------- src/platform_impl/linux/x11/mod.rs | 4 +- src/platform_impl/linux/x11/util/input.rs | 2 +- src/platform_impl/linux/x11/window.rs | 2 - 4 files changed, 56 insertions(+), 82 deletions(-) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 459573604a..56e6d09a07 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -548,81 +548,6 @@ impl EventProcessor { } } - ffi::KeyPress | ffi::KeyRelease => { - use crate::event::ElementState::{Pressed, Released}; - - // Note that in compose/pre-edit sequences, this will always be Released. - let state = if xev.get_type() == ffi::KeyPress { - Pressed - } else { - Released - }; - - let xkev: &mut ffi::XKeyEvent = xev.as_mut(); - - let window = xkev.window; - let window_id = mkwid(window); - - // Standard virtual core keyboard ID. XInput2 needs to be used to get a reliable - // value, though this should only be an issue under multiseat configurations. - let device = util::VIRTUAL_CORE_KEYBOARD; - let device_id = mkdid(device); - let keycode = xkev.keycode; - - // When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with - // a keycode of 0. - if keycode != 0 { - let keycode = keycode - KEYCODE_OFFSET as u32; - let mut ker = self.kb_state.process_key_event(keycode, state); - let physical_key = ker.keycode(); - let (logical_key, location) = ker.key(); - let text = ker.text(); - let (key_without_modifiers, _) = ker.key_without_modifiers(); - let text_with_all_modifiers = ker.text_with_all_modifiers(); - - update_modifiers!( - ModifiersState::from_x11_mask(xkev.state), - self.mod_keymap.get_modifier(xkev.keycode as ffi::KeyCode) - ); - - callback(Event::WindowEvent { - window_id, - event: WindowEvent::KeyboardInput { - device_id, - event: KeyEvent { - physical_key, - logical_key, - text, - location, - state, - repeat: false, - platform_specific: KeyEventExtra { - key_without_modifiers, - text_with_all_modifiers, - }, - }, - is_synthetic: false, - }, - }); - } - - if state == Pressed { - let written = if let Some(ic) = wt.ime.borrow().get_context(window) { - wt.xconn.lookup_utf8(ic, xkev) - } else { - return; - }; - - for chr in written.chars() { - // let event = Event::WindowEvent { - // window_id, - // event: WindowEvent::ReceivedCharacter(chr), - // }; - // callback(event); - } - } - } - ffi::GenericEvent => { let guard = if let Some(e) = GenericEventCookie::from_event(&wt.xconn, *xev) { e @@ -1092,6 +1017,56 @@ impl EventProcessor { } } + // The regular KeyPress event has a problem where if you press a dead key, a KeyPress + // event won't be emitted. XInput 2 does not have this problem. + ffi::XI_KeyPress | ffi::XI_KeyRelease => { + if let Some(active_window) = self.active_window { + let state = if xev.evtype == ffi::XI_KeyPress { + Pressed + } else { + Released + }; + + let xkev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; + + // We use `self.active_window` here as `xkev.event` has a completely different + // value for some reason. + let window_id = mkwid(active_window); + + let device_id = mkdid(xkev.deviceid); + let keycode = xkev.detail as u32; + + let keycode = keycode - KEYCODE_OFFSET as u32; + let mut ker = self.kb_state.process_key_event(keycode, state); + let physical_key = ker.keycode(); + let (logical_key, location) = ker.key(); + let text = ker.text(); + let (key_without_modifiers, _) = ker.key_without_modifiers(); + let text_with_all_modifiers = ker.text_with_all_modifiers(); + let repeat = xkev.flags & ffi::XIKeyRepeat == ffi::XIKeyRepeat; + + callback(Event::WindowEvent { + window_id, + event: WindowEvent::KeyboardInput { + device_id, + event: KeyEvent { + physical_key, + logical_key, + text, + location, + state, + repeat, + platform_specific: KeyEventExtra { + key_without_modifiers, + text_with_all_modifiers, + }, + }, + is_synthetic: false, + }, + }); + } + } + ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => { let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; @@ -1118,8 +1093,8 @@ impl EventProcessor { }), }); - // `ModifiersChanged` is dispatched here because we assume that every `KeyPress` is - // preceeded by a `RawKeyPress`. + // `ModifiersChanged` is dispatched here because we assume that every `XI_KeyPress` + // is preceeded by a `XI_RawKeyPress`. if let Some(modifier) = self.mod_keymap.get_modifier(keycode as ffi::KeyCode) { @@ -1273,7 +1248,6 @@ impl EventProcessor { F: FnMut(Event<'_, T>), { let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD); - let modifiers = device_mod_state.modifiers(); // Update modifiers state and emit key events based on which keys are currently pressed. for keycode in wt diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 825c1269a8..4d013c0415 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -671,7 +671,9 @@ impl Device { | ffi::XI_RawButtonPressMask | ffi::XI_RawButtonReleaseMask | ffi::XI_RawKeyPressMask - | ffi::XI_RawKeyReleaseMask; + | ffi::XI_RawKeyReleaseMask + | ffi::XI_KeyPressMask + | ffi::XI_KeyReleaseMask; // The request buffer is flushed when we poll for events wt.xconn .select_xinput_events(wt.root, info.deviceid, mask) diff --git a/src/platform_impl/linux/x11/util/input.rs b/src/platform_impl/linux/x11/util/input.rs index 6c7c651eed..db0f22eb03 100644 --- a/src/platform_impl/linux/x11/util/input.rs +++ b/src/platform_impl/linux/x11/util/input.rs @@ -81,12 +81,12 @@ impl XConnection { Flusher::new(self) } - #[allow(dead_code)] pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option> { let status = unsafe { (self.xlib.XkbSelectEvents)(self.display, device_id, mask, mask) }; if status == ffi::True { Some(Flusher::new(self)) } else { + // This should only happen if the XKB extension isn't initialized. None } } diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 0fa64cf164..b1b6455641 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -189,8 +189,6 @@ impl UnownedWindow { swa.event_mask = ffi::ExposureMask | ffi::StructureNotifyMask | ffi::VisibilityChangeMask - | ffi::KeyPressMask - | ffi::KeyReleaseMask | ffi::KeymapStateMask | ffi::ButtonPressMask | ffi::ButtonReleaseMask From bd03ac89e6a5cb4c4258b7960acb9bc2cbc857b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Fri, 7 May 2021 17:20:54 +0200 Subject: [PATCH 094/131] Implement dead-key handling --- src/platform_impl/linux/common/xkb_state.rs | 156 +++++++++++++++----- 1 file changed, 121 insertions(+), 35 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index ce85501ee4..7651d8370b 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -30,6 +30,7 @@ pub(crate) struct KbState { xkb_state: *mut ffi::xkb_state, xkb_compose_table: *mut ffi::xkb_compose_table, xkb_compose_state: *mut ffi::xkb_compose_state, + xkb_compose_state_2: *mut ffi::xkb_compose_state, mods_state: ModifiersState, locked: bool, scratch_buffer: Vec, @@ -174,27 +175,62 @@ impl KbState { Some(byte_slice_to_cached_string(&self.scratch_buffer)) } - pub(crate) fn compose_feed(&mut self, keysym: u32) -> Option { + fn compose_feed_normal(&mut self, keysym: u32) -> Option { + self.compose_feed(self.xkb_compose_state, keysym) + } + + fn compose_feed_2(&mut self, keysym: u32) -> Option { + self.compose_feed(self.xkb_compose_state_2, keysym) + } + + fn compose_feed( + &mut self, + xkb_compose_state: *mut ffi::xkb_compose_state, + keysym: u32, + ) -> Option { if !self.ready() || self.xkb_compose_state.is_null() { return None; } - Some(unsafe { (XKBCH.xkb_compose_state_feed)(self.xkb_compose_state, keysym) }) + Some(unsafe { (XKBCH.xkb_compose_state_feed)(xkb_compose_state, keysym) }) } - pub(crate) fn compose_status(&mut self) -> Option { - if !self.ready() || self.xkb_compose_state.is_null() { + fn compose_status_normal(&mut self) -> Option { + self.compose_status(self.xkb_compose_state) + } + + #[allow(dead_code)] + fn compose_status_2(&mut self) -> Option { + self.compose_status(self.xkb_compose_state_2) + } + + fn compose_status( + &mut self, + xkb_compose_state: *mut ffi::xkb_compose_state, + ) -> Option { + if !self.ready() || xkb_compose_state.is_null() { return None; } - Some(unsafe { (XKBCH.xkb_compose_state_get_status)(self.xkb_compose_state) }) + Some(unsafe { (XKBCH.xkb_compose_state_get_status)(xkb_compose_state) }) } - pub(crate) fn compose_get_utf8(&mut self) -> Option<&'static str> { - if !self.ready() || self.xkb_compose_state.is_null() { + fn compose_get_utf8_normal(&mut self) -> Option<&'static str> { + self.compose_get_utf8(self.xkb_compose_state) + } + + fn compose_get_utf8_2(&mut self) -> Option<&'static str> { + self.compose_get_utf8(self.xkb_compose_state_2) + } + + fn compose_get_utf8( + &mut self, + xkb_compose_state: *mut ffi::xkb_compose_state, + ) -> Option<&'static str> { + if !self.ready() || xkb_compose_state.is_null() { return None; } - let size = unsafe { - (XKBCH.xkb_compose_state_get_utf8)(self.xkb_compose_state, ptr::null_mut(), 0) - } + 1; + let size = + unsafe { (XKBCH.xkb_compose_state_get_utf8)(xkb_compose_state, ptr::null_mut(), 0) } + + 1; if size <= 1 { return None; }; @@ -204,7 +240,7 @@ impl KbState { unsafe { self.scratch_buffer.set_len(size); (XKBCH.xkb_compose_state_get_utf8)( - self.xkb_compose_state, + xkb_compose_state, self.scratch_buffer.as_mut_ptr() as *mut _, size as usize, ); @@ -232,18 +268,19 @@ impl KbState { xkb_state: ptr::null_mut(), xkb_compose_table: ptr::null_mut(), xkb_compose_state: ptr::null_mut(), + xkb_compose_state_2: ptr::null_mut(), mods_state: ModifiersState::new(), locked: false, scratch_buffer: Vec::new(), }; - unsafe { - me.init_compose(); - } + unsafe { me.init_compose() }; Ok(me) } +} +impl KbState { #[cfg(feature = "x11")] pub(crate) fn from_x11_xkb(connection: *mut xcb_connection_t) -> Result { let mut me = Self::new()?; @@ -310,7 +347,7 @@ impl KbState { Ok(state) } - pub(crate) unsafe fn init_compose(&mut self) { + unsafe fn init_compose(&mut self) { let locale = env::var_os("LC_ALL") .and_then(|v| if v.is_empty() { None } else { Some(v) }) .or_else(|| env::var_os("LC_CTYPE")) @@ -336,7 +373,12 @@ impl KbState { ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, ); - if compose_state.is_null() { + let compose_state_2 = (XKBCH.xkb_compose_state_new)( + compose_table, + ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, + ); + + if compose_state.is_null() || compose_state_2.is_null() { // init of compose state failed, continue without compose (XKBCH.xkb_compose_table_unref)(compose_table); return; @@ -344,6 +386,7 @@ impl KbState { self.xkb_compose_table = compose_table; self.xkb_compose_state = compose_state; + self.xkb_compose_state_2 = compose_state_2; } pub(crate) unsafe fn post_init(&mut self, keymap: *mut ffi::xkb_keymap) { @@ -396,7 +439,9 @@ impl KbState { Ok(()) } +} +impl KbState { pub(crate) unsafe fn key_repeats(&mut self, keycode: ffi::xkb_keycode_t) -> bool { (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, keycode + 8) == 1 } @@ -477,6 +522,7 @@ impl KbState { } } +#[derive(Copy, Clone, Debug)] enum XkbCompose { Accepted(ffi::xkb_compose_status), Ignored, @@ -495,10 +541,10 @@ impl<'a> KeyEventResults<'a> { let keysym = state.get_one_sym_raw(keycode); let compose = if compose { - Some(match state.compose_feed(keysym) { + Some(match state.compose_feed_normal(keysym) { Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED) => { // Unwrapping is safe here, as `compose_feed` returns `None` when composition is uninitialized. - XkbCompose::Accepted(state.compose_status().unwrap()) + XkbCompose::Accepted(state.compose_status_normal().unwrap()) } Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED) => XkbCompose::Ignored, None => XkbCompose::Uninitialized, @@ -521,6 +567,34 @@ impl<'a> KeyEventResults<'a> { pub fn key(&mut self) -> (Key<'static>, KeyLocation) { self.keysym_to_key(self.keysym) + .unwrap_or_else(|(key, location)| match self.compose { + Some(XkbCompose::Accepted(ffi::xkb_compose_status::XKB_COMPOSE_COMPOSING)) => { + // When pressing a dead key twice, the non-combining variant of that character will be + // produced. Since this function only concerns itself with a single keypress, we simulate + // this double press here by feeding the keysym to the compose state twice. + self.state.compose_feed_2(self.keysym); + match self.state.compose_feed_2(self.keysym) { + Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED) => ( + // Extracting only a single `char` here *should* be fine, assuming that no dead + // key's non-combining variant ever occupies more than one `char`. + Key::Dead( + self.state + .compose_get_utf8_2() + .map(|s| s.chars().nth(0).unwrap()), + ), + location, + ), + _ => (key, location), + } + } + _ => ( + self.composed_text() + .unwrap_or_else(|_| self.state.keysym_to_utf8_raw(self.keysym)) + .map(Key::Character) + .unwrap_or(key), + location, + ), + }) } pub fn key_without_modifiers(&mut self) -> (Key<'static>, KeyLocation) { @@ -541,45 +615,57 @@ impl<'a> KeyEventResults<'a> { 0 }; self.keysym_to_key(keysym) + .unwrap_or_else(|(key, location)| { + ( + self.state + .keysym_to_utf8_raw(keysym) + .map(Key::Character) + .unwrap_or(key), + location, + ) + }) } - fn keysym_to_key(&mut self, keysym: u32) -> (Key<'static>, KeyLocation) { + fn keysym_to_key( + &mut self, + keysym: u32, + ) -> Result<(Key<'static>, KeyLocation), (Key<'static>, KeyLocation)> { let location = super::keymap::keysym_location(keysym); - let mut key = super::keymap::keysym_to_key(keysym); + let key = super::keymap::keysym_to_key(keysym); if matches!(key, Key::Unidentified(_)) { - if let Some(string) = self.state.keysym_to_utf8_raw(keysym) { - key = Key::Character(string); - } + Err((key, location)) + } else { + Ok((key, location)) } - (key, location) } pub fn text(&mut self) -> Option<&'static str> { - self._text(|this| this.state.keysym_to_utf8_raw(this.keysym)) + self.composed_text() + .unwrap_or_else(|_| self.state.keysym_to_utf8_raw(self.keysym)) } pub fn text_with_all_modifiers(&mut self) -> Option<&'static str> { // TODO: Should Ctrl override any attempts to compose text? // gnome-terminal agrees, but konsole disagrees. // Should it be configurable instead? - self._text(|this| this.state.get_utf8_raw(this.keycode)) + self.composed_text() + .unwrap_or_else(|_| self.state.get_utf8_raw(self.keycode)) } - fn _text(&mut self, fallback: F) -> Option<&'static str> - where - F: FnOnce(&mut Self) -> Option<&'static str>, - { + fn composed_text(&mut self) -> Result, ()> { if let Some(compose) = &self.compose { match compose { XkbCompose::Accepted(status) => match status { - ffi::xkb_compose_status::XKB_COMPOSE_COMPOSED => self.state.compose_get_utf8(), - ffi::xkb_compose_status::XKB_COMPOSE_NOTHING => fallback(self), - _ => None, + ffi::xkb_compose_status::XKB_COMPOSE_COMPOSED => { + Ok(self.state.compose_get_utf8_normal()) + } + ffi::xkb_compose_status::XKB_COMPOSE_NOTHING => Err(()), + _ => Ok(None), }, - XkbCompose::Ignored | XkbCompose::Uninitialized => fallback(self), + XkbCompose::Ignored | XkbCompose::Uninitialized => Err(()), } } else { - fallback(self) + Err(()) } } } From fbed8d2e18a840e4da44f7ae4325f602de43806a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Fri, 7 May 2021 17:21:39 +0200 Subject: [PATCH 095/131] Remove unused module --- src/platform_impl/linux/x11/events.rs | 1008 ------------------------- src/platform_impl/linux/x11/mod.rs | 2 +- 2 files changed, 1 insertion(+), 1009 deletions(-) delete mode 100644 src/platform_impl/linux/x11/events.rs diff --git a/src/platform_impl/linux/x11/events.rs b/src/platform_impl/linux/x11/events.rs deleted file mode 100644 index e721ab5922..0000000000 --- a/src/platform_impl/linux/x11/events.rs +++ /dev/null @@ -1,1008 +0,0 @@ -use super::ffi; -use crate::keyboard::KeyCode; -use libc; - -// pub fn keysym_to_element(keysym: libc::c_uint) -> Option { -// Some(match keysym { -// ffi::XK_BackSpace => VirtualKeyCode::Back, -// ffi::XK_Tab => VirtualKeyCode::Tab, -// //ffi::XK_Linefeed => VirtualKeyCode::Linefeed, -// //ffi::XK_Clear => VirtualKeyCode::Clear, -// ffi::XK_Return => VirtualKeyCode::Return, -// //ffi::XK_Pause => VirtualKeyCode::Pause, -// //ffi::XK_Scroll_Lock => VirtualKeyCode::Scroll_lock, -// //ffi::XK_Sys_Req => VirtualKeyCode::Sys_req, -// ffi::XK_Escape => VirtualKeyCode::Escape, -// ffi::XK_Delete => VirtualKeyCode::Delete, -// ffi::XK_Multi_key => VirtualKeyCode::Compose, -// //ffi::XK_Kanji => VirtualKeyCode::Kanji, -// //ffi::XK_Muhenkan => VirtualKeyCode::Muhenkan, -// //ffi::XK_Henkan_Mode => VirtualKeyCode::Henkan_mode, -// //ffi::XK_Henkan => VirtualKeyCode::Henkan, -// //ffi::XK_Romaji => VirtualKeyCode::Romaji, -// //ffi::XK_Hiragana => VirtualKeyCode::Hiragana, -// //ffi::XK_Katakana => VirtualKeyCode::Katakana, -// //ffi::XK_Hiragana_Katakana => VirtualKeyCode::Hiragana_katakana, -// //ffi::XK_Zenkaku => VirtualKeyCode::Zenkaku, -// //ffi::XK_Hankaku => VirtualKeyCode::Hankaku, -// //ffi::XK_Zenkaku_Hankaku => VirtualKeyCode::Zenkaku_hankaku, -// //ffi::XK_Touroku => VirtualKeyCode::Touroku, -// //ffi::XK_Massyo => VirtualKeyCode::Massyo, -// //ffi::XK_Kana_Lock => VirtualKeyCode::Kana_lock, -// //ffi::XK_Kana_Shift => VirtualKeyCode::Kana_shift, -// //ffi::XK_Eisu_Shift => VirtualKeyCode::Eisu_shift, -// //ffi::XK_Eisu_toggle => VirtualKeyCode::Eisu_toggle, -// ffi::XK_Home => VirtualKeyCode::Home, -// ffi::XK_Left => VirtualKeyCode::Left, -// ffi::XK_Up => VirtualKeyCode::Up, -// ffi::XK_Right => VirtualKeyCode::Right, -// ffi::XK_Down => VirtualKeyCode::Down, -// //ffi::XK_Prior => VirtualKeyCode::Prior, -// ffi::XK_Page_Up => VirtualKeyCode::PageUp, -// //ffi::XK_Next => VirtualKeyCode::Next, -// ffi::XK_Page_Down => VirtualKeyCode::PageDown, -// ffi::XK_End => VirtualKeyCode::End, -// //ffi::XK_Begin => VirtualKeyCode::Begin, -// //ffi::XK_Win_L => VirtualKeyCode::Win_l, -// //ffi::XK_Win_R => VirtualKeyCode::Win_r, -// //ffi::XK_App => VirtualKeyCode::App, -// //ffi::XK_Select => VirtualKeyCode::Select, -// //ffi::XK_Print => VirtualKeyCode::Print, -// //ffi::XK_Execute => VirtualKeyCode::Execute, -// ffi::XK_Insert => VirtualKeyCode::Insert, -// //ffi::XK_Undo => VirtualKeyCode::Undo, -// //ffi::XK_Redo => VirtualKeyCode::Redo, -// //ffi::XK_Menu => VirtualKeyCode::Menu, -// //ffi::XK_Find => VirtualKeyCode::Find, -// //ffi::XK_Cancel => VirtualKeyCode::Cancel, -// //ffi::XK_Help => VirtualKeyCode::Help, -// //ffi::XK_Break => VirtualKeyCode::Break, -// //ffi::XK_Mode_switch => VirtualKeyCode::Mode_switch, -// //ffi::XK_script_switch => VirtualKeyCode::Script_switch, -// //ffi::XK_Num_Lock => VirtualKeyCode::Num_lock, -// //ffi::XK_KP_Space => VirtualKeyCode::Kp_space, -// //ffi::XK_KP_Tab => VirtualKeyCode::Kp_tab, -// //ffi::XK_KP_Enter => VirtualKeyCode::Kp_enter, -// //ffi::XK_KP_F1 => VirtualKeyCode::Kp_f1, -// //ffi::XK_KP_F2 => VirtualKeyCode::Kp_f2, -// //ffi::XK_KP_F3 => VirtualKeyCode::Kp_f3, -// //ffi::XK_KP_F4 => VirtualKeyCode::Kp_f4, -// ffi::XK_KP_Home => VirtualKeyCode::Home, -// ffi::XK_KP_Left => VirtualKeyCode::Left, -// ffi::XK_KP_Up => VirtualKeyCode::Up, -// ffi::XK_KP_Right => VirtualKeyCode::Right, -// ffi::XK_KP_Down => VirtualKeyCode::Down, -// //ffi::XK_KP_Prior => VirtualKeyCode::Kp_prior, -// ffi::XK_KP_Page_Up => VirtualKeyCode::PageUp, -// //ffi::XK_KP_Next => VirtualKeyCode::Kp_next, -// ffi::XK_KP_Page_Down => VirtualKeyCode::PageDown, -// ffi::XK_KP_End => VirtualKeyCode::End, -// //ffi::XK_KP_Begin => VirtualKeyCode::Kp_begin, -// ffi::XK_KP_Insert => VirtualKeyCode::Insert, -// ffi::XK_KP_Delete => VirtualKeyCode::Delete, -// ffi::XK_KP_Equal => VirtualKeyCode::NumpadEquals, -// ffi::XK_KP_Multiply => VirtualKeyCode::NumpadMultiply, -// ffi::XK_KP_Add => VirtualKeyCode::NumpadAdd, -// //ffi::XK_KP_Separator => VirtualKeyCode::Kp_separator, -// ffi::XK_KP_Subtract => VirtualKeyCode::NumpadSubtract, -// ffi::XK_KP_Decimal => VirtualKeyCode::NumpadDecimal, -// ffi::XK_KP_Divide => VirtualKeyCode::NumpadDivide, -// ffi::XK_KP_0 => VirtualKeyCode::Numpad0, -// ffi::XK_KP_1 => VirtualKeyCode::Numpad1, -// ffi::XK_KP_2 => VirtualKeyCode::Numpad2, -// ffi::XK_KP_3 => VirtualKeyCode::Numpad3, -// ffi::XK_KP_4 => VirtualKeyCode::Numpad4, -// ffi::XK_KP_5 => VirtualKeyCode::Numpad5, -// ffi::XK_KP_6 => VirtualKeyCode::Numpad6, -// ffi::XK_KP_7 => VirtualKeyCode::Numpad7, -// ffi::XK_KP_8 => VirtualKeyCode::Numpad8, -// ffi::XK_KP_9 => VirtualKeyCode::Numpad9, -// ffi::XK_F1 => VirtualKeyCode::F1, -// ffi::XK_F2 => VirtualKeyCode::F2, -// ffi::XK_F3 => VirtualKeyCode::F3, -// ffi::XK_F4 => VirtualKeyCode::F4, -// ffi::XK_F5 => VirtualKeyCode::F5, -// ffi::XK_F6 => VirtualKeyCode::F6, -// ffi::XK_F7 => VirtualKeyCode::F7, -// ffi::XK_F8 => VirtualKeyCode::F8, -// ffi::XK_F9 => VirtualKeyCode::F9, -// ffi::XK_F10 => VirtualKeyCode::F10, -// ffi::XK_F11 => VirtualKeyCode::F11, -// //ffi::XK_L1 => VirtualKeyCode::L1, -// ffi::XK_F12 => VirtualKeyCode::F12, -// //ffi::XK_L2 => VirtualKeyCode::L2, -// ffi::XK_F13 => VirtualKeyCode::F13, -// //ffi::XK_L3 => VirtualKeyCode::L3, -// ffi::XK_F14 => VirtualKeyCode::F14, -// //ffi::XK_L4 => VirtualKeyCode::L4, -// ffi::XK_F15 => VirtualKeyCode::F15, -// //ffi::XK_L5 => VirtualKeyCode::L5, -// ffi::XK_F16 => VirtualKeyCode::F16, -// //ffi::XK_L6 => VirtualKeyCode::L6, -// ffi::XK_F17 => VirtualKeyCode::F17, -// //ffi::XK_L7 => VirtualKeyCode::L7, -// ffi::XK_F18 => VirtualKeyCode::F18, -// //ffi::XK_L8 => VirtualKeyCode::L8, -// ffi::XK_F19 => VirtualKeyCode::F19, -// //ffi::XK_L9 => VirtualKeyCode::L9, -// ffi::XK_F20 => VirtualKeyCode::F20, -// //ffi::XK_L10 => VirtualKeyCode::L10, -// ffi::XK_F21 => VirtualKeyCode::F21, -// //ffi::XK_R1 => VirtualKeyCode::R1, -// ffi::XK_F22 => VirtualKeyCode::F22, -// //ffi::XK_R2 => VirtualKeyCode::R2, -// ffi::XK_F23 => VirtualKeyCode::F23, -// //ffi::XK_R3 => VirtualKeyCode::R3, -// ffi::XK_F24 => VirtualKeyCode::F24, -// //ffi::XK_R4 => VirtualKeyCode::R4, -// //ffi::XK_F25 => VirtualKeyCode::F25, -// //ffi::XK_R5 => VirtualKeyCode::R5, -// //ffi::XK_F26 => VirtualKeyCode::F26, -// //ffi::XK_R6 => VirtualKeyCode::R6, -// //ffi::XK_F27 => VirtualKeyCode::F27, -// //ffi::XK_R7 => VirtualKeyCode::R7, -// //ffi::XK_F28 => VirtualKeyCode::F28, -// //ffi::XK_R8 => VirtualKeyCode::R8, -// //ffi::XK_F29 => VirtualKeyCode::F29, -// //ffi::XK_R9 => VirtualKeyCode::R9, -// //ffi::XK_F30 => VirtualKeyCode::F30, -// //ffi::XK_R10 => VirtualKeyCode::R10, -// //ffi::XK_F31 => VirtualKeyCode::F31, -// //ffi::XK_R11 => VirtualKeyCode::R11, -// //ffi::XK_F32 => VirtualKeyCode::F32, -// //ffi::XK_R12 => VirtualKeyCode::R12, -// //ffi::XK_F33 => VirtualKeyCode::F33, -// //ffi::XK_R13 => VirtualKeyCode::R13, -// //ffi::XK_F34 => VirtualKeyCode::F34, -// //ffi::XK_R14 => VirtualKeyCode::R14, -// //ffi::XK_F35 => VirtualKeyCode::F35, -// //ffi::XK_R15 => VirtualKeyCode::R15, -// ffi::XK_Shift_L => VirtualKeyCode::LShift, -// ffi::XK_Shift_R => VirtualKeyCode::RShift, -// ffi::XK_Control_L => VirtualKeyCode::LControl, -// ffi::XK_Control_R => VirtualKeyCode::RControl, -// //ffi::XK_Caps_Lock => VirtualKeyCode::Caps_lock, -// //ffi::XK_Shift_Lock => VirtualKeyCode::Shift_lock, -// //ffi::XK_Meta_L => VirtualKeyCode::Meta_l, -// //ffi::XK_Meta_R => VirtualKeyCode::Meta_r, -// ffi::XK_Alt_L => VirtualKeyCode::LAlt, -// ffi::XK_Alt_R => VirtualKeyCode::RAlt, -// //ffi::XK_Super_L => VirtualKeyCode::Super_l, -// //ffi::XK_Super_R => VirtualKeyCode::Super_r, -// //ffi::XK_Hyper_L => VirtualKeyCode::Hyper_l, -// //ffi::XK_Hyper_R => VirtualKeyCode::Hyper_r, -// ffi::XK_ISO_Left_Tab => VirtualKeyCode::Tab, -// ffi::XK_space => VirtualKeyCode::Space, -// //ffi::XK_exclam => VirtualKeyCode::Exclam, -// //ffi::XK_quotedbl => VirtualKeyCode::Quotedbl, -// //ffi::XK_numbersign => VirtualKeyCode::Numbersign, -// //ffi::XK_dollar => VirtualKeyCode::Dollar, -// //ffi::XK_percent => VirtualKeyCode::Percent, -// //ffi::XK_ampersand => VirtualKeyCode::Ampersand, -// ffi::XK_apostrophe => VirtualKeyCode::Apostrophe, -// //ffi::XK_quoteright => VirtualKeyCode::Quoteright, -// //ffi::XK_parenleft => VirtualKeyCode::Parenleft, -// //ffi::XK_parenright => VirtualKeyCode::Parenright, -// ffi::XK_asterisk => VirtualKeyCode::Asterisk, -// ffi::XK_plus => VirtualKeyCode::Plus, -// ffi::XK_comma => VirtualKeyCode::Comma, -// ffi::XK_minus => VirtualKeyCode::Minus, -// ffi::XK_period => VirtualKeyCode::Period, -// ffi::XK_slash => VirtualKeyCode::Slash, -// ffi::XK_0 => VirtualKeyCode::Key0, -// ffi::XK_1 => VirtualKeyCode::Key1, -// ffi::XK_2 => VirtualKeyCode::Key2, -// ffi::XK_3 => VirtualKeyCode::Key3, -// ffi::XK_4 => VirtualKeyCode::Key4, -// ffi::XK_5 => VirtualKeyCode::Key5, -// ffi::XK_6 => VirtualKeyCode::Key6, -// ffi::XK_7 => VirtualKeyCode::Key7, -// ffi::XK_8 => VirtualKeyCode::Key8, -// ffi::XK_9 => VirtualKeyCode::Key9, -// ffi::XK_colon => VirtualKeyCode::Colon, -// ffi::XK_semicolon => VirtualKeyCode::Semicolon, -// //ffi::XK_less => VirtualKeyCode::Less, -// ffi::XK_equal => VirtualKeyCode::Equals, -// //ffi::XK_greater => VirtualKeyCode::Greater, -// //ffi::XK_question => VirtualKeyCode::Question, -// ffi::XK_at => VirtualKeyCode::At, -// ffi::XK_A => VirtualKeyCode::A, -// ffi::XK_B => VirtualKeyCode::B, -// ffi::XK_C => VirtualKeyCode::C, -// ffi::XK_D => VirtualKeyCode::D, -// ffi::XK_E => VirtualKeyCode::E, -// ffi::XK_F => VirtualKeyCode::F, -// ffi::XK_G => VirtualKeyCode::G, -// ffi::XK_H => VirtualKeyCode::H, -// ffi::XK_I => VirtualKeyCode::I, -// ffi::XK_J => VirtualKeyCode::J, -// ffi::XK_K => VirtualKeyCode::K, -// ffi::XK_L => VirtualKeyCode::L, -// ffi::XK_M => VirtualKeyCode::M, -// ffi::XK_N => VirtualKeyCode::N, -// ffi::XK_O => VirtualKeyCode::O, -// ffi::XK_P => VirtualKeyCode::P, -// ffi::XK_Q => VirtualKeyCode::Q, -// ffi::XK_R => VirtualKeyCode::R, -// ffi::XK_S => VirtualKeyCode::S, -// ffi::XK_T => VirtualKeyCode::T, -// ffi::XK_U => VirtualKeyCode::U, -// ffi::XK_V => VirtualKeyCode::V, -// ffi::XK_W => VirtualKeyCode::W, -// ffi::XK_X => VirtualKeyCode::X, -// ffi::XK_Y => VirtualKeyCode::Y, -// ffi::XK_Z => VirtualKeyCode::Z, -// ffi::XK_bracketleft => VirtualKeyCode::LBracket, -// ffi::XK_backslash => VirtualKeyCode::Backslash, -// ffi::XK_bracketright => VirtualKeyCode::RBracket, -// //ffi::XK_asciicircum => VirtualKeyCode::Asciicircum, -// //ffi::XK_underscore => VirtualKeyCode::Underscore, -// ffi::XK_grave => VirtualKeyCode::Grave, -// //ffi::XK_quoteleft => VirtualKeyCode::Quoteleft, -// ffi::XK_a => VirtualKeyCode::A, -// ffi::XK_b => VirtualKeyCode::B, -// ffi::XK_c => VirtualKeyCode::C, -// ffi::XK_d => VirtualKeyCode::D, -// ffi::XK_e => VirtualKeyCode::E, -// ffi::XK_f => VirtualKeyCode::F, -// ffi::XK_g => VirtualKeyCode::G, -// ffi::XK_h => VirtualKeyCode::H, -// ffi::XK_i => VirtualKeyCode::I, -// ffi::XK_j => VirtualKeyCode::J, -// ffi::XK_k => VirtualKeyCode::K, -// ffi::XK_l => VirtualKeyCode::L, -// ffi::XK_m => VirtualKeyCode::M, -// ffi::XK_n => VirtualKeyCode::N, -// ffi::XK_o => VirtualKeyCode::O, -// ffi::XK_p => VirtualKeyCode::P, -// ffi::XK_q => VirtualKeyCode::Q, -// ffi::XK_r => VirtualKeyCode::R, -// ffi::XK_s => VirtualKeyCode::S, -// ffi::XK_t => VirtualKeyCode::T, -// ffi::XK_u => VirtualKeyCode::U, -// ffi::XK_v => VirtualKeyCode::V, -// ffi::XK_w => VirtualKeyCode::W, -// ffi::XK_x => VirtualKeyCode::X, -// ffi::XK_y => VirtualKeyCode::Y, -// ffi::XK_z => VirtualKeyCode::Z, -// //ffi::XK_braceleft => VirtualKeyCode::Braceleft, -// //ffi::XK_bar => VirtualKeyCode::Bar, -// //ffi::XK_braceright => VirtualKeyCode::Braceright, -// //ffi::XK_asciitilde => VirtualKeyCode::Asciitilde, -// //ffi::XK_nobreakspace => VirtualKeyCode::Nobreakspace, -// //ffi::XK_exclamdown => VirtualKeyCode::Exclamdown, -// //ffi::XK_cent => VirtualKeyCode::Cent, -// //ffi::XK_sterling => VirtualKeyCode::Sterling, -// //ffi::XK_currency => VirtualKeyCode::Currency, -// //ffi::XK_yen => VirtualKeyCode::Yen, -// //ffi::XK_brokenbar => VirtualKeyCode::Brokenbar, -// //ffi::XK_section => VirtualKeyCode::Section, -// //ffi::XK_diaeresis => VirtualKeyCode::Diaeresis, -// //ffi::XK_copyright => VirtualKeyCode::Copyright, -// //ffi::XK_ordfeminine => VirtualKeyCode::Ordfeminine, -// //ffi::XK_guillemotleft => VirtualKeyCode::Guillemotleft, -// //ffi::XK_notsign => VirtualKeyCode::Notsign, -// //ffi::XK_hyphen => VirtualKeyCode::Hyphen, -// //ffi::XK_registered => VirtualKeyCode::Registered, -// //ffi::XK_macron => VirtualKeyCode::Macron, -// //ffi::XK_degree => VirtualKeyCode::Degree, -// //ffi::XK_plusminus => VirtualKeyCode::Plusminus, -// //ffi::XK_twosuperior => VirtualKeyCode::Twosuperior, -// //ffi::XK_threesuperior => VirtualKeyCode::Threesuperior, -// //ffi::XK_acute => VirtualKeyCode::Acute, -// //ffi::XK_mu => VirtualKeyCode::Mu, -// //ffi::XK_paragraph => VirtualKeyCode::Paragraph, -// //ffi::XK_periodcentered => VirtualKeyCode::Periodcentered, -// //ffi::XK_cedilla => VirtualKeyCode::Cedilla, -// //ffi::XK_onesuperior => VirtualKeyCode::Onesuperior, -// //ffi::XK_masculine => VirtualKeyCode::Masculine, -// //ffi::XK_guillemotright => VirtualKeyCode::Guillemotright, -// //ffi::XK_onequarter => VirtualKeyCode::Onequarter, -// //ffi::XK_onehalf => VirtualKeyCode::Onehalf, -// //ffi::XK_threequarters => VirtualKeyCode::Threequarters, -// //ffi::XK_questiondown => VirtualKeyCode::Questiondown, -// //ffi::XK_Agrave => VirtualKeyCode::Agrave, -// //ffi::XK_Aacute => VirtualKeyCode::Aacute, -// //ffi::XK_Acircumflex => VirtualKeyCode::Acircumflex, -// //ffi::XK_Atilde => VirtualKeyCode::Atilde, -// //ffi::XK_Adiaeresis => VirtualKeyCode::Adiaeresis, -// //ffi::XK_Aring => VirtualKeyCode::Aring, -// //ffi::XK_AE => VirtualKeyCode::Ae, -// //ffi::XK_Ccedilla => VirtualKeyCode::Ccedilla, -// //ffi::XK_Egrave => VirtualKeyCode::Egrave, -// //ffi::XK_Eacute => VirtualKeyCode::Eacute, -// //ffi::XK_Ecircumflex => VirtualKeyCode::Ecircumflex, -// //ffi::XK_Ediaeresis => VirtualKeyCode::Ediaeresis, -// //ffi::XK_Igrave => VirtualKeyCode::Igrave, -// //ffi::XK_Iacute => VirtualKeyCode::Iacute, -// //ffi::XK_Icircumflex => VirtualKeyCode::Icircumflex, -// //ffi::XK_Idiaeresis => VirtualKeyCode::Idiaeresis, -// //ffi::XK_ETH => VirtualKeyCode::Eth, -// //ffi::XK_Eth => VirtualKeyCode::Eth, -// //ffi::XK_Ntilde => VirtualKeyCode::Ntilde, -// //ffi::XK_Ograve => VirtualKeyCode::Ograve, -// //ffi::XK_Oacute => VirtualKeyCode::Oacute, -// //ffi::XK_Ocircumflex => VirtualKeyCode::Ocircumflex, -// //ffi::XK_Otilde => VirtualKeyCode::Otilde, -// //ffi::XK_Odiaeresis => VirtualKeyCode::Odiaeresis, -// //ffi::XK_multiply => VirtualKeyCode::Multiply, -// //ffi::XK_Ooblique => VirtualKeyCode::Ooblique, -// //ffi::XK_Ugrave => VirtualKeyCode::Ugrave, -// //ffi::XK_Uacute => VirtualKeyCode::Uacute, -// //ffi::XK_Ucircumflex => VirtualKeyCode::Ucircumflex, -// //ffi::XK_Udiaeresis => VirtualKeyCode::Udiaeresis, -// //ffi::XK_Yacute => VirtualKeyCode::Yacute, -// //ffi::XK_THORN => VirtualKeyCode::Thorn, -// //ffi::XK_Thorn => VirtualKeyCode::Thorn, -// //ffi::XK_ssharp => VirtualKeyCode::Ssharp, -// //ffi::XK_agrave => VirtualKeyCode::Agrave, -// //ffi::XK_aacute => VirtualKeyCode::Aacute, -// //ffi::XK_acircumflex => VirtualKeyCode::Acircumflex, -// //ffi::XK_atilde => VirtualKeyCode::Atilde, -// //ffi::XK_adiaeresis => VirtualKeyCode::Adiaeresis, -// //ffi::XK_aring => VirtualKeyCode::Aring, -// //ffi::XK_ae => VirtualKeyCode::Ae, -// //ffi::XK_ccedilla => VirtualKeyCode::Ccedilla, -// //ffi::XK_egrave => VirtualKeyCode::Egrave, -// //ffi::XK_eacute => VirtualKeyCode::Eacute, -// //ffi::XK_ecircumflex => VirtualKeyCode::Ecircumflex, -// //ffi::XK_ediaeresis => VirtualKeyCode::Ediaeresis, -// //ffi::XK_igrave => VirtualKeyCode::Igrave, -// //ffi::XK_iacute => VirtualKeyCode::Iacute, -// //ffi::XK_icircumflex => VirtualKeyCode::Icircumflex, -// //ffi::XK_idiaeresis => VirtualKeyCode::Idiaeresis, -// //ffi::XK_eth => VirtualKeyCode::Eth, -// //ffi::XK_ntilde => VirtualKeyCode::Ntilde, -// //ffi::XK_ograve => VirtualKeyCode::Ograve, -// //ffi::XK_oacute => VirtualKeyCode::Oacute, -// //ffi::XK_ocircumflex => VirtualKeyCode::Ocircumflex, -// //ffi::XK_otilde => VirtualKeyCode::Otilde, -// //ffi::XK_odiaeresis => VirtualKeyCode::Odiaeresis, -// //ffi::XK_division => VirtualKeyCode::Division, -// //ffi::XK_oslash => VirtualKeyCode::Oslash, -// //ffi::XK_ugrave => VirtualKeyCode::Ugrave, -// //ffi::XK_uacute => VirtualKeyCode::Uacute, -// //ffi::XK_ucircumflex => VirtualKeyCode::Ucircumflex, -// //ffi::XK_udiaeresis => VirtualKeyCode::Udiaeresis, -// //ffi::XK_yacute => VirtualKeyCode::Yacute, -// //ffi::XK_thorn => VirtualKeyCode::Thorn, -// //ffi::XK_ydiaeresis => VirtualKeyCode::Ydiaeresis, -// //ffi::XK_Aogonek => VirtualKeyCode::Aogonek, -// //ffi::XK_breve => VirtualKeyCode::Breve, -// //ffi::XK_Lstroke => VirtualKeyCode::Lstroke, -// //ffi::XK_Lcaron => VirtualKeyCode::Lcaron, -// //ffi::XK_Sacute => VirtualKeyCode::Sacute, -// //ffi::XK_Scaron => VirtualKeyCode::Scaron, -// //ffi::XK_Scedilla => VirtualKeyCode::Scedilla, -// //ffi::XK_Tcaron => VirtualKeyCode::Tcaron, -// //ffi::XK_Zacute => VirtualKeyCode::Zacute, -// //ffi::XK_Zcaron => VirtualKeyCode::Zcaron, -// //ffi::XK_Zabovedot => VirtualKeyCode::Zabovedot, -// //ffi::XK_aogonek => VirtualKeyCode::Aogonek, -// //ffi::XK_ogonek => VirtualKeyCode::Ogonek, -// //ffi::XK_lstroke => VirtualKeyCode::Lstroke, -// //ffi::XK_lcaron => VirtualKeyCode::Lcaron, -// //ffi::XK_sacute => VirtualKeyCode::Sacute, -// //ffi::XK_caron => VirtualKeyCode::Caron, -// //ffi::XK_scaron => VirtualKeyCode::Scaron, -// //ffi::XK_scedilla => VirtualKeyCode::Scedilla, -// //ffi::XK_tcaron => VirtualKeyCode::Tcaron, -// //ffi::XK_zacute => VirtualKeyCode::Zacute, -// //ffi::XK_doubleacute => VirtualKeyCode::Doubleacute, -// //ffi::XK_zcaron => VirtualKeyCode::Zcaron, -// //ffi::XK_zabovedot => VirtualKeyCode::Zabovedot, -// //ffi::XK_Racute => VirtualKeyCode::Racute, -// //ffi::XK_Abreve => VirtualKeyCode::Abreve, -// //ffi::XK_Lacute => VirtualKeyCode::Lacute, -// //ffi::XK_Cacute => VirtualKeyCode::Cacute, -// //ffi::XK_Ccaron => VirtualKeyCode::Ccaron, -// //ffi::XK_Eogonek => VirtualKeyCode::Eogonek, -// //ffi::XK_Ecaron => VirtualKeyCode::Ecaron, -// //ffi::XK_Dcaron => VirtualKeyCode::Dcaron, -// //ffi::XK_Dstroke => VirtualKeyCode::Dstroke, -// //ffi::XK_Nacute => VirtualKeyCode::Nacute, -// //ffi::XK_Ncaron => VirtualKeyCode::Ncaron, -// //ffi::XK_Odoubleacute => VirtualKeyCode::Odoubleacute, -// //ffi::XK_Rcaron => VirtualKeyCode::Rcaron, -// //ffi::XK_Uring => VirtualKeyCode::Uring, -// //ffi::XK_Udoubleacute => VirtualKeyCode::Udoubleacute, -// //ffi::XK_Tcedilla => VirtualKeyCode::Tcedilla, -// //ffi::XK_racute => VirtualKeyCode::Racute, -// //ffi::XK_abreve => VirtualKeyCode::Abreve, -// //ffi::XK_lacute => VirtualKeyCode::Lacute, -// //ffi::XK_cacute => VirtualKeyCode::Cacute, -// //ffi::XK_ccaron => VirtualKeyCode::Ccaron, -// //ffi::XK_eogonek => VirtualKeyCode::Eogonek, -// //ffi::XK_ecaron => VirtualKeyCode::Ecaron, -// //ffi::XK_dcaron => VirtualKeyCode::Dcaron, -// //ffi::XK_dstroke => VirtualKeyCode::Dstroke, -// //ffi::XK_nacute => VirtualKeyCode::Nacute, -// //ffi::XK_ncaron => VirtualKeyCode::Ncaron, -// //ffi::XK_odoubleacute => VirtualKeyCode::Odoubleacute, -// //ffi::XK_udoubleacute => VirtualKeyCode::Udoubleacute, -// //ffi::XK_rcaron => VirtualKeyCode::Rcaron, -// //ffi::XK_uring => VirtualKeyCode::Uring, -// //ffi::XK_tcedilla => VirtualKeyCode::Tcedilla, -// //ffi::XK_abovedot => VirtualKeyCode::Abovedot, -// //ffi::XK_Hstroke => VirtualKeyCode::Hstroke, -// //ffi::XK_Hcircumflex => VirtualKeyCode::Hcircumflex, -// //ffi::XK_Iabovedot => VirtualKeyCode::Iabovedot, -// //ffi::XK_Gbreve => VirtualKeyCode::Gbreve, -// //ffi::XK_Jcircumflex => VirtualKeyCode::Jcircumflex, -// //ffi::XK_hstroke => VirtualKeyCode::Hstroke, -// //ffi::XK_hcircumflex => VirtualKeyCode::Hcircumflex, -// //ffi::XK_idotless => VirtualKeyCode::Idotless, -// //ffi::XK_gbreve => VirtualKeyCode::Gbreve, -// //ffi::XK_jcircumflex => VirtualKeyCode::Jcircumflex, -// //ffi::XK_Cabovedot => VirtualKeyCode::Cabovedot, -// //ffi::XK_Ccircumflex => VirtualKeyCode::Ccircumflex, -// //ffi::XK_Gabovedot => VirtualKeyCode::Gabovedot, -// //ffi::XK_Gcircumflex => VirtualKeyCode::Gcircumflex, -// //ffi::XK_Ubreve => VirtualKeyCode::Ubreve, -// //ffi::XK_Scircumflex => VirtualKeyCode::Scircumflex, -// //ffi::XK_cabovedot => VirtualKeyCode::Cabovedot, -// //ffi::XK_ccircumflex => VirtualKeyCode::Ccircumflex, -// //ffi::XK_gabovedot => VirtualKeyCode::Gabovedot, -// //ffi::XK_gcircumflex => VirtualKeyCode::Gcircumflex, -// //ffi::XK_ubreve => VirtualKeyCode::Ubreve, -// //ffi::XK_scircumflex => VirtualKeyCode::Scircumflex, -// //ffi::XK_kra => VirtualKeyCode::Kra, -// //ffi::XK_kappa => VirtualKeyCode::Kappa, -// //ffi::XK_Rcedilla => VirtualKeyCode::Rcedilla, -// //ffi::XK_Itilde => VirtualKeyCode::Itilde, -// //ffi::XK_Lcedilla => VirtualKeyCode::Lcedilla, -// //ffi::XK_Emacron => VirtualKeyCode::Emacron, -// //ffi::XK_Gcedilla => VirtualKeyCode::Gcedilla, -// //ffi::XK_Tslash => VirtualKeyCode::Tslash, -// //ffi::XK_rcedilla => VirtualKeyCode::Rcedilla, -// //ffi::XK_itilde => VirtualKeyCode::Itilde, -// //ffi::XK_lcedilla => VirtualKeyCode::Lcedilla, -// //ffi::XK_emacron => VirtualKeyCode::Emacron, -// //ffi::XK_gcedilla => VirtualKeyCode::Gcedilla, -// //ffi::XK_tslash => VirtualKeyCode::Tslash, -// //ffi::XK_ENG => VirtualKeyCode::Eng, -// //ffi::XK_eng => VirtualKeyCode::Eng, -// //ffi::XK_Amacron => VirtualKeyCode::Amacron, -// //ffi::XK_Iogonek => VirtualKeyCode::Iogonek, -// //ffi::XK_Eabovedot => VirtualKeyCode::Eabovedot, -// //ffi::XK_Imacron => VirtualKeyCode::Imacron, -// //ffi::XK_Ncedilla => VirtualKeyCode::Ncedilla, -// //ffi::XK_Omacron => VirtualKeyCode::Omacron, -// //ffi::XK_Kcedilla => VirtualKeyCode::Kcedilla, -// //ffi::XK_Uogonek => VirtualKeyCode::Uogonek, -// //ffi::XK_Utilde => VirtualKeyCode::Utilde, -// //ffi::XK_Umacron => VirtualKeyCode::Umacron, -// //ffi::XK_amacron => VirtualKeyCode::Amacron, -// //ffi::XK_iogonek => VirtualKeyCode::Iogonek, -// //ffi::XK_eabovedot => VirtualKeyCode::Eabovedot, -// //ffi::XK_imacron => VirtualKeyCode::Imacron, -// //ffi::XK_ncedilla => VirtualKeyCode::Ncedilla, -// //ffi::XK_omacron => VirtualKeyCode::Omacron, -// //ffi::XK_kcedilla => VirtualKeyCode::Kcedilla, -// //ffi::XK_uogonek => VirtualKeyCode::Uogonek, -// //ffi::XK_utilde => VirtualKeyCode::Utilde, -// //ffi::XK_umacron => VirtualKeyCode::Umacron, -// //ffi::XK_overline => VirtualKeyCode::Overline, -// //ffi::XK_kana_fullstop => VirtualKeyCode::Kana_fullstop, -// //ffi::XK_kana_openingbracket => VirtualKeyCode::Kana_openingbracket, -// //ffi::XK_kana_closingbracket => VirtualKeyCode::Kana_closingbracket, -// //ffi::XK_kana_comma => VirtualKeyCode::Kana_comma, -// //ffi::XK_kana_conjunctive => VirtualKeyCode::Kana_conjunctive, -// //ffi::XK_kana_middledot => VirtualKeyCode::Kana_middledot, -// //ffi::XK_kana_WO => VirtualKeyCode::Kana_wo, -// //ffi::XK_kana_a => VirtualKeyCode::Kana_a, -// //ffi::XK_kana_i => VirtualKeyCode::Kana_i, -// //ffi::XK_kana_u => VirtualKeyCode::Kana_u, -// //ffi::XK_kana_e => VirtualKeyCode::Kana_e, -// //ffi::XK_kana_o => VirtualKeyCode::Kana_o, -// //ffi::XK_kana_ya => VirtualKeyCode::Kana_ya, -// //ffi::XK_kana_yu => VirtualKeyCode::Kana_yu, -// //ffi::XK_kana_yo => VirtualKeyCode::Kana_yo, -// //ffi::XK_kana_tsu => VirtualKeyCode::Kana_tsu, -// //ffi::XK_kana_tu => VirtualKeyCode::Kana_tu, -// //ffi::XK_prolongedsound => VirtualKeyCode::Prolongedsound, -// //ffi::XK_kana_A => VirtualKeyCode::Kana_a, -// //ffi::XK_kana_I => VirtualKeyCode::Kana_i, -// //ffi::XK_kana_U => VirtualKeyCode::Kana_u, -// //ffi::XK_kana_E => VirtualKeyCode::Kana_e, -// //ffi::XK_kana_O => VirtualKeyCode::Kana_o, -// //ffi::XK_kana_KA => VirtualKeyCode::Kana_ka, -// //ffi::XK_kana_KI => VirtualKeyCode::Kana_ki, -// //ffi::XK_kana_KU => VirtualKeyCode::Kana_ku, -// //ffi::XK_kana_KE => VirtualKeyCode::Kana_ke, -// //ffi::XK_kana_KO => VirtualKeyCode::Kana_ko, -// //ffi::XK_kana_SA => VirtualKeyCode::Kana_sa, -// //ffi::XK_kana_SHI => VirtualKeyCode::Kana_shi, -// //ffi::XK_kana_SU => VirtualKeyCode::Kana_su, -// //ffi::XK_kana_SE => VirtualKeyCode::Kana_se, -// //ffi::XK_kana_SO => VirtualKeyCode::Kana_so, -// //ffi::XK_kana_TA => VirtualKeyCode::Kana_ta, -// //ffi::XK_kana_CHI => VirtualKeyCode::Kana_chi, -// //ffi::XK_kana_TI => VirtualKeyCode::Kana_ti, -// //ffi::XK_kana_TSU => VirtualKeyCode::Kana_tsu, -// //ffi::XK_kana_TU => VirtualKeyCode::Kana_tu, -// //ffi::XK_kana_TE => VirtualKeyCode::Kana_te, -// //ffi::XK_kana_TO => VirtualKeyCode::Kana_to, -// //ffi::XK_kana_NA => VirtualKeyCode::Kana_na, -// //ffi::XK_kana_NI => VirtualKeyCode::Kana_ni, -// //ffi::XK_kana_NU => VirtualKeyCode::Kana_nu, -// //ffi::XK_kana_NE => VirtualKeyCode::Kana_ne, -// //ffi::XK_kana_NO => VirtualKeyCode::Kana_no, -// //ffi::XK_kana_HA => VirtualKeyCode::Kana_ha, -// //ffi::XK_kana_HI => VirtualKeyCode::Kana_hi, -// //ffi::XK_kana_FU => VirtualKeyCode::Kana_fu, -// //ffi::XK_kana_HU => VirtualKeyCode::Kana_hu, -// //ffi::XK_kana_HE => VirtualKeyCode::Kana_he, -// //ffi::XK_kana_HO => VirtualKeyCode::Kana_ho, -// //ffi::XK_kana_MA => VirtualKeyCode::Kana_ma, -// //ffi::XK_kana_MI => VirtualKeyCode::Kana_mi, -// //ffi::XK_kana_MU => VirtualKeyCode::Kana_mu, -// //ffi::XK_kana_ME => VirtualKeyCode::Kana_me, -// //ffi::XK_kana_MO => VirtualKeyCode::Kana_mo, -// //ffi::XK_kana_YA => VirtualKeyCode::Kana_ya, -// //ffi::XK_kana_YU => VirtualKeyCode::Kana_yu, -// //ffi::XK_kana_YO => VirtualKeyCode::Kana_yo, -// //ffi::XK_kana_RA => VirtualKeyCode::Kana_ra, -// //ffi::XK_kana_RI => VirtualKeyCode::Kana_ri, -// //ffi::XK_kana_RU => VirtualKeyCode::Kana_ru, -// //ffi::XK_kana_RE => VirtualKeyCode::Kana_re, -// //ffi::XK_kana_RO => VirtualKeyCode::Kana_ro, -// //ffi::XK_kana_WA => VirtualKeyCode::Kana_wa, -// //ffi::XK_kana_N => VirtualKeyCode::Kana_n, -// //ffi::XK_voicedsound => VirtualKeyCode::Voicedsound, -// //ffi::XK_semivoicedsound => VirtualKeyCode::Semivoicedsound, -// //ffi::XK_kana_switch => VirtualKeyCode::Kana_switch, -// //ffi::XK_Arabic_comma => VirtualKeyCode::Arabic_comma, -// //ffi::XK_Arabic_semicolon => VirtualKeyCode::Arabic_semicolon, -// //ffi::XK_Arabic_question_mark => VirtualKeyCode::Arabic_question_mark, -// //ffi::XK_Arabic_hamza => VirtualKeyCode::Arabic_hamza, -// //ffi::XK_Arabic_maddaonalef => VirtualKeyCode::Arabic_maddaonalef, -// //ffi::XK_Arabic_hamzaonalef => VirtualKeyCode::Arabic_hamzaonalef, -// //ffi::XK_Arabic_hamzaonwaw => VirtualKeyCode::Arabic_hamzaonwaw, -// //ffi::XK_Arabic_hamzaunderalef => VirtualKeyCode::Arabic_hamzaunderalef, -// //ffi::XK_Arabic_hamzaonyeh => VirtualKeyCode::Arabic_hamzaonyeh, -// //ffi::XK_Arabic_alef => VirtualKeyCode::Arabic_alef, -// //ffi::XK_Arabic_beh => VirtualKeyCode::Arabic_beh, -// //ffi::XK_Arabic_tehmarbuta => VirtualKeyCode::Arabic_tehmarbuta, -// //ffi::XK_Arabic_teh => VirtualKeyCode::Arabic_teh, -// //ffi::XK_Arabic_theh => VirtualKeyCode::Arabic_theh, -// //ffi::XK_Arabic_jeem => VirtualKeyCode::Arabic_jeem, -// //ffi::XK_Arabic_hah => VirtualKeyCode::Arabic_hah, -// //ffi::XK_Arabic_khah => VirtualKeyCode::Arabic_khah, -// //ffi::XK_Arabic_dal => VirtualKeyCode::Arabic_dal, -// //ffi::XK_Arabic_thal => VirtualKeyCode::Arabic_thal, -// //ffi::XK_Arabic_ra => VirtualKeyCode::Arabic_ra, -// //ffi::XK_Arabic_zain => VirtualKeyCode::Arabic_zain, -// //ffi::XK_Arabic_seen => VirtualKeyCode::Arabic_seen, -// //ffi::XK_Arabic_sheen => VirtualKeyCode::Arabic_sheen, -// //ffi::XK_Arabic_sad => VirtualKeyCode::Arabic_sad, -// //ffi::XK_Arabic_dad => VirtualKeyCode::Arabic_dad, -// //ffi::XK_Arabic_tah => VirtualKeyCode::Arabic_tah, -// //ffi::XK_Arabic_zah => VirtualKeyCode::Arabic_zah, -// //ffi::XK_Arabic_ain => VirtualKeyCode::Arabic_ain, -// //ffi::XK_Arabic_ghain => VirtualKeyCode::Arabic_ghain, -// //ffi::XK_Arabic_tatweel => VirtualKeyCode::Arabic_tatweel, -// //ffi::XK_Arabic_feh => VirtualKeyCode::Arabic_feh, -// //ffi::XK_Arabic_qaf => VirtualKeyCode::Arabic_qaf, -// //ffi::XK_Arabic_kaf => VirtualKeyCode::Arabic_kaf, -// //ffi::XK_Arabic_lam => VirtualKeyCode::Arabic_lam, -// //ffi::XK_Arabic_meem => VirtualKeyCode::Arabic_meem, -// //ffi::XK_Arabic_noon => VirtualKeyCode::Arabic_noon, -// //ffi::XK_Arabic_ha => VirtualKeyCode::Arabic_ha, -// //ffi::XK_Arabic_heh => VirtualKeyCode::Arabic_heh, -// //ffi::XK_Arabic_waw => VirtualKeyCode::Arabic_waw, -// //ffi::XK_Arabic_alefmaksura => VirtualKeyCode::Arabic_alefmaksura, -// //ffi::XK_Arabic_yeh => VirtualKeyCode::Arabic_yeh, -// //ffi::XK_Arabic_fathatan => VirtualKeyCode::Arabic_fathatan, -// //ffi::XK_Arabic_dammatan => VirtualKeyCode::Arabic_dammatan, -// //ffi::XK_Arabic_kasratan => VirtualKeyCode::Arabic_kasratan, -// //ffi::XK_Arabic_fatha => VirtualKeyCode::Arabic_fatha, -// //ffi::XK_Arabic_damma => VirtualKeyCode::Arabic_damma, -// //ffi::XK_Arabic_kasra => VirtualKeyCode::Arabic_kasra, -// //ffi::XK_Arabic_shadda => VirtualKeyCode::Arabic_shadda, -// //ffi::XK_Arabic_sukun => VirtualKeyCode::Arabic_sukun, -// //ffi::XK_Arabic_switch => VirtualKeyCode::Arabic_switch, -// //ffi::XK_Serbian_dje => VirtualKeyCode::Serbian_dje, -// //ffi::XK_Macedonia_gje => VirtualKeyCode::Macedonia_gje, -// //ffi::XK_Cyrillic_io => VirtualKeyCode::Cyrillic_io, -// //ffi::XK_Ukrainian_ie => VirtualKeyCode::Ukrainian_ie, -// //ffi::XK_Ukranian_je => VirtualKeyCode::Ukranian_je, -// //ffi::XK_Macedonia_dse => VirtualKeyCode::Macedonia_dse, -// //ffi::XK_Ukrainian_i => VirtualKeyCode::Ukrainian_i, -// //ffi::XK_Ukranian_i => VirtualKeyCode::Ukranian_i, -// //ffi::XK_Ukrainian_yi => VirtualKeyCode::Ukrainian_yi, -// //ffi::XK_Ukranian_yi => VirtualKeyCode::Ukranian_yi, -// //ffi::XK_Cyrillic_je => VirtualKeyCode::Cyrillic_je, -// //ffi::XK_Serbian_je => VirtualKeyCode::Serbian_je, -// //ffi::XK_Cyrillic_lje => VirtualKeyCode::Cyrillic_lje, -// //ffi::XK_Serbian_lje => VirtualKeyCode::Serbian_lje, -// //ffi::XK_Cyrillic_nje => VirtualKeyCode::Cyrillic_nje, -// //ffi::XK_Serbian_nje => VirtualKeyCode::Serbian_nje, -// //ffi::XK_Serbian_tshe => VirtualKeyCode::Serbian_tshe, -// //ffi::XK_Macedonia_kje => VirtualKeyCode::Macedonia_kje, -// //ffi::XK_Byelorussian_shortu => VirtualKeyCode::Byelorussian_shortu, -// //ffi::XK_Cyrillic_dzhe => VirtualKeyCode::Cyrillic_dzhe, -// //ffi::XK_Serbian_dze => VirtualKeyCode::Serbian_dze, -// //ffi::XK_numerosign => VirtualKeyCode::Numerosign, -// //ffi::XK_Serbian_DJE => VirtualKeyCode::Serbian_dje, -// //ffi::XK_Macedonia_GJE => VirtualKeyCode::Macedonia_gje, -// //ffi::XK_Cyrillic_IO => VirtualKeyCode::Cyrillic_io, -// //ffi::XK_Ukrainian_IE => VirtualKeyCode::Ukrainian_ie, -// //ffi::XK_Ukranian_JE => VirtualKeyCode::Ukranian_je, -// //ffi::XK_Macedonia_DSE => VirtualKeyCode::Macedonia_dse, -// //ffi::XK_Ukrainian_I => VirtualKeyCode::Ukrainian_i, -// //ffi::XK_Ukranian_I => VirtualKeyCode::Ukranian_i, -// //ffi::XK_Ukrainian_YI => VirtualKeyCode::Ukrainian_yi, -// //ffi::XK_Ukranian_YI => VirtualKeyCode::Ukranian_yi, -// //ffi::XK_Cyrillic_JE => VirtualKeyCode::Cyrillic_je, -// //ffi::XK_Serbian_JE => VirtualKeyCode::Serbian_je, -// //ffi::XK_Cyrillic_LJE => VirtualKeyCode::Cyrillic_lje, -// //ffi::XK_Serbian_LJE => VirtualKeyCode::Serbian_lje, -// //ffi::XK_Cyrillic_NJE => VirtualKeyCode::Cyrillic_nje, -// //ffi::XK_Serbian_NJE => VirtualKeyCode::Serbian_nje, -// //ffi::XK_Serbian_TSHE => VirtualKeyCode::Serbian_tshe, -// //ffi::XK_Macedonia_KJE => VirtualKeyCode::Macedonia_kje, -// //ffi::XK_Byelorussian_SHORTU => VirtualKeyCode::Byelorussian_shortu, -// //ffi::XK_Cyrillic_DZHE => VirtualKeyCode::Cyrillic_dzhe, -// //ffi::XK_Serbian_DZE => VirtualKeyCode::Serbian_dze, -// //ffi::XK_Cyrillic_yu => VirtualKeyCode::Cyrillic_yu, -// //ffi::XK_Cyrillic_a => VirtualKeyCode::Cyrillic_a, -// //ffi::XK_Cyrillic_be => VirtualKeyCode::Cyrillic_be, -// //ffi::XK_Cyrillic_tse => VirtualKeyCode::Cyrillic_tse, -// //ffi::XK_Cyrillic_de => VirtualKeyCode::Cyrillic_de, -// //ffi::XK_Cyrillic_ie => VirtualKeyCode::Cyrillic_ie, -// //ffi::XK_Cyrillic_ef => VirtualKeyCode::Cyrillic_ef, -// //ffi::XK_Cyrillic_ghe => VirtualKeyCode::Cyrillic_ghe, -// //ffi::XK_Cyrillic_ha => VirtualKeyCode::Cyrillic_ha, -// //ffi::XK_Cyrillic_i => VirtualKeyCode::Cyrillic_i, -// //ffi::XK_Cyrillic_shorti => VirtualKeyCode::Cyrillic_shorti, -// //ffi::XK_Cyrillic_ka => VirtualKeyCode::Cyrillic_ka, -// //ffi::XK_Cyrillic_el => VirtualKeyCode::Cyrillic_el, -// //ffi::XK_Cyrillic_em => VirtualKeyCode::Cyrillic_em, -// //ffi::XK_Cyrillic_en => VirtualKeyCode::Cyrillic_en, -// //ffi::XK_Cyrillic_o => VirtualKeyCode::Cyrillic_o, -// //ffi::XK_Cyrillic_pe => VirtualKeyCode::Cyrillic_pe, -// //ffi::XK_Cyrillic_ya => VirtualKeyCode::Cyrillic_ya, -// //ffi::XK_Cyrillic_er => VirtualKeyCode::Cyrillic_er, -// //ffi::XK_Cyrillic_es => VirtualKeyCode::Cyrillic_es, -// //ffi::XK_Cyrillic_te => VirtualKeyCode::Cyrillic_te, -// //ffi::XK_Cyrillic_u => VirtualKeyCode::Cyrillic_u, -// //ffi::XK_Cyrillic_zhe => VirtualKeyCode::Cyrillic_zhe, -// //ffi::XK_Cyrillic_ve => VirtualKeyCode::Cyrillic_ve, -// //ffi::XK_Cyrillic_softsign => VirtualKeyCode::Cyrillic_softsign, -// //ffi::XK_Cyrillic_yeru => VirtualKeyCode::Cyrillic_yeru, -// //ffi::XK_Cyrillic_ze => VirtualKeyCode::Cyrillic_ze, -// //ffi::XK_Cyrillic_sha => VirtualKeyCode::Cyrillic_sha, -// //ffi::XK_Cyrillic_e => VirtualKeyCode::Cyrillic_e, -// //ffi::XK_Cyrillic_shcha => VirtualKeyCode::Cyrillic_shcha, -// //ffi::XK_Cyrillic_che => VirtualKeyCode::Cyrillic_che, -// //ffi::XK_Cyrillic_hardsign => VirtualKeyCode::Cyrillic_hardsign, -// //ffi::XK_Cyrillic_YU => VirtualKeyCode::Cyrillic_yu, -// //ffi::XK_Cyrillic_A => VirtualKeyCode::Cyrillic_a, -// //ffi::XK_Cyrillic_BE => VirtualKeyCode::Cyrillic_be, -// //ffi::XK_Cyrillic_TSE => VirtualKeyCode::Cyrillic_tse, -// //ffi::XK_Cyrillic_DE => VirtualKeyCode::Cyrillic_de, -// //ffi::XK_Cyrillic_IE => VirtualKeyCode::Cyrillic_ie, -// //ffi::XK_Cyrillic_EF => VirtualKeyCode::Cyrillic_ef, -// //ffi::XK_Cyrillic_GHE => VirtualKeyCode::Cyrillic_ghe, -// //ffi::XK_Cyrillic_HA => VirtualKeyCode::Cyrillic_ha, -// //ffi::XK_Cyrillic_I => VirtualKeyCode::Cyrillic_i, -// //ffi::XK_Cyrillic_SHORTI => VirtualKeyCode::Cyrillic_shorti, -// //ffi::XK_Cyrillic_KA => VirtualKeyCode::Cyrillic_ka, -// //ffi::XK_Cyrillic_EL => VirtualKeyCode::Cyrillic_el, -// //ffi::XK_Cyrillic_EM => VirtualKeyCode::Cyrillic_em, -// //ffi::XK_Cyrillic_EN => VirtualKeyCode::Cyrillic_en, -// //ffi::XK_Cyrillic_O => VirtualKeyCode::Cyrillic_o, -// //ffi::XK_Cyrillic_PE => VirtualKeyCode::Cyrillic_pe, -// //ffi::XK_Cyrillic_YA => VirtualKeyCode::Cyrillic_ya, -// //ffi::XK_Cyrillic_ER => VirtualKeyCode::Cyrillic_er, -// //ffi::XK_Cyrillic_ES => VirtualKeyCode::Cyrillic_es, -// //ffi::XK_Cyrillic_TE => VirtualKeyCode::Cyrillic_te, -// //ffi::XK_Cyrillic_U => VirtualKeyCode::Cyrillic_u, -// //ffi::XK_Cyrillic_ZHE => VirtualKeyCode::Cyrillic_zhe, -// //ffi::XK_Cyrillic_VE => VirtualKeyCode::Cyrillic_ve, -// //ffi::XK_Cyrillic_SOFTSIGN => VirtualKeyCode::Cyrillic_softsign, -// //ffi::XK_Cyrillic_YERU => VirtualKeyCode::Cyrillic_yeru, -// //ffi::XK_Cyrillic_ZE => VirtualKeyCode::Cyrillic_ze, -// //ffi::XK_Cyrillic_SHA => VirtualKeyCode::Cyrillic_sha, -// //ffi::XK_Cyrillic_E => VirtualKeyCode::Cyrillic_e, -// //ffi::XK_Cyrillic_SHCHA => VirtualKeyCode::Cyrillic_shcha, -// //ffi::XK_Cyrillic_CHE => VirtualKeyCode::Cyrillic_che, -// //ffi::XK_Cyrillic_HARDSIGN => VirtualKeyCode::Cyrillic_hardsign, -// //ffi::XK_Greek_ALPHAaccent => VirtualKeyCode::Greek_alphaaccent, -// //ffi::XK_Greek_EPSILONaccent => VirtualKeyCode::Greek_epsilonaccent, -// //ffi::XK_Greek_ETAaccent => VirtualKeyCode::Greek_etaaccent, -// //ffi::XK_Greek_IOTAaccent => VirtualKeyCode::Greek_iotaaccent, -// //ffi::XK_Greek_IOTAdiaeresis => VirtualKeyCode::Greek_iotadiaeresis, -// //ffi::XK_Greek_OMICRONaccent => VirtualKeyCode::Greek_omicronaccent, -// //ffi::XK_Greek_UPSILONaccent => VirtualKeyCode::Greek_upsilonaccent, -// //ffi::XK_Greek_UPSILONdieresis => VirtualKeyCode::Greek_upsilondieresis, -// //ffi::XK_Greek_OMEGAaccent => VirtualKeyCode::Greek_omegaaccent, -// //ffi::XK_Greek_accentdieresis => VirtualKeyCode::Greek_accentdieresis, -// //ffi::XK_Greek_horizbar => VirtualKeyCode::Greek_horizbar, -// //ffi::XK_Greek_alphaaccent => VirtualKeyCode::Greek_alphaaccent, -// //ffi::XK_Greek_epsilonaccent => VirtualKeyCode::Greek_epsilonaccent, -// //ffi::XK_Greek_etaaccent => VirtualKeyCode::Greek_etaaccent, -// //ffi::XK_Greek_iotaaccent => VirtualKeyCode::Greek_iotaaccent, -// //ffi::XK_Greek_iotadieresis => VirtualKeyCode::Greek_iotadieresis, -// //ffi::XK_Greek_iotaaccentdieresis => VirtualKeyCode::Greek_iotaaccentdieresis, -// //ffi::XK_Greek_omicronaccent => VirtualKeyCode::Greek_omicronaccent, -// //ffi::XK_Greek_upsilonaccent => VirtualKeyCode::Greek_upsilonaccent, -// //ffi::XK_Greek_upsilondieresis => VirtualKeyCode::Greek_upsilondieresis, -// //ffi::XK_Greek_upsilonaccentdieresis => VirtualKeyCode::Greek_upsilonaccentdieresis, -// //ffi::XK_Greek_omegaaccent => VirtualKeyCode::Greek_omegaaccent, -// //ffi::XK_Greek_ALPHA => VirtualKeyCode::Greek_alpha, -// //ffi::XK_Greek_BETA => VirtualKeyCode::Greek_beta, -// //ffi::XK_Greek_GAMMA => VirtualKeyCode::Greek_gamma, -// //ffi::XK_Greek_DELTA => VirtualKeyCode::Greek_delta, -// //ffi::XK_Greek_EPSILON => VirtualKeyCode::Greek_epsilon, -// //ffi::XK_Greek_ZETA => VirtualKeyCode::Greek_zeta, -// //ffi::XK_Greek_ETA => VirtualKeyCode::Greek_eta, -// //ffi::XK_Greek_THETA => VirtualKeyCode::Greek_theta, -// //ffi::XK_Greek_IOTA => VirtualKeyCode::Greek_iota, -// //ffi::XK_Greek_KAPPA => VirtualKeyCode::Greek_kappa, -// //ffi::XK_Greek_LAMDA => VirtualKeyCode::Greek_lamda, -// //ffi::XK_Greek_LAMBDA => VirtualKeyCode::Greek_lambda, -// //ffi::XK_Greek_MU => VirtualKeyCode::Greek_mu, -// //ffi::XK_Greek_NU => VirtualKeyCode::Greek_nu, -// //ffi::XK_Greek_XI => VirtualKeyCode::Greek_xi, -// //ffi::XK_Greek_OMICRON => VirtualKeyCode::Greek_omicron, -// //ffi::XK_Greek_PI => VirtualKeyCode::Greek_pi, -// //ffi::XK_Greek_RHO => VirtualKeyCode::Greek_rho, -// //ffi::XK_Greek_SIGMA => VirtualKeyCode::Greek_sigma, -// //ffi::XK_Greek_TAU => VirtualKeyCode::Greek_tau, -// //ffi::XK_Greek_UPSILON => VirtualKeyCode::Greek_upsilon, -// //ffi::XK_Greek_PHI => VirtualKeyCode::Greek_phi, -// //ffi::XK_Greek_CHI => VirtualKeyCode::Greek_chi, -// //ffi::XK_Greek_PSI => VirtualKeyCode::Greek_psi, -// //ffi::XK_Greek_OMEGA => VirtualKeyCode::Greek_omega, -// //ffi::XK_Greek_alpha => VirtualKeyCode::Greek_alpha, -// //ffi::XK_Greek_beta => VirtualKeyCode::Greek_beta, -// //ffi::XK_Greek_gamma => VirtualKeyCode::Greek_gamma, -// //ffi::XK_Greek_delta => VirtualKeyCode::Greek_delta, -// //ffi::XK_Greek_epsilon => VirtualKeyCode::Greek_epsilon, -// //ffi::XK_Greek_zeta => VirtualKeyCode::Greek_zeta, -// //ffi::XK_Greek_eta => VirtualKeyCode::Greek_eta, -// //ffi::XK_Greek_theta => VirtualKeyCode::Greek_theta, -// //ffi::XK_Greek_iota => VirtualKeyCode::Greek_iota, -// //ffi::XK_Greek_kappa => VirtualKeyCode::Greek_kappa, -// //ffi::XK_Greek_lamda => VirtualKeyCode::Greek_lamda, -// //ffi::XK_Greek_lambda => VirtualKeyCode::Greek_lambda, -// //ffi::XK_Greek_mu => VirtualKeyCode::Greek_mu, -// //ffi::XK_Greek_nu => VirtualKeyCode::Greek_nu, -// //ffi::XK_Greek_xi => VirtualKeyCode::Greek_xi, -// //ffi::XK_Greek_omicron => VirtualKeyCode::Greek_omicron, -// //ffi::XK_Greek_pi => VirtualKeyCode::Greek_pi, -// //ffi::XK_Greek_rho => VirtualKeyCode::Greek_rho, -// //ffi::XK_Greek_sigma => VirtualKeyCode::Greek_sigma, -// //ffi::XK_Greek_finalsmallsigma => VirtualKeyCode::Greek_finalsmallsigma, -// //ffi::XK_Greek_tau => VirtualKeyCode::Greek_tau, -// //ffi::XK_Greek_upsilon => VirtualKeyCode::Greek_upsilon, -// //ffi::XK_Greek_phi => VirtualKeyCode::Greek_phi, -// //ffi::XK_Greek_chi => VirtualKeyCode::Greek_chi, -// //ffi::XK_Greek_psi => VirtualKeyCode::Greek_psi, -// //ffi::XK_Greek_omega => VirtualKeyCode::Greek_omega, -// //ffi::XK_Greek_switch => VirtualKeyCode::Greek_switch, -// //ffi::XK_leftradical => VirtualKeyCode::Leftradical, -// //ffi::XK_topleftradical => VirtualKeyCode::Topleftradical, -// //ffi::XK_horizconnector => VirtualKeyCode::Horizconnector, -// //ffi::XK_topintegral => VirtualKeyCode::Topintegral, -// //ffi::XK_botintegral => VirtualKeyCode::Botintegral, -// //ffi::XK_vertconnector => VirtualKeyCode::Vertconnector, -// //ffi::XK_topleftsqbracket => VirtualKeyCode::Topleftsqbracket, -// //ffi::XK_botleftsqbracket => VirtualKeyCode::Botleftsqbracket, -// //ffi::XK_toprightsqbracket => VirtualKeyCode::Toprightsqbracket, -// //ffi::XK_botrightsqbracket => VirtualKeyCode::Botrightsqbracket, -// //ffi::XK_topleftparens => VirtualKeyCode::Topleftparens, -// //ffi::XK_botleftparens => VirtualKeyCode::Botleftparens, -// //ffi::XK_toprightparens => VirtualKeyCode::Toprightparens, -// //ffi::XK_botrightparens => VirtualKeyCode::Botrightparens, -// //ffi::XK_leftmiddlecurlybrace => VirtualKeyCode::Leftmiddlecurlybrace, -// //ffi::XK_rightmiddlecurlybrace => VirtualKeyCode::Rightmiddlecurlybrace, -// //ffi::XK_topleftsummation => VirtualKeyCode::Topleftsummation, -// //ffi::XK_botleftsummation => VirtualKeyCode::Botleftsummation, -// //ffi::XK_topvertsummationconnector => VirtualKeyCode::Topvertsummationconnector, -// //ffi::XK_botvertsummationconnector => VirtualKeyCode::Botvertsummationconnector, -// //ffi::XK_toprightsummation => VirtualKeyCode::Toprightsummation, -// //ffi::XK_botrightsummation => VirtualKeyCode::Botrightsummation, -// //ffi::XK_rightmiddlesummation => VirtualKeyCode::Rightmiddlesummation, -// //ffi::XK_lessthanequal => VirtualKeyCode::Lessthanequal, -// //ffi::XK_notequal => VirtualKeyCode::Notequal, -// //ffi::XK_greaterthanequal => VirtualKeyCode::Greaterthanequal, -// //ffi::XK_integral => VirtualKeyCode::Integral, -// //ffi::XK_therefore => VirtualKeyCode::Therefore, -// //ffi::XK_variation => VirtualKeyCode::Variation, -// //ffi::XK_infinity => VirtualKeyCode::Infinity, -// //ffi::XK_nabla => VirtualKeyCode::Nabla, -// //ffi::XK_approximate => VirtualKeyCode::Approximate, -// //ffi::XK_similarequal => VirtualKeyCode::Similarequal, -// //ffi::XK_ifonlyif => VirtualKeyCode::Ifonlyif, -// //ffi::XK_implies => VirtualKeyCode::Implies, -// //ffi::XK_identical => VirtualKeyCode::Identical, -// //ffi::XK_radical => VirtualKeyCode::Radical, -// //ffi::XK_includedin => VirtualKeyCode::Includedin, -// //ffi::XK_includes => VirtualKeyCode::Includes, -// //ffi::XK_intersection => VirtualKeyCode::Intersection, -// //ffi::XK_union => VirtualKeyCode::Union, -// //ffi::XK_logicaland => VirtualKeyCode::Logicaland, -// //ffi::XK_logicalor => VirtualKeyCode::Logicalor, -// //ffi::XK_partialderivative => VirtualKeyCode::Partialderivative, -// //ffi::XK_function => VirtualKeyCode::Function, -// //ffi::XK_leftarrow => VirtualKeyCode::Leftarrow, -// //ffi::XK_uparrow => VirtualKeyCode::Uparrow, -// //ffi::XK_rightarrow => VirtualKeyCode::Rightarrow, -// //ffi::XK_downarrow => VirtualKeyCode::Downarrow, -// //ffi::XK_blank => VirtualKeyCode::Blank, -// //ffi::XK_soliddiamond => VirtualKeyCode::Soliddiamond, -// //ffi::XK_checkerboard => VirtualKeyCode::Checkerboard, -// //ffi::XK_ht => VirtualKeyCode::Ht, -// //ffi::XK_ff => VirtualKeyCode::Ff, -// //ffi::XK_cr => VirtualKeyCode::Cr, -// //ffi::XK_lf => VirtualKeyCode::Lf, -// //ffi::XK_nl => VirtualKeyCode::Nl, -// //ffi::XK_vt => VirtualKeyCode::Vt, -// //ffi::XK_lowrightcorner => VirtualKeyCode::Lowrightcorner, -// //ffi::XK_uprightcorner => VirtualKeyCode::Uprightcorner, -// //ffi::XK_upleftcorner => VirtualKeyCode::Upleftcorner, -// //ffi::XK_lowleftcorner => VirtualKeyCode::Lowleftcorner, -// //ffi::XK_crossinglines => VirtualKeyCode::Crossinglines, -// //ffi::XK_horizlinescan1 => VirtualKeyCode::Horizlinescan1, -// //ffi::XK_horizlinescan3 => VirtualKeyCode::Horizlinescan3, -// //ffi::XK_horizlinescan5 => VirtualKeyCode::Horizlinescan5, -// //ffi::XK_horizlinescan7 => VirtualKeyCode::Horizlinescan7, -// //ffi::XK_horizlinescan9 => VirtualKeyCode::Horizlinescan9, -// //ffi::XK_leftt => VirtualKeyCode::Leftt, -// //ffi::XK_rightt => VirtualKeyCode::Rightt, -// //ffi::XK_bott => VirtualKeyCode::Bott, -// //ffi::XK_topt => VirtualKeyCode::Topt, -// //ffi::XK_vertbar => VirtualKeyCode::Vertbar, -// //ffi::XK_emspace => VirtualKeyCode::Emspace, -// //ffi::XK_enspace => VirtualKeyCode::Enspace, -// //ffi::XK_em3space => VirtualKeyCode::Em3space, -// //ffi::XK_em4space => VirtualKeyCode::Em4space, -// //ffi::XK_digitspace => VirtualKeyCode::Digitspace, -// //ffi::XK_punctspace => VirtualKeyCode::Punctspace, -// //ffi::XK_thinspace => VirtualKeyCode::Thinspace, -// //ffi::XK_hairspace => VirtualKeyCode::Hairspace, -// //ffi::XK_emdash => VirtualKeyCode::Emdash, -// //ffi::XK_endash => VirtualKeyCode::Endash, -// //ffi::XK_signifblank => VirtualKeyCode::Signifblank, -// //ffi::XK_ellipsis => VirtualKeyCode::Ellipsis, -// //ffi::XK_doubbaselinedot => VirtualKeyCode::Doubbaselinedot, -// //ffi::XK_onethird => VirtualKeyCode::Onethird, -// //ffi::XK_twothirds => VirtualKeyCode::Twothirds, -// //ffi::XK_onefifth => VirtualKeyCode::Onefifth, -// //ffi::XK_twofifths => VirtualKeyCode::Twofifths, -// //ffi::XK_threefifths => VirtualKeyCode::Threefifths, -// //ffi::XK_fourfifths => VirtualKeyCode::Fourfifths, -// //ffi::XK_onesixth => VirtualKeyCode::Onesixth, -// //ffi::XK_fivesixths => VirtualKeyCode::Fivesixths, -// //ffi::XK_careof => VirtualKeyCode::Careof, -// //ffi::XK_figdash => VirtualKeyCode::Figdash, -// //ffi::XK_leftanglebracket => VirtualKeyCode::Leftanglebracket, -// //ffi::XK_decimalpoint => VirtualKeyCode::Decimalpoint, -// //ffi::XK_rightanglebracket => VirtualKeyCode::Rightanglebracket, -// //ffi::XK_marker => VirtualKeyCode::Marker, -// //ffi::XK_oneeighth => VirtualKeyCode::Oneeighth, -// //ffi::XK_threeeighths => VirtualKeyCode::Threeeighths, -// //ffi::XK_fiveeighths => VirtualKeyCode::Fiveeighths, -// //ffi::XK_seveneighths => VirtualKeyCode::Seveneighths, -// //ffi::XK_trademark => VirtualKeyCode::Trademark, -// //ffi::XK_signaturemark => VirtualKeyCode::Signaturemark, -// //ffi::XK_trademarkincircle => VirtualKeyCode::Trademarkincircle, -// //ffi::XK_leftopentriangle => VirtualKeyCode::Leftopentriangle, -// //ffi::XK_rightopentriangle => VirtualKeyCode::Rightopentriangle, -// //ffi::XK_emopencircle => VirtualKeyCode::Emopencircle, -// //ffi::XK_emopenrectangle => VirtualKeyCode::Emopenrectangle, -// //ffi::XK_leftsinglequotemark => VirtualKeyCode::Leftsinglequotemark, -// //ffi::XK_rightsinglequotemark => VirtualKeyCode::Rightsinglequotemark, -// //ffi::XK_leftdoublequotemark => VirtualKeyCode::Leftdoublequotemark, -// //ffi::XK_rightdoublequotemark => VirtualKeyCode::Rightdoublequotemark, -// //ffi::XK_prescription => VirtualKeyCode::Prescription, -// //ffi::XK_minutes => VirtualKeyCode::Minutes, -// //ffi::XK_seconds => VirtualKeyCode::Seconds, -// //ffi::XK_latincross => VirtualKeyCode::Latincross, -// //ffi::XK_hexagram => VirtualKeyCode::Hexagram, -// //ffi::XK_filledrectbullet => VirtualKeyCode::Filledrectbullet, -// //ffi::XK_filledlefttribullet => VirtualKeyCode::Filledlefttribullet, -// //ffi::XK_filledrighttribullet => VirtualKeyCode::Filledrighttribullet, -// //ffi::XK_emfilledcircle => VirtualKeyCode::Emfilledcircle, -// //ffi::XK_emfilledrect => VirtualKeyCode::Emfilledrect, -// //ffi::XK_enopencircbullet => VirtualKeyCode::Enopencircbullet, -// //ffi::XK_enopensquarebullet => VirtualKeyCode::Enopensquarebullet, -// //ffi::XK_openrectbullet => VirtualKeyCode::Openrectbullet, -// //ffi::XK_opentribulletup => VirtualKeyCode::Opentribulletup, -// //ffi::XK_opentribulletdown => VirtualKeyCode::Opentribulletdown, -// //ffi::XK_openstar => VirtualKeyCode::Openstar, -// //ffi::XK_enfilledcircbullet => VirtualKeyCode::Enfilledcircbullet, -// //ffi::XK_enfilledsqbullet => VirtualKeyCode::Enfilledsqbullet, -// //ffi::XK_filledtribulletup => VirtualKeyCode::Filledtribulletup, -// //ffi::XK_filledtribulletdown => VirtualKeyCode::Filledtribulletdown, -// //ffi::XK_leftpointer => VirtualKeyCode::Leftpointer, -// //ffi::XK_rightpointer => VirtualKeyCode::Rightpointer, -// //ffi::XK_club => VirtualKeyCode::Club, -// //ffi::XK_diamond => VirtualKeyCode::Diamond, -// //ffi::XK_heart => VirtualKeyCode::Heart, -// //ffi::XK_maltesecross => VirtualKeyCode::Maltesecross, -// //ffi::XK_dagger => VirtualKeyCode::Dagger, -// //ffi::XK_doubledagger => VirtualKeyCode::Doubledagger, -// //ffi::XK_checkmark => VirtualKeyCode::Checkmark, -// //ffi::XK_ballotcross => VirtualKeyCode::Ballotcross, -// //ffi::XK_musicalsharp => VirtualKeyCode::Musicalsharp, -// //ffi::XK_musicalflat => VirtualKeyCode::Musicalflat, -// //ffi::XK_malesymbol => VirtualKeyCode::Malesymbol, -// //ffi::XK_femalesymbol => VirtualKeyCode::Femalesymbol, -// //ffi::XK_telephone => VirtualKeyCode::Telephone, -// //ffi::XK_telephonerecorder => VirtualKeyCode::Telephonerecorder, -// //ffi::XK_phonographcopyright => VirtualKeyCode::Phonographcopyright, -// //ffi::XK_caret => VirtualKeyCode::Caret, -// //ffi::XK_singlelowquotemark => VirtualKeyCode::Singlelowquotemark, -// //ffi::XK_doublelowquotemark => VirtualKeyCode::Doublelowquotemark, -// //ffi::XK_cursor => VirtualKeyCode::Cursor, -// //ffi::XK_leftcaret => VirtualKeyCode::Leftcaret, -// //ffi::XK_rightcaret => VirtualKeyCode::Rightcaret, -// //ffi::XK_downcaret => VirtualKeyCode::Downcaret, -// //ffi::XK_upcaret => VirtualKeyCode::Upcaret, -// //ffi::XK_overbar => VirtualKeyCode::Overbar, -// //ffi::XK_downtack => VirtualKeyCode::Downtack, -// //ffi::XK_upshoe => VirtualKeyCode::Upshoe, -// //ffi::XK_downstile => VirtualKeyCode::Downstile, -// //ffi::XK_underbar => VirtualKeyCode::Underbar, -// //ffi::XK_jot => VirtualKeyCode::Jot, -// //ffi::XK_quad => VirtualKeyCode::Quad, -// //ffi::XK_uptack => VirtualKeyCode::Uptack, -// //ffi::XK_circle => VirtualKeyCode::Circle, -// //ffi::XK_upstile => VirtualKeyCode::Upstile, -// //ffi::XK_downshoe => VirtualKeyCode::Downshoe, -// //ffi::XK_rightshoe => VirtualKeyCode::Rightshoe, -// //ffi::XK_leftshoe => VirtualKeyCode::Leftshoe, -// //ffi::XK_lefttack => VirtualKeyCode::Lefttack, -// //ffi::XK_righttack => VirtualKeyCode::Righttack, -// //ffi::XK_hebrew_doublelowline => VirtualKeyCode::Hebrew_doublelowline, -// //ffi::XK_hebrew_aleph => VirtualKeyCode::Hebrew_aleph, -// //ffi::XK_hebrew_bet => VirtualKeyCode::Hebrew_bet, -// //ffi::XK_hebrew_beth => VirtualKeyCode::Hebrew_beth, -// //ffi::XK_hebrew_gimel => VirtualKeyCode::Hebrew_gimel, -// //ffi::XK_hebrew_gimmel => VirtualKeyCode::Hebrew_gimmel, -// //ffi::XK_hebrew_dalet => VirtualKeyCode::Hebrew_dalet, -// //ffi::XK_hebrew_daleth => VirtualKeyCode::Hebrew_daleth, -// //ffi::XK_hebrew_he => VirtualKeyCode::Hebrew_he, -// //ffi::XK_hebrew_waw => VirtualKeyCode::Hebrew_waw, -// //ffi::XK_hebrew_zain => VirtualKeyCode::Hebrew_zain, -// //ffi::XK_hebrew_zayin => VirtualKeyCode::Hebrew_zayin, -// //ffi::XK_hebrew_chet => VirtualKeyCode::Hebrew_chet, -// //ffi::XK_hebrew_het => VirtualKeyCode::Hebrew_het, -// //ffi::XK_hebrew_tet => VirtualKeyCode::Hebrew_tet, -// //ffi::XK_hebrew_teth => VirtualKeyCode::Hebrew_teth, -// //ffi::XK_hebrew_yod => VirtualKeyCode::Hebrew_yod, -// //ffi::XK_hebrew_finalkaph => VirtualKeyCode::Hebrew_finalkaph, -// //ffi::XK_hebrew_kaph => VirtualKeyCode::Hebrew_kaph, -// //ffi::XK_hebrew_lamed => VirtualKeyCode::Hebrew_lamed, -// //ffi::XK_hebrew_finalmem => VirtualKeyCode::Hebrew_finalmem, -// //ffi::XK_hebrew_mem => VirtualKeyCode::Hebrew_mem, -// //ffi::XK_hebrew_finalnun => VirtualKeyCode::Hebrew_finalnun, -// //ffi::XK_hebrew_nun => VirtualKeyCode::Hebrew_nun, -// //ffi::XK_hebrew_samech => VirtualKeyCode::Hebrew_samech, -// //ffi::XK_hebrew_samekh => VirtualKeyCode::Hebrew_samekh, -// //ffi::XK_hebrew_ayin => VirtualKeyCode::Hebrew_ayin, -// //ffi::XK_hebrew_finalpe => VirtualKeyCode::Hebrew_finalpe, -// //ffi::XK_hebrew_pe => VirtualKeyCode::Hebrew_pe, -// //ffi::XK_hebrew_finalzade => VirtualKeyCode::Hebrew_finalzade, -// //ffi::XK_hebrew_finalzadi => VirtualKeyCode::Hebrew_finalzadi, -// //ffi::XK_hebrew_zade => VirtualKeyCode::Hebrew_zade, -// //ffi::XK_hebrew_zadi => VirtualKeyCode::Hebrew_zadi, -// //ffi::XK_hebrew_qoph => VirtualKeyCode::Hebrew_qoph, -// //ffi::XK_hebrew_kuf => VirtualKeyCode::Hebrew_kuf, -// //ffi::XK_hebrew_resh => VirtualKeyCode::Hebrew_resh, -// //ffi::XK_hebrew_shin => VirtualKeyCode::Hebrew_shin, -// //ffi::XK_hebrew_taw => VirtualKeyCode::Hebrew_taw, -// //ffi::XK_hebrew_taf => VirtualKeyCode::Hebrew_taf, -// //ffi::XK_Hebrew_switch => VirtualKeyCode::Hebrew_switch, -// ffi::XF86XK_Back => VirtualKeyCode::NavigateBackward, -// ffi::XF86XK_Forward => VirtualKeyCode::NavigateForward, -// ffi::XF86XK_Copy => VirtualKeyCode::Copy, -// ffi::XF86XK_Paste => VirtualKeyCode::Paste, -// ffi::XF86XK_Cut => VirtualKeyCode::Cut, -// _ => return None, -// }) -// } diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 4d013c0415..c53f5966d4 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -5,10 +5,10 @@ target_os = "netbsd", target_os = "openbsd" ))] +#![allow(non_fmt_panic)] mod dnd; mod event_processor; -mod events; pub mod ffi; mod ime; mod monitor; From 1ef0b490d793f5a9ed8e5143de575a22cef65adf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Art=C3=BAr=20Kov=C3=A1cs?= Date: Sun, 9 May 2021 19:29:19 +0200 Subject: [PATCH 096/131] Fix the `drag_window` example --- examples/drag_window.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/drag_window.rs b/examples/drag_window.rs index a408c7c722..58920a0928 100644 --- a/examples/drag_window.rs +++ b/examples/drag_window.rs @@ -1,9 +1,8 @@ use simple_logger::SimpleLogger; use winit::{ - event::{ - ElementState, Event, KeyboardInput, MouseButton, StartCause, VirtualKeyCode, WindowEvent, - }, + event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoop}, + keyboard::Key, window::{Window, WindowBuilder, WindowId}, }; @@ -43,10 +42,10 @@ fn main() { name_windows(entered_id, switched, &window_1, &window_2) } WindowEvent::KeyboardInput { - input: - KeyboardInput { + event: + KeyEvent { state: ElementState::Released, - virtual_keycode: Some(VirtualKeyCode::X), + logical_key: Key::Character("x"), .. }, .. From 3cde5258a5453602961ba312f34e1c0cb2a863fe Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Mon, 10 May 2021 19:45:39 +0200 Subject: [PATCH 097/131] Update xkbcommon-dl --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3fe68b1a63..edceff721c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,7 +95,7 @@ x11-dl = { version = "2.18.5", optional = true } percent-encoding = { version = "2.0", optional = true } parking_lot = { version = "0.11.0", optional = true } memmap2 = { version = "0.2.1", optional = true } -xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "5307184" } +xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "802388064d0e5775fa09578bcad45842874f08e0" } [target.'cfg(target_arch = "wasm32")'.dependencies.web_sys] package = "web-sys" From d0351ae15cd42ab72344acdbdd54e08391efe355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Sun, 16 May 2021 23:20:45 +0200 Subject: [PATCH 098/131] Update xkbcommon-dl again --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index edceff721c..e64e242a29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,7 +95,7 @@ x11-dl = { version = "2.18.5", optional = true } percent-encoding = { version = "2.0", optional = true } parking_lot = { version = "0.11.0", optional = true } memmap2 = { version = "0.2.1", optional = true } -xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "802388064d0e5775fa09578bcad45842874f08e0" } +xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "900832888ad6f11011d1369befb344a9aa8a9610" } [target.'cfg(target_arch = "wasm32")'.dependencies.web_sys] package = "web-sys" From 98e95ac6104ee137ef7869fd02da1c7c6189ade4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Sun, 16 May 2021 23:23:18 +0200 Subject: [PATCH 099/131] Remove accidentally included attribute --- src/platform_impl/linux/x11/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index c53f5966d4..b9ac09f324 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -5,7 +5,6 @@ target_os = "netbsd", target_os = "openbsd" ))] -#![allow(non_fmt_panic)] mod dnd; mod event_processor; From 26349ebdc0e62337e0c69709a02599f30329c6ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Sun, 16 May 2021 23:26:51 +0200 Subject: [PATCH 100/131] Use `as u32` to cast `i32`s to `u32`s --- src/platform_impl/linux/x11/event_processor.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 56e6d09a07..f00182dfc9 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1151,14 +1151,13 @@ impl EventProcessor { let xev = unsafe { &*(xev as *const _ as *const ffi::XkbStateNotifyEvent) }; if matches!(xev.event_type, 2 | 3) { - // TODO: Is `as u32` fine here? self.kb_state.update_modifiers( xev.base_mods, xev.latched_mods, xev.locked_mods, - u32::from_ne_bytes(xev.base_group.to_ne_bytes()), - u32::from_ne_bytes(xev.latched_group.to_ne_bytes()), - u32::from_ne_bytes(xev.locked_group.to_ne_bytes()), + xev.base_group as u32, + xev.latched_group as u32, + xev.locked_group as u32, ) } } From 1fabfa703c27853e6aea6d1c7700c0d482cededd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Sun, 16 May 2021 23:55:48 +0200 Subject: [PATCH 101/131] Put more things behind the "wayland" feature --- src/platform_impl/linux/common/xkb_state.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 7651d8370b..e3e187a388 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -8,6 +8,9 @@ use std::ptr; #[cfg(feature = "wayland")] use memmap2::MmapOptions; +#[cfg(feature = "wayland")] +pub use sctk::seat::keyboard::RMLVO; + #[cfg(feature = "x11")] use x11_dl::xlib_xcb::xcb_connection_t; #[cfg(feature = "x11")] @@ -17,8 +20,6 @@ use xkbcommon_dl::{ self as ffi, xkb_state_component, XKBCOMMON_COMPOSE_HANDLE as XKBCH, XKBCOMMON_HANDLE as XKBH, }; -pub use sctk::seat::keyboard::RMLVO; - use crate::{ event::ElementState, keyboard::{Key, KeyCode, KeyLocation}, @@ -317,6 +318,7 @@ impl KbState { Ok(me) } + #[cfg(feature = "wayland")] pub(crate) fn from_rmlvo(rmlvo: RMLVO) -> Result { fn to_cstring(s: Option) -> Result, Error> { s.map_or(Ok(None), |s| CString::new(s).map(Option::Some)) @@ -421,6 +423,7 @@ impl KbState { self.post_init(keymap); } + #[cfg(feature = "wayland")] pub(crate) unsafe fn init_with_rmlvo( &mut self, names: ffi::xkb_rule_names, From f86c5e95fc85d0fdcf346bdf1a64b71c20f40ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Sun, 16 May 2021 23:58:48 +0200 Subject: [PATCH 102/131] Remove superfluous scope --- src/platform_impl/linux/common/xkb_state.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index e3e187a388..bc1a388c10 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -252,11 +252,10 @@ impl KbState { } pub(crate) fn new() -> Result { - { - if ffi::XKBCOMMON_OPTION.as_ref().is_none() { - return Err(Error::XKBNotFound); - } + if ffi::XKBCOMMON_OPTION.as_ref().is_none() { + return Err(Error::XKBNotFound); } + let context = unsafe { (XKBH.xkb_context_new)(ffi::xkb_context_flags::XKB_CONTEXT_NO_FLAGS) }; if context.is_null() { From 416d8ee50184ea49cbf9725f8969c7c863e8811e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Wed, 16 Jun 2021 03:46:22 +0200 Subject: [PATCH 103/131] Separate out loading of the X11 keymap --- src/platform_impl/linux/common/xkb_state.rs | 38 ++++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index bc1a388c10..446975c6de 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -26,6 +26,8 @@ use crate::{ }; pub(crate) struct KbState { + #[cfg(feature = "x11")] + xcb_connection: *mut xcb_connection_t, xkb_context: *mut ffi::xkb_context, xkb_keymap: *mut ffi::xkb_keymap, xkb_state: *mut ffi::xkb_state, @@ -263,6 +265,8 @@ impl KbState { } let mut me = KbState { + #[cfg(feature = "x11")] + xcb_connection: ptr::null_mut(), xkb_context: context, xkb_keymap: ptr::null_mut(), xkb_state: ptr::null_mut(), @@ -284,6 +288,7 @@ impl KbState { #[cfg(feature = "x11")] pub(crate) fn from_x11_xkb(connection: *mut xcb_connection_t) -> Result { let mut me = Self::new()?; + me.xcb_connection = connection; let result = unsafe { (XKBXH.xkb_x11_setup_xkb_extension)( @@ -299,22 +304,29 @@ impl KbState { }; assert_eq!(result, 1, "Failed to initialize libxkbcommon"); + unsafe { me.load_x11_keymap() }; + + Ok(me) + } + + #[cfg(feature = "x11")] + pub(crate) unsafe fn load_x11_keymap(&mut self) { + if !self.xkb_keymap.is_null() { + self.de_init(); + } + // TODO: Support keyboards other than the "virtual core keyboard device". - let core_keyboard_id = unsafe { (XKBXH.xkb_x11_get_core_keyboard_device_id)(connection) }; - let keymap = unsafe { - (XKBXH.xkb_x11_keymap_new_from_device)( - me.xkb_context, - connection, - core_keyboard_id, - xkbcommon_dl::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS, - ) - }; + let core_keyboard_id = (XKBXH.xkb_x11_get_core_keyboard_device_id)(self.xcb_connection); + let keymap = (XKBXH.xkb_x11_keymap_new_from_device)( + self.xkb_context, + self.xcb_connection, + core_keyboard_id, + xkbcommon_dl::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS, + ); assert_ne!(keymap, ptr::null_mut()); - me.xkb_keymap = keymap; - - unsafe { me.post_init(keymap) }; + self.xkb_keymap = keymap; - Ok(me) + self.post_init(keymap); } #[cfg(feature = "wayland")] From 7b5a30997ccf7ca53923c5eaebcc60eaad0b7ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Wed, 16 Jun 2021 05:41:04 +0200 Subject: [PATCH 104/131] Wire up X11 IME support --- .../linux/x11/event_processor.rs | 19 +++++++++++++++++++ src/platform_impl/linux/x11/window.rs | 1 + 2 files changed, 20 insertions(+) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index f00182dfc9..c7d291e85b 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -548,6 +548,25 @@ impl EventProcessor { } } + ffi::KeyPress => { + // TODO: Is it possible to exclusively use XInput2 events here? + let xkev: &mut ffi::XKeyEvent = xev.as_mut(); + + let window = xkev.window; + let window_id = mkwid(window); + + let written = if let Some(ic) = wt.ime.borrow().get_context(window) { + wt.xconn.lookup_utf8(ic, xkev) + } else { + return; + }; + let event = Event::WindowEvent { + window_id, + event: WindowEvent::ReceivedImeText(written), + }; + callback(event); + } + ffi::GenericEvent => { let guard = if let Some(e) = GenericEventCookie::from_event(&wt.xconn, *xev) { e diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 68a6f01891..fb5d007d6d 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -193,6 +193,7 @@ impl UnownedWindow { swa.event_mask = ffi::ExposureMask | ffi::StructureNotifyMask | ffi::VisibilityChangeMask + | ffi::KeyPressMask | ffi::KeymapStateMask | ffi::ButtonPressMask | ffi::ButtonReleaseMask From 9486ac074a960dcc9f51071fbb5e38414647fd41 Mon Sep 17 00:00:00 2001 From: Artur Kovacs Date: Sun, 4 Jul 2021 21:46:04 +0200 Subject: [PATCH 105/131] Use the currently active keyboard layout on X11 --- src/platform_impl/linux/common/xkb_state.rs | 13 +++++++++++-- src/platform_impl/linux/x11/event_processor.rs | 18 +++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 446975c6de..8c82f2df06 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -325,8 +325,9 @@ impl KbState { ); assert_ne!(keymap, ptr::null_mut()); self.xkb_keymap = keymap; - - self.post_init(keymap); + self.xkb_state = + (XKBXH.xkb_x11_state_new_from_device)(keymap, self.xcb_connection, core_keyboard_id); + self.mods_state.update_with(self.xkb_state); } #[cfg(feature = "wayland")] @@ -567,6 +568,14 @@ impl<'a> KeyEventResults<'a> { None }; + // let key_text = state.keysym_to_utf8_raw(keysym); + // unsafe { + // let layout_id = (XKBH.xkb_state_serialize_layout)(state.xkb_state, xkb_state_component::XKB_STATE_LAYOUT_EFFECTIVE); + // let layout_name_cstr = (XKBH.xkb_keymap_layout_get_name)(state.xkb_keymap, layout_id); + // let layout_name = std::ffi::CStr::from_ptr(layout_name_cstr as *mut _); + // debug!("KeyEventResults::new {:?}, {:?}", key_text, layout_name); + // } + KeyEventResults { state, keycode, diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index c7d291e85b..be15fd6733 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1169,7 +1169,23 @@ impl EventProcessor { ffi::XkbStateNotify => { let xev = unsafe { &*(xev as *const _ as *const ffi::XkbStateNotifyEvent) }; - if matches!(xev.event_type, 2 | 3) { + debug!( + "XkbStateNotify. group: {}, event_type: {}", + xev.group, xev.event_type + ); + if xev.event_type == 0 { + // This probably indicates that the keyboard layout was switched. + // Let's update the groups (i.e. the layout) + self.kb_state.update_modifiers( + xev.base_mods, + xev.latched_mods, + xev.locked_mods, + xev.base_group as u32, + xev.latched_group as u32, + xev.locked_group as u32, + ); + } + if matches!(xev.event_type as i32, ffi::KeyPress | ffi::KeyRelease) { self.kb_state.update_modifiers( xev.base_mods, xev.latched_mods, From ce2cdad4cb66d65ad12db072fa44515fd6577ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Sun, 4 Jul 2021 23:22:59 +0200 Subject: [PATCH 106/131] fixup! Wire up X11 IME support --- .../linux/x11/event_processor.rs | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index be15fd6733..f9f78219ee 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -555,16 +555,21 @@ impl EventProcessor { let window = xkev.window; let window_id = mkwid(window); - let written = if let Some(ic) = wt.ime.borrow().get_context(window) { - wt.xconn.lookup_utf8(ic, xkev) - } else { - return; - }; - let event = Event::WindowEvent { - window_id, - event: WindowEvent::ReceivedImeText(written), - }; - callback(event); + let keycode = xkev.keycode; + // When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with + // a keycode of 0. + if keycode == 0 { + let written = if let Some(ic) = wt.ime.borrow().get_context(window) { + wt.xconn.lookup_utf8(ic, xkev) + } else { + return; + }; + let event = Event::WindowEvent { + window_id, + event: WindowEvent::ReceivedImeText(written), + }; + callback(event); + } } ffi::GenericEvent => { From 575f4c995b5787481131ce27d033332caa70088d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Tue, 3 Aug 2021 15:58:48 +0200 Subject: [PATCH 107/131] Enable keysym mapping for "Alt Graph" --- src/platform_impl/linux/common/keymap.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/linux/common/keymap.rs b/src/platform_impl/linux/common/keymap.rs index 4f72adc8f9..b54a1776ab 100644 --- a/src/platform_impl/linux/common/keymap.rs +++ b/src/platform_impl/linux/common/keymap.rs @@ -436,10 +436,10 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { // XKB function and modifier keys // keysyms::XKB_KEY_ISO_Lock => Key::IsoLock, // keysyms::XKB_KEY_ISO_Level2_Latch => Key::IsoLevel2Latch, - // NOTE: I'm not quite certain if mapping the next 3 values to AltGraph is correct. - // keysyms::XKB_KEY_ISO_Level3_Shift => Key::AltGraph, - // keysyms::XKB_KEY_ISO_Level3_Latch => Key::AltGraph, - // keysyms::XKB_KEY_ISO_Level3_Lock => Key::AltGraph, + keysyms::XKB_KEY_ISO_Level3_Shift => Key::AltGraph, + // NOTE: I'm not quite certain if mapping the next 2 values to AltGraph is correct. + keysyms::XKB_KEY_ISO_Level3_Latch => Key::AltGraph, + keysyms::XKB_KEY_ISO_Level3_Lock => Key::AltGraph, // keysyms::XKB_KEY_ISO_Level5_Shift => Key::IsoLevel5Shift, // keysyms::XKB_KEY_ISO_Level5_Latch => Key::IsoLevel5Latch, // keysyms::XKB_KEY_ISO_Level5_Lock => Key::IsoLevel5Lock, From e14070e6653b16346a986463cd7538553597da99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 5 Aug 2021 00:13:45 +0200 Subject: [PATCH 108/131] Actually respect keyboard layout changes 9486ac0 did not work for me --- .../linux/x11/event_processor.rs | 67 +++++++++---------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index f9f78219ee..2a362cf5b5 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -159,24 +159,6 @@ impl EventProcessor { let event_type = xev.get_type(); match event_type { - ffi::MappingNotify => { - let mapping: &ffi::XMappingEvent = xev.as_ref(); - - if mapping.request == ffi::MappingModifier - || mapping.request == ffi::MappingKeyboard - { - unsafe { - (wt.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut()); - } - wt.xconn - .check_errors() - .expect("Failed to call XRefreshKeyboardMapping"); - - self.mod_keymap.reset_from_x_connection(&wt.xconn); - self.device_mod_state.update_keymap(&self.mod_keymap); - } - } - ffi::ClientMessage => { let client_msg: &ffi::XClientMessageEvent = xev.as_ref(); @@ -1092,6 +1074,39 @@ impl EventProcessor { } ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => { + // This is horrible, but I couldn't manage to respect keyboard layout changes + // in any other way. In fact, getting this to work at all proved so frustrating + // that I (@maroider) lost motivation to work on the keyboard event rework for + // some months. Thankfully, @ArturKovacs offered to help debug the problem + // over discord, and the following is the result of that debugging session. + // + // Without the XKB extension, the X.Org server sends us the `MappingNotify` + // event when there's been a change in the keyboard layout. This stops + // being the case when we select ourselves some XKB events with `XkbSelectEvents` + // and the "core keyboard device (0x100)" (we haven't tried with any other + // devices). We managed to reproduce this on both our machines. + // + // With the XKB extension active, it would seem like we're supposed to use the + // `XkbStateNotify` event to detect keyboard layout changes, but the `group` + // never changes value (it is always `0`). This worked for @ArturKovacs, but + // not for me. We also tried to use the `group` given to us in keypress events, + // but it remained constant there, too. + // + // We also tried to see if there was some other event that got fired when the + // keyboard layout changed, and we found a mysterious event with the value + // `85` (`0x55`). We couldn't find any reference to it in the X11 headers or + // in the X.Org server source. + // + // `KeymapNotify` did briefly look interesting based purely on the name, but + // it is only useful for checking what keys are pressed when we receive the + // event. + // + // So instead of any vaguely reasonable approach, we get this: reloading the + // keymap on *every* keypress. That's peak efficiency right there! + // + // FIXME: Someone please save our souls! Or at least our wasted CPU cycles. + unsafe { self.kb_state.load_x11_keymap() }; + let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; let state = match xev.evtype { @@ -1174,22 +1189,6 @@ impl EventProcessor { ffi::XkbStateNotify => { let xev = unsafe { &*(xev as *const _ as *const ffi::XkbStateNotifyEvent) }; - debug!( - "XkbStateNotify. group: {}, event_type: {}", - xev.group, xev.event_type - ); - if xev.event_type == 0 { - // This probably indicates that the keyboard layout was switched. - // Let's update the groups (i.e. the layout) - self.kb_state.update_modifiers( - xev.base_mods, - xev.latched_mods, - xev.locked_mods, - xev.base_group as u32, - xev.latched_group as u32, - xev.locked_group as u32, - ); - } if matches!(xev.event_type as i32, ffi::KeyPress | ffi::KeyRelease) { self.kb_state.update_modifiers( xev.base_mods, From 965148b71439d21ed06cf4e79cd0e1ab889c740a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 5 Aug 2021 19:03:25 +0200 Subject: [PATCH 109/131] Make `xkb_state` more internally consistent --- src/platform_impl/linux/common/xkb_state.rs | 77 +++++++++++-------- .../linux/wayland/seat/keyboard/handlers.rs | 6 -- .../linux/x11/event_processor.rs | 2 +- 3 files changed, 44 insertions(+), 41 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 8c82f2df06..437e5ddf0a 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -64,8 +64,8 @@ pub struct ModifiersState { } impl ModifiersState { - fn new() -> ModifiersState { - ModifiersState::default() + fn new() -> Self { + Self::default() } fn update_with(&mut self, state: *mut ffi::xkb_state) { @@ -253,7 +253,7 @@ impl KbState { Some(byte_slice_to_cached_string(&self.scratch_buffer)) } - pub(crate) fn new() -> Result { + pub(crate) fn new() -> Result { if ffi::XKBCOMMON_OPTION.as_ref().is_none() { return Err(Error::XKBNotFound); } @@ -264,7 +264,7 @@ impl KbState { return Err(Error::XKBNotFound); } - let mut me = KbState { + let mut me = Self { #[cfg(feature = "x11")] xcb_connection: ptr::null_mut(), xkb_context: context, @@ -286,7 +286,7 @@ impl KbState { impl KbState { #[cfg(feature = "x11")] - pub(crate) fn from_x11_xkb(connection: *mut xcb_connection_t) -> Result { + pub(crate) fn from_x11_xkb(connection: *mut xcb_connection_t) -> Result { let mut me = Self::new()?; me.xcb_connection = connection; @@ -304,40 +304,19 @@ impl KbState { }; assert_eq!(result, 1, "Failed to initialize libxkbcommon"); - unsafe { me.load_x11_keymap() }; + unsafe { me.init_with_x11_keymap() }; Ok(me) } - #[cfg(feature = "x11")] - pub(crate) unsafe fn load_x11_keymap(&mut self) { - if !self.xkb_keymap.is_null() { - self.de_init(); - } - - // TODO: Support keyboards other than the "virtual core keyboard device". - let core_keyboard_id = (XKBXH.xkb_x11_get_core_keyboard_device_id)(self.xcb_connection); - let keymap = (XKBXH.xkb_x11_keymap_new_from_device)( - self.xkb_context, - self.xcb_connection, - core_keyboard_id, - xkbcommon_dl::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS, - ); - assert_ne!(keymap, ptr::null_mut()); - self.xkb_keymap = keymap; - self.xkb_state = - (XKBXH.xkb_x11_state_new_from_device)(keymap, self.xcb_connection, core_keyboard_id); - self.mods_state.update_with(self.xkb_state); - } - #[cfg(feature = "wayland")] - pub(crate) fn from_rmlvo(rmlvo: RMLVO) -> Result { + pub(crate) fn from_rmlvo(rmlvo: RMLVO) -> Result { fn to_cstring(s: Option) -> Result, Error> { s.map_or(Ok(None), |s| CString::new(s).map(Option::Some)) .map_err(|_| Error::BadNames) } - let mut state = KbState::new()?; + let mut state = Self::new()?; let rules = to_cstring(rmlvo.rules)?; let model = to_cstring(rmlvo.model)?; @@ -403,22 +382,46 @@ impl KbState { self.xkb_compose_state_2 = compose_state_2; } - pub(crate) unsafe fn post_init(&mut self, keymap: *mut ffi::xkb_keymap) { - let state = (XKBH.xkb_state_new)(keymap); + unsafe fn post_init(&mut self, state: *mut ffi::xkb_state, keymap: *mut ffi::xkb_keymap) { self.xkb_keymap = keymap; self.xkb_state = state; self.mods_state.update_with(state); } - pub(crate) unsafe fn de_init(&mut self) { + unsafe fn de_init(&mut self) { (XKBH.xkb_state_unref)(self.xkb_state); self.xkb_state = ptr::null_mut(); (XKBH.xkb_keymap_unref)(self.xkb_keymap); self.xkb_keymap = ptr::null_mut(); } + #[cfg(feature = "x11")] + pub(crate) unsafe fn init_with_x11_keymap(&mut self) { + if !self.xkb_keymap.is_null() { + self.de_init(); + } + + // TODO: Support keyboards other than the "virtual core keyboard device". + let core_keyboard_id = (XKBXH.xkb_x11_get_core_keyboard_device_id)(self.xcb_connection); + let keymap = (XKBXH.xkb_x11_keymap_new_from_device)( + self.xkb_context, + self.xcb_connection, + core_keyboard_id, + xkbcommon_dl::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS, + ); + assert_ne!(keymap, ptr::null_mut()); + + let state = + (XKBXH.xkb_x11_state_new_from_device)(keymap, self.xcb_connection, core_keyboard_id); + self.post_init(state, keymap); + } + #[cfg(feature = "wayland")] pub(crate) unsafe fn init_with_fd(&mut self, fd: File, size: usize) { + if !self.xkb_keymap.is_null() { + self.de_init(); + } + let map = MmapOptions::new().len(size).map(&fd).unwrap(); let keymap = (XKBH.xkb_keymap_new_from_string)( @@ -432,7 +435,8 @@ impl KbState { panic!("Received invalid keymap from compositor."); } - self.post_init(keymap); + let state = (XKBH.xkb_state_new)(keymap); + self.post_init(state, keymap); } #[cfg(feature = "wayland")] @@ -440,6 +444,10 @@ impl KbState { &mut self, names: ffi::xkb_rule_names, ) -> Result<(), Error> { + if !self.xkb_keymap.is_null() { + self.de_init(); + } + let keymap = (XKBH.xkb_keymap_new_from_names)( self.xkb_context, &names, @@ -450,7 +458,8 @@ impl KbState { return Err(Error::BadNames); } - self.post_init(keymap); + let state = (XKBH.xkb_state_new)(keymap); + self.post_init(state, keymap); Ok(()) } diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs index d167004c6f..d9057b59a1 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs @@ -470,12 +470,6 @@ impl KbdHandler { // state is locked, ignore keymap updates return; } - if state.ready() { - // new keymap, we first deinit to free resources - unsafe { - state.de_init(); - } - } match format { wl_keyboard::KeymapFormat::XkbV1 => unsafe { state.init_with_fd(fd, size as usize); diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 2a362cf5b5..5e38b7f86c 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1105,7 +1105,7 @@ impl EventProcessor { // keymap on *every* keypress. That's peak efficiency right there! // // FIXME: Someone please save our souls! Or at least our wasted CPU cycles. - unsafe { self.kb_state.load_x11_keymap() }; + unsafe { self.kb_state.init_with_x11_keymap() }; let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; From a48f9310a1cb41ea0e4fd012005304f4fcdfe71c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 5 Aug 2021 19:14:25 +0200 Subject: [PATCH 110/131] Improve the safety of `KbState` --- src/platform_impl/linux/common/xkb_state.rs | 31 +++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 437e5ddf0a..0b7ca1638a 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -366,14 +366,21 @@ impl KbState { ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, ); + if compose_table.is_null() { + // init of compose state failed, continue without compose + (XKBCH.xkb_compose_table_unref)(compose_table); + return; + } + let compose_state_2 = (XKBCH.xkb_compose_state_new)( compose_table, ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, ); - if compose_state.is_null() || compose_state_2.is_null() { + if compose_state_2.is_null() { // init of compose state failed, continue without compose (XKBCH.xkb_compose_table_unref)(compose_table); + (XKBCH.xkb_compose_state_unref)(compose_state); return; } @@ -489,10 +496,24 @@ impl KbState { impl Drop for KbState { fn drop(&mut self) { unsafe { - (XKBCH.xkb_compose_state_unref)(self.xkb_compose_state); - (XKBCH.xkb_compose_table_unref)(self.xkb_compose_table); - (XKBH.xkb_state_unref)(self.xkb_state); - (XKBH.xkb_keymap_unref)(self.xkb_keymap); + // TODO: Simplify this. We can currently only safely assume that the `xkb_context` + // is always valid. If we can somehow guarantee the same for `xkb_state` and + // `xkb_keymap`, then we could omit their null-checks. + if !self.xkb_compose_state.is_null() { + (XKBCH.xkb_compose_state_unref)(self.xkb_compose_state); + } + if !self.xkb_compose_state_2.is_null() { + (XKBCH.xkb_compose_state_unref)(self.xkb_compose_state_2); + } + if !self.xkb_compose_table.is_null() { + (XKBCH.xkb_compose_table_unref)(self.xkb_compose_table); + } + if !self.xkb_state.is_null() { + (XKBH.xkb_state_unref)(self.xkb_state); + } + if !self.xkb_keymap.is_null() { + (XKBH.xkb_keymap_unref)(self.xkb_keymap); + } (XKBH.xkb_context_unref)(self.xkb_context); } } From 4c1e41937515f0d047ab5037bc786ab86aadb7db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 5 Aug 2021 19:21:40 +0200 Subject: [PATCH 111/131] Make a decision on control characters and dead keys --- src/platform_impl/linux/common/xkb_state.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 0b7ca1638a..40403d02ab 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -698,9 +698,9 @@ impl<'a> KeyEventResults<'a> { } pub fn text_with_all_modifiers(&mut self) -> Option<&'static str> { - // TODO: Should Ctrl override any attempts to compose text? - // gnome-terminal agrees, but konsole disagrees. - // Should it be configurable instead? + // The current behaviour makes it so composing a character overrides attempts to input a + // control character with the `Ctrl` key. We can potentially add a configuration option + // if someone specifically wants the oppsite behaviour. self.composed_text() .unwrap_or_else(|_| self.state.get_utf8_raw(self.keycode)) } From cded2102c06643753b915c8c83f047e9d39b0588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 5 Aug 2021 19:56:09 +0200 Subject: [PATCH 112/131] Store keycodes with X11's keycode offset --- src/platform_impl/linux/common/keymap.rs | 3 ++- src/platform_impl/linux/common/xkb_state.rs | 16 ++++++++-------- .../linux/wayland/seat/keyboard/handlers.rs | 4 ++-- src/platform_impl/linux/x11/event_processor.rs | 11 +++++------ 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/platform_impl/linux/common/keymap.rs b/src/platform_impl/linux/common/keymap.rs index b54a1776ab..b64932e918 100644 --- a/src/platform_impl/linux/common/keymap.rs +++ b/src/platform_impl/linux/common/keymap.rs @@ -4,7 +4,8 @@ use crate::keyboard::{Key, KeyCode, KeyLocation, NativeKeyCode}; // TODO: Do another pass on all of this -pub fn rawkey_to_keycode(rawkey: u32) -> KeyCode { +pub fn raw_keycode_to_keycode(keycode: u32) -> KeyCode { + let rawkey = keycode - 8; // The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as // libxkbcommon's documentation indicates that the keycode values we're getting from it are // defined by the Linux kernel. If Winit programs end up being run on other Unix-likes which diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 40403d02ab..558a30303f 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -148,16 +148,16 @@ impl KbState { if !self.ready() { return 0; } - unsafe { (XKBH.xkb_state_key_get_one_sym)(self.xkb_state, keycode + 8) } + unsafe { (XKBH.xkb_state_key_get_one_sym)(self.xkb_state, keycode) } } pub(crate) fn get_utf8_raw(&mut self, keycode: u32) -> Option<&'static str> { if !self.ready() { return None; } - let size = unsafe { - (XKBH.xkb_state_key_get_utf8)(self.xkb_state, keycode + 8, ptr::null_mut(), 0) - } + 1; + let size = + unsafe { (XKBH.xkb_state_key_get_utf8)(self.xkb_state, keycode, ptr::null_mut(), 0) } + + 1; if size <= 1 { return None; }; @@ -168,7 +168,7 @@ impl KbState { self.scratch_buffer.set_len(size); (XKBH.xkb_state_key_get_utf8)( self.xkb_state, - keycode + 8, + keycode, self.scratch_buffer.as_mut_ptr() as *mut _, size, ); @@ -474,7 +474,7 @@ impl KbState { impl KbState { pub(crate) unsafe fn key_repeats(&mut self, keycode: ffi::xkb_keycode_t) -> bool { - (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, keycode + 8) == 1 + (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, keycode) == 1 } #[inline] @@ -615,7 +615,7 @@ impl<'a> KeyEventResults<'a> { } pub fn keycode(&mut self) -> KeyCode { - super::keymap::rawkey_to_keycode(self.keycode) + super::keymap::raw_keycode_to_keycode(self.keycode) } pub fn key(&mut self) -> (Key<'static>, KeyLocation) { @@ -656,7 +656,7 @@ impl<'a> KeyEventResults<'a> { let keysym_count = unsafe { (XKBH.xkb_keymap_key_get_syms_by_level)( self.state.xkb_keymap, - self.keycode + 8, + self.keycode, 0, 0, &mut keysyms, diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs index d9057b59a1..7ec626e123 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs @@ -548,7 +548,7 @@ impl KbdHandler { _ => unreachable!(), }; - let mut ker = state.process_key_event(key, key_state); + let mut ker = state.process_key_event(key + 8, key_state); let physical_key = ker.keycode(); let (logical_key, location) = ker.key(); @@ -683,7 +683,7 @@ impl calloop::EventSource for RepeatSource { if let Some(ref mut data) = *current_repeat.borrow_mut() { // there is something to repeat let mut state = state.borrow_mut(); - let mut ker = state.process_key_repeat_event(data.keycode); + let mut ker = state.process_key_repeat_event(data.keycode + 8); let physical_key = ker.keycode(); let (logical_key, location) = ker.key(); diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 5e38b7f86c..f1273a061e 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1042,7 +1042,6 @@ impl EventProcessor { let device_id = mkdid(xkev.deviceid); let keycode = xkev.detail as u32; - let keycode = keycode - KEYCODE_OFFSET as u32; let mut ker = self.kb_state.process_key_event(keycode, state); let physical_key = ker.keycode(); let (logical_key, location) = ker.key(); @@ -1116,12 +1115,11 @@ impl EventProcessor { }; let device_id = mkdid(xev.sourceid); - let keycode = xev.detail; - let scancode = keycode - KEYCODE_OFFSET as i32; - if scancode < 0 { + let keycode = xev.detail as u32; + if keycode < KEYCODE_OFFSET as u32 { return; } - let physical_key = keymap::rawkey_to_keycode(scancode as u32); + let physical_key = keymap::raw_keycode_to_keycode(keycode); let modifiers = self.device_mod_state.modifiers(); callback(Event::DeviceEvent { @@ -1294,7 +1292,8 @@ impl EventProcessor { .into_iter() .filter(|k| *k >= KEYCODE_OFFSET) { - let keycode = (keycode - KEYCODE_OFFSET) as u32; + let keycode = keycode as u32; + let mut ker = kb_state.process_key_event(keycode, state); let physical_key = ker.keycode(); let (logical_key, location) = ker.key(); From 3f2509bf1d78c303f4f8ec6588df84d4013a2e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 5 Aug 2021 19:59:32 +0200 Subject: [PATCH 113/131] Log an error if we can't select XKB events --- src/platform_impl/linux/x11/util/input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/linux/x11/util/input.rs b/src/platform_impl/linux/x11/util/input.rs index db0f22eb03..24aad57937 100644 --- a/src/platform_impl/linux/x11/util/input.rs +++ b/src/platform_impl/linux/x11/util/input.rs @@ -86,7 +86,7 @@ impl XConnection { if status == ffi::True { Some(Flusher::new(self)) } else { - // This should only happen if the XKB extension isn't initialized. + error!("Could not select XKB events: The XKB extension is not initialized!"); None } } From c17707d63dfe527161632bd97885b0c29173487d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 5 Aug 2021 20:08:40 +0200 Subject: [PATCH 114/131] Remove superfluous call to `Vec::set_len` --- src/platform_impl/linux/common/xkb_state.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 558a30303f..6cdc335637 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -540,7 +540,6 @@ impl KbState { self.scratch_buffer.clear(); self.scratch_buffer.reserve(8); loop { - unsafe { self.scratch_buffer.set_len(8) }; let bytes_written = unsafe { (XKBH.xkb_keysym_to_utf8)( keysym, From 2ad1829ead920db14311bf877408d4fdaf801a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 5 Aug 2021 20:26:33 +0200 Subject: [PATCH 115/131] Revert change to the `window_icon` example I don't know how this change snuck itself in here, but it's not supposed to be here. --- 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 d1752c465b13167769a20578048d410e3da8e010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 12 Aug 2021 01:51:50 +0200 Subject: [PATCH 116/131] Send a warning if the server doesn't send a keymap --- src/platform_impl/linux/wayland/seat/keyboard/handlers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs index 7ec626e123..6452ae6570 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs @@ -475,7 +475,7 @@ impl KbdHandler { state.init_with_fd(fd, size as usize); }, wl_keyboard::KeymapFormat::NoKeymap => { - // TODO: how to handle this (hopefully never occuring) case? + warn!("The Wayland server did not send a keymap!"); } _ => unreachable!(), } From 413fd17498064fe115bf48d1993443b6350ab6b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Wed, 25 Aug 2021 00:32:07 +0200 Subject: [PATCH 117/131] Implement `KeyCodeExtScancode` on Wayland and X11 --- src/platform/scancode.rs | 1 + src/platform/unix.rs | 15 +- src/platform_impl/linux/common/keymap.rs | 172 +++++++++++++++++++++-- src/platform_impl/linux/mod.rs | 2 +- 4 files changed, 173 insertions(+), 17 deletions(-) diff --git a/src/platform/scancode.rs b/src/platform/scancode.rs index 078fe74dea..00188c6212 100644 --- a/src/platform/scancode.rs +++ b/src/platform/scancode.rs @@ -20,6 +20,7 @@ pub trait KeyCodeExtScancode { /// /// ## Platform-specific /// - **Windows:** A 16bit extended scancode + /// - **Wayland/X11**: A 32-bit X11-style keycode. // TODO: Describe what this value contains for each platform fn to_scancode(self) -> Option; diff --git a/src/platform/unix.rs b/src/platform/unix.rs index 0b2a2dbe9e..0140e1b726 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -13,9 +13,10 @@ use std::{ptr, sync::Arc}; use crate::{ event::KeyEvent, event_loop::{EventLoop, EventLoopWindowTarget}, - keyboard::Key, + keyboard::{Key, KeyCode}, monitor::MonitorHandle, - platform::modifier_supplement::KeyEventExtModifierSupplement, + platform::{modifier_supplement::KeyEventExtModifierSupplement, scancode::KeyCodeExtScancode}, + platform_impl::common::keymap, window::{Window, WindowBuilder}, }; @@ -544,3 +545,13 @@ impl KeyEventExtModifierSupplement for KeyEvent { self.platform_specific.key_without_modifiers } } + +impl KeyCodeExtScancode for KeyCode { + fn from_scancode(scancode: u32) -> KeyCode { + keymap::raw_keycode_to_keycode(scancode) + } + + fn to_scancode(self) -> Option { + keymap::keycode_to_raw(self) + } +} diff --git a/src/platform_impl/linux/common/keymap.rs b/src/platform_impl/linux/common/keymap.rs index b64932e918..610fc4f38a 100644 --- a/src/platform_impl/linux/common/keymap.rs +++ b/src/platform_impl/linux/common/keymap.rs @@ -2,8 +2,9 @@ use crate::keyboard::{Key, KeyCode, KeyLocation, NativeKeyCode}; -// TODO: Do another pass on all of this - +/// Map the raw X11-style keycode to the `KeyCode` enum. +/// +/// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses. pub fn raw_keycode_to_keycode(keycode: u32) -> KeyCode { let rawkey = keycode - 8; // The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as @@ -14,10 +15,8 @@ pub fn raw_keycode_to_keycode(keycode: u32) -> KeyCode { // Some of the keycodes are likely superfluous for our purposes, and some are ones which are // difficult to test the correctness of, or discover the purpose of. Because of this, they've // either been commented out here, or not included at all. - // - // TODO: There are probably a couple more unproblematic keycodes to map here. match rawkey { - 0 => KeyCode::Unidentified(NativeKeyCode::XKB(0)), // TODO: Is `NativeKeyCode::Unidentified` better? + 0 => KeyCode::Unidentified(NativeKeyCode::XKB(0)), 1 => KeyCode::Escape, 2 => KeyCode::Digit1, 3 => KeyCode::Digit2, @@ -93,7 +92,7 @@ pub fn raw_keycode_to_keycode(keycode: u32) -> KeyCode { 73 => KeyCode::Numpad9, 74 => KeyCode::NumpadSubtract, 75 => KeyCode::Numpad4, - 76 => KeyCode::Numpad4, + 76 => KeyCode::Numpad5, 77 => KeyCode::Numpad6, 78 => KeyCode::NumpadAdd, 79 => KeyCode::Numpad1, @@ -264,6 +263,151 @@ pub fn raw_keycode_to_keycode(keycode: u32) -> KeyCode { } } +pub fn keycode_to_raw(keycode: KeyCode) -> Option { + match keycode { + KeyCode::Unidentified(NativeKeyCode::Unidentified) => Some(240), + KeyCode::Unidentified(NativeKeyCode::XKB(raw)) => Some(raw), + KeyCode::Escape => Some(1), + KeyCode::Digit1 => Some(2), + KeyCode::Digit2 => Some(3), + KeyCode::Digit3 => Some(4), + KeyCode::Digit4 => Some(5), + KeyCode::Digit5 => Some(6), + KeyCode::Digit6 => Some(7), + KeyCode::Digit7 => Some(8), + KeyCode::Digit8 => Some(9), + KeyCode::Digit9 => Some(10), + KeyCode::Digit0 => Some(11), + KeyCode::Minus => Some(12), + KeyCode::Equal => Some(13), + KeyCode::Backspace => Some(14), + KeyCode::Tab => Some(15), + KeyCode::KeyQ => Some(16), + KeyCode::KeyW => Some(17), + KeyCode::KeyE => Some(18), + KeyCode::KeyR => Some(19), + KeyCode::KeyT => Some(20), + KeyCode::KeyY => Some(21), + KeyCode::KeyU => Some(22), + KeyCode::KeyI => Some(23), + KeyCode::KeyO => Some(24), + KeyCode::KeyP => Some(25), + KeyCode::BracketLeft => Some(26), + KeyCode::BracketRight => Some(27), + KeyCode::Enter => Some(28), + KeyCode::ControlLeft => Some(29), + KeyCode::KeyA => Some(30), + KeyCode::KeyS => Some(31), + KeyCode::KeyD => Some(32), + KeyCode::KeyF => Some(33), + KeyCode::KeyG => Some(34), + KeyCode::KeyH => Some(35), + KeyCode::KeyJ => Some(36), + KeyCode::KeyK => Some(37), + KeyCode::KeyL => Some(38), + KeyCode::Semicolon => Some(39), + KeyCode::Quote => Some(40), + KeyCode::Backquote => Some(41), + KeyCode::ShiftLeft => Some(42), + KeyCode::Backslash => Some(43), + KeyCode::KeyZ => Some(44), + KeyCode::KeyX => Some(45), + KeyCode::KeyC => Some(46), + KeyCode::KeyV => Some(47), + KeyCode::KeyB => Some(48), + KeyCode::KeyN => Some(49), + KeyCode::KeyM => Some(50), + KeyCode::Comma => Some(51), + KeyCode::Period => Some(52), + KeyCode::Slash => Some(53), + KeyCode::ShiftRight => Some(54), + KeyCode::NumpadMultiply => Some(55), + KeyCode::AltLeft => Some(56), + KeyCode::Space => Some(57), + KeyCode::CapsLock => Some(58), + KeyCode::F1 => Some(59), + KeyCode::F2 => Some(60), + KeyCode::F3 => Some(61), + KeyCode::F4 => Some(62), + KeyCode::F5 => Some(63), + KeyCode::F6 => Some(64), + KeyCode::F7 => Some(65), + KeyCode::F8 => Some(66), + KeyCode::F9 => Some(67), + KeyCode::F10 => Some(68), + KeyCode::NumLock => Some(69), + KeyCode::ScrollLock => Some(70), + KeyCode::Numpad7 => Some(71), + KeyCode::Numpad8 => Some(72), + KeyCode::Numpad9 => Some(73), + KeyCode::NumpadSubtract => Some(74), + KeyCode::Numpad4 => Some(75), + KeyCode::Numpad5 => Some(76), + KeyCode::Numpad6 => Some(77), + KeyCode::NumpadAdd => Some(78), + KeyCode::Numpad1 => Some(79), + KeyCode::Numpad2 => Some(80), + KeyCode::Numpad3 => Some(81), + KeyCode::Numpad0 => Some(82), + KeyCode::NumpadDecimal => Some(83), + KeyCode::Lang5 => Some(85), + KeyCode::IntlBackslash => Some(86), + KeyCode::F11 => Some(87), + KeyCode::F12 => Some(88), + KeyCode::IntlRo => Some(89), + KeyCode::Lang3 => Some(90), + KeyCode::Lang4 => Some(91), + KeyCode::Convert => Some(92), + KeyCode::KanaMode => Some(93), + KeyCode::NonConvert => Some(94), + KeyCode::NumpadEnter => Some(96), + KeyCode::ControlRight => Some(97), + KeyCode::NumpadDivide => Some(98), + KeyCode::PrintScreen => Some(99), + KeyCode::AltRight => Some(100), + KeyCode::Home => Some(102), + KeyCode::ArrowUp => Some(103), + KeyCode::PageUp => Some(104), + KeyCode::ArrowLeft => Some(105), + KeyCode::ArrowRight => Some(106), + KeyCode::End => Some(107), + KeyCode::ArrowDown => Some(108), + KeyCode::PageDown => Some(109), + KeyCode::Insert => Some(110), + KeyCode::Delete => Some(111), + KeyCode::AudioVolumeMute => Some(113), + KeyCode::AudioVolumeDown => Some(114), + KeyCode::AudioVolumeUp => Some(115), + KeyCode::NumpadEqual => Some(117), + KeyCode::Pause => Some(119), + KeyCode::NumpadComma => Some(121), + KeyCode::Lang1 => Some(122), + KeyCode::Lang2 => Some(123), + KeyCode::IntlYen => Some(124), + KeyCode::SuperLeft => Some(125), + KeyCode::SuperRight => Some(126), + KeyCode::ContextMenu => Some(127), + KeyCode::MediaTrackNext => Some(163), + KeyCode::MediaPlayPause => Some(164), + KeyCode::MediaTrackPrevious => Some(165), + KeyCode::MediaStop => Some(166), + KeyCode::F13 => Some(183), + KeyCode::F14 => Some(184), + KeyCode::F15 => Some(185), + KeyCode::F16 => Some(186), + KeyCode::F17 => Some(187), + KeyCode::F18 => Some(188), + KeyCode::F19 => Some(189), + KeyCode::F20 => Some(190), + KeyCode::F21 => Some(191), + KeyCode::F22 => Some(192), + KeyCode::F23 => Some(193), + KeyCode::F24 => Some(194), + _ => None, + } + .map(|raw| raw + 8) +} + pub fn keysym_to_key(keysym: u32) -> Key<'static> { use xkbcommon_dl::keysyms; match keysym { @@ -273,7 +417,7 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { // keysyms::XKB_KEY_Linefeed => Key::Linefeed, keysyms::XKB_KEY_Clear => Key::Clear, keysyms::XKB_KEY_Return => Key::Enter, - // keysyms::XKB_KEY_Pause => Key::Pause, + keysyms::XKB_KEY_Pause => Key::Pause, keysyms::XKB_KEY_Scroll_Lock => Key::ScrollLock, keysyms::XKB_KEY_Sys_Req => Key::PrintScreen, keysyms::XKB_KEY_Escape => Key::Escape, @@ -357,6 +501,8 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { // keysyms::XKB_KEY_KP_Next => Key::PageDown, keysyms::XKB_KEY_KP_Page_Down => Key::PageDown, keysyms::XKB_KEY_KP_End => Key::End, + // TODO: What is this supposed to map to? + // This is the key labeled "5" on the numpad when NumLock is off. // keysyms::XKB_KEY_KP_Begin => Key::Begin, keysyms::XKB_KEY_KP_Insert => Key::Insert, keysyms::XKB_KEY_KP_Delete => Key::Delete, @@ -423,8 +569,7 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { keysyms::XKB_KEY_Control_R => Key::Control, keysyms::XKB_KEY_Caps_Lock => Key::CapsLock, // keysyms::XKB_KEY_Shift_Lock => Key::ShiftLock, - // - // TODO: Address how renaming `Meta` to `Super` affects this set of keys. + // keysyms::XKB_KEY_Meta_L => Key::Meta, // keysyms::XKB_KEY_Meta_R => Key::Meta, keysyms::XKB_KEY_Alt_L => Key::Alt, @@ -438,7 +583,6 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { // keysyms::XKB_KEY_ISO_Lock => Key::IsoLock, // keysyms::XKB_KEY_ISO_Level2_Latch => Key::IsoLevel2Latch, keysyms::XKB_KEY_ISO_Level3_Shift => Key::AltGraph, - // NOTE: I'm not quite certain if mapping the next 2 values to AltGraph is correct. keysyms::XKB_KEY_ISO_Level3_Latch => Key::AltGraph, keysyms::XKB_KEY_ISO_Level3_Lock => Key::AltGraph, // keysyms::XKB_KEY_ISO_Level5_Shift => Key::IsoLevel5Shift, @@ -562,9 +706,9 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { keysyms::XKB_KEY_XF86Calendar => Key::LaunchCalendar, keysyms::XKB_KEY_XF86PowerDown => Key::Power, // keysyms::XKB_KEY_XF86ContrastAdjust => Key::AdjustContrast, - // keysyms::XKB_KEY_XF86RockerUp => Key::RockerUp, // TODO: Use Key::ArrowUp? - // keysyms::XKB_KEY_XF86RockerDown => Key::RockerDown, // TODO: Use Key::ArrowDown? - // keysyms::XKB_KEY_XF86RockerEnter => Key::RockerEnter, // TODO: Use Key::Enter? + // keysyms::XKB_KEY_XF86RockerUp => Key::RockerUp, + // keysyms::XKB_KEY_XF86RockerDown => Key::RockerDown, + // keysyms::XKB_KEY_XF86RockerEnter => Key::RockerEnter, // XFree86 - More "Internet" keysyms::XKB_KEY_XF86Back => Key::BrowserBack, @@ -596,7 +740,7 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { // XKB_KEY_XF86Launch0..XKB_KEY_XF86LaunchF // XKB_KEY_XF86ApplicationLeft..XKB_KEY_XF86CD - keysyms::XKB_KEY_XF86Calculater => Key::LaunchApplication2, // This must be a typo, right? + keysyms::XKB_KEY_XF86Calculater => Key::LaunchApplication2, // Nice typo, libxkbcommon :) // XKB_KEY_XF86Clear keysyms::XKB_KEY_XF86Close => Key::Close, keysyms::XKB_KEY_XF86Copy => Key::Copy, diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index b719f25f54..ee427300e2 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -42,7 +42,7 @@ pub mod wayland; pub mod x11; #[cfg(any(feature = "x11", feature = "wayland"))] -mod common; +pub mod common; #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct KeyEventExtra { From bd7793c1033fdecdd057c2c3947a0fd9fd6620d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Wed, 25 Aug 2021 00:47:28 +0200 Subject: [PATCH 118/131] Implement `reset_dead_keys` (poorly) --- src/platform_impl/linux/common/xkb_state.rs | 42 +++++++++++++++---- src/platform_impl/linux/mod.rs | 3 +- .../linux/x11/event_processor.rs | 9 ++++ 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 6cdc335637..b1ff556db4 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -5,6 +5,8 @@ use std::fs::File; use std::os::raw::c_char; use std::os::unix::ffi::OsStringExt; use std::ptr; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Mutex; #[cfg(feature = "wayland")] use memmap2::MmapOptions; @@ -25,6 +27,20 @@ use crate::{ keyboard::{Key, KeyCode, KeyLocation}, }; +// TODO: Wire this up without using a static `Mutex>`. +#[cfg(feature = "x11")] +lazy_static! { + pub(crate) static ref X11_EVPROC_NEXT_COMPOSE: Mutex> = Mutex::new(None); +} + +// TODO: Wire this up without using a static `AtomicBool`. +static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false); + +#[inline] +pub(crate) fn reset_dead_keys() { + RESET_DEAD_KEYS.store(true, Ordering::SeqCst); +} + pub(crate) struct KbState { #[cfg(feature = "x11")] xcb_connection: *mut xcb_connection_t, @@ -194,6 +210,9 @@ impl KbState { if !self.ready() || self.xkb_compose_state.is_null() { return None; } + if RESET_DEAD_KEYS.swap(false, Ordering::SeqCst) { + unsafe { self.init_compose() }; + } Some(unsafe { (XKBCH.xkb_compose_state_feed)(xkb_compose_state, keysym) }) } @@ -639,13 +658,22 @@ impl<'a> KeyEventResults<'a> { _ => (key, location), } } - _ => ( - self.composed_text() - .unwrap_or_else(|_| self.state.keysym_to_utf8_raw(self.keysym)) - .map(Key::Character) - .unwrap_or(key), - location, - ), + _ => { + let composed_text = self.composed_text(); + + #[cfg(feature = "x11")] + if let Ok(Some(composed_text)) = composed_text { + *X11_EVPROC_NEXT_COMPOSE.lock().unwrap() = Some(composed_text); + } + + ( + composed_text + .unwrap_or_else(|_| self.state.keysym_to_utf8_raw(self.keysym)) + .map(Key::Character) + .unwrap_or(key), + location, + ) + } }) } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index ee427300e2..78ccdd92b7 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -439,8 +439,9 @@ impl Window { x11_or_wayland!(match self; Window(w) => w.set_ime_position(position)) } + #[inline] pub fn reset_dead_keys(&self) { - todo!() + common::xkb_state::reset_dead_keys() } #[inline] diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index f1273a061e..d159f4409f 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -546,6 +546,15 @@ impl EventProcessor { } else { return; }; + if super::super::common::xkb_state::X11_EVPROC_NEXT_COMPOSE + .lock() + .unwrap() + .take() + .map(|composed| composed == written) + .unwrap_or(false) + { + return; + } let event = Event::WindowEvent { window_id, event: WindowEvent::ReceivedImeText(written), From 2d78be110492091ec800b3b34314de55cab3f44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 9 Sep 2021 19:25:57 +0200 Subject: [PATCH 119/131] Split the native XKB keycode enum variant in two `XkbCode` now contains keycodes while `XkbSym` contains keysyms. This may change sooner rather than later. --- src/keyboard.rs | 13 +++++++++---- src/platform_impl/linux/common/keymap.rs | 8 ++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 9c56da7be6..3aadb14435 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -175,11 +175,12 @@ pub enum NativeKeyCode { Unidentified, Windows(u16), MacOS(u32), - XKB(u32), + XkbCode(u32), + XkbSym(u32), } impl std::fmt::Debug for NativeKeyCode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use NativeKeyCode::{MacOS, Unidentified, Windows, XKB}; + use NativeKeyCode::{MacOS, Unidentified, Windows, XkbCode, XkbSym}; let mut debug_tuple; match self { Unidentified => { @@ -193,8 +194,12 @@ impl std::fmt::Debug for NativeKeyCode { debug_tuple = f.debug_tuple(name_of!(MacOS)); debug_tuple.field(v); } - XKB(v) => { - debug_tuple = f.debug_tuple(name_of!(XKB)); + XkbCode(v) => { + debug_tuple = f.debug_tuple(name_of!(XkbCode)); + debug_tuple.field(&format_args!("0x{:04X}", v)); + } + XkbSym(v) => { + debug_tuple = f.debug_tuple(name_of!(XkbSym)); debug_tuple.field(&format_args!("0x{:04X}", v)); } } diff --git a/src/platform_impl/linux/common/keymap.rs b/src/platform_impl/linux/common/keymap.rs index 610fc4f38a..8e2e6c2458 100644 --- a/src/platform_impl/linux/common/keymap.rs +++ b/src/platform_impl/linux/common/keymap.rs @@ -16,7 +16,7 @@ pub fn raw_keycode_to_keycode(keycode: u32) -> KeyCode { // difficult to test the correctness of, or discover the purpose of. Because of this, they've // either been commented out here, or not included at all. match rawkey { - 0 => KeyCode::Unidentified(NativeKeyCode::XKB(0)), + 0 => KeyCode::Unidentified(NativeKeyCode::XkbCode(0)), 1 => KeyCode::Escape, 2 => KeyCode::Digit1, 3 => KeyCode::Digit2, @@ -259,14 +259,14 @@ pub fn raw_keycode_to_keycode(keycode: u32) -> KeyCode { // 246 => KeyCode::WWAN, // 247 => KeyCode::RFKILL, // 248 => KeyCode::KEY_MICMUTE, - _ => KeyCode::Unidentified(NativeKeyCode::XKB(rawkey)), + _ => KeyCode::Unidentified(NativeKeyCode::XkbCode(rawkey)), } } pub fn keycode_to_raw(keycode: KeyCode) -> Option { match keycode { KeyCode::Unidentified(NativeKeyCode::Unidentified) => Some(240), - KeyCode::Unidentified(NativeKeyCode::XKB(raw)) => Some(raw), + KeyCode::Unidentified(NativeKeyCode::XkbCode(raw)) => Some(raw), KeyCode::Escape => Some(1), KeyCode::Digit1 => Some(2), KeyCode::Digit2 => Some(3), @@ -827,7 +827,7 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { // XKB_KEY_SunPowerSwitchShift // 0 => Key::Unidentified(NativeKeyCode::Unidentified), - _ => Key::Unidentified(NativeKeyCode::XKB(keysym)), + _ => Key::Unidentified(NativeKeyCode::XkbSym(keysym)), } } From 3ae1593f23c122b278173e9716187844a24822c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Thu, 9 Sep 2021 20:41:46 +0200 Subject: [PATCH 120/131] Add a TODO regarding the `ModifiersChanged` event. --- src/platform_impl/linux/x11/event_processor.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index d159f4409f..8691baa4c9 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1129,6 +1129,13 @@ impl EventProcessor { return; } let physical_key = keymap::raw_keycode_to_keycode(keycode); + // TODO: Figure out how redundant this is. + // This is the set of modifiers end users end up seeing. However, the set of + // modifiers used internally by the `KbState` are sourced directly from the XKB + // extension. Since we currently panic when the extension doesn't load, we should + // be able to use the modifiers supplied to us by the XKB extension. This + // requires us to have consensus on what to do if we can't load and initialize + // libxkbcommon. let modifiers = self.device_mod_state.modifiers(); callback(Event::DeviceEvent { @@ -1139,8 +1146,6 @@ impl EventProcessor { }), }); - // `ModifiersChanged` is dispatched here because we assume that every `XI_KeyPress` - // is preceeded by a `XI_RawKeyPress`. if let Some(modifier) = self.mod_keymap.get_modifier(keycode as ffi::KeyCode) { From 9fea819e84caecb30a5c8597cc531e65d718ac1d Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Thu, 9 Sep 2021 21:35:46 +0200 Subject: [PATCH 121/131] fix: meta mod key on focus handling for gnome/x11 This patch addresses an issue in the modifier key handling appearing on gnome/x11 while focussing a winit window using a meta/super/logo key shortcut. As soon as the window gained focus via a shortcut using a meta key, gnome/x11 reports a meta modifier as pressed in the focus event, even though it was just released. No key release events were sent afterwards, so the meta modifier remained "stuck" in the winit state. This patch refactors the modifier key handling following this goals: - Only rely on winit's own modifier state tracking for key and focus events, do not mix it with x11's modifier reporting - Track modifiers only via non-raw key events, so winit does not track modifier changes when not focussed - Integrate modifier handling with the synthetic key events mechanism in order to report release of all currently held modifiers when unfocussed and report set for all currently held modifiers when focussed. --- .../linux/x11/event_processor.rs | 104 ++++++++---------- 1 file changed, 43 insertions(+), 61 deletions(-) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 8691baa4c9..d1c46ed627 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -834,10 +834,6 @@ impl EventProcessor { .focus(xev.event) .expect("Failed to focus input context"); - let modifiers = ModifiersState::from_x11(&xev.mods); - - self.device_mod_state.update_state(&modifiers, None); - if self.active_window != Some(xev.event) { self.active_window = Some(xev.event); @@ -849,12 +845,16 @@ impl EventProcessor { event: Focused(true), }); - if !modifiers.is_empty() { - callback(Event::WindowEvent { - window_id, - event: WindowEvent::ModifiersChanged(modifiers), - }); - } + // Issue key press events for all pressed keys + Self::handle_pressed_keys( + &wt, + window_id, + ElementState::Pressed, + &mut self.kb_state, + &self.mod_keymap, + &mut self.device_mod_state, + &mut callback, + ); // The deviceid for this event is for a keyboard instead of a pointer, // so we have to do a little extra work. @@ -870,22 +870,12 @@ impl EventProcessor { event: CursorMoved { device_id: mkdid(pointer_id), position, - modifiers, + modifiers: self.device_mod_state.modifiers(), }, }); - - // Issue key press events for all pressed keys - Self::handle_pressed_keys( - &wt, - window_id, - ElementState::Pressed, - &mut self.kb_state, - &self.mod_keymap, - &mut self.device_mod_state, - &mut callback, - ); } } + ffi::XI_FocusOut => { let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) }; if !self.window_exists(xev.event) { @@ -910,11 +900,6 @@ impl EventProcessor { &mut callback, ); - callback(Event::WindowEvent { - window_id, - event: WindowEvent::ModifiersChanged(ModifiersState::empty()), - }); - callback(Event::WindowEvent { window_id, event: Focused(false), @@ -1059,6 +1044,27 @@ impl EventProcessor { let text_with_all_modifiers = ker.text_with_all_modifiers(); let repeat = xkev.flags & ffi::XIKeyRepeat == ffi::XIKeyRepeat; + if let Some(modifier) = + self.mod_keymap.get_modifier(keycode as ffi::KeyCode) + { + let old_modifiers = self.device_mod_state.modifiers(); + + self.device_mod_state.key_event( + state, + keycode as ffi::KeyCode, + modifier, + ); + + if old_modifiers != self.device_mod_state.modifiers() { + callback(Event::WindowEvent { + window_id, + event: WindowEvent::ModifiersChanged( + self.device_mod_state.modifiers(), + ), + }); + } + } + callback(Event::WindowEvent { window_id, event: WindowEvent::KeyboardInput { @@ -1129,14 +1135,6 @@ impl EventProcessor { return; } let physical_key = keymap::raw_keycode_to_keycode(keycode); - // TODO: Figure out how redundant this is. - // This is the set of modifiers end users end up seeing. However, the set of - // modifiers used internally by the `KbState` are sourced directly from the XKB - // extension. Since we currently panic when the extension doesn't load, we should - // be able to use the modifiers supplied to us by the XKB extension. This - // requires us to have consensus on what to do if we can't load and initialize - // libxkbcommon. - let modifiers = self.device_mod_state.modifiers(); callback(Event::DeviceEvent { device_id, @@ -1145,27 +1143,6 @@ impl EventProcessor { state, }), }); - - if let Some(modifier) = - self.mod_keymap.get_modifier(keycode as ffi::KeyCode) - { - self.device_mod_state.key_event( - state, - keycode as ffi::KeyCode, - modifier, - ); - - let new_modifiers = self.device_mod_state.modifiers(); - - if modifiers != new_modifiers { - if let Some(window_id) = self.active_window { - callback(Event::WindowEvent { - window_id: mkwid(window_id), - event: WindowEvent::ModifiersChanged(new_modifiers), - }); - } - } - } } ffi::XI_HierarchyChanged => { @@ -1316,11 +1293,16 @@ impl EventProcessor { let text_with_all_modifiers = ker.text_with_all_modifiers(); if let Some(modifier) = mod_keymap.get_modifier(keycode as ffi::KeyCode) { - device_mod_state.key_event( - ElementState::Pressed, - keycode as ffi::KeyCode, - modifier, - ); + let old_modifiers = device_mod_state.modifiers(); + + device_mod_state.key_event(state, keycode as ffi::KeyCode, modifier); + + if old_modifiers != device_mod_state.modifiers() { + callback(Event::WindowEvent { + window_id, + event: WindowEvent::ModifiersChanged(device_mod_state.modifiers()), + }); + } } callback(Event::WindowEvent { From ea59172dd16e75a2f04b324c8125eed8f32ee655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20R=C3=B8yset?= Date: Wed, 6 Oct 2021 18:21:48 +0200 Subject: [PATCH 122/131] Remove the `XkbStateNotify` handler The only thing we did in response to said event is update the `xkb_state`, but we throw that away on every keypress anyway due to the keyeyboard layout change detection hack. This commit also removes associated code which let us recieve and react to the event in the first place. If a fix is ever introduced, then this commit ought to be reverted. --- .../linux/x11/event_processor.rs | 28 ++++------------- src/platform_impl/linux/x11/mod.rs | 31 ------------------- 2 files changed, 6 insertions(+), 53 deletions(-) diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 8691baa4c9..cbe5d1bf3d 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, collections::HashMap, convert::identity, rc::Rc, slice, sync::Arc}; +use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc}; use libc::{c_char, c_int, c_long, c_ulong}; @@ -31,7 +31,6 @@ pub(super) struct EventProcessor { pub(super) randr_event_offset: c_int, pub(super) devices: RefCell>, pub(super) xi2ext: XExtension, - pub(super) xkbext: XExtension, pub(super) target: Rc>, pub(super) kb_state: KbState, pub(super) mod_keymap: ModifierKeymap, @@ -1113,6 +1112,11 @@ impl EventProcessor { // keymap on *every* keypress. That's peak efficiency right there! // // FIXME: Someone please save our souls! Or at least our wasted CPU cycles. + // + // If you do manage to find a solution, remember to re-enable (and handle) the + // `XkbStateNotify` event with `XkbSelectEventDetails` with a mask of + // `XkbAllStateComponentsMask & !XkbPointerButtonMask` like in + // . unsafe { self.kb_state.init_with_x11_keymap() }; let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; @@ -1195,26 +1199,6 @@ impl EventProcessor { } } _ => { - if event_type == self.xkbext.first_event_id { - let xev = unsafe { &*(identity(xev) as *const _ as *const ffi::XkbAnyEvent) }; - match xev.xkb_type { - ffi::XkbStateNotify => { - let xev = - unsafe { &*(xev as *const _ as *const ffi::XkbStateNotifyEvent) }; - if matches!(xev.event_type as i32, ffi::KeyPress | ffi::KeyRelease) { - self.kb_state.update_modifiers( - xev.base_mods, - xev.latched_mods, - xev.locked_mods, - xev.base_group as u32, - xev.latched_group as u32, - xev.locked_group as u32, - ) - } - } - _ => {} - } - } if event_type == self.randr_event_offset { // In the future, it would be quite easy to emit monitor hotplug events. let prev_list = monitor::invalidate_cached_monitor_list(); diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index b4ad48849e..417757d9be 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -162,27 +162,6 @@ impl EventLoop { ext }; - let xkbext = { - let mut ext = XExtension::default(); - - let res = unsafe { - (xconn.xlib.XkbQueryExtension)( - xconn.display, - &mut ext.opcode, - &mut ext.first_event_id, - &mut ext.first_error_id, - &mut 1, - &mut 0, - ) - }; - - if res == ffi::False { - panic!("X server missing XKB extension"); - } - - ext - }; - unsafe { let mut xinput_major_ver = ffi::XI_2_Major; let mut xinput_minor_ver = ffi::XI_2_Minor; @@ -242,7 +221,6 @@ impl EventLoop { randr_event_offset, ime_receiver, xi2ext, - xkbext, kb_state, mod_keymap, device_mod_state: Default::default(), @@ -258,15 +236,6 @@ impl EventLoop { .select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask) .queue(); - get_xtarget(&target) - .xconn - .select_xkb_events( - 0x100, // Use the "core keyboard device" - ffi::XkbNewKeyboardNotifyMask | ffi::XkbMapNotifyMask | ffi::XkbStateNotifyMask, - ) - .unwrap() - .queue(); - event_processor.init_device(ffi::XIAllDevices); let result = EventLoop { From efdece5daacfe1f8d3d74a8ef441aab7e8355ac9 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 21 Oct 2021 11:12:27 +0200 Subject: [PATCH 123/131] X11: Only fetch virtual keyboard events from master devices We must not report virtual keyboard events for keys that were grabbed by other applications (XGrabKey, etc.). Since grabs only affect master devices, we must consume virtual events from master devices only. --- src/platform_impl/linux/x11/mod.rs | 4 +--- src/platform_impl/linux/x11/window.rs | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 417757d9be..6aa493e215 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -626,9 +626,7 @@ impl Device { | ffi::XI_RawButtonPressMask | ffi::XI_RawButtonReleaseMask | ffi::XI_RawKeyPressMask - | ffi::XI_RawKeyReleaseMask - | ffi::XI_KeyPressMask - | ffi::XI_KeyReleaseMask; + | ffi::XI_RawKeyReleaseMask; // The request buffer is flushed when we poll for events wt.xconn .select_xinput_events(wt.root, info.deviceid, mask) diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index fb5d007d6d..494263d56e 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -407,8 +407,8 @@ impl UnownedWindow { let mask = ffi::XI_MotionMask | ffi::XI_ButtonPressMask | ffi::XI_ButtonReleaseMask - //| ffi::XI_KeyPressMask - //| ffi::XI_KeyReleaseMask + | ffi::XI_KeyPressMask + | ffi::XI_KeyReleaseMask | ffi::XI_EnterMask | ffi::XI_LeaveMask | ffi::XI_FocusInMask From 1d6adf5f89b0259261864295a8b98ff8c1c086c5 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Fri, 22 Oct 2021 21:15:17 +0200 Subject: [PATCH 124/131] X11: Rewrite input handling in terms of XInput and XKB This patch completes the port of the X11 backend from core input handling to XInput/XKB input handling. In this context the word 'core' refers to the core X11 protocol in contrast to protocol extensions such as XInput and XKB. XInput and XKB are very large protocols that extend X11 with features expected from modern desktop environments such as - Support for a rich set of input devices such as touchpads. - Support for multiple attached keyboards, mice, touchpads, tablets, etc. - Support for rich and interactive keyboard layouts. # Breaking Changes - This patch removes all processing of core input events in favor of XInput events. The legacy XIM input method protocol is based on filtering and injecting core input events. Therefore, this patch also removes support for XIM input methods. Applications are encouraged to switch to more modern IM protocols such as [IBus]. These protocols can be implemented in application space outside of winit. Note that modern toolskits such as QT5 and chromium do not support XIM. [IBus]: https://en.wikipedia.org/wiki/Intelligent_Input_Bus - This patch removes support for synthetic keyboard events. This feature cannot be implemented correctly: - XKB is a complex state machine where key presses and releases can perform a rich set of actions. For example: - Switching modifiers on and off. - Switching between keyboard layouts. - Moving the mouse cursor. These actions depend on the order the key are pressed and released. For example, if a key that switches layouts is released before a regular key, then the release of the regular key will produce different events than it would otherwise. - The winit API does not permit synthetic `ModifierChanged` events. As such, an application cannot distinguish between the user deliberately changing the active modifiers and synthetic changes. For example, consider an application that performs a drag-and-drop operation as long as the Shift modifier is active. Applications are encouraged to track the state of keys manually in a way that is suitable for their application. # New and Changed Features - Winit no longer tracks keyboard events if no winit window has the focus except that: - Raw keyboard events are still being tracked. A future patch might make this behavior optional. See #1634. - Changes to the keyboard layout are being tracked at all times. - The backend now has complete support for multiple seats. For each seat it tracks the modifier state and the focused window. In the case of `KeyboardInput` events, applications can distinguish multiple seats by tracking the value of the `device_id` field. In the case of `ModifierChanged` events, applications cannot distinguish different seats. A future patch might add a `device_id` field to `ModifierChanged` events. The following sequence of events is possible: 1. Key Press: Seat 1, Left Shift 2. Modifiers Changed: Shift 3. Key Press: Seat 2, Left Ctrl 4. Modifiers Changed: Ctrl 5. Key Press: Seat 1, KeyA, Text: "A" (due to active Shift) 6. Key Release: Seat 1, Left Shift 7. Modifiers Changed: None 8. Key Release: Seat 2, Left Ctrl 9. Modifiers Changed: None - Keyboard state and window events are now completely independent of device events. Applications can disable device events by modifying the winit source code (or in the future with a supported toggle) without incurring regressions in other areas. - Key release events no longer contain a value in the `text` and `text_with_all_modifiers` fields. - Key presses that are part of a compose sequence no longer contain a value in the `text` and `text_with_all_modifiers`. Applications that simply want to handle text input can therefore listen for key events and append the values of the `text` field to the input buffer without having to track any state. - The `logical_key` field of key events is no longer affected by compose sequences. This is in line with how browsers handle compose sequences. - Aborted compose sequences no longer produce any `text`. An aborted compose sequence is a sequence that was not completed correctly. For example, consider the following sequence of keysyms: 1. Multi_key 2. ( 3. c 4. ( `(` is not a valid continuation of the compose sequence starting with `[Multi_key, (, c]`. Therefore it aborts the sequence and no `text` is produced (not even for the final `(`). This is in line with existing practice on linux. - The `Dead` `Key` is now used exclusively for those keysyms that have `_dead_` in their name. This appears to be in line with how browsers handle dead keys. - The value of a `Dead` `Key` is in one of three categories: - If the dead key does not correspond to any particular diacritical mark, the value is `None`. For example, `dead_greek` (used to input Greek characters on a Latin keyboard). - If the dead key has a freestanding variant in unicode, the value is `Some(c)` with `c` being the freestanding character. For example, `dead_circumflex` has the value `Some('^')`. - Otherwise the value is `None`. For example, `dead_belowdot`. - `key_without_modifiers` now respects the effective XKB group. It only discards the state of modifiers. This is essential to correctly handle keyboard layouts in the GNOME desktop environment which uses XKB groups to switch between layouts. # Implementation Details - `EventProcessor` no longer uses any interior mutability. In cases where there were conflicting borrows, the code has been rewritten to use freestanding functions. - Keyboard state is now tracked exclusively by xkbcommon. The code that manually tracked some of this state has been removed. - The `xkb_state` module has been significantly simplified. The `process_key_event` function now computes all effects produced by a key press/release ahead of time. - Almost all XInput events also carry the current XKB state of its seat. We use this to track the state of modifiers eagerly and independently of keyboard events. --- Cargo.toml | 6 +- src/platform_impl/linux/common/keymap.rs | 51 ++ src/platform_impl/linux/common/xkb_state.rs | 672 ++++++-------- .../linux/wayland/seat/keyboard/handlers.rs | 58 +- .../linux/wayland/seat/keyboard/mod.rs | 12 - .../linux/x11/event_processor.rs | 822 +++++++++--------- src/platform_impl/linux/x11/ime/mod.rs | 11 - src/platform_impl/linux/x11/mod.rs | 61 +- src/platform_impl/linux/x11/util/input.rs | 99 +-- src/platform_impl/linux/x11/util/keys.rs | 92 -- src/platform_impl/linux/x11/util/mod.rs | 2 - src/platform_impl/linux/x11/util/modifiers.rs | 190 ---- 12 files changed, 828 insertions(+), 1248 deletions(-) delete mode 100644 src/platform_impl/linux/x11/util/keys.rs delete mode 100644 src/platform_impl/linux/x11/util/modifiers.rs diff --git a/Cargo.toml b/Cargo.toml index d2f3c9026b..5264fea76b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ targets = ["i686-pc-windows-msvc", "x86_64-pc-windows-msvc", "i686-unknown-linux default = ["x11", "wayland"] web-sys = ["web_sys", "wasm-bindgen", "instant/wasm-bindgen"] stdweb = ["std_web", "instant/stdweb"] -x11 = ["x11-dl", "mio", "mio-misc", "percent-encoding", "parking_lot", "xkbcommon-dl/x11"] +x11 = ["x11-dl", "mio", "mio-misc", "percent-encoding", "parking_lot"] wayland = ["wayland-client", "sctk", "memmap2"] [dependencies] @@ -93,11 +93,11 @@ wayland-client = { version = "0.28", features = [ "dlopen"] , optional = true } sctk = { package = "smithay-client-toolkit", version = "0.12.3", optional = true } mio = { version = "0.7", features = ["os-ext"], optional = true } mio-misc = { version = "1.0", optional = true } -x11-dl = { version = "2.18.5", optional = true } +x11-dl = { version = "2.19.1", optional = true } percent-encoding = { version = "2.0", optional = true } parking_lot = { version = "0.11.0", optional = true } memmap2 = { version = "0.2.1", optional = true } -xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "900832888ad6f11011d1369befb344a9aa8a9610" } +xkbcommon-dl = { git = "https://github.com/mahkoh/xkbcommon-dl", rev = "dd6a9033b1e45a2668700f14c2d47e48aeb3194f" } [target.'cfg(target_arch = "wasm32")'.dependencies.web_sys] package = "web-sys" diff --git a/src/platform_impl/linux/common/keymap.rs b/src/platform_impl/linux/common/keymap.rs index 8e2e6c2458..51083d6d38 100644 --- a/src/platform_impl/linux/common/keymap.rs +++ b/src/platform_impl/linux/common/keymap.rs @@ -826,6 +826,57 @@ pub fn keysym_to_key(keysym: u32) -> Key<'static> { keysyms::XKB_KEY_SunVideoRaiseBrightness => Key::BrightnessUp, // XKB_KEY_SunPowerSwitchShift // + // Dead keys + keysyms::XKB_KEY_dead_greek => Key::Dead(None), + keysyms::XKB_KEY_dead_currency => Key::Dead(None), + keysyms::XKB_KEY_dead_stroke => Key::Dead(None), + keysyms::XKB_KEY_dead_voiced_sound => Key::Dead(None), + keysyms::XKB_KEY_dead_semivoiced_sound => Key::Dead(None), + keysyms::XKB_KEY_dead_lowline => Key::Dead(None), + keysyms::XKB_KEY_dead_aboveverticalline => Key::Dead(None), + keysyms::XKB_KEY_dead_belowverticalline => Key::Dead(None), + keysyms::XKB_KEY_dead_longsolidusoverlay => Key::Dead(None), + keysyms::XKB_KEY_dead_grave => Key::Dead(Some('`')), + keysyms::XKB_KEY_dead_acute => Key::Dead(Some('´')), + keysyms::XKB_KEY_dead_circumflex => Key::Dead(Some('^')), + keysyms::XKB_KEY_dead_tilde => Key::Dead(Some('~')), + keysyms::XKB_KEY_dead_macron => Key::Dead(Some('¯')), + keysyms::XKB_KEY_dead_breve => Key::Dead(Some('˘')), + keysyms::XKB_KEY_dead_abovedot => Key::Dead(Some('˙')), + keysyms::XKB_KEY_dead_diaeresis => Key::Dead(Some('¨')), + keysyms::XKB_KEY_dead_abovering => Key::Dead(Some('°')), + keysyms::XKB_KEY_dead_doubleacute => Key::Dead(Some('˝')), + keysyms::XKB_KEY_dead_caron => Key::Dead(Some('ˇ')), + keysyms::XKB_KEY_dead_cedilla => Key::Dead(Some('¸')), + keysyms::XKB_KEY_dead_ogonek => Key::Dead(Some('˛')), + keysyms::XKB_KEY_dead_iota => Key::Dead(Some('ͺ')), + keysyms::XKB_KEY_dead_belowdot => Key::Dead(None), + keysyms::XKB_KEY_dead_hook => Key::Dead(None), + keysyms::XKB_KEY_dead_horn => Key::Dead(None), + keysyms::XKB_KEY_dead_abovecomma => Key::Dead(None), + keysyms::XKB_KEY_dead_abovereversedcomma => Key::Dead(None), + keysyms::XKB_KEY_dead_doublegrave => Key::Dead(None), + keysyms::XKB_KEY_dead_belowring => Key::Dead(Some('˳')), + keysyms::XKB_KEY_dead_belowmacron => Key::Dead(Some('ˍ')), + keysyms::XKB_KEY_dead_belowcircumflex => Key::Dead(None), + keysyms::XKB_KEY_dead_belowtilde => Key::Dead(Some('˷')), + keysyms::XKB_KEY_dead_belowbreve => Key::Dead(None), + keysyms::XKB_KEY_dead_belowdiaeresis => Key::Dead(None), + keysyms::XKB_KEY_dead_invertedbreve => Key::Dead(None), + keysyms::XKB_KEY_dead_belowcomma => Key::Dead(None), + keysyms::XKB_KEY_dead_a => Key::Dead(Some('a')), + keysyms::XKB_KEY_dead_A => Key::Dead(Some('A')), + keysyms::XKB_KEY_dead_e => Key::Dead(Some('e')), + keysyms::XKB_KEY_dead_E => Key::Dead(Some('E')), + keysyms::XKB_KEY_dead_i => Key::Dead(Some('i')), + keysyms::XKB_KEY_dead_I => Key::Dead(Some('I')), + keysyms::XKB_KEY_dead_o => Key::Dead(Some('o')), + keysyms::XKB_KEY_dead_O => Key::Dead(Some('O')), + keysyms::XKB_KEY_dead_u => Key::Dead(Some('u')), + keysyms::XKB_KEY_dead_U => Key::Dead(Some('U')), + keysyms::XKB_KEY_dead_small_schwa => Key::Dead(Some('ə')), + keysyms::XKB_KEY_dead_capital_schwa => Key::Dead(Some('Ə')), + 0 => Key::Unidentified(NativeKeyCode::Unidentified), _ => Key::Unidentified(NativeKeyCode::XkbSym(keysym)), } diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index b1ff556db4..f6f3a0a6fa 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -1,12 +1,8 @@ -use std::convert::TryInto; -use std::env; use std::ffi::CString; -use std::fs::File; use std::os::raw::c_char; use std::os::unix::ffi::OsStringExt; -use std::ptr; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Mutex; +use std::{char, env, ptr, slice, str}; #[cfg(feature = "wayland")] use memmap2::MmapOptions; @@ -16,23 +12,18 @@ pub use sctk::seat::keyboard::RMLVO; #[cfg(feature = "x11")] use x11_dl::xlib_xcb::xcb_connection_t; #[cfg(feature = "x11")] -use xkbcommon_dl::x11::XKBCOMMON_X11_HANDLE as XKBXH; +use xkbcommon_dl::XKBCOMMON_X11_HANDLE as XKBXH; use xkbcommon_dl::{ - self as ffi, xkb_state_component, XKBCOMMON_COMPOSE_HANDLE as XKBCH, XKBCOMMON_HANDLE as XKBH, + self as ffi, xkb_compose_status, xkb_state_component, XKBCOMMON_COMPOSE_HANDLE as XKBCH, + XKBCOMMON_HANDLE as XKBH, }; use crate::{ event::ElementState, - keyboard::{Key, KeyCode, KeyLocation}, + keyboard::{Key, KeyCode, KeyLocation, ModifiersState}, }; -// TODO: Wire this up without using a static `Mutex>`. -#[cfg(feature = "x11")] -lazy_static! { - pub(crate) static ref X11_EVPROC_NEXT_COMPOSE: Mutex> = Mutex::new(None); -} - // TODO: Wire this up without using a static `AtomicBool`. static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false); @@ -49,89 +40,83 @@ pub(crate) struct KbState { xkb_state: *mut ffi::xkb_state, xkb_compose_table: *mut ffi::xkb_compose_table, xkb_compose_state: *mut ffi::xkb_compose_state, - xkb_compose_state_2: *mut ffi::xkb_compose_state, + mod_indices: ModIndices, mods_state: ModifiersState, + #[cfg(feature = "wayland")] locked: bool, scratch_buffer: Vec, } -/// Represents the current state of the keyboard modifiers -/// -/// Each field of this struct represents a modifier and is `true` if this modifier is active. -/// -/// For some modifiers, this means that the key is currently pressed, others are toggled -/// (like caps lock). -#[derive(Copy, Clone, Debug, Default)] -pub struct ModifiersState { - /// The "control" key - pub ctrl: bool, - /// The "alt" key - pub alt: bool, - /// The "shift" key - pub shift: bool, - /// The "Caps lock" key - pub caps_lock: bool, - /// The "logo" key - /// - /// Also known as the "windows" key on most keyboards - pub logo: bool, - /// The "Num lock" key - pub num_lock: bool, +#[derive(Default)] +struct ModIndices { + ctrl: u32, + alt: u32, + shift: u32, + logo: u32, } -impl ModifiersState { - fn new() -> Self { - Self::default() +impl ModIndices { + unsafe fn from_keymap(xkb_keymap: *mut ffi::xkb_keymap) -> Self { + let ctrl = (XKBH.xkb_keymap_mod_get_index)( + xkb_keymap, + ffi::XKB_MOD_NAME_CTRL.as_ptr() as *const c_char, + ); + let alt = (XKBH.xkb_keymap_mod_get_index)( + xkb_keymap, + ffi::XKB_MOD_NAME_ALT.as_ptr() as *const c_char, + ); + let shift = (XKBH.xkb_keymap_mod_get_index)( + xkb_keymap, + ffi::XKB_MOD_NAME_SHIFT.as_ptr() as *const c_char, + ); + let logo = (XKBH.xkb_keymap_mod_get_index)( + xkb_keymap, + ffi::XKB_MOD_NAME_LOGO.as_ptr() as *const c_char, + ); + Self { + ctrl, + alt, + shift, + logo, + } } +} - fn update_with(&mut self, state: *mut ffi::xkb_state) { - self.ctrl = unsafe { - (XKBH.xkb_state_mod_name_is_active)( - state, - ffi::XKB_MOD_NAME_CTRL.as_ptr() as *const c_char, - xkb_state_component::XKB_STATE_MODS_EFFECTIVE, - ) > 0 - }; - self.alt = unsafe { - (XKBH.xkb_state_mod_name_is_active)( - state, - ffi::XKB_MOD_NAME_ALT.as_ptr() as *const c_char, - xkb_state_component::XKB_STATE_MODS_EFFECTIVE, - ) > 0 - }; - self.shift = unsafe { - (XKBH.xkb_state_mod_name_is_active)( - state, - ffi::XKB_MOD_NAME_SHIFT.as_ptr() as *const c_char, - xkb_state_component::XKB_STATE_MODS_EFFECTIVE, - ) > 0 - }; - self.caps_lock = unsafe { - (XKBH.xkb_state_mod_name_is_active)( - state, - ffi::XKB_MOD_NAME_CAPS.as_ptr() as *const c_char, - xkb_state_component::XKB_STATE_MODS_EFFECTIVE, - ) > 0 - }; - self.logo = unsafe { - (XKBH.xkb_state_mod_name_is_active)( - state, - ffi::XKB_MOD_NAME_LOGO.as_ptr() as *const c_char, - xkb_state_component::XKB_STATE_MODS_EFFECTIVE, - ) > 0 - }; - self.num_lock = unsafe { - (XKBH.xkb_state_mod_name_is_active)( - state, - ffi::XKB_MOD_NAME_NUM.as_ptr() as *const c_char, - xkb_state_component::XKB_STATE_MODS_EFFECTIVE, - ) > 0 - }; - } +unsafe fn xkb_state_to_modifiers( + state: *mut ffi::xkb_state, + indices: &ModIndices, +) -> ModifiersState { + let ctrl = (XKBH.xkb_state_mod_index_is_active)( + state, + indices.ctrl, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0; + let alt = (XKBH.xkb_state_mod_index_is_active)( + state, + indices.alt, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0; + let shift = (XKBH.xkb_state_mod_index_is_active)( + state, + indices.shift, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0; + let logo = (XKBH.xkb_state_mod_index_is_active)( + state, + indices.logo, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0; + + let mut mods = ModifiersState::empty(); + mods.set(ModifiersState::SHIFT, shift); + mods.set(ModifiersState::CONTROL, ctrl); + mods.set(ModifiersState::ALT, alt); + mods.set(ModifiersState::SUPER, logo); + mods } impl KbState { - pub(crate) fn update_modifiers( + pub(crate) fn update_state( &mut self, mods_depressed: u32, mods_latched: u32, @@ -156,7 +141,7 @@ impl KbState { }; if mask.contains(xkb_state_component::XKB_STATE_MODS_EFFECTIVE) { // effective value of mods have changed, we need to update our state - self.mods_state.update_with(self.xkb_state); + self.mods_state = unsafe { xkb_state_to_modifiers(self.xkb_state, &self.mod_indices) }; } } @@ -171,105 +156,51 @@ impl KbState { if !self.ready() { return None; } - let size = - unsafe { (XKBH.xkb_state_key_get_utf8)(self.xkb_state, keycode, ptr::null_mut(), 0) } - + 1; - if size <= 1 { - return None; - }; - self.scratch_buffer.clear(); - let size = size.try_into().unwrap(); - self.scratch_buffer.reserve(size); - unsafe { - self.scratch_buffer.set_len(size); - (XKBH.xkb_state_key_get_utf8)( - self.xkb_state, - keycode, - self.scratch_buffer.as_mut_ptr() as *mut _, - size, - ); - }; - // remove the final `\0` - self.scratch_buffer.pop(); - Some(byte_slice_to_cached_string(&self.scratch_buffer)) - } - - fn compose_feed_normal(&mut self, keysym: u32) -> Option { - self.compose_feed(self.xkb_compose_state, keysym) - } - - fn compose_feed_2(&mut self, keysym: u32) -> Option { - self.compose_feed(self.xkb_compose_state_2, keysym) + let utf32 = unsafe { (XKBH.xkb_state_key_get_utf32)(self.xkb_state, keycode) }; + char_to_str(utf32) } - fn compose_feed( - &mut self, - xkb_compose_state: *mut ffi::xkb_compose_state, - keysym: u32, - ) -> Option { + fn compose_feed(&mut self, keysym: u32) -> Option { if !self.ready() || self.xkb_compose_state.is_null() { return None; } if RESET_DEAD_KEYS.swap(false, Ordering::SeqCst) { unsafe { self.init_compose() }; } - Some(unsafe { (XKBCH.xkb_compose_state_feed)(xkb_compose_state, keysym) }) - } - - fn compose_status_normal(&mut self) -> Option { - self.compose_status(self.xkb_compose_state) - } - - #[allow(dead_code)] - fn compose_status_2(&mut self) -> Option { - self.compose_status(self.xkb_compose_state_2) + Some(unsafe { (XKBCH.xkb_compose_state_feed)(self.xkb_compose_state, keysym) }) } - fn compose_status( - &mut self, - xkb_compose_state: *mut ffi::xkb_compose_state, - ) -> Option { - if !self.ready() || xkb_compose_state.is_null() { + fn compose_status(&mut self) -> Option { + if !self.ready() || self.xkb_compose_state.is_null() { return None; } - Some(unsafe { (XKBCH.xkb_compose_state_get_status)(xkb_compose_state) }) + Some(unsafe { (XKBCH.xkb_compose_state_get_status)(self.xkb_compose_state) }) } - fn compose_get_utf8_normal(&mut self) -> Option<&'static str> { - self.compose_get_utf8(self.xkb_compose_state) - } - - fn compose_get_utf8_2(&mut self) -> Option<&'static str> { - self.compose_get_utf8(self.xkb_compose_state_2) - } - - fn compose_get_utf8( - &mut self, - xkb_compose_state: *mut ffi::xkb_compose_state, - ) -> Option<&'static str> { - if !self.ready() || xkb_compose_state.is_null() { + fn compose_get_utf8(&mut self) -> Option<&'static str> { + if !self.ready() || self.xkb_compose_state.is_null() { return None; } - let size = - unsafe { (XKBCH.xkb_compose_state_get_utf8)(xkb_compose_state, ptr::null_mut(), 0) } - + 1; - if size <= 1 { - return None; - }; - self.scratch_buffer.clear(); - let size = size.try_into().unwrap(); - self.scratch_buffer.reserve(size); - unsafe { - self.scratch_buffer.set_len(size); - (XKBCH.xkb_compose_state_get_utf8)( - xkb_compose_state, - self.scratch_buffer.as_mut_ptr() as *mut _, - size as usize, - ); - }; - // remove the final `\0` - self.scratch_buffer.pop(); - Some(byte_slice_to_cached_string(&self.scratch_buffer)) + self.scratch_buffer.truncate(0); + loop { + unsafe { + let size = (XKBCH.xkb_compose_state_get_utf8)( + self.xkb_compose_state, + self.scratch_buffer.as_mut_ptr() as *mut _, + self.scratch_buffer.capacity(), + ); + if size < 0 { + return None; + } + let size = size as usize; + if size >= self.scratch_buffer.capacity() { + self.scratch_buffer.reserve(size + 1); + continue; + } + self.scratch_buffer.set_len(size); + return Some(byte_slice_to_cached_string(&self.scratch_buffer)); + } + } } pub(crate) fn new() -> Result { @@ -283,6 +214,21 @@ impl KbState { return Err(Error::XKBNotFound); } + // let level = if log::log_enabled!(log::Level::Debug) { + // ffi::xkb_log_level::XKB_LOG_LEVEL_DEBUG + // } else if log::log_enabled!(log::Level::Info) { + // ffi::xkb_log_level::XKB_LOG_LEVEL_INFO + // } else if log::log_enabled!(log::Level::Warn) { + // ffi::xkb_log_level::XKB_LOG_LEVEL_WARNING + // } else if log::log_enabled!(log::Level::Error) { + // ffi::xkb_log_level::XKB_LOG_LEVEL_ERROR + // } else { + // ffi::xkb_log_level::XKB_LOG_LEVEL_CRITICAL + // }; + // unsafe { + // (XKBH.xkb_context_set_log_level)(context, level); + // } + let mut me = Self { #[cfg(feature = "x11")] xcb_connection: ptr::null_mut(), @@ -291,10 +237,11 @@ impl KbState { xkb_state: ptr::null_mut(), xkb_compose_table: ptr::null_mut(), xkb_compose_state: ptr::null_mut(), - xkb_compose_state_2: ptr::null_mut(), - mods_state: ModifiersState::new(), + mod_indices: Default::default(), + mods_state: ModifiersState::empty(), + #[cfg(feature = "wayland")] locked: false, - scratch_buffer: Vec::new(), + scratch_buffer: Vec::with_capacity(5), }; unsafe { me.init_compose() }; @@ -305,25 +252,14 @@ impl KbState { impl KbState { #[cfg(feature = "x11")] - pub(crate) fn from_x11_xkb(connection: *mut xcb_connection_t) -> Result { + pub(crate) fn from_x11_xkb( + connection: *mut xcb_connection_t, + device_id: std::os::raw::c_int, + ) -> Result { let mut me = Self::new()?; me.xcb_connection = connection; - let result = unsafe { - (XKBXH.xkb_x11_setup_xkb_extension)( - connection, - 1, - 2, - xkbcommon_dl::x11::xkb_x11_setup_xkb_extension_flags::XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ) - }; - assert_eq!(result, 1, "Failed to initialize libxkbcommon"); - - unsafe { me.init_with_x11_keymap() }; + unsafe { me.init_with_x11_keymap(device_id) }; Ok(me) } @@ -385,33 +321,21 @@ impl KbState { ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, ); - if compose_table.is_null() { - // init of compose state failed, continue without compose - (XKBCH.xkb_compose_table_unref)(compose_table); - return; - } - - let compose_state_2 = (XKBCH.xkb_compose_state_new)( - compose_table, - ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, - ); - - if compose_state_2.is_null() { + if compose_state.is_null() { // init of compose state failed, continue without compose (XKBCH.xkb_compose_table_unref)(compose_table); - (XKBCH.xkb_compose_state_unref)(compose_state); return; } self.xkb_compose_table = compose_table; self.xkb_compose_state = compose_state; - self.xkb_compose_state_2 = compose_state_2; } unsafe fn post_init(&mut self, state: *mut ffi::xkb_state, keymap: *mut ffi::xkb_keymap) { self.xkb_keymap = keymap; + self.mod_indices = ModIndices::from_keymap(keymap); self.xkb_state = state; - self.mods_state.update_with(state); + self.mods_state = xkb_state_to_modifiers(state, &self.mod_indices); } unsafe fn de_init(&mut self) { @@ -422,28 +346,25 @@ impl KbState { } #[cfg(feature = "x11")] - pub(crate) unsafe fn init_with_x11_keymap(&mut self) { + pub(crate) unsafe fn init_with_x11_keymap(&mut self, device_id: std::os::raw::c_int) { if !self.xkb_keymap.is_null() { self.de_init(); } - // TODO: Support keyboards other than the "virtual core keyboard device". - let core_keyboard_id = (XKBXH.xkb_x11_get_core_keyboard_device_id)(self.xcb_connection); let keymap = (XKBXH.xkb_x11_keymap_new_from_device)( self.xkb_context, self.xcb_connection, - core_keyboard_id, + device_id, xkbcommon_dl::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS, ); assert_ne!(keymap, ptr::null_mut()); - let state = - (XKBXH.xkb_x11_state_new_from_device)(keymap, self.xcb_connection, core_keyboard_id); + let state = (XKBXH.xkb_x11_state_new_from_device)(keymap, self.xcb_connection, device_id); self.post_init(state, keymap); } #[cfg(feature = "wayland")] - pub(crate) unsafe fn init_with_fd(&mut self, fd: File, size: usize) { + pub(crate) unsafe fn init_with_fd(&mut self, fd: std::fs::File, size: usize) { if !self.xkb_keymap.is_null() { self.de_init(); } @@ -492,6 +413,7 @@ impl KbState { } impl KbState { + #[cfg(feature = "wayland")] pub(crate) unsafe fn key_repeats(&mut self, keycode: ffi::xkb_keycode_t) -> bool { (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, keycode) == 1 } @@ -502,6 +424,7 @@ impl KbState { } #[inline] + #[cfg(feature = "wayland")] pub(crate) fn locked(&self) -> bool { self.locked } @@ -521,9 +444,6 @@ impl Drop for KbState { if !self.xkb_compose_state.is_null() { (XKBCH.xkb_compose_state_unref)(self.xkb_compose_state); } - if !self.xkb_compose_state_2.is_null() { - (XKBCH.xkb_compose_state_unref)(self.xkb_compose_state_2); - } if !self.xkb_compose_table.is_null() { (XKBCH.xkb_compose_table_unref)(self.xkb_compose_table); } @@ -543,211 +463,177 @@ pub enum Error { /// libxkbcommon is not available XKBNotFound, /// Provided RMLVO specified a keymap that would not be loaded + #[cfg(feature = "wayland")] BadNames, } impl KbState { - pub fn process_key_event(&mut self, keycode: u32, state: ElementState) -> KeyEventResults<'_> { - KeyEventResults::new(self, keycode, state == ElementState::Pressed) - } - - pub fn process_key_repeat_event(&mut self, keycode: u32) -> KeyEventResults<'_> { - KeyEventResults::new(self, keycode, false) - } - - fn keysym_to_utf8_raw(&mut self, keysym: u32) -> Option<&'static str> { - self.scratch_buffer.clear(); - self.scratch_buffer.reserve(8); - loop { - let bytes_written = unsafe { - (XKBH.xkb_keysym_to_utf8)( - keysym, - self.scratch_buffer.as_mut_ptr().cast(), - self.scratch_buffer.capacity(), - ) - }; - if bytes_written == 0 { - return None; - } else if bytes_written == -1 { - self.scratch_buffer.reserve(8); - } else { - unsafe { - self.scratch_buffer - .set_len(bytes_written.try_into().unwrap()) - }; - break; - } - } - - // remove the final `\0` - self.scratch_buffer.pop(); - Some(byte_slice_to_cached_string(&self.scratch_buffer)) - } -} - -#[derive(Copy, Clone, Debug)] -enum XkbCompose { - Accepted(ffi::xkb_compose_status), - Ignored, - Uninitialized, -} - -pub(crate) struct KeyEventResults<'a> { - state: &'a mut KbState, - keycode: u32, - keysym: u32, - compose: Option, -} + pub fn process_key_event( + &mut self, + keycode: u32, + group: u32, + state: ElementState, + ) -> KeyEventResults { + let keysym = self.get_one_sym_raw(keycode); -impl<'a> KeyEventResults<'a> { - fn new(state: &'a mut KbState, keycode: u32, compose: bool) -> Self { - let keysym = state.get_one_sym_raw(keycode); + let (text, text_with_all_modifiers); - let compose = if compose { - Some(match state.compose_feed_normal(keysym) { + if state == ElementState::Pressed { + // This is a press or repeat event. Feed the keysym to the compose engine. + match self.compose_feed(keysym) { Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED) => { - // Unwrapping is safe here, as `compose_feed` returns `None` when composition is uninitialized. - XkbCompose::Accepted(state.compose_status_normal().unwrap()) + // The keysym potentially affected the compose state. Check it. + match self.compose_status().unwrap() { + xkb_compose_status::XKB_COMPOSE_NOTHING => { + // There is no ongoing composing. Use the keysym on its own. + text = keysym_to_utf8_raw(keysym); + text_with_all_modifiers = self.get_utf8_raw(keycode); + } + xkb_compose_status::XKB_COMPOSE_COMPOSING => { + // Composing is ongoing and not yet completed. No text is produced. + text = None; + text_with_all_modifiers = None; + } + xkb_compose_status::XKB_COMPOSE_COMPOSED => { + // This keysym completed the sequence. The text is the result. + text = self.compose_get_utf8(); + // The current behaviour makes it so composing a character overrides attempts to input a + // control character with the `Ctrl` key. We can potentially add a configuration option + // if someone specifically wants the opposite behaviour. + text_with_all_modifiers = text; + } + xkb_compose_status::XKB_COMPOSE_CANCELLED => { + // Before this keysym, composing was ongoing. This keysym was not a possible + // continuation of the sequence and thus aborted composing. The standard + // behavior on linux in this case is to ignore both the sequence and this keysym. + text = None; + text_with_all_modifiers = None; + } + } + } + Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED) => { + // This keysym is a modifier and thus has no effect on the engine. Nor does it produce + // text. + text = None; + text_with_all_modifiers = None; + } + _ => { + // The compose engine is disabled. Use the keysym on its own. + text = keysym_to_utf8_raw(keysym); + text_with_all_modifiers = self.get_utf8_raw(keycode); } - Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED) => XkbCompose::Ignored, - None => XkbCompose::Uninitialized, - }) + } } else { - None + // This is a key release. No text is produced. + text = None; + text_with_all_modifiers = None; + } + + let key_without_modifiers = { + // This will become a pointer to an array which libxkbcommon owns, so we don't need to deallocate it. + let mut keysyms = ptr::null(); + let keysym_count = unsafe { + (XKBH.xkb_keymap_key_get_syms_by_level)( + self.xkb_keymap, + keycode, + group, + 0, + &mut keysyms, + ) + }; + let keysym = if keysym_count == 1 { + unsafe { *keysyms } + } else { + 0 + }; + keysym_to_key(keysym) }; - // let key_text = state.keysym_to_utf8_raw(keysym); - // unsafe { - // let layout_id = (XKBH.xkb_state_serialize_layout)(state.xkb_state, xkb_state_component::XKB_STATE_LAYOUT_EFFECTIVE); - // let layout_name_cstr = (XKBH.xkb_keymap_layout_get_name)(state.xkb_keymap, layout_id); - // let layout_name = std::ffi::CStr::from_ptr(layout_name_cstr as *mut _); - // debug!("KeyEventResults::new {:?}, {:?}", key_text, layout_name); - // } + let res = KeyEventResults { + keycode: super::keymap::raw_keycode_to_keycode(keycode), + location: super::keymap::keysym_location(keysym), + key: keysym_to_key(keysym), + key_without_modifiers, + text, + text_with_all_modifiers, + }; - KeyEventResults { - state, - keycode, - keysym, - compose, - } - } + // log::trace!("{:?}", res); - pub fn keycode(&mut self) -> KeyCode { - super::keymap::raw_keycode_to_keycode(self.keycode) + res } +} - pub fn key(&mut self) -> (Key<'static>, KeyLocation) { - self.keysym_to_key(self.keysym) - .unwrap_or_else(|(key, location)| match self.compose { - Some(XkbCompose::Accepted(ffi::xkb_compose_status::XKB_COMPOSE_COMPOSING)) => { - // When pressing a dead key twice, the non-combining variant of that character will be - // produced. Since this function only concerns itself with a single keypress, we simulate - // this double press here by feeding the keysym to the compose state twice. - self.state.compose_feed_2(self.keysym); - match self.state.compose_feed_2(self.keysym) { - Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED) => ( - // Extracting only a single `char` here *should* be fine, assuming that no dead - // key's non-combining variant ever occupies more than one `char`. - Key::Dead( - self.state - .compose_get_utf8_2() - .map(|s| s.chars().nth(0).unwrap()), - ), - location, - ), - _ => (key, location), - } - } - _ => { - let composed_text = self.composed_text(); - - #[cfg(feature = "x11")] - if let Ok(Some(composed_text)) = composed_text { - *X11_EVPROC_NEXT_COMPOSE.lock().unwrap() = Some(composed_text); - } +#[derive(Debug)] +pub(crate) struct KeyEventResults { + pub keycode: KeyCode, + pub location: KeyLocation, + pub key: Key<'static>, + pub key_without_modifiers: Key<'static>, + pub text: Option<&'static str>, + pub text_with_all_modifiers: Option<&'static str>, +} - ( - composed_text - .unwrap_or_else(|_| self.state.keysym_to_utf8_raw(self.keysym)) - .map(Key::Character) - .unwrap_or(key), - location, - ) - } - }) +fn keysym_to_key(keysym: u32) -> Key<'static> { + let key = super::keymap::keysym_to_key(keysym); + if let Key::Unidentified(_) = key { + keysym_to_utf8_raw(keysym) + .map(Key::Character) + .unwrap_or(key) + } else { + key } +} - pub fn key_without_modifiers(&mut self) -> (Key<'static>, KeyLocation) { - // This will become a pointer to an array which libxkbcommon owns, so we don't need to deallocate it. - let mut keysyms = ptr::null(); - let keysym_count = unsafe { - (XKBH.xkb_keymap_key_get_syms_by_level)( - self.state.xkb_keymap, - self.keycode, - 0, - 0, - &mut keysyms, - ) - }; - let keysym = if keysym_count == 1 { - unsafe { *keysyms } - } else { - 0 - }; - self.keysym_to_key(keysym) - .unwrap_or_else(|(key, location)| { - ( - self.state - .keysym_to_utf8_raw(keysym) - .map(Key::Character) - .unwrap_or(key), - location, - ) - }) - } +fn keysym_to_utf8_raw(keysym: u32) -> Option<&'static str> { + let utf32 = unsafe { (XKBH.xkb_keysym_to_utf32)(keysym) }; + char_to_str(utf32) +} - fn keysym_to_key( - &mut self, - keysym: u32, - ) -> Result<(Key<'static>, KeyLocation), (Key<'static>, KeyLocation)> { - let location = super::keymap::keysym_location(keysym); - let key = super::keymap::keysym_to_key(keysym); - if matches!(key, Key::Unidentified(_)) { - Err((key, location)) - } else { - Ok((key, location)) +fn char_to_str(utf32: u32) -> Option<&'static str> { + use std::cell::RefCell; + use std::collections::HashMap; + + if utf32 == 0 { + return None; + } + + if utf32 < 128 { + static ASCII: [u8; 128] = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, + ]; + unsafe { + debug_assert_eq!(ASCII[utf32 as usize], utf32 as u8); + return Some(str::from_utf8_unchecked(slice::from_raw_parts( + &ASCII[utf32 as usize], + 1, + ))); } } - pub fn text(&mut self) -> Option<&'static str> { - self.composed_text() - .unwrap_or_else(|_| self.state.keysym_to_utf8_raw(self.keysym)) - } - - pub fn text_with_all_modifiers(&mut self) -> Option<&'static str> { - // The current behaviour makes it so composing a character overrides attempts to input a - // control character with the `Ctrl` key. We can potentially add a configuration option - // if someone specifically wants the oppsite behaviour. - self.composed_text() - .unwrap_or_else(|_| self.state.get_utf8_raw(self.keycode)) + thread_local! { + static STRING_CACHE: RefCell> = RefCell::new(HashMap::new()); } - fn composed_text(&mut self) -> Result, ()> { - if let Some(compose) = &self.compose { - match compose { - XkbCompose::Accepted(status) => match status { - ffi::xkb_compose_status::XKB_COMPOSE_COMPOSED => { - Ok(self.state.compose_get_utf8_normal()) - } - ffi::xkb_compose_status::XKB_COMPOSE_NOTHING => Err(()), - _ => Ok(None), - }, - XkbCompose::Ignored | XkbCompose::Uninitialized => Err(()), - } + return STRING_CACHE.with(|cache| { + let mut cache = cache.borrow_mut(); + if let Some(string) = cache.get(&utf32) { + Some(*string) } else { - Err(()) + let mut buf = [0; 4]; + let char = char::from_u32(utf32).unwrap(); + let string: &'static str = + Box::leak(char.encode_utf8(&mut buf).to_string().into_boxed_str()); + cache.insert(utf32, string); + Some(string) } - } + }); } fn byte_slice_to_cached_string(bytes: &[u8]) -> &'static str { @@ -758,7 +644,7 @@ fn byte_slice_to_cached_string(bytes: &[u8]) -> &'static str { static STRING_CACHE: RefCell> = RefCell::new(HashSet::new()); } - let string = std::str::from_utf8(bytes).unwrap(); + let string = str::from_utf8(bytes).unwrap(); STRING_CACHE.with(|cache| { let mut cache = cache.borrow_mut(); diff --git a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs index 6452ae6570..e06e0c82cb 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/handlers.rs @@ -219,7 +219,7 @@ pub enum Event<'a> { /// The key modifiers have changed state Modifiers { /// current state of the modifiers - modifiers: xkb_state::ModifiersState, + modifiers: ModifiersState, }, /// A key event occurred Key { @@ -319,6 +319,7 @@ where current_repeat, details: repeat, }), + group: 0, }; (handler, source) }; @@ -368,6 +369,7 @@ struct RepeatDetails { struct KbdHandler { state: Rc>, + group: u32, callback: Rc>, repeat: Option, } @@ -379,7 +381,7 @@ struct KbdRepeat { } impl KbdRepeat { - fn start_repeat(&self, key: u32, keyboard: wl_keyboard::WlKeyboard, time: u32) { + fn start_repeat(&self, key: u32, group: u32, keyboard: wl_keyboard::WlKeyboard, time: u32) { // Start a new repetition, overwriting the previous ones self.timer_handle.cancel_all_timeouts(); @@ -391,6 +393,7 @@ impl KbdRepeat { *self.current_repeat.borrow_mut() = Some(RepeatData { keyboard, + group, keycode: key, gap, time: (time + self.details.delay) as u64 * 1000, @@ -548,24 +551,17 @@ impl KbdHandler { _ => unreachable!(), }; - let mut ker = state.process_key_event(key + 8, key_state); - - let physical_key = ker.keycode(); - let (logical_key, location) = ker.key(); - let text = ker.text(); - let (key_without_modifiers, _) = ker.key_without_modifiers(); - let text_with_all_modifiers = ker.text_with_all_modifiers(); - + let ker = state.process_key_event(key + 8, self.group, key_state); let repeats = unsafe { state.key_repeats(key) }; ( - physical_key, - logical_key, - text, - location, + ker.keycode, + ker.key, + ker.text, + ker.location, key_state, - key_without_modifiers, - text_with_all_modifiers, + ker.key_without_modifiers, + ker.text_with_all_modifiers, repeats, ) }; @@ -574,7 +570,7 @@ impl KbdHandler { if let Some(ref mut repeat_handle) = self.repeat { if repeats { if state == ElementState::Pressed { - repeat_handle.start_repeat(key, object.clone(), time); + repeat_handle.start_repeat(key, self.group, object.clone(), time); } else { repeat_handle.stop_repeat(key); } @@ -609,8 +605,9 @@ impl KbdHandler { dispatch_data: client::DispatchData<'_>, ) { { + self.group = group; let mut state = self.state.borrow_mut(); - state.update_modifiers(mods_depressed, mods_latched, mods_locked, 0, 0, group); + state.update_state(mods_depressed, mods_latched, mods_locked, 0, 0, group); (&mut *self.callback.borrow_mut())( Event::Modifiers { modifiers: state.mods_state(), @@ -639,6 +636,7 @@ impl KbdHandler { struct RepeatData { keyboard: wl_keyboard::WlKeyboard, + group: u32, keycode: u32, /// Gap between key presses in microseconds. gap: u64, @@ -683,25 +681,23 @@ impl calloop::EventSource for RepeatSource { if let Some(ref mut data) = *current_repeat.borrow_mut() { // there is something to repeat let mut state = state.borrow_mut(); - let mut ker = state.process_key_repeat_event(data.keycode + 8); - - let physical_key = ker.keycode(); - let (logical_key, location) = ker.key(); - let text = ker.text(); - let (key_without_modifiers, _) = ker.key_without_modifiers(); - let text_with_all_modifiers = ker.text_with_all_modifiers(); + let ker = state.process_key_event( + data.keycode + 8, + data.group, + ElementState::Pressed, + ); let new_time = data.gap + data.time; // Notify the callback. callback( Event::Repeat { time: (new_time / 1000) as u32, - physical_key, - logical_key, - text, - location, - key_without_modifiers, - text_with_all_modifiers, + physical_key: ker.keycode, + logical_key: ker.key, + text: ker.text, + location: ker.location, + key_without_modifiers: ker.key_without_modifiers, + text_with_all_modifiers: ker.text_with_all_modifiers, }, &mut data.keyboard, ); diff --git a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs b/src/platform_impl/linux/wayland/seat/keyboard/mod.rs index 1a57b441b0..d2fd21daf7 100644 --- a/src/platform_impl/linux/wayland/seat/keyboard/mod.rs +++ b/src/platform_impl/linux/wayland/seat/keyboard/mod.rs @@ -9,7 +9,6 @@ use sctk::reexports::client::protocol::wl_seat::WlSeat; use sctk::reexports::client::Attached; use crate::keyboard::ModifiersState; -use crate::platform_impl::platform::common::xkb_state; use crate::platform_impl::wayland::event_loop::WinitState; use crate::platform_impl::wayland::WindowId; @@ -89,14 +88,3 @@ impl KeyboardInner { } } } - -impl From for ModifiersState { - fn from(mods: xkb_state::ModifiersState) -> ModifiersState { - let mut wl_mods = ModifiersState::empty(); - wl_mods.set(ModifiersState::SHIFT, mods.shift); - wl_mods.set(ModifiersState::CONTROL, mods.ctrl); - wl_mods.set(ModifiersState::ALT, mods.alt); - wl_mods.set(ModifiersState::SUPER, mods.logo); - wl_mods - } -} diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 9dd110d9dd..c4dc2fa34f 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1,21 +1,21 @@ -use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc}; +use std::{collections::HashMap, rc::Rc, slice, sync::Arc}; -use libc::{c_char, c_int, c_long, c_ulong}; +use libc::{c_char, c_int, c_long, c_uint, c_ulong}; use parking_lot::MutexGuard; +use SeatFocus::{KbFocus, PtrFocus}; use super::{ ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd, DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId, XExtension, }; -use util::modifiers::{ModifierKeyState, ModifierKeymap}; - use crate::{ dpi::{PhysicalPosition, PhysicalSize}, - event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, TouchPhase, WindowEvent}, + event::{DeviceEvent, Event, KeyEvent, RawKeyEvent, TouchPhase, WindowEvent}, event_loop::EventLoopWindowTarget as RootELW, keyboard::ModifiersState, + platform::unix::x11::EventLoopWindowTarget, platform_impl::platform::{ common::{keymap, xkb_state::KbState}, KeyEventExtra, @@ -25,41 +25,81 @@ use crate::{ /// The X11 documentation states: "Keycodes lie in the inclusive range [8,255]". const KEYCODE_OFFSET: u8 = 8; +pub(super) struct Seat { + kb_state: KbState, + /// The master keyboard of this seat + keyboard: c_int, + /// The master pointer of this seat + pointer: c_int, + /// The window that has this seats keyboard focus + kb_focus: Option, + /// The window that has this seats pointer focus + ptr_focus: Option, + /// The latest modifiers state + current_modifiers: ModifiersState, +} + +enum SeatFocus { + KbFocus, + PtrFocus, +} + pub(super) struct EventProcessor { pub(super) dnd: Dnd, pub(super) ime_receiver: ImeReceiver, pub(super) randr_event_offset: c_int, - pub(super) devices: RefCell>, + pub(super) devices: HashMap, pub(super) xi2ext: XExtension, + pub(super) xkb_base_event: c_int, pub(super) target: Rc>, - pub(super) kb_state: KbState, - pub(super) mod_keymap: ModifierKeymap, - pub(super) device_mod_state: ModifierKeyState, + pub(super) seats: Vec, // Number of touch events currently in progress pub(super) num_touch: u32, pub(super) first_touch: Option, - // Currently focused window belonging to this process - pub(super) active_window: Option, } impl EventProcessor { - pub(super) fn init_device(&self, device: c_int) { - let wt = get_xtarget(&self.target); - let mut devices = self.devices.borrow_mut(); + pub(super) fn init_device( + target: &RootELW, + devices: &mut HashMap, + seats: &mut Vec, + device: c_int, + ) { + let wt = get_xtarget(target); if let Some(info) = DeviceInfo::get(&wt.xconn, device) { for info in info.iter() { - devices.insert(DeviceId(info.deviceid), Device::new(&self, info)); + let device_id = DeviceId(info.deviceid); + if info._use == ffi::XIMasterKeyboard { + if devices.contains_key(&device_id) { + seats.retain(|s| s.keyboard != info.deviceid); + } + let xconn = &wt.xconn; + let connection = unsafe { (xconn.xlib_xcb.XGetXCBConnection)(xconn.display) }; + let kb_state = KbState::from_x11_xkb(connection, info.deviceid).unwrap(); + seats.push(Seat { + kb_state, + keyboard: info.deviceid, + pointer: info.attachment, + kb_focus: None, + ptr_focus: None, + current_modifiers: ModifiersState::empty(), + }); + } + devices.insert(device_id, Device::new(wt, info)); } } } - fn with_window(&self, window_id: ffi::Window, callback: F) -> Option + fn with_window( + wt: &EventLoopWindowTarget, + window_id: ffi::Window, + callback: F, + ) -> Option where F: Fn(&Arc) -> Ret, { let mut deleted = false; let window_id = WindowId(window_id); - let wt = get_xtarget(&self.target); let result = wt .windows .borrow() @@ -77,8 +117,8 @@ impl EventProcessor { result } - fn window_exists(&self, window_id: ffi::Window) -> bool { - self.with_window(window_id, |_| ()).is_some() + fn window_exists(wt: &EventLoopWindowTarget, window_id: ffi::Window) -> bool { + Self::with_window(wt, window_id, |_| ()).is_some() } pub(super) fn poll(&self) -> bool { @@ -135,27 +175,6 @@ impl EventProcessor { return; } - // We can't call a `&mut self` method because of the above borrow, - // so we use this macro for repeated modifier state updates. - macro_rules! update_modifiers { - ( $state:expr , $modifier:expr ) => {{ - match ($state, $modifier) { - (state, modifier) => { - if let Some(modifiers) = - self.device_mod_state.update_state(&state, modifier) - { - if let Some(window_id) = self.active_window { - callback(Event::WindowEvent { - window_id: mkwid(window_id), - event: WindowEvent::ModifiersChanged(modifiers), - }); - } - } - } - } - }}; - } - let event_type = xev.get_type(); match event_type { ffi::ClientMessage => { @@ -318,7 +337,7 @@ impl EventProcessor { let xwindow = xev.window; let window_id = mkwid(xwindow); - if let Some(window) = self.with_window(xwindow, Arc::clone) { + if let Some(window) = Self::with_window(wt, xwindow, Arc::clone) { // So apparently... // `XSendEvent` (synthetic `ConfigureNotify`) -> position relative to root // `XConfigureNotify` (real `ConfigureNotify`) -> position relative to parent @@ -481,7 +500,7 @@ impl EventProcessor { // effect is that we waste some time trying to query unsupported properties. wt.xconn.update_cached_wm_info(wt.root); - self.with_window(xev.window, |window| { + Self::with_window(wt, xev.window, |window| { window.invalidate_cached_frame_extents(); }); } @@ -513,7 +532,7 @@ impl EventProcessor { let xev: &ffi::XVisibilityEvent = xev.as_ref(); let xwindow = xev.window; - self.with_window(xwindow, |window| window.visibility_notify()); + Self::with_window(wt, xwindow, |window| window.visibility_notify()); } ffi::Expose => { @@ -529,39 +548,6 @@ impl EventProcessor { } } - ffi::KeyPress => { - // TODO: Is it possible to exclusively use XInput2 events here? - let xkev: &mut ffi::XKeyEvent = xev.as_mut(); - - let window = xkev.window; - let window_id = mkwid(window); - - let keycode = xkev.keycode; - // When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with - // a keycode of 0. - if keycode == 0 { - let written = if let Some(ic) = wt.ime.borrow().get_context(window) { - wt.xconn.lookup_utf8(ic, xkev) - } else { - return; - }; - if super::super::common::xkb_state::X11_EVPROC_NEXT_COMPOSE - .lock() - .unwrap() - .take() - .map(|composed| composed == written) - .unwrap_or(false) - { - return; - } - let event = Event::WindowEvent { - window_id, - event: WindowEvent::ReceivedImeText(written), - }; - callback(event); - } - } - ffi::GenericEvent => { let guard = if let Some(e) = GenericEventCookie::from_event(&wt.xconn, *xev) { e @@ -578,24 +564,26 @@ impl EventProcessor { MouseButton::{Left, Middle, Other, Right}, MouseScrollDelta::LineDelta, Touch, - WindowEvent::{ - AxisMotion, CursorEntered, CursorLeft, CursorMoved, Focused, MouseInput, - MouseWheel, - }, + WindowEvent::{AxisMotion, CursorMoved, MouseInput, MouseWheel}, }; match xev.evtype { ffi::XI_ButtonPress | ffi::XI_ButtonRelease => { let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; - let window_id = mkwid(xev.event); - let device_id = mkdid(xev.deviceid); if (xev.flags & ffi::XIPointerEmulated) != 0 { // Deliver multi-touch events instead of emulated mouse events. return; } - let modifiers = ModifiersState::from_x11(&xev.mods); - update_modifiers!(modifiers, None); + let seat = match find_seat_by_pointer(&mut self.seats, xev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); + Self::update_seat_focus(seat, PtrFocus, wt, Some(xev.event), &mut callback); + + let window_id = mkwid(xev.event); + let device_id = mkdid(seat.keyboard); let state = if xev.evtype == ffi::XI_ButtonPress { Pressed @@ -603,33 +591,23 @@ impl EventProcessor { Released }; match xev.detail as u32 { - ffi::Button1 => callback(Event::WindowEvent { - window_id, - event: MouseInput { - device_id, - state, - button: Left, - modifiers, - }, - }), - ffi::Button2 => callback(Event::WindowEvent { - window_id, - event: MouseInput { - device_id, - state, - button: Middle, - modifiers, - }, - }), - ffi::Button3 => callback(Event::WindowEvent { - window_id, - event: MouseInput { - device_id, - state, - button: Right, - modifiers, - }, - }), + ffi::Button1 | ffi::Button2 | ffi::Button3 => { + let button = match xev.detail as u32 { + ffi::Button1 => Left, + ffi::Button2 => Middle, + ffi::Button3 => Right, + _ => unreachable!(), + }; + callback(Event::WindowEvent { + window_id, + event: MouseInput { + device_id, + state, + button, + modifiers: seat.current_modifiers, + }, + }) + } // Suppress emulated scroll wheel clicks, since we handle the real motion events for those. // In practice, even clicky scroll wheels appear to be reported by evdev (and XInput2 in @@ -648,7 +626,7 @@ impl EventProcessor { _ => unreachable!(), }, phase: TouchPhase::Moved, - modifiers, + modifiers: seat.current_modifiers, }, }); } @@ -660,21 +638,26 @@ impl EventProcessor { device_id, state, button: Other(x as u16), - modifiers, + modifiers: seat.current_modifiers, }, }), } } ffi::XI_Motion => { let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; - let device_id = mkdid(xev.deviceid); + + let seat = match find_seat_by_pointer(&mut self.seats, xev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); + Self::update_seat_focus(seat, PtrFocus, wt, Some(xev.event), &mut callback); + + let device_id = mkdid(seat.keyboard); let window_id = mkwid(xev.event); let new_cursor_pos = (xev.event_x, xev.event_y); - let modifiers = ModifiersState::from_x11(&xev.mods); - update_modifiers!(modifiers, None); - - let cursor_moved = self.with_window(xev.event, |window| { + let cursor_moved = Self::with_window(wt, xev.event, |window| { let mut shared_state_lock = window.shared_state.lock(); util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos) }); @@ -686,7 +669,7 @@ impl EventProcessor { event: CursorMoved { device_id, position, - modifiers, + modifiers: seat.current_modifiers, }, }); } else if cursor_moved.is_none() { @@ -702,11 +685,11 @@ impl EventProcessor { xev.valuators.mask_len as usize, ) }; - let mut devices = self.devices.borrow_mut(); - let physical_device = match devices.get_mut(&DeviceId(xev.sourceid)) { - Some(device) => device, - None => return, - }; + let physical_device = + match self.devices.get_mut(&DeviceId(xev.sourceid)) { + Some(device) => device, + None => return, + }; let mut value = xev.valuators.values; for i in 0..xev.valuators.mask_len * 8 { @@ -733,7 +716,7 @@ impl EventProcessor { } }, phase: TouchPhase::Moved, - modifiers, + modifiers: seat.current_modifiers, }, }); } else { @@ -758,11 +741,16 @@ impl EventProcessor { ffi::XI_Enter => { let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) }; - let window_id = mkwid(xev.event); - let device_id = mkdid(xev.deviceid); + let seat = match find_seat_by_pointer(&mut self.seats, xev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); + Self::update_seat_focus(seat, PtrFocus, wt, Some(xev.event), &mut callback); + + let device_id = mkdid(seat.keyboard); if let Some(all_info) = DeviceInfo::get(&wt.xconn, ffi::XIAllDevices) { - let mut devices = self.devices.borrow_mut(); for device_info in all_info.iter() { if device_info.deviceid == xev.sourceid // This is needed for resetting to work correctly on i3, and @@ -772,40 +760,22 @@ impl EventProcessor { || device_info.attachment == xev.sourceid { let device_id = DeviceId(device_info.deviceid); - if let Some(device) = devices.get_mut(&device_id) { + if let Some(device) = self.devices.get_mut(&device_id) { device.reset_scroll_position(device_info); } } } } - if self.window_exists(xev.event) { - callback(Event::WindowEvent { - window_id, - event: CursorEntered { device_id }, - }); - + if let Some(window) = seat.ptr_focus { let position = PhysicalPosition::new(xev.event_x, xev.event_y); - // The mods field on this event isn't actually populated, so query the - // pointer device. In the future, we can likely remove this round-trip by - // relying on `Xkb` for modifier values. - // - // This needs to only be done after confirming the window still exists, - // since otherwise we risk getting a `BadWindow` error if the window was - // dropped with queued events. - let modifiers = wt - .xconn - .query_pointer(xev.event, xev.deviceid) - .expect("Failed to query pointer device") - .get_modifier_state(); - callback(Event::WindowEvent { - window_id, + window_id: mkwid(window), event: CursorMoved { device_id, position, - modifiers, + modifiers: seat.current_modifiers, }, }); } @@ -813,17 +783,12 @@ impl EventProcessor { ffi::XI_Leave => { let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) }; - // Leave, FocusIn, and FocusOut can be received by a window that's already - // been destroyed, which the user presumably doesn't want to deal with. - let window_closed = !self.window_exists(xev.event); - if !window_closed { - callback(Event::WindowEvent { - window_id: mkwid(xev.event), - event: CursorLeft { - device_id: mkdid(xev.deviceid), - }, - }); - } + let seat = match find_seat_by_pointer(&mut self.seats, xev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); + Self::update_seat_focus(seat, PtrFocus, wt, None, &mut callback); } ffi::XI_FocusIn => { let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) }; @@ -833,91 +798,48 @@ impl EventProcessor { .focus(xev.event) .expect("Failed to focus input context"); - if self.active_window != Some(xev.event) { - self.active_window = Some(xev.event); - - let window_id = mkwid(xev.event); - let position = PhysicalPosition::new(xev.event_x, xev.event_y); - - callback(Event::WindowEvent { - window_id, - event: Focused(true), - }); - - // Issue key press events for all pressed keys - Self::handle_pressed_keys( - &wt, - window_id, - ElementState::Pressed, - &mut self.kb_state, - &self.mod_keymap, - &mut self.device_mod_state, - &mut callback, - ); - - // The deviceid for this event is for a keyboard instead of a pointer, - // so we have to do a little extra work. - let pointer_id = self - .devices - .borrow() - .get(&DeviceId(xev.deviceid)) - .map(|device| device.attachment) - .unwrap_or(2); - - callback(Event::WindowEvent { - window_id, - event: CursorMoved { - device_id: mkdid(pointer_id), - position, - modifiers: self.device_mod_state.modifiers(), - }, - }); - } + let seat = match find_seat(&mut self.seats, xev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); + Self::update_seat_focus(seat, KbFocus, wt, Some(xev.event), &mut callback); } ffi::XI_FocusOut => { let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) }; - if !self.window_exists(xev.event) { - return; - } + wt.ime .borrow_mut() .unfocus(xev.event) .expect("Failed to unfocus input context"); - if self.active_window.take() == Some(xev.event) { - let window_id = mkwid(xev.event); - - // Issue key release events for all pressed keys - Self::handle_pressed_keys( - &wt, - window_id, - ElementState::Released, - &mut self.kb_state, - &self.mod_keymap, - &mut self.device_mod_state, - &mut callback, - ); - - callback(Event::WindowEvent { - window_id, - event: Focused(false), - }) - } + let seat = match find_seat(&mut self.seats, xev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); + Self::update_seat_focus(seat, KbFocus, wt, None, &mut callback); } ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => { let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; - let window_id = mkwid(xev.event); let phase = match xev.evtype { ffi::XI_TouchBegin => TouchPhase::Started, ffi::XI_TouchUpdate => TouchPhase::Moved, ffi::XI_TouchEnd => TouchPhase::Ended, _ => unreachable!(), }; - if self.window_exists(xev.event) { + let seat = match find_seat_by_pointer(&mut self.seats, xev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); + Self::update_seat_focus(seat, PtrFocus, wt, Some(xev.event), &mut callback); + + if let Some(window) = seat.ptr_focus { + let window_id = mkwid(window); let id = xev.detail as u64; - let modifiers = self.device_mod_state.modifiers(); let location = PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64); @@ -928,9 +850,9 @@ impl EventProcessor { callback(Event::WindowEvent { window_id, event: WindowEvent::CursorMoved { - device_id: mkdid(util::VIRTUAL_CORE_POINTER), + device_id: mkdid(seat.keyboard), position: location.cast(), - modifiers, + modifiers: seat.current_modifiers, }, }); } @@ -938,7 +860,7 @@ impl EventProcessor { callback(Event::WindowEvent { window_id, event: WindowEvent::Touch(Touch { - device_id: mkdid(xev.deviceid), + device_id: mkdid(seat.keyboard), phase, location, force: None, // TODO @@ -1019,65 +941,46 @@ impl EventProcessor { // The regular KeyPress event has a problem where if you press a dead key, a KeyPress // event won't be emitted. XInput 2 does not have this problem. ffi::XI_KeyPress | ffi::XI_KeyRelease => { - if let Some(active_window) = self.active_window { + let xkev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; + + let seat = match find_seat(&mut self.seats, xkev.deviceid) { + Some(seat) => seat, + _ => return, + }; + Self::update_seat_kb_xi(seat, &xkev.mods, &xkev.group, &mut callback); + Self::update_seat_focus(seat, KbFocus, wt, Some(xkev.event), &mut callback); + + if let Some(focus) = seat.kb_focus { let state = if xev.evtype == ffi::XI_KeyPress { Pressed } else { Released }; - let xkev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; - - // We use `self.active_window` here as `xkev.event` has a completely different - // value for some reason. - let window_id = mkwid(active_window); - - let device_id = mkdid(xkev.deviceid); + let device_id = mkdid(seat.keyboard); let keycode = xkev.detail as u32; - let mut ker = self.kb_state.process_key_event(keycode, state); - let physical_key = ker.keycode(); - let (logical_key, location) = ker.key(); - let text = ker.text(); - let (key_without_modifiers, _) = ker.key_without_modifiers(); - let text_with_all_modifiers = ker.text_with_all_modifiers(); + let ker = seat.kb_state.process_key_event( + keycode, + xkev.group.effective as u32, + state, + ); let repeat = xkev.flags & ffi::XIKeyRepeat == ffi::XIKeyRepeat; - if let Some(modifier) = - self.mod_keymap.get_modifier(keycode as ffi::KeyCode) - { - let old_modifiers = self.device_mod_state.modifiers(); - - self.device_mod_state.key_event( - state, - keycode as ffi::KeyCode, - modifier, - ); - - if old_modifiers != self.device_mod_state.modifiers() { - callback(Event::WindowEvent { - window_id, - event: WindowEvent::ModifiersChanged( - self.device_mod_state.modifiers(), - ), - }); - } - } - callback(Event::WindowEvent { - window_id, + window_id: mkwid(focus), event: WindowEvent::KeyboardInput { device_id, event: KeyEvent { - physical_key, - logical_key, - text, - location, + physical_key: ker.keycode, + logical_key: ker.key, + text: ker.text, + location: ker.location, state, repeat, platform_specific: KeyEventExtra { - key_without_modifiers, - text_with_all_modifiers, + key_without_modifiers: ker.key_without_modifiers, + text_with_all_modifiers: ker.text_with_all_modifiers, }, }, is_synthetic: false, @@ -1087,44 +990,6 @@ impl EventProcessor { } ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => { - // This is horrible, but I couldn't manage to respect keyboard layout changes - // in any other way. In fact, getting this to work at all proved so frustrating - // that I (@maroider) lost motivation to work on the keyboard event rework for - // some months. Thankfully, @ArturKovacs offered to help debug the problem - // over discord, and the following is the result of that debugging session. - // - // Without the XKB extension, the X.Org server sends us the `MappingNotify` - // event when there's been a change in the keyboard layout. This stops - // being the case when we select ourselves some XKB events with `XkbSelectEvents` - // and the "core keyboard device (0x100)" (we haven't tried with any other - // devices). We managed to reproduce this on both our machines. - // - // With the XKB extension active, it would seem like we're supposed to use the - // `XkbStateNotify` event to detect keyboard layout changes, but the `group` - // never changes value (it is always `0`). This worked for @ArturKovacs, but - // not for me. We also tried to use the `group` given to us in keypress events, - // but it remained constant there, too. - // - // We also tried to see if there was some other event that got fired when the - // keyboard layout changed, and we found a mysterious event with the value - // `85` (`0x55`). We couldn't find any reference to it in the X11 headers or - // in the X.Org server source. - // - // `KeymapNotify` did briefly look interesting based purely on the name, but - // it is only useful for checking what keys are pressed when we receive the - // event. - // - // So instead of any vaguely reasonable approach, we get this: reloading the - // keymap on *every* keypress. That's peak efficiency right there! - // - // FIXME: Someone please save our souls! Or at least our wasted CPU cycles. - // - // If you do manage to find a solution, remember to re-enable (and handle) the - // `XkbStateNotify` event with `XkbSelectEventDetails` with a mask of - // `XkbAllStateComponentsMask & !XkbPointerButtonMask` like in - // . - unsafe { self.kb_state.init_with_x11_keymap() }; - let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; let state = match xev.evtype { @@ -1155,7 +1020,12 @@ impl EventProcessor { unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) } { if 0 != info.flags & (ffi::XISlaveAdded | ffi::XIMasterAdded) { - self.init_device(info.deviceid); + Self::init_device( + &self.target, + &mut self.devices, + &mut self.seats, + info.deviceid, + ); callback(Event::DeviceEvent { device_id: mkdid(info.deviceid), event: DeviceEvent::Added, @@ -1166,8 +1036,10 @@ impl EventProcessor { device_id: mkdid(info.deviceid), event: DeviceEvent::Removed, }); - let mut devices = self.devices.borrow_mut(); - devices.remove(&DeviceId(info.deviceid)); + self.devices.remove(&DeviceId(info.deviceid)); + if info._use == ffi::XIMasterKeyboard { + self.seats.retain(|s| s.keyboard != info.deviceid); + } } } } @@ -1175,68 +1047,95 @@ impl EventProcessor { _ => {} } } - _ => { - if event_type == self.randr_event_offset { - // In the future, it would be quite easy to emit monitor hotplug events. - let prev_list = monitor::invalidate_cached_monitor_list(); - if let Some(prev_list) = prev_list { - let new_list = wt.xconn.available_monitors(); - for new_monitor in new_list { - prev_list - .iter() - .find(|prev_monitor| prev_monitor.name == new_monitor.name) - .map(|prev_monitor| { - if new_monitor.scale_factor != prev_monitor.scale_factor { - for (window_id, window) in wt.windows.borrow().iter() { - if let Some(window) = window.upgrade() { - // Check if the window is on this monitor - let monitor = window.current_monitor(); - if monitor.name == new_monitor.name { - let (width, height) = - window.inner_size_physical(); - let (new_width, new_height) = window - .adjust_for_dpi( - prev_monitor.scale_factor, - new_monitor.scale_factor, - width, - height, - &*window.shared_state.lock(), - ); - - let window_id = crate::window::WindowId( - crate::platform_impl::platform::WindowId::X( - *window_id, - ), + _ if event_type == self.randr_event_offset => { + // In the future, it would be quite easy to emit monitor hotplug events. + let prev_list = monitor::invalidate_cached_monitor_list(); + if let Some(prev_list) = prev_list { + let new_list = wt.xconn.available_monitors(); + for new_monitor in new_list { + prev_list + .iter() + .find(|prev_monitor| prev_monitor.name == new_monitor.name) + .map(|prev_monitor| { + if new_monitor.scale_factor != prev_monitor.scale_factor { + for (window_id, window) in wt.windows.borrow().iter() { + if let Some(window) = window.upgrade() { + // Check if the window is on this monitor + let monitor = window.current_monitor(); + if monitor.name == new_monitor.name { + let (width, height) = window.inner_size_physical(); + let (new_width, new_height) = window + .adjust_for_dpi( + prev_monitor.scale_factor, + new_monitor.scale_factor, + width, + height, + &*window.shared_state.lock(), + ); + + let window_id = crate::window::WindowId( + crate::platform_impl::platform::WindowId::X( + *window_id, + ), + ); + let old_inner_size = + PhysicalSize::new(width, height); + let mut new_inner_size = + PhysicalSize::new(new_width, new_height); + + callback(Event::WindowEvent { + window_id, + event: WindowEvent::ScaleFactorChanged { + scale_factor: new_monitor.scale_factor, + new_inner_size: &mut new_inner_size, + }, + }); + + if new_inner_size != old_inner_size { + let (new_width, new_height) = + new_inner_size.into(); + window.set_inner_size_physical( + new_width, new_height, ); - let old_inner_size = - PhysicalSize::new(width, height); - let mut new_inner_size = - PhysicalSize::new(new_width, new_height); - - callback(Event::WindowEvent { - window_id, - event: WindowEvent::ScaleFactorChanged { - scale_factor: new_monitor.scale_factor, - new_inner_size: &mut new_inner_size, - }, - }); - - if new_inner_size != old_inner_size { - let (new_width, new_height) = - new_inner_size.into(); - window.set_inner_size_physical( - new_width, new_height, - ); - } } } } } - }); + } + }); + } + } + } + _ if event_type == self.xkb_base_event => { + let xev = unsafe { &*(xev as *const _ as *const ffi::XkbAnyEvent) }; + let seat = match find_seat(&mut self.seats, xev.device as c_int) { + Some(state) => state, + _ => return, + }; + match xev.xkb_type { + ffi::XkbMapNotify | ffi::XkbNewKeyboardNotify => { + unsafe { + seat.kb_state.init_with_x11_keymap(xev.device as c_int); } + Self::modifiers_changed(seat, &mut callback); + } + ffi::XkbStateNotify => { + let xev = unsafe { &*(xev as *const _ as *const ffi::XkbStateNotifyEvent) }; + Self::update_seat_kb( + seat, + xev.base_mods as u32, + xev.latched_mods as u32, + xev.locked_mods as u32, + xev.base_group as u32, + xev.latched_group as u32, + xev.locked_group as u32, + &mut callback, + ); } + _ => {} } } + _ => {} } match self.ime_receiver.try_recv() { @@ -1247,67 +1146,152 @@ impl EventProcessor { } } - fn handle_pressed_keys( - wt: &super::EventLoopWindowTarget, - window_id: crate::window::WindowId, - state: ElementState, - kb_state: &mut KbState, - mod_keymap: &ModifierKeymap, - device_mod_state: &mut ModifierKeyState, + /// Updates the window that has this seat's keyboard/pointer focus. + /// + /// This function must be called whenever an event occurs that could potentially imply a + /// change of the focus. Some non-multi-seat-aware window managers treat all focus changes + /// as changes of the core-seat focus. In these cases the X server will not send FocusIn/Out + /// events for additional seats. By calling this function on every keyboard/pointer input, + /// we update the focus as necessary. + fn update_seat_focus( + seat: &mut Seat, + component: SeatFocus, + wt: &EventLoopWindowTarget, + mut focus: Option, callback: &mut F, ) where F: FnMut(Event<'_, T>), { - let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD); - - // Update modifiers state and emit key events based on which keys are currently pressed. - for keycode in wt - .xconn - .query_keymap() - .into_iter() - .filter(|k| *k >= KEYCODE_OFFSET) - { - let keycode = keycode as u32; - - let mut ker = kb_state.process_key_event(keycode, state); - let physical_key = ker.keycode(); - let (logical_key, location) = ker.key(); - let text = ker.text(); - let (key_without_modifiers, _) = ker.key_without_modifiers(); - let text_with_all_modifiers = ker.text_with_all_modifiers(); + let old_focus_count = seat.kb_focus.is_some() as u8 + seat.ptr_focus.is_some() as u8; + let seat_focus = match component { + KbFocus => &mut seat.kb_focus, + PtrFocus => &mut seat.ptr_focus, + }; + if *seat_focus == focus { + return; + } + if let Some(new) = focus { + if !Self::window_exists(wt, new) { + if seat_focus.is_none() { + return; + } + focus = None; + } + } + if seat_focus.is_some() != focus.is_some() { + let new_focus_count = if focus.is_some() { + old_focus_count + 1 + } else { + old_focus_count - 1 + }; + if (new_focus_count == 0) != (old_focus_count == 0) { + let mask = if new_focus_count == 0 { + 0 + } else { + ffi::XkbModifierStateMask + }; + wt.xconn + .select_xkb_event_details( + seat.keyboard as c_uint, + ffi::XkbStateNotify as c_uint, + mask, + ) + .unwrap() + .queue(); + } + } + let device_id = mkdid(seat.keyboard); + if let Some(focus) = *seat_focus { + let event = match component { + KbFocus => WindowEvent::Focused(false), + PtrFocus => WindowEvent::CursorLeft { device_id }, + }; + callback(Event::WindowEvent { + window_id: mkwid(focus), + event, + }); + } + *seat_focus = focus; + if let Some(focus) = *seat_focus { + let event = match component { + KbFocus => WindowEvent::Focused(true), + PtrFocus => WindowEvent::CursorEntered { device_id }, + }; + callback(Event::WindowEvent { + window_id: mkwid(focus), + event, + }); + callback(Event::WindowEvent { + window_id: mkwid(focus), + event: WindowEvent::ModifiersChanged(seat.current_modifiers), + }); + } + } - if let Some(modifier) = mod_keymap.get_modifier(keycode as ffi::KeyCode) { - let old_modifiers = device_mod_state.modifiers(); + fn update_seat_kb_xi( + seat: &mut Seat, + mods: &ffi::XIModifierState, + group: &ffi::XIGroupState, + callback: &mut F, + ) where + F: FnMut(Event<'_, T>), + { + Self::update_seat_kb( + seat, + mods.base as u32, + mods.latched as u32, + mods.locked as u32, + group.base as u32, + group.latched as u32, + group.locked as u32, + callback, + ); + } - device_mod_state.key_event(state, keycode as ffi::KeyCode, modifier); + fn update_seat_kb( + seat: &mut Seat, + mods_depressed: u32, + mods_latched: u32, + mods_locked: u32, + depressed_group: u32, + latched_group: u32, + locked_group: u32, + callback: &mut F, + ) where + F: FnMut(Event<'_, T>), + { + seat.kb_state.update_state( + mods_depressed, + mods_latched, + mods_locked, + depressed_group, + latched_group, + locked_group, + ); + Self::modifiers_changed(seat, callback); + } - if old_modifiers != device_mod_state.modifiers() { + fn modifiers_changed(seat: &mut Seat, callback: &mut F) + where + F: FnMut(Event<'_, T>), + { + let new = seat.kb_state.mods_state(); + if seat.current_modifiers != new { + seat.current_modifiers = new; + let targets = [seat.kb_focus, seat.ptr_focus]; + let targets = if seat.kb_focus == seat.ptr_focus { + &targets[..1] + } else { + &targets[..] + }; + for target in targets { + if let Some(target) = target { callback(Event::WindowEvent { - window_id, - event: WindowEvent::ModifiersChanged(device_mod_state.modifiers()), + window_id: mkwid(*target), + event: WindowEvent::ModifiersChanged(new), }); } } - - callback(Event::WindowEvent { - window_id, - event: WindowEvent::KeyboardInput { - device_id, - event: KeyEvent { - physical_key, - logical_key, - text, - location, - state, - repeat: false, - platform_specific: KeyEventExtra { - key_without_modifiers, - text_with_all_modifiers, - }, - }, - is_synthetic: true, - }, - }); } } } @@ -1331,3 +1315,11 @@ fn is_first_touch(first: &mut Option, num: &mut u32, id: u64, phase: TouchP *first == Some(id) } + +fn find_seat(seats: &mut [Seat], kb: c_int) -> Option<&mut Seat> { + seats.iter_mut().find(|s| s.keyboard == kb) +} + +fn find_seat_by_pointer(seats: &mut [Seat], pointer: c_int) -> Option<&mut Seat> { + seats.iter_mut().find(|s| s.pointer == pointer) +} diff --git a/src/platform_impl/linux/x11/ime/mod.rs b/src/platform_impl/linux/x11/ime/mod.rs index b95da71101..4e2db1ef5e 100644 --- a/src/platform_impl/linux/x11/ime/mod.rs +++ b/src/platform_impl/linux/x11/ime/mod.rs @@ -99,17 +99,6 @@ impl Ime { Ok(!self.is_destroyed()) } - pub fn get_context(&self, window: ffi::Window) -> Option { - if self.is_destroyed() { - return None; - } - if let Some(&Some(ref context)) = self.inner.contexts.get(&window) { - Some(context.ic) - } else { - None - } - } - pub fn remove_context(&mut self, window: ffi::Window) -> Result { if let Some(Some(context)) = self.inner.contexts.remove(&window) { unsafe { diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 6aa493e215..0e3465aa9f 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -50,9 +50,7 @@ use self::{ dnd::{Dnd, DndState}, event_processor::EventProcessor, ime::{Ime, ImeCreationError, ImeReceiver, ImeSender}, - util::modifiers::ModifierKeymap, }; -use super::common::xkb_state::KbState; use crate::{ error::OsError as RootOsError, event::{Event, StartCause}, @@ -61,6 +59,11 @@ use crate::{ window::WindowAttributes, }; +use xkbcommon_dl::{ + xkb_x11_setup_xkb_extension_flags::XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, + XKBCOMMON_X11_HANDLE as XKBXH, +}; + const X_TOKEN: Token = Token(0); const USER_REDRAW_TOKEN: Token = Token(1); @@ -162,6 +165,26 @@ impl EventLoop { ext }; + let xkb_base_event = { + let mut base_event = 0; + + let result = unsafe { + (XKBXH.xkb_x11_setup_xkb_extension)( + (xconn.xlib_xcb.XGetXCBConnection)(xconn.display), + 1, // major + 0, // minor + XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, + ptr::null_mut(), // major out + ptr::null_mut(), // minor out + &mut base_event, + ptr::null_mut(), // base error + ) + }; + assert_eq!(result, 1, "Failed to initialize libxkbcommon"); + + base_event + }; + unsafe { let mut xinput_major_ver = ffi::XI_2_Major; let mut xinput_minor_ver = ffi::XI_2_Minor; @@ -180,9 +203,6 @@ impl EventLoop { xconn.update_cached_wm_info(root); - let mut mod_keymap = ModifierKeymap::new(); - mod_keymap.reset_from_x_connection(&xconn); - let poll = Poll::new().unwrap(); let waker = Arc::new(Waker::new(poll.registry(), USER_REDRAW_TOKEN).unwrap()); let queue = Arc::new(NotificationQueue::new(waker)); @@ -195,10 +215,6 @@ impl EventLoop { let (redraw_sender, redraw_channel) = channel(queue, NotificationId::gen_next()); - let kb_state = - KbState::from_x11_xkb(unsafe { (xconn.xlib_xcb.XGetXCBConnection)(xconn.display) }) - .unwrap(); - let target = Rc::new(RootELW { p: super::EventLoopWindowTarget::X(EventLoopWindowTarget { ime, @@ -214,19 +230,17 @@ impl EventLoop { _marker: ::std::marker::PhantomData, }); - let event_processor = EventProcessor { + let mut event_processor = EventProcessor { target: target.clone(), dnd, devices: Default::default(), randr_event_offset, ime_receiver, xi2ext, - kb_state, - mod_keymap, - device_mod_state: Default::default(), + xkb_base_event: xkb_base_event as c_int, num_touch: 0, first_touch: None, - active_window: None, + seats: Default::default(), }; // Register for device hotplug events @@ -236,7 +250,12 @@ impl EventLoop { .select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask) .queue(); - event_processor.init_device(ffi::XIAllDevices); + EventProcessor::init_device( + &target, + &mut event_processor.devices, + &mut event_processor.seats, + ffi::XIAllDevices, + ); let result = EventLoop { poll, @@ -614,11 +633,19 @@ enum ScrollOrientation { } impl Device { - fn new(el: &EventProcessor, info: &ffi::XIDeviceInfo) -> Self { + fn new(wt: &EventLoopWindowTarget, info: &ffi::XIDeviceInfo) -> Self { let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() }; let mut scroll_axes = Vec::new(); - let wt = get_xtarget(&el.target); + if info._use == ffi::XIMasterKeyboard { + wt.xconn + .select_xkb_events( + info.deviceid as c_uint, + ffi::XkbNewKeyboardNotifyMask | ffi::XkbMapNotifyMask, + ) + .unwrap() + .queue(); + } if Device::physical_device(info) { // Register for global raw events diff --git a/src/platform_impl/linux/x11/util/input.rs b/src/platform_impl/linux/x11/util/input.rs index 24aad57937..daef20b69e 100644 --- a/src/platform_impl/linux/x11/util/input.rs +++ b/src/platform_impl/linux/x11/util/input.rs @@ -1,30 +1,6 @@ -use std::{slice, str}; - use super::*; -use crate::keyboard::ModifiersState; pub const VIRTUAL_CORE_POINTER: c_int = 2; -pub const VIRTUAL_CORE_KEYBOARD: c_int = 3; - -// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to -// re-allocate (and make another round-trip) in the *vast* majority of cases. -// To test if `lookup_utf8` works correctly, set this to 1. -const TEXT_BUFFER_SIZE: usize = 1024; - -impl ModifiersState { - pub(crate) fn from_x11(state: &ffi::XIModifierState) -> Self { - ModifiersState::from_x11_mask(state.effective as c_uint) - } - - pub(crate) fn from_x11_mask(mask: c_uint) -> Self { - let mut m = ModifiersState::empty(); - m.set(ModifiersState::ALT, mask & ffi::Mod1Mask != 0); - m.set(ModifiersState::SHIFT, mask & ffi::ShiftMask != 0); - m.set(ModifiersState::CONTROL, mask & ffi::ControlMask != 0); - m.set(ModifiersState::SUPER, mask & ffi::Mod4Mask != 0); - m - } -} // NOTE: Some of these fields are not used, but may be of use in the future. pub struct PointerState<'a> { @@ -36,17 +12,9 @@ pub struct PointerState<'a> { pub win_x: c_double, pub win_y: c_double, buttons: ffi::XIButtonState, - modifiers: ffi::XIModifierState, - pub group: ffi::XIGroupState, pub relative_to_window: bool, } -impl<'a> PointerState<'a> { - pub fn get_modifier_state(&self) -> ModifiersState { - ModifiersState::from_x11(&self.modifiers) - } -} - impl<'a> Drop for PointerState<'a> { fn drop(&mut self) { if !self.buttons.mask.is_null() { @@ -91,6 +59,23 @@ impl XConnection { } } + pub fn select_xkb_event_details( + &self, + device_id: c_uint, + event: c_uint, + mask: c_ulong, + ) -> Option> { + let status = unsafe { + (self.xlib.XkbSelectEventDetails)(self.display, device_id, event, mask, mask) + }; + if status == ffi::True { + Some(Flusher::new(self)) + } else { + error!("Could not select XKB events: The XKB extension is not initialized!"); + None + } + } + pub fn query_pointer( &self, window: ffi::Window, @@ -133,58 +118,8 @@ impl XConnection { win_x, win_y, buttons, - modifiers, - group, relative_to_window, }) } } - - fn lookup_utf8_inner( - &self, - ic: ffi::XIC, - key_event: &mut ffi::XKeyEvent, - buffer: *mut u8, - size: usize, - ) -> (ffi::KeySym, ffi::Status, c_int) { - let mut keysym: ffi::KeySym = 0; - let mut status: ffi::Status = 0; - let count = unsafe { - (self.xlib.Xutf8LookupString)( - ic, - key_event, - buffer as *mut c_char, - size as c_int, - &mut keysym, - &mut status, - ) - }; - (keysym, status, count) - } - - pub fn lookup_utf8(&self, ic: ffi::XIC, key_event: &mut ffi::XKeyEvent) -> String { - // `assume_init` is safe here because the array consists of `MaybeUninit` values, - // which do not require initialization. - let mut buffer: [MaybeUninit; TEXT_BUFFER_SIZE] = - unsafe { MaybeUninit::uninit().assume_init() }; - // If the buffer overflows, we'll make a new one on the heap. - let mut vec; - - let (_, status, count) = - self.lookup_utf8_inner(ic, key_event, buffer.as_mut_ptr() as *mut u8, buffer.len()); - - let bytes = if status == ffi::XBufferOverflow { - vec = Vec::with_capacity(count as usize); - let (_, _, new_count) = - self.lookup_utf8_inner(ic, key_event, vec.as_mut_ptr(), vec.capacity()); - debug_assert_eq!(count, new_count); - - unsafe { vec.set_len(count as usize) }; - &vec[..count as usize] - } else { - unsafe { slice::from_raw_parts(buffer.as_ptr() as *const u8, count as usize) } - }; - - str::from_utf8(bytes).unwrap_or("").to_string() - } } diff --git a/src/platform_impl/linux/x11/util/keys.rs b/src/platform_impl/linux/x11/util/keys.rs deleted file mode 100644 index fc0c9d9062..0000000000 --- a/src/platform_impl/linux/x11/util/keys.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::{iter::Enumerate, ptr, slice::Iter}; - -use super::*; - -pub struct Keymap { - keys: [u8; 32], -} - -pub struct KeymapIter<'a> { - iter: Enumerate>, - index: usize, - item: Option, -} - -impl Keymap { - pub fn iter(&self) -> KeymapIter<'_> { - KeymapIter { - iter: self.keys.iter().enumerate(), - index: 0, - item: None, - } - } -} - -impl<'a> IntoIterator for &'a Keymap { - type Item = ffi::KeyCode; - type IntoIter = KeymapIter<'a>; - - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl Iterator for KeymapIter<'_> { - type Item = ffi::KeyCode; - - fn next(&mut self) -> Option { - if self.item.is_none() { - while let Some((index, &item)) = self.iter.next() { - if item != 0 { - self.index = index; - self.item = Some(item); - break; - } - } - } - - self.item.take().map(|item| { - debug_assert!(item != 0); - - let bit = first_bit(item); - - if item != bit { - // Remove the first bit; save the rest for further iterations - self.item = Some(item ^ bit); - } - - let shift = bit.trailing_zeros() + (self.index * 8) as u32; - shift as ffi::KeyCode - }) - } -} - -impl XConnection { - pub fn keycode_to_keysym(&self, keycode: ffi::KeyCode) -> ffi::KeySym { - unsafe { (self.xlib.XKeycodeToKeysym)(self.display, keycode, 0) } - } - - pub fn lookup_keysym(&self, xkev: &mut ffi::XKeyEvent) -> ffi::KeySym { - let mut keysym = 0; - - unsafe { - (self.xlib.XLookupString)(xkev, ptr::null_mut(), 0, &mut keysym, ptr::null_mut()); - } - - keysym - } - - pub fn query_keymap(&self) -> Keymap { - let mut keys = [0; 32]; - - unsafe { - (self.xlib.XQueryKeymap)(self.display, keys.as_mut_ptr() as *mut c_char); - } - - Keymap { keys } - } -} - -fn first_bit(b: u8) -> u8 { - 1 << b.trailing_zeros() -} diff --git a/src/platform_impl/linux/x11/util/mod.rs b/src/platform_impl/linux/x11/util/mod.rs index df3d89138a..b7ea4e7690 100644 --- a/src/platform_impl/linux/x11/util/mod.rs +++ b/src/platform_impl/linux/x11/util/mod.rs @@ -9,9 +9,7 @@ mod geometry; mod hint; mod icon; mod input; -pub mod keys; mod memory; -pub mod modifiers; mod randr; mod window_property; mod wm; diff --git a/src/platform_impl/linux/x11/util/modifiers.rs b/src/platform_impl/linux/x11/util/modifiers.rs deleted file mode 100644 index d7e10fa0b2..0000000000 --- a/src/platform_impl/linux/x11/util/modifiers.rs +++ /dev/null @@ -1,190 +0,0 @@ -use std::{collections::HashMap, slice}; - -use super::*; - -use crate::event::ElementState; -use crate::keyboard::ModifiersState; - -// Offsets within XModifierKeymap to each set of keycodes. -// We are only interested in Shift, Control, Alt, and Logo. -// -// There are 8 sets total. The order of keycode sets is: -// Shift, Lock, Control, Mod1 (Alt), Mod2, Mod3, Mod4 (Logo), Mod5 -// -// https://tronche.com/gui/x/xlib/input/XSetModifierMapping.html -const SHIFT_OFFSET: usize = 0; -const CONTROL_OFFSET: usize = 2; -const ALT_OFFSET: usize = 3; -const LOGO_OFFSET: usize = 6; -const NUM_MODS: usize = 8; - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum Modifier { - Alt, - Ctrl, - Shift, - Logo, -} - -#[derive(Debug, Default)] -pub struct ModifierKeymap { - // Maps keycodes to modifiers - keys: HashMap, -} - -#[derive(Clone, Debug, Default)] -pub struct ModifierKeyState { - // Contains currently pressed modifier keys and their corresponding modifiers - keys: HashMap, - state: ModifiersState, -} - -impl ModifierKeymap { - pub fn new() -> ModifierKeymap { - ModifierKeymap::default() - } - - pub fn get_modifier(&self, keycode: ffi::KeyCode) -> Option { - self.keys.get(&keycode).cloned() - } - - pub fn reset_from_x_connection(&mut self, xconn: &XConnection) { - unsafe { - let keymap = (xconn.xlib.XGetModifierMapping)(xconn.display); - - if keymap.is_null() { - panic!("failed to allocate XModifierKeymap"); - } - - self.reset_from_x_keymap(&*keymap); - - (xconn.xlib.XFreeModifiermap)(keymap); - } - } - - pub fn reset_from_x_keymap(&mut self, keymap: &ffi::XModifierKeymap) { - let keys_per_mod = keymap.max_keypermod as usize; - - let keys = unsafe { - slice::from_raw_parts(keymap.modifiermap as *const _, keys_per_mod * NUM_MODS) - }; - - self.keys.clear(); - - self.read_x_keys(keys, SHIFT_OFFSET, keys_per_mod, Modifier::Shift); - self.read_x_keys(keys, CONTROL_OFFSET, keys_per_mod, Modifier::Ctrl); - self.read_x_keys(keys, ALT_OFFSET, keys_per_mod, Modifier::Alt); - self.read_x_keys(keys, LOGO_OFFSET, keys_per_mod, Modifier::Logo); - } - - fn read_x_keys( - &mut self, - keys: &[ffi::KeyCode], - offset: usize, - keys_per_mod: usize, - modifier: Modifier, - ) { - let start = offset * keys_per_mod; - let end = start + keys_per_mod; - - for &keycode in &keys[start..end] { - if keycode != 0 { - self.keys.insert(keycode, modifier); - } - } - } -} - -impl ModifierKeyState { - pub fn update_keymap(&mut self, mods: &ModifierKeymap) { - self.keys.retain(|k, v| { - if let Some(m) = mods.get_modifier(*k) { - *v = m; - true - } else { - false - } - }); - - self.reset_state(); - } - - pub fn update_state( - &mut self, - state: &ModifiersState, - except: Option, - ) -> Option { - let mut new_state = *state; - - match except { - Some(Modifier::Alt) => new_state.set(ModifiersState::ALT, self.state.alt_key()), - Some(Modifier::Ctrl) => { - new_state.set(ModifiersState::CONTROL, self.state.control_key()) - } - Some(Modifier::Shift) => new_state.set(ModifiersState::SHIFT, self.state.shift_key()), - Some(Modifier::Logo) => new_state.set(ModifiersState::SUPER, self.state.super_key()), - None => (), - } - - if self.state == new_state { - None - } else { - self.keys.retain(|_k, v| get_modifier(&new_state, *v)); - self.state = new_state; - Some(new_state) - } - } - - pub fn modifiers(&self) -> ModifiersState { - self.state - } - - pub fn key_event(&mut self, state: ElementState, keycode: ffi::KeyCode, modifier: Modifier) { - match state { - ElementState::Pressed => self.key_press(keycode, modifier), - ElementState::Released => self.key_release(keycode), - } - } - - pub fn key_press(&mut self, keycode: ffi::KeyCode, modifier: Modifier) { - self.keys.insert(keycode, modifier); - - set_modifier(&mut self.state, modifier, true); - } - - pub fn key_release(&mut self, keycode: ffi::KeyCode) { - if let Some(modifier) = self.keys.remove(&keycode) { - if self.keys.values().find(|&&m| m == modifier).is_none() { - set_modifier(&mut self.state, modifier, false); - } - } - } - - fn reset_state(&mut self) { - let mut new_state = ModifiersState::default(); - - for &m in self.keys.values() { - set_modifier(&mut new_state, m, true); - } - - self.state = new_state; - } -} - -fn get_modifier(state: &ModifiersState, modifier: Modifier) -> bool { - match modifier { - Modifier::Alt => state.alt_key(), - Modifier::Ctrl => state.control_key(), - Modifier::Shift => state.shift_key(), - Modifier::Logo => state.super_key(), - } -} - -fn set_modifier(state: &mut ModifiersState, modifier: Modifier, value: bool) { - match modifier { - Modifier::Alt => state.set(ModifiersState::ALT, value), - Modifier::Ctrl => state.set(ModifiersState::CONTROL, value), - Modifier::Shift => state.set(ModifiersState::SHIFT, value), - Modifier::Logo => state.set(ModifiersState::SUPER, value), - } -} From 86039d8a403326b9c1014376601155a026c501d6 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 23 Nov 2021 17:01:12 +0100 Subject: [PATCH 125/131] X11: Implement reset_dead_keys This patch implements reset_dead_keys for X11. The previous implementation was incorrect in that it only affected a single instance of KbState and that it relied on global state which could interfere with other event loops. This patch also removes the previous implementation for Wayland. The wayland backend is architecturally broken because it processes key events as soon as they are received and not immediately before they are dispatched. As such a call to reset_dead_keys would possibly not even affect the following key event because it has already been computed. Before reset_dead_keys can be implemented, the backend must be refactored to take this into account. --- src/platform_impl/linux/common/xkb_state.rs | 18 ++++++------------ src/platform_impl/linux/mod.rs | 2 +- src/platform_impl/linux/wayland/window/mod.rs | 5 +++++ src/platform_impl/linux/x11/event_processor.rs | 10 ++++++++++ src/platform_impl/linux/x11/mod.rs | 3 +++ src/platform_impl/linux/x11/window.rs | 9 +++++++++ 6 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index f6f3a0a6fa..0fdad4f5d5 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -1,7 +1,6 @@ use std::ffi::CString; use std::os::raw::c_char; use std::os::unix::ffi::OsStringExt; -use std::sync::atomic::{AtomicBool, Ordering}; use std::{char, env, ptr, slice, str}; #[cfg(feature = "wayland")] @@ -24,14 +23,6 @@ use crate::{ keyboard::{Key, KeyCode, KeyLocation, ModifiersState}, }; -// TODO: Wire this up without using a static `AtomicBool`. -static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false); - -#[inline] -pub(crate) fn reset_dead_keys() { - RESET_DEAD_KEYS.store(true, Ordering::SeqCst); -} - pub(crate) struct KbState { #[cfg(feature = "x11")] xcb_connection: *mut xcb_connection_t, @@ -164,12 +155,15 @@ impl KbState { if !self.ready() || self.xkb_compose_state.is_null() { return None; } - if RESET_DEAD_KEYS.swap(false, Ordering::SeqCst) { - unsafe { self.init_compose() }; - } Some(unsafe { (XKBCH.xkb_compose_state_feed)(self.xkb_compose_state, keysym) }) } + pub fn reset_dead_keys(&mut self) { + unsafe { + (XKBCH.xkb_compose_state_reset)(self.xkb_compose_state); + } + } + fn compose_status(&mut self) -> Option { if !self.ready() || self.xkb_compose_state.is_null() { return None; diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 78ccdd92b7..8e7b3d92d0 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -441,7 +441,7 @@ impl Window { #[inline] pub fn reset_dead_keys(&self) { - common::xkb_state::reset_dead_keys() + x11_or_wayland!(match self; Window(w) => w.reset_dead_keys()) } #[inline] diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 5f59651edb..fc6784c7c8 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -610,6 +610,11 @@ impl Window { self.event_loop_awakener.ping(); } + #[inline] + pub fn reset_dead_keys(&self) { + // not implemented + } + #[inline] pub fn display(&self) -> &Display { &self.display diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index c4dc2fa34f..f26126ed1e 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1,3 +1,4 @@ +use std::sync::atomic::Ordering::Relaxed; use std::{collections::HashMap, rc::Rc, slice, sync::Arc}; use libc::{c_char, c_int, c_long, c_uint, c_ulong}; @@ -160,6 +161,15 @@ impl EventProcessor { F: FnMut(Event<'_, T>), { let wt = get_xtarget(&self.target); + { + let reset_dead_keys = wt.reset_dead_keys.load(Relaxed); + if reset_dead_keys != 0 { + for seat in &mut self.seats { + seat.kb_state.reset_dead_keys(); + } + wt.reset_dead_keys.fetch_sub(reset_dead_keys, Relaxed); + } + } // XFilterEvent tells us when an event has been discarded by the input method. // Specifically, this involves all of the KeyPress events in compose/pre-edit sequences, // along with an extra copy of the KeyRelease events. This also prevents backspace and diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 0e3465aa9f..798000bb93 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -21,6 +21,7 @@ pub use self::{ xdisplay::{XConnection, XError, XNotSupported}, }; +use std::sync::atomic::AtomicUsize; use std::{ cell::RefCell, collections::{HashMap, HashSet}, @@ -76,6 +77,7 @@ pub struct EventLoopWindowTarget { ime: RefCell, windows: RefCell>>, redraw_sender: Sender, + reset_dead_keys: Arc, _marker: ::std::marker::PhantomData, } @@ -226,6 +228,7 @@ impl EventLoop { wm_delete_window, net_wm_ping, redraw_sender, + reset_dead_keys: Arc::new(AtomicUsize::new(0)), }), _marker: ::std::marker::PhantomData, }); diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 494263d56e..765d284d5c 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -1,4 +1,6 @@ use raw_window_handle::unix::XlibHandle; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::Relaxed; use std::{ cmp, env, ffi::CString, @@ -104,6 +106,7 @@ pub struct UnownedWindow { ime_sender: Mutex, pub shared_state: Mutex, redraw_sender: Sender, + reset_dead_keys: Arc, } impl UnownedWindow { @@ -252,6 +255,7 @@ impl UnownedWindow { ime_sender: Mutex::new(event_loop.ime_sender.clone()), shared_state: SharedState::new(guessed_monitor, window_attrs.visible), redraw_sender: event_loop.redraw_sender.clone(), + reset_dead_keys: event_loop.reset_dead_keys.clone(), }; // Title must be set before mapping. Some tiling window managers (i.e. i3) use the window @@ -1339,6 +1343,11 @@ impl UnownedWindow { self.set_ime_position_physical(x, y); } + #[inline] + pub fn reset_dead_keys(&self) { + self.reset_dead_keys.fetch_add(1, Relaxed); + } + #[inline] pub fn request_user_attention(&self, request_type: Option) { let mut wm_hints = self From 31d6261ffb6f0c9f4848ab4c6791df5ce8b3457f Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Fri, 22 Oct 2021 18:04:01 +0200 Subject: [PATCH 126/131] X11: Remove IME support As of the previous patch, this code no longer has any effect. A future patch *might* restore XIM support. --- .../linux/x11/event_processor.rs | 27 +- src/platform_impl/linux/x11/ime/callbacks.rs | 175 ----------- src/platform_impl/linux/x11/ime/context.rs | 139 --------- src/platform_impl/linux/x11/ime/inner.rs | 68 ----- .../linux/x11/ime/input_method.rs | 278 ------------------ src/platform_impl/linux/x11/ime/mod.rs | 152 ---------- src/platform_impl/linux/x11/mod.rs | 43 +-- src/platform_impl/linux/x11/window.rs | 34 +-- 8 files changed, 7 insertions(+), 909 deletions(-) delete mode 100644 src/platform_impl/linux/x11/ime/callbacks.rs delete mode 100644 src/platform_impl/linux/x11/ime/context.rs delete mode 100644 src/platform_impl/linux/x11/ime/inner.rs delete mode 100644 src/platform_impl/linux/x11/ime/input_method.rs delete mode 100644 src/platform_impl/linux/x11/ime/mod.rs diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index f26126ed1e..65df373343 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -8,7 +8,7 @@ use SeatFocus::{KbFocus, PtrFocus}; use super::{ ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd, DndState, - GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId, XExtension, + GenericEventCookie, ScrollOrientation, UnownedWindow, WindowId, XExtension, }; use crate::{ @@ -47,7 +47,6 @@ enum SeatFocus { pub(super) struct EventProcessor { pub(super) dnd: Dnd, - pub(super) ime_receiver: ImeReceiver, pub(super) randr_event_offset: c_int, pub(super) devices: HashMap, pub(super) xi2ext: XExtension, @@ -525,13 +524,6 @@ impl EventProcessor { // cleanup again here. wt.windows.borrow_mut().remove(&WindowId(window)); - // Since all XIM stuff needs to happen from the same thread, we destroy the input - // context here instead of when dropping the window. - wt.ime - .borrow_mut() - .remove_context(window) - .expect("Failed to destroy input context"); - callback(Event::WindowEvent { window_id, event: WindowEvent::Destroyed, @@ -803,11 +795,6 @@ impl EventProcessor { ffi::XI_FocusIn => { let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) }; - wt.ime - .borrow_mut() - .focus(xev.event) - .expect("Failed to focus input context"); - let seat = match find_seat(&mut self.seats, xev.deviceid) { Some(seat) => seat, _ => return, @@ -819,11 +806,6 @@ impl EventProcessor { ffi::XI_FocusOut => { let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) }; - wt.ime - .borrow_mut() - .unfocus(xev.event) - .expect("Failed to unfocus input context"); - let seat = match find_seat(&mut self.seats, xev.deviceid) { Some(seat) => seat, _ => return, @@ -1147,13 +1129,6 @@ impl EventProcessor { } _ => {} } - - match self.ime_receiver.try_recv() { - Ok((window_id, x, y)) => { - wt.ime.borrow_mut().send_xim_spot(window_id, x, y); - } - Err(_) => (), - } } /// Updates the window that has this seat's keyboard/pointer focus. diff --git a/src/platform_impl/linux/x11/ime/callbacks.rs b/src/platform_impl/linux/x11/ime/callbacks.rs deleted file mode 100644 index f254a04e55..0000000000 --- a/src/platform_impl/linux/x11/ime/callbacks.rs +++ /dev/null @@ -1,175 +0,0 @@ -use std::{collections::HashMap, os::raw::c_char, ptr, sync::Arc}; - -use super::{ffi, XConnection, XError}; - -use super::{ - context::{ImeContext, ImeContextCreationError}, - inner::{close_im, ImeInner}, - input_method::PotentialInputMethods, -}; - -pub unsafe fn xim_set_callback( - xconn: &Arc, - xim: ffi::XIM, - field: *const c_char, - callback: *mut ffi::XIMCallback, -) -> Result<(), XError> { - // It's advisable to wrap variadic FFI functions in our own functions, as we want to minimize - // access that isn't type-checked. - (xconn.xlib.XSetIMValues)(xim, field, callback, ptr::null_mut::<()>()); - xconn.check_errors() -} - -// Set a callback for when an input method matching the current locale modifiers becomes -// available. Note that this has nothing to do with what input methods are open or able to be -// opened, and simply uses the modifiers that are set when the callback is set. -// * This is called per locale modifier, not per input method opened with that locale modifier. -// * Trying to set this for multiple locale modifiers causes problems, i.e. one of the rebuilt -// input contexts would always silently fail to use the input method. -pub unsafe fn set_instantiate_callback( - xconn: &Arc, - client_data: ffi::XPointer, -) -> Result<(), XError> { - (xconn.xlib.XRegisterIMInstantiateCallback)( - xconn.display, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - Some(xim_instantiate_callback), - client_data, - ); - xconn.check_errors() -} - -pub unsafe fn unset_instantiate_callback( - xconn: &Arc, - client_data: ffi::XPointer, -) -> Result<(), XError> { - (xconn.xlib.XUnregisterIMInstantiateCallback)( - xconn.display, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - Some(xim_instantiate_callback), - client_data, - ); - xconn.check_errors() -} - -pub unsafe fn set_destroy_callback( - xconn: &Arc, - im: ffi::XIM, - inner: &ImeInner, -) -> Result<(), XError> { - xim_set_callback( - &xconn, - im, - ffi::XNDestroyCallback_0.as_ptr() as *const _, - &inner.destroy_callback as *const _ as *mut _, - ) -} - -#[derive(Debug)] -enum ReplaceImError { - MethodOpenFailed(PotentialInputMethods), - ContextCreationFailed(ImeContextCreationError), - SetDestroyCallbackFailed(XError), -} - -// Attempt to replace current IM (which may or may not be presently valid) with a new one. This -// includes replacing all existing input contexts and free'ing resources as necessary. This only -// modifies existing state if all operations succeed. -unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> { - let xconn = &(*inner).xconn; - - let (new_im, is_fallback) = { - let new_im = (*inner).potential_input_methods.open_im(xconn, None); - let is_fallback = new_im.is_fallback(); - ( - new_im.ok().ok_or_else(|| { - ReplaceImError::MethodOpenFailed((*inner).potential_input_methods.clone()) - })?, - is_fallback, - ) - }; - - // It's important to always set a destroy callback, since there's otherwise potential for us - // to try to use or free a resource that's already been destroyed on the server. - { - let result = set_destroy_callback(xconn, new_im.im, &*inner); - if result.is_err() { - let _ = close_im(xconn, new_im.im); - } - result - } - .map_err(ReplaceImError::SetDestroyCallbackFailed)?; - - let mut new_contexts = HashMap::new(); - for (window, old_context) in (*inner).contexts.iter() { - let spot = old_context.as_ref().map(|old_context| old_context.ic_spot); - let new_context = { - let result = ImeContext::new(xconn, new_im.im, *window, spot); - if result.is_err() { - let _ = close_im(xconn, new_im.im); - } - result.map_err(ReplaceImError::ContextCreationFailed)? - }; - new_contexts.insert(*window, Some(new_context)); - } - - // If we've made it this far, everything succeeded. - let _ = (*inner).destroy_all_contexts_if_necessary(); - let _ = (*inner).close_im_if_necessary(); - (*inner).im = new_im.im; - (*inner).contexts = new_contexts; - (*inner).is_destroyed = false; - (*inner).is_fallback = is_fallback; - Ok(()) -} - -pub unsafe extern "C" fn xim_instantiate_callback( - _display: *mut ffi::Display, - client_data: ffi::XPointer, - // This field is unsupplied. - _call_data: ffi::XPointer, -) { - let inner: *mut ImeInner = client_data as _; - if !inner.is_null() { - let xconn = &(*inner).xconn; - let result = replace_im(inner); - if result.is_ok() { - let _ = unset_instantiate_callback(xconn, client_data); - (*inner).is_fallback = false; - } else if result.is_err() && (*inner).is_destroyed { - // We have no usable input methods! - result.expect("Failed to reopen input method"); - } - } -} - -// This callback is triggered when the input method is closed on the server end. When this -// happens, XCloseIM/XDestroyIC doesn't need to be called, as the resources have already been -// free'd (attempting to do so causes our connection to freeze). -pub unsafe extern "C" fn xim_destroy_callback( - _xim: ffi::XIM, - client_data: ffi::XPointer, - // This field is unsupplied. - _call_data: ffi::XPointer, -) { - let inner: *mut ImeInner = client_data as _; - if !inner.is_null() { - (*inner).is_destroyed = true; - let xconn = &(*inner).xconn; - if !(*inner).is_fallback { - let _ = set_instantiate_callback(xconn, client_data); - // Attempt to open fallback input method. - let result = replace_im(inner); - if result.is_ok() { - (*inner).is_fallback = true; - } else { - // We have no usable input methods! - result.expect("Failed to open fallback input method"); - } - } - } -} diff --git a/src/platform_impl/linux/x11/ime/context.rs b/src/platform_impl/linux/x11/ime/context.rs deleted file mode 100644 index 8c2ff4cf50..0000000000 --- a/src/platform_impl/linux/x11/ime/context.rs +++ /dev/null @@ -1,139 +0,0 @@ -use std::{ - os::raw::{c_short, c_void}, - ptr, - sync::Arc, -}; - -use super::{ffi, util, XConnection, XError}; - -#[derive(Debug)] -pub enum ImeContextCreationError { - XError(XError), - Null, -} - -unsafe fn create_pre_edit_attr<'a>( - xconn: &'a Arc, - ic_spot: &'a ffi::XPoint, -) -> util::XSmartPointer<'a, c_void> { - util::XSmartPointer::new( - xconn, - (xconn.xlib.XVaCreateNestedList)( - 0, - ffi::XNSpotLocation_0.as_ptr() as *const _, - ic_spot, - ptr::null_mut::<()>(), - ), - ) - .expect("XVaCreateNestedList returned NULL") -} - -// WARNING: this struct doesn't destroy its XIC resource when dropped. -// This is intentional, as it doesn't have enough information to know whether or not the context -// still exists on the server. Since `ImeInner` has that awareness, destruction must be handled -// through `ImeInner`. -#[derive(Debug)] -pub struct ImeContext { - pub ic: ffi::XIC, - pub ic_spot: ffi::XPoint, -} - -impl ImeContext { - pub unsafe fn new( - xconn: &Arc, - im: ffi::XIM, - window: ffi::Window, - ic_spot: Option, - ) -> Result { - let ic = if let Some(ic_spot) = ic_spot { - ImeContext::create_ic_with_spot(xconn, im, window, ic_spot) - } else { - ImeContext::create_ic(xconn, im, window) - }; - - let ic = ic.ok_or(ImeContextCreationError::Null)?; - xconn - .check_errors() - .map_err(ImeContextCreationError::XError)?; - - Ok(ImeContext { - ic, - ic_spot: ic_spot.unwrap_or_else(|| ffi::XPoint { x: 0, y: 0 }), - }) - } - - unsafe fn create_ic( - xconn: &Arc, - im: ffi::XIM, - window: ffi::Window, - ) -> Option { - let ic = (xconn.xlib.XCreateIC)( - im, - ffi::XNInputStyle_0.as_ptr() as *const _, - ffi::XIMPreeditNothing | ffi::XIMStatusNothing, - ffi::XNClientWindow_0.as_ptr() as *const _, - window, - ptr::null_mut::<()>(), - ); - if ic.is_null() { - None - } else { - Some(ic) - } - } - - unsafe fn create_ic_with_spot( - xconn: &Arc, - im: ffi::XIM, - window: ffi::Window, - ic_spot: ffi::XPoint, - ) -> Option { - let pre_edit_attr = create_pre_edit_attr(xconn, &ic_spot); - let ic = (xconn.xlib.XCreateIC)( - im, - ffi::XNInputStyle_0.as_ptr() as *const _, - ffi::XIMPreeditNothing | ffi::XIMStatusNothing, - ffi::XNClientWindow_0.as_ptr() as *const _, - window, - ffi::XNPreeditAttributes_0.as_ptr() as *const _, - pre_edit_attr.ptr, - ptr::null_mut::<()>(), - ); - if ic.is_null() { - None - } else { - Some(ic) - } - } - - pub fn focus(&self, xconn: &Arc) -> Result<(), XError> { - unsafe { - (xconn.xlib.XSetICFocus)(self.ic); - } - xconn.check_errors() - } - - pub fn unfocus(&self, xconn: &Arc) -> Result<(), XError> { - unsafe { - (xconn.xlib.XUnsetICFocus)(self.ic); - } - xconn.check_errors() - } - - pub fn set_spot(&mut self, xconn: &Arc, x: c_short, y: c_short) { - if self.ic_spot.x == x && self.ic_spot.y == y { - return; - } - self.ic_spot = ffi::XPoint { x, y }; - - unsafe { - let pre_edit_attr = create_pre_edit_attr(xconn, &self.ic_spot); - (xconn.xlib.XSetICValues)( - self.ic, - ffi::XNPreeditAttributes_0.as_ptr() as *const _, - pre_edit_attr.ptr, - ptr::null_mut::<()>(), - ); - } - } -} diff --git a/src/platform_impl/linux/x11/ime/inner.rs b/src/platform_impl/linux/x11/ime/inner.rs deleted file mode 100644 index 011e22aa13..0000000000 --- a/src/platform_impl/linux/x11/ime/inner.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::{collections::HashMap, mem, ptr, sync::Arc}; - -use super::{ffi, XConnection, XError}; - -use super::{context::ImeContext, input_method::PotentialInputMethods}; - -pub unsafe fn close_im(xconn: &Arc, im: ffi::XIM) -> Result<(), XError> { - (xconn.xlib.XCloseIM)(im); - xconn.check_errors() -} - -pub unsafe fn destroy_ic(xconn: &Arc, ic: ffi::XIC) -> Result<(), XError> { - (xconn.xlib.XDestroyIC)(ic); - xconn.check_errors() -} - -pub struct ImeInner { - pub xconn: Arc, - // WARNING: this is initially null! - pub im: ffi::XIM, - pub potential_input_methods: PotentialInputMethods, - pub contexts: HashMap>, - // WARNING: this is initially zeroed! - pub destroy_callback: ffi::XIMCallback, - // Indicates whether or not the the input method was destroyed on the server end - // (i.e. if ibus/fcitx/etc. was terminated/restarted) - pub is_destroyed: bool, - pub is_fallback: bool, -} - -impl ImeInner { - pub fn new(xconn: Arc, potential_input_methods: PotentialInputMethods) -> Self { - ImeInner { - xconn, - im: ptr::null_mut(), - potential_input_methods, - contexts: HashMap::new(), - destroy_callback: unsafe { mem::zeroed() }, - is_destroyed: false, - is_fallback: false, - } - } - - pub unsafe fn close_im_if_necessary(&self) -> Result { - if !self.is_destroyed { - close_im(&self.xconn, self.im).map(|_| true) - } else { - Ok(false) - } - } - - pub unsafe fn destroy_ic_if_necessary(&self, ic: ffi::XIC) -> Result { - if !self.is_destroyed { - destroy_ic(&self.xconn, ic).map(|_| true) - } else { - Ok(false) - } - } - - pub unsafe fn destroy_all_contexts_if_necessary(&self) -> Result { - for context in self.contexts.values() { - if let &Some(ref context) = context { - self.destroy_ic_if_necessary(context.ic)?; - } - } - Ok(!self.is_destroyed) - } -} diff --git a/src/platform_impl/linux/x11/ime/input_method.rs b/src/platform_impl/linux/x11/ime/input_method.rs deleted file mode 100644 index 142c150199..0000000000 --- a/src/platform_impl/linux/x11/ime/input_method.rs +++ /dev/null @@ -1,278 +0,0 @@ -use std::{ - env, - ffi::{CStr, CString, IntoStringError}, - fmt, - os::raw::c_char, - ptr, - sync::Arc, -}; - -use parking_lot::Mutex; - -use super::{ffi, util, XConnection, XError}; - -lazy_static! { - static ref GLOBAL_LOCK: Mutex<()> = Default::default(); -} - -unsafe fn open_im(xconn: &Arc, locale_modifiers: &CStr) -> Option { - let _lock = GLOBAL_LOCK.lock(); - - // XSetLocaleModifiers returns... - // * The current locale modifiers if it's given a NULL pointer. - // * The new locale modifiers if we succeeded in setting them. - // * NULL if the locale modifiers string is malformed or if the - // current locale is not supported by Xlib. - (xconn.xlib.XSetLocaleModifiers)(locale_modifiers.as_ptr()); - - let im = (xconn.xlib.XOpenIM)( - xconn.display, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ); - - if im.is_null() { - None - } else { - Some(im) - } -} - -#[derive(Debug)] -pub struct InputMethod { - pub im: ffi::XIM, - name: String, -} - -impl InputMethod { - fn new(im: ffi::XIM, name: String) -> Self { - InputMethod { im, name } - } -} - -#[derive(Debug)] -pub enum InputMethodResult { - /// Input method used locale modifier from `XMODIFIERS` environment variable. - XModifiers(InputMethod), - /// Input method used internal fallback locale modifier. - Fallback(InputMethod), - /// Input method could not be opened using any locale modifier tried. - Failure, -} - -impl InputMethodResult { - pub fn is_fallback(&self) -> bool { - if let &InputMethodResult::Fallback(_) = self { - true - } else { - false - } - } - - pub fn ok(self) -> Option { - use self::InputMethodResult::*; - match self { - XModifiers(im) | Fallback(im) => Some(im), - Failure => None, - } - } -} - -#[derive(Debug, Clone)] -enum GetXimServersError { - XError(XError), - GetPropertyError(util::GetPropertyError), - InvalidUtf8(IntoStringError), -} - -// The root window has a property named XIM_SERVERS, which contains a list of atoms represeting -// the availabile XIM servers. For instance, if you're using ibus, it would contain an atom named -// "@server=ibus". It's possible for this property to contain multiple atoms, though presumably -// rare. Note that we replace "@server=" with "@im=" in order to match the format of locale -// modifiers, since we don't want a user who's looking at logs to ask "am I supposed to set -// XMODIFIERS to `@server=ibus`?!?" -unsafe fn get_xim_servers(xconn: &Arc) -> Result, GetXimServersError> { - let servers_atom = xconn.get_atom_unchecked(b"XIM_SERVERS\0"); - - let root = (xconn.xlib.XDefaultRootWindow)(xconn.display); - - let mut atoms: Vec = xconn - .get_property(root, servers_atom, ffi::XA_ATOM) - .map_err(GetXimServersError::GetPropertyError)?; - - let mut names: Vec<*const c_char> = Vec::with_capacity(atoms.len()); - (xconn.xlib.XGetAtomNames)( - xconn.display, - atoms.as_mut_ptr(), - atoms.len() as _, - names.as_mut_ptr() as _, - ); - names.set_len(atoms.len()); - - let mut formatted_names = Vec::with_capacity(names.len()); - for name in names { - let string = CStr::from_ptr(name) - .to_owned() - .into_string() - .map_err(GetXimServersError::InvalidUtf8)?; - (xconn.xlib.XFree)(name as _); - formatted_names.push(string.replace("@server=", "@im=")); - } - xconn.check_errors().map_err(GetXimServersError::XError)?; - Ok(formatted_names) -} - -#[derive(Clone)] -struct InputMethodName { - c_string: CString, - string: String, -} - -impl InputMethodName { - pub fn from_string(string: String) -> Self { - let c_string = CString::new(string.clone()) - .expect("String used to construct CString contained null byte"); - InputMethodName { c_string, string } - } - - pub fn from_str(string: &str) -> Self { - let c_string = - CString::new(string).expect("String used to construct CString contained null byte"); - InputMethodName { - c_string, - string: string.to_owned(), - } - } -} - -impl fmt::Debug for InputMethodName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.string.fmt(f) - } -} - -#[derive(Debug, Clone)] -struct PotentialInputMethod { - name: InputMethodName, - successful: Option, -} - -impl PotentialInputMethod { - pub fn from_string(string: String) -> Self { - PotentialInputMethod { - name: InputMethodName::from_string(string), - successful: None, - } - } - - pub fn from_str(string: &str) -> Self { - PotentialInputMethod { - name: InputMethodName::from_str(string), - successful: None, - } - } - - pub fn reset(&mut self) { - self.successful = None; - } - - pub fn open_im(&mut self, xconn: &Arc) -> Option { - let im = unsafe { open_im(xconn, &self.name.c_string) }; - self.successful = Some(im.is_some()); - im.map(|im| InputMethod::new(im, self.name.string.clone())) - } -} - -// By logging this struct, you get a sequential listing of every locale modifier tried, where it -// came from, and if it succceeded. -#[derive(Debug, Clone)] -pub struct PotentialInputMethods { - // On correctly configured systems, the XMODIFIERS environemnt variable tells us everything we - // need to know. - xmodifiers: Option, - // We have some standard options at our disposal that should ostensibly always work. For users - // who only need compose sequences, this ensures that the program launches without a hitch - // For users who need more sophisticated IME features, this is more or less a silent failure. - // Logging features should be added in the future to allow both audiences to be effectively - // served. - fallbacks: [PotentialInputMethod; 2], - // For diagnostic purposes, we include the list of XIM servers that the server reports as - // being available. - _xim_servers: Result, GetXimServersError>, -} - -impl PotentialInputMethods { - pub fn new(xconn: &Arc) -> Self { - let xmodifiers = env::var("XMODIFIERS") - .ok() - .map(PotentialInputMethod::from_string); - PotentialInputMethods { - // Since passing "" to XSetLocaleModifiers results in it defaulting to the value of - // XMODIFIERS, it's worth noting what happens if XMODIFIERS is also "". If simply - // running the program with `XMODIFIERS="" cargo run`, then assuming XMODIFIERS is - // defined in the profile (or parent environment) then that parent XMODIFIERS is used. - // If that XMODIFIERS value is also "" (i.e. if you ran `export XMODIFIERS=""`), then - // XSetLocaleModifiers uses the default local input method. Note that defining - // XMODIFIERS as "" is different from XMODIFIERS not being defined at all, since in - // that case, we get `None` and end up skipping ahead to the next method. - xmodifiers, - fallbacks: [ - // This is a standard input method that supports compose equences, which should - // always be available. `@im=none` appears to mean the same thing. - PotentialInputMethod::from_str("@im=local"), - // This explicitly specifies to use the implementation-dependent default, though - // that seems to be equivalent to just using the local input method. - PotentialInputMethod::from_str("@im="), - ], - // The XIM_SERVERS property can have surprising values. For instance, when I exited - // ibus to run fcitx, it retained the value denoting ibus. Even more surprising is - // that the fcitx input method could only be successfully opened using "@im=ibus". - // Presumably due to this quirk, it's actually possible to alternate between ibus and - // fcitx in a running application. - _xim_servers: unsafe { get_xim_servers(xconn) }, - } - } - - // This resets the `successful` field of every potential input method, ensuring we have - // accurate information when this struct is re-used by the destruction/instantiation callbacks. - fn reset(&mut self) { - if let Some(ref mut input_method) = self.xmodifiers { - input_method.reset(); - } - - for input_method in &mut self.fallbacks { - input_method.reset(); - } - } - - pub fn open_im( - &mut self, - xconn: &Arc, - callback: Option<&dyn Fn() -> ()>, - ) -> InputMethodResult { - use self::InputMethodResult::*; - - self.reset(); - - if let Some(ref mut input_method) = self.xmodifiers { - let im = input_method.open_im(xconn); - if let Some(im) = im { - return XModifiers(im); - } else { - if let Some(ref callback) = callback { - callback(); - } - } - } - - for input_method in &mut self.fallbacks { - let im = input_method.open_im(xconn); - if let Some(im) = im { - return Fallback(im); - } - } - - Failure - } -} diff --git a/src/platform_impl/linux/x11/ime/mod.rs b/src/platform_impl/linux/x11/ime/mod.rs deleted file mode 100644 index 4e2db1ef5e..0000000000 --- a/src/platform_impl/linux/x11/ime/mod.rs +++ /dev/null @@ -1,152 +0,0 @@ -// Important: all XIM calls need to happen from the same thread! - -mod callbacks; -mod context; -mod inner; -mod input_method; - -use std::sync::{ - mpsc::{Receiver, Sender}, - Arc, -}; - -use super::{ffi, util, XConnection, XError}; - -pub use self::context::ImeContextCreationError; -use self::{ - callbacks::*, - context::ImeContext, - inner::{close_im, ImeInner}, - input_method::PotentialInputMethods, -}; - -pub type ImeReceiver = Receiver<(ffi::Window, i16, i16)>; -pub type ImeSender = Sender<(ffi::Window, i16, i16)>; - -#[derive(Debug)] -pub enum ImeCreationError { - OpenFailure(PotentialInputMethods), - SetDestroyCallbackFailed(XError), -} - -pub struct Ime { - xconn: Arc, - // The actual meat of this struct is boxed away, since it needs to have a fixed location in - // memory so we can pass a pointer to it around. - inner: Box, -} - -impl Ime { - pub fn new(xconn: Arc) -> Result { - let potential_input_methods = PotentialInputMethods::new(&xconn); - - let (mut inner, client_data) = { - let mut inner = Box::new(ImeInner::new(xconn, potential_input_methods)); - let inner_ptr = Box::into_raw(inner); - let client_data = inner_ptr as _; - let destroy_callback = ffi::XIMCallback { - client_data, - callback: Some(xim_destroy_callback), - }; - inner = unsafe { Box::from_raw(inner_ptr) }; - inner.destroy_callback = destroy_callback; - (inner, client_data) - }; - - let xconn = Arc::clone(&inner.xconn); - - let input_method = inner.potential_input_methods.open_im( - &xconn, - Some(&|| { - let _ = unsafe { set_instantiate_callback(&xconn, client_data) }; - }), - ); - - let is_fallback = input_method.is_fallback(); - if let Some(input_method) = input_method.ok() { - inner.im = input_method.im; - inner.is_fallback = is_fallback; - unsafe { - let result = set_destroy_callback(&xconn, input_method.im, &*inner) - .map_err(ImeCreationError::SetDestroyCallbackFailed); - if result.is_err() { - let _ = close_im(&xconn, input_method.im); - } - result?; - } - Ok(Ime { xconn, inner }) - } else { - Err(ImeCreationError::OpenFailure(inner.potential_input_methods)) - } - } - - pub fn is_destroyed(&self) -> bool { - self.inner.is_destroyed - } - - // This pattern is used for various methods here: - // Ok(_) indicates that nothing went wrong internally - // Ok(true) indicates that the action was actually performed - // Ok(false) indicates that the action is not presently applicable - pub fn create_context(&mut self, window: ffi::Window) -> Result { - let context = if self.is_destroyed() { - // Create empty entry in map, so that when IME is rebuilt, this window has a context. - None - } else { - Some(unsafe { ImeContext::new(&self.inner.xconn, self.inner.im, window, None) }?) - }; - self.inner.contexts.insert(window, context); - Ok(!self.is_destroyed()) - } - - pub fn remove_context(&mut self, window: ffi::Window) -> Result { - if let Some(Some(context)) = self.inner.contexts.remove(&window) { - unsafe { - self.inner.destroy_ic_if_necessary(context.ic)?; - } - Ok(true) - } else { - Ok(false) - } - } - - pub fn focus(&mut self, window: ffi::Window) -> Result { - if self.is_destroyed() { - return Ok(false); - } - if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) { - context.focus(&self.xconn).map(|_| true) - } else { - Ok(false) - } - } - - pub fn unfocus(&mut self, window: ffi::Window) -> Result { - if self.is_destroyed() { - return Ok(false); - } - if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) { - context.unfocus(&self.xconn).map(|_| true) - } else { - Ok(false) - } - } - - pub fn send_xim_spot(&mut self, window: ffi::Window, x: i16, y: i16) { - if self.is_destroyed() { - return; - } - if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) { - context.set_spot(&self.xconn, x as _, y as _); - } - } -} - -impl Drop for Ime { - fn drop(&mut self) { - unsafe { - let _ = self.inner.destroy_all_contexts_if_necessary(); - let _ = self.inner.close_im_if_necessary(); - } - } -} diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 798000bb93..16e461c782 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -9,7 +9,6 @@ mod dnd; mod event_processor; pub mod ffi; -mod ime; mod monitor; pub mod util; mod window; @@ -33,11 +32,11 @@ use std::{ rc::Rc, slice, sync::mpsc::Receiver, - sync::{mpsc, Arc, Weak}, + sync::{Arc, Weak}, time::{Duration, Instant}, }; -use libc::{self, setlocale, LC_CTYPE}; +use libc::{self}; use mio::{unix::SourceFd, Events, Interest, Poll, Token, Waker}; @@ -50,7 +49,6 @@ use mio_misc::{ use self::{ dnd::{Dnd, DndState}, event_processor::EventProcessor, - ime::{Ime, ImeCreationError, ImeReceiver, ImeSender}, }; use crate::{ error::OsError as RootOsError, @@ -72,9 +70,7 @@ pub struct EventLoopWindowTarget { xconn: Arc, wm_delete_window: ffi::Atom, net_wm_ping: ffi::Atom, - ime_sender: ImeSender, root: ffi::Window, - ime: RefCell, windows: RefCell>>, redraw_sender: Sender, reset_dead_keys: Arc, @@ -113,38 +109,6 @@ impl EventLoop { let dnd = Dnd::new(Arc::clone(&xconn)) .expect("Failed to call XInternAtoms when initializing drag and drop"); - let (ime_sender, ime_receiver) = mpsc::channel(); - // Input methods will open successfully without setting the locale, but it won't be - // possible to actually commit pre-edit sequences. - unsafe { - // Remember default locale to restore it if target locale is unsupported - // by Xlib - let default_locale = setlocale(LC_CTYPE, ptr::null()); - setlocale(LC_CTYPE, b"\0".as_ptr() as *const _); - - // Check if set locale is supported by Xlib. - // If not, calls to some Xlib functions like `XSetLocaleModifiers` - // will fail. - let locale_supported = (xconn.xlib.XSupportsLocale)() == 1; - if !locale_supported { - let unsupported_locale = setlocale(LC_CTYPE, ptr::null()); - warn!( - "Unsupported locale \"{}\". Restoring default locale \"{}\".", - CStr::from_ptr(unsupported_locale).to_string_lossy(), - CStr::from_ptr(default_locale).to_string_lossy() - ); - // Restore default locale - setlocale(LC_CTYPE, default_locale); - } - } - let ime = RefCell::new({ - let result = Ime::new(Arc::clone(&xconn)); - if let Err(ImeCreationError::OpenFailure(ref state)) = result { - panic!("Failed to open input method: {:#?}", state); - } - result.expect("Failed to set input method destruction callback") - }); - let randr_event_offset = xconn .select_xrandr_input(root) .expect("Failed to query XRandR extension"); @@ -219,11 +183,9 @@ impl EventLoop { let target = Rc::new(RootELW { p: super::EventLoopWindowTarget::X(EventLoopWindowTarget { - ime, root, windows: Default::default(), _marker: ::std::marker::PhantomData, - ime_sender, xconn, wm_delete_window, net_wm_ping, @@ -238,7 +200,6 @@ impl EventLoop { dnd, devices: Default::default(), randr_event_offset, - ime_receiver, xi2ext, xkb_base_event: xkb_base_event as c_int, num_touch: 0, diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 765d284d5c..bdde554b59 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -20,14 +20,13 @@ use crate::{ error::{ExternalError, NotSupportedError, OsError as RootOsError}, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, platform_impl::{ - x11::{ime::ImeContextCreationError, MonitorHandle as X11MonitorHandle}, - MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes, - VideoMode as PlatformVideoMode, + x11::MonitorHandle as X11MonitorHandle, MonitorHandle as PlatformMonitorHandle, OsError, + PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode, }, window::{CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes}, }; -use super::{ffi, util, EventLoopWindowTarget, ImeSender, WindowId, XConnection, XError}; +use super::{ffi, util, EventLoopWindowTarget, WindowId, XConnection, XError}; #[derive(Debug)] pub struct SharedState { @@ -103,7 +102,6 @@ pub struct UnownedWindow { cursor: Mutex, cursor_grabbed: Mutex, cursor_visible: Mutex, - ime_sender: Mutex, pub shared_state: Mutex, redraw_sender: Sender, reset_dead_keys: Arc, @@ -252,7 +250,6 @@ impl UnownedWindow { cursor: Default::default(), cursor_grabbed: Mutex::new(false), cursor_visible: Mutex::new(true), - ime_sender: Mutex::new(event_loop.ime_sender.clone()), shared_state: SharedState::new(guessed_monitor, window_attrs.visible), redraw_sender: event_loop.redraw_sender.clone(), reset_dead_keys: event_loop.reset_dead_keys.clone(), @@ -426,19 +423,6 @@ impl UnownedWindow { .select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask) .queue(); - { - let result = event_loop.ime.borrow_mut().create_context(window.xwindow); - if let Err(err) = result { - let e = match err { - ImeContextCreationError::XError(err) => OsError::XError(err), - ImeContextCreationError::Null => { - OsError::XMisc("IME Context creation failed") - } - }; - return Err(os_error!(e)); - } - } - // These properties must be set after mapping if window_attrs.maximized { window.set_maximized_inner(window_attrs.maximized).queue(); @@ -1330,18 +1314,8 @@ impl UnownedWindow { .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err)))) } - pub(crate) fn set_ime_position_physical(&self, x: i32, y: i32) { - let _ = self - .ime_sender - .lock() - .send((self.xwindow, x as i16, y as i16)); - } - #[inline] - pub fn set_ime_position(&self, spot: Position) { - let (x, y) = spot.to_physical::(self.scale_factor()).into(); - self.set_ime_position_physical(x, y); - } + pub fn set_ime_position(&self, _spot: Position) {} #[inline] pub fn reset_dead_keys(&self) { From 21cc33ceca5858f6e4ece68cb11a6ed2890fe517 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 18 Nov 2021 22:16:17 +0100 Subject: [PATCH 127/131] X11: Port the backend from Xlib to XCB # Background Clients communicate with the X server by sending and receiving size-prefixed binary messages. The format of these messages is described in the form of XML files at [freedesktop]. All of these messages can be described in the form of `repr(C)` structs with potentially trailing variably-sized data. [freedesktop]: https://gitlab.freedesktop.org/xorg/proto/xorgproto XCB is a C library that provides a way to send and receive these messages. It consists of - A small number of hand-written functions that implement a send and receive queue and a way to pair requests and replies via sequence numbers. - A large number of functions that are auto generated from the aforementioned XML files. These functions perform serialization and deserialization of requests before passing them on. Each message passed from XCB to the application consists of a single allocated buffer that can later be freed with `free`. XCB has many desirable properties: - The API is uniform. With the exception of the hand-written functions, the behavior of a function can be determined purely by looking at its name and signature. - Since received messages consist of a single allocation, memory management is easy and can be automated. - Since strings in the X protocol are not null terminated, it is not necessary to convert rust strings to C strings. - Functions that generate a reply are able the return an error. That is, they act as if they returned `Result` and it is easy to write rust wrappers with this return value. - It is completely thread safe. Xlib is a C library that nowadays uses XCB as a transport layer. On top of this it implements a C API with the following properties: - Strings are 0 terminated. - Returned values can contain nested allocations and must be freed with appropriate functions. - It uses global state. - It is not thread-safe by default. - Errors are handled via a callback function and cannot easily be matched with the functions that generated them. It gets worse when the application uses Xlib via multiple threads or with multiple connections. - By default all errors cause the application to terminate. - IO errors always cause the application to terminate. (In 2019 an API was introduced to modify this behavior.) - It provides some utility functions that make certain tasks easier. This includes an XIM client. - While each X protocol message is composed of 8, 16, and 32 bit units, Xlib exposes these as `char`, `short`, and `long`. It internally converts between 32 bit and `long` units. # Backwards Compatibility Some applications require winit to provide an Xlib connection to them. Notably, the GLX API is specified to work on top of Xlib and cannot be used without an Xlib connection. This patch allows applications to opt into using an Xlib connection by enabling the `xlib` feature. In this case they can retrieve the Xlib connection by calling the `xlib_display` function. Even if this feature is enabled, applications and users are able to opt out of using an Xlib connection by setting the `WINIT_DISABLE_XLIB` environment variable. This can be useful if a dependency enables the `xlib` feature but it is not necessary for the functionality of the application. # Breaking Changes - The `x11` module is no longer exported. This module was previously public but `doc(hidden)`. - The `xlib_xconnection` function has been removed. This function was previously `doc(hidden)`. - The `xlib_window` function has been renamed to `x11_window` and now returns `u32` instead of `c_long`. - The `xlib_screen_id` function has been renamed to `x11_screen_id` and now returns `u32` instead of `c_long`. - `WindowBuilder` now accepts a `u32` screen id instead of `c_long`. - `WindowBuilder` previously accepted an untyped pointer specifying an Xlib visual and unsoundly converted it internally to an Xlib visual pointer. It now accepts a new type that allows the user to specify the desired visual id and depth. - The backend now has a hard dependency on libxcb and associated libraries. libxcb is always available as it is a dependency of libx11 but some distributions (notably Debian) split associated libraries such as libxcb-xinput into separate packages that the user might have to install. # New and Changed Features - If the `xlib` feature is enabled, the new `xlib_display` function returns the Xlib connection unless this connection was disabled via the `WINIT_DISABLE_XLIB` environment variable. - The new `xcb_connection` function returns the XCB connection. - The backend now supports multiple screens throughout. It was already possible for the user to specify a screen to use via `WindowBuilder` but in many places the backend always used the default screen. - Many places that previously panicked now simply log an error. - Error messages are much improved. --- Cargo.toml | 6 +- src/platform/unix.rs | 140 +-- src/platform_impl/linux/common/xkb_state.rs | 16 +- src/platform_impl/linux/mod.rs | 71 +- src/platform_impl/linux/x11/dnd.rs | 159 ++- .../linux/x11/event_processor.rs | 608 ++++++----- src/platform_impl/linux/x11/ffi.rs | 4 - src/platform_impl/linux/x11/mod.rs | 397 ++++--- src/platform_impl/linux/x11/monitor.rs | 208 ++-- src/platform_impl/linux/x11/util/atom.rs | 72 +- .../linux/x11/util/client_msg.rs | 92 +- src/platform_impl/linux/x11/util/cursor.rs | 176 ++-- src/platform_impl/linux/x11/util/format.rs | 55 - src/platform_impl/linux/x11/util/geometry.rs | 180 ++-- src/platform_impl/linux/x11/util/hint.rs | 265 ++--- src/platform_impl/linux/x11/util/icon.rs | 2 +- src/platform_impl/linux/x11/util/input.rs | 179 ++-- src/platform_impl/linux/x11/util/memory.rs | 59 -- src/platform_impl/linux/x11/util/mod.rs | 88 +- src/platform_impl/linux/x11/util/queue.rs | 42 + src/platform_impl/linux/x11/util/randr.rs | 197 ++-- .../linux/x11/util/window_property.rs | 165 +-- src/platform_impl/linux/x11/util/wm.rs | 59 +- src/platform_impl/linux/x11/window.rs | 993 ++++++++++-------- src/platform_impl/linux/x11/xdisplay.rs | 426 ++++++-- src/platform_impl/linux/x11/xlib.rs | 147 +++ 26 files changed, 2472 insertions(+), 2334 deletions(-) delete mode 100644 src/platform_impl/linux/x11/ffi.rs delete mode 100644 src/platform_impl/linux/x11/util/format.rs delete mode 100644 src/platform_impl/linux/x11/util/memory.rs create mode 100644 src/platform_impl/linux/x11/util/queue.rs create mode 100644 src/platform_impl/linux/x11/xlib.rs diff --git a/Cargo.toml b/Cargo.toml index 5264fea76b..b025833557 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,8 @@ targets = ["i686-pc-windows-msvc", "x86_64-pc-windows-msvc", "i686-unknown-linux default = ["x11", "wayland"] web-sys = ["web_sys", "wasm-bindgen", "instant/wasm-bindgen"] stdweb = ["std_web", "instant/stdweb"] -x11 = ["x11-dl", "mio", "mio-misc", "percent-encoding", "parking_lot"] +x11 = ["mio", "mio-misc", "percent-encoding", "parking_lot", "xcb-dl", "xcb-dl-util", "thiserror"] +xlib = ["x11", "x11-dl"] wayland = ["wayland-client", "sctk", "memmap2"] [dependencies] @@ -93,7 +94,10 @@ wayland-client = { version = "0.28", features = [ "dlopen"] , optional = true } sctk = { package = "smithay-client-toolkit", version = "0.12.3", optional = true } mio = { version = "0.7", features = ["os-ext"], optional = true } mio-misc = { version = "1.0", optional = true } +xcb-dl = { version = "0.2.0", optional = true } x11-dl = { version = "2.19.1", optional = true } +xcb-dl-util = { version = "0.2.0", features = ["xcb_xfixes", "xcb_xinput", "xcb_xkb", "xcb_render", "xcb_randr"], optional = true } +thiserror = { version = "1.0.30", optional = true } percent-encoding = { version = "2.0", optional = true } parking_lot = { version = "0.11.0", optional = true } memmap2 = { version = "0.2.1", optional = true } diff --git a/src/platform/unix.rs b/src/platform/unix.rs index 0140e1b726..4e3df19cbd 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -7,8 +7,6 @@ ))] use std::os::raw; -#[cfg(feature = "x11")] -use std::{ptr, sync::Arc}; use crate::{ event::KeyEvent, @@ -22,17 +20,11 @@ use crate::{ #[cfg(feature = "x11")] use crate::dpi::Size; -#[cfg(feature = "x11")] -use crate::platform_impl::x11::{ffi::XVisualInfo, XConnection}; use crate::platform_impl::{ EventLoop as LinuxEventLoop, EventLoopWindowTarget as LinuxEventLoopWindowTarget, Window as LinuxWindow, }; -// TODO: stupid hack so that glutin can do its work -#[doc(hidden)] -#[cfg(feature = "x11")] -pub use crate::platform_impl::x11; #[cfg(feature = "x11")] pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupported}; @@ -46,9 +38,22 @@ pub trait EventLoopWindowTargetExtUnix { #[cfg(feature = "x11")] fn is_x11(&self) -> bool; - #[doc(hidden)] + /// This function returns the underlying xlib `Display`. + /// + /// Returns `None` if the event loop doesn't use X11 or if xlib support was disabled by + /// setting the `WINIT_DISABLE_XLIB` environment variable. + /// + /// The pointer will become invalid when the `EventLoop` is destroyed. + #[cfg(feature = "xlib")] + fn xlib_display(&self) -> Option<*mut raw::c_void>; + + /// This function returns the underlying `xcb_connection_t`. + /// + /// Returns `None` if the event loop doesn't use X11 (if it uses wayland for example). + /// + /// The pointer will become invalid when the `EventLoop` is destroyed. #[cfg(feature = "x11")] - fn xlib_xconnection(&self) -> Option>; + fn xcb_connection(&self) -> Option<*mut raw::c_void>; /// Returns a pointer to the `wl_display` object of wayland that is used by this /// `EventLoopWindowTarget`. @@ -74,14 +79,24 @@ impl EventLoopWindowTargetExtUnix for EventLoopWindowTarget { } #[inline] - #[doc(hidden)] + #[cfg(feature = "xlib")] + fn xlib_display(&self) -> Option<*mut raw::c_void> { + if let LinuxEventLoopWindowTarget::X(e) = &self.p { + if let Some(xlib) = &e.x_connection().xlib { + return Some(xlib.dpy as _); + } + } + None + } + + #[inline] #[cfg(feature = "x11")] - fn xlib_xconnection(&self) -> Option> { - match self.p { - LinuxEventLoopWindowTarget::X(ref e) => Some(e.x_connection().clone()), - #[cfg(feature = "wayland")] - _ => None, + fn xcb_connection(&self) -> Option<*mut raw::c_void> { + #[allow(irrefutable_let_patterns)] + if let LinuxEventLoopWindowTarget::X(e) = &self.p { + return Some(e.x_connection().c as _); } + None } #[inline] @@ -196,32 +211,32 @@ impl EventLoopExtUnix for EventLoop { /// Additional methods on `Window` that are specific to Unix. pub trait WindowExtUnix { - /// Returns the ID of the `Window` xlib object that is used by this window. + /// Returns the ID of the X11 window. /// - /// Returns `None` if the window doesn't use xlib (if it uses wayland for example). + /// Returns `None` if the window doesn't use X11 (if it uses wayland for example). #[cfg(feature = "x11")] - fn xlib_window(&self) -> Option; + fn x11_window(&self) -> Option; - /// Returns a pointer to the `Display` object of xlib that is used by this window. + /// Returns the ID of the X11 screen. /// - /// Returns `None` if the window doesn't use xlib (if it uses wayland for example). - /// - /// The pointer will become invalid when the glutin `Window` is destroyed. - #[cfg(feature = "x11")] - fn xlib_display(&self) -> Option<*mut raw::c_void>; - + /// Returns `None` if the window doesn't use X11 (if it uses wayland for example). #[cfg(feature = "x11")] - fn xlib_screen_id(&self) -> Option; + fn x11_screen_id(&self) -> Option; - #[doc(hidden)] - #[cfg(feature = "x11")] - fn xlib_xconnection(&self) -> Option>; + /// This function returns the underlying xlib `Display`. + /// + /// Returns `None` if the event loop doesn't use X11 or if xlib support was disabled by + /// setting the `WINIT_DISABLE_XLIB` environment variable. + /// + /// The pointer will become invalid when the `EventLoop` is destroyed. + #[cfg(feature = "xlib")] + fn xlib_display(&self) -> Option<*mut raw::c_void>; - /// This function returns the underlying `xcb_connection_t` of an xlib `Display`. + /// This function returns the underlying `xcb_connection_t`. /// - /// Returns `None` if the window doesn't use xlib (if it uses wayland for example). + /// Returns `None` if the event loop doesn't use X11 (if it uses wayland for example). /// - /// The pointer will become invalid when the glutin `Window` is destroyed. + /// The pointer will become invalid when the `Window` is destroyed. #[cfg(feature = "x11")] fn xcb_connection(&self) -> Option<*mut raw::c_void>; @@ -258,50 +273,40 @@ pub trait WindowExtUnix { impl WindowExtUnix for Window { #[inline] #[cfg(feature = "x11")] - fn xlib_window(&self) -> Option { - match self.window { - LinuxWindow::X(ref w) => Some(w.xlib_window()), - #[cfg(feature = "wayland")] - _ => None, + fn x11_window(&self) -> Option { + #[allow(irrefutable_let_patterns)] + if let LinuxWindow::X(w) = &self.window { + return Some(w.xwindow); } + None } #[inline] #[cfg(feature = "x11")] - fn xlib_display(&self) -> Option<*mut raw::c_void> { + fn x11_screen_id(&self) -> Option { match self.window { - LinuxWindow::X(ref w) => Some(w.xlib_display()), + LinuxWindow::X(ref w) => Some(w.screen.screen_id as _), #[cfg(feature = "wayland")] _ => None, } } #[inline] - #[cfg(feature = "x11")] - fn xlib_screen_id(&self) -> Option { - match self.window { - LinuxWindow::X(ref w) => Some(w.xlib_screen_id()), - #[cfg(feature = "wayland")] - _ => None, - } - } - - #[inline] - #[doc(hidden)] - #[cfg(feature = "x11")] - fn xlib_xconnection(&self) -> Option> { - match self.window { - LinuxWindow::X(ref w) => Some(w.xlib_xconnection()), - #[cfg(feature = "wayland")] - _ => None, + #[cfg(feature = "xlib")] + fn xlib_display(&self) -> Option<*mut raw::c_void> { + if let LinuxWindow::X(e) = &self.window { + if let Some(xlib) = &e.xconn.xlib { + return Some(xlib.dpy as _); + } } + None } #[inline] #[cfg(feature = "x11")] fn xcb_connection(&self) -> Option<*mut raw::c_void> { match self.window { - LinuxWindow::X(ref w) => Some(w.xcb_connection()), + LinuxWindow::X(ref w) => Some(w.xconn.c as _), #[cfg(feature = "wayland")] _ => None, } @@ -346,9 +351,9 @@ impl WindowExtUnix for Window { /// Additional methods on `WindowBuilder` that are specific to Unix. pub trait WindowBuilderExtUnix { #[cfg(feature = "x11")] - fn with_x11_visual(self, visual_infos: *const T) -> Self; + fn with_x11_visual(self, visual_infos: XVisualInfos) -> Self; #[cfg(feature = "x11")] - fn with_x11_screen(self, screen_id: i32) -> Self; + fn with_x11_screen(self, screen_id: u32) -> Self; /// Build window with `WM_CLASS` hint; defaults to the name of the binary. Only relevant on X11. #[cfg(feature = "x11")] @@ -378,20 +383,23 @@ pub trait WindowBuilderExtUnix { fn with_app_id(self, app_id: String) -> Self; } +#[derive(Copy, Clone, Debug, Default)] +pub struct XVisualInfos { + pub visual_id: Option, + pub depth: Option, +} + impl WindowBuilderExtUnix for WindowBuilder { #[inline] #[cfg(feature = "x11")] - fn with_x11_visual(mut self, visual_infos: *const T) -> Self { - { - self.platform_specific.visual_infos = - Some(unsafe { ptr::read(visual_infos as *const XVisualInfo) }); - } + fn with_x11_visual(mut self, visual_infos: XVisualInfos) -> Self { + self.platform_specific.visual_infos = visual_infos; self } #[inline] #[cfg(feature = "x11")] - fn with_x11_screen(mut self, screen_id: i32) -> Self { + fn with_x11_screen(mut self, screen_id: u32) -> Self { self.platform_specific.screen_id = Some(screen_id); self } diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs index 0fdad4f5d5..3660d1a599 100644 --- a/src/platform_impl/linux/common/xkb_state.rs +++ b/src/platform_impl/linux/common/xkb_state.rs @@ -9,7 +9,7 @@ use memmap2::MmapOptions; pub use sctk::seat::keyboard::RMLVO; #[cfg(feature = "x11")] -use x11_dl::xlib_xcb::xcb_connection_t; +use xcb_dl::ffi::xcb_connection_t; #[cfg(feature = "x11")] use xkbcommon_dl::XKBCOMMON_X11_HANDLE as XKBXH; @@ -248,7 +248,7 @@ impl KbState { #[cfg(feature = "x11")] pub(crate) fn from_x11_xkb( connection: *mut xcb_connection_t, - device_id: std::os::raw::c_int, + device_id: xcb_dl::ffi::xcb_input_device_id_t, ) -> Result { let mut me = Self::new()?; me.xcb_connection = connection; @@ -340,20 +340,24 @@ impl KbState { } #[cfg(feature = "x11")] - pub(crate) unsafe fn init_with_x11_keymap(&mut self, device_id: std::os::raw::c_int) { + pub(crate) unsafe fn init_with_x11_keymap( + &mut self, + device_id: xcb_dl::ffi::xcb_input_device_id_t, + ) { if !self.xkb_keymap.is_null() { self.de_init(); } let keymap = (XKBXH.xkb_x11_keymap_new_from_device)( self.xkb_context, - self.xcb_connection, - device_id, + self.xcb_connection as _, + device_id as _, xkbcommon_dl::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS, ); assert_ne!(keymap, ptr::null_mut()); - let state = (XKBXH.xkb_x11_state_new_from_device)(keymap, self.xcb_connection, device_id); + let state = + (XKBXH.xkb_x11_state_new_from_device)(keymap, self.xcb_connection as _, device_id as _); self.post_init(state, keymap); } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 8e7b3d92d0..fb7162fb7d 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -11,18 +11,16 @@ compile_error!("Please select a feature to build for unix: `x11`, `wayland`"); #[cfg(feature = "wayland")] use std::error::Error; -use std::{collections::VecDeque, env, fmt}; #[cfg(feature = "x11")] -use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Arc}; +use std::sync::Arc; +use std::{collections::VecDeque, env, fmt}; -#[cfg(feature = "x11")] -use parking_lot::Mutex; use raw_window_handle::RawWindowHandle; #[cfg(feature = "x11")] pub use self::x11::XNotSupported; #[cfg(feature = "x11")] -use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection, XError}; +use self::x11::{util::WindowType as XWindowType, XConnection, XError}; use crate::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, @@ -62,9 +60,9 @@ const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND"; #[derive(Clone)] pub struct PlatformSpecificWindowBuilderAttributes { #[cfg(feature = "x11")] - pub visual_infos: Option, + pub visual_infos: crate::platform::unix::XVisualInfos, #[cfg(feature = "x11")] - pub screen_id: Option, + pub screen_id: Option, #[cfg(feature = "x11")] pub resize_increments: Option, #[cfg(feature = "x11")] @@ -85,7 +83,7 @@ impl Default for PlatformSpecificWindowBuilderAttributes { fn default() -> Self { Self { #[cfg(feature = "x11")] - visual_infos: None, + visual_infos: Default::default(), #[cfg(feature = "x11")] screen_id: None, #[cfg(feature = "x11")] @@ -106,12 +104,6 @@ impl Default for PlatformSpecificWindowBuilderAttributes { } } -#[cfg(feature = "x11")] -lazy_static! { - pub static ref X11_BACKEND: Mutex, XNotSupported>> = - Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)); -} - #[derive(Debug, Clone)] pub enum OsError { #[cfg(feature = "x11")] @@ -515,46 +507,20 @@ impl Window { pub fn raw_window_handle(&self) -> RawWindowHandle { match self { #[cfg(feature = "x11")] - &Window::X(ref window) => RawWindowHandle::Xlib(window.raw_window_handle()), + &Window::X(ref window) => match &window.xconn.xlib { + Some(xlib) => RawWindowHandle::Xlib(raw_window_handle::unix::XlibHandle { + window: window.xwindow as _, + display: xlib.dpy as _, + ..raw_window_handle::unix::XlibHandle::empty() + }), + _ => RawWindowHandle::Xcb(window.raw_window_handle()), + }, #[cfg(feature = "wayland")] &Window::Wayland(ref window) => RawWindowHandle::Wayland(window.raw_window_handle()), } } } -#[cfg(feature = "x11")] -unsafe extern "C" fn x_error_callback( - display: *mut x11::ffi::Display, - event: *mut x11::ffi::XErrorEvent, -) -> c_int { - let xconn_lock = X11_BACKEND.lock(); - if let Ok(ref xconn) = *xconn_lock { - // `assume_init` is safe here because the array consists of `MaybeUninit` values, - // which do not require initialization. - let mut buf: [MaybeUninit; 1024] = MaybeUninit::uninit().assume_init(); - (xconn.xlib.XGetErrorText)( - display, - (*event).error_code as c_int, - buf.as_mut_ptr() as *mut c_char, - buf.len() as c_int, - ); - let description = CStr::from_ptr(buf.as_ptr() as *const c_char).to_string_lossy(); - - let error = XError { - description: description.into_owned(), - error_code: (*event).error_code, - request_code: (*event).request_code, - minor_code: (*event).minor_code, - }; - - error!("X11 error: {:#?}", error); - - *xconn.latest_error.lock() = Some(error); - } - // Fun fact: this return value is completely ignored. - 0 -} - pub enum EventLoop { #[cfg(feature = "wayland")] Wayland(wayland::EventLoop), @@ -651,12 +617,9 @@ impl EventLoop { #[cfg(feature = "x11")] pub fn new_x11_any_thread() -> Result, XNotSupported> { - let xconn = match X11_BACKEND.lock().as_ref() { - Ok(xconn) => xconn.clone(), - Err(err) => return Err(err.clone()), - }; - - Ok(EventLoop::X(x11::EventLoop::new(xconn))) + Ok(EventLoop::X(x11::EventLoop::new(Arc::new( + XConnection::new()?, + )))) } pub fn create_proxy(&self) -> EventLoopProxy { diff --git a/src/platform_impl/linux/x11/dnd.rs b/src/platform_impl/linux/x11/dnd.rs index 997228cd59..6d5dc4d0b6 100644 --- a/src/platform_impl/linux/x11/dnd.rs +++ b/src/platform_impl/linux/x11/dnd.rs @@ -8,55 +8,42 @@ use std::{ use percent_encoding::percent_decode; -use super::{ffi, util, XConnection, XError}; +use super::{ffi, XConnection}; +use xcb_dl_util::error::XcbError; +use xcb_dl_util::property::XcbGetPropertyError; #[derive(Debug)] pub struct DndAtoms { - pub aware: ffi::Atom, - pub enter: ffi::Atom, - pub leave: ffi::Atom, - pub drop: ffi::Atom, - pub position: ffi::Atom, - pub status: ffi::Atom, - pub action_private: ffi::Atom, - pub selection: ffi::Atom, - pub finished: ffi::Atom, - pub type_list: ffi::Atom, - pub uri_list: ffi::Atom, - pub none: ffi::Atom, + pub aware: ffi::xcb_atom_t, + pub enter: ffi::xcb_atom_t, + pub leave: ffi::xcb_atom_t, + pub drop: ffi::xcb_atom_t, + pub position: ffi::xcb_atom_t, + pub status: ffi::xcb_atom_t, + pub action_private: ffi::xcb_atom_t, + pub selection: ffi::xcb_atom_t, + pub finished: ffi::xcb_atom_t, + pub type_list: ffi::xcb_atom_t, + pub uri_list: ffi::xcb_atom_t, + pub none: ffi::xcb_atom_t, } impl DndAtoms { - pub fn new(xconn: &Arc) -> Result { - let names = [ - b"XdndAware\0".as_ptr() as *mut c_char, - b"XdndEnter\0".as_ptr() as *mut c_char, - b"XdndLeave\0".as_ptr() as *mut c_char, - b"XdndDrop\0".as_ptr() as *mut c_char, - b"XdndPosition\0".as_ptr() as *mut c_char, - b"XdndStatus\0".as_ptr() as *mut c_char, - b"XdndActionPrivate\0".as_ptr() as *mut c_char, - b"XdndSelection\0".as_ptr() as *mut c_char, - b"XdndFinished\0".as_ptr() as *mut c_char, - b"XdndTypeList\0".as_ptr() as *mut c_char, - b"text/uri-list\0".as_ptr() as *mut c_char, - b"None\0".as_ptr() as *mut c_char, - ]; - let atoms = unsafe { xconn.get_atoms(&names) }?; - Ok(DndAtoms { - aware: atoms[0], - enter: atoms[1], - leave: atoms[2], - drop: atoms[3], - position: atoms[4], - status: atoms[5], - action_private: atoms[6], - selection: atoms[7], - finished: atoms[8], - type_list: atoms[9], - uri_list: atoms[10], - none: atoms[11], - }) + pub fn new(xconn: &Arc) -> Self { + DndAtoms { + aware: xconn.get_atom("XdndAware"), + enter: xconn.get_atom("XdndEnter"), + leave: xconn.get_atom("XdndLeave"), + drop: xconn.get_atom("XdndDrop"), + position: xconn.get_atom("XdndPosition"), + status: xconn.get_atom("XdndStatus"), + action_private: xconn.get_atom("XdndActionPrivate"), + selection: xconn.get_atom("XdndSelection"), + finished: xconn.get_atom("XdndFinished"), + type_list: xconn.get_atom("XdndTypeList"), + uri_list: xconn.get_atom("text/uri-list"), + none: xconn.get_atom("None"), + } } } @@ -91,25 +78,25 @@ pub struct Dnd { xconn: Arc, pub atoms: DndAtoms, // Populated by XdndEnter event handler - pub version: Option, - pub type_list: Option>, + pub version: Option, + pub type_list: Option>, // Populated by XdndPosition event handler - pub source_window: Option, + pub source_window: Option, // Populated by SelectionNotify event handler (triggered by XdndPosition event handler) pub result: Option, DndDataParseError>>, } impl Dnd { - pub fn new(xconn: Arc) -> Result { - let atoms = DndAtoms::new(&xconn)?; - Ok(Dnd { + pub fn new(xconn: Arc) -> Self { + let atoms = DndAtoms::new(&xconn); + Dnd { xconn, atoms, version: None, type_list: None, source_window: None, result: None, - }) + } } pub fn reset(&mut self) { @@ -121,69 +108,67 @@ impl Dnd { pub unsafe fn send_status( &self, - this_window: c_ulong, - target_window: c_ulong, + this_window: ffi::xcb_window_t, + target_window: ffi::xcb_window_t, state: DndState, - ) -> Result<(), XError> { + ) { let (accepted, action) = match state { - DndState::Accepted => (1, self.atoms.action_private as c_long), - DndState::Rejected => (0, self.atoms.none as c_long), + DndState::Accepted => (1, self.atoms.action_private), + DndState::Rejected => (0, self.atoms.none), }; - self.xconn - .send_client_msg( - target_window, - target_window, - self.atoms.status, - None, - [this_window as c_long, accepted, 0, 0, action], - ) - .flush() + let pending = self.xconn.send_client_msg( + target_window, + target_window, + self.atoms.status, + None, + [this_window, accepted, 0, 0, action], + ); + self.xconn.discard(pending); } pub unsafe fn send_finished( &self, - this_window: c_ulong, - target_window: c_ulong, + this_window: ffi::xcb_window_t, + target_window: ffi::xcb_window_t, state: DndState, - ) -> Result<(), XError> { + ) -> Result<(), XcbError> { let (accepted, action) = match state { - DndState::Accepted => (1, self.atoms.action_private as c_long), - DndState::Rejected => (0, self.atoms.none as c_long), + DndState::Accepted => (1, self.atoms.action_private), + DndState::Rejected => (0, self.atoms.none), }; - self.xconn - .send_client_msg( - target_window, - target_window, - self.atoms.finished, - None, - [this_window as c_long, accepted, action, 0, 0], - ) - .flush() + let pending = self.xconn.send_client_msg( + target_window, + target_window, + self.atoms.finished, + None, + [this_window, accepted, action, 0, 0], + ); + self.xconn.check_pending1(pending) } pub unsafe fn get_type_list( &self, - source_window: c_ulong, - ) -> Result, util::GetPropertyError> { + source_window: ffi::xcb_window_t, + ) -> Result, XcbGetPropertyError> { self.xconn - .get_property(source_window, self.atoms.type_list, ffi::XA_ATOM) + .get_property(source_window, self.atoms.type_list, ffi::XCB_ATOM_ATOM) } - pub unsafe fn convert_selection(&self, window: c_ulong, time: c_ulong) { - (self.xconn.xlib.XConvertSelection)( - self.xconn.display, + pub unsafe fn convert_selection(&self, window: ffi::xcb_window_t, time: ffi::xcb_time_t) { + self.xconn.xcb.xcb_convert_selection( + self.xconn.c, + window, self.atoms.selection, self.atoms.uri_list, self.atoms.selection, - window, time, ); } pub unsafe fn read_data( &self, - window: c_ulong, - ) -> Result, util::GetPropertyError> { + window: ffi::xcb_window_t, + ) -> Result, XcbGetPropertyError> { self.xconn .get_property(window, self.atoms.selection, self.atoms.uri_list) } diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 65df373343..8dcc045b91 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -1,14 +1,12 @@ use std::sync::atomic::Ordering::Relaxed; use std::{collections::HashMap, rc::Rc, slice, sync::Arc}; -use libc::{c_char, c_int, c_long, c_uint, c_ulong}; - use parking_lot::MutexGuard; use SeatFocus::{KbFocus, PtrFocus}; use super::{ - ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd, DndState, - GenericEventCookie, ScrollOrientation, UnownedWindow, WindowId, XExtension, + ffi, get_xtarget, mkdid, mkwid, util, Device, DeviceId, DeviceInfo, Dnd, DndState, + ScrollOrientation, UnownedWindow, WindowId, }; use crate::{ @@ -16,11 +14,11 @@ use crate::{ event::{DeviceEvent, Event, KeyEvent, RawKeyEvent, TouchPhase, WindowEvent}, event_loop::EventLoopWindowTarget as RootELW, keyboard::ModifiersState, - platform::unix::x11::EventLoopWindowTarget, platform_impl::platform::{ common::{keymap, xkb_state::KbState}, KeyEventExtra, }, + platform_impl::x11::EventLoopWindowTarget, }; /// The X11 documentation states: "Keycodes lie in the inclusive range [8,255]". @@ -29,15 +27,17 @@ const KEYCODE_OFFSET: u8 = 8; pub(super) struct Seat { kb_state: KbState, /// The master keyboard of this seat - keyboard: c_int, + keyboard: ffi::xcb_input_device_id_t, /// The master pointer of this seat - pointer: c_int, + pointer: ffi::xcb_input_device_id_t, /// The window that has this seats keyboard focus - kb_focus: Option, + kb_focus: Option, /// The window that has this seats pointer focus - ptr_focus: Option, + ptr_focus: Option, /// The latest modifiers state current_modifiers: ModifiersState, + + num_errors: usize, } enum SeatFocus { @@ -47,10 +47,7 @@ enum SeatFocus { pub(super) struct EventProcessor { pub(super) dnd: Dnd, - pub(super) randr_event_offset: c_int, pub(super) devices: HashMap, - pub(super) xi2ext: XExtension, - pub(super) xkb_base_event: c_int, pub(super) target: Rc>, pub(super) seats: Vec, // Number of touch events currently in progress @@ -63,19 +60,27 @@ impl EventProcessor { target: &RootELW, devices: &mut HashMap, seats: &mut Vec, - device: c_int, + device: ffi::xcb_input_device_id_t, ) { let wt = get_xtarget(target); if let Some(info) = DeviceInfo::get(&wt.xconn, device) { - for info in info.iter() { + for info in info { + let info = unsafe { &*info }; let device_id = DeviceId(info.deviceid); - if info._use == ffi::XIMasterKeyboard { + if info.type_ == ffi::XCB_INPUT_DEVICE_TYPE_MASTER_KEYBOARD as _ { if devices.contains_key(&device_id) { seats.retain(|s| s.keyboard != info.deviceid); } - let xconn = &wt.xconn; - let connection = unsafe { (xconn.xlib_xcb.XGetXCBConnection)(xconn.display) }; - let kb_state = KbState::from_x11_xkb(connection, info.deviceid).unwrap(); + match wt.xconn.make_auto_repeat_detectable(device_id.0) { + Ok(true) => {} + Ok(false) => log::warn!("X server does not support detectable auto-repeat"), + Err(e) => log::error!( + "Could not enable detectable auto repeat for device {}: {}", + device_id.0, + e + ), + } + let kb_state = KbState::from_x11_xkb(wt.xconn.c, info.deviceid).unwrap(); seats.push(Seat { kb_state, keyboard: info.deviceid, @@ -83,6 +88,7 @@ impl EventProcessor { kb_focus: None, ptr_focus: None, current_modifiers: ModifiersState::empty(), + num_errors: 0, }); } devices.insert(device_id, Device::new(wt, info)); @@ -92,7 +98,7 @@ impl EventProcessor { fn with_window( wt: &EventLoopWindowTarget, - window_id: ffi::Window, + window_id: ffi::xcb_window_t, callback: F, ) -> Option where @@ -117,45 +123,11 @@ impl EventProcessor { result } - fn window_exists(wt: &EventLoopWindowTarget, window_id: ffi::Window) -> bool { + fn window_exists(wt: &EventLoopWindowTarget, window_id: ffi::xcb_window_t) -> bool { Self::with_window(wt, window_id, |_| ()).is_some() } - pub(super) fn poll(&self) -> bool { - let wt = get_xtarget(&self.target); - let result = unsafe { (wt.xconn.xlib.XPending)(wt.xconn.display) }; - - result != 0 - } - - pub(super) unsafe fn poll_one_event(&mut self, event_ptr: *mut ffi::XEvent) -> bool { - let wt = get_xtarget(&self.target); - // This function is used to poll and remove a single event - // from the Xlib event queue in a non-blocking, atomic way. - // XCheckIfEvent is non-blocking and removes events from queue. - // XNextEvent can't be used because it blocks while holding the - // global Xlib mutex. - // XPeekEvent does not remove events from the queue. - unsafe extern "C" fn predicate( - _display: *mut ffi::Display, - _event: *mut ffi::XEvent, - _arg: *mut c_char, - ) -> c_int { - // This predicate always returns "true" (1) to accept all events - 1 - } - - let result = (wt.xconn.xlib.XCheckIfEvent)( - wt.xconn.display, - event_ptr, - Some(predicate), - std::ptr::null_mut(), - ); - - result != 0 - } - - pub(super) fn process_event(&mut self, xev: &mut ffi::XEvent, mut callback: F) + pub(super) fn process_event(&mut self, xev: &mut ffi::xcb_generic_event_t, mut callback: F) where F: FnMut(Event<'_, T>), { @@ -169,62 +141,58 @@ impl EventProcessor { wt.reset_dead_keys.fetch_sub(reset_dead_keys, Relaxed); } } - // XFilterEvent tells us when an event has been discarded by the input method. - // Specifically, this involves all of the KeyPress events in compose/pre-edit sequences, - // along with an extra copy of the KeyRelease events. This also prevents backspace and - // arrow keys from being detected twice. - if ffi::True - == unsafe { - (wt.xconn.xlib.XFilterEvent)(xev, { - let xev: &ffi::XAnyEvent = xev.as_ref(); - xev.window - }) - } - { - return; - } - let event_type = xev.get_type(); - match event_type { - ffi::ClientMessage => { - let client_msg: &ffi::XClientMessageEvent = xev.as_ref(); + let response_type = xev.response_type & 0x7f; + match response_type { + 0 => unsafe { + let error = &*(xev as *const _ as *const ffi::xcb_generic_error_t); + let error = wt.xconn.errors.parse(error); + log::error!("An unchecked error occurred: {}", error); + }, + + ffi::XCB_CLIENT_MESSAGE => { + let client_msg = + unsafe { &*(xev as *const _ as *const ffi::xcb_client_message_event_t) }; + let data32 = unsafe { client_msg.data.data32 }; let window = client_msg.window; let window_id = mkwid(window); - if client_msg.data.get_long(0) as ffi::Atom == wt.wm_delete_window { + if data32[0] == wt.wm_delete_window { callback(Event::WindowEvent { window_id, event: WindowEvent::CloseRequested, }); - } else if client_msg.data.get_long(0) as ffi::Atom == wt.net_wm_ping { - let response_msg: &mut ffi::XClientMessageEvent = xev.as_mut(); - response_msg.window = wt.root; - wt.xconn - .send_event( - wt.root, - Some(ffi::SubstructureNotifyMask | ffi::SubstructureRedirectMask), - *response_msg, - ) - .queue(); - } else if client_msg.message_type == self.dnd.atoms.enter { - let source_window = client_msg.data.get_long(0) as c_ulong; - let flags = client_msg.data.get_long(1); + } else if data32[0] == wt.net_wm_ping { + Self::with_window(wt, window, |w| { + let response_msg = ffi::xcb_client_message_event_t { + window: w.screen.root, + ..*client_msg + }; + let pending = wt.xconn.send_event( + w.screen.root, + Some( + ffi::XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY + | ffi::XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + ), + &response_msg, + ); + wt.xconn.discard(pending); + }); + } else if client_msg.type_ == self.dnd.atoms.enter { + let source_window = data32[0]; + let flags = data32[1]; let version = flags >> 24; self.dnd.version = Some(version); - let has_more_types = flags - (flags & (c_long::max_value() - 1)) == 1; + let has_more_types = flags - (flags & (u32::MAX - 1)) == 1; if !has_more_types { - let type_list = vec![ - client_msg.data.get_long(2) as c_ulong, - client_msg.data.get_long(3) as c_ulong, - client_msg.data.get_long(4) as c_ulong, - ]; + let type_list = vec![data32[2], data32[3], data32[4]]; self.dnd.type_list = Some(type_list); } else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) } { self.dnd.type_list = Some(more_types); } - } else if client_msg.message_type == self.dnd.atoms.position { + } else if client_msg.type_ == self.dnd.atoms.position { // This event occurs every time the mouse moves while a file's being dragged // over our window. We emit HoveredFile in response; while the macOS backend // does that upon a drag entering, XDND doesn't have access to the actual drop @@ -233,7 +201,7 @@ impl EventProcessor { // supply position updates with `HoveredFile` or another event, implementing // that here would be trivial. - let source_window = client_msg.data.get_long(0) as c_ulong; + let source_window = data32[0]; // Equivalent to `(x << shift) | y` // where `shift = mem::size_of::() * 8` @@ -261,27 +229,25 @@ impl EventProcessor { unsafe { if self.dnd.result.is_none() { let time = if version >= 1 { - client_msg.data.get_long(3) as c_ulong + data32[3] } else { // In version 0, time isn't specified - ffi::CurrentTime + ffi::XCB_TIME_CURRENT_TIME }; // This results in the `SelectionNotify` event below self.dnd.convert_selection(window, time); } self.dnd - .send_status(window, source_window, DndState::Accepted) - .expect("Failed to send `XdndStatus` message."); + .send_status(window, source_window, DndState::Accepted); } } else { unsafe { self.dnd - .send_status(window, source_window, DndState::Rejected) - .expect("Failed to send `XdndStatus` message."); + .send_status(window, source_window, DndState::Rejected); } self.dnd.reset(); } - } else if client_msg.message_type == self.dnd.atoms.drop { + } else if client_msg.type_ == self.dnd.atoms.drop { let (source_window, state) = if let Some(source_window) = self.dnd.source_window { if let Some(Ok(ref path_list)) = self.dnd.result { @@ -296,7 +262,7 @@ impl EventProcessor { } else { // `source_window` won't be part of our DND state if we already rejected the drop in our // `XdndPosition` handler. - let source_window = client_msg.data.get_long(0) as c_ulong; + let source_window = data32[0]; (source_window, DndState::Rejected) }; unsafe { @@ -305,7 +271,7 @@ impl EventProcessor { .expect("Failed to send `XdndFinished` message."); } self.dnd.reset(); - } else if client_msg.message_type == self.dnd.atoms.leave { + } else if client_msg.type_ == self.dnd.atoms.leave { self.dnd.reset(); callback(Event::WindowEvent { window_id, @@ -314,8 +280,9 @@ impl EventProcessor { } } - ffi::SelectionNotify => { - let xsel: &ffi::XSelectionEvent = xev.as_ref(); + ffi::XCB_SELECTION_NOTIFY => { + let xsel = + unsafe { &*(xev as *const _ as *const ffi::xcb_selection_notify_event_t) }; let window = xsel.requestor; let window_id = mkwid(window); @@ -341,8 +308,9 @@ impl EventProcessor { } } - ffi::ConfigureNotify => { - let xev: &ffi::XConfigureEvent = xev.as_ref(); + ffi::XCB_CONFIGURE_NOTIFY => { + let xev = + unsafe { &*(xev as *const _ as *const ffi::xcb_configure_notify_event_t) }; let xwindow = xev.window; let window_id = mkwid(xwindow); @@ -354,7 +322,7 @@ impl EventProcessor { // We don't want to send `Moved` when this is false, since then every `Resized` // (whether the window moved or not) is accompanied by an extraneous `Moved` event // that has a position relative to the parent window. - let is_synthetic = xev.send_event == ffi::True; + let is_synthetic = xev.response_type & 0x80 != 0; // These are both in physical space. let new_inner_size = (xev.width as u32, xev.height as u32); @@ -396,8 +364,7 @@ impl EventProcessor { .as_ref() .cloned() .unwrap_or_else(|| { - let frame_extents = - wt.xconn.get_frame_extents_heuristic(xwindow, wt.root); + let frame_extents = wt.xconn.get_frame_extents_heuristic(&window); shared_state_lock.frame_extents = Some(frame_extents.clone()); frame_extents }); @@ -479,7 +446,9 @@ impl EventProcessor { // WMs constrain the window size, making the resize fail. This would cause an endless stream of // XResizeWindow requests, making Xorg, the winit client, and the WM consume 100% of CPU. if let Some(adjusted_size) = shared_state_lock.dpi_adjusted { - if new_inner_size == adjusted_size || !util::wm_name_is_one_of(&["Xfwm4"]) { + if new_inner_size == adjusted_size + || !window.screen.wm_name_is_one_of(&["Xfwm4"]) + { // When this finally happens, the event will not be synthetic. shared_state_lock.dpi_adjusted = None; } else { @@ -499,23 +468,23 @@ impl EventProcessor { } } - ffi::ReparentNotify => { - let xev: &ffi::XReparentEvent = xev.as_ref(); + ffi::XCB_REPARENT_NOTIFY => { + let xev = unsafe { &*(xev as *const _ as *const ffi::xcb_reparent_notify_event_t) }; // This is generally a reliable way to detect when the window manager's been // replaced, though this event is only fired by reparenting window managers // (which is almost all of them). Failing to correctly update WM info doesn't // really have much impact, since on the WMs affected (xmonad, dwm, etc.) the only // effect is that we waste some time trying to query unsupported properties. - wt.xconn.update_cached_wm_info(wt.root); + wt.xconn.update_cached_wm_info(); Self::with_window(wt, xev.window, |window| { window.invalidate_cached_frame_extents(); }); } - ffi::DestroyNotify => { - let xev: &ffi::XDestroyWindowEvent = xev.as_ref(); + ffi::XCB_DESTROY_NOTIFY => { + let xev = unsafe { &*(xev as *const _ as *const ffi::xcb_destroy_notify_event_t) }; let window = xev.window; let window_id = mkwid(window); @@ -530,15 +499,16 @@ impl EventProcessor { }); } - ffi::VisibilityNotify => { - let xev: &ffi::XVisibilityEvent = xev.as_ref(); + ffi::XCB_VISIBILITY_NOTIFY => { + let xev = + unsafe { &*(xev as *const _ as *const ffi::xcb_visibility_notify_event_t) }; let xwindow = xev.window; Self::with_window(wt, xwindow, |window| window.visibility_notify()); } - ffi::Expose => { - let xev: &ffi::XExposeEvent = xev.as_ref(); + ffi::XCB_EXPOSE => { + let xev = unsafe { &*(xev as *const _ as *const ffi::xcb_expose_event_t) }; // Multiple Expose events may be received for subareas of a window. // We issue `RedrawRequested` only for the last event of such a series. @@ -550,14 +520,10 @@ impl EventProcessor { } } - ffi::GenericEvent => { - let guard = if let Some(e) = GenericEventCookie::from_event(&wt.xconn, *xev) { - e - } else { - return; - }; - let xev = &guard.cookie; - if self.xi2ext.opcode != xev.extension { + ffi::XCB_GE_GENERIC => { + let xev = unsafe { &*(xev as *const _ as *const ffi::xcb_ge_generic_event_t) }; + + if wt.xconn.xinput_extension != xev.extension { return; } @@ -569,10 +535,12 @@ impl EventProcessor { WindowEvent::{AxisMotion, CursorMoved, MouseInput, MouseWheel}, }; - match xev.evtype { - ffi::XI_ButtonPress | ffi::XI_ButtonRelease => { - let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; - if (xev.flags & ffi::XIPointerEmulated) != 0 { + match xev.event_type { + ffi::XCB_INPUT_BUTTON_PRESS | ffi::XCB_INPUT_BUTTON_RELEASE => { + let xev = unsafe { + &*(xev as *const _ as *const ffi::xcb_input_button_press_event_t) + }; + if (xev.flags & ffi::XCB_INPUT_POINTER_EVENT_FLAGS_POINTER_EMULATED) != 0 { // Deliver multi-touch events instead of emulated mouse events. return; } @@ -587,17 +555,19 @@ impl EventProcessor { let window_id = mkwid(xev.event); let device_id = mkdid(seat.keyboard); - let state = if xev.evtype == ffi::XI_ButtonPress { + let state = if xev.event_type == ffi::XCB_INPUT_BUTTON_PRESS { Pressed } else { Released }; match xev.detail as u32 { - ffi::Button1 | ffi::Button2 | ffi::Button3 => { + ffi::XCB_BUTTON_INDEX_1 + | ffi::XCB_BUTTON_INDEX_2 + | ffi::XCB_BUTTON_INDEX_3 => { let button = match xev.detail as u32 { - ffi::Button1 => Left, - ffi::Button2 => Middle, - ffi::Button3 => Right, + ffi::XCB_BUTTON_INDEX_1 => Left, + ffi::XCB_BUTTON_INDEX_2 => Middle, + ffi::XCB_BUTTON_INDEX_3 => Right, _ => unreachable!(), }; callback(Event::WindowEvent { @@ -615,7 +585,9 @@ impl EventProcessor { // In practice, even clicky scroll wheels appear to be reported by evdev (and XInput2 in // turn) as axis motion, so we don't otherwise special-case these button presses. 4 | 5 | 6 | 7 => { - if xev.flags & ffi::XIPointerEmulated == 0 { + if xev.flags & ffi::XCB_INPUT_POINTER_EVENT_FLAGS_POINTER_EMULATED + == 0 + { callback(Event::WindowEvent { window_id, event: MouseWheel { @@ -645,8 +617,9 @@ impl EventProcessor { }), } } - ffi::XI_Motion => { - let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; + ffi::XCB_INPUT_MOTION => { + let xev = + unsafe { &*(xev as *const _ as *const ffi::xcb_input_motion_event_t) }; let seat = match find_seat_by_pointer(&mut self.seats, xev.deviceid) { Some(seat) => seat, @@ -657,14 +630,16 @@ impl EventProcessor { let device_id = mkdid(seat.keyboard); let window_id = mkwid(xev.event); - let new_cursor_pos = (xev.event_x, xev.event_y); + let event_x = util::fp1616_to_f64(xev.event_x); + let event_y = util::fp1616_to_f64(xev.event_y); + let new_cursor_pos = (event_x, event_y); let cursor_moved = Self::with_window(wt, xev.event, |window| { let mut shared_state_lock = window.shared_state.lock(); util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos) }); if cursor_moved == Some(true) { - let position = PhysicalPosition::new(xev.event_x, xev.event_y); + let position = PhysicalPosition::new(event_x, event_y); callback(Event::WindowEvent { window_id, @@ -683,8 +658,15 @@ impl EventProcessor { { let mask = unsafe { slice::from_raw_parts( - xev.valuators.mask, - xev.valuators.mask_len as usize, + wt.xconn.xinput.xcb_input_button_press_valuator_mask(xev), + xev.valuators_len as usize, + ) + }; + let axis_ids = mask_to_axis_ids(mask); + let axes = unsafe { + slice::from_raw_parts( + wt.xconn.xinput.xcb_input_button_press_axisvalues(xev), + axis_ids.len(), ) }; let physical_device = @@ -693,45 +675,41 @@ impl EventProcessor { None => return, }; - let mut value = xev.valuators.values; - for i in 0..xev.valuators.mask_len * 8 { - if ffi::XIMaskIsSet(mask, i) { - let x = unsafe { *value }; - if let Some(&mut (_, ref mut info)) = physical_device - .scroll_axes - .iter_mut() - .find(|&&mut (axis, _)| axis == i) - { - let delta = (x - info.position) / info.increment; - info.position = x; - events.push(Event::WindowEvent { - window_id, - event: MouseWheel { - device_id, - delta: match info.orientation { - ScrollOrientation::Horizontal => { - LineDelta(delta as f32, 0.0) - } - // X11 vertical scroll coordinates are opposite to winit's - ScrollOrientation::Vertical => { - LineDelta(0.0, -delta as f32) - } - }, - phase: TouchPhase::Moved, - modifiers: seat.current_modifiers, - }, - }); - } else { - events.push(Event::WindowEvent { - window_id, - event: AxisMotion { - device_id, - axis: i as u32, - value: unsafe { *value }, + for (&i, &x) in axis_ids.iter().zip(axes.iter()) { + let x = util::fp3232_to_f64(x); + if let Some(&mut (_, ref mut info)) = physical_device + .scroll_axes + .iter_mut() + .find(|&&mut (axis, _)| axis == i) + { + let delta = (x - info.position) / info.increment; + info.position = x; + events.push(Event::WindowEvent { + window_id, + event: MouseWheel { + device_id, + delta: match info.orientation { + ScrollOrientation::Horizontal => { + LineDelta(delta as f32, 0.0) + } + // X11 vertical scroll coordinates are opposite to winit's + ScrollOrientation::Vertical => { + LineDelta(0.0, -delta as f32) + } }, - }); - } - value = unsafe { value.offset(1) }; + phase: TouchPhase::Moved, + modifiers: seat.current_modifiers, + }, + }); + } else { + events.push(Event::WindowEvent { + window_id, + event: AxisMotion { + device_id, + axis: i as u32, + value: x, + }, + }); } } } @@ -740,8 +718,9 @@ impl EventProcessor { } } - ffi::XI_Enter => { - let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) }; + ffi::XCB_INPUT_ENTER => { + let xev = + unsafe { &*(xev as *const _ as *const ffi::xcb_input_enter_event_t) }; let seat = match find_seat_by_pointer(&mut self.seats, xev.deviceid) { Some(seat) => seat, @@ -752,8 +731,11 @@ impl EventProcessor { let device_id = mkdid(seat.keyboard); - if let Some(all_info) = DeviceInfo::get(&wt.xconn, ffi::XIAllDevices) { - for device_info in all_info.iter() { + if let Some(all_info) = + DeviceInfo::get(&wt.xconn, ffi::XCB_INPUT_DEVICE_ALL as _) + { + for device_info in all_info { + let device_info = unsafe { &*device_info }; if device_info.deviceid == xev.sourceid // This is needed for resetting to work correctly on i3, and // presumably some other WMs. On those, `XI_Enter` doesn't include @@ -763,14 +745,16 @@ impl EventProcessor { { let device_id = DeviceId(device_info.deviceid); if let Some(device) = self.devices.get_mut(&device_id) { - device.reset_scroll_position(device_info); + device.reset_scroll_position(wt, device_info); } } } } if let Some(window) = seat.ptr_focus { - let position = PhysicalPosition::new(xev.event_x, xev.event_y); + let event_x = util::fp1616_to_f64(xev.event_x); + let event_y = util::fp1616_to_f64(xev.event_y); + let position = PhysicalPosition::new(event_x, event_y); callback(Event::WindowEvent { window_id: mkwid(window), @@ -782,8 +766,9 @@ impl EventProcessor { }); } } - ffi::XI_Leave => { - let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) }; + ffi::XCB_INPUT_LEAVE => { + let xev = + unsafe { &*(xev as *const _ as *const ffi::xcb_input_leave_event_t) }; let seat = match find_seat_by_pointer(&mut self.seats, xev.deviceid) { Some(seat) => seat, @@ -792,8 +777,10 @@ impl EventProcessor { Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); Self::update_seat_focus(seat, PtrFocus, wt, None, &mut callback); } - ffi::XI_FocusIn => { - let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) }; + ffi::XCB_INPUT_FOCUS_IN => { + let xev = unsafe { + &*(xev as *const _ as *const ffi::xcb_input_focus_in_event_t) + }; let seat = match find_seat(&mut self.seats, xev.deviceid) { Some(seat) => seat, @@ -802,9 +789,10 @@ impl EventProcessor { Self::update_seat_kb_xi(seat, &xev.mods, &xev.group, &mut callback); Self::update_seat_focus(seat, KbFocus, wt, Some(xev.event), &mut callback); } - - ffi::XI_FocusOut => { - let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) }; + ffi::XCB_INPUT_FOCUS_OUT => { + let xev = unsafe { + &*(xev as *const _ as *const ffi::xcb_input_focus_out_event_t) + }; let seat = match find_seat(&mut self.seats, xev.deviceid) { Some(seat) => seat, @@ -814,12 +802,16 @@ impl EventProcessor { Self::update_seat_focus(seat, KbFocus, wt, None, &mut callback); } - ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => { - let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; - let phase = match xev.evtype { - ffi::XI_TouchBegin => TouchPhase::Started, - ffi::XI_TouchUpdate => TouchPhase::Moved, - ffi::XI_TouchEnd => TouchPhase::Ended, + ffi::XCB_INPUT_TOUCH_BEGIN + | ffi::XCB_INPUT_TOUCH_UPDATE + | ffi::XCB_INPUT_TOUCH_END => { + let xev = unsafe { + &*(xev as *const _ as *const ffi::xcb_input_touch_begin_event_t) + }; + let phase = match xev.event_type { + ffi::XCB_INPUT_TOUCH_BEGIN => TouchPhase::Started, + ffi::XCB_INPUT_TOUCH_UPDATE => TouchPhase::Moved, + ffi::XCB_INPUT_TOUCH_END => TouchPhase::Ended, _ => unreachable!(), }; let seat = match find_seat_by_pointer(&mut self.seats, xev.deviceid) { @@ -832,8 +824,9 @@ impl EventProcessor { if let Some(window) = seat.ptr_focus { let window_id = mkwid(window); let id = xev.detail as u64; - let location = - PhysicalPosition::new(xev.event_x as f64, xev.event_y as f64); + let event_x = util::fp1616_to_f64(xev.event_x); + let event_y = util::fp1616_to_f64(xev.event_y); + let location = PhysicalPosition::new(event_x, event_y); // Mouse cursor position changes when touch events are received. // Only the first concurrently active touch ID moves the mouse cursor. @@ -862,16 +855,18 @@ impl EventProcessor { } } - ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => { - let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; - if xev.flags & ffi::XIPointerEmulated == 0 { + ffi::XCB_INPUT_RAW_BUTTON_PRESS | ffi::XCB_INPUT_RAW_BUTTON_RELEASE => { + let xev = unsafe { + &*(xev as *const _ as *const ffi::xcb_input_raw_button_press_event_t) + }; + if xev.flags & ffi::XCB_INPUT_POINTER_EVENT_FLAGS_POINTER_EMULATED == 0 { callback(Event::DeviceEvent { device_id: mkdid(xev.deviceid), event: DeviceEvent::Button { button: xev.detail as u32, - state: match xev.evtype { - ffi::XI_RawButtonPress => Pressed, - ffi::XI_RawButtonRelease => Released, + state: match xev.event_type { + ffi::XCB_INPUT_RAW_BUTTON_PRESS => Pressed, + ffi::XCB_INPUT_RAW_BUTTON_RELEASE => Released, _ => unreachable!(), }, }, @@ -879,40 +874,47 @@ impl EventProcessor { } } - ffi::XI_RawMotion => { - let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; + ffi::XCB_INPUT_RAW_MOTION => { + let xev = unsafe { + &*(xev as *const _ as *const ffi::xcb_input_raw_motion_event_t) + }; let did = mkdid(xev.deviceid); let mask = unsafe { slice::from_raw_parts( - xev.valuators.mask, - xev.valuators.mask_len as usize, + wt.xconn + .xinput + .xcb_input_raw_button_press_valuator_mask(xev), + xev.valuators_len as usize, + ) + }; + let axis_ids = mask_to_axis_ids(mask); + let axes = unsafe { + slice::from_raw_parts( + wt.xconn.xinput.xcb_input_raw_button_press_axisvalues(xev), + axis_ids.len(), ) }; - let mut value = xev.raw_values; let mut mouse_delta = (0.0, 0.0); let mut scroll_delta = (0.0, 0.0); - for i in 0..xev.valuators.mask_len * 8 { - if ffi::XIMaskIsSet(mask, i) { - let x = unsafe { *value }; - // We assume that every XInput2 device with analog axes is a pointing device emitting - // relative coordinates. - match i { - 0 => mouse_delta.0 = x, - 1 => mouse_delta.1 = x, - 2 => scroll_delta.0 = x as f32, - 3 => scroll_delta.1 = x as f32, - _ => {} - } - callback(Event::DeviceEvent { - device_id: did, - event: DeviceEvent::Motion { - axis: i as u32, - value: x, - }, - }); - value = unsafe { value.offset(1) }; + for (&i, &x) in axis_ids.iter().zip(axes.iter()) { + let x = util::fp3232_to_f64(x); + // We assume that every XInput2 device with analog axes is a pointing device emitting + // relative coordinates. + match i { + 0 => mouse_delta.0 = x, + 1 => mouse_delta.1 = x, + 2 => scroll_delta.0 = x as f32, + 3 => scroll_delta.1 = x as f32, + _ => {} } + callback(Event::DeviceEvent { + device_id: did, + event: DeviceEvent::Motion { + axis: i as u32, + value: x, + }, + }); } if mouse_delta != (0.0, 0.0) { callback(Event::DeviceEvent { @@ -932,8 +934,10 @@ impl EventProcessor { // The regular KeyPress event has a problem where if you press a dead key, a KeyPress // event won't be emitted. XInput 2 does not have this problem. - ffi::XI_KeyPress | ffi::XI_KeyRelease => { - let xkev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) }; + ffi::XCB_INPUT_KEY_PRESS | ffi::XCB_INPUT_KEY_RELEASE => { + let xkev = unsafe { + &*(xev as *const _ as *const ffi::xcb_input_key_press_event_t) + }; let seat = match find_seat(&mut self.seats, xkev.deviceid) { Some(seat) => seat, @@ -943,7 +947,7 @@ impl EventProcessor { Self::update_seat_focus(seat, KbFocus, wt, Some(xkev.event), &mut callback); if let Some(focus) = seat.kb_focus { - let state = if xev.evtype == ffi::XI_KeyPress { + let state = if xev.event_type == ffi::XCB_INPUT_KEY_PRESS { Pressed } else { Released @@ -957,7 +961,8 @@ impl EventProcessor { xkev.group.effective as u32, state, ); - let repeat = xkev.flags & ffi::XIKeyRepeat == ffi::XIKeyRepeat; + let repeat = + xkev.flags & ffi::XCB_INPUT_KEY_EVENT_FLAGS_KEY_REPEAT != 0; callback(Event::WindowEvent { window_id: mkwid(focus), @@ -981,17 +986,19 @@ impl EventProcessor { } } - ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => { - let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) }; + ffi::XCB_INPUT_RAW_KEY_PRESS | ffi::XCB_INPUT_RAW_KEY_RELEASE => { + let xev = unsafe { + &*(xev as *const _ as *const ffi::xcb_input_raw_key_press_event_t) + }; - let state = match xev.evtype { - ffi::XI_RawKeyPress => Pressed, - ffi::XI_RawKeyRelease => Released, + let state = match xev.event_type { + ffi::XCB_INPUT_RAW_KEY_PRESS => Pressed, + ffi::XCB_INPUT_RAW_KEY_RELEASE => Released, _ => unreachable!(), }; let device_id = mkdid(xev.sourceid); - let keycode = xev.detail as u32; + let keycode = xev.detail; if keycode < KEYCODE_OFFSET as u32 { return; } @@ -1006,12 +1013,21 @@ impl EventProcessor { }); } - ffi::XI_HierarchyChanged => { - let xev: &ffi::XIHierarchyEvent = unsafe { &*(xev.data as *const _) }; - for info in - unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) } - { - if 0 != info.flags & (ffi::XISlaveAdded | ffi::XIMasterAdded) { + ffi::XCB_INPUT_HIERARCHY => { + let xev = unsafe { + &*(xev as *const _ as *const ffi::xcb_input_hierarchy_event_t) + }; + let infos = unsafe { + slice::from_raw_parts( + wt.xconn.xinput.xcb_input_hierarchy_infos(xev), + xev.num_infos as _, + ) + }; + for info in infos { + if 0 != info.flags + & (ffi::XCB_INPUT_HIERARCHY_MASK_SLAVE_ADDED + | ffi::XCB_INPUT_HIERARCHY_MASK_MASTER_ADDED) + { Self::init_device( &self.target, &mut self.devices, @@ -1022,14 +1038,17 @@ impl EventProcessor { device_id: mkdid(info.deviceid), event: DeviceEvent::Added, }); - } else if 0 != info.flags & (ffi::XISlaveRemoved | ffi::XIMasterRemoved) + } else if 0 + != info.flags + & (ffi::XCB_INPUT_HIERARCHY_MASK_SLAVE_REMOVED + | ffi::XCB_INPUT_HIERARCHY_MASK_MASTER_REMOVED) { callback(Event::DeviceEvent { device_id: mkdid(info.deviceid), event: DeviceEvent::Removed, }); self.devices.remove(&DeviceId(info.deviceid)); - if info._use == ffi::XIMasterKeyboard { + if info.type_ == ffi::XCB_INPUT_DEVICE_TYPE_MASTER_KEYBOARD as _ { self.seats.retain(|s| s.keyboard != info.deviceid); } } @@ -1039,9 +1058,9 @@ impl EventProcessor { _ => {} } } - _ if event_type == self.randr_event_offset => { + _ if response_type == wt.xconn.randr_first_event => { // In the future, it would be quite easy to emit monitor hotplug events. - let prev_list = monitor::invalidate_cached_monitor_list(); + let prev_list = wt.xconn.invalidate_cached_monitor_list(); if let Some(prev_list) = prev_list { let new_list = wt.xconn.available_monitors(); for new_monitor in new_list { @@ -1098,21 +1117,23 @@ impl EventProcessor { } } } - _ if event_type == self.xkb_base_event => { - let xev = unsafe { &*(xev as *const _ as *const ffi::XkbAnyEvent) }; - let seat = match find_seat(&mut self.seats, xev.device as c_int) { + _ if response_type == wt.xconn.xkb_first_event => { + let xev = unsafe { &*(xev as *const _ as *const ffi::xcb_xkb_map_notify_event_t) }; + let seat = match find_seat(&mut self.seats, xev.device_id as _) { Some(state) => state, _ => return, }; match xev.xkb_type { - ffi::XkbMapNotify | ffi::XkbNewKeyboardNotify => { + ffi::XCB_XKB_MAP_NOTIFY | ffi::XCB_XKB_NEW_KEYBOARD_NOTIFY => { unsafe { - seat.kb_state.init_with_x11_keymap(xev.device as c_int); + seat.kb_state.init_with_x11_keymap(xev.device_id as _); } Self::modifiers_changed(seat, &mut callback); } - ffi::XkbStateNotify => { - let xev = unsafe { &*(xev as *const _ as *const ffi::XkbStateNotifyEvent) }; + ffi::XCB_XKB_STATE_NOTIFY => { + let xev = unsafe { + &*(xev as *const _ as *const ffi::xcb_xkb_state_notify_event_t) + }; Self::update_seat_kb( seat, xev.base_mods as u32, @@ -1142,7 +1163,7 @@ impl EventProcessor { seat: &mut Seat, component: SeatFocus, wt: &EventLoopWindowTarget, - mut focus: Option, + mut focus: Option, callback: &mut F, ) where F: FnMut(Event<'_, T>), @@ -1170,19 +1191,31 @@ impl EventProcessor { old_focus_count - 1 }; if (new_focus_count == 0) != (old_focus_count == 0) { - let mask = if new_focus_count == 0 { + let state_details = if new_focus_count == 0 { 0 } else { - ffi::XkbModifierStateMask + ffi::XCB_XKB_STATE_PART_MODIFIER_STATE as _ + }; + let details = ffi::xcb_xkb_select_events_details_t { + affect_state: ffi::XCB_XKB_STATE_PART_MODIFIER_STATE as _, + state_details, + ..Default::default() }; - wt.xconn - .select_xkb_event_details( - seat.keyboard as c_uint, - ffi::XkbStateNotify as c_uint, - mask, - ) - .unwrap() - .queue(); + let pending = wt.xconn.select_xkb_event_details( + seat.keyboard, + ffi::XCB_XKB_EVENT_TYPE_STATE_NOTIFY, + &details, + ); + if let Err(e) = wt.xconn.check_pending1(pending) { + const MAX_ERRORS: usize = 5; + if seat.num_errors < MAX_ERRORS { + seat.num_errors += 1; + log::warn!("Could not change XKB selected events: {}", e); + if seat.num_errors == MAX_ERRORS { + log::warn!("Future warnings of this kind will be suppressed"); + } + } + } } } let device_id = mkdid(seat.keyboard); @@ -1215,17 +1248,17 @@ impl EventProcessor { fn update_seat_kb_xi( seat: &mut Seat, - mods: &ffi::XIModifierState, - group: &ffi::XIGroupState, + mods: &ffi::xcb_input_modifier_info_t, + group: &ffi::xcb_input_group_info_t, callback: &mut F, ) where F: FnMut(Event<'_, T>), { Self::update_seat_kb( seat, - mods.base as u32, - mods.latched as u32, - mods.locked as u32, + mods.base, + mods.latched, + mods.locked, group.base as u32, group.latched as u32, group.locked as u32, @@ -1301,10 +1334,25 @@ fn is_first_touch(first: &mut Option, num: &mut u32, id: u64, phase: TouchP *first == Some(id) } -fn find_seat(seats: &mut [Seat], kb: c_int) -> Option<&mut Seat> { +fn find_seat(seats: &mut [Seat], kb: ffi::xcb_input_device_id_t) -> Option<&mut Seat> { seats.iter_mut().find(|s| s.keyboard == kb) } -fn find_seat_by_pointer(seats: &mut [Seat], pointer: c_int) -> Option<&mut Seat> { +fn find_seat_by_pointer( + seats: &mut [Seat], + pointer: ffi::xcb_input_device_id_t, +) -> Option<&mut Seat> { seats.iter_mut().find(|s| s.pointer == pointer) } + +fn mask_to_axis_ids(mask: &[u32]) -> Vec { + let mut axis_ids = vec![]; + for i in 0..mask.len() { + for j in 0..32 { + if (mask[i] >> j) & 1 == 1 { + axis_ids.push((i * 32 + j) as u16); + } + } + } + axis_ids +} diff --git a/src/platform_impl/linux/x11/ffi.rs b/src/platform_impl/linux/x11/ffi.rs deleted file mode 100644 index 6d7c20894b..0000000000 --- a/src/platform_impl/linux/x11/ffi.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub use x11_dl::{ - error::OpenError, keysym::*, xcursor::*, xinput::*, xinput2::*, xlib::*, xlib_xcb::*, - xrandr::*, xrender::*, -}; diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 16e461c782..f04085a1b2 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -8,11 +8,11 @@ mod dnd; mod event_processor; -pub mod ffi; mod monitor; pub mod util; mod window; mod xdisplay; +pub mod xlib; pub use self::{ monitor::{MonitorHandle, VideoMode}, @@ -24,20 +24,15 @@ use std::sync::atomic::AtomicUsize; use std::{ cell::RefCell, collections::{HashMap, HashSet}, - ffi::CStr, - mem::{self, MaybeUninit}, ops::Deref, os::raw::*, ptr, rc::Rc, - slice, sync::mpsc::Receiver, sync::{Arc, Weak}, time::{Duration, Instant}, }; -use libc::{self}; - use mio::{unix::SourceFd, Events, Interest, Poll, Token, Waker}; use mio_misc::{ @@ -58,19 +53,17 @@ use crate::{ window::WindowAttributes, }; -use xkbcommon_dl::{ - xkb_x11_setup_xkb_extension_flags::XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, - XKBCOMMON_X11_HANDLE as XKBXH, -}; +use crate::platform_impl::x11::util::EventQueue; +use xcb_dl::{ffi, XcbXinput}; +use xcb_dl_util::xcb_box::XcbBox; const X_TOKEN: Token = Token(0); const USER_REDRAW_TOKEN: Token = Token(1); pub struct EventLoopWindowTarget { xconn: Arc, - wm_delete_window: ffi::Atom, - net_wm_ping: ffi::Atom, - root: ffi::Window, + wm_delete_window: ffi::xcb_atom_t, + net_wm_ping: ffi::xcb_atom_t, windows: RefCell>>, redraw_sender: Sender, reset_dead_keys: Arc, @@ -79,6 +72,7 @@ pub struct EventLoopWindowTarget { pub struct EventLoop { poll: Poll, + event_queue: EventQueue, event_processor: EventProcessor, redraw_channel: Receiver, user_channel: Receiver, @@ -100,90 +94,30 @@ impl Clone for EventLoopProxy { impl EventLoop { pub fn new(xconn: Arc) -> EventLoop { - let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) }; - - let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") }; - - let net_wm_ping = unsafe { xconn.get_atom_unchecked(b"_NET_WM_PING\0") }; - - let dnd = Dnd::new(Arc::clone(&xconn)) - .expect("Failed to call XInternAtoms when initializing drag and drop"); - - let randr_event_offset = xconn - .select_xrandr_input(root) - .expect("Failed to query XRandR extension"); - - let xi2ext = unsafe { - let mut ext = XExtension::default(); - - let res = (xconn.xlib.XQueryExtension)( - xconn.display, - b"XInputExtension\0".as_ptr() as *const c_char, - &mut ext.opcode, - &mut ext.first_event_id, - &mut ext.first_error_id, - ); - - if res == ffi::False { - panic!("X server missing XInput extension"); - } + let wm_delete_window = xconn.get_atom("WM_DELETE_WINDOW"); - ext - }; + let net_wm_ping = xconn.get_atom("_NET_WM_PING"); - let xkb_base_event = { - let mut base_event = 0; - - let result = unsafe { - (XKBXH.xkb_x11_setup_xkb_extension)( - (xconn.xlib_xcb.XGetXCBConnection)(xconn.display), - 1, // major - 0, // minor - XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, - ptr::null_mut(), // major out - ptr::null_mut(), // minor out - &mut base_event, - ptr::null_mut(), // base error - ) - }; - assert_eq!(result, 1, "Failed to initialize libxkbcommon"); - - base_event - }; + let dnd = Dnd::new(Arc::clone(&xconn)); - unsafe { - let mut xinput_major_ver = ffi::XI_2_Major; - let mut xinput_minor_ver = ffi::XI_2_Minor; - if (xconn.xinput2.XIQueryVersion)( - xconn.display, - &mut xinput_major_ver, - &mut xinput_minor_ver, - ) != ffi::Success as libc::c_int - { - panic!( - "X server has XInput extension {}.{} but does not support XInput2", - xinput_major_ver, xinput_minor_ver, - ); - } - } - - xconn.update_cached_wm_info(root); + xconn.update_cached_wm_info(); let poll = Poll::new().unwrap(); let waker = Arc::new(Waker::new(poll.registry(), USER_REDRAW_TOKEN).unwrap()); let queue = Arc::new(NotificationQueue::new(waker)); poll.registry() - .register(&mut SourceFd(&xconn.x11_fd), X_TOKEN, Interest::READABLE) + .register(&mut SourceFd(&xconn.fd), X_TOKEN, Interest::READABLE) .unwrap(); let (user_sender, user_channel) = channel(queue.clone(), NotificationId::gen_next()); let (redraw_sender, redraw_channel) = channel(queue, NotificationId::gen_next()); + let event_queue = EventQueue::new(&xconn); + let target = Rc::new(RootELW { p: super::EventLoopWindowTarget::X(EventLoopWindowTarget { - root, windows: Default::default(), _marker: ::std::marker::PhantomData, xconn, @@ -199,30 +133,34 @@ impl EventLoop { target: target.clone(), dnd, devices: Default::default(), - randr_event_offset, - xi2ext, - xkb_base_event: xkb_base_event as c_int, num_touch: 0, first_touch: None, seats: Default::default(), }; // Register for device hotplug events - // (The request buffer is flushed during `init_device`) - get_xtarget(&target) - .xconn - .select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask) - .queue(); + let wt = get_xtarget(&target); + for screen in &wt.xconn.screens { + let pending = wt.xconn.select_xinput_events( + screen.root, + ffi::XCB_INPUT_DEVICE_ALL as _, + ffi::XCB_INPUT_XI_EVENT_MASK_HIERARCHY, + ); + if let Err(e) = wt.xconn.check_pending1(pending) { + log::error!("Cannot listen for device hotplug events: {}", e); + } + } EventProcessor::init_device( &target, &mut event_processor.devices, &mut event_processor.seats, - ffi::XIAllDevices, + ffi::XCB_INPUT_DEVICE_ALL as _, ); let result = EventLoop { poll, + event_queue, redraw_channel, user_channel, user_sender, @@ -262,6 +200,9 @@ impl EventLoop { // Process all pending events self.drain_events(&mut callback, &mut control_flow); + // Send unchecked requests + self.flush_requests(); + // Empty the user event buffer { while let Ok(event) = self.user_channel.try_recv() { @@ -342,9 +283,9 @@ impl EventLoop { } } - // If the XConnection already contains buffered events, we don't - // need to wait for data on the socket. - if !self.event_processor.poll() { + // If the event queue already contains buffered events, we must not + // wait for data on the socket. + if !self.event_queue.has_pending_events() { self.poll.poll(&mut events, timeout).unwrap(); events.clear(); } @@ -379,13 +320,11 @@ impl EventLoop { F: FnMut(Event<'_, T>, &RootELW, &mut ControlFlow), { let target = &self.target; - let mut xev = MaybeUninit::uninit(); let wt = get_xtarget(&self.target); - while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } { - let mut xev = unsafe { xev.assume_init() }; - self.event_processor.process_event(&mut xev, |event| { + while let Some(mut event) = self.event_queue.poll_for_event() { + self.event_processor.process_event(&mut *event, |event| { sticky_exit_callback( event, target, @@ -404,6 +343,13 @@ impl EventLoop { }); } } + + fn flush_requests(&self) { + let wt = get_xtarget(&self.target); + if let Err(e) = wt.xconn.flush() { + panic!("The connection to the X server failed: {}", e); + } + } } pub(crate) fn get_xtarget(target: &RootELW) -> &EventLoopWindowTarget { @@ -436,57 +382,71 @@ impl EventLoopProxy { struct DeviceInfo<'a> { xconn: &'a XConnection, - info: *const ffi::XIDeviceInfo, - count: usize, + _reply: XcbBox, + iter: ffi::xcb_input_xi_device_info_iterator_t, } impl<'a> DeviceInfo<'a> { - fn get(xconn: &'a XConnection, device: c_int) -> Option { + fn get(xconn: &'a XConnection, device: ffi::xcb_input_device_id_t) -> Option { unsafe { - let mut count = 0; - let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count); - xconn.check_errors().ok()?; - - if info.is_null() || count == 0 { - None - } else { - Some(DeviceInfo { - xconn, - info, - count: count as usize, - }) - } + let mut err = ptr::null_mut(); + let reply = xconn.xinput.xcb_input_xi_query_device_reply( + xconn.c, + xconn.xinput.xcb_input_xi_query_device(xconn.c, device), + &mut err, + ); + let reply = match xconn.check(reply, err) { + Ok(i) => i, + Err(e) => { + log::error!("Could not query device data: {}", e); + return None; + } + }; + let iter = xconn + .xinput + .xcb_input_xi_query_device_infos_iterator(&*reply); + Some(DeviceInfo { + xconn, + _reply: reply, + iter, + }) } } } -impl<'a> Drop for DeviceInfo<'a> { - fn drop(&mut self) { - assert!(!self.info.is_null()); - unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) }; - } -} +impl<'a> Iterator for DeviceInfo<'a> { + type Item = *mut ffi::xcb_input_xi_device_info_t; -impl<'a> Deref for DeviceInfo<'a> { - type Target = [ffi::XIDeviceInfo]; - fn deref(&self) -> &Self::Target { - unsafe { slice::from_raw_parts(self.info, self.count) } + fn next(&mut self) -> Option { + if self.iter.rem > 0 { + let data = self.iter.data; + unsafe { + self.xconn + .xinput + .xcb_input_xi_device_info_next(&mut self.iter); + } + Some(data) + } else { + None + } } } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WindowId(ffi::Window); +pub struct WindowId(ffi::xcb_window_t); impl WindowId { + #[cfg(not(feature = "wayland"))] pub unsafe fn dummy() -> Self { WindowId(0) } } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(c_int); +pub struct DeviceId(ffi::xcb_input_device_id_t); impl DeviceId { + #[cfg(not(feature = "wayland"))] pub unsafe fn dummy() -> Self { DeviceId(0) } @@ -522,44 +482,14 @@ impl Drop for Window { let window = self.deref(); let xconn = &window.xconn; unsafe { - (xconn.xlib.XDestroyWindow)(xconn.display, window.id().0); - // If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about. - let _ = xconn.check_errors(); - } - } -} - -/// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure. This is a wrapper to -/// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed -struct GenericEventCookie<'a> { - xconn: &'a XConnection, - cookie: ffi::XGenericEventCookie, -} - -impl<'a> GenericEventCookie<'a> { - fn from_event<'b>( - xconn: &'b XConnection, - event: ffi::XEvent, - ) -> Option> { - unsafe { - let mut cookie: ffi::XGenericEventCookie = From::from(event); - if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True { - Some(GenericEventCookie { xconn, cookie }) - } else { - None + let cookie = xconn.xcb.xcb_destroy_window_checked(xconn.c, window.id().0); + if let Err(e) = xconn.check_cookie(cookie) { + log::error!("Could not destroy window: {}", e); } } } } -impl<'a> Drop for GenericEventCookie<'a> { - fn drop(&mut self) { - unsafe { - (self.xconn.xlib.XFreeEventData)(self.xconn.display, &mut self.cookie); - } - } -} - #[derive(Debug, Default, Copy, Clone)] struct XExtension { opcode: c_int, @@ -567,20 +497,20 @@ struct XExtension { first_error_id: c_int, } -fn mkwid(w: ffi::Window) -> crate::window::WindowId { +fn mkwid(w: ffi::xcb_window_t) -> crate::window::WindowId { crate::window::WindowId(crate::platform_impl::WindowId::X(WindowId(w))) } -fn mkdid(w: c_int) -> crate::event::DeviceId { +fn mkdid(w: ffi::xcb_input_device_id_t) -> crate::event::DeviceId { crate::event::DeviceId(crate::platform_impl::DeviceId::X(DeviceId(w))) } #[derive(Debug)] struct Device { name: String, - scroll_axes: Vec<(i32, ScrollAxis)>, + scroll_axes: Vec<(u16, ScrollAxis)>, // For master devices, this is the paired device (pointer <-> keyboard). // For slave devices, this is the master. - attachment: c_int, + attachment: ffi::xcb_input_device_id_t, } #[derive(Debug, Copy, Clone)] @@ -597,47 +527,73 @@ enum ScrollOrientation { } impl Device { - fn new(wt: &EventLoopWindowTarget, info: &ffi::XIDeviceInfo) -> Self { - let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() }; + fn new( + wt: &EventLoopWindowTarget, + info: &ffi::xcb_input_xi_device_info_t, + ) -> Self { + let name = unsafe { + let name = std::slice::from_raw_parts( + wt.xconn.xinput.xcb_input_xi_device_info_name(info) as _, + info.name_len as _, + ); + String::from_utf8_lossy(name) + }; let mut scroll_axes = Vec::new(); - if info._use == ffi::XIMasterKeyboard { - wt.xconn - .select_xkb_events( - info.deviceid as c_uint, - ffi::XkbNewKeyboardNotifyMask | ffi::XkbMapNotifyMask, - ) - .unwrap() - .queue(); + if info.type_ == ffi::XCB_INPUT_DEVICE_TYPE_MASTER_KEYBOARD as u16 { + let pending = wt.xconn.select_xkb_events( + info.deviceid, + ffi::XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | ffi::XCB_XKB_EVENT_TYPE_MAP_NOTIFY, + ); + if let Err(e) = wt.xconn.check_pending1(pending) { + log::error!( + "Cannot listen for keyboard layout changes of keyboard {}: {}", + info.deviceid, + e + ); + } } if Device::physical_device(info) { // Register for global raw events - let mask = ffi::XI_RawMotionMask - | ffi::XI_RawButtonPressMask - | ffi::XI_RawButtonReleaseMask - | ffi::XI_RawKeyPressMask - | ffi::XI_RawKeyReleaseMask; - // The request buffer is flushed when we poll for events - wt.xconn - .select_xinput_events(wt.root, info.deviceid, mask) - .queue(); + let mask = ffi::XCB_INPUT_XI_EVENT_MASK_RAW_MOTION + | ffi::XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS + | ffi::XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE + | ffi::XCB_INPUT_XI_EVENT_MASK_RAW_KEY_PRESS + | ffi::XCB_INPUT_XI_EVENT_MASK_RAW_KEY_RELEASE; + for screen in &wt.xconn.screens { + let pending = wt + .xconn + .select_xinput_events(screen.root, info.deviceid, mask); + if let Err(e) = wt.xconn.check_pending1(pending) { + log::error!( + "Cannot listen for raw input events of device {}: {}", + info.deviceid, + e + ); + } + } // Identify scroll axes - for class_ptr in Device::classes(info) { - let class = unsafe { &**class_ptr }; - match class._type { - ffi::XIScrollClass => { + let classes = unsafe { Classes::new(&wt.xconn.xinput, info) }; + for class in classes { + match class.type_ as ffi::xcb_input_device_class_type_t { + ffi::XCB_INPUT_DEVICE_CLASS_TYPE_SCROLL => { let info = unsafe { - mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class) + &*(class as *const _ as *const ffi::xcb_input_scroll_class_t) }; scroll_axes.push(( info.number, ScrollAxis { - increment: info.increment, - orientation: match info.scroll_type { - ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal, - ffi::XIScrollTypeVertical => ScrollOrientation::Vertical, + increment: util::fp3232_to_f64(info.increment), + orientation: match info.scroll_type as ffi::xcb_input_scroll_type_t + { + ffi::XCB_INPUT_SCROLL_TYPE_HORIZONTAL => { + ScrollOrientation::Horizontal + } + ffi::XCB_INPUT_SCROLL_TYPE_VERTICAL => { + ScrollOrientation::Vertical + } _ => unreachable!(), }, position: 0.0, @@ -654,25 +610,29 @@ impl Device { scroll_axes, attachment: info.attachment, }; - device.reset_scroll_position(info); + device.reset_scroll_position(wt, info); device } - fn reset_scroll_position(&mut self, info: &ffi::XIDeviceInfo) { + fn reset_scroll_position( + &mut self, + wt: &EventLoopWindowTarget, + info: &ffi::xcb_input_xi_device_info_t, + ) { if Device::physical_device(info) { - for class_ptr in Device::classes(info) { - let class = unsafe { &**class_ptr }; - match class._type { - ffi::XIValuatorClass => { + let classes = unsafe { Classes::new(&wt.xconn.xinput, info) }; + for class in classes { + match class.type_ as ffi::xcb_input_device_class_type_t { + ffi::XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR => { let info = unsafe { - mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class) + &*(class as *const _ as *const ffi::xcb_input_valuator_class_t) }; if let Some(&mut (_, ref mut axis)) = self .scroll_axes .iter_mut() .find(|&&mut (axis, _)| axis == info.number) { - axis.position = info.value; + axis.position = util::fp3232_to_f64(info.value); } } _ => {} @@ -682,19 +642,40 @@ impl Device { } #[inline] - fn physical_device(info: &ffi::XIDeviceInfo) -> bool { - info._use == ffi::XISlaveKeyboard - || info._use == ffi::XISlavePointer - || info._use == ffi::XIFloatingSlave + fn physical_device(info: &ffi::xcb_input_xi_device_info_t) -> bool { + let ty = info.type_ as ffi::xcb_input_device_type_t; + ty == ffi::XCB_INPUT_DEVICE_TYPE_SLAVE_KEYBOARD + || ty == ffi::XCB_INPUT_DEVICE_TYPE_SLAVE_POINTER + || ty == ffi::XCB_INPUT_DEVICE_TYPE_FLOATING_SLAVE } +} - #[inline] - fn classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo] { - unsafe { - slice::from_raw_parts( - info.classes as *const *const ffi::XIAnyClassInfo, - info.num_classes as usize, - ) +struct Classes<'a> { + xinput: &'a XcbXinput, + iter: ffi::xcb_input_device_class_iterator_t, +} + +impl<'a> Classes<'a> { + unsafe fn new(xinput: &'a XcbXinput, info: &'a ffi::xcb_input_xi_device_info_t) -> Self { + Self { + xinput, + iter: xinput.xcb_input_xi_device_info_classes_iterator(info), + } + } +} + +impl<'a> Iterator for Classes<'a> { + type Item = &'a ffi::xcb_input_device_class_t; + + fn next(&mut self) -> Option { + if self.iter.rem > 0 { + unsafe { + let res = &*self.iter.data; + self.xinput.xcb_input_device_class_next(&mut self.iter); + Some(res) + } + } else { + None } } } diff --git a/src/platform_impl/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs index 274e0cda9f..ae009c51bd 100644 --- a/src/platform_impl/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -1,38 +1,24 @@ -use std::os::raw::*; - -use parking_lot::Mutex; - -use super::{ - ffi::{ - RRCrtc, RRCrtcChangeNotifyMask, RRMode, RROutputPropertyNotifyMask, - RRScreenChangeNotifyMask, True, Window, XRRCrtcInfo, XRRScreenResources, - }, - util, XConnection, XError, -}; +use super::{ffi, util, XConnection}; +use crate::platform_impl::x11::xdisplay::Screen; use crate::{ dpi::{PhysicalPosition, PhysicalSize}, monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode}, platform_impl::{MonitorHandle as PlatformMonitorHandle, VideoMode as PlatformVideoMode}, }; +use std::ptr; +use std::sync::Arc; +use xcb_dl_util::error::XcbError; +use xcb_dl_util::xcb_box::XcbBox; // Used for testing. This should always be committed as false. const DISABLE_MONITOR_LIST_CACHING: bool = false; -lazy_static! { - static ref MONITORS: Mutex>> = Mutex::default(); -} - -pub fn invalidate_cached_monitor_list() -> Option> { - // We update this lazily. - (*MONITORS.lock()).take() -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct VideoMode { - pub(crate) size: (u32, u32), + pub(crate) size: (u16, u16), pub(crate) bit_depth: u16, pub(crate) refresh_rate: u16, - pub(crate) native_mode: RRMode, + pub(crate) native_mode: u32, pub(crate) monitor: Option, } @@ -63,9 +49,11 @@ impl VideoMode { #[derive(Debug, Clone)] pub struct MonitorHandle { /// The actual id - pub(crate) id: RRCrtc, + pub(crate) id: ffi::xcb_randr_crtc_t, /// The name of the monitor pub(crate) name: String, + /// The screen this monitor is attached to + pub(crate) screen: Option>, /// The size of the monitor dimensions: (u32, u32), /// The position of the monitor in the X screen @@ -109,31 +97,38 @@ impl std::hash::Hash for MonitorHandle { impl MonitorHandle { fn new( xconn: &XConnection, - resources: *mut XRRScreenResources, - id: RRCrtc, - crtc: *mut XRRCrtcInfo, + screen: &Arc, + resources: &ffi::xcb_randr_get_screen_resources_reply_t, + id: ffi::xcb_randr_crtc_t, + crtc: &ffi::xcb_randr_get_crtc_info_reply_t, primary: bool, - ) -> Option { - let (name, scale_factor, video_modes) = unsafe { xconn.get_output_info(resources, crtc)? }; - let dimensions = unsafe { ((*crtc).width as u32, (*crtc).height as u32) }; - let position = unsafe { ((*crtc).x as i32, (*crtc).y as i32) }; + ) -> Result, XcbError> { + let output_info = unsafe { xconn.get_output_info(screen, resources, crtc)? }; + let (name, scale_factor, video_modes) = match output_info { + Some(o) => o, + _ => return Ok(None), + }; + let dimensions = (crtc.width as u32, crtc.height as u32); + let position = (crtc.x as i32, crtc.y as i32); let rect = util::AaRect::new(position, dimensions); - Some(MonitorHandle { + Ok(Some(MonitorHandle { id, name, + screen: Some(screen.clone()), scale_factor, dimensions, position, primary, rect, video_modes, - }) + })) } pub fn dummy() -> Self { MonitorHandle { id: 0, name: "".into(), + screen: None, scale_factor: 1.0, dimensions: (1, 1), position: (0, 0), @@ -184,7 +179,13 @@ impl MonitorHandle { impl XConnection { pub fn get_monitor_for_window(&self, window_rect: Option) -> MonitorHandle { - let monitors = self.available_monitors(); + let monitors = match self.available_monitors_inner() { + Ok(m) => m, + Err(e) => { + log::error!("Could not retrieve monitors: {}", e); + return MonitorHandle::dummy(); + } + }; if monitors.is_empty() { // Return a dummy monitor to avoid panicking @@ -211,41 +212,51 @@ impl XConnection { matched_monitor.to_owned() } - fn query_monitor_list(&self) -> Vec { + unsafe fn screen_resources( + &self, + screen: &Screen, + ) -> Result, XcbError> { + let cookie = if self.randr_version >= (1, 3) { + self.randr + .xcb_randr_get_screen_resources_current(self.c, screen.root) + .sequence + } else { + self.randr + .xcb_randr_get_screen_resources(self.c, screen.root) + .sequence + }; + let mut err = ptr::null_mut(); + let reply = self.xcb.xcb_wait_for_reply(self.c, cookie, &mut err) as *mut _; + self.check(reply, err) + } + + fn query_monitor_list(&self, screen: &Arc) -> Result, XcbError> { unsafe { - let mut major = 0; - let mut minor = 0; - (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor); - - let root = (self.xlib.XDefaultRootWindow)(self.display); - let resources = if (major == 1 && minor >= 3) || major > 1 { - (self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root) - } else { - // WARNING: this function is supposedly very slow, on the order of hundreds of ms. - // Upon failure, `resources` will be null. - (self.xrandr.XRRGetScreenResources)(self.display, root) - }; - - if resources.is_null() { - panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist."); - } + let resources = self.screen_resources(screen)?; + let crtcs = std::slice::from_raw_parts( + self.randr.xcb_randr_get_screen_resources_crtcs(&*resources), + resources.num_crtcs as _, + ); - let mut available; let mut has_primary = false; - let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root); - available = Vec::with_capacity((*resources).ncrtc as usize); - for crtc_index in 0..(*resources).ncrtc { - let crtc_id = *((*resources).crtcs.offset(crtc_index as isize)); - let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id); - let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0; + let mut err = ptr::null_mut(); + let primary = self.randr.xcb_randr_get_output_primary_reply( + self.c, + self.randr.xcb_randr_get_output_primary(self.c, screen.root), + &mut err, + ); + let primary = self.check(primary, err)?.output; + let mut available = Vec::with_capacity(crtcs.len()); + for &crtc_id in crtcs { + let crtc = self.get_crtc_info(crtc_id)?; + let is_active = crtc.width > 0 && crtc.height > 0 && crtc.num_outputs > 0; if is_active { - let is_primary = *(*crtc).outputs.offset(0) == primary; + let is_primary = *self.randr.xcb_randr_get_crtc_info_outputs(&*crtc) == primary; has_primary |= is_primary; - MonitorHandle::new(self, resources, crtc_id, crtc, is_primary) + MonitorHandle::new(self, screen, &resources, crtc_id, &crtc, is_primary)? .map(|monitor_id| available.push(monitor_id)); } - (self.xrandr.XRRFreeCrtcInfo)(crtc); } // If no monitors were detected as being primary, we just pick one ourselves! @@ -256,59 +267,48 @@ impl XConnection { } } - (self.xrandr.XRRFreeScreenResources)(resources); - available + Ok(available) } } pub fn available_monitors(&self) -> Vec { - let mut monitors_lock = MONITORS.lock(); - (*monitors_lock) - .as_ref() - .cloned() - .or_else(|| { - let monitors = Some(self.query_monitor_list()); - if !DISABLE_MONITOR_LIST_CACHING { - (*monitors_lock) = monitors.clone(); - } - monitors - }) - .unwrap() + match self.available_monitors_inner() { + Ok(m) => m, + Err(e) => { + log::error!("Could not retrive monitors: {}", e); + vec![] + } + } } - #[inline] - pub fn primary_monitor(&self) -> MonitorHandle { - self.available_monitors() - .into_iter() - .find(|monitor| monitor.primary) - .unwrap_or_else(MonitorHandle::dummy) + pub fn available_monitors_inner(&self) -> Result, XcbError> { + let mut monitors_lock = self.monitors.lock(); + if let Some(monitors) = &*monitors_lock { + return Ok(monitors.clone()); + } + let mut monitors = vec![]; + for screen in &self.screens { + monitors.extend(self.query_monitor_list(screen)?); + } + if !DISABLE_MONITOR_LIST_CACHING { + (*monitors_lock) = Some(monitors.clone()); + } + Ok(monitors) } - pub fn select_xrandr_input(&self, root: Window) -> Result { - let has_xrandr = unsafe { - let mut major = 0; - let mut minor = 0; - (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor) - }; - assert!( - has_xrandr == True, - "[winit] XRandR extension not available." - ); - - let mut event_offset = 0; - let mut error_offset = 0; - let status = unsafe { - (self.xrandr.XRRQueryExtension)(self.display, &mut event_offset, &mut error_offset) - }; - - if status != True { - self.check_errors()?; - unreachable!("[winit] `XRRQueryExtension` failed but no error was received."); + #[inline] + pub fn primary_monitor(&self) -> MonitorHandle { + match self.available_monitors_inner() { + Ok(m) => m + .into_iter() + .find(|monitor| monitor.primary) + .unwrap_or_else(MonitorHandle::dummy), + _ => MonitorHandle::dummy(), } + } - let mask = RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask | RRScreenChangeNotifyMask; - unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) }; - - Ok(event_offset) + pub fn invalidate_cached_monitor_list(&self) -> Option> { + // We update this lazily. + (*self.monitors.lock()).take() } } diff --git a/src/platform_impl/linux/x11/util/atom.rs b/src/platform_impl/linux/x11/util/atom.rs index 4138722483..ba0466a27f 100644 --- a/src/platform_impl/linux/x11/util/atom.rs +++ b/src/platform_impl/linux/x11/util/atom.rs @@ -1,71 +1,29 @@ -use std::{ - collections::HashMap, - ffi::{CStr, CString}, - fmt::Debug, - os::raw::*, -}; - -use parking_lot::Mutex; - use super::*; -type AtomCache = HashMap; - -lazy_static! { - static ref ATOM_CACHE: Mutex = Mutex::new(HashMap::with_capacity(2048)); -} - impl XConnection { - pub fn get_atom + Debug>(&self, name: T) -> ffi::Atom { - let name = name.as_ref(); - let mut atom_cache_lock = ATOM_CACHE.lock(); + pub fn get_atom(&self, name: &str) -> ffi::xcb_atom_t { + let mut atom_cache_lock = self.atom_cache.lock(); let cached_atom = (*atom_cache_lock).get(name).cloned(); if let Some(atom) = cached_atom { atom } else { - let atom = unsafe { - (self.xlib.XInternAtom)(self.display, name.as_ptr() as *const c_char, ffi::False) - }; - if atom == 0 { - panic!( - "`XInternAtom` failed, which really shouldn't happen. Atom: {:?}, Error: {:#?}", - name, - self.check_errors(), - ); - } - /*println!( - "XInternAtom name:{:?} atom:{:?}", - name, - atom, - );*/ + let atom = self.get_atom_uncached(name); (*atom_cache_lock).insert(name.to_owned(), atom); atom } } - pub unsafe fn get_atom_unchecked(&self, name: &[u8]) -> ffi::Atom { - debug_assert!(CStr::from_bytes_with_nul(name).is_ok()); - let name = CStr::from_bytes_with_nul_unchecked(name); - self.get_atom(name) - } - - // Note: this doesn't use caching, for the sake of simplicity. - // If you're dealing with this many atoms, you'll usually want to cache them locally anyway. - pub unsafe fn get_atoms(&self, names: &[*mut c_char]) -> Result, XError> { - let mut atoms = Vec::with_capacity(names.len()); - (self.xlib.XInternAtoms)( - self.display, - names.as_ptr() as *mut _, - names.len() as c_int, - ffi::False, - atoms.as_mut_ptr(), - ); - self.check_errors()?; - atoms.set_len(names.len()); - /*println!( - "XInternAtoms atoms:{:?}", - atoms, - );*/ - Ok(atoms) + pub fn get_atom_uncached(&self, name: &str) -> ffi::xcb_atom_t { + unsafe { + let cookie = self + .xcb + .xcb_intern_atom(self.c, 0, name.len() as _, name.as_ptr() as _); + let mut err = ptr::null_mut(); + let reply = self.xcb.xcb_intern_atom_reply(self.c, cookie, &mut err); + match self.check(reply, err) { + Ok(r) => r.atom, + Err(e) => panic!("Could not intern the atom `{}`: {}", name, e), + } + } } } diff --git a/src/platform_impl/linux/x11/util/client_msg.rs b/src/platform_impl/linux/x11/util/client_msg.rs index 2f2b7edf9c..8679de90fa 100644 --- a/src/platform_impl/linux/x11/util/client_msg.rs +++ b/src/platform_impl/linux/x11/util/client_msg.rs @@ -1,46 +1,72 @@ use super::*; - -pub type ClientMsgPayload = [c_long; 5]; +use xcb_dl_util::format::XcbDataType; +use xcb_dl_util::void::XcbPendingCommand; impl XConnection { - pub fn send_event>( + pub fn send_event( &self, - target_window: c_ulong, - event_mask: Option, - event: T, - ) -> Flusher<'_> { - let event_mask = event_mask.unwrap_or(ffi::NoEventMask); + target_window: ffi::xcb_window_t, + event_mask: Option, + event: &T, + ) -> XcbPendingCommand { + assert_eq!( + mem::size_of::(), + mem::size_of::() + ); + let event_mask = event_mask.unwrap_or(ffi::XCB_EVENT_MASK_NO_EVENT); unsafe { - (self.xlib.XSendEvent)( - self.display, - target_window, - ffi::False, - event_mask, - &mut event.into(), - ); + self.xcb + .xcb_send_event_checked( + self.c, + 0, + target_window, + event_mask, + event as *const _ as _, + ) + .into() } - Flusher::new(self) } - pub fn send_client_msg( + pub fn send_client_msg( &self, - window: c_ulong, // The window this is "about"; not necessarily this window - target_window: c_ulong, // The window we're sending to - message_type: ffi::Atom, - event_mask: Option, - data: ClientMsgPayload, - ) -> Flusher<'_> { - let event = ffi::XClientMessageEvent { - type_: ffi::ClientMessage, - display: self.display, + window: ffi::xcb_window_t, // The window this is "about"; not necessarily this window + target_window: ffi::xcb_window_t, // The window we're sending to + message_type: ffi::xcb_atom_t, + event_mask: Option, + data: T, + ) -> XcbPendingCommand { + let event = ffi::xcb_client_message_event_t { + response_type: ffi::XCB_CLIENT_MESSAGE, + format: T::DataType::XCB_BITS, + type_: message_type, window, - message_type, - format: c_long::FORMAT as c_int, - data: unsafe { mem::transmute(data) }, - // These fields are ignored by `XSendEvent` - serial: 0, - send_event: 0, + data: unsafe { + assert_eq!(mem::size_of_val(&data), mem::size_of::<[u8; 20]>()); + ffi::xcb_client_message_data_t { + data8: *(&data as *const T as *const [u8; 20]), + } + }, + ..Default::default() }; - self.send_event(target_window, event_mask, event) + self.send_event(target_window, event_mask, &event) } } + +pub unsafe trait ClientMessageType { + type DataType: XcbDataType; +} + +macro_rules! imp { + ($ty:ty, $n:expr) => { + unsafe impl ClientMessageType for [$ty; $n] { + type DataType = $ty; + } + }; +} + +imp!(u8, 20); +imp!(u16, 10); +imp!(u32, 5); +imp!(i8, 20); +imp!(i16, 10); +imp!(i32, 5); diff --git a/src/platform_impl/linux/x11/util/cursor.rs b/src/platform_impl/linux/x11/util/cursor.rs index 684af49da9..40271474ad 100644 --- a/src/platform_impl/linux/x11/util/cursor.rs +++ b/src/platform_impl/linux/x11/util/cursor.rs @@ -1,9 +1,11 @@ use crate::window::CursorIcon; use super::*; +use std::slice; +use xcb_dl_util::cursor::{XcbCursorImage, XcbLoadCursorConfig}; impl XConnection { - pub fn set_cursor_icon(&self, window: ffi::Window, cursor: Option) { + pub fn set_cursor_icon(&self, window: ffi::xcb_window_t, cursor: Option) { let cursor = *self .cursor_cache .lock() @@ -13,117 +15,127 @@ impl XConnection { self.update_cursor(window, cursor); } - fn create_empty_cursor(&self) -> ffi::Cursor { - let data = 0; - let pixmap = unsafe { - let screen = (self.xlib.XDefaultScreen)(self.display); - let window = (self.xlib.XRootWindow)(self.display, screen); - (self.xlib.XCreateBitmapFromData)(self.display, window, &data, 1, 1) + fn create_empty_cursor(&self) -> ffi::xcb_cursor_t { + let image = XcbCursorImage { + width: 1, + height: 1, + xhot: 0, + yhot: 0, + delay: 0, + pixels: vec![0], + ..Default::default() }; - - if pixmap == 0 { - panic!("failed to allocate pixmap for cursor"); - } - unsafe { - // We don't care about this color, since it only fills bytes - // in the pixmap which are not 0 in the mask. - let mut dummy_color = MaybeUninit::uninit(); - let cursor = (self.xlib.XCreatePixmapCursor)( - self.display, - pixmap, - pixmap, - dummy_color.as_mut_ptr(), - dummy_color.as_mut_ptr(), - 0, - 0, - ); - (self.xlib.XFreePixmap)(self.display, pixmap); - - cursor + let cursor = + self.cursors + .create_cursor(&self.xcb, &self.render, slice::from_ref(&image)); + match cursor { + Ok(c) => c, + Err(e) => { + log::error!("Could not create empty cursor: {}", e); + 0 + } + } } } - fn load_cursor(&self, name: &[u8]) -> ffi::Cursor { + fn load_cursor(&self, name: &str) -> Option { unsafe { - (self.xcursor.XcursorLibraryLoadCursor)(self.display, name.as_ptr() as *const c_char) + let config = XcbLoadCursorConfig { + name, + ..Default::default() + }; + match self.cursors.load_cursor(&self.xcb, &self.render, &config) { + Ok(c) => Some(c), + Err(e) => { + log::debug!("Could not load cursor {}: {}", name, e); + None + } + } } } - fn load_first_existing_cursor(&self, names: &[&[u8]]) -> ffi::Cursor { + fn load_first_existing_cursor(&self, names: &[&str]) -> Option { for name in names.iter() { - let xcursor = self.load_cursor(name); - if xcursor != 0 { - return xcursor; + if let Some(xcursor) = self.load_cursor(name) { + return Some(xcursor); } } - 0 + None } - fn get_cursor(&self, cursor: Option) -> ffi::Cursor { + fn get_cursor(&self, cursor: Option) -> ffi::xcb_cursor_t { let cursor = match cursor { Some(cursor) => cursor, None => return self.create_empty_cursor(), }; - let load = |name: &[u8]| self.load_cursor(name); + let load = |name: &str| self.load_cursor(name); - let loadn = |names: &[&[u8]]| self.load_first_existing_cursor(names); + let loadn = |names: &[&str]| self.load_first_existing_cursor(names); // Try multiple names in some cases where the name // differs on the desktop environments or themes. // // Try the better looking (or more suiting) names first. - match cursor { - CursorIcon::Alias => load(b"link\0"), - CursorIcon::Arrow => load(b"arrow\0"), - CursorIcon::Cell => load(b"plus\0"), - CursorIcon::Copy => load(b"copy\0"), - CursorIcon::Crosshair => load(b"crosshair\0"), - CursorIcon::Default => load(b"left_ptr\0"), - CursorIcon::Hand => loadn(&[b"hand2\0", b"hand1\0"]), - CursorIcon::Help => load(b"question_arrow\0"), - CursorIcon::Move => load(b"move\0"), - CursorIcon::Grab => loadn(&[b"openhand\0", b"grab\0"]), - CursorIcon::Grabbing => loadn(&[b"closedhand\0", b"grabbing\0"]), - CursorIcon::Progress => load(b"left_ptr_watch\0"), - CursorIcon::AllScroll => load(b"all-scroll\0"), - CursorIcon::ContextMenu => load(b"context-menu\0"), - - CursorIcon::NoDrop => loadn(&[b"no-drop\0", b"circle\0"]), - CursorIcon::NotAllowed => load(b"crossed_circle\0"), + let cursor = match cursor { + CursorIcon::Alias => load("link"), + CursorIcon::Arrow => load("arrow"), + CursorIcon::Cell => load("plus"), + CursorIcon::Copy => load("copy"), + CursorIcon::Crosshair => load("crosshair"), + CursorIcon::Default => load("left_ptr"), + CursorIcon::Hand => loadn(&["hand2", "hand1"]), + CursorIcon::Help => load("question_arrow"), + CursorIcon::Move => load("move"), + CursorIcon::Grab => loadn(&["openhand", "grab"]), + CursorIcon::Grabbing => loadn(&["closedhand", "grabbing"]), + CursorIcon::Progress => load("left_ptr_watch"), + CursorIcon::AllScroll => load("all-scroll"), + CursorIcon::ContextMenu => load("context-menu"), + + CursorIcon::NoDrop => loadn(&["no-drop", "circle"]), + CursorIcon::NotAllowed => load("crossed_circle"), // Resize cursors - CursorIcon::EResize => load(b"right_side\0"), - CursorIcon::NResize => load(b"top_side\0"), - CursorIcon::NeResize => load(b"top_right_corner\0"), - CursorIcon::NwResize => load(b"top_left_corner\0"), - CursorIcon::SResize => load(b"bottom_side\0"), - CursorIcon::SeResize => load(b"bottom_right_corner\0"), - CursorIcon::SwResize => load(b"bottom_left_corner\0"), - CursorIcon::WResize => load(b"left_side\0"), - CursorIcon::EwResize => load(b"h_double_arrow\0"), - CursorIcon::NsResize => load(b"v_double_arrow\0"), - CursorIcon::NwseResize => loadn(&[b"bd_double_arrow\0", b"size_bdiag\0"]), - CursorIcon::NeswResize => loadn(&[b"fd_double_arrow\0", b"size_fdiag\0"]), - CursorIcon::ColResize => loadn(&[b"split_h\0", b"h_double_arrow\0"]), - CursorIcon::RowResize => loadn(&[b"split_v\0", b"v_double_arrow\0"]), - - CursorIcon::Text => loadn(&[b"text\0", b"xterm\0"]), - CursorIcon::VerticalText => load(b"vertical-text\0"), - - CursorIcon::Wait => load(b"watch\0"), - - CursorIcon::ZoomIn => load(b"zoom-in\0"), - CursorIcon::ZoomOut => load(b"zoom-out\0"), - } + CursorIcon::EResize => load("right_side"), + CursorIcon::NResize => load("top_side"), + CursorIcon::NeResize => load("top_right_corner"), + CursorIcon::NwResize => load("top_left_corner"), + CursorIcon::SResize => load("bottom_side"), + CursorIcon::SeResize => load("bottom_right_corner"), + CursorIcon::SwResize => load("bottom_left_corner"), + CursorIcon::WResize => load("left_side"), + CursorIcon::EwResize => load("h_double_arrow"), + CursorIcon::NsResize => load("v_double_arrow"), + CursorIcon::NwseResize => loadn(&["bd_double_arrow", "size_bdiag"]), + CursorIcon::NeswResize => loadn(&["fd_double_arrow", "size_fdiag"]), + CursorIcon::ColResize => loadn(&["split_h", "h_double_arrow"]), + CursorIcon::RowResize => loadn(&["split_v", "v_double_arrow"]), + + CursorIcon::Text => loadn(&["text", "xterm"]), + CursorIcon::VerticalText => load("vertical-text"), + + CursorIcon::Wait => load("watch"), + + CursorIcon::ZoomIn => load("zoom-in"), + CursorIcon::ZoomOut => load("zoom-out"), + }; + + cursor.unwrap_or(0) } - fn update_cursor(&self, window: ffi::Window, cursor: ffi::Cursor) { + fn update_cursor(&self, window: ffi::xcb_window_t, cursor: ffi::xcb_cursor_t) { unsafe { - (self.xlib.XDefineCursor)(self.display, window, cursor); - - self.flush_requests().expect("Failed to set the cursor"); + let cookie = self.xcb.xcb_change_window_attributes( + self.c, + window, + ffi::XCB_CW_CURSOR, + &cursor as *const _ as _, + ); + if let Err(e) = self.check_cookie(cookie) { + log::error!("Failed to set the cursor: {}", e); + } } } } diff --git a/src/platform_impl/linux/x11/util/format.rs b/src/platform_impl/linux/x11/util/format.rs deleted file mode 100644 index 1ab5e01f7e..0000000000 --- a/src/platform_impl/linux/x11/util/format.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::{fmt::Debug, mem, os::raw::*}; - -// This isn't actually the number of the bits in the format. -// X11 does a match on this value to determine which type to call sizeof on. -// Thus, we use 32 for c_long, since 32 maps to c_long which maps to 64. -// ...if that sounds confusing, then you know why this enum is here. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum Format { - Char = 8, - Short = 16, - Long = 32, -} - -impl Format { - pub fn from_format(format: usize) -> Option { - match format { - 8 => Some(Format::Char), - 16 => Some(Format::Short), - 32 => Some(Format::Long), - _ => None, - } - } - - pub fn get_actual_size(&self) -> usize { - match self { - &Format::Char => mem::size_of::(), - &Format::Short => mem::size_of::(), - &Format::Long => mem::size_of::(), - } - } -} - -pub trait Formattable: Debug + Clone + Copy + PartialEq + PartialOrd { - const FORMAT: Format; -} - -// You might be surprised by the absence of c_int, but not as surprised as X11 would be by the presence of it. -impl Formattable for c_schar { - const FORMAT: Format = Format::Char; -} -impl Formattable for c_uchar { - const FORMAT: Format = Format::Char; -} -impl Formattable for c_short { - const FORMAT: Format = Format::Short; -} -impl Formattable for c_ushort { - const FORMAT: Format = Format::Short; -} -impl Formattable for c_long { - const FORMAT: Format = Format::Long; -} -impl Formattable for c_ulong { - const FORMAT: Format = Format::Long; -} diff --git a/src/platform_impl/linux/x11/util/geometry.rs b/src/platform_impl/linux/x11/util/geometry.rs index d8f466fc2a..b8d89293e6 100644 --- a/src/platform_impl/linux/x11/util/geometry.rs +++ b/src/platform_impl/linux/x11/util/geometry.rs @@ -1,3 +1,5 @@ +use crate::platform_impl::x11::xdisplay::Screen; +use crate::platform_impl::x11::UnownedWindow; use std::cmp; use super::*; @@ -42,41 +44,41 @@ impl AaRect { #[derive(Debug, Default)] pub struct TranslatedCoords { - pub x_rel_root: c_int, - pub y_rel_root: c_int, - pub child: ffi::Window, + pub x_rel_root: i16, + pub y_rel_root: i16, + pub child: ffi::xcb_window_t, } #[derive(Debug, Default)] pub struct Geometry { - pub root: ffi::Window, + pub root: ffi::xcb_window_t, // If you want positions relative to the root window, use translate_coords. // Note that the overwhelming majority of window managers are reparenting WMs, thus the window // ID we get from window creation is for a nested window used as the window's client area. If // you call get_geometry with that window ID, then you'll get the position of that client area // window relative to the parent it's nested in (the frame), which isn't helpful if you want // to know the frame position. - pub x_rel_parent: c_int, - pub y_rel_parent: c_int, + pub x_rel_parent: i16, + pub y_rel_parent: i16, // In that same case, this will give you client area size. - pub width: c_uint, - pub height: c_uint, + pub width: u16, + pub height: u16, // xmonad and dwm were the only WMs tested that use the border return at all. // The majority of WMs seem to simply fill it with 0 unconditionally. - pub border: c_uint, - pub depth: c_uint, + pub border: u16, + pub depth: u8, } #[derive(Debug, Clone)] pub struct FrameExtents { - pub left: c_ulong, - pub right: c_ulong, - pub top: c_ulong, - pub bottom: c_ulong, + pub left: u32, + pub right: u32, + pub top: u32, + pub bottom: u32, } impl FrameExtents { - pub fn new(left: c_ulong, right: c_ulong, top: c_ulong, bottom: c_ulong) -> Self { + pub fn new(left: u32, right: u32, top: u32, bottom: u32) -> Self { FrameExtents { left, right, @@ -85,7 +87,7 @@ impl FrameExtents { } } - pub fn from_border(border: c_ulong) -> Self { + pub fn from_border(border: u32) -> Self { Self::new(border, border, border, border) } } @@ -144,62 +146,60 @@ impl XConnection { // This is adequate for inner_position pub fn translate_coords( &self, - window: ffi::Window, - root: ffi::Window, - ) -> Result { - let mut coords = TranslatedCoords::default(); - + window: ffi::xcb_window_t, + root: ffi::xcb_window_t, + ) -> Result { unsafe { - (self.xlib.XTranslateCoordinates)( - self.display, - window, - root, - 0, - 0, - &mut coords.x_rel_root, - &mut coords.y_rel_root, - &mut coords.child, + let mut err = ptr::null_mut(); + let reply = self.xcb.xcb_translate_coordinates_reply( + self.c, + self.xcb + .xcb_translate_coordinates(self.c, window, root, 0, 0), + &mut err, ); + let reply = self.check(reply, err)?; + Ok(TranslatedCoords { + x_rel_root: reply.dst_x, + y_rel_root: reply.dst_y, + child: reply.child, + }) } - - self.check_errors()?; - Ok(coords) } // This is adequate for inner_size - pub fn get_geometry(&self, window: ffi::Window) -> Result { - let mut geometry = Geometry::default(); - - let _status = unsafe { - (self.xlib.XGetGeometry)( - self.display, - window, - &mut geometry.root, - &mut geometry.x_rel_parent, - &mut geometry.y_rel_parent, - &mut geometry.width, - &mut geometry.height, - &mut geometry.border, - &mut geometry.depth, - ) - }; - - self.check_errors()?; - Ok(geometry) + pub fn get_geometry(&self, window: ffi::xcb_window_t) -> Result { + unsafe { + let mut err = ptr::null_mut(); + let reply = self.xcb.xcb_get_geometry_reply( + self.c, + self.xcb.xcb_get_geometry(self.c, window), + &mut err, + ); + let reply = self.check(reply, err)?; + Ok(Geometry { + root: reply.root, + x_rel_parent: reply.x, + y_rel_parent: reply.y, + width: reply.width, + height: reply.height, + border: reply.border_width, + depth: reply.depth, + }) + } } - fn get_frame_extents(&self, window: ffi::Window) -> Option { - let extents_atom = unsafe { self.get_atom_unchecked(b"_NET_FRAME_EXTENTS\0") }; + fn get_frame_extents(&self, window: &UnownedWindow) -> Option { + let extents_atom = self.get_atom("_NET_FRAME_EXTENTS"); - if !hint_is_supported(extents_atom) { + if !window.screen.hint_is_supported(extents_atom) { return None; } // Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't // support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to // be unsupported by many smaller WMs. - let extents: Option> = self - .get_property(window, extents_atom, ffi::XA_CARDINAL) + let extents: Option> = self + .get_property(window.xwindow, extents_atom, ffi::XCB_ATOM_CARDINAL) .ok(); extents.and_then(|extents| { @@ -216,52 +216,37 @@ impl XConnection { }) } - pub fn is_top_level(&self, window: ffi::Window, root: ffi::Window) -> Option { - let client_list_atom = unsafe { self.get_atom_unchecked(b"_NET_CLIENT_LIST\0") }; + pub fn is_top_level(&self, screen: &Screen, window: ffi::xcb_window_t) -> Option { + let client_list_atom = self.get_atom("_NET_CLIENT_LIST"); - if !hint_is_supported(client_list_atom) { + if !screen.hint_is_supported(client_list_atom) { return None; } - let client_list: Option> = self - .get_property(root, client_list_atom, ffi::XA_WINDOW) + let client_list: Option> = self + .get_property(screen.root, client_list_atom, ffi::XCB_ATOM_WINDOW) .ok(); client_list.map(|client_list| client_list.contains(&window)) } - fn get_parent_window(&self, window: ffi::Window) -> Result { - let parent = unsafe { - let mut root = 0; - let mut parent = 0; - let mut children: *mut ffi::Window = ptr::null_mut(); - let mut nchildren = 0; - - // What's filled into `parent` if `window` is the root window? - let _status = (self.xlib.XQueryTree)( - self.display, - window, - &mut root, - &mut parent, - &mut children, - &mut nchildren, + fn get_parent_window(&self, window: ffi::xcb_window_t) -> Result { + unsafe { + let mut err = ptr::null_mut(); + let reply = self.xcb.xcb_query_tree_reply( + self.c, + self.xcb.xcb_query_tree(self.c, window), + &mut err, ); - - // The list of children isn't used - if children != ptr::null_mut() { - (self.xlib.XFree)(children as *mut _); - } - - parent - }; - self.check_errors().map(|_| parent) + Ok(self.check(reply, err)?.parent) + } } fn climb_hierarchy( &self, - window: ffi::Window, - root: ffi::Window, - ) -> Result { + window: ffi::xcb_window_t, + root: ffi::xcb_window_t, + ) -> Result { let mut outer_window = window; loop { let candidate = self.get_parent_window(outer_window)?; @@ -273,11 +258,7 @@ impl XConnection { Ok(outer_window) } - pub fn get_frame_extents_heuristic( - &self, - window: ffi::Window, - root: ffi::Window, - ) -> FrameExtentsHeuristic { + pub fn get_frame_extents_heuristic(&self, window: &UnownedWindow) -> FrameExtentsHeuristic { use self::FrameExtentsHeuristicPath::*; // Position relative to root window. @@ -286,14 +267,14 @@ impl XConnection { // that, fullscreen windows often aren't nested. let (inner_y_rel_root, child) = { let coords = self - .translate_coords(window, root) + .translate_coords(window.xwindow, window.screen.root) .expect("Failed to translate window coordinates"); (coords.y_rel_root, coords.child) }; let (width, height, border) = { let inner_geometry = self - .get_geometry(window) + .get_geometry(window.xwindow) .expect("Failed to get inner window geometry"); ( inner_geometry.width, @@ -307,7 +288,8 @@ impl XConnection { // when y is on the range [0, 2] and if the window has been unfocused since being // undecorated (or was undecorated upon construction), the first condition is true, // requiring us to rely on the second condition. - let nested = !(window == child || self.is_top_level(child, root) == Some(true)); + let nested = + !(window.xwindow == child || self.is_top_level(&window.screen, child) == Some(true)); // Hopefully the WM supports EWMH, allowing us to get exact info on the window frames. if let Some(mut frame_extents) = self.get_frame_extents(window) { @@ -346,7 +328,7 @@ impl XConnection { // just climb up the hierarchy and get the geometry of the outermost window we're // nested in. let outer_window = self - .climb_hierarchy(window, root) + .climb_hierarchy(window.xwindow, window.screen.root) .expect("Failed to climb window hierarchy"); let (outer_y, outer_width, outer_height) = { let outer_geometry = self @@ -363,7 +345,7 @@ impl XConnection { // area, we can figure out what's in between. let diff_x = outer_width.saturating_sub(width); let diff_y = outer_height.saturating_sub(height); - let offset_y = inner_y_rel_root.saturating_sub(outer_y) as c_uint; + let offset_y = inner_y_rel_root.saturating_sub(outer_y) as u16; let left = diff_x / 2; let right = left; diff --git a/src/platform_impl/linux/x11/util/hint.rs b/src/platform_impl/linux/x11/util/hint.rs index 222d81748e..5f9cc82601 100644 --- a/src/platform_impl/linux/x11/util/hint.rs +++ b/src/platform_impl/linux/x11/util/hint.rs @@ -1,7 +1,10 @@ -use std::slice; use std::sync::Arc; use super::*; +use std::convert::TryInto; +use thiserror::Error; +use xcb_dl_util::hint::{XcbHints, XcbHintsError, XcbSizeHints, XcbSizeHintsError}; +use xcb_dl_util::property::XcbGetPropertyError; #[derive(Debug)] #[allow(dead_code)] @@ -71,25 +74,25 @@ impl Default for WindowType { } impl WindowType { - pub(crate) fn as_atom(&self, xconn: &Arc) -> ffi::Atom { + pub(crate) fn as_atom(&self, xconn: &Arc) -> ffi::xcb_atom_t { use self::WindowType::*; - let atom_name: &[u8] = match *self { - Desktop => b"_NET_WM_WINDOW_TYPE_DESKTOP\0", - Dock => b"_NET_WM_WINDOW_TYPE_DOCK\0", - Toolbar => b"_NET_WM_WINDOW_TYPE_TOOLBAR\0", - Menu => b"_NET_WM_WINDOW_TYPE_MENU\0", - Utility => b"_NET_WM_WINDOW_TYPE_UTILITY\0", - Splash => b"_NET_WM_WINDOW_TYPE_SPLASH\0", - Dialog => b"_NET_WM_WINDOW_TYPE_DIALOG\0", - DropdownMenu => b"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0", - PopupMenu => b"_NET_WM_WINDOW_TYPE_POPUP_MENU\0", - Tooltip => b"_NET_WM_WINDOW_TYPE_TOOLTIP\0", - Notification => b"_NET_WM_WINDOW_TYPE_NOTIFICATION\0", - Combo => b"_NET_WM_WINDOW_TYPE_COMBO\0", - Dnd => b"_NET_WM_WINDOW_TYPE_DND\0", - Normal => b"_NET_WM_WINDOW_TYPE_NORMAL\0", + let atom_name: &str = match *self { + Desktop => "_NET_WM_WINDOW_TYPE_DESKTOP", + Dock => "_NET_WM_WINDOW_TYPE_DOCK", + Toolbar => "_NET_WM_WINDOW_TYPE_TOOLBAR", + Menu => "_NET_WM_WINDOW_TYPE_MENU", + Utility => "_NET_WM_WINDOW_TYPE_UTILITY", + Splash => "_NET_WM_WINDOW_TYPE_SPLASH", + Dialog => "_NET_WM_WINDOW_TYPE_DIALOG", + DropdownMenu => "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", + PopupMenu => "_NET_WM_WINDOW_TYPE_POPUP_MENU", + Tooltip => "_NET_WM_WINDOW_TYPE_TOOLTIP", + Notification => "_NET_WM_WINDOW_TYPE_NOTIFICATION", + Combo => "_NET_WM_WINDOW_TYPE_COMBO", + Dnd => "_NET_WM_WINDOW_TYPE_DND", + Normal => "_NET_WM_WINDOW_TYPE_NORMAL", }; - unsafe { xconn.get_atom_unchecked(atom_name) } + xconn.get_atom(atom_name) } } @@ -97,30 +100,27 @@ pub struct MotifHints { hints: MwmHints, } -#[repr(C)] struct MwmHints { - flags: c_ulong, - functions: c_ulong, - decorations: c_ulong, - input_mode: c_long, - status: c_ulong, + flags: u32, + functions: u32, + decorations: u32, + input_mode: u32, + status: u32, } #[allow(dead_code)] mod mwm { - use libc::c_ulong; - // Motif WM hints are obsolete, but still widely supported. // https://stackoverflow.com/a/1909708 - pub const MWM_HINTS_FUNCTIONS: c_ulong = 1 << 0; - pub const MWM_HINTS_DECORATIONS: c_ulong = 1 << 1; - - pub const MWM_FUNC_ALL: c_ulong = 1 << 0; - pub const MWM_FUNC_RESIZE: c_ulong = 1 << 1; - pub const MWM_FUNC_MOVE: c_ulong = 1 << 2; - pub const MWM_FUNC_MINIMIZE: c_ulong = 1 << 3; - pub const MWM_FUNC_MAXIMIZE: c_ulong = 1 << 4; - pub const MWM_FUNC_CLOSE: c_ulong = 1 << 5; + pub const MWM_HINTS_FUNCTIONS: u32 = 1 << 0; + pub const MWM_HINTS_DECORATIONS: u32 = 1 << 1; + + pub const MWM_FUNC_ALL: u32 = 1 << 0; + pub const MWM_FUNC_RESIZE: u32 = 1 << 1; + pub const MWM_FUNC_MOVE: u32 = 1 << 2; + pub const MWM_FUNC_MINIMIZE: u32 = 1 << 3; + pub const MWM_FUNC_MAXIMIZE: u32 = 1 << 4; + pub const MWM_FUNC_CLOSE: u32 = 1 << 5; } impl MotifHints { @@ -138,7 +138,7 @@ impl MotifHints { pub fn set_decorations(&mut self, decorations: bool) { self.hints.flags |= mwm::MWM_HINTS_DECORATIONS; - self.hints.decorations = decorations as c_ulong; + self.hints.decorations = decorations as u32; } pub fn set_maximizable(&mut self, maximizable: bool) { @@ -149,7 +149,7 @@ impl MotifHints { } } - fn add_func(&mut self, func: c_ulong) { + fn add_func(&mut self, func: u32) { if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS != 0 { if self.hints.functions & mwm::MWM_FUNC_ALL != 0 { self.hints.functions &= !func; @@ -159,7 +159,7 @@ impl MotifHints { } } - fn remove_func(&mut self, func: c_ulong) { + fn remove_func(&mut self, func: u32) { if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS == 0 { self.hints.flags |= mwm::MWM_HINTS_FUNCTIONS; self.hints.functions = mwm::MWM_FUNC_ALL; @@ -174,168 +174,103 @@ impl MotifHints { } impl MwmHints { - fn as_slice(&self) -> &[c_ulong] { - unsafe { slice::from_raw_parts(self as *const _ as *const c_ulong, 5) } + fn as_array(&self) -> [u32; 5] { + [ + self.flags, + self.functions, + self.decorations, + self.input_mode, + self.status, + ] } } -pub struct NormalHints<'a> { - size_hints: XSmartPointer<'a, ffi::XSizeHints>, -} - -impl<'a> NormalHints<'a> { - pub fn new(xconn: &'a XConnection) -> Self { - NormalHints { - size_hints: xconn.alloc_size_hints(), - } - } - - pub fn get_position(&self) -> Option<(i32, i32)> { - if has_flag(self.size_hints.flags, ffi::PPosition) { - Some((self.size_hints.x as i32, self.size_hints.y as i32)) - } else { - None - } - } - - pub fn set_position(&mut self, position: Option<(i32, i32)>) { - if let Some((x, y)) = position { - self.size_hints.flags |= ffi::PPosition; - self.size_hints.x = x as c_int; - self.size_hints.y = y as c_int; - } else { - self.size_hints.flags &= !ffi::PPosition; - } - } - - // WARNING: This hint is obsolete - pub fn set_size(&mut self, size: Option<(u32, u32)>) { - if let Some((width, height)) = size { - self.size_hints.flags |= ffi::PSize; - self.size_hints.width = width as c_int; - self.size_hints.height = height as c_int; - } else { - self.size_hints.flags &= !ffi::PSize; - } - } - - pub fn set_max_size(&mut self, max_size: Option<(u32, u32)>) { - if let Some((max_width, max_height)) = max_size { - self.size_hints.flags |= ffi::PMaxSize; - self.size_hints.max_width = max_width as c_int; - self.size_hints.max_height = max_height as c_int; - } else { - self.size_hints.flags &= !ffi::PMaxSize; - } - } - - pub fn set_min_size(&mut self, min_size: Option<(u32, u32)>) { - if let Some((min_width, min_height)) = min_size { - self.size_hints.flags |= ffi::PMinSize; - self.size_hints.min_width = min_width as c_int; - self.size_hints.min_height = min_height as c_int; - } else { - self.size_hints.flags &= !ffi::PMinSize; - } - } - - pub fn set_resize_increments(&mut self, resize_increments: Option<(u32, u32)>) { - if let Some((width_inc, height_inc)) = resize_increments { - self.size_hints.flags |= ffi::PResizeInc; - self.size_hints.width_inc = width_inc as c_int; - self.size_hints.height_inc = height_inc as c_int; - } else { - self.size_hints.flags &= !ffi::PResizeInc; - } - } - - pub fn set_base_size(&mut self, base_size: Option<(u32, u32)>) { - if let Some((base_width, base_height)) = base_size { - self.size_hints.flags |= ffi::PBaseSize; - self.size_hints.base_width = base_width as c_int; - self.size_hints.base_height = base_height as c_int; - } else { - self.size_hints.flags &= !ffi::PBaseSize; - } - } +#[derive(Debug, Error)] +pub enum HintsError { + #[error("Could not convert the property contents to XcbHints: {0}")] + Contents(#[from] XcbHintsError), + #[error("Could not convert the property contents to XcbSizeHints: {0}")] + SizeContents(#[from] XcbSizeHintsError), + #[error("Could not retrieve the property: {0}")] + Property(#[from] XcbGetPropertyError), + #[error("An xcb error occurred: {0}")] + Xcb(#[from] XcbError), } impl XConnection { - pub fn get_wm_hints( - &self, - window: ffi::Window, - ) -> Result, XError> { - let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) }; - self.check_errors()?; - let wm_hints = if wm_hints.is_null() { - self.alloc_wm_hints() - } else { - XSmartPointer::new(self, wm_hints).unwrap() + pub fn get_wm_hints(&self, window: ffi::xcb_window_t) -> Result { + let prop = self.get_property::(window, ffi::XCB_ATOM_WM_HINTS, ffi::XCB_ATOM_WM_HINTS); + let bytes = match prop { + Ok(b) => b, + Err(XcbGetPropertyError::Unset) => return Ok(XcbHints::default()), + Err(e) => return Err(e.into()), }; - Ok(wm_hints) + Ok((&*bytes).try_into()?) } - pub fn set_wm_hints( - &self, - window: ffi::Window, - wm_hints: XSmartPointer<'_, ffi::XWMHints>, - ) -> Flusher<'_> { - unsafe { - (self.xlib.XSetWMHints)(self.display, window, wm_hints.ptr); - } - Flusher::new(self) + pub fn set_wm_hints(&self, window: ffi::xcb_window_t, wm_hints: XcbHints) -> XcbPendingCommand { + self.change_property( + window, + ffi::XCB_ATOM_WM_HINTS, + ffi::XCB_ATOM_WM_HINTS, + PropMode::Replace, + wm_hints.as_bytes(), + ) } - pub fn get_normal_hints(&self, window: ffi::Window) -> Result, XError> { - let size_hints = self.alloc_size_hints(); - let mut supplied_by_user = MaybeUninit::uninit(); - unsafe { - (self.xlib.XGetWMNormalHints)( - self.display, - window, - size_hints.ptr, - supplied_by_user.as_mut_ptr(), - ); - } - self.check_errors().map(|_| NormalHints { size_hints }) + pub fn get_normal_hints(&self, window: ffi::xcb_window_t) -> Result { + let bytes = self.get_property::( + window, + ffi::XCB_ATOM_WM_NORMAL_HINTS, + ffi::XCB_ATOM_WM_SIZE_HINTS, + )?; + Ok((&*bytes).try_into()?) } pub fn set_normal_hints( &self, - window: ffi::Window, - normal_hints: NormalHints<'_>, - ) -> Flusher<'_> { - unsafe { - (self.xlib.XSetWMNormalHints)(self.display, window, normal_hints.size_hints.ptr); - } - Flusher::new(self) + window: ffi::xcb_window_t, + normal_hints: XcbSizeHints, + ) -> XcbPendingCommand { + self.change_property( + window, + ffi::XCB_ATOM_WM_NORMAL_HINTS, + ffi::XCB_ATOM_WM_SIZE_HINTS, + PropMode::Replace, + normal_hints.as_bytes(), + ) } - pub fn get_motif_hints(&self, window: ffi::Window) -> MotifHints { - let motif_hints = unsafe { self.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") }; + pub fn get_motif_hints(&self, window: ffi::xcb_window_t) -> MotifHints { + let motif_hints = self.get_atom("_MOTIF_WM_HINTS"); let mut hints = MotifHints::new(); - if let Ok(props) = self.get_property::(window, motif_hints, motif_hints) { + if let Ok(props) = self.get_property::(window, motif_hints, motif_hints) { hints.hints.flags = props.get(0).cloned().unwrap_or(0); hints.hints.functions = props.get(1).cloned().unwrap_or(0); hints.hints.decorations = props.get(2).cloned().unwrap_or(0); - hints.hints.input_mode = props.get(3).cloned().unwrap_or(0) as c_long; + hints.hints.input_mode = props.get(3).cloned().unwrap_or(0); hints.hints.status = props.get(4).cloned().unwrap_or(0); } hints } - pub fn set_motif_hints(&self, window: ffi::Window, hints: &MotifHints) -> Flusher<'_> { - let motif_hints = unsafe { self.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") }; + pub fn set_motif_hints( + &self, + window: ffi::xcb_window_t, + hints: &MotifHints, + ) -> XcbPendingCommand { + let motif_hints = self.get_atom("_MOTIF_WM_HINTS"); self.change_property( window, motif_hints, motif_hints, PropMode::Replace, - hints.hints.as_slice(), + &hints.hints.as_array(), ) + .into() } } diff --git a/src/platform_impl/linux/x11/util/icon.rs b/src/platform_impl/linux/x11/util/icon.rs index eb13d48b5b..b1da46818f 100644 --- a/src/platform_impl/linux/x11/util/icon.rs +++ b/src/platform_impl/linux/x11/util/icon.rs @@ -4,7 +4,7 @@ use crate::icon::{Icon, Pixel, PIXEL_SIZE}; impl Pixel { pub fn to_packed_argb(&self) -> Cardinal { let mut cardinal = 0; - assert!(CARDINAL_SIZE >= PIXEL_SIZE); + assert_eq!(CARDINAL_SIZE, PIXEL_SIZE); let as_bytes = &mut cardinal as *mut _ as *mut u8; unsafe { *as_bytes.offset(0) = self.b; diff --git a/src/platform_impl/linux/x11/util/input.rs b/src/platform_impl/linux/x11/util/input.rs index daef20b69e..c4b0602e45 100644 --- a/src/platform_impl/linux/x11/util/input.rs +++ b/src/platform_impl/linux/x11/util/input.rs @@ -1,125 +1,108 @@ use super::*; +use xcb_dl_util::error::XcbError; +use xcb_dl_util::xcb_box::XcbBox; -pub const VIRTUAL_CORE_POINTER: c_int = 2; - -// NOTE: Some of these fields are not used, but may be of use in the future. -pub struct PointerState<'a> { - xconn: &'a XConnection, - pub root: ffi::Window, - pub child: ffi::Window, - pub root_x: c_double, - pub root_y: c_double, - pub win_x: c_double, - pub win_y: c_double, - buttons: ffi::XIButtonState, - pub relative_to_window: bool, -} - -impl<'a> Drop for PointerState<'a> { - fn drop(&mut self) { - if !self.buttons.mask.is_null() { - unsafe { - // This is why you need to read the docs carefully... - (self.xconn.xlib.XFree)(self.buttons.mask as _); - } - } - } -} +pub const VIRTUAL_CORE_POINTER: ffi::xcb_input_device_id_t = 2; impl XConnection { pub fn select_xinput_events( &self, - window: c_ulong, - device_id: c_int, - mask: i32, - ) -> Flusher<'_> { - let mut event_mask = ffi::XIEventMask { - deviceid: device_id, - mask: &mask as *const _ as *mut c_uchar, - mask_len: mem::size_of_val(&mask) as c_int, - }; + window: ffi::xcb_window_t, + device_id: ffi::xcb_input_device_id_t, + mask: u32, + ) -> XcbPendingCommand { unsafe { - (self.xinput2.XISelectEvents)( - self.display, + xcb_dl_util::input::select_events_checked( + &self.xinput, + self.c, window, - &mut event_mask as *mut ffi::XIEventMask, - 1, // number of masks to read from pointer above - ); + device_id, + [mask], + ) + .into() } - Flusher::new(self) } - pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option> { - let status = unsafe { (self.xlib.XkbSelectEvents)(self.display, device_id, mask, mask) }; - if status == ffi::True { - Some(Flusher::new(self)) - } else { - error!("Could not select XKB events: The XKB extension is not initialized!"); - None + pub fn select_xkb_events( + &self, + device_id: ffi::xcb_input_device_id_t, + events: ffi::xcb_xkb_event_type_t, + ) -> XcbPendingCommand { + unsafe { + self.xkb + .xcb_xkb_select_events_checked( + self.c, + device_id, + events as _, + 0, + events as _, + !0, + !0, + ptr::null(), + ) + .into() } } pub fn select_xkb_event_details( &self, - device_id: c_uint, - event: c_uint, - mask: c_ulong, - ) -> Option> { - let status = unsafe { - (self.xlib.XkbSelectEventDetails)(self.display, device_id, event, mask, mask) - }; - if status == ffi::True { - Some(Flusher::new(self)) - } else { - error!("Could not select XKB events: The XKB extension is not initialized!"); - None + device_id: ffi::xcb_input_device_id_t, + events: ffi::xcb_xkb_event_type_t, + details: &ffi::xcb_xkb_select_events_details_t, + ) -> XcbPendingCommand { + unsafe { + self.xkb + .xcb_xkb_select_events_aux_checked( + self.c, + device_id, + events as _, + 0, + 0, + !0, + !0, + details, + ) + .into() } } pub fn query_pointer( &self, - window: ffi::Window, - device_id: c_int, - ) -> Result, XError> { + window: ffi::xcb_window_t, + device_id: ffi::xcb_input_device_id_t, + ) -> Result, XcbError> { unsafe { - let mut root = 0; - let mut child = 0; - let mut root_x = 0.0; - let mut root_y = 0.0; - let mut win_x = 0.0; - let mut win_y = 0.0; - let mut buttons = Default::default(); - let mut modifiers = Default::default(); - let mut group = Default::default(); + let mut err = ptr::null_mut(); + let reply = self.xinput.xcb_input_xi_query_pointer_reply( + self.c, + self.xinput + .xcb_input_xi_query_pointer(self.c, window, device_id), + &mut err, + ); + self.check(reply, err) + } + } - let relative_to_window = (self.xinput2.XIQueryPointer)( - self.display, + pub fn make_auto_repeat_detectable( + &self, + device_id: ffi::xcb_input_device_id_t, + ) -> Result { + unsafe { + let cookie = self.xkb.xcb_xkb_per_client_flags( + self.c, device_id, - window, - &mut root, - &mut child, - &mut root_x, - &mut root_y, - &mut win_x, - &mut win_y, - &mut buttons, - &mut modifiers, - &mut group, - ) == ffi::True; - - self.check_errors()?; - - Ok(PointerState { - xconn: self, - root, - child, - root_x, - root_y, - win_x, - win_y, - buttons, - relative_to_window, - }) + ffi::XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, + ffi::XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, + 0, + 0, + 0, + ); + let mut err = ptr::null_mut(); + let reply = self + .xkb + .xcb_xkb_per_client_flags_reply(self.c, cookie, &mut err); + let reply = self.check(reply, err)?; + Ok(reply.supported & ffi::XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT != 0) } } } diff --git a/src/platform_impl/linux/x11/util/memory.rs b/src/platform_impl/linux/x11/util/memory.rs deleted file mode 100644 index c85cc37139..0000000000 --- a/src/platform_impl/linux/x11/util/memory.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::ops::{Deref, DerefMut}; - -use super::*; - -pub struct XSmartPointer<'a, T> { - xconn: &'a XConnection, - pub ptr: *mut T, -} - -impl<'a, T> XSmartPointer<'a, T> { - // You're responsible for only passing things to this that should be XFree'd. - // Returns None if ptr is null. - pub fn new(xconn: &'a XConnection, ptr: *mut T) -> Option { - if !ptr.is_null() { - Some(XSmartPointer { xconn, ptr }) - } else { - None - } - } -} - -impl<'a, T> Deref for XSmartPointer<'a, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.ptr } - } -} - -impl<'a, T> DerefMut for XSmartPointer<'a, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.ptr } - } -} - -impl<'a, T> Drop for XSmartPointer<'a, T> { - fn drop(&mut self) { - unsafe { - (self.xconn.xlib.XFree)(self.ptr as *mut _); - } - } -} - -impl XConnection { - pub fn alloc_class_hint(&self) -> XSmartPointer<'_, ffi::XClassHint> { - XSmartPointer::new(self, unsafe { (self.xlib.XAllocClassHint)() }) - .expect("`XAllocClassHint` returned null; out of memory") - } - - pub fn alloc_size_hints(&self) -> XSmartPointer<'_, ffi::XSizeHints> { - XSmartPointer::new(self, unsafe { (self.xlib.XAllocSizeHints)() }) - .expect("`XAllocSizeHints` returned null; out of memory") - } - - pub fn alloc_wm_hints(&self) -> XSmartPointer<'_, ffi::XWMHints> { - XSmartPointer::new(self, unsafe { (self.xlib.XAllocWMHints)() }) - .expect("`XAllocWMHints` returned null; out of memory") - } -} diff --git a/src/platform_impl/linux/x11/util/mod.rs b/src/platform_impl/linux/x11/util/mod.rs index b7ea4e7690..9efab6a936 100644 --- a/src/platform_impl/linux/x11/util/mod.rs +++ b/src/platform_impl/linux/x11/util/mod.rs @@ -4,29 +4,29 @@ mod atom; mod client_msg; mod cursor; -mod format; mod geometry; mod hint; mod icon; mod input; -mod memory; +mod queue; mod randr; mod window_property; mod wm; pub use self::{ - atom::*, client_msg::*, format::*, geometry::*, hint::*, icon::*, input::*, memory::*, - randr::*, window_property::*, wm::*, + atom::*, client_msg::*, geometry::*, hint::*, icon::*, input::*, queue::*, randr::*, + window_property::*, wm::*, }; use std::{ - mem::{self, MaybeUninit}, - ops::BitAnd, - os::raw::*, + mem::{self}, ptr, }; -use super::{ffi, XConnection, XError}; +use super::{ffi, XConnection}; +use xcb_dl_util::error::XcbError; +use xcb_dl_util::void::{XcbPendingCommand, XcbPendingCommands}; +use xcb_dl_util::xcb_box::XcbBox; pub fn maybe_change(field: &mut Option, value: T) -> bool { let wrapped = Some(value); @@ -38,58 +38,50 @@ pub fn maybe_change(field: &mut Option, value: T) -> bool { } } -pub fn has_flag(bitset: T, flag: T) -> bool -where - T: Copy + PartialEq + BitAnd, -{ - bitset & flag == flag +pub fn fp1616_to_f64(x: ffi::xcb_input_fp1616_t) -> f64 { + (x as f64 * 1.0) / ((1 << 16) as f64) } -#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."] -pub struct Flusher<'a> { - xconn: &'a XConnection, +pub fn fp3232_to_f64(x: ffi::xcb_input_fp3232_t) -> f64 { + x.integral as f64 + (x.frac as f64) / ((1u64 << 32) as f64) } -impl<'a> Flusher<'a> { - pub fn new(xconn: &'a XConnection) -> Self { - Flusher { xconn } +impl XConnection { + pub fn check_cookie(&self, cookie: ffi::xcb_void_cookie_t) -> Result<(), XcbError> { + unsafe { self.errors.check_cookie(&self.xcb, cookie) } } - // "I want this request sent now!" - pub fn flush(self) -> Result<(), XError> { - self.xconn.flush_requests() + pub unsafe fn check( + &self, + val: *mut T, + err: *mut ffi::xcb_generic_error_t, + ) -> Result, XcbError> { + self.errors.check(&self.xcb, val, err) } - // "I want the response now too!" - pub fn sync(self) -> Result<(), XError> { - self.xconn.sync_with_server() + pub fn check_pending1(&self, pending: XcbPendingCommand) -> Result<(), XcbError> { + unsafe { pending.check(&self.xcb, &self.errors) } } - // "I'm aware that this request hasn't been sent, and I'm okay with waiting." - pub fn queue(self) {} -} + pub fn check_pending(&self, pending: XcbPendingCommands) -> Result<(), XcbError> { + unsafe { pending.check(&self.xcb, &self.errors) } + } -impl XConnection { - // This is impoartant, so pay attention! - // Xlib has an output buffer, and tries to hide the async nature of X from you. - // This buffer contains the requests you make, and is flushed under various circumstances: - // 1. `XPending`, `XNextEvent`, and `XWindowEvent` flush "as needed" - // 2. `XFlush` explicitly flushes - // 3. `XSync` flushes and blocks until all requests are responded to - // 4. Calls that have a return dependent on a response (i.e. `XGetWindowProperty`) sync internally. - // When in doubt, check the X11 source; if a function calls `_XReply`, it flushes and waits. - // All util functions that abstract an async function will return a `Flusher`. - pub fn flush_requests(&self) -> Result<(), XError> { - unsafe { (self.xlib.XFlush)(self.display) }; - //println!("XFlush"); - // This isn't necessarily a useful time to check for errors (since our request hasn't - // necessarily been processed yet) - self.check_errors() + pub fn discard(&self, pending: XcbPendingCommand) { + unsafe { pending.discard(&self.xcb, self.c) } + } + + pub fn flush(&self) -> Result<(), XcbError> { + unsafe { + if self.xcb.xcb_flush(self.c) == 0 { + self.errors.check_connection(&self.xcb) + } else { + Ok(()) + } + } } - pub fn sync_with_server(&self) -> Result<(), XError> { - unsafe { (self.xlib.XSync)(self.display, ffi::False) }; - //println!("XSync"); - self.check_errors() + pub fn generate_id(&self) -> u32 { + unsafe { self.xcb.xcb_generate_id(self.c) } } } diff --git a/src/platform_impl/linux/x11/util/queue.rs b/src/platform_impl/linux/x11/util/queue.rs new file mode 100644 index 0000000000..9d5f3c422e --- /dev/null +++ b/src/platform_impl/linux/x11/util/queue.rs @@ -0,0 +1,42 @@ +use crate::platform_impl::x11::XConnection; +use std::sync::Arc; +use xcb_dl::ffi; +use xcb_dl_util::xcb_box::XcbBox; + +pub struct EventQueue { + xconn: Arc, + pending: Option>, +} + +impl EventQueue { + pub fn new(xconn: &Arc) -> Self { + Self { + xconn: xconn.clone(), + pending: None, + } + } + + pub fn has_pending_events(&mut self) -> bool { + if let Some(event) = self.poll_for_event() { + self.pending = Some(event); + } + self.pending.is_some() + } + + pub fn poll_for_event(&mut self) -> Option> { + if self.pending.is_some() { + return self.pending.take(); + } + unsafe { + let event = self.xconn.xcb.xcb_poll_for_event(self.xconn.c); + if event.is_null() { + if let Err(e) = self.xconn.errors.check_connection(&self.xconn.xcb) { + panic!("The X connection is broken: {}", e); + } + None + } else { + Some(XcbBox::new(event)) + } + } + } +} diff --git a/src/platform_impl/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs index b9258c68f1..72c2e77fed 100644 --- a/src/platform_impl/linux/x11/util/randr.rs +++ b/src/platform_impl/linux/x11/util/randr.rs @@ -1,9 +1,7 @@ -use std::{env, slice, str::FromStr}; +use std::{env, slice, str, str::FromStr}; -use super::{ - ffi::{CurrentTime, RRCrtc, RRMode, Success, XRRCrtcInfo, XRRScreenResources}, - *, -}; +use super::*; +use crate::platform_impl::x11::xdisplay::Screen; use crate::{dpi::validate_scale_factor, platform_impl::platform::x11::VideoMode}; /// Represents values of `WINIT_HIDPI_FACTOR`. @@ -32,44 +30,53 @@ pub fn calc_dpi_factor( impl XConnection { // Retrieve DPI from Xft.dpi property - pub unsafe fn get_xft_dpi(&self) -> Option { - (self.xlib.XrmInitialize)(); - let resource_manager_str = (self.xlib.XResourceManagerString)(self.display); - if resource_manager_str == ptr::null_mut() { - return None; - } - if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() { - let name: &str = "Xft.dpi:\t"; - for pair in res.split("\n") { - if pair.starts_with(&name) { - let res = &pair[name.len()..]; - return f64::from_str(&res).ok(); - } + pub unsafe fn get_xft_dpi(&self, screen: &Screen) -> Option { + let resource_manager_str = self + .get_property::( + screen.root, + ffi::XCB_ATOM_RESOURCE_MANAGER, + ffi::XCB_ATOM_STRING, + ) + .ok()?; + for pair in resource_manager_str.split(|b| *b == b'\n') { + if let Some(res) = pair.strip_prefix(b"Xft.dpi:\t") { + return f64::from_str(str::from_utf8(res).ok()?).ok(); } } None } pub unsafe fn get_output_info( &self, - resources: *mut XRRScreenResources, - crtc: *mut XRRCrtcInfo, - ) -> Option<(String, f64, Vec)> { - let output_info = - (self.xrandr.XRRGetOutputInfo)(self.display, resources, *(*crtc).outputs.offset(0)); - if output_info.is_null() { - // When calling `XRRGetOutputInfo` on a virtual monitor (versus a physical display) - // it's possible for it to return null. - // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816596 - let _ = self.check_errors(); // discard `BadRROutput` error - return None; - } - - let screen = (self.xlib.XDefaultScreen)(self.display); - let bit_depth = (self.xlib.XDefaultDepth)(self.display, screen); + screen: &Screen, + resources: &ffi::xcb_randr_get_screen_resources_reply_t, + crtc: &ffi::xcb_randr_get_crtc_info_reply_t, + ) -> Result)>, XcbError> { + let mut err = ptr::null_mut(); + + let first_output = { + if crtc.num_outputs == 0 { + return Ok(None); + } + *self.randr.xcb_randr_get_crtc_info_outputs(crtc) + }; + let output_info = { + let reply = self.randr.xcb_randr_get_output_info_reply( + self.c, + self.randr + .xcb_randr_get_output_info(self.c, first_output, 0), + &mut err, + ); + self.check(reply, err)? + }; + let output_modes = slice::from_raw_parts( + self.randr.xcb_randr_get_output_info_modes(&*output_info), + output_info.num_modes as _, + ); - let output_modes = - slice::from_raw_parts((*output_info).modes, (*output_info).nmode as usize); - let resource_modes = slice::from_raw_parts((*resources).modes, (*resources).nmode as usize); + let resource_modes = slice::from_raw_parts( + self.randr.xcb_randr_get_screen_resources_modes(resources), + resources.num_modes as _, + ); let modes = resource_modes .iter() @@ -77,8 +84,8 @@ impl XConnection { // modes in the array in XRRScreenResources .filter(|x| output_modes.iter().any(|id| x.id == *id)) .map(|x| { - let refresh_rate = if x.dotClock > 0 && x.hTotal > 0 && x.vTotal > 0 { - x.dotClock as u64 * 1000 / (x.hTotal as u64 * x.vTotal as u64) + let refresh_rate = if x.dot_clock > 0 && x.htotal > 0 && x.vtotal > 0 { + x.dot_clock as u64 * 1000 / (x.htotal as u64 * x.vtotal as u64) } else { 0 }; @@ -86,7 +93,7 @@ impl XConnection { VideoMode { size: (x.width, x.height), refresh_rate: (refresh_rate as f32 / 1000.0).round() as u16, - bit_depth: bit_depth as u16, + bit_depth: screen.root_depth as u16, native_mode: x.id, // This is populated in `MonitorHandle::video_modes` as the // video mode is returned to the user @@ -96,8 +103,8 @@ impl XConnection { .collect(); let name_slice = slice::from_raw_parts( - (*output_info).name as *mut u8, - (*output_info).nameLen as usize, + self.randr.xcb_randr_get_output_info_name(&*output_info), + output_info.name_len as usize, ); let name = String::from_utf8_lossy(name_slice).into(); // Override DPI if `WINIT_X11_SCALE_FACTOR` variable is set @@ -127,11 +134,8 @@ impl XConnection { let scale_factor = match dpi_env { EnvVarDPI::Randr => calc_dpi_factor( - ((*crtc).width as u32, (*crtc).height as u32), - ( - (*output_info).mm_width as u64, - (*output_info).mm_height as u64, - ), + (crtc.width as u32, crtc.height as u32), + (output_info.mm_width as u64, output_info.mm_height as u64), ), EnvVarDPI::Scale(dpi_override) => { if !validate_scale_factor(dpi_override) { @@ -143,78 +147,69 @@ impl XConnection { dpi_override } EnvVarDPI::NotSet => { - if let Some(dpi) = self.get_xft_dpi() { + if let Some(dpi) = self.get_xft_dpi(screen) { dpi / 96. } else { calc_dpi_factor( - ((*crtc).width as u32, (*crtc).height as u32), - ( - (*output_info).mm_width as u64, - (*output_info).mm_height as u64, - ), + (crtc.width as u32, crtc.height as u32), + (output_info.mm_width as u64, output_info.mm_height as u64), ) } } }; - (self.xrandr.XRRFreeOutputInfo)(output_info); - Some((name, scale_factor, modes)) + Ok(Some((name, scale_factor, modes))) } - pub fn set_crtc_config(&self, crtc_id: RRCrtc, mode_id: RRMode) -> Result<(), ()> { + pub fn set_crtc_config( + &self, + crtc_id: ffi::xcb_randr_crtc_t, + mode_id: ffi::xcb_randr_mode_t, + ) -> Result<(), XcbError> { + let info = self.get_crtc_info(crtc_id)?; unsafe { - let mut major = 0; - let mut minor = 0; - (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor); - - let root = (self.xlib.XDefaultRootWindow)(self.display); - let resources = if (major == 1 && minor >= 3) || major > 1 { - (self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root) - } else { - (self.xrandr.XRRGetScreenResources)(self.display, root) - }; - - let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id); - let status = (self.xrandr.XRRSetCrtcConfig)( - self.display, - resources, + let cookie = self.randr.xcb_randr_set_crtc_config( + self.c, crtc_id, - CurrentTime, - (*crtc).x, - (*crtc).y, + ffi::XCB_TIME_CURRENT_TIME, + // See the comment in get_crtc_info. + 0, + info.x, + info.y, mode_id, - (*crtc).rotation, - (*crtc).outputs.offset(0), - 1, + info.rotation, + info.num_outputs as _, + self.randr.xcb_randr_get_crtc_info_outputs(&*info), ); - - (self.xrandr.XRRFreeCrtcInfo)(crtc); - (self.xrandr.XRRFreeScreenResources)(resources); - - if status == Success as i32 { - Ok(()) - } else { - Err(()) - } + let mut err = ptr::null_mut(); + let reply = self + .randr + .xcb_randr_set_crtc_config_reply(self.c, cookie, &mut err); + self.check(reply, err).map(|_| ()) } } - pub fn get_crtc_mode(&self, crtc_id: RRCrtc) -> RRMode { - unsafe { - let mut major = 0; - let mut minor = 0; - (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor); - let root = (self.xlib.XDefaultRootWindow)(self.display); - let resources = if (major == 1 && minor >= 3) || major > 1 { - (self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root) - } else { - (self.xrandr.XRRGetScreenResources)(self.display, root) - }; + pub fn get_crtc_mode( + &self, + crtc_id: ffi::xcb_randr_crtc_t, + ) -> Result { + self.get_crtc_info(crtc_id).map(|i| i.mode) + } - let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id); - let mode = (*crtc).mode; - (self.xrandr.XRRFreeCrtcInfo)(crtc); - (self.xrandr.XRRFreeScreenResources)(resources); - mode + pub fn get_crtc_info( + &self, + crtc_id: ffi::xcb_randr_crtc_t, + ) -> Result, XcbError> { + unsafe { + let mut err = ptr::null_mut(); + let reply = self.randr.xcb_randr_get_crtc_info_reply( + self.c, + // NOTE: The randr spec says that the last argument must be equal to the current + // timestamp on the server but this has never been enforced by the x server. + // Therefore we can save us a call to retrieve that value. + self.randr.xcb_randr_get_crtc_info(self.c, crtc_id, 0), + &mut err, + ); + self.check(reply, err) } } } diff --git a/src/platform_impl/linux/x11/util/window_property.rs b/src/platform_impl/linux/x11/util/window_property.rs index 34977f36d2..e9987699e2 100644 --- a/src/platform_impl/linux/x11/util/window_property.rs +++ b/src/platform_impl/linux/x11/util/window_property.rs @@ -1,142 +1,61 @@ use super::*; +use xcb_dl_util::format::XcbDataType; +use xcb_dl_util::property::XcbGetPropertyError; -pub type Cardinal = c_long; -pub const CARDINAL_SIZE: usize = mem::size_of::(); - -#[derive(Debug, Clone)] -pub enum GetPropertyError { - XError(XError), - TypeMismatch(ffi::Atom), - FormatMismatch(c_int), - NothingAllocated, -} - -impl GetPropertyError { - pub fn is_actual_property_type(&self, t: ffi::Atom) -> bool { - if let GetPropertyError::TypeMismatch(actual_type) = *self { - actual_type == t - } else { - false - } - } -} - -// Number of 32-bit chunks to retrieve per iteration of get_property's inner loop. -// To test if `get_property` works correctly, set this to 1. -const PROPERTY_BUFFER_SIZE: c_long = 1024; // 4k of RAM ought to be enough for anyone! +pub type Cardinal = u32; +pub const CARDINAL_SIZE: usize = mem::size_of::(); #[derive(Debug)] #[allow(dead_code)] +#[repr(u32)] pub enum PropMode { - Replace = ffi::PropModeReplace as isize, - Prepend = ffi::PropModePrepend as isize, - Append = ffi::PropModeAppend as isize, + Replace = ffi::XCB_PROP_MODE_REPLACE, + Prepend = ffi::XCB_PROP_MODE_PREPEND, + Append = ffi::XCB_PROP_MODE_APPEND, } impl XConnection { - pub fn get_property( + pub fn get_property( &self, - window: c_ulong, - property: ffi::Atom, - property_type: ffi::Atom, - ) -> Result, GetPropertyError> { - let mut data = Vec::new(); - let mut offset = 0; - - let mut done = false; - let mut actual_type = 0; - let mut actual_format = 0; - let mut quantity_returned = 0; - let mut bytes_after = 0; - let mut buf: *mut c_uchar = ptr::null_mut(); - - while !done { - unsafe { - (self.xlib.XGetWindowProperty)( - self.display, - window, - property, - // This offset is in terms of 32-bit chunks. - offset, - // This is the quanity of 32-bit chunks to receive at once. - PROPERTY_BUFFER_SIZE, - ffi::False, - property_type, - &mut actual_type, - &mut actual_format, - // This is the quantity of items we retrieved in our format, NOT of 32-bit chunks! - &mut quantity_returned, - // ...and this is a quantity of bytes. So, this function deals in 3 different units. - &mut bytes_after, - &mut buf, - ); - - if let Err(e) = self.check_errors() { - return Err(GetPropertyError::XError(e)); - } - - if actual_type != property_type { - return Err(GetPropertyError::TypeMismatch(actual_type)); - } - - let format_mismatch = Format::from_format(actual_format as _) != Some(T::FORMAT); - if format_mismatch { - return Err(GetPropertyError::FormatMismatch(actual_format)); - } - - if !buf.is_null() { - offset += PROPERTY_BUFFER_SIZE; - let new_data = - std::slice::from_raw_parts(buf as *mut T, quantity_returned as usize); - /*println!( - "XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}", - property, - mem::size_of::() * 8, - data.len(), - offset, - quantity_returned, - new_data, - );*/ - data.extend_from_slice(&new_data); - // Fun fact: XGetWindowProperty allocates one extra byte at the end. - (self.xlib.XFree)(buf as _); // Don't try to access new_data after this. - } else { - return Err(GetPropertyError::NothingAllocated); - } - - done = bytes_after == 0; - } + window: ffi::xcb_window_t, + property: ffi::xcb_atom_t, + property_type: ffi::xcb_atom_t, + ) -> Result, XcbGetPropertyError> { + const STEP: u32 = 256 * 1024; + unsafe { + xcb_dl_util::property::get_property( + &self.xcb, + &self.errors, + window, + property, + property_type, + false, + STEP, + ) } - - Ok(data) } - pub fn change_property<'a, T: Formattable>( - &'a self, - window: c_ulong, - property: ffi::Atom, - property_type: ffi::Atom, + pub fn change_property( + &self, + window: ffi::xcb_window_t, + property: ffi::xcb_atom_t, + property_type: ffi::xcb_atom_t, mode: PropMode, new_value: &[T], - ) -> Flusher<'a> { - debug_assert_eq!(mem::size_of::(), T::FORMAT.get_actual_size()); + ) -> XcbPendingCommand { unsafe { - (self.xlib.XChangeProperty)( - self.display, - window, - property, - property_type, - T::FORMAT as c_int, - mode as c_int, - new_value.as_ptr() as *const c_uchar, - new_value.len() as c_int, - ); + self.xcb + .xcb_change_property_checked( + self.c, + mode as u8, + window, + property, + property_type, + T::XCB_BITS, + new_value.len() as _, + new_value.as_ptr() as _, + ) + .into() } - /*println!( - "XChangeProperty prop:{:?} val:{:?}", - property, - new_value, - );*/ - Flusher::new(self) } } diff --git a/src/platform_impl/linux/x11/util/wm.rs b/src/platform_impl/linux/x11/util/wm.rs index 693bc5c02d..eb7debcc23 100644 --- a/src/platform_impl/linux/x11/util/wm.rs +++ b/src/platform_impl/linux/x11/util/wm.rs @@ -1,40 +1,23 @@ -use parking_lot::Mutex; - use super::*; - -// This info is global to the window manager. -lazy_static! { - static ref SUPPORTED_HINTS: Mutex> = Mutex::new(Vec::with_capacity(0)); - static ref WM_NAME: Mutex> = Mutex::new(None); -} - -pub fn hint_is_supported(hint: ffi::Atom) -> bool { - (*SUPPORTED_HINTS.lock()).contains(&hint) -} - -pub fn wm_name_is_one_of(names: &[&str]) -> bool { - if let Some(ref name) = *WM_NAME.lock() { - names.contains(&name.as_str()) - } else { - false - } -} +use xcb_dl_util::property::XcbGetPropertyError; impl XConnection { - pub fn update_cached_wm_info(&self, root: ffi::Window) { - *SUPPORTED_HINTS.lock() = self.get_supported_hints(root); - *WM_NAME.lock() = self.get_wm_name(root); + pub fn update_cached_wm_info(&self) { + for screen in &self.screens { + *screen.supported_hints.lock() = self.get_supported_hints(screen.root); + *screen.wm_name.lock() = self.get_wm_name(screen.root); + } } - fn get_supported_hints(&self, root: ffi::Window) -> Vec { - let supported_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTED\0") }; - self.get_property(root, supported_atom, ffi::XA_ATOM) + fn get_supported_hints(&self, root: ffi::xcb_window_t) -> Vec { + let supported_atom = self.get_atom("_NET_SUPPORTED"); + self.get_property(root, supported_atom, ffi::XCB_ATOM_ATOM) .unwrap_or_else(|_| Vec::with_capacity(0)) } - fn get_wm_name(&self, root: ffi::Window) -> Option { - let check_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTING_WM_CHECK\0") }; - let wm_name_atom = unsafe { self.get_atom_unchecked(b"_NET_WM_NAME\0") }; + fn get_wm_name(&self, root: ffi::xcb_window_t) -> Option { + let check_atom = self.get_atom("_NET_SUPPORTING_WM_CHECK"); + let wm_name_atom = self.get_atom("_NET_WM_NAME"); // Mutter/Muffin/Budgie doesn't have _NET_SUPPORTING_WM_CHECK in its _NET_SUPPORTED, despite // it working and being supported. This has been reported upstream, but due to the @@ -58,7 +41,7 @@ impl XConnection { // Querying this property on the root window will give us the ID of a child window created by // the WM. let root_window_wm_check = { - let result = self.get_property(root, check_atom, ffi::XA_WINDOW); + let result = self.get_property(root, check_atom, ffi::XCB_ATOM_WINDOW); let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned()); @@ -72,7 +55,7 @@ impl XConnection { // Querying the same property on the child window we were given, we should get this child // window's ID again. let child_window_wm_check = { - let result = self.get_property(root_window_wm_check, check_atom, ffi::XA_WINDOW); + let result = self.get_property(root_window_wm_check, check_atom, ffi::XCB_ATOM_WINDOW); let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned()); @@ -90,7 +73,7 @@ impl XConnection { // All of that work gives us a window ID that we can get the WM name from. let wm_name = { - let utf8_string_atom = unsafe { self.get_atom_unchecked(b"UTF8_STRING\0") }; + let utf8_string_atom = self.get_atom("UTF8_STRING"); let result = self.get_property(root_window_wm_check, wm_name_atom, utf8_string_atom); @@ -100,14 +83,16 @@ impl XConnection { // against it, though). // The unofficial 1.4 fork of IceWM still includes the extra details, but properly // returns a UTF8 string that isn't null-terminated. - let no_utf8 = if let Err(ref err) = result { - err.is_actual_property_type(ffi::XA_STRING) - } else { - false + let no_utf8 = match result { + Err(XcbGetPropertyError::InvalidPropertyType { + actual: ffi::XCB_ATOM_STRING, + .. + }) => true, + _ => false, }; if no_utf8 { - self.get_property(root_window_wm_check, wm_name_atom, ffi::XA_STRING) + self.get_property(root_window_wm_check, wm_name_atom, ffi::XCB_ATOM_STRING) } else { result } diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index bdde554b59..4b6c74cd8e 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -1,10 +1,10 @@ -use raw_window_handle::unix::XlibHandle; +use raw_window_handle::unix::XcbHandle; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::Relaxed; use std::{ cmp, env, ffi::CString, - mem::{self, replace, MaybeUninit}, + mem::{replace, MaybeUninit}, os::raw::*, path::Path, ptr, slice, @@ -26,7 +26,12 @@ use crate::{ window::{CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes}, }; -use super::{ffi, util, EventLoopWindowTarget, WindowId, XConnection, XError}; +use super::{ffi, util, EventLoopWindowTarget, WindowId, XConnection}; +use crate::platform_impl::x11::util::HintsError; +use crate::platform_impl::x11::util::PropMode; +use crate::platform_impl::x11::xdisplay::Screen; +use xcb_dl_util::hint::XcbSizeHints; +use xcb_dl_util::void::{XcbPendingCommand, XcbPendingCommands}; #[derive(Debug)] pub struct SharedState { @@ -43,7 +48,7 @@ pub struct SharedState { // Used to restore position after exiting fullscreen pub restore_position: Option<(i32, i32)>, // Used to restore video mode after exiting fullscreen - pub desktop_video_mode: Option<(ffi::RRCrtc, ffi::RRMode)>, + pub desktop_video_mode: Option<(ffi::xcb_randr_crtc_t, ffi::xcb_randr_mode_t)>, pub frame_extents: Option, pub min_inner_size: Option, pub max_inner_size: Option, @@ -95,10 +100,9 @@ unsafe impl Send for UnownedWindow {} unsafe impl Sync for UnownedWindow {} pub struct UnownedWindow { - pub xconn: Arc, // never changes - xwindow: ffi::Window, // never changes - root: ffi::Window, // never changes - screen_id: i32, // never changes + pub xconn: Arc, // never changes + pub xwindow: ffi::xcb_window_t, // never changes + pub screen: Arc, cursor: Mutex, cursor_grabbed: Mutex, cursor_visible: Mutex, @@ -114,17 +118,29 @@ impl UnownedWindow { pl_attribs: PlatformSpecificWindowBuilderAttributes, ) -> Result { let xconn = &event_loop.xconn; - let root = event_loop.root; + + let screen_id = pl_attribs + .screen_id + .map(|s| s as usize) + .unwrap_or(xconn.default_screen_id); + let screen = match xconn.screens.iter().skip(screen_id).next() { + Some(s) => s, + _ => return Err(os_error!(OsError::XMisc("Screen id out of bounds"))), + }; + + let root = screen.root; let mut monitors = xconn.available_monitors(); let guessed_monitor = if monitors.is_empty() { X11MonitorHandle::dummy() } else { xconn - .query_pointer(root, util::VIRTUAL_CORE_POINTER) + .query_pointer(root as _, util::VIRTUAL_CORE_POINTER) .ok() .and_then(|pointer_state| { - let (x, y) = (pointer_state.root_x as i64, pointer_state.root_y as i64); + let root_x = util::fp1616_to_f64(pointer_state.root_x); + let root_y = util::fp1616_to_f64(pointer_state.root_y); + let (x, y) = (root_x as i64, root_y as i64); for i in 0..monitors.len() { if monitors[i].rect.contains_point(x, y) { @@ -175,78 +191,84 @@ impl UnownedWindow { dimensions }; - let screen_id = match pl_attribs.screen_id { - Some(id) => id, - None => unsafe { (xconn.xlib.XDefaultScreen)(xconn.display) }, - }; + let mut commands = XcbPendingCommands::new(); // creating - let mut set_win_attr = { - let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() }; - swa.colormap = if let Some(vi) = pl_attribs.visual_infos { + let set_win_attr = { + let mut swa = ffi::xcb_create_window_value_list_t::default(); + if let Some(visual_id) = pl_attribs.visual_infos.visual_id { unsafe { - let visual = vi.visual; - (xconn.xlib.XCreateColormap)(xconn.display, root, visual, ffi::AllocNone) + swa.colormap = xconn.generate_id(); + commands.push( + xconn + .xcb + .xcb_create_colormap_checked(xconn.c, 0, swa.colormap, root, visual_id) + .into(), + ); } - } else { - 0 - }; - swa.event_mask = ffi::ExposureMask - | ffi::StructureNotifyMask - | ffi::VisibilityChangeMask - | ffi::KeyPressMask - | ffi::KeymapStateMask - | ffi::ButtonPressMask - | ffi::ButtonReleaseMask - | ffi::PointerMotionMask; + } + swa.event_mask = ffi::XCB_EVENT_MASK_EXPOSURE + | ffi::XCB_EVENT_MASK_STRUCTURE_NOTIFY + | ffi::XCB_EVENT_MASK_VISIBILITY_CHANGE + | ffi::XCB_EVENT_MASK_KEY_PRESS + | ffi::XCB_EVENT_MASK_KEYMAP_STATE + | ffi::XCB_EVENT_MASK_BUTTON_PRESS + | ffi::XCB_EVENT_MASK_BUTTON_RELEASE + | ffi::XCB_EVENT_MASK_POINTER_MOTION; swa.border_pixel = 0; - swa.override_redirect = pl_attribs.override_redirect as c_int; + swa.override_redirect = pl_attribs.override_redirect as u32; swa }; - let mut window_attributes = ffi::CWBorderPixel | ffi::CWColormap | ffi::CWEventMask; + let mut window_attributes = + ffi::XCB_CW_BORDER_PIXEL | ffi::XCB_CW_COLORMAP | ffi::XCB_CW_EVENT_MASK; if pl_attribs.override_redirect { - window_attributes |= ffi::CWOverrideRedirect; + window_attributes |= ffi::XCB_CW_OVERRIDE_REDIRECT; } // finally creating the window - let xwindow = unsafe { - (xconn.xlib.XCreateWindow)( - xconn.display, - root, - position.map_or(0, |p: PhysicalPosition| p.x as c_int), - position.map_or(0, |p: PhysicalPosition| p.y as c_int), - dimensions.0 as c_uint, - dimensions.1 as c_uint, - 0, - match pl_attribs.visual_infos { - Some(vi) => vi.depth, - None => ffi::CopyFromParent, - }, - ffi::InputOutput as c_uint, - // TODO: If window wants transparency and `visual_infos` is None, - // we need to find our own visual which has an `alphaMask` which - // is > 0, like we do in glutin. - // - // It is non obvious which masks, if any, we should pass to - // `XGetVisualInfo`. winit doesn't receive any info about what - // properties the user wants. Users should consider choosing the - // visual themselves as glutin does. - match pl_attribs.visual_infos { - Some(vi) => vi.visual, - None => ffi::CopyFromParent as *mut ffi::Visual, - }, - window_attributes, - &mut set_win_attr, - ) + let xwindow = xconn.generate_id(); + unsafe { + let pending = xconn + .xcb + .xcb_create_window_aux_checked( + xconn.c, + pl_attribs + .visual_infos + .depth + .unwrap_or(ffi::XCB_COPY_FROM_PARENT as _), + xwindow, + screen.root, + position.map_or(0, |p: PhysicalPosition| p.x as i16), + position.map_or(0, |p: PhysicalPosition| p.y as i16), + dimensions.0 as u16, + dimensions.1 as u16, + 0, // border width + ffi::XCB_WINDOW_CLASS_INPUT_OUTPUT as u16, + // TODO: If window wants transparency and `visual_infos` is None, + // we need to find our own visual which has an `alphaMask` which + // is > 0, like we do in glutin. + // + // It is non obvious which masks, if any, we should pass to + // `XGetVisualInfo`. winit doesn't receive any info about what + // properties the user wants. Users should consider choosing the + // visual themselves as glutin does. + pl_attribs + .visual_infos + .visual_id + .unwrap_or(ffi::XCB_COPY_FROM_PARENT as _), + window_attributes, + &set_win_attr, + ) + .into(); + commands.push(pending); }; let mut window = UnownedWindow { xconn: Arc::clone(xconn), xwindow, - root, - screen_id, + screen: screen.clone(), cursor: Default::default(), cursor_grabbed: Mutex::new(false), cursor_visible: Mutex::new(true), @@ -258,33 +280,24 @@ impl UnownedWindow { // Title must be set before mapping. Some tiling window managers (i.e. i3) use the window // title to determine placement/etc., so doing this after mapping would cause the WM to // act on the wrong title state. - window.set_title_inner(&window_attrs.title).queue(); - window - .set_decorations_inner(window_attrs.decorations) - .queue(); + commands.extend(window.set_title_inner(&window_attrs.title)); + commands.push(window.set_decorations_inner(window_attrs.decorations)); { // Enable drag and drop (TODO: extend API to make this toggleable) - unsafe { - let dnd_aware_atom = xconn.get_atom_unchecked(b"XdndAware\0"); - let version = &[5 as c_ulong]; // Latest version; hasn't changed since 2002 - xconn.change_property( - window.xwindow, - dnd_aware_atom, - ffi::XA_ATOM, - util::PropMode::Replace, - version, - ) - } - .queue(); + let dnd_aware_atom = xconn.get_atom("XdndAware"); + let version = &[5u32]; // Latest version; hasn't changed since 2002 + commands.push(xconn.change_property( + window.xwindow, + dnd_aware_atom, + ffi::XCB_ATOM_ATOM, + util::PropMode::Replace, + version, + )); // WM_CLASS must be set *before* mapping the window, as per ICCCM! { let (class, instance) = if let Some((instance, class)) = pl_attribs.class { - let instance = CString::new(instance.as_str()) - .expect("`WM_CLASS` instance contained null byte"); - let class = - CString::new(class.as_str()).expect("`WM_CLASS` class contained null byte"); (instance, class) } else { let class = env::args() @@ -294,33 +307,29 @@ impl UnownedWindow { .and_then(|path| Path::new(path).file_name()) .and_then(|bin_name| bin_name.to_str()) .map(|bin_name| bin_name.to_owned()) - .or_else(|| Some(window_attrs.title.clone())) - .and_then(|string| CString::new(string.as_str()).ok()) - .expect("Default `WM_CLASS` class contained null byte"); + .unwrap_or_else(|| window_attrs.title.clone()); // This environment variable is extraordinarily unlikely to actually be used... let instance = env::var("RESOURCE_NAME") .ok() - .and_then(|instance| CString::new(instance.as_str()).ok()) - .or_else(|| Some(class.clone())) - .expect("Default `WM_CLASS` instance contained null byte"); + .unwrap_or_else(|| class.clone()); (instance, class) }; - let mut class_hint = xconn.alloc_class_hint(); - (*class_hint).res_name = class.as_ptr() as *mut c_char; - (*class_hint).res_class = instance.as_ptr() as *mut c_char; - - unsafe { - (xconn.xlib.XSetClassHint)(xconn.display, window.xwindow, class_hint.ptr); - } //.queue(); + commands.push(xconn.change_property( + window.xwindow, + ffi::XCB_ATOM_WM_CLASS, + ffi::XCB_ATOM_STRING, + PropMode::Replace, + format!("{}\0{}\0", class, instance).as_bytes(), + )); } - window.set_pid().map(|flusher| flusher.queue()); + window.set_pid().map(|cmds| commands.extend(cmds)); - window.set_window_types(pl_attribs.x11_window_types).queue(); + commands.push(window.set_window_types(pl_attribs.x11_window_types)); if let Some(variant) = pl_attribs.gtk_theme_variant { - window.set_gtk_theme_variant(variant).queue(); + commands.push(window.set_gtk_theme_variant(variant)); } // set size hints @@ -333,7 +342,7 @@ impl UnownedWindow { .map(|size| size.to_physical::(scale_factor)); if !window_attrs.resizable { - if util::wm_name_is_one_of(&["Xfwm4"]) { + if screen.wm_name_is_one_of(&["Xfwm4"]) { warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4"); } else { max_inner_size = Some(dimensions.into()); @@ -347,7 +356,7 @@ impl UnownedWindow { } } - let mut normal_hints = util::NormalHints::new(xconn); + let mut normal_hints = XcbSizeHints::default(); normal_hints.set_position(position.map(|PhysicalPosition { x, y }| (x, y))); normal_hints.set_size(Some(dimensions)); normal_hints.set_min_size(min_inner_size.map(Into::into)); @@ -362,75 +371,66 @@ impl UnownedWindow { .base_size .map(|size| size.to_physical::(scale_factor).into()), ); - xconn.set_normal_hints(window.xwindow, normal_hints).queue(); + commands.push(xconn.set_normal_hints(window.xwindow, normal_hints)); } // Set window icons if let Some(icon) = window_attrs.window_icon { - window.set_icon_inner(icon).queue(); + commands.push(window.set_icon_inner(icon)); } // Opt into handling window close - unsafe { - (xconn.xlib.XSetWMProtocols)( - xconn.display, - window.xwindow, - &[event_loop.wm_delete_window, event_loop.net_wm_ping] as *const ffi::Atom - as *mut ffi::Atom, - 2, - ); - } //.queue(); + { + let prop = xconn.get_atom("WM_PROTOCOLS"); + let pending = xconn + .change_property( + window.xwindow, + prop, + ffi::XCB_ATOM_ATOM, + PropMode::Replace, + &[event_loop.wm_delete_window, event_loop.net_wm_ping], + ) + .into(); + commands.push(pending); + } // Set visibility (map window) if window_attrs.visible { - unsafe { - (xconn.xlib.XMapRaised)(xconn.display, window.xwindow); - } //.queue(); - } - - // Attempt to make keyboard input repeat detectable - unsafe { - let mut supported_ptr = ffi::False; - (xconn.xlib.XkbSetDetectableAutoRepeat)( - xconn.display, - ffi::True, - &mut supported_ptr, - ); - if supported_ptr == ffi::False { - return Err(os_error!(OsError::XMisc( - "`XkbSetDetectableAutoRepeat` failed" - ))); - } + commands.extend(window.map_raised()); } // Select XInput2 events let mask = { - let mask = ffi::XI_MotionMask - | ffi::XI_ButtonPressMask - | ffi::XI_ButtonReleaseMask - | ffi::XI_KeyPressMask - | ffi::XI_KeyReleaseMask - | ffi::XI_EnterMask - | ffi::XI_LeaveMask - | ffi::XI_FocusInMask - | ffi::XI_FocusOutMask - | ffi::XI_TouchBeginMask - | ffi::XI_TouchUpdateMask - | ffi::XI_TouchEndMask; + let mask = ffi::XCB_INPUT_XI_EVENT_MASK_MOTION + | ffi::XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS + | ffi::XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE + | ffi::XCB_INPUT_XI_EVENT_MASK_KEY_PRESS + | ffi::XCB_INPUT_XI_EVENT_MASK_KEY_RELEASE + | ffi::XCB_INPUT_XI_EVENT_MASK_ENTER + | ffi::XCB_INPUT_XI_EVENT_MASK_LEAVE + | ffi::XCB_INPUT_XI_EVENT_MASK_FOCUS_IN + | ffi::XCB_INPUT_XI_EVENT_MASK_FOCUS_OUT + | ffi::XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN + | ffi::XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE + | ffi::XCB_INPUT_XI_EVENT_MASK_TOUCH_END; mask }; - xconn - .select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask) - .queue(); + let pending = xconn.select_xinput_events( + window.xwindow as _, + ffi::XCB_INPUT_DEVICE_ALL_MASTER as _, + mask, + ); + commands.push(pending); // These properties must be set after mapping if window_attrs.maximized { - window.set_maximized_inner(window_attrs.maximized).queue(); + commands.push(window.set_maximized_inner(window_attrs.maximized)); } if window_attrs.fullscreen.is_some() { - window - .set_fullscreen_inner(window_attrs.fullscreen.clone()) - .map(|flusher| flusher.queue()); + if let Some(pending) = window.set_fullscreen_inner(window_attrs.fullscreen.clone()) + { + commands.extend(pending); + } if let Some(PhysicalPosition { x, y }) = position { let shared_state = window.shared_state.get_mut(); @@ -439,22 +439,21 @@ impl UnownedWindow { } } if window_attrs.always_on_top { - window - .set_always_on_top_inner(window_attrs.always_on_top) - .queue(); + commands.push(window.set_always_on_top_inner(window_attrs.always_on_top)); } } // We never want to give the user a broken window, since by then, it's too late to handle. - xconn - .sync_with_server() - .map(|_| window) - .map_err(|x_err| os_error!(OsError::XError(x_err))) + if let Err(e) = xconn.check_pending(commands) { + Err(os_error!(OsError::XError(e.into()))) + } else { + Ok(window) + } } - fn set_pid(&self) -> Option> { - let pid_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_PID\0") }; - let client_machine_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") }; + fn set_pid(&self) -> Option { + let pid_atom = self.xconn.get_atom("_NET_WM_PID"); + let client_machine_atom = self.xconn.get_atom("WM_CLIENT_MACHINE"); unsafe { // 64 would suffice for Linux, but 256 will be enough everywhere (as per SUSv2). For instance, this is // the limit defined by OpenBSD. @@ -472,28 +471,26 @@ impl UnownedWindow { let hostname = slice::from_raw_parts(buffer.as_ptr() as *const c_char, hostname_length); - self.xconn - .change_property( - self.xwindow, - pid_atom, - ffi::XA_CARDINAL, - util::PropMode::Replace, - &[libc::getpid() as util::Cardinal], - ) - .queue(); - let flusher = self.xconn.change_property( + let pending1 = self.xconn.change_property( + self.xwindow, + pid_atom, + ffi::XCB_ATOM_CARDINAL, + util::PropMode::Replace, + &[libc::getpid() as util::Cardinal], + ); + let pending2 = self.xconn.change_property( self.xwindow, client_machine_atom, - ffi::XA_STRING, + ffi::XCB_ATOM_STRING, util::PropMode::Replace, &hostname[0..hostname_length], ); - Some(flusher) + Some(pending1.and_then(pending2)) } } - fn set_window_types(&self, window_types: Vec) -> util::Flusher<'_> { - let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_WINDOW_TYPE\0") }; + fn set_window_types(&self, window_types: Vec) -> XcbPendingCommand { + let hint_atom = self.xconn.get_atom("_NET_WM_WINDOW_TYPE"); let atoms: Vec<_> = window_types .iter() .map(|t| t.as_atom(&self.xconn)) @@ -502,15 +499,15 @@ impl UnownedWindow { self.xconn.change_property( self.xwindow, hint_atom, - ffi::XA_ATOM, + ffi::XCB_ATOM_ATOM, util::PropMode::Replace, &atoms, ) } - fn set_gtk_theme_variant(&self, variant: String) -> util::Flusher<'_> { - let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_GTK_THEME_VARIANT\0") }; - let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") }; + fn set_gtk_theme_variant(&self, variant: String) -> XcbPendingCommand { + let hint_atom = self.xconn.get_atom("_GTK_THEME_VARIANT"); + let utf8_atom = self.xconn.get_atom("UTF8_STRING"); let variant = CString::new(variant).expect("`_GTK_THEME_VARIANT` contained null byte"); self.xconn.change_property( self.xwindow, @@ -524,16 +521,18 @@ impl UnownedWindow { fn set_netwm( &self, operation: util::StateOperation, - properties: (c_long, c_long, c_long, c_long), - ) -> util::Flusher<'_> { - let state_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE\0") }; + properties: (u32, u32, u32, u32), + ) -> XcbPendingCommand { + let state_atom = self.xconn.get_atom("_NET_WM_STATE"); self.xconn.send_client_msg( self.xwindow, - self.root, + self.screen.root, state_atom, - Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask), + Some( + ffi::XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | ffi::XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, + ), [ - operation as c_long, + operation as u32, properties.0, properties.1, properties.2, @@ -542,28 +541,34 @@ impl UnownedWindow { ) } - fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher<'_> { - let fullscreen_atom = - unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_FULLSCREEN\0") }; - let flusher = self.set_netwm(fullscreen.into(), (fullscreen_atom as c_long, 0, 0, 0)); + fn set_fullscreen_hint(&self, fullscreen: bool) -> XcbPendingCommands { + let fullscreen_atom = self.xconn.get_atom("_NET_WM_STATE_FULLSCREEN"); + let mut pending: XcbPendingCommands = self + .set_netwm(fullscreen.into(), (fullscreen_atom, 0, 0, 0)) + .into(); if fullscreen { // Ensure that the fullscreen window receives input focus to prevent // locking up the user's display. unsafe { - (self.xconn.xlib.XSetInputFocus)( - self.xconn.display, - self.xwindow, - ffi::RevertToParent, - ffi::CurrentTime, - ); + let p = self + .xconn + .xcb + .xcb_set_input_focus_checked( + self.xconn.c, + ffi::XCB_INPUT_FOCUS_PARENT as _, + self.xwindow, + ffi::XCB_TIME_CURRENT_TIME, + ) + .into(); + pending.push(p); } } - flusher + pending } - fn set_fullscreen_inner(&self, fullscreen: Option) -> Option> { + fn set_fullscreen_inner(&self, fullscreen: Option) -> Option { let mut shared_state_lock = self.shared_state.lock(); match shared_state_lock.visibility { @@ -600,7 +605,7 @@ impl UnownedWindow { ) => { let monitor = video_mode.monitor.as_ref().unwrap(); shared_state_lock.desktop_video_mode = - Some((monitor.id, self.xconn.get_crtc_mode(monitor.id))); + Some((monitor.id, self.xconn.get_crtc_mode(monitor.id).unwrap())); } // Restore desktop video mode upon exiting exclusive fullscreen (&Some(Fullscreen::Exclusive(_)), &None) @@ -617,13 +622,13 @@ impl UnownedWindow { match fullscreen { None => { - let flusher = self.set_fullscreen_hint(false); + let mut pending = self.set_fullscreen_hint(false); let mut shared_state_lock = self.shared_state.lock(); if let Some(position) = shared_state_lock.restore_position.take() { drop(shared_state_lock); - self.set_position_inner(position.0, position.1).queue(); + pending.push(self.set_position_inner(position.0, position.1)); } - Some(flusher) + Some(pending) } Some(fullscreen) => { let (video_mode, monitor) = match fullscreen { @@ -677,9 +682,11 @@ impl UnownedWindow { let window_position = self.outer_position_physical(); self.shared_state.lock().restore_position = Some(window_position); let monitor_origin: (i32, i32) = monitor.position().into(); - self.set_position_inner(monitor_origin.0, monitor_origin.1) - .queue(); - Some(self.set_fullscreen_hint(true)) + let mut pending: XcbPendingCommands = self + .set_position_inner(monitor_origin.0, monitor_origin.1) + .into(); + pending.extend(self.set_fullscreen_hint(true)); + Some(pending) } } } @@ -696,10 +703,14 @@ impl UnownedWindow { #[inline] pub fn set_fullscreen(&self, fullscreen: Option) { - if let Some(flusher) = self.set_fullscreen_inner(fullscreen) { - flusher - .sync() - .expect("Failed to change window fullscreen state"); + let fs = fullscreen.is_some(); + if let Some(pending) = self.set_fullscreen_inner(fullscreen) { + if let Err(e) = self.xconn.check_pending(pending) { + log::error!("Failed to change window fullscreen state: {}", e); + if fs { + panic!("Could not exit fullscreen."); + } + } self.invalidate_cached_frame_extents(); } } @@ -709,9 +720,12 @@ impl UnownedWindow { let mut shared_state = self.shared_state.lock(); match shared_state.visibility { - Visibility::No => unsafe { - (self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow); - }, + Visibility::No => { + let pending = self.unmap(); + if let Err(e) = self.xconn.check_pending1(pending) { + log::error!("Failed to unmap window: {}", e); + } + } Visibility::Yes => (), Visibility::YesWait => { shared_state.visibility = Visibility::Yes; @@ -730,93 +744,85 @@ impl UnownedWindow { } pub fn available_monitors(&self) -> Vec { - self.xconn.available_monitors() + match self.xconn.available_monitors_inner() { + Ok(m) => m, + Err(e) => { + log::error!("Could not query available monitors: {}", e); + vec![] + } + } } pub fn primary_monitor(&self) -> X11MonitorHandle { self.xconn.primary_monitor() } - fn set_minimized_inner(&self, minimized: bool) -> util::Flusher<'_> { - unsafe { - if minimized { - let screen = (self.xconn.xlib.XDefaultScreen)(self.xconn.display); - - (self.xconn.xlib.XIconifyWindow)(self.xconn.display, self.xwindow, screen); - - util::Flusher::new(&self.xconn) - } else { - let atom = self.xconn.get_atom_unchecked(b"_NET_ACTIVE_WINDOW\0"); - - self.xconn.send_client_msg( - self.xwindow, - self.root, - atom, - Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask), - [1, ffi::CurrentTime as c_long, 0, 0, 0], - ) - } - } + fn set_minimized_inner(&self, minimized: bool) -> XcbPendingCommand { + let (atom, data) = if minimized { + ("WM_CHANGE_STATE", 3) // 3 = IconicState + } else { + ("_NET_ACTIVE_WINDOW", 1) // 1 = Active + }; + self.xconn.send_client_msg( + self.xwindow, + self.screen.root, + self.xconn.get_atom(atom), + Some( + ffi::XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | ffi::XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, + ), + [data, 0, 0, 0, 0], + ) } #[inline] pub fn set_minimized(&self, minimized: bool) { - self.set_minimized_inner(minimized) - .flush() - .expect("Failed to change window minimization"); + let pending = self.set_minimized_inner(minimized); + if let Err(e) = self.xconn.check_pending1(pending) { + log::error!("Could not change minimized state: {}", e); + } } - fn set_maximized_inner(&self, maximized: bool) -> util::Flusher<'_> { - let horz_atom = unsafe { - self.xconn - .get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_HORZ\0") - }; - let vert_atom = unsafe { - self.xconn - .get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_VERT\0") - }; - self.set_netwm( - maximized.into(), - (horz_atom as c_long, vert_atom as c_long, 0, 0), - ) + fn set_maximized_inner(&self, maximized: bool) -> XcbPendingCommand { + let horz_atom = self.xconn.get_atom("_NET_WM_STATE_MAXIMIZED_HORZ"); + let vert_atom = self.xconn.get_atom("_NET_WM_STATE_MAXIMIZED_VERT"); + self.set_netwm(maximized.into(), (horz_atom, vert_atom, 0, 0)) } #[inline] pub fn set_maximized(&self, maximized: bool) { - self.set_maximized_inner(maximized) - .flush() - .expect("Failed to change window maximization"); + let pending = self.set_maximized_inner(maximized); + if let Err(e) = self.xconn.check_pending1(pending) { + log::error!("Failed to change window maximization: {}", e); + } self.invalidate_cached_frame_extents(); } - fn set_title_inner(&self, title: &str) -> util::Flusher<'_> { - let wm_name_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_NAME\0") }; - let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") }; - let title = CString::new(title).expect("Window title contained null byte"); - unsafe { - (self.xconn.xlib.XStoreName)( - self.xconn.display, - self.xwindow, - title.as_ptr() as *const c_char, - ); - self.xconn.change_property( - self.xwindow, - wm_name_atom, - utf8_atom, - util::PropMode::Replace, - title.as_bytes(), - ) - } + fn set_title_inner(&self, title: &str) -> XcbPendingCommands { + let pending1 = self.xconn.change_property( + self.xwindow, + ffi::XCB_ATOM_WM_NAME, + ffi::XCB_ATOM_STRING, + util::PropMode::Replace, + title.as_bytes(), + ); + let pending2 = self.xconn.change_property( + self.xwindow, + self.xconn.get_atom("_NET_WM_NAME"), + self.xconn.get_atom("UTF8_STRING"), + util::PropMode::Replace, + title.as_bytes(), + ); + pending1.and_then(pending2) } #[inline] pub fn set_title(&self, title: &str) { - self.set_title_inner(title) - .flush() - .expect("Failed to set window title"); + if let Err(e) = self.xconn.check_pending(self.set_title_inner(title)) { + log::error!("Could not set window title: {}", e); + } } - fn set_decorations_inner(&self, decorations: bool) -> util::Flusher<'_> { + fn set_decorations_inner(&self, decorations: bool) -> XcbPendingCommand { let mut hints = self.xconn.get_motif_hints(self.xwindow); hints.set_decorations(decorations); @@ -826,13 +832,14 @@ impl UnownedWindow { #[inline] pub fn set_decorations(&self, decorations: bool) { - self.set_decorations_inner(decorations) - .flush() - .expect("Failed to set decoration state"); + let pending = self.set_decorations_inner(decorations); + if let Err(e) = self.xconn.check_pending1(pending) { + log::error!("Could not set window decorations: {}", e); + } self.invalidate_cached_frame_extents(); } - fn set_maximizable_inner(&self, maximizable: bool) -> util::Flusher<'_> { + fn set_maximizable_inner(&self, maximizable: bool) -> XcbPendingCommand { let mut hints = self.xconn.get_motif_hints(self.xwindow); hints.set_maximizable(maximizable); @@ -840,37 +847,38 @@ impl UnownedWindow { self.xconn.set_motif_hints(self.xwindow, &hints) } - fn set_always_on_top_inner(&self, always_on_top: bool) -> util::Flusher<'_> { - let above_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_ABOVE\0") }; - self.set_netwm(always_on_top.into(), (above_atom as c_long, 0, 0, 0)) + fn set_always_on_top_inner(&self, always_on_top: bool) -> XcbPendingCommand { + let above_atom = self.xconn.get_atom("_NET_WM_STATE_ABOVE"); + self.set_netwm(always_on_top.into(), (above_atom, 0, 0, 0)) } #[inline] pub fn set_always_on_top(&self, always_on_top: bool) { - self.set_always_on_top_inner(always_on_top) - .flush() - .expect("Failed to set always-on-top state"); + let pending = self.set_always_on_top_inner(always_on_top); + if let Err(e) = self.xconn.check_pending1(pending) { + log::error!("Could not set always-on-top property: {}", e); + } } - fn set_icon_inner(&self, icon: Icon) -> util::Flusher<'_> { - let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") }; + fn set_icon_inner(&self, icon: Icon) -> XcbPendingCommand { + let icon_atom = self.xconn.get_atom("_NET_WM_ICON"); let data = icon.to_cardinals(); self.xconn.change_property( self.xwindow, icon_atom, - ffi::XA_CARDINAL, + ffi::XCB_ATOM_CARDINAL, util::PropMode::Replace, data.as_slice(), ) } - fn unset_icon_inner(&self) -> util::Flusher<'_> { - let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") }; + fn unset_icon_inner(&self) -> XcbPendingCommand { + let icon_atom = self.xconn.get_atom("_NET_WM_ICON"); let empty_data: [util::Cardinal; 0] = []; self.xconn.change_property( self.xwindow, icon_atom, - ffi::XA_CARDINAL, + ffi::XCB_ATOM_CARDINAL, util::PropMode::Replace, &empty_data, ) @@ -878,12 +886,13 @@ impl UnownedWindow { #[inline] pub fn set_window_icon(&self, icon: Option) { - match icon { + let pending = match icon { Some(icon) => self.set_icon_inner(icon), None => self.unset_icon_inner(), + }; + if let Err(e) = self.xconn.check_pending1(pending) { + log::error!("Could not set window icon: {}", e); } - .flush() - .expect("Failed to set icons"); } #[inline] @@ -898,28 +907,51 @@ impl UnownedWindow { } if visible { - unsafe { - (self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow); + if let Err(e) = self.xconn.check_pending(self.map_raised()) { + panic!("Failed to map window: {}", e); } - self.xconn - .flush_requests() - .expect("Failed to call XMapRaised"); shared_state.visibility = Visibility::YesWait; } else { - unsafe { - (self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow); + if let Err(e) = self.xconn.check_pending1(self.unmap()) { + panic!("Failed to unmap window: {}", e); } - self.xconn - .flush_requests() - .expect("Failed to call XUnmapWindow"); shared_state.visibility = Visibility::No; } } + fn map_raised(&self) -> XcbPendingCommands { + unsafe { + let above = ffi::XCB_STACK_MODE_ABOVE as u32; + let pending1: XcbPendingCommand = self + .xconn + .xcb + .xcb_configure_window_checked( + self.xconn.c, + self.xwindow, + ffi::XCB_CONFIG_WINDOW_STACK_MODE as u16, + &above as *const _ as *const _, + ) + .into(); + let pending2 = self + .xconn + .xcb + .xcb_map_window_checked(self.xconn.c, self.xwindow) + .into(); + pending1.and_then(pending2) + } + } + + fn unmap(&self) -> XcbPendingCommand { + unsafe { + self.xconn + .xcb + .xcb_unmap_window_checked(self.xconn.c, self.xwindow) + .into() + } + } + fn update_cached_frame_extents(&self) { - let extents = self - .xconn - .get_frame_extents_heuristic(self.xwindow, self.root); + let extents = self.xconn.get_frame_extents_heuristic(self); (*self.shared_state.lock()).frame_extents = Some(extents); } @@ -954,8 +986,8 @@ impl UnownedWindow { // This should be okay to unwrap since the only error XTranslateCoordinates can return // is BadWindow, and if the window handle is bad we have bigger problems. self.xconn - .translate_coords(self.xwindow, self.root) - .map(|coords| (coords.x_rel_root, coords.y_rel_root)) + .translate_coords(self.xwindow, self.screen.root) + .map(|coords| (coords.x_rel_root as i32, coords.y_rel_root as i32)) .unwrap() } @@ -964,10 +996,10 @@ impl UnownedWindow { Ok(self.inner_position_physical().into()) } - pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher<'_> { + pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> XcbPendingCommand { // There are a few WMs that set client area position rather than window position, so // we'll translate for consistency. - if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) { + if self.screen.wm_name_is_one_of(&["Enlightenment", "FVWM"]) { let extents = (*self.shared_state.lock()).frame_extents.clone(); if let Some(extents) = extents { x += extents.frame_extents.left as i32; @@ -978,15 +1010,23 @@ impl UnownedWindow { } } unsafe { - (self.xconn.xlib.XMoveWindow)(self.xconn.display, self.xwindow, x as c_int, y as c_int); + self.xconn + .xcb + .xcb_configure_window_checked( + self.xconn.c, + self.xwindow, + (ffi::XCB_CONFIG_WINDOW_X | ffi::XCB_CONFIG_WINDOW_Y) as _, + [x, y].as_ptr() as _, + ) + .into() } - util::Flusher::new(&self.xconn) } pub(crate) fn set_position_physical(&self, x: i32, y: i32) { - self.set_position_inner(x, y) - .flush() - .expect("Failed to call `XMoveWindow`"); + let pending = self.set_position_inner(x, y); + if let Err(e) = self.xconn.check_pending1(pending) { + log::error!("Failed to set position: {}", e); + } } #[inline] @@ -998,10 +1038,16 @@ impl UnownedWindow { pub(crate) fn inner_size_physical(&self) -> (u32, u32) { // This should be okay to unwrap since the only error XGetGeometry can return // is BadWindow, and if the window handle is bad we have bigger problems. - self.xconn + let res = self + .xconn .get_geometry(self.xwindow) - .map(|geo| (geo.width, geo.height)) - .unwrap() + .map(|geo| (geo.width as u32, geo.height as u32)); + match res { + Ok(r) => r, + Err(e) => { + panic!("Could not retrieve window size: {}", e); + } + } } #[inline] @@ -1022,16 +1068,20 @@ impl UnownedWindow { } pub(crate) fn set_inner_size_physical(&self, width: u32, height: u32) { - unsafe { - (self.xconn.xlib.XResizeWindow)( - self.xconn.display, - self.xwindow, - width as c_uint, - height as c_uint, - ); - self.xconn.flush_requests() + let pending = unsafe { + self.xconn + .xcb + .xcb_configure_window_checked( + self.xconn.c, + self.xwindow, + (ffi::XCB_CONFIG_WINDOW_WIDTH | ffi::XCB_CONFIG_WINDOW_HEIGHT) as _, + [width, height].as_ptr() as _, + ) + .into() + }; + if let Err(e) = self.xconn.check_pending1(pending) { + log::error!("Failed to resize window: {}", e); } - .expect("Failed to call `XResizeWindow`"); } #[inline] @@ -1041,20 +1091,20 @@ impl UnownedWindow { self.set_inner_size_physical(width, height); } - fn update_normal_hints(&self, callback: F) -> Result<(), XError> + fn update_normal_hints(&self, callback: F) -> Result where - F: FnOnce(&mut util::NormalHints<'_>) -> (), + F: FnOnce(&mut XcbSizeHints) -> (), { let mut normal_hints = self.xconn.get_normal_hints(self.xwindow)?; callback(&mut normal_hints); - self.xconn - .set_normal_hints(self.xwindow, normal_hints) - .flush() + Ok(self.xconn.set_normal_hints(self.xwindow, normal_hints)) } - pub(crate) fn set_min_inner_size_physical(&self, dimensions: Option<(u32, u32)>) { + pub(crate) fn set_min_inner_size_physical( + &self, + dimensions: Option<(u32, u32)>, + ) -> Result { self.update_normal_hints(|normal_hints| normal_hints.set_min_size(dimensions)) - .expect("Failed to call `XSetWMNormalHints`"); } #[inline] @@ -1062,12 +1112,17 @@ impl UnownedWindow { self.shared_state.lock().min_inner_size = dimensions; let physical_dimensions = dimensions.map(|dimensions| dimensions.to_physical::(self.scale_factor()).into()); - self.set_min_inner_size_physical(physical_dimensions); + let pending = self.set_min_inner_size_physical(physical_dimensions); + if let Err(e) = pending.and_then(|p| self.xconn.check_pending1(p).map_err(|e| e.into())) { + log::error!("Could not set minimum size: {}", e); + } } - pub(crate) fn set_max_inner_size_physical(&self, dimensions: Option<(u32, u32)>) { + pub(crate) fn set_max_inner_size_physical( + &self, + dimensions: Option<(u32, u32)>, + ) -> Result { self.update_normal_hints(|normal_hints| normal_hints.set_max_size(dimensions)) - .expect("Failed to call `XSetWMNormalHints`"); } #[inline] @@ -1075,7 +1130,10 @@ impl UnownedWindow { self.shared_state.lock().max_inner_size = dimensions; let physical_dimensions = dimensions.map(|dimensions| dimensions.to_physical::(self.scale_factor()).into()); - self.set_max_inner_size_physical(physical_dimensions); + let pending = self.set_max_inner_size_physical(physical_dimensions); + if let Err(e) = pending.and_then(|p| self.xconn.check_pending1(p).map_err(|e| e.into())) { + log::error!("Could not set maximum size: {}", e); + } } pub(crate) fn adjust_for_dpi( @@ -1087,7 +1145,7 @@ impl UnownedWindow { shared_state: &SharedState, ) -> (u32, u32) { let scale_factor = new_scale_factor / old_scale_factor; - self.update_normal_hints(|normal_hints| { + let pending = self.update_normal_hints(|normal_hints| { let dpi_adjuster = |size: Size| -> (u32, u32) { size.to_physical::(new_scale_factor).into() }; let max_size = shared_state.max_inner_size.map(&dpi_adjuster); @@ -1098,8 +1156,10 @@ impl UnownedWindow { normal_hints.set_min_size(min_size); normal_hints.set_resize_increments(resize_increments); normal_hints.set_base_size(base_size); - }) - .expect("Failed to update normal hints"); + }); + if let Err(e) = pending.and_then(|p| self.xconn.check_pending1(p).map_err(|e| e.into())) { + log::error!("Could not update normal hints: {}", e); + } let new_width = (width as f64 * scale_factor).round() as u32; let new_height = (height as f64 * scale_factor).round() as u32; @@ -1108,7 +1168,7 @@ impl UnownedWindow { } pub fn set_resizable(&self, resizable: bool) { - if util::wm_name_is_one_of(&["Xfwm4"]) { + if self.screen.wm_name_is_one_of(&["Xfwm4"]) { // Making the window unresizable on Xfwm prevents further changes to `WM_NORMAL_HINTS` from being detected. // This makes it impossible for resizing to be re-enabled, and also breaks DPI scaling. As such, we choose // the lesser of two evils and do nothing. @@ -1127,7 +1187,7 @@ impl UnownedWindow { (window_size.clone(), window_size) }; - self.set_maximizable_inner(resizable).queue(); + let pending1 = self.set_maximizable_inner(resizable); let scale_factor = self.scale_factor(); let min_inner_size = min_size @@ -1136,43 +1196,32 @@ impl UnownedWindow { let max_inner_size = max_size .map(|size| size.to_physical::(scale_factor)) .map(Into::into); - self.update_normal_hints(|normal_hints| { + let pending2 = self.update_normal_hints(|normal_hints| { normal_hints.set_min_size(min_inner_size); normal_hints.set_max_size(max_inner_size); - }) - .expect("Failed to call `XSetWMNormalHints`"); - } - - #[inline] - pub fn xlib_display(&self) -> *mut c_void { - self.xconn.display as _ - } - - #[inline] - pub fn xlib_screen_id(&self) -> c_int { - self.screen_id - } - - #[inline] - pub fn xlib_xconnection(&self) -> Arc { - Arc::clone(&self.xconn) - } - - #[inline] - pub fn xlib_window(&self) -> c_ulong { - self.xwindow - } + }); + + let res = match pending2 { + Ok(p) => self + .xconn + .check_pending(pending1.and_then(p)) + .map_err(|e| e.into()), + Err(e) => { + self.xconn.discard(pending1); + Err(e) + } + }; - #[inline] - pub fn xcb_connection(&self) -> *mut c_void { - unsafe { (self.xconn.xlib_xcb.XGetXCBConnection)(self.xconn.display) as *mut _ } + if let Err(e) = res { + log::error!("Could not change the resizable property: {}", e); + } } #[inline] pub fn set_cursor_icon(&self, cursor: CursorIcon) { let old_cursor = replace(&mut *self.cursor.lock(), cursor); if cursor != old_cursor && *self.cursor_visible.lock() { - self.xconn.set_cursor_icon(self.xwindow, Some(cursor)); + self.xconn.set_cursor_icon(self.xwindow as _, Some(cursor)); } } @@ -1182,55 +1231,78 @@ impl UnownedWindow { if grab == *grabbed_lock { return Ok(()); } - unsafe { + let ungrab = unsafe { // We ungrab before grabbing to prevent passive grabs from causing `AlreadyGrabbed`. // Therefore, this is common to both codepaths. - (self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime); - } + self.xconn + .xcb + .xcb_ungrab_pointer_checked(self.xconn.c, ffi::XCB_TIME_CURRENT_TIME) + .into() + }; let result = if grab { - let result = unsafe { - (self.xconn.xlib.XGrabPointer)( - self.xconn.display, - self.xwindow, - ffi::True, - (ffi::ButtonPressMask - | ffi::ButtonReleaseMask - | ffi::EnterWindowMask - | ffi::LeaveWindowMask - | ffi::PointerMotionMask - | ffi::PointerMotionHintMask - | ffi::Button1MotionMask - | ffi::Button2MotionMask - | ffi::Button3MotionMask - | ffi::Button4MotionMask - | ffi::Button5MotionMask - | ffi::ButtonMotionMask - | ffi::KeymapStateMask) as c_uint, - ffi::GrabModeAsync, - ffi::GrabModeAsync, - self.xwindow, - 0, - ffi::CurrentTime, - ) - }; + self.xconn.discard(ungrab); + loop { + let result = unsafe { + let cookie = self.xconn.xcb.xcb_grab_pointer( + self.xconn.c, + 1, + self.xwindow, + (ffi::XCB_EVENT_MASK_BUTTON_PRESS + | ffi::XCB_EVENT_MASK_BUTTON_RELEASE + | ffi::XCB_EVENT_MASK_ENTER_WINDOW + | ffi::XCB_EVENT_MASK_LEAVE_WINDOW + | ffi::XCB_EVENT_MASK_POINTER_MOTION + | ffi::XCB_EVENT_MASK_POINTER_MOTION_HINT + | ffi::XCB_EVENT_MASK_BUTTON_1_MOTION + | ffi::XCB_EVENT_MASK_BUTTON_2_MOTION + | ffi::XCB_EVENT_MASK_BUTTON_3_MOTION + | ffi::XCB_EVENT_MASK_BUTTON_4_MOTION + | ffi::XCB_EVENT_MASK_BUTTON_5_MOTION + | ffi::XCB_EVENT_MASK_BUTTON_MOTION + | ffi::XCB_EVENT_MASK_KEYMAP_STATE) as u16, + ffi::XCB_GRAB_MODE_ASYNC as u8, + ffi::XCB_GRAB_MODE_ASYNC as u8, + self.xwindow, + 0, + ffi::XCB_TIME_CURRENT_TIME, + ); + let mut err = ptr::null_mut(); + let reply = + self.xconn + .xcb + .xcb_grab_pointer_reply(self.xconn.c, cookie, &mut err); + self.xconn.check(reply, err) + }; + let reply = match result { + Ok(r) => r, + Err(e) => { + break Err(ExternalError::Os(os_error!(OsError::XError(e.into())))); + } + }; - match result { - ffi::GrabSuccess => Ok(()), - ffi::AlreadyGrabbed => { - Err("Cursor could not be grabbed: already grabbed by another client") - } - ffi::GrabInvalidTime => Err("Cursor could not be grabbed: invalid time"), - ffi::GrabNotViewable => { - Err("Cursor could not be grabbed: grab location not viewable") + let res = match reply.status as ffi::xcb_grab_status_t { + ffi::XCB_GRAB_STATUS_SUCCESS => Ok(()), + ffi::XCB_GRAB_STATUS_ALREADY_GRABBED => { + Err("Cursor could not be grabbed: already grabbed by another client") + } + ffi::XCB_GRAB_STATUS_INVALID_TIME => { + Err("Cursor could not be grabbed: invalid time") + } + ffi::XCB_GRAB_STATUS_NOT_VIEWABLE => { + Err("Cursor could not be grabbed: grab location not viewable") + } + ffi::XCB_GRAB_STATUS_FROZEN => { + Err("Cursor could not be grabbed: frozen by another client") + } + _ => unreachable!(), } - ffi::GrabFrozen => Err("Cursor could not be grabbed: frozen by another client"), - _ => unreachable!(), + .map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err)))); + break res; } - .map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err)))) } else { self.xconn - .flush_requests() - .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err)))) + .check_pending1(ungrab) + .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err.into())))) }; if result.is_ok() { *grabbed_lock = grab; @@ -1251,7 +1323,7 @@ impl UnownedWindow { }; *visible_lock = visible; drop(visible_lock); - self.xconn.set_cursor_icon(self.xwindow, cursor); + self.xconn.set_cursor_icon(self.xwindow as _, cursor); } #[inline] @@ -1259,59 +1331,69 @@ impl UnownedWindow { self.current_monitor().scale_factor } - pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> { + pub fn set_cursor_position_physical(&self, x: i16, y: i16) -> Result<(), ExternalError> { unsafe { - (self.xconn.xlib.XWarpPointer)(self.xconn.display, 0, self.xwindow, 0, 0, 0, 0, x, y); + let pending = self + .xconn + .xcb + .xcb_warp_pointer_checked(self.xconn.c, 0, self.xwindow, 0, 0, 0, 0, x, y) + .into(); self.xconn - .flush_requests() - .map_err(|e| ExternalError::Os(os_error!(OsError::XError(e)))) + .check_pending1(pending) + .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err.into())))) } } #[inline] pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> { - let (x, y) = position.to_physical::(self.scale_factor()).into(); + let (x, y) = position.to_physical::(self.scale_factor()).into(); self.set_cursor_position_physical(x, y) } pub fn drag_window(&self) -> Result<(), ExternalError> { let pointer = self .xconn - .query_pointer(self.xwindow, util::VIRTUAL_CORE_POINTER) - .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err))))?; + .query_pointer(self.xwindow as _, util::VIRTUAL_CORE_POINTER) + .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err.into()))))?; let window = self.inner_position().map_err(ExternalError::NotSupported)?; - let message = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_MOVERESIZE\0") }; + let message = self.xconn.get_atom("_NET_WM_MOVERESIZE"); // we can't use `set_cursor_grab(false)` here because it doesn't run `XUngrabPointer` // if the cursor isn't currently grabbed let mut grabbed_lock = self.cursor_grabbed.lock(); - unsafe { - (self.xconn.xlib.XUngrabPointer)(self.xconn.display, ffi::CurrentTime); - } + let pending = unsafe { + self.xconn + .xcb + .xcb_ungrab_pointer_checked(self.xconn.c, ffi::XCB_TIME_CURRENT_TIME) + .into() + }; self.xconn - .flush_requests() - .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err))))?; + .check_pending1(pending) + .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err.into()))))?; *grabbed_lock = false; // we keep the lock until we are done + let pending = self.xconn.send_client_msg( + self.xwindow, + self.screen.root, + message, + Some( + ffi::XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | ffi::XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, + ), + [ + window.x + util::fp1616_to_f64(pointer.win_x) as i32, + window.y + util::fp1616_to_f64(pointer.win_y) as i32, + 8, // _NET_WM_MOVERESIZE_MOVE + ffi::XCB_BUTTON_INDEX_1 as _, + 1, + ], + ); + self.xconn - .send_client_msg( - self.xwindow, - self.root, - message, - Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask), - [ - (window.x as c_long + pointer.win_x as c_long), - (window.y as c_long + pointer.win_y as c_long), - 8, // _NET_WM_MOVERESIZE_MOVE - ffi::Button1 as c_long, - 1, - ], - ) - .flush() - .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err)))) + .check_pending1(pending) + .map_err(|err| ExternalError::Os(os_error!(OsError::XError(err.into())))) } #[inline] @@ -1324,19 +1406,18 @@ impl UnownedWindow { #[inline] pub fn request_user_attention(&self, request_type: Option) { - let mut wm_hints = self - .xconn - .get_wm_hints(self.xwindow) - .expect("`XGetWMHints` failed"); - if request_type.is_some() { - (*wm_hints).flags |= ffi::XUrgencyHint; - } else { - (*wm_hints).flags &= !ffi::XUrgencyHint; + let mut wm_hints = match self.xconn.get_wm_hints(self.xwindow) { + Ok(h) => h, + Err(e) => { + log::error!("Could not retrieve WM hints: {}", e); + return; + } + }; + wm_hints.set_urgency(request_type.is_some()); + let pending = self.xconn.set_wm_hints(self.xwindow, wm_hints); + if let Err(e) = self.xconn.check_pending1(pending) { + log::error!("Could not set WM hints: {}", e); } - self.xconn - .set_wm_hints(self.xwindow, wm_hints) - .flush() - .expect("Failed to set urgency hint"); } #[inline] @@ -1350,11 +1431,11 @@ impl UnownedWindow { } #[inline] - pub fn raw_window_handle(&self) -> XlibHandle { - XlibHandle { + pub fn raw_window_handle(&self) -> XcbHandle { + XcbHandle { window: self.xwindow, - display: self.xconn.display as _, - ..XlibHandle::empty() + connection: self.xconn.c as _, + ..XcbHandle::empty() } } } diff --git a/src/platform_impl/linux/x11/xdisplay.rs b/src/platform_impl/linux/x11/xdisplay.rs index 25065f045d..2a02aed5a9 100644 --- a/src/platform_impl/linux/x11/xdisplay.rs +++ b/src/platform_impl/linux/x11/xdisplay.rs @@ -1,109 +1,326 @@ -use std::{collections::HashMap, error::Error, fmt, os::raw::c_int, ptr}; +use std::sync::Arc; +use std::{collections::HashMap, fmt, mem, os::raw::c_int, ptr}; -use libc; use parking_lot::Mutex; use crate::window::CursorIcon; use super::ffi; +use crate::platform_impl::x11::xlib::Xlib; +use crate::platform_impl::x11::MonitorHandle; +use thiserror::Error; +use xcb_dl::{Xcb, XcbRandr, XcbRender, XcbXfixes, XcbXinput, XcbXkb}; +use xcb_dl_util::cursor::XcbCursorContext; +use xcb_dl_util::error::{XcbError, XcbErrorParser}; /// A connection to an X server. pub struct XConnection { - pub xlib: ffi::Xlib, - /// Exposes XRandR functions from version < 1.5 - pub xrandr: ffi::Xrandr_2_2_0, - /// Exposes XRandR functions from version = 1.5 - pub xrandr_1_5: Option, - pub xcursor: ffi::Xcursor, - pub xinput2: ffi::XInput2, - pub xlib_xcb: ffi::Xlib_xcb, - pub xrender: ffi::Xrender, - pub display: *mut ffi::Display, - pub x11_fd: c_int, - pub latest_error: Mutex>, - pub cursor_cache: Mutex, ffi::Cursor>>, + pub c: *mut ffi::xcb_connection_t, + pub fd: c_int, + + pub atom_cache: Mutex>, + + pub default_screen_id: usize, + pub screens: Vec>, + + pub errors: XcbErrorParser, + + pub xcb: Box, + + pub xkb: Box, + pub xkb_first_event: u8, + + pub xfixes: Box, + pub xfixes_first_event: u8, + + pub xinput: Box, + pub xinput_extension: u8, + + pub render: Box, + + pub randr: Box, + pub randr_version: (u32, u32), + pub randr_first_event: u8, + + pub cursors: XcbCursorContext, + pub cursor_cache: Mutex, ffi::xcb_cursor_t>>, + + pub monitors: Mutex>>, + + pub xlib: Option, } unsafe impl Send for XConnection {} unsafe impl Sync for XConnection {} -pub type XErrorHandler = - Option libc::c_int>; - impl XConnection { - pub fn new(error_handler: XErrorHandler) -> Result { - // opening the libraries - let xlib = ffi::Xlib::open()?; - let xcursor = ffi::Xcursor::open()?; - let xrandr = ffi::Xrandr_2_2_0::open()?; - let xrandr_1_5 = ffi::Xrandr::open().ok(); - let xinput2 = ffi::XInput2::open()?; - let xlib_xcb = ffi::Xlib_xcb::open()?; - let xrender = ffi::Xrender::open()?; - - unsafe { (xlib.XInitThreads)() }; - unsafe { (xlib.XSetErrorHandler)(error_handler) }; - - // calling XOpenDisplay - let display = unsafe { - let display = (xlib.XOpenDisplay)(ptr::null()); - if display.is_null() { - return Err(XNotSupported::XOpenDisplayFailed); - } - display + pub fn new() -> Result { + unsafe { Self::new_unsafe() } + } + + unsafe fn new_unsafe() -> Result { + macro_rules! load { + ($id:ident, $name:expr) => { + match $id::load_loose() { + Ok(l) => Box::new(l), + Err(e) => { + return Err(XNotSupported::LibraryOpenError { + library: $name.to_string(), + error: e.to_string(), + }) + } + } + }; + } + + let xcb = load!(Xcb, "libxcb"); + let xfixes = load!(XcbXfixes, "libxcb_xfixes"); + let xinput = load!(XcbXinput, "libxcb_xinput"); + let render = load!(XcbRender, "libxcb_render"); + let xkb = load!(XcbXkb, "libxcb_xkb"); + let randr = load!(XcbRandr, "libxcb_randr"); + + let (c, default_screen_id, xlib) = if super::xlib::use_xlib() { + let xlib = super::xlib::connect()?; + (xlib.c, xlib.default_screen_id, Some(xlib)) + } else { + let mut default_screen_id = 0; + let c = xcb.xcb_connect(ptr::null(), &mut default_screen_id); + (c, default_screen_id, None) }; - // Get X11 socket file descriptor - let fd = unsafe { (xlib.XConnectionNumber)(display) }; + let errors = XcbErrorParser::new(&xcb, c); - Ok(XConnection { - xlib, - xrandr, - xrandr_1_5, - xcursor, - xinput2, - xlib_xcb, - xrender, - display, - x11_fd: fd, - latest_error: Mutex::new(None), - cursor_cache: Default::default(), - }) - } + if let Err(e) = errors.check_connection(&xcb) { + return Err(XNotSupported::ConnectFailed { + error: e.to_string(), + }); + } - /// Checks whether an error has been triggered by the previous function calls. - #[inline] - pub fn check_errors(&self) -> Result<(), XError> { - let error = self.latest_error.lock().take(); - if let Some(error) = error { - Err(error) + let close = if xlib.is_some() { + None } else { - Ok(()) + Some(CloseConnection { c, xcb: &xcb }) + }; + + xcb_dl_util::log::log_connection(log::Level::Trace, &xcb, c); + + macro_rules! check_ext { + ($ext:expr, $name:expr) => {{ + let data = xcb.xcb_get_extension_data(c, $ext); + if data.is_null() || (*data).present == 0 { + return Err(XNotSupported::MissingExtension { + extension: $name.to_string(), + }); + } + data + }}; } - } - /// Ignores any previous error. - #[inline] - pub fn ignore_error(&self) { - *self.latest_error.lock() = None; + let xinput_data = check_ext!(xinput.xcb_input_id(), ffi::XCB_INPUT_NAME_STR); + let xfixes_data = check_ext!(xfixes.xcb_xfixes_id(), ffi::XCB_XFIXES_NAME_STR); + let _render_data = check_ext!(render.xcb_render_id(), ffi::XCB_RENDER_NAME_STR); + let xkb_data = check_ext!(xkb.xcb_xkb_id(), ffi::XCB_XKB_NAME_STR); + let randr_data = check_ext!(randr.xcb_randr_id(), ffi::XCB_RANDR_NAME_STR); + + macro_rules! enable_extension { + ($so:expr, $query:ident, $reply:ident, $major:expr, $minor:expr, $name:expr) => { + enable_extension!( + $so, + $query, + $reply, + major_version, + minor_version, + $major, + $minor, + $name + ) + }; + ($so:expr, $query:ident, $reply:ident, $server_major:ident, $server_minor:ident, $major:expr, $minor:expr, $name:expr) => {{ + let mut err = ptr::null_mut(); + let res = $so.$reply(c, $so.$query(c, $major, $minor), &mut err); + let res = match errors.check(&xcb, res, err) { + Ok(r) => r, + Err(e) => { + log::error!("Could not enable `{}` extension: {}", $name, e); + return Err(XNotSupported::MissingExtension { + extension: $name.to_string(), + }); + } + }; + let version = (res.$server_major, res.$server_major); + if version < ($major, $minor) { + log::warn!( + "winit uses the `{}` extension in version {:?} but the X server only \ + provides version {:?}, some features might be unavailable.", + $name, + ($major, $minor), + version + ); + } + version + }}; + } + + enable_extension!( + xfixes, + xcb_xfixes_query_version, + xcb_xfixes_query_version_reply, + 1, + 0, + ffi::XCB_XFIXES_NAME_STR + ); + enable_extension!( + xinput, + xcb_input_xi_query_version, + xcb_input_xi_query_version_reply, + 2, + 2, + ffi::XCB_INPUT_NAME_STR + ); + enable_extension!( + render, + xcb_render_query_version, + xcb_render_query_version_reply, + 0, + 8, + ffi::XCB_RENDER_NAME_STR + ); + enable_extension!( + xkb, + xcb_xkb_use_extension, + xcb_xkb_use_extension_reply, + server_major, + server_minor, + 1, + 0, + ffi::XCB_XKB_NAME_STR + ); + let randr_version = enable_extension!( + randr, + xcb_randr_query_version, + xcb_randr_query_version_reply, + 1, + 3, + ffi::XCB_RANDR_NAME_STR + ); + + let cursors = XcbCursorContext::new(&xcb, &render, c); + + let fd = xcb.xcb_get_file_descriptor(c); + + let screens = { + let mut screen_iter = xcb.xcb_setup_roots_iterator(xcb.xcb_get_setup(c)); + let mut screens = vec![]; + let mut i = 0; + while screen_iter.rem > 0 { + let screen = &*screen_iter.data; + screens.push(Arc::new(Screen { + screen_id: i, + root: screen.root, + root_depth: screen.root_depth, + supported_hints: Default::default(), + wm_name: Default::default(), + })); + xcb.xcb_screen_next(&mut screen_iter); + i += 1; + } + screens + }; + let default_screen_id = default_screen_id.max(0) as usize; + if default_screen_id > screens.len() { + return Err(XNotSupported::ConnectFailed { + error: format!("Default screen id out of bounds"), + }); + } + + for screen in &screens { + let screen = &*screen; + let mask = ffi::XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE + | ffi::XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY + | ffi::XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE; + let cookie = randr.xcb_randr_select_input_checked(c, screen.root, mask as _); + if let Err(e) = errors.check_cookie(&xcb, cookie) { + return Err(XNotSupported::ConnectFailed { + error: format!("Cannot listen for RandR events: {}", e), + }); + } + } + + mem::forget(close); + + Ok(XConnection { + atom_cache: Default::default(), + c, + fd, + default_screen_id, + screens, + cursors, + errors, + xcb, + xkb, + xkb_first_event: (*xkb_data).first_event, + xfixes, + xfixes_first_event: (*xfixes_data).first_event, + xinput, + xinput_extension: (*xinput_data).major_opcode, + render, + randr, + randr_version, + randr_first_event: (*randr_data).first_event, + cursor_cache: Default::default(), + monitors: Default::default(), + xlib, + }) } } impl fmt::Debug for XConnection { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.display.fmt(f) + f.debug_struct("XConnection").finish_non_exhaustive() } } impl Drop for XConnection { #[inline] fn drop(&mut self) { - unsafe { (self.xlib.XCloseDisplay)(self.display) }; + if self.xlib.is_none() { + unsafe { + self.xcb.xcb_disconnect(self.c); + } + } } } -/// Error triggered by xlib. -#[derive(Debug, Clone)] +#[derive(Debug)] +pub struct Screen { + pub screen_id: usize, + pub root: ffi::xcb_window_t, + pub root_depth: u8, + pub supported_hints: Mutex>, + pub wm_name: Mutex>, +} + +impl Screen { + pub fn hint_is_supported(&self, hint: ffi::xcb_atom_t) -> bool { + self.supported_hints.lock().contains(&hint) + } + + pub fn wm_name_is_one_of(&self, names: &[&str]) -> bool { + if let Some(ref name) = *self.wm_name.lock() { + names.contains(&name.as_str()) + } else { + false + } + } +} + +unsafe impl Sync for Screen {} +unsafe impl Send for Screen {} + +/// Error triggered by xcb. +#[derive(Clone, Debug, Error)] +#[error("X error: {description} (code: {error_code}, request code: {request_code}, minor code: {minor_code})")] pub struct XError { pub description: String, pub error_code: u8, @@ -111,55 +328,44 @@ pub struct XError { pub minor_code: u8, } -impl Error for XError {} - -impl fmt::Display for XError { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!( - formatter, - "X error: {} (code: {}, request code: {}, minor code: {})", - self.description, self.error_code, self.request_code, self.minor_code - ) +impl From for XError { + fn from(e: XcbError) -> Self { + Self { + description: e.to_string(), + error_code: e.error_code, + request_code: e.major, + minor_code: e.minor as _, + } } } -/// Error returned if this system doesn't have XLib or can't create an X connection. -#[derive(Clone, Debug)] +/// Error returned if this system doesn't have libxcb or can't create an X connection. +#[derive(Clone, Debug, Error)] +#[non_exhaustive] pub enum XNotSupported { /// Failed to load one or several shared libraries. - LibraryOpenError(ffi::OpenError), - /// Connecting to the X server with `XOpenDisplay` failed. - XOpenDisplayFailed, // TODO: add better message -} - -impl From for XNotSupported { - #[inline] - fn from(err: ffi::OpenError) -> XNotSupported { - XNotSupported::LibraryOpenError(err) - } + #[error("Could not open {library}: {error}")] + LibraryOpenError { library: String, error: String }, + /// A required extension is not available. + #[error("The required extension {extension} is not available")] + MissingExtension { extension: String }, + /// Connecting to the X server with `xcb_connect` failed. + #[error("Cannot connect to the X server: {error}")] + ConnectFailed { error: String }, + /// The X server has no attached screens. + #[error("The X server has no attached screens")] + NoScreens, } -impl XNotSupported { - fn description(&self) -> &'static str { - match self { - XNotSupported::LibraryOpenError(_) => "Failed to load one of xlib's shared libraries", - XNotSupported::XOpenDisplayFailed => "Failed to open connection to X server", - } - } +struct CloseConnection<'a> { + c: *mut ffi::xcb_connection_t, + xcb: &'a Xcb, } -impl Error for XNotSupported { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match *self { - XNotSupported::LibraryOpenError(ref err) => Some(err), - _ => None, +impl<'a> Drop for CloseConnection<'a> { + fn drop(&mut self) { + unsafe { + self.xcb.xcb_disconnect(self.c); } } } - -impl fmt::Display for XNotSupported { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - formatter.write_str(self.description()) - } -} diff --git a/src/platform_impl/linux/x11/xlib.rs b/src/platform_impl/linux/x11/xlib.rs new file mode 100644 index 0000000000..e62cebcb75 --- /dev/null +++ b/src/platform_impl/linux/x11/xlib.rs @@ -0,0 +1,147 @@ +pub use imp::*; + +#[cfg(not(feature = "xlib"))] +mod imp { + use crate::platform::unix::XNotSupported; + use std::os::raw::c_int; + use xcb_dl::ffi; + + pub struct Xlib { + pub dpy: usize, + pub c: *mut ffi::xcb_connection_t, + pub default_screen_id: c_int, + } + + pub fn connect() -> Result { + unreachable!(); + } + + pub fn use_xlib() -> bool { + false + } +} + +#[cfg(feature = "xlib")] +mod imp { + use crate::platform::unix::XNotSupported; + use crate::platform_impl::x11::XError; + use parking_lot::Mutex; + use std::ffi::CStr; + use std::mem::{ManuallyDrop, MaybeUninit}; + use std::os::raw::{c_char, c_int}; + use std::ptr; + use std::sync::{Arc, Weak}; + use x11_dl::xlib_xcb::XEventQueueOwner; + use xcb_dl::ffi; + + pub struct Xlib { + pub dpy: usize, + xlib: Arc, + pub c: *mut ffi::xcb_connection_t, + pub default_screen_id: c_int, + } + + static LIB: Mutex>>> = + parking_lot::const_mutex(None); + + pub fn connect() -> Result { + unsafe { + let mut lib = LIB.lock(); + let xlib = loop { + if let Some(lib) = &*lib { + if let Some(lib) = lib.upgrade() { + break lib; + } + } + match x11_dl::xlib::Xlib::open() { + Ok(l) => { + let xlib = Arc::new(l); + *lib = Some(ManuallyDrop::new(Arc::downgrade(&xlib))); + break xlib; + } + Err(e) => { + return Err(XNotSupported::LibraryOpenError { + library: "libX11".to_string(), + error: e.to_string(), + }) + } + } + }; + let xlib_xcb = match x11_dl::xlib_xcb::Xlib_xcb::open() { + Ok(l) => l, + Err(e) => { + return Err(XNotSupported::LibraryOpenError { + library: "libX11-xcb".to_string(), + error: e.to_string(), + }) + } + }; + (xlib.XInitThreads)(); + let dpy = (xlib.XOpenDisplay)(ptr::null()); + if dpy.is_null() { + return Err(XNotSupported::ConnectFailed { + error: "Unknown xlib error".to_string(), + }); + } + let default_screen_id = (xlib.XDefaultScreen)(dpy); + let c = (xlib_xcb.XGetXCBConnection)(dpy) as _; + (xlib_xcb.XSetEventQueueOwner)(dpy, XEventQueueOwner::XCBOwnsEventQueue); + (xlib.XSetErrorHandler)(Some(x_error_callback)); + Ok(Xlib { + dpy: dpy as _, + xlib, + c, + default_screen_id, + }) + } + } + + impl Drop for Xlib { + fn drop(&mut self) { + unsafe { + (self.xlib.XCloseDisplay)(self.dpy as _); + } + } + } + + unsafe extern "C" fn x_error_callback( + display: *mut x11_dl::xlib::Display, + event: *mut x11_dl::xlib::XErrorEvent, + ) -> c_int { + let lib = LIB.lock(); + if let Some(lib) = &*lib { + if let Some(lib) = lib.upgrade() { + let error = translate_error(&lib, display, &*event); + log::error!("X11 error: {:#?}", error); + } + } + 0 + } + + unsafe fn translate_error( + lib: &x11_dl::xlib::Xlib, + display: *mut x11_dl::xlib::Display, + event: &x11_dl::xlib::XErrorEvent, + ) -> XError { + // `assume_init` is safe here because the array consists of `MaybeUninit` values, + // which do not require initialization. + let mut buf: [MaybeUninit; 1024] = MaybeUninit::uninit().assume_init(); + (lib.XGetErrorText)( + display, + event.error_code as c_int, + buf.as_mut_ptr() as *mut c_char, + buf.len() as c_int, + ); + let description = CStr::from_ptr(buf.as_ptr() as *const c_char).to_string_lossy(); + XError { + description: description.into_owned(), + error_code: event.error_code, + request_code: event.request_code, + minor_code: event.minor_code, + } + } + + pub fn use_xlib() -> bool { + std::env::var_os("WINIT_DISABLE_XLIB").is_none() + } +} From 5eef772243ed41d1940950c72b2f9271a7618cd1 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 16 Nov 2021 23:58:47 +0100 Subject: [PATCH 128/131] X11: Expose native device ids This patch adds an extension method that allows the user to retrieve the XInput device id. --- src/platform/unix.rs | 22 ++++++++++++++++++++++ src/platform_impl/linux/x11/mod.rs | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/platform/unix.rs b/src/platform/unix.rs index 4e3df19cbd..b92eb6e12b 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -20,6 +20,7 @@ use crate::{ #[cfg(feature = "x11")] use crate::dpi::Size; +use crate::event::DeviceId; use crate::platform_impl::{ EventLoop as LinuxEventLoop, EventLoopWindowTarget as LinuxEventLoopWindowTarget, Window as LinuxWindow, @@ -454,6 +455,27 @@ impl WindowBuilderExtUnix for WindowBuilder { } } +/// Additional methods on `DeviceId` that are specific to Unix. +pub trait DeviceIdExtUnix { + /// Returns the native xinput identifier of the device. + /// + /// Returns `None` if the `DeviceId` does not belong to an X server. + #[cfg(feature = "x11")] + fn xinput_id(&self) -> Option; +} + +impl DeviceIdExtUnix for DeviceId { + #[inline] + #[cfg(feature = "x11")] + fn xinput_id(&self) -> Option { + #[allow(irrefutable_let_patterns)] + if let crate::platform_impl::DeviceId::X(id) = self.0 { + return Some(id.0 as _); + } + None + } +} + /// Additional methods on `MonitorHandle` that are specific to Linux. pub trait MonitorHandleExtUnix { /// Returns the inner identifier of the monitor. diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index f04085a1b2..9c1ad77f8e 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -443,7 +443,7 @@ impl WindowId { } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(ffi::xcb_input_device_id_t); +pub struct DeviceId(pub(crate) ffi::xcb_input_device_id_t); impl DeviceId { #[cfg(not(feature = "wayland"))] From 816ad7d2b0e9ac60eb23015efb146c027ce4c731 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 17 Nov 2021 00:00:07 +0100 Subject: [PATCH 129/131] X11: Fix set_minimized This patch fixes unminimizing via `set_minimized`. Previously it would ask the window manager to give focus to our window. But the window manager is free to ignore this message and to not even map our window. The documented way to exit minimized state is to simply map the window. --- src/platform_impl/linux/x11/window.rs | 38 ++++++++++++++++----------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 4b6c74cd8e..6bc173260a 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -757,27 +757,35 @@ impl UnownedWindow { self.xconn.primary_monitor() } - fn set_minimized_inner(&self, minimized: bool) -> XcbPendingCommand { - let (atom, data) = if minimized { - ("WM_CHANGE_STATE", 3) // 3 = IconicState + fn set_minimized_inner(&self, minimized: bool) -> XcbPendingCommands { + let mut pending = XcbPendingCommands::new(); + if minimized { + pending.push( + self.xconn + .send_client_msg( + self.xwindow, + self.screen.root, + self.xconn.get_atom("WM_CHANGE_STATE"), + Some( + ffi::XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT + | ffi::XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, + ), + [3, 0, 0, 0, 0], + ) + .into(), + ); } else { - ("_NET_ACTIVE_WINDOW", 1) // 1 = Active - }; - self.xconn.send_client_msg( - self.xwindow, - self.screen.root, - self.xconn.get_atom(atom), - Some( - ffi::XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | ffi::XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, - ), - [data, 0, 0, 0, 0], - ) + if self.shared_state.lock().visibility != Visibility::No { + pending.extend(self.map_raised()); + } + } + pending } #[inline] pub fn set_minimized(&self, minimized: bool) { let pending = self.set_minimized_inner(minimized); - if let Err(e) = self.xconn.check_pending1(pending) { + if let Err(e) = self.xconn.check_pending(pending) { log::error!("Could not change minimized state: {}", e); } } From a6ab7d4db38777007c71f64df27097c671761d1b Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Tue, 23 Nov 2021 22:27:47 +0100 Subject: [PATCH 130/131] X11: Fix run_return --- src/platform_impl/linux/x11/mod.rs | 20 ++++++++++++++------ src/platform_impl/linux/x11/util/queue.rs | 12 ++++++++++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 9c1ad77f8e..a52d705aa1 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -251,6 +251,18 @@ impl EventLoop { ); } + if self.event_queue.has_pending_events() { + // If there are pending events that have already been read from the socket + // but not yet dispatched, we HAVE to handle them now. Otherwise, if the + // application is using run_return, it has no way to get notified that it + // should call run_return again. The application will probably try to wait + // for the socket to become readable but that's no good because the socket + // might be empty while we already have events queued. + // + // TODO: Should we change `cause`? + continue; + } + let start = Instant::now(); let (deadline, timeout); @@ -283,12 +295,8 @@ impl EventLoop { } } - // If the event queue already contains buffered events, we must not - // wait for data on the socket. - if !self.event_queue.has_pending_events() { - self.poll.poll(&mut events, timeout).unwrap(); - events.clear(); - } + self.poll.poll(&mut events, timeout).unwrap(); + events.clear(); let wait_cancelled = deadline.map_or(false, |deadline| Instant::now() < deadline); diff --git a/src/platform_impl/linux/x11/util/queue.rs b/src/platform_impl/linux/x11/util/queue.rs index 9d5f3c422e..9db37c60ee 100644 --- a/src/platform_impl/linux/x11/util/queue.rs +++ b/src/platform_impl/linux/x11/util/queue.rs @@ -17,18 +17,26 @@ impl EventQueue { } pub fn has_pending_events(&mut self) -> bool { - if let Some(event) = self.poll_for_event() { + if let Some(event) = self.poll_for_event2(true) { self.pending = Some(event); } self.pending.is_some() } pub fn poll_for_event(&mut self) -> Option> { + self.poll_for_event2(false) + } + + fn poll_for_event2(&mut self, only_queued: bool) -> Option> { if self.pending.is_some() { return self.pending.take(); } unsafe { - let event = self.xconn.xcb.xcb_poll_for_event(self.xconn.c); + let event = if only_queued { + self.xconn.xcb.xcb_poll_for_queued_event(self.xconn.c) + } else { + self.xconn.xcb.xcb_poll_for_event(self.xconn.c) + }; if event.is_null() { if let Err(e) = self.xconn.errors.check_connection(&self.xconn.xcb) { panic!("The X connection is broken: {}", e); From 6f91c74bcb115eaa5a49882d8d6e4c8be2bfa8de Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 1 Dec 2021 19:39:20 +0100 Subject: [PATCH 131/131] X11: Preserve restore position when switching fullscreen modes --- src/platform_impl/linux/x11/window.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 6bc173260a..df979f97e0 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -679,8 +679,15 @@ impl UnownedWindow { .expect("failed to set video mode"); } - let window_position = self.outer_position_physical(); - self.shared_state.lock().restore_position = Some(window_position); + { + let window_position = self.outer_position_physical(); + let mut ss = self.shared_state.lock(); + // Don't overwrite the restore position if we're switching between + // fullscreen modes. + if ss.restore_position.is_none() { + ss.restore_position = Some(window_position); + } + } let monitor_origin: (i32, i32) = monitor.position().into(); let mut pending: XcbPendingCommands = self .set_position_inner(monitor_origin.0, monitor_origin.1)