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`    &mdash; 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}