diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360.c b/src/joystick/hidapi/SDL_hidapi_xbox360.c index 208771e5f9..b3393c4cf8 100644 --- a/src/joystick/hidapi/SDL_hidapi_xbox360.c +++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c @@ -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; } diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360.h b/src/joystick/hidapi/SDL_hidapi_xbox360.h new file mode 100644 index 0000000000..df866b36d2 --- /dev/null +++ b/src/joystick/hidapi/SDL_hidapi_xbox360.h @@ -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; \ No newline at end of file diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360w.c b/src/joystick/hidapi/SDL_hidapi_xbox360w.c index 57206bb8f8..81ea01e67c 100644 --- a/src/joystick/hidapi/SDL_hidapi_xbox360w.c +++ b/src/joystick/hidapi/SDL_hidapi_xbox360w.c @@ -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);