src/joystick/windows/SDL_dxjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 31 Mar 2014 10:38:26 -0700
changeset 8689 671977fe0abd
parent 8629 4c01875a4620
child 8820 0e935d5b193a
permissions -rw-r--r--
Fixed the joystick side of XInput haptic detection on Windows 8
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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 #include "../../SDL_internal.h"
    22 
    23 #ifdef SDL_JOYSTICK_DINPUT
    24 
    25 /* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de
    26  * A. Formiga's WINMM driver.
    27  *
    28  * Hats and sliders are completely untested; the app I'm writing this for mostly
    29  * doesn't use them and I don't own any joysticks with them.
    30  *
    31  * We don't bother to use event notification here.  It doesn't seem to work
    32  * with polled devices, and it's fine to call IDirectInputDevice8_GetDeviceData and
    33  * let it return 0 events. */
    34 
    35 #include "SDL_error.h"
    36 #include "SDL_assert.h"
    37 #include "SDL_events.h"
    38 #include "SDL_thread.h"
    39 #include "SDL_timer.h"
    40 #include "SDL_mutex.h"
    41 #include "SDL_events.h"
    42 #include "SDL_hints.h"
    43 #include "SDL_joystick.h"
    44 #include "../SDL_sysjoystick.h"
    45 #if !SDL_EVENTS_DISABLED
    46 #include "../../events/SDL_events_c.h"
    47 #endif
    48 
    49 #define INITGUID /* Only set here, if set twice will cause mingw32 to break. */
    50 #include "SDL_dxjoystick_c.h"
    51 
    52 #if SDL_HAPTIC_DINPUT
    53 #include "../../haptic/windows/SDL_syshaptic_c.h"    /* For haptic hot plugging */
    54 #endif
    55 
    56 #ifndef DIDFT_OPTIONAL
    57 #define DIDFT_OPTIONAL      0x80000000
    58 #endif
    59 
    60 DEFINE_GUID(GUID_DEVINTERFACE_HID, 0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);
    61 
    62 
    63 #define INPUT_QSIZE 32      /* Buffer up to 32 input messages */
    64 #define AXIS_MIN    -32768  /* minimum value for axis coordinate */
    65 #define AXIS_MAX    32767   /* maximum value for axis coordinate */
    66 #define JOY_AXIS_THRESHOLD  (((AXIS_MAX)-(AXIS_MIN))/100)   /* 1% motion */
    67 
    68 /* external variables referenced. */
    69 extern HWND SDL_HelperWindow;
    70 
    71 
    72 /* local variables */
    73 static SDL_bool coinitialized = SDL_FALSE;
    74 static LPDIRECTINPUT8 dinput = NULL;
    75 static SDL_bool s_bDeviceAdded = SDL_FALSE;
    76 static SDL_bool s_bDeviceRemoved = SDL_FALSE;
    77 static SDL_JoystickID s_nInstanceID = -1;
    78 static SDL_cond *s_condJoystickThread = NULL;
    79 static SDL_mutex *s_mutexJoyStickEnum = NULL;
    80 static SDL_Thread *s_threadJoystick = NULL;
    81 static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
    82 static SDL_bool s_bXInputEnabled = SDL_TRUE;
    83 
    84 XInputGetState_t SDL_XInputGetState = NULL;
    85 XInputSetState_t SDL_XInputSetState = NULL;
    86 XInputGetCapabilities_t SDL_XInputGetCapabilities = NULL;
    87 DWORD SDL_XInputVersion = 0;
    88 
    89 static HANDLE s_pXInputDLL = 0;
    90 static int s_XInputDLLRefCount = 0;
    91 
    92 int
    93 WIN_LoadXInputDLL(void)
    94 {
    95     DWORD version = 0;
    96 
    97     if (s_pXInputDLL) {
    98         SDL_assert(s_XInputDLLRefCount > 0);
    99         s_XInputDLLRefCount++;
   100         return 0;  /* already loaded */
   101     }
   102 
   103     version = (1 << 16) | 4;
   104     s_pXInputDLL = LoadLibrary( L"XInput1_4.dll" );  /* 1.4 Ships with Windows 8. */
   105     if (!s_pXInputDLL) {
   106         version = (1 << 16) | 3;
   107         s_pXInputDLL = LoadLibrary( L"XInput1_3.dll" );  /* 1.3 Ships with Vista and Win7, can be installed as a redistributable component. */
   108     }
   109     if (!s_pXInputDLL) {
   110         s_pXInputDLL = LoadLibrary( L"bin\\XInput1_3.dll" );
   111     }
   112     if (!s_pXInputDLL) {
   113         return -1;
   114     }
   115 
   116     SDL_assert(s_XInputDLLRefCount == 0);
   117     SDL_XInputVersion = version;
   118     s_XInputDLLRefCount = 1;
   119 
   120     /* 100 is the ordinal for _XInputGetStateEx, which returns the same struct as XinputGetState, but with extra data in wButtons for the guide button, we think... */
   121     SDL_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, (LPCSTR)100 );
   122     SDL_XInputSetState = (XInputSetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputSetState" );
   123     SDL_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetCapabilities" );
   124     if ( !SDL_XInputGetState || !SDL_XInputSetState || !SDL_XInputGetCapabilities ) {
   125         WIN_UnloadXInputDLL();
   126         return -1;
   127     }
   128 
   129     return 0;
   130 }
   131 
   132 void
   133 WIN_UnloadXInputDLL(void)
   134 {
   135     if ( s_pXInputDLL ) {
   136         SDL_assert(s_XInputDLLRefCount > 0);
   137         if (--s_XInputDLLRefCount == 0) {
   138             FreeLibrary( s_pXInputDLL );
   139             s_pXInputDLL = NULL;
   140         }
   141     } else {
   142         SDL_assert(s_XInputDLLRefCount == 0);
   143     }
   144 }
   145 
   146 
   147 extern HRESULT(WINAPI * DInputCreate) (HINSTANCE hinst, DWORD dwVersion,
   148                                        LPDIRECTINPUT * ppDI,
   149                                        LPUNKNOWN punkOuter);
   150 struct JoyStick_DeviceData_
   151 {
   152     SDL_JoystickGUID guid;
   153     DIDEVICEINSTANCE dxdevice;
   154     char *joystickname;
   155     Uint8 send_add_event;
   156     SDL_JoystickID nInstanceID;
   157     SDL_bool bXInputDevice;
   158     Uint8 XInputUserId;
   159     struct JoyStick_DeviceData_ *pNext;
   160 };
   161 
   162 typedef struct JoyStick_DeviceData_ JoyStick_DeviceData;
   163 
   164 static JoyStick_DeviceData *SYS_Joystick;    /* array to hold joystick ID values */
   165 
   166 /* local prototypes */
   167 static int SetDIerror(const char *function, HRESULT code);
   168 static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE *
   169                                            pdidInstance, VOID * pContext);
   170 static BOOL CALLBACK EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev,
   171                                             LPVOID pvRef);
   172 static void SortDevObjects(SDL_Joystick *joystick);
   173 static Uint8 TranslatePOV(DWORD value);
   174 static int SDL_PrivateJoystickAxis_Int(SDL_Joystick * joystick, Uint8 axis,
   175                                        Sint16 value);
   176 static int SDL_PrivateJoystickHat_Int(SDL_Joystick * joystick, Uint8 hat,
   177                                       Uint8 value);
   178 static int SDL_PrivateJoystickButton_Int(SDL_Joystick * joystick,
   179                                          Uint8 button, Uint8 state);
   180 
   181 /* Taken from Wine - Thanks! */
   182 DIOBJECTDATAFORMAT dfDIJoystick2[] = {
   183   { &GUID_XAxis,DIJOFS_X,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   184   { &GUID_YAxis,DIJOFS_Y,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   185   { &GUID_ZAxis,DIJOFS_Z,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   186   { &GUID_RxAxis,DIJOFS_RX,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   187   { &GUID_RyAxis,DIJOFS_RY,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   188   { &GUID_RzAxis,DIJOFS_RZ,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   189   { &GUID_Slider,DIJOFS_SLIDER(0),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   190   { &GUID_Slider,DIJOFS_SLIDER(1),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   191   { &GUID_POV,DIJOFS_POV(0),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE,0},
   192   { &GUID_POV,DIJOFS_POV(1),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE,0},
   193   { &GUID_POV,DIJOFS_POV(2),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE,0},
   194   { &GUID_POV,DIJOFS_POV(3),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE,0},
   195   { NULL,DIJOFS_BUTTON(0),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   196   { NULL,DIJOFS_BUTTON(1),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   197   { NULL,DIJOFS_BUTTON(2),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   198   { NULL,DIJOFS_BUTTON(3),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   199   { NULL,DIJOFS_BUTTON(4),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   200   { NULL,DIJOFS_BUTTON(5),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   201   { NULL,DIJOFS_BUTTON(6),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   202   { NULL,DIJOFS_BUTTON(7),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   203   { NULL,DIJOFS_BUTTON(8),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   204   { NULL,DIJOFS_BUTTON(9),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   205   { NULL,DIJOFS_BUTTON(10),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   206   { NULL,DIJOFS_BUTTON(11),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   207   { NULL,DIJOFS_BUTTON(12),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   208   { NULL,DIJOFS_BUTTON(13),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   209   { NULL,DIJOFS_BUTTON(14),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   210   { NULL,DIJOFS_BUTTON(15),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   211   { NULL,DIJOFS_BUTTON(16),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   212   { NULL,DIJOFS_BUTTON(17),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   213   { NULL,DIJOFS_BUTTON(18),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   214   { NULL,DIJOFS_BUTTON(19),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   215   { NULL,DIJOFS_BUTTON(20),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   216   { NULL,DIJOFS_BUTTON(21),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   217   { NULL,DIJOFS_BUTTON(22),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   218   { NULL,DIJOFS_BUTTON(23),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   219   { NULL,DIJOFS_BUTTON(24),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   220   { NULL,DIJOFS_BUTTON(25),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   221   { NULL,DIJOFS_BUTTON(26),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   222   { NULL,DIJOFS_BUTTON(27),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   223   { NULL,DIJOFS_BUTTON(28),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   224   { NULL,DIJOFS_BUTTON(29),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   225   { NULL,DIJOFS_BUTTON(30),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   226   { NULL,DIJOFS_BUTTON(31),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   227   { NULL,DIJOFS_BUTTON(32),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   228   { NULL,DIJOFS_BUTTON(33),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   229   { NULL,DIJOFS_BUTTON(34),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   230   { NULL,DIJOFS_BUTTON(35),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   231   { NULL,DIJOFS_BUTTON(36),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   232   { NULL,DIJOFS_BUTTON(37),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   233   { NULL,DIJOFS_BUTTON(38),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   234   { NULL,DIJOFS_BUTTON(39),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   235   { NULL,DIJOFS_BUTTON(40),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   236   { NULL,DIJOFS_BUTTON(41),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   237   { NULL,DIJOFS_BUTTON(42),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   238   { NULL,DIJOFS_BUTTON(43),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   239   { NULL,DIJOFS_BUTTON(44),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   240   { NULL,DIJOFS_BUTTON(45),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   241   { NULL,DIJOFS_BUTTON(46),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   242   { NULL,DIJOFS_BUTTON(47),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   243   { NULL,DIJOFS_BUTTON(48),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   244   { NULL,DIJOFS_BUTTON(49),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   245   { NULL,DIJOFS_BUTTON(50),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   246   { NULL,DIJOFS_BUTTON(51),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   247   { NULL,DIJOFS_BUTTON(52),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   248   { NULL,DIJOFS_BUTTON(53),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   249   { NULL,DIJOFS_BUTTON(54),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   250   { NULL,DIJOFS_BUTTON(55),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   251   { NULL,DIJOFS_BUTTON(56),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   252   { NULL,DIJOFS_BUTTON(57),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   253   { NULL,DIJOFS_BUTTON(58),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   254   { NULL,DIJOFS_BUTTON(59),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   255   { NULL,DIJOFS_BUTTON(60),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   256   { NULL,DIJOFS_BUTTON(61),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   257   { NULL,DIJOFS_BUTTON(62),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   258   { NULL,DIJOFS_BUTTON(63),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   259   { NULL,DIJOFS_BUTTON(64),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   260   { NULL,DIJOFS_BUTTON(65),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   261   { NULL,DIJOFS_BUTTON(66),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   262   { NULL,DIJOFS_BUTTON(67),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   263   { NULL,DIJOFS_BUTTON(68),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   264   { NULL,DIJOFS_BUTTON(69),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   265   { NULL,DIJOFS_BUTTON(70),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   266   { NULL,DIJOFS_BUTTON(71),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   267   { NULL,DIJOFS_BUTTON(72),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   268   { NULL,DIJOFS_BUTTON(73),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   269   { NULL,DIJOFS_BUTTON(74),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   270   { NULL,DIJOFS_BUTTON(75),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   271   { NULL,DIJOFS_BUTTON(76),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   272   { NULL,DIJOFS_BUTTON(77),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   273   { NULL,DIJOFS_BUTTON(78),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   274   { NULL,DIJOFS_BUTTON(79),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   275   { NULL,DIJOFS_BUTTON(80),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   276   { NULL,DIJOFS_BUTTON(81),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   277   { NULL,DIJOFS_BUTTON(82),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   278   { NULL,DIJOFS_BUTTON(83),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   279   { NULL,DIJOFS_BUTTON(84),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   280   { NULL,DIJOFS_BUTTON(85),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   281   { NULL,DIJOFS_BUTTON(86),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   282   { NULL,DIJOFS_BUTTON(87),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   283   { NULL,DIJOFS_BUTTON(88),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   284   { NULL,DIJOFS_BUTTON(89),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   285   { NULL,DIJOFS_BUTTON(90),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   286   { NULL,DIJOFS_BUTTON(91),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   287   { NULL,DIJOFS_BUTTON(92),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   288   { NULL,DIJOFS_BUTTON(93),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   289   { NULL,DIJOFS_BUTTON(94),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   290   { NULL,DIJOFS_BUTTON(95),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   291   { NULL,DIJOFS_BUTTON(96),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   292   { NULL,DIJOFS_BUTTON(97),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   293   { NULL,DIJOFS_BUTTON(98),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   294   { NULL,DIJOFS_BUTTON(99),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   295   { NULL,DIJOFS_BUTTON(100),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   296   { NULL,DIJOFS_BUTTON(101),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   297   { NULL,DIJOFS_BUTTON(102),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   298   { NULL,DIJOFS_BUTTON(103),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   299   { NULL,DIJOFS_BUTTON(104),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   300   { NULL,DIJOFS_BUTTON(105),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   301   { NULL,DIJOFS_BUTTON(106),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   302   { NULL,DIJOFS_BUTTON(107),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   303   { NULL,DIJOFS_BUTTON(108),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   304   { NULL,DIJOFS_BUTTON(109),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   305   { NULL,DIJOFS_BUTTON(110),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   306   { NULL,DIJOFS_BUTTON(111),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   307   { NULL,DIJOFS_BUTTON(112),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   308   { NULL,DIJOFS_BUTTON(113),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   309   { NULL,DIJOFS_BUTTON(114),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   310   { NULL,DIJOFS_BUTTON(115),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   311   { NULL,DIJOFS_BUTTON(116),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   312   { NULL,DIJOFS_BUTTON(117),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   313   { NULL,DIJOFS_BUTTON(118),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   314   { NULL,DIJOFS_BUTTON(119),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   315   { NULL,DIJOFS_BUTTON(120),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   316   { NULL,DIJOFS_BUTTON(121),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   317   { NULL,DIJOFS_BUTTON(122),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   318   { NULL,DIJOFS_BUTTON(123),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   319   { NULL,DIJOFS_BUTTON(124),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   320   { NULL,DIJOFS_BUTTON(125),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   321   { NULL,DIJOFS_BUTTON(126),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   322   { NULL,DIJOFS_BUTTON(127),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
   323   { &GUID_XAxis,FIELD_OFFSET(DIJOYSTATE2,lVX),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   324   { &GUID_YAxis,FIELD_OFFSET(DIJOYSTATE2,lVY),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   325   { &GUID_ZAxis,FIELD_OFFSET(DIJOYSTATE2,lVZ),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   326   { &GUID_RxAxis,FIELD_OFFSET(DIJOYSTATE2,lVRx),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   327   { &GUID_RyAxis,FIELD_OFFSET(DIJOYSTATE2,lVRy),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   328   { &GUID_RzAxis,FIELD_OFFSET(DIJOYSTATE2,lVRz),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   329   { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglVSlider[0]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   330   { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglVSlider[1]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   331   { &GUID_XAxis,FIELD_OFFSET(DIJOYSTATE2,lAX),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   332   { &GUID_YAxis,FIELD_OFFSET(DIJOYSTATE2,lAY),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   333   { &GUID_ZAxis,FIELD_OFFSET(DIJOYSTATE2,lAZ),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   334   { &GUID_RxAxis,FIELD_OFFSET(DIJOYSTATE2,lARx),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   335   { &GUID_RyAxis,FIELD_OFFSET(DIJOYSTATE2,lARy),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   336   { &GUID_RzAxis,FIELD_OFFSET(DIJOYSTATE2,lARz),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   337   { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglASlider[0]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   338   { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglASlider[1]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   339   { &GUID_XAxis,FIELD_OFFSET(DIJOYSTATE2,lFX),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   340   { &GUID_YAxis,FIELD_OFFSET(DIJOYSTATE2,lFY),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   341   { &GUID_ZAxis,FIELD_OFFSET(DIJOYSTATE2,lFZ),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   342   { &GUID_RxAxis,FIELD_OFFSET(DIJOYSTATE2,lFRx),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   343   { &GUID_RyAxis,FIELD_OFFSET(DIJOYSTATE2,lFRy),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   344   { &GUID_RzAxis,FIELD_OFFSET(DIJOYSTATE2,lFRz),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   345   { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglFSlider[0]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   346   { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglFSlider[1]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
   347 };
   348 
   349 const DIDATAFORMAT c_dfDIJoystick2 = {
   350     sizeof(DIDATAFORMAT),
   351     sizeof(DIOBJECTDATAFORMAT),
   352     DIDF_ABSAXIS,
   353     sizeof(DIJOYSTATE2),
   354     SDL_arraysize(dfDIJoystick2),
   355     dfDIJoystick2
   356 };
   357 
   358 
   359 /* Convert a DirectInput return code to a text message */
   360 static int
   361 SetDIerror(const char *function, HRESULT code)
   362 {
   363     /*
   364     return SDL_SetError("%s() [%s]: %s", function,
   365                  DXGetErrorString9A(code), DXGetErrorDescription9A(code));
   366      */
   367     return SDL_SetError("%s() DirectX error %d", function, code);
   368 }
   369 
   370 
   371 #define SAFE_RELEASE(p)                             \
   372 {                                                   \
   373     if (p) {                                        \
   374         (p)->lpVtbl->Release((p));                  \
   375         (p) = 0;                                    \
   376     }                                               \
   377 }
   378 
   379 DEFINE_GUID(IID_ValveStreamingGamepad,  MAKELONG( 0x28DE, 0x11FF ),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44);
   380 DEFINE_GUID(IID_X360WiredGamepad,  MAKELONG( 0x045E, 0x02A1 ),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44);
   381 DEFINE_GUID(IID_X360WirelessGamepad,  MAKELONG( 0x045E, 0x028E ),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44);
   382 
   383 static PRAWINPUTDEVICELIST SDL_RawDevList = NULL;
   384 static UINT SDL_RawDevListCount = 0;
   385 
   386 static SDL_bool
   387 SDL_IsXInputDevice( const GUID* pGuidProductFromDirectInput )
   388 {
   389     static const GUID *s_XInputProductGUID[] = {
   390         &IID_ValveStreamingGamepad,
   391         &IID_X360WiredGamepad,   /* Microsoft's wired X360 controller for Windows. */
   392         &IID_X360WirelessGamepad /* Microsoft's wireless X360 controller for Windows. */
   393     };
   394 
   395     size_t iDevice;
   396     UINT i;
   397 
   398     if (!s_bXInputEnabled) {
   399         return SDL_FALSE;
   400     }
   401 
   402     /* Check for well known XInput device GUIDs */
   403     /* This lets us skip RAWINPUT for popular devices. Also, we need to do this for the Valve Streaming Gamepad because it's virtualized and doesn't show up in the device list. */
   404     for ( iDevice = 0; iDevice < SDL_arraysize(s_XInputProductGUID); ++iDevice ) {
   405         if (SDL_memcmp(pGuidProductFromDirectInput, s_XInputProductGUID[iDevice], sizeof(GUID)) == 0) {
   406             return SDL_TRUE;
   407         }
   408     }
   409 
   410     /* Go through RAWINPUT (WinXP and later) to find HID devices. */
   411     /* Cache this if we end up using it. */
   412     if (SDL_RawDevList == NULL) {
   413         if ((GetRawInputDeviceList(NULL, &SDL_RawDevListCount, sizeof (RAWINPUTDEVICELIST)) == -1) || (!SDL_RawDevListCount)) {
   414             return SDL_FALSE;  /* oh well. */
   415         }
   416 
   417         SDL_RawDevList = (PRAWINPUTDEVICELIST) SDL_malloc(sizeof (RAWINPUTDEVICELIST) * SDL_RawDevListCount);
   418         if (SDL_RawDevList == NULL) {
   419             SDL_OutOfMemory();
   420             return SDL_FALSE;
   421         }
   422 
   423         if (GetRawInputDeviceList(SDL_RawDevList, &SDL_RawDevListCount, sizeof (RAWINPUTDEVICELIST)) == -1) {
   424              SDL_free(SDL_RawDevList);
   425              SDL_RawDevList = NULL;
   426              return SDL_FALSE;  /* oh well. */
   427         }
   428     }
   429 
   430     for (i = 0; i < SDL_RawDevListCount; i++) {
   431         RID_DEVICE_INFO rdi;
   432         char devName[128];
   433         UINT rdiSize = sizeof (rdi);
   434         UINT nameSize = SDL_arraysize(devName);
   435 
   436         rdi.cbSize = sizeof (rdi);
   437         if ( (SDL_RawDevList[i].dwType == RIM_TYPEHID) &&
   438              (GetRawInputDeviceInfoA(SDL_RawDevList[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != ((UINT)-1)) &&
   439              (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == ((LONG)pGuidProductFromDirectInput->Data1)) &&
   440              (GetRawInputDeviceInfoA(SDL_RawDevList[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != ((UINT)-1)) &&
   441              (SDL_strstr(devName, "IG_") != NULL) ) {
   442              return SDL_TRUE;
   443         }
   444     }
   445 
   446     return SDL_FALSE;
   447 }
   448 
   449 
   450 static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE;
   451 
   452 /* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal
   453  */
   454 LRESULT CALLBACK SDL_PrivateJoystickDetectProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)    {
   455     switch (message)    {
   456     case WM_DEVICECHANGE:
   457         switch (wParam) {
   458         case DBT_DEVICEARRIVAL:
   459             if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)    {
   460                 s_bWindowsDeviceChanged = SDL_TRUE;
   461             }
   462             break;
   463         case DBT_DEVICEREMOVECOMPLETE:
   464             if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)    {
   465                 s_bWindowsDeviceChanged = SDL_TRUE;
   466             }
   467             break;
   468         }
   469         return 0;
   470     }
   471 
   472     return DefWindowProc (hwnd, message, wParam, lParam);
   473 }
   474 
   475 
   476 DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, \
   477     0xC0, 0x4F, 0xB9, 0x51, 0xED);
   478 
   479 /* Function/thread to scan the system for joysticks.
   480  */
   481 static int
   482 SDL_JoystickThread(void *_data)
   483 {
   484     HWND messageWindow = 0;
   485     HDEVNOTIFY hNotify = 0;
   486     DEV_BROADCAST_DEVICEINTERFACE dbh;
   487     SDL_bool bOpenedXInputDevices[SDL_XINPUT_MAX_DEVICES];
   488     WNDCLASSEX wincl;
   489 
   490     SDL_zero(bOpenedXInputDevices);
   491 
   492     WIN_CoInitialize();
   493 
   494     SDL_memset( &wincl, 0x0, sizeof(wincl) );
   495     wincl.hInstance = GetModuleHandle( NULL );
   496     wincl.lpszClassName = L"Message";
   497     wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc;      /* This function is called by windows */
   498     wincl.cbSize = sizeof (WNDCLASSEX);
   499 
   500     if (!RegisterClassEx (&wincl))
   501     {
   502         return SDL_SetError("Failed to create register class for joystick autodetect.", GetLastError());
   503     }
   504 
   505     messageWindow = (HWND)CreateWindowEx( 0,  L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL );
   506     if ( !messageWindow )
   507     {
   508         return SDL_SetError("Failed to create message window for joystick autodetect.", GetLastError());
   509     }
   510 
   511     SDL_zero(dbh);
   512 
   513     dbh.dbcc_size = sizeof(dbh);
   514     dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
   515     dbh.dbcc_classguid = GUID_DEVINTERFACE_HID;
   516 
   517     hNotify = RegisterDeviceNotification( messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE );
   518     if ( !hNotify )
   519     {
   520         return SDL_SetError("Failed to create notify device for joystick autodetect.", GetLastError());
   521     }
   522 
   523     SDL_LockMutex( s_mutexJoyStickEnum );
   524     while ( s_bJoystickThreadQuit == SDL_FALSE )
   525     {
   526         MSG messages;
   527         SDL_bool bXInputChanged = SDL_FALSE;
   528 
   529         SDL_CondWaitTimeout( s_condJoystickThread, s_mutexJoyStickEnum, 300 );
   530 
   531         while ( s_bJoystickThreadQuit == SDL_FALSE && PeekMessage(&messages, messageWindow, 0, 0, PM_NOREMOVE) )
   532         {
   533             if ( GetMessage(&messages, messageWindow, 0, 0) != 0 )  {
   534                 TranslateMessage(&messages);
   535                 DispatchMessage(&messages);
   536             }
   537         }
   538 
   539         if ( s_bXInputEnabled && XINPUTGETCAPABILITIES ) {
   540             /* scan for any change in XInput devices */
   541             Uint8 userId;
   542             for (userId = 0; userId < SDL_XINPUT_MAX_DEVICES; userId++) {
   543                 XINPUT_CAPABILITIES capabilities;
   544                 const DWORD result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities );
   545                 const SDL_bool available = (result == ERROR_SUCCESS);
   546                 if (bOpenedXInputDevices[userId] != available) {
   547                     bXInputChanged = SDL_TRUE;
   548                     bOpenedXInputDevices[userId] = available;
   549                 }
   550             }
   551         }
   552 
   553         if (s_bWindowsDeviceChanged || bXInputChanged) {
   554             SDL_UnlockMutex( s_mutexJoyStickEnum );  /* let main thread go while we SDL_Delay(). */
   555             SDL_Delay( 300 ); /* wait for direct input to find out about this device */
   556             SDL_LockMutex( s_mutexJoyStickEnum );
   557 
   558             s_bDeviceRemoved = SDL_TRUE;
   559             s_bDeviceAdded = SDL_TRUE;
   560             s_bWindowsDeviceChanged = SDL_FALSE;
   561         }
   562     }
   563     SDL_UnlockMutex( s_mutexJoyStickEnum );
   564 
   565     if ( hNotify )
   566         UnregisterDeviceNotification( hNotify );
   567 
   568     if ( messageWindow )
   569         DestroyWindow( messageWindow );
   570 
   571     UnregisterClass( wincl.lpszClassName, wincl.hInstance );
   572     messageWindow = 0;
   573     WIN_CoUninitialize();
   574     return 1;
   575 }
   576 
   577 
   578 /* Function to scan the system for joysticks.
   579  * This function should set SDL_numjoysticks to the number of available
   580  * joysticks.  Joystick 0 should be the system default joystick.
   581  * It should return 0, or -1 on an unrecoverable fatal error.
   582  */
   583 int
   584 SDL_SYS_JoystickInit(void)
   585 {
   586     HRESULT result;
   587     HINSTANCE instance;
   588     const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
   589     if (env && !SDL_atoi(env)) {
   590         s_bXInputEnabled = SDL_FALSE;
   591     }
   592 
   593     result = WIN_CoInitialize();
   594     if (FAILED(result)) {
   595         return SetDIerror("CoInitialize", result);
   596     }
   597 
   598     coinitialized = SDL_TRUE;
   599 
   600     result = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER,
   601                               &IID_IDirectInput8, (LPVOID)&dinput);
   602 
   603     if (FAILED(result)) {
   604         SDL_SYS_JoystickQuit();
   605         return SetDIerror("CoCreateInstance", result);
   606     }
   607 
   608     /* Because we used CoCreateInstance, we need to Initialize it, first. */
   609     instance = GetModuleHandle(NULL);
   610     if (instance == NULL) {
   611         SDL_SYS_JoystickQuit();
   612         return SDL_SetError("GetModuleHandle() failed with error code %d.", GetLastError());
   613     }
   614     result = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION);
   615 
   616     if (FAILED(result)) {
   617         SDL_SYS_JoystickQuit();
   618         return SetDIerror("IDirectInput::Initialize", result);
   619     }
   620 
   621     if ((s_bXInputEnabled) && (WIN_LoadXInputDLL() == -1)) {
   622         s_bXInputEnabled = SDL_FALSE;  /* oh well. */
   623     }
   624 
   625     s_mutexJoyStickEnum = SDL_CreateMutex();
   626     s_condJoystickThread = SDL_CreateCond();
   627     s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */
   628 
   629     SDL_SYS_JoystickDetect();
   630 
   631     if ( !s_threadJoystick )
   632     {
   633         s_bJoystickThreadQuit = SDL_FALSE;
   634         /* spin up the thread to detect hotplug of devices */
   635 #if defined(__WIN32__) && !defined(HAVE_LIBC)
   636 #undef SDL_CreateThread
   637 #if SDL_DYNAMIC_API
   638         s_threadJoystick= SDL_CreateThread_REAL( SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL );
   639 #else
   640         s_threadJoystick= SDL_CreateThread( SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL );
   641 #endif
   642 #else
   643         s_threadJoystick = SDL_CreateThread( SDL_JoystickThread, "SDL_joystick", NULL );
   644 #endif
   645     }
   646         return SDL_SYS_NumJoysticks();
   647 }
   648 
   649 /* return the number of joysticks that are connected right now */
   650 int SDL_SYS_NumJoysticks()
   651 {
   652     int nJoysticks = 0;
   653     JoyStick_DeviceData *device = SYS_Joystick;
   654     while ( device )
   655     {
   656         nJoysticks++;
   657         device = device->pNext;
   658     }
   659 
   660     return nJoysticks;
   661 }
   662 
   663 /* helper function for direct input, gets called for each connected joystick */
   664 static BOOL CALLBACK
   665     EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
   666 {
   667     JoyStick_DeviceData *pNewJoystick;
   668     JoyStick_DeviceData *pPrevJoystick = NULL;
   669 
   670     if (SDL_IsXInputDevice( &pdidInstance->guidProduct )) {
   671         return DIENUM_CONTINUE;  /* ignore XInput devices here, keep going. */
   672     }
   673 
   674     pNewJoystick = *(JoyStick_DeviceData **)pContext;
   675     while ( pNewJoystick )
   676     {
   677         if ( !SDL_memcmp( &pNewJoystick->dxdevice.guidInstance, &pdidInstance->guidInstance, sizeof(pNewJoystick->dxdevice.guidInstance) ) )
   678         {
   679             /* if we are replacing the front of the list then update it */
   680             if ( pNewJoystick == *(JoyStick_DeviceData **)pContext )
   681             {
   682                 *(JoyStick_DeviceData **)pContext = pNewJoystick->pNext;
   683             }
   684             else if ( pPrevJoystick )
   685             {
   686                 pPrevJoystick->pNext = pNewJoystick->pNext;
   687             }
   688 
   689             pNewJoystick->pNext = SYS_Joystick;
   690             SYS_Joystick = pNewJoystick;
   691 
   692             return DIENUM_CONTINUE; /* already have this joystick loaded, just keep going */
   693         }
   694 
   695         pPrevJoystick = pNewJoystick;
   696         pNewJoystick = pNewJoystick->pNext;
   697     }
   698 
   699     pNewJoystick = (JoyStick_DeviceData *)SDL_malloc( sizeof(JoyStick_DeviceData) );
   700     if (!pNewJoystick) {
   701         return DIENUM_CONTINUE; /* better luck next time? */
   702     }
   703 
   704     SDL_zerop(pNewJoystick);
   705     pNewJoystick->joystickname = WIN_StringToUTF8(pdidInstance->tszProductName);
   706     if (!pNewJoystick->joystickname) {
   707         SDL_free(pNewJoystick);
   708         return DIENUM_CONTINUE; /* better luck next time? */
   709     }
   710 
   711     SDL_memcpy(&(pNewJoystick->dxdevice), pdidInstance,
   712         sizeof(DIDEVICEINSTANCE));
   713 
   714     pNewJoystick->XInputUserId = INVALID_XINPUT_USERID;
   715     pNewJoystick->send_add_event = 1;
   716     pNewJoystick->nInstanceID = ++s_nInstanceID;
   717     SDL_memcpy( &pNewJoystick->guid, &pdidInstance->guidProduct, sizeof(pNewJoystick->guid) );
   718     pNewJoystick->pNext = SYS_Joystick;
   719     SYS_Joystick = pNewJoystick;
   720 
   721     s_bDeviceAdded = SDL_TRUE;
   722 
   723     return DIENUM_CONTINUE; /* get next device, please */
   724 }
   725 
   726 static void
   727 AddXInputDevice(const Uint8 userid, JoyStick_DeviceData **pContext)
   728 {
   729     char name[32];
   730     JoyStick_DeviceData *pPrevJoystick = NULL;
   731     JoyStick_DeviceData *pNewJoystick = *pContext;
   732 
   733     while (pNewJoystick) {
   734         if ((pNewJoystick->bXInputDevice) && (pNewJoystick->XInputUserId == userid)) {
   735             /* if we are replacing the front of the list then update it */
   736             if (pNewJoystick == *pContext) {
   737                 *pContext = pNewJoystick->pNext;
   738             } else if (pPrevJoystick) {
   739                 pPrevJoystick->pNext = pNewJoystick->pNext;
   740             }
   741 
   742             pNewJoystick->pNext = SYS_Joystick;
   743             SYS_Joystick = pNewJoystick;
   744             return;   /* already in the list. */
   745         }
   746 
   747         pPrevJoystick = pNewJoystick;
   748         pNewJoystick = pNewJoystick->pNext;
   749     }
   750 
   751     pNewJoystick = (JoyStick_DeviceData *) SDL_malloc(sizeof (JoyStick_DeviceData));
   752     if (!pNewJoystick) {
   753         return; /* better luck next time? */
   754     }
   755     SDL_zerop(pNewJoystick);
   756 
   757     SDL_snprintf(name, sizeof (name), "XInput Controller #%u", ((unsigned int) userid) + 1);
   758     pNewJoystick->joystickname = SDL_strdup(name);
   759     if (!pNewJoystick->joystickname) {
   760         SDL_free(pNewJoystick);
   761         return; /* better luck next time? */
   762     }
   763 
   764     pNewJoystick->bXInputDevice = SDL_TRUE;
   765     pNewJoystick->XInputUserId = userid;
   766     pNewJoystick->send_add_event = 1;
   767     pNewJoystick->nInstanceID = ++s_nInstanceID;
   768     pNewJoystick->pNext = SYS_Joystick;
   769     SYS_Joystick = pNewJoystick;
   770 
   771     s_bDeviceAdded = SDL_TRUE;
   772 }
   773 
   774 static void
   775 EnumXInputDevices(JoyStick_DeviceData **pContext)
   776 {
   777     if (s_bXInputEnabled) {
   778         int iuserid;
   779         /* iterate in reverse, so these are in the final list in ascending numeric order. */
   780         for (iuserid = SDL_XINPUT_MAX_DEVICES-1; iuserid >= 0; iuserid--) {
   781             const Uint8 userid = (Uint8) iuserid;
   782             XINPUT_CAPABILITIES capabilities;
   783             if (XINPUTGETCAPABILITIES(userid, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) {
   784                 /* Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad. */
   785                 /* !!! FIXME: we might want to support steering wheels or guitars or whatever later. */
   786                 if (capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD) {
   787                     AddXInputDevice(userid, pContext);
   788                 }
   789             }
   790         }
   791     }
   792 }
   793 
   794 
   795 /* detect any new joysticks being inserted into the system */
   796 void SDL_SYS_JoystickDetect()
   797 {
   798     JoyStick_DeviceData *pCurList = NULL;
   799     /* only enum the devices if the joystick thread told us something changed */
   800     if ( s_bDeviceAdded || s_bDeviceRemoved )
   801     {
   802         SDL_LockMutex( s_mutexJoyStickEnum );
   803 
   804         s_bDeviceAdded = SDL_FALSE;
   805         s_bDeviceRemoved = SDL_FALSE;
   806 
   807         pCurList = SYS_Joystick;
   808         SYS_Joystick = NULL;
   809 
   810         /* Look for DirectInput joysticks, wheels, head trackers, gamepads, etc.. */
   811         IDirectInput8_EnumDevices(dinput,
   812             DI8DEVCLASS_GAMECTRL,
   813             EnumJoysticksCallback,
   814             &pCurList, DIEDFL_ATTACHEDONLY);
   815 
   816         SDL_free(SDL_RawDevList);  /* in case we used this in DirectInput enumerator. */
   817         SDL_RawDevList = NULL;
   818         SDL_RawDevListCount = 0;
   819 
   820         /* Look for XInput devices. Do this last, so they're first in the final list. */
   821         EnumXInputDevices(&pCurList);
   822 
   823         SDL_UnlockMutex( s_mutexJoyStickEnum );
   824     }
   825 
   826     if ( pCurList )
   827     {
   828         while ( pCurList )
   829         {
   830             JoyStick_DeviceData *pListNext = NULL;
   831 
   832 #if SDL_HAPTIC_DINPUT
   833             if (pCurList->bXInputDevice) {
   834                 XInputHaptic_MaybeRemoveDevice(pCurList->XInputUserId);
   835             } else {
   836                 DirectInputHaptic_MaybeRemoveDevice(&pCurList->dxdevice);
   837             }
   838 #endif
   839 
   840 #if !SDL_EVENTS_DISABLED
   841             {
   842             SDL_Event event;
   843             event.type = SDL_JOYDEVICEREMOVED;
   844 
   845             if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   846                 event.jdevice.which = pCurList->nInstanceID;
   847                 if ((SDL_EventOK == NULL)
   848                     || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   849                         SDL_PushEvent(&event);
   850                 }
   851             }
   852             }
   853 #endif /* !SDL_EVENTS_DISABLED */
   854 
   855             pListNext = pCurList->pNext;
   856             SDL_free(pCurList->joystickname);
   857             SDL_free(pCurList);
   858             pCurList = pListNext;
   859         }
   860 
   861     }
   862 
   863     if ( s_bDeviceAdded )
   864     {
   865         JoyStick_DeviceData *pNewJoystick;
   866         int device_index = 0;
   867         s_bDeviceAdded = SDL_FALSE;
   868         pNewJoystick = SYS_Joystick;
   869         while ( pNewJoystick )
   870         {
   871             if ( pNewJoystick->send_add_event )
   872             {
   873 #if SDL_HAPTIC_DINPUT
   874                 if (pNewJoystick->bXInputDevice) {
   875                     XInputHaptic_MaybeAddDevice(pNewJoystick->XInputUserId);
   876                 } else {
   877                     DirectInputHaptic_MaybeAddDevice(&pNewJoystick->dxdevice);
   878                 }
   879 #endif
   880 
   881 #if !SDL_EVENTS_DISABLED
   882                 {
   883                 SDL_Event event;
   884                 event.type = SDL_JOYDEVICEADDED;
   885 
   886                 if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   887                     event.jdevice.which = device_index;
   888                     if ((SDL_EventOK == NULL)
   889                         || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   890                             SDL_PushEvent(&event);
   891                     }
   892                 }
   893                 }
   894 #endif /* !SDL_EVENTS_DISABLED */
   895                 pNewJoystick->send_add_event = 0;
   896             }
   897             device_index++;
   898             pNewJoystick = pNewJoystick->pNext;
   899         }
   900     }
   901 }
   902 
   903 /* we need to poll if we have pending hotplug device changes or connected devices */
   904 SDL_bool SDL_SYS_JoystickNeedsPolling()
   905 {
   906     /* we have a new device or one was pulled, we need to think this frame please */
   907     if ( s_bDeviceAdded || s_bDeviceRemoved )
   908         return SDL_TRUE;
   909 
   910     return SDL_FALSE;
   911 }
   912 
   913 /* Function to get the device-dependent name of a joystick */
   914 const char *
   915 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
   916 {
   917     JoyStick_DeviceData *device = SYS_Joystick;
   918 
   919     for (; device_index > 0; device_index--)
   920         device = device->pNext;
   921 
   922     return device->joystickname;
   923 }
   924 
   925 /* Function to perform the mapping between current device instance and this joysticks instance id */
   926 SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
   927 {
   928     JoyStick_DeviceData *device = SYS_Joystick;
   929     int index;
   930 
   931     for (index = device_index; index > 0; index--)
   932         device = device->pNext;
   933 
   934     return device->nInstanceID;
   935 }
   936 
   937 /* Function to open a joystick for use.
   938    The joystick to open is specified by the index field of the joystick.
   939    This should fill the nbuttons and naxes fields of the joystick structure.
   940    It returns 0, or -1 if there is an error.
   941  */
   942 int
   943 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
   944 {
   945     HRESULT result;
   946     JoyStick_DeviceData *joystickdevice = SYS_Joystick;
   947 
   948     for (; device_index > 0; device_index--)
   949         joystickdevice = joystickdevice->pNext;
   950 
   951     /* allocate memory for system specific hardware data */
   952     joystick->instance_id = joystickdevice->nInstanceID;
   953     joystick->closed = 0;
   954     joystick->hwdata =
   955         (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
   956     if (joystick->hwdata == NULL) {
   957         return SDL_OutOfMemory();
   958     }
   959     SDL_zerop(joystick->hwdata);
   960 
   961     if (joystickdevice->bXInputDevice) {
   962         const Uint8 userId = joystickdevice->XInputUserId;
   963         XINPUT_CAPABILITIES capabilities;
   964         XINPUT_VIBRATION state;
   965 
   966         SDL_assert(s_bXInputEnabled);
   967         SDL_assert(XINPUTGETCAPABILITIES);
   968         SDL_assert(XINPUTSETSTATE);
   969         SDL_assert(userId >= 0);
   970         SDL_assert(userId < SDL_XINPUT_MAX_DEVICES);
   971 
   972         joystick->hwdata->bXInputDevice = SDL_TRUE;
   973 
   974         if (XINPUTGETCAPABILITIES(userId, XINPUT_FLAG_GAMEPAD, &capabilities) != ERROR_SUCCESS) {
   975             SDL_free(joystick->hwdata);
   976             joystick->hwdata = NULL;
   977             return SDL_SetError("Failed to obtain XInput device capabilities. Device disconnected?");
   978         } else {
   979             /* Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad. */
   980             SDL_assert(capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD);
   981             SDL_zero(state);
   982             joystick->hwdata->bXInputHaptic = (XINPUTSETSTATE(userId, &state) == ERROR_SUCCESS);
   983             joystick->hwdata->userid = userId;
   984 
   985             /* The XInput API has a hard coded button/axis mapping, so we just match it */
   986             joystick->naxes = 6;
   987             joystick->nbuttons = 15;
   988             joystick->nballs = 0;
   989             joystick->nhats = 0;
   990         }
   991     } else {  /* use DirectInput, not XInput. */
   992         LPDIRECTINPUTDEVICE8 device;
   993         DIPROPDWORD dipdw;
   994 
   995         joystick->hwdata->buffered = 1;
   996         joystick->hwdata->removed = 0;
   997         joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS);
   998         joystick->hwdata->guid = joystickdevice->guid;
   999 
  1000         SDL_zero(dipdw);
  1001         dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  1002         dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  1003 
  1004         result =
  1005             IDirectInput8_CreateDevice(dinput,
  1006                                       &(joystickdevice->dxdevice.guidInstance), &device, NULL);
  1007         if (FAILED(result)) {
  1008             return SetDIerror("IDirectInput::CreateDevice", result);
  1009         }
  1010 
  1011         /* Now get the IDirectInputDevice8 interface, instead. */
  1012         result = IDirectInputDevice8_QueryInterface(device,
  1013                                                    &IID_IDirectInputDevice8,
  1014                                                    (LPVOID *) & joystick->
  1015                                                    hwdata->InputDevice);
  1016         /* We are done with this object.  Use the stored one from now on. */
  1017         IDirectInputDevice8_Release(device);
  1018 
  1019         if (FAILED(result)) {
  1020             return SetDIerror("IDirectInputDevice8::QueryInterface", result);
  1021         }
  1022 
  1023         /* Acquire shared access. Exclusive access is required for forces,
  1024          * though. */
  1025         result =
  1026             IDirectInputDevice8_SetCooperativeLevel(joystick->hwdata->
  1027                                                     InputDevice, SDL_HelperWindow,
  1028                                                     DISCL_EXCLUSIVE |
  1029                                                     DISCL_BACKGROUND);
  1030         if (FAILED(result)) {
  1031             return SetDIerror("IDirectInputDevice8::SetCooperativeLevel", result);
  1032         }
  1033 
  1034         /* Use the extended data structure: DIJOYSTATE2. */
  1035         result =
  1036             IDirectInputDevice8_SetDataFormat(joystick->hwdata->InputDevice,
  1037                                               &c_dfDIJoystick2);
  1038         if (FAILED(result)) {
  1039             return SetDIerror("IDirectInputDevice8::SetDataFormat", result);
  1040         }
  1041 
  1042         /* Get device capabilities */
  1043         result =
  1044             IDirectInputDevice8_GetCapabilities(joystick->hwdata->InputDevice,
  1045                                                 &joystick->hwdata->Capabilities);
  1046 
  1047         if (FAILED(result)) {
  1048             return SetDIerror("IDirectInputDevice8::GetCapabilities", result);
  1049         }
  1050 
  1051         /* Force capable? */
  1052         if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
  1053 
  1054             result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
  1055 
  1056             if (FAILED(result)) {
  1057                 return SetDIerror("IDirectInputDevice8::Acquire", result);
  1058             }
  1059 
  1060             /* reset all accuators. */
  1061             result =
  1062                 IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->
  1063                                                              InputDevice,
  1064                                                              DISFFC_RESET);
  1065 
  1066             /* Not necessarily supported, ignore if not supported.
  1067             if (FAILED(result)) {
  1068                 return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand", result);
  1069             }
  1070             */
  1071 
  1072             result = IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
  1073 
  1074             if (FAILED(result)) {
  1075                 return SetDIerror("IDirectInputDevice8::Unacquire", result);
  1076             }
  1077 
  1078             /* Turn on auto-centering for a ForceFeedback device (until told
  1079              * otherwise). */
  1080             dipdw.diph.dwObj = 0;
  1081             dipdw.diph.dwHow = DIPH_DEVICE;
  1082             dipdw.dwData = DIPROPAUTOCENTER_ON;
  1083 
  1084             result =
  1085                 IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
  1086                                                 DIPROP_AUTOCENTER, &dipdw.diph);
  1087 
  1088             /* Not necessarily supported, ignore if not supported.
  1089             if (FAILED(result)) {
  1090                 return SetDIerror("IDirectInputDevice8::SetProperty", result);
  1091             }
  1092             */
  1093         }
  1094 
  1095         /* What buttons and axes does it have? */
  1096         IDirectInputDevice8_EnumObjects(joystick->hwdata->InputDevice,
  1097                                         EnumDevObjectsCallback, joystick,
  1098                                         DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);
  1099 
  1100         /* Reorder the input objects. Some devices do not report the X axis as
  1101          * the first axis, for example. */
  1102         SortDevObjects(joystick);
  1103 
  1104         dipdw.diph.dwObj = 0;
  1105         dipdw.diph.dwHow = DIPH_DEVICE;
  1106         dipdw.dwData = INPUT_QSIZE;
  1107 
  1108         /* Set the buffer size */
  1109         result =
  1110             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
  1111                                             DIPROP_BUFFERSIZE, &dipdw.diph);
  1112 
  1113         if (result == DI_POLLEDDEVICE) {
  1114             /* This device doesn't support buffering, so we're forced
  1115              * to use less reliable polling. */
  1116             joystick->hwdata->buffered = 0;
  1117         } else if (FAILED(result)) {
  1118             return SetDIerror("IDirectInputDevice8::SetProperty", result);
  1119         }
  1120     }
  1121     return (0);
  1122 }
  1123 
  1124 /* return true if this joystick is plugged in right now */
  1125 SDL_bool SDL_SYS_JoystickAttached( SDL_Joystick * joystick )
  1126 {
  1127     return joystick->closed == 0 && joystick->hwdata->removed == 0;
  1128 }
  1129 
  1130 
  1131 /* Sort using the data offset into the DInput struct.
  1132  * This gives a reasonable ordering for the inputs. */
  1133 static int
  1134 SortDevFunc(const void *a, const void *b)
  1135 {
  1136     const input_t *inputA = (const input_t*)a;
  1137     const input_t *inputB = (const input_t*)b;
  1138 
  1139     if (inputA->ofs < inputB->ofs)
  1140         return -1;
  1141     if (inputA->ofs > inputB->ofs)
  1142         return 1;
  1143     return 0;
  1144 }
  1145 
  1146 /* Sort the input objects and recalculate the indices for each input. */
  1147 static void
  1148 SortDevObjects(SDL_Joystick *joystick)
  1149 {
  1150     input_t *inputs = joystick->hwdata->Inputs;
  1151     int nButtons = 0;
  1152     int nHats = 0;
  1153     int nAxis = 0;
  1154     int n;
  1155 
  1156     SDL_qsort(inputs, joystick->hwdata->NumInputs, sizeof(input_t), SortDevFunc);
  1157 
  1158     for (n = 0; n < joystick->hwdata->NumInputs; n++)
  1159     {
  1160         switch (inputs[n].type)
  1161         {
  1162         case BUTTON:
  1163             inputs[n].num = nButtons;
  1164             nButtons++;
  1165             break;
  1166 
  1167         case HAT:
  1168             inputs[n].num = nHats;
  1169             nHats++;
  1170             break;
  1171 
  1172         case AXIS:
  1173             inputs[n].num = nAxis;
  1174             nAxis++;
  1175             break;
  1176         }
  1177     }
  1178 }
  1179 
  1180 static BOOL CALLBACK
  1181 EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
  1182 {
  1183     SDL_Joystick *joystick = (SDL_Joystick *) pvRef;
  1184     HRESULT result;
  1185     input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs];
  1186 
  1187     if (dev->dwType & DIDFT_BUTTON) {
  1188         in->type = BUTTON;
  1189         in->num = joystick->nbuttons;
  1190         in->ofs = DIJOFS_BUTTON( in->num );
  1191         joystick->nbuttons++;
  1192     } else if (dev->dwType & DIDFT_POV) {
  1193         in->type = HAT;
  1194         in->num = joystick->nhats;
  1195         in->ofs = DIJOFS_POV( in->num );
  1196         joystick->nhats++;
  1197     } else if (dev->dwType & DIDFT_AXIS) {
  1198         DIPROPRANGE diprg;
  1199         DIPROPDWORD dilong;
  1200 
  1201         in->type = AXIS;
  1202         in->num = joystick->naxes;
  1203         /* work our the axis this guy maps too, thanks for the code icculus! */
  1204         if ( !SDL_memcmp( &dev->guidType, &GUID_XAxis, sizeof(dev->guidType) ) )
  1205             in->ofs = DIJOFS_X;
  1206         else if ( !SDL_memcmp( &dev->guidType, &GUID_YAxis, sizeof(dev->guidType) ) )
  1207             in->ofs = DIJOFS_Y;
  1208         else if ( !SDL_memcmp( &dev->guidType, &GUID_ZAxis, sizeof(dev->guidType) ) )
  1209             in->ofs = DIJOFS_Z;
  1210         else if ( !SDL_memcmp( &dev->guidType, &GUID_RxAxis, sizeof(dev->guidType) ) )
  1211             in->ofs = DIJOFS_RX;
  1212         else if ( !SDL_memcmp( &dev->guidType, &GUID_RyAxis, sizeof(dev->guidType) ) )
  1213             in->ofs = DIJOFS_RY;
  1214         else if ( !SDL_memcmp( &dev->guidType, &GUID_RzAxis, sizeof(dev->guidType) ) )
  1215             in->ofs = DIJOFS_RZ;
  1216         else if ( !SDL_memcmp( &dev->guidType, &GUID_Slider, sizeof(dev->guidType) ) )
  1217         {
  1218             in->ofs = DIJOFS_SLIDER( joystick->hwdata->NumSliders );
  1219             ++joystick->hwdata->NumSliders;
  1220         }
  1221         else
  1222         {
  1223              return DIENUM_CONTINUE; /* not an axis we can grok */
  1224         }
  1225 
  1226         diprg.diph.dwSize = sizeof(diprg);
  1227         diprg.diph.dwHeaderSize = sizeof(diprg.diph);
  1228         diprg.diph.dwObj = dev->dwType;
  1229         diprg.diph.dwHow = DIPH_BYID;
  1230         diprg.lMin = AXIS_MIN;
  1231         diprg.lMax = AXIS_MAX;
  1232 
  1233         result =
  1234             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
  1235                                             DIPROP_RANGE, &diprg.diph);
  1236         if (FAILED(result)) {
  1237             return DIENUM_CONTINUE;     /* don't use this axis */
  1238         }
  1239 
  1240         /* Set dead zone to 0. */
  1241         dilong.diph.dwSize = sizeof(dilong);
  1242         dilong.diph.dwHeaderSize = sizeof(dilong.diph);
  1243         dilong.diph.dwObj = dev->dwType;
  1244         dilong.diph.dwHow = DIPH_BYID;
  1245         dilong.dwData = 0;
  1246         result =
  1247             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
  1248                                             DIPROP_DEADZONE, &dilong.diph);
  1249         if (FAILED(result)) {
  1250             return DIENUM_CONTINUE;     /* don't use this axis */
  1251         }
  1252 
  1253         joystick->naxes++;
  1254     } else {
  1255         /* not supported at this time */
  1256         return DIENUM_CONTINUE;
  1257     }
  1258 
  1259     joystick->hwdata->NumInputs++;
  1260 
  1261     if (joystick->hwdata->NumInputs == MAX_INPUTS) {
  1262         return DIENUM_STOP;     /* too many */
  1263     }
  1264 
  1265     return DIENUM_CONTINUE;
  1266 }
  1267 
  1268 /* Function to update the state of a joystick - called as a device poll.
  1269  * This function shouldn't update the joystick structure directly,
  1270  * but instead should call SDL_PrivateJoystick*() to deliver events
  1271  * and update joystick device state.
  1272  */
  1273 void
  1274 SDL_SYS_JoystickUpdate_Polled(SDL_Joystick * joystick)
  1275 {
  1276     DIJOYSTATE2 state;
  1277     HRESULT result;
  1278     int i;
  1279 
  1280     result =
  1281         IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
  1282                                            sizeof(DIJOYSTATE2), &state);
  1283     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
  1284         IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
  1285         result =
  1286             IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
  1287                                                sizeof(DIJOYSTATE2), &state);
  1288     }
  1289 
  1290     if ( result != DI_OK )
  1291     {
  1292         joystick->hwdata->send_remove_event = 1;
  1293         joystick->hwdata->removed = 1;
  1294         return;
  1295     }
  1296 
  1297     /* Set each known axis, button and POV. */
  1298     for (i = 0; i < joystick->hwdata->NumInputs; ++i) {
  1299         const input_t *in = &joystick->hwdata->Inputs[i];
  1300 
  1301         switch (in->type) {
  1302         case AXIS:
  1303             switch (in->ofs) {
  1304             case DIJOFS_X:
  1305                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1306                                             (Sint16) state.lX);
  1307                 break;
  1308             case DIJOFS_Y:
  1309                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1310                                             (Sint16) state.lY);
  1311                 break;
  1312             case DIJOFS_Z:
  1313                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1314                                             (Sint16) state.lZ);
  1315                 break;
  1316             case DIJOFS_RX:
  1317                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1318                                             (Sint16) state.lRx);
  1319                 break;
  1320             case DIJOFS_RY:
  1321                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1322                                             (Sint16) state.lRy);
  1323                 break;
  1324             case DIJOFS_RZ:
  1325                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1326                                             (Sint16) state.lRz);
  1327                 break;
  1328             case DIJOFS_SLIDER(0):
  1329                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1330                                             (Sint16) state.rglSlider[0]);
  1331                 break;
  1332             case DIJOFS_SLIDER(1):
  1333                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1334                                             (Sint16) state.rglSlider[1]);
  1335                 break;
  1336             }
  1337 
  1338             break;
  1339 
  1340         case BUTTON:
  1341             SDL_PrivateJoystickButton_Int(joystick, in->num,
  1342                                           (Uint8) (state.
  1343                                                    rgbButtons[in->ofs -
  1344                                                               DIJOFS_BUTTON0]
  1345                                                    ? SDL_PRESSED :
  1346                                                    SDL_RELEASED));
  1347             break;
  1348         case HAT:
  1349             {
  1350                 Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs -
  1351                                                        DIJOFS_POV(0)]);
  1352                 SDL_PrivateJoystickHat_Int(joystick, in->num, pos);
  1353                 break;
  1354             }
  1355         }
  1356     }
  1357 }
  1358 
  1359 void
  1360 SDL_SYS_JoystickUpdate_Buffered(SDL_Joystick * joystick)
  1361 {
  1362     int i;
  1363     HRESULT result;
  1364     DWORD numevents;
  1365     DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
  1366 
  1367     numevents = INPUT_QSIZE;
  1368     result =
  1369         IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
  1370                                           sizeof(DIDEVICEOBJECTDATA), evtbuf,
  1371                                           &numevents, 0);
  1372     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
  1373         IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
  1374         result =
  1375             IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
  1376                                               sizeof(DIDEVICEOBJECTDATA),
  1377                                               evtbuf, &numevents, 0);
  1378     }
  1379 
  1380     /* Handle the events or punt */
  1381     if (FAILED(result))
  1382     {
  1383         joystick->hwdata->send_remove_event = 1;
  1384         joystick->hwdata->removed = 1;
  1385         return;
  1386     }
  1387 
  1388     for (i = 0; i < (int) numevents; ++i) {
  1389         int j;
  1390 
  1391         for (j = 0; j < joystick->hwdata->NumInputs; ++j) {
  1392             const input_t *in = &joystick->hwdata->Inputs[j];
  1393 
  1394             if (evtbuf[i].dwOfs != in->ofs)
  1395                 continue;
  1396 
  1397             switch (in->type) {
  1398             case AXIS:
  1399                 SDL_PrivateJoystickAxis(joystick, in->num,
  1400                                         (Sint16) evtbuf[i].dwData);
  1401                 break;
  1402             case BUTTON:
  1403                 SDL_PrivateJoystickButton(joystick, in->num,
  1404                                           (Uint8) (evtbuf[i].
  1405                                                    dwData ? SDL_PRESSED :
  1406                                                    SDL_RELEASED));
  1407                 break;
  1408             case HAT:
  1409                 {
  1410                     Uint8 pos = TranslatePOV(evtbuf[i].dwData);
  1411                     SDL_PrivateJoystickHat(joystick, in->num, pos);
  1412                 }
  1413             }
  1414         }
  1415     }
  1416 }
  1417 
  1418 
  1419 /* Function to return > 0 if a bit array of buttons differs after applying a mask
  1420 */
  1421 int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask )
  1422 {
  1423     return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask );
  1424 }
  1425 
  1426 /* Function to update the state of a XInput style joystick.
  1427 */
  1428 void
  1429 SDL_SYS_JoystickUpdate_XInput(SDL_Joystick * joystick)
  1430 {
  1431     HRESULT result;
  1432 
  1433     if ( !XINPUTGETSTATE )
  1434         return;
  1435 
  1436     result = XINPUTGETSTATE( joystick->hwdata->userid, &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot] );
  1437     if ( result == ERROR_DEVICE_NOT_CONNECTED )
  1438     {
  1439         joystick->hwdata->send_remove_event = 1;
  1440         joystick->hwdata->removed = 1;
  1441         return;
  1442     }
  1443 
  1444     /* only fire events if the data changed from last time */
  1445     if ( joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot].dwPacketNumber != 0
  1446         && joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot].dwPacketNumber != joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot^1].dwPacketNumber )
  1447     {
  1448         XINPUT_STATE_EX *pXInputState = &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot];
  1449         XINPUT_STATE_EX *pXInputStatePrev = &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot ^ 1];
  1450 
  1451         /* !!! FIXME: why isn't this just using SDL_PrivateJoystickAxis_Int()? */
  1452         SDL_PrivateJoystickAxis( joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX );
  1453         SDL_PrivateJoystickAxis( joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)) );
  1454         SDL_PrivateJoystickAxis( joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX );
  1455         SDL_PrivateJoystickAxis( joystick, 3, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)) );
  1456         SDL_PrivateJoystickAxis( joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger*65535/255) - 32768));
  1457         SDL_PrivateJoystickAxis( joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger*65535/255) - 32768));
  1458 
  1459         /* !!! FIXME: why isn't this just using SDL_PrivateJoystickButton_Int(), instead of keeping these two alternating state buffers? */
  1460         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP ) )
  1461             SDL_PrivateJoystickButton(joystick, 0, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ? SDL_PRESSED :  SDL_RELEASED );
  1462         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN ) )
  1463             SDL_PrivateJoystickButton(joystick, 1, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ? SDL_PRESSED :    SDL_RELEASED );
  1464         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT ) )
  1465             SDL_PrivateJoystickButton(joystick, 2, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ? SDL_PRESSED :    SDL_RELEASED );
  1466         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ) )
  1467             SDL_PrivateJoystickButton(joystick, 3, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? SDL_PRESSED :   SDL_RELEASED );
  1468         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_START ) )
  1469             SDL_PrivateJoystickButton(joystick, 4, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_START ? SDL_PRESSED :    SDL_RELEASED );
  1470         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_BACK ) )
  1471             SDL_PrivateJoystickButton(joystick, 5, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_BACK ? SDL_PRESSED : SDL_RELEASED );
  1472         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB ) )
  1473             SDL_PrivateJoystickButton(joystick, 6, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ? SDL_PRESSED :   SDL_RELEASED );
  1474         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB ) )
  1475             SDL_PrivateJoystickButton(joystick, 7, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ? SDL_PRESSED :  SDL_RELEASED );
  1476         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ) )
  1477             SDL_PrivateJoystickButton(joystick, 8, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER ? SDL_PRESSED :    SDL_RELEASED );
  1478         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ) )
  1479             SDL_PrivateJoystickButton(joystick, 9, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? SDL_PRESSED :   SDL_RELEASED );
  1480         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_A ) )
  1481             SDL_PrivateJoystickButton(joystick, 10, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_A ? SDL_PRESSED :   SDL_RELEASED );
  1482         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_B ) )
  1483             SDL_PrivateJoystickButton(joystick, 11, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_B ? SDL_PRESSED :   SDL_RELEASED );
  1484         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_X ) )
  1485             SDL_PrivateJoystickButton(joystick, 12, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_X ? SDL_PRESSED :   SDL_RELEASED );
  1486         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_Y ) )
  1487             SDL_PrivateJoystickButton(joystick, 13, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_Y ? SDL_PRESSED :   SDL_RELEASED );
  1488         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons,  0x400 ) )
  1489             SDL_PrivateJoystickButton(joystick, 14, pXInputState->Gamepad.wButtons & 0x400 ? SDL_PRESSED :  SDL_RELEASED ); /* 0x400 is the undocumented code for the guide button */
  1490 
  1491         joystick->hwdata->currentXInputSlot ^= 1;
  1492 
  1493     }
  1494 }
  1495 
  1496 
  1497 static Uint8
  1498 TranslatePOV(DWORD value)
  1499 {
  1500     const int HAT_VALS[] = {
  1501         SDL_HAT_UP,
  1502         SDL_HAT_UP | SDL_HAT_RIGHT,
  1503         SDL_HAT_RIGHT,
  1504         SDL_HAT_DOWN | SDL_HAT_RIGHT,
  1505         SDL_HAT_DOWN,
  1506         SDL_HAT_DOWN | SDL_HAT_LEFT,
  1507         SDL_HAT_LEFT,
  1508         SDL_HAT_UP | SDL_HAT_LEFT
  1509     };
  1510 
  1511     if (LOWORD(value) == 0xFFFF)
  1512         return SDL_HAT_CENTERED;
  1513 
  1514     /* Round the value up: */
  1515     value += 4500 / 2;
  1516     value %= 36000;
  1517     value /= 4500;
  1518 
  1519     if (value >= 8)
  1520         return SDL_HAT_CENTERED;        /* shouldn't happen */
  1521 
  1522     return HAT_VALS[value];
  1523 }
  1524 
  1525 /* SDL_PrivateJoystick* doesn't discard duplicate events, so we need to
  1526  * do it. */
  1527 /* !!! FIXME: SDL_PrivateJoystickAxis _does_ discard duplicate events now. Ditch this code. */
  1528 static int
  1529 SDL_PrivateJoystickAxis_Int(SDL_Joystick * joystick, Uint8 axis, Sint16 value)
  1530 {
  1531     if (joystick->axes[axis] != value)
  1532         return SDL_PrivateJoystickAxis(joystick, axis, value);
  1533     return 0;
  1534 }
  1535 
  1536 static int
  1537 SDL_PrivateJoystickHat_Int(SDL_Joystick * joystick, Uint8 hat, Uint8 value)
  1538 {
  1539     if (joystick->hats[hat] != value)
  1540         return SDL_PrivateJoystickHat(joystick, hat, value);
  1541     return 0;
  1542 }
  1543 
  1544 static int
  1545 SDL_PrivateJoystickButton_Int(SDL_Joystick * joystick, Uint8 button,
  1546                               Uint8 state)
  1547 {
  1548     if (joystick->buttons[button] != state)
  1549         return SDL_PrivateJoystickButton(joystick, button, state);
  1550     return 0;
  1551 }
  1552 
  1553 void
  1554 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
  1555 {
  1556     HRESULT result;
  1557 
  1558     if ( joystick->closed || !joystick->hwdata )
  1559         return;
  1560 
  1561     if (joystick->hwdata->bXInputDevice)
  1562     {
  1563         SDL_SYS_JoystickUpdate_XInput(joystick);
  1564     }
  1565     else
  1566     {
  1567         result = IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);
  1568         if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
  1569             IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
  1570             IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);
  1571         }
  1572 
  1573         if (joystick->hwdata->buffered)
  1574             SDL_SYS_JoystickUpdate_Buffered(joystick);
  1575         else
  1576             SDL_SYS_JoystickUpdate_Polled(joystick);
  1577     }
  1578 
  1579     if ( joystick->hwdata->removed )
  1580     {
  1581         joystick->closed = 1;
  1582         joystick->uncentered = 1;
  1583     }
  1584 }
  1585 
  1586 /* Function to close a joystick after use */
  1587 void
  1588 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
  1589 {
  1590     if (!joystick->hwdata->bXInputDevice) {
  1591         IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
  1592         IDirectInputDevice8_Release(joystick->hwdata->InputDevice);
  1593     }
  1594 
  1595     /* free system specific hardware data */
  1596     SDL_free(joystick->hwdata);
  1597 
  1598     joystick->closed = 1;
  1599 }
  1600 
  1601 /* Function to perform any system-specific joystick related cleanup */
  1602 void
  1603 SDL_SYS_JoystickQuit(void)
  1604 {
  1605     JoyStick_DeviceData *device = SYS_Joystick;
  1606 
  1607     while ( device )
  1608     {
  1609         JoyStick_DeviceData *device_next = device->pNext;
  1610         SDL_free(device->joystickname);
  1611         SDL_free(device);
  1612         device = device_next;
  1613     }
  1614     SYS_Joystick = NULL;
  1615 
  1616     if ( s_threadJoystick )
  1617     {
  1618         SDL_LockMutex( s_mutexJoyStickEnum );
  1619         s_bJoystickThreadQuit = SDL_TRUE;
  1620         SDL_CondBroadcast( s_condJoystickThread ); /* signal the joystick thread to quit */
  1621         SDL_UnlockMutex( s_mutexJoyStickEnum );
  1622         SDL_WaitThread( s_threadJoystick, NULL ); /* wait for it to bugger off */
  1623 
  1624         SDL_DestroyMutex( s_mutexJoyStickEnum );
  1625         SDL_DestroyCond( s_condJoystickThread );
  1626         s_condJoystickThread= NULL;
  1627         s_mutexJoyStickEnum = NULL;
  1628         s_threadJoystick = NULL;
  1629     }
  1630 
  1631     if (dinput != NULL) {
  1632         IDirectInput8_Release(dinput);
  1633         dinput = NULL;
  1634     }
  1635 
  1636     if (coinitialized) {
  1637         WIN_CoUninitialize();
  1638         coinitialized = SDL_FALSE;
  1639     }
  1640 
  1641     if (s_bXInputEnabled) {
  1642         WIN_UnloadXInputDLL();
  1643     }
  1644 }
  1645 
  1646 /* return the stable device guid for this device index */
  1647 SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
  1648 {
  1649     JoyStick_DeviceData *device = SYS_Joystick;
  1650     int index;
  1651 
  1652     for (index = device_index; index > 0; index--)
  1653         device = device->pNext;
  1654 
  1655     return device->guid;
  1656 }
  1657 
  1658 SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
  1659 {
  1660     return joystick->hwdata->guid;
  1661 }
  1662 
  1663 /* return SDL_TRUE if this device is using XInput */
  1664 SDL_bool SDL_SYS_IsXInputDeviceIndex(int device_index)
  1665 {
  1666     JoyStick_DeviceData *device = SYS_Joystick;
  1667     int index;
  1668 
  1669     for (index = device_index; index > 0; index--)
  1670         device = device->pNext;
  1671 
  1672     return device->bXInputDevice;
  1673 }
  1674 
  1675 /* return SDL_TRUE if this device was opened with XInput */
  1676 SDL_bool SDL_SYS_IsXInputJoystick(SDL_Joystick * joystick)
  1677 {
  1678     return joystick->hwdata->bXInputDevice;
  1679 }
  1680 
  1681 #endif /* SDL_JOYSTICK_DINPUT */
  1682 
  1683 /* vi: set ts=4 sw=4 expandtab: */