diff --git a/drivers/input/usbhidd/src/main.rs b/drivers/input/usbhidd/src/main.rs index 15c5b778..472ec4cf 100644 --- a/drivers/input/usbhidd/src/main.rs +++ b/drivers/input/usbhidd/src/main.rs @@ -1,7 +1,7 @@ -use anyhow::{Context, Result}; +use anyhow::{bail, ensure, Context, Result}; use std::{env, thread, time}; -use inputd::ProducerHandle; +use inputd::InputProducer; use orbclient::KeyEvent as OrbKeyEvent; use rehid::{ report_desc::{ReportTy, REPORT_DESC_TY}, @@ -15,161 +15,219 @@ use xhcid_interface::{ mod reqs; -fn send_key_event(display: &mut ProducerHandle, usage_page: u16, usage: u16, pressed: bool) { - let scancode = match usage_page { - 0x07 => match usage { - 0x04 => orbclient::K_A, - 0x05 => orbclient::K_B, - 0x06 => orbclient::K_C, - 0x07 => orbclient::K_D, - 0x08 => orbclient::K_E, - 0x09 => orbclient::K_F, - 0x0A => orbclient::K_G, - 0x0B => orbclient::K_H, - 0x0C => orbclient::K_I, - 0x0D => orbclient::K_J, - 0x0E => orbclient::K_K, - 0x0F => orbclient::K_L, - 0x10 => orbclient::K_M, - 0x11 => orbclient::K_N, - 0x12 => orbclient::K_O, - 0x13 => orbclient::K_P, - 0x14 => orbclient::K_Q, - 0x15 => orbclient::K_R, - 0x16 => orbclient::K_S, - 0x17 => orbclient::K_T, - 0x18 => orbclient::K_U, - 0x19 => orbclient::K_V, - 0x1A => orbclient::K_W, - 0x1B => orbclient::K_X, - 0x1C => orbclient::K_Y, - 0x1D => orbclient::K_Z, - 0x1E => orbclient::K_1, - 0x1F => orbclient::K_2, - 0x20 => orbclient::K_3, - 0x21 => orbclient::K_4, - 0x22 => orbclient::K_5, - 0x23 => orbclient::K_6, - 0x24 => orbclient::K_7, - 0x25 => orbclient::K_8, - 0x26 => orbclient::K_9, - 0x27 => orbclient::K_0, - 0x28 => orbclient::K_ENTER, - 0x29 => orbclient::K_ESC, - 0x2A => orbclient::K_BKSP, - 0x2B => orbclient::K_TAB, - 0x2C => orbclient::K_SPACE, - 0x2D => orbclient::K_MINUS, - 0x2E => orbclient::K_EQUALS, - 0x2F => orbclient::K_BRACE_OPEN, - 0x30 => orbclient::K_BRACE_CLOSE, - 0x31 => orbclient::K_BACKSLASH, - // 0x32 non-us # and ~ - 0x32 => 0x56, - 0x33 => orbclient::K_SEMICOLON, - 0x34 => orbclient::K_QUOTE, - 0x35 => orbclient::K_TICK, - 0x36 => orbclient::K_COMMA, - 0x37 => orbclient::K_PERIOD, - 0x38 => orbclient::K_SLASH, - 0x39 => orbclient::K_CAPS, - 0x3A => orbclient::K_F1, - 0x3B => orbclient::K_F2, - 0x3C => orbclient::K_F3, - 0x3D => orbclient::K_F4, - 0x3E => orbclient::K_F5, - 0x3F => orbclient::K_F6, - 0x40 => orbclient::K_F7, - 0x41 => orbclient::K_F8, - 0x42 => orbclient::K_F9, - 0x43 => orbclient::K_F10, - 0x44 => orbclient::K_F11, - 0x45 => orbclient::K_F12, - 0x46 => orbclient::K_PRTSC, - 0x47 => orbclient::K_SCROLL, - // 0x48 pause - 0x49 => orbclient::K_INS, - 0x4A => orbclient::K_HOME, - 0x4B => orbclient::K_PGUP, - 0x4C => orbclient::K_DEL, - 0x4D => orbclient::K_END, - 0x4E => orbclient::K_PGDN, - 0x4F => orbclient::K_RIGHT, - 0x50 => orbclient::K_LEFT, - 0x51 => orbclient::K_DOWN, - 0x52 => orbclient::K_UP, - 0x53 => orbclient::K_NUM, - 0x54 => orbclient::K_NUM_SLASH, - 0x55 => orbclient::K_NUM_ASTERISK, - 0x56 => orbclient::K_NUM_MINUS, - 0x57 => orbclient::K_NUM_PLUS, - 0x58 => orbclient::K_NUM_ENTER, - 0x59 => orbclient::K_NUM_1, - 0x5A => orbclient::K_NUM_2, - 0x5B => orbclient::K_NUM_3, - 0x5C => orbclient::K_NUM_4, - 0x5D => orbclient::K_NUM_5, - 0x5E => orbclient::K_NUM_6, - 0x5F => orbclient::K_NUM_7, - 0x60 => orbclient::K_NUM_8, - 0x61 => orbclient::K_NUM_9, - 0x62 => orbclient::K_NUM_0, - // 0x62 num . - // 0x64 non-us \ and | - 0x64 => orbclient::K_APP, - 0x66 => orbclient::K_POWER, - // 0x67 num = - // unmapped values - 0xE0 => orbclient::K_LEFT_CTRL, - 0xE1 => orbclient::K_LEFT_SHIFT, - 0xE2 => orbclient::K_ALT, - 0xE3 => orbclient::K_LEFT_SUPER, - 0xE4 => orbclient::K_RIGHT_CTRL, - 0xE5 => orbclient::K_RIGHT_SHIFT, - 0xE6 => orbclient::K_ALT_GR, - 0xE7 => orbclient::K_RIGHT_SUPER, - // reserved values - _ => { - log::warn!("unknown usage_page {:#x} usage {:#x}", usage_page, usage); - return; - } - }, - _ => { - log::warn!("unknown usage_page {:#x}", usage_page); +const MAX_MOUSE_BUTTONS: usize = 8; +const MAX_RETRIES: u32 = 3; +const RETRY_BASE_DELAY_MS: u64 = 2; +const REPORT_DESC_MAX_SIZE: usize = 4096; +const REPORT_DESC_MIN_SIZE: usize = 3; + +/// USB HID Usage (Keyboard page 0x07) → orbclient scancode lookup table. +/// Covers usage codes 0x04–0xE7 per USB HID Usage Tables spec v1.12. +/// Entries default to 0 (unmapped); only defined usages are populated. +static HID_KEYBOARD_TO_SCANCODE: [u8; 0xE8] = { + let mut table = [0u8; 0xE8]; + table[0x04] = orbclient::K_A; + table[0x05] = orbclient::K_B; + table[0x06] = orbclient::K_C; + table[0x07] = orbclient::K_D; + table[0x08] = orbclient::K_E; + table[0x09] = orbclient::K_F; + table[0x0A] = orbclient::K_G; + table[0x0B] = orbclient::K_H; + table[0x0C] = orbclient::K_I; + table[0x0D] = orbclient::K_J; + table[0x0E] = orbclient::K_K; + table[0x0F] = orbclient::K_L; + table[0x10] = orbclient::K_M; + table[0x11] = orbclient::K_N; + table[0x12] = orbclient::K_O; + table[0x13] = orbclient::K_P; + table[0x14] = orbclient::K_Q; + table[0x15] = orbclient::K_R; + table[0x16] = orbclient::K_S; + table[0x17] = orbclient::K_T; + table[0x18] = orbclient::K_U; + table[0x19] = orbclient::K_V; + table[0x1A] = orbclient::K_W; + table[0x1B] = orbclient::K_X; + table[0x1C] = orbclient::K_Y; + table[0x1D] = orbclient::K_Z; + table[0x1E] = orbclient::K_1; + table[0x1F] = orbclient::K_2; + table[0x20] = orbclient::K_3; + table[0x21] = orbclient::K_4; + table[0x22] = orbclient::K_5; + table[0x23] = orbclient::K_6; + table[0x24] = orbclient::K_7; + table[0x25] = orbclient::K_8; + table[0x26] = orbclient::K_9; + table[0x27] = orbclient::K_0; + table[0x28] = orbclient::K_ENTER; + table[0x29] = orbclient::K_ESC; + table[0x2A] = orbclient::K_BKSP; + table[0x2B] = orbclient::K_TAB; + table[0x2C] = orbclient::K_SPACE; + table[0x2D] = orbclient::K_MINUS; + table[0x2E] = orbclient::K_EQUALS; + table[0x2F] = orbclient::K_BRACE_OPEN; + table[0x30] = orbclient::K_BRACE_CLOSE; + table[0x31] = orbclient::K_BACKSLASH; + table[0x32] = 0x56; + table[0x33] = orbclient::K_SEMICOLON; + table[0x34] = orbclient::K_QUOTE; + table[0x35] = orbclient::K_TICK; + table[0x36] = orbclient::K_COMMA; + table[0x37] = orbclient::K_PERIOD; + table[0x38] = orbclient::K_SLASH; + table[0x39] = orbclient::K_CAPS; + table[0x3A] = orbclient::K_F1; + table[0x3B] = orbclient::K_F2; + table[0x3C] = orbclient::K_F3; + table[0x3D] = orbclient::K_F4; + table[0x3E] = orbclient::K_F5; + table[0x3F] = orbclient::K_F6; + table[0x40] = orbclient::K_F7; + table[0x41] = orbclient::K_F8; + table[0x42] = orbclient::K_F9; + table[0x43] = orbclient::K_F10; + table[0x44] = orbclient::K_F11; + table[0x45] = orbclient::K_F12; + table[0x46] = orbclient::K_PRTSC; + table[0x47] = orbclient::K_SCROLL; + table[0x49] = orbclient::K_INS; + table[0x4A] = orbclient::K_HOME; + table[0x4B] = orbclient::K_PGUP; + table[0x4C] = orbclient::K_DEL; + table[0x4D] = orbclient::K_END; + table[0x4E] = orbclient::K_PGDN; + table[0x4F] = orbclient::K_RIGHT; + table[0x50] = orbclient::K_LEFT; + table[0x51] = orbclient::K_DOWN; + table[0x52] = orbclient::K_UP; + table[0x53] = orbclient::K_NUM; + table[0x54] = orbclient::K_NUM_SLASH; + table[0x55] = orbclient::K_NUM_ASTERISK; + table[0x56] = orbclient::K_NUM_MINUS; + table[0x57] = orbclient::K_NUM_PLUS; + table[0x58] = orbclient::K_NUM_ENTER; + table[0x59] = orbclient::K_NUM_1; + table[0x5A] = orbclient::K_NUM_2; + table[0x5B] = orbclient::K_NUM_3; + table[0x5C] = orbclient::K_NUM_4; + table[0x5D] = orbclient::K_NUM_5; + table[0x5E] = orbclient::K_NUM_6; + table[0x5F] = orbclient::K_NUM_7; + table[0x60] = orbclient::K_NUM_8; + table[0x61] = orbclient::K_NUM_9; + table[0x62] = orbclient::K_NUM_0; + table[0x64] = orbclient::K_APP; + table[0x66] = orbclient::K_POWER; + table[0xE0] = orbclient::K_LEFT_CTRL; + table[0xE1] = orbclient::K_LEFT_SHIFT; + table[0xE2] = orbclient::K_ALT; + table[0xE3] = orbclient::K_LEFT_SUPER; + table[0xE4] = orbclient::K_RIGHT_CTRL; + table[0xE5] = orbclient::K_RIGHT_SHIFT; + table[0xE6] = orbclient::K_ALT_GR; + table[0xE7] = orbclient::K_RIGHT_SUPER; + table +}; + +fn hid_usage_to_scancode(usage: u16) -> Option { + let idx = usage as usize; + if idx < HID_KEYBOARD_TO_SCANCODE.len() { + let sc = HID_KEYBOARD_TO_SCANCODE[idx]; + if sc != 0 { + return Some(sc); + } + } + None +} + +fn send_key_event(display: &mut InputProducer, usage_page: u16, usage: u16, pressed: bool) { + if usage_page != 0x07 { + log::warn!("send_key_event: unexpected usage_page {:#x}", usage_page); + return; + } + let scancode = match hid_usage_to_scancode(usage) { + Some(sc) => sc, + None => { + log::warn!("unmapped HID keyboard usage {:#x}", usage); return; } }; - let key_event = OrbKeyEvent { character: '\0', scancode, pressed, }; + if let Err(err) = display.write_event(key_event.to_event()) { + log::warn!("failed to send key event: {}", err); + } +} - match display.write_event(key_event.to_event()) { - Ok(_) => (), - Err(err) => { - log::warn!("failed to send key event to orbital: {}", err); +fn validate_report_descriptor(bytes: &[u8]) -> Result<()> { + ensure!( + bytes.len() >= REPORT_DESC_MIN_SIZE, + "report descriptor too short: {} bytes (minimum {})", + bytes.len(), + REPORT_DESC_MIN_SIZE + ); + ensure!( + bytes.len() <= REPORT_DESC_MAX_SIZE, + "report descriptor too large: {} bytes (maximum {})", + bytes.len(), + REPORT_DESC_MAX_SIZE + ); + let mut depth = 0u32; + let mut i = 0; + while i < bytes.len() { + let b = bytes[i]; + let size = match b & 0x03 { + 0 => 0, + 1 => 1, + 2 => 2, + 3 => 4, + _ => unreachable!(), + }; + let item_type = (b >> 2) & 0x03; + if item_type == 0x0A { + depth += 1; + } else if item_type == 0x0C { + if depth == 0 { + bail!( + "unbalanced Collection/EndCollection in report descriptor at offset {}", + i + ); + } + depth -= 1; } + i += 1 + size as usize; } + ensure!( + depth == 0, + "unbalanced Collection/EndCollection (depth {} at end)", + depth + ); + Ok(()) } fn main() -> Result<()> { let mut args = env::args().skip(1); - const USAGE: &'static str = "usbhidd "; + const USAGE: &str = "usbhidd "; - let scheme = args.next().expect(USAGE); - let port = args + let scheme = args.next().context(USAGE)?; + let port: PortId = args .next() - .expect(USAGE) - .parse::() - .expect("Expected port ID"); - let interface_num = args + .context(USAGE)? + .parse() + .map_err(|e| anyhow::anyhow!("Expected port ID: {}", e))?; + let interface_num: u8 = args .next() - .expect(USAGE) - .parse::() - .expect("Expected integer as input of interface"); + .context(USAGE)? + .parse() + .map_err(|e| anyhow::anyhow!("Expected interface number: {}", e))?; let name = format!("{}_{}_{}_hid", scheme, port, interface_num); common::setup_logging( @@ -218,7 +276,6 @@ fn main() -> Result<()> { } }); let hid_desc = if_desc.hid_descs.iter().find_map(|hid_desc| { - //TODO: should we do any filtering? Some(hid_desc) })?; Some((if_desc.clone(), endp_desc_opt, hid_desc)) @@ -240,31 +297,39 @@ fn main() -> Result<()> { }) .context("Failed to configure endpoints")?; - //TODO: do we need to set protocol to report? It fails for mice. - - //TODO: dynamically create good values, fix xhcid so it does not block on each request - // This sets all reports to a duration of 4ms reqs::set_idle(&handle, 1, 0, interface_num as u16).context("Failed to set idle")?; - let report_desc_len = hid_desc.desc_len; - assert_eq!(hid_desc.desc_ty, REPORT_DESC_TY); + let report_desc_len = hid_desc.desc_len as usize; + ensure!( + hid_desc.desc_ty == REPORT_DESC_TY, + "unexpected HID descriptor type: expected {}, got {}", + REPORT_DESC_TY, + hid_desc.desc_ty + ); + ensure!( + report_desc_len >= REPORT_DESC_MIN_SIZE && report_desc_len <= REPORT_DESC_MAX_SIZE, + "suspicious report descriptor length: {}", + report_desc_len + ); - let mut report_desc_bytes = vec![0u8; report_desc_len as usize]; + let mut report_desc_bytes = vec![0u8; report_desc_len]; handle .get_descriptor( PortReqRecipient::Interface, REPORT_DESC_TY, 0, - //TODO: should this be an index into interface_descs? interface_num as u16, &mut report_desc_bytes, ) .context("Failed to retrieve report descriptor")?; + validate_report_descriptor(&report_desc_bytes) + .context("HID report descriptor validation failed")?; + let mut handler = - ReportHandler::new(&report_desc_bytes).expect("failed to parse report descriptor"); + ReportHandler::new(&report_desc_bytes).map_err(|e| anyhow::anyhow!("failed to parse report descriptor: {}", e))?; - let report_len = match endp_desc_opt { + let report_len = match &endp_desc_opt { Some((_endp_num, endp_desc)) => endp_desc.max_packet_size as usize, None => handler.total_byte_length as usize, }; @@ -272,7 +337,9 @@ fn main() -> Result<()> { let report_ty = ReportTy::Input; let report_id = 0; - let mut display = ProducerHandle::new().context("Failed to open input socket")?; + let producer_name = format!("usb-{}-if{}", port, interface_num); + let mut display = InputProducer::new_named_or_fallback(&producer_name) + .context("Failed to open input socket")?; let mut endpoint_opt = match endp_desc_opt { Some((endp_num, _endp_desc)) => match handle.open_endpoint(endp_num as u8) { Ok(ok) => Some(ok), @@ -286,172 +353,168 @@ fn main() -> Result<()> { let mut left_shift = false; let mut right_shift = false; let mut last_mouse_pos = (0, 0); - let mut last_buttons = [false, false, false]; + let mut last_buttons = [false; MAX_MOUSE_BUTTONS]; + let mut consecutive_errors: u32 = 0; + loop { - //TODO: get frequency from device - //TODO: use sleeps when accuracy is better: thread::sleep(time::Duration::from_millis(10)); - let timer = time::Instant::now(); - while timer.elapsed() < time::Duration::from_millis(1) { - thread::yield_now(); - } + thread::sleep(time::Duration::from_millis(1)); - if let Some(endpoint) = &mut endpoint_opt { - // interrupt transfer - endpoint - .transfer_read(&mut report_buffer) - .context("failed to get report")?; + let transfer_result: Result<(), anyhow::Error> = if let Some(endpoint) = &mut endpoint_opt { + endpoint.transfer_read(&mut report_buffer) + .map(|_| ()) + .context("interrupt transfer failed") } else { - // control transfer - reqs::get_report( - &handle, - report_ty, - report_id, - //TODO: should this be an index into interface_descs? - interface_num as u16, - &mut report_buffer, - ) - .context("failed to get report")?; + reqs::get_report(&handle, report_ty, report_id, interface_num as u16, &mut report_buffer) + .map(|_| ()) + .context("control transfer failed") + }; + + if let Err(err) = transfer_result { + consecutive_errors += 1; + if consecutive_errors >= MAX_RETRIES { + bail!( + "transfer failed {} times consecutively: {}", + consecutive_errors, + err + ); + } + let delay = RETRY_BASE_DELAY_MS * (1 << consecutive_errors.min(4)); + log::warn!( + "transfer failed ({}/{}), retry in {}ms: {}", + consecutive_errors, + MAX_RETRIES, + delay, + err + ); + thread::sleep(time::Duration::from_millis(delay)); + continue; } + consecutive_errors = 0; let mut mouse_pos = last_mouse_pos; let mut mouse_dx = 0i32; let mut mouse_dy = 0i32; let mut scroll_y = 0i32; let mut buttons = last_buttons; - for event in handler - .handle(&report_buffer) - .expect("failed to parse report") - { - log::debug!("{}", event); - if event.usage_page == UsagePage::GenericDesktop as u16 { - if event.usage == GenericDesktopUsage::X as u16 { - if event.relative { - mouse_dx += event.value as i32; - } else { - mouse_pos.0 = event.value as i32; - } - } else if event.usage == GenericDesktopUsage::Y as u16 { - if event.relative { - mouse_dy += event.value as i32; - } else { - mouse_pos.1 = event.value as i32; - } - } else if event.usage == GenericDesktopUsage::Wheel as u16 { - //TODO: what is X scroll? - if event.relative { - scroll_y += event.value as i32; - } else { - log::warn!("absolute mouse wheel not supported"); + + match handler.handle(&report_buffer) { + Ok(events) => { + for event in events { + log::debug!("{}", event); + if event.usage_page == UsagePage::GenericDesktop as u16 { + if event.usage == GenericDesktopUsage::X as u16 { + if event.relative { + mouse_dx += event.value as i32; + } else { + mouse_pos.0 = event.value as i32; + } + } else if event.usage == GenericDesktopUsage::Y as u16 { + if event.relative { + mouse_dy += event.value as i32; + } else { + mouse_pos.1 = event.value as i32; + } + } else if event.usage == GenericDesktopUsage::Wheel as u16 { + if event.relative { + scroll_y += event.value as i32; + } else { + log::warn!("absolute mouse wheel not supported"); + } + } else if event.usage == GenericDesktopUsage::Z as u16 { + if event.relative && event.value != 0 { + let scroll_event = orbclient::event::ScrollEvent { + x: event.value as i32, + y: 0, + }; + if let Err(err) = display.write_event(scroll_event.to_event()) { + log::warn!("failed to send hscroll: {}", err); + } + } + } else { + log::info!( + "unsupported generic desktop usage 0x{:X}:0x{:X} value {}", + event.usage_page, + event.usage, + event.value + ); + } + } else if event.usage_page == UsagePage::KeyboardOrKeypad as u16 { + let pressed = event.value != 0; + if event.usage == 0xE1 { + left_shift = pressed; + } else if event.usage == 0xE5 { + right_shift = pressed; + } + send_key_event(&mut display, event.usage_page, event.usage, pressed); + } else if event.usage_page == UsagePage::Button as u16 { + let btn_idx = event.usage as usize; + if btn_idx > 0 && btn_idx <= MAX_MOUSE_BUTTONS { + buttons[btn_idx - 1] = event.value != 0; + } else if btn_idx > MAX_MOUSE_BUTTONS { + log::debug!( + "ignoring button {} (max {})", + btn_idx, + MAX_MOUSE_BUTTONS + ); + } + } else if event.usage_page < 0xFF00 { + log::info!( + "unsupported usage 0x{:X}:0x{:X} value {}", + event.usage_page, + event.usage, + event.value + ); } - } else { - log::info!( - "unsupported generic desktop usage 0x{:X}:0x{:X} value {}", - event.usage_page, - event.usage, - event.value - ); } - } else if event.usage_page == UsagePage::KeyboardOrKeypad as u16 { - let (pressed, shift_opt) = if event.value != 0 { - (true, Some(left_shift | right_shift)) - } else { - (false, None) - }; - if event.usage == 0xE1 { - left_shift = pressed; - } else if event.usage == 0xE5 { - right_shift = pressed; - } - send_key_event(&mut display, event.usage_page, event.usage, pressed); - } else if event.usage_page == UsagePage::Button as u16 { - if event.usage > 0 && event.usage as usize <= buttons.len() { - buttons[event.usage as usize - 1] = event.value != 0; - } else { - log::info!( - "unsupported buttons usage 0x{:X}:0x{:X} value {}", - event.usage_page, - event.usage, - event.value - ); - } - } else if event.usage_page >= 0xFF00 { - // Ignore vendor defined event - } else { - log::info!( - "unsupported usage 0x{:X}:0x{:X} value {}", - event.usage_page, - event.usage, - event.value - ); + } + Err(err) => { + log::warn!("failed to parse HID report: {}", err); } } if mouse_pos != last_mouse_pos { last_mouse_pos = mouse_pos; - - // TODO // ps2d uses 0..=65535 as range, while usb uses 0..=32767. orbital // expects the former range, so multiply by two here to temporarily - // align with orbital expectation. This workaround will make cursor - // looks out of sync in QEMU using virtio-vga with usb-tablet. + // align with orbital expectation. let mouse_event = orbclient::event::MouseEvent { x: mouse_pos.0 * 2, y: mouse_pos.1 * 2, }; - - match display.write_event(mouse_event.to_event()) { - Ok(_) => (), - Err(err) => { - log::warn!("failed to send mouse event to orbital: {}", err); - } + if let Err(err) = display.write_event(mouse_event.to_event()) { + log::warn!("failed to send mouse event: {}", err); } } if mouse_dx != 0 || mouse_dy != 0 { - // TODO: This is a filter to prevent random mouse jumps if mouse_dx > -127 && mouse_dx < 127 { let mouse_event = orbclient::event::MouseRelativeEvent { dx: mouse_dx, dy: mouse_dy, }; - - match display.write_event(mouse_event.to_event()) { - Ok(_) => (), - Err(err) => { - log::warn!("failed to send mouse event to orbital: {}", err); - } + if let Err(err) = display.write_event(mouse_event.to_event()) { + log::warn!("failed to send relative mouse event: {}", err); } } } if scroll_y != 0 { let scroll_event = orbclient::event::ScrollEvent { x: 0, y: scroll_y }; - - match display.write_event(scroll_event.to_event()) { - Ok(_) => (), - Err(err) => { - log::warn!("failed to send scroll event to orbital: {}", err); - } + if let Err(err) = display.write_event(scroll_event.to_event()) { + log::warn!("failed to send scroll event: {}", err); } } if buttons != last_buttons { last_buttons = buttons; - let button_event = orbclient::event::ButtonEvent { left: buttons[0], right: buttons[1], middle: buttons[2], }; - - match display.write_event(button_event.to_event()) { - Ok(_) => (), - Err(err) => { - log::warn!("failed to send button event to orbital: {}", err); - } + if let Err(err) = display.write_event(button_event.to_event()) { + log::warn!("failed to send button event: {}", err); } } - - // log::trace!("took {}ms", timer.elapsed().as_millis()) } }