From 4fc813f3ef3ccc1941b0e323fff5ad825cc6eb6b Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 23 Jun 2026 20:21:40 -0600 Subject: [PATCH] Send controller events from usbhidd --- Cargo.lock | 4 +- Cargo.toml | 2 +- drivers/input/usbhidd/src/main.rs | 552 ++++++++++++++---------------- drivers/inputd/src/main.rs | 12 + 4 files changed, 265 insertions(+), 305 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 55ddc6e20e..6df99b8b3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1423,9 +1423,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "orbclient" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7617479bc75c12124d08d04307c562603b317a4162d785760a17d2c2e52345c" +checksum = "1d6a81febac10e8d8e15c33b67a4a88807d587d9ac5689392239b6b83d9fa2ae" dependencies = [ "libc", "libredox", diff --git a/Cargo.toml b/Cargo.toml index 7d188c84e4..fb82815675 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,7 @@ fdt = "0.1.5" libc = "0.2.181" log = "0.4" libredox = "0.1.17" -orbclient = "0.4.0" +orbclient = "0.4.1" psf-rs = "2.2.0" parking_lot = "0.12" pico-args = "0.5" diff --git a/drivers/input/usbhidd/src/main.rs b/drivers/input/usbhidd/src/main.rs index 2dcee31ea2..8d5de62948 100644 --- a/drivers/input/usbhidd/src/main.rs +++ b/drivers/input/usbhidd/src/main.rs @@ -1,17 +1,12 @@ use anyhow::{Context, Result}; -use std::{ - collections::{BTreeMap, HashMap}, - env, - sync::mpsc, - thread, time, -}; +use std::{collections::HashMap, env, thread, time}; use inputd::ProducerHandle; use orbclient::KeyEvent as OrbKeyEvent; use rehid::{ - hidreport::{Report, Usage}, + hidreport::Report, report_desc::{ReportTy, REPORT_DESC_TY}, - report_handler::ReportHandler, + report_handler::{ReportEvent, ReportHandler}, usage_tables::{GenericDesktopUsage, UsagePage}, }; use xhcid_interface::{ @@ -164,6 +159,38 @@ fn send_key_event(display: &mut ProducerHandle, usage_page: u16, usage: u16, pre } } +fn send_gamepad_axis(display: &mut ProducerHandle, axis: u16, value: i64) { + let event = orbclient::ControllerAxisEvent { + // Filled in by inputd + id: 0, + axis, + value: value as i32, + }; + + match display.write_event(event.to_event()) { + Ok(_) => (), + Err(err) => { + log::warn!("failed to send axis event to orbital: {}", err); + } + } +} + +fn send_gamepad_button(display: &mut ProducerHandle, button: u16, pressed: bool) { + let event = orbclient::ControllerButtonEvent { + // Filled in by inputd + id: 0, + button, + pressed, + }; + + match display.write_event(event.to_event()) { + Ok(_) => (), + Err(err) => { + log::warn!("failed to send button event to orbital: {}", err); + } + } +} + fn main() -> Result<()> { let mut args = env::args().skip(1); @@ -336,41 +363,9 @@ fn main() -> Result<()> { None => None, }; - let (tx, rx) = mpsc::channel(); - thread::spawn(move || -> Result<()> { - let mut report_buffer = vec![0u8; report_len]; - let report_ty = ReportTy::Input; - let report_id = 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(); - } - - if let Some(endpoint) = &mut endpoint_opt { - // interrupt transfer - endpoint - .transfer_read(&mut report_buffer) - .context("failed to get report")?; - } 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")?; - } - - tx.send(report_buffer.clone()) - .context("failed to send report to main thread")?; - } - }); + let mut report_buffer = vec![0u8; report_len]; + let report_ty = ReportTy::Input; + let report_id = 0; let mut left_shift = false; let mut right_shift = false; @@ -378,286 +373,239 @@ fn main() -> Result<()> { let mut last_buttons = [false, false, false]; let mut gamepad_state = HashMap::new(); 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(); + } + + if let Some(endpoint) = &mut endpoint_opt { + // interrupt transfer + endpoint + .transfer_read(&mut report_buffer) + .context("failed to get report")?; + } 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")?; + } + 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; - match rx.try_recv() { - Ok(report_buffer) => { - for event in handler - .handle(&report_buffer) - .expect("failed to parse report") - { - let mut gamepad = false; - if let Some(collections) = - report_collections.get(&(event.report_id, event.field_id)) - { - for collection in collections { - for usage in collection.usages() { - match UsagePage::from_repr(usage.usage_page.into()) { - Some(UsagePage::GenericDesktop) => { - match GenericDesktopUsage::from_repr(usage.usage_id.into()) - { - Some(GenericDesktopUsage::GamePad) => { - gamepad = true; - } - _ => {} - } + for event in handler + .handle(&report_buffer) + .expect("failed to parse report") + { + let mut gamepad = false; + if let Some(collections) = report_collections.get(&(event.report_id, event.field_id)) { + for collection in collections { + for usage in collection.usages() { + match UsagePage::from_repr(usage.usage_page.into()) { + Some(UsagePage::GenericDesktop) => { + match GenericDesktopUsage::from_repr(usage.usage_id.into()) { + Some(GenericDesktopUsage::GamePad) => { + gamepad = true; } _ => {} } } + _ => {} } - } else { - log::warn!( - "collections missing for report {:?} field {}", - event.report_id, - event.field_id - ); } + } + } else { + log::warn!( + "collections missing for report {:?} field {}", + event.report_id, + event.field_id + ); + } - // Special handling for gamepad - //TODO: make this more generic - if gamepad { - let last = gamepad_state - .get(&(event.usage_page, event.usage)) - .map_or(0, |x: &(i64, i64)| x.1); - gamepad_state.insert((event.usage_page, event.usage), (event.value, last)); - continue; - } - - match UsagePage::from_repr(event.usage_page) { - Some(UsagePage::GenericDesktop) => { - match GenericDesktopUsage::from_repr(event.usage) { - Some(GenericDesktopUsage::X) => { - if event.relative { - mouse_dx += event.value as i32; - } else { - mouse_pos.0 = event.value as i32; - } - } - Some(GenericDesktopUsage::Y) => { - if event.relative { - mouse_dy += event.value as i32; - } else { - mouse_pos.1 = event.value as i32; - } - } - Some(GenericDesktopUsage::Wheel) => { - //TODO: what is X scroll? - if event.relative { - scroll_y += event.value as i32; - } else { - log::warn!("absolute mouse wheel not supported"); - } - } - unsupported => { - log::info!( - "unsupported generic desktop usage 0x{:X}:0x{:X} ({:?}) value {}", - event.usage_page, - event.usage, - unsupported, - event.value - ); - } - } - } - Some(UsagePage::KeyboardOrKeypad) => { - 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); - } - Some(UsagePage::Button) => { - 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 + // Special handling for gamepad + //TODO: make this more generic + if gamepad { + let ReportEvent { + usage_page, + usage, + value, + .. + } = event; + let last = gamepad_state + .get(&(usage_page, usage)) + .map_or(0, |x: &(i64, i64)| x.0); + gamepad_state.insert((usage_page, usage), (value, last)); + if value == last { + continue; + } + match UsagePage::from_repr(usage_page) { + Some(UsagePage::GenericDesktop) => { + match GenericDesktopUsage::from_repr(usage) { + Some(GenericDesktopUsage::X) => { + send_gamepad_axis( + &mut display, + orbclient::CONTROLLER_AXIS_X, + value, ); } - } - _ => { - 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 + Some(GenericDesktopUsage::Y) => { + send_gamepad_axis( + &mut display, + orbclient::CONTROLLER_AXIS_Y, + value, ); } + Some(GenericDesktopUsage::Z) => { + send_gamepad_axis( + &mut display, + orbclient::CONTROLLER_AXIS_Z, + value, + ); + } + Some(GenericDesktopUsage::Rx) => { + send_gamepad_axis( + &mut display, + orbclient::CONTROLLER_AXIS_RX, + value, + ); + } + Some(GenericDesktopUsage::Ry) => { + send_gamepad_axis( + &mut display, + orbclient::CONTROLLER_AXIS_RY, + value, + ); + } + Some(GenericDesktopUsage::Rz) => { + send_gamepad_axis( + &mut display, + orbclient::CONTROLLER_AXIS_RZ, + value, + ); + } + Some(GenericDesktopUsage::DpadLeft) => { + send_gamepad_button( + &mut display, + orbclient::CONTROLLER_DPAD_LEFT, + value != 0, + ); + } + Some(GenericDesktopUsage::DpadRight) => { + send_gamepad_button( + &mut display, + orbclient::CONTROLLER_DPAD_RIGHT, + value != 0, + ); + } + Some(GenericDesktopUsage::DpadUp) => { + send_gamepad_button( + &mut display, + orbclient::CONTROLLER_DPAD_UP, + value != 0, + ); + } + Some(GenericDesktopUsage::DpadDown) => { + send_gamepad_button( + &mut display, + orbclient::CONTROLLER_DPAD_DOWN, + value != 0, + ); + } + _ => {} } } - } - } - Err(mpsc::TryRecvError::Empty) => { - //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(); - } - } - Err(err) => { - anyhow::bail!("failed to recv report buffer from thread: {:?}", err); - } - } - - for (&(usage_page, usage), &(value, last)) in gamepad_state.iter() { - let gamepad_axis = |value| { - let deadzone = 8096; - if value < -deadzone { - value + deadzone - } else if value > deadzone { - value - deadzone - } else { - 0 - } - }; - let mut gamepad_key = |scancode, pressed| { - let key_event = OrbKeyEvent { - character: '\0', - scancode, - pressed, - }; - - match display.write_event(key_event.to_event()) { - Ok(_) => (), - Err(err) => { - log::warn!("failed to send key event to orbital: {}", err); - } - } - }; - let mut gamepad_axis_keys = |value, last, k_neg, k_pos| { - let threshold = 10240; - let press_neg = value < -threshold; - let press_pos = value > threshold; - if press_neg != (last < -threshold) { - gamepad_key(k_neg, press_neg); - } - if press_pos != (last > threshold) { - gamepad_key(k_pos, press_pos); - } - }; - match UsagePage::from_repr(usage_page) { - Some(UsagePage::GenericDesktop) => match GenericDesktopUsage::from_repr(usage) { - Some(GenericDesktopUsage::X) => { - gamepad_axis_keys(value, last, orbclient::K_A, orbclient::K_D); - } - Some(GenericDesktopUsage::Y) => { - gamepad_axis_keys(value, last, orbclient::K_S, orbclient::K_W); - } - Some(GenericDesktopUsage::Z) => { - let pressed = value > 64; - if pressed != (last > 64) { - gamepad_key(orbclient::K_Q, pressed); - } - } - Some(GenericDesktopUsage::Rx) => { - mouse_dx += (gamepad_axis(value) / 4096) as i32; - } - Some(GenericDesktopUsage::Ry) => { - mouse_dy -= (gamepad_axis(value) / 4096) as i32; - } - Some(GenericDesktopUsage::Rz) => { - let pressed = value > 64; - if pressed != (last > 64) { - gamepad_key(orbclient::K_E, pressed); - } - } - Some(GenericDesktopUsage::DpadLeft) => { - if value != last { - gamepad_key(orbclient::K_LEFT, value != 0); - } - } - Some(GenericDesktopUsage::DpadRight) => { - if value != last { - gamepad_key(orbclient::K_RIGHT, value != 0); - } - } - Some(GenericDesktopUsage::DpadUp) => { - if value != last { - gamepad_key(orbclient::K_UP, value != 0); - } - } - Some(GenericDesktopUsage::DpadDown) => { - if value != last { - gamepad_key(orbclient::K_DOWN, value != 0); - } + Some(UsagePage::Button) => { + send_gamepad_button(&mut display, usage, value != 0); } _ => {} - }, - Some(UsagePage::Button) => match usage { - // A - 1 => { - if value != last { - gamepad_key(orbclient::K_SEMICOLON, value != 0) - } - } - // B - 2 => { - if value != last { - gamepad_key(orbclient::K_L, value != 0) - } - } - // X - 3 => { - if value != last { - gamepad_key(orbclient::K_O, value != 0) - } - } - // Y - 4 => { - if value != last { - gamepad_key(orbclient::K_K, value != 0) - } - } - // LB - 5 => { - if value != last { - gamepad_key(orbclient::K_I, value != 0) - } - } - // RB - 6 => { - if value != last { - gamepad_key(orbclient::K_P, value != 0) - } - } - // Select - 7 => { - if value != last { - gamepad_key(orbclient::K_SPACE, value != 0) - } - } - // Start - 8 => { - if value != last { - gamepad_key(orbclient::K_ENTER, value != 0) - } - } - _ => { - //log::warn!("unknown gamepad button {}", usage) - } - }, - _ => {} + } + continue; + } + + match UsagePage::from_repr(event.usage_page) { + Some(UsagePage::GenericDesktop) => { + match GenericDesktopUsage::from_repr(event.usage) { + Some(GenericDesktopUsage::X) => { + if event.relative { + mouse_dx += event.value as i32; + } else { + mouse_pos.0 = event.value as i32; + } + } + Some(GenericDesktopUsage::Y) => { + if event.relative { + mouse_dy += event.value as i32; + } else { + mouse_pos.1 = event.value as i32; + } + } + Some(GenericDesktopUsage::Wheel) => { + //TODO: what is X scroll? + if event.relative { + scroll_y += event.value as i32; + } else { + log::warn!("absolute mouse wheel not supported"); + } + } + unsupported => { + log::info!( + "unsupported generic desktop usage 0x{:X}:0x{:X} ({:?}) value {}", + event.usage_page, + event.usage, + unsupported, + event.value + ); + } + } + } + Some(UsagePage::KeyboardOrKeypad) => { + 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); + } + Some(UsagePage::Button) => { + 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 + ); + } + } + _ => { + 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 + ); + } + } } - } - for (_, (value, last)) in gamepad_state.iter_mut() { - *last = *value; } if mouse_pos != last_mouse_pos { diff --git a/drivers/inputd/src/main.rs b/drivers/inputd/src/main.rs index 9f24080c28..4f0eec6359 100644 --- a/drivers/inputd/src/main.rs +++ b/drivers/inputd/src/main.rs @@ -429,6 +429,18 @@ impl SchemeSync for InputScheme { } }, + // Set ID for controller events + EventOption::ControllerAxis(mut axis_event) => { + //TODO: what to do with overflow? + axis_event.id = id as u32; + events.to_mut()[i] = axis_event.to_event(); + } + EventOption::ControllerButton(mut button_event) => { + //TODO: what to do with overflow? + button_event.id = id as u32; + events.to_mut()[i] = button_event.to_event(); + } + _ => continue, }