Skip to content

Commit

Permalink
Replaced SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE and SDL_HINT_JOYSTICK_HI…
Browse files Browse the repository at this point in the history
…DAPI_PS5_RUMBLE with SDL_HINT_JOYSTICK_ENHANCED_REPORTS

Also added support for SDL_HINT_JOYSTICK_ENHANCED_REPORTS for Nintendo Switch controllers
  • Loading branch information
slouken committed Jan 3, 2025
1 parent 1c04ebe commit aa403be
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 186 deletions.
70 changes: 19 additions & 51 deletions include/SDL3/SDL_hints.h
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,25 @@ extern "C" {
*/
#define SDL_HINT_JOYSTICK_DEVICE "SDL_JOYSTICK_DEVICE"

/**
* A variable controlling whether enhanced reports should be used for controllers when using the HIDAPI driver.
*
* Enhanced reports allow rumble and effects on PlayStation controllers and gyro on Nintendo Switch controllers, but break Windows DirectInput for other applications that don't use SDL.
*
* Once enhanced reports are enabled, they can't be disabled on PlayStation controllers without power cycling the controller.
*
* The variable can be set to the following values:
*
* - "0": enhanced reports are not enabled.
* - "1": enhanced reports are enabled. (default)
* - "auto": enhanced features are advertised to the application, but SDL doesn't change the controller report mode unless the application explicitly requests it.
*
* This hint can be enabled anytime.
*
* \since This hint is available since SDL 3.1.3.
*/
#define SDL_HINT_JOYSTICK_ENHANCED_REPORTS "SDL_JOYSTICK_ENHANCED_REPORTS"

/**
* A variable containing a list of flightstick style controllers.
*
Expand Down Expand Up @@ -1571,32 +1590,6 @@ extern "C" {
*/
#define SDL_HINT_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL "SDL_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL"

/**
* A variable controlling whether extended input reports should be used for
* PS4 controllers when using the HIDAPI driver.
*
* The variable can be set to the following values:
*
* - "0": extended reports are not enabled. (default)
* - "1": extended reports are enabled.
*
* Extended input reports allow rumble on Bluetooth PS4 controllers, but break
* DirectInput handling for applications that don't use SDL.
*
* Once extended reports are enabled, they can not be disabled without power
* cycling the controller.
*
* For compatibility with applications written for versions of SDL prior to
* the introduction of PS5 controller support, this value will also control
* the state of extended reports on PS5 controllers when the
* SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE hint is not explicitly set.
*
* This hint can be enabled anytime.
*
* \since This hint is available since SDL 3.1.3.
*/
#define SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE "SDL_JOYSTICK_HIDAPI_PS4_RUMBLE"

/**
* A variable controlling whether the HIDAPI driver for PS5 controllers should
* be used.
Expand Down Expand Up @@ -1627,31 +1620,6 @@ extern "C" {
*/
#define SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED "SDL_JOYSTICK_HIDAPI_PS5_PLAYER_LED"

/**
* A variable controlling whether extended input reports should be used for
* PS5 controllers when using the HIDAPI driver.
*
* The variable can be set to the following values:
*
* - "0": extended reports are not enabled. (default)
* - "1": extended reports.
*
* Extended input reports allow rumble on Bluetooth PS5 controllers, but break
* DirectInput handling for applications that don't use SDL.
*
* Once extended reports are enabled, they can not be disabled without power
* cycling the controller.
*
* For compatibility with applications written for versions of SDL prior to
* the introduction of PS5 controller support, this value defaults to the
* value of SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE.
*
* This hint can be enabled anytime.
*
* \since This hint is available since SDL 3.1.3.
*/
#define SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE "SDL_JOYSTICK_HIDAPI_PS5_RUMBLE"

/**
* A variable controlling whether the HIDAPI driver for NVIDIA SHIELD
* controllers should be used.
Expand Down
74 changes: 35 additions & 39 deletions src/joystick/hidapi/SDL_hidapi_ps4.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,16 @@ typedef struct
} IMUCalibrationData;

/* Rumble hint mode:
* default: enhanced features are available if the controller is using enhanced reports
* "0": enhanced features are never used
* "1": enhanced features are always used
* "auto": enhanced features are advertised to the application, but SDL doesn't touch the controller state unless the application explicitly requests it.
*/
typedef enum
{
PS4_RUMBLE_HINT_DEFAULT,
PS4_RUMBLE_HINT_OFF,
PS4_RUMBLE_HINT_ON,
PS4_RUMBLE_HINT_AUTO
} SDL_PS4_RumbleHintMode;
PS4_ENHANCED_REPORT_HINT_OFF,
PS4_ENHANCED_REPORT_HINT_ON,
PS4_ENHANCED_REPORT_HINT_AUTO
} HIDAPI_PS4_EnhancedReportHint;

