mirror of
https://github.com/joleuger/vuinputd.git
synced 2026-07-18 00:45:07 +00:00
244 lines
6.8 KiB
Rust
244 lines
6.8 KiB
Rust
// SPDX-License-Identifier: MIT
|
|
//
|
|
// Author: Johannes Leupolz <dev@leupolz.eu>
|
|
|
|
use crate::devices::device_base::{fetch_device_node, open_uinput, Device, DeviceState, BUS_USB};
|
|
use libc::{c_int, close, open};
|
|
use std::io;
|
|
use uinput_ioctls::*;
|
|
|
|
// Xbox Gamepad codes
|
|
// https://github.com/torvalds/linux/blob/master/Documentation/input/gamepad.rst
|
|
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h// Keys and Buttons
|
|
// https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c
|
|
pub const BTN_SOUTH: u16 = 0x130;
|
|
pub const BTN_EAST: u16 = 0x131;
|
|
pub const BTN_NORTH: u16 = 0x133;
|
|
pub const BTN_WEST: u16 = 0x134;
|
|
pub const BTN_TL: u16 = 0x136;
|
|
pub const BTN_TR: u16 = 0x137;
|
|
pub const BTN_SELECT: u16 = 0x13a;
|
|
pub const BTN_START: u16 = 0x13b;
|
|
pub const BTN_MODE: u16 = 0x13c;
|
|
pub const BTN_THUMBL: u16 = 0x13d;
|
|
pub const BTN_THUMBR: u16 = 0x13e;
|
|
|
|
// Absolute Axes
|
|
pub const ABS_X: u16 = 0x00;
|
|
pub const ABS_Y: u16 = 0x01;
|
|
pub const ABS_Z: u16 = 0x02;
|
|
pub const ABS_RX: u16 = 0x03;
|
|
pub const ABS_RY: u16 = 0x04;
|
|
pub const ABS_RZ: u16 = 0x05;
|
|
pub const ABS_HAT0X: u16 = 0x10;
|
|
pub const ABS_HAT0Y: u16 = 0x11;
|
|
|
|
// Force Feedback
|
|
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/input.h
|
|
pub const FF_RUMBLE: u16 = 0x50;
|
|
pub const FF_PERIODIC: u16 = 0x51;
|
|
pub const FF_CONSTANT: u16 = 0x52;
|
|
pub const FF_RAMP: u16 = 0x57;
|
|
pub const FF_SINE: u16 = 0x5a;
|
|
pub const FF_GAIN: u16 = 0x60;
|
|
|
|
/// Setup Xbox gamepad device
|
|
/// https://github.com/LizardByte/Sunshine/blob/master/src/platform/linux/input/inputtino_gamepad.cpp
|
|
/// https://github.com/games-on-whales/inputtino/blob/stable/src/uinput/joypad_xbox.cpp
|
|
unsafe fn setup_xbox_gamepad(fd: c_int) -> io::Result<()> {
|
|
// EV_SYN
|
|
ui_set_evbit(fd, super::EV_SYN.try_into().unwrap())?;
|
|
|
|
// EV_KEY
|
|
ui_set_evbit(fd, super::EV_KEY.try_into().unwrap())?;
|
|
ui_set_keybit(fd, BTN_WEST.try_into().unwrap())?;
|
|
ui_set_keybit(fd, BTN_EAST.try_into().unwrap())?;
|
|
ui_set_keybit(fd, BTN_NORTH.try_into().unwrap())?;
|
|
ui_set_keybit(fd, BTN_SOUTH.try_into().unwrap())?;
|
|
ui_set_keybit(fd, BTN_THUMBL.try_into().unwrap())?;
|
|
ui_set_keybit(fd, BTN_THUMBR.try_into().unwrap())?;
|
|
ui_set_keybit(fd, BTN_TR.try_into().unwrap())?;
|
|
ui_set_keybit(fd, BTN_TL.try_into().unwrap())?;
|
|
ui_set_keybit(fd, BTN_SELECT.try_into().unwrap())?;
|
|
ui_set_keybit(fd, BTN_MODE.try_into().unwrap())?;
|
|
ui_set_keybit(fd, BTN_START.try_into().unwrap())?;
|
|
|
|
// EV_ABS
|
|
ui_set_evbit(fd, super::EV_ABS.try_into().unwrap())?;
|
|
// EV_ABS dpad
|
|
let abs_info_dpad = libc::input_absinfo {
|
|
value: 0,
|
|
minimum: -1,
|
|
maximum: 1,
|
|
fuzz: 0,
|
|
flat: 0,
|
|
resolution: 0,
|
|
};
|
|
ui_abs_setup(
|
|
fd,
|
|
&libc::uinput_abs_setup {
|
|
code: ABS_HAT0Y,
|
|
absinfo: abs_info_dpad.clone(),
|
|
},
|
|
)?;
|
|
ui_abs_setup(
|
|
fd,
|
|
&libc::uinput_abs_setup {
|
|
code: ABS_HAT0X,
|
|
absinfo: abs_info_dpad.clone(),
|
|
},
|
|
)?;
|
|
// EV_ABS stick
|
|
let abs_info_stick = libc::input_absinfo {
|
|
value: 0,
|
|
minimum: -32768,
|
|
maximum: 32767,
|
|
fuzz: 16,
|
|
flat: 128,
|
|
resolution: 0,
|
|
};
|
|
ui_abs_setup(
|
|
fd,
|
|
&libc::uinput_abs_setup {
|
|
code: ABS_X,
|
|
absinfo: abs_info_stick.clone(),
|
|
},
|
|
)?;
|
|
ui_abs_setup(
|
|
fd,
|
|
&libc::uinput_abs_setup {
|
|
code: ABS_RX,
|
|
absinfo: abs_info_stick.clone(),
|
|
},
|
|
)?;
|
|
ui_abs_setup(
|
|
fd,
|
|
&libc::uinput_abs_setup {
|
|
code: ABS_Y,
|
|
absinfo: abs_info_stick.clone(),
|
|
},
|
|
)?;
|
|
ui_abs_setup(
|
|
fd,
|
|
&libc::uinput_abs_setup {
|
|
code: ABS_RY,
|
|
absinfo: abs_info_stick.clone(),
|
|
},
|
|
)?;
|
|
// EV_ABS trigger
|
|
let abs_info_trigger = libc::input_absinfo {
|
|
value: 0,
|
|
minimum: 0,
|
|
maximum: 255,
|
|
fuzz: 0,
|
|
flat: 0,
|
|
resolution: 0,
|
|
};
|
|
ui_abs_setup(
|
|
fd,
|
|
&libc::uinput_abs_setup {
|
|
code: ABS_Z,
|
|
absinfo: abs_info_trigger.clone(),
|
|
},
|
|
)?;
|
|
ui_abs_setup(
|
|
fd,
|
|
&libc::uinput_abs_setup {
|
|
code: ABS_RZ,
|
|
absinfo: abs_info_trigger.clone(),
|
|
},
|
|
)?;
|
|
|
|
// EV_FF
|
|
ui_set_evbit(fd, super::EV_FF.try_into().unwrap())?;
|
|
ui_set_ffbit(fd, FF_RUMBLE.try_into().unwrap())?;
|
|
ui_set_ffbit(fd, FF_CONSTANT.try_into().unwrap())?;
|
|
ui_set_ffbit(fd, FF_PERIODIC.try_into().unwrap())?;
|
|
ui_set_ffbit(fd, FF_SINE.try_into().unwrap())?;
|
|
ui_set_ffbit(fd, FF_RAMP.try_into().unwrap())?;
|
|
ui_set_ffbit(fd, FF_GAIN.try_into().unwrap())?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub struct XboxGamepadDevice {
|
|
state: DeviceState,
|
|
}
|
|
|
|
impl Device for XboxGamepadDevice {
|
|
fn name() -> &'static str {
|
|
"Xbox Gamepad"
|
|
}
|
|
|
|
fn state(&self) -> &DeviceState {
|
|
&self.state
|
|
}
|
|
|
|
fn state_mut(&mut self) -> &mut DeviceState {
|
|
&mut self.state
|
|
}
|
|
|
|
fn get_event_device(&self) -> Result<c_int, io::Error> {
|
|
Ok(self.state.event_device_fd)
|
|
}
|
|
|
|
fn create(device: Option<&str>, name: &str) -> Result<Self, io::Error> {
|
|
let fd = open_uinput(device)?;
|
|
|
|
unsafe { setup_xbox_gamepad(fd)? };
|
|
|
|
let temp_device = XboxGamepadDevice {
|
|
state: DeviceState {
|
|
uinput_fd: fd,
|
|
sysname: String::new(),
|
|
device_name: name.to_string(),
|
|
event_device_node: String::new(),
|
|
event_device_fd: -1,
|
|
events: Vec::new(),
|
|
},
|
|
};
|
|
temp_device.setup_device(name, 0xbeef, 0xdead, BUS_USB)?;
|
|
|
|
unsafe {
|
|
ui_dev_create(fd).map_err(|e| {
|
|
eprintln!("ui_dev_create failed: {:?}", e);
|
|
e
|
|
})?;
|
|
}
|
|
|
|
let sysname = temp_device.get_sysname()?;
|
|
|
|
let event_device_node = fetch_device_node(&sysname)?;
|
|
let event_device_fd = unsafe {
|
|
open(
|
|
event_device_node.as_ptr() as *const i8,
|
|
libc::O_RDONLY | libc::O_NONBLOCK,
|
|
)
|
|
};
|
|
if event_device_fd < 0 {
|
|
return Err(io::Error::last_os_error());
|
|
}
|
|
|
|
Ok(XboxGamepadDevice {
|
|
state: DeviceState {
|
|
uinput_fd: fd,
|
|
sysname,
|
|
device_name: name.to_string(),
|
|
event_device_node,
|
|
event_device_fd,
|
|
events: Vec::new(),
|
|
},
|
|
})
|
|
}
|
|
|
|
fn destroy(self) {
|
|
unsafe {
|
|
ui_dev_destroy(self.state.uinput_fd).unwrap_or_else(|e| {
|
|
eprintln!("ui_dev_destroy failed: {:?}", e);
|
|
std::process::exit(1);
|
|
});
|
|
close(self.state.uinput_fd);
|
|
close(self.state.event_device_fd);
|
|
}
|
|
}
|
|
}
|