mirror of https://github.com/encounter/SDL.git
Make sure we don't send Xbox controllers rumble so quickly that it overwhelms the firmware
Fixes https://github.com/libsdl-org/SDL/issues/6435
This commit is contained in:
parent
38af459dd9
commit
6875e62af3
|
@ -36,6 +36,8 @@ typedef struct SDL_HIDAPI_RumbleRequest
|
||||||
SDL_HIDAPI_Device *device;
|
SDL_HIDAPI_Device *device;
|
||||||
Uint8 data[2*USB_PACKET_LENGTH]; /* need enough space for the biggest report: dualshock4 is 78 bytes */
|
Uint8 data[2*USB_PACKET_LENGTH]; /* need enough space for the biggest report: dualshock4 is 78 bytes */
|
||||||
int size;
|
int size;
|
||||||
|
SDL_HIDAPI_RumbleSentCallback callback;
|
||||||
|
void *userdata;
|
||||||
struct SDL_HIDAPI_RumbleRequest *prev;
|
struct SDL_HIDAPI_RumbleRequest *prev;
|
||||||
|
|
||||||
} SDL_HIDAPI_RumbleRequest;
|
} SDL_HIDAPI_RumbleRequest;
|
||||||
|
@ -83,6 +85,9 @@ static int SDLCALL SDL_HIDAPI_RumbleThread(void *data)
|
||||||
SDL_hid_write(request->device->dev, request->data, request->size);
|
SDL_hid_write(request->device->dev, request->data, request->size);
|
||||||
}
|
}
|
||||||
SDL_UnlockMutex(request->device->dev_lock);
|
SDL_UnlockMutex(request->device->dev_lock);
|
||||||
|
if (request->callback) {
|
||||||
|
request->callback(request->userdata);
|
||||||
|
}
|
||||||
(void)SDL_AtomicDecRef(&request->device->rumble_pending);
|
(void)SDL_AtomicDecRef(&request->device->rumble_pending);
|
||||||
SDL_free(request);
|
SDL_free(request);
|
||||||
|
|
||||||
|
@ -116,6 +121,9 @@ SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
|
||||||
}
|
}
|
||||||
ctx->requests_tail = request->prev;
|
ctx->requests_tail = request->prev;
|
||||||
|
|
||||||
|
if (request->callback) {
|
||||||
|
request->callback(request->userdata);
|
||||||
|
}
|
||||||
(void)SDL_AtomicDecRef(&request->device->rumble_pending);
|
(void)SDL_AtomicDecRef(&request->device->rumble_pending);
|
||||||
SDL_free(request);
|
SDL_free(request);
|
||||||
}
|
}
|
||||||
|
@ -192,6 +200,11 @@ SDL_bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **da
|
||||||
}
|
}
|
||||||
|
|
||||||
int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
|
int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
|
||||||
|
{
|
||||||
|
return SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(device, data, size, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size, SDL_HIDAPI_RumbleSentCallback callback, void *userdata)
|
||||||
{
|
{
|
||||||
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
|
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
|
||||||
SDL_HIDAPI_RumbleRequest *request;
|
SDL_HIDAPI_RumbleRequest *request;
|
||||||
|
@ -209,6 +222,8 @@ int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data,
|
||||||
request->device = device;
|
request->device = device;
|
||||||
SDL_memcpy(request->data, data, size);
|
SDL_memcpy(request->data, data, size);
|
||||||
request->size = size;
|
request->size = size;
|
||||||
|
request->callback = callback;
|
||||||
|
request->userdata = userdata;
|
||||||
|
|
||||||
SDL_AtomicIncRef(&device->rumble_pending);
|
SDL_AtomicIncRef(&device->rumble_pending);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
int SDL_HIDAPI_LockRumble(void);
|
int SDL_HIDAPI_LockRumble(void);
|
||||||
SDL_bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size);
|
SDL_bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size);
|
||||||
int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size);
|
int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size);
|
||||||
|
typedef void (*SDL_HIDAPI_RumbleSentCallback)(void *userdata);
|
||||||
|
int SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size, SDL_HIDAPI_RumbleSentCallback callback, void *userdata);
|
||||||
void SDL_HIDAPI_UnlockRumble(void);
|
void SDL_HIDAPI_UnlockRumble(void);
|
||||||
|
|
||||||
/* Simple API, will replace any pending rumble with the new data */
|
/* Simple API, will replace any pending rumble with the new data */
|
||||||
|
|
|
@ -100,12 +100,18 @@ static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
XBOX_ONE_INIT_STATE_START_NEGOTIATING = 0,
|
XBOX_ONE_INIT_STATE_START_NEGOTIATING,
|
||||||
XBOX_ONE_INIT_STATE_NEGOTIATING = 1,
|
XBOX_ONE_INIT_STATE_NEGOTIATING,
|
||||||
XBOX_ONE_INIT_STATE_PREPARE_INPUT = 2,
|
XBOX_ONE_INIT_STATE_PREPARE_INPUT,
|
||||||
XBOX_ONE_INIT_STATE_COMPLETE = 3
|
XBOX_ONE_INIT_STATE_COMPLETE,
|
||||||
} SDL_XboxOneInitState;
|
} SDL_XboxOneInitState;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
XBOX_ONE_RUMBLE_STATE_IDLE,
|
||||||
|
XBOX_ONE_RUMBLE_STATE_QUEUED,
|
||||||
|
XBOX_ONE_RUMBLE_STATE_BUSY
|
||||||
|
} SDL_XboxOneRumbleState;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SDL_HIDAPI_Device *device;
|
SDL_HIDAPI_Device *device;
|
||||||
Uint16 vendor_id;
|
Uint16 vendor_id;
|
||||||
|
@ -125,6 +131,9 @@ typedef struct {
|
||||||
Uint8 high_frequency_rumble;
|
Uint8 high_frequency_rumble;
|
||||||
Uint8 left_trigger_rumble;
|
Uint8 left_trigger_rumble;
|
||||||
Uint8 right_trigger_rumble;
|
Uint8 right_trigger_rumble;
|
||||||
|
SDL_XboxOneRumbleState rumble_state;
|
||||||
|
Uint32 rumble_time;
|
||||||
|
SDL_bool rumble_pending;
|
||||||
Uint8 last_state[USB_PACKET_LENGTH];
|
Uint8 last_state[USB_PACKET_LENGTH];
|
||||||
} SDL_DriverXboxOne_Context;
|
} SDL_DriverXboxOne_Context;
|
||||||
|
|
||||||
|
@ -411,6 +420,9 @@ HIDAPI_DriverXboxOne_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst
|
||||||
ctx->high_frequency_rumble = 0;
|
ctx->high_frequency_rumble = 0;
|
||||||
ctx->left_trigger_rumble = 0;
|
ctx->left_trigger_rumble = 0;
|
||||||
ctx->right_trigger_rumble = 0;
|
ctx->right_trigger_rumble = 0;
|
||||||
|
ctx->rumble_state = XBOX_ONE_RUMBLE_STATE_IDLE;
|
||||||
|
ctx->rumble_time = 0;
|
||||||
|
ctx->rumble_pending = SDL_FALSE;
|
||||||
SDL_zeroa(ctx->last_state);
|
SDL_zeroa(ctx->last_state);
|
||||||
|
|
||||||
/* Initialize the joystick capabilities */
|
/* Initialize the joystick capabilities */
|
||||||
|
@ -432,11 +444,47 @@ HIDAPI_DriverXboxOne_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst
|
||||||
return SDL_TRUE;
|
return SDL_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
HIDAPI_DriverXboxOne_RumbleSent(void *userdata)
|
||||||
|
{
|
||||||
|
SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)userdata;
|
||||||
|
ctx->rumble_time = SDL_GetTicks();
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
HIDAPI_DriverXboxOne_UpdateRumble(SDL_HIDAPI_Device *device)
|
HIDAPI_DriverXboxOne_UpdateRumble(SDL_HIDAPI_Device *device)
|
||||||
{
|
{
|
||||||
SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
|
SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
|
||||||
|
|
||||||
|
if (ctx->rumble_state == XBOX_ONE_RUMBLE_STATE_QUEUED) {
|
||||||
|
if (ctx->rumble_time) {
|
||||||
|
ctx->rumble_state = XBOX_ONE_RUMBLE_STATE_BUSY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->rumble_state == XBOX_ONE_RUMBLE_STATE_BUSY) {
|
||||||
|
const Uint32 RUMBLE_BUSY_TIME_MS = ctx->bluetooth ? 50 : 10;
|
||||||
|
if (SDL_TICKS_PASSED(SDL_GetTicks(), ctx->rumble_time + RUMBLE_BUSY_TIME_MS)) {
|
||||||
|
ctx->rumble_time = 0;
|
||||||
|
ctx->rumble_state = XBOX_ONE_RUMBLE_STATE_IDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx->rumble_pending) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->rumble_state != XBOX_ONE_RUMBLE_STATE_IDLE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're no longer pending, even if we fail to send the rumble below */
|
||||||
|
ctx->rumble_pending = SDL_FALSE;
|
||||||
|
|
||||||
|
if (SDL_HIDAPI_LockRumble() < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->bluetooth) {
|
if (ctx->bluetooth) {
|
||||||
Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xEB };
|
Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xEB };
|
||||||
|
|
||||||
|
@ -445,7 +493,7 @@ HIDAPI_DriverXboxOne_UpdateRumble(SDL_HIDAPI_Device *device)
|
||||||
rumble_packet[4] = ctx->low_frequency_rumble;
|
rumble_packet[4] = ctx->low_frequency_rumble;
|
||||||
rumble_packet[5] = ctx->high_frequency_rumble;
|
rumble_packet[5] = ctx->high_frequency_rumble;
|
||||||
|
|
||||||
if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
|
if (SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(device, rumble_packet, sizeof(rumble_packet), HIDAPI_DriverXboxOne_RumbleSent, ctx) != sizeof(rumble_packet)) {
|
||||||
return SDL_SetError("Couldn't send rumble packet");
|
return SDL_SetError("Couldn't send rumble packet");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -456,10 +504,13 @@ HIDAPI_DriverXboxOne_UpdateRumble(SDL_HIDAPI_Device *device)
|
||||||
rumble_packet[8] = ctx->low_frequency_rumble;
|
rumble_packet[8] = ctx->low_frequency_rumble;
|
||||||
rumble_packet[9] = ctx->high_frequency_rumble;
|
rumble_packet[9] = ctx->high_frequency_rumble;
|
||||||
|
|
||||||
if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
|
if (SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(device, rumble_packet, sizeof(rumble_packet), HIDAPI_DriverXboxOne_RumbleSent, ctx) != sizeof(rumble_packet)) {
|
||||||
return SDL_SetError("Couldn't send rumble packet");
|
return SDL_SetError("Couldn't send rumble packet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx->rumble_state = XBOX_ONE_RUMBLE_STATE_QUEUED;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,6 +522,7 @@ HIDAPI_DriverXboxOne_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joy
|
||||||
/* Magnitude is 1..100 so scale the 16-bit input here */
|
/* Magnitude is 1..100 so scale the 16-bit input here */
|
||||||
ctx->low_frequency_rumble = low_frequency_rumble / 655;
|
ctx->low_frequency_rumble = low_frequency_rumble / 655;
|
||||||
ctx->high_frequency_rumble = high_frequency_rumble / 655;
|
ctx->high_frequency_rumble = high_frequency_rumble / 655;
|
||||||
|
ctx->rumble_pending = SDL_TRUE;
|
||||||
|
|
||||||
return HIDAPI_DriverXboxOne_UpdateRumble(device);
|
return HIDAPI_DriverXboxOne_UpdateRumble(device);
|
||||||
}
|
}
|
||||||
|
@ -487,6 +539,7 @@ HIDAPI_DriverXboxOne_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joyst
|
||||||
/* Magnitude is 1..100 so scale the 16-bit input here */
|
/* Magnitude is 1..100 so scale the 16-bit input here */
|
||||||
ctx->left_trigger_rumble = left_rumble / 655;
|
ctx->left_trigger_rumble = left_rumble / 655;
|
||||||
ctx->right_trigger_rumble = right_rumble / 655;
|
ctx->right_trigger_rumble = right_rumble / 655;
|
||||||
|
ctx->rumble_pending = SDL_TRUE;
|
||||||
|
|
||||||
return HIDAPI_DriverXboxOne_UpdateRumble(device);
|
return HIDAPI_DriverXboxOne_UpdateRumble(device);
|
||||||
}
|
}
|
||||||
|
@ -1180,6 +1233,7 @@ HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||||
}
|
}
|
||||||
|
|
||||||
HIDAPI_DriverXboxOne_UpdateInitState(device, ctx);
|
HIDAPI_DriverXboxOne_UpdateInitState(device, ctx);
|
||||||
|
HIDAPI_DriverXboxOne_UpdateRumble(device);
|
||||||
|
|
||||||
if (size < 0) {
|
if (size < 0) {
|
||||||
/* Read error, device is disconnected */
|
/* Read error, device is disconnected */
|
||||||
|
|
Loading…
Reference in New Issue