From 97989a2c6abe825f7ae9a3c91d0a7ec59486916e Mon Sep 17 00:00:00 2001 From: Aceeri Date: Mon, 29 Dec 2025 18:03:12 -0800 Subject: [PATCH 1/4] macos implementation of keycode_to_key --- winit-appkit/src/event.rs | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/winit-appkit/src/event.rs b/winit-appkit/src/event.rs index 434a1de761..3fbba50c92 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,30 @@ 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. +pub fn keycode_to_key(keycode: KeyCode, modifiers: ModifiersState, caps_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) +} From 0d96eecfb5866d1c0c2189548483d8f716cec86b Mon Sep 17 00:00:00 2001 From: Aceeri Date: Mon, 29 Dec 2025 18:04:32 -0800 Subject: [PATCH 2/4] Expose keycode_to_key --- winit-appkit/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winit-appkit/src/lib.rs b/winit-appkit/src/lib.rs index 949e2f799b..de2afe9af3 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::{keycode_to_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; From 365a4eeb8c92f362e631d08f276f4db7cee81752 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Mon, 29 Dec 2025 20:14:42 -0800 Subject: [PATCH 3/4] Rename to physical_to_logical_key, add stubs for other platforms --- winit-android/src/lib.rs | 11 ++++++ winit-appkit/src/event.rs | 9 ++++- winit-appkit/src/lib.rs | 2 +- winit-orbital/src/lib.rs | 11 ++++++ winit-uikit/src/lib.rs | 11 ++++++ winit-web/src/lib.rs | 11 ++++++ winit-win32/src/lib.rs | 11 ++++++ winit/src/platform/key_map.rs | 51 ++++++++++++++++++++++++++++ winit/src/platform/mod.rs | 13 +++++++ winit/src/platform_impl/linux/mod.rs | 11 ++++++ 10 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 winit/src/platform/key_map.rs 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 3fbba50c92..615a835631 100644 --- a/winit-appkit/src/event.rs +++ b/winit-appkit/src/event.rs @@ -628,7 +628,14 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey { } /// Query the logical key for a physical key under the current keyboard layout. -pub fn keycode_to_key(keycode: KeyCode, modifiers: ModifiersState, caps_lock: bool) -> Key { +/// +/// 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); }; diff --git a/winit-appkit/src/lib.rs b/winit-appkit/src/lib.rs index de2afe9af3..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::{keycode_to_key, 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..7f576d426f --- /dev/null +++ b/winit/src/platform/key_map.rs @@ -0,0 +1,51 @@ +//! Query logical keys from physical key codes under the current keyboard layout. +//! +//! This module provides the [`KeyCodeExtKeyMap`] trait which extends [`KeyCode`] +//! with methods to query what logical key a physical key produces. +//! +//! ## Platform Support +//! +//! - **macOS**: Fully supported via `UCKeyTranslate`. +//! - **Other platforms**: Not yet implemented, returns `Key::Unidentified`. + +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. + /// + /// # Arguments + /// + /// * `modifiers` - The modifier key state (Shift, Ctrl, Alt, Meta) + /// * `caps_lock` - Whether Caps Lock is toggled on + /// * `num_lock` - Whether Num Lock is toggled on + /// + /// # 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); + /// ``` + fn physical_to_logical_key(self, modifiers: ModifiersState, caps_lock: bool, num_lock: bool) -> Key; + + /// Returns the logical key without any modifiers applied. + /// + /// Equivalent to `physical_to_logical_key(ModifiersState::empty(), false, false)`. + fn physical_to_logical_key_unmodified(self) -> Key; +} + +impl KeyCodeExtKeyMap for KeyCode { + #[inline] + fn physical_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) + } + + #[inline] + fn physical_to_logical_key_unmodified(self) -> Key { + self.physical_to_logical_key(ModifiersState::empty(), false, false) + } +} 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; From 30b0c75ba5276cc6e254d7040fc6985703d6b2c0 Mon Sep 17 00:00:00 2001 From: Aceeri Date: Mon, 29 Dec 2025 20:23:06 -0800 Subject: [PATCH 4/4] Better docs --- winit/src/platform/key_map.rs | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/winit/src/platform/key_map.rs b/winit/src/platform/key_map.rs index 7f576d426f..2b66abca90 100644 --- a/winit/src/platform/key_map.rs +++ b/winit/src/platform/key_map.rs @@ -1,12 +1,4 @@ //! Query logical keys from physical key codes under the current keyboard layout. -//! -//! This module provides the [`KeyCodeExtKeyMap`] trait which extends [`KeyCode`] -//! with methods to query what logical key a physical key produces. -//! -//! ## Platform Support -//! -//! - **macOS**: Fully supported via `UCKeyTranslate`. -//! - **Other platforms**: Not yet implemented, returns `Key::Unidentified`. use crate::keyboard::{Key, KeyCode, ModifiersState}; @@ -15,12 +7,6 @@ pub trait KeyCodeExtKeyMap { /// Returns the logical key that would be produced by this physical key /// with the given modifiers under the current keyboard layout. /// - /// # Arguments - /// - /// * `modifiers` - The modifier key state (Shift, Ctrl, Alt, Meta) - /// * `caps_lock` - Whether Caps Lock is toggled on - /// * `num_lock` - Whether Num Lock is toggled on - /// /// # Example /// /// ```ignore @@ -30,22 +16,19 @@ pub trait KeyCodeExtKeyMap { /// // Get what the 'A' key produces with Shift held /// let key = KeyCode::KeyA.physical_to_logical_key(ModifiersState::SHIFT, false, false); /// ``` - fn physical_to_logical_key(self, modifiers: ModifiersState, caps_lock: bool, num_lock: bool) -> Key; - - /// Returns the logical key without any modifiers applied. /// - /// Equivalent to `physical_to_logical_key(ModifiersState::empty(), false, false)`. - fn physical_to_logical_key_unmodified(self) -> Key; + /// ## 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 physical_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) - } - - #[inline] - fn physical_to_logical_key_unmodified(self) -> Key { - self.physical_to_logical_key(ModifiersState::empty(), false, false) + 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, + ) } }