mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
(xinput) Clear stale pad state when XInputGetState fails
Companion to fda77fa, which fixed the equivalent issue in dinput.
Both xinput_joypad.c and xinput_hybrid_joypad.c poll into the cached
state directly and only inspect the result to detect connect/disconnect
transitions:
status = g_XInputGetStateEx(i, &state->xstate);
success = status == ERROR_SUCCESS;
new_connected = status != ERROR_DEVICE_NOT_CONNECTED;
if (new_connected != state->connected)
{
...
}
'success' is computed but only ever read inside that branch, so when a
call fails for any reason other than ERROR_DEVICE_NOT_CONNECTED the pad
remains marked connected and 'state->xstate' keeps whatever was read
last. XInput can return ERROR_ACCESS_DENIED or ERROR_BUSY, and
transient failures occur during device arbitration - for instance when
another process takes exclusive access, or across a fast user switch.
xinput_joypad_button() gates only on state->connected, then reads
state->xstate.Gamepad.wButtons, which carries the XINPUT_GAMEPAD_DPAD_*
bits. A direction held at the time of the last good read therefore
continues to be reported for as long as the failure persists.
Read into a temporary and copy it into the cached state only on
success, zeroing the cache otherwise. Reading into a temporary also
avoids leaving the cached state indeterminate should the call fail
after partially writing the output buffer.
Unlike the dinput POV case, a plain memset is correct here:
XINPUT_GAMEPAD.wButtons uses zero to mean no buttons pressed, whereas
DirectInput's rgdwPOV[] uses zero to mean north.
winraw_joypad.c was checked and does not share this defect: it is
event-driven via WM_INPUT with RIDEV_INPUTSINK, so it continues to
receive reports while unfocused, and its cached hats[] hold converted
bitmasks where zero already means centred.
This commit is contained in:
1 parent
b37da8ce41
commit
8b85429fcc
2 files changed
+36
-2
No files matched your search
@@ -1149,6 +1149,7 @@ static void xinput_joypad_poll(void)
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
DWORD status;
|
||||
XINPUT_STATE tmp_xstate;
|
||||
bool success, new_connected;
|
||||
xinput_joypad_state *state;
|
||||
/* On UWP, controllers may become available after initialization.
|
||||
@@ -1163,9 +1164,22 @@ static void xinput_joypad_poll(void)
|
||||
#endif
|
||||
|
||||
state = &g_xinput_states[i];
|
||||
status = g_XInputGetStateEx(i, &state->xstate);
|
||||
/* Read into a temporary: XInputGetStateEx() writing directly
|
||||
* into the cached state would leave it indeterminate if the
|
||||
* call fails partway. */
|
||||
status = g_XInputGetStateEx(i, &tmp_xstate);
|
||||
success = (status == ERROR_SUCCESS);
|
||||
new_connected = (status != ERROR_DEVICE_NOT_CONNECTED);
|
||||
|
||||
if (success)
|
||||
state->xstate = tmp_xstate;
|
||||
else
|
||||
/* Any status other than ERROR_DEVICE_NOT_CONNECTED leaves
|
||||
* the pad marked connected, so the cached state would keep
|
||||
* reporting the buttons held at the last good read - see
|
||||
* the matching comment in xinput_joypad.c */
|
||||
memset(&state->xstate, 0, sizeof(state->xstate));
|
||||
|
||||
if (new_connected != state->connected)
|
||||
{
|
||||
state->connected = new_connected;
|
||||
|
||||
@@ -575,6 +575,7 @@ static void xinput_joypad_poll(void)
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
DWORD status;
|
||||
XINPUT_STATE tmp_xstate;
|
||||
bool success, new_connected;
|
||||
xinput_joypad_state *state;
|
||||
/* On UWP, controllers may become available after initialization.
|
||||
@@ -589,10 +590,29 @@ static void xinput_joypad_poll(void)
|
||||
#endif
|
||||
|
||||
state = &g_xinput_states[i];
|
||||
status = g_XInputGetStateEx(i, &state->xstate);
|
||||
/* Read into a temporary: XInputGetStateEx() writing directly
|
||||
* into the cached state would leave it indeterminate if the
|
||||
* call fails partway. */
|
||||
status = g_XInputGetStateEx(i, &tmp_xstate);
|
||||
success = status == ERROR_SUCCESS;
|
||||
new_connected = status != ERROR_DEVICE_NOT_CONNECTED;
|
||||
|
||||
if (success)
|
||||
state->xstate = tmp_xstate;
|
||||
else
|
||||
{
|
||||
/* The pad is still considered connected for any status
|
||||
* other than ERROR_DEVICE_NOT_CONNECTED (access denied,
|
||||
* busy, transient failures during device arbitration),
|
||||
* so the cached state would otherwise keep reporting
|
||||
* whatever was held at the time of the last good read -
|
||||
* including the Gamepad.wButtons D-pad bits, which
|
||||
* xinput_joypad_button() consumes with no staleness
|
||||
* check. Treat an unreadable pad as having nothing
|
||||
* pressed. */
|
||||
memset(&state->xstate, 0, sizeof(state->xstate));
|
||||
}
|
||||
|
||||
if (new_connected != state->connected)
|
||||
{
|
||||
/* Normally, dinput handles device insertion/removal for us, but
|
||||
|
||||
Reference in new issue
Block a user