xinput/functions/get_state_.rs
1use crate::*;
2
3use bytemuck::Zeroable;
4
5
6
7/// \[[microsoft.com](https://learn.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputgetstate)\]
8/// XInputGetState
9///
10/// Retrieves the current state of the specified controller.
11///
12/// ### Arguments
13/// * `user_index` — The controller to get the state of (<code>0 .. [xuser::MAX_COUNT]</code>.)
14///
15/// ### Example
16/// ```rust
17/// let gamepad = 0;
18/// let state : xinput::State = xinput::get_state(gamepad).unwrap_or_default();
19/// println!("{state:#?}");
20/// ```
21///
22/// ### Output
23/// ```text
24/// State {
25/// packet_number: 305,
26/// gamepad: Gamepad {
27/// buttons: Buttons::None,
28/// left_trigger: 0,
29/// right_trigger: 0,
30/// left_thumb_x: 2479,
31/// left_thumb_y: -707,
32/// right_thumb_x: -48,
33/// right_thumb_y: -1028,
34/// },
35/// }
36/// ```
37///
38/// ### Errors
39/// * [error::BAD_ARGUMENTS] - Invalid `user_index` (expected <code>0 .. [xuser::MAX_COUNT]</code>)
40/// * [error::DEVICE_NOT_CONNECTED] - No gamepad connected for `user_index`.
41/// * [error::INVALID_FUNCTION] - API unavailable: XInput not loaded
42pub fn get_state(user_index: impl TryInto<u32>) -> Result<State, Error> {
43 fn_context!(xinput::get_state => XInputGetState);
44 #[allow(non_snake_case)] let XInputGetState = imports::XInputGetState.load(core::sync::atomic::Ordering::Relaxed);
45 let user_index = user_index.try_into().map_err(|_| fn_param_error!(user_index, error::BAD_ARGUMENTS))?;
46
47 let mut state = State::zeroed();
48 // SAFETY: ✔️
49 // * fuzzed in `tests/fuzz-xinput.rs`
50 // * tested in `examples/d3d9-02-xinput.rs`
51 // * `user_index` is well tested
52 // * `state` is out-only, fixed size, no `cbSize` field, never null, all bit patterns sane
53 let code = unsafe { XInputGetState(user_index, state.as_mut()) };
54 check_success!(code)?;
55 Ok(state)
56}
57
58#[test] fn test_valid_params() {
59 for user_index in 0 .. 4 {
60 if let Err(err) = get_state(user_index) {
61 assert!(matches!(err.kind(), error::DEVICE_NOT_CONNECTED | error::CO_E_NOTINITIALIZED), "unexpected error type: {err:?}");
62 }
63 }
64}
65
66#[test] fn test_bad_user_index() {
67 for user_index in xuser::invalids().chain(Some(xuser::INDEX_ANY)) {
68 let err = get_state(user_index).expect_err("expected error for invalid user_index");
69 assert!(matches!(err.kind(), error::BAD_ARGUMENTS | error::CO_E_NOTINITIALIZED), "unexpected error type: {err:?}");
70 }
71}