use denise::KeyCode;
pub const UNIDENTIFIED: u32 = 0x8000_0000;
const RAW_MASK: u32 = 0x7FFF_FFFF;
macro_rules! key_table {
($( $c_name:ident = $value:literal => $variant:ident ),* $(,)?) => {
$(
#[doc = concat!("[`KeyCode::", stringify!($variant), "`] as an ABI number.")]
pub const $c_name: u32 = $value;
)*
pub fn to_abi(code: KeyCode) -> u32 {
match code {
$( KeyCode::$variant => $value, )*
KeyCode::Unidentified(raw) => UNIDENTIFIED | (raw & RAW_MASK),
_ => UNIDENTIFIED,
}
}
pub fn from_abi(value: u32) -> Option<KeyCode> {
match value {
$( $value => Some(KeyCode::$variant), )*
v if v & UNIDENTIFIED != 0 => Some(KeyCode::Unidentified(v & RAW_MASK)),
_ => None,
}
}
pub const TABLE: &[(&str, u32)] = &[ $( (stringify!($c_name), $value) ),* ];
};
}
key_table! {
DENISE_KEY_A = 0x41 => A,
DENISE_KEY_B = 0x42 => B,
DENISE_KEY_C = 0x43 => C,
DENISE_KEY_D = 0x44 => D,
DENISE_KEY_E = 0x45 => E,
DENISE_KEY_F = 0x46 => F,
DENISE_KEY_G = 0x47 => G,
DENISE_KEY_H = 0x48 => H,
DENISE_KEY_I = 0x49 => I,
DENISE_KEY_J = 0x4a => J,
DENISE_KEY_K = 0x4b => K,
DENISE_KEY_L = 0x4c => L,
DENISE_KEY_M = 0x4d => M,
DENISE_KEY_N = 0x4e => N,
DENISE_KEY_O = 0x4f => O,
DENISE_KEY_P = 0x50 => P,
DENISE_KEY_Q = 0x51 => Q,
DENISE_KEY_R = 0x52 => R,
DENISE_KEY_S = 0x53 => S,
DENISE_KEY_T = 0x54 => T,
DENISE_KEY_U = 0x55 => U,
DENISE_KEY_V = 0x56 => V,
DENISE_KEY_W = 0x57 => W,
DENISE_KEY_X = 0x58 => X,
DENISE_KEY_Y = 0x59 => Y,
DENISE_KEY_Z = 0x5a => Z,
DENISE_KEY_0 = 0x30 => Digit0,
DENISE_KEY_1 = 0x31 => Digit1,
DENISE_KEY_2 = 0x32 => Digit2,
DENISE_KEY_3 = 0x33 => Digit3,
DENISE_KEY_4 = 0x34 => Digit4,
DENISE_KEY_5 = 0x35 => Digit5,
DENISE_KEY_6 = 0x36 => Digit6,
DENISE_KEY_7 = 0x37 => Digit7,
DENISE_KEY_8 = 0x38 => Digit8,
DENISE_KEY_9 = 0x39 => Digit9,
DENISE_KEY_SPACE = 0x20 => Space,
DENISE_KEY_QUOTE = 0x27 => Quote,
DENISE_KEY_COMMA = 0x2c => Comma,
DENISE_KEY_MINUS = 0x2d => Minus,
DENISE_KEY_PERIOD = 0x2e => Period,
DENISE_KEY_SLASH = 0x2f => Slash,
DENISE_KEY_SEMICOLON = 0x3b => Semicolon,
DENISE_KEY_EQUAL = 0x3d => Equal,
DENISE_KEY_BRACKET_LEFT = 0x5b => BracketLeft,
DENISE_KEY_BACKSLASH = 0x5c => Backslash,
DENISE_KEY_BRACKET_RIGHT = 0x5d => BracketRight,
DENISE_KEY_BACKQUOTE = 0x60 => Backquote,
DENISE_KEY_ESCAPE = 0x100 => Escape,
DENISE_KEY_ENTER = 0x101 => Enter,
DENISE_KEY_TAB = 0x102 => Tab,
DENISE_KEY_BACKSPACE = 0x103 => Backspace,
DENISE_KEY_DELETE = 0x104 => Delete,
DENISE_KEY_INSERT = 0x105 => Insert,
DENISE_KEY_HOME = 0x106 => Home,
DENISE_KEY_END = 0x107 => End,
DENISE_KEY_PAGE_UP = 0x108 => PageUp,
DENISE_KEY_PAGE_DOWN = 0x109 => PageDown,
DENISE_KEY_ARROW_UP = 0x10a => ArrowUp,
DENISE_KEY_ARROW_DOWN = 0x10b => ArrowDown,
DENISE_KEY_ARROW_LEFT = 0x10c => ArrowLeft,
DENISE_KEY_ARROW_RIGHT = 0x10d => ArrowRight,
DENISE_KEY_CAPS_LOCK = 0x10e => CapsLock,
DENISE_KEY_NUM_LOCK = 0x10f => NumLock,
DENISE_KEY_SCROLL_LOCK = 0x110 => ScrollLock,
DENISE_KEY_INTL_BACKSLASH = 0x111 => IntlBackslash,
DENISE_KEY_SHIFT_LEFT = 0x120 => ShiftLeft,
DENISE_KEY_SHIFT_RIGHT = 0x121 => ShiftRight,
DENISE_KEY_CONTROL_LEFT = 0x122 => ControlLeft,
DENISE_KEY_CONTROL_RIGHT = 0x123 => ControlRight,
DENISE_KEY_ALT_LEFT = 0x124 => AltLeft,
DENISE_KEY_ALT_RIGHT = 0x125 => AltRight,
DENISE_KEY_SUPER_LEFT = 0x126 => SuperLeft,
DENISE_KEY_SUPER_RIGHT = 0x127 => SuperRight,
DENISE_KEY_F1 = 0x201 => F1,
DENISE_KEY_F2 = 0x202 => F2,
DENISE_KEY_F3 = 0x203 => F3,
DENISE_KEY_F4 = 0x204 => F4,
DENISE_KEY_F5 = 0x205 => F5,
DENISE_KEY_F6 = 0x206 => F6,
DENISE_KEY_F7 = 0x207 => F7,
DENISE_KEY_F8 = 0x208 => F8,
DENISE_KEY_F9 = 0x209 => F9,
DENISE_KEY_F10 = 0x20a => F10,
DENISE_KEY_F11 = 0x20b => F11,
DENISE_KEY_F12 = 0x20c => F12,
DENISE_KEY_NUMPAD_0 = 0x300 => Numpad0,
DENISE_KEY_NUMPAD_1 = 0x301 => Numpad1,
DENISE_KEY_NUMPAD_2 = 0x302 => Numpad2,
DENISE_KEY_NUMPAD_3 = 0x303 => Numpad3,
DENISE_KEY_NUMPAD_4 = 0x304 => Numpad4,
DENISE_KEY_NUMPAD_5 = 0x305 => Numpad5,
DENISE_KEY_NUMPAD_6 = 0x306 => Numpad6,
DENISE_KEY_NUMPAD_7 = 0x307 => Numpad7,
DENISE_KEY_NUMPAD_8 = 0x308 => Numpad8,
DENISE_KEY_NUMPAD_9 = 0x309 => Numpad9,
DENISE_KEY_NUMPAD_ENTER = 0x30a => NumpadEnter,
DENISE_KEY_NUMPAD_ADD = 0x30b => NumpadAdd,
DENISE_KEY_NUMPAD_SUBTRACT = 0x30c => NumpadSubtract,
DENISE_KEY_NUMPAD_MULTIPLY = 0x30d => NumpadMultiply,
DENISE_KEY_NUMPAD_DIVIDE = 0x30e => NumpadDivide,
DENISE_KEY_NUMPAD_DECIMAL = 0x30f => NumpadDecimal,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_entry_round_trips() {
for &(name, value) in TABLE {
let code = from_abi(value).unwrap_or_else(|| panic!("{name} = {value:#x} unmapped"));
assert_eq!(to_abi(code), value, "{name} did not survive the round trip");
}
}
#[test]
fn values_and_names_are_distinct() {
let mut values: Vec<u32> = TABLE.iter().map(|&(_, v)| v).collect();
values.sort_unstable();
let before = values.len();
values.dedup();
assert_eq!(values.len(), before, "two keys share a number");
let mut names: Vec<&str> = TABLE.iter().map(|&(n, _)| n).collect();
names.sort_unstable();
let before = names.len();
names.dedup();
assert_eq!(names.len(), before, "two keys share a name");
}
#[test]
fn no_named_key_collides_with_the_unidentified_flag() {
for &(name, value) in TABLE {
assert_eq!(value & UNIDENTIFIED, 0, "{name} sets the unidentified bit");
}
}
#[test]
fn an_unnamed_key_carries_its_scancode() {
let code = KeyCode::Unidentified(194);
assert_eq!(to_abi(code), UNIDENTIFIED | 194);
assert_eq!(from_abi(UNIDENTIFIED | 194), Some(code));
}
#[test]
fn a_number_that_names_nothing_is_rejected() {
assert_eq!(from_abi(0), None);
assert_eq!(from_abi(0x400), None);
}
#[test]
fn ascii_positions_are_numbered_with_their_ascii() {
assert_eq!(to_abi(KeyCode::A), u32::from(b'A'));
assert_eq!(to_abi(KeyCode::Digit7), u32::from(b'7'));
assert_eq!(to_abi(KeyCode::Semicolon), u32::from(b';'));
assert_eq!(to_abi(KeyCode::Space), u32::from(b' '));
assert_eq!(to_abi(KeyCode::F5), 0x200 + 5);
assert_eq!(to_abi(KeyCode::Numpad3), 0x300 + 3);
}
}