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#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
12pub enum Ref {
13 Chorded(u8),
15 Auxiliary(u8),
17}
18
19pub type ChordId = u8;
21
22#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
24#[serde(from = "heapless::Vec<u16, MAX_CHORD_SIZE>")]
25pub struct ChordIndices {
26 indices: Slice<u16, MAX_CHORD_SIZE>,
28}
29
30impl ChordIndices {
31 pub const fn from_slice(indices: &[u16]) -> ChordIndices {
35 ChordIndices {
36 indices: Slice::from_slice(indices),
37 }
38 }
39
40 pub const fn as_slice(&self) -> &[u16] {
42 self.indices.as_slice()
43 }
44
45 pub fn has_index(&self, index: u16) -> bool {
47 self.as_slice().contains(&index)
48 }
49
50 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#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
64pub struct Config {
65 #[serde(default = "default_timeout")]
69 pub timeout: u16,
70
71 pub chords: Slice<ChordIndices, MAX_CHORDS>,
73}
74
75fn default_timeout() -> u16 {
76 DEFAULT_CONFIG.timeout
77}
78
79pub const DEFAULT_CONFIG: Config = Config {
81 timeout: 200,
82 chords: Slice::from_slice(&[]),
83};
84
85impl Default for Config {
86 fn default() -> Self {
88 DEFAULT_CONFIG
89 }
90}
91
92#[derive(Debug, Clone, PartialEq)]
94pub struct ChordState {
95 pub index: usize,
97 pub chord: ChordIndices,
99 pub is_satisfied: bool,
101}
102
103#[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
111pub const DEFAULT_CONTEXT: Context = Context::from_config(DEFAULT_CONFIG);
113
114impl Context {
115 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 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 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(|&(_index, chord)| chord.has_index(keymap_index))
189 .filter(|&(_index, chord)| {
190 chords_indices_span.is_empty()
192 || chord.indices.iter().all(|&i| {
193 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 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 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#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
291pub struct Key<R: Copy> {
292 pub chords: Slice<(ChordId, R), MAX_OVERLAPPING_CHORD_SIZE>,
294 pub passthrough: R,
296}
297
298impl<R: Copy> Key<R> {
299 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 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 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 if let Some(ch_state) = ch_state {
387 let maybe_new_key_ref = match ch_state {
388 ChordResolution::Chord(resolved_chord_id) => {
389 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#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
439pub struct AuxiliaryKey<R> {
440 pub passthrough: R,
442}
443
444impl<R: Copy> AuxiliaryKey<R> {
445 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 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#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
522pub enum Event {
523 ChordResolved(ChordResolution),
525
526 Timeout,
528}
529
530#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
532pub enum ChordResolution {
533 Chord(ChordId),
535 Passthrough,
537}
538
539#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
541pub enum PendingChordState {
542 Resolved(ChordResolution),
544 Pending(Option<ChordId>),
548}
549
550#[derive(Debug, Clone, PartialEq)]
552pub struct PendingKeyState {
553 pressed_indices: heapless::Vec<u16, { MAX_CHORD_SIZE }>,
555 possible_chords: heapless::Vec<ChordState, { MAX_CHORDS }>,
557}
558
559impl PendingKeyState {
560 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 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 PendingChordState::Resolved(ChordResolution::Chord(*index as u8))
589 }
590 [] => {
591 PendingChordState::Resolved(ChordResolution::Passthrough)
594 }
595 satisfiable_chords => {
596 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 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 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 let maybe_satisfied_chord_id = self
633 .satisfied_chord()
634 .map(|chord_state| chord_state.index as u8);
635
636 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 if push_res.is_err() {
647 panic!();
648 }
649
650 self.possible_chords
652 .retain(|chord_state| chord_state.chord.has_index(pressed_keymap_index));
653
654 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 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 None => Some(ChordResolution::Passthrough),
689 }
690 } else {
691 None
692 }
693 }
694 _ => None,
695 }
696 }
697}
698
699#[derive(Debug, Clone, PartialEq)]
701pub struct KeyState;
702
703#[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 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 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 let timeout_ev = key::Event::key_event(keymap_index, Event::Timeout);
822 let actual_resolution = pks.handle_event(keymap_index, timeout_ev);
823
824 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 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 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 let expected_resolution = Some(ChordResolution::Passthrough);
846 assert_eq!(expected_resolution, actual_resolution);
847 }
848
849 #[test]
856 fn test_press_chorded_key_resolves_unambiguous_aux_state_as_chord() {
857 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 let chord_press = input::Event::Press { keymap_index: 1 }.into();
870 let actual_resolution = pks.handle_event(keymap_index, chord_press);
871
872 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 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 let chorded_key_release = input::Event::Release { keymap_index }.into();
890 let actual_resolution = pks.handle_event(keymap_index, chorded_key_release);
891
892 let expected_resolution = Some(ChordResolution::Passthrough);
894 assert_eq!(expected_resolution, actual_resolution);
895 }
896}