Don't send rumble packets too quickly to Nintendo Switch Pro controllers over Bluetooth

This commit is contained in:
Sam Lantinga 2020-04-17 15:08:48 -07:00
parent c02f54a0ed
commit 2b32385502
1 changed files with 91 additions and 14 deletions

View File

@ -38,6 +38,17 @@
#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
/* Define this to get log output for rumble logic */
/*#define DEBUG_RUMBLE*/
/* How often you can write rumble commands to the controller in Bluetooth mode
If you send commands more frequently than this, you can turn off the controller.
*/
#define RUMBLE_WRITE_FREQUENCY_MS 25
/* How often you have to refresh a long duration rumble to keep the motors running */
#define RUMBLE_REFRESH_FREQUENCY_MS 40
typedef enum {
k_eSwitchInputReportIDs_SubcommandReply = 0x21,
k_eSwitchInputReportIDs_FullControllerState = 0x30,
@ -202,7 +213,10 @@ typedef struct {
SwitchCommonOutputPacket_t m_RumblePacket;
Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength];
SDL_bool m_bRumbleActive;
Uint32 m_unRumbleRefresh;
Uint32 m_unRumbleSent;
SDL_bool m_bRumblePending;
SDL_bool m_bRumbleZeroPending;
Uint32 m_unRumblePending;
SwitchInputOnlyControllerStatePacket_t m_lastInputOnlyState;
SwitchSimpleStatePacket_t m_lastSimpleState;
@ -471,14 +485,7 @@ static SDL_bool WriteRumble(SDL_DriverSwitch_Context *ctx)
ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
/* Refresh the rumble state periodically */
if (ctx->m_bRumbleActive) {
ctx->m_unRumbleRefresh = SDL_GetTicks() + 30;
if (!ctx->m_unRumbleRefresh) {
ctx->m_unRumbleRefresh = 1;
}
} else {
ctx->m_unRumbleRefresh = 0;
}
ctx->m_unRumbleSent = SDL_GetTicks();
return WritePacket(ctx, (Uint8 *)&ctx->m_RumblePacket, sizeof(ctx->m_RumblePacket));
}
@ -812,10 +819,8 @@ error:
}
static int
HIDAPI_DriverSwitch_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
HIDAPI_DriverSwitch_ActuallyRumbleJoystick(SDL_DriverSwitch_Context *ctx, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
/* Experimentally determined rumble values. These will only matter on some controllers as tested ones
* seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble
*
@ -848,6 +853,73 @@ HIDAPI_DriverSwitch_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joys
return 0;
}
static int
HIDAPI_DriverSwitch_SendPendingRumble(SDL_DriverSwitch_Context *ctx)
{
if ((SDL_GetTicks() - ctx->m_unRumbleSent) < RUMBLE_WRITE_FREQUENCY_MS) {
return 0;
}
if (ctx->m_bRumblePending) {
Uint16 low_frequency_rumble = (Uint16)(ctx->m_unRumblePending >> 16);
Uint16 high_frequency_rumble = (Uint16)ctx->m_unRumblePending;
#ifdef DEBUG_RUMBLE
SDL_Log("Sent pending rumble %d/%d\n", low_frequency_rumble, high_frequency_rumble);
#endif
ctx->m_bRumblePending = SDL_FALSE;
ctx->m_unRumblePending = 0;
return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, low_frequency_rumble, high_frequency_rumble);
}
if (ctx->m_bRumbleZeroPending) {
ctx->m_bRumbleZeroPending = SDL_FALSE;
#ifdef DEBUG_RUMBLE
SDL_Log("Sent pending zero rumble\n");
#endif
return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, 0, 0);
}
return 0;
}
static int
HIDAPI_DriverSwitch_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
{
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
if (ctx->m_bRumblePending) {
if (HIDAPI_DriverSwitch_SendPendingRumble(ctx) < 0) {
return -1;
}
}
if (ctx->m_bUsingBluetooth && (SDL_GetTicks() - ctx->m_unRumbleSent) < RUMBLE_WRITE_FREQUENCY_MS) {
if (low_frequency_rumble || high_frequency_rumble) {
Uint32 unRumblePending = ((Uint32)low_frequency_rumble << 16) | high_frequency_rumble;
/* Keep the highest rumble intensity in the given interval */
if (unRumblePending > ctx->m_unRumblePending) {
ctx->m_unRumblePending = unRumblePending;
}
ctx->m_bRumblePending = SDL_TRUE;
ctx->m_bRumbleZeroPending = SDL_FALSE;
} else {
/* When rumble is complete, turn it off */
ctx->m_bRumbleZeroPending = SDL_TRUE;
}
return 0;
}
#ifdef DEBUG_RUMBLE
SDL_Log("Sent rumble %d/%d\n", low_frequency_rumble, high_frequency_rumble);
#endif
return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, low_frequency_rumble, high_frequency_rumble);
}
static void HandleInputOnlyControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchInputOnlyControllerStatePacket_t *packet)
{
Sint16 axis;
@ -1141,8 +1213,13 @@ HIDAPI_DriverSwitch_UpdateDevice(SDL_HIDAPI_Device *device)
}
}
if (ctx->m_bRumbleActive &&
SDL_TICKS_PASSED(SDL_GetTicks(), ctx->m_unRumbleRefresh)) {
if (ctx->m_bRumblePending || ctx->m_bRumbleZeroPending) {
HIDAPI_DriverSwitch_SendPendingRumble(ctx);
} else if (ctx->m_bRumbleActive &&
SDL_TICKS_PASSED(SDL_GetTicks(), ctx->m_unRumbleSent + RUMBLE_REFRESH_FREQUENCY_MS)) {
#ifdef DEBUG_RUMBLE
SDL_Log("Sent continuing rumble\n");
#endif
WriteRumble(ctx);
}