mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-03-16 13:47:28 +00:00
extract capabilities for 360 controllers over libusb (#15183)
Read capabilities when using xinput controllers via the libusb backend This gives us access to the subtype on linux and macOS, and gives us a lot of data we can use for handling more detailed device types when I look into a unified api for exposing instrument data later.
This commit is contained in:
@@ -23,9 +23,11 @@
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
#include "../../SDL_hints_c.h"
|
||||
#include "../../misc/SDL_libusb.h"
|
||||
#include "../SDL_sysjoystick.h"
|
||||
#include "SDL_hidapijoystick_c.h"
|
||||
#include "SDL_hidapi_rumble.h"
|
||||
#include "SDL_hidapi_xbox360.h"
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
|
||||
|
||||
@@ -42,6 +44,7 @@ typedef struct
|
||||
SDL_Joystick *joystick;
|
||||
int player_index;
|
||||
bool player_lights;
|
||||
SDL_xinput_capabilities capabilities;
|
||||
Uint8 last_state[USB_PACKET_LENGTH];
|
||||
#ifdef SDL_PLATFORM_MACOS
|
||||
bool controlled_by_360controller;
|
||||
@@ -82,6 +85,103 @@ static bool IsControlledBy360ControllerDriverMacOS(SDL_HIDAPI_Device *device)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
static void FetchXInputCapabilities(SDL_HIDAPI_Device *device)
|
||||
{
|
||||
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
|
||||
SDL_LibUSBContext *libusb_ctx;
|
||||
if (SDL_InitLibUSB(&libusb_ctx)) {
|
||||
libusb_device_handle *handle = (libusb_device_handle *)SDL_GetPointerProperty(SDL_hid_get_properties(device->dev), SDL_PROP_HIDAPI_LIBUSB_DEVICE_HANDLE_POINTER, NULL);
|
||||
if (handle == NULL) {
|
||||
SDL_QuitLibUSB();
|
||||
return;
|
||||
}
|
||||
libusb_device *dev = libusb_ctx->get_device(handle);
|
||||
if (dev == NULL) {
|
||||
SDL_QuitLibUSB();
|
||||
return;
|
||||
}
|
||||
struct libusb_config_descriptor *conf_desc = NULL;
|
||||
const struct libusb_interface_descriptor *intf_desc;
|
||||
libusb_ctx->get_active_config_descriptor(dev, &conf_desc);
|
||||
if (conf_desc == NULL || conf_desc->bNumInterfaces < device->interface_number) {
|
||||
SDL_QuitLibUSB();
|
||||
return;
|
||||
}
|
||||
const struct libusb_interface *intf = &conf_desc->interface[device->interface_number];
|
||||
intf_desc = &intf->altsetting[0];
|
||||
if (intf_desc->extra_length == 17 && intf_desc->extra[1] == 0x21) {
|
||||
ctx->capabilities.type = intf_desc->extra[3];
|
||||
ctx->capabilities.subType = intf_desc->extra[4];
|
||||
switch (ctx->capabilities.subType) {
|
||||
case 0x01: // XINPUT_DEVSUBTYPE_GAMEPAD
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_GAMEPAD;
|
||||
break;
|
||||
case 0x02: // XINPUT_DEVSUBTYPE_WHEEL
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_WHEEL;
|
||||
break;
|
||||
case 0x03: // XINPUT_DEVSUBTYPE_ARCADE_STICK
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_ARCADE_STICK;
|
||||
break;
|
||||
case 0x04: // XINPUT_DEVSUBTYPE_FLIGHT_STICK
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_FLIGHT_STICK;
|
||||
break;
|
||||
case 0x05: // XINPUT_DEVSUBTYPE_DANCE_PAD
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_DANCE_PAD;
|
||||
break;
|
||||
case 0x06: // XINPUT_DEVSUBTYPE_GUITAR
|
||||
case 0x07: // XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE
|
||||
case 0x0B: // XINPUT_DEVSUBTYPE_GUITAR_BASS
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_GUITAR;
|
||||
break;
|
||||
case 0x08: // XINPUT_DEVSUBTYPE_DRUM_KIT
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_DRUM_KIT;
|
||||
break;
|
||||
case 0x13: // XINPUT_DEVSUBTYPE_ARCADE_PAD
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_ARCADE_PAD;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
device->guid.data[15] = ctx->capabilities.subType;
|
||||
unsigned char buf[20];
|
||||
int ret = libusb_ctx->control_transfer(handle, 0xC1, 0x01, 0x100, 0x0, buf, sizeof(buf), 100);
|
||||
if (ret == sizeof(buf)) {
|
||||
ctx->capabilities.flags = LOAD16(buf[18], buf[19]);
|
||||
ctx->capabilities.gamepad.wButtons = LOAD16(buf[2], buf[3]);
|
||||
ctx->capabilities.gamepad.bLeftTrigger = buf[4];
|
||||
ctx->capabilities.gamepad.bRightTrigger = buf[5];
|
||||
ctx->capabilities.gamepad.sThumbLX = LOAD16(buf[6], buf[7]);
|
||||
ctx->capabilities.gamepad.sThumbLY = LOAD16(buf[8], buf[9]);
|
||||
ctx->capabilities.gamepad.sThumbRX = LOAD16(buf[10], buf[11]);
|
||||
ctx->capabilities.gamepad.sThumbRY = LOAD16(buf[12], buf[13]);
|
||||
}
|
||||
ret = libusb_ctx->control_transfer(handle, 0xC1, 0x01, 0x00, 0x0, buf, 8, 100);
|
||||
if (ret == 8) {
|
||||
ctx->capabilities.vibration.wLeftMotorSpeed = buf[3] << 8;
|
||||
ctx->capabilities.vibration.wRightMotorSpeed = buf[4] << 8;
|
||||
}
|
||||
#ifdef DEBUG_XBOX_PROTOCOL
|
||||
SDL_Log("Xbox 360 capabilities:");
|
||||
SDL_Log(" type: %02x", ctx->capabilities.type);
|
||||
SDL_Log(" subType: %02x", ctx->capabilities.subType);
|
||||
SDL_Log(" flags: %04x", ctx->capabilities.flags);
|
||||
SDL_Log(" wButtons: %02x", ctx->capabilities.gamepad.wButtons);
|
||||
SDL_Log(" bLeftTrigger: %02x", ctx->capabilities.gamepad.bLeftTrigger);
|
||||
SDL_Log(" bRightTrigger: %02x", ctx->capabilities.gamepad.bRightTrigger);
|
||||
SDL_Log(" sThumbLX: %02x", ctx->capabilities.gamepad.sThumbLX);
|
||||
SDL_Log(" sThumbLY: %02x", ctx->capabilities.gamepad.sThumbLY);
|
||||
SDL_Log(" sThumbRX: %02x", ctx->capabilities.gamepad.sThumbRX);
|
||||
SDL_Log(" sThumbRY: %02x", ctx->capabilities.gamepad.sThumbRY);
|
||||
SDL_Log(" wLeftMotorSpeed: %02x", ctx->capabilities.vibration.wLeftMotorSpeed);
|
||||
SDL_Log(" wRightMotorSpeed: %02x", ctx->capabilities.vibration.wRightMotorSpeed);
|
||||
#endif
|
||||
}
|
||||
SDL_QuitLibUSB();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool HIDAPI_DriverXbox360_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
|
||||
{
|
||||
const int XB360W_IFACE_PROTOCOL = 129; // Wireless
|
||||
@@ -227,7 +327,9 @@ static bool HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy
|
||||
joystick->nbuttons = 11;
|
||||
joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
|
||||
joystick->nhats = 1;
|
||||
|
||||
#ifdef HAVE_LIBUSB
|
||||
FetchXInputCapabilities(device);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
27
src/joystick/hidapi/SDL_hidapi_xbox360.h
Normal file
27
src/joystick/hidapi/SDL_hidapi_xbox360.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#define FLAG_FORCE_FEEDBACK 0x01
|
||||
#define FLAG_WIRELESS 0x02
|
||||
#define FLAG_VOICE 0x04
|
||||
#define FLAG_PLUGIN_MODULES 0x08
|
||||
#define FLAG_NO_NAVIGATION 0x10
|
||||
|
||||
typedef struct {
|
||||
uint8_t type;
|
||||
uint8_t subType;
|
||||
uint16_t flags;
|
||||
struct {
|
||||
uint16_t wButtons;
|
||||
uint8_t bLeftTrigger;
|
||||
uint8_t bRightTrigger;
|
||||
int16_t sThumbLX;
|
||||
int16_t sThumbLY;
|
||||
int16_t sThumbRX;
|
||||
int16_t sThumbRY;
|
||||
} gamepad;
|
||||
|
||||
struct {
|
||||
uint16_t wLeftMotorSpeed;
|
||||
uint16_t wRightMotorSpeed;
|
||||
} vibration;
|
||||
} SDL_xinput_capabilities;
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "../SDL_sysjoystick.h"
|
||||
#include "SDL_hidapijoystick_c.h"
|
||||
#include "SDL_hidapi_rumble.h"
|
||||
#include "SDL_hidapi_xbox360.h"
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
|
||||
|
||||
@@ -38,6 +39,7 @@ typedef struct
|
||||
bool connected;
|
||||
int player_index;
|
||||
bool player_lights;
|
||||
SDL_xinput_capabilities capabilities;
|
||||
Uint8 last_state[USB_PACKET_LENGTH];
|
||||
} SDL_DriverXbox360W_Context;
|
||||
|
||||
@@ -179,6 +181,18 @@ static bool HIDAPI_DriverXbox360W_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Jo
|
||||
joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
|
||||
joystick->nhats = 1;
|
||||
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;
|
||||
ctx->capabilities.type = 1;
|
||||
ctx->capabilities.flags = FLAG_WIRELESS;
|
||||
ctx->capabilities.subType = SDL_JOYSTICK_TYPE_GAMEPAD;
|
||||
ctx->capabilities.gamepad.wButtons = 0xFFFF;
|
||||
ctx->capabilities.gamepad.bLeftTrigger = 0xFF;
|
||||
ctx->capabilities.gamepad.bRightTrigger = 0xFF;
|
||||
ctx->capabilities.gamepad.sThumbLX = 0xFFC0;
|
||||
ctx->capabilities.gamepad.sThumbLY = 0xFFC0;
|
||||
ctx->capabilities.gamepad.sThumbRX = 0xFFC0;
|
||||
ctx->capabilities.gamepad.sThumbRY = 0xFFC0;
|
||||
ctx->capabilities.vibration.wLeftMotorSpeed = 0xFFFF;
|
||||
ctx->capabilities.vibration.wRightMotorSpeed = 0xFFFF;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -328,6 +342,44 @@ static bool HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
if (joystick) {
|
||||
UpdatePowerLevel(joystick, data[17]);
|
||||
}
|
||||
ctx->capabilities.type = 1;
|
||||
ctx->capabilities.subType = data[25] & 0x7f;
|
||||
if ((data[25] & 0x80) != 0) {
|
||||
ctx->capabilities.flags |= FLAG_FORCE_FEEDBACK;
|
||||
}
|
||||
switch (data[25] & 0x7f) {
|
||||
case 0x01: // XINPUT_DEVSUBTYPE_GAMEPAD
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_GAMEPAD;
|
||||
break;
|
||||
case 0x02: // XINPUT_DEVSUBTYPE_WHEEL
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_WHEEL;
|
||||
break;
|
||||
case 0x03: // XINPUT_DEVSUBTYPE_ARCADE_STICK
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_ARCADE_STICK;
|
||||
break;
|
||||
case 0x04: // XINPUT_DEVSUBTYPE_FLIGHT_STICK
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_FLIGHT_STICK;
|
||||
break;
|
||||
case 0x05: // XINPUT_DEVSUBTYPE_DANCE_PAD
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_DANCE_PAD;
|
||||
break;
|
||||
case 0x06: // XINPUT_DEVSUBTYPE_GUITAR
|
||||
case 0x07: // XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE
|
||||
case 0x0B: // XINPUT_DEVSUBTYPE_GUITAR_BASS
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_GUITAR;
|
||||
break;
|
||||
case 0x08: // XINPUT_DEVSUBTYPE_DRUM_KIT
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_DRUM_KIT;
|
||||
break;
|
||||
case 0x13: // XINPUT_DEVSUBTYPE_ARCADE_PAD
|
||||
device->joystick_type = SDL_JOYSTICK_TYPE_ARCADE_PAD;
|
||||
break;
|
||||
}
|
||||
device->guid.data[15] = ctx->capabilities.subType;
|
||||
const Uint8 capabilities_packet[] = { 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
if (SDL_hid_write(device->dev, capabilities_packet, sizeof(capabilities_packet)) != sizeof(capabilities_packet)) {
|
||||
SDL_SetError("Couldn't write capabilities_packet packet");
|
||||
}
|
||||
} else if (size == 29 && data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x00 && data[3] == 0x13) {
|
||||
#ifdef DEBUG_JOYSTICK
|
||||
SDL_Log("Battery status: %d", data[4]);
|
||||
@@ -335,6 +387,32 @@ static bool HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device *device)
|
||||
if (joystick) {
|
||||
UpdatePowerLevel(joystick, data[4]);
|
||||
}
|
||||
} else if (data[0] == 0x00 && data[1] == 0x05 && data[5] == 0x12) {
|
||||
ctx->capabilities.gamepad.wButtons = LOAD16(data[6], data[7]);
|
||||
ctx->capabilities.gamepad.bLeftTrigger = data[8];
|
||||
ctx->capabilities.gamepad.bRightTrigger = data[9];
|
||||
ctx->capabilities.gamepad.sThumbLX = LOAD16(data[10], data[11]);
|
||||
ctx->capabilities.gamepad.sThumbLY = LOAD16(data[12], data[13]);
|
||||
ctx->capabilities.gamepad.sThumbRX = LOAD16(data[14], data[15]);
|
||||
ctx->capabilities.gamepad.sThumbRY = LOAD16(data[16], data[17]);
|
||||
ctx->capabilities.flags |= data[20];
|
||||
ctx->capabilities.vibration.wLeftMotorSpeed = data[18] << 8;
|
||||
ctx->capabilities.vibration.wRightMotorSpeed = data[19] << 8;
|
||||
#ifdef DEBUG_XBOX_PROTOCOL
|
||||
SDL_Log("Xbox 360 capabilities:");
|
||||
SDL_Log(" type: %02x", ctx->capabilities.type);
|
||||
SDL_Log(" subType: %02x", ctx->capabilities.subType);
|
||||
SDL_Log(" flags: %02x", ctx->capabilities.flags);
|
||||
SDL_Log(" wButtons: %02x", ctx->capabilities.gamepad.wButtons);
|
||||
SDL_Log(" bLeftTrigger: %02x", ctx->capabilities.gamepad.bLeftTrigger);
|
||||
SDL_Log(" bRightTrigger: %02x", ctx->capabilities.gamepad.bRightTrigger);
|
||||
SDL_Log(" sThumbLX: %02x", ctx->capabilities.gamepad.sThumbLX);
|
||||
SDL_Log(" sThumbLY: %02x", ctx->capabilities.gamepad.sThumbLY);
|
||||
SDL_Log(" sThumbRX: %02x", ctx->capabilities.gamepad.sThumbRX);
|
||||
SDL_Log(" sThumbRY: %02x", ctx->capabilities.gamepad.sThumbRY);
|
||||
SDL_Log(" wLeftMotorSpeed: %02x", ctx->capabilities.vibration.wLeftMotorSpeed);
|
||||
SDL_Log(" wRightMotorSpeed: %02x", ctx->capabilities.vibration.wRightMotorSpeed);
|
||||
#endif
|
||||
} else if (size == 29 && data[0] == 0x00 && (data[1] & 0x01) == 0x01) {
|
||||
if (joystick) {
|
||||
HIDAPI_DriverXbox360W_HandleStatePacket(joystick, device->dev, ctx, data + 4, size - 4);
|
||||
|
||||
Reference in New Issue
Block a user