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, 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
83impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize> core::fmt::Debug
84 for Config<MAX_CHORDS, MAX_CHORD_SIZE>
85{
86 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87 f.debug_struct("Config")
88 .field("timeout", &self.timeout)
89 .field("chords", &self.chords.as_slice())
90 .field("required_idle_time", &self.required_idle_time)
91 .finish()
92 }
93}
94
95pub const DEFAULT_TIMEOUT: u16 = 200;
97
98const fn default_timeout() -> u16 {
99 DEFAULT_TIMEOUT
100}
101
102impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize> Config<MAX_CHORDS, MAX_CHORD_SIZE> {
103 pub const fn new() -> Self {
105 Self {
106 timeout: DEFAULT_TIMEOUT,
107 chords: Slice::from_slice(&[]),
108 required_idle_time: None,
109 }
110 }
111}
112
113impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize> Default
114 for Config<MAX_CHORDS, MAX_CHORD_SIZE>
115{
116 fn default() -> Self {
118 Self::new()
119 }
120}
121
122#[derive(Debug, Clone, PartialEq)]
124pub struct ChordState<const MAX_CHORD_SIZE: usize> {
125 pub index: usize,
127 pub chord: ChordIndices<MAX_CHORD_SIZE>,
129 pub is_satisfied: bool,
131}
132
133struct PressedIndicesDebugHelper<'a, const MAX_PRESSED_INDICES: usize> {
134 pressed_indices: &'a [Option<u16>; MAX_PRESSED_INDICES],
135}
136
137impl<const MAX_PRESSED_INDICES: usize> core::fmt::Debug
138 for PressedIndicesDebugHelper<'_, MAX_PRESSED_INDICES>
139{
140 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
141 let last_non_empty_pi_pos = self
143 .pressed_indices
144 .iter()
145 .rposition(|pi| pi.is_some())
146 .map_or(0, |pos| pos + 1);
147 if last_non_empty_pi_pos < MAX_PRESSED_INDICES {
148 f.debug_list()
149 .entries(&self.pressed_indices[..last_non_empty_pi_pos])
150 .finish_non_exhaustive()
151 } else {
152 f.debug_list().entries(&self.pressed_indices[..]).finish()
153 }
154 }
155}
156
157struct PressedChordsDebugHelper<'a, const MAX_CHORDS: usize> {
158 pressed_chords: &'a [bool; MAX_CHORDS],
159}
160
161impl<const MAX_CHORDS: usize> core::fmt::Debug for PressedChordsDebugHelper<'_, MAX_CHORDS> {
162 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
163 let last_true_pc_pos = self
165 .pressed_chords
166 .iter()
167 .rposition(|&pc| pc)
168 .map_or(0, |pos| pos + 1);
169 if last_true_pc_pos < MAX_CHORDS {
170 f.debug_list()
171 .entries(&self.pressed_chords[..last_true_pc_pos])
172 .finish_non_exhaustive()
173 } else {
174 f.debug_list().entries(&self.pressed_chords[..]).finish()
175 }
176 }
177}
178
179#[derive(Clone, Copy, PartialEq)]
181pub struct Context<
182 const MAX_CHORDS: usize,
183 const MAX_CHORD_SIZE: usize,
184 const MAX_PRESSED_INDICES: usize,
185> {
186 config: Config<MAX_CHORDS, MAX_CHORD_SIZE>,
187 pressed_indices: [Option<u16>; MAX_PRESSED_INDICES],
188 pressed_chords: [bool; MAX_CHORDS],
189 idle_time_ms: u32,
190 ignore_idle_time: bool,
191 latest_resolved_chord: Option<ChordId>,
192}
193
194impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize, const MAX_PRESSED_INDICES: usize> Debug
195 for Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>
196{
197 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
198 f.debug_struct("Context")
199 .field("config", &self.config)
200 .field(
201 "pressed_indices",
202 &PressedIndicesDebugHelper {
203 pressed_indices: &self.pressed_indices,
204 },
205 )
206 .field(
207 "pressed_chords",
208 &PressedChordsDebugHelper {
209 pressed_chords: &self.pressed_chords,
210 },
211 )
212 .field("idle_time_ms", &self.idle_time_ms)
213 .field("ignore_idle_time", &self.ignore_idle_time)
214 .field("latest_resolved_chord", &self.latest_resolved_chord)
215 .finish()
216 }
217}
218
219impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize, const MAX_PRESSED_INDICES: usize>
220 Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>
221{
222 pub const fn from_config(config: Config<MAX_CHORDS, MAX_CHORD_SIZE>) -> Self {
224 let pressed_indices = [None; MAX_PRESSED_INDICES];
225 Context {
226 config,
227 pressed_indices,
228 pressed_chords: [false; MAX_CHORDS],
229 idle_time_ms: 0,
230 ignore_idle_time: false,
231 latest_resolved_chord: None,
232 }
233 }
234
235 pub fn update_keymap_context(
237 &mut self,
238 keymap::KeymapContext { idle_time_ms, .. }: &keymap::KeymapContext,
239 ) {
240 self.idle_time_ms = *idle_time_ms;
241 }
242
243 fn sufficient_idle_time(&self) -> bool {
244 let sufficient_idle_time =
245 self.idle_time_ms >= self.config.required_idle_time.unwrap_or(0) as u32;
246
247 sufficient_idle_time || self.ignore_idle_time
248 }
249
250 fn pressed_chord_with_index(&self, keymap_index: u16) -> Option<ChordState<MAX_CHORD_SIZE>> {
251 self.pressed_chords
252 .iter()
253 .enumerate()
254 .filter_map(|(index, &is_pressed)| {
255 if is_pressed {
256 Some(ChordState {
257 index,
258 chord: self.config.chords[index],
259 is_satisfied: true,
260 })
261 } else {
262 None
263 }
264 })
265 .find(|ChordState { chord, .. }| chord.has_index(keymap_index))
266 }
267
268 fn pressed_chords_indices_span(&self) -> heapless::Vec<u16, MAX_PRESSED_INDICES> {
270 let mut res: heapless::Vec<u16, MAX_PRESSED_INDICES> = heapless::Vec::new();
271
272 let pressed_chords =
273 self.pressed_chords
274 .iter()
275 .enumerate()
276 .filter_map(|(index, &is_pressed)| {
277 if is_pressed {
278 Some(&self.config.chords[index])
279 } else {
280 None
281 }
282 });
283
284 pressed_chords.for_each(|&chord| {
285 for &i in chord.as_slice() {
286 if let Err(pos) = res.binary_search(&i) {
287 let _ = res.insert(pos, i);
288 }
289 }
290 });
291
292 res
293 }
294
295 pub fn chords_for_keymap_index(
301 &self,
302 keymap_index: u16,
303 ) -> heapless::Vec<ChordState<MAX_CHORD_SIZE>, { MAX_CHORDS }> {
304 match self.pressed_chord_with_index(keymap_index) {
305 Some(chord_state) => {
306 let mut chords = heapless::Vec::new();
307 let _ = chords.push(chord_state);
308 chords
309 }
310 None => {
311 let chords_indices_span = self.pressed_chords_indices_span();
312 self.config
313 .chords
314 .iter()
315 .enumerate()
316 .filter(|&(_index, chord)| chord.has_index(keymap_index))
318 .filter(|&(_index, chord)| {
319 chords_indices_span.is_empty()
321 || chord.indices.iter().all(|&i| {
322 chords_indices_span.binary_search(&i).is_err()
324 })
325 })
326 .map(|(index, &chord)| ChordState {
327 index,
328 chord,
329 is_satisfied: false,
330 })
331 .collect()
332 }
333 }
334 }
335
336 fn insert_pressed_index(&mut self, pos: usize, index: u16) {
337 if self.pressed_indices.is_empty() {
338 return;
339 }
340
341 let mut i = self.pressed_indices.len() - 1;
342 while i > pos {
343 self.pressed_indices[i] = self.pressed_indices[i - 1];
344 i -= 1;
345 }
346
347 self.pressed_indices[pos] = Some(index);
348 }
349
350 fn remove_pressed_index(&mut self, pos: usize) {
351 if self.pressed_indices.is_empty() {
352 return;
353 }
354
355 let mut i = pos;
356 while i < self.pressed_indices.len() - 1 {
357 self.pressed_indices[i] = self.pressed_indices[i + 1];
358 i += 1;
359 }
360
361 self.pressed_indices[self.pressed_indices.len() - 1] = None;
362 }
363
364 fn press_index(&mut self, index: u16) {
365 match self
366 .pressed_indices
367 .binary_search_by_key(&index, |&k| k.unwrap_or(u16::MAX))
368 {
369 Ok(_) => {}
370 Err(pos) => self.insert_pressed_index(pos, index),
371 }
372 }
373
374 fn release_index(&mut self, index: u16) {
375 if let Ok(pos) = self
376 .pressed_indices
377 .binary_search_by_key(&index, |&k| k.unwrap_or(u16::MAX))
378 {
379 self.remove_pressed_index(pos)
380 }
381 }
382
383 fn handle_event(&mut self, event: key::Event<Event>) {
385 match event {
386 key::Event::Input(input::Event::Press { keymap_index }) => {
387 self.press_index(keymap_index);
388
389 let span = self.pressed_chords_indices_span();
393 if span.contains(&keymap_index) {
394 self.ignore_idle_time = true;
396 } else {
397 if let Some(chord_id) = self.latest_resolved_chord {
399 let chord_indices = self.config.chords[chord_id as usize];
400 self.ignore_idle_time = chord_indices.has_index(keymap_index);
401 } else {
402 self.ignore_idle_time = false;
403
404 self.latest_resolved_chord = None;
407 }
408 }
409 }
410 key::Event::Input(input::Event::Release { keymap_index }) => {
411 self.release_index(keymap_index);
412
413 self.config
416 .chords
417 .iter()
418 .enumerate()
419 .for_each(|(chord_id, chord_indices)| {
420 if chord_indices.has_index(keymap_index) {
421 self.pressed_chords[chord_id] = false;
422 }
423 });
424 }
425 key::Event::Key {
426 keymap_index: _,
427 key_event: Event::ChordResolved(ChordResolution::Chord(chord_id)),
428 } => {
429 self.pressed_chords[chord_id as usize] = true;
430 self.latest_resolved_chord = Some(chord_id);
431 }
432 _ => {}
433 }
434 }
435}
436
437impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize, const MAX_PRESSED_INDICES: usize>
438 key::Context for Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>
439{
440 type Event = Event;
441
442 fn handle_event(&mut self, event: key::Event<Self::Event>) -> key::KeyEvents<Self::Event> {
443 self.handle_event(event);
444 key::KeyEvents::no_events()
445 }
446}
447
448#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
453pub struct Key<
454 R: Copy,
455 const MAX_CHORDS: usize,
456 const MAX_CHORD_SIZE: usize,
457 const MAX_OVERLAPPING_CHORD_SIZE: usize,
458 const MAX_PRESSED_INDICES: usize,
459> {
460 pub chords: Slice<(ChordId, R), MAX_OVERLAPPING_CHORD_SIZE>,
462 pub passthrough: R,
464 #[serde(default)]
465 marker: PhantomData<(
466 [(); MAX_CHORDS],
467 [(); MAX_CHORD_SIZE],
468 [(); MAX_PRESSED_INDICES],
469 )>,
470}
471
472impl<
473 R: Copy,
474 const MAX_CHORDS: usize,
475 const MAX_CHORD_SIZE: usize,
476 const MAX_OVERLAPPING_CHORD_SIZE: usize,
477 const MAX_PRESSED_INDICES: usize,
478 > Key<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_OVERLAPPING_CHORD_SIZE, MAX_PRESSED_INDICES>
479{
480 pub fn new_pressed_key(
482 &self,
483 context: &Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
484 keymap_index: u16,
485 ) -> (
486 key::PressedKeyResult<
487 R,
488 PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
489 KeyState,
490 >,
491 key::KeyEvents<Event>,
492 ) {
493 let pks = PendingKeyState::new(context, keymap_index);
494
495 let chord_resolution = if context.sufficient_idle_time() {
496 pks.check_resolution()
497 } else {
498 PendingChordState::Resolved(ChordResolution::Passthrough)
499 };
500
501 if let PendingChordState::Resolved(resolution) = chord_resolution {
502 let maybe_new_key_ref = match resolution {
503 ChordResolution::Chord(resolved_chord_id) => {
504 if let Some(resolved_chord_indices) =
507 context.config.chords.get(resolved_chord_id as usize)
508 {
509 if resolved_chord_indices.as_slice()[0] == keymap_index {
510 if let Some((_, new_key_ref)) = self
511 .chords
512 .iter()
513 .find(|(ch_id, _)| *ch_id == resolved_chord_id)
514 {
515 Some(*new_key_ref)
516 } else {
517 panic!("check_resolution has invalid chord id")
518 }
519 } else {
520 None
521 }
522 } else {
523 panic!("check_resolution has invalid chord id")
524 }
525 }
526 ChordResolution::Passthrough => Some(self.passthrough),
527 };
528
529 if let Some(new_key_ref) = maybe_new_key_ref {
530 let pkr =
531 key::PressedKeyResult::NewPressedKey(key::NewPressedKey::key(new_key_ref));
532 let pke = key::KeyEvents::no_events();
533
534 (pkr, pke)
535 } else {
536 let pkr = key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp);
537 let pke = key::KeyEvents::no_events();
538 (pkr, pke)
539 }
540 } else {
541 let pkr = key::PressedKeyResult::Pending(pks);
542
543 let timeout_ev = Event::Timeout;
544 let sch_ev = key::ScheduledEvent::after(
545 context.config.timeout,
546 key::Event::key_event(keymap_index, timeout_ev),
547 );
548 let pke = key::KeyEvents::scheduled_event(sch_ev);
549
550 (pkr, pke)
551 }
552 }
553
554 pub const fn new(chords: &[(ChordId, R)], passthrough: R) -> Self {
556 let chords = Slice::from_slice(chords);
557 Key {
558 chords,
559 passthrough,
560 marker: PhantomData,
561 }
562 }
563
564 fn update_pending_state(
565 &self,
566 pending_state: &mut PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
567 keymap_index: u16,
568 context: &Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
569 event: key::Event<Event>,
570 ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Event>) {
571 let ch_state = pending_state.handle_event(keymap_index, event);
572
573 if let Some(ch_state) = ch_state {
575 let maybe_new_key_ref = match ch_state {
576 ChordResolution::Chord(resolved_chord_id) => {
577 if let Some(resolved_chord_indices) =
580 context.config.chords.get(resolved_chord_id as usize)
581 {
582 if resolved_chord_indices.as_slice()[0] == keymap_index {
583 if let Some((_, key_ref)) = self
584 .chords
585 .iter()
586 .find(|(ch_id, _)| *ch_id == resolved_chord_id)
587 {
588 Some(*key_ref)
589 } else {
590 panic!("event's chord resolution has invalid chord id")
591 }
592 } else {
593 None
594 }
595 } else {
596 panic!("event's chord resolution has invalid chord id")
597 }
598 }
599 ChordResolution::Passthrough => Some(self.passthrough),
600 };
601
602 let ch_r_ev = Event::ChordResolved(ch_state);
603 let sch_ev =
604 key::ScheduledEvent::immediate(key::Event::key_event(keymap_index, ch_r_ev));
605
606 if let Some(new_key_ref) = maybe_new_key_ref {
607 let pke = key::KeyEvents::scheduled_event(sch_ev);
608
609 (Some(key::NewPressedKey::key(new_key_ref)), pke)
610 } else {
611 let pke = key::KeyEvents::scheduled_event(sch_ev);
612 (Some(key::NewPressedKey::no_op()), pke)
613 }
614 } else {
615 (None, key::KeyEvents::no_events())
616 }
617 }
618}
619
620#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
627pub struct AuxiliaryKey<
628 R,
629 const MAX_CHORDS: usize,
630 const MAX_CHORD_SIZE: usize,
631 const MAX_PRESSED_INDICES: usize,
632> {
633 pub passthrough: R,
635 #[serde(default)]
636 marker: PhantomData<(
637 [(); MAX_CHORDS],
638 [(); MAX_CHORD_SIZE],
639 [(); MAX_PRESSED_INDICES],
640 )>,
641}
642
643impl<
644 R: Copy,
645 const MAX_CHORDS: usize,
646 const MAX_CHORD_SIZE: usize,
647 const MAX_PRESSED_INDICES: usize,
648 > AuxiliaryKey<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>
649{
650 pub fn new_pressed_key(
652 &self,
653 context: &Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
654 keymap_index: u16,
655 ) -> (
656 key::PressedKeyResult<
657 R,
658 PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
659 KeyState,
660 >,
661 key::KeyEvents<Event>,
662 ) {
663 let pks = PendingKeyState::new(context, keymap_index);
664
665 let chord_resolution = if context.sufficient_idle_time() {
666 pks.check_resolution()
667 } else {
668 PendingChordState::Resolved(ChordResolution::Passthrough)
669 };
670
671 if let PendingChordState::Resolved(resolution) = chord_resolution {
672 match resolution {
673 ChordResolution::Chord(_resolved_chord_id) => {
674 let pkr = key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp);
675 let pke = key::KeyEvents::no_events();
676
677 (pkr, pke)
678 }
679 ChordResolution::Passthrough => {
680 let new_key_ref = self.passthrough;
681 let pkr =
682 key::PressedKeyResult::NewPressedKey(key::NewPressedKey::key(new_key_ref));
683 let pke = key::KeyEvents::no_events();
684 (pkr, pke)
685 }
686 }
687 } else {
688 let pkr = key::PressedKeyResult::Pending(pks);
689
690 let timeout_ev = Event::Timeout;
691 let sch_ev = key::ScheduledEvent::after(
692 context.config.timeout,
693 key::Event::key_event(keymap_index, timeout_ev),
694 );
695 let pke = key::KeyEvents::scheduled_event(sch_ev);
696
697 (pkr, pke)
698 }
699 }
700
701 pub const fn new(passthrough: R) -> Self {
703 AuxiliaryKey {
704 passthrough,
705 marker: PhantomData,
706 }
707 }
708
709 fn update_pending_state(
710 &self,
711 pending_state: &mut PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
712 keymap_index: u16,
713 event: key::Event<Event>,
714 ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Event>) {
715 let ch_state = pending_state.handle_event(keymap_index, event);
716 if let Some(ChordResolution::Passthrough) = ch_state {
717 let ch_r_ev = Event::ChordResolved(ChordResolution::Passthrough);
718 let sch_ev =
719 key::ScheduledEvent::immediate(key::Event::key_event(keymap_index, ch_r_ev));
720 let pke = key::KeyEvents::scheduled_event(sch_ev);
721
722 (Some(key::NewPressedKey::key(self.passthrough)), pke)
723 } else if let Some(ChordResolution::Chord(resolved_chord_id)) = ch_state {
724 let ch_r_ev = Event::ChordResolved(ChordResolution::Chord(resolved_chord_id));
725 let pke = key::KeyEvents::event(key::Event::key_event(keymap_index, ch_r_ev));
726
727 (Some(key::NewPressedKey::no_op()), pke)
728 } else {
729 (None, key::KeyEvents::no_events())
730 }
731 }
732}
733
734#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
736pub enum Event {
737 ChordResolved(ChordResolution),
739
740 Timeout,
742}
743
744#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
746pub enum ChordResolution {
747 Chord(ChordId),
749 Passthrough,
751}
752
753#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
755pub enum PendingChordState {
756 Resolved(ChordResolution),
758 Pending(Option<ChordId>),
762}
763
764#[derive(Debug, Clone, PartialEq)]
766pub struct PendingKeyState<
767 const MAX_CHORDS: usize,
768 const MAX_CHORD_SIZE: usize,
769 const MAX_PRESSED_INDICES: usize,
770> {
771 pressed_indices: heapless::Vec<u16, { MAX_CHORD_SIZE }>,
773 possible_chords: heapless::Vec<ChordState<MAX_CHORD_SIZE>, { MAX_CHORDS }>,
775 marker: PhantomData<[(); MAX_PRESSED_INDICES]>,
776}
777
778impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize, const MAX_PRESSED_INDICES: usize>
779 PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>
780{
781 pub fn new(
783 context: &Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
784 keymap_index: u16,
785 ) -> Self {
786 let mut pressed_indices = heapless::Vec::new();
787 let _ = pressed_indices.push(keymap_index);
788 let possible_chords = context.chords_for_keymap_index(keymap_index);
789
790 Self {
791 pressed_indices,
792 possible_chords,
793 marker: PhantomData,
794 }
795 }
796
797 fn satisfied_chord(&self) -> Option<&ChordState<MAX_CHORD_SIZE>> {
799 self.possible_chords
800 .iter()
801 .find(|&ChordState { is_satisfied, .. }| *is_satisfied)
802 }
803
804 fn check_resolution(&self) -> PendingChordState {
805 match self.possible_chords.as_slice() {
806 [ChordState {
807 index,
808 is_satisfied,
809 ..
810 }] if *is_satisfied => {
811 PendingChordState::Resolved(ChordResolution::Chord(*index as u8))
815 }
816 [] => {
817 PendingChordState::Resolved(ChordResolution::Passthrough)
820 }
821 satisfiable_chords => {
822 PendingChordState::Pending(
824 satisfiable_chords
825 .iter()
826 .find(|&ChordState { is_satisfied, .. }| *is_satisfied)
827 .map(|&ChordState { index, .. }| index as u8),
828 )
829 }
830 }
831 }
832
833 pub fn handle_event(
835 &mut self,
836 keymap_index: u16,
837 event: key::Event<Event>,
838 ) -> Option<ChordResolution> {
839 match event {
840 key::Event::Key {
841 keymap_index: _ev_idx,
842 key_event: Event::Timeout,
843 } => {
844 let maybe_satisfied_chord_id = self
846 .satisfied_chord()
847 .map(|chord_state| chord_state.index as u8);
848 match maybe_satisfied_chord_id {
849 Some(satisfied_chord_id) => Some(ChordResolution::Chord(satisfied_chord_id)),
850 _ => Some(ChordResolution::Passthrough),
851 }
852 }
853 key::Event::Input(input::Event::Press {
854 keymap_index: pressed_keymap_index,
855 }) => {
856 let maybe_satisfied_chord_id = self
859 .satisfied_chord()
860 .map(|chord_state| chord_state.index as u8);
861
862 let pos = self
864 .pressed_indices
865 .binary_search(&keymap_index)
866 .unwrap_or_else(|e| e);
867 let push_res = self.pressed_indices.insert(pos, pressed_keymap_index);
868 if push_res.is_err() {
873 panic!();
874 }
875
876 self.possible_chords
878 .retain(|chord_state| chord_state.chord.has_index(pressed_keymap_index));
879
880 for chord in self.possible_chords.iter_mut() {
882 chord.is_satisfied = chord.chord.is_satisfied_by(&self.pressed_indices);
883 }
884
885 let resolution = match self.check_resolution() {
886 PendingChordState::Resolved(resolution) => Some(resolution),
887 PendingChordState::Pending(_) => None,
888 };
889
890 match (resolution, maybe_satisfied_chord_id) {
893 (Some(ChordResolution::Passthrough), Some(satisfied_chord_id)) => {
894 Some(ChordResolution::Chord(satisfied_chord_id))
895 }
896 _ => resolution,
897 }
898 }
899 key::Event::Input(input::Event::Release {
900 keymap_index: released_keymap_index,
901 }) => {
902 if released_keymap_index == keymap_index {
903 let maybe_satisfied_chord_id = self
904 .satisfied_chord()
905 .map(|chord_state| chord_state.index as u8);
906
907 match maybe_satisfied_chord_id {
908 Some(satisfied_chord_id) => {
909 Some(ChordResolution::Chord(satisfied_chord_id))
910 }
911
912 None => Some(ChordResolution::Passthrough),
915 }
916 } else {
917 None
918 }
919 }
920 _ => None,
921 }
922 }
923}
924
925#[derive(Debug, Clone, Copy, PartialEq)]
927pub struct KeyState;
928
929#[derive(Debug, Clone, Copy, PartialEq)]
931pub struct System<
932 R: Copy + Debug + PartialEq,
933 Keys: Index<
934 usize,
935 Output = Key<
936 R,
937 MAX_CHORDS,
938 MAX_CHORD_SIZE,
939 MAX_OVERLAPPING_CHORD_SIZE,
940 MAX_PRESSED_INDICES,
941 >,
942 >,
943 AuxiliaryKeys: Index<usize, Output = AuxiliaryKey<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>>,
944 const MAX_CHORDS: usize,
945 const MAX_CHORD_SIZE: usize,
946 const MAX_OVERLAPPING_CHORD_SIZE: usize,
947 const MAX_PRESSED_INDICES: usize,
948> {
949 keys: Keys,
950 auxiliary_keys: AuxiliaryKeys,
951}
952
953impl<
954 R: Copy + Debug + PartialEq,
955 Keys: Index<
956 usize,
957 Output = Key<
958 R,
959 MAX_CHORDS,
960 MAX_CHORD_SIZE,
961 MAX_OVERLAPPING_CHORD_SIZE,
962 MAX_PRESSED_INDICES,
963 >,
964 >,
965 AuxiliaryKeys: Index<usize, Output = AuxiliaryKey<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>>,
966 const MAX_CHORDS: usize,
967 const MAX_CHORD_SIZE: usize,
968 const MAX_OVERLAPPING_CHORD_SIZE: usize,
969 const MAX_PRESSED_INDICES: usize,
970 >
971 System<
972 R,
973 Keys,
974 AuxiliaryKeys,
975 MAX_CHORDS,
976 MAX_CHORD_SIZE,
977 MAX_OVERLAPPING_CHORD_SIZE,
978 MAX_PRESSED_INDICES,
979 >
980{
981 pub const fn new(keys: Keys, auxiliary_keys: AuxiliaryKeys) -> Self {
983 Self {
984 keys,
985 auxiliary_keys,
986 }
987 }
988}
989
990impl<
991 R: Copy + Debug + PartialEq,
992 Keys: Debug
993 + Index<
994 usize,
995 Output = Key<
996 R,
997 MAX_CHORDS,
998 MAX_CHORD_SIZE,
999 MAX_OVERLAPPING_CHORD_SIZE,
1000 MAX_PRESSED_INDICES,
1001 >,
1002 >,
1003 AuxiliaryKeys: Debug
1004 + Index<usize, Output = AuxiliaryKey<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>>,
1005 const MAX_CHORDS: usize,
1006 const MAX_CHORD_SIZE: usize,
1007 const MAX_OVERLAPPING_CHORD_SIZE: usize,
1008 const MAX_PRESSED_INDICES: usize,
1009 > key::System<R>
1010 for System<
1011 R,
1012 Keys,
1013 AuxiliaryKeys,
1014 MAX_CHORDS,
1015 MAX_CHORD_SIZE,
1016 MAX_OVERLAPPING_CHORD_SIZE,
1017 MAX_PRESSED_INDICES,
1018 >
1019{
1020 type Ref = Ref;
1021 type Context = Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>;
1022 type Event = Event;
1023 type PendingKeyState = PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>;
1024 type KeyState = KeyState;
1025
1026 fn new_pressed_key(
1027 &self,
1028 keymap_index: u16,
1029 context: &Self::Context,
1030 key_ref: Ref,
1031 ) -> (
1032 key::PressedKeyResult<R, Self::PendingKeyState, Self::KeyState>,
1033 key::KeyEvents<Self::Event>,
1034 ) {
1035 match key_ref {
1036 Ref::Chorded(i) => self.keys[i as usize].new_pressed_key(context, keymap_index),
1037 Ref::Auxiliary(i) => {
1038 self.auxiliary_keys[i as usize].new_pressed_key(context, keymap_index)
1039 }
1040 }
1041 }
1042
1043 fn update_pending_state(
1044 &self,
1045 pending_state: &mut Self::PendingKeyState,
1046 keymap_index: u16,
1047 context: &Self::Context,
1048 key_ref: Ref,
1049 event: key::Event<Self::Event>,
1050 ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Self::Event>) {
1051 match key_ref {
1052 Ref::Chorded(i) => self.keys[i as usize].update_pending_state(
1053 pending_state,
1054 keymap_index,
1055 context,
1056 event,
1057 ),
1058 Ref::Auxiliary(i) => self.auxiliary_keys[i as usize].update_pending_state(
1059 pending_state,
1060 keymap_index,
1061 event,
1062 ),
1063 }
1064 }
1065
1066 fn update_state(
1067 &self,
1068 _key_state: &mut Self::KeyState,
1069 _key_ref: &Self::Ref,
1070 _context: &Self::Context,
1071 _keymap_index: u16,
1072 _event: key::Event<Self::Event>,
1073 ) -> key::KeyEvents<Self::Event> {
1074 panic!()
1075 }
1076
1077 fn key_output(
1078 &self,
1079 _key_ref: &Self::Ref,
1080 _key_state: &Self::KeyState,
1081 ) -> Option<key::KeyOutput> {
1082 panic!()
1083 }
1084}
1085
1086#[cfg(test)]
1087#[allow(clippy::unwrap_used, clippy::expect_used)]
1088mod tests {
1089 use super::*;
1090
1091 use key::keyboard;
1092
1093 const MAX_CHORDS: usize = 4;
1094 const MAX_CHORD_SIZE: usize = 16;
1095 const MAX_PRESSED_INDICES: usize = MAX_CHORD_SIZE * 2;
1096
1097 const DEFAULT_CONTEXT: Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES> =
1098 Context::from_config(Config::new());
1099
1100 type AuxiliaryKey =
1101 super::AuxiliaryKey<keyboard::Ref, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>;
1102 type PendingKeyState = super::PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>;
1103
1104 #[test]
1105 fn test_sizeof_ref() {
1106 assert_eq!(2, core::mem::size_of::<Ref>());
1107 }
1108
1109 #[test]
1110 fn test_sizeof_event() {
1111 assert_eq!(2, core::mem::size_of::<Event>());
1112 }
1113
1114 #[test]
1115 fn test_timeout_resolves_unsatisfied_aux_state_as_passthrough_key() {
1116 let context = DEFAULT_CONTEXT;
1118 let expected_ref = keyboard::Ref::KeyCode(0x04);
1119 let _chorded_key = AuxiliaryKey::new(expected_ref);
1120 let keymap_index: u16 = 0;
1121 let mut pks: PendingKeyState = PendingKeyState::new(&context, keymap_index);
1122
1123 let timeout_ev = key::Event::key_event(keymap_index, Event::Timeout);
1125 let actual_resolution = pks.handle_event(keymap_index, timeout_ev);
1126
1127 let expected_resolution = Some(ChordResolution::Passthrough);
1129 assert_eq!(expected_resolution, actual_resolution);
1130 }
1131
1132 #[test]
1133 fn test_press_non_chorded_key_resolves_aux_state_as_interrupted() {
1134 let context = DEFAULT_CONTEXT;
1136 let expected_ref = keyboard::Ref::KeyCode(0x04);
1137 let _chorded_key = AuxiliaryKey::new(expected_ref);
1138 let keymap_index: u16 = 0;
1139 let mut pks: PendingKeyState = PendingKeyState::new(&context, keymap_index);
1140
1141 let non_chord_press = input::Event::Press { keymap_index: 9 }.into();
1143 let actual_resolution = pks.handle_event(keymap_index, non_chord_press);
1144
1145 let expected_resolution = Some(ChordResolution::Passthrough);
1147 assert_eq!(expected_resolution, actual_resolution);
1148 }
1149
1150 #[test]
1157 fn test_press_chorded_key_resolves_unambiguous_aux_state_as_chord() {
1158 let mut context = Context::from_config(Config {
1160 chords: Slice::from_slice(&[ChordIndices::from_slice(&[0, 1])]),
1161 ..Config::new()
1162 });
1163 let passthrough = keyboard::Ref::KeyCode(0x04);
1164 let _chorded_key = AuxiliaryKey::new(passthrough);
1165 let keymap_index: u16 = 0;
1166 context.handle_event(key::Event::Input(input::Event::Press { keymap_index: 0 }));
1167 let mut pks: PendingKeyState = PendingKeyState::new(&context, keymap_index);
1168
1169 let chord_press = input::Event::Press { keymap_index: 1 }.into();
1171 let actual_resolution = pks.handle_event(keymap_index, chord_press);
1172
1173 let expected_resolution = Some(ChordResolution::Chord(0));
1175 assert_eq!(expected_resolution, actual_resolution);
1176 }
1177
1178 #[test]
1179 fn test_release_pending_aux_state_resolves_as_tapped_key() {
1180 let context = DEFAULT_CONTEXT;
1182 let expected_ref = keyboard::Ref::KeyCode(0x04);
1183 let _chorded_key = AuxiliaryKey::new(expected_ref);
1184 let keymap_index: u16 = 0;
1185 let mut pks: PendingKeyState = PendingKeyState::new(&context, keymap_index);
1186
1187 let chorded_key_release = input::Event::Release { keymap_index }.into();
1189 let actual_resolution = pks.handle_event(keymap_index, chorded_key_release);
1190
1191 let expected_resolution = Some(ChordResolution::Passthrough);
1193 assert_eq!(expected_resolution, actual_resolution);
1194 }
1195}