use crate::shared::*;
use gilrs::ev::state::AxisData;
use gilrs::*;
pub(crate) struct GamepadManager {
gilrs: Gilrs,
gamepad_id: Option<GamepadId>,
input: InputState,
}
impl GamepadManager {
pub fn new() -> Self {
let mut gilrs = Gilrs::new().unwrap();
let gamepad_id = gilrs.next_event().map(|Event { id, .. }| id);
Self {
gilrs,
gamepad_id,
input: InputState::default(),
}
}
pub fn update_input(&mut self, input: InputState) {
self.input = input;
}
pub fn read_input(&mut self) -> Option<InputState> {
if self.gamepad_id.is_none() {
self.gamepad_id = self.gilrs.next_event().map(|Event { id, .. }| id);
}
while self.gilrs.next_event().is_some() {}
let Some(gamepad_id) = self.gamepad_id else {
return Some(self.input.clone());
};
let gamepad = self.gilrs.connected_gamepad(gamepad_id)?;
let pad = read_pad(gamepad);
let buttons_array = [
gamepad.is_pressed(Button::South), gamepad.is_pressed(Button::East), gamepad.is_pressed(Button::West), gamepad.is_pressed(Button::North), gamepad.is_pressed(Button::Start),
];
let mut buttons = 0u8;
for b in buttons_array.into_iter().rev() {
buttons = buttons << 1 | u8::from(b);
}
let buttons = self.input.buttons | buttons;
let pad = match pad {
Some(pad) => Some(pad),
None => self.input.pad.clone(),
};
Some(InputState { pad, buttons })
}
}
fn read_pad(gamepad: Gamepad<'_>) -> Option<Pad> {
if let Some(pad) = read_dpad(gamepad) {
return Some(pad);
}
if let Some(pad) = read_left_pad(gamepad) {
return Some(pad);
}
if let Some(pad) = read_right_pad(gamepad) {
return Some(pad);
}
None
}
fn read_dpad(gamepad: Gamepad<'_>) -> Option<Pad> {
if gamepad.is_pressed(Button::DPadDown) {
return Some(Pad { x: 0, y: -1000 });
}
if gamepad.is_pressed(Button::DPadUp) {
return Some(Pad { x: 0, y: 1000 });
}
if gamepad.is_pressed(Button::DPadLeft) {
return Some(Pad { x: -1000, y: 0 });
}
if gamepad.is_pressed(Button::DPadRight) {
return Some(Pad { x: 1000, y: 0 });
}
None
}
fn read_left_pad(gamepad: Gamepad<'_>) -> Option<Pad> {
let is_precise = gamepad.product_id() == Some(0x1337);
let pad_pressed =
gamepad.is_pressed(Button::LeftTrigger) | gamepad.is_pressed(Button::LeftThumb);
if is_precise || pad_pressed {
let maybe_point = make_point(
gamepad.axis_data(Axis::LeftStickX),
gamepad.axis_data(Axis::LeftStickY),
);
let point = maybe_point?;
if is_precise && point.x == 0 && point.y == 0 {
return None;
}
return Some(point);
};
None
}
fn read_right_pad(gamepad: Gamepad<'_>) -> Option<Pad> {
let point = make_point(
gamepad.axis_data(Axis::RightStickX),
gamepad.axis_data(Axis::RightStickY),
);
let point = point?;
if gamepad.is_pressed(Button::RightThumb) {
return Some(point);
}
let is_precise = gamepad.vendor_id() == Some(0x28de);
if is_precise {
if point.x == 0 && point.y == 0 {
return None;
}
return Some(point);
}
let x_zero = (-50..=50).contains(&point.x);
let y_zero = (-50..=50).contains(&point.y);
if x_zero && y_zero {
return None;
}
Some(point)
}
fn make_point(x: Option<&AxisData>, y: Option<&AxisData>) -> Option<Pad> {
let x = data_to_i16(x);
let y = data_to_i16(y);
match (x, y) {
(Some(x), Some(y)) => Some(Pad { x, y }),
_ => None,
}
}
fn data_to_i16(v: Option<&AxisData>) -> Option<i16> {
let v = v?;
let v = v.value();
let r = (v * 1000.) as i16;
Some(r)
}