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