winit_core/
keyboard.rs

1//! Types related to the keyboard.
2
3use bitflags::bitflags;
4pub use keyboard_types::{Code as KeyCode, Location as KeyLocation, NamedKey};
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7pub use smol_str::SmolStr;
8
9/// Contains the platform-native physical key identifier
10///
11/// The exact values vary from platform to platform (which is part of why this is a per-platform
12/// enum), but the values are primarily tied to the key's physical location on the keyboard.
13///
14/// This enum is primarily used to store raw keycodes when Winit doesn't map a given native
15/// physical key identifier to a meaningful [`KeyCode`] variant. In the presence of identifiers we
16/// haven't mapped for you yet, this lets you use use [`KeyCode`] to:
17///
18/// - Correctly match key press and release events.
19/// - On non-Web platforms, support assigning keybinds to virtually any key through a UI.
20#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22pub enum NativeKeyCode {
23    Unidentified,
24    /// An Android "scancode".
25    Android(u32),
26    /// A macOS "scancode".
27    MacOS(u16),
28    /// A Windows "scancode".
29    Windows(u16),
30    /// An XKB "keycode".
31    Xkb(u32),
32}
33
34impl std::fmt::Debug for NativeKeyCode {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        use NativeKeyCode::{Android, MacOS, Unidentified, Windows, Xkb};
37        let mut debug_tuple;
38        match self {
39            Unidentified => {
40                debug_tuple = f.debug_tuple("Unidentified");
41            },
42            Android(code) => {
43                debug_tuple = f.debug_tuple("Android");
44                debug_tuple.field(&format_args!("0x{code:04X}"));
45            },
46            MacOS(code) => {
47                debug_tuple = f.debug_tuple("MacOS");
48                debug_tuple.field(&format_args!("0x{code:04X}"));
49            },
50            Windows(code) => {
51                debug_tuple = f.debug_tuple("Windows");
52                debug_tuple.field(&format_args!("0x{code:04X}"));
53            },
54            Xkb(code) => {
55                debug_tuple = f.debug_tuple("Xkb");
56                debug_tuple.field(&format_args!("0x{code:04X}"));
57            },
58        }
59        debug_tuple.finish()
60    }
61}
62
63/// Contains the platform-native logical key identifier
64///
65/// Exactly what that means differs from platform to platform, but the values are to some degree
66/// tied to the currently active keyboard layout. The same key on the same keyboard may also report
67/// different values on different platforms, which is one of the reasons this is a per-platform
68/// enum.
69///
70/// This enum is primarily used to store raw keysym when Winit doesn't map a given native logical
71/// key identifier to a meaningful [`Key`] variant. This lets you use [`Key`], and let the user
72/// define keybinds which work in the presence of identifiers we haven't mapped for you yet.
73#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
74#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
75pub enum NativeKey {
76    Unidentified,
77    /// An Android "keycode", which is similar to a "virtual-key code" on Windows.
78    Android(u32),
79    /// A macOS "scancode". There does not appear to be any direct analogue to either keysyms or
80    /// "virtual-key" codes in macOS, so we report the scancode instead.
81    MacOS(u16),
82    /// A Windows "virtual-key code".
83    Windows(u16),
84    /// An XKB "keysym".
85    Xkb(u32),
86    /// A "key value string".
87    Web(SmolStr),
88}
89
90impl std::fmt::Debug for NativeKey {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        use NativeKey::{Android, MacOS, Unidentified, Web, Windows, Xkb};
93        let mut debug_tuple;
94        match self {
95            Unidentified => {
96                debug_tuple = f.debug_tuple("Unidentified");
97            },
98            Android(code) => {
99                debug_tuple = f.debug_tuple("Android");
100                debug_tuple.field(&format_args!("0x{code:04X}"));
101            },
102            MacOS(code) => {
103                debug_tuple = f.debug_tuple("MacOS");
104                debug_tuple.field(&format_args!("0x{code:04X}"));
105            },
106            Windows(code) => {
107                debug_tuple = f.debug_tuple("Windows");
108                debug_tuple.field(&format_args!("0x{code:04X}"));
109            },
110            Xkb(code) => {
111                debug_tuple = f.debug_tuple("Xkb");
112                debug_tuple.field(&format_args!("0x{code:04X}"));
113            },
114            Web(code) => {
115                debug_tuple = f.debug_tuple("Web");
116                debug_tuple.field(code);
117            },
118        }
119        debug_tuple.finish()
120    }
121}
122
123impl From<NativeKeyCode> for NativeKey {
124    #[inline]
125    fn from(code: NativeKeyCode) -> Self {
126        match code {
127            NativeKeyCode::Unidentified => NativeKey::Unidentified,
128            NativeKeyCode::Android(x) => NativeKey::Android(x),
129            NativeKeyCode::MacOS(x) => NativeKey::MacOS(x),
130            NativeKeyCode::Windows(x) => NativeKey::Windows(x),
131            NativeKeyCode::Xkb(x) => NativeKey::Xkb(x),
132        }
133    }
134}
135
136impl PartialEq<NativeKey> for NativeKeyCode {
137    #[allow(clippy::cmp_owned)] // uses less code than direct match; target is stack allocated
138    #[inline]
139    fn eq(&self, rhs: &NativeKey) -> bool {
140        NativeKey::from(*self) == *rhs
141    }
142}
143
144impl PartialEq<NativeKeyCode> for NativeKey {
145    #[inline]
146    fn eq(&self, rhs: &NativeKeyCode) -> bool {
147        rhs == self
148    }
149}
150
151/// Represents the location of a physical key.
152///
153/// Winit will not emit [`KeyCode::Unidentified`] when it cannot recognize the key, instead it will
154/// emit [`PhysicalKey::Unidentified`] with additional data about the key.
155#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
156#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
157pub enum PhysicalKey {
158    /// A known key code
159    Code(KeyCode),
160    /// This variant is used when the key cannot be translated to a [`KeyCode`]
161    ///
162    /// The native keycode is provided (if available) so you're able to more reliably match
163    /// key-press and key-release events by hashing the [`PhysicalKey`]. It is also possible to use
164    /// this for keybinds for non-standard keys, but such keybinds are tied to a given platform.
165    Unidentified(NativeKeyCode),
166}
167
168impl From<KeyCode> for PhysicalKey {
169    #[inline]
170    fn from(code: KeyCode) -> Self {
171        PhysicalKey::Code(code)
172    }
173}
174
175impl From<PhysicalKey> for KeyCode {
176    #[inline]
177    fn from(key: PhysicalKey) -> Self {
178        match key {
179            PhysicalKey::Code(code) => code,
180            PhysicalKey::Unidentified(_) => KeyCode::Unidentified,
181        }
182    }
183}
184
185impl From<NativeKeyCode> for PhysicalKey {
186    #[inline]
187    fn from(code: NativeKeyCode) -> Self {
188        PhysicalKey::Unidentified(code)
189    }
190}
191
192impl PartialEq<KeyCode> for PhysicalKey {
193    #[inline]
194    fn eq(&self, rhs: &KeyCode) -> bool {
195        match self {
196            PhysicalKey::Code(code) => code == rhs,
197            _ => false,
198        }
199    }
200}
201
202impl PartialEq<PhysicalKey> for KeyCode {
203    #[inline]
204    fn eq(&self, rhs: &PhysicalKey) -> bool {
205        rhs == self
206    }
207}
208
209impl PartialEq<NativeKeyCode> for PhysicalKey {
210    #[inline]
211    fn eq(&self, rhs: &NativeKeyCode) -> bool {
212        match self {
213            PhysicalKey::Unidentified(code) => code == rhs,
214            _ => false,
215        }
216    }
217}
218
219impl PartialEq<PhysicalKey> for NativeKeyCode {
220    #[inline]
221    fn eq(&self, rhs: &PhysicalKey) -> bool {
222        rhs == self
223    }
224}
225
226/// Key represents the meaning of a keypress.
227///
228/// This is a superset of the UI Events Specification's [`KeyboardEvent.key`] with
229/// additions:
230/// - All simple variants are wrapped under the `Named` variant
231/// - The `Unidentified` variant here, can still identify a key through it's `NativeKeyCode`.
232/// - The `Dead` variant here, can specify the character which is inserted when pressing the
233///   dead-key twice.
234///
235/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/
236#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
237#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
238pub enum Key<Str = SmolStr> {
239    /// A simple (unparameterised) action
240    Named(NamedKey),
241
242    /// A key string that corresponds to the character typed by the user, taking into account the
243    /// user’s current locale setting, and any system-level keyboard mapping overrides that are in
244    /// effect.
245    Character(Str),
246
247    /// This variant is used when the key cannot be translated to any other variant.
248    ///
249    /// The native key is provided (if available) in order to allow the user to specify keybindings
250    /// for keys which are not defined by this API, mainly through some sort of UI.
251    Unidentified(NativeKey),
252
253    /// Contains the text representation of the dead-key when available.
254    ///
255    /// ## Platform-specific
256    /// - **Web:** Always contains `None`
257    Dead(Option<char>),
258}
259
260impl From<NamedKey> for Key {
261    #[inline]
262    fn from(action: NamedKey) -> Self {
263        Key::Named(action)
264    }
265}
266
267impl From<NativeKey> for Key {
268    #[inline]
269    fn from(code: NativeKey) -> Self {
270        Key::Unidentified(code)
271    }
272}
273
274impl<Str> PartialEq<NamedKey> for Key<Str> {
275    #[inline]
276    fn eq(&self, rhs: &NamedKey) -> bool {
277        match self {
278            Key::Named(a) => a == rhs,
279            _ => false,
280        }
281    }
282}
283
284impl<Str: PartialEq<str>> PartialEq<str> for Key<Str> {
285    #[inline]
286    fn eq(&self, rhs: &str) -> bool {
287        match self {
288            Key::Character(s) => s == rhs,
289            _ => false,
290        }
291    }
292}
293
294impl<Str: PartialEq<str>> PartialEq<&str> for Key<Str> {
295    #[inline]
296    fn eq(&self, rhs: &&str) -> bool {
297        self == *rhs
298    }
299}
300
301impl<Str> PartialEq<NativeKey> for Key<Str> {
302    #[inline]
303    fn eq(&self, rhs: &NativeKey) -> bool {
304        match self {
305            Key::Unidentified(code) => code == rhs,
306            _ => false,
307        }
308    }
309}
310
311impl<Str> PartialEq<Key<Str>> for NativeKey {
312    #[inline]
313    fn eq(&self, rhs: &Key<Str>) -> bool {
314        rhs == self
315    }
316}
317
318impl Key<SmolStr> {
319    /// Convert `Key::Character(SmolStr)` to `Key::Character(&str)` so you can more easily match on
320    /// `Key`. All other variants remain unchanged.
321    pub fn as_ref(&self) -> Key<&str> {
322        match self {
323            Key::Named(a) => Key::Named(*a),
324            Key::Character(ch) => Key::Character(ch.as_str()),
325            Key::Dead(d) => Key::Dead(*d),
326            Key::Unidentified(u) => Key::Unidentified(u.clone()),
327        }
328    }
329}
330
331impl Key {
332    /// Convert a key to its approximate textual equivalent.
333    ///
334    /// # Examples
335    ///
336    /// ```
337    /// # #[cfg(target_family = "wasm")]
338    /// # wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
339    /// # #[cfg_attr(target_family = "wasm", wasm_bindgen_test::wasm_bindgen_test)]
340    /// # fn main() {
341    /// use winit_core::keyboard::{Key, NamedKey};
342    ///
343    /// assert_eq!(Key::Character("a".into()).to_text(), Some("a"));
344    /// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r"));
345    /// assert_eq!(Key::Named(NamedKey::F20).to_text(), None);
346    /// # }
347    /// ```
348    pub fn to_text(&self) -> Option<&str> {
349        match self {
350            Key::Named(action) => match action {
351                NamedKey::Enter => Some("\r"),
352                NamedKey::Backspace => Some("\x08"),
353                NamedKey::Tab => Some("\t"),
354                NamedKey::Escape => Some("\x1b"),
355                _ => None,
356            },
357            Key::Character(ch) => Some(ch.as_str()),
358            _ => None,
359        }
360    }
361}
362
363bitflags! {
364    /// Represents the current logical state of the keyboard modifiers
365    ///
366    /// Each flag represents a modifier and is set if this modifier is active.
367    ///
368    /// Note that the modifier key can be physically released with the modifier
369    /// still being marked as active, as in the case of sticky modifiers.
370    /// See [`ModifiersKeyState`] for more details on what "sticky" means.
371    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
372    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
373    pub struct ModifiersState: u32 {
374        /// The "shift" key.
375        const SHIFT = 0b100;
376        /// The "control" key.
377        const CONTROL = 0b100 << 3;
378        /// The "alt" key.
379        const ALT = 0b100 << 6;
380        /// This is the "windows" key on PC and "command" key on Mac.
381        const META = 0b100 << 9;
382        #[deprecated = "use META instead"]
383        const SUPER = Self::META.bits();
384    }
385}
386
387impl ModifiersState {
388    /// Returns whether the shift modifier is active.
389    pub fn shift_key(&self) -> bool {
390        self.intersects(Self::SHIFT)
391    }
392
393    /// Returns whether the control modifier is active.
394    pub fn control_key(&self) -> bool {
395        self.intersects(Self::CONTROL)
396    }
397
398    /// Returns whether the alt modifier is active.
399    pub fn alt_key(&self) -> bool {
400        self.intersects(Self::ALT)
401    }
402
403    /// Returns whether the meta modifier is active.
404    pub fn meta_key(&self) -> bool {
405        self.intersects(Self::META)
406    }
407}
408
409/// The logical state of the particular modifiers key.
410///
411/// NOTE: while the modifier can only be in a binary active/inactive state, it might be helpful to
412/// note the context re. how its state changes by physical key events.
413///
414/// `↓` / `↑` denote physical press/release[^1]:
415///
416/// | Type              | Activated           | Deactivated | Comment |
417/// | ----------------- | :-----------------: | :---------: | ------- |
418/// | __Regular__       | `↓`                 | `↑`         | Active while being held |
419/// | __Sticky__        | `↓`                 | `↓` unless lock is enabled<br>`↓`/`↑`[^2] __non__-sticky key | Temporarily "stuck"; other `Sticky` keys have no effect |
420/// | __Sticky Locked__ | `↓` <br>if `Sticky` | `↓`         | Similar to `Toggle`, but deactivating `↓` turns on `Regular` effect |
421/// | __Toggle__        | `↓`                 | `↓`         | `↑` from the activating `↓` has no effect |
422///
423/// `Sticky` effect avoids the need to press and hold multiple modifiers for a single shortcut and
424/// is usually a platform-wide option that affects modifiers _commonly_ used in shortcuts:
425/// <kbd>Shift</kbd>, <kbd>Control</kbd>, <kbd>Alt</kbd>, <kbd>Meta</kbd>.
426///
427/// `Toggle` type is typically a property of a modifier, for example, <kbd>Caps Lock</kbd>.
428///
429/// These active states are __not__ differentiated here.
430///
431/// [^1]: For virtual/on-screen keyboards physical press/release can be a mouse click or a finger tap or a voice command.
432/// [^2]: platform-dependent
433#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
434#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
435pub enum ModifiersKeyState {
436    /// The particular modifier is active or logically, but not necessarily physically, pressed.
437    Pressed,
438    /// The state of the key is unknown.
439    ///
440    /// Can include cases when the key is active or logically pressed, for example, when a sticky
441    /// **Shift** is active, the OS might not preserve information that it was activated by
442    /// RightShift, so the state of [`ModifiersKeys::RSHIFT`] will be unknown while the state
443    /// of [`ModifiersState::SHIFT`] will be active.
444    #[default]
445    Unknown,
446}
447
448// NOTE: the exact modifier key is not used to represent modifiers state in the
449// first place due to a fact that modifiers state could be changed without any
450// key being pressed and on some platforms like Wayland/X11 which key resulted
451// in modifiers change is hidden, also, not that it really matters.
452//
453// The reason this API is even exposed is mostly to provide a way for users
454// to treat modifiers differently based on their position, which is required
455// on macOS due to their AltGr/Option situation.
456bitflags! {
457    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
458    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
459    pub struct ModifiersKeys: u8 {
460        const LSHIFT   = 0b0000_0001;
461        const RSHIFT   = 0b0000_0010;
462        const LCONTROL = 0b0000_0100;
463        const RCONTROL = 0b0000_1000;
464        const LALT     = 0b0001_0000;
465        const RALT     = 0b0010_0000;
466        const LMETA    = 0b0100_0000;
467        const RMETA    = 0b1000_0000;
468        #[deprecated = "use LMETA instead"]
469        const LSUPER   = Self::LMETA.bits();
470        #[deprecated = "use RMETA instead"]
471        const RSUPER   = Self::RMETA.bits();
472    }
473}