smart_keymap/key/
chorded.rs

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