sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_rawinputjoystick.c (71804B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 2019 Sam Lantinga <slouken@libsdl.org>
      4 
      5   This software is provided 'as-is', without any express or implied
      6   warranty.  In no event will the authors be held liable for any damages
      7   arising from the use of this software.
      8 
      9   Permission is granted to anyone to use this software for any purpose,
     10   including commercial applications, and to alter it and redistribute it
     11   freely, subject to the following restrictions:
     12 
     13   1. The origin of this software must not be misrepresented; you must not
     14      claim that you wrote the original software. If you use this software
     15      in a product, an acknowledgment in the product documentation would be
     16      appreciated but is not required.
     17   2. Altered source versions must be plainly marked as such, and must not be
     18      misrepresented as being the original software.
     19   3. This notice may not be removed or altered from any source distribution.
     20 
     21 */
     22 /*
     23   RAWINPUT Joystick API for better handling XInput-capable devices on Windows.
     24 
     25   XInput is limited to 4 devices.
     26   Windows.Gaming.Input does not get inputs from XBox One controllers when not in the foreground.
     27   DirectInput does not get inputs from XBox One controllers when not in the foreground, nor rumble or accurate triggers.
     28   RawInput does not get rumble or accurate triggers.
     29 
     30   So, combine them as best we can!
     31 */
     32 #include "../../SDL_internal.h"
     33 
     34 #if SDL_JOYSTICK_RAWINPUT
     35 
     36 #include "SDL_endian.h"
     37 #include "SDL_events.h"
     38 #include "SDL_hints.h"
     39 #include "SDL_mutex.h"
     40 #include "SDL_timer.h"
     41 #include "../usb_ids.h"
     42 #include "../SDL_sysjoystick.h"
     43 #include "../../core/windows/SDL_windows.h"
     44 #include "../../core/windows/SDL_hid.h"
     45 #include "../hidapi/SDL_hidapijoystick_c.h"
     46 
     47 #define SDL_JOYSTICK_RAWINPUT_XINPUT
     48 #ifdef SDL_WINDOWS10_SDK
     49 #define SDL_JOYSTICK_RAWINPUT_WGI
     50 #endif
     51 
     52 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
     53 #include "../../core/windows/SDL_xinput.h"
     54 #endif
     55 
     56 #ifdef SDL_JOYSTICK_RAWINPUT_WGI
     57 #include "../../core/windows/SDL_windows.h"
     58 typedef struct WindowsGamingInputGamepadState WindowsGamingInputGamepadState;
     59 #define GamepadButtons_GUIDE 0x40000000
     60 #define COBJMACROS
     61 #include "windows.gaming.input.h"
     62 #endif
     63 
     64 #if defined(SDL_JOYSTICK_RAWINPUT_XINPUT) || defined(SDL_JOYSTICK_RAWINPUT_WGI)
     65 #define SDL_JOYSTICK_RAWINPUT_MATCHING
     66 #define SDL_JOYSTICK_RAWINPUT_MATCH_AXES
     67 #endif
     68 
     69 /*#define DEBUG_RAWINPUT*/
     70 
     71 #ifndef RIDEV_EXINPUTSINK
     72 #define RIDEV_EXINPUTSINK       0x00001000
     73 #define RIDEV_DEVNOTIFY         0x00002000
     74 #endif
     75 
     76 #ifndef WM_INPUT_DEVICE_CHANGE
     77 #define WM_INPUT_DEVICE_CHANGE          0x00FE
     78 #endif
     79 #ifndef WM_INPUT
     80 #define WM_INPUT                        0x00FF
     81 #endif
     82 #ifndef GIDC_ARRIVAL
     83 #define GIDC_ARRIVAL             1
     84 #define GIDC_REMOVAL             2
     85 #endif
     86 
     87 
     88 static SDL_bool SDL_RAWINPUT_inited = SDL_FALSE;
     89 static int SDL_RAWINPUT_numjoysticks = 0;
     90 static SDL_mutex *SDL_RAWINPUT_mutex = NULL;
     91 
     92 static void RAWINPUT_JoystickClose(SDL_Joystick *joystick);
     93 
     94 typedef struct _SDL_RAWINPUT_Device
     95 {
     96     SDL_atomic_t refcount;
     97     char *name;
     98     Uint16 vendor_id;
     99     Uint16 product_id;
    100     Uint16 version;
    101     SDL_JoystickGUID guid;
    102     SDL_bool is_xinput;
    103     PHIDP_PREPARSED_DATA preparsed_data;
    104 
    105     HANDLE hDevice;
    106     SDL_Joystick *joystick;
    107     SDL_JoystickID joystick_id;
    108 
    109     struct _SDL_RAWINPUT_Device *next;
    110 } SDL_RAWINPUT_Device;
    111 
    112 struct joystick_hwdata
    113 {
    114     SDL_bool is_xinput;
    115     PHIDP_PREPARSED_DATA preparsed_data;
    116     ULONG max_data_length;
    117     HIDP_DATA *data;
    118     USHORT *button_indices;
    119     USHORT *axis_indices;
    120     USHORT *hat_indices;
    121     SDL_bool guide_hack;
    122     SDL_bool trigger_hack;
    123     USHORT trigger_hack_index;
    124 
    125 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING
    126     Uint32 match_state; /* Low 16 bits for button states, high 16 for 4 4bit axes */
    127     Uint32 last_state_packet;
    128 #endif
    129 
    130 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
    131     SDL_bool xinput_enabled;
    132     SDL_bool xinput_correlated;
    133     Uint8 xinput_correlation_id;
    134     Uint8 xinput_correlation_count;
    135     Uint8 xinput_uncorrelate_count;
    136     Uint8 xinput_slot;
    137 #endif
    138 
    139 #ifdef SDL_JOYSTICK_RAWINPUT_WGI
    140     SDL_bool wgi_correlated;
    141     Uint8 wgi_correlation_id;
    142     Uint8 wgi_correlation_count;
    143     Uint8 wgi_uncorrelate_count;
    144     WindowsGamingInputGamepadState *wgi_slot;
    145 #endif
    146 
    147     SDL_RAWINPUT_Device *device;
    148 };
    149 typedef struct joystick_hwdata RAWINPUT_DeviceContext;
    150 
    151 SDL_RAWINPUT_Device *SDL_RAWINPUT_devices;
    152 
    153 static const Uint16 subscribed_devices[] = {
    154     USB_USAGE_GENERIC_GAMEPAD,
    155     /* Don't need Joystick for any devices we're handling here (XInput-capable)
    156     USB_USAGE_GENERIC_JOYSTICK,
    157     USB_USAGE_GENERIC_MULTIAXISCONTROLLER,
    158     */
    159 };
    160 
    161 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING
    162 
    163 static struct {
    164     Uint32 last_state_packet;
    165     SDL_Joystick *joystick;
    166     SDL_Joystick *last_joystick;
    167 } guide_button_candidate;
    168 
    169 typedef struct WindowsMatchState {
    170 #ifdef SDL_JOYSTICK_RAWINPUT_MATCH_AXES
    171     SHORT match_axes[4];
    172 #endif
    173 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
    174     WORD xinput_buttons;
    175 #endif
    176 #ifdef SDL_JOYSTICK_RAWINPUT_WGI
    177     Uint32 wgi_buttons;
    178 #endif
    179     SDL_bool any_data;
    180 } WindowsMatchState;
    181 
    182 static void RAWINPUT_FillMatchState(WindowsMatchState *state, Uint32 match_state)
    183 {
    184 #ifdef SDL_JOYSTICK_RAWINPUT_MATCH_AXES
    185     int ii;
    186 #endif
    187 
    188     state->any_data = SDL_FALSE;
    189 #ifdef SDL_JOYSTICK_RAWINPUT_MATCH_AXES
    190     /*  SHORT state->match_axes[4] = {
    191             (match_state & 0x000F0000) >> 4,
    192             (match_state & 0x00F00000) >> 8,
    193             (match_state & 0x0F000000) >> 12,
    194             (match_state & 0xF0000000) >> 16,
    195         }; */
    196     for (ii = 0; ii < 4; ii++) {
    197         state->match_axes[ii] = (match_state & (0x000F0000 << (ii * 4))) >> (4 + ii * 4);
    198         if ((Uint32)(state->match_axes[ii] + 0x1000) > 0x2000) { /* match_state bit is not 0xF, 0x1, or 0x2 */
    199             state->any_data = SDL_TRUE;
    200         }
    201     }
    202 #endif /* SDL_JOYSTICK_RAWINPUT_MATCH_AXES */
    203 
    204 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
    205     /* Match axes by checking if the distance between the high 4 bits of axis and the 4 bits from match_state is 1 or less */
    206 #define XInputAxesMatch(gamepad) (\
    207    (Uint32)(gamepad.sThumbLX - state->match_axes[0] + 0x1000) <= 0x2fff && \
    208    (Uint32)(~gamepad.sThumbLY - state->match_axes[1] + 0x1000) <= 0x2fff && \
    209    (Uint32)(gamepad.sThumbRX - state->match_axes[2] + 0x1000) <= 0x2fff && \
    210    (Uint32)(~gamepad.sThumbRY - state->match_axes[3] + 0x1000) <= 0x2fff)
    211     /* Explicit
    212 #define XInputAxesMatch(gamepad) (\
    213     SDL_abs((Sint8)((gamepad.sThumbLX & 0xF000) >> 8) - ((match_state & 0x000F0000) >> 12)) <= 0x10 && \
    214     SDL_abs((Sint8)((~gamepad.sThumbLY & 0xF000) >> 8) - ((match_state & 0x00F00000) >> 16)) <= 0x10 && \
    215     SDL_abs((Sint8)((gamepad.sThumbRX & 0xF000) >> 8) - ((match_state & 0x0F000000) >> 20)) <= 0x10 && \
    216     SDL_abs((Sint8)((~gamepad.sThumbRY & 0xF000) >> 8) - ((match_state & 0xF0000000) >> 24)) <= 0x10) */
    217 
    218     state->xinput_buttons =
    219         /* Bitwise map .RLDUWVQTS.KYXBA -> YXBA..WVQTKSRLDU */
    220         match_state << 12 | (match_state & 0x0780) >> 1 | (match_state & 0x0010) << 1 | (match_state & 0x0040) >> 2 | (match_state & 0x7800) >> 11;
    221     /*  Explicit
    222         ((match_state & (1<<SDL_CONTROLLER_BUTTON_A)) ? XINPUT_GAMEPAD_A : 0) |
    223         ((match_state & (1<<SDL_CONTROLLER_BUTTON_B)) ? XINPUT_GAMEPAD_B : 0) |
    224         ((match_state & (1<<SDL_CONTROLLER_BUTTON_X)) ? XINPUT_GAMEPAD_X : 0) |
    225         ((match_state & (1<<SDL_CONTROLLER_BUTTON_Y)) ? XINPUT_GAMEPAD_Y : 0) |
    226         ((match_state & (1<<SDL_CONTROLLER_BUTTON_BACK)) ? XINPUT_GAMEPAD_BACK : 0) |
    227         ((match_state & (1<<SDL_CONTROLLER_BUTTON_START)) ? XINPUT_GAMEPAD_START : 0) |
    228         ((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSTICK)) ? XINPUT_GAMEPAD_LEFT_THUMB : 0) |
    229         ((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSTICK)) ? XINPUT_GAMEPAD_RIGHT_THUMB: 0) |
    230         ((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) ? XINPUT_GAMEPAD_LEFT_SHOULDER : 0) |
    231         ((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) ? XINPUT_GAMEPAD_RIGHT_SHOULDER : 0) |
    232         ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_UP)) ? XINPUT_GAMEPAD_DPAD_UP : 0) |
    233         ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_DOWN)) ? XINPUT_GAMEPAD_DPAD_DOWN : 0) |
    234         ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_LEFT)) ? XINPUT_GAMEPAD_DPAD_LEFT : 0) |
    235         ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) ? XINPUT_GAMEPAD_DPAD_RIGHT : 0);
    236     */
    237 
    238     if (state->xinput_buttons)
    239         state->any_data = SDL_TRUE;
    240 #endif
    241 
    242 #ifdef SDL_JOYSTICK_RAWINPUT_WGI
    243     /* Match axes by checking if the distance between the high 4 bits of axis and the 4 bits from match_state is 1 or less */
    244 #define WindowsGamingInputAxesMatch(gamepad) (\
    245     (Uint16)(((Sint16)(gamepad.LeftThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[0] + 0x1000) <= 0x2fff && \
    246     (Uint16)((~(Sint16)(gamepad.LeftThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[1] + 0x1000) <= 0x2fff && \
    247     (Uint16)(((Sint16)(gamepad.RightThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[2] + 0x1000) <= 0x2fff && \
    248     (Uint16)((~(Sint16)(gamepad.RightThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[3] + 0x1000) <= 0x2fff)
    249 
    250 
    251     state->wgi_buttons =
    252         /* Bitwise map .RLD UWVQ TS.K YXBA -> ..QT WVRL DUYX BAKS */
    253         /*  RStick/LStick (QT)         RShould/LShould  (WV)                 DPad R/L/D/U                          YXBA                         bac(K)                      (S)tart */
    254         (match_state & 0x0180) << 5 | (match_state & 0x0600) << 1 | (match_state & 0x7800) >> 5 | (match_state & 0x000F) << 2 | (match_state & 0x0010) >> 3 | (match_state & 0x0040) >> 6;
    255     /*  Explicit
    256         ((match_state & (1<<SDL_CONTROLLER_BUTTON_A)) ? GamepadButtons_A : 0) |
    257         ((match_state & (1<<SDL_CONTROLLER_BUTTON_B)) ? GamepadButtons_B : 0) |
    258         ((match_state & (1<<SDL_CONTROLLER_BUTTON_X)) ? GamepadButtons_X : 0) |
    259         ((match_state & (1<<SDL_CONTROLLER_BUTTON_Y)) ? GamepadButtons_Y : 0) |
    260         ((match_state & (1<<SDL_CONTROLLER_BUTTON_BACK)) ? GamepadButtons_View : 0) |
    261         ((match_state & (1<<SDL_CONTROLLER_BUTTON_START)) ? GamepadButtons_Menu : 0) |
    262         ((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSTICK)) ? GamepadButtons_LeftThumbstick : 0) |
    263         ((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSTICK)) ? GamepadButtons_RightThumbstick: 0) |
    264         ((match_state & (1<<SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) ? GamepadButtons_LeftShoulder: 0) |
    265         ((match_state & (1<<SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) ? GamepadButtons_RightShoulder: 0) |
    266         ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_UP)) ? GamepadButtons_DPadUp : 0) |
    267         ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_DOWN)) ? GamepadButtons_DPadDown : 0) |
    268         ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_LEFT)) ? GamepadButtons_DPadLeft : 0) |
    269         ((match_state & (1<<SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) ? GamepadButtons_DPadRight : 0); */
    270 
    271     if (state->wgi_buttons)
    272         state->any_data = SDL_TRUE;
    273 #endif
    274 
    275 }
    276 
    277 #endif /* SDL_JOYSTICK_RAWINPUT_MATCHING */
    278 
    279 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
    280 
    281 static struct {
    282     XINPUT_STATE_EX state;
    283     XINPUT_BATTERY_INFORMATION_EX battery;
    284     SDL_bool connected; /* Currently has an active XInput device */
    285     SDL_bool used; /* Is currently mapped to an SDL device */
    286     Uint8 correlation_id;
    287 } xinput_state[XUSER_MAX_COUNT];
    288 static SDL_bool xinput_device_change = SDL_TRUE;
    289 static SDL_bool xinput_state_dirty = SDL_TRUE;
    290 
    291 static void
    292 RAWINPUT_UpdateXInput()
    293 {
    294     DWORD user_index;
    295     if (xinput_device_change) {
    296         for (user_index = 0; user_index < XUSER_MAX_COUNT; user_index++) {
    297             XINPUT_CAPABILITIES capabilities;
    298             xinput_state[user_index].connected = (XINPUTGETCAPABILITIES(user_index, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) ? SDL_TRUE : SDL_FALSE;
    299         }
    300         xinput_device_change = SDL_FALSE;
    301         xinput_state_dirty = SDL_TRUE;
    302     }
    303     if (xinput_state_dirty) {
    304         xinput_state_dirty = SDL_FALSE;
    305         for (user_index = 0; user_index < SDL_arraysize(xinput_state); ++user_index) {
    306             if (xinput_state[user_index].connected) {
    307                 if (XINPUTGETSTATE(user_index, &xinput_state[user_index].state) != ERROR_SUCCESS) {
    308                     xinput_state[user_index].connected = SDL_FALSE;
    309                 }
    310                 xinput_state[user_index].battery.BatteryType = BATTERY_TYPE_UNKNOWN;
    311                 XINPUTGETBATTERYINFORMATION(user_index, BATTERY_DEVTYPE_GAMEPAD, &xinput_state[user_index].battery);
    312             }
    313         }
    314     }
    315 }
    316 
    317 static void
    318 RAWINPUT_MarkXInputSlotUsed(Uint8 xinput_slot)
    319 {
    320     if (xinput_slot != XUSER_INDEX_ANY) {
    321         xinput_state[xinput_slot].used = SDL_TRUE;
    322     }
    323 }
    324 
    325 static void
    326 RAWINPUT_MarkXInputSlotFree(Uint8 xinput_slot)
    327 {
    328     if (xinput_slot != XUSER_INDEX_ANY) {
    329         xinput_state[xinput_slot].used = SDL_FALSE;
    330     }
    331 }
    332 static SDL_bool
    333 RAWINPUT_MissingXInputSlot()
    334 {
    335     int ii;
    336     for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) {
    337         if (xinput_state[ii].connected && !xinput_state[ii].used) {
    338             return SDL_TRUE;
    339         }
    340     }
    341     return SDL_FALSE;
    342 }
    343 
    344 static SDL_bool
    345 RAWINPUT_XInputSlotMatches(const WindowsMatchState *state, Uint8 slot_idx)
    346 {
    347     if (xinput_state[slot_idx].connected) {
    348         WORD xinput_buttons = xinput_state[slot_idx].state.Gamepad.wButtons;
    349         if ((xinput_buttons & ~XINPUT_GAMEPAD_GUIDE) == state->xinput_buttons
    350 #ifdef SDL_JOYSTICK_RAWINPUT_MATCH_AXES
    351             && XInputAxesMatch(xinput_state[slot_idx].state.Gamepad)
    352 #endif
    353             ) {
    354             return SDL_TRUE;
    355         }
    356     }
    357     return SDL_FALSE;
    358 }
    359 
    360 
    361 static SDL_bool
    362 RAWINPUT_GuessXInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, Uint8 *slot_idx)
    363 {
    364     int user_index;
    365     int match_count;
    366 
    367     *slot_idx = 0;
    368 
    369     match_count = 0;
    370     for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) {
    371         if (!xinput_state[user_index].used && RAWINPUT_XInputSlotMatches(state, user_index)) {
    372             ++match_count;
    373             *slot_idx = (Uint8)user_index;
    374             /* Incrementing correlation_id for any match, as negative evidence for others being correlated */
    375             *correlation_id = ++xinput_state[user_index].correlation_id;
    376         }
    377     }
    378     /* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched.
    379        Note that we're still invalidating *other* potential correlations if we have more than one match or we have no
    380        data. */
    381     if (match_count == 1 && state->any_data) {
    382         return SDL_TRUE;
    383     }
    384     return SDL_FALSE;
    385 }
    386 
    387 #endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */
    388 
    389 #ifdef SDL_JOYSTICK_RAWINPUT_WGI
    390 
    391 typedef struct WindowsGamingInputGamepadState {
    392     __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
    393     struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state;
    394     RAWINPUT_DeviceContext *correlated_context;
    395     SDL_bool used; /* Is currently mapped to an SDL device */
    396     SDL_bool connected; /* Just used during update to track disconnected */
    397     Uint8 correlation_id;
    398     struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration;
    399 } WindowsGamingInputGamepadState;
    400 
    401 static struct {
    402     WindowsGamingInputGamepadState **per_gamepad;
    403     int per_gamepad_count;
    404     SDL_bool initialized;
    405     SDL_bool dirty;
    406     SDL_bool need_device_list_update;
    407     int ref_count;
    408     __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics;
    409 } wgi_state;
    410 
    411 static void
    412 RAWINPUT_MarkWindowsGamingInputSlotUsed(WindowsGamingInputGamepadState *wgi_slot, RAWINPUT_DeviceContext *ctx)
    413 {
    414     wgi_slot->used = SDL_TRUE;
    415     wgi_slot->correlated_context = ctx;
    416 }
    417 
    418 static void
    419 RAWINPUT_MarkWindowsGamingInputSlotFree(WindowsGamingInputGamepadState *wgi_slot)
    420 {
    421     wgi_slot->used = SDL_FALSE;
    422     wgi_slot->correlated_context = NULL;
    423 }
    424 
    425 static SDL_bool
    426 RAWINPUT_MissingWindowsGamingInputSlot()
    427 {
    428     int ii;
    429     for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
    430         if (!wgi_state.per_gamepad[ii]->used) {
    431             return SDL_TRUE;
    432         }
    433     }
    434     return SDL_FALSE;
    435 }
    436 
    437 static void
    438 RAWINPUT_UpdateWindowsGamingInput()
    439 {
    440     int ii;
    441     if (!wgi_state.gamepad_statics)
    442         return;
    443 
    444     if (!wgi_state.dirty)
    445         return;
    446 
    447     wgi_state.dirty = SDL_FALSE;
    448 
    449     if (wgi_state.need_device_list_update) {
    450         wgi_state.need_device_list_update = SDL_FALSE;
    451         for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
    452             wgi_state.per_gamepad[ii]->connected = SDL_FALSE;
    453         }
    454         HRESULT hr;
    455         __FIVectorView_1_Windows__CGaming__CInput__CGamepad *gamepads;
    456 
    457         hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_get_Gamepads(wgi_state.gamepad_statics, &gamepads);
    458         if (SUCCEEDED(hr)) {
    459             unsigned int num_gamepads;
    460 
    461             hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_get_Size(gamepads, &num_gamepads);
    462             if (SUCCEEDED(hr)) {
    463                 unsigned int i;
    464                 for (i = 0; i < num_gamepads; ++i) {
    465                     __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad;
    466 
    467                     hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, i, &gamepad);
    468                     if (SUCCEEDED(hr)) {
    469                         SDL_bool found = SDL_FALSE;
    470                         int jj;
    471                         for (jj = 0; jj < wgi_state.per_gamepad_count ; jj++) {
    472                             if (wgi_state.per_gamepad[jj]->gamepad == gamepad) {
    473                                 found = SDL_TRUE;
    474                                 wgi_state.per_gamepad[jj]->connected = SDL_TRUE;
    475                                 break;
    476                             }
    477                         }
    478                         if (!found) {
    479                             /* New device, add it */
    480                             wgi_state.per_gamepad_count++;
    481                             wgi_state.per_gamepad = SDL_realloc(wgi_state.per_gamepad, sizeof(wgi_state.per_gamepad[0]) * wgi_state.per_gamepad_count);
    482                             if (!wgi_state.per_gamepad) {
    483                                 SDL_OutOfMemory();
    484                                 return;
    485                             }
    486                             WindowsGamingInputGamepadState *gamepad_state = SDL_calloc(1, sizeof(*gamepad_state));
    487                             if (!gamepad_state) {
    488                                 SDL_OutOfMemory();
    489                                 return;
    490                             }
    491                             wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1] = gamepad_state;
    492                             gamepad_state->gamepad = gamepad;
    493                             gamepad_state->connected = SDL_TRUE;
    494                         } else {
    495                             /* Already tracked */
    496                             __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad);
    497                         }
    498                     }
    499                 }
    500                 for (ii = wgi_state.per_gamepad_count - 1; ii >= 0; ii--) {
    501                     WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii];
    502                     if (!gamepad_state->connected) {
    503                         /* Device missing, must be disconnected */
    504                         if (gamepad_state->correlated_context) {
    505                             gamepad_state->correlated_context->wgi_correlated = SDL_FALSE;
    506                             gamepad_state->correlated_context->wgi_slot = NULL;
    507                         }
    508                         __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad_state->gamepad);
    509                         SDL_free(gamepad_state);
    510                         wgi_state.per_gamepad[ii] = wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1];
    511                         --wgi_state.per_gamepad_count;
    512                     }
    513                 }
    514             }
    515             __FIVectorView_1_Windows__CGaming__CInput__CGamepad_Release(gamepads);
    516         }
    517     } /* need_device_list_update */
    518 
    519     for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
    520         HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(wgi_state.per_gamepad[ii]->gamepad, &wgi_state.per_gamepad[ii]->state);
    521         if (!SUCCEEDED(hr)) {
    522             wgi_state.per_gamepad[ii]->connected = SDL_FALSE; /* Not used by anything, currently */
    523         }
    524     }
    525 }
    526 static void
    527 RAWINPUT_InitWindowsGamingInput(RAWINPUT_DeviceContext *ctx)
    528 {
    529     wgi_state.need_device_list_update = SDL_TRUE;
    530     wgi_state.ref_count++;
    531     if (!wgi_state.initialized) {
    532         /* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */
    533         if (FAILED(WIN_CoInitialize())) {
    534             return;
    535         }
    536         wgi_state.initialized = SDL_TRUE;
    537         wgi_state.dirty = SDL_TRUE;
    538 
    539         static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } };
    540         HRESULT hr;
    541         HMODULE hModule = LoadLibraryA("combase.dll");
    542         if (hModule != NULL) {
    543             typedef HRESULT (WINAPI *WindowsCreateStringReference_t)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING* string);
    544             typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory);
    545 
    546             WindowsCreateStringReference_t WindowsCreateStringReferenceFunc = (WindowsCreateStringReference_t)GetProcAddress(hModule, "WindowsCreateStringReference");
    547             RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory");
    548             if (WindowsCreateStringReferenceFunc && RoGetActivationFactoryFunc) {
    549                 LPTSTR pNamespace = L"Windows.Gaming.Input.Gamepad";
    550                 HSTRING_HEADER hNamespaceStringHeader;
    551                 HSTRING hNamespaceString;
    552 
    553                 hr = WindowsCreateStringReferenceFunc(pNamespace, (UINT32)SDL_wcslen(pNamespace), &hNamespaceStringHeader, &hNamespaceString);
    554                 if (SUCCEEDED(hr)) {
    555                     RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, &wgi_state.gamepad_statics);
    556                 }
    557             }
    558             FreeLibrary(hModule);
    559         }
    560     }
    561 }
    562 
    563 static SDL_bool
    564 RAWINPUT_WindowsGamingInputSlotMatches(const WindowsMatchState *state, WindowsGamingInputGamepadState *slot)
    565 {
    566     Uint32 wgi_buttons = slot->state.Buttons;
    567     if ((wgi_buttons & 0x3FFF) == state->wgi_buttons
    568 #ifdef SDL_JOYSTICK_RAWINPUT_MATCH_AXES
    569             && WindowsGamingInputAxesMatch(slot->state)
    570 #endif
    571        ) {
    572         return SDL_TRUE;
    573     }
    574     return SDL_FALSE;
    575 }
    576 
    577 static SDL_bool
    578 RAWINPUT_GuessWindowsGamingInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, WindowsGamingInputGamepadState **slot)
    579 {
    580     int match_count, user_index;
    581 
    582     match_count = 0;
    583     for (user_index = 0; user_index < wgi_state.per_gamepad_count; ++user_index) {
    584         WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[user_index];
    585         if (RAWINPUT_WindowsGamingInputSlotMatches(state, gamepad_state)) {
    586             ++match_count;
    587             *slot = gamepad_state;
    588             /* Incrementing correlation_id for any match, as negative evidence for others being correlated */
    589             *correlation_id = ++gamepad_state->correlation_id;
    590         }
    591     }
    592     /* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched.
    593        Note that we're still invalidating *other* potential correlations if we have more than one match or we have no
    594        data. */
    595     if (match_count == 1 && state->any_data) {
    596         return SDL_TRUE;
    597     }
    598     return SDL_FALSE;
    599 }
    600 
    601 static void
    602 RAWINPUT_QuitWindowsGamingInput(RAWINPUT_DeviceContext *ctx)
    603 {
    604     wgi_state.need_device_list_update = SDL_TRUE;
    605     --wgi_state.ref_count;
    606     if (!wgi_state.ref_count && wgi_state.initialized) {
    607         int ii;
    608         for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
    609             __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(wgi_state.per_gamepad[ii]->gamepad);
    610         }
    611         if (wgi_state.per_gamepad) {
    612             SDL_free(wgi_state.per_gamepad);
    613             wgi_state.per_gamepad = NULL;
    614         }
    615         wgi_state.per_gamepad_count = 0;
    616         if (wgi_state.gamepad_statics) {
    617             __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi_state.gamepad_statics);
    618             wgi_state.gamepad_statics = NULL;
    619         }
    620         WIN_CoUninitialize();
    621         wgi_state.initialized = SDL_FALSE;
    622     }
    623 }
    624 
    625 #endif /* SDL_JOYSTICK_RAWINPUT_WGI */
    626 
    627 
    628 static SDL_RAWINPUT_Device *
    629 RAWINPUT_AcquireDevice(SDL_RAWINPUT_Device *device)
    630 {
    631     SDL_AtomicIncRef(&device->refcount);
    632     return device;
    633 }
    634 
    635 static void
    636 RAWINPUT_ReleaseDevice(SDL_RAWINPUT_Device *device)
    637 {
    638 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
    639     if (device->joystick) {
    640         RAWINPUT_DeviceContext *ctx = device->joystick->hwdata;
    641 
    642         if (ctx->xinput_enabled && ctx->xinput_correlated) {
    643             RAWINPUT_MarkXInputSlotFree(ctx->xinput_slot);
    644             ctx->xinput_correlated = SDL_FALSE;
    645         }
    646     }
    647 #endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */
    648 
    649     if (SDL_AtomicDecRef(&device->refcount)) {
    650         if (device->preparsed_data) {
    651             SDL_HidD_FreePreparsedData(device->preparsed_data);
    652         }
    653         SDL_free(device->name);
    654         SDL_free(device);
    655     }
    656 }
    657 
    658 static SDL_RAWINPUT_Device *
    659 RAWINPUT_DeviceFromHandle(HANDLE hDevice)
    660 {
    661     SDL_RAWINPUT_Device *curr;
    662 
    663     for (curr = SDL_RAWINPUT_devices; curr; curr = curr->next) {
    664         if (curr->hDevice == hDevice)
    665             return curr;
    666     }
    667     return NULL;
    668 }
    669 
    670 static void
    671 RAWINPUT_AddDevice(HANDLE hDevice)
    672 {
    673 #define CHECK(exp) { if(!(exp)) goto err; }
    674     SDL_RAWINPUT_Device *device = NULL;
    675     SDL_RAWINPUT_Device *curr, *last;
    676     RID_DEVICE_INFO rdi;
    677     UINT rdi_size = sizeof(rdi);
    678     char dev_name[MAX_PATH];
    679     UINT name_size = SDL_arraysize(dev_name);
    680     HANDLE hFile = INVALID_HANDLE_VALUE;
    681 
    682     /* Make sure we're not trying to add the same device twice */
    683     if (RAWINPUT_DeviceFromHandle(hDevice)) {
    684         return;
    685     }
    686 
    687     /* Figure out what kind of device it is */
    688     CHECK(GetRawInputDeviceInfoA(hDevice, RIDI_DEVICEINFO, &rdi, &rdi_size) != (UINT)-1);
    689     CHECK(rdi.dwType == RIM_TYPEHID);
    690 
    691     /* Get the device "name" (HID Path) */
    692     CHECK(GetRawInputDeviceInfoA(hDevice, RIDI_DEVICENAME, dev_name, &name_size) != (UINT)-1);
    693     /* Only take XInput-capable devices */
    694     CHECK(SDL_strstr(dev_name, "IG_") != NULL);
    695 #ifdef SDL_JOYSTICK_HIDAPI
    696     /* Don't take devices handled by HIDAPI */
    697     CHECK(!HIDAPI_IsDevicePresent((Uint16)rdi.hid.dwVendorId, (Uint16)rdi.hid.dwProductId, (Uint16)rdi.hid.dwVersionNumber, ""));
    698 #endif
    699 
    700     CHECK(device = (SDL_RAWINPUT_Device *)SDL_calloc(1, sizeof(SDL_RAWINPUT_Device)));
    701     device->hDevice = hDevice;
    702     device->vendor_id = (Uint16)rdi.hid.dwVendorId;
    703     device->product_id = (Uint16)rdi.hid.dwProductId;
    704     device->version = (Uint16)rdi.hid.dwVersionNumber;
    705     device->is_xinput = SDL_TRUE;
    706 
    707     {
    708         const Uint16 vendor = device->vendor_id;
    709         const Uint16 product = device->product_id;
    710         const Uint16 version = device->version;
    711         Uint16 *guid16 = (Uint16 *)device->guid.data;
    712 
    713         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
    714         *guid16++ = 0;
    715         *guid16++ = SDL_SwapLE16(vendor);
    716         *guid16++ = 0;
    717         *guid16++ = SDL_SwapLE16(product);
    718         *guid16++ = 0;
    719         *guid16++ = SDL_SwapLE16(version);
    720         *guid16++ = 0;
    721 
    722         /* Note that this is a RAWINPUT device for special handling elsewhere */
    723         device->guid.data[14] = 'r';
    724         device->guid.data[15] = 0;
    725     }
    726 
    727     hFile = CreateFileA(dev_name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    728     CHECK(hFile != INVALID_HANDLE_VALUE);
    729 
    730     {
    731         char *manufacturer_string = NULL;
    732         char *product_string = NULL;
    733         WCHAR string[128];
    734 
    735         if (SDL_HidD_GetManufacturerString(hFile, string, sizeof(string))) {
    736             manufacturer_string = WIN_StringToUTF8(string);
    737         }
    738         if (SDL_HidD_GetProductString(hFile, string, sizeof(string))) {
    739             product_string = WIN_StringToUTF8(string);
    740         }
    741 
    742         device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, manufacturer_string, product_string);
    743 
    744         if (manufacturer_string) {
    745             SDL_free(manufacturer_string);
    746         }
    747         if (product_string) {
    748             SDL_free(product_string);
    749         }
    750     }
    751 
    752     CHECK(SDL_HidD_GetPreparsedData(hFile, &device->preparsed_data));
    753 
    754     CloseHandle(hFile);
    755     hFile = INVALID_HANDLE_VALUE;
    756 
    757     device->joystick_id = SDL_GetNextJoystickInstanceID();
    758 
    759 #ifdef DEBUG_RAWINPUT
    760     SDL_Log("Adding RAWINPUT device '%s' VID 0x%.4x, PID 0x%.4x, version %d, handle 0x%.8x\n", device->name, device->vendor_id, device->product_id, device->version, device->hDevice);
    761 #endif
    762 
    763     /* Add it to the list */
    764     RAWINPUT_AcquireDevice(device);
    765     for (curr = SDL_RAWINPUT_devices, last = NULL; curr; last = curr, curr = curr->next) {
    766         continue;
    767     }
    768     if (last) {
    769         last->next = device;
    770     } else {
    771         SDL_RAWINPUT_devices = device;
    772     }
    773 
    774     ++SDL_RAWINPUT_numjoysticks;
    775 
    776     SDL_PrivateJoystickAdded(device->joystick_id);
    777 
    778     return;
    779 
    780 err:
    781     if (hFile != INVALID_HANDLE_VALUE) {
    782         CloseHandle(hFile);
    783     }
    784     if (device) {
    785         if (device->name)
    786             SDL_free(device->name);
    787         SDL_free(device);
    788     }
    789 #undef CHECK
    790 }
    791 
    792 static void
    793 RAWINPUT_DelDevice(SDL_RAWINPUT_Device *device, SDL_bool send_event)
    794 {
    795     SDL_RAWINPUT_Device *curr, *last;
    796     for (curr = SDL_RAWINPUT_devices, last = NULL; curr; last = curr, curr = curr->next) {
    797         if (curr == device) {
    798             if (last) {
    799                 last->next = curr->next;
    800             } else {
    801                 SDL_RAWINPUT_devices = curr->next;
    802             }
    803             --SDL_RAWINPUT_numjoysticks;
    804 
    805             SDL_PrivateJoystickRemoved(device->joystick_id);
    806 
    807 #ifdef DEBUG_RAWINPUT
    808             SDL_Log("Removing RAWINPUT device '%s' VID 0x%.4x, PID 0x%.4x, version %d, handle %p\n", device->name, device->vendor_id, device->product_id, device->version, device->hDevice);
    809 #endif
    810             RAWINPUT_ReleaseDevice(device);
    811             return;
    812         }
    813     }
    814 }
    815 
    816 static int
    817 RAWINPUT_JoystickInit(void)
    818 {
    819     UINT device_count = 0;
    820 
    821     SDL_assert(!SDL_RAWINPUT_inited);
    822 
    823     if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_RAWINPUT, SDL_TRUE)) {
    824         return -1;
    825     }
    826 
    827     if (WIN_LoadHIDDLL() < 0) {
    828         return -1;
    829     }
    830 
    831     SDL_RAWINPUT_mutex = SDL_CreateMutex();
    832     SDL_RAWINPUT_inited = SDL_TRUE;
    833 
    834     if ((GetRawInputDeviceList(NULL, &device_count, sizeof(RAWINPUTDEVICELIST)) != -1) && device_count > 0) {
    835         PRAWINPUTDEVICELIST devices = NULL;
    836         UINT i;
    837 
    838         devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * device_count);
    839         if (devices) {
    840             if (GetRawInputDeviceList(devices, &device_count, sizeof(RAWINPUTDEVICELIST)) != -1) {
    841                 for (i = 0; i < device_count; ++i) {
    842                     RAWINPUT_AddDevice(devices[i].hDevice);
    843                 }
    844             }
    845             SDL_free(devices);
    846         }
    847     }
    848 
    849     return 0;
    850 }
    851 
    852 static int
    853 RAWINPUT_JoystickGetCount(void)
    854 {
    855     return SDL_RAWINPUT_numjoysticks;
    856 }
    857 
    858 SDL_bool
    859 RAWINPUT_IsEnabled()
    860 {
    861     return SDL_RAWINPUT_inited;
    862 }
    863 
    864 SDL_bool
    865 RAWINPUT_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
    866 {
    867     SDL_RAWINPUT_Device *device;
    868 
    869     /* If we're being asked about a device, that means another API just detected one, so rescan */
    870 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
    871     xinput_device_change = SDL_TRUE;
    872 #endif
    873 #ifdef SDL_JOYSTICK_RAWINPUT_WGI
    874     wgi_state.need_device_list_update = SDL_TRUE;
    875 #endif
    876 
    877     device = SDL_RAWINPUT_devices;
    878     while (device) {
    879         if (vendor_id == device->vendor_id && product_id == device->product_id ) {
    880             return SDL_TRUE;
    881         }
    882 
    883         /* The Xbox 360 wireless controller shows up as product 0 in WGI */
    884         if (vendor_id == device->vendor_id && product_id == 0 &&
    885             name && SDL_strstr(device->name, name) != NULL) {
    886             return SDL_TRUE;
    887         }
    888 
    889         /* The Xbox One controller shows up as a hardcoded raw input VID/PID */
    890         if (name && SDL_strcmp(name, "Xbox One Game Controller") == 0 &&
    891             device->vendor_id == USB_VENDOR_MICROSOFT &&
    892             device->product_id == USB_PRODUCT_XBOX_ONE_RAW_INPUT_CONTROLLER) {
    893             return SDL_TRUE;
    894         }
    895 
    896         device = device->next;
    897     }
    898     return SDL_FALSE;
    899 }
    900 
    901 static void
    902 RAWINPUT_PostUpdate(void)
    903 {
    904 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING
    905     SDL_bool unmapped_guide_pressed = SDL_FALSE;
    906 
    907 #ifdef SDL_JOYSTICK_RAWINPUT_WGI
    908     if (!wgi_state.dirty) {
    909         int ii;
    910         for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) {
    911             WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii];
    912             if (!gamepad_state->used && (gamepad_state->state.Buttons & GamepadButtons_GUIDE)) {
    913                 unmapped_guide_pressed = SDL_TRUE;
    914                 break;
    915             }
    916         }
    917     }
    918     wgi_state.dirty = SDL_TRUE;
    919 #endif
    920 
    921 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
    922     if (!xinput_state_dirty) {
    923         int ii;
    924         for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) {
    925             if (xinput_state[ii].connected && !xinput_state[ii].used && (xinput_state[ii].state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE)) {
    926                 unmapped_guide_pressed = SDL_TRUE;
    927                 break;
    928             }
    929         }
    930     }
    931     xinput_state_dirty = SDL_TRUE;
    932 #endif
    933 
    934     if (unmapped_guide_pressed) {
    935         if (guide_button_candidate.joystick && !guide_button_candidate.last_joystick) {
    936             SDL_Joystick *joystick = guide_button_candidate.joystick;
    937             RAWINPUT_DeviceContext *ctx = joystick->hwdata;
    938             if (ctx->guide_hack) {
    939                 int guide_button = joystick->nbuttons - 1;
    940 
    941                 SDL_PrivateJoystickButton(guide_button_candidate.joystick, guide_button, SDL_PRESSED);
    942             }
    943             guide_button_candidate.last_joystick = guide_button_candidate.joystick;
    944         }
    945     } else if (guide_button_candidate.last_joystick) {
    946         SDL_Joystick *joystick = guide_button_candidate.last_joystick;
    947         RAWINPUT_DeviceContext *ctx = joystick->hwdata;
    948         if (ctx->guide_hack) {
    949             int guide_button = joystick->nbuttons - 1;
    950 
    951             SDL_PrivateJoystickButton(joystick, guide_button, SDL_RELEASED);
    952         }
    953         guide_button_candidate.last_joystick = NULL;
    954     }
    955     guide_button_candidate.joystick = NULL;
    956 
    957 #endif /* SDL_JOYSTICK_RAWINPUT_MATCHING */
    958 }
    959 
    960 static void
    961 RAWINPUT_JoystickDetect(void)
    962 {
    963     RAWINPUT_PostUpdate();
    964 }
    965 
    966 static SDL_RAWINPUT_Device *
    967 RAWINPUT_GetDeviceByIndex(int device_index)
    968 {
    969     SDL_RAWINPUT_Device *device = SDL_RAWINPUT_devices;
    970     while (device) {
    971         if (device_index == 0) {
    972             break;
    973         }
    974         --device_index;
    975         device = device->next;
    976     }
    977     return device;
    978 }
    979 
    980 static const char *
    981 RAWINPUT_JoystickGetDeviceName(int device_index)
    982 {
    983     return RAWINPUT_GetDeviceByIndex(device_index)->name;
    984 }
    985 
    986 static int
    987 RAWINPUT_JoystickGetDevicePlayerIndex(int device_index)
    988 {
    989     return -1;
    990 }
    991 
    992 static void
    993 RAWINPUT_JoystickSetDevicePlayerIndex(int device_index, int player_index)
    994 {
    995 }
    996 
    997 
    998 static SDL_JoystickGUID
    999 RAWINPUT_JoystickGetDeviceGUID(int device_index)
   1000 {
   1001     return RAWINPUT_GetDeviceByIndex(device_index)->guid;
   1002 }
   1003 
   1004 static SDL_JoystickID
   1005 RAWINPUT_JoystickGetDeviceInstanceID(int device_index)
   1006 {
   1007     return RAWINPUT_GetDeviceByIndex(device_index)->joystick_id;
   1008 }
   1009 
   1010 static int
   1011 RAWINPUT_SortValueCaps(const void *A, const void *B)
   1012 {
   1013     HIDP_VALUE_CAPS *capsA = (HIDP_VALUE_CAPS *)A;
   1014     HIDP_VALUE_CAPS *capsB = (HIDP_VALUE_CAPS *)B;
   1015 
   1016     /* Sort by Usage for single values, or UsageMax for range of values */
   1017     return (int)capsA->NotRange.Usage - capsB->NotRange.Usage;
   1018 }
   1019 
   1020 static int
   1021 RAWINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index)
   1022 {
   1023     SDL_RAWINPUT_Device *device = RAWINPUT_GetDeviceByIndex(device_index);
   1024     RAWINPUT_DeviceContext *ctx;
   1025     HIDP_CAPS caps;
   1026     HIDP_BUTTON_CAPS *button_caps;
   1027     HIDP_VALUE_CAPS *value_caps;
   1028     ULONG i;
   1029 
   1030     ctx = (RAWINPUT_DeviceContext *)SDL_calloc(1, sizeof(RAWINPUT_DeviceContext));
   1031     if (!ctx) {
   1032         return SDL_OutOfMemory();
   1033     }
   1034     joystick->hwdata = ctx;
   1035 
   1036     ctx->device = RAWINPUT_AcquireDevice(device);
   1037     device->joystick = joystick;
   1038 
   1039     if (device->is_xinput) {
   1040         /* We'll try to get guide button and trigger axes from XInput */
   1041 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
   1042         xinput_device_change = SDL_TRUE;
   1043         ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_CORRELATE_XINPUT, SDL_TRUE);
   1044         if (ctx->xinput_enabled && (WIN_LoadXInputDLL() < 0 || !XINPUTGETSTATE)) {
   1045             ctx->xinput_enabled = SDL_FALSE;
   1046         }
   1047         ctx->xinput_slot = XUSER_INDEX_ANY;
   1048 #endif
   1049 #ifdef SDL_JOYSTICK_RAWINPUT_WGI
   1050         RAWINPUT_InitWindowsGamingInput(ctx);
   1051 #endif
   1052     }
   1053 
   1054     ctx->is_xinput = device->is_xinput;
   1055     ctx->preparsed_data = device->preparsed_data;
   1056     ctx->max_data_length = SDL_HidP_MaxDataListLength(HidP_Input, ctx->preparsed_data);
   1057     ctx->data = (HIDP_DATA *)SDL_malloc(ctx->max_data_length * sizeof(*ctx->data));
   1058     if (!ctx->data) {
   1059         RAWINPUT_JoystickClose(joystick);
   1060         return SDL_OutOfMemory();
   1061     }
   1062 
   1063     if (SDL_HidP_GetCaps(ctx->preparsed_data, &caps) != HIDP_STATUS_SUCCESS) {
   1064         RAWINPUT_JoystickClose(joystick);
   1065         return SDL_SetError("Couldn't get device capabilities");
   1066     }
   1067 
   1068     button_caps = SDL_stack_alloc(HIDP_BUTTON_CAPS, caps.NumberInputButtonCaps);
   1069     if (SDL_HidP_GetButtonCaps(HidP_Input, button_caps, &caps.NumberInputButtonCaps, ctx->preparsed_data) != HIDP_STATUS_SUCCESS) {
   1070         RAWINPUT_JoystickClose(joystick);
   1071         return SDL_SetError("Couldn't get device button capabilities");
   1072     }
   1073 
   1074     value_caps = SDL_stack_alloc(HIDP_VALUE_CAPS, caps.NumberInputValueCaps);
   1075     if (SDL_HidP_GetValueCaps(HidP_Input, value_caps, &caps.NumberInputValueCaps, ctx->preparsed_data) != HIDP_STATUS_SUCCESS) {
   1076         RAWINPUT_JoystickClose(joystick);
   1077         return SDL_SetError("Couldn't get device value capabilities");
   1078     }
   1079 
   1080     /* Sort the axes by usage, so X comes before Y, etc. */
   1081     SDL_qsort(value_caps, caps.NumberInputValueCaps, sizeof(*value_caps), RAWINPUT_SortValueCaps);
   1082 
   1083     for (i = 0; i < caps.NumberInputButtonCaps; ++i) {
   1084         HIDP_BUTTON_CAPS *cap = &button_caps[i];
   1085 
   1086         if (cap->UsagePage == USB_USAGEPAGE_BUTTON) {
   1087             int count;
   1088 
   1089             if (cap->IsRange) {
   1090                 count = 1 + (cap->Range.DataIndexMax - cap->Range.DataIndexMin);
   1091             } else {
   1092                 count = 1;
   1093             }
   1094 
   1095             joystick->nbuttons += count;
   1096         }
   1097     }
   1098 
   1099     if (joystick->nbuttons > 0) {
   1100         int button_index = 0;
   1101 
   1102         ctx->button_indices = (USHORT *)SDL_malloc(joystick->nbuttons * sizeof(*ctx->button_indices));
   1103         if (!ctx->button_indices) {
   1104             RAWINPUT_JoystickClose(joystick);
   1105             return SDL_OutOfMemory();
   1106         }
   1107 
   1108         for (i = 0; i < caps.NumberInputButtonCaps; ++i) {
   1109             HIDP_BUTTON_CAPS *cap = &button_caps[i];
   1110 
   1111             if (cap->UsagePage == USB_USAGEPAGE_BUTTON) {
   1112                 if (cap->IsRange) {
   1113                     int j, count = 1 + (cap->Range.DataIndexMax - cap->Range.DataIndexMin);
   1114 
   1115                     for (j = 0; j < count; ++j) {
   1116                         ctx->button_indices[button_index++] = cap->Range.DataIndexMin + j;
   1117                     }
   1118                 } else {
   1119                     ctx->button_indices[button_index++] = cap->NotRange.DataIndex;
   1120                 }
   1121             }
   1122         }
   1123     }
   1124     if (ctx->is_xinput && joystick->nbuttons == 10) {
   1125         ctx->guide_hack = SDL_TRUE;
   1126         joystick->nbuttons += 1;
   1127     }
   1128 
   1129     for (i = 0; i < caps.NumberInputValueCaps; ++i) {
   1130         HIDP_VALUE_CAPS *cap = &value_caps[i];
   1131 
   1132         if (cap->IsRange) {
   1133             continue;
   1134         }
   1135 
   1136         if (ctx->trigger_hack && cap->NotRange.Usage == USB_USAGE_GENERIC_Z) {
   1137             continue;
   1138         }
   1139 
   1140         if (cap->NotRange.Usage == USB_USAGE_GENERIC_HAT) {
   1141             joystick->nhats += 1;
   1142             continue;
   1143         }
   1144 
   1145         if (ctx->is_xinput && cap->NotRange.Usage == USB_USAGE_GENERIC_Z) {
   1146             continue;
   1147         }
   1148 
   1149         joystick->naxes += 1;
   1150     }
   1151 
   1152     if (joystick->naxes > 0) {
   1153         int axis_index = 0;
   1154 
   1155         ctx->axis_indices = (USHORT *)SDL_malloc(joystick->naxes * sizeof(*ctx->axis_indices));
   1156         if (!ctx->axis_indices) {
   1157             RAWINPUT_JoystickClose(joystick);
   1158             return SDL_OutOfMemory();
   1159         }
   1160 
   1161         for (i = 0; i < caps.NumberInputValueCaps; ++i) {
   1162             HIDP_VALUE_CAPS *cap = &value_caps[i];
   1163 
   1164             if (cap->IsRange) {
   1165                 continue;
   1166             }
   1167 
   1168             if (cap->NotRange.Usage == USB_USAGE_GENERIC_HAT) {
   1169                 continue;
   1170             }
   1171 
   1172             if (ctx->is_xinput && cap->NotRange.Usage == USB_USAGE_GENERIC_Z) {
   1173                 ctx->trigger_hack = SDL_TRUE;
   1174                 ctx->trigger_hack_index = cap->NotRange.DataIndex;
   1175                 continue;
   1176             }
   1177 
   1178             ctx->axis_indices[axis_index++] = cap->NotRange.DataIndex;
   1179         }
   1180     }
   1181     if (ctx->trigger_hack) {
   1182         joystick->naxes += 2;
   1183     }
   1184 
   1185     if (joystick->nhats > 0) {
   1186         int hat_index = 0;
   1187 
   1188         ctx->hat_indices = (USHORT *)SDL_malloc(joystick->nhats * sizeof(*ctx->hat_indices));
   1189         if (!ctx->hat_indices) {
   1190             RAWINPUT_JoystickClose(joystick);
   1191             return SDL_OutOfMemory();
   1192         }
   1193 
   1194         for (i = 0; i < caps.NumberInputValueCaps; ++i) {
   1195             HIDP_VALUE_CAPS *cap = &value_caps[i];
   1196 
   1197             if (cap->IsRange) {
   1198                 continue;
   1199             }
   1200 
   1201             if (cap->NotRange.Usage != USB_USAGE_GENERIC_HAT) {
   1202                 continue;
   1203             }
   1204 
   1205             ctx->hat_indices[hat_index++] = cap->NotRange.DataIndex;
   1206         }
   1207     }
   1208 
   1209     SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_UNKNOWN);
   1210 
   1211     return 0;
   1212 }
   1213 
   1214 static int
   1215 RAWINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
   1216 {
   1217 #if defined(SDL_JOYSTICK_RAWINPUT_WGI) || defined(SDL_JOYSTICK_RAWINPUT_XINPUT)
   1218     RAWINPUT_DeviceContext *ctx = joystick->hwdata;
   1219 #endif
   1220 
   1221     SDL_bool rumbled = SDL_FALSE;
   1222 
   1223 #ifdef SDL_JOYSTICK_RAWINPUT_WGI
   1224     if (!rumbled && ctx->wgi_correlated) {
   1225         WindowsGamingInputGamepadState *gamepad_state = ctx->wgi_slot;
   1226         HRESULT hr;
   1227         gamepad_state->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16;
   1228         gamepad_state->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16;
   1229         hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(gamepad_state->gamepad, gamepad_state->vibration);
   1230         if (SUCCEEDED(hr)) {
   1231             rumbled = SDL_TRUE;
   1232         }
   1233     }
   1234 #endif
   1235 
   1236 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
   1237     if (!rumbled && ctx->xinput_correlated) {
   1238         XINPUT_VIBRATION XVibration;
   1239 
   1240         if (!XINPUTSETSTATE) {
   1241             return SDL_Unsupported();
   1242         }
   1243 
   1244         XVibration.wLeftMotorSpeed = low_frequency_rumble;
   1245         XVibration.wRightMotorSpeed = high_frequency_rumble;
   1246         if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) == ERROR_SUCCESS) {
   1247             rumbled = SDL_TRUE;
   1248         } else {
   1249             return SDL_SetError("XInputSetState() failed");
   1250         }
   1251     }
   1252 #endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */
   1253 
   1254     return 0;
   1255 }
   1256 
   1257 static int
   1258 RAWINPUT_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
   1259 {
   1260 #if defined(SDL_JOYSTICK_RAWINPUT_WGI)
   1261     RAWINPUT_DeviceContext *ctx = joystick->hwdata;
   1262     
   1263     if (ctx->wgi_correlated) {
   1264         WindowsGamingInputGamepadState *gamepad_state = ctx->wgi_slot;
   1265         HRESULT hr;
   1266         gamepad_state->vibration.LeftTrigger = (DOUBLE)left_rumble / SDL_MAX_UINT16;
   1267         gamepad_state->vibration.RightTrigger = (DOUBLE)right_rumble / SDL_MAX_UINT16;
   1268         hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(gamepad_state->gamepad, gamepad_state->vibration);
   1269         if (!SUCCEEDED(hr)) {
   1270             return SDL_SetError("Setting vibration failed: 0x%x\n", hr);
   1271         }
   1272     }
   1273     return 0;
   1274 #else
   1275     return SDL_Unsupported();
   1276 #endif
   1277 }
   1278 
   1279 static SDL_bool
   1280 RAWINPUT_JoystickHasLED(SDL_Joystick *joystick)
   1281 {
   1282     return SDL_FALSE;
   1283 }
   1284 
   1285 static int
   1286 RAWINPUT_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
   1287 {
   1288     return SDL_Unsupported();
   1289 }
   1290 
   1291 static int
   1292 RAWINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled)
   1293 {
   1294     return SDL_Unsupported();
   1295 }
   1296 
   1297 static HIDP_DATA *GetData(USHORT index, HIDP_DATA *data, ULONG length)
   1298 {
   1299     ULONG i;
   1300 
   1301     /* Check to see if the data is at the expected offset */
   1302     if (index < length && data[index].DataIndex == index) {
   1303         return &data[index];
   1304     }
   1305 
   1306     /* Loop through the data to find it */
   1307     for (i = 0; i < length; ++i) {
   1308         if (data[i].DataIndex == index) {
   1309             return &data[i];
   1310         }
   1311     }
   1312     return NULL;
   1313 }
   1314 
   1315 /* This is the packet format for Xbox 360 and Xbox One controllers on Windows,
   1316    however with this interface there is no rumble support, no guide button,
   1317    and the left and right triggers are tied together as a single axis.
   1318 
   1319    We use XInput and Windows.Gaming.Input to make up for these shortcomings.
   1320  */
   1321 static void
   1322 RAWINPUT_HandleStatePacket(SDL_Joystick *joystick, Uint8 *data, int size)
   1323 {
   1324     RAWINPUT_DeviceContext *ctx = joystick->hwdata;
   1325 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING
   1326     /* Map new buttons and axes into game controller controls */
   1327     static const int button_map[] = {
   1328         SDL_CONTROLLER_BUTTON_A,
   1329         SDL_CONTROLLER_BUTTON_B,
   1330         SDL_CONTROLLER_BUTTON_X,
   1331         SDL_CONTROLLER_BUTTON_Y,
   1332         SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
   1333         SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
   1334         SDL_CONTROLLER_BUTTON_BACK,
   1335         SDL_CONTROLLER_BUTTON_START,
   1336         SDL_CONTROLLER_BUTTON_LEFTSTICK,
   1337         SDL_CONTROLLER_BUTTON_RIGHTSTICK
   1338     };
   1339 #define HAT_MASK ((1 << SDL_CONTROLLER_BUTTON_DPAD_UP) | (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN) | (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT) | (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT))
   1340     static const int hat_map[] = {
   1341         0,
   1342         (1 << SDL_CONTROLLER_BUTTON_DPAD_UP),
   1343         (1 << SDL_CONTROLLER_BUTTON_DPAD_UP) | (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT),
   1344         (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT),
   1345         (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN) | (1 << SDL_CONTROLLER_BUTTON_DPAD_RIGHT),
   1346         (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN),
   1347         (1 << SDL_CONTROLLER_BUTTON_DPAD_DOWN) | (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT),
   1348         (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT),
   1349         (1 << SDL_CONTROLLER_BUTTON_DPAD_UP) | (1 << SDL_CONTROLLER_BUTTON_DPAD_LEFT),
   1350     };
   1351     Uint32 match_state = ctx->match_state;
   1352     /* Update match_state with button bit, then fall through */
   1353 #define SDL_PrivateJoystickButton(joystick, button, state) if (button < SDL_arraysize(button_map)) { if (state) match_state |= 1 << button_map[button]; else match_state &= ~(1 << button_map[button]); } SDL_PrivateJoystickButton(joystick, button, state)
   1354 #ifdef SDL_JOYSTICK_RAWINPUT_MATCH_AXES
   1355     /* Grab high 4 bits of value, then fall through */
   1356 #define SDL_PrivateJoystickAxis(joystick, axis, value) if (axis < 4) match_state = (match_state & ~(0xF << (4 * axis + 16))) | ((value) & 0xF000) << (4 * axis + 4); SDL_PrivateJoystickAxis(joystick, axis, value)
   1357 #endif
   1358 #endif /* SDL_JOYSTICK_RAWINPUT_MATCHING */
   1359 
   1360     ULONG data_length = ctx->max_data_length;
   1361     int i;
   1362     int nbuttons = joystick->nbuttons - (ctx->guide_hack * 1);
   1363     int naxes = joystick->naxes - (ctx->trigger_hack * 2);
   1364     int nhats = joystick->nhats;
   1365     Uint32 button_mask = 0;
   1366 
   1367     if (SDL_HidP_GetData(HidP_Input, ctx->data, &data_length, ctx->preparsed_data, (PCHAR)data, size) != HIDP_STATUS_SUCCESS) {
   1368         return;
   1369     }
   1370 
   1371     for (i = 0; i < nbuttons; ++i) {
   1372         HIDP_DATA *item = GetData(ctx->button_indices[i], ctx->data, data_length);
   1373         if (item && item->On) {
   1374             button_mask |= (1 << i);
   1375         }
   1376     }
   1377     for (i = 0; i < nbuttons; ++i) {
   1378         SDL_PrivateJoystickButton(joystick, i, (button_mask & (1 << i)) ? SDL_PRESSED : SDL_RELEASED);
   1379     }
   1380 
   1381     for (i = 0; i < naxes; ++i) {
   1382         HIDP_DATA *item = GetData(ctx->axis_indices[i], ctx->data, data_length);
   1383         if (item) {
   1384             Sint16 axis = (int)(Uint16)item->RawValue - 0x8000;
   1385             SDL_PrivateJoystickAxis(joystick, i, axis);
   1386         }
   1387     }
   1388 
   1389     for (i = 0; i < nhats; ++i) {
   1390         HIDP_DATA *item = GetData(ctx->hat_indices[i], ctx->data, data_length);
   1391         if (item) {
   1392             const Uint8 hat_states[] = {
   1393                 SDL_HAT_CENTERED,
   1394                 SDL_HAT_UP,
   1395                 SDL_HAT_UP | SDL_HAT_RIGHT,
   1396                 SDL_HAT_RIGHT,
   1397                 SDL_HAT_DOWN | SDL_HAT_RIGHT,
   1398                 SDL_HAT_DOWN,
   1399                 SDL_HAT_DOWN | SDL_HAT_LEFT,
   1400                 SDL_HAT_LEFT,
   1401                 SDL_HAT_UP | SDL_HAT_LEFT,
   1402             };
   1403             ULONG state = item->RawValue;
   1404 
   1405             if (state < SDL_arraysize(hat_states)) {
   1406 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING
   1407                 match_state = (match_state & ~HAT_MASK) | hat_map[state];
   1408 #endif
   1409                 SDL_PrivateJoystickHat(joystick, i, hat_states[state]);
   1410             }
   1411         }
   1412     }
   1413 
   1414 #ifdef SDL_PrivateJoystickButton
   1415 #undef SDL_PrivateJoystickButton
   1416 #endif
   1417 #ifdef SDL_PrivateJoystickAxis
   1418 #undef SDL_PrivateJoystickAxis
   1419 #endif
   1420 
   1421     if (ctx->trigger_hack) {
   1422         SDL_bool has_trigger_data = SDL_FALSE;
   1423 
   1424 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
   1425         /* Prefer XInput over WindowsGamingInput, it continues to provide data in the background */
   1426         if (!has_trigger_data && ctx->xinput_enabled && ctx->xinput_correlated) {
   1427             has_trigger_data = SDL_TRUE;
   1428         }
   1429 #endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */
   1430 
   1431 #ifdef SDL_JOYSTICK_RAWINPUT_WGI
   1432         if (!has_trigger_data && ctx->wgi_correlated) {
   1433             has_trigger_data = SDL_TRUE;
   1434         }
   1435 #endif /* SDL_JOYSTICK_RAWINPUT_WGI */
   1436 
   1437         if (!has_trigger_data) {
   1438             HIDP_DATA *item = GetData(ctx->trigger_hack_index, ctx->data, data_length);
   1439             if (item) {
   1440                 int left_trigger = joystick->naxes - 2;
   1441                 int right_trigger = joystick->naxes - 1;
   1442                 Sint16 value = (int)(Uint16)item->RawValue - 0x8000;
   1443                 if (value < 0) {
   1444                     value = -value * 2 - 32769;
   1445                     SDL_PrivateJoystickAxis(joystick, left_trigger, SDL_MIN_SINT16);
   1446                     SDL_PrivateJoystickAxis(joystick, right_trigger, value);
   1447                 } else if (value > 0) {
   1448                     value = value * 2 - 32767;
   1449                     SDL_PrivateJoystickAxis(joystick, left_trigger, value);
   1450                     SDL_PrivateJoystickAxis(joystick, right_trigger, SDL_MIN_SINT16);
   1451                 } else {
   1452                     SDL_PrivateJoystickAxis(joystick, left_trigger, SDL_MIN_SINT16);
   1453                     SDL_PrivateJoystickAxis(joystick, right_trigger, SDL_MIN_SINT16);
   1454                 }
   1455             }
   1456         }
   1457     }
   1458 
   1459 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING
   1460     if (ctx->is_xinput) {
   1461         ctx->match_state = match_state;
   1462         ctx->last_state_packet = SDL_GetTicks();
   1463     }
   1464 #endif
   1465 }
   1466 
   1467 static void
   1468 RAWINPUT_UpdateOtherAPIs(SDL_Joystick *joystick)
   1469 {
   1470 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING
   1471     RAWINPUT_DeviceContext *ctx = joystick->hwdata;
   1472     SDL_bool has_trigger_data = SDL_FALSE;
   1473     SDL_bool correlated = SDL_FALSE;
   1474     WindowsMatchState match_state_xinput;
   1475     int guide_button = joystick->nbuttons - 1;
   1476     int left_trigger = joystick->naxes - 2;
   1477     int right_trigger = joystick->naxes - 1;
   1478 
   1479     RAWINPUT_FillMatchState(&match_state_xinput, ctx->match_state);
   1480 
   1481 #ifdef SDL_JOYSTICK_RAWINPUT_WGI
   1482     /* Parallel logic to WINDOWS_XINPUT below */
   1483     RAWINPUT_UpdateWindowsGamingInput();
   1484     if (ctx->wgi_correlated) {
   1485         /* We have been previously correlated, ensure we are still matching, see comments in XINPUT section */
   1486         if (RAWINPUT_WindowsGamingInputSlotMatches(&match_state_xinput, ctx->wgi_slot)) {
   1487             ctx->wgi_uncorrelate_count = 0;
   1488         } else {
   1489             ++ctx->wgi_uncorrelate_count;
   1490             /* Only un-correlate if this is consistent over multiple Update() calls - the timing of polling/event
   1491               pumping can easily cause this to uncorrelate for a frame.  2 seemed reliable in my testing, but
   1492               let's set it to 3 to be safe.  An incorrect un-correlation will simply result in lower precision
   1493               triggers for a frame. */
   1494             if (ctx->wgi_uncorrelate_count >= 3) {
   1495 #ifdef DEBUG_RAWINPUT
   1496                 SDL_Log("UN-Correlated joystick %d to WindowsGamingInput device #%d\n", joystick->instance_id, ctx->wgi_slot);
   1497 #endif
   1498                 RAWINPUT_MarkWindowsGamingInputSlotFree(ctx->wgi_slot);
   1499                 ctx->wgi_correlated = SDL_FALSE;
   1500                 ctx->wgi_correlation_count = 0;
   1501                 /* Force release of Guide button, it can't possibly be down on this device now. */
   1502                 /* It gets left down if we were actually correlated incorrectly and it was released on the WindowsGamingInput
   1503                   device but we didn't get a state packet. */
   1504                 if (ctx->guide_hack) {
   1505                     SDL_PrivateJoystickButton(joystick, guide_button, SDL_RELEASED);
   1506                 }
   1507             }
   1508         }
   1509     }
   1510     if (!ctx->wgi_correlated) {
   1511         SDL_bool new_correlation_count = 0;
   1512         if (RAWINPUT_MissingWindowsGamingInputSlot()) {
   1513             Uint8 correlation_id;
   1514             WindowsGamingInputGamepadState *slot_idx;
   1515             if (RAWINPUT_GuessWindowsGamingInputSlot(&match_state_xinput, &correlation_id, &slot_idx)) {
   1516                 /* we match exactly one WindowsGamingInput device */
   1517                 /* Probably can do without wgi_correlation_count, just check and clear wgi_slot to NULL, unless we need
   1518                    even more frames to be sure. */
   1519                 if (ctx->wgi_correlation_count && ctx->wgi_slot == slot_idx) {
   1520                     /* was correlated previously, and still the same device */
   1521                     if (ctx->wgi_correlation_id + 1 == correlation_id) {
   1522                         /* no one else was correlated in the meantime */
   1523                         new_correlation_count = ctx->wgi_correlation_count + 1;
   1524                         if (new_correlation_count == 2) {
   1525                             /* correlation stayed steady and uncontested across multiple frames, guaranteed match */
   1526                             ctx->wgi_correlated = SDL_TRUE;
   1527 #ifdef DEBUG_RAWINPUT
   1528                             SDL_Log("Correlated joystick %d to WindowsGamingInput device #%d\n", joystick->instance_id, slot_idx);
   1529 #endif
   1530                             correlated = SDL_TRUE;
   1531                             RAWINPUT_MarkWindowsGamingInputSlotUsed(ctx->wgi_slot, ctx);
   1532                             /* If the generalized Guide button was using us, it doesn't need to anymore */
   1533                             if (guide_button_candidate.joystick == joystick)
   1534                                 guide_button_candidate.joystick = NULL;
   1535                             if (guide_button_candidate.last_joystick == joystick)
   1536                                 guide_button_candidate.last_joystick = NULL;
   1537                         }
   1538                     } else {
   1539                         /* someone else also possibly correlated to this device, start over */
   1540                         new_correlation_count = 1;
   1541                     }
   1542                 } else {
   1543                     /* new possible correlation */
   1544                     new_correlation_count = 1;
   1545                     ctx->wgi_slot = slot_idx;
   1546                 }
   1547                 ctx->wgi_correlation_id = correlation_id;
   1548             } else {
   1549                 /* Match multiple WindowsGamingInput devices, or none (possibly due to no buttons pressed) */
   1550             }
   1551         }
   1552         ctx->wgi_correlation_count = new_correlation_count;
   1553     } else {
   1554         correlated = SDL_TRUE;
   1555     }
   1556 #endif /* SDL_JOYSTICK_RAWINPUT_WGI */
   1557 
   1558 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
   1559     /* Parallel logic to WINDOWS_GAMING_INPUT above */
   1560     if (ctx->xinput_enabled) {
   1561         RAWINPUT_UpdateXInput();
   1562         if (ctx->xinput_correlated) {
   1563             /* We have been previously correlated, ensure we are still matching */
   1564             /* This is required to deal with two (mostly) un-preventable mis-correlation situations:
   1565               A) Since the HID data stream does not provide an initial state (but polling XInput does), if we open
   1566                  5 controllers (#1-4 XInput mapped, #5 is not), and controller 1 had the A button down (and we don't
   1567                  know), and the user presses A on controller #5, we'll see exactly 1 controller with A down (#5) and
   1568                  exactly 1 XInput device with A down (#1), and incorrectly correlate.  This code will then un-correlate
   1569                  when A is released from either controller #1 or #5.
   1570               B) Since the app may not open all controllers, we could have a similar situation where only controller #5
   1571                  is opened, and the user holds A on controllers #1 and #5 simultaneously - again we see only 1 controller
   1572                  with A down and 1 XInput device with A down, and incorrectly correlate.  This should be very unusual
   1573                  (only when apps do not open all controllers, yet are listening to Guide button presses, yet
   1574                  for some reason want to ignore guide button presses on the un-opened controllers, yet users are
   1575                  pressing buttons on the unopened controllers), and will resolve itself when either button is released
   1576                  and we un-correlate.  We could prevent this by processing the state packets for *all* controllers,
   1577                  even un-opened ones, as that would allow more precise correlation.
   1578             */
   1579             if (RAWINPUT_XInputSlotMatches(&match_state_xinput, ctx->xinput_slot)) {
   1580                 ctx->xinput_uncorrelate_count = 0;
   1581             } else {
   1582                 ++ctx->xinput_uncorrelate_count;
   1583                 /* Only un-correlate if this is consistent over multiple Update() calls - the timing of polling/event
   1584                   pumping can easily cause this to uncorrelate for a frame.  2 seemed reliable in my testing, but
   1585                   let's set it to 3 to be safe.  An incorrect un-correlation will simply result in lower precision
   1586                   triggers for a frame. */
   1587                 if (ctx->xinput_uncorrelate_count >= 3) {
   1588 #ifdef DEBUG_RAWINPUT
   1589                     SDL_Log("UN-Correlated joystick %d to XInput device #%d\n", joystick->instance_id, ctx->xinput_slot);
   1590 #endif
   1591                     RAWINPUT_MarkXInputSlotFree(ctx->xinput_slot);
   1592                     ctx->xinput_correlated = SDL_FALSE;
   1593                     ctx->xinput_correlation_count = 0;
   1594                     /* Force release of Guide button, it can't possibly be down on this device now. */
   1595                     /* It gets left down if we were actually correlated incorrectly and it was released on the XInput
   1596                       device but we didn't get a state packet. */
   1597                     if (ctx->guide_hack) {
   1598                         SDL_PrivateJoystickButton(joystick, guide_button, SDL_RELEASED);
   1599                     }
   1600                 }
   1601             }
   1602         }
   1603         if (!ctx->xinput_correlated) {
   1604             Uint8 new_correlation_count = 0;
   1605             if (RAWINPUT_MissingXInputSlot()) {
   1606                 Uint8 correlation_id = 0;
   1607                 Uint8 slot_idx = 0;
   1608                 if (RAWINPUT_GuessXInputSlot(&match_state_xinput, &correlation_id, &slot_idx)) {
   1609                     /* we match exactly one XInput device */
   1610                     /* Probably can do without xinput_correlation_count, just check and clear xinput_slot to ANY, unless
   1611                        we need even more frames to be sure */
   1612                     if (ctx->xinput_correlation_count && ctx->xinput_slot == slot_idx) {
   1613                         /* was correlated previously, and still the same device */
   1614                         if (ctx->xinput_correlation_id + 1 == correlation_id) {
   1615                             /* no one else was correlated in the meantime */
   1616                             new_correlation_count = ctx->xinput_correlation_count + 1;
   1617                             if (new_correlation_count == 2) {
   1618                                 /* correlation stayed steady and uncontested across multiple frames, guaranteed match */
   1619                                 ctx->xinput_correlated = SDL_TRUE;
   1620 #ifdef DEBUG_RAWINPUT
   1621                                 SDL_Log("Correlated joystick %d to XInput device #%d\n", joystick->instance_id, slot_idx);
   1622 #endif
   1623                                 correlated = SDL_TRUE;
   1624                                 RAWINPUT_MarkXInputSlotUsed(ctx->xinput_slot);
   1625                                 /* If the generalized Guide button was using us, it doesn't need to anymore */
   1626                                 if (guide_button_candidate.joystick == joystick)
   1627                                     guide_button_candidate.joystick = NULL;
   1628                                 if (guide_button_candidate.last_joystick == joystick)
   1629                                     guide_button_candidate.last_joystick = NULL;
   1630                             }
   1631                         } else {
   1632                             /* someone else also possibly correlated to this device, start over */
   1633                             new_correlation_count = 1;
   1634                         }
   1635                     } else {
   1636                         /* new possible correlation */
   1637                         new_correlation_count = 1;
   1638                         ctx->xinput_slot = slot_idx;
   1639                     }
   1640                     ctx->xinput_correlation_id = correlation_id;
   1641                 } else {
   1642                     /* Match multiple XInput devices, or none (possibly due to no buttons pressed) */
   1643                 }
   1644             }
   1645             ctx->xinput_correlation_count = new_correlation_count;
   1646         } else {
   1647             correlated = SDL_TRUE;
   1648         }
   1649     }
   1650 #endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */
   1651 
   1652     /* Poll for trigger data once (not per-state-packet) */
   1653 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
   1654     /* Prefer XInput over WindowsGamingInput, it continues to provide data in the background */
   1655     if (!has_trigger_data && ctx->xinput_enabled && ctx->xinput_correlated) {
   1656         RAWINPUT_UpdateXInput();
   1657         if (xinput_state[ctx->xinput_slot].connected) {
   1658             XINPUT_BATTERY_INFORMATION_EX *battery_info = &xinput_state[ctx->xinput_slot].battery;
   1659 
   1660             if (ctx->guide_hack) {
   1661                 SDL_PrivateJoystickButton(joystick, guide_button, (xinput_state[ctx->xinput_slot].state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
   1662             }
   1663             if (ctx->trigger_hack) {
   1664                 SDL_PrivateJoystickAxis(joystick, left_trigger, ((int)xinput_state[ctx->xinput_slot].state.Gamepad.bLeftTrigger * 257) - 32768);
   1665                 SDL_PrivateJoystickAxis(joystick, right_trigger, ((int)xinput_state[ctx->xinput_slot].state.Gamepad.bRightTrigger * 257) - 32768);
   1666             }
   1667             has_trigger_data = SDL_TRUE;
   1668 
   1669             if (battery_info->BatteryType != BATTERY_TYPE_UNKNOWN) {
   1670                 SDL_JoystickPowerLevel ePowerLevel = SDL_JOYSTICK_POWER_UNKNOWN;
   1671                 if (battery_info->BatteryType == BATTERY_TYPE_WIRED) {
   1672                     ePowerLevel = SDL_JOYSTICK_POWER_WIRED;
   1673                 } else {
   1674                     switch (battery_info->BatteryLevel) {
   1675                     case BATTERY_LEVEL_EMPTY:
   1676                         ePowerLevel = SDL_JOYSTICK_POWER_EMPTY;
   1677                         break;
   1678                     case BATTERY_LEVEL_LOW:
   1679                         ePowerLevel = SDL_JOYSTICK_POWER_LOW;
   1680                         break;
   1681                     case BATTERY_LEVEL_MEDIUM:
   1682                         ePowerLevel = SDL_JOYSTICK_POWER_MEDIUM;
   1683                         break;
   1684                     default:
   1685                     case BATTERY_LEVEL_FULL:
   1686                         ePowerLevel = SDL_JOYSTICK_POWER_FULL;
   1687                         break;
   1688                     }
   1689                 }
   1690                 SDL_PrivateJoystickBatteryLevel(joystick, ePowerLevel);
   1691             }
   1692         }
   1693     }
   1694 #endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */
   1695 
   1696 #ifdef SDL_JOYSTICK_RAWINPUT_WGI
   1697     if (!has_trigger_data && ctx->wgi_correlated) {
   1698         RAWINPUT_UpdateWindowsGamingInput(); /* May detect disconnect / cause uncorrelation */
   1699         if (ctx->wgi_correlated) { /* Still connected */
   1700             struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading *state = &ctx->wgi_slot->state;
   1701 
   1702             if (ctx->guide_hack) {
   1703                 SDL_PrivateJoystickButton(joystick, guide_button, (state->Buttons & GamepadButtons_GUIDE) ? SDL_PRESSED : SDL_RELEASED);
   1704             }
   1705             if (ctx->trigger_hack) {
   1706                 SDL_PrivateJoystickAxis(joystick, left_trigger, ((int)(state->LeftTrigger * SDL_MAX_UINT16)) - 32768);
   1707                 SDL_PrivateJoystickAxis(joystick, right_trigger, ((int)(state->RightTrigger * SDL_MAX_UINT16)) - 32768);
   1708             }
   1709             has_trigger_data = SDL_TRUE;
   1710         }
   1711     }
   1712 #endif /* SDL_JOYSTICK_RAWINPUT_WGI */
   1713 
   1714     if (!correlated) {
   1715         if (!guide_button_candidate.joystick ||
   1716             (ctx->last_state_packet && (
   1717                 !guide_button_candidate.last_state_packet ||
   1718                 SDL_TICKS_PASSED(ctx->last_state_packet, guide_button_candidate.last_state_packet)
   1719             ))
   1720         ) {
   1721             guide_button_candidate.joystick = joystick;
   1722             guide_button_candidate.last_state_packet = ctx->last_state_packet;
   1723         }
   1724     }
   1725 #endif /* SDL_JOYSTICK_RAWINPUT_MATCHING */
   1726 }
   1727 
   1728 static void
   1729 RAWINPUT_JoystickUpdate(SDL_Joystick *joystick)
   1730 {
   1731     RAWINPUT_UpdateOtherAPIs(joystick);
   1732 }
   1733 
   1734 static void
   1735 RAWINPUT_JoystickClose(SDL_Joystick *joystick)
   1736 {
   1737     RAWINPUT_DeviceContext *ctx = joystick->hwdata;
   1738 
   1739 #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING
   1740     if (guide_button_candidate.joystick == joystick)
   1741         guide_button_candidate.joystick = NULL;
   1742     if (guide_button_candidate.last_joystick == joystick)
   1743         guide_button_candidate.last_joystick = NULL;
   1744 #endif
   1745 
   1746     if (ctx) {
   1747         SDL_RAWINPUT_Device *device;
   1748 
   1749 #ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
   1750         xinput_device_change = SDL_TRUE;
   1751         if (ctx->xinput_enabled) {
   1752             if (ctx->xinput_correlated) {
   1753                 RAWINPUT_MarkXInputSlotFree(ctx->xinput_slot);
   1754             }
   1755             WIN_UnloadXInputDLL();
   1756         }
   1757 #endif
   1758 #ifdef SDL_JOYSTICK_RAWINPUT_WGI
   1759         RAWINPUT_QuitWindowsGamingInput(ctx);
   1760 #endif
   1761 
   1762         device = ctx->device;
   1763         if (device) {
   1764             SDL_assert(device->joystick == joystick);
   1765             device->joystick = NULL;
   1766             RAWINPUT_ReleaseDevice(device);
   1767         }
   1768 
   1769         SDL_free(ctx->data);
   1770         SDL_free(ctx->button_indices);
   1771         SDL_free(ctx->axis_indices);
   1772         SDL_free(ctx->hat_indices);
   1773         SDL_free(ctx);
   1774         joystick->hwdata = NULL;
   1775     }
   1776 }
   1777 
   1778 SDL_bool
   1779 RAWINPUT_RegisterNotifications(HWND hWnd)
   1780 {
   1781     RAWINPUTDEVICE rid[SDL_arraysize(subscribed_devices)];
   1782     int i;
   1783 
   1784     for (i = 0; i < SDL_arraysize(subscribed_devices); i++) {
   1785         rid[i].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP;
   1786         rid[i].usUsage = subscribed_devices[i];
   1787         rid[i].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; /* Receive messages when in background, including device add/remove */
   1788         rid[i].hwndTarget = hWnd;
   1789     }
   1790 
   1791     if (!RegisterRawInputDevices(rid, SDL_arraysize(rid), sizeof(RAWINPUTDEVICE))) {
   1792         SDL_SetError("Couldn't register for raw input events");
   1793         return SDL_FALSE;
   1794     }
   1795     return SDL_TRUE;
   1796 }
   1797 
   1798 void
   1799 RAWINPUT_UnregisterNotifications()
   1800 {
   1801     int i;
   1802     RAWINPUTDEVICE rid[SDL_arraysize(subscribed_devices)];
   1803 
   1804     for (i = 0; i < SDL_arraysize(subscribed_devices); i++) {
   1805         rid[i].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP;
   1806         rid[i].usUsage = subscribed_devices[i];
   1807         rid[i].dwFlags = RIDEV_REMOVE;
   1808         rid[i].hwndTarget = NULL;
   1809     }
   1810 
   1811     if (!RegisterRawInputDevices(rid, SDL_arraysize(rid), sizeof(RAWINPUTDEVICE))) {
   1812         SDL_SetError("Couldn't unregister for raw input events");
   1813         return;
   1814     }
   1815 }
   1816     
   1817 LRESULT CALLBACK
   1818 RAWINPUT_WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
   1819 {
   1820     LRESULT result = -1;
   1821 
   1822     SDL_LockMutex(SDL_RAWINPUT_mutex);
   1823 
   1824     if (SDL_RAWINPUT_inited) {
   1825         switch (msg) {
   1826             case WM_INPUT_DEVICE_CHANGE:
   1827             {
   1828                 HANDLE hDevice = (HANDLE)lParam;
   1829                 switch (wParam) {
   1830                 case GIDC_ARRIVAL:
   1831                     RAWINPUT_AddDevice(hDevice);
   1832                     break;
   1833                 case GIDC_REMOVAL:
   1834                 {
   1835                     SDL_RAWINPUT_Device *device;
   1836                     device = RAWINPUT_DeviceFromHandle(hDevice);
   1837                     if (device) {
   1838                         RAWINPUT_DelDevice(device, SDL_TRUE);
   1839                     }
   1840                     break;
   1841                 }
   1842                 default:
   1843                     break;
   1844                 }
   1845             }
   1846             result = 0;
   1847             break;
   1848 
   1849             case WM_INPUT:
   1850             {
   1851                 Uint8 data[sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + USB_PACKET_LENGTH];
   1852                 UINT buffer_size = SDL_arraysize(data);
   1853 
   1854                 if ((int)GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data, &buffer_size, sizeof(RAWINPUTHEADER)) > 0) {
   1855                     PRAWINPUT raw_input = (PRAWINPUT)data;
   1856                     SDL_RAWINPUT_Device *device = RAWINPUT_DeviceFromHandle(raw_input->header.hDevice);
   1857                     if (device) {
   1858                         SDL_Joystick *joystick = device->joystick;
   1859                         if (joystick) {
   1860                             RAWINPUT_HandleStatePacket(joystick, raw_input->data.hid.bRawData, raw_input->data.hid.dwSizeHid);
   1861                         }
   1862                     }
   1863                 }
   1864             }
   1865             result = 0;
   1866             break;
   1867         }
   1868     }
   1869 
   1870     SDL_UnlockMutex(SDL_RAWINPUT_mutex);
   1871 
   1872     if (result >= 0) {
   1873         return result;
   1874     }
   1875     return CallWindowProc(DefWindowProc, hWnd, msg, wParam, lParam);
   1876 }
   1877 
   1878 static void
   1879 RAWINPUT_JoystickQuit(void)
   1880 {
   1881     if (!SDL_RAWINPUT_inited) {
   1882         return;
   1883     }
   1884 
   1885     SDL_LockMutex(SDL_RAWINPUT_mutex);
   1886 
   1887     while (SDL_RAWINPUT_devices) {
   1888         RAWINPUT_DelDevice(SDL_RAWINPUT_devices, SDL_FALSE);
   1889     }
   1890 
   1891     WIN_UnloadHIDDLL();
   1892 
   1893     SDL_RAWINPUT_numjoysticks = 0;
   1894 
   1895     SDL_RAWINPUT_inited = SDL_FALSE;
   1896 
   1897     SDL_UnlockMutex(SDL_RAWINPUT_mutex);
   1898     SDL_DestroyMutex(SDL_RAWINPUT_mutex);
   1899     SDL_RAWINPUT_mutex = NULL;
   1900 }
   1901 
   1902 static SDL_bool
   1903 RAWINPUT_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
   1904 {
   1905     return SDL_FALSE;
   1906 }
   1907 
   1908 SDL_JoystickDriver SDL_RAWINPUT_JoystickDriver =
   1909 {
   1910     RAWINPUT_JoystickInit,
   1911     RAWINPUT_JoystickGetCount,
   1912     RAWINPUT_JoystickDetect,
   1913     RAWINPUT_JoystickGetDeviceName,
   1914     RAWINPUT_JoystickGetDevicePlayerIndex,
   1915     RAWINPUT_JoystickSetDevicePlayerIndex,
   1916     RAWINPUT_JoystickGetDeviceGUID,
   1917     RAWINPUT_JoystickGetDeviceInstanceID,
   1918     RAWINPUT_JoystickOpen,
   1919     RAWINPUT_JoystickRumble,
   1920     RAWINPUT_JoystickRumbleTriggers,
   1921     RAWINPUT_JoystickHasLED,
   1922     RAWINPUT_JoystickSetLED,
   1923     RAWINPUT_JoystickSetSensorsEnabled,
   1924     RAWINPUT_JoystickUpdate,
   1925     RAWINPUT_JoystickClose,
   1926     RAWINPUT_JoystickQuit,
   1927     RAWINPUT_JoystickGetGamepadMapping
   1928 };
   1929 
   1930 #endif /* SDL_JOYSTICK_RAWINPUT */
   1931 
   1932 /* vi: set ts=4 sw=4 expandtab: */