smart_keymap/key/
sticky.rs

1use core::fmt::Debug;
2use core::marker::Copy;
3use core::marker::PhantomData;
4use core::ops::Index;
5
6use serde::Deserialize;
7
8use crate::input;
9use crate::key;
10use crate::keymap;
11
12/// Reference for a sticky modifier key.
13#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
14pub struct Ref(pub u8);
15
16/// When the sticky modifiers activate.
17#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
18pub enum StickyKeyActivation {
19    /// Sticky modifiers activate when the sticky key is released.
20    OnStickyKeyRelease,
21    // TODO: add another config option for "on next key press"
22    // /// Sticky modifiers activate when the next key is pressed.
23    // OnNextKeyPress,
24}
25
26/// When the sticky modifiers release.
27#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
28pub enum StickyKeyRelease {
29    /// Sticky modifiers release when the modified key is released.
30    OnModifiedKeyRelease,
31    /// Sticky modifiers release when a key is pressed after the modified key.
32    OnNextKeyPress,
33}
34
35/// Sticky Key configuration.
36#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
37pub struct Config {
38    /// The sticky key activation mode.
39    #[serde(default = "default_activation")]
40    pub activation: StickyKeyActivation,
41    /// When the sticky modifiers release.
42    #[serde(default = "default_release")]
43    pub release: StickyKeyRelease,
44}
45
46/// Default activation.
47pub const DEFAULT_ACTIVATION: StickyKeyActivation = StickyKeyActivation::OnStickyKeyRelease;
48
49/// Default sticky release behaviour.
50pub const DEFAULT_RELEASE: StickyKeyRelease = StickyKeyRelease::OnModifiedKeyRelease;
51
52fn default_activation() -> StickyKeyActivation {
53    DEFAULT_ACTIVATION
54}
55
56fn default_release() -> StickyKeyRelease {
57    DEFAULT_RELEASE
58}
59
60/// The default [Config].
61pub const DEFAULT_CONFIG: Config = Config {
62    activation: DEFAULT_ACTIVATION,
63    release: DEFAULT_RELEASE,
64};
65
66impl Config {
67    /// Constructs a new default config.
68    pub const fn new() -> Self {
69        DEFAULT_CONFIG
70    }
71}
72
73impl Default for Config {
74    /// Returns the default context.
75    fn default() -> Self {
76        Self::new()
77    }
78}
79
80const MAX_STICKY_MODIFIERS: u8 = 4;
81
82/// Sticky Modifiers context.
83#[derive(Debug, Clone, Copy)]
84pub struct Context {
85    /// The sticky modifier key configuration.
86    pub config: Config,
87    /// Sticky modifiers.
88    pub active_modifiers: [key::KeyboardModifiers; MAX_STICKY_MODIFIERS as usize],
89    /// Number af active sticky modifiers.
90    pub active_modifier_count: u8,
91    /// Index of the next output resolved once a sticky key has been released.
92    pub pressed_keymap_index: Option<u16>,
93}
94
95impl Context {
96    /// Constructs a context from the given config
97    pub const fn from_config(config: Config) -> Context {
98        Context {
99            config,
100            active_modifiers: [key::KeyboardModifiers::NONE; MAX_STICKY_MODIFIERS as usize],
101            active_modifier_count: 0,
102            pressed_keymap_index: None,
103        }
104    }
105
106    /// Updates the context with the given event.
107    fn handle_event(&mut self, event: key::Event<Event>) -> key::KeyEvents<Event> {
108        // Cases:
109        //
110        // - No sticky key has been pressed.
111        //   - Event: any, when active_modifiers is None.
112        //   - Ctx same as default.
113        // - Sticky key has been tapped,
114        //    (pressed, released, without interruption),
115        //   - Event: Event::ActivateModifiers
116        //   - Ctx has sticky key active.
117        //     - Virtual Key modifier is pressed (if config StickyKeyActivation::OnStickyKeyRelease)
118        //   - add the activated modifiers to self.activated_modifiers
119        // - Next key has been pressed
120        //   ("modified key")
121        //   - Event: Keymap::ResolvedKeyOutput (active modifiers is Some(), pressed_keymap_index is None)
122        //   - Virtual Key modifier is pressed  (if config StickyKeyActivation::OnNextKeyPress)
123        //   - Ctx still has the sticky key active.
124        // - Same next key has been released
125        //   ("modified key")
126        //   - Event: input::Event::KeyRelease, with the same keymap index.
127        //   - Ctx deactivates the sticky key.
128        //     - Virtual Key modifier is released.
129        // - Another key has been pressed,
130        //    after sticky modifiers are pressed.
131        //   - Event: Keymap::ResolvedKeyOutput (active modifiers is Some(), pressed_keymap_index is Some())
132        //   - c.f. ZMK's quick-release.
133
134        match (self.active_modifier_count, event) {
135            // Case:
136            //  - a sticky key has been released.
137            (
138                0,
139                key::Event::Key {
140                    key_event: Event::ActivateModifiers(mods),
141                    ..
142                },
143            ) => {
144                self.active_modifiers[0] = mods;
145                self.active_modifier_count = 1;
146
147                key::KeyEvents::no_events()
148            }
149            // Case:
150            //  - another sticky key has been released.
151            (
152                active_modifier_count,
153                key::Event::Key {
154                    key_event: Event::ActivateModifiers(mods),
155                    ..
156                },
157            ) => {
158                if active_modifier_count < MAX_STICKY_MODIFIERS {
159                    self.active_modifiers[active_modifier_count as usize] = mods;
160                    self.active_modifier_count += 1;
161                }
162
163                key::KeyEvents::no_events()
164            }
165            // Case:
166            //  - Next key has been pressed, this is the "modified key";
167            //     this key gets modified until it is released.
168            (
169                active_modifier_count,
170                key::Event::Keymap(keymap::KeymapEvent::ResolvedKeyOutput { keymap_index, .. }),
171            ) if active_modifier_count > 0 => {
172                let pke = key::KeyEvents::no_events();
173
174                // The sticky key deactivates (releases)
175                //  once the modified key releases.
176                //
177                // Track the keymap index that resolved the key state.
178                self.pressed_keymap_index = Some(keymap_index);
179
180                // if matches!(self.config.activation, StickyKeyActivation::OnNextKeyPress) {
181                //     // TODO: if the config is to activate on key press, send the VK here
182                // }
183
184                pke
185            }
186            // Case:
187            //  - Modified key is released.
188            (
189                active_modifier_count,
190                key::Event::Input(input::Event::Release {
191                    keymap_index: ev_kmi,
192                }),
193            ) if Some(ev_kmi) == self.pressed_keymap_index && active_modifier_count > 0 => {
194                // Modified key has been released; release the VK.
195                let mut pke = key::KeyEvents::no_events();
196
197                self.active_modifiers[..active_modifier_count as usize]
198                    .iter()
199                    .for_each(|&m| {
200                        let sticky_key_output = key::KeyOutput::from_key_modifiers(m);
201                        let vk_ev = key::Event::Input(input::Event::VirtualKeyRelease {
202                            key_output: sticky_key_output,
203                        });
204                        pke.add_event(key::ScheduledEvent::immediate(vk_ev));
205                    });
206
207                self.active_modifier_count = 0;
208                self.pressed_keymap_index = None;
209
210                pke
211            }
212            // Case: after the sticky key modifiers are modifying a key,
213            //        another key is pressed,
214            //        & the config.release is OnNextKeyPress.
215            //  - Modified key is released.
216            (active_modifier_count, key::Event::Input(input::Event::Press { .. }))
217                if self.pressed_keymap_index.is_some()
218                    && self.config.release == StickyKeyRelease::OnNextKeyPress =>
219            {
220                // Another key has been pressed (& config is to release sticky modifiers);
221                //  release the VK.
222                let mut pke = key::KeyEvents::no_events();
223
224                self.active_modifiers[..active_modifier_count as usize]
225                    .iter()
226                    .for_each(|&m| {
227                        let sticky_key_output = key::KeyOutput::from_key_modifiers(m);
228                        let vk_ev = key::Event::Input(input::Event::VirtualKeyRelease {
229                            key_output: sticky_key_output,
230                        });
231                        pke.add_event(key::ScheduledEvent::immediate(vk_ev));
232                    });
233
234                self.active_modifier_count = 0;
235                self.pressed_keymap_index = None;
236
237                pke
238            }
239            _ => key::KeyEvents::no_events(),
240        }
241    }
242}
243
244impl key::Context for Context {
245    type Event = Event;
246
247    fn handle_event(&mut self, event: key::Event<Self::Event>) -> key::KeyEvents<Self::Event> {
248        self.handle_event(event)
249    }
250}
251
252/// Sticky Modifier key events.
253#[derive(Debug, Clone, Copy, PartialEq)]
254pub enum Event {
255    /// Activates the given modifier(s) as "sticky"
256    ActivateModifiers(key::KeyboardModifiers),
257}
258
259/// A key for HID Keyboard usage codes.
260#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
261pub struct Key {
262    /// The sticky key modifiers.
263    pub sticky_modifiers: key::KeyboardModifiers,
264}
265
266impl Key {
267    /// Constructs a key with the given key_code.
268    pub const fn new(sticky_modifiers: key::KeyboardModifiers) -> Self {
269        Key { sticky_modifiers }
270    }
271
272    /// Constructs a pressed key state
273    pub fn new_pressed_key(&self) -> KeyState {
274        KeyState::new()
275    }
276}
277
278/// The pending key state type for sticky modifier keys. (No pending state).
279#[derive(Debug, Clone, Copy, PartialEq)]
280pub struct PendingKeyState;
281
282/// Whether the pressed Sticky modifier key is "sticky" or "regular".
283#[derive(Debug, Clone, Copy, Eq, PartialEq)]
284pub enum Behavior {
285    /// Key state is "sticky". (Will activate sticky modifier when released).
286    Sticky,
287    /// Key state is "regular". (No sticky modifiers activated when released).
288    Regular,
289}
290
291/// Key state for sticky modifier keys.
292#[derive(Debug, Clone, Copy, PartialEq)]
293pub struct KeyState {
294    behavior: Behavior,
295}
296
297impl KeyState {
298    /// Constructs a new key state with the given sticky modifiers.
299    pub fn new() -> Self {
300        KeyState {
301            behavior: Behavior::Sticky,
302        }
303    }
304}
305
306impl Default for KeyState {
307    /// Returns the default key state.
308    fn default() -> Self {
309        Self::new()
310    }
311}
312
313impl KeyState {
314    /// Handle the given event.
315    pub fn update_state(
316        &mut self,
317        key: &Key,
318        context: &Context,
319        keymap_index: u16,
320        event: key::Event<Event>,
321    ) -> key::KeyEvents<Event> {
322        //  - If another key is *pressed*, then we're no longer a sticky key.
323        //  - If this key is released & it's a sticky key
324        //     (& the config is for "eager sticky mod"),
325        //     then emit a VK with the mods; emit event "activate".
326        match self.behavior {
327            Behavior::Sticky => match event {
328                key::Event::Keymap(keymap::KeymapEvent::ResolvedKeyOutput { .. }) => {
329                    // Another key has been pressed.
330                    // The sticky modifier key acts as a regular key.
331                    self.behavior = Behavior::Regular;
332
333                    key::KeyEvents::no_events()
334                }
335                key::Event::Input(input::Event::Release {
336                    keymap_index: released_index,
337                }) if released_index == keymap_index => {
338                    // The sticky key has been released.
339                    match context.config.activation {
340                        StickyKeyActivation::OnStickyKeyRelease => {
341                            let sticky_ev = Event::ActivateModifiers(key.sticky_modifiers);
342                            let k_ev = key::Event::key_event(keymap_index, sticky_ev);
343
344                            let sticky_key_output =
345                                key::KeyOutput::from_key_modifiers(key.sticky_modifiers);
346                            let vk_ev = key::Event::Input(input::Event::VirtualKeyPress {
347                                key_output: sticky_key_output,
348                            });
349
350                            let mut pke = key::KeyEvents::event(k_ev);
351                            pke.add_event(key::ScheduledEvent::immediate(vk_ev));
352                            pke
353                        }
354                    }
355                }
356                _ => key::KeyEvents::no_events(),
357            },
358            Behavior::Regular => key::KeyEvents::no_events(),
359        }
360    }
361
362    /// Key output for the pressed key state.
363    pub fn key_output(&self, key: &Key) -> Option<key::KeyOutput> {
364        match self.behavior {
365            Behavior::Sticky => None,
366            Behavior::Regular => Some(key::KeyOutput::from_key_modifiers(key.sticky_modifiers)),
367        }
368    }
369}
370
371/// The [key::System] implementation for keyboard keys.
372#[derive(Debug, Clone, Copy, PartialEq)]
373pub struct System<R, Keys: Index<usize, Output = Key>> {
374    keys: Keys,
375    marker: PhantomData<R>,
376}
377
378impl<R, Keys: Index<usize, Output = Key>> System<R, Keys> {
379    /// Constructs a new [System] with the given key data.
380    pub const fn new(keys: Keys) -> Self {
381        Self {
382            keys,
383            marker: PhantomData,
384        }
385    }
386}
387
388impl<R: Debug, Keys: Debug + Index<usize, Output = Key>> key::System<R> for System<R, Keys> {
389    type Ref = Ref;
390    type Context = Context;
391    type Event = Event;
392    type PendingKeyState = PendingKeyState;
393    type KeyState = KeyState;
394
395    fn new_pressed_key(
396        &self,
397        _keymap_index: u16,
398        _context: &Self::Context,
399        Ref(key_index): Ref,
400    ) -> (
401        key::PressedKeyResult<R, Self::PendingKeyState, Self::KeyState>,
402        key::KeyEvents<Self::Event>,
403    ) {
404        let key = &self.keys[key_index as usize];
405        let ks = key.new_pressed_key();
406        let pks = key::PressedKeyResult::Resolved(ks);
407        let pke = key::KeyEvents::no_events();
408        (pks, pke)
409    }
410
411    fn update_pending_state(
412        &self,
413        _pending_state: &mut Self::PendingKeyState,
414        _keymap_index: u16,
415        _context: &Self::Context,
416        _key_ref: Ref,
417        _event: key::Event<Self::Event>,
418    ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Self::Event>) {
419        panic!()
420    }
421
422    fn update_state(
423        &self,
424        key_state: &mut Self::KeyState,
425        Ref(key_index): &Self::Ref,
426        context: &Self::Context,
427        keymap_index: u16,
428        event: key::Event<Self::Event>,
429    ) -> key::KeyEvents<Self::Event> {
430        let key = &self.keys[*key_index as usize];
431        key_state.update_state(key, context, keymap_index, event)
432    }
433
434    fn key_output(
435        &self,
436        Ref(key_index): &Self::Ref,
437        key_state: &Self::KeyState,
438    ) -> Option<key::KeyOutput> {
439        let key = &self.keys[*key_index as usize];
440        key_state.key_output(key)
441    }
442}
443
444#[cfg(test)]
445mod tests {
446    use super::*;
447
448    #[test]
449    fn test_sizeof_ref() {
450        assert_eq!(1, core::mem::size_of::<Ref>());
451    }
452
453    #[test]
454    fn test_sizeof_event() {
455        assert_eq!(1, core::mem::size_of::<Event>());
456    }
457}