lesavka/common/src/hid.rs

81 lines
2.9 KiB
Rust
Raw Normal View History

//! Shared HID mapping helpers used by both the client and server crates.
/// Map a printable character to a USB HID usage plus modifier byte.
///
/// Inputs: a Unicode scalar value that should be typed through the HID gadget.
/// Outputs: `Some((usage, modifiers))` for supported ASCII characters, or
/// `None` when the character cannot be represented by the current keyboard map.
/// Why: server-side paste injection and client-side keymap tests must agree on
/// the exact HID encoding so they do not drift apart over time.
#[must_use]
pub fn char_to_usage(c: char) -> Option<(u8, u8)> {
let shift = 0x02; // left shift in HID modifier byte
match c {
'a'..='z' => Some((0x04 + (c as u8 - b'a'), 0)),
'A'..='Z' => Some((0x04 + (c as u8 - b'A'), shift)),
'1'..='9' => Some((0x1E + (c as u8 - b'1'), 0)),
'0' => Some((0x27, 0)),
'!' => Some((0x1E, shift)),
'@' => Some((0x1F, shift)),
'#' => Some((0x20, shift)),
'$' => Some((0x21, shift)),
'%' => Some((0x22, shift)),
'^' => Some((0x23, shift)),
'&' => Some((0x24, shift)),
'*' => Some((0x25, shift)),
'(' => Some((0x26, shift)),
')' => Some((0x27, shift)),
'-' => Some((0x2D, 0)),
'_' => Some((0x2D, shift)),
'=' => Some((0x2E, 0)),
'+' => Some((0x2E, shift)),
'[' => Some((0x2F, 0)),
'{' => Some((0x2F, shift)),
']' => Some((0x30, 0)),
'}' => Some((0x30, shift)),
'\\' => Some((0x31, 0)),
'|' => Some((0x31, shift)),
';' => Some((0x33, 0)),
':' => Some((0x33, shift)),
'\'' => Some((0x34, 0)),
'"' => Some((0x34, shift)),
'`' => Some((0x35, 0)),
'~' => Some((0x35, shift)),
',' => Some((0x36, 0)),
'<' => Some((0x36, shift)),
'.' => Some((0x37, 0)),
'>' => Some((0x37, shift)),
'/' => Some((0x38, 0)),
'?' => Some((0x38, shift)),
' ' => Some((0x2C, 0)),
'\n' | '\r' => Some((0x28, 0)),
'\t' => Some((0x2B, 0)),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::char_to_usage;
#[test]
fn char_to_usage_maps_letters_numbers_and_shifted_symbols() {
assert_eq!(char_to_usage('a'), Some((0x04, 0)));
assert_eq!(char_to_usage('Z'), Some((0x1D, 0x02)));
assert_eq!(char_to_usage('0'), Some((0x27, 0)));
assert_eq!(char_to_usage('9'), Some((0x26, 0)));
assert_eq!(char_to_usage(' '), Some((0x2C, 0)));
assert_eq!(char_to_usage('\n'), Some((0x28, 0)));
assert_eq!(char_to_usage('\t'), Some((0x2B, 0)));
assert_eq!(char_to_usage('{'), Some((0x2F, 0x02)));
assert_eq!(char_to_usage('~'), Some((0x35, 0x02)));
assert_eq!(char_to_usage('?'), Some((0x38, 0x02)));
}
#[test]
fn char_to_usage_rejects_unsupported_chars() {
assert_eq!(char_to_usage('é'), None);
assert_eq!(char_to_usage('\u{2603}'), None);
}
}