Skip to main content

smart_keymap_core/key/
tri_state.rs

1//! Tri-state keys: start / continue / interrupt.
2//!
3//! A tri-state key owns a session across later presses of the same key.
4//! The first press *starts*
5//!  (typically virtual-hold a modifier and tap a key),
6//! later presses of the same key *continue*
7//!  (tap again while the hold stays),
8//! and any other resolved key *interrupts*
9//!  (releases the hold).
10//!
11//! The physical key is a [`crate::key::NewPressedKey::NoOp`]; HID output is
12//!  injected with [`crate::input::Event::VirtualKeyPress`] /
13//!  [`crate::input::Event::VirtualKeyRelease`].
14//! Hold is pressed before tap so the first report is the chord, not a
15//!  naked tap.
16//!
17//! Classic use is Alt-Tab (a "swapper"): start holds Left Alt and taps Tab,
18//!  continue taps Tab, interrupt releases Left Alt.
19
20use core::fmt::Debug;
21use core::marker::PhantomData;
22use core::ops::Index;
23
24use serde::Deserialize;
25
26use crate::input;
27use crate::key;
28use crate::keymap;
29
30/// Reference for a tri-state key (index into [System] key data).
31#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
32pub struct Ref(pub u8);
33
34/// A tri-state key: virtual-hold `hold` across taps of `tap`.
35#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
36pub struct Key {
37    /// Output held for the life of the session (start press, interrupt release).
38    pub hold: key::KeyOutput,
39    /// Output tapped on start and on each continue.
40    pub tap: key::KeyOutput,
41}
42
43impl Key {
44    /// Constructs a tri-state key.
45    pub const fn new(hold: key::KeyOutput, tap: key::KeyOutput) -> Self {
46        Self { hold, tap }
47    }
48}
49
50/// Armed session: `hold` is virtually pressed until interrupt.
51#[derive(Debug, Clone, Copy, PartialEq)]
52struct Session {
53    keymap_index: u16,
54    hold: key::KeyOutput,
55    tap: key::KeyOutput,
56    tap_held: bool,
57}
58
59/// Tri-state context: at most one session is armed.
60#[derive(Debug, Clone, Copy, PartialEq)]
61pub struct Context {
62    session: Option<Session>,
63}
64
65impl Default for Context {
66    fn default() -> Self {
67        Self::new()
68    }
69}
70
71impl Context {
72    /// Constructs an idle context.
73    pub const fn new() -> Self {
74        Context { session: None }
75    }
76
77    /// Clear the session without emitting virtual releases.
78    pub fn reset(&mut self) {
79        *self = Self::new();
80    }
81
82    /// Whether a session is currently armed.
83    pub fn is_active(&self) -> bool {
84        self.session.is_some()
85    }
86
87    /// Whether the armed session belongs to `keymap_index`.
88    pub fn is_session_for(&self, keymap_index: u16) -> bool {
89        matches!(self.session, Some(s) if s.keymap_index == keymap_index)
90    }
91
92    fn end_session(&mut self) -> key::KeyEvents<Event> {
93        match self.session.take() {
94            None => key::KeyEvents::no_events(),
95            Some(session) => {
96                let mut pke = key::KeyEvents::no_events();
97                if session.tap_held {
98                    pke.add_event(key::Event::Input(input::Event::VirtualKeyRelease {
99                        key_output: session.tap,
100                    }));
101                }
102                pke.add_event(key::Event::Input(input::Event::VirtualKeyRelease {
103                    key_output: session.hold,
104                }));
105                pke
106            }
107        }
108    }
109
110    fn handle_event(&mut self, event: key::Event<Event>) -> key::KeyEvents<Event> {
111        match event {
112            key::Event::Key {
113                keymap_index,
114                key_event: Event::Start { hold, tap },
115            } => {
116                let mut pke = self.end_session();
117                self.session = Some(Session {
118                    keymap_index,
119                    hold,
120                    tap,
121                    tap_held: true,
122                });
123                pke.add_event(key::Event::Input(input::Event::VirtualKeyPress {
124                    key_output: hold,
125                }));
126                pke.add_event(key::Event::Input(input::Event::VirtualKeyPress {
127                    key_output: tap,
128                }));
129                pke
130            }
131            key::Event::Key {
132                keymap_index,
133                key_event: Event::Continue,
134            } => match self.session.as_mut() {
135                Some(session) if session.keymap_index == keymap_index => {
136                    session.tap_held = true;
137                    key::KeyEvents::event(key::Event::Input(input::Event::VirtualKeyPress {
138                        key_output: session.tap,
139                    }))
140                }
141                _ => key::KeyEvents::no_events(),
142            },
143            key::Event::Input(input::Event::Release { keymap_index }) => {
144                match self.session.as_mut() {
145                    Some(session) if session.keymap_index == keymap_index && session.tap_held => {
146                        session.tap_held = false;
147                        key::KeyEvents::event(key::Event::Input(input::Event::VirtualKeyRelease {
148                            key_output: session.tap,
149                        }))
150                    }
151                    _ => key::KeyEvents::no_events(),
152                }
153            }
154            key::Event::Keymap(keymap::KeymapEvent::ResolvedKeyOutput { keymap_index, .. }) => {
155                match self.session {
156                    Some(session) if session.keymap_index != keymap_index => self.end_session(),
157                    _ => key::KeyEvents::no_events(),
158                }
159            }
160            _ => key::KeyEvents::no_events(),
161        }
162    }
163}
164
165impl key::Context for Context {
166    type Event = Event;
167
168    fn handle_event(&mut self, event: key::Event<Self::Event>) -> key::KeyEvents<Self::Event> {
169        self.handle_event(event)
170    }
171
172    fn reset(&mut self) {
173        Context::reset(self);
174    }
175}
176
177/// Tri-state events.
178#[derive(Debug, Clone, Copy, PartialEq)]
179pub enum Event {
180    /// First press (or a different tri-state key): arm `hold` and tap `tap`.
181    Start {
182        /// Output to virtual-hold for the session.
183        hold: key::KeyOutput,
184        /// Output to tap now.
185        tap: key::KeyOutput,
186    },
187    /// Re-press of the armed key: tap again; `hold` stays.
188    Continue,
189}
190
191/// Pending key state type for tri-state keys. (No pending state.)
192#[derive(Debug, Clone, Copy, PartialEq)]
193pub struct PendingKeyState;
194
195/// Key state used by [System]. (No per-key state; behaviour is on [Context].)
196#[derive(Debug, Clone, Copy, PartialEq)]
197pub struct KeyState;
198
199/// The [key::System] implementation for tri-state keys.
200#[derive(Debug, Clone, Copy, PartialEq)]
201pub struct System<R, Keys: Index<usize, Output = Key>> {
202    keys: Keys,
203    marker: PhantomData<R>,
204}
205
206impl<R, Keys: Index<usize, Output = Key>> System<R, Keys> {
207    /// Constructs a new [System] with the given key data.
208    pub const fn new(keys: Keys) -> Self {
209        Self {
210            keys,
211            marker: PhantomData,
212        }
213    }
214}
215
216impl<R: Debug, Keys: Debug + Index<usize, Output = Key>> key::System<R> for System<R, Keys> {
217    type Ref = Ref;
218    type Context = Context;
219    type Event = Event;
220    type PendingKeyState = PendingKeyState;
221    type KeyState = KeyState;
222
223    fn new_pressed_key(
224        &self,
225        keymap_index: u16,
226        context: &Self::Context,
227        Ref(key_index): Ref,
228    ) -> (
229        key::PressedKeyResult<R, Self::PendingKeyState, Self::KeyState>,
230        key::KeyEvents<Self::Event>,
231    ) {
232        let Key { hold, tap } = self.keys[key_index as usize];
233        let key_event = if context.is_session_for(keymap_index) {
234            Event::Continue
235        } else {
236            Event::Start { hold, tap }
237        };
238        let pkr = key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp);
239        let pke = key::KeyEvents::event(key::Event::key_event(keymap_index, key_event));
240        (pkr, pke)
241    }
242
243    fn update_pending_state(
244        &self,
245        _pending_state: &mut Self::PendingKeyState,
246        _keymap_index: u16,
247        _context: &Self::Context,
248        _key_ref: Ref,
249        _event: key::Event<Self::Event>,
250    ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Self::Event>) {
251        panic!()
252    }
253
254    fn update_state(
255        &self,
256        _key_state: &mut Self::KeyState,
257        _ref: &Self::Ref,
258        _context: &Self::Context,
259        _keymap_index: u16,
260        _event: key::Event<Self::Event>,
261    ) -> key::KeyEvents<Self::Event> {
262        panic!()
263    }
264
265    fn key_output(
266        &self,
267        _key_ref: &Self::Ref,
268        _key_state: &Self::KeyState,
269    ) -> Option<key::KeyOutput> {
270        panic!()
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use super::*;
277
278    #[test]
279    fn test_sizeof_ref() {
280        assert_eq!(1, core::mem::size_of::<Ref>());
281    }
282
283    #[test]
284    fn test_sizeof_event() {
285        assert_eq!(14, core::mem::size_of::<Event>());
286    }
287
288    #[test]
289    fn start_arms_session() {
290        let mut ctx = Context::new();
291        let hold = key::KeyOutput::from_key_code(0xE2);
292        let tap = key::KeyOutput::from_key_code(0x2B);
293        let _ = key::Context::handle_event(
294            &mut ctx,
295            key::Event::key_event(0, Event::Start { hold, tap }),
296        );
297        assert!(ctx.is_active());
298        assert!(ctx.is_session_for(0));
299    }
300
301    #[test]
302    fn other_resolved_output_ends_session() {
303        let mut ctx = Context::new();
304        let hold = key::KeyOutput::from_key_code(0xE2);
305        let tap = key::KeyOutput::from_key_code(0x2B);
306        let _ = key::Context::handle_event(
307            &mut ctx,
308            key::Event::key_event(0, Event::Start { hold, tap }),
309        );
310        let _ = key::Context::handle_event(
311            &mut ctx,
312            key::Event::Keymap(keymap::KeymapEvent::ResolvedKeyOutput {
313                keymap_index: 1,
314                key_output: key::KeyOutput::from_key_code(0x04),
315            }),
316        );
317        assert!(!ctx.is_active());
318    }
319
320    #[test]
321    fn own_resolved_output_does_not_end_session() {
322        let mut ctx = Context::new();
323        let hold = key::KeyOutput::from_key_code(0xE2);
324        let tap = key::KeyOutput::from_key_code(0x2B);
325        let _ = key::Context::handle_event(
326            &mut ctx,
327            key::Event::key_event(0, Event::Start { hold, tap }),
328        );
329        let _ = key::Context::handle_event(
330            &mut ctx,
331            key::Event::Keymap(keymap::KeymapEvent::ResolvedKeyOutput {
332                keymap_index: 0,
333                key_output: tap,
334            }),
335        );
336        assert!(ctx.is_active());
337    }
338}