Jelajahi Sumber

Add support for GIP guitars via gameinput (#15301)

(cherry picked from commit 7f86f9107d0270f963a7896180e80dc6ed28ea9e)
Sanjay Govind 2 hari lalu
induk
melakukan
8d7f469fb5
3 mengubah file dengan 290 tambahan dan 108 penghapusan
  1. 20 0
      include/SDL3/SDL_hints.h
  2. 262 108
      src/joystick/gdk/SDL_gameinputjoystick.cpp
  3. 8 0
      src/joystick/usb_ids.h

+ 20 - 0
include/SDL3/SDL_hints.h

@@ -1412,6 +1412,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.

+ 262 - 108
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;
     }
 

+ 8 - 0
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
@@ -101,6 +102,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
@@ -126,6 +130,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
@@ -145,6 +152,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