Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterfa
final int XB360W_IFACE_PROTOCOL = 129; // Wireless
final int[] SUPPORTED_VENDORS = {
0x0079, // GPD Win 2
0x0351, // CRKD
0x044f, // Thrustmaster
0x045e, // Microsoft
0x046d, // Logitech
Expand All @@ -243,6 +244,7 @@ private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterfa
0x0f0d, // Hori
0x1038, // SteelSeries
0x11c9, // Nacon
0x1209, // Generic
0x12ab, // Unknown
0x1430, // RedOctane
0x146b, // BigBen
Expand All @@ -257,7 +259,9 @@ private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterfa
0x2c22, // Qanba
0x2dc8, // 8BitDo
0x3537, // GameSir
0x3651, // CRKD
0x37d7, // Flydigi
0x3958, // Red Octane Games
0x9886, // ASTRO Gaming
};

Expand All @@ -279,6 +283,7 @@ private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterfa
final int XB1_IFACE_SUBCLASS = 71;
final int XB1_IFACE_PROTOCOL = 208;
final int[] SUPPORTED_VENDORS = {
0x0351, // CRKD
0x03f0, // HP
0x044f, // Thrustmaster
0x045e, // Microsoft
Expand All @@ -287,6 +292,7 @@ private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterfa
0x0e6f, // PDP
0x0f0d, // Hori
0x10f5, // Turtle Beach
0x1209, // Generic
0x1532, // Razer Wildcat
0x20d6, // PowerA
0x24c6, // PowerA
Expand All @@ -296,7 +302,9 @@ private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterfa
0x2e95, // SCUF
0x3285, // Nacon
0x3537, // GameSir
0x3651, // CRKD
0x366c, // ByoWave
0x3958, // Red Octane Games
};

