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