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