diff --git a/winit-android/src/lib.rs b/winit-android/src/lib.rs index 989c9716df..a60934dd5e 100644 --- a/winit-android/src/lib.rs +++ b/winit-android/src/lib.rs @@ -82,6 +82,17 @@ pub use crate::event_loop::{ ActiveEventLoop, EventLoop, EventLoopProxy, PlatformSpecificEventLoopAttributes, PlatformSpecificWindowAttributes, Window, }; +use winit_core::keyboard::{Key, KeyCode, ModifiersState, NativeKey}; + +/// Stub implementation for physical to logical key mapping on Android. +pub fn physical_to_logical_key( + _keycode: KeyCode, + _modifiers: ModifiersState, + _caps_lock: bool, + _num_lock: bool, +) -> Key { + Key::Unidentified(NativeKey::Unidentified) +} /// Additional methods on [`EventLoop`] that are specific to Android. pub trait EventLoopExtAndroid { diff --git a/winit-appkit/src/event.rs b/winit-appkit/src/event.rs index 434a1de761..615a835631 100644 --- a/winit-appkit/src/event.rs +++ b/winit-appkit/src/event.rs @@ -14,8 +14,7 @@ use winit_core::keyboard::{ use super::ffi; -/// Ignores ALL modifiers. -pub fn get_modifierless_char(scancode: u16) -> Key { +pub fn scancode_to_key(scancode: u16, modifiers: u32) -> Key { let Some(ptr) = NonNull::new(unsafe { ffi::TISCopyCurrentKeyboardLayoutInputSource() }) else { tracing::error!("`TISCopyCurrentKeyboardLayoutInputSource` returned null ptr"); return Key::Unidentified(NativeKey::MacOS(scancode)); @@ -35,7 +34,6 @@ pub fn get_modifierless_char(scancode: u16) -> Key { let mut result_len = 0; let mut dead_keys = 0; - let modifiers = 0; let mut string = [0; 16]; let translate_result = unsafe { ffi::UCKeyTranslate( @@ -106,8 +104,8 @@ pub(crate) fn create_key_event(ns_event: &NSEvent, is_press: bool, is_repeat: bo let key_from_code = code_to_key(physical_key, scancode); let (logical_key, key_without_modifiers) = if matches!(key_from_code, Key::Unidentified(_)) { - // `get_modifierless_char/key_without_modifiers` ignores ALL modifiers. - let key_without_modifiers = get_modifierless_char(scancode); + // `scancode_to_key/key_without_modifiers` ignores ALL modifiers with no flags + let key_without_modifiers = scancode_to_key(scancode, 0u32); let modifiers = ns_event.modifierFlags(); let has_ctrl = modifiers.contains(NSEventModifierFlags::Control); @@ -628,3 +626,37 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey { _ => return PhysicalKey::Unidentified(NativeKeyCode::MacOS(scancode as u16)), }) } + +/// Query the logical key for a physical key under the current keyboard layout. +/// +/// Note: `num_lock` is ignored on macOS as it doesn't have traditional NumLock. +pub fn physical_to_logical_key( + keycode: KeyCode, + modifiers: ModifiersState, + caps_lock: bool, + _num_lock: bool, +) -> Key { + let Some(scancode) = physicalkey_to_scancode(PhysicalKey::Code(keycode)) else { + return Key::Unidentified(NativeKey::Unidentified); + }; + + // UCKeyTranslate modifier format (Carbon modifiers >> 8) + let mut uc_modifiers = 0u32; + if modifiers.meta_key() { + uc_modifiers |= 1; // cmdKey + } + if modifiers.shift_key() { + uc_modifiers |= 2; // shiftKey + } + if caps_lock { + uc_modifiers |= 4; // alphaLock + } + if modifiers.alt_key() { + uc_modifiers |= 8; // optionKey + } + if modifiers.control_key() { + uc_modifiers |= 16; // controlKey + } + + scancode_to_key(scancode as u16, uc_modifiers) +} diff --git a/winit-appkit/src/lib.rs b/winit-appkit/src/lib.rs index 949e2f799b..ed32acb543 100644 --- a/winit-appkit/src/lib.rs +++ b/winit-appkit/src/lib.rs @@ -92,7 +92,7 @@ use winit_core::event_loop::ActiveEventLoop; use winit_core::monitor::MonitorHandle; use winit_core::window::{PlatformWindowAttributes, Window}; -pub use self::event::{physicalkey_to_scancode, scancode_to_physicalkey}; +pub use self::event::{physical_to_logical_key, physicalkey_to_scancode, scancode_to_physicalkey}; use self::event_loop::ActiveEventLoop as AppKitActiveEventLoop; pub use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes}; use self::monitor::MonitorHandle as AppKitMonitorHandle; diff --git a/winit-orbital/src/lib.rs b/winit-orbital/src/lib.rs index 71efc82180..5a08513ccd 100644 --- a/winit-orbital/src/lib.rs +++ b/winit-orbital/src/lib.rs @@ -6,6 +6,17 @@ use std::{fmt, str}; pub use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes}; +use winit_core::keyboard::{Key, KeyCode, ModifiersState, NativeKey}; + +/// Stub implementation for physical to logical key mapping on Orbital. +pub fn physical_to_logical_key( + _keycode: KeyCode, + _modifiers: ModifiersState, + _caps_lock: bool, + _num_lock: bool, +) -> Key { + Key::Unidentified(NativeKey::Unidentified) +} macro_rules! os_error { ($error:expr) => {{ winit_core::error::OsError::new(line!(), file!(), $error) }}; diff --git a/winit-uikit/src/lib.rs b/winit-uikit/src/lib.rs index 7ae806f2e3..a8b355967d 100644 --- a/winit-uikit/src/lib.rs +++ b/winit-uikit/src/lib.rs @@ -118,6 +118,17 @@ use winit_core::window::{PlatformWindowAttributes, Window}; pub use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes}; use self::monitor::MonitorHandle as UIKitMonitorHandle; use self::window::Window as UIKitWindow; +use winit_core::keyboard::{Key, KeyCode, ModifiersState, NativeKey}; + +/// Stub implementation for physical to logical key mapping on iOS. +pub fn physical_to_logical_key( + _keycode: KeyCode, + _modifiers: ModifiersState, + _caps_lock: bool, + _num_lock: bool, +) -> Key { + Key::Unidentified(NativeKey::Unidentified) +} /// Additional methods on [`Window`] that are specific to iOS. pub trait WindowExtIOS { diff --git a/winit-web/src/lib.rs b/winit-web/src/lib.rs index 70650846da..e8b1d66e2f 100644 --- a/winit-web/src/lib.rs +++ b/winit-web/src/lib.rs @@ -94,6 +94,17 @@ use winit_core::window::{PlatformWindowAttributes, Window}; pub use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes}; use self::web_sys as backend; use self::window::Window as WebWindow; +use winit_core::keyboard::{Key, KeyCode, ModifiersState, NativeKey}; + +/// Stub implementation for physical to logical key mapping on Web. +pub fn physical_to_logical_key( + _keycode: KeyCode, + _modifiers: ModifiersState, + _caps_lock: bool, + _num_lock: bool, +) -> Key { + Key::Unidentified(NativeKey::Unidentified) +} use crate::cursor::CustomCursorFuture as PlatformCustomCursorFuture; use crate::event_loop::ActiveEventLoop as WebActiveEventLoop; use crate::main_thread::{MainThreadMarker, MainThreadSafe}; diff --git a/winit-win32/src/lib.rs b/winit-win32/src/lib.rs index 569d1f32c1..18548e9c69 100644 --- a/winit-win32/src/lib.rs +++ b/winit-win32/src/lib.rs @@ -39,6 +39,17 @@ use self::icon::{RaiiIcon, SelectedCursor}; pub use self::keyboard::{physicalkey_to_scancode, scancode_to_physicalkey}; pub use self::monitor::{MonitorHandle, VideoModeHandle}; pub use self::window::Window; +use winit_core::keyboard::{Key, KeyCode, ModifiersState, NativeKey}; + +/// Stub implementation for physical to logical key mapping on Windows. +pub fn physical_to_logical_key( + _keycode: KeyCode, + _modifiers: ModifiersState, + _caps_lock: bool, + _num_lock: bool, +) -> Key { + Key::Unidentified(NativeKey::Unidentified) +} /// Window Handle type used by Win32 API pub type HWND = *mut c_void; diff --git a/winit/src/platform/key_map.rs b/winit/src/platform/key_map.rs new file mode 100644 index 0000000000..2b66abca90 --- /dev/null +++ b/winit/src/platform/key_map.rs @@ -0,0 +1,34 @@ +//! Query logical keys from physical key codes under the current keyboard layout. + +use crate::keyboard::{Key, KeyCode, ModifiersState}; + +/// Extension trait for [`KeyCode`] to query logical key mappings. +pub trait KeyCodeExtKeyMap { + /// Returns the logical key that would be produced by this physical key + /// with the given modifiers under the current keyboard layout. + /// + /// # Example + /// + /// ```ignore + /// use winit::keyboard::{KeyCode, ModifiersState}; + /// use winit::platform::key_map::KeyCodeExtKeyMap; + /// + /// // Get what the 'A' key produces with Shift held + /// let key = KeyCode::KeyA.physical_to_logical_key(ModifiersState::SHIFT, false, false); + /// ``` + /// + /// ## Platform Support + /// + /// - **macOS**: Supported, does not use `num_lock`. + /// - **Other platforms**: Not yet implemented, returns `Key::Unidentified`. + fn to_logical_key(self, modifiers: ModifiersState, caps_lock: bool, num_lock: bool) -> Key; +} + +impl KeyCodeExtKeyMap for KeyCode { + #[inline] + fn to_logical_key(self, modifiers: ModifiersState, caps_lock: bool, num_lock: bool) -> Key { + crate::platform_impl::platform::physical_to_logical_key( + self, modifiers, caps_lock, num_lock, + ) + } +} diff --git a/winit/src/platform/mod.rs b/winit/src/platform/mod.rs index 34efd0ffcf..106a7510ea 100644 --- a/winit/src/platform/mod.rs +++ b/winit/src/platform/mod.rs @@ -42,3 +42,16 @@ pub use winit_x11 as x11; pub mod scancode; #[cfg(any(x11_platform, wayland_platform, docsrs))] pub mod startup_notify; + +#[cfg(any( + windows_platform, + macos_platform, + x11_platform, + wayland_platform, + android_platform, + ios_platform, + web_platform, + orbital_platform, + docsrs +))] +pub mod key_map; diff --git a/winit/src/platform_impl/linux/mod.rs b/winit/src/platform_impl/linux/mod.rs index 877e0dc621..f81e8b5f1e 100644 --- a/winit/src/platform_impl/linux/mod.rs +++ b/winit/src/platform_impl/linux/mod.rs @@ -9,6 +9,17 @@ use std::time::Duration; pub(crate) use winit_common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey}; use winit_core::application::ApplicationHandler; +use winit_core::keyboard::{Key, KeyCode, ModifiersState, NativeKey}; + +/// Stub implementation for physical to logical key mapping on Linux. +pub fn physical_to_logical_key( + _keycode: KeyCode, + _modifiers: ModifiersState, + _caps_lock: bool, + _num_lock: bool, +) -> Key { + Key::Unidentified(NativeKey::Unidentified) +} use winit_core::error::{EventLoopError, NotSupportedError}; use winit_core::event_loop::ActiveEventLoop; use winit_core::event_loop::pump_events::PumpStatus;