1use core::fmt::Debug;
2use core::marker::PhantomData;
3use core::ops::Index;
4
5use serde::Deserialize;
6
7use crate::{input, key, keymap, slice::Slice};
8
9#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
11pub enum Ref {
12 Chorded(u8),
14 Auxiliary(u8),
16}
17
18pub type ChordId = u8;
20
21#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
23#[serde(from = "heapless::Vec<u16, MAX_CHORD_SIZE>")]
24pub struct ChordIndices<const MAX_CHORD_SIZE: usize> {
25 indices: Slice<u16, MAX_CHORD_SIZE>,
27}
28
29impl<const MAX_CHORD_SIZE: usize> ChordIndices<MAX_CHORD_SIZE> {
30 pub const fn from_slice(indices: &[u16]) -> ChordIndices<MAX_CHORD_SIZE> {
34 ChordIndices {
35 indices: Slice::from_slice(indices),
36 }
37 }
38
39 pub const fn as_slice(&self) -> &[u16] {
41 self.indices.as_slice()
42 }
43
44 pub fn has_index(&self, index: u16) -> bool {
46 self.as_slice().contains(&index)
47 }
48
49 pub fn is_satisfied_by(&self, indices: &[u16]) -> bool {
51 self.as_slice().iter().all(|&i| indices.contains(&i))
52 }
53}
54
55impl<const MAX_CHORD_SIZE: usize> From<heapless::Vec<u16, MAX_CHORD_SIZE>>
56 for ChordIndices<MAX_CHORD_SIZE>
57{
58 fn from(v: heapless::Vec<u16, MAX_CHORD_SIZE>) -> Self {
59 ChordIndices::from_slice(&v)
60 }
61}
62
63#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
65pub struct Config<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize> {
66 #[serde(default = "default_timeout")]
70 pub timeout: u16,
71
72 pub chords: Slice<ChordIndices<MAX_CHORD_SIZE>, MAX_CHORDS>,
74
75 pub required_idle_time: Option<u16>,
81}
82
83pub const DEFAULT_TIMEOUT: u16 = 200;
85
86const fn default_timeout() -> u16 {
87 DEFAULT_TIMEOUT
88}
89
90impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize> Config<MAX_CHORDS, MAX_CHORD_SIZE> {
91 pub const fn new() -> Self {
93 Self {
94 timeout: DEFAULT_TIMEOUT,
95 chords: Slice::from_slice(&[]),
96 required_idle_time: None,
97 }
98 }
99}
100
101impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize> Default
102 for Config<MAX_CHORDS, MAX_CHORD_SIZE>
103{
104 fn default() -> Self {
106 Self::new()
107 }
108}
109
110#[derive(Debug, Clone, PartialEq)]
112pub struct ChordState<const MAX_CHORD_SIZE: usize> {
113 pub index: usize,
115 pub chord: ChordIndices<MAX_CHORD_SIZE>,
117 pub is_satisfied: bool,
119}
120
121#[derive(Debug, Clone, Copy, PartialEq)]
123pub struct Context<
124 const MAX_CHORDS: usize,
125 const MAX_CHORD_SIZE: usize,
126 const MAX_PRESSED_INDICES: usize,
127> {
128 config: Config<MAX_CHORDS, MAX_CHORD_SIZE>,
129 pressed_indices: [Option<u16>; MAX_PRESSED_INDICES],
130 pressed_chords: [bool; MAX_CHORDS],
131 idle_time_ms: u32,
132 ignore_idle_time: bool,
133 latest_resolved_chord: Option<ChordId>,
134}
135
136impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize, const MAX_PRESSED_INDICES: usize>
137 Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>
138{
139 pub const fn from_config(config: Config<MAX_CHORDS, MAX_CHORD_SIZE>) -> Self {
141 let pressed_indices = [None; MAX_PRESSED_INDICES];
142 Context {
143 config,
144 pressed_indices,
145 pressed_chords: [false; MAX_CHORDS],
146 idle_time_ms: 0,
147 ignore_idle_time: false,
148 latest_resolved_chord: None,
149 }
150 }
151
152 pub fn update_keymap_context(
154 &mut self,
155 keymap::KeymapContext { idle_time_ms, .. }: &keymap::KeymapContext,
156 ) {
157 self.idle_time_ms = *idle_time_ms;
158 }
159
160 fn sufficient_idle_time(&self) -> bool {
161 let sufficient_idle_time =
162 self.idle_time_ms >= self.config.required_idle_time.unwrap_or(0) as u32;
163
164 sufficient_idle_time || self.ignore_idle_time
165 }
166
167 fn pressed_chord_with_index(&self, keymap_index: u16) -> Option<ChordState<MAX_CHORD_SIZE>> {
168 self.pressed_chords
169 .iter()
170 .enumerate()
171 .filter_map(|(index, &is_pressed)| {
172 if is_pressed {
173 Some(ChordState {
174 index,
175 chord: self.config.chords[index],
176 is_satisfied: true,
177 })
178 } else {
179 None
180 }
181 })
182 .find(|ChordState { chord, .. }| chord.has_index(keymap_index))
183 }
184
185 fn pressed_chords_indices_span(&self) -> heapless::Vec<u16, MAX_PRESSED_INDICES> {
187 let mut res: heapless::Vec<u16, MAX_PRESSED_INDICES> = heapless::Vec::new();
188
189 let pressed_chords =
190 self.pressed_chords
191 .iter()
192 .enumerate()
193 .filter_map(|(index, &is_pressed)| {
194 if is_pressed {
195 Some(&self.config.chords[index])
196 } else {
197 None
198 }
199 });
200
201 pressed_chords.for_each(|&chord| {
202 for &i in chord.as_slice() {
203 if let Err(pos) = res.binary_search(&i) {
204 res.insert(pos, i).unwrap();
205 }
206 }
207 });
208
209 res
210 }
211
212 pub fn chords_for_keymap_index(
218 &self,
219 keymap_index: u16,
220 ) -> heapless::Vec<ChordState<MAX_CHORD_SIZE>, { MAX_CHORDS }> {
221 match self.pressed_chord_with_index(keymap_index) {
222 Some(chord_state) => heapless::Vec::from_slice(&[chord_state]).unwrap(),
223 None => {
224 let chords_indices_span = self.pressed_chords_indices_span();
225 self.config
226 .chords
227 .iter()
228 .enumerate()
229 .filter(|&(_index, chord)| chord.has_index(keymap_index))
231 .filter(|&(_index, chord)| {
232 chords_indices_span.is_empty()
234 || chord.indices.iter().all(|&i| {
235 chords_indices_span.binary_search(&i).is_err()
237 })
238 })
239 .map(|(index, &chord)| ChordState {
240 index,
241 chord,
242 is_satisfied: false,
243 })
244 .collect()
245 }
246 }
247 }
248
249 fn insert_pressed_index(&mut self, pos: usize, index: u16) {
250 if self.pressed_indices.is_empty() {
251 return;
252 }
253
254 let mut i = self.pressed_indices.len() - 1;
255 while i > pos {
256 self.pressed_indices[i] = self.pressed_indices[i - 1];
257 i -= 1;
258 }
259
260 self.pressed_indices[pos] = Some(index);
261 }
262
263 fn remove_pressed_index(&mut self, pos: usize) {
264 if self.pressed_indices.is_empty() {
265 return;
266 }
267
268 let mut i = pos;
269 while i < self.pressed_indices.len() - 1 {
270 self.pressed_indices[i] = self.pressed_indices[i + 1];
271 i += 1;
272 }
273
274 self.pressed_indices[self.pressed_indices.len() - 1] = None;
275 }
276
277 fn press_index(&mut self, index: u16) {
278 match self
279 .pressed_indices
280 .binary_search_by_key(&index, |&k| k.unwrap_or(u16::MAX))
281 {
282 Ok(_) => {}
283 Err(pos) => self.insert_pressed_index(pos, index),
284 }
285 }
286
287 fn release_index(&mut self, index: u16) {
288 if let Ok(pos) = self
289 .pressed_indices
290 .binary_search_by_key(&index, |&k| k.unwrap_or(u16::MAX))
291 {
292 self.remove_pressed_index(pos)
293 }
294 }
295
296 fn handle_event(&mut self, event: key::Event<Event>) {
298 match event {
299 key::Event::Input(input::Event::Press { keymap_index }) => {
300 self.press_index(keymap_index);
301
302 let span = self.pressed_chords_indices_span();
306 if span.contains(&keymap_index) {
307 self.ignore_idle_time = true;
309 } else {
310 if let Some(chord_id) = self.latest_resolved_chord {
312 let chord_indices = self.config.chords[chord_id as usize];
313 self.ignore_idle_time = chord_indices.has_index(keymap_index);
314 } else {
315 self.ignore_idle_time = false;
316
317 self.latest_resolved_chord = None;
320 }
321 }
322 }
323 key::Event::Input(input::Event::Release { keymap_index }) => {
324 self.release_index(keymap_index);
325
326 self.config
329 .chords
330 .iter()
331 .enumerate()
332 .for_each(|(chord_id, chord_indices)| {
333 if chord_indices.has_index(keymap_index) {
334 self.pressed_chords[chord_id] = false;
335 }
336 });
337 }
338 key::Event::Key {
339 keymap_index: _,
340 key_event: Event::ChordResolved(ChordResolution::Chord(chord_id)),
341 } => {
342 self.pressed_chords[chord_id as usize] = true;
343 self.latest_resolved_chord = Some(chord_id);
344 }
345 _ => {}
346 }
347 }
348}
349
350impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize, const MAX_PRESSED_INDICES: usize>
351 key::Context for Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>
352{
353 type Event = Event;
354
355 fn handle_event(&mut self, event: key::Event<Self::Event>) -> key::KeyEvents<Self::Event> {
356 self.handle_event(event);
357 key::KeyEvents::no_events()
358 }
359}
360
361#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
366pub struct Key<
367 R: Copy,
368 const MAX_CHORDS: usize,
369 const MAX_CHORD_SIZE: usize,
370 const MAX_OVERLAPPING_CHORD_SIZE: usize,
371 const MAX_PRESSED_INDICES: usize,
372> {
373 pub chords: Slice<(ChordId, R), MAX_OVERLAPPING_CHORD_SIZE>,
375 pub passthrough: R,
377 #[serde(default)]
378 marker: PhantomData<(
379 [(); MAX_CHORDS],
380 [(); MAX_CHORD_SIZE],
381 [(); MAX_PRESSED_INDICES],
382 )>,
383}
384
385impl<
386 R: Copy,
387 const MAX_CHORDS: usize,
388 const MAX_CHORD_SIZE: usize,
389 const MAX_OVERLAPPING_CHORD_SIZE: usize,
390 const MAX_PRESSED_INDICES: usize,
391 > Key<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_OVERLAPPING_CHORD_SIZE, MAX_PRESSED_INDICES>
392{
393 pub fn new_pressed_key(
395 &self,
396 context: &Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
397 keymap_index: u16,
398 ) -> (
399 key::PressedKeyResult<
400 R,
401 PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
402 KeyState,
403 >,
404 key::KeyEvents<Event>,
405 ) {
406 let pks = PendingKeyState::new(context, keymap_index);
407
408 let chord_resolution = if context.sufficient_idle_time() {
409 pks.check_resolution()
410 } else {
411 PendingChordState::Resolved(ChordResolution::Passthrough)
412 };
413
414 if let PendingChordState::Resolved(resolution) = chord_resolution {
415 let maybe_new_key_ref = match resolution {
416 ChordResolution::Chord(resolved_chord_id) => {
417 if let Some(resolved_chord_indices) =
420 context.config.chords.get(resolved_chord_id as usize)
421 {
422 if resolved_chord_indices.as_slice()[0] == keymap_index {
423 if let Some((_, new_key_ref)) = self
424 .chords
425 .iter()
426 .find(|(ch_id, _)| *ch_id == resolved_chord_id)
427 {
428 Some(*new_key_ref)
429 } else {
430 panic!("check_resolution has invalid chord id")
431 }
432 } else {
433 None
434 }
435 } else {
436 panic!("check_resolution has invalid chord id")
437 }
438 }
439 ChordResolution::Passthrough => Some(self.passthrough),
440 };
441
442 if let Some(new_key_ref) = maybe_new_key_ref {
443 let pkr =
444 key::PressedKeyResult::NewPressedKey(key::NewPressedKey::key(new_key_ref));
445 let pke = key::KeyEvents::no_events();
446
447 (pkr, pke)
448 } else {
449 let pkr = key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp);
450 let pke = key::KeyEvents::no_events();
451 (pkr, pke)
452 }
453 } else {
454 let pkr = key::PressedKeyResult::Pending(pks);
455
456 let timeout_ev = Event::Timeout;
457 let sch_ev = key::ScheduledEvent::after(
458 context.config.timeout,
459 key::Event::key_event(keymap_index, timeout_ev),
460 );
461 let pke = key::KeyEvents::scheduled_event(sch_ev);
462
463 (pkr, pke)
464 }
465 }
466
467 pub const fn new(chords: &[(ChordId, R)], passthrough: R) -> Self {
469 let chords = Slice::from_slice(chords);
470 Key {
471 chords,
472 passthrough,
473 marker: PhantomData,
474 }
475 }
476
477 fn update_pending_state(
478 &self,
479 pending_state: &mut PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
480 keymap_index: u16,
481 context: &Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
482 event: key::Event<Event>,
483 ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Event>) {
484 let ch_state = pending_state.handle_event(keymap_index, event);
485
486 if let Some(ch_state) = ch_state {
488 let maybe_new_key_ref = match ch_state {
489 ChordResolution::Chord(resolved_chord_id) => {
490 if let Some(resolved_chord_indices) =
493 context.config.chords.get(resolved_chord_id as usize)
494 {
495 if resolved_chord_indices.as_slice()[0] == keymap_index {
496 if let Some((_, key_ref)) = self
497 .chords
498 .iter()
499 .find(|(ch_id, _)| *ch_id == resolved_chord_id)
500 {
501 Some(*key_ref)
502 } else {
503 panic!("event's chord resolution has invalid chord id")
504 }
505 } else {
506 None
507 }
508 } else {
509 panic!("event's chord resolution has invalid chord id")
510 }
511 }
512 ChordResolution::Passthrough => Some(self.passthrough),
513 };
514
515 let ch_r_ev = Event::ChordResolved(ch_state);
516 let sch_ev =
517 key::ScheduledEvent::immediate(key::Event::key_event(keymap_index, ch_r_ev));
518
519 if let Some(new_key_ref) = maybe_new_key_ref {
520 let pke = key::KeyEvents::scheduled_event(sch_ev);
521
522 (Some(key::NewPressedKey::key(new_key_ref)), pke)
523 } else {
524 let pke = key::KeyEvents::scheduled_event(sch_ev);
525 (Some(key::NewPressedKey::no_op()), pke)
526 }
527 } else {
528 (None, key::KeyEvents::no_events())
529 }
530 }
531}
532
533#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
540pub struct AuxiliaryKey<
541 R,
542 const MAX_CHORDS: usize,
543 const MAX_CHORD_SIZE: usize,
544 const MAX_PRESSED_INDICES: usize,
545> {
546 pub passthrough: R,
548 #[serde(default)]
549 marker: PhantomData<(
550 [(); MAX_CHORDS],
551 [(); MAX_CHORD_SIZE],
552 [(); MAX_PRESSED_INDICES],
553 )>,
554}
555
556impl<
557 R: Copy,
558 const MAX_CHORDS: usize,
559 const MAX_CHORD_SIZE: usize,
560 const MAX_PRESSED_INDICES: usize,
561 > AuxiliaryKey<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>
562{
563 pub fn new_pressed_key(
565 &self,
566 context: &Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
567 keymap_index: u16,
568 ) -> (
569 key::PressedKeyResult<
570 R,
571 PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
572 KeyState,
573 >,
574 key::KeyEvents<Event>,
575 ) {
576 let pks = PendingKeyState::new(context, keymap_index);
577
578 let chord_resolution = if context.sufficient_idle_time() {
579 pks.check_resolution()
580 } else {
581 PendingChordState::Resolved(ChordResolution::Passthrough)
582 };
583
584 if let PendingChordState::Resolved(resolution) = chord_resolution {
585 match resolution {
586 ChordResolution::Chord(_resolved_chord_id) => {
587 let pkr = key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp);
588 let pke = key::KeyEvents::no_events();
589
590 (pkr, pke)
591 }
592 ChordResolution::Passthrough => {
593 let new_key_ref = self.passthrough;
594 let pkr =
595 key::PressedKeyResult::NewPressedKey(key::NewPressedKey::key(new_key_ref));
596 let pke = key::KeyEvents::no_events();
597 (pkr, pke)
598 }
599 }
600 } else {
601 let pkr = key::PressedKeyResult::Pending(pks);
602
603 let timeout_ev = Event::Timeout;
604 let sch_ev = key::ScheduledEvent::after(
605 context.config.timeout,
606 key::Event::key_event(keymap_index, timeout_ev),
607 );
608 let pke = key::KeyEvents::scheduled_event(sch_ev);
609
610 (pkr, pke)
611 }
612 }
613
614 pub const fn new(passthrough: R) -> Self {
616 AuxiliaryKey {
617 passthrough,
618 marker: PhantomData,
619 }
620 }
621
622 fn update_pending_state(
623 &self,
624 pending_state: &mut PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
625 keymap_index: u16,
626 event: key::Event<Event>,
627 ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Event>) {
628 let ch_state = pending_state.handle_event(keymap_index, event);
629 if let Some(ChordResolution::Passthrough) = ch_state {
630 let ch_r_ev = Event::ChordResolved(ChordResolution::Passthrough);
631 let sch_ev =
632 key::ScheduledEvent::immediate(key::Event::key_event(keymap_index, ch_r_ev));
633 let pke = key::KeyEvents::scheduled_event(sch_ev);
634
635 (Some(key::NewPressedKey::key(self.passthrough)), pke)
636 } else if let Some(ChordResolution::Chord(resolved_chord_id)) = ch_state {
637 let ch_r_ev = Event::ChordResolved(ChordResolution::Chord(resolved_chord_id));
638 let pke = key::KeyEvents::event(key::Event::key_event(keymap_index, ch_r_ev));
639
640 (Some(key::NewPressedKey::no_op()), pke)
641 } else {
642 (None, key::KeyEvents::no_events())
643 }
644 }
645}
646
647#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
649pub enum Event {
650 ChordResolved(ChordResolution),
652
653 Timeout,
655}
656
657#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
659pub enum ChordResolution {
660 Chord(ChordId),
662 Passthrough,
664}
665
666#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
668pub enum PendingChordState {
669 Resolved(ChordResolution),
671 Pending(Option<ChordId>),
675}
676
677#[derive(Debug, Clone, PartialEq)]
679pub struct PendingKeyState<
680 const MAX_CHORDS: usize,
681 const MAX_CHORD_SIZE: usize,
682 const MAX_PRESSED_INDICES: usize,
683> {
684 pressed_indices: heapless::Vec<u16, { MAX_CHORD_SIZE }>,
686 possible_chords: heapless::Vec<ChordState<MAX_CHORD_SIZE>, { MAX_CHORDS }>,
688 marker: PhantomData<[(); MAX_PRESSED_INDICES]>,
689}
690
691impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize, const MAX_PRESSED_INDICES: usize>
692 PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>
693{
694 pub fn new(
696 context: &Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
697 keymap_index: u16,
698 ) -> Self {
699 let pressed_indices = heapless::Vec::from_slice(&[keymap_index]).unwrap();
700 let possible_chords = context.chords_for_keymap_index(keymap_index);
701
702 Self {
703 pressed_indices,
704 possible_chords,
705 marker: PhantomData,
706 }
707 }
708
709 fn satisfied_chord(&self) -> Option<&ChordState<MAX_CHORD_SIZE>> {
711 self.possible_chords
712 .iter()
713 .find(|&ChordState { is_satisfied, .. }| *is_satisfied)
714 }
715
716 fn check_resolution(&self) -> PendingChordState {
717 match self.possible_chords.as_slice() {
718 [ChordState {
719 index,
720 is_satisfied,
721 ..
722 }] if *is_satisfied => {
723 PendingChordState::Resolved(ChordResolution::Chord(*index as u8))
727 }
728 [] => {
729 PendingChordState::Resolved(ChordResolution::Passthrough)
732 }
733 satisfiable_chords => {
734 PendingChordState::Pending(
736 satisfiable_chords
737 .iter()
738 .find(|&ChordState { is_satisfied, .. }| *is_satisfied)
739 .map(|&ChordState { index, .. }| index as u8),
740 )
741 }
742 }
743 }
744
745 pub fn handle_event(
747 &mut self,
748 keymap_index: u16,
749 event: key::Event<Event>,
750 ) -> Option<ChordResolution> {
751 match event {
752 key::Event::Key {
753 keymap_index: _ev_idx,
754 key_event: Event::Timeout,
755 } => {
756 let maybe_satisfied_chord_id = self
758 .satisfied_chord()
759 .map(|chord_state| chord_state.index as u8);
760 match maybe_satisfied_chord_id {
761 Some(satisfied_chord_id) => Some(ChordResolution::Chord(satisfied_chord_id)),
762 _ => Some(ChordResolution::Passthrough),
763 }
764 }
765 key::Event::Input(input::Event::Press {
766 keymap_index: pressed_keymap_index,
767 }) => {
768 let maybe_satisfied_chord_id = self
771 .satisfied_chord()
772 .map(|chord_state| chord_state.index as u8);
773
774 let pos = self
776 .pressed_indices
777 .binary_search(&keymap_index)
778 .unwrap_or_else(|e| e);
779 let push_res = self.pressed_indices.insert(pos, pressed_keymap_index);
780 if push_res.is_err() {
785 panic!();
786 }
787
788 self.possible_chords
790 .retain(|chord_state| chord_state.chord.has_index(pressed_keymap_index));
791
792 for chord in self.possible_chords.iter_mut() {
794 chord.is_satisfied = chord.chord.is_satisfied_by(&self.pressed_indices);
795 }
796
797 let resolution = match self.check_resolution() {
798 PendingChordState::Resolved(resolution) => Some(resolution),
799 PendingChordState::Pending(_) => None,
800 };
801
802 match (resolution, maybe_satisfied_chord_id) {
805 (Some(ChordResolution::Passthrough), Some(satisfied_chord_id)) => {
806 Some(ChordResolution::Chord(satisfied_chord_id))
807 }
808 _ => resolution,
809 }
810 }
811 key::Event::Input(input::Event::Release {
812 keymap_index: released_keymap_index,
813 }) => {
814 if released_keymap_index == keymap_index {
815 let maybe_satisfied_chord_id = self
816 .satisfied_chord()
817 .map(|chord_state| chord_state.index as u8);
818
819 match maybe_satisfied_chord_id {
820 Some(satisfied_chord_id) => {
821 Some(ChordResolution::Chord(satisfied_chord_id))
822 }
823
824 None => Some(ChordResolution::Passthrough),
827 }
828 } else {
829 None
830 }
831 }
832 _ => None,
833 }
834 }
835}
836
837#[derive(Debug, Clone, Copy, PartialEq)]
839pub struct KeyState;
840
841#[derive(Debug, Clone, Copy, PartialEq)]
843pub struct System<
844 R: Copy + Debug + PartialEq,
845 Keys: Index<
846 usize,
847 Output = Key<
848 R,
849 MAX_CHORDS,
850 MAX_CHORD_SIZE,
851 MAX_OVERLAPPING_CHORD_SIZE,
852 MAX_PRESSED_INDICES,
853 >,
854 >,
855 AuxiliaryKeys: Index<usize, Output = AuxiliaryKey<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>>,
856 const MAX_CHORDS: usize,
857 const MAX_CHORD_SIZE: usize,
858 const MAX_OVERLAPPING_CHORD_SIZE: usize,
859 const MAX_PRESSED_INDICES: usize,
860> {
861 keys: Keys,
862 auxiliary_keys: AuxiliaryKeys,
863}
864
865impl<
866 R: Copy + Debug + PartialEq,
867 Keys: Index<
868 usize,
869 Output = Key<
870 R,
871 MAX_CHORDS,
872 MAX_CHORD_SIZE,
873 MAX_OVERLAPPING_CHORD_SIZE,
874 MAX_PRESSED_INDICES,
875 >,
876 >,
877 AuxiliaryKeys: Index<usize, Output = AuxiliaryKey<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>>,
878 const MAX_CHORDS: usize,
879 const MAX_CHORD_SIZE: usize,
880 const MAX_OVERLAPPING_CHORD_SIZE: usize,
881 const MAX_PRESSED_INDICES: usize,
882 >
883 System<
884 R,
885 Keys,
886 AuxiliaryKeys,
887 MAX_CHORDS,
888 MAX_CHORD_SIZE,
889 MAX_OVERLAPPING_CHORD_SIZE,
890 MAX_PRESSED_INDICES,
891 >
892{
893 pub const fn new(keys: Keys, auxiliary_keys: AuxiliaryKeys) -> Self {
895 Self {
896 keys,
897 auxiliary_keys,
898 }
899 }
900}
901
902impl<
903 R: Copy + Debug + PartialEq,
904 Keys: Debug
905 + Index<
906 usize,
907 Output = Key<
908 R,
909 MAX_CHORDS,
910 MAX_CHORD_SIZE,
911 MAX_OVERLAPPING_CHORD_SIZE,
912 MAX_PRESSED_INDICES,
913 >,
914 >,
915 AuxiliaryKeys: Debug
916 + Index<usize, Output = AuxiliaryKey<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>>,
917 const MAX_CHORDS: usize,
918 const MAX_CHORD_SIZE: usize,
919 const MAX_OVERLAPPING_CHORD_SIZE: usize,
920 const MAX_PRESSED_INDICES: usize,
921 > key::System<R>
922 for System<
923 R,
924 Keys,
925 AuxiliaryKeys,
926 MAX_CHORDS,
927 MAX_CHORD_SIZE,
928 MAX_OVERLAPPING_CHORD_SIZE,
929 MAX_PRESSED_INDICES,
930 >
931{
932 type Ref = Ref;
933 type Context = Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>;
934 type Event = Event;
935 type PendingKeyState = PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>;
936 type KeyState = KeyState;
937
938 fn new_pressed_key(
939 &self,
940 keymap_index: u16,
941 context: &Self::Context,
942 key_ref: Ref,
943 ) -> (
944 key::PressedKeyResult<R, Self::PendingKeyState, Self::KeyState>,
945 key::KeyEvents<Self::Event>,
946 ) {
947 match key_ref {
948 Ref::Chorded(i) => self.keys[i as usize].new_pressed_key(context, keymap_index),
949 Ref::Auxiliary(i) => {
950 self.auxiliary_keys[i as usize].new_pressed_key(context, keymap_index)
951 }
952 }
953 }
954
955 fn update_pending_state(
956 &self,
957 pending_state: &mut Self::PendingKeyState,
958 keymap_index: u16,
959 context: &Self::Context,
960 key_ref: Ref,
961 event: key::Event<Self::Event>,
962 ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Self::Event>) {
963 match key_ref {
964 Ref::Chorded(i) => self.keys[i as usize].update_pending_state(
965 pending_state,
966 keymap_index,
967 context,
968 event,
969 ),
970 Ref::Auxiliary(i) => self.auxiliary_keys[i as usize].update_pending_state(
971 pending_state,
972 keymap_index,
973 event,
974 ),
975 }
976 }
977
978 fn update_state(
979 &self,
980 _key_state: &mut Self::KeyState,
981 _key_ref: &Self::Ref,
982 _context: &Self::Context,
983 _keymap_index: u16,
984 _event: key::Event<Self::Event>,
985 ) -> key::KeyEvents<Self::Event> {
986 panic!()
987 }
988
989 fn key_output(
990 &self,
991 _key_ref: &Self::Ref,
992 _key_state: &Self::KeyState,
993 ) -> Option<key::KeyOutput> {
994 panic!()
995 }
996}
997
998#[cfg(test)]
999mod tests {
1000 use super::*;
1001
1002 use key::keyboard;
1003
1004 const MAX_CHORDS: usize = 4;
1005 const MAX_CHORD_SIZE: usize = 16;
1006 const MAX_PRESSED_INDICES: usize = MAX_CHORD_SIZE * 2;
1007
1008 const DEFAULT_CONTEXT: Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES> =
1009 Context::from_config(Config::new());
1010
1011 type AuxiliaryKey =
1012 super::AuxiliaryKey<keyboard::Ref, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>;
1013 type PendingKeyState = super::PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>;
1014
1015 #[test]
1016 fn test_sizeof_ref() {
1017 assert_eq!(2, core::mem::size_of::<Ref>());
1018 }
1019
1020 #[test]
1021 fn test_sizeof_event() {
1022 assert_eq!(2, core::mem::size_of::<Event>());
1023 }
1024
1025 #[test]
1026 fn test_timeout_resolves_unsatisfied_aux_state_as_passthrough_key() {
1027 let context = DEFAULT_CONTEXT;
1029 let expected_ref = keyboard::Ref::KeyCode(0x04);
1030 let _chorded_key = AuxiliaryKey::new(expected_ref);
1031 let keymap_index: u16 = 0;
1032 let mut pks: PendingKeyState = PendingKeyState::new(&context, keymap_index);
1033
1034 let timeout_ev = key::Event::key_event(keymap_index, Event::Timeout);
1036 let actual_resolution = pks.handle_event(keymap_index, timeout_ev);
1037
1038 let expected_resolution = Some(ChordResolution::Passthrough);
1040 assert_eq!(expected_resolution, actual_resolution);
1041 }
1042
1043 #[test]
1044 fn test_press_non_chorded_key_resolves_aux_state_as_interrupted() {
1045 let context = DEFAULT_CONTEXT;
1047 let expected_ref = keyboard::Ref::KeyCode(0x04);
1048 let _chorded_key = AuxiliaryKey::new(expected_ref);
1049 let keymap_index: u16 = 0;
1050 let mut pks: PendingKeyState = PendingKeyState::new(&context, keymap_index);
1051
1052 let non_chord_press = input::Event::Press { keymap_index: 9 }.into();
1054 let actual_resolution = pks.handle_event(keymap_index, non_chord_press);
1055
1056 let expected_resolution = Some(ChordResolution::Passthrough);
1058 assert_eq!(expected_resolution, actual_resolution);
1059 }
1060
1061 #[test]
1068 fn test_press_chorded_key_resolves_unambiguous_aux_state_as_chord() {
1069 let mut context = Context::from_config(Config {
1071 chords: Slice::from_slice(&[ChordIndices::from_slice(&[0, 1])]),
1072 ..Config::new()
1073 });
1074 let passthrough = keyboard::Ref::KeyCode(0x04);
1075 let _chorded_key = AuxiliaryKey::new(passthrough);
1076 let keymap_index: u16 = 0;
1077 context.handle_event(key::Event::Input(input::Event::Press { keymap_index: 0 }));
1078 let mut pks: PendingKeyState = PendingKeyState::new(&context, keymap_index);
1079
1080 let chord_press = input::Event::Press { keymap_index: 1 }.into();
1082 let actual_resolution = pks.handle_event(keymap_index, chord_press);
1083
1084 let expected_resolution = Some(ChordResolution::Chord(0));
1086 assert_eq!(expected_resolution, actual_resolution);
1087 }
1088
1089 #[test]
1090 fn test_release_pending_aux_state_resolves_as_tapped_key() {
1091 let context = DEFAULT_CONTEXT;
1093 let expected_ref = keyboard::Ref::KeyCode(0x04);
1094 let _chorded_key = AuxiliaryKey::new(expected_ref);
1095 let keymap_index: u16 = 0;
1096 let mut pks: PendingKeyState = PendingKeyState::new(&context, keymap_index);
1097
1098 let chorded_key_release = input::Event::Release { keymap_index }.into();
1100 let actual_resolution = pks.handle_event(keymap_index, chorded_key_release);
1101
1102 let expected_resolution = Some(ChordResolution::Passthrough);
1104 assert_eq!(expected_resolution, actual_resolution);
1105 }
1106}