use evdev::{
AbsInfo, AbsoluteAxisCode, AttributeSet, FFEffectCode, KeyCode, UinputAbsSetup,
uinput::VirtualDevice,
};
use gilrs::{Axis, Button};
use std::error::Error;
pub const MAX_FF_EFFECTS: i16 = 16;
pub const AXIS_MAX: f32 = u16::MAX as f32;
pub const AXIS_HALF: f32 = AXIS_MAX / 2.0;
pub fn scale_stick(val: f32, invert: bool) -> i32 {
let val = if invert { -val } else { val };
((val + 1.0) * AXIS_HALF).round() as i32
}
pub fn scale_trigger(val: f32) -> i32 {
(val * AXIS_MAX).round() as i32
}
#[derive(Clone)]
pub struct VirtualGamepadInfo {
pub name: String,
pub vendor_id: Option<u16>,
pub product_id: Option<u16>,
}
impl<'a> From<&'a gilrs::Gamepad<'a>> for VirtualGamepadInfo {
fn from(gp: &'a gilrs::Gamepad<'a>) -> Self {
Self {
name: gp.os_name().to_string(),
vendor_id: gp.vendor_id(),
product_id: gp.product_id(),
}
}
}
pub fn create_virtual_gamepad(
info: &VirtualGamepadInfo,
tag: Option<&str>,
) -> Result<VirtualDevice, Box<dyn Error>> {
let max = AXIS_MAX as i32;
let mid = AXIS_HALF as i32;
let abs_stick_setup = AbsInfo::new(mid, 0, max, 0, 0, 0);
let abs_trigger_setup = AbsInfo::new(0, 0, max, 0, 0, 0);
let keys = AttributeSet::from_iter([
KeyCode::BTN_NORTH,
KeyCode::BTN_SOUTH,
KeyCode::BTN_EAST,
KeyCode::BTN_WEST,
KeyCode::BTN_TL, KeyCode::BTN_TR, KeyCode::BTN_TL2, KeyCode::BTN_TR2, KeyCode::BTN_THUMBL,
KeyCode::BTN_THUMBR,
KeyCode::BTN_SELECT,
KeyCode::BTN_START,
KeyCode::BTN_MODE,
KeyCode::BTN_DPAD_UP,
KeyCode::BTN_DPAD_DOWN,
KeyCode::BTN_DPAD_LEFT,
KeyCode::BTN_DPAD_RIGHT,
]);
let abs_axes = [
(AbsoluteAxisCode::ABS_X, abs_stick_setup), (AbsoluteAxisCode::ABS_Y, abs_stick_setup), (AbsoluteAxisCode::ABS_Z, abs_trigger_setup), (AbsoluteAxisCode::ABS_RX, abs_stick_setup), (AbsoluteAxisCode::ABS_RY, abs_stick_setup), (AbsoluteAxisCode::ABS_RZ, abs_trigger_setup), (AbsoluteAxisCode::ABS_HAT0X, abs_stick_setup), (AbsoluteAxisCode::ABS_HAT0Y, abs_stick_setup), ];
let mut builder = VirtualDevice::builder()?;
let name = if let Some(tag_str) = tag {
format!("{} {}", info.name, tag_str)
} else {
info.name.clone()
};
builder = builder.name(&name);
if let (Some(vendor), Some(product)) = (info.vendor_id, info.product_id) {
builder = builder.input_id(evdev::InputId::new(
evdev::BusType::BUS_USB,
vendor,
product,
0x4242,
));
}
builder = builder.with_keys(&keys)?;
for (code, info) in abs_axes {
let setup = UinputAbsSetup::new(code, info);
builder = builder.with_absolute_axis(&setup)?;
}
let ff_effects = AttributeSet::from_iter([
FFEffectCode::FF_RUMBLE,
]);
builder = builder.with_ff(&ff_effects)?;
builder = builder.with_ff_effects_max(MAX_FF_EFFECTS as u32);
Ok(builder.build()?)
}
pub fn gilrs_button_to_evdev_key(button: Button) -> Option<KeyCode> {
match button {
Button::West => Some(KeyCode::BTN_NORTH), Button::South => Some(KeyCode::BTN_SOUTH),
Button::East => Some(KeyCode::BTN_EAST),
Button::North => Some(KeyCode::BTN_WEST), Button::LeftTrigger => Some(KeyCode::BTN_TL), Button::RightTrigger => Some(KeyCode::BTN_TR), Button::LeftTrigger2 => Some(KeyCode::BTN_TL2), Button::RightTrigger2 => Some(KeyCode::BTN_TR2), Button::LeftThumb => Some(KeyCode::BTN_THUMBL),
Button::RightThumb => Some(KeyCode::BTN_THUMBR),
Button::Select => Some(KeyCode::BTN_SELECT),
Button::Start => Some(KeyCode::BTN_START),
Button::Mode => Some(KeyCode::BTN_MODE),
Button::DPadUp => Some(KeyCode::BTN_DPAD_UP),
Button::DPadDown => Some(KeyCode::BTN_DPAD_DOWN),
Button::DPadLeft => Some(KeyCode::BTN_DPAD_LEFT),
Button::DPadRight => Some(KeyCode::BTN_DPAD_RIGHT),
_ => None,
}
}
pub fn gilrs_button_to_evdev_axis(button: Button) -> Option<AbsoluteAxisCode> {
match button {
Button::LeftTrigger2 => Some(AbsoluteAxisCode::ABS_Z),
Button::RightTrigger2 => Some(AbsoluteAxisCode::ABS_RZ),
Button::DPadUp => Some(AbsoluteAxisCode::ABS_HAT0Y),
Button::DPadDown => Some(AbsoluteAxisCode::ABS_HAT0Y),
Button::DPadLeft => Some(AbsoluteAxisCode::ABS_HAT0X),
Button::DPadRight => Some(AbsoluteAxisCode::ABS_HAT0X),
_ => None,
}
}
pub fn gilrs_axis_to_evdev_axis(axis: Axis) -> Option<AbsoluteAxisCode> {
match axis {
Axis::LeftStickX => Some(AbsoluteAxisCode::ABS_X),
Axis::LeftStickY => Some(AbsoluteAxisCode::ABS_Y),
Axis::LeftZ => Some(AbsoluteAxisCode::ABS_Z), Axis::RightStickX => Some(AbsoluteAxisCode::ABS_RX),
Axis::RightStickY => Some(AbsoluteAxisCode::ABS_RY),
Axis::RightZ => Some(AbsoluteAxisCode::ABS_RZ), _ => None,
}
}
pub fn dpad_axis_pair(button: Button) -> Option<[Button; 2]> {
match button {
Button::DPadUp | Button::DPadDown => Some([Button::DPadUp, Button::DPadDown]),
Button::DPadLeft | Button::DPadRight => Some([Button::DPadLeft, Button::DPadRight]),
_ => None,
}
}
pub fn generate_neutral_gamepad_events() -> Vec<evdev::InputEvent> {
use evdev::{AbsoluteAxisCode, InputEvent, KeyCode};
let mut events = Vec::new();
let center_value = AXIS_HALF as i32;
events.push(InputEvent::new(
evdev::EventType::ABSOLUTE.0,
AbsoluteAxisCode::ABS_X.0,
center_value,
));
events.push(InputEvent::new(
evdev::EventType::ABSOLUTE.0,
AbsoluteAxisCode::ABS_Y.0,
center_value,
));
events.push(InputEvent::new(
evdev::EventType::ABSOLUTE.0,
AbsoluteAxisCode::ABS_RX.0,
center_value,
));
events.push(InputEvent::new(
evdev::EventType::ABSOLUTE.0,
AbsoluteAxisCode::ABS_RY.0,
center_value,
));
events.push(InputEvent::new(
evdev::EventType::ABSOLUTE.0,
AbsoluteAxisCode::ABS_Z.0,
0,
));
events.push(InputEvent::new(
evdev::EventType::ABSOLUTE.0,
AbsoluteAxisCode::ABS_RZ.0,
0,
));
events.push(InputEvent::new(
evdev::EventType::ABSOLUTE.0,
AbsoluteAxisCode::ABS_HAT0X.0,
center_value,
));
events.push(InputEvent::new(
evdev::EventType::ABSOLUTE.0,
AbsoluteAxisCode::ABS_HAT0Y.0,
center_value,
));
let buttons = [
KeyCode::BTN_NORTH,
KeyCode::BTN_SOUTH,
KeyCode::BTN_EAST,
KeyCode::BTN_WEST,
KeyCode::BTN_TL, KeyCode::BTN_TR, KeyCode::BTN_TL2, KeyCode::BTN_TR2, KeyCode::BTN_THUMBL,
KeyCode::BTN_THUMBR,
KeyCode::BTN_SELECT,
KeyCode::BTN_START,
KeyCode::BTN_MODE,
KeyCode::BTN_DPAD_UP,
KeyCode::BTN_DPAD_DOWN,
KeyCode::BTN_DPAD_LEFT,
KeyCode::BTN_DPAD_RIGHT,
];
for button in buttons {
events.push(InputEvent::new(evdev::EventType::KEY.0, button.0, 0));
}
events
}