Skip to main content

sdl_keybridge/
keycode.rs

1//! Logical keycodes (layout-aware) — SDL's `SDLK_*` constants.
2//!
3//! For printable keys the keycode is the Unicode code point of the base
4//! character produced by the key in the current layout (e.g. `SDLK_A = 'a'`).
5//! Non-printable keys are encoded as `scancode | SCANCODE_MASK`.
6
7use crate::scancode::Scancode;
8
9/// Bit OR-ed into non-printable keycodes to distinguish them from Unicode
10/// code points. Value is identical in SDL2 and SDL3.
11pub const SCANCODE_MASK: u32 = 1 << 30;
12
13/// A logical keycode — either a Unicode code point or `scancode | SCANCODE_MASK`.
14#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
15#[repr(transparent)]
16pub struct Keycode(pub u32);
17
18impl Keycode {
19    #[inline]
20    pub const fn new(raw: u32) -> Self {
21        Self(raw)
22    }
23
24    #[inline]
25    pub const fn raw(self) -> u32 {
26        self.0
27    }
28
29    /// Create a keycode from a scancode (encodes with `SCANCODE_MASK`).
30    #[inline]
31    pub const fn from_scancode(sc: Scancode) -> Self {
32        Self(sc.raw() | SCANCODE_MASK)
33    }
34
35    /// If this keycode is scancode-encoded, return the scancode portion.
36    #[inline]
37    pub const fn to_scancode(self) -> Option<Scancode> {
38        if self.0 & SCANCODE_MASK != 0 {
39            Some(Scancode::new(self.0 & !SCANCODE_MASK))
40        } else {
41            None
42        }
43    }
44
45    /// If this keycode is a printable Unicode code point, return it as a char.
46    #[inline]
47    pub fn to_char(self) -> Option<char> {
48        if self.0 & SCANCODE_MASK != 0 {
49            None
50        } else {
51            char::from_u32(self.0)
52        }
53    }
54}
55
56impl From<u32> for Keycode {
57    #[inline]
58    fn from(value: u32) -> Self {
59        Keycode(value)
60    }
61}
62
63impl From<Keycode> for u32 {
64    #[inline]
65    fn from(value: Keycode) -> Self {
66        value.0
67    }
68}
69
70impl From<char> for Keycode {
71    #[inline]
72    fn from(c: char) -> Self {
73        Keycode(c as u32)
74    }
75}
76
77/// Common `SDLK_*` constants. Values identical in SDL2 and SDL3.
78impl Keycode {
79    pub const UNKNOWN: Keycode = Keycode(0);
80    pub const RETURN: Keycode = Keycode(b'\r' as u32);
81    pub const ESCAPE: Keycode = Keycode::from_scancode(Scancode::ESCAPE);
82    pub const BACKSPACE: Keycode = Keycode(0x08);
83    pub const TAB: Keycode = Keycode(b'\t' as u32);
84    pub const SPACE: Keycode = Keycode(b' ' as u32);
85
86    pub const EXCLAIM: Keycode = Keycode(b'!' as u32);
87    pub const QUOTEDBL: Keycode = Keycode(b'"' as u32);
88    pub const HASH: Keycode = Keycode(b'#' as u32);
89    pub const DOLLAR: Keycode = Keycode(b'$' as u32);
90    pub const PERCENT: Keycode = Keycode(b'%' as u32);
91    pub const AMPERSAND: Keycode = Keycode(b'&' as u32);
92    pub const QUOTE: Keycode = Keycode(b'\'' as u32);
93    pub const LEFTPAREN: Keycode = Keycode(b'(' as u32);
94    pub const RIGHTPAREN: Keycode = Keycode(b')' as u32);
95    pub const ASTERISK: Keycode = Keycode(b'*' as u32);
96    pub const PLUS: Keycode = Keycode(b'+' as u32);
97    pub const COMMA: Keycode = Keycode(b',' as u32);
98    pub const MINUS: Keycode = Keycode(b'-' as u32);
99    pub const PERIOD: Keycode = Keycode(b'.' as u32);
100    pub const SLASH: Keycode = Keycode(b'/' as u32);
101
102    pub const NUM_0: Keycode = Keycode(b'0' as u32);
103    pub const NUM_1: Keycode = Keycode(b'1' as u32);
104    pub const NUM_2: Keycode = Keycode(b'2' as u32);
105    pub const NUM_3: Keycode = Keycode(b'3' as u32);
106    pub const NUM_4: Keycode = Keycode(b'4' as u32);
107    pub const NUM_5: Keycode = Keycode(b'5' as u32);
108    pub const NUM_6: Keycode = Keycode(b'6' as u32);
109    pub const NUM_7: Keycode = Keycode(b'7' as u32);
110    pub const NUM_8: Keycode = Keycode(b'8' as u32);
111    pub const NUM_9: Keycode = Keycode(b'9' as u32);
112
113    pub const COLON: Keycode = Keycode(b':' as u32);
114    pub const SEMICOLON: Keycode = Keycode(b';' as u32);
115    pub const LESS: Keycode = Keycode(b'<' as u32);
116    pub const EQUALS: Keycode = Keycode(b'=' as u32);
117    pub const GREATER: Keycode = Keycode(b'>' as u32);
118    pub const QUESTION: Keycode = Keycode(b'?' as u32);
119    pub const AT: Keycode = Keycode(b'@' as u32);
120    pub const LEFTBRACKET: Keycode = Keycode(b'[' as u32);
121    pub const BACKSLASH: Keycode = Keycode(b'\\' as u32);
122    pub const RIGHTBRACKET: Keycode = Keycode(b']' as u32);
123    pub const CARET: Keycode = Keycode(b'^' as u32);
124    pub const UNDERSCORE: Keycode = Keycode(b'_' as u32);
125    pub const BACKQUOTE: Keycode = Keycode(b'`' as u32);
126
127    // Letters — lowercase Unicode code points.
128    pub const A: Keycode = Keycode(b'a' as u32);
129    pub const B: Keycode = Keycode(b'b' as u32);
130    pub const C: Keycode = Keycode(b'c' as u32);
131    pub const D: Keycode = Keycode(b'd' as u32);
132    pub const E: Keycode = Keycode(b'e' as u32);
133    pub const F: Keycode = Keycode(b'f' as u32);
134    pub const G: Keycode = Keycode(b'g' as u32);
135    pub const H: Keycode = Keycode(b'h' as u32);
136    pub const I: Keycode = Keycode(b'i' as u32);
137    pub const J: Keycode = Keycode(b'j' as u32);
138    pub const K: Keycode = Keycode(b'k' as u32);
139    pub const L: Keycode = Keycode(b'l' as u32);
140    pub const M: Keycode = Keycode(b'm' as u32);
141    pub const N: Keycode = Keycode(b'n' as u32);
142    pub const O: Keycode = Keycode(b'o' as u32);
143    pub const P: Keycode = Keycode(b'p' as u32);
144    pub const Q: Keycode = Keycode(b'q' as u32);
145    pub const R: Keycode = Keycode(b'r' as u32);
146    pub const S: Keycode = Keycode(b's' as u32);
147    pub const T: Keycode = Keycode(b't' as u32);
148    pub const U: Keycode = Keycode(b'u' as u32);
149    pub const V: Keycode = Keycode(b'v' as u32);
150    pub const W: Keycode = Keycode(b'w' as u32);
151    pub const X: Keycode = Keycode(b'x' as u32);
152    pub const Y: Keycode = Keycode(b'y' as u32);
153    pub const Z: Keycode = Keycode(b'z' as u32);
154
155    // Non-printable — encoded via SCANCODE_MASK.
156    pub const CAPSLOCK: Keycode = Keycode::from_scancode(Scancode::CAPSLOCK);
157
158    pub const F1: Keycode = Keycode::from_scancode(Scancode::F1);
159    pub const F2: Keycode = Keycode::from_scancode(Scancode::F2);
160    pub const F3: Keycode = Keycode::from_scancode(Scancode::F3);
161    pub const F4: Keycode = Keycode::from_scancode(Scancode::F4);
162    pub const F5: Keycode = Keycode::from_scancode(Scancode::F5);
163    pub const F6: Keycode = Keycode::from_scancode(Scancode::F6);
164    pub const F7: Keycode = Keycode::from_scancode(Scancode::F7);
165    pub const F8: Keycode = Keycode::from_scancode(Scancode::F8);
166    pub const F9: Keycode = Keycode::from_scancode(Scancode::F9);
167    pub const F10: Keycode = Keycode::from_scancode(Scancode::F10);
168    pub const F11: Keycode = Keycode::from_scancode(Scancode::F11);
169    pub const F12: Keycode = Keycode::from_scancode(Scancode::F12);
170    pub const F13: Keycode = Keycode::from_scancode(Scancode::F13);
171    pub const F14: Keycode = Keycode::from_scancode(Scancode::F14);
172    pub const F15: Keycode = Keycode::from_scancode(Scancode::F15);
173    pub const F16: Keycode = Keycode::from_scancode(Scancode::F16);
174    pub const F17: Keycode = Keycode::from_scancode(Scancode::F17);
175    pub const F18: Keycode = Keycode::from_scancode(Scancode::F18);
176    pub const F19: Keycode = Keycode::from_scancode(Scancode::F19);
177    pub const F20: Keycode = Keycode::from_scancode(Scancode::F20);
178    pub const F21: Keycode = Keycode::from_scancode(Scancode::F21);
179    pub const F22: Keycode = Keycode::from_scancode(Scancode::F22);
180    pub const F23: Keycode = Keycode::from_scancode(Scancode::F23);
181    pub const F24: Keycode = Keycode::from_scancode(Scancode::F24);
182
183    pub const PRINT_SCREEN: Keycode = Keycode::from_scancode(Scancode::PRINT_SCREEN);
184    pub const SCROLL_LOCK: Keycode = Keycode::from_scancode(Scancode::SCROLL_LOCK);
185    pub const PAUSE: Keycode = Keycode::from_scancode(Scancode::PAUSE);
186    pub const INSERT: Keycode = Keycode::from_scancode(Scancode::INSERT);
187    pub const HOME: Keycode = Keycode::from_scancode(Scancode::HOME);
188    pub const PAGE_UP: Keycode = Keycode::from_scancode(Scancode::PAGE_UP);
189    pub const DELETE: Keycode = Keycode(0x7f);
190    pub const END: Keycode = Keycode::from_scancode(Scancode::END);
191    pub const PAGE_DOWN: Keycode = Keycode::from_scancode(Scancode::PAGE_DOWN);
192    pub const RIGHT: Keycode = Keycode::from_scancode(Scancode::RIGHT);
193    pub const LEFT: Keycode = Keycode::from_scancode(Scancode::LEFT);
194    pub const DOWN: Keycode = Keycode::from_scancode(Scancode::DOWN);
195    pub const UP: Keycode = Keycode::from_scancode(Scancode::UP);
196
197    pub const NUM_LOCK_CLEAR: Keycode = Keycode::from_scancode(Scancode::NUM_LOCK_CLEAR);
198    pub const KP_DIVIDE: Keycode = Keycode::from_scancode(Scancode::KP_DIVIDE);
199    pub const KP_MULTIPLY: Keycode = Keycode::from_scancode(Scancode::KP_MULTIPLY);
200    pub const KP_MINUS: Keycode = Keycode::from_scancode(Scancode::KP_MINUS);
201    pub const KP_PLUS: Keycode = Keycode::from_scancode(Scancode::KP_PLUS);
202    pub const KP_ENTER: Keycode = Keycode::from_scancode(Scancode::KP_ENTER);
203    pub const KP_1: Keycode = Keycode::from_scancode(Scancode::KP_1);
204    pub const KP_2: Keycode = Keycode::from_scancode(Scancode::KP_2);
205    pub const KP_3: Keycode = Keycode::from_scancode(Scancode::KP_3);
206    pub const KP_4: Keycode = Keycode::from_scancode(Scancode::KP_4);
207    pub const KP_5: Keycode = Keycode::from_scancode(Scancode::KP_5);
208    pub const KP_6: Keycode = Keycode::from_scancode(Scancode::KP_6);
209    pub const KP_7: Keycode = Keycode::from_scancode(Scancode::KP_7);
210    pub const KP_8: Keycode = Keycode::from_scancode(Scancode::KP_8);
211    pub const KP_9: Keycode = Keycode::from_scancode(Scancode::KP_9);
212    pub const KP_0: Keycode = Keycode::from_scancode(Scancode::KP_0);
213    pub const KP_PERIOD: Keycode = Keycode::from_scancode(Scancode::KP_PERIOD);
214    pub const KP_EQUALS: Keycode = Keycode::from_scancode(Scancode::KP_EQUALS);
215
216    pub const APPLICATION: Keycode = Keycode::from_scancode(Scancode::APPLICATION);
217    pub const MENU: Keycode = Keycode::from_scancode(Scancode::MENU);
218
219    pub const LCTRL: Keycode = Keycode::from_scancode(Scancode::LCTRL);
220    pub const LSHIFT: Keycode = Keycode::from_scancode(Scancode::LSHIFT);
221    pub const LALT: Keycode = Keycode::from_scancode(Scancode::LALT);
222    pub const LGUI: Keycode = Keycode::from_scancode(Scancode::LGUI);
223    pub const RCTRL: Keycode = Keycode::from_scancode(Scancode::RCTRL);
224    pub const RSHIFT: Keycode = Keycode::from_scancode(Scancode::RSHIFT);
225    pub const RALT: Keycode = Keycode::from_scancode(Scancode::RALT);
226    pub const RGUI: Keycode = Keycode::from_scancode(Scancode::RGUI);
227}
228
229#[cfg(test)]
230mod tests {
231    use super::*;
232
233    #[test]
234    fn printable_keycodes_are_unicode() {
235        assert_eq!(Keycode::A.raw(), 0x61);
236        assert_eq!(Keycode::Z.raw(), 0x7A);
237        assert_eq!(Keycode::SPACE.raw(), 0x20);
238        assert_eq!(Keycode::A.to_char(), Some('a'));
239        assert_eq!(Keycode::SPACE.to_scancode(), None);
240    }
241
242    #[test]
243    fn nonprintable_keycodes_use_scancode_mask() {
244        assert!(Keycode::ESCAPE.raw() & SCANCODE_MASK != 0);
245        assert_eq!(Keycode::ESCAPE.to_scancode(), Some(Scancode::ESCAPE));
246        assert_eq!(Keycode::F5.to_scancode(), Some(Scancode::F5));
247        assert_eq!(Keycode::F5.to_char(), None);
248    }
249}