vcs-controller/src/joystick_source.rs
2024-05-20 22:12:15 -06:00

110 lines
3.6 KiB
Rust

use crate::coordinator::{ApplicationEvent, MoveEvent};
use async_channel::Sender;
use gilrs::{ev::filter::FilterFn, Axis, Button, Event, EventType, Filter, Gilrs, GilrsBuilder};
use std::{
panic::{self, AssertUnwindSafe},
sync::{atomic::AtomicBool, Arc},
time::Duration,
};
use tracing::{info, warn};
static MAX_SENT_ZEROS: u32 = 10;
struct UnknownSlayer;
impl FilterFn for UnknownSlayer {
fn filter(&self, ev: Option<Event>, _gilrs: &mut Gilrs) -> Option<Event> {
match ev {
Some(Event {
event: EventType::ButtonPressed(Button::Unknown, ..),
id,
..
})
| Some(Event {
event: EventType::ButtonReleased(Button::Unknown, ..),
id,
..
})
| Some(Event {
event: EventType::AxisChanged(Axis::Unknown, ..),
id,
..
}) => Some(Event::new(id, EventType::Dropped)),
_ => ev,
}
}
}
#[tracing::instrument]
pub async fn joystick_loop(tx: Sender<ApplicationEvent>, is_alive: Arc<AtomicBool>) {
let mut gilrs = GilrsBuilder::new().set_update_state(false).build().unwrap();
is_alive.store(true, std::sync::atomic::Ordering::SeqCst);
let mut curr_x: i32 = 0;
let mut curr_y: i32 = 0;
let mut past_x: i32 = 0;
let mut past_y: i32 = 0;
let mut count_zeros: u32 = 0;
loop {
// catch unwind because some buttons on the joystick will panic the gilrs object
match panic::catch_unwind(AssertUnwindSafe(|| {
// get the next event, and if it is an axis we are interested in, update the
// corresponding variable
while let Some(evt) = gilrs.next_event().filter_ev(&UnknownSlayer {}, &mut gilrs) {
match evt.event {
gilrs::EventType::AxisChanged(gilrs::Axis::LeftStickY, val, _) => {
curr_y = (val * 100.0) as i32;
if curr_y > -10 && curr_y < 10 {
curr_y = 0;
}
}
gilrs::EventType::AxisChanged(gilrs::Axis::LeftStickX, val, _) => {
curr_x = (val * 100.0) as i32;
if curr_x > -10 && curr_x < 10 {
curr_x = 0;
}
}
_ => {}
}
}
})) {
Ok(_) => {}
Err(_) => {
info!("panic-causing event captured in gilrs event handler")
}
}
if (curr_x != past_x || curr_y != past_y) || (count_zeros < MAX_SENT_ZEROS) {
past_x = curr_x;
past_y = curr_y;
if curr_x == 0 && curr_y == 0 {
count_zeros += 1;
} else {
count_zeros = 0;
}
match tx.try_send(ApplicationEvent::MoveEvent(
MoveEvent {
x: curr_x,
y: curr_y,
},
crate::coordinator::ConnectionType::Local,
)) {
Ok(_) => {}
Err(async_channel::TrySendError::Closed(_)) => {
info!("MEC is closed, stopping Joystick loop");
break;
}
Err(async_channel::TrySendError::Full(_)) => {
warn!("The MEC is full!")
}
}
}
tokio::time::sleep(Duration::from_millis(50)).await;
}
is_alive.store(false, std::sync::atomic::Ordering::SeqCst);
}