Make it possible to turn on PS4 rumble effects at runtime using the hint

This commit is contained in:
Sam Lantinga 2020-12-22 20:58:32 -08:00
parent 390b2cf0d3
commit a30adae567
2 changed files with 113 additions and 30 deletions

View File

@ -30,6 +30,7 @@
#include "SDL_timer.h" #include "SDL_timer.h"
#include "SDL_joystick.h" #include "SDL_joystick.h"
#include "SDL_gamecontroller.h" #include "SDL_gamecontroller.h"
#include "../../SDL_hints_c.h"
#include "../SDL_sysjoystick.h" #include "../SDL_sysjoystick.h"
#include "SDL_hidapijoystick_c.h" #include "SDL_hidapijoystick_c.h"
#include "SDL_hidapi_rumble.h" #include "SDL_hidapi_rumble.h"
@ -119,11 +120,14 @@ typedef struct {
} IMUCalibrationData; } IMUCalibrationData;
typedef struct { typedef struct {
SDL_HIDAPI_Device *device;
SDL_Joystick *joystick;
SDL_bool is_dongle; SDL_bool is_dongle;
SDL_bool is_bluetooth; SDL_bool is_bluetooth;
SDL_bool official_controller; SDL_bool official_controller;
SDL_bool audio_supported; SDL_bool audio_supported;
SDL_bool effects_supported; SDL_bool effects_supported;
SDL_bool enhanced_mode;
SDL_bool report_sensors; SDL_bool report_sensors;
SDL_bool hardware_calibration; SDL_bool hardware_calibration;
IMUCalibrationData calibration[6]; IMUCalibrationData calibration[6];
@ -387,6 +391,10 @@ HIDAPI_DriverPS4_UpdateEffects(SDL_HIDAPI_Device *device)
return SDL_Unsupported(); return SDL_Unsupported();
} }
if (!ctx->enhanced_mode) {
return SDL_Unsupported();
}
SDL_zero(data); SDL_zero(data);
if (ctx->is_bluetooth) { if (ctx->is_bluetooth) {
@ -432,6 +440,32 @@ HIDAPI_DriverPS4_UpdateEffects(SDL_HIDAPI_Device *device)
return 0; return 0;
} }
static void
HIDAPI_DriverPS4_SetEnhancedMode(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
if (!ctx->enhanced_mode) {
ctx->enhanced_mode = SDL_TRUE;
SDL_PrivateJoystickAddTouchpad(joystick, 2);
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO);
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL);
HIDAPI_DriverPS4_UpdateEffects(device);
}
}
static void SDLCALL SDL_PS4RumbleHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)userdata;
/* This is a one-way trip, you can't switch the controller back to simple report mode */
if (SDL_GetStringBoolean(hint, SDL_FALSE)) {
HIDAPI_DriverPS4_SetEnhancedMode(ctx->device, ctx->joystick);
}
}
static void static void
HIDAPI_DriverPS4_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) HIDAPI_DriverPS4_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
{ {
@ -451,12 +485,15 @@ static SDL_bool
HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{ {
SDL_DriverPS4_Context *ctx; SDL_DriverPS4_Context *ctx;
SDL_bool enhanced_mode = SDL_FALSE;
ctx = (SDL_DriverPS4_Context *)SDL_calloc(1, sizeof(*ctx)); ctx = (SDL_DriverPS4_Context *)SDL_calloc(1, sizeof(*ctx));
if (!ctx) { if (!ctx) {
SDL_OutOfMemory(); SDL_OutOfMemory();
return SDL_FALSE; return SDL_FALSE;
} }
ctx->device = device;
ctx->joystick = joystick;
device->dev = hid_open_path(device->path, 0); device->dev = hid_open_path(device->path, 0);
if (!device->dev) { if (!device->dev) {
@ -471,6 +508,7 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
if (ctx->is_dongle) { if (ctx->is_dongle) {
ctx->is_bluetooth = SDL_FALSE; ctx->is_bluetooth = SDL_FALSE;
ctx->official_controller = SDL_TRUE; ctx->official_controller = SDL_TRUE;
enhanced_mode = SDL_TRUE;
} else if (device->vendor_id == USB_VENDOR_SONY) { } else if (device->vendor_id == USB_VENDOR_SONY) {
Uint8 data[USB_PACKET_LENGTH]; Uint8 data[USB_PACKET_LENGTH];
int size; int size;
@ -484,13 +522,30 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
data[6], data[5], data[4], data[3], data[2], data[1]); data[6], data[5], data[4], data[3], data[2], data[1]);
joystick->serial = SDL_strdup(serial); joystick->serial = SDL_strdup(serial);
ctx->is_bluetooth = SDL_FALSE; ctx->is_bluetooth = SDL_FALSE;
enhanced_mode = SDL_TRUE;
} else { } else {
ctx->is_bluetooth = SDL_TRUE; ctx->is_bluetooth = SDL_TRUE;
/* Read a report to see if we're in enhanced mode */
size = hid_read_timeout(device->dev, data, sizeof(data), 16);
#ifdef DEBUG_PS4_PROTOCOL
if (size > 0) {
HIDAPI_DumpPacket("PS4 first packet: size = %d", data, size);
} else {
SDL_Log("PS4 first packet: size = %d\n", size);
}
#endif
if (size > 0 &&
data[0] >= k_EPS4ReportIdBluetoothState1 &&
data[0] <= k_EPS4ReportIdBluetoothState9) {
enhanced_mode = SDL_TRUE;
}
} }
ctx->official_controller = SDL_TRUE; ctx->official_controller = SDL_TRUE;
} else { } else {
/* Third party controllers appear to all be wired */ /* Third party controllers appear to all be wired */
ctx->is_bluetooth = SDL_FALSE; ctx->is_bluetooth = SDL_FALSE;
enhanced_mode = SDL_TRUE;
} }
#ifdef DEBUG_PS4 #ifdef DEBUG_PS4
SDL_Log("PS4 dongle = %s, bluetooth = %s\n", ctx->is_dongle ? "TRUE" : "FALSE", ctx->is_bluetooth ? "TRUE" : "FALSE"); SDL_Log("PS4 dongle = %s, bluetooth = %s\n", ctx->is_dongle ? "TRUE" : "FALSE", ctx->is_bluetooth ? "TRUE" : "FALSE");
@ -503,28 +558,42 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
} }
if (HIDAPI_DriverPS4_CanRumble(device->vendor_id, device->product_id)) { if (HIDAPI_DriverPS4_CanRumble(device->vendor_id, device->product_id)) {
if (ctx->is_bluetooth) { ctx->effects_supported = SDL_TRUE;
ctx->effects_supported = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, SDL_FALSE); }
} else {
ctx->effects_supported = SDL_TRUE; if (!joystick->serial && device->serial && SDL_strlen(device->serial) == 12) {
int i, j;
char serial[18];
j = -1;
for (i = 0; i < 12; i += 2) {
j += 1;
SDL_memcpy(&serial[j], &device->serial[i], 2);
j += 2;
serial[j] = '-';
} }
serial[j] = '\0';
joystick->serial = SDL_strdup(serial);
} }
/* Initialize player index (needed for setting LEDs) */ /* Initialize player index (needed for setting LEDs) */
ctx->player_index = SDL_JoystickGetPlayerIndex(joystick); ctx->player_index = SDL_JoystickGetPlayerIndex(joystick);
/* Initialize LED and effect state */ /* Initialize the joystick capabilities
HIDAPI_DriverPS4_UpdateEffects(device); *
* We can't dynamically add the touchpad button, so always report it here
/* Initialize the joystick capabilities */ */
joystick->nbuttons = 16; joystick->nbuttons = 16;
joystick->naxes = SDL_CONTROLLER_AXIS_MAX; joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; joystick->epowerlevel = ctx->is_bluetooth ? SDL_JOYSTICK_POWER_UNKNOWN : SDL_JOYSTICK_POWER_WIRED;
SDL_PrivateJoystickAddTouchpad(joystick, 2);
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO);
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL);
if (enhanced_mode) {
HIDAPI_DriverPS4_SetEnhancedMode(device, joystick);
} else {
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE,
SDL_PS4RumbleHintChanged, ctx);
}
return SDL_TRUE; return SDL_TRUE;
} }
@ -569,6 +638,10 @@ HIDAPI_DriverPS4_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joysti
{ {
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
if (!ctx->enhanced_mode) {
return SDL_Unsupported();
}
if (enabled) { if (enabled) {
HIDAPI_DriverPS4_LoadCalibrationData(device); HIDAPI_DriverPS4_LoadCalibrationData(device);
} }
@ -729,7 +802,7 @@ HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
{ {
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
SDL_Joystick *joystick = NULL; SDL_Joystick *joystick = NULL;
Uint8 data[USB_PACKET_LENGTH]; Uint8 data[USB_PACKET_LENGTH*2];
int size; int size;
if (device->num_joysticks > 0) { if (device->num_joysticks > 0) {
@ -756,6 +829,10 @@ HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
case k_EPS4ReportIdBluetoothState7: case k_EPS4ReportIdBluetoothState7:
case k_EPS4ReportIdBluetoothState8: case k_EPS4ReportIdBluetoothState8:
case k_EPS4ReportIdBluetoothState9: case k_EPS4ReportIdBluetoothState9:
if (!ctx->enhanced_mode) {
/* This is the extended report, we can enable effects now */
HIDAPI_DriverPS4_SetEnhancedMode(device, joystick);
}
/* Bluetooth state packets have two additional bytes at the beginning, the first notes if HID is present */ /* Bluetooth state packets have two additional bytes at the beginning, the first notes if HID is present */
if (data[1] & 0x80) { if (data[1] & 0x80) {
HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t*)&data[3]); HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t*)&data[3]);
@ -779,6 +856,11 @@ HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
static void static void
HIDAPI_DriverPS4_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) HIDAPI_DriverPS4_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{ {
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE,
SDL_PS4RumbleHintChanged, ctx);
hid_close(device->dev); hid_close(device->dev);
device->dev = NULL; device->dev = NULL;

View File

@ -154,7 +154,7 @@ typedef struct {
SDL_HIDAPI_Device *device; SDL_HIDAPI_Device *device;
SDL_Joystick *joystick; SDL_Joystick *joystick;
SDL_bool is_bluetooth; SDL_bool is_bluetooth;
SDL_bool effects_supported; SDL_bool enhanced_mode;
SDL_bool report_sensors; SDL_bool report_sensors;
SDL_bool hardware_calibration; SDL_bool hardware_calibration;
IMUCalibrationData calibration[6]; IMUCalibrationData calibration[6];
@ -382,7 +382,7 @@ HIDAPI_DriverPS5_UpdateEffects(SDL_HIDAPI_Device *device, EDS5Effect effect)
int *pending_size; int *pending_size;
int maximum_size; int maximum_size;
if (!ctx->effects_supported) { if (!ctx->enhanced_mode) {
return SDL_Unsupported(); return SDL_Unsupported();
} }
@ -508,12 +508,12 @@ HIDAPI_DriverPS5_CheckPendingLEDReset(SDL_HIDAPI_Device *device)
} }
static void static void
HIDAPI_DriverPS5_SetEffectsSupported(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) HIDAPI_DriverPS5_SetEnhancedMode(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{ {
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
if (!ctx->effects_supported) { if (!ctx->enhanced_mode) {
ctx->effects_supported = SDL_TRUE; ctx->enhanced_mode = SDL_TRUE;
SDL_PrivateJoystickAddTouchpad(joystick, 2); SDL_PrivateJoystickAddTouchpad(joystick, 2);
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO); SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO);
@ -530,7 +530,7 @@ static void SDLCALL SDL_PS5RumbleHintChanged(void *userdata, const char *name, c
/* This is a one-way trip, you can't switch the controller back to simple report mode */ /* This is a one-way trip, you can't switch the controller back to simple report mode */
if (SDL_GetStringBoolean(hint, SDL_FALSE)) { if (SDL_GetStringBoolean(hint, SDL_FALSE)) {
HIDAPI_DriverPS5_SetEffectsSupported(ctx->device, ctx->joystick); HIDAPI_DriverPS5_SetEnhancedMode(ctx->device, ctx->joystick);
} }
} }
@ -556,7 +556,7 @@ HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
SDL_DriverPS5_Context *ctx; SDL_DriverPS5_Context *ctx;
Uint8 data[USB_PACKET_LENGTH*2]; Uint8 data[USB_PACKET_LENGTH*2];
int size; int size;
SDL_bool effects_supported = SDL_FALSE; SDL_bool enhanced_mode = SDL_FALSE;
ctx = (SDL_DriverPS5_Context *)SDL_calloc(1, sizeof(*ctx)); ctx = (SDL_DriverPS5_Context *)SDL_calloc(1, sizeof(*ctx));
if (!ctx) { if (!ctx) {
@ -587,18 +587,18 @@ HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
if (size == 64) { if (size == 64) {
/* Connected over USB */ /* Connected over USB */
ctx->is_bluetooth = SDL_FALSE; ctx->is_bluetooth = SDL_FALSE;
effects_supported = SDL_TRUE; enhanced_mode = SDL_TRUE;
} else if (size > 0 && data[0] == k_EPS5ReportIdBluetoothEffects) { } else if (size > 0 && data[0] == k_EPS5ReportIdBluetoothEffects) {
/* Connected over Bluetooth, using enhanced reports */ /* Connected over Bluetooth, using enhanced reports */
ctx->is_bluetooth = SDL_TRUE; ctx->is_bluetooth = SDL_TRUE;
effects_supported = SDL_TRUE; enhanced_mode = SDL_TRUE;
} else { } else {
/* Connected over Bluetooth, using simple reports (DirectInput enabled) */ /* Connected over Bluetooth, using simple reports (DirectInput enabled) */
ctx->is_bluetooth = SDL_TRUE; ctx->is_bluetooth = SDL_TRUE;
effects_supported = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, SDL_FALSE); enhanced_mode = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, SDL_FALSE);
} }
if (effects_supported) { if (enhanced_mode) {
/* Read the serial number (Bluetooth address in reverse byte order) /* Read the serial number (Bluetooth address in reverse byte order)
This will also enable enhanced reports over Bluetooth This will also enable enhanced reports over Bluetooth
*/ */
@ -636,9 +636,10 @@ HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
*/ */
joystick->nbuttons = 17; joystick->nbuttons = 17;
joystick->naxes = SDL_CONTROLLER_AXIS_MAX; joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
joystick->epowerlevel = ctx->is_bluetooth ? SDL_JOYSTICK_POWER_UNKNOWN : SDL_JOYSTICK_POWER_WIRED;
if (effects_supported) { if (enhanced_mode) {
HIDAPI_DriverPS5_SetEffectsSupported(device, joystick); HIDAPI_DriverPS5_SetEnhancedMode(device, joystick);
} else { } else {
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE,
SDL_PS5RumbleHintChanged, ctx); SDL_PS5RumbleHintChanged, ctx);
@ -691,7 +692,7 @@ HIDAPI_DriverPS5_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joysti
{ {
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
if (!ctx->effects_supported) { if (!ctx->enhanced_mode) {
return SDL_Unsupported(); return SDL_Unsupported();
} }
@ -969,9 +970,9 @@ HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device)
} }
break; break;
case k_EPS5ReportIdBluetoothState: case k_EPS5ReportIdBluetoothState:
if (!ctx->effects_supported) { if (!ctx->enhanced_mode) {
/* This is the extended report, we can enable effects now */ /* This is the extended report, we can enable effects now */
HIDAPI_DriverPS5_SetEffectsSupported(device, joystick); HIDAPI_DriverPS5_SetEnhancedMode(device, joystick);
} }
if (ctx->led_reset_state == k_EDS5LEDResetStatePending) { if (ctx->led_reset_state == k_EDS5LEDResetStatePending) {
HIDAPI_DriverPS5_CheckPendingLEDReset(device); HIDAPI_DriverPS5_CheckPendingLEDReset(device);