use std::collections::BTreeSet; use anyhow::Result; use inputd::ProducerHandle; use orbclient::{ ButtonEvent, KeyEvent, MouseRelativeEvent, ScrollEvent, K_ALT, K_ALT_GR, K_BKSP, K_BRACE_CLOSE, K_BRACE_OPEN, K_CAPS, K_COMMA, K_ENTER, K_EQUALS, K_ESC, K_LEFT_CTRL, K_LEFT_SHIFT, K_LEFT_SUPER, K_MINUS, K_PERIOD, K_QUOTE, K_RIGHT_CTRL, K_RIGHT_SHIFT, K_RIGHT_SUPER, K_SEMICOLON, K_SLASH, K_SPACE, K_TAB, K_TICK, }; use crate::hid::ReportDescriptorSummary; pub struct InputForwarder { producer: ProducerHandle, keyboard_state: BTreeSet, last_buttons: u8, } impl InputForwarder { pub fn new() -> Result { Ok(Self { producer: ProducerHandle::new()?, keyboard_state: BTreeSet::new(), last_buttons: 0, }) } pub fn forward_report( &mut self, summary: &ReportDescriptorSummary, report: &[u8], ) -> Result<()> { if report.is_empty() { return Ok(()); } if summary.has_keyboard_page && report.len() >= 8 { self.forward_boot_keyboard(report)?; return Ok(()); } if summary.has_pointer_page && report.len() >= 3 { self.forward_boot_pointer(report)?; return Ok(()); } Ok(()) } fn forward_boot_keyboard(&mut self, report: &[u8]) -> Result<()> { let modifiers = report[0]; for (bit, scancode) in [ (0_u8, K_LEFT_CTRL), (1, K_LEFT_SHIFT), (2, K_ALT), (3, K_LEFT_SUPER), (4, K_RIGHT_CTRL), (5, K_RIGHT_SHIFT), (6, K_ALT_GR), (7, K_RIGHT_SUPER), ] { self.producer.write_event( KeyEvent { character: '\0', scancode, pressed: modifiers & (1 << bit) != 0, } .to_event(), )?; } let current = report[2..8] .iter() .copied() .filter(|code| *code != 0) .collect::>(); for code in current.difference(&self.keyboard_state) { if let Some(scancode) = map_boot_keyboard_usage(*code) { self.producer.write_event( KeyEvent { character: '\0', scancode, pressed: true, } .to_event(), )?; } } for code in self.keyboard_state.difference(¤t) { if let Some(scancode) = map_boot_keyboard_usage(*code) { self.producer.write_event( KeyEvent { character: '\0', scancode, pressed: false, } .to_event(), )?; } } self.keyboard_state = current; Ok(()) } fn forward_boot_pointer(&mut self, report: &[u8]) -> Result<()> { let dx = i8::from_ne_bytes([report[1]]) as i32; let dy = i8::from_ne_bytes([report[2]]) as i32; if dx != 0 || dy != 0 { self.producer .write_event(MouseRelativeEvent { dx, dy }.to_event())?; } if let Some(scroll) = report.get(3).copied() { let scroll = i8::from_ne_bytes([scroll]) as i32; if scroll != 0 { self.producer .write_event(ScrollEvent { x: 0, y: scroll }.to_event())?; } } let buttons = report[0] & 0x07; for index in 0..3 { let mask = 1 << index; if (buttons & mask) != (self.last_buttons & mask) { self.producer.write_event( ButtonEvent { left: buttons & 0x01 != 0, middle: buttons & 0x04 != 0, right: buttons & 0x02 != 0, } .to_event(), )?; break; } } self.last_buttons = buttons; Ok(()) } } fn map_boot_keyboard_usage(usage: u8) -> Option { Some(match usage { 0x04..=0x1D => b'a' + (usage - 0x04), 0x1E => b'1', 0x1F => b'2', 0x20 => b'3', 0x21 => b'4', 0x22 => b'5', 0x23 => b'6', 0x24 => b'7', 0x25 => b'8', 0x26 => b'9', 0x27 => b'0', 0x28 => K_ENTER, 0x29 => K_ESC, 0x2A => K_BKSP, 0x2B => K_TAB, 0x2C => K_SPACE, 0x2D => K_MINUS, 0x2E => K_EQUALS, 0x2F => K_BRACE_OPEN, 0x30 => K_BRACE_CLOSE, 0x33 => K_SEMICOLON, 0x34 => K_QUOTE, 0x35 => K_TICK, 0x36 => K_COMMA, 0x37 => K_PERIOD, 0x38 => K_SLASH, 0x39 => K_CAPS, _ => return None, }) }