XInput: Fix rumble overload by limiting on state changes and interval

This commit is contained in:
Ryunam 2025-05-28 17:20:33 +02:00
parent 4e30df6392
commit bd9561d93c
2 changed files with 44 additions and 8 deletions

View File

@ -31,6 +31,7 @@
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#include <boolean.h>
#include <retro_inline.h>
@ -68,6 +69,8 @@ typedef struct
bool connected;
} xinput_joypad_state;
#define RUMBLE_INTERVAL 0.005
/* TODO/FIXME - static globals */
static int g_xinput_pad_indexes[MAX_USERS];
static unsigned g_last_xinput_pad_idx = 0;
@ -90,6 +93,7 @@ static XINPUT_FEEDBACK g_xinput_rumble_states[4];
static XINPUT_VIBRATION g_xinput_rumble_states[4];
#endif
static xinput_joypad_state g_xinput_states[4];
static clock_t last_rumble_time[4] = {0};
/* Buttons are provided by XInput as bits of a uint16.
* Map from rarch button index (0..10) to a mask to
@ -646,17 +650,31 @@ static bool xinput_joypad_rumble(unsigned pad,
if (xuser == -1)
return dinput_joypad_set_rumble(pad, effect, strength);
XINPUT_VIBRATION *state = &g_xinput_rumble_states[xuser];
XINPUT_VIBRATION new_state = *state;
/* Consider the low frequency (left) motor the "strong" one. */
if (effect == RETRO_RUMBLE_STRONG)
g_xinput_rumble_states[xuser].wLeftMotorSpeed = strength;
new_state.wLeftMotorSpeed = strength;
else if (effect == RETRO_RUMBLE_WEAK)
g_xinput_rumble_states[xuser].wRightMotorSpeed = strength;
new_state.wRightMotorSpeed = strength;
bool rumble_state_unchanged = ((new_state.wLeftMotorSpeed == state->wLeftMotorSpeed) &&
(new_state.wRightMotorSpeed == state->wRightMotorSpeed));
clock_t now = clock();
double time_since_last_rumble = (double)(now - last_rumble_time[xuser]) / CLOCKS_PER_SEC;
bool rumble_interval_unelapsed = (time_since_last_rumble < RUMBLE_INTERVAL);
if (rumble_state_unchanged || rumble_interval_unelapsed)
return true;
if (!g_XInputSetState)
return false;
return (g_XInputSetState(xuser, &g_xinput_rumble_states[xuser])
== 0);
*state = new_state;
last_rumble_time[xuser] = now;
return (g_XInputSetState(xuser, state) == ERROR_SUCCESS);
}
static void xinput_joypad_destroy(void)

View File

@ -25,6 +25,7 @@
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#include <boolean.h>
#include <retro_inline.h>
@ -51,6 +52,8 @@ typedef struct
bool connected;
} xinput_joypad_state;
#define RUMBLE_INTERVAL 0.005
/* Function pointer, to be assigned with dylib_proc */
typedef uint32_t (__stdcall *XInputGetStateEx_t)(uint32_t, XINPUT_STATE*);
typedef uint32_t (__stdcall *XInputSetState_t)(uint32_t, XINPUT_VIBRATION*);
@ -71,6 +74,7 @@ static XINPUT_FEEDBACK g_xinput_rumble_states[4];
static XINPUT_VIBRATION g_xinput_rumble_states[4];
#endif
static xinput_joypad_state g_xinput_states[4];
static clock_t last_rumble_time[4] = {0};
/* Buttons are provided by XInput as bits of a uint16.
* Map from rarch button index (0..10) to a mask to
@ -371,17 +375,31 @@ static bool xinput_joypad_rumble(unsigned pad,
if (xuser == -1)
return false;
XINPUT_VIBRATION *state = &g_xinput_rumble_states[xuser];
XINPUT_VIBRATION new_state = *state;
/* Consider the low frequency (left) motor the "strong" one. */
if (effect == RETRO_RUMBLE_STRONG)
g_xinput_rumble_states[xuser].wLeftMotorSpeed = strength;
new_state.wLeftMotorSpeed = strength;
else if (effect == RETRO_RUMBLE_WEAK)
g_xinput_rumble_states[xuser].wRightMotorSpeed = strength;
new_state.wRightMotorSpeed = strength;
bool rumble_state_unchanged = ((new_state.wLeftMotorSpeed == state->wLeftMotorSpeed) &&
(new_state.wRightMotorSpeed == state->wRightMotorSpeed));
clock_t now = clock();
double time_since_last_rumble = (double)(now - last_rumble_time[xuser]) / CLOCKS_PER_SEC;
bool rumble_interval_unelapsed = (time_since_last_rumble < RUMBLE_INTERVAL);
if (rumble_state_unchanged || rumble_interval_unelapsed)
return true;
if (!g_XInputSetState)
return false;
return (g_XInputSetState(xuser, &g_xinput_rumble_states[xuser])
== 0);
*state = new_state;
last_rumble_time[xuser] = now;
return (g_XInputSetState(xuser, state) == ERROR_SUCCESS);
}
input_device_driver_t xinput_joypad = {