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