From 7f86f9107d0270f963a7896180e80dc6ed28ea9e Mon Sep 17 00:00:00 2001
From: Sanjay Govind <[EMAIL REDACTED]>
Date: Sat, 4 Apr 2026 12:02:14 +1300
Subject: [PATCH] Add support for GIP guitars via gameinput (#15301)
---
include/SDL3/SDL_hints.h | 20 ++
src/joystick/gdk/SDL_gameinputjoystick.cpp | 370 +++++++++++++++------
src/joystick/usb_ids.h | 8 +
3 files changed, 290 insertions(+), 108 deletions(-)
diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index b02b13b8f4b42..0c1a19d36d688 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -1444,6 +1444,26 @@ extern "C" {
*/
#define SDL_HINT_JOYSTICK_GAMEINPUT "SDL_JOYSTICK_GAMEINPUT"
+/**
+ * A variable controlling whether GameInput should be used for handling
+ * GIP devices that require raw report processing, but aren't supported
+ * by HIDRAW, such as Xbox One Guitars.
+ *
+ * Note that this is only supported with GameInput 3 or newer.
+ *
+ * The variable can be set to the following values:
+ *
+ * - "0": GameInput is not used to handle raw GIP devices.
+ * - "1": GameInput is used.
+ *
+ * The default is "1" when using GameInput 3 or newer, and is "0" otherwise.
+ *
+ * This hint should be set before SDL is initialized.
+ *
+ * \since This hint is available since SDL 3.4.4.
+ */
+#define SDL_HINT_JOYSTICK_GAMEINPUT_RAW "SDL_JOYSTICK_GAMEINPUT_RAW"
+
/**
* A variable containing a list of devices known to have a GameCube form
* factor.
diff --git a/src/joystick/gdk/SDL_gameinputjoystick.cpp b/src/joystick/gdk/SDL_gameinputjoystick.cpp
index d60251a71e00a..5d373a25504b5 100644
--- a/src/joystick/gdk/SDL_gameinputjoystick.cpp
+++ b/src/joystick/gdk/SDL_gameinputjoystick.cpp
@@ -44,6 +44,15 @@ enum
SDL_GAMEPAD_BUTTON_GAMEINPUT_SHARE = 11
};
+enum
+{
+ SDL_GAMEINPUT_RAWTYPE_NONE,
+ SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR,
+ SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_DRUM_KIT,
+ SDL_GAMEINPUT_RAWTYPE_GUITAR_HERO_LIVE_GUITAR,
+ SDL_GAMEINPUT_RAWTYPE_LEGACY_ADAPTER,
+};
+
typedef struct GAMEINPUT_InternalDevice
{
IGameInputDevice *device;
@@ -52,6 +61,7 @@ typedef struct GAMEINPUT_InternalDevice
SDL_GUID guid; // generated by SDL
SDL_JoystickID device_instance; // generated by SDL
const GameInputDeviceInfo *info;
+ int raw_type;
int steam_virtual_gamepad_slot;
bool isAdded;
bool isDeleteRequested;
@@ -89,9 +99,66 @@ static bool GAMEINPUT_InternalIsGamepad(const GameInputDeviceInfo *info)
return false;
}
+static Uint8 GAMEINPUT_GetDeviceRawType(const GameInputDeviceInfo *info)
+{
+#if GAMEINPUT_API_VERSION >= 3
+ GameInputKind supportedInput = info->supportedInput;
+ if (supportedInput & GameInputKindRawDeviceReport) {
+ switch (info->vendorId) {
+ case USB_VENDOR_MADCATZ:
+ switch (info->productId) {
+ case USB_PRODUCT_MADCATZ_XB1_STRATOCASTER_GUITAR:
+ return SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR;
+ case USB_PRODUCT_MADCATZ_XB1_DRUM_KIT:
+ return SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_DRUM_KIT;
+ case USB_PRODUCT_MADCATZ_XB1_LEGACY_ADAPTER:
+ return SDL_GAMEINPUT_RAWTYPE_LEGACY_ADAPTER;
+ default:
+ break;
+ }
+ break;
+ case USB_VENDOR_PDP:
+ switch (info->productId) {
+ case USB_PRODUCT_PDP_XB1_JAGUAR_GUITAR:
+ case USB_PRODUCT_PDP_XB1_RIFFMASTER_GUITAR:
+ return SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR;
+ case USB_PRODUCT_PDP_XB1_DRUM_KIT:
+ return SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_DRUM_KIT;
+ default:
+ break;
+ }
+ break;
+ case USB_VENDOR_CRKD:
+ switch (info->productId) {
+ case USB_PRODUCT_PDP_XB1_JAGUAR_GUITAR:
+ return SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR;
+ default:
+ break;
+ }
+ break;
+ case USB_VENDOR_RED_OCTANE:
+ switch (info->productId) {
+ case USB_PRODUCT_RED_OCTANE_XB1_GUITAR_HERO_LIVE_GUITAR:
+ return SDL_GAMEINPUT_RAWTYPE_GUITAR_HERO_LIVE_GUITAR;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+#endif // GAMEINPUT_API_VERSION >= 3
+ return SDL_GAMEINPUT_RAWTYPE_NONE;
+}
static Uint8 GAMEINPUT_GetDeviceSubtype(const GameInputDeviceInfo *info)
{
GameInputKind supportedInput = info->supportedInput;
+ Uint8 rawType = GAMEINPUT_GetDeviceRawType(info);
+ if (rawType == SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR || rawType == SDL_GAMEINPUT_RAWTYPE_GUITAR_HERO_LIVE_GUITAR) {
+ return SDL_JOYSTICK_TYPE_GUITAR;
+ }
+ if (rawType == SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_DRUM_KIT) {
+ return SDL_JOYSTICK_TYPE_DRUM_KIT;
+ }
if (supportedInput & GameInputKindRacingWheel) {
return SDL_JOYSTICK_TYPE_WHEEL;
}
@@ -132,6 +199,7 @@ static bool GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
const char *product_string = NULL;
Uint8 driver_signature = 'g';
Uint8 subtype = 0;
+ int raw_type = SDL_GAMEINPUT_RAWTYPE_NONE;
char tmp[4];
int idx = 0;
@@ -154,6 +222,7 @@ static bool GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
product = info->productId;
//version = (info->firmwareVersion.major << 8) | info->firmwareVersion.minor;
subtype = GAMEINPUT_GetDeviceSubtype(info);
+ raw_type = GAMEINPUT_GetDeviceRawType(info);
#if GAMEINPUT_API_VERSION >= 1
if (info->displayName) {
@@ -177,7 +246,7 @@ static bool GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
}
#endif
- if (!GAMEINPUT_InternalIsGamepad(info)) {
+ if (!GAMEINPUT_InternalIsGamepad(info) && raw_type == SDL_GAMEINPUT_RAWTYPE_NONE) {
#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_DIRECTINPUT, true) || SDL_XINPUT_Enabled()) {
// Let other backends handle non-gamepad controllers to possibly avoid bugs and/or regressions.
@@ -226,6 +295,7 @@ static bool GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
elem->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, NULL, product_string, driver_signature, subtype);
elem->device_instance = SDL_GetNextObjectID();
elem->info = info;
+ elem->raw_type = raw_type;
#if GAMEINPUT_API_VERSION >= 1
elem->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot(info->pnpPath);
#else
@@ -318,11 +388,20 @@ static void CALLBACK GAMEINPUT_InternalJoystickDeviceCallback(
static void GAMEINPUT_JoystickDetect(void);
static void GAMEINPUT_JoystickQuit(void);
+static bool GAMEINPUT_IsRawGameInputEnabled(void)
+{
+#if GAMEINPUT_API_VERSION >= 3
+ return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_GAMEINPUT_RAW, true);
+#else
+ return false;
+#endif
+}
+
static bool GAMEINPUT_JoystickInit(void)
{
HRESULT hr;
- if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_GAMEINPUT, SDL_GAMEINPUT_DEFAULT)) {
+ if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_GAMEINPUT, SDL_GAMEINPUT_DEFAULT) && !GAMEINPUT_IsRawGameInputEnabled()) {
return true;
}
@@ -336,8 +415,16 @@ static bool GAMEINPUT_JoystickInit(void)
g_pGameInput->SetFocusPolicy(GameInputEnableBackgroundInput | GameInputEnableBackgroundGuideButton | GameInputEnableBackgroundShareButton);
#endif
+ GameInputKind kind = GameInputKindUnknown;
+ if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_GAMEINPUT, SDL_GAMEINPUT_DEFAULT)) {
+ kind |= GameInputKindController;
+ }
+ if (GAMEINPUT_IsRawGameInputEnabled()) {
+ kind |= GameInputKindRawDeviceReport;
+ }
+
hr = g_pGameInput->RegisterDeviceCallback(NULL,
- GameInputKindController,
+ kind,
GameInputDeviceConnected,
GameInputBlockingEnumeration,
NULL,
@@ -527,7 +614,7 @@ static bool GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index)
hwdata->devref = elem;
joystick->hwdata = hwdata;
- if (GAMEINPUT_InternalIsGamepad(info)) {
+ if (GAMEINPUT_InternalIsGamepad(info) || GAMEINPUT_GetDeviceRawType(info) != SDL_GAMEINPUT_RAWTYPE_NONE) {
joystick->naxes = 6;
joystick->nbuttons = 11;
joystick->nhats = 1;
@@ -612,139 +699,206 @@ static bool GAMEINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool ena
return true;
}
-static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
+static void GAMEINPUT_GuitarUpdate(SDL_Joystick *joystick, IGameInputReading *reading, Uint64 timestamp)
{
- GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata;
- IGameInputDevice *device = hwdata->devref->device;
- const GameInputDeviceInfo *info = hwdata->devref->info;
- IGameInputReading *reading = NULL;
- Uint64 timestamp;
- GameInputGamepadState state;
- HRESULT hr;
-
- hr = g_pGameInput->GetCurrentReading(info->supportedInput, device, &reading);
- if (FAILED(hr)) {
- // don't SetError here since there can be a legitimate case when there's no reading avail
- return;
- }
-
- timestamp = SDL_US_TO_NS(reading->GetTimestamp() + g_GameInputTimestampOffset);
-
- if (GAMEINPUT_InternalIsGamepad(info)) {
- static WORD s_XInputButtons[] = {
- GameInputGamepadA, // SDL_GAMEPAD_BUTTON_SOUTH
- GameInputGamepadB, // SDL_GAMEPAD_BUTTON_EAST
- GameInputGamepadX, // SDL_GAMEPAD_BUTTON_WEST
- GameInputGamepadY, // SDL_GAMEPAD_BUTTON_NORTH
- GameInputGamepadView, // SDL_GAMEPAD_BUTTON_BACK
- 0, // The guide button is not available
- GameInputGamepadMenu, // SDL_GAMEPAD_BUTTON_START
- GameInputGamepadLeftThumbstick, // SDL_GAMEPAD_BUTTON_LEFT_STICK
- GameInputGamepadRightThumbstick, // SDL_GAMEPAD_BUTTON_RIGHT_STICK
- GameInputGamepadLeftShoulder, // SDL_GAMEPAD_BUTTON_LEFT_SHOULDER
- GameInputGamepadRightShoulder, // SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER
+ IGameInputRawDeviceReport* rawState;
+ if (reading->GetRawReport(&rawState)) {
+ static WORD 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;
-
- if (reading->GetGamepadState(&state)) {
- for (btnidx = 0; btnidx < SDL_arraysize(s_XInputButtons); ++btnidx) {
- WORD button_mask = s_XInputButtons[btnidx];
+ uint8_t rawData[40];
+ SDL_memset(rawData, 0, sizeof(rawData));
+ size_t len = rawState->GetRawData(sizeof(rawData), rawData);
+ uint16_t buttons = rawData[0] | rawData[1] << 8;
+ if (len >= 10) {
+ for (btnidx = 0; btnidx < SDL_arraysize(s_GuitarButtons); ++btnidx) {
+ WORD button_mask = s_GuitarButtons[btnidx];
if (!button_mask) {
continue;
}
- bool down = ((state.buttons & button_mask) != 0);
+ bool down = ((buttons & button_mask) != 0);
SDL_SendJoystickButton(timestamp, joystick, btnidx, down);
}
-
- if (state.buttons & GameInputGamepadDPadUp) {
+ if (buttons & 0x0100) {
hat |= SDL_HAT_UP;
}
- if (state.buttons & GameInputGamepadDPadDown) {
+ if (buttons & 0x0200) {
hat |= SDL_HAT_DOWN;
}
- if (state.buttons & GameInputGamepadDPadLeft) {
+ if (buttons & 0x0400) {
hat |= SDL_HAT_LEFT;
}
- if (state.buttons & GameInputGamepadDPadRight) {
+ if (buttons & 0x0800) {
hat |= SDL_HAT_RIGHT;
}
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
+ SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, (rawData[3] * 257) - 32768);
+ // PS3 RB guitars had tilt on right shoulder
+ SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, rawData[2] >= 0xD0);
+ // PS3 RB guitars send L2 when using solo buttons
+ SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, (rawData[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[rawData[4] >> 4]);
+ }
+ }
+}
+
+static void GAMEINPUT_GamepadUpdate(SDL_Joystick *joystick, IGameInputReading *reading, Uint64 timestamp) {
+ GameInputGamepadState state;
+ static WORD s_XInputButtons[] = {
+ GameInputGamepadA, // SDL_GAMEPAD_BUTTON_SOUTH
+ GameInputGamepadB, // SDL_GAMEPAD_BUTTON_EAST
+ GameInputGamepadX, // SDL_GAMEPAD_BUTTON_WEST
+ GameInputGamepadY, // SDL_GAMEPAD_BUTTON_NORTH
+ GameInputGamepadView, // SDL_GAMEPAD_BUTTON_BACK
+ 0, // The guide button is not available
+ GameInputGamepadMenu, // SDL_GAMEPAD_BUTTON_START
+ GameInputGamepadLeftThumbstick, // SDL_GAMEPAD_BUTTON_LEFT_STICK
+ GameInputGamepadRightThumbstick, // SDL_GAMEPAD_BUTTON_RIGHT_STICK
+ GameInputGamepadLeftShoulder, // SDL_GAMEPAD_BUTTON_LEFT_SHOULDER
+ GameInputGamepadRightShoulder, // SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER
+ };
+ Uint8 btnidx = 0, hat = 0;
+
+ if (reading->GetGamepadState(&state)) {
+ for (btnidx = 0; btnidx < SDL_arraysize(s_XInputButtons); ++btnidx) {
+ WORD button_mask = s_XInputButtons[btnidx];
+ if (!button_mask) {
+ continue;
+ }
+ bool down = ((state.buttons & button_mask) != 0);
+ SDL_SendJoystickButton(timestamp, joystick, btnidx, down);
+ }
+
+ if (state.buttons & GameInputGamepadDPadUp) {
+ hat |= SDL_HAT_UP;
+ }
+ if (state.buttons & GameInputGamepadDPadDown) {
+ hat |= SDL_HAT_DOWN;
+ }
+ if (state.buttons & GameInputGamepadDPadLeft) {
+ hat |= SDL_HAT_LEFT;
+ }
+ if (state.buttons & GameInputGamepadDPadRight) {
+ hat |= SDL_HAT_RIGHT;
+ }
+ SDL_SendJoystickHat(timestamp, joystick, 0, hat);
#define CONVERT_AXIS(v) (Sint16)(((v) < 0.0f) ? ((v)*32768.0f) : ((v)*32767.0f))
- SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, CONVERT_AXIS(state.leftThumbstickX));
- SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, CONVERT_AXIS(-state.leftThumbstickY));
- SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, CONVERT_AXIS(state.rightThumbstickX));
- SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, CONVERT_AXIS(-state.rightThumbstickY));
+ SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, CONVERT_AXIS(state.leftThumbstickX));
+ SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, CONVERT_AXIS(-state.leftThumbstickY));
+ SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, CONVERT_AXIS(state.rightThumbstickX));
+ SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, CONVERT_AXIS(-state.rightThumbstickY));
#undef CONVERT_AXIS
#define CONVERT_TRIGGER(v) (Sint16)((v)*65535.0f - 32768.0f)
- SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, CONVERT_TRIGGER(state.leftTrigger));
- SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, CONVERT_TRIGGER(state.rightTrigger));
+ SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, CONVERT_TRIGGER(state.leftTrigger));
+ SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, CONVERT_TRIGGER(state.rightTrigger));
#undef CONVERT_TRIGGER
+ }
+}
+
+static void GAMEINPUT_ControllerUpdate(SDL_Joystick *joystick, IGameInputReading *reading, Uint64 timestamp)
+{
+ bool *button_state = SDL_stack_alloc(bool, joystick->nbuttons);
+ float *axis_state = SDL_stack_alloc(float, joystick->naxes);
+ GameInputSwitchPosition *switch_state = SDL_stack_alloc(GameInputSwitchPosition, joystick->nhats);
+
+ if (button_state) {
+ uint32_t i;
+ uint32_t button_count = reading->GetControllerButtonState(joystick->nbuttons, button_state);
+ for (i = 0; i < button_count; ++i) {
+ SDL_SendJoystickButton(timestamp, joystick, (Uint8)i, button_state[i]);
}
- } else {
- bool *button_state = SDL_stack_alloc(bool, joystick->nbuttons);
- float *axis_state = SDL_stack_alloc(float, joystick->naxes);
- GameInputSwitchPosition *switch_state = SDL_stack_alloc(GameInputSwitchPosition, joystick->nhats);
-
- if (button_state) {
- uint32_t i;
- uint32_t button_count = reading->GetControllerButtonState(joystick->nbuttons, button_state);
- for (i = 0; i < button_count; ++i) {
- SDL_SendJoystickButton(timestamp, joystick, (Uint8)i, button_state[i]);
- }
- SDL_stack_free(button_state);
- }
+ SDL_stack_free(button_state);
+ }
#define CONVERT_AXIS(v) (Sint16)((v)*65535.0f - 32768.0f)
- if (axis_state) {
- uint32_t i;
- uint32_t axis_count = reading->GetControllerAxisState(joystick->naxes, axis_state);
- for (i = 0; i < axis_count; ++i) {
- SDL_SendJoystickAxis(timestamp, joystick, (Uint8)i, CONVERT_AXIS(axis_state[i]));
- }
- SDL_stack_free(axis_state);
+ if (axis_state) {
+ uint32_t i;
+ uint32_t axis_count = reading->GetControllerAxisState(joystick->naxes, axis_state);
+ for (i = 0; i < axis_count; ++i) {
+ SDL_SendJoystickAxis(timestamp, joystick, (Uint8)i, CONVERT_AXIS(axis_state[i]));
}
+ SDL_stack_free(axis_state);
+ }
#undef CONVERT_AXIS
- if (switch_state) {
- uint32_t i;
- uint32_t switch_count = reading->GetControllerSwitchState(joystick->nhats, switch_state);
- for (i = 0; i < switch_count; ++i) {
- Uint8 hat;
- switch (switch_state[i]) {
- case GameInputSwitchUp:
- hat = SDL_HAT_UP;
- break;
- case GameInputSwitchUpRight:
- hat = SDL_HAT_UP | SDL_HAT_RIGHT;
- break;
- case GameInputSwitchRight:
- hat = SDL_HAT_RIGHT;
- break;
- case GameInputSwitchDownRight:
- hat = SDL_HAT_DOWN | SDL_HAT_RIGHT;
- break;
- case GameInputSwitchDown:
- hat = SDL_HAT_DOWN;
- break;
- case GameInputSwitchDownLeft:
- hat = SDL_HAT_DOWN | SDL_HAT_LEFT;
- break;
- case GameInputSwitchLeft:
- hat = SDL_HAT_LEFT;
- break;
- case GameInputSwitchUpLeft:
- hat = SDL_HAT_UP | SDL_HAT_LEFT;
- break;
- case GameInputSwitchCenter:
- default:
- hat = SDL_HAT_CENTERED;
- break;
- }
- SDL_SendJoystickHat(timestamp, joystick, (Uint8)i, hat);
+ if (switch_state) {
+ uint32_t i;
+ uint32_t switch_count = reading->GetControllerSwitchState(joystick->nhats, switch_state);
+ for (i = 0; i < switch_count; ++i) {
+ Uint8 hat;
+ switch (switch_state[i]) {
+ case GameInputSwitchUp:
+ hat = SDL_HAT_UP;
+ break;
+ case GameInputSwitchUpRight:
+ hat = SDL_HAT_UP | SDL_HAT_RIGHT;
+ break;
+ case GameInputSwitchRight:
+ hat = SDL_HAT_RIGHT;
+ break;
+ case GameInputSwitchDownRight:
+ hat = SDL_HAT_DOWN | SDL_HAT_RIGHT;
+ break;
+ case GameInputSwitchDown:
+ hat = SDL_HAT_DOWN;
+ break;
+ case GameInputSwitchDownLeft:
+ hat = SDL_HAT_DOWN | SDL_HAT_LEFT;
+ break;
+ case GameInputSwitchLeft:
+ hat = SDL_HAT_LEFT;
+ break;
+ case GameInputSwitchUpLeft:
+ hat = SDL_HAT_UP | SDL_HAT_LEFT;
+ break;
+ case GameInputSwitchCenter:
+ default:
+ hat = SDL_HAT_CENTERED;
+ break;
}
- SDL_stack_free(switch_state);
+ SDL_SendJoystickHat(timestamp, joystick, (Uint8)i, hat);
}
+ SDL_stack_free(switch_state);
+ }
+}
+
+static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
+{
+ GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata;
+ GAMEINPUT_InternalDevice *internal_device = hwdata->devref;
+ IGameInputDevice *device = hwdata->devref->device;
+ const GameInputDeviceInfo *info = hwdata->devref->info;
+ IGameInputReading *reading = NULL;
+ Uint64 timestamp;
+ HRESULT hr;
+
+ hr = g_pGameInput->GetCurrentReading(info->supportedInput, device, &reading);
+ if (FAILED(hr)) {
+ // don't SetError here since there can be a legitimate case when there's no reading avail
+ return;
+ }
+
+ 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 (GAMEINPUT_InternalIsGamepad(info)) {
+ GAMEINPUT_GamepadUpdate(joystick, reading, timestamp);
+ } else {
+ GAMEINPUT_ControllerUpdate(joystick, reading, timestamp);
}
#ifdef GAMEINPUT_SENSOR_SUPPORT
@@ -821,7 +975,7 @@ static bool GAMEINPUT_JoystickGetGamepadMapping(int device_index, SDL_GamepadMap
{
GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index);
- if (!GAMEINPUT_InternalIsGamepad(elem->info)) {
+ if (!GAMEINPUT_InternalIsGamepad(elem->info) && elem->raw_type == SDL_GAMEINPUT_RAWTYPE_NONE) {
return false;
}
diff --git a/src/joystick/usb_ids.h b/src/joystick/usb_ids.h
index e2afd509da037..6095f1eb66dca 100644
--- a/src/joystick/usb_ids.h
+++ b/src/joystick/usb_ids.h
@@ -53,6 +53,7 @@
#define USB_VENDOR_POWERA_ALT 0x20d6
#define USB_VENDOR_QANBA 0x2c22
#define USB_VENDOR_RAZER 0x1532
+#define USB_VENDOR_RED_OCTANE 0x1430
#define USB_VENDOR_SAITEK 0x06a3
#define USB_VENDOR_SCEA 0x12ba
#define USB_VENDOR_SHANWAN 0x2563
@@ -102,6 +103,9 @@
#define USB_PRODUCT_LOGITECH_F310 0xc216
#define USB_PRODUCT_LOGITECH_CHILLSTREAM 0xcad1
#define USB_PRODUCT_MADCATZ_SAITEK_SIDE_PANEL_CONTROL_DECK 0x2218
+#define USB_PRODUCT_MADCATZ_XB1_STRATOCASTER_GUITAR 0x4161
+#define USB_PRODUCT_MADCATZ_XB1_DRUM_KIT 0x4262
+#define USB_PRODUCT_MADCATZ_XB1_LEGACY_ADAPTER 0x4164
#define USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS4_WIRELESS 0x0d16
#define USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS4_WIRED 0x0d17
#define USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS5_WIRELESS 0x0d18
@@ -127,6 +131,9 @@
#define USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V104 0x7214
#define USB_PRODUCT_PDP_ROCK_CANDY 0x0246
#define USB_PRODUCT_PDP_REALMZ_WIRELESS 0x018c
+#define USB_PRODUCT_PDP_XB1_DRUM_KIT 0x0171
+#define USB_PRODUCT_PDP_XB1_JAGUAR_GUITAR 0x0170
+#define USB_PRODUCT_PDP_XB1_RIFFMASTER_GUITAR 0x0248
#define USB_PRODUCT_POWERA_MINI 0x541a
#define USB_PRODUCT_RAZER_ATROX 0x0a00
#define USB_PRODUCT_RAZER_KITSUNE 0x1012
@@ -146,6 +153,7 @@
#define USB_PRODUCT_RAZER_WOLVERINE_V2_PRO_XBOX_WIRED 0x1010
#define USB_PRODUCT_RAZER_WOLVERINE_V2_PRO_XBOX_WIRELESS 0x1011
#define USB_PRODUCT_RAZER_WOLVERINE_V3_PRO 0x0a3f
+#define USB_PRODUCT_RED_OCTANE_XB1_GUITAR_HERO_LIVE_GUITAR 0x0170
#define USB_PRODUCT_SAITEK_CYBORG_V3 0xf622
#define USB_PRODUCT_SCEA_PS3_GH_GUITAR 0x0100
#define USB_PRODUCT_SCEA_PS3_GH_DRUMS 0x0120