mirror of
https://github.com/SK8-ENGINE/skate-3-rust-engine.git
synced 2026-10-26 19:47:44 +00:00
105 lines
3.3 KiB
Rust
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)
|
|
}
|