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 res.insert(pos, i).unwrap();
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) => heapless::Vec::from_slice(&[chord_state]).unwrap(),
306 None => {
307 let chords_indices_span = self.pressed_chords_indices_span();
308 self.config
309 .chords
310 .iter()
311 .enumerate()
312 .filter(|&(_index, chord)| chord.has_index(keymap_index))
314 .filter(|&(_index, chord)| {
315 chords_indices_span.is_empty()
317 || chord.indices.iter().all(|&i| {
318 chords_indices_span.binary_search(&i).is_err()
320 })
321 })
322 .map(|(index, &chord)| ChordState {
323 index,
324 chord,
325 is_satisfied: false,
326 })
327 .collect()
328 }
329 }
330 }
331
332 fn insert_pressed_index(&mut self, pos: usize, index: u16) {
333 if self.pressed_indices.is_empty() {
334 return;
335 }
336
337 let mut i = self.pressed_indices.len() - 1;
338 while i > pos {
339 self.pressed_indices[i] = self.pressed_indices[i - 1];
340 i -= 1;
341 }
342
343 self.pressed_indices[pos] = Some(index);
344 }
345
346 fn remove_pressed_index(&mut self, pos: usize) {
347 if self.pressed_indices.is_empty() {
348 return;
349 }
350
351 let mut i = pos;
352 while i < self.pressed_indices.len() - 1 {
353 self.pressed_indices[i] = self.pressed_indices[i + 1];
354 i += 1;
355 }
356
357 self.pressed_indices[self.pressed_indices.len() - 1] = None;
358 }
359
360 fn press_index(&mut self, index: u16) {
361 match self
362 .pressed_indices
363 .binary_search_by_key(&index, |&k| k.unwrap_or(u16::MAX))
364 {
365 Ok(_) => {}
366 Err(pos) => self.insert_pressed_index(pos, index),
367 }
368 }
369
370 fn release_index(&mut self, index: u16) {
371 if let Ok(pos) = self
372 .pressed_indices
373 .binary_search_by_key(&index, |&k| k.unwrap_or(u16::MAX))
374 {
375 self.remove_pressed_index(pos)
376 }
377 }
378
379 fn handle_event(&mut self, event: key::Event<Event>) {
381 match event {
382 key::Event::Input(input::Event::Press { keymap_index }) => {
383 self.press_index(keymap_index);
384
385 let span = self.pressed_chords_indices_span();
389 if span.contains(&keymap_index) {
390 self.ignore_idle_time = true;
392 } else {
393 if let Some(chord_id) = self.latest_resolved_chord {
395 let chord_indices = self.config.chords[chord_id as usize];
396 self.ignore_idle_time = chord_indices.has_index(keymap_index);
397 } else {
398 self.ignore_idle_time = false;
399
400 self.latest_resolved_chord = None;
403 }
404 }
405 }
406 key::Event::Input(input::Event::Release { keymap_index }) => {
407 self.release_index(keymap_index);
408
409 self.config
412 .chords
413 .iter()
414 .enumerate()
415 .for_each(|(chord_id, chord_indices)| {
416 if chord_indices.has_index(keymap_index) {
417 self.pressed_chords[chord_id] = false;
418 }
419 });
420 }
421 key::Event::Key {
422 keymap_index: _,
423 key_event: Event::ChordResolved(ChordResolution::Chord(chord_id)),
424 } => {
425 self.pressed_chords[chord_id as usize] = true;
426 self.latest_resolved_chord = Some(chord_id);
427 }
428 _ => {}
429 }
430 }
431}
432
433impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize, const MAX_PRESSED_INDICES: usize>
434 key::Context for Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>
435{
436 type Event = Event;
437
438 fn handle_event(&mut self, event: key::Event<Self::Event>) -> key::KeyEvents<Self::Event> {
439 self.handle_event(event);
440 key::KeyEvents::no_events()
441 }
442}
443
444#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
449pub struct Key<
450 R: Copy,
451 const MAX_CHORDS: usize,
452 const MAX_CHORD_SIZE: usize,
453 const MAX_OVERLAPPING_CHORD_SIZE: usize,
454 const MAX_PRESSED_INDICES: usize,
455> {
456 pub chords: Slice<(ChordId, R), MAX_OVERLAPPING_CHORD_SIZE>,
458 pub passthrough: R,
460 #[serde(default)]
461 marker: PhantomData<(
462 [(); MAX_CHORDS],
463 [(); MAX_CHORD_SIZE],
464 [(); MAX_PRESSED_INDICES],
465 )>,
466}
467
468impl<
469 R: Copy,
470 const MAX_CHORDS: usize,
471 const MAX_CHORD_SIZE: usize,
472 const MAX_OVERLAPPING_CHORD_SIZE: usize,
473 const MAX_PRESSED_INDICES: usize,
474 > Key<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_OVERLAPPING_CHORD_SIZE, MAX_PRESSED_INDICES>
475{
476 pub fn new_pressed_key(
478 &self,
479 context: &Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
480 keymap_index: u16,
481 ) -> (
482 key::PressedKeyResult<
483 R,
484 PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
485 KeyState,
486 >,
487 key::KeyEvents<Event>,
488 ) {
489 let pks = PendingKeyState::new(context, keymap_index);
490
491 let chord_resolution = if context.sufficient_idle_time() {
492 pks.check_resolution()
493 } else {
494 PendingChordState::Resolved(ChordResolution::Passthrough)
495 };
496
497 if let PendingChordState::Resolved(resolution) = chord_resolution {
498 let maybe_new_key_ref = match resolution {
499 ChordResolution::Chord(resolved_chord_id) => {
500 if let Some(resolved_chord_indices) =
503 context.config.chords.get(resolved_chord_id as usize)
504 {
505 if resolved_chord_indices.as_slice()[0] == keymap_index {
506 if let Some((_, new_key_ref)) = self
507 .chords
508 .iter()
509 .find(|(ch_id, _)| *ch_id == resolved_chord_id)
510 {
511 Some(*new_key_ref)
512 } else {
513 panic!("check_resolution has invalid chord id")
514 }
515 } else {
516 None
517 }
518 } else {
519 panic!("check_resolution has invalid chord id")
520 }
521 }
522 ChordResolution::Passthrough => Some(self.passthrough),
523 };
524
525 if let Some(new_key_ref) = maybe_new_key_ref {
526 let pkr =
527 key::PressedKeyResult::NewPressedKey(key::NewPressedKey::key(new_key_ref));
528 let pke = key::KeyEvents::no_events();
529
530 (pkr, pke)
531 } else {
532 let pkr = key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp);
533 let pke = key::KeyEvents::no_events();
534 (pkr, pke)
535 }
536 } else {
537 let pkr = key::PressedKeyResult::Pending(pks);
538
539 let timeout_ev = Event::Timeout;
540 let sch_ev = key::ScheduledEvent::after(
541 context.config.timeout,
542 key::Event::key_event(keymap_index, timeout_ev),
543 );
544 let pke = key::KeyEvents::scheduled_event(sch_ev);
545
546 (pkr, pke)
547 }
548 }
549
550 pub const fn new(chords: &[(ChordId, R)], passthrough: R) -> Self {
552 let chords = Slice::from_slice(chords);
553 Key {
554 chords,
555 passthrough,
556 marker: PhantomData,
557 }
558 }
559
560 fn update_pending_state(
561 &self,
562 pending_state: &mut PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
563 keymap_index: u16,
564 context: &Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
565 event: key::Event<Event>,
566 ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Event>) {
567 let ch_state = pending_state.handle_event(keymap_index, event);
568
569 if let Some(ch_state) = ch_state {
571 let maybe_new_key_ref = match ch_state {
572 ChordResolution::Chord(resolved_chord_id) => {
573 if let Some(resolved_chord_indices) =
576 context.config.chords.get(resolved_chord_id as usize)
577 {
578 if resolved_chord_indices.as_slice()[0] == keymap_index {
579 if let Some((_, key_ref)) = self
580 .chords
581 .iter()
582 .find(|(ch_id, _)| *ch_id == resolved_chord_id)
583 {
584 Some(*key_ref)
585 } else {
586 panic!("event's chord resolution has invalid chord id")
587 }
588 } else {
589 None
590 }
591 } else {
592 panic!("event's chord resolution has invalid chord id")
593 }
594 }
595 ChordResolution::Passthrough => Some(self.passthrough),
596 };
597
598 let ch_r_ev = Event::ChordResolved(ch_state);
599 let sch_ev =
600 key::ScheduledEvent::immediate(key::Event::key_event(keymap_index, ch_r_ev));
601
602 if let Some(new_key_ref) = maybe_new_key_ref {
603 let pke = key::KeyEvents::scheduled_event(sch_ev);
604
605 (Some(key::NewPressedKey::key(new_key_ref)), pke)
606 } else {
607 let pke = key::KeyEvents::scheduled_event(sch_ev);
608 (Some(key::NewPressedKey::no_op()), pke)
609 }
610 } else {
611 (None, key::KeyEvents::no_events())
612 }
613 }
614}
615
616#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
623pub struct AuxiliaryKey<
624 R,
625 const MAX_CHORDS: usize,
626 const MAX_CHORD_SIZE: usize,
627 const MAX_PRESSED_INDICES: usize,
628> {
629 pub passthrough: R,
631 #[serde(default)]
632 marker: PhantomData<(
633 [(); MAX_CHORDS],
634 [(); MAX_CHORD_SIZE],
635 [(); MAX_PRESSED_INDICES],
636 )>,
637}
638
639impl<
640 R: Copy,
641 const MAX_CHORDS: usize,
642 const MAX_CHORD_SIZE: usize,
643 const MAX_PRESSED_INDICES: usize,
644 > AuxiliaryKey<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>
645{
646 pub fn new_pressed_key(
648 &self,
649 context: &Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
650 keymap_index: u16,
651 ) -> (
652 key::PressedKeyResult<
653 R,
654 PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
655 KeyState,
656 >,
657 key::KeyEvents<Event>,
658 ) {
659 let pks = PendingKeyState::new(context, keymap_index);
660
661 let chord_resolution = if context.sufficient_idle_time() {
662 pks.check_resolution()
663 } else {
664 PendingChordState::Resolved(ChordResolution::Passthrough)
665 };
666
667 if let PendingChordState::Resolved(resolution) = chord_resolution {
668 match resolution {
669 ChordResolution::Chord(_resolved_chord_id) => {
670 let pkr = key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp);
671 let pke = key::KeyEvents::no_events();
672
673 (pkr, pke)
674 }
675 ChordResolution::Passthrough => {
676 let new_key_ref = self.passthrough;
677 let pkr =
678 key::PressedKeyResult::NewPressedKey(key::NewPressedKey::key(new_key_ref));
679 let pke = key::KeyEvents::no_events();
680 (pkr, pke)
681 }
682 }
683 } else {
684 let pkr = key::PressedKeyResult::Pending(pks);
685
686 let timeout_ev = Event::Timeout;
687 let sch_ev = key::ScheduledEvent::after(
688 context.config.timeout,
689 key::Event::key_event(keymap_index, timeout_ev),
690 );
691 let pke = key::KeyEvents::scheduled_event(sch_ev);
692
693 (pkr, pke)
694 }
695 }
696
697 pub const fn new(passthrough: R) -> Self {
699 AuxiliaryKey {
700 passthrough,
701 marker: PhantomData,
702 }
703 }
704
705 fn update_pending_state(
706 &self,
707 pending_state: &mut PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
708 keymap_index: u16,
709 event: key::Event<Event>,
710 ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Event>) {
711 let ch_state = pending_state.handle_event(keymap_index, event);
712 if let Some(ChordResolution::Passthrough) = ch_state {
713 let ch_r_ev = Event::ChordResolved(ChordResolution::Passthrough);
714 let sch_ev =
715 key::ScheduledEvent::immediate(key::Event::key_event(keymap_index, ch_r_ev));
716 let pke = key::KeyEvents::scheduled_event(sch_ev);
717
718 (Some(key::NewPressedKey::key(self.passthrough)), pke)
719 } else if let Some(ChordResolution::Chord(resolved_chord_id)) = ch_state {
720 let ch_r_ev = Event::ChordResolved(ChordResolution::Chord(resolved_chord_id));
721 let pke = key::KeyEvents::event(key::Event::key_event(keymap_index, ch_r_ev));
722
723 (Some(key::NewPressedKey::no_op()), pke)
724 } else {
725 (None, key::KeyEvents::no_events())
726 }
727 }
728}
729
730#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
732pub enum Event {
733 ChordResolved(ChordResolution),
735
736 Timeout,
738}
739
740#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
742pub enum ChordResolution {
743 Chord(ChordId),
745 Passthrough,
747}
748
749#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
751pub enum PendingChordState {
752 Resolved(ChordResolution),
754 Pending(Option<ChordId>),
758}
759
760#[derive(Debug, Clone, PartialEq)]
762pub struct PendingKeyState<
763 const MAX_CHORDS: usize,
764 const MAX_CHORD_SIZE: usize,
765 const MAX_PRESSED_INDICES: usize,
766> {
767 pressed_indices: heapless::Vec<u16, { MAX_CHORD_SIZE }>,
769 possible_chords: heapless::Vec<ChordState<MAX_CHORD_SIZE>, { MAX_CHORDS }>,
771 marker: PhantomData<[(); MAX_PRESSED_INDICES]>,
772}
773
774impl<const MAX_CHORDS: usize, const MAX_CHORD_SIZE: usize, const MAX_PRESSED_INDICES: usize>
775 PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>
776{
777 pub fn new(
779 context: &Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>,
780 keymap_index: u16,
781 ) -> Self {
782 let pressed_indices = heapless::Vec::from_slice(&[keymap_index]).unwrap();
783 let possible_chords = context.chords_for_keymap_index(keymap_index);
784
785 Self {
786 pressed_indices,
787 possible_chords,
788 marker: PhantomData,
789 }
790 }
791
792 fn satisfied_chord(&self) -> Option<&ChordState<MAX_CHORD_SIZE>> {
794 self.possible_chords
795 .iter()
796 .find(|&ChordState { is_satisfied, .. }| *is_satisfied)
797 }
798
799 fn check_resolution(&self) -> PendingChordState {
800 match self.possible_chords.as_slice() {
801 [ChordState {
802 index,
803 is_satisfied,
804 ..
805 }] if *is_satisfied => {
806 PendingChordState::Resolved(ChordResolution::Chord(*index as u8))
810 }
811 [] => {
812 PendingChordState::Resolved(ChordResolution::Passthrough)
815 }
816 satisfiable_chords => {
817 PendingChordState::Pending(
819 satisfiable_chords
820 .iter()
821 .find(|&ChordState { is_satisfied, .. }| *is_satisfied)
822 .map(|&ChordState { index, .. }| index as u8),
823 )
824 }
825 }
826 }
827
828 pub fn handle_event(
830 &mut self,
831 keymap_index: u16,
832 event: key::Event<Event>,
833 ) -> Option<ChordResolution> {
834 match event {
835 key::Event::Key {
836 keymap_index: _ev_idx,
837 key_event: Event::Timeout,
838 } => {
839 let maybe_satisfied_chord_id = self
841 .satisfied_chord()
842 .map(|chord_state| chord_state.index as u8);
843 match maybe_satisfied_chord_id {
844 Some(satisfied_chord_id) => Some(ChordResolution::Chord(satisfied_chord_id)),
845 _ => Some(ChordResolution::Passthrough),
846 }
847 }
848 key::Event::Input(input::Event::Press {
849 keymap_index: pressed_keymap_index,
850 }) => {
851 let maybe_satisfied_chord_id = self
854 .satisfied_chord()
855 .map(|chord_state| chord_state.index as u8);
856
857 let pos = self
859 .pressed_indices
860 .binary_search(&keymap_index)
861 .unwrap_or_else(|e| e);
862 let push_res = self.pressed_indices.insert(pos, pressed_keymap_index);
863 if push_res.is_err() {
868 panic!();
869 }
870
871 self.possible_chords
873 .retain(|chord_state| chord_state.chord.has_index(pressed_keymap_index));
874
875 for chord in self.possible_chords.iter_mut() {
877 chord.is_satisfied = chord.chord.is_satisfied_by(&self.pressed_indices);
878 }
879
880 let resolution = match self.check_resolution() {
881 PendingChordState::Resolved(resolution) => Some(resolution),
882 PendingChordState::Pending(_) => None,
883 };
884
885 match (resolution, maybe_satisfied_chord_id) {
888 (Some(ChordResolution::Passthrough), Some(satisfied_chord_id)) => {
889 Some(ChordResolution::Chord(satisfied_chord_id))
890 }
891 _ => resolution,
892 }
893 }
894 key::Event::Input(input::Event::Release {
895 keymap_index: released_keymap_index,
896 }) => {
897 if released_keymap_index == keymap_index {
898 let maybe_satisfied_chord_id = self
899 .satisfied_chord()
900 .map(|chord_state| chord_state.index as u8);
901
902 match maybe_satisfied_chord_id {
903 Some(satisfied_chord_id) => {
904 Some(ChordResolution::Chord(satisfied_chord_id))
905 }
906
907 None => Some(ChordResolution::Passthrough),
910 }
911 } else {
912 None
913 }
914 }
915 _ => None,
916 }
917 }
918}
919
920#[derive(Debug, Clone, Copy, PartialEq)]
922pub struct KeyState;
923
924#[derive(Debug, Clone, Copy, PartialEq)]
926pub struct System<
927 R: Copy + Debug + PartialEq,
928 Keys: Index<
929 usize,
930 Output = Key<
931 R,
932 MAX_CHORDS,
933 MAX_CHORD_SIZE,
934 MAX_OVERLAPPING_CHORD_SIZE,
935 MAX_PRESSED_INDICES,
936 >,
937 >,
938 AuxiliaryKeys: Index<usize, Output = AuxiliaryKey<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>>,
939 const MAX_CHORDS: usize,
940 const MAX_CHORD_SIZE: usize,
941 const MAX_OVERLAPPING_CHORD_SIZE: usize,
942 const MAX_PRESSED_INDICES: usize,
943> {
944 keys: Keys,
945 auxiliary_keys: AuxiliaryKeys,
946}
947
948impl<
949 R: Copy + Debug + PartialEq,
950 Keys: Index<
951 usize,
952 Output = Key<
953 R,
954 MAX_CHORDS,
955 MAX_CHORD_SIZE,
956 MAX_OVERLAPPING_CHORD_SIZE,
957 MAX_PRESSED_INDICES,
958 >,
959 >,
960 AuxiliaryKeys: Index<usize, Output = AuxiliaryKey<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>>,
961 const MAX_CHORDS: usize,
962 const MAX_CHORD_SIZE: usize,
963 const MAX_OVERLAPPING_CHORD_SIZE: usize,
964 const MAX_PRESSED_INDICES: usize,
965 >
966 System<
967 R,
968 Keys,
969 AuxiliaryKeys,
970 MAX_CHORDS,
971 MAX_CHORD_SIZE,
972 MAX_OVERLAPPING_CHORD_SIZE,
973 MAX_PRESSED_INDICES,
974 >
975{
976 pub const fn new(keys: Keys, auxiliary_keys: AuxiliaryKeys) -> Self {
978 Self {
979 keys,
980 auxiliary_keys,
981 }
982 }
983}
984
985impl<
986 R: Copy + Debug + PartialEq,
987 Keys: Debug
988 + Index<
989 usize,
990 Output = Key<
991 R,
992 MAX_CHORDS,
993 MAX_CHORD_SIZE,
994 MAX_OVERLAPPING_CHORD_SIZE,
995 MAX_PRESSED_INDICES,
996 >,
997 >,
998 AuxiliaryKeys: Debug
999 + Index<usize, Output = AuxiliaryKey<R, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>>,
1000 const MAX_CHORDS: usize,
1001 const MAX_CHORD_SIZE: usize,
1002 const MAX_OVERLAPPING_CHORD_SIZE: usize,
1003 const MAX_PRESSED_INDICES: usize,
1004 > key::System<R>
1005 for System<
1006 R,
1007 Keys,
1008 AuxiliaryKeys,
1009 MAX_CHORDS,
1010 MAX_CHORD_SIZE,
1011 MAX_OVERLAPPING_CHORD_SIZE,
1012 MAX_PRESSED_INDICES,
1013 >
1014{
1015 type Ref = Ref;
1016 type Context = Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>;
1017 type Event = Event;
1018 type PendingKeyState = PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>;
1019 type KeyState = KeyState;
1020
1021 fn new_pressed_key(
1022 &self,
1023 keymap_index: u16,
1024 context: &Self::Context,
1025 key_ref: Ref,
1026 ) -> (
1027 key::PressedKeyResult<R, Self::PendingKeyState, Self::KeyState>,
1028 key::KeyEvents<Self::Event>,
1029 ) {
1030 match key_ref {
1031 Ref::Chorded(i) => self.keys[i as usize].new_pressed_key(context, keymap_index),
1032 Ref::Auxiliary(i) => {
1033 self.auxiliary_keys[i as usize].new_pressed_key(context, keymap_index)
1034 }
1035 }
1036 }
1037
1038 fn update_pending_state(
1039 &self,
1040 pending_state: &mut Self::PendingKeyState,
1041 keymap_index: u16,
1042 context: &Self::Context,
1043 key_ref: Ref,
1044 event: key::Event<Self::Event>,
1045 ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Self::Event>) {
1046 match key_ref {
1047 Ref::Chorded(i) => self.keys[i as usize].update_pending_state(
1048 pending_state,
1049 keymap_index,
1050 context,
1051 event,
1052 ),
1053 Ref::Auxiliary(i) => self.auxiliary_keys[i as usize].update_pending_state(
1054 pending_state,
1055 keymap_index,
1056 event,
1057 ),
1058 }
1059 }
1060
1061 fn update_state(
1062 &self,
1063 _key_state: &mut Self::KeyState,
1064 _key_ref: &Self::Ref,
1065 _context: &Self::Context,
1066 _keymap_index: u16,
1067 _event: key::Event<Self::Event>,
1068 ) -> key::KeyEvents<Self::Event> {
1069 panic!()
1070 }
1071
1072 fn key_output(
1073 &self,
1074 _key_ref: &Self::Ref,
1075 _key_state: &Self::KeyState,
1076 ) -> Option<key::KeyOutput> {
1077 panic!()
1078 }
1079}
1080
1081#[cfg(test)]
1082mod tests {
1083 use super::*;
1084
1085 use key::keyboard;
1086
1087 const MAX_CHORDS: usize = 4;
1088 const MAX_CHORD_SIZE: usize = 16;
1089 const MAX_PRESSED_INDICES: usize = MAX_CHORD_SIZE * 2;
1090
1091 const DEFAULT_CONTEXT: Context<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES> =
1092 Context::from_config(Config::new());
1093
1094 type AuxiliaryKey =
1095 super::AuxiliaryKey<keyboard::Ref, MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>;
1096 type PendingKeyState = super::PendingKeyState<MAX_CHORDS, MAX_CHORD_SIZE, MAX_PRESSED_INDICES>;
1097
1098 #[test]
1099 fn test_sizeof_ref() {
1100 assert_eq!(2, core::mem::size_of::<Ref>());
1101 }
1102
1103 #[test]
1104 fn test_sizeof_event() {
1105 assert_eq!(2, core::mem::size_of::<Event>());
1106 }
1107
1108 #[test]
1109 fn test_timeout_resolves_unsatisfied_aux_state_as_passthrough_key() {
1110 let context = DEFAULT_CONTEXT;
1112 let expected_ref = keyboard::Ref::KeyCode(0x04);
1113 let _chorded_key = AuxiliaryKey::new(expected_ref);
1114 let keymap_index: u16 = 0;
1115 let mut pks: PendingKeyState = PendingKeyState::new(&context, keymap_index);
1116
1117 let timeout_ev = key::Event::key_event(keymap_index, Event::Timeout);
1119 let actual_resolution = pks.handle_event(keymap_index, timeout_ev);
1120
1121 let expected_resolution = Some(ChordResolution::Passthrough);
1123 assert_eq!(expected_resolution, actual_resolution);
1124 }
1125
1126 #[test]
1127 fn test_press_non_chorded_key_resolves_aux_state_as_interrupted() {
1128 let context = DEFAULT_CONTEXT;
1130 let expected_ref = keyboard::Ref::KeyCode(0x04);
1131 let _chorded_key = AuxiliaryKey::new(expected_ref);
1132 let keymap_index: u16 = 0;
1133 let mut pks: PendingKeyState = PendingKeyState::new(&context, keymap_index);
1134
1135 let non_chord_press = input::Event::Press { keymap_index: 9 }.into();
1137 let actual_resolution = pks.handle_event(keymap_index, non_chord_press);
1138
1139 let expected_resolution = Some(ChordResolution::Passthrough);
1141 assert_eq!(expected_resolution, actual_resolution);
1142 }
1143
1144 #[test]
1151 fn test_press_chorded_key_resolves_unambiguous_aux_state_as_chord() {
1152 let mut context = Context::from_config(Config {
1154 chords: Slice::from_slice(&[ChordIndices::from_slice(&[0, 1])]),
1155 ..Config::new()
1156 });
1157 let passthrough = keyboard::Ref::KeyCode(0x04);
1158 let _chorded_key = AuxiliaryKey::new(passthrough);
1159 let keymap_index: u16 = 0;
1160 context.handle_event(key::Event::Input(input::Event::Press { keymap_index: 0 }));
1161 let mut pks: PendingKeyState = PendingKeyState::new(&context, keymap_index);
1162
1163 let chord_press = input::Event::Press { keymap_index: 1 }.into();
1165 let actual_resolution = pks.handle_event(keymap_index, chord_press);
1166
1167 let expected_resolution = Some(ChordResolution::Chord(0));
1169 assert_eq!(expected_resolution, actual_resolution);
1170 }
1171
1172 #[test]
1173 fn test_release_pending_aux_state_resolves_as_tapped_key() {
1174 let context = DEFAULT_CONTEXT;
1176 let expected_ref = keyboard::Ref::KeyCode(0x04);
1177 let _chorded_key = AuxiliaryKey::new(expected_ref);
1178 let keymap_index: u16 = 0;
1179 let mut pks: PendingKeyState = PendingKeyState::new(&context, keymap_index);
1180
1181 let chorded_key_release = input::Event::Release { keymap_index }.into();
1183 let actual_resolution = pks.handle_event(keymap_index, chorded_key_release);
1184
1185 let expected_resolution = Some(ChordResolution::Passthrough);
1187 assert_eq!(expected_resolution, actual_resolution);
1188 }
1189}