use crate::keys::{Arrow, KeyEvent, KeyInput, Modifier};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Key {
Char(char, char),
Modifier(Modifier),
Enter,
Backspace,
Tab,
}
#[rustfmt::skip]
const LAYOUT: [[Key; 14]; 4] = {
use Key::{Backspace, Char as C, Enter, Modifier as M, Tab};
use Modifier::{Alt, Ctrl, Fn, Opt, Shift};
[
[C('`', '~'), C('1', '!'), C('2', '@'), C('3', '#'), C('4', '$'), C('5', '%'), C('6', '^'),
C('7', '&'), C('8', '*'), C('9', '('), C('0', ')'), C('-', '_'), C('=', '+'), Backspace],
[Tab, C('q', 'Q'), C('w', 'W'), C('e', 'E'), C('r', 'R'), C('t', 'T'), C('y', 'Y'),
C('u', 'U'), C('i', 'I'), C('o', 'O'), C('p', 'P'), C('[', '{'), C(']', '}'), C('\\', '|')],
[M(Fn), M(Shift), C('a', 'A'), C('s', 'S'), C('d', 'D'), C('f', 'F'), C('g', 'G'),
C('h', 'H'), C('j', 'J'), C('k', 'K'), C('l', 'L'), C(';', ':'), C('\'', '"'), Enter],
[M(Ctrl), M(Opt), M(Alt), C('z', 'Z'), C('x', 'X'), C('c', 'C'), C('v', 'V'),
C('b', 'B'), C('n', 'N'), C('m', 'M'), C(',', '<'), C('.', '>'), C('/', '?'), C(' ', ' ')],
]
};
fn key(number: u8) -> Option<Key> {
let index = number.checked_sub(1).filter(|&index| index < 80)?;
let (row, column) = (usize::from(index / 10), usize::from(index % 10));
if column >= 8 {
return None;
}
LAYOUT
.get(column % 4)?
.get(row * 2 + usize::from(column >= 4))
.copied()
}
const fn fn_layer(key: Key) -> Option<KeyInput> {
Some(match key {
Key::Char('`', _) => KeyInput::Escape,
Key::Char('\\', _) | Key::Backspace => KeyInput::Delete,
Key::Char(';', _) => KeyInput::Arrow(Arrow::Up),
Key::Char(',', _) => KeyInput::Arrow(Arrow::Left),
Key::Char('.', _) => KeyInput::Arrow(Arrow::Down),
Key::Char('/', _) => KeyInput::Arrow(Arrow::Right),
_ => return None,
})
}
const fn typed(key: Key, shift: bool, fn_held: bool) -> KeyInput {
if fn_held && let Some(input) = fn_layer(key) {
return input;
}
match key {
Key::Char(plain, shifted) => KeyInput::Char(if shift { shifted } else { plain }),
Key::Modifier(modifier) => KeyInput::Modifier(modifier),
Key::Enter => KeyInput::Enter,
Key::Backspace => KeyInput::Backspace,
Key::Tab => KeyInput::Tab,
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Keymap {
held: u128,
shifted: u128,
fn_layer: u128,
}
impl Keymap {
#[must_use]
pub const fn new() -> Self {
Self {
held: 0,
shifted: 0,
fn_layer: 0,
}
}
pub fn event(&mut self, event: u8) -> Option<KeyEvent> {
let number = event & 0x7F;
let key = key(number)?;
let bit = 1 << number;
if event & 0x80 != 0 {
self.held |= bit;
let shift = self.holds(Key::Modifier(Modifier::Shift));
let fn_held = self.holds(Key::Modifier(Modifier::Fn));
self.shifted = if shift {
self.shifted | bit
} else {
self.shifted & !bit
};
self.fn_layer = if fn_held {
self.fn_layer | bit
} else {
self.fn_layer & !bit
};
Some(KeyEvent::Pressed(typed(key, shift, fn_held)))
} else {
self.held &= !bit;
let typed = typed(key, self.shifted & bit != 0, self.fn_layer & bit != 0);
Some(KeyEvent::Released(typed))
}
}
#[must_use]
pub fn is_held(&self, input: KeyInput) -> bool {
self.held_keys().any(|key| {
[(false, false), (true, false), (false, true)]
.into_iter()
.any(|(shift, fn_held)| typed(key, shift, fn_held) == input)
})
}
fn holds(&self, key: Key) -> bool {
self.held_keys().any(|held| held == key)
}
fn held_keys(&self) -> impl Iterator<Item = Key> + use<'_> {
(1..=80)
.filter(|&number| self.held & 1 << number != 0)
.filter_map(key)
}
}
#[cfg(test)]
mod tests {
use super::*;
const fn press(number: u8) -> u8 {
number | 0x80
}
fn typed(keymap: &mut Keymap, event: u8) -> Option<KeyInput> {
keymap
.event(event)
.filter(|event| event.is_press())
.map(KeyEvent::input)
}
const FN: u8 = 3;
const SHIFT: u8 = 7;
const A: u8 = 13;
const SEMICOLON: u8 = 57;
#[test]
fn a_key_types_its_character_and_shift_the_upper_one() {
let mut keymap = Keymap::default();
assert_eq!(typed(&mut keymap, press(A)), Some(KeyInput::Char('a')));
assert_eq!(typed(&mut keymap, A), None);
assert_eq!(
typed(&mut keymap, press(SHIFT)),
Some(KeyInput::Modifier(Modifier::Shift))
);
assert_eq!(typed(&mut keymap, press(A)), Some(KeyInput::Char('A')));
assert_eq!(typed(&mut keymap, SHIFT), None);
assert_eq!(typed(&mut keymap, press(A)), Some(KeyInput::Char('a')));
}
#[test]
fn fn_gives_the_arrows_over_shift() {
let mut keymap = Keymap::default();
typed(&mut keymap, press(FN));
typed(&mut keymap, press(SHIFT));
assert_eq!(
typed(&mut keymap, press(SEMICOLON)),
Some(KeyInput::Arrow(Arrow::Up))
);
assert_eq!(typed(&mut keymap, press(A)), Some(KeyInput::Char('A')));
}
#[test]
fn a_key_is_held_from_its_press_to_its_release() {
let mut keymap = Keymap::default();
assert!(!keymap.is_held(KeyInput::Char('a')));
typed(&mut keymap, press(A));
assert!(keymap.is_held(KeyInput::Char('a')));
assert!(keymap.is_held(KeyInput::Char('A')), "the same key");
assert!(!keymap.is_held(KeyInput::Char('s')));
typed(&mut keymap, A);
assert!(!keymap.is_held(KeyInput::Char('a')));
}
#[test]
fn modifiers_and_fn_layer_keys_are_held_too() {
let mut keymap = Keymap::default();
typed(&mut keymap, press(FN));
typed(&mut keymap, press(SEMICOLON));
assert!(keymap.is_held(KeyInput::Modifier(Modifier::Fn)));
assert!(keymap.is_held(KeyInput::Arrow(Arrow::Up)));
assert!(keymap.is_held(KeyInput::Char(';')), "the same key");
assert!(!keymap.is_held(KeyInput::Modifier(Modifier::Shift)));
typed(&mut keymap, FN);
assert!(keymap.is_held(KeyInput::Arrow(Arrow::Up)));
}
#[test]
fn a_release_types_what_its_press_did() {
let mut keymap = Keymap::default();
assert_eq!(
keymap.event(press(A)),
Some(KeyEvent::Pressed(KeyInput::Char('a')))
);
keymap.event(press(SHIFT));
assert_eq!(
keymap.event(A),
Some(KeyEvent::Released(KeyInput::Char('a'))),
"pressed before Shift"
);
keymap.event(press(A));
keymap.event(SHIFT);
assert_eq!(
keymap.event(A),
Some(KeyEvent::Released(KeyInput::Char('A'))),
"pressed with Shift"
);
keymap.event(press(FN));
keymap.event(press(SEMICOLON));
keymap.event(FN);
assert_eq!(
keymap.event(SEMICOLON),
Some(KeyEvent::Released(KeyInput::Arrow(Arrow::Up)))
);
assert_eq!(
keymap.event(SHIFT),
Some(KeyEvent::Released(KeyInput::Modifier(Modifier::Shift)))
);
}
#[test]
fn numbers_off_the_keyboard_type_nothing() {
let mut keymap = Keymap::default();
assert_eq!(typed(&mut keymap, 0), None);
assert_eq!(typed(&mut keymap, press(0)), None);
assert_eq!(typed(&mut keymap, press(81)), None);
assert_eq!(typed(&mut keymap, press(97)), None);
assert_eq!(typed(&mut keymap, press(71)), None);
assert_eq!(typed(&mut keymap, press(9)), None);
}
#[test]
fn every_layout_position_has_one_key_number() {
let found = (1..=80).filter(|&number| key(number).is_some()).count();
assert_eq!(found, 56);
}
}