use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use std::time::Duration;
use inset_embedder::{KeyData, KeyEventDeviceType, KeyEventType};
use inset_foundation::{App, Handle};
use crate::keyboard_key::{LogicalKeyboardKey, PhysicalKeyboardKey};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum KeyboardLockMode {
NumLock,
ScrollLock,
CapsLock,
}
impl KeyboardLockMode {
pub fn logical_key(&self) -> LogicalKeyboardKey {
match self {
KeyboardLockMode::NumLock => LogicalKeyboardKey::NUM_LOCK,
KeyboardLockMode::ScrollLock => LogicalKeyboardKey::SCROLL_LOCK,
KeyboardLockMode::CapsLock => LogicalKeyboardKey::CAPS_LOCK,
}
}
pub fn find_lock_by_logical_key(logical_key: LogicalKeyboardKey) -> Option<KeyboardLockMode> {
KNOWN_LOCK_MODES
.iter()
.copied()
.find(|lock_mode| lock_mode.logical_key() == logical_key)
}
}
static KNOWN_LOCK_MODES: &[KeyboardLockMode] = &[
KeyboardLockMode::NumLock,
KeyboardLockMode::ScrollLock,
KeyboardLockMode::CapsLock,
];
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct KeyDownEvent {
pub physical_key: PhysicalKeyboardKey,
pub logical_key: LogicalKeyboardKey,
pub character: Option<String>,
pub time_stamp: Duration,
pub device_type: KeyEventDeviceType,
pub synthesized: bool,
}
impl KeyDownEvent {
pub fn new(
physical_key: PhysicalKeyboardKey,
logical_key: LogicalKeyboardKey,
time_stamp: Duration,
) -> KeyDownEvent {
KeyDownEvent {
physical_key,
logical_key,
character: None,
time_stamp,
device_type: KeyEventDeviceType::Keyboard,
synthesized: false,
}
}
pub fn character(mut self, character: impl Into<String>) -> KeyDownEvent {
self.character = Some(character.into());
self
}
pub fn device_type(mut self, device_type: KeyEventDeviceType) -> KeyDownEvent {
self.device_type = device_type;
self
}
pub fn synthesized(mut self, synthesized: bool) -> KeyDownEvent {
self.synthesized = synthesized;
self
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct KeyUpEvent {
pub physical_key: PhysicalKeyboardKey,
pub logical_key: LogicalKeyboardKey,
pub time_stamp: Duration,
pub device_type: KeyEventDeviceType,
pub synthesized: bool,
}
impl KeyUpEvent {
pub fn new(
physical_key: PhysicalKeyboardKey,
logical_key: LogicalKeyboardKey,
time_stamp: Duration,
) -> KeyUpEvent {
KeyUpEvent {
physical_key,
logical_key,
time_stamp,
device_type: KeyEventDeviceType::Keyboard,
synthesized: false,
}
}
pub fn device_type(mut self, device_type: KeyEventDeviceType) -> KeyUpEvent {
self.device_type = device_type;
self
}
pub fn synthesized(mut self, synthesized: bool) -> KeyUpEvent {
self.synthesized = synthesized;
self
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct KeyRepeatEvent {
pub physical_key: PhysicalKeyboardKey,
pub logical_key: LogicalKeyboardKey,
pub character: Option<String>,
pub time_stamp: Duration,
pub device_type: KeyEventDeviceType,
}
impl KeyRepeatEvent {
pub fn new(
physical_key: PhysicalKeyboardKey,
logical_key: LogicalKeyboardKey,
time_stamp: Duration,
) -> KeyRepeatEvent {
KeyRepeatEvent {
physical_key,
logical_key,
character: None,
time_stamp,
device_type: KeyEventDeviceType::Keyboard,
}
}
pub fn character(mut self, character: impl Into<String>) -> KeyRepeatEvent {
self.character = Some(character.into());
self
}
pub fn device_type(mut self, device_type: KeyEventDeviceType) -> KeyRepeatEvent {
self.device_type = device_type;
self
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum KeyEvent {
Down(KeyDownEvent),
Up(KeyUpEvent),
Repeat(KeyRepeatEvent),
}
impl KeyEvent {
pub fn physical_key(&self) -> PhysicalKeyboardKey {
match self {
KeyEvent::Down(event) => event.physical_key,
KeyEvent::Up(event) => event.physical_key,
KeyEvent::Repeat(event) => event.physical_key,
}
}
pub fn logical_key(&self) -> LogicalKeyboardKey {
match self {
KeyEvent::Down(event) => event.logical_key,
KeyEvent::Up(event) => event.logical_key,
KeyEvent::Repeat(event) => event.logical_key,
}
}
pub fn character(&self) -> Option<&str> {
match self {
KeyEvent::Down(event) => event.character.as_deref(),
KeyEvent::Up(_) => None,
KeyEvent::Repeat(event) => event.character.as_deref(),
}
}
pub fn time_stamp(&self) -> Duration {
match self {
KeyEvent::Down(event) => event.time_stamp,
KeyEvent::Up(event) => event.time_stamp,
KeyEvent::Repeat(event) => event.time_stamp,
}
}
pub fn device_type(&self) -> KeyEventDeviceType {
match self {
KeyEvent::Down(event) => event.device_type,
KeyEvent::Up(event) => event.device_type,
KeyEvent::Repeat(event) => event.device_type,
}
}
pub fn synthesized(&self) -> bool {
match self {
KeyEvent::Down(event) => event.synthesized,
KeyEvent::Up(event) => event.synthesized,
KeyEvent::Repeat(_) => false,
}
}
pub fn to_key_data(&self) -> KeyData {
KeyData {
time_stamp: self.time_stamp(),
event_type: match self {
KeyEvent::Down(_) => KeyEventType::Down,
KeyEvent::Up(_) => KeyEventType::Up,
KeyEvent::Repeat(_) => KeyEventType::Repeat,
},
device_type: self.device_type(),
physical: self.physical_key().usb_hid_usage,
logical: self.logical_key().key_id,
character: self.character().map(str::to_owned),
synthesized: self.synthesized(),
}
}
}
impl From<KeyDownEvent> for KeyEvent {
fn from(event: KeyDownEvent) -> KeyEvent {
KeyEvent::Down(event)
}
}
impl From<KeyUpEvent> for KeyEvent {
fn from(event: KeyUpEvent) -> KeyEvent {
KeyEvent::Up(event)
}
}
impl From<KeyRepeatEvent> for KeyEvent {
fn from(event: KeyRepeatEvent) -> KeyEvent {
KeyEvent::Repeat(event)
}
}
pub type KeyEventCallback = Rc<dyn Fn(&mut App, &KeyEvent) -> bool>;
#[derive(Default)]
pub struct HardwareKeyboard {
pressed_keys: HashMap<PhysicalKeyboardKey, LogicalKeyboardKey>,
lock_modes: HashSet<KeyboardLockMode>,
handlers: Vec<KeyEventCallback>,
during_dispatch: bool,
modified_handlers: Option<Vec<KeyEventCallback>>,
}
impl HardwareKeyboard {
pub fn instance(app: &mut App) -> Handle<HardwareKeyboard> {
app.singleton::<HardwareKeyboard>()
}
pub fn physical_keys_pressed(self: Handle<Self>, app: &App) -> HashSet<PhysicalKeyboardKey> {
app.get(self).pressed_keys.keys().copied().collect()
}
pub fn logical_keys_pressed(self: Handle<Self>, app: &App) -> HashSet<LogicalKeyboardKey> {
app.get(self).pressed_keys.values().copied().collect()
}
pub fn look_up_layout(
self: Handle<Self>,
app: &App,
physical_key: PhysicalKeyboardKey,
) -> Option<LogicalKeyboardKey> {
app.get(self).pressed_keys.get(&physical_key).copied()
}
pub fn lock_modes_enabled(self: Handle<Self>, app: &App) -> HashSet<KeyboardLockMode> {
app.get(self).lock_modes.clone()
}
pub fn is_logical_key_pressed(self: Handle<Self>, app: &App, key: LogicalKeyboardKey) -> bool {
app.get(self)
.pressed_keys
.values()
.any(|&pressed| pressed == key)
}
pub fn is_physical_key_pressed(
self: Handle<Self>,
app: &App,
key: PhysicalKeyboardKey,
) -> bool {
app.get(self).pressed_keys.contains_key(&key)
}
pub fn is_control_pressed(self: Handle<Self>, app: &App) -> bool {
self.is_logical_key_pressed(app, LogicalKeyboardKey::CONTROL_LEFT)
|| self.is_logical_key_pressed(app, LogicalKeyboardKey::CONTROL_RIGHT)
}
pub fn is_shift_pressed(self: Handle<Self>, app: &App) -> bool {
self.is_logical_key_pressed(app, LogicalKeyboardKey::SHIFT_LEFT)
|| self.is_logical_key_pressed(app, LogicalKeyboardKey::SHIFT_RIGHT)
}
pub fn is_alt_pressed(self: Handle<Self>, app: &App) -> bool {
self.is_logical_key_pressed(app, LogicalKeyboardKey::ALT_LEFT)
|| self.is_logical_key_pressed(app, LogicalKeyboardKey::ALT_RIGHT)
}
pub fn is_meta_pressed(self: Handle<Self>, app: &App) -> bool {
self.is_logical_key_pressed(app, LogicalKeyboardKey::META_LEFT)
|| self.is_logical_key_pressed(app, LogicalKeyboardKey::META_RIGHT)
}
pub fn add_handler(self: Handle<Self>, app: &mut App, handler: KeyEventCallback) {
let keyboard = app.get_mut(self);
if keyboard.during_dispatch {
keyboard
.modified_handlers
.get_or_insert_with(|| keyboard.handlers.clone())
.push(handler);
} else {
keyboard.handlers.push(handler);
}
}
pub fn remove_handler(self: Handle<Self>, app: &mut App, handler: &KeyEventCallback) {
let keyboard = app.get_mut(self);
let handlers = if keyboard.during_dispatch {
keyboard
.modified_handlers
.get_or_insert_with(|| keyboard.handlers.clone())
} else {
&mut keyboard.handlers
};
if let Some(index) = handlers
.iter()
.position(|candidate| Rc::ptr_eq(candidate, handler))
{
handlers.remove(index);
}
}
pub fn sync_keyboard_state(
self: Handle<Self>,
app: &mut App,
keyboard_state: &HashMap<u64, u64>,
) {
let keyboard = app.get_mut(self);
for (&physical, &logical) in keyboard_state {
keyboard.pressed_keys.insert(
PhysicalKeyboardKey::new(physical),
LogicalKeyboardKey::new(logical),
);
}
}
fn dispatch_key_event(self: Handle<Self>, app: &mut App, event: &KeyEvent) -> bool {
debug_assert!(
!app.get(self).during_dispatch,
"Nested keyboard dispatching is not supported"
);
app.get_mut(self).during_dispatch = true;
let mut handled = false;
let handlers = app.get(self).handlers.clone();
for handler in handlers {
let this_result = handler(app, event);
handled = handled || this_result;
}
let keyboard = app.get_mut(self);
keyboard.during_dispatch = false;
if let Some(modified_handlers) = keyboard.modified_handlers.take() {
keyboard.handlers = modified_handlers;
}
handled
}
pub fn handle_key_event(self: Handle<Self>, app: &mut App, event: &KeyEvent) -> bool {
let physical_key = event.physical_key();
let logical_key = event.logical_key();
match event {
KeyEvent::Down(_) => {
app.get_mut(self)
.pressed_keys
.insert(physical_key, logical_key);
if let Some(lock_mode) = KeyboardLockMode::find_lock_by_logical_key(logical_key) {
let lock_modes = &mut app.get_mut(self).lock_modes;
if lock_modes.contains(&lock_mode) {
lock_modes.remove(&lock_mode);
} else {
lock_modes.insert(lock_mode);
}
}
}
KeyEvent::Up(_) => {
app.get_mut(self).pressed_keys.remove(&physical_key);
}
KeyEvent::Repeat(_) => {
app.get_mut(self)
.pressed_keys
.insert(physical_key, logical_key);
}
}
self.dispatch_key_event(app, event)
}
pub fn clear_state(self: Handle<Self>, app: &mut App) {
let keyboard = app.get_mut(self);
keyboard.pressed_keys.clear();
keyboard.lock_modes.clear();
keyboard.handlers.clear();
debug_assert!(keyboard.modified_handlers.is_none());
}
}
#[derive(Default)]
pub struct KeyEventManager {
hardware_keyboard: Option<Handle<HardwareKeyboard>>,
}
impl KeyEventManager {
pub fn instance(app: &mut App) -> Handle<KeyEventManager> {
let this: Handle<KeyEventManager> = app.singleton();
if app.get(this).hardware_keyboard.is_none() {
let hardware_keyboard = HardwareKeyboard::instance(app);
app.get_mut(this).hardware_keyboard = Some(hardware_keyboard);
}
this
}
pub fn hardware_keyboard(self: Handle<Self>, app: &App) -> Handle<HardwareKeyboard> {
app.get(self)
.hardware_keyboard
.expect("KeyEventManager::instance must run first")
}
pub fn handle_key_data(self: Handle<Self>, app: &mut App, data: KeyData) -> bool {
if data.physical == 0 && data.logical == 0 {
return false;
}
debug_assert!(data.physical != 0 && data.logical != 0);
let event = KeyEventManager::event_from_data(data);
self.hardware_keyboard(app).handle_key_event(app, &event)
}
fn event_from_data(key_data: KeyData) -> KeyEvent {
let physical_key = PhysicalKeyboardKey::find_key_by_code(key_data.physical)
.unwrap_or(PhysicalKeyboardKey::new(key_data.physical));
let logical_key = LogicalKeyboardKey::find_key_by_key_id(key_data.logical)
.unwrap_or(LogicalKeyboardKey::new(key_data.logical));
let time_stamp = key_data.time_stamp;
match key_data.event_type {
KeyEventType::Down => {
let mut event = KeyDownEvent::new(physical_key, logical_key, time_stamp)
.synthesized(key_data.synthesized)
.device_type(key_data.device_type);
if let Some(character) = key_data.character {
event = event.character(character);
}
KeyEvent::Down(event)
}
KeyEventType::Up => {
debug_assert!(key_data.character.is_none());
KeyEvent::Up(
KeyUpEvent::new(physical_key, logical_key, time_stamp)
.synthesized(key_data.synthesized)
.device_type(key_data.device_type),
)
}
KeyEventType::Repeat => {
let mut event = KeyRepeatEvent::new(physical_key, logical_key, time_stamp)
.device_type(key_data.device_type);
if let Some(character) = key_data.character {
event = event.character(character);
}
KeyEvent::Repeat(event)
}
}
}
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use std::time::Duration;
use inset_embedder::{KeyData, KeyEventType};
use inset_foundation::{App, AppCell};
use super::{
HardwareKeyboard, KeyDownEvent, KeyEvent, KeyEventCallback, KeyEventManager,
KeyRepeatEvent, KeyUpEvent, KeyboardLockMode,
};
use crate::keyboard_key::{LogicalKeyboardKey, PhysicalKeyboardKey};
fn down(physical: PhysicalKeyboardKey, logical: LogicalKeyboardKey) -> KeyEvent {
KeyEvent::Down(KeyDownEvent::new(physical, logical, Duration::ZERO))
}
fn up(physical: PhysicalKeyboardKey, logical: LogicalKeyboardKey) -> KeyEvent {
KeyEvent::Up(KeyUpEvent::new(physical, logical, Duration::ZERO))
}
#[test]
fn an_event_gives_back_the_key_data_it_was_made_from() {
let data = KeyData {
time_stamp: Duration::from_millis(1234),
event_type: KeyEventType::Repeat,
physical: PhysicalKeyboardKey::KEY_O.usb_hid_usage,
logical: LogicalKeyboardKey::KEY_O.key_id,
character: Some("o".to_owned()),
..KeyData::default()
};
assert_eq!(
KeyEventManager::event_from_data(data.clone()).to_key_data(),
data
);
}
#[test]
fn a_down_up_pair_updates_the_pressed_key_sets() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let keyboard = HardwareKeyboard::instance(&mut app);
keyboard.handle_key_event(
&mut app,
&down(PhysicalKeyboardKey::KEY_A, LogicalKeyboardKey::KEY_A),
);
assert_eq!(
keyboard.physical_keys_pressed(&app),
HashSet::from([PhysicalKeyboardKey::KEY_A])
);
assert_eq!(
keyboard.logical_keys_pressed(&app),
HashSet::from([LogicalKeyboardKey::KEY_A])
);
assert!(keyboard.is_physical_key_pressed(&app, PhysicalKeyboardKey::KEY_A));
assert!(keyboard.is_logical_key_pressed(&app, LogicalKeyboardKey::KEY_A));
assert_eq!(
keyboard.look_up_layout(&app, PhysicalKeyboardKey::KEY_A),
Some(LogicalKeyboardKey::KEY_A)
);
keyboard.handle_key_event(
&mut app,
&up(PhysicalKeyboardKey::KEY_A, LogicalKeyboardKey::KEY_A),
);
assert!(keyboard.physical_keys_pressed(&app).is_empty());
assert!(keyboard.logical_keys_pressed(&app).is_empty());
}
#[test]
fn a_repeat_event_keeps_the_key_pressed_and_updates_the_logical_key() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let keyboard = HardwareKeyboard::instance(&mut app);
keyboard.handle_key_event(
&mut app,
&down(PhysicalKeyboardKey::KEY_A, LogicalKeyboardKey::KEY_A),
);
keyboard.handle_key_event(
&mut app,
&KeyEvent::Repeat(
KeyRepeatEvent::new(
PhysicalKeyboardKey::KEY_A,
LogicalKeyboardKey::KEY_Q,
Duration::ZERO,
)
.character("q"),
),
);
assert_eq!(
keyboard.physical_keys_pressed(&app),
HashSet::from([PhysicalKeyboardKey::KEY_A])
);
assert_eq!(
keyboard.look_up_layout(&app, PhysicalKeyboardKey::KEY_A),
Some(LogicalKeyboardKey::KEY_Q)
);
}
#[test]
fn is_shift_pressed_answers_for_either_side() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let keyboard = HardwareKeyboard::instance(&mut app);
assert!(!keyboard.is_shift_pressed(&app));
keyboard.handle_key_event(
&mut app,
&down(
PhysicalKeyboardKey::SHIFT_RIGHT,
LogicalKeyboardKey::SHIFT_RIGHT,
),
);
assert!(keyboard.is_shift_pressed(&app));
assert!(!keyboard.is_control_pressed(&app));
keyboard.handle_key_event(
&mut app,
&up(
PhysicalKeyboardKey::SHIFT_RIGHT,
LogicalKeyboardKey::SHIFT_RIGHT,
),
);
keyboard.handle_key_event(
&mut app,
&down(
PhysicalKeyboardKey::SHIFT_LEFT,
LogicalKeyboardKey::SHIFT_LEFT,
),
);
assert!(keyboard.is_shift_pressed(&app));
}
#[test]
fn a_lock_key_toggles_its_mode_on_every_down() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let keyboard = HardwareKeyboard::instance(&mut app);
let caps_lock = down(
PhysicalKeyboardKey::CAPS_LOCK,
LogicalKeyboardKey::CAPS_LOCK,
);
keyboard.handle_key_event(&mut app, &caps_lock);
assert_eq!(
keyboard.lock_modes_enabled(&app),
HashSet::from([KeyboardLockMode::CapsLock])
);
keyboard.handle_key_event(
&mut app,
&up(
PhysicalKeyboardKey::CAPS_LOCK,
LogicalKeyboardKey::CAPS_LOCK,
),
);
assert_eq!(
keyboard.lock_modes_enabled(&app),
HashSet::from([KeyboardLockMode::CapsLock])
);
keyboard.handle_key_event(&mut app, &caps_lock);
assert!(keyboard.lock_modes_enabled(&app).is_empty());
}
#[test]
fn handlers_receive_events_and_can_claim_them() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let keyboard = HardwareKeyboard::instance(&mut app);
let seen: Rc<RefCell<Vec<LogicalKeyboardKey>>> = Rc::new(RefCell::new(Vec::new()));
let recorded = Rc::clone(&seen);
let observer: KeyEventCallback = Rc::new(move |_app, event: &KeyEvent| {
recorded.borrow_mut().push(event.logical_key());
false
});
let claimer: KeyEventCallback = Rc::new(|_app, _event: &KeyEvent| true);
keyboard.add_handler(&mut app, Rc::clone(&observer));
assert!(!keyboard.handle_key_event(
&mut app,
&down(PhysicalKeyboardKey::KEY_A, LogicalKeyboardKey::KEY_A)
));
assert_eq!(*seen.borrow(), vec![LogicalKeyboardKey::KEY_A]);
keyboard.add_handler(&mut app, Rc::clone(&claimer));
assert!(keyboard.handle_key_event(
&mut app,
&up(PhysicalKeyboardKey::KEY_A, LogicalKeyboardKey::KEY_A)
));
assert_eq!(seen.borrow().len(), 2, "every handler runs");
keyboard.remove_handler(&mut app, &observer);
keyboard.handle_key_event(
&mut app,
&down(PhysicalKeyboardKey::KEY_B, LogicalKeyboardKey::KEY_B),
);
assert_eq!(seen.borrow().len(), 2, "a removed handler is not called");
keyboard.clear_state(&mut app);
assert!(keyboard.physical_keys_pressed(&app).is_empty());
assert!(!keyboard.handle_key_event(
&mut app,
&down(PhysicalKeyboardKey::KEY_C, LogicalKeyboardKey::KEY_C)
));
}
#[test]
fn a_handler_added_during_dispatch_takes_effect_after_it() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let keyboard = HardwareKeyboard::instance(&mut app);
let calls = Rc::new(RefCell::new(0));
let counted = Rc::clone(&calls);
let late: KeyEventCallback = Rc::new(move |_app, _event: &KeyEvent| {
*counted.borrow_mut() += 1;
false
});
let adder: KeyEventCallback = Rc::new(move |app: &mut App, _event: &KeyEvent| {
let keyboard = HardwareKeyboard::instance(app);
keyboard.add_handler(app, Rc::clone(&late));
false
});
keyboard.add_handler(&mut app, adder);
keyboard.handle_key_event(
&mut app,
&down(PhysicalKeyboardKey::KEY_A, LogicalKeyboardKey::KEY_A),
);
assert_eq!(*calls.borrow(), 0, "the addition waits for the dispatch");
keyboard.handle_key_event(
&mut app,
&up(PhysicalKeyboardKey::KEY_A, LogicalKeyboardKey::KEY_A),
);
assert_eq!(*calls.borrow(), 1);
}
#[test]
fn key_data_becomes_an_event_the_keyboard_records() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let manager = KeyEventManager::instance(&mut app);
let keyboard = manager.hardware_keyboard(&app);
assert!(!manager.handle_key_data(
&mut app,
KeyData {
physical: 0,
logical: 0,
..KeyData::default()
}
));
assert!(keyboard.physical_keys_pressed(&app).is_empty());
manager.handle_key_data(
&mut app,
KeyData {
time_stamp: Duration::from_millis(3),
event_type: KeyEventType::Down,
physical: PhysicalKeyboardKey::KEY_A.usb_hid_usage,
logical: LogicalKeyboardKey::KEY_A.key_id,
character: Some("a".to_owned()),
..KeyData::default()
},
);
assert_eq!(
keyboard.physical_keys_pressed(&app),
HashSet::from([PhysicalKeyboardKey::KEY_A])
);
manager.handle_key_data(
&mut app,
KeyData {
time_stamp: Duration::from_millis(4),
event_type: KeyEventType::Up,
physical: PhysicalKeyboardKey::KEY_A.usb_hid_usage,
logical: LogicalKeyboardKey::KEY_A.key_id,
..KeyData::default()
},
);
assert!(keyboard.physical_keys_pressed(&app).is_empty());
}
#[test]
fn sync_keyboard_state_records_what_the_host_reports() {
let cell = AppCell::new();
let mut app = cell.borrow_mut();
let keyboard = HardwareKeyboard::instance(&mut app);
keyboard.sync_keyboard_state(
&mut app,
&HashMap::from([(
PhysicalKeyboardKey::SHIFT_LEFT.usb_hid_usage,
LogicalKeyboardKey::SHIFT_LEFT.key_id,
)]),
);
assert!(keyboard.is_shift_pressed(&app));
}
}