Send controller events from usbhidd
This commit is contained in:
Generated
+2
-2
@@ -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",
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
+250
-302
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user