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
11pub type ChordId = u8;
13
14#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
16#[serde(from = "heapless::Vec<u16, MAX_CHORD_SIZE>")]
17pub struct ChordIndices {
18 indices: Slice<u16, MAX_CHORD_SIZE>,
20}
21
22impl ChordIndices {
23 pub const fn from_slice(indices: &[u16]) -> ChordIndices {
27 ChordIndices {
28 indices: Slice::from_slice(indices),
29 }
30 }
31
32 pub const fn as_slice(&self) -> &[u16] {
34 self.indices.as_slice()
35 }
36
37 pub fn has_index(&self, index: u16) -> bool {
39 self.as_slice().iter().any(|&i| i == index)
40 }
41
42 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#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
56pub struct Config {
57 #[serde(default = "default_timeout")]
61 pub timeout: u16,
62
63 pub chords: Slice<ChordIndices, MAX_CHORDS>,
65}
66
67fn default_timeout() -> u16 {
68 DEFAULT_CONFIG.timeout
69}
70
71pub const DEFAULT_CONFIG: Config = Config {
73 timeout: 200,
74 chords: Slice::from_slice(&[]),
75};
76
77impl Default for Config {
78 fn default() -> Self {
80 DEFAULT_CONFIG
81 }
82}
83
84#[derive(Debug, Clone, PartialEq)]
86pub struct ChordState {
87 pub index: usize,
89 pub chord: ChordIndices,
91 pub is_satisfied: bool,
93}
94
95#[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
103pub const DEFAULT_CONTEXT: Context = Context::from_config(DEFAULT_CONFIG);
105
106impl Context {
107 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 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 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(|&(_index, chord)| chord.has_index(keymap_index))
181 .filter(|&(_index, chord)| {
182 chords_indices_span.is_empty()
184 || chord.indices.iter().all(|&i| {
185 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 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 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#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
283pub struct Key<K: Copy> {
284 pub chords: Slice<(ChordId, K), MAX_OVERLAPPING_CHORD_SIZE>,
286 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 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 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 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 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 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 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 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, 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#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
511pub struct AuxiliaryKey<K> {
512 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 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); 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 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); 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#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
661pub enum Event {
662 ChordResolved(ChordResolution),
664
665 Timeout,
667}
668
669#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
671pub enum ChordResolution {
672 Chord(ChordId),
674 Passthrough,
676}
677
678#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
680pub enum PendingChordState {
681 Resolved(ChordResolution),
683 Pending(Option<ChordId>),
687}
688
689#[derive(Debug, Clone, PartialEq)]
691pub struct PendingKeyState {
692 pressed_indices: heapless::Vec<u16, { MAX_CHORD_SIZE }>,
694 possible_chords: heapless::Vec<ChordState, { MAX_CHORDS }>,
696}
697
698impl PendingKeyState {
699 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 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 PendingChordState::Resolved(ChordResolution::Chord(*index as u8))
728 }
729 [] => {
730 PendingChordState::Resolved(ChordResolution::Passthrough)
733 }
734 satisfiable_chords => {
735 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 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 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 let maybe_satisfied_chord_id = self
772 .satisfied_chord()
773 .map(|chord_state| chord_state.index as u8);
774
775 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 if push_res.is_err() {
786 panic!();
787 }
788
789 self.possible_chords
791 .retain(|chord_state| chord_state.chord.has_index(pressed_keymap_index));
792
793 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 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 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 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 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 let expected_res = Some(ChordResolution::Passthrough);
864 assert_eq!(expected_res, actual_res);
865 }
866
867 #[test]
871 fn test_press_non_chorded_key_resolves_aux_state_as_interrupted() {
872 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 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 let expected_res = Some(ChordResolution::Passthrough);
887 assert_eq!(expected_res, actual_res);
888 }
889
890 #[test]
897 fn test_press_chorded_key_resolves_unambiguous_aux_state_as_chord() {
898 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 let chord_press = input::Event::Press { keymap_index: 1 }.into();
915 let actual_res = pks.handle_event(keymap_index, chord_press);
916
917 let expected_res = Some(ChordResolution::Chord(0));
919 assert_eq!(expected_res, actual_res);
920 }
921
922 #[test]
930 fn test_release_pending_aux_state_resolves_as_tapped_key() {
931 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 let chorded_key_release = input::Event::Release { keymap_index }.into();
942 let actual_res = pks.handle_event(keymap_index, chorded_key_release);
943
944 let expected_res = Some(ChordResolution::Passthrough);
946 assert_eq!(expected_res, actual_res);
947 }
948}