1use core::fmt::Debug;
19use core::ops::Index;
20
21use serde::Deserialize;
22
23use crate::{input, key, keymap, slice::Slice};
24
25#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
27pub enum Ref {
28 Sequence(u8),
33 Auxiliary(u8),
35 SequenceStart,
37}
38
39pub type SequenceId = u8;
41
42#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
44#[serde(from = "heapless::Vec<u16, MAX_SEQUENCE_LEN>")]
45pub struct SequenceIndices<const MAX_SEQUENCE_LEN: usize> {
46 indices: Slice<u16, MAX_SEQUENCE_LEN>,
47}
48
49impl<const MAX_SEQUENCE_LEN: usize> SequenceIndices<MAX_SEQUENCE_LEN> {
50 pub const fn from_slice(indices: &[u16]) -> Self {
52 Self {
53 indices: Slice::from_slice(indices),
54 }
55 }
56
57 pub const fn as_slice(&self) -> &[u16] {
59 self.indices.as_slice()
60 }
61}
62
63impl<const MAX_SEQUENCE_LEN: usize> From<heapless::Vec<u16, MAX_SEQUENCE_LEN>>
64 for SequenceIndices<MAX_SEQUENCE_LEN>
65{
66 fn from(v: heapless::Vec<u16, MAX_SEQUENCE_LEN>) -> Self {
67 Self::from_slice(&v)
68 }
69}
70
71#[derive(Deserialize, Clone, Copy, PartialEq)]
73pub struct Config<const MAX_SEQUENCES: usize, const MAX_SEQUENCE_LEN: usize> {
74 #[serde(default = "default_timeout")]
76 pub timeout: u16,
77
78 pub sequences: Slice<SequenceIndices<MAX_SEQUENCE_LEN>, MAX_SEQUENCES>,
80
81 pub required_idle_time: Option<u16>,
83}
84
85impl<const MAX_SEQUENCES: usize, const MAX_SEQUENCE_LEN: usize> core::fmt::Debug
86 for Config<MAX_SEQUENCES, MAX_SEQUENCE_LEN>
87{
88 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
89 f.debug_struct("Config")
90 .field("timeout", &self.timeout)
91 .field("sequences", &self.sequences.as_slice())
92 .field("required_idle_time", &self.required_idle_time)
93 .finish()
94 }
95}
96
97pub const DEFAULT_TIMEOUT: u16 = 1000;
99
100const fn default_timeout() -> u16 {
101 DEFAULT_TIMEOUT
102}
103
104impl<const MAX_SEQUENCES: usize, const MAX_SEQUENCE_LEN: usize>
105 Config<MAX_SEQUENCES, MAX_SEQUENCE_LEN>
106{
107 pub const fn new() -> Self {
109 Self {
110 timeout: DEFAULT_TIMEOUT,
111 sequences: Slice::from_slice(&[]),
112 required_idle_time: None,
113 }
114 }
115}
116
117impl<const MAX_SEQUENCES: usize, const MAX_SEQUENCE_LEN: usize> Default
118 for Config<MAX_SEQUENCES, MAX_SEQUENCE_LEN>
119{
120 fn default() -> Self {
121 Self::new()
122 }
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127pub enum PressOutcome {
128 Inactive,
130 Continue,
132 Resolved(SequenceId),
134 Aborted,
136}
137
138#[derive(Clone, Copy, PartialEq)]
140pub struct Context<const MAX_SEQUENCES: usize, const MAX_SEQUENCE_LEN: usize> {
141 config: Config<MAX_SEQUENCES, MAX_SEQUENCE_LEN>,
142 mode_active: bool,
143 idle_time_ms: u32,
144 timeout_generation: u16,
145 buffer: [u16; MAX_SEQUENCE_LEN],
146 buffer_len: usize,
147 last_press_outcome: PressOutcome,
149}
150
151impl<const MAX_SEQUENCES: usize, const MAX_SEQUENCE_LEN: usize> Debug
152 for Context<MAX_SEQUENCES, MAX_SEQUENCE_LEN>
153{
154 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
155 f.debug_struct("Context")
156 .field("config", &self.config)
157 .field("mode_active", &self.mode_active)
158 .field("idle_time_ms", &self.idle_time_ms)
159 .field("timeout_generation", &self.timeout_generation)
160 .field("buffer", &&self.buffer[..self.buffer_len])
161 .field("last_press_outcome", &self.last_press_outcome)
162 .finish()
163 }
164}
165
166impl<const MAX_SEQUENCES: usize, const MAX_SEQUENCE_LEN: usize>
167 Context<MAX_SEQUENCES, MAX_SEQUENCE_LEN>
168{
169 pub const fn from_config(config: Config<MAX_SEQUENCES, MAX_SEQUENCE_LEN>) -> Self {
171 Self {
172 config,
173 mode_active: false,
174 idle_time_ms: 0,
175 timeout_generation: 0,
176 buffer: [0; MAX_SEQUENCE_LEN],
177 buffer_len: 0,
178 last_press_outcome: PressOutcome::Inactive,
179 }
180 }
181
182 pub fn reset(&mut self) {
184 *self = Self::from_config(self.config);
185 }
186
187 pub fn is_armed(&self) -> bool {
189 self.mode_active
190 }
191
192 pub fn config(&self) -> &Config<MAX_SEQUENCES, MAX_SEQUENCE_LEN> {
194 &self.config
195 }
196
197 pub fn last_press_outcome(&self) -> PressOutcome {
199 self.last_press_outcome
200 }
201
202 fn sufficient_idle_time(&self) -> bool {
203 self.idle_time_ms >= self.config.required_idle_time.unwrap_or(0) as u32
204 }
205
206 fn bump_timeout(&mut self) -> u16 {
207 self.timeout_generation = self.timeout_generation.wrapping_add(1);
208 self.timeout_generation
209 }
210
211 fn arm(&mut self) {
212 self.mode_active = true;
213 self.buffer_len = 0;
214 self.bump_timeout();
215 self.last_press_outcome = PressOutcome::Inactive;
216 }
217
218 fn disarm(&mut self) {
219 self.mode_active = false;
220 self.buffer_len = 0;
221 self.bump_timeout();
222 }
223
224 fn buffer_slice(&self) -> &[u16] {
225 &self.buffer[..self.buffer_len]
226 }
227
228 fn schedule_timeout(&self, gen_id: u16) -> key::KeyEvents<Event> {
229 key::KeyEvents::scheduled_event(key::ScheduledEvent::after(
230 self.config.timeout,
231 key::Event::key_event(0, Event::Timeout(gen_id)),
232 ))
233 }
234
235 fn candidates_for_buffer(&self) -> heapless::Vec<SequenceId, MAX_SEQUENCES> {
236 let buffer = self.buffer_slice();
237 self.config
238 .sequences
239 .iter()
240 .enumerate()
241 .filter(|(_, seq)| {
242 let s = seq.as_slice();
243 s.len() >= buffer.len() && s[..buffer.len()] == *buffer
244 })
245 .map(|(id, _)| id as SequenceId)
246 .collect()
247 }
248
249 fn exact_match_id(&self, candidates: &[SequenceId]) -> Option<SequenceId> {
250 let buffer = self.buffer_slice();
251 candidates
252 .iter()
253 .copied()
254 .find(|&id| self.config.sequences[id as usize].as_slice() == buffer)
255 }
256
257 fn has_longer(&self, candidates: &[SequenceId]) -> bool {
258 candidates
259 .iter()
260 .any(|&id| self.config.sequences[id as usize].as_slice().len() > self.buffer_len)
261 }
262
263 fn step_press(&mut self, keymap_index: u16) {
265 if self.buffer_len >= MAX_SEQUENCE_LEN {
266 self.disarm();
267 self.last_press_outcome = PressOutcome::Aborted;
268 } else {
269 self.buffer[self.buffer_len] = keymap_index;
270 self.buffer_len += 1;
271 let candidates = self.candidates_for_buffer();
272 match (
273 self.exact_match_id(&candidates),
274 self.has_longer(&candidates),
275 ) {
276 (Some(id), false) => {
277 self.disarm();
278 self.last_press_outcome = PressOutcome::Resolved(id);
279 }
280 (None, false) => {
281 self.disarm();
283 self.last_press_outcome = PressOutcome::Aborted;
284 }
285 _ => {
286 self.last_press_outcome = PressOutcome::Continue;
288 self.bump_timeout();
289 }
290 }
291 }
292 }
293
294 pub fn update_keymap_context(
296 &mut self,
297 keymap::KeymapContext { idle_time_ms, .. }: &keymap::KeymapContext,
298 ) {
299 self.idle_time_ms = *idle_time_ms;
300 }
301
302 fn handle_event(&mut self, event: key::Event<Event>) -> key::KeyEvents<Event> {
303 match event {
304 key::Event::Input(input::Event::Press { keymap_index }) => {
305 if self.mode_active {
306 self.step_press(keymap_index);
307 match self.last_press_outcome {
308 PressOutcome::Continue => self.schedule_timeout(self.timeout_generation),
309 PressOutcome::Resolved(id) => key::KeyEvents::event(key::Event::key_event(
310 keymap_index,
311 Event::SequenceResolved(id),
312 )),
313 PressOutcome::Aborted => key::KeyEvents::event(key::Event::key_event(
314 keymap_index,
315 Event::Aborted,
316 )),
317 PressOutcome::Inactive => key::KeyEvents::no_events(),
318 }
319 } else {
320 self.last_press_outcome = PressOutcome::Inactive;
321 key::KeyEvents::no_events()
322 }
323 }
324 key::Event::Key {
325 key_event: Event::Arm,
326 ..
327 } => {
328 if self.sufficient_idle_time() {
329 self.arm();
330 self.schedule_timeout(self.timeout_generation)
331 } else {
332 key::KeyEvents::no_events()
333 }
334 }
335 key::Event::Key {
336 key_event: Event::Restart,
337 ..
338 } => {
339 self.arm();
340 self.schedule_timeout(self.timeout_generation)
341 }
342 key::Event::Key {
343 key_event: Event::Timeout(gen),
344 ..
345 } => {
346 if self.mode_active && gen == self.timeout_generation {
347 self.disarm();
351 self.last_press_outcome = PressOutcome::Aborted;
352 }
353 key::KeyEvents::no_events()
354 }
355 _ => key::KeyEvents::no_events(),
356 }
357 }
358}
359
360impl<const MAX_SEQUENCES: usize, const MAX_SEQUENCE_LEN: usize> key::Context
361 for Context<MAX_SEQUENCES, MAX_SEQUENCE_LEN>
362{
363 type Event = Event;
364
365 fn handle_event(&mut self, event: key::Event<Self::Event>) -> key::KeyEvents<Self::Event> {
366 self.handle_event(event)
367 }
368
369 fn reset(&mut self) {
370 Context::reset(self);
371 }
372}
373
374#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
376pub enum Event {
377 Arm,
379 Restart,
381 Timeout(u16),
383 SequenceResolved(SequenceId),
385 Aborted,
387}
388
389#[derive(Debug, Clone, Copy, PartialEq)]
391pub struct PendingKeyState;
392
393#[derive(Debug, Clone, Copy, PartialEq)]
395pub struct KeyState;
396
397#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
399pub struct Key<R: Copy, const MAX_OVERLAPPING: usize> {
400 pub sequences: Slice<(SequenceId, R), MAX_OVERLAPPING>,
402 pub passthrough: R,
404}
405
406impl<R: Copy, const MAX_OVERLAPPING: usize> Key<R, MAX_OVERLAPPING> {
407 pub const fn new(sequences: &[(SequenceId, R)], passthrough: R) -> Self {
409 Self {
410 sequences: Slice::from_slice(sequences),
411 passthrough,
412 }
413 }
414
415 pub fn binding_for(&self, id: SequenceId) -> Option<R> {
417 self.sequences
418 .iter()
419 .find(|(sid, _)| *sid == id)
420 .map(|(_, r)| *r)
421 }
422}
423
424#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
426pub struct AuxiliaryKey<R: Copy> {
427 pub passthrough: R,
429}
430
431impl<R: Copy> AuxiliaryKey<R> {
432 pub const fn new(passthrough: R) -> Self {
434 Self { passthrough }
435 }
436}
437
438#[derive(Debug, Clone, Copy, PartialEq)]
440pub struct System<
441 R: Copy + Debug + PartialEq,
442 Keys: Index<usize, Output = Key<R, MAX_OVERLAPPING>> + AsRef<[Key<R, MAX_OVERLAPPING>]>,
443 AuxiliaryKeys: Index<usize, Output = AuxiliaryKey<R>>,
444 const MAX_SEQUENCES: usize,
445 const MAX_SEQUENCE_LEN: usize,
446 const MAX_OVERLAPPING: usize,
447> {
448 keys: Keys,
449 auxiliary_keys: AuxiliaryKeys,
450}
451
452impl<
453 R: Copy + Debug + PartialEq,
454 Keys: Index<usize, Output = Key<R, MAX_OVERLAPPING>> + AsRef<[Key<R, MAX_OVERLAPPING>]>,
455 AuxiliaryKeys: Index<usize, Output = AuxiliaryKey<R>>,
456 const MAX_SEQUENCES: usize,
457 const MAX_SEQUENCE_LEN: usize,
458 const MAX_OVERLAPPING: usize,
459 > System<R, Keys, AuxiliaryKeys, MAX_SEQUENCES, MAX_SEQUENCE_LEN, MAX_OVERLAPPING>
460{
461 pub const fn new(keys: Keys, auxiliary_keys: AuxiliaryKeys) -> Self {
463 Self {
464 keys,
465 auxiliary_keys,
466 }
467 }
468
469 fn binding_for(&self, id: SequenceId) -> Option<R> {
470 self.keys.as_ref().iter().find_map(|k| k.binding_for(id))
471 }
472}
473
474impl<
475 R: Copy + Debug + PartialEq,
476 Keys: Debug + Index<usize, Output = Key<R, MAX_OVERLAPPING>> + AsRef<[Key<R, MAX_OVERLAPPING>]>,
477 AuxiliaryKeys: Debug + Index<usize, Output = AuxiliaryKey<R>>,
478 const MAX_SEQUENCES: usize,
479 const MAX_SEQUENCE_LEN: usize,
480 const MAX_OVERLAPPING: usize,
481 > key::System<R>
482 for System<R, Keys, AuxiliaryKeys, MAX_SEQUENCES, MAX_SEQUENCE_LEN, MAX_OVERLAPPING>
483{
484 type Ref = Ref;
485 type Context = Context<MAX_SEQUENCES, MAX_SEQUENCE_LEN>;
486 type Event = Event;
487 type PendingKeyState = PendingKeyState;
488 type KeyState = KeyState;
489
490 fn new_pressed_key(
491 &self,
492 keymap_index: u16,
493 context: &Self::Context,
494 key_ref: Ref,
495 ) -> (
496 key::PressedKeyResult<R, Self::PendingKeyState, Self::KeyState>,
497 key::KeyEvents<Self::Event>,
498 ) {
499 match key_ref {
500 Ref::SequenceStart => {
501 let ev = if context.is_armed() {
502 Event::Restart
506 } else {
507 Event::Arm
508 };
509 let pke = key::KeyEvents::event(key::Event::key_event(keymap_index, ev));
510 (
511 key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp),
512 pke,
513 )
514 }
515 Ref::Sequence(i) | Ref::Auxiliary(i) => {
516 let passthrough = match key_ref {
517 Ref::Sequence(idx) => self.keys[idx as usize].passthrough,
518 Ref::Auxiliary(idx) => self.auxiliary_keys[idx as usize].passthrough,
519 Ref::SequenceStart => unreachable!(),
520 };
521 let _ = i;
522
523 match context.last_press_outcome() {
527 PressOutcome::Inactive => (
528 key::PressedKeyResult::NewPressedKey(key::NewPressedKey::key(passthrough)),
529 key::KeyEvents::no_events(),
530 ),
531 PressOutcome::Continue | PressOutcome::Aborted => (
532 key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp),
533 key::KeyEvents::no_events(),
534 ),
535 PressOutcome::Resolved(id) => {
536 if let Some(r) = self.binding_for(id) {
537 (
538 key::PressedKeyResult::NewPressedKey(key::NewPressedKey::key(r)),
539 key::KeyEvents::no_events(),
540 )
541 } else {
542 (
543 key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp),
544 key::KeyEvents::no_events(),
545 )
546 }
547 }
548 }
549 }
550 }
551 }
552
553 fn update_pending_state(
554 &self,
555 _pending_state: &mut Self::PendingKeyState,
556 _keymap_index: u16,
557 _context: &Self::Context,
558 _key_ref: Ref,
559 _event: key::Event<Self::Event>,
560 ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Self::Event>) {
561 (None, key::KeyEvents::no_events())
562 }
563
564 fn update_state(
565 &self,
566 _key_state: &mut Self::KeyState,
567 _key_ref: &Self::Ref,
568 _context: &Self::Context,
569 _keymap_index: u16,
570 _event: key::Event<Self::Event>,
571 ) -> key::KeyEvents<Self::Event> {
572 key::KeyEvents::no_events()
573 }
574
575 fn key_output(
576 &self,
577 _key_ref: &Self::Ref,
578 _key_state: &Self::KeyState,
579 ) -> Option<key::KeyOutput> {
580 None
581 }
582}
583
584#[cfg(test)]
585#[allow(clippy::unwrap_used, clippy::expect_used)]
586mod tests {
587 use super::*;
588
589 const MAX_SEQUENCES: usize = 4;
590 const MAX_SEQUENCE_LEN: usize = 4;
591
592 type Ctx = Context<MAX_SEQUENCES, MAX_SEQUENCE_LEN>;
593
594 fn ctx_with(sequences: &[&[u16]]) -> Ctx {
595 match sequences.len() {
596 1 => Context::from_config(Config {
597 sequences: Slice::from_slice(&[SequenceIndices::from_slice(sequences[0])]),
598 ..Config::new()
599 }),
600 2 => Context::from_config(Config {
601 sequences: Slice::from_slice(&[
602 SequenceIndices::from_slice(sequences[0]),
603 SequenceIndices::from_slice(sequences[1]),
604 ]),
605 ..Config::new()
606 }),
607 _ => Context::from_config(Config::new()),
608 }
609 }
610
611 #[test]
612 fn start_arms_mode() {
613 let mut ctx = Ctx::from_config(Config::new());
614 let _ = ctx.handle_event(key::Event::key_event(0, Event::Arm));
615 assert!(ctx.is_armed());
616 }
617
618 #[test]
619 fn two_step_resolves() {
620 let mut ctx = ctx_with(&[&[0, 1]]);
622
623 let _ = ctx.handle_event(key::Event::key_event(9, Event::Arm));
625 assert!(ctx.is_armed());
626 let _ = ctx.handle_event(key::Event::Input(input::Event::Press { keymap_index: 0 }));
627 assert_eq!(ctx.last_press_outcome(), PressOutcome::Continue);
628 let _ = ctx.handle_event(key::Event::Input(input::Event::Press { keymap_index: 1 }));
629
630 assert_eq!(ctx.last_press_outcome(), PressOutcome::Resolved(0));
632 assert!(!ctx.is_armed());
633 }
634
635 #[test]
636 fn unknown_aborts() {
637 let mut ctx = ctx_with(&[&[0, 1]]);
639
640 let _ = ctx.handle_event(key::Event::key_event(9, Event::Arm));
642 let _ = ctx.handle_event(key::Event::Input(input::Event::Press { keymap_index: 0 }));
643 let _ = ctx.handle_event(key::Event::Input(input::Event::Press { keymap_index: 9 }));
644
645 assert_eq!(ctx.last_press_outcome(), PressOutcome::Aborted);
647 assert!(!ctx.is_armed());
648 }
649
650 #[test]
651 fn timeout_aborts_strict_prefix() {
652 let mut ctx = ctx_with(&[&[0, 1, 2]]);
654
655 let _ = ctx.handle_event(key::Event::key_event(9, Event::Arm));
657 let _ = ctx.handle_event(key::Event::Input(input::Event::Press { keymap_index: 0 }));
658 let gen = ctx.timeout_generation;
659 let _ = ctx.handle_event(key::Event::key_event(0, Event::Timeout(gen)));
660
661 assert_eq!(ctx.last_press_outcome(), PressOutcome::Aborted);
663 assert!(!ctx.is_armed());
664 }
665}