use gilrs::{Button, Event, EventType, GamepadId, Gilrs};
use crate::prelude::Controller::{Playstation, Switch, Xbox};
pub use gilrs::Error;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug)]
pub struct GameController {
gilrs: Gilrs,
pub direction: DirectionState,
pub action: ActionState,
pub menu: MenuState,
last_connected: Option<GamepadId>,
}
impl GameController {
pub fn new() -> Result<Self, Error> {
Ok(GameController {
gilrs: Gilrs::new()?,
direction: DirectionState::default(),
action: ActionState::default(),
menu: MenuState::default(),
last_connected: None,
})
}
pub fn new_unchecked() -> Self {
Self::new().expect("Failed to init controller")
}
pub fn from_state(
direction: DirectionState,
action: ActionState,
menu: MenuState,
) -> Result<Self, Error> {
Ok(GameController {
gilrs: Gilrs::new()?,
direction,
action,
menu,
last_connected: None,
})
}
pub fn from_state_unchecked(
direction: DirectionState,
action: ActionState,
menu: MenuState,
) -> Self {
Self::from_state(direction, action, menu).expect("Failed to init controller")
}
}
impl GameController {
pub fn any_connected(&self) -> bool {
self.gilrs.gamepads().any(|(_, pad)| pad.is_connected())
}
pub fn to_state(&self) -> (DirectionState, ActionState, MenuState) {
(
self.direction.clone(),
self.action.clone(),
self.menu.clone(),
)
}
pub fn mask(&self) -> u16 {
self.menu.to_mask() | self.action.to_mask() | self.direction.to_mask()
}
pub fn update(&mut self) {
while let Some(Event {
id, event, time: _, ..
}) = self.gilrs.next_event()
{
match event {
EventType::ButtonPressed(button, _code) => self.set_state(button, true),
EventType::ButtonReleased(button, _code) => {
self.last_connected = Some(id);
self.set_state(button, false);
}
EventType::Disconnected => {
if self.last_connected == Some(id) {
self.last_connected = None;
}
}
_ => {}
}
}
}
fn set_state(&mut self, button: Button, pressed: bool) {
match button {
Button::South => self.action.south = pressed,
Button::East => self.action.east = pressed,
Button::North => self.action.north = pressed,
Button::West => self.action.west = pressed,
Button::C => {}
Button::Z => {}
Button::LeftTrigger => {}
Button::LeftTrigger2 => {}
Button::RightTrigger => {}
Button::RightTrigger2 => {}
Button::Select => {}
Button::Start => self.menu.start = pressed,
Button::Mode => {}
Button::LeftThumb => {}
Button::RightThumb => {}
Button::DPadUp => self.direction.up = pressed,
Button::DPadDown => self.direction.down = pressed,
Button::DPadLeft => self.direction.left = pressed,
Button::DPadRight => self.direction.right = pressed,
Button::Unknown => {}
}
}
pub fn get_controller_type(&self) -> Option<Controller> {
if let Some(id) = self.last_connected {
let uuid = self.gilrs.gamepad(id).uuid();
let vendor = u16::from_le_bytes([uuid[4], uuid[5]]);
let product = u16::from_le_bytes([uuid[8], uuid[9]]);
match (vendor, product) {
(0x54c, 0xdf2 | 0xce6 | 0xcda | 0x9cc | 0x5c4 | 0x268) => Some(Playstation),
(
0x45e,
0x202 | 0x285 | 0x289 | 0x28e | 0x28f | 0x2d1 | 0x2dd | 0x2e0 | 0x2e3 | 0x2ea
| 0x2fd | 0xb12 | 0xb00,
) => Some(Xbox),
(0x57e, 0x2009) => Some(Switch),
_ => None,
}
} else {
None
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Controller {
Playstation,
Xbox,
Switch,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct DirectionState {
pub up: bool,
pub down: bool,
pub left: bool,
pub right: bool,
}
impl DirectionState {
pub fn to_mask(&self) -> u16 {
(if self.up { CNTR_MASK_UP } else { 0 })
| (if self.down { CNTR_MASK_DOWN } else { 0 })
| (if self.left { CNTR_MASK_LEFT } else { 0 })
| (if self.right { CNTR_MASK_RIGHT } else { 0 })
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct MenuState {
pub start: bool,
}
impl MenuState {
pub fn to_mask(&self) -> u16 {
if self.start {
CNTR_MASK_START
} else {
0
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct ActionState {
pub north: bool,
pub south: bool,
pub west: bool,
pub east: bool,
}
impl ActionState {
pub fn to_mask(&self) -> u16 {
(if self.north { CNTR_MASK_NORTH } else { 0 })
| (if self.south { CNTR_MASK_SOUTH } else { 0 })
| (if self.west { CNTR_MASK_WEST } else { 0 })
| (if self.east { CNTR_MASK_EAST } else { 0 })
}
}
pub const CNTR_MASK_UP: u16 = 0b10000000_00000000;
pub const CNTR_MASK_DOWN: u16 = 0b01000000_00000000;
pub const CNTR_MASK_LEFT: u16 = 0b00100000_00000000;
pub const CNTR_MASK_RIGHT: u16 = 0b00010000_00000000;
pub const CNTR_MASK_START: u16 = 0b00000001_00000000;
pub const CNTR_MASK_NORTH: u16 = 0b00000000_00001000;
pub const CNTR_MASK_SOUTH: u16 = 0b00000000_00000100;
pub const CNTR_MASK_EAST: u16 = 0b00000000_00000010;
pub const CNTR_MASK_WEST: u16 = 0b00000000_00000001;
#[cfg(test)]
mod test {
use crate::prelude::{GameController, CNTR_MASK_EAST, CNTR_MASK_UP};
#[test]
fn check_mask() {
let mut controller = GameController::new_unchecked();
controller.direction.up = true;
assert_eq!(controller.mask(), CNTR_MASK_UP);
controller.action.east = true;
assert_eq!(controller.mask(), CNTR_MASK_UP | CNTR_MASK_EAST);
}
}