use crate::backends::GamepadEngineBackend;
use crate::types::*;
use winapi::{
shared::{
minwindef::DWORD,
winerror::{ERROR_DEVICE_NOT_CONNECTED, ERROR_SUCCESS},
},
um::xinput::*,
};
pub(crate) struct XInputBackend {
gamepads: Vec<GamepadState>,
}
impl XInputBackend {
pub fn new() -> Self {
XInputBackend {
gamepads: Vec::new(),
}
}
fn get_input_state(&mut self, i: DWORD) -> Result<XInputState, GamepadError> {
let mut output: XINPUT_STATE = unsafe { ::std::mem::zeroed() };
let result: DWORD = unsafe { XInputGetState(i, &mut output) };
match result {
ERROR_SUCCESS => Ok(XInputState { raw: output }),
ERROR_DEVICE_NOT_CONNECTED => Err(GamepadError::new(
format!("Device not connected for slot {}", i),
ErrorType::GamepadNotConnected { slot: i as u8 },
)),
_ => Err(GamepadError::new(
format!("Error code: {}", result),
ErrorType::Unknown,
)),
}
}
}
impl GamepadEngineBackend for XInputBackend {
fn update(&mut self) -> Result<(), GamepadError> {
let mut gamepads = Vec::new();
for i in 0..XUSER_MAX_COUNT {
match self.get_input_state(i) {
Ok(state) => gamepads.push(state.to_gamepad()),
Err(e) => {
match e.error_type {
ErrorType::GamepadNotConnected { slot: _ } => {} ErrorType::Unknown => return Err(e),
}
}
}
}
for i in 0..self.gamepads.len() {
if let Some(prev_gamepad) = self.gamepads.get(i) {
if let Some(new_gamepad) = gamepads.get_mut(i) {
for (button, button_state) in prev_gamepad.buttons() {
let state = new_gamepad
.buttons
.entry(button.clone())
.or_insert(ButtonState::default());
state.was_pressed = button_state.is_pressed;
}
}
}
}
self.gamepads = gamepads;
Ok(())
}
fn poll_events(&mut self) -> Vec<GamepadEvent> {
Vec::new()
}
fn gamepads(&self) -> &Vec<GamepadState> {
&self.gamepads
}
fn gamepads_mut(&mut self) -> &mut Vec<GamepadState> {
&mut self.gamepads
}
}
pub(crate) struct XInputState {
pub raw: XINPUT_STATE,
}
impl ::std::cmp::PartialEq for XInputState {
fn eq(&self, other: &XInputState) -> bool {
self.raw.dwPacketNumber == other.raw.dwPacketNumber
}
}
impl ::std::cmp::Eq for XInputState {}
impl ::std::fmt::Debug for XInputState {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "XInputState (_)")
}
}
impl XInputState {
pub fn to_gamepad(self) -> GamepadState {
let mut gamepad = GamepadState::new();
let joysticks = &mut gamepad.joysticks;
joysticks.insert(Joystick::Left, JoystickState::new(self.left_stick_raw(), self.left_stick_normalized()));
joysticks.insert(Joystick::Right, JoystickState::new(self.right_stick_raw(), self.right_stick_normalized()));
let buttons = &mut gamepad.buttons;
buttons.insert(Button::DPadNorth, ButtonState::new(self.arrow_up(), false));
buttons.insert(
Button::DPadSouth,
ButtonState::new(self.arrow_down(), false),
);
buttons.insert(
Button::DPadEast,
ButtonState::new(self.arrow_right(), false),
);
buttons.insert(Button::DPadWest, ButtonState::new(self.arrow_left(), false));
buttons.insert(Button::North, ButtonState::new(self.north_button(), false));
buttons.insert(Button::South, ButtonState::new(self.south_button(), false));
buttons.insert(Button::East, ButtonState::new(self.east_button(), false));
buttons.insert(Button::West, ButtonState::new(self.west_button(), false));
buttons.insert(
Button::LeftShoulder,
ButtonState::new(self.left_shoulder(), false),
);
buttons.insert(
Button::LeftTrigger,
ButtonState::new(self.left_trigger_bool(), false),
);
buttons.insert(
Button::RightShoulder,
ButtonState::new(self.right_shoulder(), false),
);
buttons.insert(
Button::RightTrigger,
ButtonState::new(self.right_trigger_bool(), false),
);
buttons.insert(
Button::RightStick,
ButtonState::new(self.right_thumb_button(), false),
);
buttons.insert(
Button::LeftStick,
ButtonState::new(self.left_thumb_button(), false),
);
buttons.insert(
Button::Select,
ButtonState::new(self.select_button(), false),
);
buttons.insert(Button::Start, ButtonState::new(self.start_button(), false));
buttons.insert(Button::Menu, ButtonState::new(false, false));
gamepad
}
#[inline]
pub fn north_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_Y != 0
}
#[inline]
pub fn south_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_A != 0
}
#[inline]
pub fn east_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_B != 0
}
#[inline]
pub fn west_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_X != 0
}
#[inline]
pub fn arrow_up(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP != 0
}
#[inline]
pub fn arrow_down(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN != 0
}
#[inline]
pub fn arrow_left(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT != 0
}
#[inline]
pub fn arrow_right(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT != 0
}
#[inline]
pub fn start_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_START != 0
}
#[inline]
pub fn select_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_BACK != 0
}
#[inline]
pub fn left_shoulder(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER != 0
}
#[inline]
pub fn right_shoulder(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER != 0
}
pub const TRIGGER_THRESHOLD: u8 = XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
#[inline]
pub fn left_trigger(&self) -> u8 {
self.raw.Gamepad.bLeftTrigger
}
#[inline]
pub fn right_trigger(&self) -> u8 {
self.raw.Gamepad.bRightTrigger
}
#[inline]
pub fn left_trigger_bool(&self) -> bool {
self.left_trigger() >= XInputState::TRIGGER_THRESHOLD
}
#[inline]
pub fn right_trigger_bool(&self) -> bool {
self.right_trigger() >= XInputState::TRIGGER_THRESHOLD
}
#[inline]
pub fn left_thumb_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB != 0
}
#[inline]
pub fn right_thumb_button(&self) -> bool {
self.raw.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB != 0
}
pub const LEFT_STICK_DEADZONE: i16 = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE;
pub const RIGHT_STICK_DEADZONE: i16 = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
#[inline]
pub fn left_stick_raw(&self) -> (i16, i16) {
(self.raw.Gamepad.sThumbLX, self.raw.Gamepad.sThumbLY)
}
#[inline]
pub fn right_stick_raw(&self) -> (i16, i16) {
(self.raw.Gamepad.sThumbRX, self.raw.Gamepad.sThumbRY)
}
#[inline]
pub fn left_stick_normalized(&self) -> (f32, f32) {
XInputState::normalize_raw_stick_value(
self.left_stick_raw(),
XInputState::LEFT_STICK_DEADZONE,
)
}
#[inline]
pub fn right_stick_normalized(&self) -> (f32, f32) {
XInputState::normalize_raw_stick_value(
self.right_stick_raw(),
XInputState::RIGHT_STICK_DEADZONE,
)
}
#[inline]
pub fn normalize_raw_stick_value(raw_stick: (i16, i16), deadzone: i16) -> (f32, f32) {
let deadzone_float = deadzone.max(0).min(i16::max_value() - 1) as f32;
let raw_float = (raw_stick.0 as f32, raw_stick.1 as f32);
let length = (raw_float.0 * raw_float.0 + raw_float.1 * raw_float.1).sqrt();
let normalized = (raw_float.0 / length, raw_float.1 / length);
if length > deadzone_float {
let length = length.min(32_767.0);
let scale = (length - deadzone_float) / (32_767.0 - deadzone_float);
(normalized.0 * scale, normalized.1 * scale)
} else {
(0.0, 0.0)
}
}
}