typedef struct
{
Expand All @@ -154,7 +152,7 @@ typedef struct
bool vibration_supported;
bool touchpad_supported;
bool effects_supported;
SDL_PS4_RumbleHintMode rumble_hint;
HIDAPI_PS4_EnhancedReportHint enhanced_report_hint;
bool enhanced_reports;
bool enhanced_mode;
bool enhanced_mode_available;
Expand Down Expand Up @@ -696,7 +694,7 @@ static void HIDAPI_DriverPS4_TickleBluetooth(SDL_HIDAPI_Device *device)
SDL_HIDAPI_SendRumbleAndUnlock(device, data, sizeof(data));
}
} else {
#if 0 /* The 8BitDo Zero 2 has perfect emulation of a PS4 controllers, except it
#if 0 /* The 8BitDo Zero 2 has perfect emulation of a PS4 controller, except it
* only sends reports when the state changes, so we can't disconnect here.
*/
// We can't even send an invalid effects packet, or it will put the controller in enhanced mode
Expand All @@ -709,6 +707,9 @@ static void HIDAPI_DriverPS4_TickleBluetooth(SDL_HIDAPI_Device *device)

static void HIDAPI_DriverPS4_SetEnhancedModeAvailable(SDL_DriverPS4_Context *ctx)
{
if (ctx->enhanced_mode_available) {
return;
}
ctx->enhanced_mode_available = true;

if (ctx->touchpad_supported) {
Expand All @@ -730,9 +731,7 @@ static void HIDAPI_DriverPS4_SetEnhancedModeAvailable(SDL_DriverPS4_Context *ctx

static void HIDAPI_DriverPS4_SetEnhancedMode(SDL_DriverPS4_Context *ctx)
{
if (!ctx->enhanced_mode_available) {
HIDAPI_DriverPS4_SetEnhancedModeAvailable(ctx);
}
HIDAPI_DriverPS4_SetEnhancedModeAvailable(ctx);

if (!ctx->enhanced_mode) {
ctx->enhanced_mode = true;
Expand All @@ -742,55 +741,52 @@ static void HIDAPI_DriverPS4_SetEnhancedMode(SDL_DriverPS4_Context *ctx)
}
}

static void HIDAPI_DriverPS4_SetRumbleHintMode(SDL_DriverPS4_Context *ctx, SDL_PS4_RumbleHintMode rumble_hint)
static void HIDAPI_DriverPS4_SetEnhancedReportHint(SDL_DriverPS4_Context *ctx, HIDAPI_PS4_EnhancedReportHint enhanced_report_hint)
{
switch (rumble_hint) {
case PS4_RUMBLE_HINT_DEFAULT:
if (ctx->enhanced_reports) {
HIDAPI_DriverPS4_SetEnhancedMode(ctx);
}
break;
case PS4_RUMBLE_HINT_OFF:
switch (enhanced_report_hint) {
case PS4_ENHANCED_REPORT_HINT_OFF:
// Nothing to do, enhanced mode is a one-way ticket
break;
case PS4_RUMBLE_HINT_ON:
case PS4_ENHANCED_REPORT_HINT_ON:
HIDAPI_DriverPS4_SetEnhancedMode(ctx);
break;
case PS4_RUMBLE_HINT_AUTO:
case PS4_ENHANCED_REPORT_HINT_AUTO:
HIDAPI_DriverPS4_SetEnhancedModeAvailable(ctx);
break;
}
ctx->rumble_hint = rumble_hint;
ctx->enhanced_report_hint = enhanced_report_hint;
}

static void HIDAPI_DriverPS4_UpdateEnhancedModeOnEnhancedReport(SDL_DriverPS4_Context *ctx)
{
ctx->enhanced_reports = true;

if (ctx->rumble_hint == PS4_RUMBLE_HINT_DEFAULT) {
HIDAPI_DriverPS4_SetRumbleHintMode(ctx, PS4_RUMBLE_HINT_ON);
if (ctx->enhanced_report_hint == PS4_ENHANCED_REPORT_HINT_AUTO) {
HIDAPI_DriverPS4_SetEnhancedReportHint(ctx, PS4_ENHANCED_REPORT_HINT_ON);
}
}

static void HIDAPI_DriverPS4_UpdateEnhancedModeOnApplicationUsage(SDL_DriverPS4_Context *ctx)
{
if (ctx->rumble_hint == PS4_RUMBLE_HINT_AUTO) {
HIDAPI_DriverPS4_SetRumbleHintMode(ctx, PS4_RUMBLE_HINT_ON);
if (ctx->enhanced_report_hint == PS4_ENHANCED_REPORT_HINT_AUTO) {
HIDAPI_DriverPS4_SetEnhancedReportHint(ctx, PS4_ENHANCED_REPORT_HINT_ON);
}
}

static void SDLCALL SDL_PS4RumbleHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
static void SDLCALL SDL_PS4EnhancedReportsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)userdata;

if (!hint) {
HIDAPI_DriverPS4_SetRumbleHintMode(ctx, PS4_RUMBLE_HINT_DEFAULT);
} else if (SDL_strcasecmp(hint, "auto") == 0) {
HIDAPI_DriverPS4_SetRumbleHintMode(ctx, PS4_RUMBLE_HINT_AUTO);
} else if (SDL_GetStringBoolean(hint, false)) {
HIDAPI_DriverPS4_SetRumbleHintMode(ctx, PS4_RUMBLE_HINT_ON);
if (ctx->device->is_bluetooth) {
if (hint && SDL_strcasecmp(hint, "auto") == 0) {
HIDAPI_DriverPS4_SetEnhancedReportHint(ctx, PS4_ENHANCED_REPORT_HINT_AUTO);
} else if (SDL_GetStringBoolean(hint, true)) {
HIDAPI_DriverPS4_SetEnhancedReportHint(ctx, PS4_ENHANCED_REPORT_HINT_ON);
} else {
HIDAPI_DriverPS4_SetEnhancedReportHint(ctx, PS4_ENHANCED_REPORT_HINT_OFF);
}
} else {
HIDAPI_DriverPS4_SetRumbleHintMode(ctx, PS4_RUMBLE_HINT_OFF);
HIDAPI_DriverPS4_SetEnhancedReportHint(ctx, PS4_ENHANCED_REPORT_HINT_ON);
}
}

Expand Down Expand Up @@ -868,8 +864,8 @@ static bool HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystic

SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL,
SDL_PS4ReportIntervalHintChanged, ctx);
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE,
SDL_PS4RumbleHintChanged, ctx);
SDL_AddHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS,
SDL_PS4EnhancedReportsChanged, ctx);
return true;
}

Expand Down Expand Up @@ -1287,7 +1283,7 @@ static bool HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
case k_EPS4ReportIdBluetoothState7:
case k_EPS4ReportIdBluetoothState8:
case k_EPS4ReportIdBluetoothState9:
// This is the extended report, we can enable effects now in default mode
// This is the extended report, we can enable effects now in auto mode
HIDAPI_DriverPS4_UpdateEnhancedModeOnEnhancedReport(ctx);

// Bluetooth state packets have two additional bytes at the beginning, the first notes if HID is present
Expand Down Expand Up @@ -1353,8 +1349,8 @@ static void HIDAPI_DriverPS4_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joysti

SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL,
SDL_PS4ReportIntervalHintChanged, ctx);
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE,
SDL_PS4RumbleHintChanged, ctx);
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS,
SDL_PS4EnhancedReportsChanged, ctx);

ctx->joystick = NULL;
}
Expand Down
Loading

0 comments on commit aa403be

Please sign in to comment.