if (usbInterface.getId() == 0 &&
Expand Down
3 changes: 2 additions & 1 deletion include/SDL3/SDL_hints.h
Original file line number Diff line number Diff line change
Expand Up @@ -2291,7 +2291,8 @@ extern "C" {
* - "0": Assume this is a generic controller.
* - "1": Reset the controller to get metadata.
*
* By default the controller is not reset.
* By default the controller is reset. This is so we can properly detect
* the controller type.
*
* This hint should be set before initializing joysticks and gamepads.
*
Expand Down
8 changes: 8 additions & 0 deletions src/hidapi/libusb/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,7 @@ static int is_xbox360(unsigned short vendor_id, const struct libusb_interface_de
static const int xb360w_iface_protocol = 129; /* Wireless */
static const int supported_vendors[] = {
0x0079, /* GPD Win 2 */
0x0351, /* CRKD */
0x044f, /* Thrustmaster */
0x045e, /* Microsoft */
0x046d, /* Logitech */
Expand All @@ -913,6 +914,7 @@ static int is_xbox360(unsigned short vendor_id, const struct libusb_interface_de
0x0f0d, /* Hori */
0x1038, /* SteelSeries */
0x11c9, /* Nacon */
0x1209, /* Generic */
0x12ab, /* Unknown */
0x1430, /* RedOctane */
0x146b, /* BigBen */
Expand All @@ -927,7 +929,9 @@ static int is_xbox360(unsigned short vendor_id, const struct libusb_interface_de
0x2c22, /* Qanba */
0x2dc8, /* 8BitDo */
0x3537, /* GameSir */
0x3651, /* CRKD */
0x37d7, /* Flydigi */
0x3958, /* Red Octane Games */
0x9886, /* ASTRO Gaming */
};

Expand All @@ -950,6 +954,7 @@ static int is_xboxone(unsigned short vendor_id, const struct libusb_interface_de
static const int xb1_iface_subclass = 71;
static const int xb1_iface_protocol = 208;
static const int supported_vendors[] = {
0x0351, /* CRKD */
0x03f0, /* HP */
0x044f, /* Thrustmaster */
0x045e, /* Microsoft */
Expand All @@ -958,6 +963,7 @@ static int is_xboxone(unsigned short vendor_id, const struct libusb_interface_de
0x0e6f, /* PDP */
0x0f0d, /* Hori */
0x10f5, /* Turtle Beach */
0x1209, /* Generic */
0x1532, /* Razer Wildcat */
0x20d6, /* PowerA */
0x24c6, /* PowerA */
Expand All @@ -967,7 +973,9 @@ static int is_xboxone(unsigned short vendor_id, const struct libusb_interface_de
0x2e95, /* SCUF */
0x3285, /* Nacon */
0x3537, /* GameSir */
0x3651, /* CRKD */
0x366c, /* ByoWave */
0x3958, /* Red Octane Games */
};

if (intf_desc->bInterfaceNumber == 0 &&
Expand Down
43 changes: 42 additions & 1 deletion src/joystick/gdk/SDL_gameinputjoystick.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,46 @@ static bool GAMEINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool ena
joystick->hwdata->report_sensors = enabled;
return true;
}

static void GAMEINPUT_DrumUpdate(SDL_Joystick *joystick, IGameInputReading *reading, Uint64 timestamp)
{
#if GAMEINPUT_API_VERSION >= 3
IGameInputRawDeviceReport *rawState;
if (reading->GetRawReport(&rawState)) {
uint8_t rawData[40];
SDL_memset(rawData, 0, sizeof(rawData));
size_t len = rawState->GetRawData(sizeof(rawData), rawData);
Uint8 hat = 0;
uint16_t buttons = rawData[0] | rawData[1] << 8;
if (len >= 6) {
if ((buttons & 0x0100) || (rawData[4] & 0xf0)) {
hat |= SDL_HAT_UP;
}
if ((buttons & 0x0200) || (rawData[4] & 0x0f)) {
hat |= SDL_HAT_DOWN;
}
if (buttons & 0x0400) {
hat |= SDL_HAT_LEFT;
}
if (buttons & 0x0800) {
hat |= SDL_HAT_RIGHT;
}
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
// The rest of the instruments are mapped to PS3 style inputs
// The PS3 used flags for dictating if a pad or a cymbal was hit, so we emulate that here
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, (buttons & 0x0008));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, (buttons & 0x0004));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, (buttons & 0x1000));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, (buttons & 0x2000));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, (rawData[3] & 0x0f) || (rawData[5] & 0xf0) || (buttons & 0x0010));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, (rawData[2] & 0xf0) || (buttons & 0x0020));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, (rawData[3] & 0xf0) || (rawData[4] & 0x0f) || (buttons & 0x0040));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, (rawData[2] & 0x0f) || (rawData[4] & 0xf0) || (buttons & 0x0080));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, rawData[2] || rawData[3]);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, rawData[4] || rawData[5]);
}
}
#endif // GAMEINPUT_API_VERSION >= 3
}
static void GAMEINPUT_GuitarUpdate(SDL_Joystick *joystick, IGameInputReading *reading, Uint64 timestamp)
{
#if GAMEINPUT_API_VERSION >= 3
Expand Down Expand Up @@ -916,6 +955,8 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
timestamp = SDL_US_TO_NS(reading->GetTimestamp() + g_GameInputTimestampOffset);
if (internal_device->raw_type == SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR) {
GAMEINPUT_GuitarUpdate(joystick, reading, timestamp);
} else if (internal_device->raw_type == SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_DRUM_KIT) {
GAMEINPUT_DrumUpdate(joystick, reading, timestamp);
} else if (GAMEINPUT_InternalIsGamepad(info)) {
GAMEINPUT_GamepadUpdate(joystick, reading, timestamp);
} else {
Expand Down
151 changes: 149 additions & 2 deletions src/joystick/hidapi/SDL_hidapi_gip.c
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ typedef enum
GIP_TYPE_NAVIGATION_CONTROLLER = 4,
GIP_TYPE_CHATPAD = 5,
GIP_TYPE_HEADSET = 6,
GIP_TYPE_GUITAR = 7,
GIP_TYPE_DRUM_KIT = 8,
GIP_TYPE_LIVE_GUITAR = 9,
} GIP_AttachmentType;

typedef enum
Expand Down Expand Up @@ -304,6 +307,11 @@ MAKE_GUID(GUID_IEliteButtons, 0x37d19ff7, 0xb5c6, 0x49d1, 0xa7, 0x5e, 0x03, 0xb2
MAKE_GUID(GUID_IGamepad, 0x082e402c, 0x07df, 0x45e1, 0xa5, 0xab, 0xa3, 0x12, 0x7a, 0xf1, 0x97, 0xb5);
MAKE_GUID(GUID_NavigationController, 0xb8f31fe7, 0x7386, 0x40e9, 0xa9, 0xf8, 0x2f, 0x21, 0x26, 0x3a, 0xcf, 0xb7);
MAKE_GUID(GUID_Wheel, 0x646979cf, 0x6b71, 0x4e96, 0x8d, 0xf9, 0x59, 0xe3, 0x98, 0xd7, 0x42, 0x0c);
MAKE_GUID(GUID_PDP_Guitar, 0x1a266af6, 0x3a46, 0x45e3, 0xb9, 0xb6, 0x0f, 0x2c, 0x0b, 0x2c, 0x1e, 0xbe);
MAKE_GUID(GUID_Madcatz_Guitar, 0x0d2ae438, 0x7f7d, 0x4933, 0x86, 0x93, 0x30, 0xfc, 0x55, 0x01, 0x8e, 0x77);
MAKE_GUID(GUID_PDP_DrumKit, 0xa503f9b0, 0x955e, 0x47c4, 0xa2, 0xed, 0xb1, 0x33, 0x6f, 0xa7, 0x70, 0x3e);
MAKE_GUID(GUID_Madcatz_DrumKit, 0x06182893, 0xCCE0, 0x4B85, 0x92, 0x71, 0x0A, 0x10, 0xDB, 0xAB, 0x7E, 0x07);
MAKE_GUID(GUID_GuitarHero_Live_Guitar, 0xfd12fdd9, 0x8e73, 0x47c7, 0xa2, 0x31, 0x96, 0x26, 0x8c, 0x38, 0x00, 0x9a);

/*
* The following GUIDs are observed, but the exact meanings aren't known, so
Expand Down Expand Up @@ -1258,6 +1266,9 @@ static bool GIP_EnsureMetadata(GIP_Attachment *attachment)
attachment->metadata_retries = 0;
return GIP_SendSystemMessage(attachment, GIP_CMD_METADATA, 0, NULL, 0);
} else {
if (attachment->device->reset_for_metadata) {
return true;
}
return GIP_SetMetadataDefaults(attachment);
}
default:
Expand Down Expand Up @@ -1592,6 +1603,31 @@ static bool GIP_HandleCommandMetadataRespose(
expected_guid = &GUID_IHeadset;
break;
}
if (SDL_strcmp(type, "Activision.Xbox.Input.GH7") == 0) {
attachment->attachment_type = GIP_TYPE_LIVE_GUITAR;
expected_guid = &GUID_GuitarHero_Live_Guitar;
break;
}
if (SDL_strcmp(type, "MadCatz.Xbox.Guitar.Stratocaster") == 0) {
attachment->attachment_type = GIP_TYPE_GUITAR;
expected_guid = &GUID_Madcatz_Guitar;
break;
}
if (SDL_strcmp(type, "PDP.Xbox.Guitar.Jaguar") == 0) {
attachment->attachment_type = GIP_TYPE_GUITAR;
expected_guid = &GUID_PDP_Guitar;
break;
}
if (SDL_strcmp(type, "MadCatz.Xbox.Drums.Glam") == 0) {
attachment->attachment_type = GIP_TYPE_DRUM_KIT;
expected_guid = &GUID_Madcatz_DrumKit;
break;
}
if (SDL_strcmp(type, "PDP.Xbox.Drums.Tablah") == 0) {
attachment->attachment_type = GIP_TYPE_DRUM_KIT;
expected_guid = &GUID_PDP_DrumKit;
break;
}
}

found_expected_guid = !expected_guid;
Expand Down Expand Up @@ -2027,6 +2063,99 @@ static void GIP_HandleGamepadReport(
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~axis);
}

static void GIP_HandleGuitarReport(
GIP_Attachment *attachment,
SDL_Joystick *joystick,
Uint64 timestamp,
const Uint8 *bytes,
int num_bytes)
{
static uint16_t s_GuitarButtons[] = {
0x0010, // SDL_GAMEPAD_BUTTON_SOUTH
0x0020, // SDL_GAMEPAD_BUTTON_EAST
0x0040, // SDL_GAMEPAD_BUTTON_WEST
0x0080, // SDL_GAMEPAD_BUTTON_NORTH
0x0008, // SDL_GAMEPAD_BUTTON_BACK
0, // The guide button is not available
0x0004, // SDL_GAMEPAD_BUTTON_START
0, // right joystick click unavailable
0x4000, // SDL_GAMEPAD_BUTTON_RIGHT_STICK
0x1000, // SDL_GAMEPAD_BUTTON_LEFT_SHOULDER
0, // right shoulder unavailable
};
Uint8 btnidx = 0, hat = 0;
uint16_t buttons = bytes[0] | bytes[1] << 8;
if (num_bytes >= 10) {
for (btnidx = 0; btnidx < SDL_arraysize(s_GuitarButtons); ++btnidx) {
uint16_t button_mask = s_GuitarButtons[btnidx];
if (!button_mask) {
continue;
}
bool down = ((buttons & button_mask) != 0);
SDL_SendJoystickButton(timestamp, joystick, btnidx, down);
}
if (buttons & 0x0100) {
hat |= SDL_HAT_UP;
}
if (buttons & 0x0200) {
hat |= SDL_HAT_DOWN;
}
if (buttons & 0x0400) {
hat |= SDL_HAT_LEFT;
}
if (buttons & 0x0800) {
hat |= SDL_HAT_RIGHT;
}
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, (bytes[3] * 257) - 32768);
// PS3 RB guitars had tilt on right shoulder
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, bytes[2] >= 0xD0);
// PS3 RB guitars send L2 when using solo buttons
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, (bytes[6]) ? 32767 : -32768);
// Align pickup selector mappings with PS3 instruments
static const Sint16 effects_mappings[] = {-26880, -13568, -1792, 11008, 24576};
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, effects_mappings[bytes[4] >> 4]);
}
}

static void GIP_HandleDrumKitReport(
GIP_Attachment *attachment,
SDL_Joystick *joystick,
Uint64 timestamp,
const Uint8 *bytes,
int num_bytes)
{
Uint8 hat = 0;
uint16_t buttons = bytes[0] | bytes[1] << 8;
if (num_bytes >= 6) {
if ((buttons & 0x0100) || (bytes[4] & 0xf0)) {
hat |= SDL_HAT_UP;
}
if ((buttons & 0x0200) || (bytes[4] & 0x0f)) {
hat |= SDL_HAT_DOWN;
}
if (buttons & 0x0400) {
hat |= SDL_HAT_LEFT;
}
if (buttons & 0x0800) {
hat |= SDL_HAT_RIGHT;
}
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
// The rest of the instruments are mapped to PS3 style inputs
// The PS3 used flags for dictating if a pad or a cymbal was hit, so we emulate that here
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, (buttons & 0x0008));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, (buttons & 0x0004));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, (buttons & 0x1000));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, (buttons & 0x2000));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, (bytes[3] & 0x0f) || (bytes[5] & 0xf0) || (buttons & 0x0010));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, (bytes[2] & 0xf0) || (buttons & 0x0020));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, (bytes[3] & 0xf0) || (bytes[4] & 0x0f) || (buttons & 0x0040));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, (bytes[2] & 0x0f) || (bytes[4] & 0xf0) || (buttons & 0x0080));
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, bytes[2] || bytes[3]);
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, bytes[4] || bytes[5]);
}
}

static void GIP_HandleArcadeStickReport(
GIP_Attachment *attachment,
SDL_Joystick *joystick,
Expand Down Expand Up @@ -2149,7 +2278,7 @@ static bool GIP_HandleLLInputReport(
return true;
}

if (num_bytes < 14) {
if (num_bytes < 6) {
SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "GIP: Discarding too-short input report");
return false;
}
Expand All @@ -2167,6 +2296,12 @@ static bool GIP_HandleLLInputReport(
case GIP_TYPE_FLIGHT_STICK:
GIP_HandleFlightStickReport(attachment, joystick, timestamp, bytes, num_bytes);
break;
case GIP_TYPE_GUITAR:
GIP_HandleGuitarReport(attachment, joystick, timestamp, bytes, num_bytes);
break;
case GIP_TYPE_DRUM_KIT:
GIP_HandleDrumKitReport(attachment, joystick, timestamp, bytes, num_bytes);
break;
}

if (attachment->features & GIP_FEATURE_ELITE_BUTTONS) {
Expand Down Expand Up @@ -2654,7 +2789,7 @@ static bool HIDAPI_DriverGIP_InitDevice(SDL_HIDAPI_Device *device)
return false;
}
ctx->device = device;
ctx->reset_for_metadata = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_GIP_RESET_FOR_METADATA, false);
ctx->reset_for_metadata = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_GIP_RESET_FOR_METADATA, true);

attachment = GIP_EnsureAttachment(ctx, 0);
GIP_HandleQuirks(attachment);
Expand Down Expand Up @@ -2739,6 +2874,18 @@ static bool HIDAPI_DriverGIP_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystic
joystick->naxes += attachment->extra_axes - 1;
}

if (attachment->attachment_type == GIP_TYPE_GUITAR) {
device->joystick_type = SDL_JOYSTICK_TYPE_GUITAR;
}

if (attachment->attachment_type == GIP_TYPE_DRUM_KIT) {
device->joystick_type = SDL_JOYSTICK_TYPE_DRUM_KIT;
}

if (attachment->attachment_type == GIP_TYPE_LIVE_GUITAR) {
device->joystick_type = SDL_JOYSTICK_TYPE_GUITAR;
}

joystick->nhats = 1;

return true;
Expand Down
Loading