Skip to main content

sdl_keybridge/
keymod.rs

1//! Modifier bitmask — SDL's `SDL_Keymod` / `KMOD_*` values.
2//!
3//! Values are shared across SDL2 and SDL3. SDL3 adds `LEVEL5 = 0x0004` which
4//! did not exist in SDL2; callers targeting SDL2 should simply not set it.
5
6/// Bitmask of modifier keys currently pressed / latched.
7#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
8#[repr(transparent)]
9pub struct KeyMod(pub u16);
10
11impl KeyMod {
12    pub const NONE: KeyMod = KeyMod(0x0000);
13    pub const LSHIFT: KeyMod = KeyMod(0x0001);
14    pub const RSHIFT: KeyMod = KeyMod(0x0002);
15    pub const LEVEL5: KeyMod = KeyMod(0x0004);
16    pub const LCTRL: KeyMod = KeyMod(0x0040);
17    pub const RCTRL: KeyMod = KeyMod(0x0080);
18    pub const LALT: KeyMod = KeyMod(0x0100);
19    pub const RALT: KeyMod = KeyMod(0x0200);
20    pub const LGUI: KeyMod = KeyMod(0x0400);
21    pub const RGUI: KeyMod = KeyMod(0x0800);
22    pub const NUM: KeyMod = KeyMod(0x1000);
23    pub const CAPS: KeyMod = KeyMod(0x2000);
24    pub const MODE: KeyMod = KeyMod(0x4000);
25    pub const SCROLL: KeyMod = KeyMod(0x8000);
26
27    pub const SHIFT: KeyMod = KeyMod(Self::LSHIFT.0 | Self::RSHIFT.0);
28    pub const CTRL: KeyMod = KeyMod(Self::LCTRL.0 | Self::RCTRL.0);
29    pub const ALT: KeyMod = KeyMod(Self::LALT.0 | Self::RALT.0);
30    pub const GUI: KeyMod = KeyMod(Self::LGUI.0 | Self::RGUI.0);
31
32    #[inline]
33    pub const fn new(raw: u16) -> Self {
34        Self(raw)
35    }
36
37    #[inline]
38    pub const fn raw(self) -> u16 {
39        self.0
40    }
41
42    #[inline]
43    pub const fn contains(self, other: KeyMod) -> bool {
44        (self.0 & other.0) == other.0
45    }
46
47    #[inline]
48    pub const fn intersects(self, other: KeyMod) -> bool {
49        (self.0 & other.0) != 0
50    }
51
52    #[inline]
53    pub const fn is_empty(self) -> bool {
54        self.0 == 0
55    }
56
57    /// True if either Shift key is pressed.
58    #[inline]
59    pub const fn shift(self) -> bool {
60        self.intersects(Self::SHIFT)
61    }
62
63    /// True if either Ctrl key is pressed.
64    #[inline]
65    pub const fn ctrl(self) -> bool {
66        self.intersects(Self::CTRL)
67    }
68
69    /// True if either Alt key is pressed.
70    #[inline]
71    pub const fn alt(self) -> bool {
72        self.intersects(Self::ALT)
73    }
74
75    /// True if either GUI (Win / Cmd / Super) key is pressed.
76    #[inline]
77    pub const fn gui(self) -> bool {
78        self.intersects(Self::GUI)
79    }
80
81    /// True if the "AltGr" state is active — either `MODE` latched or `RALT`
82    /// held (the usual encoding on Linux/Windows keyboards).
83    #[inline]
84    pub const fn altgr(self) -> bool {
85        self.intersects(KeyMod(Self::MODE.0 | Self::RALT.0))
86    }
87
88    /// True if Caps Lock is currently latched.
89    #[inline]
90    pub const fn caps(self) -> bool {
91        self.contains(Self::CAPS)
92    }
93
94    /// True if Num Lock is currently latched.
95    #[inline]
96    pub const fn num(self) -> bool {
97        self.contains(Self::NUM)
98    }
99}
100
101impl std::ops::BitOr for KeyMod {
102    type Output = KeyMod;
103    #[inline]
104    fn bitor(self, rhs: KeyMod) -> KeyMod {
105        KeyMod(self.0 | rhs.0)
106    }
107}
108
109impl std::ops::BitAnd for KeyMod {
110    type Output = KeyMod;
111    #[inline]
112    fn bitand(self, rhs: KeyMod) -> KeyMod {
113        KeyMod(self.0 & rhs.0)
114    }
115}
116
117impl std::ops::BitOrAssign for KeyMod {
118    #[inline]
119    fn bitor_assign(&mut self, rhs: KeyMod) {
120        self.0 |= rhs.0;
121    }
122}
123
124impl std::ops::BitAndAssign for KeyMod {
125    #[inline]
126    fn bitand_assign(&mut self, rhs: KeyMod) {
127        self.0 &= rhs.0;
128    }
129}
130
131impl std::ops::Not for KeyMod {
132    type Output = KeyMod;
133    #[inline]
134    fn not(self) -> KeyMod {
135        KeyMod(!self.0)
136    }
137}
138
139impl From<u16> for KeyMod {
140    #[inline]
141    fn from(value: u16) -> Self {
142        KeyMod(value)
143    }
144}
145
146impl From<KeyMod> for u16 {
147    #[inline]
148    fn from(value: KeyMod) -> Self {
149        value.0
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn sdl_compatible_values() {
159        assert_eq!(KeyMod::LSHIFT.raw(), 0x0001);
160        assert_eq!(KeyMod::LCTRL.raw(), 0x0040);
161        assert_eq!(KeyMod::LALT.raw(), 0x0100);
162        assert_eq!(KeyMod::LGUI.raw(), 0x0400);
163        assert_eq!(KeyMod::NUM.raw(), 0x1000);
164        assert_eq!(KeyMod::CAPS.raw(), 0x2000);
165        assert_eq!(KeyMod::MODE.raw(), 0x4000);
166    }
167
168    #[test]
169    fn modifier_groups() {
170        assert!((KeyMod::LSHIFT | KeyMod::LCTRL).shift());
171        assert!((KeyMod::LSHIFT | KeyMod::LCTRL).ctrl());
172        assert!((KeyMod::RALT).altgr());
173        assert!((KeyMod::MODE).altgr());
174        assert!(!(KeyMod::LALT).altgr());
175    }
176}