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