304 lines
8.1 KiB
Rust
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 {}
|