engine/presentation/src/input/controller.rs
2025-03-31 13:48:46 +02:00

304 lines
8.1 KiB
Rust

use crate::Result;
use ui::prelude::*;
use super::controlleraxis::ControllerAxis;
use sdl3::{self, GamepadSubsystem};
#[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: sdl3::gamepad::Gamepad,
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: &GamepadSubsystem,
id: u32,
deadzones: ControllerDeadzones,
) -> Result<Controller> {
let sdl2_controller = controller_subsystem.open(id)?;
let controller_axis = ControllerAxis::default();
Ok(Controller {
name: sdl2_controller.name().unwrap_or_default(),
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,
})
}
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<bool> {
if self.updated_right_trigger {
self.updated_right_trigger = false;
Some(self.last_right_trigger)
} else {
None
}
}
pub fn left_trigger(&mut self) -> Option<bool> {
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 {}