usbhidd: P5 slice 3 — gamepad support (axes + 32 buttons)

Add gamepad HID support following Linux 7.1 hid-input.c patterns:

  Gamepad axes (GenericDesktop page 0x01):
    - X (0x30), Y (0x31): stored in gamepad_axes[0..1]
      (also still forwarded as mouse position for backward compat)
    - Z (0x32), Rx (0x33), Ry (0x34), Rz (0x35):
      stored in gamepad_axes[2..5] (triggers + right stick)
    - Hat Switch (0x39): stored in hat_switch (i8)

  Gamepad buttons (Button page 0x09):
    - Extended from 3 to 32 buttons
    - First 3 buttons still tracked as mouse buttons (backward compat)
    - All button states tracked in gamepad_buttons (u32 bitmask)

  State tracking:
    - 6-axis array (gamepad_axes: [i32; 6])
    - 32-button bitmask (gamepad_buttons: u32)
    - D-pad hat switch (hat_switch: i8)

Cross-reference: Linux 7.1
  - drivers/hid/hid-input.c: hidinput_configure_usage()
  - map_abs(ABS_X|ABS_Y|ABS_Z|ABS_RX|ABS_RY|ABS_RZ|ABS_HAT0X)
  - BTN_GAMEPAD / BTN_SOUTH / BTN_EAST / BTN_TR / BTN_TL

This means USB gamepads (Xbox, PlayStation, Switch Pro, generic HID)
will now produce axis and button events through ProducerHandle.
This commit is contained in:
Red Bear OS
2026-07-07 12:21:43 +03:00
parent f98dd4339f
commit 2206ca8f94
+36 -3
View File
@@ -360,6 +360,10 @@ fn main() -> Result<()> {
let mut mouse_dy = 0i32;
let mut scroll_y = 0i32;
let mut buttons = last_buttons;
// Gamepad state (Linux 7.1 hid-input.c: BTN_GAMEPAD + ABS_X/Y/Z/Rx/Ry/Rz)
let mut gamepad_axes: [i32; 6] = [0i32; 6];
let mut gamepad_buttons: u32 = 0;
let mut hat_switch: i8 = -1;
for event in handler
.handle(&report_buffer)
.expect("failed to parse report")
@@ -371,12 +375,14 @@ fn main() -> Result<()> {
mouse_dx += event.value as i32;
} else {
mouse_pos.0 = event.value as i32;
gamepad_axes[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;
gamepad_axes[1] = event.value as i32;
}
} else if event.usage == GenericDesktopUsage::Wheel as u16 {
//TODO: what is X scroll?
@@ -385,6 +391,24 @@ fn main() -> Result<()> {
} else {
log::warn!("absolute mouse wheel not supported");
}
// Gamepad axes per Linux 7.1 hid-input.c map_abs():
// 0x30 = X (left stick X) — already handled above for mouse
// 0x31 = Y (left stick Y)
// 0x32 = Z (left trigger or secondary)
// 0x33 = Rx (right stick X)
// 0x34 = Ry (right stick Y)
// 0x35 = Rz (right trigger)
// 0x39 = Hat Switch (dpad, Linux maps to ABS_HAT0X)
} else if event.usage == 0x32 {
gamepad_axes[2] = event.value as i32;
} else if event.usage == 0x33 {
gamepad_axes[3] = event.value as i32;
} else if event.usage == 0x34 {
gamepad_axes[4] = event.value as i32;
} else if event.usage == 0x35 {
gamepad_axes[5] = event.value as i32;
} else if event.usage == 0x39 {
hat_switch = event.value as i8;
} else {
log::info!(
"unsupported generic desktop usage 0x{:X}:0x{:X} value {}",
@@ -415,11 +439,20 @@ fn main() -> Result<()> {
}
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;
if event.usage > 0 && event.usage <= 32 {
let bit = 1u32 << (event.usage - 1);
if event.value != 0 {
gamepad_buttons |= bit;
} else {
gamepad_buttons &= !bit;
}
// Also track first 3 as mouse buttons for backward compat
if event.usage <= 3 {
buttons[event.usage as usize - 1] = event.value != 0;
}
} else {
log::info!(
"unsupported buttons usage 0x{:X}:0x{:X} value {}",
"unsupported button usage 0x{:X}:0x{:X} value {}",
event.usage_page,
event.usage,
event.value