Skip to main content

smart_keymap_core/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
82struct ActiveModifiersDebugHelper<'a> {
83    active_modifiers: &'a [key::KeyboardModifiers; MAX_STICKY_MODIFIERS as usize],
84}
85
86impl core::fmt::Debug for ActiveModifiersDebugHelper<'_> {
87    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
88        // Reverse-find the last non-empty modifier to avoid printing large arrays.
89        let last_active_pos = self
90            .active_modifiers
91            .iter()
92            .rposition(|&pc| pc != key::KeyboardModifiers::NONE)
93            .map_or(0, |pos| pos + 1);
94        if last_active_pos < MAX_STICKY_MODIFIERS as usize {
95            f.debug_list()
96                .entries(&self.active_modifiers[..last_active_pos])
97                .finish_non_exhaustive()
98        } else {
99            f.debug_list().entries(&self.active_modifiers[..]).finish()
100        }
101    }
102}
103
104/// Sticky Modifiers context.
105#[derive(Clone, Copy)]
106pub struct Context {
107    /// The sticky modifier key configuration.
108    pub config: Config,
109    /// Sticky modifiers.
110    pub active_modifiers: [key::KeyboardModifiers; MAX_STICKY_MODIFIERS as usize],
111    /// Number af active sticky modifiers.
112    pub active_modifier_count: u8,
113    /// Index of the next output resolved once a sticky key has been released.
114    pub pressed_keymap_index: Option<u16>,
115}
116
117impl core::fmt::Debug for Context {
118    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
119        f.debug_struct("Context")
120            .field("config", &self.config)
121            .field(
122                "active_modifiers",
123                &ActiveModifiersDebugHelper {
124                    active_modifiers: &self.active_modifiers,
125                },
126            )
127            .field("active_modifier_count", &self.active_modifier_count)
128            .field("pressed_keymap_index", &self.pressed_keymap_index)
129            .finish()
130    }
131}
132
133impl Context {
134    /// Constructs a context from the given config
135    pub const fn from_config(config: Config) -> Context {
136        Context {
137            config,
138            active_modifiers: [key::KeyboardModifiers::NONE; MAX_STICKY_MODIFIERS as usize],
139            active_modifier_count: 0,
140            pressed_keymap_index: None,
141        }
142    }
143
144    /// Re-construct from context's [Config], clearing active sticky modifiers.
145    pub fn reset(&mut self) {
146        *self = Self::from_config(self.config);
147    }
148
149    /// Updates the context with the given event.
150    fn handle_event(&mut self, event: key::Event<Event>) -> key::KeyEvents<Event> {
151        // Cases:
152        //
153        // - No sticky key has been pressed.
154        //   - Event: any, when active_modifiers is None.
155        //   - Ctx same as default.
156        // - Sticky key has been tapped,
157        //    (pressed, released, without interruption),
158        //   - Event: Event::ActivateModifiers
159        //   - Ctx has sticky key active.
160        //     - Virtual Key modifier is pressed (if config StickyKeyActivation::OnStickyKeyRelease)
161        //   - add the activated modifiers to self.activated_modifiers
162        // - Next key has been pressed
163        //   ("modified key")
164        //   - Event: Keymap::ResolvedKeyOutput (active modifiers is Some(), pressed_keymap_index is None)
165        //   - Virtual Key modifier is pressed  (if config StickyKeyActivation::OnNextKeyPress)
166        //   - Ctx still has the sticky key active.
167        // - Same next key has been released
168        //   ("modified key")
169        //   - Event: input::Event::KeyRelease, with the same keymap index.
170        //   - Ctx deactivates the sticky key.
171        //     - Virtual Key modifier is released.
172        // - Another key has been pressed,
173        //    after sticky modifiers are pressed.
174        //   - Event: Keymap::ResolvedKeyOutput (active modifiers is Some(), pressed_keymap_index is Some())
175        //   - c.f. ZMK's quick-release.
176
177        match (self.active_modifier_count, event) {
178            // Case:
179            //  - a sticky key has been released.
180            (
181                0,
182                key::Event::Key {
183                    key_event: Event::ActivateModifiers(mods),
184                    ..
185                },
186            ) => {
187                self.active_modifiers[0] = mods;
188                self.active_modifier_count = 1;
189
190                key::KeyEvents::no_events()
191            }
192            // Case:
193            //  - another sticky key has been released.
194            (
195                active_modifier_count,
196                key::Event::Key {
197                    key_event: Event::ActivateModifiers(mods),
198                    ..
199                },
200            ) => {
201                if active_modifier_count < MAX_STICKY_MODIFIERS {
202                    self.active_modifiers[active_modifier_count as usize] = mods;
203                    self.active_modifier_count += 1;
204                }
205
206                key::KeyEvents::no_events()
207            }
208            // Case:
209            //  - Next key has been pressed, this is the "modified key";
210            //     this key gets modified until it is released.
211            (
212                active_modifier_count,
213                key::Event::Keymap(keymap::KeymapEvent::ResolvedKeyOutput { keymap_index, .. }),
214            ) if active_modifier_count > 0 => {
215                let pke = key::KeyEvents::no_events();
216
217                // The sticky key deactivates (releases)
218                //  once the modified key releases.
219                //
220                // Track the keymap index that resolved the key state.
221                self.pressed_keymap_index = Some(keymap_index);
222
223                // if matches!(self.config.activation, StickyKeyActivation::OnNextKeyPress) {
224                //     // TODO: if the config is to activate on key press, send the VK here
225                // }
226
227                pke
228            }
229            // Case:
230            //  - Modified key is released.
231            (
232                active_modifier_count,
233                key::Event::Input(input::Event::Release {
234                    keymap_index: ev_kmi,
235                }),
236            ) if Some(ev_kmi) == self.pressed_keymap_index && active_modifier_count > 0 => {
237                // Modified key has been released; release the VK.
238                let mut pke = key::KeyEvents::no_events();
239
240                self.active_modifiers[..active_modifier_count as usize]
241                    .iter()
242                    .for_each(|&m| {
243                        let sticky_key_output = key::KeyOutput::from_key_modifiers(m);
244                        let vk_ev = key::Event::Input(input::Event::VirtualKeyRelease {
245                            key_output: sticky_key_output,
246                        });
247                        pke.add_event(key::ScheduledEvent::immediate(vk_ev));
248                    });
249
250                self.active_modifier_count = 0;
251                self.pressed_keymap_index = None;
252
253                pke
254            }
255            // Case: after the sticky key modifiers are modifying a key,
256            //        another key is pressed,
257            //        & the config.release is OnNextKeyPress.
258            //  - Modified key is released.
259            (active_modifier_count, key::Event::Input(input::Event::Press { .. }))
260                if self.pressed_keymap_index.is_some()
261                    && self.config.release == StickyKeyRelease::OnNextKeyPress =>
262            {
263                // Another key has been pressed (& config is to release sticky modifiers);
264                //  release the VK.
265                let mut pke = key::KeyEvents::no_events();
266
267                self.active_modifiers[..active_modifier_count as usize]
268                    .iter()
269                    .for_each(|&m| {
270                        let sticky_key_output = key::KeyOutput::from_key_modifiers(m);
271                        let vk_ev = key::Event::Input(input::Event::VirtualKeyRelease {
272                            key_output: sticky_key_output,
273                        });
274                        pke.add_event(key::ScheduledEvent::immediate(vk_ev));
275                    });
276
277                self.active_modifier_count = 0;
278                self.pressed_keymap_index = None;
279
280                pke
281            }
282            _ => key::KeyEvents::no_events(),
283        }
284    }
285}
286
287impl key::Context for Context {
288    type Event = Event;
289
290    fn handle_event(&mut self, event: key::Event<Self::Event>) -> key::KeyEvents<Self::Event> {
291        self.handle_event(event)
292    }
293
294    fn reset(&mut self) {
295        Context::reset(self);
296    }
297}
298
299/// Sticky Modifier key events.
300#[derive(Debug, Clone, Copy, PartialEq)]
301pub enum Event {
302    /// Activates the given modifier(s) as "sticky"
303    ActivateModifiers(key::KeyboardModifiers),
304}
305
306/// A key for HID Keyboard usage codes.
307#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
308pub struct Key {
309    /// The sticky key modifiers.
310    pub sticky_modifiers: key::KeyboardModifiers,
311}
312
313impl Key {
314    /// Constructs a key with the given key_code.
315    pub const fn new(sticky_modifiers: key::KeyboardModifiers) -> Self {
316        Key { sticky_modifiers }
317    }
318
319    /// Constructs a pressed key state
320    pub fn new_pressed_key(&self) -> KeyState {
321        KeyState::new()
322    }
323}
324
325/// The pending key state type for sticky modifier keys. (No pending state).
326#[derive(Debug, Clone, Copy, PartialEq)]
327pub struct PendingKeyState;
328
329/// Whether the pressed Sticky modifier key is "sticky" or "regular".
330#[derive(Debug, Clone, Copy, Eq, PartialEq)]
331pub enum Behavior {
332    /// Key state is "sticky". (Will activate sticky modifier when released).
333    Sticky,
334    /// Key state is "regular". (No sticky modifiers activated when released).
335    Regular,
336}
337
338/// Key state for sticky modifier keys.
339#[derive(Debug, Clone, Copy, PartialEq)]
340pub struct KeyState {
341    behavior: Behavior,
342}
343
344impl KeyState {
345    /// Constructs a new key state with the given sticky modifiers.
346    pub fn new() -> Self {
347        KeyState {
348            behavior: Behavior::Sticky,
349        }
350    }
351}
352
353impl Default for KeyState {
354    /// Returns the default key state.
355    fn default() -> Self {
356        Self::new()
357    }
358}
359
360impl KeyState {
361    /// Handle the given event.
362    pub fn update_state(
363        &mut self,
364        key: &Key,
365        context: &Context,
366        keymap_index: u16,
367        event: key::Event<Event>,
368    ) -> key::KeyEvents<Event> {
369        //  - If another key is *pressed*, then we're no longer a sticky key.
370        //  - If this key is released & it's a sticky key
371        //     (& the config is for "eager sticky mod"),
372        //     then emit a VK with the mods; emit event "activate".
373        match self.behavior {
374            Behavior::Sticky => match event {
375                key::Event::Keymap(keymap::KeymapEvent::ResolvedKeyOutput { .. }) => {
376                    // Another key has been pressed.
377                    // The sticky modifier key acts as a regular key.
378                    self.behavior = Behavior::Regular;
379
380                    key::KeyEvents::no_events()
381                }
382                key::Event::Input(input::Event::Release {
383                    keymap_index: released_index,
384                }) if released_index == keymap_index => {
385                    // The sticky key has been released.
386                    match context.config.activation {
387                        StickyKeyActivation::OnStickyKeyRelease => {
388                            let sticky_ev = Event::ActivateModifiers(key.sticky_modifiers);
389                            let k_ev = key::Event::key_event(keymap_index, sticky_ev);
390
391                            let sticky_key_output =
392                                key::KeyOutput::from_key_modifiers(key.sticky_modifiers);
393                            let vk_ev = key::Event::Input(input::Event::VirtualKeyPress {
394                                key_output: sticky_key_output,
395                            });
396
397                            let mut pke = key::KeyEvents::event(k_ev);
398                            pke.add_event(key::ScheduledEvent::immediate(vk_ev));
399                            pke
400                        }
401                    }
402                }
403                _ => key::KeyEvents::no_events(),
404            },
405            Behavior::Regular => key::KeyEvents::no_events(),
406        }
407    }
408
409    /// Key output for the pressed key state.
410    pub fn key_output(&self, key: &Key) -> Option<key::KeyOutput> {
411        match self.behavior {
412            Behavior::Sticky => None,
413            Behavior::Regular => Some(key::KeyOutput::from_key_modifiers(key.sticky_modifiers)),
414        }
415    }
416}
417
418/// The [key::System] implementation for keyboard keys.
419#[derive(Debug, Clone, Copy, PartialEq)]
420pub struct System<R, Keys: Index<usize, Output = Key>> {
421    keys: Keys,
422    marker: PhantomData<R>,
423}
424
425impl<R, Keys: Index<usize, Output = Key>> System<R, Keys> {
426    /// Constructs a new [System] with the given key data.
427    pub const fn new(keys: Keys) -> Self {
428        Self {
429            keys,
430            marker: PhantomData,
431        }
432    }
433}
434
435impl<R: Debug, Keys: Debug + Index<usize, Output = Key>> key::System<R> for System<R, Keys> {
436    type Ref = Ref;
437    type Context = Context;
438    type Event = Event;
439    type PendingKeyState = PendingKeyState;
440    type KeyState = KeyState;
441
442    fn new_pressed_key(
443        &self,
444        _keymap_index: u16,
445        _context: &Self::Context,
446        Ref(key_index): Ref,
447    ) -> (
448        key::PressedKeyResult<R, Self::PendingKeyState, Self::KeyState>,
449        key::KeyEvents<Self::Event>,
450    ) {
451        let key = &self.keys[key_index as usize];
452        let ks = key.new_pressed_key();
453        let pks = key::PressedKeyResult::Resolved(ks);
454        let pke = key::KeyEvents::no_events();
455        (pks, pke)
456    }
457
458    fn update_pending_state(
459        &self,
460        _pending_state: &mut Self::PendingKeyState,
461        _keymap_index: u16,
462        _context: &Self::Context,
463        _key_ref: Ref,
464        _event: key::Event<Self::Event>,
465    ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Self::Event>) {
466        panic!()
467    }
468
469    fn update_state(
470        &self,
471        key_state: &mut Self::KeyState,
472        Ref(key_index): &Self::Ref,
473        context: &Self::Context,
474        keymap_index: u16,
475        event: key::Event<Self::Event>,
476    ) -> key::KeyEvents<Self::Event> {
477        let key = &self.keys[*key_index as usize];
478        key_state.update_state(key, context, keymap_index, event)
479    }
480
481    fn key_output(
482        &self,
483        Ref(key_index): &Self::Ref,
484        key_state: &Self::KeyState,
485    ) -> Option<key::KeyOutput> {
486        let key = &self.keys[*key_index as usize];
487        key_state.key_output(key)
488    }
489}
490
491#[cfg(test)]
492mod tests {
493    use super::*;
494
495    #[test]
496    fn test_sizeof_ref() {
497        assert_eq!(1, core::mem::size_of::<Ref>());
498    }
499
500    #[test]
501    fn test_sizeof_event() {
502        assert_eq!(1, core::mem::size_of::<Event>());
503    }
504}