Skip to main content

wkb/
modifiers.rs

1use std::{collections::BTreeMap, fmt};
2
3// Max modifier slots — keymaps typically have 10-20 modifiers
4const MAX_MOD_SLOTS: usize = 32;
5
6// Key constants
7pub const LEFT_CTRL: u32 = 29;
8pub const LEFT_SHIFT: u32 = 42;
9pub const RIGHT_SHIFT: u32 = 54;
10pub const RIGHT_CTRL: u32 = 97;
11pub const ALT: u32 = 56;
12pub const ALTGR: u32 = 100;
13pub const LOGO: u32 = 125;
14pub const CAPS_LOCK: u32 = 58;
15pub const NUM_LOCK: u32 = 69;
16pub const SCROLL_LOCK: u32 = 70;
17// pub const BACKSPACE: u32 = 14;
18// pub const TAB: u32 = 15;
19
20#[derive(Debug, Clone, Copy, PartialEq)]
21pub enum KeyDirection {
22    Up,
23    Down,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq)]
27pub enum ModType {
28    None,
29    Level2,
30    Level3,
31    Level5,
32    Compose,
33    Caps,
34    Num,
35    Scroll,
36}
37
38#[derive(Debug, Clone)]
39pub enum ModKind {
40    Pressed {
41        pressed: bool,
42        mod_type: ModType,
43    },
44    Lock {
45        pressed: bool,
46        locked: u8,
47        mod_type: ModType,
48    },
49    Latch {
50        pressed: bool,
51        latched: bool,
52        mod_type: ModType,
53    },
54    None,
55}
56
57impl ModKind {
58    pub fn update(&mut self, key_direction: KeyDirection) {
59        match self {
60            ModKind::Pressed {
61                ref mut pressed,
62                mod_type: _,
63            } => match key_direction {
64                KeyDirection::Down => *pressed = true,
65                KeyDirection::Up => *pressed = false,
66            },
67            ModKind::Lock {
68                ref mut pressed,
69                ref mut locked,
70                mod_type: _,
71            } => match key_direction {
72                KeyDirection::Down => {
73                    *pressed = true;
74                    if *locked == 0 {
75                        *locked = 2;
76                    }
77                }
78                KeyDirection::Up => {
79                    *pressed = false;
80                    if *locked != 0 {
81                        *locked -= 1;
82                    }
83                }
84            },
85            ModKind::Latch {
86                ref mut pressed,
87                ref mut latched,
88                mod_type: _,
89            } => match key_direction {
90                KeyDirection::Down => {
91                    *pressed = true;
92                    *latched = !*latched;
93                }
94                KeyDirection::Up => {
95                    *pressed = false;
96                }
97            },
98            ModKind::None => {}
99        }
100    }
101
102    fn unlatch(&mut self) {
103        if let ModKind::Latch {
104            pressed: _,
105            latched,
106            mod_type: _,
107        } = self
108        {
109            *latched = false
110        }
111    }
112
113    pub fn locked(&self) -> bool {
114        match self {
115            ModKind::Pressed {
116                pressed: _,
117                mod_type: _,
118            } => false,
119            ModKind::Lock {
120                pressed: _,
121                locked,
122                mod_type: _,
123            } => locked > &0,
124            ModKind::Latch {
125                pressed: _,
126                latched: _,
127                mod_type: _,
128            } => false,
129            ModKind::None => false,
130        }
131    }
132
133    pub fn is_active(&self) -> bool {
134        match self {
135            ModKind::Pressed {
136                pressed,
137                mod_type: _,
138            } => *pressed,
139            ModKind::Lock {
140                pressed: _,
141                locked,
142                mod_type: _,
143            } => locked > &0,
144            ModKind::Latch {
145                pressed: _,
146                latched,
147                mod_type: _,
148            } => *latched,
149            ModKind::None => false,
150        }
151    }
152
153    pub(crate) fn get_modkind_from_modtype(&self, mod_type: ModType) -> Option<ModKind> {
154        match self {
155            ModKind::Pressed { mod_type: m_t, .. }
156            | ModKind::Lock { mod_type: m_t, .. }
157            | ModKind::Latch { mod_type: m_t, .. } => {
158                if *m_t == mod_type {
159                    Some(self.clone())
160                } else {
161                    None
162                }
163            }
164            ModKind::None => None,
165        }
166    }
167}
168
169#[derive(Debug, Clone)]
170pub enum Modifier {
171    Single(ModKind),
172    Leveled(BTreeMap<u8, ModKind>),
173}
174
175#[derive(Debug, Clone)]
176pub struct Modifiers {
177    /// Flat array of (evdev_code, Modifier) pairs. Typically 10-20 entries.
178    entries: Vec<(u32, Modifier)>,
179}
180
181impl fmt::Display for Modifiers {
182    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183        for (code, modifier) in &self.entries {
184            write!(f, "code {}: ", code)?;
185            match modifier {
186                Modifier::Single(mod_kind) => {
187                    write!(f, "{:?}", mod_kind)?;
188                }
189                Modifier::Leveled(map) => {
190                    writeln!(f, "[")?;
191                    for (index, mod_kind) in map {
192                        writeln!(f, "   index {}: {:?}, ", index, mod_kind)?;
193                    }
194                    write!(f, "]")?;
195                }
196            }
197            writeln!(f)?;
198        }
199        Ok(())
200    }
201}
202
203impl Default for Modifiers {
204    fn default() -> Self {
205        let entries = vec![
206            (
207                LEFT_CTRL,
208                Modifier::Single(ModKind::Pressed {
209                    pressed: false,
210                    mod_type: ModType::None,
211                }),
212            ),
213            (
214                RIGHT_CTRL,
215                Modifier::Single(ModKind::Pressed {
216                    pressed: false,
217                    mod_type: ModType::None,
218                }),
219            ),
220            (
221                LEFT_SHIFT,
222                Modifier::Single(ModKind::Pressed {
223                    pressed: false,
224                    mod_type: ModType::Level2,
225                }),
226            ),
227            (
228                RIGHT_SHIFT,
229                Modifier::Single(ModKind::Pressed {
230                    pressed: false,
231                    mod_type: ModType::Level2,
232                }),
233            ),
234            (
235                ALT,
236                Modifier::Single(ModKind::Pressed {
237                    pressed: false,
238                    mod_type: ModType::None,
239                }),
240            ),
241            (
242                ALTGR,
243                Modifier::Single(ModKind::Pressed {
244                    pressed: false,
245                    mod_type: ModType::None,
246                }),
247            ),
248            (
249                LOGO,
250                Modifier::Single(ModKind::Pressed {
251                    pressed: false,
252                    mod_type: ModType::None,
253                }),
254            ),
255            (
256                CAPS_LOCK,
257                Modifier::Single(ModKind::Lock {
258                    pressed: false,
259                    locked: 0,
260                    mod_type: ModType::Caps,
261                }),
262            ),
263            (
264                NUM_LOCK,
265                Modifier::Single(ModKind::Lock {
266                    pressed: false,
267                    locked: 0,
268                    mod_type: ModType::Num,
269                }),
270            ),
271            (
272                SCROLL_LOCK,
273                Modifier::Single(ModKind::Lock {
274                    pressed: false,
275                    locked: 0,
276                    mod_type: ModType::Scroll,
277                }),
278            ),
279        ];
280        Self { entries }
281    }
282}
283
284impl Modifiers {
285    pub fn new() -> Self {
286        Self {
287            entries: Vec::with_capacity(MAX_MOD_SLOTS),
288        }
289    }
290
291    /// Get a reference to a modifier by evdev code.
292    #[inline]
293    pub fn get(&self, evdev_code: u32) -> Option<&Modifier> {
294        self.entries
295            .iter()
296            .find(|(c, _)| *c == evdev_code)
297            .map(|(_, m)| m)
298    }
299
300    /// Get a mutable reference to a modifier by evdev code.
301    #[inline]
302    pub fn get_mut(&mut self, evdev_code: u32) -> Option<&mut Modifier> {
303        self.entries
304            .iter_mut()
305            .find(|(c, _)| *c == evdev_code)
306            .map(|(_, m)| m)
307    }
308
309    /// Iterate over all (evdev_code, modifier) pairs.
310    #[inline]
311    pub fn iter(&self) -> impl Iterator<Item = (&u32, &Modifier)> {
312        self.entries.iter().map(|(c, m)| (c, m))
313    }
314
315    /// Insert or replace a modifier for the given evdev code.
316    pub fn set_modifier(&mut self, evdev_code: u32, modifier: Modifier) {
317        if let Some((_, existing)) = self.entries.iter_mut().find(|(c, _)| *c == evdev_code) {
318            *existing = modifier;
319        } else {
320            self.entries.push((evdev_code, modifier));
321        }
322    }
323
324    pub fn active_mod_type(&self, mod_type: ModType) -> bool {
325        self.entries.iter().any(|(_, modifier)| match modifier {
326            Modifier::Single(mod_kind) => {
327                if let Some(mk) = mod_kind.get_modkind_from_modtype(mod_type) {
328                    mk.is_active()
329                } else {
330                    false
331                }
332            }
333            Modifier::Leveled(map) => map.values().any(|mod_kind| {
334                if let Some(mk) = mod_kind.get_modkind_from_modtype(mod_type) {
335                    mk.is_active()
336                } else {
337                    false
338                }
339            }),
340        })
341    }
342
343    /// Check for active None-type modifier AND compute level2/3/5 in a single scan.
344    /// Returns (has_active_none, level2, level3, level5).
345    #[inline]
346    pub fn active_none_and_levels(&self) -> (bool, bool, bool, bool) {
347        let mut none_active = false;
348        let mut l2 = false;
349        let mut l3 = false;
350        let mut l5 = false;
351
352        for (_, modifier) in &self.entries {
353            match modifier {
354                Modifier::Single(mk) => {
355                    Self::check_mod_kind(mk, &mut none_active, &mut l2, &mut l3, &mut l5);
356                }
357                Modifier::Leveled(map) => {
358                    for mk in map.values() {
359                        Self::check_mod_kind(mk, &mut none_active, &mut l2, &mut l3, &mut l5);
360                    }
361                }
362            }
363        }
364        (none_active, l2, l3, l5)
365    }
366
367    #[inline(always)]
368    fn check_mod_kind(
369        mk: &ModKind,
370        none_active: &mut bool,
371        l2: &mut bool,
372        l3: &mut bool,
373        l5: &mut bool,
374    ) {
375        match mk {
376            ModKind::Pressed { pressed, mod_type } if *pressed => match mod_type {
377                ModType::None => *none_active = true,
378                ModType::Level2 => *l2 = true,
379                ModType::Level3 => *l3 = true,
380                ModType::Level5 => *l5 = true,
381                _ => {}
382            },
383            ModKind::Lock {
384                locked, mod_type, ..
385            } if *locked > 0 => match mod_type {
386                ModType::None => *none_active = true,
387                ModType::Level2 => *l2 = true,
388                ModType::Level3 => *l3 = true,
389                ModType::Level5 => *l5 = true,
390                _ => {}
391            },
392            ModKind::Latch {
393                latched, mod_type, ..
394            } if *latched => match mod_type {
395                ModType::None => *none_active = true,
396                ModType::Level2 => *l2 = true,
397                ModType::Level3 => *l3 = true,
398                ModType::Level5 => *l5 = true,
399                _ => {}
400            },
401            _ => {}
402        }
403    }
404
405    pub fn unlatch(&mut self) {
406        self.entries
407            .iter_mut()
408            .for_each(|(_, modifier)| match modifier {
409                Modifier::Single(mod_kind) => {
410                    mod_kind.unlatch();
411                }
412                Modifier::Leveled(map) => {
413                    map.values_mut().for_each(|mod_kind| {
414                        mod_kind.unlatch();
415                    });
416                }
417            });
418    }
419
420    pub fn locked(&self, evdev_code: u32) -> bool {
421        self.get(evdev_code).is_some_and(|modifier| match modifier {
422            Modifier::Single(mod_kind) => mod_kind.locked(),
423            Modifier::Leveled(map) => map.values().any(|mod_kind| mod_kind.locked()),
424        })
425    }
426
427    pub fn locked_with_type(&self, evdev_code: u32, mod_type: ModType) -> bool {
428        self.get(evdev_code).is_some_and(|modifier| match modifier {
429            Modifier::Single(mod_kind) => {
430                mod_kind.locked() && mod_kind.get_modkind_from_modtype(mod_type).is_some()
431            }
432            Modifier::Leveled(map) => map.values().any(|mod_kind| {
433                mod_kind.locked() && mod_kind.get_modkind_from_modtype(mod_type).is_some()
434            }),
435        })
436    }
437
438    #[inline]
439    pub fn set_state(&mut self, evdev_code: u32, key_direction: KeyDirection) -> bool {
440        let pos = match self.entries.iter().position(|(c, _)| *c == evdev_code) {
441            Some(p) => p,
442            None => return false,
443        };
444        let is_leveled = matches!(&self.entries[pos].1, Modifier::Leveled(_));
445        if is_leveled {
446            let (_, l2, l3, l5) = self.active_none_and_levels();
447            let level = level_index(l5, l3, l2) as u8;
448            if let Modifier::Leveled(map) = &mut self.entries[pos].1 {
449                if let Some(mod_kind) = map.get_mut(&level) {
450                    mod_kind.update(key_direction);
451                } else if let Some(mod_kind) = map.get_mut(&0) {
452                    mod_kind.update(key_direction);
453                } else {
454                    return false;
455                }
456            }
457        } else if let Modifier::Single(mod_kind) = &mut self.entries[pos].1 {
458            mod_kind.update(key_direction);
459        }
460        true
461    }
462}
463
464#[inline(always)]
465pub fn level_index(level5: bool, level3: bool, level2: bool) -> usize {
466    ((level5 as usize) << 2) | ((level3 as usize) << 1) | (level2 as usize)
467}