1#![doc = include_str!("doc_de_chorded.md")]
2
3use core::fmt::Debug;
4
5use serde::Deserialize;
6
7use crate::{input, key};
8
9pub use crate::init::MAX_CHORDS;
10
11const MAX_CHORD_SIZE: usize = 2;
13
14#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
16#[cfg_attr(feature = "std", serde(untagged))]
17pub enum ChordIndices {
18 Chord2(u16, u16),
20}
21
22impl ChordIndices {
23 pub fn has_index(&self, index: u16) -> bool {
25 match self {
26 ChordIndices::Chord2(i0, i1) => i0 == &index || i1 == &index,
27 }
28 }
29
30 pub fn is_satisfied_by(&self, indices: &[u16]) -> bool {
32 match self {
33 ChordIndices::Chord2(i0, i1) => indices.contains(i0) && indices.contains(i1),
34 }
35 }
36}
37
38#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
40pub struct Config {
41 #[serde(default = "default_timeout")]
45 pub timeout: u16,
46
47 #[serde(default = "default_chords")]
49 #[serde(deserialize_with = "deserialize_chords")]
50 pub chords: [Option<ChordIndices>; MAX_CHORDS],
51}
52
53fn default_timeout() -> u16 {
54 DEFAULT_CONFIG.timeout
55}
56
57fn default_chords() -> [Option<ChordIndices>; MAX_CHORDS] {
58 DEFAULT_CONFIG.chords
59}
60
61fn deserialize_chords<'de, D>(
63 deserializer: D,
64) -> Result<[Option<ChordIndices>; MAX_CHORDS], D::Error>
65where
66 D: serde::Deserializer<'de>,
67{
68 let mut v: heapless::Vec<Option<ChordIndices>, MAX_CHORDS> =
69 Deserialize::deserialize(deserializer)?;
70
71 while !v.is_full() {
72 v.push(None).unwrap();
73 }
74
75 v.into_array()
76 .map_err(|_| serde::de::Error::custom("unable to deserialize"))
77}
78
79pub const DEFAULT_CONFIG: Config = Config {
81 timeout: 200,
82 chords: [None; MAX_CHORDS],
83};
84
85impl Default for Config {
86 fn default() -> Self {
88 DEFAULT_CONFIG
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq)]
94pub struct Context {
95 config: Config,
96
97 pressed_indices: [Option<u16>; MAX_CHORD_SIZE * MAX_CHORDS],
98}
99
100pub const DEFAULT_CONTEXT: Context = Context::from_config(DEFAULT_CONFIG);
102
103impl Context {
104 pub const fn from_config(config: Config) -> Context {
106 let pressed_indices = [None; MAX_CHORD_SIZE * MAX_CHORDS];
107 Context {
108 config,
109 pressed_indices,
110 }
111 }
112
113 pub fn chords_for_indices(
117 &self,
118 indices: &[u16],
119 ) -> heapless::Vec<ChordIndices, { MAX_CHORDS }> {
120 self.config
121 .chords
122 .iter()
123 .filter_map(|&c| c)
124 .filter(|c| indices.iter().all(|&i| c.has_index(i)))
125 .collect()
126 }
127
128 fn sibling_indices(&self, index: u16) -> heapless::Vec<u16, { MAX_CHORD_SIZE * MAX_CHORDS }> {
135 let mut res: heapless::Vec<u16, { MAX_CHORD_SIZE * MAX_CHORDS }> = heapless::Vec::new();
136
137 let chords = self.chords_for_indices(&[index]);
138
139 chords.iter().for_each(|&ch| match ch {
140 ChordIndices::Chord2(i0, i1) => {
141 if let Err(pos) = res.binary_search(&i0) {
142 res.insert(pos, i0).unwrap();
143 }
144 if let Err(pos) = res.binary_search(&i1) {
145 res.insert(pos, i1).unwrap();
146 }
147 }
148 });
149
150 res
151 }
152
153 fn insert_pressed_index(&mut self, pos: usize, index: u16) {
154 if self.pressed_indices.is_empty() {
155 return;
156 }
157
158 let mut i = self.pressed_indices.len() - 1;
159 while i > pos {
160 self.pressed_indices[i] = self.pressed_indices[i - 1];
161 i -= 1;
162 }
163
164 self.pressed_indices[pos] = Some(index);
165 }
166
167 fn remove_pressed_index(&mut self, pos: usize) {
168 if self.pressed_indices.is_empty() {
169 return;
170 }
171
172 let mut i = pos;
173 while i < self.pressed_indices.len() - 1 {
174 self.pressed_indices[i] = self.pressed_indices[i + 1];
175 i += 1;
176 }
177
178 self.pressed_indices[self.pressed_indices.len() - 1] = None;
179 }
180
181 fn press_index(&mut self, index: u16) {
182 match self
183 .pressed_indices
184 .binary_search_by_key(&index, |&k| k.unwrap_or(u16::MAX))
185 {
186 Ok(_) => {}
187 Err(pos) => self.insert_pressed_index(pos, index),
188 }
189 }
190
191 fn release_index(&mut self, index: u16) {
192 if let Ok(pos) = self
193 .pressed_indices
194 .binary_search_by_key(&index, |&k| k.unwrap_or(u16::MAX))
195 {
196 self.remove_pressed_index(pos)
197 }
198 }
199
200 fn pressed_indices(&self) -> heapless::Vec<u16, { MAX_CHORD_SIZE * MAX_CHORDS }> {
201 self.pressed_indices.iter().filter_map(|&i| i).collect()
202 }
203
204 pub fn handle_event(&mut self, event: key::Event<Event>) {
206 match event {
207 key::Event::Input(input::Event::Press { keymap_index }) => {
208 self.press_index(keymap_index);
209 }
210 key::Event::Input(input::Event::Release { keymap_index }) => {
211 self.release_index(keymap_index);
212 }
213 key::Event::Key {
214 keymap_index,
215 key_event: Event::ChordResolved(ChordResolution::Passthrough),
216 } => self.release_index(keymap_index),
217 _ => {}
218 }
219 }
220}
221
222#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
227pub struct Key<K> {
228 pub chord: K,
230 pub passthrough: K,
232}
233
234impl<K: key::Key> Key<K>
235where
236 for<'c> &'c K::Context: Into<&'c Context>,
237 K::Event: TryInto<Event>,
238 K::Event: From<Event>,
239 K::PendingKeyState: From<PendingKeyState>,
240 K::KeyState: From<key::NoOpKeyState<K::Context, K::Event>>,
241{
242 pub fn new_pressed_key(
244 &self,
245 context: &K::Context,
246 key_path: key::KeyPath,
247 ) -> (
248 key::PressedKeyResult<K::PendingKeyState, K::KeyState>,
249 key::KeyEvents<K::Event>,
250 ) {
251 let keymap_index: u16 = key_path[0];
252 let pks = PendingKeyState::new(context.into(), keymap_index);
253
254 let chord_resolution = pks.check_resolution(context.into());
255
256 if let Some(resolution) = chord_resolution {
257 let (i, key) = match resolution {
258 ChordResolution::Chord => (1, &self.chord),
259 ChordResolution::Passthrough => (0, &self.passthrough),
260 };
261
262 let (pkr, pke) = key.new_pressed_key(context, key_path);
263 (pkr.add_path_item(i), pke)
265 } else {
266 let pk = key::PressedKeyResult::Pending(key_path, pks.into());
267
268 let timeout_ev = Event::Timeout;
269 let ctx: &Context = context.into();
270 let sch_ev = key::ScheduledEvent::after(
271 ctx.config.timeout,
272 key::Event::key_event(keymap_index, timeout_ev),
273 );
274 let pke = key::KeyEvents::scheduled_event(sch_ev.into_scheduled_event());
275
276 (pk, pke)
277 }
278 }
279}
280
281impl<K> Key<K> {
282 pub const fn new(chord: K, passthrough: K) -> Self {
284 Key { chord, passthrough }
285 }
286}
287
288impl<
289 K: key::Key<
290 Context = crate::init::Context,
291 Event = crate::init::Event,
292 PendingKeyState = crate::init::PendingKeyState,
293 KeyState = crate::init::KeyState,
294 >,
295 > key::Key for Key<K>
296{
297 type Context = crate::init::Context;
298 type Event = crate::init::Event;
299 type PendingKeyState = crate::init::PendingKeyState;
300 type KeyState = crate::init::KeyState;
301
302 fn new_pressed_key(
303 &self,
304 context: &Self::Context,
305 key_path: key::KeyPath,
306 ) -> (
307 key::PressedKeyResult<Self::PendingKeyState, Self::KeyState>,
308 key::KeyEvents<Self::Event>,
309 ) {
310 self.new_pressed_key(context, key_path)
311 }
312
313 fn handle_event(
314 &self,
315 pending_state: &mut Self::PendingKeyState,
316 context: &Self::Context,
317 key_path: key::KeyPath,
318 event: key::Event<Self::Event>,
319 ) -> (
320 Option<key::PressedKeyResult<Self::PendingKeyState, Self::KeyState>>,
321 key::KeyEvents<Self::Event>,
322 ) {
323 let keymap_index: u16 = key_path[0];
324 let ch_pks_res: Result<&mut PendingKeyState, _> = pending_state.try_into();
325 if let Ok(ch_pks) = ch_pks_res {
326 if let Ok(ch_ev) = event.try_into_key_event(|e| e.try_into()) {
327 let ch_state = ch_pks.handle_event(context.into(), keymap_index, ch_ev);
328 if let Some(ch_state) = ch_state {
329 let (i, nk) = match ch_state {
330 key::chorded::ChordResolution::Chord => (1, &self.chord),
331 key::chorded::ChordResolution::Passthrough => (0, &self.passthrough),
332 };
333 let (pkr, mut pke) = nk.new_pressed_key(context, key_path);
334 let pkr = pkr.add_path_item(i);
336
337 let ch_r_ev = key::chorded::Event::ChordResolved(ch_state);
338 let sch_ev = key::ScheduledEvent::immediate(key::Event::key_event(
339 keymap_index,
340 ch_r_ev.into(),
341 ));
342 pke.add_event(sch_ev);
343
344 (Some(pkr), pke)
345 } else {
346 (None, key::KeyEvents::no_events())
347 }
348 } else {
349 (None, key::KeyEvents::no_events())
350 }
351 } else {
352 (None, key::KeyEvents::no_events())
353 }
354 }
355
356 fn lookup(
357 &self,
358 path: &[u16],
359 ) -> &dyn key::Key<
360 Context = Self::Context,
361 Event = Self::Event,
362 PendingKeyState = Self::PendingKeyState,
363 KeyState = Self::KeyState,
364 > {
365 match path {
366 [] => self,
367 [0, path @ ..] => self.passthrough.lookup(path),
369 [1, path @ ..] => self.chord.lookup(path),
370 _ => panic!(),
371 }
372 }
373}
374
375#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
382pub struct AuxiliaryKey<K> {
383 pub passthrough: K,
385}
386
387impl<K: key::Key> AuxiliaryKey<K>
388where
389 for<'c> &'c K::Context: Into<&'c Context>,
390 K::Event: TryInto<Event>,
391 K::Event: From<Event>,
392 K::PendingKeyState: From<PendingKeyState>,
393 K::KeyState: From<key::NoOpKeyState<K::Context, K::Event>>,
394{
395 pub fn new_pressed_key(
397 &self,
398 context: &K::Context,
399 key_path: key::KeyPath,
400 ) -> (
401 key::PressedKeyResult<K::PendingKeyState, K::KeyState>,
402 key::KeyEvents<K::Event>,
403 ) {
404 let keymap_index: u16 = key_path[0];
405 let pks = PendingKeyState::new(context.into(), keymap_index);
406
407 let chord_resolution = pks.check_resolution(context.into());
408
409 if let Some(resolution) = chord_resolution {
410 match resolution {
411 ChordResolution::Chord => {
412 let pk = key::PressedKeyResult::Resolved(key::NoOpKeyState::new().into());
413 let pke = key::KeyEvents::no_events();
414
415 (pk, pke)
416 }
417 ChordResolution::Passthrough => self.passthrough.new_pressed_key(context, key_path),
419 }
420 } else {
421 let pk = key::PressedKeyResult::Pending(key_path, pks.into());
422
423 let timeout_ev = Event::Timeout;
424 let ctx: &Context = context.into();
425 let sch_ev = key::ScheduledEvent::after(
426 ctx.config.timeout,
427 key::Event::key_event(keymap_index, timeout_ev),
428 );
429 let pke = key::KeyEvents::scheduled_event(sch_ev.into_scheduled_event());
430
431 (pk, pke)
432 }
433 }
434}
435
436impl<K> AuxiliaryKey<K> {
437 pub const fn new(passthrough: K) -> Self {
439 AuxiliaryKey { passthrough }
440 }
441}
442
443impl<
444 K: key::Key<
445 Context = crate::init::Context,
446 Event = crate::init::Event,
447 PendingKeyState = crate::init::PendingKeyState,
448 KeyState = crate::init::KeyState,
449 >,
450 > key::Key for AuxiliaryKey<K>
451{
452 type Context = crate::init::Context;
453 type Event = crate::init::Event;
454 type PendingKeyState = crate::init::PendingKeyState;
455 type KeyState = crate::init::KeyState;
456
457 fn new_pressed_key(
458 &self,
459 context: &Self::Context,
460 key_path: key::KeyPath,
461 ) -> (
462 key::PressedKeyResult<Self::PendingKeyState, Self::KeyState>,
463 key::KeyEvents<Self::Event>,
464 ) {
465 self.new_pressed_key(context, key_path)
466 }
467
468 fn handle_event(
469 &self,
470 pending_state: &mut Self::PendingKeyState,
471 context: &Self::Context,
472 key_path: key::KeyPath,
473 event: key::Event<Self::Event>,
474 ) -> (
475 Option<key::PressedKeyResult<Self::PendingKeyState, Self::KeyState>>,
476 key::KeyEvents<Self::Event>,
477 ) {
478 let keymap_index = key_path[0];
479 let ch_pks_res: Result<&mut PendingKeyState, _> = pending_state.try_into();
480 if let Ok(ch_pks) = ch_pks_res {
481 if let Ok(ch_ev) = event.try_into_key_event(|e| e.try_into()) {
482 let ch_state = ch_pks.handle_event(context.into(), keymap_index, ch_ev);
483 if let Some(key::chorded::ChordResolution::Passthrough) = ch_state {
484 let nk = &self.passthrough;
485 let (pkr, mut pke) = nk.new_pressed_key(context, key_path);
486
487 let ch_r_ev = key::chorded::Event::ChordResolved(
490 key::chorded::ChordResolution::Passthrough,
491 );
492 let sch_ev = key::ScheduledEvent::immediate(key::Event::key_event(
493 keymap_index,
494 ch_r_ev.into(),
495 ));
496 pke.add_event(sch_ev);
497
498 (Some(pkr), pke)
499 } else if let Some(key::chorded::ChordResolution::Chord) = ch_state {
500 let ch_r_ev =
501 key::chorded::Event::ChordResolved(key::chorded::ChordResolution::Chord);
502 let pke =
503 key::KeyEvents::event(key::Event::key_event(keymap_index, ch_r_ev.into()));
504
505 (
506 Some(key::PressedKeyResult::Resolved(
507 key::NoOpKeyState::new().into(),
508 )),
509 pke,
510 )
511 } else {
512 (None, key::KeyEvents::no_events())
513 }
514 } else {
515 (None, key::KeyEvents::no_events())
516 }
517 } else {
518 (None, key::KeyEvents::no_events())
519 }
520 }
521
522 fn lookup(
523 &self,
524 path: &[u16],
525 ) -> &dyn key::Key<
526 Context = Self::Context,
527 Event = Self::Event,
528 PendingKeyState = Self::PendingKeyState,
529 KeyState = Self::KeyState,
530 > {
531 match path {
532 [] => self,
533 _ => self.passthrough.lookup(path),
534 }
535 }
536}
537
538#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
540pub enum Event {
541 ChordResolved(ChordResolution),
543
544 Timeout,
546}
547
548#[derive(Debug, Clone, Copy, PartialEq)]
557pub enum ChordSatisfaction {
558 Unsatisfied,
560 Satisfied,
562}
563
564#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
566pub enum ChordResolution {
567 Chord,
569 Passthrough,
571}
572
573#[derive(Debug, Clone, PartialEq)]
575pub struct PendingKeyState {
576 pressed_indices: heapless::Vec<u16, { MAX_CHORD_SIZE }>,
578 satisfaction: ChordSatisfaction,
580}
581
582impl PendingKeyState {
583 pub fn new(context: &Context, keymap_index: u16) -> Self {
585 let sibling_indices = context.sibling_indices(keymap_index);
586 let pressed_indices: heapless::Vec<u16, MAX_CHORD_SIZE> = context
587 .pressed_indices()
588 .iter()
589 .filter(|i| sibling_indices.contains(i))
590 .copied()
591 .collect();
592
593 Self {
594 pressed_indices,
595 satisfaction: ChordSatisfaction::Unsatisfied,
596 }
597 }
598
599 fn check_resolution(&self, context: &Context) -> Option<ChordResolution> {
600 let chords = context.chords_for_indices(self.pressed_indices.as_slice());
601 match chords.as_slice() {
602 [ch] if ch.is_satisfied_by(&self.pressed_indices) => {
603 Some(ChordResolution::Chord)
607 }
608 [] => {
609 Some(ChordResolution::Passthrough)
612 }
613 _ => {
614 None
616 }
617 }
618 }
619
620 pub fn handle_event(
622 &mut self,
623 context: &Context,
624 keymap_index: u16,
625 event: key::Event<Event>,
626 ) -> Option<ChordResolution> {
627 match event {
628 key::Event::Key {
629 keymap_index: _ev_idx,
630 key_event: Event::Timeout,
631 } => {
632 Some(ChordResolution::Passthrough)
635 }
636 key::Event::Input(input::Event::Press {
637 keymap_index: pressed_keymap_index,
638 }) => {
639 let pos = self
643 .pressed_indices
644 .binary_search(&keymap_index)
645 .unwrap_or_else(|e| e);
646
647 let push_res = self.pressed_indices.insert(pos, pressed_keymap_index);
648
649 if push_res.is_err() {
654 panic!();
655 }
656
657 self.check_resolution(context)
658 }
659 key::Event::Input(input::Event::Release {
660 keymap_index: released_keymap_index,
661 }) => {
662 if released_keymap_index == keymap_index {
663 Some(ChordResolution::Passthrough)
666 } else {
667 None
668 }
669 }
670 _ => None,
671 }
672 }
673}
674
675#[cfg(test)]
676mod tests {
677 use super::*;
678
679 use key::composite;
680 use key::keyboard;
681
682 use key::Context as _;
683
684 #[test]
685 fn test_timeout_resolves_unsatisfied_aux_state_as_passthrough_key() {
686 let context = key::composite::Context::default();
688 let expected_key = keyboard::Key::new(0x04);
689 let _chorded_key = AuxiliaryKey {
690 passthrough: expected_key,
691 };
692 let keymap_index: u16 = 0;
693 let mut pks: PendingKeyState = PendingKeyState::new((&context).into(), keymap_index);
694
695 let timeout_ev = key::Event::key_event(keymap_index, Event::Timeout).into_key_event();
697 let actual_res = pks.handle_event((&context).into(), keymap_index, timeout_ev);
698
699 let expected_res = Some(ChordResolution::Passthrough);
701 assert_eq!(expected_res, actual_res);
702 }
703
704 #[test]
708 fn test_press_non_chorded_key_resolves_aux_state_as_interrupted() {
709 let context = key::composite::Context::default();
711 let expected_key = keyboard::Key::new(0x04);
712 let _chorded_key = AuxiliaryKey {
713 passthrough: expected_key,
714 };
715 let keymap_index: u16 = 0;
716 let mut pks: PendingKeyState = PendingKeyState::new((&context).into(), keymap_index);
717
718 let non_chord_press = input::Event::Press { keymap_index: 9 }.into();
720 let actual_res = pks.handle_event((&context).into(), keymap_index, non_chord_press);
721
722 let expected_res = Some(ChordResolution::Passthrough);
724 assert_eq!(expected_res, actual_res);
725 }
726
727 #[test]
734 fn test_press_chorded_key_resolves_unambiguous_aux_state_as_chord() {
735 let mut context = key::composite::Context::from_config(composite::Config {
737 chorded: Config {
738 chords: [Some(ChordIndices::Chord2(0, 1)), None, None, None],
739 ..DEFAULT_CONFIG
740 },
741 ..composite::DEFAULT_CONFIG
742 });
743
744 let passthrough = keyboard::Key::new(0x04);
745 let _chorded_key = AuxiliaryKey { passthrough };
746 let keymap_index: u16 = 0;
747 context.handle_event(key::Event::Input(input::Event::Press { keymap_index: 0 }));
748 let mut pks: PendingKeyState = PendingKeyState::new((&context).into(), keymap_index);
749
750 let chord_press = input::Event::Press { keymap_index: 1 }.into();
752 let actual_res = pks.handle_event((&context).into(), keymap_index, chord_press);
753
754 let expected_res = Some(ChordResolution::Chord);
756 assert_eq!(expected_res, actual_res);
757 }
758
759 #[test]
767 fn test_release_pending_aux_state_resolves_as_tapped_key() {
768 let context = key::composite::Context::default();
770 let expected_key = keyboard::Key::new(0x04);
771 let _chorded_key = AuxiliaryKey {
772 passthrough: expected_key,
773 };
774 let keymap_index: u16 = 0;
775 let mut pks: PendingKeyState = PendingKeyState::new((&context).into(), keymap_index);
776
777 let chorded_key_release = input::Event::Release { keymap_index }.into();
779 let actual_res = pks.handle_event((&context).into(), keymap_index, chorded_key_release);
780
781 let expected_res = Some(ChordResolution::Passthrough);
783 assert_eq!(expected_res, actual_res);
784 }
785}