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, _gilrs: &mut Gilrs) -> Option { 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, is_alive: Arc) { 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); }