use crate::Result; use ui::prelude::*; use super::controlleraxis::ControllerAxis; use sdl2::{self, GameControllerSubsystem}; #[derive(PartialEq)] enum State { Disable, Between(f32), Enable(f32), } #[derive(Debug, Clone)] pub struct ControllerDeadzones { pub axis_enable_deadzone: f32, pub axis_disable_deadzone: f32, pub trigger_enable_deadzone: f32, pub trigger_disable_deadzone: f32, } impl Default for ControllerDeadzones { fn default() -> Self { ControllerDeadzones { axis_enable_deadzone: 0.25, axis_disable_deadzone: 0.15, trigger_enable_deadzone: 0.75, trigger_disable_deadzone: 0.5, } } } pub struct Controller { _sdl2_controller: sdl2::controller::GameController, name: String, id: u32, controller_axis: ControllerAxis, last_direction: GuiDirection, updated_left_trigger: bool, last_left_trigger: bool, updated_right_trigger: bool, last_right_trigger: bool, deadzones: ControllerDeadzones, } impl Controller { pub fn new( controller_subsystem: &GameControllerSubsystem, id: u32, deadzones: ControllerDeadzones, ) -> Result { if controller_subsystem.is_game_controller(id) { let sdl2_controller = controller_subsystem.open(id)?; let controller_axis = ControllerAxis::default(); Ok(Controller { name: sdl2_controller.name(), id, _sdl2_controller: sdl2_controller, controller_axis, last_direction: GuiDirection::None, updated_left_trigger: false, last_left_trigger: false, updated_right_trigger: false, last_right_trigger: false, deadzones, }) } else { Err(anyhow::Error::msg("Not a game controller")) } } pub fn name(&self) -> &String { &self.name } pub fn id(&self) -> u32 { self.id } #[inline] fn state(v: f32, enable_deadzone: f32, disable_deadzone: f32) -> State { if v.abs() < disable_deadzone { State::Disable } else if v.abs() > enable_deadzone { State::Enable(v) } else { State::Between(v) } } fn check_direction(&self, x: f32, y: f32) -> GuiDirection { let x_state = Self::state( x, self.deadzones.axis_enable_deadzone, self.deadzones.axis_disable_deadzone, ); let y_state = Self::state( y, self.deadzones.axis_enable_deadzone, self.deadzones.axis_disable_deadzone, ); match (x_state, y_state) { (State::Disable, State::Disable) => GuiDirection::None, (State::Disable, State::Between(_)) => self.last_direction, (State::Between(_), State::Disable) => self.last_direction, (State::Between(_), State::Between(_)) => self.last_direction, (State::Disable, State::Enable(y)) => { if y < 0.0 { GuiDirection::Down } else { GuiDirection::Up } } (State::Enable(x), State::Disable) => { if x < 0.0 { GuiDirection::Left } else { GuiDirection::Right } } (State::Between(_), State::Enable(y)) => { if y < 0.0 { GuiDirection::Down } else { GuiDirection::Up } } (State::Enable(x), State::Between(_)) => { if x < 0.0 { GuiDirection::Left } else { GuiDirection::Right } } (State::Enable(x), State::Enable(y)) => { if x < 0.0 { if y < 0.0 { if x < y { GuiDirection::Down } else { GuiDirection::Left } } else { if x.abs() < y { GuiDirection::Up } else { GuiDirection::Left } } } else { if x < 0.0 { if x < y.abs() { GuiDirection::Down } else { GuiDirection::Right } } else { if x < y { GuiDirection::Up } else { GuiDirection::Right } } } } } } fn check_trigger(&self, value: f32, last: bool) -> bool { let state = Self::state( value, self.deadzones.trigger_enable_deadzone, self.deadzones.trigger_disable_deadzone, ); match state { State::Disable => false, State::Enable(_) => true, State::Between(_) => last, } } pub(crate) fn set_left_x(&mut self, x: f32) { self.controller_axis.left_stick.x = x; let direction = self.check_direction( self.controller_axis.left_stick.x, self.controller_axis.left_stick.y, ); if direction != self.last_direction { self.last_direction = direction; } } pub(crate) fn set_left_y(&mut self, y: f32) { self.controller_axis.left_stick.y = y; let direction = self.check_direction( self.controller_axis.left_stick.x, self.controller_axis.left_stick.y, ); if direction != self.last_direction { self.last_direction = direction; } } pub(crate) fn set_right_x(&mut self, x: f32) { self.controller_axis.right_stick.x = x; } pub(crate) fn set_right_y(&mut self, y: f32) { self.controller_axis.right_stick.y = y; } pub(crate) fn set_left_trigger(&mut self, trigger: f32) { self.controller_axis.left_trigger = trigger; let left_trigger = self.check_trigger(trigger, self.last_left_trigger); if left_trigger != self.last_left_trigger { self.updated_left_trigger = true; self.last_left_trigger = left_trigger; } } pub(crate) fn set_right_trigger(&mut self, trigger: f32) { self.controller_axis.right_trigger = trigger; let right_trigger = self.check_trigger(trigger, self.last_right_trigger); if right_trigger != self.last_right_trigger { self.updated_right_trigger = true; self.last_right_trigger = right_trigger; } } pub fn controller_axis(&self) -> ControllerAxis { self.controller_axis } pub fn direction(&self) -> GuiDirection { self.last_direction } pub fn right_trigger(&mut self) -> Option { if self.updated_right_trigger { self.updated_right_trigger = false; Some(self.last_right_trigger) } else { None } } pub fn left_trigger(&mut self) -> Option { if self.updated_left_trigger { self.updated_left_trigger = false; Some(self.last_left_trigger) } else { None } } } impl std::fmt::Debug for Controller { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Controller") .field("name", &self.name) .field("axis_enable_deadzone", &self.deadzones.axis_enable_deadzone) .field( "axis_disable_deadzone", &self.deadzones.axis_disable_deadzone, ) .field( "trigger_enable_deadzone", &self.deadzones.trigger_enable_deadzone, ) .field( "trigger_disable_deadzone", &self.deadzones.trigger_disable_deadzone, ) .field("id", &self.id) .finish() } } unsafe impl Send for Controller {} unsafe impl Sync for Controller {}