early-access version 1617
This commit is contained in:
@@ -23,7 +23,6 @@
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_log.h"
|
||||
#include "SDL_events.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_haptic.h"
|
||||
@@ -364,6 +363,30 @@ HIDAPI_DriverGameCube_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *jo
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverGameCube_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverGameCube_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverGameCube_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverGameCube_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverGameCube_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
@@ -403,8 +426,12 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube =
|
||||
HIDAPI_DriverGameCube_UpdateDevice,
|
||||
HIDAPI_DriverGameCube_OpenJoystick,
|
||||
HIDAPI_DriverGameCube_RumbleJoystick,
|
||||
HIDAPI_DriverGameCube_RumbleJoystickTriggers,
|
||||
HIDAPI_DriverGameCube_HasJoystickLED,
|
||||
HIDAPI_DriverGameCube_SetJoystickLED,
|
||||
HIDAPI_DriverGameCube_SetJoystickSensorsEnabled,
|
||||
HIDAPI_DriverGameCube_CloseJoystick,
|
||||
HIDAPI_DriverGameCube_FreeDevice
|
||||
HIDAPI_DriverGameCube_FreeDevice,
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_GAMECUBE */
|
||||
|
505
externals/SDL/src/joystick/hidapi/SDL_hidapi_ps4.c
vendored
505
externals/SDL/src/joystick/hidapi/SDL_hidapi_ps4.c
vendored
@@ -26,7 +26,6 @@
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_log.h"
|
||||
#include "SDL_events.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_joystick.h"
|
||||
@@ -38,11 +37,30 @@
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_PS4
|
||||
|
||||
/* Define this if you want to log all packets from the controller */
|
||||
/*#define DEBUG_PS4_PROTOCOL*/
|
||||
|
||||
/* Define this if you want to log calibration data */
|
||||
/*#define DEBUG_PS4_CALIBRATION*/
|
||||
|
||||
#define GYRO_RES_PER_DEGREE 1024.0f
|
||||
#define ACCEL_RES_PER_G 8192.0f
|
||||
|
||||
#define LOAD16(A, B) (Sint16)((Uint16)(A) | (((Uint16)(B)) << 8))
|
||||
|
||||
typedef enum
|
||||
{
|
||||
k_EPS4ReportIdUsbState = 1,
|
||||
k_EPS4ReportIdUsbEffects = 5,
|
||||
k_EPS4ReportIdBluetoothState = 17,
|
||||
k_EPS4ReportIdBluetoothState1 = 17,
|
||||
k_EPS4ReportIdBluetoothState2 = 18,
|
||||
k_EPS4ReportIdBluetoothState3 = 19,
|
||||
k_EPS4ReportIdBluetoothState4 = 20,
|
||||
k_EPS4ReportIdBluetoothState5 = 21,
|
||||
k_EPS4ReportIdBluetoothState6 = 22,
|
||||
k_EPS4ReportIdBluetoothState7 = 23,
|
||||
k_EPS4ReportIdBluetoothState8 = 24,
|
||||
k_EPS4ReportIdBluetoothState9 = 25,
|
||||
k_EPS4ReportIdBluetoothEffects = 17,
|
||||
k_EPS4ReportIdDisconnectMessage = 226,
|
||||
} EPS4ReportId;
|
||||
@@ -64,19 +82,19 @@ typedef struct
|
||||
Uint8 ucTriggerLeft;
|
||||
Uint8 ucTriggerRight;
|
||||
Uint8 _rgucPad0[ 3 ];
|
||||
Sint16 sGyroX;
|
||||
Sint16 sGyroY;
|
||||
Sint16 sGyroZ;
|
||||
Sint16 sAccelX;
|
||||
Sint16 sAccelY;
|
||||
Sint16 sAccelZ;
|
||||
Uint8 rgucGyroX[2];
|
||||
Uint8 rgucGyroY[2];
|
||||
Uint8 rgucGyroZ[2];
|
||||
Uint8 rgucAccelX[2];
|
||||
Uint8 rgucAccelY[2];
|
||||
Uint8 rgucAccelZ[2];
|
||||
Uint8 _rgucPad1[ 5 ];
|
||||
Uint8 ucBatteryLevel;
|
||||
Uint8 _rgucPad2[ 4 ];
|
||||
Uint8 ucTrackpadCounter1;
|
||||
Uint8 rgucTrackpadData1[ 3 ];
|
||||
Uint8 ucTrackpadCounter2;
|
||||
Uint8 rgucTrackpadData2[ 3 ];
|
||||
Uint8 ucTouchpadCounter1;
|
||||
Uint8 rgucTouchpadData1[ 3 ];
|
||||
Uint8 ucTouchpadCounter2;
|
||||
Uint8 rgucTouchpadData2[ 3 ];
|
||||
} PS4StatePacket_t;
|
||||
|
||||
typedef struct
|
||||
@@ -95,43 +113,37 @@ typedef struct
|
||||
Uint8 ucVolumeSpeaker;
|
||||
} DS4EffectsState_t;
|
||||
|
||||
typedef struct {
|
||||
Sint16 bias;
|
||||
float sensitivity;
|
||||
} IMUCalibrationData;
|
||||
|
||||
typedef struct {
|
||||
SDL_bool is_dongle;
|
||||
SDL_bool is_bluetooth;
|
||||
SDL_bool official_controller;
|
||||
SDL_bool audio_supported;
|
||||
SDL_bool rumble_supported;
|
||||
SDL_bool effects_supported;
|
||||
SDL_bool report_sensors;
|
||||
SDL_bool hardware_calibration;
|
||||
IMUCalibrationData calibration[6];
|
||||
int player_index;
|
||||
Uint8 rumble_left;
|
||||
Uint8 rumble_right;
|
||||
SDL_bool color_set;
|
||||
Uint8 led_red;
|
||||
Uint8 led_green;
|
||||
Uint8 led_blue;
|
||||
Uint8 volume;
|
||||
Uint32 last_volume_check;
|
||||
PS4StatePacket_t last_state;
|
||||
} SDL_DriverPS4_Context;
|
||||
|
||||
|
||||
/* Public domain CRC implementation adapted from:
|
||||
http://home.thep.lu.se/~bjorn/crc/crc32_simple.c
|
||||
*/
|
||||
static Uint32 crc32_for_byte(Uint32 r)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < 8; ++i) {
|
||||
r = (r & 1? 0: (Uint32)0xEDB88320L) ^ r >> 1;
|
||||
}
|
||||
return r ^ (Uint32)0xFF000000L;
|
||||
}
|
||||
|
||||
static Uint32 crc32(Uint32 crc, const void *data, int count)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < count; ++i) {
|
||||
crc = crc32_for_byte((Uint8)crc ^ ((const Uint8*)data)[i]) ^ crc >> 8;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverPS4_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
|
||||
{
|
||||
return (type == SDL_CONTROLLER_TYPE_PS4);
|
||||
return (type == SDL_CONTROLLER_TYPE_PS4) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
static const char *
|
||||
@@ -143,34 +155,11 @@ HIDAPI_DriverPS4_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static SDL_bool ReadFeatureReport(hid_device *dev, Uint8 report_id, Uint8 *data, size_t size)
|
||||
static int ReadFeatureReport(hid_device *dev, Uint8 report_id, Uint8 *report, size_t length)
|
||||
{
|
||||
Uint8 report[USB_PACKET_LENGTH + 1];
|
||||
|
||||
SDL_memset(report, 0, sizeof(report));
|
||||
SDL_memset(report, 0, length);
|
||||
report[0] = report_id;
|
||||
if (hid_get_feature_report(dev, report, sizeof(report)) < 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
SDL_memcpy(data, report, SDL_min(size, sizeof(report)));
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
static SDL_bool CheckUSBConnected(hid_device *dev)
|
||||
{
|
||||
int i;
|
||||
Uint8 data[16];
|
||||
|
||||
/* This will fail if we're on Bluetooth */
|
||||
if (ReadFeatureReport(dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data))) {
|
||||
for (i = 0; i < sizeof(data); ++i) {
|
||||
if (data[i] != 0x00) {
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
/* Maybe the dongle without a connected controller? */
|
||||
}
|
||||
return SDL_FALSE;
|
||||
return hid_get_feature_report(dev, report, length);
|
||||
}
|
||||
|
||||
static SDL_bool HIDAPI_DriverPS4_CanRumble(Uint16 vendor_id, Uint16 product_id)
|
||||
@@ -222,7 +211,226 @@ HIDAPI_DriverPS4_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble);
|
||||
static void
|
||||
HIDAPI_DriverPS4_LoadCalibrationData(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
|
||||
int i, tries, size;
|
||||
SDL_bool have_data = SDL_FALSE;
|
||||
Uint8 data[USB_PACKET_LENGTH];
|
||||
|
||||
if (!ctx->official_controller) {
|
||||
#ifdef DEBUG_PS4_CALIBRATION
|
||||
SDL_Log("Not an official controller, ignoring calibration\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
for( tries = 0; tries < 5; ++tries ) {
|
||||
/* For Bluetooth controllers, this report switches them into advanced report mode */
|
||||
size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdGyroCalibration_USB, data, sizeof(data));
|
||||
if (size < 35) {
|
||||
#ifdef DEBUG_PS4_CALIBRATION
|
||||
SDL_Log("Short read of calibration data: %d, ignoring calibration\n", size);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->is_bluetooth) {
|
||||
size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdGyroCalibration_BT, data, sizeof(data));
|
||||
if (size < 35) {
|
||||
#ifdef DEBUG_PS4_CALIBRATION
|
||||
SDL_Log("Short read of calibration data: %d, ignoring calibration\n", size);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* In some cases this report returns all zeros. Usually immediately after connection with the PS4 Dongle */
|
||||
for (i = 0; i < size; ++i) {
|
||||
if (data[i]) {
|
||||
have_data = SDL_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (have_data) {
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_Delay(2);
|
||||
}
|
||||
|
||||
if (have_data) {
|
||||
Sint16 sGyroPitchBias, sGyroYawBias, sGyroRollBias;
|
||||
Sint16 sGyroPitchPlus, sGyroPitchMinus;
|
||||
Sint16 sGyroYawPlus, sGyroYawMinus;
|
||||
Sint16 sGyroRollPlus, sGyroRollMinus;
|
||||
Sint16 sGyroSpeedPlus, sGyroSpeedMinus;
|
||||
|
||||
Sint16 sAccXPlus, sAccXMinus;
|
||||
Sint16 sAccYPlus, sAccYMinus;
|
||||
Sint16 sAccZPlus, sAccZMinus;
|
||||
|
||||
float flNumerator;
|
||||
Sint16 sRange2g;
|
||||
|
||||
#ifdef DEBUG_PS4_CALIBRATION
|
||||
HIDAPI_DumpPacket("PS4 calibration packet: size = %d", data, size);
|
||||
#endif
|
||||
|
||||
sGyroPitchBias = LOAD16(data[1], data[2]);
|
||||
sGyroYawBias = LOAD16(data[3], data[4]);
|
||||
sGyroRollBias = LOAD16(data[5], data[6]);
|
||||
|
||||
if (ctx->is_bluetooth || ctx->is_dongle) {
|
||||
sGyroPitchPlus = LOAD16(data[7], data[8]);
|
||||
sGyroYawPlus = LOAD16(data[9], data[10]);
|
||||
sGyroRollPlus = LOAD16(data[11], data[12]);
|
||||
sGyroPitchMinus = LOAD16(data[13], data[14]);
|
||||
sGyroYawMinus = LOAD16(data[15], data[16]);
|
||||
sGyroRollMinus = LOAD16(data[17], data[18]);
|
||||
} else {
|
||||
sGyroPitchPlus = LOAD16(data[7], data[8]);
|
||||
sGyroPitchMinus = LOAD16(data[9], data[10]);
|
||||
sGyroYawPlus = LOAD16(data[11], data[12]);
|
||||
sGyroYawMinus = LOAD16(data[13], data[14]);
|
||||
sGyroRollPlus = LOAD16(data[15], data[16]);
|
||||
sGyroRollMinus = LOAD16(data[17], data[18]);
|
||||
}
|
||||
|
||||
sGyroSpeedPlus = LOAD16(data[19], data[20]);
|
||||
sGyroSpeedMinus = LOAD16(data[21], data[22]);
|
||||
|
||||
sAccXPlus = LOAD16(data[23], data[24]);
|
||||
sAccXMinus = LOAD16(data[25], data[26]);
|
||||
sAccYPlus = LOAD16(data[27], data[28]);
|
||||
sAccYMinus = LOAD16(data[29], data[30]);
|
||||
sAccZPlus = LOAD16(data[31], data[32]);
|
||||
sAccZMinus = LOAD16(data[33], data[34]);
|
||||
|
||||
flNumerator = (sGyroSpeedPlus + sGyroSpeedMinus) * GYRO_RES_PER_DEGREE;
|
||||
ctx->calibration[0].bias = sGyroPitchBias;
|
||||
ctx->calibration[0].sensitivity = flNumerator / (sGyroPitchPlus - sGyroPitchMinus);
|
||||
|
||||
ctx->calibration[1].bias = sGyroYawBias;
|
||||
ctx->calibration[1].sensitivity = flNumerator / (sGyroYawPlus - sGyroYawMinus);
|
||||
|
||||
ctx->calibration[2].bias = sGyroRollBias;
|
||||
ctx->calibration[2].sensitivity = flNumerator / (sGyroRollPlus - sGyroRollMinus);
|
||||
|
||||
sRange2g = sAccXPlus - sAccXMinus;
|
||||
ctx->calibration[3].bias = sAccXPlus - sRange2g / 2;
|
||||
ctx->calibration[3].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
|
||||
|
||||
sRange2g = sAccYPlus - sAccYMinus;
|
||||
ctx->calibration[4].bias = sAccYPlus - sRange2g / 2;
|
||||
ctx->calibration[4].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
|
||||
|
||||
sRange2g = sAccZPlus - sAccZMinus;
|
||||
ctx->calibration[5].bias = sAccZPlus - sRange2g / 2;
|
||||
ctx->calibration[5].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
|
||||
|
||||
ctx->hardware_calibration = SDL_TRUE;
|
||||
for (i = 0; i < 6; ++i) {
|
||||
float divisor = (i < 3 ? 64.0f : 1.0f);
|
||||
#ifdef DEBUG_PS4_CALIBRATION
|
||||
SDL_Log("calibration[%d] bias = %d, sensitivity = %f\n", i, ctx->calibration[i].bias, ctx->calibration[i].sensitivity);
|
||||
#endif
|
||||
/* Some controllers have a bad calibration */
|
||||
if ((SDL_abs(ctx->calibration[i].bias) > 1024) || (SDL_fabs(1.0f - ctx->calibration[i].sensitivity / divisor) > 0.5f)) {
|
||||
#ifdef DEBUG_PS4_CALIBRATION
|
||||
SDL_Log("invalid calibration, ignoring\n");
|
||||
#endif
|
||||
ctx->hardware_calibration = SDL_FALSE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#ifdef DEBUG_PS4_CALIBRATION
|
||||
SDL_Log("Calibration data not available\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static float
|
||||
HIDAPI_DriverPS4_ApplyCalibrationData(SDL_DriverPS4_Context *ctx, int index, Sint16 value)
|
||||
{
|
||||
float result;
|
||||
|
||||
if (ctx->hardware_calibration) {
|
||||
IMUCalibrationData *calibration = &ctx->calibration[index];
|
||||
|
||||
result = (value - calibration->bias) * calibration->sensitivity;
|
||||
} else if (index < 3) {
|
||||
result = value * 64.f;
|
||||
} else {
|
||||
result = value;
|
||||
}
|
||||
|
||||
/* Convert the raw data to the units expected by SDL */
|
||||
if (index < 3) {
|
||||
result = (result / GYRO_RES_PER_DEGREE) * (float)M_PI / 180.0f;
|
||||
} else {
|
||||
result = (result / ACCEL_RES_PER_G) * SDL_STANDARD_GRAVITY;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverPS4_UpdateEffects(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
|
||||
DS4EffectsState_t *effects;
|
||||
Uint8 data[78];
|
||||
int report_size, offset;
|
||||
|
||||
if (!ctx->effects_supported) {
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
SDL_zero(data);
|
||||
|
||||
if (ctx->is_bluetooth) {
|
||||
data[0] = k_EPS4ReportIdBluetoothEffects;
|
||||
data[1] = 0xC0 | 0x04; /* Magic value HID + CRC, also sets interval to 4ms for samples */
|
||||
data[3] = 0x03; /* 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval */
|
||||
|
||||
report_size = 78;
|
||||
offset = 6;
|
||||
} else {
|
||||
data[0] = k_EPS4ReportIdUsbEffects;
|
||||
data[1] = 0x07; /* Magic value */
|
||||
|
||||
report_size = 32;
|
||||
offset = 4;
|
||||
}
|
||||
effects = (DS4EffectsState_t *)&data[offset];
|
||||
|
||||
effects->ucRumbleLeft = ctx->rumble_left;
|
||||
effects->ucRumbleRight = ctx->rumble_right;
|
||||
|
||||
/* Populate the LED state with the appropriate color from our lookup table */
|
||||
if (ctx->color_set) {
|
||||
effects->ucLedRed = ctx->led_red;
|
||||
effects->ucLedGreen = ctx->led_green;
|
||||
effects->ucLedBlue = ctx->led_blue;
|
||||
} else {
|
||||
SetLedsForPlayerIndex(effects, ctx->player_index);
|
||||
}
|
||||
|
||||
if (ctx->is_bluetooth) {
|
||||
/* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */
|
||||
Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */
|
||||
Uint32 unCRC;
|
||||
unCRC = SDL_crc32(0, &ubHdr, 1);
|
||||
unCRC = SDL_crc32(unCRC, data, (size_t)(report_size - sizeof(unCRC)));
|
||||
SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
|
||||
}
|
||||
|
||||
if (SDL_HIDAPI_SendRumble(device, data, report_size) != report_size) {
|
||||
return SDL_SetError("Couldn't send rumble packet");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverPS4_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
|
||||
@@ -236,7 +444,7 @@ HIDAPI_DriverPS4_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID
|
||||
ctx->player_index = player_index;
|
||||
|
||||
/* This will set the new LED state based on the new player index */
|
||||
HIDAPI_DriverPS4_RumbleJoystick(device, SDL_JoystickFromInstanceID(instance_id), 0, 0);
|
||||
HIDAPI_DriverPS4_UpdateEffects(device);
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
@@ -262,8 +470,24 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
ctx->is_dongle = (device->vendor_id == USB_VENDOR_SONY && device->product_id == USB_PRODUCT_SONY_DS4_DONGLE);
|
||||
if (ctx->is_dongle) {
|
||||
ctx->is_bluetooth = SDL_FALSE;
|
||||
ctx->official_controller = SDL_TRUE;
|
||||
} else if (device->vendor_id == USB_VENDOR_SONY) {
|
||||
ctx->is_bluetooth = !CheckUSBConnected(device->dev);
|
||||
Uint8 data[USB_PACKET_LENGTH];
|
||||
int size;
|
||||
|
||||
/* This will fail if we're on Bluetooth */
|
||||
size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data));
|
||||
if (size >= 7) {
|
||||
char serial[18];
|
||||
|
||||
SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
|
||||
data[6], data[5], data[4], data[3], data[2], data[1]);
|
||||
joystick->serial = SDL_strdup(serial);
|
||||
ctx->is_bluetooth = SDL_FALSE;
|
||||
} else {
|
||||
ctx->is_bluetooth = SDL_TRUE;
|
||||
}
|
||||
ctx->official_controller = SDL_TRUE;
|
||||
} else {
|
||||
/* Third party controllers appear to all be wired */
|
||||
ctx->is_bluetooth = SDL_FALSE;
|
||||
@@ -280,9 +504,9 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
|
||||
if (HIDAPI_DriverPS4_CanRumble(device->vendor_id, device->product_id)) {
|
||||
if (ctx->is_bluetooth) {
|
||||
ctx->rumble_supported = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, SDL_FALSE);
|
||||
ctx->effects_supported = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, SDL_FALSE);
|
||||
} else {
|
||||
ctx->rumble_supported = SDL_TRUE;
|
||||
ctx->effects_supported = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,13 +514,17 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
ctx->player_index = SDL_JoystickGetPlayerIndex(joystick);
|
||||
|
||||
/* Initialize LED and effect state */
|
||||
HIDAPI_DriverPS4_RumbleJoystick(device, joystick, 0, 0);
|
||||
HIDAPI_DriverPS4_UpdateEffects(device);
|
||||
|
||||
/* Initialize the joystick capabilities */
|
||||
joystick->nbuttons = 16;
|
||||
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
|
||||
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
|
||||
|
||||
SDL_PrivateJoystickAddTouchpad(joystick, 2);
|
||||
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO);
|
||||
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL);
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
@@ -304,58 +532,59 @@ static int
|
||||
HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
|
||||
{
|
||||
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
|
||||
DS4EffectsState_t *effects;
|
||||
Uint8 data[78];
|
||||
int report_size, offset;
|
||||
|
||||
if (!ctx->rumble_supported) {
|
||||
return SDL_Unsupported();
|
||||
ctx->rumble_left = (low_frequency_rumble >> 8);
|
||||
ctx->rumble_right = (high_frequency_rumble >> 8);
|
||||
|
||||
return HIDAPI_DriverPS4_UpdateEffects(device);
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverPS4_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverPS4_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverPS4_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
|
||||
|
||||
ctx->color_set = SDL_TRUE;
|
||||
ctx->led_red = red;
|
||||
ctx->led_green = green;
|
||||
ctx->led_blue = blue;
|
||||
|
||||
return HIDAPI_DriverPS4_UpdateEffects(device);
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverPS4_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
|
||||
{
|
||||
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
|
||||
|
||||
if (enabled) {
|
||||
HIDAPI_DriverPS4_LoadCalibrationData(device);
|
||||
}
|
||||
ctx->report_sensors = enabled;
|
||||
|
||||
/* In order to send rumble, we have to send a complete effect packet */
|
||||
SDL_memset(data, 0, sizeof(data));
|
||||
|
||||
if (ctx->is_bluetooth) {
|
||||
data[0] = k_EPS4ReportIdBluetoothEffects;
|
||||
data[1] = 0xC0 | 0x04; /* Magic value HID + CRC, also sets interval to 4ms for samples */
|
||||
data[3] = 0x03; /* 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval */
|
||||
|
||||
report_size = 78;
|
||||
offset = 6;
|
||||
} else {
|
||||
data[0] = k_EPS4ReportIdUsbEffects;
|
||||
data[1] = 0x07; /* Magic value */
|
||||
|
||||
report_size = 32;
|
||||
offset = 4;
|
||||
}
|
||||
effects = (DS4EffectsState_t *)&data[offset];
|
||||
|
||||
effects->ucRumbleLeft = (low_frequency_rumble >> 8);
|
||||
effects->ucRumbleRight = (high_frequency_rumble >> 8);
|
||||
|
||||
/* Populate the LED state with the appropriate color from our lookup table */
|
||||
SetLedsForPlayerIndex(effects, ctx->player_index);
|
||||
|
||||
if (ctx->is_bluetooth) {
|
||||
/* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */
|
||||
Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */
|
||||
Uint32 unCRC;
|
||||
unCRC = crc32(0, &ubHdr, 1);
|
||||
unCRC = crc32(unCRC, data, (Uint32)(report_size - sizeof(unCRC)));
|
||||
SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
|
||||
}
|
||||
|
||||
if (SDL_HIDAPI_SendRumble(device, data, report_size) != report_size) {
|
||||
return SDL_SetError("Couldn't send rumble packet");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet)
|
||||
{
|
||||
static const float TOUCHPAD_SCALEX = 1.0f / 1920;
|
||||
static const float TOUCHPAD_SCALEY = 1.0f / 920; /* This is noted as being 944 resolution, but 920 feels better */
|
||||
Sint16 axis;
|
||||
Uint8 touchpad_state;
|
||||
int touchpad_x, touchpad_y;
|
||||
|
||||
if (ctx->last_state.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) {
|
||||
{
|
||||
@@ -423,6 +652,15 @@ HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
|
||||
/* Some fightsticks, ex: Victrix FS Pro will only this these digital trigger bits and not the analog values so this needs to run whenever the
|
||||
trigger is evaluated
|
||||
*/
|
||||
if ((packet->rgucButtonsHatAndCounter[1] & 0x0C) != 0) {
|
||||
Uint8 data = packet->rgucButtonsHatAndCounter[1];
|
||||
packet->ucTriggerLeft = (data & 0x04) && packet->ucTriggerLeft == 0 ? 255 : packet->ucTriggerLeft;
|
||||
packet->ucTriggerRight = (data & 0x08) && packet->ucTriggerRight == 0 ? 255 : packet->ucTriggerRight;
|
||||
}
|
||||
|
||||
if (ctx->last_state.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) {
|
||||
Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03);
|
||||
|
||||
@@ -459,6 +697,30 @@ HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_
|
||||
}
|
||||
}
|
||||
|
||||
touchpad_state = ((packet->ucTouchpadCounter1 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
|
||||
touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8);
|
||||
touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4);
|
||||
SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
|
||||
|
||||
touchpad_state = ((packet->ucTouchpadCounter2 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
|
||||
touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8);
|
||||
touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4);
|
||||
SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
|
||||
|
||||
if (ctx->report_sensors) {
|
||||
float data[3];
|
||||
|
||||
data[0] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 0, LOAD16(packet->rgucGyroX[0], packet->rgucGyroX[1]));
|
||||
data[1] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 1, LOAD16(packet->rgucGyroY[0], packet->rgucGyroY[1]));
|
||||
data[2] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 2, LOAD16(packet->rgucGyroZ[0], packet->rgucGyroZ[1]));
|
||||
SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_GYRO, data, 3);
|
||||
|
||||
data[0] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 3, LOAD16(packet->rgucAccelX[0], packet->rgucAccelX[1]));
|
||||
data[1] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 4, LOAD16(packet->rgucAccelY[0], packet->rgucAccelY[1]));
|
||||
data[2] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 5, LOAD16(packet->rgucAccelZ[0], packet->rgucAccelZ[1]));
|
||||
SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_ACCEL, data, 3);
|
||||
}
|
||||
|
||||
SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
|
||||
}
|
||||
|
||||
@@ -478,13 +740,26 @@ HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
}
|
||||
|
||||
while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
|
||||
#ifdef DEBUG_PS4_PROTOCOL
|
||||
HIDAPI_DumpPacket("PS4 packet: size = %d", data, size);
|
||||
#endif
|
||||
switch (data[0]) {
|
||||
case k_EPS4ReportIdUsbState:
|
||||
HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[1]);
|
||||
break;
|
||||
case k_EPS4ReportIdBluetoothState:
|
||||
/* Bluetooth state packets have two additional bytes at the beginning */
|
||||
HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[3]);
|
||||
case k_EPS4ReportIdBluetoothState1:
|
||||
case k_EPS4ReportIdBluetoothState2:
|
||||
case k_EPS4ReportIdBluetoothState3:
|
||||
case k_EPS4ReportIdBluetoothState4:
|
||||
case k_EPS4ReportIdBluetoothState5:
|
||||
case k_EPS4ReportIdBluetoothState6:
|
||||
case k_EPS4ReportIdBluetoothState7:
|
||||
case k_EPS4ReportIdBluetoothState8:
|
||||
case k_EPS4ReportIdBluetoothState9:
|
||||
/* Bluetooth state packets have two additional bytes at the beginning, the first notes if HID is present */
|
||||
if (data[1] & 0x80) {
|
||||
HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t*)&data[3]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG_JOYSTICK
|
||||
@@ -528,8 +803,12 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 =
|
||||
HIDAPI_DriverPS4_UpdateDevice,
|
||||
HIDAPI_DriverPS4_OpenJoystick,
|
||||
HIDAPI_DriverPS4_RumbleJoystick,
|
||||
HIDAPI_DriverPS4_RumbleJoystickTriggers,
|
||||
HIDAPI_DriverPS4_HasJoystickLED,
|
||||
HIDAPI_DriverPS4_SetJoystickLED,
|
||||
HIDAPI_DriverPS4_SetJoystickSensorsEnabled,
|
||||
HIDAPI_DriverPS4_CloseJoystick,
|
||||
HIDAPI_DriverPS4_FreeDevice
|
||||
HIDAPI_DriverPS4_FreeDevice,
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_PS4 */
|
||||
|
949
externals/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c
vendored
Executable file
949
externals/SDL/src/joystick/hidapi/SDL_hidapi_ps5.c
vendored
Executable file
@@ -0,0 +1,949 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_events.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_joystick.h"
|
||||
#include "SDL_gamecontroller.h"
|
||||
#include "../SDL_sysjoystick.h"
|
||||
#include "SDL_hidapijoystick_c.h"
|
||||
#include "SDL_hidapi_rumble.h"
|
||||
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_PS5
|
||||
|
||||
/* Define this if you want to log all packets from the controller */
|
||||
/*#define DEBUG_PS5_PROTOCOL*/
|
||||
|
||||
/* Define this if you want to log calibration data */
|
||||
/*#define DEBUG_PS5_CALIBRATION*/
|
||||
|
||||
#define GYRO_RES_PER_DEGREE 1024.0f
|
||||
#define ACCEL_RES_PER_G 8192.0f
|
||||
#define BLUETOOTH_DISCONNECT_TIMEOUT_MS 500
|
||||
|
||||
#define LOAD16(A, B) (Sint16)((Uint16)(A) | (((Uint16)(B)) << 8))
|
||||
|
||||
typedef enum
|
||||
{
|
||||
k_EPS5ReportIdState = 0x01,
|
||||
k_EPS5ReportIdUsbEffects = 0x02,
|
||||
k_EPS5ReportIdBluetoothEffects = 0x31,
|
||||
k_EPS5ReportIdBluetoothState = 0x31,
|
||||
} EPS5ReportId;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
k_EPS5FeatureReportIdCalibration = 0x05,
|
||||
k_EPS5FeatureReportIdSerialNumber = 0x09,
|
||||
} EPS5FeatureReportId;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Uint8 ucLeftJoystickX;
|
||||
Uint8 ucLeftJoystickY;
|
||||
Uint8 ucRightJoystickX;
|
||||
Uint8 ucRightJoystickY;
|
||||
Uint8 rgucButtonsHatAndCounter[3];
|
||||
Uint8 ucTriggerLeft;
|
||||
Uint8 ucTriggerRight;
|
||||
} PS5SimpleStatePacket_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Uint8 ucLeftJoystickX; /* 0 */
|
||||
Uint8 ucLeftJoystickY; /* 1 */
|
||||
Uint8 ucRightJoystickX; /* 2 */
|
||||
Uint8 ucRightJoystickY; /* 3 */
|
||||
Uint8 ucTriggerLeft; /* 4 */
|
||||
Uint8 ucTriggerRight; /* 5 */
|
||||
Uint8 ucCounter; /* 6 */
|
||||
Uint8 rgucButtonsAndHat[3]; /* 7 */
|
||||
Uint8 ucZero; /* 10 */
|
||||
Uint8 rgucPacketSequence[4]; /* 11 - 32 bit little endian */
|
||||
Uint8 rgucGyroX[2]; /* 15 */
|
||||
Uint8 rgucGyroY[2]; /* 17 */
|
||||
Uint8 rgucGyroZ[2]; /* 19 */
|
||||
Uint8 rgucAccelX[2]; /* 21 */
|
||||
Uint8 rgucAccelY[2]; /* 23 */
|
||||
Uint8 rgucAccelZ[2]; /* 25 */
|
||||
Uint8 rgucTimer1[4]; /* 27 - 32 bit little endian */
|
||||
Uint8 ucBatteryTemp; /* 31 */
|
||||
Uint8 ucTouchpadCounter1; /* 32 - high bit clear + counter */
|
||||
Uint8 rgucTouchpadData1[3]; /* 33 - X/Y, 12 bits per axis */
|
||||
Uint8 ucTouchpadCounter2; /* 36 - high bit clear + counter */
|
||||
Uint8 rgucTouchpadData2[3]; /* 37 - X/Y, 12 bits per axis */
|
||||
Uint8 rgucUnknown1[8]; /* 40 */
|
||||
Uint8 rgucTimer2[4]; /* 48 - 32 bit little endian */
|
||||
Uint8 ucBatteryLevel; /* 52 */
|
||||
Uint8 ucConnectState; /* 53 - 0x08 = USB, 0x01 = headphone */
|
||||
|
||||
/* There's more unknown data at the end, and a 32-bit CRC on Bluetooth */
|
||||
} PS5StatePacket_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Uint8 ucEnableBits1; /* 0 */
|
||||
Uint8 ucEnableBits2; /* 1 */
|
||||
Uint8 ucRumbleRight; /* 2 */
|
||||
Uint8 ucRumbleLeft; /* 3 */
|
||||
Uint8 ucHeadphoneVolume; /* 4 */
|
||||
Uint8 ucSpeakerVolume; /* 5 */
|
||||
Uint8 ucMicrophoneVolume; /* 6 */
|
||||
Uint8 ucAudioEnableBits; /* 7 */
|
||||
Uint8 ucMicLightMode; /* 8 */
|
||||
Uint8 ucAudioMuteBits; /* 9 */
|
||||
Uint8 rgucRightTriggerEffect[11]; /* 10 */
|
||||
Uint8 rgucLeftTriggerEffect[11]; /* 21 */
|
||||
Uint8 rgucUnknown1[6]; /* 32 */
|
||||
Uint8 ucLedFlags; /* 38 */
|
||||
Uint8 rgucUnknown2[2]; /* 39 */
|
||||
Uint8 ucLedAnim; /* 41 */
|
||||
Uint8 ucLedBrightness; /* 42 */
|
||||
Uint8 ucPadLights; /* 43 */
|
||||
Uint8 ucLedRed; /* 44 */
|
||||
Uint8 ucLedGreen; /* 45 */
|
||||
Uint8 ucLedBlue; /* 46 */
|
||||
} DS5EffectsState_t;
|
||||
|
||||
typedef enum {
|
||||
k_EDS5EffectNone,
|
||||
k_EDS5EffectRumbleStart,
|
||||
k_EDS5EffectRumble,
|
||||
k_EDS5EffectLEDReset,
|
||||
k_EDS5EffectLED,
|
||||
k_EDS5EffectPadLights,
|
||||
k_EDS5EffectMicLight,
|
||||
} EDS5Effect;
|
||||
|
||||
typedef enum {
|
||||
k_EDS5LEDResetStateNone,
|
||||
k_EDS5LEDResetStatePending,
|
||||
k_EDS5LEDResetStateComplete,
|
||||
} EDS5LEDResetState;
|
||||
|
||||
typedef struct {
|
||||
Sint16 bias;
|
||||
float sensitivity;
|
||||
} IMUCalibrationData;
|
||||
|
||||
typedef struct {
|
||||
SDL_bool is_bluetooth;
|
||||
SDL_bool report_sensors;
|
||||
SDL_bool hardware_calibration;
|
||||
IMUCalibrationData calibration[6];
|
||||
Uint32 last_packet;
|
||||
int player_index;
|
||||
Uint8 rumble_left;
|
||||
Uint8 rumble_right;
|
||||
SDL_bool color_set;
|
||||
Uint8 led_red;
|
||||
Uint8 led_green;
|
||||
Uint8 led_blue;
|
||||
EDS5LEDResetState led_reset_state;
|
||||
union
|
||||
{
|
||||
PS5SimpleStatePacket_t simple;
|
||||
PS5StatePacket_t state;
|
||||
} last_state;
|
||||
} SDL_DriverPS5_Context;
|
||||
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverPS5_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
|
||||
{
|
||||
return (type == SDL_CONTROLLER_TYPE_PS5) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
static const char *
|
||||
HIDAPI_DriverPS5_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
|
||||
{
|
||||
if (vendor_id == USB_VENDOR_SONY) {
|
||||
return "PS5 Controller";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ReadFeatureReport(hid_device *dev, Uint8 report_id, Uint8 *report, size_t length)
|
||||
{
|
||||
SDL_memset(report, 0, length);
|
||||
report[0] = report_id;
|
||||
return hid_get_feature_report(dev, report, length);
|
||||
}
|
||||
|
||||
static void
|
||||
SetLedsForPlayerIndex(DS5EffectsState_t *effects, int player_index)
|
||||
{
|
||||
/* This list is the same as what hid-sony.c uses in the Linux kernel.
|
||||
The first 4 values correspond to what the PS4 assigns.
|
||||
*/
|
||||
static const Uint8 colors[7][3] = {
|
||||
{ 0x00, 0x00, 0x40 }, /* Blue */
|
||||
{ 0x40, 0x00, 0x00 }, /* Red */
|
||||
{ 0x00, 0x40, 0x00 }, /* Green */
|
||||
{ 0x20, 0x00, 0x20 }, /* Pink */
|
||||
{ 0x02, 0x01, 0x00 }, /* Orange */
|
||||
{ 0x00, 0x01, 0x01 }, /* Teal */
|
||||
{ 0x01, 0x01, 0x01 } /* White */
|
||||
};
|
||||
|
||||
if (player_index >= 0) {
|
||||
player_index %= SDL_arraysize(colors);
|
||||
} else {
|
||||
player_index = 0;
|
||||
}
|
||||
|
||||
effects->ucLedRed = colors[player_index][0];
|
||||
effects->ucLedGreen = colors[player_index][1];
|
||||
effects->ucLedBlue = colors[player_index][2];
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
return HIDAPI_JoystickConnected(device, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverPS5_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverPS5_LoadCalibrationData(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
|
||||
int i, size;
|
||||
Uint8 data[USB_PACKET_LENGTH];
|
||||
|
||||
size = ReadFeatureReport(device->dev, k_EPS5FeatureReportIdCalibration, data, sizeof(data));
|
||||
if (size < 35) {
|
||||
#ifdef DEBUG_PS5_CALIBRATION
|
||||
SDL_Log("Short read of calibration data: %d, ignoring calibration\n", size);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
Sint16 sGyroPitchBias, sGyroYawBias, sGyroRollBias;
|
||||
Sint16 sGyroPitchPlus, sGyroPitchMinus;
|
||||
Sint16 sGyroYawPlus, sGyroYawMinus;
|
||||
Sint16 sGyroRollPlus, sGyroRollMinus;
|
||||
Sint16 sGyroSpeedPlus, sGyroSpeedMinus;
|
||||
|
||||
Sint16 sAccXPlus, sAccXMinus;
|
||||
Sint16 sAccYPlus, sAccYMinus;
|
||||
Sint16 sAccZPlus, sAccZMinus;
|
||||
|
||||
float flNumerator;
|
||||
Sint16 sRange2g;
|
||||
|
||||
#ifdef DEBUG_PS5_CALIBRATION
|
||||
HIDAPI_DumpPacket("PS5 calibration packet: size = %d", data, size);
|
||||
#endif
|
||||
|
||||
sGyroPitchBias = LOAD16(data[1], data[2]);
|
||||
sGyroYawBias = LOAD16(data[3], data[4]);
|
||||
sGyroRollBias = LOAD16(data[5], data[6]);
|
||||
|
||||
sGyroPitchPlus = LOAD16(data[7], data[8]);
|
||||
sGyroPitchMinus = LOAD16(data[9], data[10]);
|
||||
sGyroYawPlus = LOAD16(data[11], data[12]);
|
||||
sGyroYawMinus = LOAD16(data[13], data[14]);
|
||||
sGyroRollPlus = LOAD16(data[15], data[16]);
|
||||
sGyroRollMinus = LOAD16(data[17], data[18]);
|
||||
|
||||
sGyroSpeedPlus = LOAD16(data[19], data[20]);
|
||||
sGyroSpeedMinus = LOAD16(data[21], data[22]);
|
||||
|
||||
sAccXPlus = LOAD16(data[23], data[24]);
|
||||
sAccXMinus = LOAD16(data[25], data[26]);
|
||||
sAccYPlus = LOAD16(data[27], data[28]);
|
||||
sAccYMinus = LOAD16(data[29], data[30]);
|
||||
sAccZPlus = LOAD16(data[31], data[32]);
|
||||
sAccZMinus = LOAD16(data[33], data[34]);
|
||||
|
||||
flNumerator = (sGyroSpeedPlus + sGyroSpeedMinus) * GYRO_RES_PER_DEGREE;
|
||||
ctx->calibration[0].bias = sGyroPitchBias;
|
||||
ctx->calibration[0].sensitivity = flNumerator / (sGyroPitchPlus - sGyroPitchMinus);
|
||||
|
||||
ctx->calibration[1].bias = sGyroYawBias;
|
||||
ctx->calibration[1].sensitivity = flNumerator / (sGyroYawPlus - sGyroYawMinus);
|
||||
|
||||
ctx->calibration[2].bias = sGyroRollBias;
|
||||
ctx->calibration[2].sensitivity = flNumerator / (sGyroRollPlus - sGyroRollMinus);
|
||||
|
||||
sRange2g = sAccXPlus - sAccXMinus;
|
||||
ctx->calibration[3].bias = sAccXPlus - sRange2g / 2;
|
||||
ctx->calibration[3].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
|
||||
|
||||
sRange2g = sAccYPlus - sAccYMinus;
|
||||
ctx->calibration[4].bias = sAccYPlus - sRange2g / 2;
|
||||
ctx->calibration[4].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
|
||||
|
||||
sRange2g = sAccZPlus - sAccZMinus;
|
||||
ctx->calibration[5].bias = sAccZPlus - sRange2g / 2;
|
||||
ctx->calibration[5].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
|
||||
|
||||
ctx->hardware_calibration = SDL_TRUE;
|
||||
for (i = 0; i < 6; ++i) {
|
||||
float divisor = (i < 3 ? 64.0f : 1.0f);
|
||||
#ifdef DEBUG_PS5_CALIBRATION
|
||||
SDL_Log("calibration[%d] bias = %d, sensitivity = %f\n", i, ctx->calibration[i].bias, ctx->calibration[i].sensitivity);
|
||||
#endif
|
||||
/* Some controllers have a bad calibration */
|
||||
if ((SDL_abs(ctx->calibration[i].bias) > 1024) || (SDL_fabs(1.0f - ctx->calibration[i].sensitivity / divisor) > 0.5f)) {
|
||||
#ifdef DEBUG_PS5_CALIBRATION
|
||||
SDL_Log("invalid calibration, ignoring\n");
|
||||
#endif
|
||||
ctx->hardware_calibration = SDL_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static float
|
||||
HIDAPI_DriverPS5_ApplyCalibrationData(SDL_DriverPS5_Context *ctx, int index, Sint16 value)
|
||||
{
|
||||
float result;
|
||||
|
||||
if (ctx->hardware_calibration) {
|
||||
IMUCalibrationData *calibration = &ctx->calibration[index];
|
||||
|
||||
result = (value - calibration->bias) * calibration->sensitivity;
|
||||
} else if (index < 3) {
|
||||
result = value * 64.f;
|
||||
} else {
|
||||
result = value;
|
||||
}
|
||||
|
||||
/* Convert the raw data to the units expected by SDL */
|
||||
if (index < 3) {
|
||||
result = (result / GYRO_RES_PER_DEGREE) * (float)M_PI / 180.0f;
|
||||
} else {
|
||||
result = (result / ACCEL_RES_PER_G) * SDL_STANDARD_GRAVITY;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverPS5_UpdateEffects(SDL_HIDAPI_Device *device, EDS5Effect effect)
|
||||
{
|
||||
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
|
||||
DS5EffectsState_t *effects;
|
||||
Uint8 data[78];
|
||||
int report_size, offset;
|
||||
Uint8 *pending_data;
|
||||
int *pending_size;
|
||||
int maximum_size;
|
||||
|
||||
|
||||
SDL_zero(data);
|
||||
|
||||
if (ctx->is_bluetooth) {
|
||||
data[0] = k_EPS5ReportIdBluetoothEffects;
|
||||
data[1] = 0x02; /* Magic value */
|
||||
|
||||
report_size = 78;
|
||||
offset = 2;
|
||||
} else {
|
||||
data[0] = k_EPS5ReportIdUsbEffects;
|
||||
|
||||
report_size = 48;
|
||||
offset = 1;
|
||||
}
|
||||
effects = (DS5EffectsState_t *)&data[offset];
|
||||
|
||||
/* Make sure the Bluetooth connection sequence has completed before sending LED color change */
|
||||
if (effect == k_EDS5EffectLED && ctx->is_bluetooth) {
|
||||
if (ctx->led_reset_state != k_EDS5LEDResetStateComplete) {
|
||||
ctx->led_reset_state = k_EDS5LEDResetStatePending;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->rumble_left || ctx->rumble_right) {
|
||||
effects->ucEnableBits1 |= 0x01; /* Enable rumble emulation */
|
||||
effects->ucEnableBits1 |= 0x02; /* Disable audio haptics */
|
||||
|
||||
/* Shift to reduce effective rumble strength to match Xbox controllers */
|
||||
effects->ucRumbleLeft = ctx->rumble_left >> 1;
|
||||
effects->ucRumbleRight = ctx->rumble_right >> 1;
|
||||
} else {
|
||||
/* Leaving emulated rumble bits off will restore audio haptics */
|
||||
}
|
||||
|
||||
switch (effect) {
|
||||
case k_EDS5EffectRumbleStart:
|
||||
effects->ucEnableBits1 |= 0x02; /* Disable audio haptics */
|
||||
break;
|
||||
case k_EDS5EffectRumble:
|
||||
/* Already handled above */
|
||||
break;
|
||||
case k_EDS5EffectLEDReset:
|
||||
effects->ucEnableBits2 |= 0x08; /* Reset LED state */
|
||||
break;
|
||||
case k_EDS5EffectLED:
|
||||
effects->ucEnableBits2 |= 0x04; /* Enable LED color */
|
||||
|
||||
/* Populate the LED state with the appropriate color from our lookup table */
|
||||
if (ctx->color_set) {
|
||||
effects->ucLedRed = ctx->led_red;
|
||||
effects->ucLedGreen = ctx->led_green;
|
||||
effects->ucLedBlue = ctx->led_blue;
|
||||
} else {
|
||||
SetLedsForPlayerIndex(effects, ctx->player_index);
|
||||
}
|
||||
break;
|
||||
case k_EDS5EffectPadLights:
|
||||
effects->ucEnableBits2 |= 0x10; /* Enable touchpad lights */
|
||||
|
||||
effects->ucPadLights = 0x00; /* Bitmask, 0x1F enables all lights, 0x20 changes instantly instead of fade */
|
||||
break;
|
||||
case k_EDS5EffectMicLight:
|
||||
effects->ucEnableBits2 |= 0x01; /* Enable microphone light */
|
||||
|
||||
effects->ucMicLightMode = 0; /* Bitmask, 0x00 = off, 0x01 = solid, 0x02 = pulse */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->is_bluetooth) {
|
||||
/* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */
|
||||
Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */
|
||||
Uint32 unCRC;
|
||||
unCRC = SDL_crc32(0, &ubHdr, 1);
|
||||
unCRC = SDL_crc32(unCRC, data, (size_t)(report_size - sizeof(unCRC)));
|
||||
SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
|
||||
}
|
||||
|
||||
if (SDL_HIDAPI_LockRumble() < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* See if we can update an existing pending request */
|
||||
if (SDL_HIDAPI_GetPendingRumbleLocked(device, &pending_data, &pending_size, &maximum_size)) {
|
||||
DS5EffectsState_t *pending_effects = (DS5EffectsState_t *)&pending_data[offset];
|
||||
if (report_size == *pending_size &&
|
||||
effects->ucEnableBits1 == pending_effects->ucEnableBits1 &&
|
||||
effects->ucEnableBits2 == pending_effects->ucEnableBits2) {
|
||||
/* We're simply updating the data for this request */
|
||||
SDL_memcpy(pending_data, data, report_size);
|
||||
SDL_HIDAPI_UnlockRumble();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return SDL_HIDAPI_SendRumbleAndUnlock(device, data, report_size);
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverPS5_SetBluetooth(SDL_HIDAPI_Device *device, SDL_bool is_bluetooth)
|
||||
{
|
||||
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
|
||||
|
||||
if (ctx->is_bluetooth != is_bluetooth) {
|
||||
ctx->is_bluetooth = is_bluetooth;
|
||||
HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLED);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverPS5_CheckPendingLEDReset(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
|
||||
const PS5StatePacket_t *packet = &ctx->last_state.state;
|
||||
|
||||
/* Check the timer to make sure the Bluetooth connection LED animation is complete */
|
||||
const Uint32 connection_complete = 10000000;
|
||||
Uint32 timer = ((Uint32)packet->rgucTimer1[0] << 0) |
|
||||
((Uint32)packet->rgucTimer1[1] << 8) |
|
||||
((Uint32)packet->rgucTimer1[2] << 16) |
|
||||
((Uint32)packet->rgucTimer1[3] << 24);
|
||||
if (timer >= connection_complete) {
|
||||
HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLEDReset);
|
||||
|
||||
ctx->led_reset_state = k_EDS5LEDResetStateComplete;
|
||||
|
||||
HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLED);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverPS5_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
|
||||
{
|
||||
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
|
||||
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->player_index = player_index;
|
||||
|
||||
/* This will set the new LED state based on the new player index */
|
||||
HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLED);
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
SDL_DriverPS5_Context *ctx;
|
||||
Uint8 data[USB_PACKET_LENGTH];
|
||||
|
||||
ctx = (SDL_DriverPS5_Context *)SDL_calloc(1, sizeof(*ctx));
|
||||
if (!ctx) {
|
||||
SDL_OutOfMemory();
|
||||
return SDL_FALSE;
|
||||
}
|
||||
ctx->last_packet = SDL_GetTicks();
|
||||
|
||||
device->dev = hid_open_path(device->path, 0);
|
||||
if (!device->dev) {
|
||||
SDL_free(ctx);
|
||||
SDL_SetError("Couldn't open %s", device->path);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
device->context = ctx;
|
||||
|
||||
/* Read the serial number (Bluetooth address in reverse byte order)
|
||||
This will also enable enhanced reports over Bluetooth
|
||||
*/
|
||||
if (ReadFeatureReport(device->dev, k_EPS5FeatureReportIdSerialNumber, data, sizeof(data)) >= 7) {
|
||||
char serial[18];
|
||||
|
||||
SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
|
||||
data[6], data[5], data[4], data[3], data[2], data[1]);
|
||||
joystick->serial = SDL_strdup(serial);
|
||||
}
|
||||
|
||||
/* Initialize player index (needed for setting LEDs) */
|
||||
ctx->player_index = SDL_JoystickGetPlayerIndex(joystick);
|
||||
|
||||
/* Initialize LED and effect state */
|
||||
HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLED);
|
||||
|
||||
/* Initialize the joystick capabilities */
|
||||
joystick->nbuttons = 17;
|
||||
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
|
||||
|
||||
SDL_PrivateJoystickAddTouchpad(joystick, 2);
|
||||
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO);
|
||||
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL);
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverPS5_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
|
||||
{
|
||||
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
|
||||
|
||||
if (!ctx->rumble_left && !ctx->rumble_right) {
|
||||
HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectRumbleStart);
|
||||
}
|
||||
|
||||
ctx->rumble_left = (low_frequency_rumble >> 8);
|
||||
ctx->rumble_right = (high_frequency_rumble >> 8);
|
||||
|
||||
return HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectRumble);
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverPS5_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverPS5_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverPS5_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
|
||||
|
||||
ctx->color_set = SDL_TRUE;
|
||||
ctx->led_red = red;
|
||||
ctx->led_green = green;
|
||||
ctx->led_blue = blue;
|
||||
|
||||
return HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLED);
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverPS5_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
|
||||
{
|
||||
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
|
||||
|
||||
if (enabled) {
|
||||
HIDAPI_DriverPS5_LoadCalibrationData(device);
|
||||
}
|
||||
ctx->report_sensors = enabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS5_Context *ctx, PS5SimpleStatePacket_t *packet)
|
||||
{
|
||||
Sint16 axis;
|
||||
|
||||
if (ctx->last_state.simple.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) {
|
||||
{
|
||||
Uint8 data = (packet->rgucButtonsHatAndCounter[0] >> 4);
|
||||
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
{
|
||||
Uint8 data = (packet->rgucButtonsHatAndCounter[0] & 0x0F);
|
||||
SDL_bool dpad_up = SDL_FALSE;
|
||||
SDL_bool dpad_down = SDL_FALSE;
|
||||
SDL_bool dpad_left = SDL_FALSE;
|
||||
SDL_bool dpad_right = SDL_FALSE;
|
||||
|
||||
switch (data) {
|
||||
case 0:
|
||||
dpad_up = SDL_TRUE;
|
||||
break;
|
||||
case 1:
|
||||
dpad_up = SDL_TRUE;
|
||||
dpad_right = SDL_TRUE;
|
||||
break;
|
||||
case 2:
|
||||
dpad_right = SDL_TRUE;
|
||||
break;
|
||||
case 3:
|
||||
dpad_right = SDL_TRUE;
|
||||
dpad_down = SDL_TRUE;
|
||||
break;
|
||||
case 4:
|
||||
dpad_down = SDL_TRUE;
|
||||
break;
|
||||
case 5:
|
||||
dpad_left = SDL_TRUE;
|
||||
dpad_down = SDL_TRUE;
|
||||
break;
|
||||
case 6:
|
||||
dpad_left = SDL_TRUE;
|
||||
break;
|
||||
case 7:
|
||||
dpad_up = SDL_TRUE;
|
||||
dpad_left = SDL_TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->last_state.simple.rgucButtonsHatAndCounter[1] != packet->rgucButtonsHatAndCounter[1]) {
|
||||
Uint8 data = packet->rgucButtonsHatAndCounter[1];
|
||||
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
|
||||
if (ctx->last_state.simple.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) {
|
||||
Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03);
|
||||
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, 15, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
|
||||
axis = ((int)packet->ucTriggerLeft * 257) - 32768;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
|
||||
axis = ((int)packet->ucTriggerRight * 257) - 32768;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
|
||||
axis = ((int)packet->ucLeftJoystickX * 257) - 32768;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
|
||||
axis = ((int)packet->ucLeftJoystickY * 257) - 32768;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
|
||||
axis = ((int)packet->ucRightJoystickX * 257) - 32768;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
|
||||
axis = ((int)packet->ucRightJoystickY * 257) - 32768;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
|
||||
|
||||
SDL_memcpy(&ctx->last_state.simple, packet, sizeof(ctx->last_state.simple));
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacket_t *packet)
|
||||
{
|
||||
static const float TOUCHPAD_SCALEX = 1.0f / 1920;
|
||||
static const float TOUCHPAD_SCALEY = 1.0f / 1070;
|
||||
Sint16 axis;
|
||||
Uint8 touchpad_state;
|
||||
int touchpad_x, touchpad_y;
|
||||
|
||||
if (ctx->last_state.state.rgucButtonsAndHat[0] != packet->rgucButtonsAndHat[0]) {
|
||||
{
|
||||
Uint8 data = (packet->rgucButtonsAndHat[0] >> 4);
|
||||
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
{
|
||||
Uint8 data = (packet->rgucButtonsAndHat[0] & 0x0F);
|
||||
SDL_bool dpad_up = SDL_FALSE;
|
||||
SDL_bool dpad_down = SDL_FALSE;
|
||||
SDL_bool dpad_left = SDL_FALSE;
|
||||
SDL_bool dpad_right = SDL_FALSE;
|
||||
|
||||
switch (data) {
|
||||
case 0:
|
||||
dpad_up = SDL_TRUE;
|
||||
break;
|
||||
case 1:
|
||||
dpad_up = SDL_TRUE;
|
||||
dpad_right = SDL_TRUE;
|
||||
break;
|
||||
case 2:
|
||||
dpad_right = SDL_TRUE;
|
||||
break;
|
||||
case 3:
|
||||
dpad_right = SDL_TRUE;
|
||||
dpad_down = SDL_TRUE;
|
||||
break;
|
||||
case 4:
|
||||
dpad_down = SDL_TRUE;
|
||||
break;
|
||||
case 5:
|
||||
dpad_left = SDL_TRUE;
|
||||
dpad_down = SDL_TRUE;
|
||||
break;
|
||||
case 6:
|
||||
dpad_left = SDL_TRUE;
|
||||
break;
|
||||
case 7:
|
||||
dpad_up = SDL_TRUE;
|
||||
dpad_left = SDL_TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->last_state.state.rgucButtonsAndHat[1] != packet->rgucButtonsAndHat[1]) {
|
||||
Uint8 data = packet->rgucButtonsAndHat[1];
|
||||
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
|
||||
if (ctx->last_state.state.rgucButtonsAndHat[2] != packet->rgucButtonsAndHat[2]) {
|
||||
Uint8 data = packet->rgucButtonsAndHat[2];
|
||||
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, 15, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, 16, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
|
||||
axis = ((int)packet->ucTriggerLeft * 257) - 32768;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
|
||||
axis = ((int)packet->ucTriggerRight * 257) - 32768;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
|
||||
axis = ((int)packet->ucLeftJoystickX * 257) - 32768;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
|
||||
axis = ((int)packet->ucLeftJoystickY * 257) - 32768;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
|
||||
axis = ((int)packet->ucRightJoystickX * 257) - 32768;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
|
||||
axis = ((int)packet->ucRightJoystickY * 257) - 32768;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
|
||||
|
||||
if (packet->ucBatteryLevel & 0x10) {
|
||||
/* 0x20 set means fully charged */
|
||||
SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_WIRED);
|
||||
} else {
|
||||
/* Battery level ranges from 0 to 10 */
|
||||
int level = (packet->ucBatteryLevel & 0xF);
|
||||
if (level == 0) {
|
||||
SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_EMPTY);
|
||||
} else if (level <= 2) {
|
||||
SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_LOW);
|
||||
} else if (level <= 7) {
|
||||
SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_MEDIUM);
|
||||
} else {
|
||||
SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL);
|
||||
}
|
||||
}
|
||||
|
||||
touchpad_state = ((packet->ucTouchpadCounter1 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
|
||||
touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8);
|
||||
touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4);
|
||||
SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
|
||||
|
||||
touchpad_state = ((packet->ucTouchpadCounter2 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
|
||||
touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8);
|
||||
touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4);
|
||||
SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
|
||||
|
||||
if (ctx->report_sensors) {
|
||||
float data[3];
|
||||
|
||||
data[0] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 0, LOAD16(packet->rgucGyroX[0], packet->rgucGyroX[1]));
|
||||
data[1] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 1, LOAD16(packet->rgucGyroY[0], packet->rgucGyroY[1]));
|
||||
data[2] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 2, LOAD16(packet->rgucGyroZ[0], packet->rgucGyroZ[1]));
|
||||
SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_GYRO, data, 3);
|
||||
|
||||
data[0] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 3, LOAD16(packet->rgucAccelX[0], packet->rgucAccelX[1]));
|
||||
data[1] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 4, LOAD16(packet->rgucAccelY[0], packet->rgucAccelY[1]));
|
||||
data[2] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 5, LOAD16(packet->rgucAccelZ[0], packet->rgucAccelZ[1]));
|
||||
SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_ACCEL, data, 3);
|
||||
}
|
||||
|
||||
SDL_memcpy(&ctx->last_state.state, packet, sizeof(ctx->last_state.state));
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
|
||||
SDL_Joystick *joystick = NULL;
|
||||
Uint8 data[USB_PACKET_LENGTH*2];
|
||||
int size;
|
||||
int packet_count = 0;
|
||||
|
||||
if (device->num_joysticks > 0) {
|
||||
joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
|
||||
}
|
||||
if (!joystick) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
|
||||
#ifdef DEBUG_PS5_PROTOCOL
|
||||
HIDAPI_DumpPacket("PS5 packet: size = %d", data, size);
|
||||
#endif
|
||||
++packet_count;
|
||||
ctx->last_packet = SDL_GetTicks();
|
||||
|
||||
switch (data[0]) {
|
||||
case k_EPS5ReportIdState:
|
||||
if (size == 10) {
|
||||
HIDAPI_DriverPS5_SetBluetooth(device, SDL_TRUE); /* Simple state packet over Bluetooth */
|
||||
HIDAPI_DriverPS5_HandleSimpleStatePacket(joystick, device->dev, ctx, (PS5SimpleStatePacket_t *)&data[1]);
|
||||
} else {
|
||||
HIDAPI_DriverPS5_SetBluetooth(device, SDL_FALSE);
|
||||
HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[1]);
|
||||
}
|
||||
break;
|
||||
case k_EPS5ReportIdBluetoothState:
|
||||
HIDAPI_DriverPS5_SetBluetooth(device, SDL_TRUE);
|
||||
HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[2]);
|
||||
if (ctx->led_reset_state == k_EDS5LEDResetStatePending) {
|
||||
HIDAPI_DriverPS5_CheckPendingLEDReset(device);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
#ifdef DEBUG_JOYSTICK
|
||||
SDL_Log("Unknown PS5 packet: 0x%.2x\n", data[0]);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->is_bluetooth && packet_count == 0) {
|
||||
/* Check to see if it looks like the device disconnected */
|
||||
if (SDL_TICKS_PASSED(SDL_GetTicks(), ctx->last_packet + BLUETOOTH_DISCONNECT_TIMEOUT_MS)) {
|
||||
/* Send an empty output report to tickle the Bluetooth stack */
|
||||
HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectNone);
|
||||
}
|
||||
}
|
||||
|
||||
if (size < 0) {
|
||||
/* Read error, device is disconnected */
|
||||
HIDAPI_JoystickDisconnected(device, joystick->instance_id);
|
||||
}
|
||||
return (size >= 0);
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverPS5_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
hid_close(device->dev);
|
||||
device->dev = NULL;
|
||||
|
||||
SDL_free(device->context);
|
||||
device->context = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverPS5_FreeDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
}
|
||||
|
||||
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS5 =
|
||||
{
|
||||
SDL_HINT_JOYSTICK_HIDAPI_PS5,
|
||||
SDL_TRUE,
|
||||
HIDAPI_DriverPS5_IsSupportedDevice,
|
||||
HIDAPI_DriverPS5_GetDeviceName,
|
||||
HIDAPI_DriverPS5_InitDevice,
|
||||
HIDAPI_DriverPS5_GetDevicePlayerIndex,
|
||||
HIDAPI_DriverPS5_SetDevicePlayerIndex,
|
||||
HIDAPI_DriverPS5_UpdateDevice,
|
||||
HIDAPI_DriverPS5_OpenJoystick,
|
||||
HIDAPI_DriverPS5_RumbleJoystick,
|
||||
HIDAPI_DriverPS5_RumbleJoystickTriggers,
|
||||
HIDAPI_DriverPS5_HasJoystickLED,
|
||||
HIDAPI_DriverPS5_SetJoystickLED,
|
||||
HIDAPI_DriverPS5_SetJoystickSensorsEnabled,
|
||||
HIDAPI_DriverPS5_CloseJoystick,
|
||||
HIDAPI_DriverPS5_FreeDevice,
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_PS5 */
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
@@ -24,7 +24,6 @@
|
||||
|
||||
/* Handle rumble on a separate thread so it doesn't block the application */
|
||||
|
||||
#include "SDL_assert.h"
|
||||
#include "SDL_thread.h"
|
||||
#include "SDL_hidapijoystick_c.h"
|
||||
#include "SDL_hidapi_rumble.h"
|
||||
@@ -90,6 +89,8 @@ static int SDL_HIDAPI_RumbleThread(void *data)
|
||||
static void
|
||||
SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
|
||||
{
|
||||
SDL_HIDAPI_RumbleRequest *request;
|
||||
|
||||
SDL_AtomicSet(&ctx->running, SDL_FALSE);
|
||||
|
||||
if (ctx->thread) {
|
||||
@@ -100,9 +101,18 @@ SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
|
||||
ctx->thread = NULL;
|
||||
}
|
||||
|
||||
/* This should always be called with an empty queue */
|
||||
SDL_assert(!ctx->requests_head);
|
||||
SDL_assert(!ctx->requests_tail);
|
||||
SDL_LockMutex(ctx->lock);
|
||||
while (ctx->requests_tail) {
|
||||
request = ctx->requests_tail;
|
||||
if (request == ctx->requests_head) {
|
||||
ctx->requests_head = NULL;
|
||||
}
|
||||
ctx->requests_tail = request->prev;
|
||||
|
||||
(void)SDL_AtomicDecRef(&request->device->rumble_pending);
|
||||
SDL_free(request);
|
||||
}
|
||||
SDL_UnlockMutex(ctx->lock);
|
||||
|
||||
if (ctx->request_sem) {
|
||||
SDL_DestroySemaphore(ctx->request_sem);
|
||||
@@ -157,16 +167,20 @@ int SDL_HIDAPI_LockRumble(void)
|
||||
SDL_bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size)
|
||||
{
|
||||
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
|
||||
SDL_HIDAPI_RumbleRequest *request;
|
||||
SDL_HIDAPI_RumbleRequest *request, *found;
|
||||
|
||||
found = NULL;
|
||||
for (request = ctx->requests_tail; request; request = request->prev) {
|
||||
if (request->device == device) {
|
||||
*data = request->data;
|
||||
*size = &request->size;
|
||||
*maximum_size = sizeof(request->data);
|
||||
return SDL_TRUE;
|
||||
found = request;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
*data = found->data;
|
||||
*size = &found->size;
|
||||
*maximum_size = sizeof(found->data);
|
||||
return SDL_TRUE;
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,6 @@
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_log.h"
|
||||
#include "SDL_events.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_joystick.h"
|
||||
@@ -138,8 +137,8 @@ typedef struct SteamControllerStateInternal_t
|
||||
#define STEAM_BUTTON_RIGHTPAD_CLICKED_MASK 0x00040000
|
||||
#define STEAM_LEFTPAD_FINGERDOWN_MASK 0x00080000
|
||||
#define STEAM_RIGHTPAD_FINGERDOWN_MASK 0x00100000
|
||||
#define STEAM_JOYSTICK_BUTTON_MASK 0x00400000
|
||||
#define STEAM_LEFTPAD_AND_JOYSTICK_MASK 0x00800000
|
||||
#define STEAM_JOYSTICK_BUTTON_MASK 0x00400000
|
||||
#define STEAM_LEFTPAD_AND_JOYSTICK_MASK 0x00800000
|
||||
|
||||
|
||||
// Look for report version 0x0001, type WIRELESS (3), length >= 1 byte
|
||||
@@ -1011,7 +1010,7 @@ HIDAPI_DriverSteam_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystic
|
||||
InitializeSteamControllerPacketAssembler(&ctx->m_assembler);
|
||||
|
||||
/* Initialize the joystick capabilities */
|
||||
joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
|
||||
joystick->nbuttons = 17;
|
||||
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
|
||||
|
||||
return SDL_TRUE;
|
||||
@@ -1035,6 +1034,33 @@ HIDAPI_DriverSteam_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverSteam_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverSteam_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
/* You should use the full Steam Input API for LED support */
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverSteam_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
/* You should use the full Steam Input API for LED support */
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverSteam_SetSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
|
||||
{
|
||||
/* You should use the full Steam Input API for sensor support */
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
@@ -1098,6 +1124,10 @@ HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK,
|
||||
(ctx->m_state.ulButtons & STEAM_JOYSTICK_BUTTON_MASK) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MISC1 + 0,
|
||||
(ctx->m_state.ulButtons & STEAM_BUTTON_BACK_LEFT_MASK) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MISC1 + 1,
|
||||
(ctx->m_state.ulButtons & STEAM_BUTTON_BACK_RIGHT_MASK) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
{
|
||||
/* Minimum distance from center of pad to register a direction */
|
||||
@@ -1165,8 +1195,12 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam =
|
||||
HIDAPI_DriverSteam_UpdateDevice,
|
||||
HIDAPI_DriverSteam_OpenJoystick,
|
||||
HIDAPI_DriverSteam_RumbleJoystick,
|
||||
HIDAPI_DriverSteam_RumbleJoystickTriggers,
|
||||
HIDAPI_DriverSteam_HasJoystickLED,
|
||||
HIDAPI_DriverSteam_SetJoystickLED,
|
||||
HIDAPI_DriverSteam_SetSensorsEnabled,
|
||||
HIDAPI_DriverSteam_CloseJoystick,
|
||||
HIDAPI_DriverSteam_FreeDevice
|
||||
HIDAPI_DriverSteam_FreeDevice,
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_STEAM */
|
||||
|
@@ -26,7 +26,6 @@
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_log.h"
|
||||
#include "SDL_events.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_joystick.h"
|
||||
@@ -39,6 +38,20 @@
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
|
||||
|
||||
/* Define this if you want to log all packets from the controller */
|
||||
/*#define DEBUG_SWITCH_PROTOCOL*/
|
||||
|
||||
/* 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,
|
||||
@@ -203,7 +216,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;
|
||||
@@ -226,7 +242,24 @@ typedef struct {
|
||||
} SDL_DriverSwitch_Context;
|
||||
|
||||
|
||||
static SDL_bool IsGameCubeFormFactor(int vendor_id, int product_id)
|
||||
static SDL_bool
|
||||
HasHomeLED(int vendor_id, int product_id)
|
||||
{
|
||||
/* The Power A Nintendo Switch Pro controllers don't have a Home LED */
|
||||
if (vendor_id == 0 && product_id == 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* HORI Wireless Switch Pad */
|
||||
if (vendor_id == 0x0f0d && product_id == 0x00f6) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
IsGameCubeFormFactor(int vendor_id, int product_id)
|
||||
{
|
||||
static Uint32 gamecube_formfactor[] = {
|
||||
MAKE_VIDPID(0x0e6f, 0x0185), /* PDP Wired Fight Pad Pro for Nintendo Switch */
|
||||
@@ -255,7 +288,7 @@ HIDAPI_DriverSwitch_IsSupportedDevice(const char *name, SDL_GameControllerType t
|
||||
if (SDL_strcmp( name, "HORI Wireless Switch Pad" ) == 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO);
|
||||
return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
static const char *
|
||||
@@ -455,14 +488,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));
|
||||
}
|
||||
@@ -524,7 +550,7 @@ static SDL_bool SetSlotLED(SDL_DriverSwitch_Context *ctx, Uint8 slot)
|
||||
return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetPlayerLights, &led_data, sizeof(led_data), NULL);
|
||||
}
|
||||
|
||||
static SDL_bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
|
||||
static SDL_bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx, Uint8 input_mode)
|
||||
{
|
||||
Uint8 *pStickCal;
|
||||
size_t stick, axis;
|
||||
@@ -577,7 +603,7 @@ static SDL_bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->m_bUsingBluetooth) {
|
||||
if (input_mode == k_eSwitchInputReportIDs_SimpleControllerState) {
|
||||
for (stick = 0; stick < 2; ++stick) {
|
||||
for(axis = 0; axis < 2; ++axis) {
|
||||
ctx->m_StickExtents[stick].axis[axis].sMin = (Sint16)(SDL_MIN_SINT16 * 0.5f);
|
||||
@@ -708,8 +734,7 @@ HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joysti
|
||||
/* Find out whether or not we can send output reports */
|
||||
ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(device->vendor_id, device->product_id);
|
||||
if (!ctx->m_bInputOnly) {
|
||||
/* The Power A Nintendo Switch Pro controllers don't have a Home LED */
|
||||
ctx->m_bHasHomeLED = (device->vendor_id != 0 && device->product_id != 0) ? SDL_TRUE : SDL_FALSE;
|
||||
ctx->m_bHasHomeLED = HasHomeLED(device->vendor_id, device->product_id);
|
||||
|
||||
/* Initialize rumble data */
|
||||
SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
|
||||
@@ -720,7 +745,24 @@ HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joysti
|
||||
ctx->m_bUsingBluetooth = SDL_TRUE;
|
||||
}
|
||||
|
||||
if (!LoadStickCalibration(ctx)) {
|
||||
/* Determine the desired input mode (needed before loading stick calibration) */
|
||||
if (ctx->m_bUsingBluetooth) {
|
||||
input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
|
||||
} else {
|
||||
input_mode = k_eSwitchInputReportIDs_FullControllerState;
|
||||
}
|
||||
|
||||
/* The official Nintendo Switch Pro Controller supports FullControllerState over bluetooth
|
||||
* just fine. We really should use that, or else the epowerlevel code in
|
||||
* HandleFullControllerState is completely pointless. We need full state if we want battery
|
||||
* level and we only care about battery level over bluetooth anyway.
|
||||
*/
|
||||
if (device->vendor_id == USB_VENDOR_NINTENDO &&
|
||||
device->product_id == USB_PRODUCT_NINTENDO_SWITCH_PRO) {
|
||||
input_mode = k_eSwitchInputReportIDs_FullControllerState;
|
||||
}
|
||||
|
||||
if (!LoadStickCalibration(ctx, input_mode)) {
|
||||
SDL_SetError("Couldn't load stick calibration");
|
||||
goto error;
|
||||
}
|
||||
@@ -730,12 +772,7 @@ HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joysti
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Set the desired input mode */
|
||||
if (ctx->m_bUsingBluetooth) {
|
||||
input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
|
||||
} else {
|
||||
input_mode = k_eSwitchInputReportIDs_FullControllerState;
|
||||
}
|
||||
/* Set desired input mode */
|
||||
if (!SetInputMode(ctx, input_mode)) {
|
||||
SDL_SetError("Couldn't set input mode");
|
||||
goto error;
|
||||
@@ -766,7 +803,7 @@ HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joysti
|
||||
SDL_GameControllerButtonReportingHintChanged, ctx);
|
||||
|
||||
/* Initialize the joystick capabilities */
|
||||
joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
|
||||
joystick->nbuttons = 16;
|
||||
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
|
||||
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
|
||||
|
||||
@@ -785,10 +822,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
|
||||
*
|
||||
@@ -821,6 +856,98 @@ 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 int
|
||||
HIDAPI_DriverSwitch_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverSwitch_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
/* Doesn't have an RGB LED, so don't return true here */
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverSwitch_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverSwitch_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static void HandleInputOnlyControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchInputOnlyControllerStatePacket_t *packet)
|
||||
{
|
||||
Sint16 axis;
|
||||
@@ -848,6 +975,7 @@ static void HandleInputOnlyControllerState(SDL_Joystick *joystick, SDL_DriverSwi
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MISC1, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
|
||||
if (packet->ucStickHat != ctx->m_lastInputOnlyState.ucStickHat) {
|
||||
@@ -925,10 +1053,10 @@ static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch
|
||||
|
||||
if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
|
||||
Uint8 data = packet->rgucButtons[0];
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_A), (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_B), (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_X), (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_Y), (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_A), (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_B), (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_X), (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_Y), (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
|
||||
|
||||
@@ -946,6 +1074,7 @@ static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MISC1, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
|
||||
if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
|
||||
@@ -1013,10 +1142,10 @@ static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_C
|
||||
|
||||
if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {
|
||||
Uint8 data = packet->controllerState.rgucButtons[0];
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_X), (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_Y), (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_A), (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_B), (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_A), (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_B), (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_X), (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_Y), (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
|
||||
axis = (data & 0x80) ? 32767 : -32768;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
|
||||
@@ -1030,6 +1159,7 @@ static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_C
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MISC1, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
|
||||
if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {
|
||||
@@ -1098,6 +1228,9 @@ HIDAPI_DriverSwitch_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
}
|
||||
|
||||
while ((size = ReadInput(ctx)) > 0) {
|
||||
#ifdef DEBUG_SWITCH_PROTOCOL
|
||||
HIDAPI_DumpPacket("Nintendo Switch packet: size = %d", ctx->m_rgucReadBuffer, size);
|
||||
#endif
|
||||
if (ctx->m_bInputOnly) {
|
||||
HandleInputOnlyControllerState(joystick, ctx, (SwitchInputOnlyControllerStatePacket_t *)&ctx->m_rgucReadBuffer[0]);
|
||||
} else {
|
||||
@@ -1114,8 +1247,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);
|
||||
}
|
||||
|
||||
@@ -1163,8 +1301,12 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch =
|
||||
HIDAPI_DriverSwitch_UpdateDevice,
|
||||
HIDAPI_DriverSwitch_OpenJoystick,
|
||||
HIDAPI_DriverSwitch_RumbleJoystick,
|
||||
HIDAPI_DriverSwitch_RumbleJoystickTriggers,
|
||||
HIDAPI_DriverSwitch_HasJoystickLED,
|
||||
HIDAPI_DriverSwitch_SetJoystickLED,
|
||||
HIDAPI_DriverSwitch_SetJoystickSensorsEnabled,
|
||||
HIDAPI_DriverSwitch_CloseJoystick,
|
||||
HIDAPI_DriverSwitch_FreeDevice
|
||||
HIDAPI_DriverSwitch_FreeDevice,
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_SWITCH */
|
||||
|
@@ -23,7 +23,6 @@
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_log.h"
|
||||
#include "SDL_events.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_joystick.h"
|
||||
@@ -35,215 +34,14 @@
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
|
||||
|
||||
#ifdef __WIN32__
|
||||
#define SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
|
||||
/* This requires the Windows 10 SDK to build */
|
||||
/*#define SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT*/
|
||||
#endif
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
|
||||
#include "../../core/windows/SDL_xinput.h"
|
||||
#endif
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#define COBJMACROS
|
||||
#include "windows.gaming.input.h"
|
||||
#endif
|
||||
/* Define this if you want to log all packets from the controller */
|
||||
/*#define DEBUG_XBOX_PROTOCOL*/
|
||||
|
||||
|
||||
typedef struct {
|
||||
Uint8 last_state[USB_PACKET_LENGTH];
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
|
||||
SDL_bool xinput_enabled;
|
||||
Uint8 xinput_slot;
|
||||
#endif
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
|
||||
SDL_bool coinitialized;
|
||||
__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;
|
||||
__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
|
||||
struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
|
||||
#endif
|
||||
} SDL_DriverXbox360_Context;
|
||||
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
|
||||
static Uint8 xinput_slots;
|
||||
|
||||
static void
|
||||
HIDAPI_DriverXbox360_MarkXInputSlotUsed(Uint8 xinput_slot)
|
||||
{
|
||||
if (xinput_slot != XUSER_INDEX_ANY) {
|
||||
xinput_slots |= (0x01 << xinput_slot);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverXbox360_MarkXInputSlotFree(Uint8 xinput_slot)
|
||||
{
|
||||
if (xinput_slot != XUSER_INDEX_ANY) {
|
||||
xinput_slots &= ~(0x01 << xinput_slot);
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverXbox360_MissingXInputSlot()
|
||||
{
|
||||
return xinput_slots != 0x0F;
|
||||
}
|
||||
|
||||
static Uint8
|
||||
HIDAPI_DriverXbox360_GuessXInputSlot(WORD wButtons)
|
||||
{
|
||||
DWORD user_index;
|
||||
int match_count;
|
||||
Uint8 match_slot;
|
||||
|
||||
if (!XINPUTGETSTATE) {
|
||||
return XUSER_INDEX_ANY;
|
||||
}
|
||||
|
||||
match_count = 0;
|
||||
for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) {
|
||||
XINPUT_STATE_EX xinput_state;
|
||||
|
||||
if (XINPUTGETSTATE(user_index, &xinput_state) == ERROR_SUCCESS) {
|
||||
if (xinput_state.Gamepad.wButtons == wButtons) {
|
||||
++match_count;
|
||||
match_slot = (Uint8)user_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match_count == 1) {
|
||||
return match_slot;
|
||||
}
|
||||
return XUSER_INDEX_ANY;
|
||||
}
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
|
||||
|
||||
static void
|
||||
HIDAPI_DriverXbox360_InitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
|
||||
{
|
||||
/* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */
|
||||
if (FAILED(WIN_CoInitialize())) {
|
||||
return;
|
||||
}
|
||||
ctx->coinitialized = SDL_TRUE;
|
||||
|
||||
{
|
||||
static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } };
|
||||
HRESULT hr;
|
||||
HMODULE hModule = LoadLibraryA("combase.dll");
|
||||
if (hModule != NULL) {
|
||||
typedef HRESULT (WINAPI *WindowsCreateString_t)(PCNZWCH sourceString, UINT32 length, HSTRING* string);
|
||||
typedef HRESULT (WINAPI *WindowsDeleteString_t)(HSTRING string);
|
||||
typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory);
|
||||
|
||||
WindowsCreateString_t WindowsCreateStringFunc = (WindowsCreateString_t)GetProcAddress(hModule, "WindowsCreateString");
|
||||
WindowsDeleteString_t WindowsDeleteStringFunc = (WindowsDeleteString_t)GetProcAddress(hModule, "WindowsDeleteString");
|
||||
RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory");
|
||||
if (WindowsCreateStringFunc && WindowsDeleteStringFunc && RoGetActivationFactoryFunc) {
|
||||
LPTSTR pNamespace = L"Windows.Gaming.Input.Gamepad";
|
||||
HSTRING hNamespaceString;
|
||||
|
||||
hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString);
|
||||
if (SUCCEEDED(hr)) {
|
||||
RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, &ctx->gamepad_statics);
|
||||
WindowsDeleteStringFunc(hNamespaceString);
|
||||
}
|
||||
}
|
||||
FreeLibrary(hModule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8
|
||||
HIDAPI_DriverXbox360_GetGamepadButtonsForMatch(__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad)
|
||||
{
|
||||
HRESULT hr;
|
||||
struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
|
||||
Uint8 buttons = 0;
|
||||
|
||||
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(gamepad, &state);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (state.Buttons & GamepadButtons_A) {
|
||||
buttons |= (1 << SDL_CONTROLLER_BUTTON_A);
|
||||
}
|
||||
if (state.Buttons & GamepadButtons_B) {
|
||||
buttons |= (1 << SDL_CONTROLLER_BUTTON_B);
|
||||
}
|
||||
if (state.Buttons & GamepadButtons_X) {
|
||||
buttons |= (1 << SDL_CONTROLLER_BUTTON_X);
|
||||
}
|
||||
if (state.Buttons & GamepadButtons_Y) {
|
||||
buttons |= (1 << SDL_CONTROLLER_BUTTON_Y);
|
||||
}
|
||||
}
|
||||
return buttons;
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverXbox360_GuessGamepad(SDL_DriverXbox360_Context *ctx, Uint8 buttons)
|
||||
{
|
||||
HRESULT hr;
|
||||
__FIVectorView_1_Windows__CGaming__CInput__CGamepad *gamepads;
|
||||
|
||||
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_get_Gamepads(ctx->gamepad_statics, &gamepads);
|
||||
if (SUCCEEDED(hr)) {
|
||||
unsigned int i, num_gamepads;
|
||||
|
||||
hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_get_Size(gamepads, &num_gamepads);
|
||||
if (SUCCEEDED(hr)) {
|
||||
int match_count;
|
||||
unsigned int match_slot;
|
||||
|
||||
match_count = 0;
|
||||
for (i = 0; i < num_gamepads; ++i) {
|
||||
__x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
|
||||
|
||||
hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, i, &gamepad);
|
||||
if (SUCCEEDED(hr)) {
|
||||
Uint8 gamepad_buttons = HIDAPI_DriverXbox360_GetGamepadButtonsForMatch(gamepad);
|
||||
if (buttons == gamepad_buttons) {
|
||||
++match_count;
|
||||
match_slot = i;
|
||||
}
|
||||
__x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);
|
||||
}
|
||||
}
|
||||
if (match_count == 1) {
|
||||
hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, match_slot, &ctx->gamepad);
|
||||
if (SUCCEEDED(hr)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
__FIVectorView_1_Windows__CGaming__CInput__CGamepad_Release(gamepads);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverXbox360_QuitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
|
||||
{
|
||||
if (ctx->gamepad_statics) {
|
||||
__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(ctx->gamepad_statics);
|
||||
ctx->gamepad_statics = NULL;
|
||||
}
|
||||
if (ctx->gamepad) {
|
||||
__x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(ctx->gamepad);
|
||||
ctx->gamepad = NULL;
|
||||
}
|
||||
|
||||
if (ctx->coinitialized) {
|
||||
WIN_CoUninitialize();
|
||||
ctx->coinitialized = SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
|
||||
|
||||
#if defined(__MACOSX__)
|
||||
static SDL_bool
|
||||
IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
|
||||
@@ -283,6 +81,7 @@ HIDAPI_DriverXbox360_IsSupportedDevice(const char *name, SDL_GameControllerType
|
||||
/* This is the Steam Virtual Gamepad, which isn't supported by this driver */
|
||||
return SDL_FALSE;
|
||||
}
|
||||
#endif
|
||||
#if defined(__MACOSX__)
|
||||
/* Wired Xbox One controllers are handled by this driver, interfacing with
|
||||
the 360Controller driver available from:
|
||||
@@ -293,10 +92,9 @@ HIDAPI_DriverXbox360_IsSupportedDevice(const char *name, SDL_GameControllerType
|
||||
if (IsBluetoothXboxOneController(vendor_id, product_id)) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
#endif
|
||||
return (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE);
|
||||
return (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE) ? SDL_TRUE : SDL_FALSE;
|
||||
#else
|
||||
return (type == SDL_CONTROLLER_TYPE_XBOX360);
|
||||
return (type == SDL_CONTROLLER_TYPE_XBOX360) ? SDL_TRUE : SDL_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -332,9 +130,10 @@ HIDAPI_DriverXbox360_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_Joystic
|
||||
static void
|
||||
HIDAPI_DriverXbox360_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
|
||||
{
|
||||
if (device->dev) {
|
||||
SetSlotLED(device->dev, (player_index % 4));
|
||||
if (!device->dev) {
|
||||
return;
|
||||
}
|
||||
SetSlotLED(device->dev, (player_index % 4));
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
@@ -351,23 +150,12 @@ HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst
|
||||
|
||||
device->dev = hid_open_path(device->path, 0);
|
||||
if (!device->dev) {
|
||||
SDL_free(ctx);
|
||||
SDL_SetError("Couldn't open %s", device->path);
|
||||
SDL_free(ctx);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
device->context = ctx;
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
|
||||
ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE);
|
||||
if (ctx->xinput_enabled && WIN_LoadXInputDLL() < 0) {
|
||||
ctx->xinput_enabled = SDL_FALSE;
|
||||
}
|
||||
ctx->xinput_slot = XUSER_INDEX_ANY;
|
||||
#endif
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
|
||||
HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx);
|
||||
#endif
|
||||
|
||||
/* Set the controller LED */
|
||||
player_index = SDL_JoystickGetPlayerIndex(joystick);
|
||||
if (player_index >= 0) {
|
||||
@@ -375,7 +163,7 @@ HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst
|
||||
}
|
||||
|
||||
/* Initialize the joystick capabilities */
|
||||
joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
|
||||
joystick->nbuttons = 15;
|
||||
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
|
||||
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
|
||||
|
||||
@@ -385,46 +173,6 @@ HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst
|
||||
static int
|
||||
HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
|
||||
{
|
||||
#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT)
|
||||
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
|
||||
#endif
|
||||
|
||||
#ifdef __WIN32__
|
||||
SDL_bool rumbled = SDL_FALSE;
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
|
||||
if (!rumbled && ctx->gamepad) {
|
||||
HRESULT hr;
|
||||
|
||||
ctx->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
|
||||
ctx->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
|
||||
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(ctx->gamepad, ctx->vibration);
|
||||
if (SUCCEEDED(hr)) {
|
||||
rumbled = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
|
||||
if (!rumbled && ctx->xinput_slot != XUSER_INDEX_ANY) {
|
||||
XINPUT_VIBRATION XVibration;
|
||||
|
||||
if (!XINPUTSETSTATE) {
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
XVibration.wLeftMotorSpeed = low_frequency_rumble;
|
||||
XVibration.wRightMotorSpeed = high_frequency_rumble;
|
||||
if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) == ERROR_SUCCESS) {
|
||||
rumbled = SDL_TRUE;
|
||||
} else {
|
||||
return SDL_SetError("XInputSetState() failed");
|
||||
}
|
||||
}
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
|
||||
|
||||
#else /* !__WIN32__ */
|
||||
|
||||
#ifdef __MACOSX__
|
||||
if (IsBluetoothXboxOneController(device->vendor_id, device->product_id)) {
|
||||
Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00 };
|
||||
@@ -458,185 +206,36 @@ HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joy
|
||||
return SDL_SetError("Couldn't send rumble packet");
|
||||
}
|
||||
#endif
|
||||
#endif /* __WIN32__ */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __WIN32__
|
||||
/* This is the packet format for Xbox 360 and Xbox One controllers on Windows,
|
||||
however with this interface there is no rumble support, no guide button,
|
||||
and the left and right triggers are tied together as a single axis.
|
||||
|
||||
We use XInput and Windows.Gaming.Input to make up for these shortcomings.
|
||||
*/
|
||||
static void
|
||||
HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
|
||||
static int
|
||||
HIDAPI_DriverXbox360_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
Sint16 axis;
|
||||
SDL_bool has_trigger_data = SDL_FALSE;
|
||||
|
||||
if (ctx->last_state[10] != data[10]) {
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[10] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[10] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[10] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[10] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[10] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[10] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[10] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[10] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
|
||||
}
|
||||
|
||||
if (ctx->last_state[11] != data[11]) {
|
||||
SDL_bool dpad_up = SDL_FALSE;
|
||||
SDL_bool dpad_down = SDL_FALSE;
|
||||
SDL_bool dpad_left = SDL_FALSE;
|
||||
SDL_bool dpad_right = SDL_FALSE;
|
||||
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[11] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[11] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
|
||||
|
||||
switch (data[11] & 0x3C) {
|
||||
case 4:
|
||||
dpad_up = SDL_TRUE;
|
||||
break;
|
||||
case 8:
|
||||
dpad_up = SDL_TRUE;
|
||||
dpad_right = SDL_TRUE;
|
||||
break;
|
||||
case 12:
|
||||
dpad_right = SDL_TRUE;
|
||||
break;
|
||||
case 16:
|
||||
dpad_right = SDL_TRUE;
|
||||
dpad_down = SDL_TRUE;
|
||||
break;
|
||||
case 20:
|
||||
dpad_down = SDL_TRUE;
|
||||
break;
|
||||
case 24:
|
||||
dpad_left = SDL_TRUE;
|
||||
dpad_down = SDL_TRUE;
|
||||
break;
|
||||
case 28:
|
||||
dpad_left = SDL_TRUE;
|
||||
break;
|
||||
case 32:
|
||||
dpad_up = SDL_TRUE;
|
||||
dpad_left = SDL_TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
|
||||
}
|
||||
|
||||
axis = (int)*(Uint16*)(&data[0]) - 0x8000;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
|
||||
axis = (int)*(Uint16*)(&data[2]) - 0x8000;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
|
||||
axis = (int)*(Uint16*)(&data[4]) - 0x8000;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
|
||||
axis = (int)*(Uint16*)(&data[6]) - 0x8000;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
|
||||
if (ctx->gamepad_statics && !ctx->gamepad) {
|
||||
Uint8 buttons = 0;
|
||||
|
||||
if (data[10] & 0x01) {
|
||||
buttons |= (1 << SDL_CONTROLLER_BUTTON_A);
|
||||
}
|
||||
if (data[10] & 0x02) {
|
||||
buttons |= (1 << SDL_CONTROLLER_BUTTON_B);
|
||||
}
|
||||
if (data[10] & 0x04) {
|
||||
buttons |= (1 << SDL_CONTROLLER_BUTTON_X);
|
||||
}
|
||||
if (data[10] & 0x08) {
|
||||
buttons |= (1 << SDL_CONTROLLER_BUTTON_Y);
|
||||
}
|
||||
if (buttons != 0) {
|
||||
HIDAPI_DriverXbox360_GuessGamepad(ctx, buttons);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->gamepad) {
|
||||
HRESULT hr;
|
||||
struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
|
||||
|
||||
hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(ctx->gamepad, &state);
|
||||
if (SUCCEEDED(hr)) {
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (state.Buttons & 0x40000000) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)(state.LeftTrigger * SDL_MAX_UINT16)) - 32768);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)(state.RightTrigger * SDL_MAX_UINT16)) - 32768);
|
||||
has_trigger_data = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
|
||||
if (ctx->xinput_enabled) {
|
||||
if (ctx->xinput_slot == XUSER_INDEX_ANY && HIDAPI_DriverXbox360_MissingXInputSlot()) {
|
||||
WORD wButtons = 0;
|
||||
|
||||
if (data[10] & 0x01) {
|
||||
wButtons |= XINPUT_GAMEPAD_A;
|
||||
}
|
||||
if (data[10] & 0x02) {
|
||||
wButtons |= XINPUT_GAMEPAD_B;
|
||||
}
|
||||
if (data[10] & 0x04) {
|
||||
wButtons |= XINPUT_GAMEPAD_X;
|
||||
}
|
||||
if (data[10] & 0x08) {
|
||||
wButtons |= XINPUT_GAMEPAD_Y;
|
||||
}
|
||||
if (wButtons != 0) {
|
||||
Uint8 xinput_slot = HIDAPI_DriverXbox360_GuessXInputSlot(wButtons);
|
||||
if (xinput_slot != XUSER_INDEX_ANY) {
|
||||
HIDAPI_DriverXbox360_MarkXInputSlotUsed(xinput_slot);
|
||||
ctx->xinput_slot = xinput_slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_trigger_data && ctx->xinput_slot != XUSER_INDEX_ANY) {
|
||||
XINPUT_STATE_EX xinput_state;
|
||||
|
||||
if (XINPUTGETSTATE(ctx->xinput_slot, &xinput_state) == ERROR_SUCCESS) {
|
||||
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (xinput_state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)xinput_state.Gamepad.bLeftTrigger * 257) - 32768);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)xinput_state.Gamepad.bRightTrigger * 257) - 32768);
|
||||
has_trigger_data = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */
|
||||
|
||||
if (!has_trigger_data) {
|
||||
axis = (data[9] * 257) - 32768;
|
||||
if (data[9] < 0x80) {
|
||||
axis = -axis * 2 - 32769;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
|
||||
} else if (data[9] > 0x80) {
|
||||
axis = axis * 2 - 32767;
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
|
||||
} else {
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16);
|
||||
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverXbox360_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
/* Doesn't have an RGB LED, so don't return true here */
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverXbox360_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverXbox360_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
#else
|
||||
|
||||
static void
|
||||
HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
|
||||
HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size)
|
||||
{
|
||||
Sint16 axis;
|
||||
#ifdef __MACOSX__
|
||||
@@ -687,7 +286,6 @@ HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev,
|
||||
|
||||
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
|
||||
}
|
||||
#endif /* __WIN32__ */
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
@@ -695,7 +293,7 @@ HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
|
||||
SDL_Joystick *joystick = NULL;
|
||||
Uint8 data[USB_PACKET_LENGTH];
|
||||
int size;
|
||||
int size = 0;
|
||||
|
||||
if (device->num_joysticks > 0) {
|
||||
joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
|
||||
@@ -705,7 +303,12 @@ HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
}
|
||||
|
||||
while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
|
||||
HIDAPI_DriverXbox360_HandleStatePacket(joystick, device->dev, ctx, data, size);
|
||||
#ifdef DEBUG_XBOX_PROTOCOL
|
||||
HIDAPI_DumpPacket("Xbox 360 packet: size = %d", data, size);
|
||||
#endif
|
||||
if (data[0] == 0x00) {
|
||||
HIDAPI_DriverXbox360_HandleStatePacket(joystick, ctx, data, size);
|
||||
}
|
||||
}
|
||||
|
||||
if (size < 0) {
|
||||
@@ -718,22 +321,10 @@ HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
static void
|
||||
HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
|
||||
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
|
||||
#endif
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
|
||||
if (ctx->xinput_enabled) {
|
||||
HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot);
|
||||
WIN_UnloadXInputDLL();
|
||||
if (device->dev) {
|
||||
hid_close(device->dev);
|
||||
device->dev = NULL;
|
||||
}
|
||||
#endif
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
|
||||
HIDAPI_DriverXbox360_QuitWindowsGamingInput(ctx);
|
||||
#endif
|
||||
|
||||
hid_close(device->dev);
|
||||
device->dev = NULL;
|
||||
|
||||
SDL_free(device->context);
|
||||
device->context = NULL;
|
||||
@@ -756,8 +347,12 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
|
||||
HIDAPI_DriverXbox360_UpdateDevice,
|
||||
HIDAPI_DriverXbox360_OpenJoystick,
|
||||
HIDAPI_DriverXbox360_RumbleJoystick,
|
||||
HIDAPI_DriverXbox360_RumbleJoystickTriggers,
|
||||
HIDAPI_DriverXbox360_HasJoystickLED,
|
||||
HIDAPI_DriverXbox360_SetJoystickLED,
|
||||
HIDAPI_DriverXbox360_SetJoystickSensorsEnabled,
|
||||
HIDAPI_DriverXbox360_CloseJoystick,
|
||||
HIDAPI_DriverXbox360_FreeDevice
|
||||
HIDAPI_DriverXbox360_FreeDevice,
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
|
||||
|
@@ -23,7 +23,6 @@
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_log.h"
|
||||
#include "SDL_events.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_joystick.h"
|
||||
@@ -35,6 +34,9 @@
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
|
||||
|
||||
/* Define this if you want to log all packets from the controller */
|
||||
/*#define DEBUG_XBOX_PROTOCOL*/
|
||||
|
||||
|
||||
typedef struct {
|
||||
SDL_bool connected;
|
||||
@@ -126,6 +128,9 @@ HIDAPI_DriverXbox360W_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_Joysti
|
||||
static void
|
||||
HIDAPI_DriverXbox360W_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
|
||||
{
|
||||
if (!device->dev) {
|
||||
return;
|
||||
}
|
||||
SetSlotLED(device->dev, (player_index % 4));
|
||||
}
|
||||
|
||||
@@ -137,7 +142,7 @@ HIDAPI_DriverXbox360W_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joys
|
||||
SDL_zeroa(ctx->last_state);
|
||||
|
||||
/* Initialize the joystick capabilities */
|
||||
joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
|
||||
joystick->nbuttons = 15;
|
||||
joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
|
||||
joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN;
|
||||
|
||||
@@ -158,6 +163,31 @@ HIDAPI_DriverXbox360W_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *jo
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverXbox360W_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_DriverXbox360W_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
|
||||
{
|
||||
/* Doesn't have an RGB LED, so don't return true here */
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverXbox360W_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_DriverXbox360W_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_DriverXbox360W_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360W_Context *ctx, Uint8 *data, int size)
|
||||
{
|
||||
@@ -220,6 +250,9 @@ HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
}
|
||||
|
||||
while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
|
||||
#ifdef DEBUG_XBOX_PROTOCOL
|
||||
HIDAPI_DumpPacket("Xbox 360 wireless packet: size = %d", data, size);
|
||||
#endif
|
||||
if (size == 2 && data[0] == 0x08) {
|
||||
SDL_bool connected = (data[1] & 0x80) ? SDL_TRUE : SDL_FALSE;
|
||||
#ifdef DEBUG_JOYSTICK
|
||||
@@ -295,8 +328,12 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360W =
|
||||
HIDAPI_DriverXbox360W_UpdateDevice,
|
||||
HIDAPI_DriverXbox360W_OpenJoystick,
|
||||
HIDAPI_DriverXbox360W_RumbleJoystick,
|
||||
HIDAPI_DriverXbox360W_RumbleJoystickTriggers,
|
||||
HIDAPI_DriverXbox360W_HasJoystickLED,
|
||||
HIDAPI_DriverXbox360W_SetJoystickLED,
|
||||
HIDAPI_DriverXbox360W_SetJoystickSensorsEnabled,
|
||||
HIDAPI_DriverXbox360W_CloseJoystick,
|
||||
HIDAPI_DriverXbox360W_FreeDevice
|
||||
HIDAPI_DriverXbox360W_FreeDevice,
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -22,11 +22,9 @@
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
#include "SDL_assert.h"
|
||||
#include "SDL_atomic.h"
|
||||
#include "SDL_endian.h"
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_log.h"
|
||||
#include "SDL_thread.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_joystick.h"
|
||||
@@ -37,6 +35,7 @@
|
||||
|
||||
#if defined(__WIN32__)
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#include "../windows/SDL_rawinputjoystick_c.h"
|
||||
#endif
|
||||
|
||||
#if defined(__MACOSX__)
|
||||
@@ -63,6 +62,9 @@ static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_PS4
|
||||
&SDL_HIDAPI_DriverPS4,
|
||||
#endif
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_PS5
|
||||
&SDL_HIDAPI_DriverPS5,
|
||||
#endif
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_STEAM
|
||||
&SDL_HIDAPI_DriverSteam,
|
||||
#endif
|
||||
@@ -189,7 +191,7 @@ HIDAPI_InitializeDiscovery()
|
||||
#if defined(__WIN32__)
|
||||
SDL_HIDAPI_discovery.m_nThreadID = SDL_ThreadID();
|
||||
|
||||
SDL_memset(&SDL_HIDAPI_discovery.m_wndClass, 0x0, sizeof(SDL_HIDAPI_discovery.m_wndClass));
|
||||
SDL_zero(SDL_HIDAPI_discovery.m_wndClass);
|
||||
SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL);
|
||||
SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION";
|
||||
SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc; /* This function is called by windows */
|
||||
@@ -200,8 +202,8 @@ HIDAPI_InitializeDiscovery()
|
||||
|
||||
{
|
||||
DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast;
|
||||
SDL_memset( &devBroadcast, 0x0, sizeof( devBroadcast ) );
|
||||
|
||||
SDL_zero(devBroadcast);
|
||||
devBroadcast.dbcc_size = sizeof( devBroadcast );
|
||||
devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||||
devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
|
||||
@@ -387,6 +389,27 @@ HIDAPI_ShutdownDiscovery()
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
HIDAPI_DumpPacket(const char *prefix, Uint8 *data, int size)
|
||||
{
|
||||
int i;
|
||||
char *buffer;
|
||||
size_t length = SDL_strlen(prefix) + 11*(USB_PACKET_LENGTH/8) + (5*USB_PACKET_LENGTH*2) + 1 + 1;
|
||||
int start = 0, amount = size;
|
||||
|
||||
buffer = (char *)SDL_malloc(length);
|
||||
SDL_snprintf(buffer, length, prefix, size);
|
||||
for (i = start; i < start+amount; ++i) {
|
||||
if ((i % 8) == 0) {
|
||||
SDL_snprintf(&buffer[SDL_strlen(buffer)], length - SDL_strlen(buffer), "\n%.2d: ", i);
|
||||
}
|
||||
SDL_snprintf(&buffer[SDL_strlen(buffer)], length - SDL_strlen(buffer), " 0x%.2x", data[i]);
|
||||
}
|
||||
SDL_strlcat(buffer, "\n", length);
|
||||
SDL_Log("%s", buffer);
|
||||
SDL_free(buffer);
|
||||
}
|
||||
|
||||
static void HIDAPI_JoystickDetect(void);
|
||||
static void HIDAPI_JoystickClose(SDL_Joystick * joystick);
|
||||
|
||||
@@ -419,11 +442,13 @@ HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
|
||||
return NULL;
|
||||
}
|
||||
if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
|
||||
return NULL;
|
||||
if (device->vendor_id != USB_VENDOR_VALVE) {
|
||||
if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {
|
||||
return NULL;
|
||||
}
|
||||
if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
|
||||
@@ -561,7 +586,7 @@ HIDAPI_JoystickInit(void)
|
||||
}
|
||||
|
||||
#if defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__TVOS__)
|
||||
/* The hidapi framwork is weak-linked on Apple platforms */
|
||||
/* The hidapi framwork is weak-linked on Apple platforms */
|
||||
int HID_API_EXPORT HID_API_CALL hid_init(void) __attribute__((weak_import));
|
||||
|
||||
if (hid_init == NULL) {
|
||||
@@ -615,7 +640,7 @@ HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID)
|
||||
void
|
||||
HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)
|
||||
{
|
||||
int i;
|
||||
int i, size;
|
||||
|
||||
for (i = 0; i < device->num_joysticks; ++i) {
|
||||
if (device->joysticks[i] == joystickID) {
|
||||
@@ -624,8 +649,10 @@ HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID
|
||||
HIDAPI_JoystickClose(joystick);
|
||||
}
|
||||
|
||||
SDL_memmove(&device->joysticks[i], &device->joysticks[i+1], device->num_joysticks - i - 1);
|
||||
size = (device->num_joysticks - i - 1) * sizeof(SDL_JoystickID);
|
||||
SDL_memmove(&device->joysticks[i], &device->joysticks[i+1], size);
|
||||
--device->num_joysticks;
|
||||
|
||||
--SDL_HIDAPI_numjoysticks;
|
||||
if (device->num_joysticks == 0) {
|
||||
SDL_free(device->joysticks);
|
||||
@@ -646,6 +673,24 @@ HIDAPI_JoystickGetCount(void)
|
||||
return SDL_HIDAPI_numjoysticks;
|
||||
}
|
||||
|
||||
static char *
|
||||
HIDAPI_ConvertString(const wchar_t *wide_string)
|
||||
{
|
||||
char *string = NULL;
|
||||
|
||||
if (wide_string) {
|
||||
string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t));
|
||||
if (!string) {
|
||||
if (sizeof(wchar_t) == sizeof(Uint16)) {
|
||||
string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t));
|
||||
} else if (sizeof(wchar_t) == sizeof(Uint32)) {
|
||||
string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)wide_string, (SDL_wcslen(wide_string)+1)*sizeof(wchar_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_AddDevice(struct hid_device_info *info)
|
||||
{
|
||||
@@ -698,59 +743,64 @@ HIDAPI_AddDevice(struct hid_device_info *info)
|
||||
device->dev_lock = SDL_CreateMutex();
|
||||
|
||||
/* Need the device name before getting the driver to know whether to ignore this device */
|
||||
if (!device->name) {
|
||||
const char *name = SDL_GetCustomJoystickName(device->vendor_id, device->product_id);
|
||||
if (name) {
|
||||
device->name = SDL_strdup(name);
|
||||
}
|
||||
}
|
||||
if (!device->name && info->manufacturer_string && info->product_string) {
|
||||
const char *manufacturer_remapped;
|
||||
char *manufacturer_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
|
||||
char *product_string = SDL_iconv_string("UTF-8", "WCHAR_T", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
|
||||
if (!manufacturer_string && !product_string) {
|
||||
if (sizeof(wchar_t) == sizeof(Uint16)) {
|
||||
manufacturer_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
|
||||
product_string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
|
||||
} else if (sizeof(wchar_t) == sizeof(Uint32)) {
|
||||
manufacturer_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->manufacturer_string, (SDL_wcslen(info->manufacturer_string)+1)*sizeof(wchar_t));
|
||||
product_string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char*)info->product_string, (SDL_wcslen(info->product_string)+1)*sizeof(wchar_t));
|
||||
{
|
||||
char *manufacturer_string = HIDAPI_ConvertString(info->manufacturer_string);
|
||||
char *product_string = HIDAPI_ConvertString(info->product_string);
|
||||
char *serial_number = HIDAPI_ConvertString(info->serial_number);
|
||||
|
||||
device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, manufacturer_string, product_string);
|
||||
if (SDL_strncmp(device->name, "0x", 2) == 0) {
|
||||
/* Couldn't find a controller name, try to give it one based on device type */
|
||||
const char *name = NULL;
|
||||
|
||||
switch (SDL_GetJoystickGameControllerType(NULL, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) {
|
||||
case SDL_CONTROLLER_TYPE_XBOX360:
|
||||
name = "Xbox 360 Controller";
|
||||
break;
|
||||
case SDL_CONTROLLER_TYPE_XBOXONE:
|
||||
name = "Xbox One Controller";
|
||||
break;
|
||||
case SDL_CONTROLLER_TYPE_PS3:
|
||||
name = "PS3 Controller";
|
||||
break;
|
||||
case SDL_CONTROLLER_TYPE_PS4:
|
||||
name = "PS4 Controller";
|
||||
break;
|
||||
case SDL_CONTROLLER_TYPE_PS5:
|
||||
name = "PS5 Controller";
|
||||
break;
|
||||
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
|
||||
name = "Nintendo Switch Pro Controller";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
SDL_free(device->name);
|
||||
device->name = SDL_strdup(name);
|
||||
}
|
||||
}
|
||||
|
||||
manufacturer_remapped = SDL_GetCustomJoystickManufacturer(manufacturer_string);
|
||||
if (manufacturer_remapped != manufacturer_string) {
|
||||
SDL_free(manufacturer_string);
|
||||
manufacturer_string = SDL_strdup(manufacturer_remapped);
|
||||
}
|
||||
|
||||
if (manufacturer_string && product_string) {
|
||||
size_t name_size = (SDL_strlen(manufacturer_string) + 1 + SDL_strlen(product_string) + 1);
|
||||
device->name = (char *)SDL_malloc(name_size);
|
||||
if (device->name) {
|
||||
if (SDL_strncasecmp(manufacturer_string, product_string, SDL_strlen(manufacturer_string)) == 0) {
|
||||
SDL_strlcpy(device->name, product_string, name_size);
|
||||
} else {
|
||||
SDL_snprintf(device->name, name_size, "%s %s", manufacturer_string, product_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (manufacturer_string) {
|
||||
SDL_free(manufacturer_string);
|
||||
}
|
||||
if (product_string) {
|
||||
SDL_free(product_string);
|
||||
}
|
||||
}
|
||||
if (!device->name) {
|
||||
size_t name_size = (6 + 1 + 6 + 1);
|
||||
device->name = (char *)SDL_malloc(name_size);
|
||||
|
||||
if (serial_number && *serial_number) {
|
||||
device->serial = serial_number;
|
||||
} else {
|
||||
SDL_free(serial_number);
|
||||
}
|
||||
|
||||
if (!device->name) {
|
||||
SDL_free(device->serial);
|
||||
SDL_free(device->path);
|
||||
SDL_free(device);
|
||||
return;
|
||||
}
|
||||
SDL_snprintf(device->name, name_size, "0x%.4x/0x%.4x", info->vendor_id, info->product_id);
|
||||
}
|
||||
|
||||
/* Add it to the list */
|
||||
@@ -763,7 +813,7 @@ HIDAPI_AddDevice(struct hid_device_info *info)
|
||||
HIDAPI_SetupDeviceDriver(device);
|
||||
|
||||
#ifdef DEBUG_HIDAPI
|
||||
SDL_Log("Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, interface %d, interface_class %d, interface_subclass %d, interface_protocol %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s (%s)\n", device->name, device->vendor_id, device->product_id, device->version, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage, device->path, device->driver ? device->driver->hint : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED");
|
||||
SDL_Log("Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, serial %s, interface %d, interface_class %d, interface_subclass %d, interface_protocol %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s (%s)\n", device->name, device->vendor_id, device->product_id, device->version, device->serial ? device->serial : "NONE", device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage, device->path, device->driver ? device->driver->hint : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -772,6 +822,11 @@ static void
|
||||
HIDAPI_DelDevice(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
SDL_HIDAPI_Device *curr, *last;
|
||||
|
||||
#ifdef DEBUG_HIDAPI
|
||||
SDL_Log("Removing HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, serial %s, interface %d, interface_class %d, interface_subclass %d, interface_protocol %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s (%s)\n", device->name, device->vendor_id, device->product_id, device->version, device->serial ? device->serial : "NONE", device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage, device->path, device->driver ? device->driver->hint : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED");
|
||||
#endif
|
||||
|
||||
for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
|
||||
if (curr == device) {
|
||||
if (last) {
|
||||
@@ -782,7 +837,13 @@ HIDAPI_DelDevice(SDL_HIDAPI_Device *device)
|
||||
|
||||
HIDAPI_CleanupDeviceDriver(device);
|
||||
|
||||
/* Make sure the rumble thread is done with this device */
|
||||
while (SDL_AtomicGet(&device->rumble_pending) > 0) {
|
||||
SDL_Delay(10);
|
||||
}
|
||||
|
||||
SDL_DestroyMutex(device->dev_lock);
|
||||
SDL_free(device->serial);
|
||||
SDL_free(device->name);
|
||||
SDL_free(device->path);
|
||||
SDL_free(device);
|
||||
@@ -836,6 +897,36 @@ HIDAPI_UpdateDeviceList(void)
|
||||
SDL_UnlockJoysticks();
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_IsEquivalentToDevice(Uint16 vendor_id, Uint16 product_id, SDL_HIDAPI_Device *device)
|
||||
{
|
||||
if (vendor_id == device->vendor_id && product_id == device->product_id) {
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
if (vendor_id == USB_VENDOR_MICROSOFT) {
|
||||
/* If we're looking for the wireless XBox 360 controller, also look for the dongle */
|
||||
if (product_id == 0x02a1 && device->product_id == 0x0719) {
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
/* If we're looking for the raw input Xbox One controller, match it against any other Xbox One controller */
|
||||
if (product_id == USB_PRODUCT_XBOX_ONE_RAW_INPUT_CONTROLLER &&
|
||||
SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol) == SDL_CONTROLLER_TYPE_XBOXONE) {
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
/* If we're looking for an XInput controller, match it against any other Xbox controller */
|
||||
if (product_id == USB_PRODUCT_XBOX_ONE_XINPUT_CONTROLLER) {
|
||||
SDL_GameControllerType type = SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
|
||||
if (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE) {
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
SDL_bool
|
||||
HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
|
||||
{
|
||||
@@ -875,18 +966,14 @@ HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, cons
|
||||
SDL_LockJoysticks();
|
||||
device = SDL_HIDAPI_devices;
|
||||
while (device) {
|
||||
if (device->vendor_id == vendor_id && device->product_id == product_id && device->driver) {
|
||||
if (device->driver &&
|
||||
HIDAPI_IsEquivalentToDevice(vendor_id, product_id, device)) {
|
||||
result = SDL_TRUE;
|
||||
}
|
||||
device = device->next;
|
||||
}
|
||||
SDL_UnlockJoysticks();
|
||||
|
||||
/* If we're looking for the wireless XBox 360 controller, also look for the dongle */
|
||||
if (!result && vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x02a1) {
|
||||
return HIDAPI_IsDevicePresent(USB_VENDOR_MICROSOFT, 0x0719, version, name);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_HIDAPI
|
||||
SDL_Log("HIDAPI_IsDevicePresent() returning %s for 0x%.4x / 0x%.4x\n", result ? "true" : "false", vendor_id, product_id);
|
||||
#endif
|
||||
@@ -920,7 +1007,9 @@ HIDAPI_UpdateDevices(void)
|
||||
while (device) {
|
||||
if (device->driver) {
|
||||
if (SDL_TryLockMutex(device->dev_lock) == 0) {
|
||||
device->updating = SDL_TRUE;
|
||||
device->driver->UpdateDevice(device);
|
||||
device->updating = SDL_FALSE;
|
||||
SDL_UnlockMutex(device->dev_lock);
|
||||
}
|
||||
}
|
||||
@@ -1014,6 +1103,10 @@ HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!joystick->serial && device->serial) {
|
||||
joystick->serial = SDL_strdup(device->serial);
|
||||
}
|
||||
|
||||
joystick->hwdata = hwdata;
|
||||
return 0;
|
||||
}
|
||||
@@ -1035,6 +1128,71 @@ HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (joystick->hwdata) {
|
||||
SDL_HIDAPI_Device *device = joystick->hwdata->device;
|
||||
|
||||
result = device->driver->RumbleJoystickTriggers(device, joystick, left_rumble, right_rumble);
|
||||
} else {
|
||||
SDL_SetError("Rumble failed, device disconnected");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_JoystickHasLED(SDL_Joystick * joystick)
|
||||
{
|
||||
SDL_bool result = SDL_FALSE;
|
||||
|
||||
if (joystick->hwdata) {
|
||||
SDL_HIDAPI_Device *device = joystick->hwdata->device;
|
||||
|
||||
result = device->driver->HasJoystickLED(device, joystick);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (joystick->hwdata) {
|
||||
SDL_HIDAPI_Device *device = joystick->hwdata->device;
|
||||
|
||||
result = device->driver->SetJoystickLED(device, joystick, red, green, blue);
|
||||
} else {
|
||||
SDL_SetError("SetLED failed, device disconnected");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
HIDAPI_JoystickSetSensorsEnabled(SDL_Joystick * joystick, SDL_bool enabled)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (joystick->hwdata) {
|
||||
SDL_HIDAPI_Device *device = joystick->hwdata->device;
|
||||
|
||||
result = device->driver->SetJoystickSensorsEnabled(device, joystick, enabled);
|
||||
} else {
|
||||
SDL_SetError("SetSensorsEnabled failed, device disconnected");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
|
||||
{
|
||||
@@ -1046,10 +1204,21 @@ HIDAPI_JoystickClose(SDL_Joystick * joystick)
|
||||
{
|
||||
if (joystick->hwdata) {
|
||||
SDL_HIDAPI_Device *device = joystick->hwdata->device;
|
||||
int i;
|
||||
|
||||
/* Wait for pending rumble to complete */
|
||||
while (SDL_AtomicGet(&device->rumble_pending) > 0) {
|
||||
SDL_Delay(10);
|
||||
/* Wait up to 30 ms for pending rumble to complete */
|
||||
if (device->updating) {
|
||||
/* Unlock the device so rumble can complete */
|
||||
SDL_UnlockMutex(device->dev_lock);
|
||||
}
|
||||
for (i = 0; i < 3; ++i) {
|
||||
if (SDL_AtomicGet(&device->rumble_pending) > 0) {
|
||||
SDL_Delay(10);
|
||||
}
|
||||
}
|
||||
if (device->updating) {
|
||||
/* Relock the device */
|
||||
SDL_LockMutex(device->dev_lock);
|
||||
}
|
||||
|
||||
device->driver->CloseJoystick(device, joystick);
|
||||
@@ -1068,11 +1237,14 @@ HIDAPI_JoystickQuit(void)
|
||||
|
||||
HIDAPI_ShutdownDiscovery();
|
||||
|
||||
SDL_HIDAPI_QuitRumble();
|
||||
|
||||
while (SDL_HIDAPI_devices) {
|
||||
HIDAPI_DelDevice(SDL_HIDAPI_devices);
|
||||
}
|
||||
|
||||
SDL_HIDAPI_QuitRumble();
|
||||
/* Make sure the drivers cleaned up properly */
|
||||
SDL_assert(SDL_HIDAPI_numjoysticks == 0);
|
||||
|
||||
for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
|
||||
SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
|
||||
@@ -1083,13 +1255,16 @@ HIDAPI_JoystickQuit(void)
|
||||
|
||||
hid_exit();
|
||||
|
||||
/* Make sure the drivers cleaned up properly */
|
||||
SDL_assert(SDL_HIDAPI_numjoysticks == 0);
|
||||
|
||||
shutting_down = SDL_FALSE;
|
||||
initialized = SDL_FALSE;
|
||||
}
|
||||
|
||||
static SDL_bool
|
||||
HIDAPI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
|
||||
{
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
|
||||
{
|
||||
HIDAPI_JoystickInit,
|
||||
@@ -1102,9 +1277,14 @@ SDL_JoystickDriver SDL_HIDAPI_JoystickDriver =
|
||||
HIDAPI_JoystickGetDeviceInstanceID,
|
||||
HIDAPI_JoystickOpen,
|
||||
HIDAPI_JoystickRumble,
|
||||
HIDAPI_JoystickRumbleTriggers,
|
||||
HIDAPI_JoystickHasLED,
|
||||
HIDAPI_JoystickSetLED,
|
||||
HIDAPI_JoystickSetSensorsEnabled,
|
||||
HIDAPI_JoystickUpdate,
|
||||
HIDAPI_JoystickClose,
|
||||
HIDAPI_JoystickQuit,
|
||||
HIDAPI_JoystickGetGamepadMapping
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI */
|
||||
|
@@ -32,23 +32,12 @@
|
||||
|
||||
/* This is the full set of HIDAPI drivers available */
|
||||
#define SDL_JOYSTICK_HIDAPI_PS4
|
||||
#define SDL_JOYSTICK_HIDAPI_PS5
|
||||
#define SDL_JOYSTICK_HIDAPI_SWITCH
|
||||
#define SDL_JOYSTICK_HIDAPI_XBOX360
|
||||
#define SDL_JOYSTICK_HIDAPI_XBOXONE
|
||||
#define SDL_JOYSTICK_HIDAPI_GAMECUBE
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
/* On Windows, Xbox One controllers are handled by the Xbox 360 driver */
|
||||
#undef SDL_JOYSTICK_HIDAPI_XBOXONE
|
||||
/* It turns out HIDAPI for Xbox controllers doesn't allow background input */
|
||||
#undef SDL_JOYSTICK_HIDAPI_XBOX360
|
||||
#endif
|
||||
|
||||
#ifdef __MACOSX__
|
||||
/* On Mac OS X, Xbox One controllers are handled by the Xbox 360 driver */
|
||||
#undef SDL_JOYSTICK_HIDAPI_XBOXONE
|
||||
#endif
|
||||
|
||||
#if defined(__IPHONEOS__) || defined(__TVOS__) || defined(__ANDROID__)
|
||||
/* Very basic Steam Controller support on mobile devices */
|
||||
#define SDL_JOYSTICK_HIDAPI_STEAM
|
||||
@@ -67,6 +56,7 @@ typedef struct _SDL_HIDAPI_Device
|
||||
Uint16 vendor_id;
|
||||
Uint16 product_id;
|
||||
Uint16 version;
|
||||
char *serial;
|
||||
SDL_JoystickGUID guid;
|
||||
int interface_number; /* Available on Windows and Linux */
|
||||
int interface_class;
|
||||
@@ -86,6 +76,9 @@ typedef struct _SDL_HIDAPI_Device
|
||||
/* Used during scanning for device changes */
|
||||
SDL_bool seen;
|
||||
|
||||
/* Used to flag that the device is being updated */
|
||||
SDL_bool updating;
|
||||
|
||||
struct _SDL_HIDAPI_Device *next;
|
||||
} SDL_HIDAPI_Device;
|
||||
|
||||
@@ -101,6 +94,10 @@ typedef struct _SDL_HIDAPI_DeviceDriver
|
||||
SDL_bool (*UpdateDevice)(SDL_HIDAPI_Device *device);
|
||||
SDL_bool (*OpenJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick);
|
||||
int (*RumbleJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble);
|
||||
int (*RumbleJoystickTriggers)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble);
|
||||
SDL_bool (*HasJoystickLED)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick);
|
||||
int (*SetJoystickLED)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue);
|
||||
int (*SetJoystickSensorsEnabled)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled);
|
||||
void (*CloseJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick);
|
||||
void (*FreeDevice)(SDL_HIDAPI_Device *device);
|
||||
|
||||
@@ -109,6 +106,7 @@ typedef struct _SDL_HIDAPI_DeviceDriver
|
||||
|
||||
/* HIDAPI device support */
|
||||
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4;
|
||||
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS5;
|
||||
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam;
|
||||
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch;
|
||||
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360;
|
||||
@@ -123,6 +121,8 @@ extern void HIDAPI_UpdateDevices(void);
|
||||
extern SDL_bool HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID);
|
||||
extern void HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID);
|
||||
|
||||
extern void HIDAPI_DumpPacket(const char *prefix, Uint8 *data, int size);
|
||||
|
||||
#endif /* SDL_JOYSTICK_HIDAPI_H */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
|
@@ -1,8 +1,23 @@
|
||||
//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
|
||||
//
|
||||
// Purpose: Defines constants used to communicate with Valve controllers.
|
||||
//
|
||||
//==================================================================================================
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 2020 Valve Corporation
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef _CONTROLLER_CONSTANTS_
|
||||
#define _CONTROLLER_CONSTANTS_
|
||||
|
@@ -1,8 +1,23 @@
|
||||
//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
|
||||
//
|
||||
// Purpose: Defines methods and structures used to communicate with Valve controllers.
|
||||
//
|
||||
//==================================================================================================
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 2020 Valve Corporation
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#ifndef _CONTROLLER_STRUCTS_
|
||||
#define _CONTROLLER_STRUCTS_
|
||||
|
||||
|
Reference in New Issue
Block a user