smart_keymap/key/
sticky.rs

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