#![doc = include_str!("doc_de_chorded.md")]
use core::fmt::Debug;
use serde::Deserialize;
use crate::{input, key};
use key::PressedKey;
pub use crate::init::MAX_CHORDS;
const MAX_CHORD_SIZE: usize = 2;
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "std", serde(untagged))]
pub enum ChordIndices {
Chord2(u16, u16),
}
impl ChordIndices {
pub fn has_index(&self, index: u16) -> bool {
match self {
ChordIndices::Chord2(i0, i1) => i0 == &index || i1 == &index,
}
}
pub fn is_satisfied_by(&self, indices: &[u16]) -> bool {
match self {
ChordIndices::Chord2(i0, i1) => indices.contains(i0) && indices.contains(i1),
}
}
}
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
pub struct Config {
#[serde(default = "default_timeout")]
pub timeout: u16,
#[serde(default = "default_chords")]
#[serde(deserialize_with = "deserialize_chords")]
pub chords: [Option<ChordIndices>; MAX_CHORDS],
}
fn default_timeout() -> u16 {
DEFAULT_CONFIG.timeout
}
fn default_chords() -> [Option<ChordIndices>; MAX_CHORDS] {
DEFAULT_CONFIG.chords
}
fn deserialize_chords<'de, D>(
deserializer: D,
) -> Result<[Option<ChordIndices>; MAX_CHORDS], D::Error>
where
D: serde::Deserializer<'de>,
{
let mut v: heapless::Vec<Option<ChordIndices>, MAX_CHORDS> =
Deserialize::deserialize(deserializer)?;
while !v.is_full() {
v.push(None).unwrap();
}
v.into_array()
.map_err(|_| serde::de::Error::custom("unable to deserialize"))
}
pub const DEFAULT_CONFIG: Config = Config {
timeout: 200,
chords: [None; MAX_CHORDS],
};
impl Default for Config {
fn default() -> Self {
DEFAULT_CONFIG
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Context {
pub config: Config,
pressed_indices: [Option<u16>; MAX_CHORD_SIZE * MAX_CHORDS],
}
pub const DEFAULT_CONTEXT: Context = Context::from_config(DEFAULT_CONFIG);
impl Context {
pub const fn from_config(config: Config) -> Context {
let pressed_indices = [None; MAX_CHORD_SIZE * MAX_CHORDS];
Context {
config,
pressed_indices,
}
}
pub fn chords_for_indices(
&self,
indices: &[u16],
) -> heapless::Vec<ChordIndices, { MAX_CHORDS }> {
self.config
.chords
.iter()
.filter_map(|&c| c)
.filter(|c| indices.iter().all(|i| c.has_index(*i)))
.collect()
}
fn sibling_indices(&self, index: u16) -> heapless::Vec<u16, { MAX_CHORD_SIZE * MAX_CHORDS }> {
let mut res: heapless::Vec<u16, { MAX_CHORD_SIZE * MAX_CHORDS }> = heapless::Vec::new();
let chords = self.chords_for_indices(&[index]);
chords.iter().for_each(|ch| match ch {
ChordIndices::Chord2(i0, i1) => {
if let Err(pos) = res.binary_search(&i0) {
res.insert(pos, *i0).unwrap();
}
if let Err(pos) = res.binary_search(&i1) {
res.insert(pos, *i1).unwrap();
}
}
});
res
}
fn insert_pressed_index(&mut self, pos: usize, index: u16) {
if self.pressed_indices.is_empty() {
return;
}
let mut i = self.pressed_indices.len() - 1;
while i > pos {
self.pressed_indices[i] = self.pressed_indices[i - 1];
i -= 1;
}
self.pressed_indices[pos] = Some(index);
}
fn remove_pressed_index(&mut self, pos: usize) {
if self.pressed_indices.is_empty() {
return;
}
let mut i = pos;
while i < self.pressed_indices.len() - 1 {
self.pressed_indices[i] = self.pressed_indices[i + 1];
i += 1;
}
self.pressed_indices[self.pressed_indices.len() - 1] = None;
}
fn press_index(&mut self, index: u16) {
match self
.pressed_indices
.binary_search_by_key(&index, |&k| k.unwrap_or(u16::MAX))
{
Ok(_) => {}
Err(pos) => self.insert_pressed_index(pos, index),
}
}
fn release_index(&mut self, index: u16) {
match self
.pressed_indices
.binary_search_by_key(&index, |&k| k.unwrap_or(u16::MAX))
{
Ok(pos) => self.remove_pressed_index(pos),
Err(_) => {}
}
}
fn pressed_indices(&self) -> heapless::Vec<u16, { MAX_CHORD_SIZE * MAX_CHORDS }> {
self.pressed_indices.iter().filter_map(|&i| i).collect()
}
pub fn handle_event(&mut self, event: key::Event<Event>) {
match event {
key::Event::Input(input::Event::Press { keymap_index }) => {
self.press_index(keymap_index);
}
key::Event::Input(input::Event::Release { keymap_index }) => {
self.release_index(keymap_index);
}
key::Event::Key {
keymap_index,
key_event,
} => match key_event {
Event::ChordResolved(false) => {
self.release_index(keymap_index);
}
_ => {}
},
_ => {}
}
}
}
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
pub struct Key<K> {
chord: K,
passthrough: K,
}
impl<K: key::Key + Copy> Key<K>
where
K::Context: Into<Context>,
K::Event: TryInto<Event>,
K::Event: From<Event>,
{
pub const fn new(chord: K, passthrough: K) -> Self {
Key { chord, passthrough }
}
pub fn new_pressed_key(
&self,
context: K::Context,
keymap_index: u16,
) -> (
input::PressedKey<Self, PressedKeyState<K>>,
key::PressedKeyEvents<K::Event>,
) {
let mut pk = input::PressedKey {
keymap_index,
key: *self,
pressed_key_state: PressedKeyState::new(context, keymap_index),
};
let timeout_ev = Event::Timeout;
let sch_ev = key::ScheduledEvent::after(
context.into().config.timeout,
key::Event::key_event(keymap_index, timeout_ev),
);
let mut pke = key::PressedKeyEvents::scheduled_event(sch_ev.into_scheduled_event());
let n_pke = pk
.pressed_key_state
.check_resolution(context, keymap_index, self);
pke.extend(n_pke);
(pk, pke)
}
pub fn map_key<T: key::Key + Copy>(self, f: fn(K) -> T) -> Key<T> {
let Key { chord, passthrough } = self;
Key {
chord: f(chord),
passthrough: f(passthrough),
}
}
pub fn into_key<T: key::Key + Copy>(self) -> Key<T>
where
K: Into<T>,
{
self.map_key(|k| k.into())
}
}
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
pub struct AuxiliaryKey<K> {
passthrough: K,
}
impl<K: key::Key + Copy> AuxiliaryKey<K>
where
K::Context: Into<Context>,
K::Event: TryInto<Event>,
K::Event: From<Event>,
{
pub const fn new(passthrough: K) -> Self {
AuxiliaryKey { passthrough }
}
pub fn new_pressed_key(
&self,
context: K::Context,
keymap_index: u16,
) -> (
input::PressedKey<Self, PressedKeyState<K>>,
key::PressedKeyEvents<K::Event>,
) {
let mut pk = input::PressedKey {
keymap_index,
key: *self,
pressed_key_state: PressedKeyState::new(context, keymap_index),
};
let timeout_ev = Event::Timeout;
let sch_ev = key::ScheduledEvent::after(
context.into().config.timeout,
key::Event::key_event(keymap_index, timeout_ev),
);
let mut pke = key::PressedKeyEvents::scheduled_event(sch_ev.into_scheduled_event());
let n_pke = pk
.pressed_key_state
.check_resolution(context, keymap_index, self);
pke.extend(n_pke);
(pk, pke)
}
pub fn map_key<T: key::Key + Copy>(self, f: fn(K) -> T) -> AuxiliaryKey<T> {
let AuxiliaryKey { passthrough } = self;
AuxiliaryKey {
passthrough: f(passthrough),
}
}
pub fn into_key<T: key::Key + Copy>(self) -> AuxiliaryKey<T>
where
K: Into<T>,
{
self.map_key(|k| k.into())
}
}
pub trait ChordedKey<K: key::Key> {
fn passthrough_key(&self) -> &K;
fn chorded_key(&self) -> Option<&K>;
}
impl<K: key::Key> ChordedKey<K> for Key<K> {
fn passthrough_key(&self) -> &K {
&self.passthrough
}
fn chorded_key(&self) -> Option<&K> {
Some(&self.chord)
}
}
impl<K: key::Key> ChordedKey<K> for AuxiliaryKey<K> {
fn passthrough_key(&self) -> &K {
&self.passthrough
}
fn chorded_key(&self) -> Option<&K> {
None
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub enum Event {
ChordResolved(bool),
Timeout,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ChordSatisfaction {
Unsatisfied,
Satisfied,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ChordResolution<PK> {
Chord(Option<PK>),
Passthrough(PK),
}
#[derive(Debug, PartialEq)]
pub enum PressedKeyState<K: key::Key> {
Pending {
pressed_indices: heapless::Vec<u16, { MAX_CHORD_SIZE }>,
satisfaction: ChordSatisfaction,
},
Resolved(ChordResolution<K::PressedKey>),
}
impl<K: key::Key> PressedKeyState<K>
where
K::Context: Into<Context>,
K::Event: TryInto<Event>,
K::Event: From<Event>,
{
pub fn new(context: K::Context, keymap_index: u16) -> Self {
let sibling_indices = context.into().sibling_indices(keymap_index);
let pressed_indices: heapless::Vec<u16, MAX_CHORD_SIZE> = context
.into()
.pressed_indices()
.iter()
.filter(|i| sibling_indices.contains(i))
.copied()
.collect();
Self::Pending {
pressed_indices,
satisfaction: ChordSatisfaction::Unsatisfied,
}
}
fn check_resolution<C: ChordedKey<K>>(
&mut self,
context: K::Context,
keymap_index: u16,
key: &C,
) -> key::PressedKeyEvents<K::Event> {
match self {
Self::Pending {
pressed_indices,
satisfaction: _,
} => {
let ctx: Context = context.into();
let chords = ctx.chords_for_indices(pressed_indices.as_slice());
match chords.as_slice() {
[ch] if ch.is_satisfied_by(&pressed_indices) => {
self.resolve_as_chord(context, keymap_index, key)
}
[] => {
self.resolve_as_passthrough(context, keymap_index, key)
}
_ => {
key::PressedKeyEvents::no_events()
}
}
}
_ => key::PressedKeyEvents::no_events(),
}
}
fn resolve_as_passthrough<C: ChordedKey<K>>(
&mut self,
context: K::Context,
keymap_index: u16,
key: &C,
) -> key::PressedKeyEvents<K::Event> {
let k = key.passthrough_key();
let (pk, mut n_pke) = k.new_pressed_key(context, keymap_index);
*self = Self::Resolved(ChordResolution::Passthrough(pk));
let resolved_ev = Event::ChordResolved(false);
let key_ev = key::Event::key_event(keymap_index, resolved_ev);
let sch_ev = key::ScheduledEvent::immediate(key_ev);
n_pke.add_event(sch_ev.into_scheduled_event());
n_pke
}
fn resolve_as_chord<C: ChordedKey<K>>(
&mut self,
context: K::Context,
keymap_index: u16,
key: &C,
) -> key::PressedKeyEvents<K::Event> {
let resolved_ev = Event::ChordResolved(true);
let key_ev = key::Event::key_event(keymap_index, resolved_ev);
if let Some(k) = key.chorded_key() {
let (pk, mut n_pke) = k.new_pressed_key(context, keymap_index);
*self = Self::Resolved(ChordResolution::Chord(Some(pk)));
let sch_ev = key::ScheduledEvent::immediate(key_ev);
n_pke.add_event(sch_ev.into_scheduled_event());
n_pke
} else {
*self = Self::Resolved(ChordResolution::Chord(None));
key::PressedKeyEvents::event(key_ev.into_key_event())
}
}
pub fn handle_event_for<C: ChordedKey<K>>(
&mut self,
context: K::Context,
keymap_index: u16,
key: &C,
event: key::Event<K::Event>,
) -> key::PressedKeyEvents<K::Event> {
let mut pke = key::PressedKeyEvents::no_events();
match self {
Self::Pending {
pressed_indices,
satisfaction: _,
} => {
match event {
key::Event::Key {
keymap_index: _ev_idx,
key_event,
} => {
if let Ok(ev) = key_event.try_into() {
match ev {
Event::Timeout => {
let n_pke =
self.resolve_as_passthrough(context, keymap_index, key);
pke.extend(n_pke);
}
_ => {}
}
}
}
key::Event::Input(input::Event::Press {
keymap_index: pressed_keymap_index,
}) => {
let pos = pressed_indices
.binary_search(&keymap_index)
.unwrap_or_else(|e| e);
let push_res = pressed_indices.insert(pos, pressed_keymap_index);
if push_res.is_err() {
panic!();
}
let n_pke = self.check_resolution(context, keymap_index, key);
pke.extend(n_pke);
}
key::Event::Input(input::Event::Release {
keymap_index: released_keymap_index,
}) => {
if released_keymap_index == keymap_index {
let n_pke = self.resolve_as_passthrough(context, keymap_index, key);
pke.extend(n_pke);
}
}
_ => {}
}
}
Self::Resolved(chord_res) => match chord_res {
ChordResolution::Chord(Some(pk)) => {
let n_pke = pk.handle_event(context, event);
pke.extend(n_pke);
}
ChordResolution::Passthrough(pk) => {
let n_pke = pk.handle_event(context, event);
pke.extend(n_pke);
}
_ => {}
},
}
pke
}
pub fn key_output(&self) -> key::KeyOutputState {
use key::PressedKey as _;
match self {
Self::Pending { .. } => key::KeyOutputState::pending(),
Self::Resolved(ChordResolution::Chord(None)) => key::KeyOutputState::no_output(),
Self::Resolved(ChordResolution::Chord(Some(pk))) => pk.key_output(),
Self::Resolved(ChordResolution::Passthrough(pk)) => pk.key_output(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use key::composite;
use key::keyboard;
use key::Context as _;
use key::PressedKey;
#[test]
fn test_timeout_resolves_unsatisfied_aux_state_as_passthrough_key() {
let context = key::composite::Context::default();
let expected_key = keyboard::Key::new(0x04);
let chorded_key = AuxiliaryKey {
passthrough: expected_key,
};
let keymap_index: u16 = 0;
let mut pks: PressedKeyState<keyboard::Key> = PressedKeyState::new(context, keymap_index);
let timeout_ev = key::Event::key_event(keymap_index, Event::Timeout).into_key_event();
let _actual_events = pks.handle_event_for(context, keymap_index, &chorded_key, timeout_ev);
let actual_output = pks.key_output();
let (pk, _expected_events) =
key::Key::new_pressed_key(&expected_key, context, keymap_index);
let expected_output = pk.key_output();
assert_eq!(expected_output, actual_output);
}
#[test]
fn test_press_non_chorded_key_resolves_aux_state_as_interrupted() {
let context = key::composite::Context::default();
let expected_key = keyboard::Key::new(0x04);
let chorded_key = AuxiliaryKey {
passthrough: expected_key,
};
let keymap_index: u16 = 0;
let mut pks: PressedKeyState<keyboard::Key> = PressedKeyState::new(context, keymap_index);
let non_chord_press = input::Event::Press { keymap_index: 9 }.into();
let _actual_events =
pks.handle_event_for(context, keymap_index, &chorded_key, non_chord_press);
let actual_output = pks.key_output();
let (pk, _expected_events) =
key::Key::new_pressed_key(&expected_key, context, keymap_index);
let expected_output = pk.key_output();
assert_eq!(expected_output, actual_output);
}
#[test]
fn test_press_chorded_key_resolves_unambiguous_aux_state_as_chord() {
let mut context = key::composite::Context {
chorded_context: Context::from_config(Config {
chords: [Some(ChordIndices::Chord2(0, 1)), None, None, None],
..DEFAULT_CONFIG
}),
..composite::DEFAULT_CONTEXT
};
let passthrough = keyboard::Key::new(0x04);
let chorded_key = AuxiliaryKey { passthrough };
let keymap_index: u16 = 0;
context.handle_event(key::Event::Input(input::Event::Press { keymap_index: 0 }));
let mut pks: PressedKeyState<keyboard::Key> = PressedKeyState::new(context, keymap_index);
let chord_press = input::Event::Press { keymap_index: 1 }.into();
let _actual_events = pks.handle_event_for(context, keymap_index, &chorded_key, chord_press);
let actual_output = pks.key_output();
let expected_output = key::KeyOutputState::no_output();
assert_eq!(expected_output, actual_output);
}
#[test]
fn test_release_pending_aux_state_resolves_as_tapped_key() {
let context = key::composite::Context::default();
let expected_key = keyboard::Key::new(0x04);
let chorded_key = AuxiliaryKey {
passthrough: expected_key,
};
let keymap_index: u16 = 0;
let mut pks: PressedKeyState<keyboard::Key> = PressedKeyState::new(context, keymap_index);
let chorded_key_release = input::Event::Release { keymap_index }.into();
let _actual_events =
pks.handle_event_for(context, keymap_index, &chorded_key, chorded_key_release);
let actual_output = pks.key_output();
let (pk, _expected_events) =
key::Key::new_pressed_key(&expected_key, context, keymap_index);
let expected_output = pk.key_output();
assert_eq!(expected_output, actual_output);
}
}