smart_keymap/key/
chorded.rs

1#![doc = include_str!("doc_de_chorded.md")]
2
3use core::fmt::Debug;
4
5use serde::Deserialize;
6
7use crate::{input, key, slice::Slice};
8
9pub use crate::init::{MAX_CHORDS, MAX_CHORD_SIZE, MAX_OVERLAPPING_CHORD_SIZE};
10
11/// A chord identifier.
12pub type ChordId = u8;
13
14/// Chords are defined by an (unordered) set of keymap indices into the keymap.
15#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
16#[serde(from = "heapless::Vec<u16, MAX_CHORD_SIZE>")]
17pub struct ChordIndices {
18    /// A slice of keymap indices.
19    indices: Slice<u16, MAX_CHORD_SIZE>,
20}
21
22impl ChordIndices {
23    /// Constructs a new [ChordIndices] value from the given slice.
24    ///
25    /// The given slice must be less than [MAX_CHORD_SIZE] in length.
26    pub const fn from_slice(indices: &[u16]) -> ChordIndices {
27        ChordIndices {
28            indices: Slice::from_slice(indices),
29        }
30    }
31
32    /// The chord indices as a slice.
33    pub const fn as_slice(&self) -> &[u16] {
34        self.indices.as_slice()
35    }
36
37    /// Whether the given index is part of the chord.
38    pub fn has_index(&self, index: u16) -> bool {
39        self.as_slice().iter().any(|&i| i == index)
40    }
41
42    /// Whether the chord is satisfied by the given indices.
43    pub fn is_satisfied_by(&self, indices: &[u16]) -> bool {
44        self.as_slice().iter().all(|&i| indices.contains(&i))
45    }
46}
47
48impl From<heapless::Vec<u16, MAX_CHORD_SIZE>> for ChordIndices {
49    fn from(v: heapless::Vec<u16, MAX_CHORD_SIZE>) -> Self {
50        ChordIndices::from_slice(&v)
51    }
52}
53
54/// Chord definitions.
55#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
56pub struct Config {
57    /// The timeout (in number of milliseconds) for a chorded key to resolve.
58    ///
59    /// (Resolves as passthrough key if no chord is satisfied).
60    #[serde(default = "default_timeout")]
61    pub timeout: u16,
62
63    /// The keymap chords.
64    pub chords: Slice<ChordIndices, MAX_CHORDS>,
65}
66
67fn default_timeout() -> u16 {
68    DEFAULT_CONFIG.timeout
69}
70
71/// Default config.
72pub const DEFAULT_CONFIG: Config = Config {
73    timeout: 200,
74    chords: Slice::from_slice(&[]),
75};
76
77impl Default for Config {
78    /// Returns the default context.
79    fn default() -> Self {
80        DEFAULT_CONFIG
81    }
82}
83
84/// State for a key chord.
85#[derive(Debug, Clone, PartialEq)]
86pub struct ChordState {
87    /// The chord index in the chorded config.
88    pub index: usize,
89    /// The chord's indices.
90    pub chord: ChordIndices,
91    /// Whether the chord is satisfied by the pressed indices.
92    pub is_satisfied: bool,
93}
94
95/// Chord definitions.
96#[derive(Debug, Clone, Copy, PartialEq)]
97pub struct Context {
98    config: Config,
99    pressed_indices: [Option<u16>; MAX_CHORD_SIZE * MAX_CHORDS],
100    pressed_chords: [bool; MAX_CHORDS],
101}
102
103/// Default context.
104pub const DEFAULT_CONTEXT: Context = Context::from_config(DEFAULT_CONFIG);
105
106impl Context {
107    /// Constructs a context from the given config
108    pub const fn from_config(config: Config) -> Context {
109        let pressed_indices = [None; MAX_CHORD_SIZE * MAX_CHORDS];
110        Context {
111            config,
112            pressed_indices,
113            pressed_chords: [false; MAX_CHORDS],
114        }
115    }
116
117    fn pressed_chord_with_index(&self, keymap_index: u16) -> Option<ChordState> {
118        self.pressed_chords
119            .iter()
120            .enumerate()
121            .filter_map(|(index, &is_pressed)| {
122                if is_pressed {
123                    Some(ChordState {
124                        index,
125                        chord: self.config.chords[index],
126                        is_satisfied: true,
127                    })
128                } else {
129                    None
130                }
131            })
132            .find(|ChordState { chord, .. }| chord.has_index(keymap_index))
133    }
134
135    // Span of indices of pressed chords.
136    fn pressed_chords_indices_span(&self) -> heapless::Vec<u16, { MAX_CHORD_SIZE * MAX_CHORDS }> {
137        let mut res: heapless::Vec<u16, { MAX_CHORD_SIZE * MAX_CHORDS }> = heapless::Vec::new();
138
139        let pressed_chords =
140            self.pressed_chords
141                .iter()
142                .enumerate()
143                .filter_map(|(index, &is_pressed)| {
144                    if is_pressed {
145                        Some(&self.config.chords[index])
146                    } else {
147                        None
148                    }
149                });
150
151        pressed_chords.for_each(|&chord| {
152            for &i in chord.as_slice() {
153                if let Err(pos) = res.binary_search(&i) {
154                    res.insert(pos, i).unwrap();
155                }
156            }
157        });
158
159        res
160    }
161
162    /// Returns the chords for the given keymap index.
163    ///
164    /// - If a chord with that index is resolved as active, return a vec with only that chord.
165    /// - Otherwise, return a vec with all the chords which include the keymap index
166    ///   and could be satisfied. (i.e. chords which do not overlap with resolved active chords).
167    pub fn chords_for_keymap_index(
168        &self,
169        keymap_index: u16,
170    ) -> heapless::Vec<ChordState, { MAX_CHORDS }> {
171        match self.pressed_chord_with_index(keymap_index) {
172            Some(chord_state) => heapless::Vec::from_slice(&[chord_state]).unwrap(),
173            None => {
174                let chords_indices_span = self.pressed_chords_indices_span();
175                self.config
176                    .chords
177                    .iter()
178                    .enumerate()
179                    // filter: satisfiable chords
180                    .filter(|&(_index, chord)| chord.has_index(keymap_index))
181                    .filter(|&(_index, chord)| {
182                        // Filter out chords which overlap with resolved active chords.
183                        chords_indices_span.is_empty()
184                            || chord.indices.iter().all(|&i| {
185                                // The chord index is not part of the pressed chords indices span.
186                                chords_indices_span.binary_search(&i).is_err()
187                            })
188                    })
189                    .map(|(index, &chord)| ChordState {
190                        index,
191                        chord,
192                        is_satisfied: false,
193                    })
194                    .collect()
195            }
196        }
197    }
198
199    fn insert_pressed_index(&mut self, pos: usize, index: u16) {
200        if self.pressed_indices.is_empty() {
201            return;
202        }
203
204        let mut i = self.pressed_indices.len() - 1;
205        while i > pos {
206            self.pressed_indices[i] = self.pressed_indices[i - 1];
207            i -= 1;
208        }
209
210        self.pressed_indices[pos] = Some(index);
211    }
212
213    fn remove_pressed_index(&mut self, pos: usize) {
214        if self.pressed_indices.is_empty() {
215            return;
216        }
217
218        let mut i = pos;
219        while i < self.pressed_indices.len() - 1 {
220            self.pressed_indices[i] = self.pressed_indices[i + 1];
221            i += 1;
222        }
223
224        self.pressed_indices[self.pressed_indices.len() - 1] = None;
225    }
226
227    fn press_index(&mut self, index: u16) {
228        match self
229            .pressed_indices
230            .binary_search_by_key(&index, |&k| k.unwrap_or(u16::MAX))
231        {
232            Ok(_) => {}
233            Err(pos) => self.insert_pressed_index(pos, index),
234        }
235    }
236
237    fn release_index(&mut self, index: u16) {
238        if let Ok(pos) = self
239            .pressed_indices
240            .binary_search_by_key(&index, |&k| k.unwrap_or(u16::MAX))
241        {
242            self.remove_pressed_index(pos)
243        }
244    }
245
246    /// Updates the context for the given key event.
247    pub fn handle_event(&mut self, event: key::Event<Event>) {
248        match event {
249            key::Event::Input(input::Event::Press { keymap_index }) => {
250                self.press_index(keymap_index);
251            }
252            key::Event::Input(input::Event::Release { keymap_index }) => {
253                self.release_index(keymap_index);
254
255                // Ensure every chord which includes this keymap index
256                //  is not marked as 'pressed'.
257                self.config
258                    .chords
259                    .iter()
260                    .enumerate()
261                    .for_each(|(chord_id, chord_indices)| {
262                        if chord_indices.has_index(keymap_index) {
263                            self.pressed_chords[chord_id] = false;
264                        }
265                    });
266            }
267            key::Event::Key {
268                keymap_index: _,
269                key_event: Event::ChordResolved(ChordResolution::Chord(chord_id)),
270            } => {
271                self.pressed_chords[chord_id as usize] = true;
272            }
273            _ => {}
274        }
275    }
276}
277
278/// Primary Chorded key (with a passthrough key).
279///
280/// The primary key is the key with the lowest index in the chord,
281///  and has the key used for the resolved chord.
282#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
283pub struct Key<K: Copy> {
284    /// The chorded key
285    pub chords: Slice<(ChordId, K), MAX_OVERLAPPING_CHORD_SIZE>,
286    /// The passthrough key
287    pub passthrough: K,
288}
289
290impl<K: Copy + key::Key> Key<K>
291where
292    for<'c> &'c K::Context: Into<&'c Context>,
293    K::Event: TryInto<Event>,
294    K::Event: From<Event>,
295    K::PendingKeyState: From<PendingKeyState>,
296{
297    /// Constructs new pressed key.
298    pub fn new_pressed_key(
299        &self,
300        context: &K::Context,
301        key_path: key::KeyPath,
302    ) -> (
303        key::PressedKeyResult<K::PendingKeyState, K::KeyState>,
304        key::KeyEvents<K::Event>,
305    ) {
306        let chorded_ctx: &Context = context.into();
307
308        let keymap_index = key_path.keymap_index();
309        let pks = PendingKeyState::new(context.into(), keymap_index);
310
311        let chord_resolution = pks.check_resolution();
312
313        if let PendingChordState::Resolved(resolution) = chord_resolution {
314            let maybe_pathel_key = match resolution {
315                ChordResolution::Chord(resolved_chord_id) => {
316                    // Whether the resolved chord is associated with this key.
317                    // (i.e. the resolved chord's primary keymap index is this keymap index).
318                    if let Some(resolved_chord_indices) =
319                        chorded_ctx.config.chords.get(resolved_chord_id as usize)
320                    {
321                        if resolved_chord_indices.as_slice()[0] == keymap_index {
322                            if let Some((_, _k)) = self
323                                .chords
324                                .iter()
325                                .find(|(ch_id, _)| *ch_id == resolved_chord_id)
326                            {
327                                Some(1 + resolved_chord_id)
328                            } else {
329                                panic!("check_resolution has invalid chord id")
330                            }
331                        } else {
332                            None
333                        }
334                    } else {
335                        panic!("check_resolution has invalid chord id")
336                    }
337                }
338                ChordResolution::Passthrough => Some(0),
339            };
340
341            if let Some(i) = maybe_pathel_key {
342                // PRESSED KEY PATH: add Chord (0 = passthrough, 1 = 1+chord_id)
343                let new_key_path = key_path.append_path_item(i as u16);
344                let pkr = key::PressedKeyResult::NewPressedKey(key::NewPressedKey::key_path(
345                    new_key_path,
346                ));
347                let pke = key::KeyEvents::no_events();
348
349                (pkr, pke)
350            } else {
351                let pkr = key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp);
352                let pke = key::KeyEvents::no_events();
353                (pkr, pke)
354            }
355        } else {
356            let pkr = key::PressedKeyResult::Pending(key_path, pks.into());
357
358            let timeout_ev = Event::Timeout;
359            let ctx: &Context = context.into();
360            let sch_ev = key::ScheduledEvent::after(
361                ctx.config.timeout,
362                key::Event::key_event(keymap_index, timeout_ev),
363            );
364            let pke = key::KeyEvents::scheduled_event(sch_ev.into_scheduled_event());
365
366            (pkr, pke)
367        }
368    }
369}
370
371impl<K: Copy> Key<K> {
372    /// Constructs new chorded key.
373    pub const fn new(chords: &[(ChordId, K)], passthrough: K) -> Self {
374        let chords = Slice::from_slice(chords);
375        Key {
376            chords,
377            passthrough,
378        }
379    }
380}
381
382impl<
383        K: Copy
384            + key::Key<
385                Context = crate::init::Context,
386                Event = crate::init::Event,
387                PendingKeyState = crate::init::PendingKeyState,
388                KeyState = crate::init::KeyState,
389            >,
390    > key::Key for Key<K>
391{
392    type Context = crate::init::Context;
393    type Event = crate::init::Event;
394    type PendingKeyState = crate::init::PendingKeyState;
395    type KeyState = crate::init::KeyState;
396
397    fn new_pressed_key(
398        &self,
399        context: &Self::Context,
400        key_path: key::KeyPath,
401    ) -> (
402        key::PressedKeyResult<Self::PendingKeyState, Self::KeyState>,
403        key::KeyEvents<Self::Event>,
404    ) {
405        self.new_pressed_key(context, key_path)
406    }
407
408    fn handle_event(
409        &self,
410        pending_state: &mut Self::PendingKeyState,
411        context: &Self::Context,
412        key_path: key::KeyPath,
413        event: key::Event<Self::Event>,
414    ) -> (Option<key::NewPressedKey>, key::KeyEvents<Self::Event>) {
415        let keymap_index = key_path.keymap_index();
416        let ch_pks_res: Result<&mut PendingKeyState, _> = pending_state.try_into();
417        if let Ok(ch_pks) = ch_pks_res {
418            if let Ok(ch_ev) = event.try_into_key_event(|e| e.try_into()) {
419                let ch_state = ch_pks.handle_event(keymap_index, ch_ev);
420
421                // Whether handling the event resulted in a chord resolution.
422                if let Some(ch_state) = ch_state {
423                    let chorded_ctx: &Context = context.into();
424                    let maybe_pathel_and_key = match ch_state {
425                        ChordResolution::Chord(resolved_chord_id) => {
426                            // Whether the resolved chord is associated with this key.
427                            // (i.e. the resolved chord's primary keymap index is this keymap index).
428                            if let Some(resolved_chord_indices) =
429                                chorded_ctx.config.chords.get(resolved_chord_id as usize)
430                            {
431                                if resolved_chord_indices.as_slice()[0] == keymap_index {
432                                    if let Some((_, _k)) = self
433                                        .chords
434                                        .iter()
435                                        .find(|(ch_id, _)| *ch_id == resolved_chord_id)
436                                    {
437                                        Some(1 + resolved_chord_id)
438                                    } else {
439                                        panic!("event's chord resolution has invalid chord id")
440                                    }
441                                } else {
442                                    None
443                                }
444                            } else {
445                                panic!("event's chord resolution has invalid chord id")
446                            }
447                        }
448                        ChordResolution::Passthrough => Some(0),
449                    };
450
451                    let ch_r_ev = Event::ChordResolved(ch_state);
452                    let sch_ev = key::ScheduledEvent::immediate(key::Event::key_event(
453                        keymap_index,
454                        ch_r_ev.into(),
455                    ));
456
457                    if let Some(i) = maybe_pathel_and_key {
458                        // PRESSED KEY PATH: add Chord (0 = passthrough, 1 = 1+chord_id)
459                        let new_key_path = key_path.append_path_item(i as u16);
460
461                        let pke = key::KeyEvents::scheduled_event(sch_ev);
462
463                        (Some(key::NewPressedKey::key_path(new_key_path)), pke)
464                    } else {
465                        let pke = key::KeyEvents::scheduled_event(sch_ev);
466                        (Some(key::NewPressedKey::no_op()), pke)
467                    }
468                } else {
469                    (None, key::KeyEvents::no_events())
470                }
471            } else {
472                (None, key::KeyEvents::no_events())
473            }
474        } else {
475            (None, key::KeyEvents::no_events())
476        }
477    }
478
479    fn lookup(
480        &self,
481        path: &[u16],
482    ) -> &dyn key::Key<
483        Context = Self::Context,
484        Event = Self::Event,
485        PendingKeyState = Self::PendingKeyState,
486        KeyState = Self::KeyState,
487    > {
488        match path {
489            [] => self,
490            // 0 = passthrough, 1 = 1+chord_id
491            [0, path @ ..] => self.passthrough.lookup(path),
492            [chord_id_plus_1, path @ ..] => {
493                let chord_id = *chord_id_plus_1 as u8 - 1;
494                if let Some((_, k)) = self.chords.iter().find(|(ch_id, _)| *ch_id == chord_id) {
495                    k.lookup(path)
496                } else {
497                    panic!("invalid lookup path for key::chorded::Key, invalid chord id")
498                }
499            }
500        }
501    }
502}
503
504/// Auxiliary chorded key (with a passthrough key).
505///
506/// The auxiliary keys are chorded keys,
507///  but don't store the resolved chord key.
508/// (i.e. After te primary chorded key, the remaining keys
509///  in the chord are defined with auxiliary chorded keys).
510#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
511pub struct AuxiliaryKey<K> {
512    /// The passthrough key
513    pub passthrough: K,
514}
515
516impl<K: key::Key> AuxiliaryKey<K>
517where
518    for<'c> &'c K::Context: Into<&'c Context>,
519    K::Event: TryInto<Event>,
520    K::Event: From<Event>,
521    K::PendingKeyState: From<PendingKeyState>,
522{
523    /// Constructs new pressed key.
524    pub fn new_pressed_key(
525        &self,
526        context: &K::Context,
527        key_path: key::KeyPath,
528    ) -> (
529        key::PressedKeyResult<K::PendingKeyState, K::KeyState>,
530        key::KeyEvents<K::Event>,
531    ) {
532        let keymap_index = key_path.keymap_index();
533        let pks = PendingKeyState::new(context.into(), keymap_index);
534
535        let chord_resolution = pks.check_resolution();
536
537        if let PendingChordState::Resolved(resolution) = chord_resolution {
538            match resolution {
539                ChordResolution::Chord(_resolved_chord_id) => {
540                    let pkr = key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp);
541                    let pke = key::KeyEvents::no_events();
542
543                    (pkr, pke)
544                }
545                ChordResolution::Passthrough => {
546                    let new_key_path = key_path.append_path_item(0); // 0 = passthrough key
547                    let pkr = key::PressedKeyResult::NewPressedKey(key::NewPressedKey::key_path(
548                        new_key_path,
549                    ));
550                    let pke = key::KeyEvents::no_events();
551                    (pkr, pke)
552                }
553            }
554        } else {
555            let pkr = key::PressedKeyResult::Pending(key_path, pks.into());
556
557            let timeout_ev = Event::Timeout;
558            let ctx: &Context = context.into();
559            let sch_ev = key::ScheduledEvent::after(
560                ctx.config.timeout,
561                key::Event::key_event(keymap_index, timeout_ev),
562            );
563            let pke = key::KeyEvents::scheduled_event(sch_ev.into_scheduled_event());
564
565            (pkr, pke)
566        }
567    }
568}
569
570impl<K> AuxiliaryKey<K> {
571    /// Constructs new auxiliary chorded key.
572    pub const fn new(passthrough: K) -> Self {
573        AuxiliaryKey { passthrough }
574    }
575}
576
577impl<
578        K: key::Key<
579            Context = crate::init::Context,
580            Event = crate::init::Event,
581            PendingKeyState = crate::init::PendingKeyState,
582            KeyState = crate::init::KeyState,
583        >,
584    > key::Key for AuxiliaryKey<K>
585{
586    type Context = crate::init::Context;
587    type Event = crate::init::Event;
588    type PendingKeyState = crate::init::PendingKeyState;
589    type KeyState = crate::init::KeyState;
590
591    fn new_pressed_key(
592        &self,
593        context: &Self::Context,
594        key_path: key::KeyPath,
595    ) -> (
596        key::PressedKeyResult<Self::PendingKeyState, Self::KeyState>,
597        key::KeyEvents<Self::Event>,
598    ) {
599        self.new_pressed_key(context, key_path)
600    }
601
602    fn handle_event(
603        &self,
604        pending_state: &mut Self::PendingKeyState,
605        _context: &Self::Context,
606        key_path: key::KeyPath,
607        event: key::Event<Self::Event>,
608    ) -> (Option<key::NewPressedKey>, key::KeyEvents<Self::Event>) {
609        let keymap_index = key_path.keymap_index();
610        let ch_pks_res: Result<&mut PendingKeyState, _> = pending_state.try_into();
611        if let Ok(ch_pks) = ch_pks_res {
612            if let Ok(ch_ev) = event.try_into_key_event(|e| e.try_into()) {
613                let ch_state = ch_pks.handle_event(keymap_index, ch_ev);
614                if let Some(ChordResolution::Passthrough) = ch_state {
615                    let new_key_path = key_path.append_path_item(0); // 0 = passthrough key
616
617                    let ch_r_ev = Event::ChordResolved(ChordResolution::Passthrough);
618                    let sch_ev = key::ScheduledEvent::immediate(key::Event::key_event(
619                        keymap_index,
620                        ch_r_ev.into(),
621                    ));
622                    let pke = key::KeyEvents::scheduled_event(sch_ev);
623
624                    (Some(key::NewPressedKey::key_path(new_key_path)), pke)
625                } else if let Some(ChordResolution::Chord(resolved_chord_id)) = ch_state {
626                    let ch_r_ev = Event::ChordResolved(ChordResolution::Chord(resolved_chord_id));
627                    let pke =
628                        key::KeyEvents::event(key::Event::key_event(keymap_index, ch_r_ev.into()));
629
630                    (Some(key::NewPressedKey::no_op()), pke)
631                } else {
632                    (None, key::KeyEvents::no_events())
633                }
634            } else {
635                (None, key::KeyEvents::no_events())
636            }
637        } else {
638            (None, key::KeyEvents::no_events())
639        }
640    }
641
642    fn lookup(
643        &self,
644        path: &[u16],
645    ) -> &dyn key::Key<
646        Context = Self::Context,
647        Event = Self::Event,
648        PendingKeyState = Self::PendingKeyState,
649        KeyState = Self::KeyState,
650    > {
651        match path {
652            [] => self,
653            [0, path @ ..] => self.passthrough.lookup(path),
654            _ => panic!("illegal path"),
655        }
656    }
657}
658
659/// Events for chorded keys.
660#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
661pub enum Event {
662    /// The chorded key was resolved.
663    ChordResolved(ChordResolution),
664
665    /// Timed out waiting for chord to be satisfied.
666    Timeout,
667}
668
669/// Whether the pressed key state has resolved to a chord or not.
670#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
671pub enum ChordResolution {
672    /// Resolved as chord.
673    Chord(ChordId),
674    /// Resolved as passthrough key.
675    Passthrough,
676}
677
678/// The resolution state of a chorded key.
679#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
680pub enum PendingChordState {
681    /// The key state is resolved (as chord or as passthrough).
682    Resolved(ChordResolution),
683    /// The key chord state is pending.
684    ///
685    /// The chord may be pending with the ID of a satisfied chord.
686    Pending(Option<ChordId>),
687}
688
689/// State for pressed keys.
690#[derive(Debug, Clone, PartialEq)]
691pub struct PendingKeyState {
692    /// The keymap indices which have been pressed while the key is pending.
693    pressed_indices: heapless::Vec<u16, { MAX_CHORD_SIZE }>,
694    /// The chords which this pending key could resolve to.
695    possible_chords: heapless::Vec<ChordState, { MAX_CHORDS }>,
696}
697
698impl PendingKeyState {
699    /// Constructs a new [PendingKeyState].
700    pub fn new(context: &Context, keymap_index: u16) -> Self {
701        let pressed_indices = heapless::Vec::from_slice(&[keymap_index]).unwrap();
702        let possible_chords = context.chords_for_keymap_index(keymap_index);
703
704        Self {
705            pressed_indices,
706            possible_chords,
707        }
708    }
709
710    /// Finds the chord state amongst possible_chords which is satisfied (if it exists).
711    fn satisfied_chord(&self) -> Option<&ChordState> {
712        self.possible_chords
713            .iter()
714            .find(|&ChordState { is_satisfied, .. }| *is_satisfied)
715    }
716
717    fn check_resolution(&self) -> PendingChordState {
718        match self.possible_chords.as_slice() {
719            [ChordState {
720                index,
721                is_satisfied,
722                ..
723            }] if *is_satisfied => {
724                // Only one chord is satisfied by pressed indices.
725                //
726                // This resolves the chord.
727                PendingChordState::Resolved(ChordResolution::Chord(*index as u8))
728            }
729            [] => {
730                // Otherwise, this key state resolves to "Passthrough",
731                //  since it has been interrupted by an unrelated key press.
732                PendingChordState::Resolved(ChordResolution::Passthrough)
733            }
734            satisfiable_chords => {
735                // Overlapping chords.
736                PendingChordState::Pending(
737                    satisfiable_chords
738                        .iter()
739                        .find(|&ChordState { is_satisfied, .. }| *is_satisfied)
740                        .map(|&ChordState { index, .. }| index as u8),
741                )
742            }
743        }
744    }
745
746    /// Handle PKS for primary chorded key.
747    pub fn handle_event(
748        &mut self,
749        keymap_index: u16,
750        event: key::Event<Event>,
751    ) -> Option<ChordResolution> {
752        match event {
753            key::Event::Key {
754                keymap_index: _ev_idx,
755                key_event: Event::Timeout,
756            } => {
757                // Timed out before chord unambiguously resolved.
758                let maybe_satisfied_chord_id = self
759                    .satisfied_chord()
760                    .map(|chord_state| chord_state.index as u8);
761                match maybe_satisfied_chord_id {
762                    Some(satisfied_chord_id) => Some(ChordResolution::Chord(satisfied_chord_id)),
763                    _ => Some(ChordResolution::Passthrough),
764                }
765            }
766            key::Event::Input(input::Event::Press {
767                keymap_index: pressed_keymap_index,
768            }) => {
769                // Another key was pressed.
770
771                let maybe_satisfied_chord_id = self
772                    .satisfied_chord()
773                    .map(|chord_state| chord_state.index as u8);
774
775                // Update pressed_indices.
776                let pos = self
777                    .pressed_indices
778                    .binary_search(&keymap_index)
779                    .unwrap_or_else(|e| e);
780                let push_res = self.pressed_indices.insert(pos, pressed_keymap_index);
781                // pressed_indices has capacity of MAX_CHORD_SIZE.
782                // pressed_indices will only be full without resolving
783                // if multiple chords with max chord size
784                //  having the same indices.
785                if push_res.is_err() {
786                    panic!();
787                }
788
789                // Chords only remain possible if they have the pressed keymap index.
790                self.possible_chords
791                    .retain(|chord_state| chord_state.chord.has_index(pressed_keymap_index));
792
793                // Re-evaluate the chord satisfaction states.
794                for chord in self.possible_chords.iter_mut() {
795                    chord.is_satisfied = chord.chord.is_satisfied_by(&self.pressed_indices);
796                }
797
798                let resolution = match self.check_resolution() {
799                    PendingChordState::Resolved(resolution) => Some(resolution),
800                    PendingChordState::Pending(_) => None,
801                };
802
803                // If the chord resolution is now passthrough (i.e. no chords satisfiable),
804                // then resolve the chord with the satisfied chord.
805                match (resolution, maybe_satisfied_chord_id) {
806                    (Some(ChordResolution::Passthrough), Some(satisfied_chord_id)) => {
807                        Some(ChordResolution::Chord(satisfied_chord_id))
808                    }
809                    _ => resolution,
810                }
811            }
812            key::Event::Input(input::Event::Release {
813                keymap_index: released_keymap_index,
814            }) => {
815                if released_keymap_index == keymap_index {
816                    let maybe_satisfied_chord_id = self
817                        .satisfied_chord()
818                        .map(|chord_state| chord_state.index as u8);
819
820                    match maybe_satisfied_chord_id {
821                        Some(satisfied_chord_id) => {
822                            Some(ChordResolution::Chord(satisfied_chord_id))
823                        }
824
825                        // This key state resolves to "Passthrough",
826                        //  since it has been released before any chord is satisfied.
827                        None => Some(ChordResolution::Passthrough),
828                    }
829                } else {
830                    None
831                }
832            }
833            _ => None,
834        }
835    }
836}
837
838#[cfg(test)]
839mod tests {
840    use super::*;
841
842    use key::composite;
843    use key::keyboard;
844
845    use key::Context as _;
846
847    #[test]
848    fn test_timeout_resolves_unsatisfied_aux_state_as_passthrough_key() {
849        // Assemble: an Auxilary chorded key, and its PKS.
850        let context = key::composite::Context::default();
851        let expected_key = keyboard::Key::new(0x04);
852        let _chorded_key = AuxiliaryKey {
853            passthrough: expected_key,
854        };
855        let keymap_index: u16 = 0;
856        let mut pks: PendingKeyState = PendingKeyState::new((&context).into(), keymap_index);
857
858        // Act: handle a timeout ev.
859        let timeout_ev = key::Event::key_event(keymap_index, Event::Timeout).into_key_event();
860        let actual_res = pks.handle_event(keymap_index, timeout_ev);
861
862        // Assert
863        let expected_res = Some(ChordResolution::Passthrough);
864        assert_eq!(expected_res, actual_res);
865    }
866
867    // #[test]
868    // fn test_timeout_resolves_satisfied_key_state_as_chord() {}
869
870    #[test]
871    fn test_press_non_chorded_key_resolves_aux_state_as_interrupted() {
872        // Assemble: an Auxilary chorded key, and its PKS.
873        let context = key::composite::Context::default();
874        let expected_key = keyboard::Key::new(0x04);
875        let _chorded_key = AuxiliaryKey {
876            passthrough: expected_key,
877        };
878        let keymap_index: u16 = 0;
879        let mut pks: PendingKeyState = PendingKeyState::new((&context).into(), keymap_index);
880
881        // Act: handle a key press, for an index that's not part of any chord.
882        let non_chord_press = input::Event::Press { keymap_index: 9 }.into();
883        let actual_res = pks.handle_event(keymap_index, non_chord_press);
884
885        // Assert
886        let expected_res = Some(ChordResolution::Passthrough);
887        assert_eq!(expected_res, actual_res);
888    }
889
890    // "unambiguous" in the sense that the chord
891    // is not overlapped by another chord.
892    // e.g. chord "01" is overlapped by chord "012",
893    //  and "pressed {0, 1}" would be 'ambiguous';
894    //  wheres "pressed {0, 1, 2}" would be 'unambiguous'.
895
896    #[test]
897    fn test_press_chorded_key_resolves_unambiguous_aux_state_as_chord() {
898        // Assemble: an Auxilary chorded key, and its PKS, with chord 01.
899        let mut context = key::composite::Context::from_config(composite::Config {
900            chorded: Config {
901                chords: Slice::from_slice(&[ChordIndices::from_slice(&[0, 1])]),
902                ..DEFAULT_CONFIG
903            },
904            ..composite::DEFAULT_CONFIG
905        });
906
907        let passthrough = keyboard::Key::new(0x04);
908        let _chorded_key = AuxiliaryKey { passthrough };
909        let keymap_index: u16 = 0;
910        context.handle_event(key::Event::Input(input::Event::Press { keymap_index: 0 }));
911        let mut pks: PendingKeyState = PendingKeyState::new((&context).into(), keymap_index);
912
913        // Act: handle a key press, for an index that completes (satisfies unambiguously) the chord.
914        let chord_press = input::Event::Press { keymap_index: 1 }.into();
915        let actual_res = pks.handle_event(keymap_index, chord_press);
916
917        // Assert: resolved aux key should have no events, should have (resolved) no output.
918        let expected_res = Some(ChordResolution::Chord(0));
919        assert_eq!(expected_res, actual_res);
920    }
921
922    // #[test]
923    // fn test_release_resolved_chord_state_releases_chord() {}
924
925    // This is better covered with an integration test.
926    // #[test]
927    // fn test_release_resolved_aux_passthrough_state_releases_passthrough_key() {}
928
929    #[test]
930    fn test_release_pending_aux_state_resolves_as_tapped_key() {
931        // Assemble: an Auxilary chorded key, and its PKS.
932        let context = key::composite::Context::default();
933        let expected_key = keyboard::Key::new(0x04);
934        let _chorded_key = AuxiliaryKey {
935            passthrough: expected_key,
936        };
937        let keymap_index: u16 = 0;
938        let mut pks: PendingKeyState = PendingKeyState::new((&context).into(), keymap_index);
939
940        // Act: handle a key press, for an index that's not part of any chord.
941        let chorded_key_release = input::Event::Release { keymap_index }.into();
942        let actual_res = pks.handle_event(keymap_index, chorded_key_release);
943
944        // Assert
945        let expected_res = Some(ChordResolution::Passthrough);
946        assert_eq!(expected_res, actual_res);
947    }
948}