diff --git a/src/joystick/hidapi/SDL_hidapi_rumble.c b/src/joystick/hidapi/SDL_hidapi_rumble.c index 9e4571f437..e15ecf0524 100644 --- a/src/joystick/hidapi/SDL_hidapi_rumble.c +++ b/src/joystick/hidapi/SDL_hidapi_rumble.c @@ -36,6 +36,8 @@ typedef struct SDL_HIDAPI_RumbleRequest SDL_HIDAPI_Device *device; Uint8 data[2*USB_PACKET_LENGTH]; /* need enough space for the biggest report: dualshock4 is 78 bytes */ int size; + SDL_HIDAPI_RumbleSentCallback callback; + void *userdata; struct SDL_HIDAPI_RumbleRequest *prev; } 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_UnlockMutex(request->device->dev_lock); + if (request->callback) { + request->callback(request->userdata); + } (void)SDL_AtomicDecRef(&request->device->rumble_pending); SDL_free(request); @@ -116,6 +121,9 @@ SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx) } ctx->requests_tail = request->prev; + if (request->callback) { + request->callback(request->userdata); + } (void)SDL_AtomicDecRef(&request->device->rumble_pending); 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) +{ + 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_RumbleRequest *request; @@ -209,6 +222,8 @@ int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, request->device = device; SDL_memcpy(request->data, data, size); request->size = size; + request->callback = callback; + request->userdata = userdata; SDL_AtomicIncRef(&device->rumble_pending); diff --git a/src/joystick/hidapi/SDL_hidapi_rumble.h b/src/joystick/hidapi/SDL_hidapi_rumble.h index b04794a9a4..bf9f1adba5 100644 --- a/src/joystick/hidapi/SDL_hidapi_rumble.h +++ b/src/joystick/hidapi/SDL_hidapi_rumble.h @@ -28,6 +28,8 @@ int SDL_HIDAPI_LockRumble(void); 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); +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); /* Simple API, will replace any pending rumble with the new data */ diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c index 18f004dae8..770be6d51f 100644 --- a/src/joystick/hidapi/SDL_hidapi_xboxone.c +++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c @@ -100,12 +100,18 @@ static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = { }; typedef enum { - XBOX_ONE_INIT_STATE_START_NEGOTIATING = 0, - XBOX_ONE_INIT_STATE_NEGOTIATING = 1, - XBOX_ONE_INIT_STATE_PREPARE_INPUT = 2, - XBOX_ONE_INIT_STATE_COMPLETE = 3 + XBOX_ONE_INIT_STATE_START_NEGOTIATING, + XBOX_ONE_INIT_STATE_NEGOTIATING, + XBOX_ONE_INIT_STATE_PREPARE_INPUT, + XBOX_ONE_INIT_STATE_COMPLETE, } SDL_XboxOneInitState; +typedef enum { + XBOX_ONE_RUMBLE_STATE_IDLE, + XBOX_ONE_RUMBLE_STATE_QUEUED, + XBOX_ONE_RUMBLE_STATE_BUSY +} SDL_XboxOneRumbleState; + typedef struct { SDL_HIDAPI_Device *device; Uint16 vendor_id; @@ -125,6 +131,9 @@ typedef struct { Uint8 high_frequency_rumble; Uint8 left_trigger_rumble; Uint8 right_trigger_rumble; + SDL_XboxOneRumbleState rumble_state; + Uint32 rumble_time; + SDL_bool rumble_pending; Uint8 last_state[USB_PACKET_LENGTH]; } SDL_DriverXboxOne_Context; @@ -411,6 +420,9 @@ HIDAPI_DriverXboxOne_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst ctx->high_frequency_rumble = 0; ctx->left_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); /* Initialize the joystick capabilities */ @@ -432,11 +444,47 @@ HIDAPI_DriverXboxOne_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst 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 HIDAPI_DriverXboxOne_UpdateRumble(SDL_HIDAPI_Device *device) { 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) { 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[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"); } } else { @@ -456,10 +504,13 @@ HIDAPI_DriverXboxOne_UpdateRumble(SDL_HIDAPI_Device *device) rumble_packet[8] = ctx->low_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"); } } + + ctx->rumble_state = XBOX_ONE_RUMBLE_STATE_QUEUED; + 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 */ ctx->low_frequency_rumble = low_frequency_rumble / 655; ctx->high_frequency_rumble = high_frequency_rumble / 655; + ctx->rumble_pending = SDL_TRUE; 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 */ ctx->left_trigger_rumble = left_rumble / 655; ctx->right_trigger_rumble = right_rumble / 655; + ctx->rumble_pending = SDL_TRUE; return HIDAPI_DriverXboxOne_UpdateRumble(device); } @@ -1180,6 +1233,7 @@ HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device) } HIDAPI_DriverXboxOne_UpdateInitState(device, ctx); + HIDAPI_DriverXboxOne_UpdateRumble(device); if (size < 0) { /* Read error, device is disconnected */