diff --git a/src/joystick/hidapi/SDL_hidapi_gamecube.c b/src/joystick/hidapi/SDL_hidapi_gamecube.c index 5b39b8b03e..3b41e82334 100644 --- a/src/joystick/hidapi/SDL_hidapi_gamecube.c +++ b/src/joystick/hidapi/SDL_hidapi_gamecube.c @@ -37,6 +37,7 @@ #endif #define MAX_CONTROLLERS 4 +#define PC_RUMBLE_REFRESH 16 typedef struct { @@ -50,6 +51,8 @@ typedef struct // Without this variable, hid_write starts to lag a TON bool rumbleUpdate; bool useRumbleBrake; + bool rumbleActive; + Uint64 pc_rumble_sent; } SDL_DriverGameCube_Context; static void HIDAPI_DriverGameCube_RegisterHints(SDL_HintCallback callback, void *userdata) @@ -202,12 +205,26 @@ static bool HIDAPI_DriverGameCube_InitDevice(SDL_HIDAPI_Device *device) } if (ctx->pc_mode) { + // Check to see if this firmware supports rumble + bool rumbleAllowed = false; + if ((size = SDL_hid_read_timeout(device->dev, packet, sizeof(packet), 0)) > 0) { +#ifdef DEBUG_GAMECUBE_PROTOCOL + HIDAPI_DumpPacket("Nintendo GameCube packet: size = %d", packet, size); +#endif + if (size == 9) { + // This is firmware version 0x7 or newer + // Rumble is supported if the second USB cable is plugged in + rumbleAllowed = true; + } + } #ifdef SDL_PLATFORM_WIN32 // We get a separate device for each slot + ctx->rumbleAllowed[0] = rumbleAllowed; ResetAxisRange(ctx, 0); HIDAPI_JoystickConnected(device, &ctx->joysticks[0]); #else for (i = 0; i < MAX_CONTROLLERS; ++i) { + ctx->rumbleAllowed[i] = rumbleAllowed; ResetAxisRange(ctx, i); HIDAPI_JoystickConnected(device, &ctx->joysticks[i]); } @@ -427,6 +444,14 @@ static void HIDAPI_DriverGameCube_HandleNintendoPacket(SDL_HIDAPI_Device *device } } +static void HIDAPI_DriverGameCube_SendPCRumble(SDL_HIDAPI_Device *device, SDL_DriverGameCube_Context *ctx) +{ + Uint8 rumblepkt[3] = { 0x00, 0x00, 0x00 }; + rumblepkt[1] = rumblepkt[2] = ctx->rumbleActive ? 0xFF : 0x00; + SDL_HIDAPI_SendRumble(device, rumblepkt, sizeof(rumblepkt)); + ctx->pc_rumble_sent = SDL_GetTicks(); +} + static bool HIDAPI_DriverGameCube_UpdateDevice(SDL_HIDAPI_Device *device) { SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context; @@ -456,6 +481,11 @@ static bool HIDAPI_DriverGameCube_UpdateDevice(SDL_HIDAPI_Device *device) } } + // PC_Mode rumble needs constant packets in order to keep rumble going + if (ctx->pc_mode && ctx->rumbleActive && SDL_GetTicks() >= (ctx->pc_rumble_sent + PC_RUMBLE_REFRESH)) { + HIDAPI_DriverGameCube_SendPCRumble(device, ctx); + } + // Write rumble packet if (ctx->rumbleUpdate) { SDL_HIDAPI_SendRumble(device, ctx->rumble, sizeof(ctx->rumble)); @@ -496,33 +526,51 @@ static bool HIDAPI_DriverGameCube_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_ SDL_AssertJoysticksLocked(); if (ctx->pc_mode) { - return SDL_Unsupported(); - } - - for (i = 0; i < MAX_CONTROLLERS; i += 1) { - if (joystick->instance_id == ctx->joysticks[i]) { - if (ctx->wireless[i]) { - return SDL_SetError("Nintendo GameCube WaveBird controllers do not support rumble"); - } - if (!ctx->rumbleAllowed[i]) { - return SDL_SetError("Second USB cable for WUP-028 not connected"); - } - if (ctx->useRumbleBrake) { - if (low_frequency_rumble == 0 && high_frequency_rumble > 0) { - val = 0; // if only low is 0 we want to do a regular stop - } else if (low_frequency_rumble == 0 && high_frequency_rumble == 0) { - val = 2; // if both frequencies are 0 we want to do a hard stop - } else { - val = 1; // normal rumble + for (i = 0; i < MAX_CONTROLLERS; i += 1) { + if (joystick->instance_id == ctx->joysticks[i]) { + if (!ctx->rumbleAllowed[i]) { + return SDL_SetError("Rumble is disabled because the adapter is not at least of firmware 0x7"); } - } else { - val = (low_frequency_rumble > 0 || high_frequency_rumble > 0); + bool shouldrumble = false, coast = false; + if (ctx->useRumbleBrake) { + coast = (low_frequency_rumble == 0 && high_frequency_rumble > 0); + } + shouldrumble = (low_frequency_rumble > 0 || high_frequency_rumble > 0); + if (coast) { + ctx->rumbleActive = false; + } else if (shouldrumble != ctx->rumbleActive) { + ctx->rumbleActive = shouldrumble; + HIDAPI_DriverGameCube_SendPCRumble(device, ctx); + } + return true; } - if (val != ctx->rumble[i + 1]) { - ctx->rumble[i + 1] = val; - ctx->rumbleUpdate = true; + } + } else { + for (i = 0; i < MAX_CONTROLLERS; i += 1) { + if (joystick->instance_id == ctx->joysticks[i]) { + if (ctx->wireless[i]) { + return SDL_SetError("Nintendo GameCube WaveBird controllers do not support rumble"); + } + if (!ctx->rumbleAllowed[i]) { + return SDL_SetError("Second USB cable for WUP-028 not connected"); + } + if (ctx->useRumbleBrake) { + if (low_frequency_rumble == 0 && high_frequency_rumble > 0) { + val = 0; // if only low is 0 we want to do a regular stop + } else if (low_frequency_rumble == 0 && high_frequency_rumble == 0) { + val = 2; // if both frequencies are 0 we want to do a hard stop + } else { + val = 1; // normal rumble + } + } else { + val = (low_frequency_rumble > 0 || high_frequency_rumble > 0); + } + if (val != ctx->rumble[i + 1]) { + ctx->rumble[i + 1] = val; + ctx->rumbleUpdate = true; + } + return true; } - return true; } } @@ -541,10 +589,17 @@ static Uint32 HIDAPI_DriverGameCube_GetJoystickCapabilities(SDL_HIDAPI_Device *d Uint32 result = 0; SDL_AssertJoysticksLocked(); - - if (!ctx->pc_mode) { - Uint8 i; - + Uint8 i; + if (ctx->pc_mode) { + for (i = 0; i < MAX_CONTROLLERS; i += 1) { + if (joystick->instance_id == ctx->joysticks[i]) { + if (ctx->rumbleAllowed[i]) { + result |= SDL_JOYSTICK_CAP_RUMBLE; + break; + } + } + } + } else { for (i = 0; i < MAX_CONTROLLERS; i += 1) { if (joystick->instance_id == ctx->joysticks[i]) { if (!ctx->wireless[i] && ctx->rumbleAllowed[i]) { @@ -582,6 +637,11 @@ static void HIDAPI_DriverGameCube_CloseJoystick(SDL_HIDAPI_Device *device, SDL_J SDL_HIDAPI_SendRumble(device, ctx->rumble, sizeof(ctx->rumble)); ctx->rumbleUpdate = false; } + + if (ctx->pc_mode && ctx->rumbleActive) { + ctx->rumbleActive = false; + HIDAPI_DriverGameCube_SendPCRumble(device, ctx); + } } static void HIDAPI_DriverGameCube_FreeDevice(SDL_HIDAPI_Device *device)