Files
skate-3-rust-engine/crates/skate-game/src/input/platform.rs
T

105 lines
3.3 KiB
Rust

//! Windows device transport. Raw signed axes/trigger bytes reach the TU3
//! converter without Bevy/gilrs deadzones or normalized-axis reconstruction.
use skate_core::input::xbox::XboxState;
pub(crate) struct DevicePacket {
pub number: u32,
pub state: XboxState,
pub subtype: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum DeviceError {
Disconnected,
State(u32),
Capabilities(u32),
#[cfg(not(windows))]
UnsupportedPlatform,
}
#[cfg(windows)]
mod windows {
use super::*;
use std::mem::MaybeUninit;
// ABI from the installed Windows SDK Xinput.h. No OS-owned pointers are
// retained and only successful calls permit reading output storage.
#[repr(C)]
struct Gamepad {
buttons: u16,
left_trigger: u8,
right_trigger: u8,
left_x: i16,
left_y: i16,
right_x: i16,
right_y: i16,
}
#[repr(C)]
struct State {
number: u32,
gamepad: Gamepad,
}
#[repr(C)]
struct Vibration {
left: u16,
right: u16,
}
#[repr(C)]
struct Capabilities {
device_type: u8,
subtype: u8,
flags: u16,
gamepad: Gamepad,
vibration: Vibration,
}
const _: () = assert!(size_of::<Gamepad>() == 12);
const _: () = assert!(size_of::<State>() == 16);
const _: () = assert!(size_of::<Capabilities>() == 20);
#[link(name = "xinput")]
unsafe extern "system" {
fn XInputGetState(index: u32, state: *mut State) -> u32;
fn XInputGetCapabilities(index: u32, flags: u32, capabilities: *mut Capabilities) -> u32;
}
pub(super) fn poll(index: u32) -> Result<DevicePacket, DeviceError> {
let mut state = MaybeUninit::<State>::uninit();
// SAFETY: properly aligned writable storage with the SDK's exact C ABI.
let result = unsafe { XInputGetState(index, state.as_mut_ptr()) };
if result == 1167 {
return Err(DeviceError::Disconnected);
}
if result != 0 {
return Err(DeviceError::State(result));
}
let mut capabilities = MaybeUninit::<Capabilities>::uninit();
// TU3 8296D480 obtains subtype with capability flags=1. A capability
// failure stays explicit; it must not invent the converter's byte 13.
// SAFETY: same output-storage contract as above.
let result = unsafe { XInputGetCapabilities(index, 1, capabilities.as_mut_ptr()) };
if result != 0 {
return Err(DeviceError::Capabilities(result));
}
// SAFETY: both successful SDK calls initialized their complete structs.
let (state, capabilities) = unsafe { (state.assume_init(), capabilities.assume_init()) };
Ok(DevicePacket {
number: state.number,
state: XboxState {
buttons: state.gamepad.buttons,
triggers: [state.gamepad.left_trigger, state.gamepad.right_trigger],
left: [state.gamepad.left_x, state.gamepad.left_y],
right: [state.gamepad.right_x, state.gamepad.right_y],
},
subtype: capabilities.subtype,
})
}
}
pub(crate) fn poll(index: usize) -> Result<DevicePacket, DeviceError> {
assert!(index < 4);
#[cfg(windows)]
return windows::poll(index as u32);
#[cfg(not(windows))]
Err(DeviceError::UnsupportedPlatform)
}