src/joystick/windows/SDL_dxjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 21 Aug 2013 10:31:44 -0700
changeset 7684 dee82553d409
parent 7663 53fe1b64eb2d
child 7685 89f669598b87
permissions -rw-r--r--
Fix SDL xinput code to work at all when xinput has devices at high indexes but no device connected at lower index, for instance 0->disconnected, 1->wireles, 2->wired. Previously the SDL code assumed the indexes were always used up in order which is not true at all and lead to a bunch of failure cases where controllers would go unrecognized.

This entire function is kind of a mess and more complicated than needed, but I don't want to refactor it too heavily tonight. May look at improving how the indexes are assigned more significanly later. The way it handles not finding a valid "gamepad" type device is also super broken, it leaves in place the xinput bindings but opens the controller with dinput and ends up with completely wrong mappings, not solving that now, but fixing the bug where we'd very frequently not find a controller due to gaps in assigned player numbers should mostly avoid it.
     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 			while ( 1 )
  1018 			{
  1019 				result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities );
  1020 				if ( result == ERROR_SUCCESS )
  1021 				{
  1022 					const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
  1023 					SDL_bool bIsSupported = SDL_FALSE;
  1024 					/* Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad. */
  1025 					bIsSupported = ( capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD );
  1026 
  1027 					if ( !bIsSupported )
  1028 					{
  1029 						joystickdevice->bXInputDevice = SDL_FALSE;
  1030 					}
  1031 					else
  1032 					{
  1033 						/* valid */
  1034 						joystick->hwdata->bXInputDevice = SDL_TRUE;
  1035 						if ((!bIs14OrLater) || (capabilities.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
  1036 							joystick->hwdata->bXInputHaptic = SDL_TRUE;
  1037 						}
  1038 						SDL_memset( joystick->hwdata->XInputState, 0x0, sizeof(joystick->hwdata->XInputState) );
  1039 						joystickdevice->XInputUserId = userId;
  1040 						joystick->hwdata->userid = userId;
  1041 						joystick->hwdata->currentXInputSlot = 0;
  1042 						/* The XInput API has a hard coded button/axis mapping, so we just match it */
  1043 						joystick->naxes = 6;
  1044 						joystick->nbuttons = 15;
  1045 						joystick->nballs = 0;
  1046 						joystick->nhats = 0;
  1047 					}
  1048 					break;
  1049 				}
  1050 				else
  1051 				{
  1052 					if ( userId < XUSER_MAX_COUNT && result == ERROR_DEVICE_NOT_CONNECTED )
  1053 					{
  1054 						/* scan the opened joysticks and pick the next free xinput userid for this one */
  1055 						++userId;
  1056 
  1057 						joysticklist = SYS_Joystick;
  1058 						for( ; joysticklist; joysticklist = joysticklist->pNext)
  1059 						{
  1060 							if ( joysticklist->bXInputDevice && joysticklist->XInputUserId == userId )
  1061 								userId++;
  1062 						}
  1063 
  1064 						if ( userId >= XUSER_MAX_COUNT )
  1065 						{
  1066 							joystickdevice->bXInputDevice = SDL_FALSE;
  1067 							break;
  1068 						}
  1069 					}
  1070 					else
  1071 					{
  1072 						joystickdevice->bXInputDevice = SDL_FALSE;
  1073 						break;
  1074 					}
  1075 				}
  1076 			}
  1077         }
  1078         else
  1079         {
  1080             joystickdevice->bXInputDevice = SDL_FALSE;
  1081         }
  1082     }
  1083 
  1084     if ( joystickdevice->bXInputDevice == SDL_FALSE )
  1085     {
  1086         joystick->hwdata->bXInputDevice = SDL_FALSE;
  1087 
  1088         result =
  1089             IDirectInput8_CreateDevice(dinput,
  1090                                       &(joystickdevice->dxdevice.guidInstance), &device, NULL);
  1091         if (FAILED(result)) {
  1092             return SetDIerror("IDirectInput::CreateDevice", result);
  1093         }
  1094 
  1095         /* Now get the IDirectInputDevice8 interface, instead. */
  1096         result = IDirectInputDevice8_QueryInterface(device,
  1097                                                    &IID_IDirectInputDevice8,
  1098                                                    (LPVOID *) & joystick->
  1099                                                    hwdata->InputDevice);
  1100         /* We are done with this object.  Use the stored one from now on. */
  1101         IDirectInputDevice8_Release(device);
  1102 
  1103         if (FAILED(result)) {
  1104             return SetDIerror("IDirectInputDevice8::QueryInterface", result);
  1105         }
  1106 
  1107         /* Acquire shared access. Exclusive access is required for forces,
  1108          * though. */
  1109         result =
  1110             IDirectInputDevice8_SetCooperativeLevel(joystick->hwdata->
  1111                                                     InputDevice, SDL_HelperWindow,
  1112                                                     DISCL_NONEXCLUSIVE |
  1113                                                     DISCL_BACKGROUND);
  1114         if (FAILED(result)) {
  1115             return SetDIerror("IDirectInputDevice8::SetCooperativeLevel", result);
  1116         }
  1117 
  1118         /* Use the extended data structure: DIJOYSTATE2. */
  1119         result =
  1120             IDirectInputDevice8_SetDataFormat(joystick->hwdata->InputDevice,
  1121                                               &c_dfDIJoystick2);
  1122         if (FAILED(result)) {
  1123             return SetDIerror("IDirectInputDevice8::SetDataFormat", result);
  1124         }
  1125 
  1126         /* Get device capabilities */
  1127         result =
  1128             IDirectInputDevice8_GetCapabilities(joystick->hwdata->InputDevice,
  1129                                                 &joystick->hwdata->Capabilities);
  1130 
  1131         if (FAILED(result)) {
  1132             return SetDIerror("IDirectInputDevice8::GetCapabilities", result);
  1133         }
  1134 
  1135         /* Force capable? */
  1136         if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
  1137 
  1138             result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
  1139 
  1140             if (FAILED(result)) {
  1141                 return SetDIerror("IDirectInputDevice8::Acquire", result);
  1142             }
  1143 
  1144             /* reset all accuators. */
  1145             result =
  1146                 IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->
  1147                                                              InputDevice,
  1148                                                              DISFFC_RESET);
  1149 
  1150             /* Not necessarily supported, ignore if not supported.
  1151             if (FAILED(result)) {
  1152                 return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand", result);
  1153             }
  1154             */
  1155 
  1156             result = IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
  1157 
  1158             if (FAILED(result)) {
  1159                 return SetDIerror("IDirectInputDevice8::Unacquire", result);
  1160             }
  1161 
  1162             /* Turn on auto-centering for a ForceFeedback device (until told
  1163              * otherwise). */
  1164             dipdw.diph.dwObj = 0;
  1165             dipdw.diph.dwHow = DIPH_DEVICE;
  1166             dipdw.dwData = DIPROPAUTOCENTER_ON;
  1167 
  1168             result =
  1169                 IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
  1170                                                 DIPROP_AUTOCENTER, &dipdw.diph);
  1171 
  1172             /* Not necessarily supported, ignore if not supported.
  1173             if (FAILED(result)) {
  1174                 return SetDIerror("IDirectInputDevice8::SetProperty", result);
  1175             }
  1176             */
  1177         }
  1178 
  1179         /* What buttons and axes does it have? */
  1180         IDirectInputDevice8_EnumObjects(joystick->hwdata->InputDevice,
  1181                                         EnumDevObjectsCallback, joystick,
  1182                                         DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);
  1183 
  1184         /* Reorder the input objects. Some devices do not report the X axis as
  1185          * the first axis, for example. */
  1186         SortDevObjects(joystick);
  1187 
  1188         dipdw.diph.dwObj = 0;
  1189         dipdw.diph.dwHow = DIPH_DEVICE;
  1190         dipdw.dwData = INPUT_QSIZE;
  1191 
  1192         /* Set the buffer size */
  1193         result =
  1194             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
  1195                                             DIPROP_BUFFERSIZE, &dipdw.diph);
  1196 
  1197         if (result == DI_POLLEDDEVICE) {
  1198             /* This device doesn't support buffering, so we're forced
  1199              * to use less reliable polling. */
  1200             joystick->hwdata->buffered = 0;
  1201         } else if (FAILED(result)) {
  1202             return SetDIerror("IDirectInputDevice8::SetProperty", result);
  1203         }
  1204     }
  1205     return (0);
  1206 }
  1207 
  1208 /* return true if this joystick is plugged in right now */
  1209 SDL_bool SDL_SYS_JoystickAttached( SDL_Joystick * joystick )
  1210 {
  1211     return joystick->closed == 0 && joystick->hwdata->removed == 0;
  1212 }
  1213 
  1214 
  1215 /* Sort using the data offset into the DInput struct.
  1216  * This gives a reasonable ordering for the inputs. */
  1217 static int
  1218 SortDevFunc(const void *a, const void *b)
  1219 {
  1220     const input_t *inputA = (const input_t*)a;
  1221     const input_t *inputB = (const input_t*)b;
  1222 
  1223     if (inputA->ofs < inputB->ofs)
  1224         return -1;
  1225     if (inputA->ofs > inputB->ofs)
  1226         return 1;
  1227     return 0;
  1228 }
  1229 
  1230 /* Sort the input objects and recalculate the indices for each input. */
  1231 static void
  1232 SortDevObjects(SDL_Joystick *joystick)
  1233 {
  1234     input_t *inputs = joystick->hwdata->Inputs;
  1235     int nButtons = 0;
  1236     int nHats = 0;
  1237     int nAxis = 0;
  1238     int n;
  1239 
  1240     SDL_qsort(inputs, joystick->hwdata->NumInputs, sizeof(input_t), SortDevFunc);
  1241 
  1242     for (n = 0; n < joystick->hwdata->NumInputs; n++)
  1243     {
  1244         switch (inputs[n].type)
  1245         {
  1246         case BUTTON:
  1247             inputs[n].num = nButtons;
  1248             nButtons++;
  1249             break;
  1250 
  1251         case HAT:
  1252             inputs[n].num = nHats;
  1253             nHats++;
  1254             break;
  1255 
  1256         case AXIS:
  1257             inputs[n].num = nAxis;
  1258             nAxis++;
  1259             break;
  1260         }
  1261     }
  1262 }
  1263 
  1264 static BOOL CALLBACK
  1265 EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
  1266 {
  1267     SDL_Joystick *joystick = (SDL_Joystick *) pvRef;
  1268     HRESULT result;
  1269     input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs];
  1270 
  1271     if (dev->dwType & DIDFT_BUTTON) {
  1272         in->type = BUTTON;
  1273         in->num = joystick->nbuttons;
  1274         in->ofs = DIJOFS_BUTTON( in->num );
  1275         joystick->nbuttons++;
  1276     } else if (dev->dwType & DIDFT_POV) {
  1277         in->type = HAT;
  1278         in->num = joystick->nhats;
  1279         in->ofs = DIJOFS_POV( in->num );
  1280         joystick->nhats++;
  1281     } else if (dev->dwType & DIDFT_AXIS) {
  1282         DIPROPRANGE diprg;
  1283         DIPROPDWORD dilong;
  1284 
  1285         in->type = AXIS;
  1286         in->num = joystick->naxes;
  1287         /* work our the axis this guy maps too, thanks for the code icculus! */
  1288         if ( !SDL_memcmp( &dev->guidType, &GUID_XAxis, sizeof(dev->guidType) ) )
  1289             in->ofs = DIJOFS_X;
  1290         else if ( !SDL_memcmp( &dev->guidType, &GUID_YAxis, sizeof(dev->guidType) ) )
  1291             in->ofs = DIJOFS_Y;
  1292         else if ( !SDL_memcmp( &dev->guidType, &GUID_ZAxis, sizeof(dev->guidType) ) )
  1293             in->ofs = DIJOFS_Z;
  1294         else if ( !SDL_memcmp( &dev->guidType, &GUID_RxAxis, sizeof(dev->guidType) ) )
  1295             in->ofs = DIJOFS_RX;
  1296         else if ( !SDL_memcmp( &dev->guidType, &GUID_RyAxis, sizeof(dev->guidType) ) )
  1297             in->ofs = DIJOFS_RY;
  1298         else if ( !SDL_memcmp( &dev->guidType, &GUID_RzAxis, sizeof(dev->guidType) ) )
  1299             in->ofs = DIJOFS_RZ;
  1300         else if ( !SDL_memcmp( &dev->guidType, &GUID_Slider, sizeof(dev->guidType) ) )
  1301         {
  1302             in->ofs = DIJOFS_SLIDER( joystick->hwdata->NumSliders );
  1303             ++joystick->hwdata->NumSliders;
  1304         }
  1305         else
  1306         {
  1307              return DIENUM_CONTINUE; /* not an axis we can grok */
  1308         }
  1309 
  1310         diprg.diph.dwSize = sizeof(diprg);
  1311         diprg.diph.dwHeaderSize = sizeof(diprg.diph);
  1312         diprg.diph.dwObj = dev->dwType;
  1313         diprg.diph.dwHow = DIPH_BYID;
  1314         diprg.lMin = AXIS_MIN;
  1315         diprg.lMax = AXIS_MAX;
  1316 
  1317         result =
  1318             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
  1319                                             DIPROP_RANGE, &diprg.diph);
  1320         if (FAILED(result)) {
  1321             return DIENUM_CONTINUE;     /* don't use this axis */
  1322         }
  1323 
  1324         /* Set dead zone to 0. */
  1325         dilong.diph.dwSize = sizeof(dilong);
  1326         dilong.diph.dwHeaderSize = sizeof(dilong.diph);
  1327         dilong.diph.dwObj = dev->dwType;
  1328         dilong.diph.dwHow = DIPH_BYID;
  1329         dilong.dwData = 0;
  1330         result =
  1331             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
  1332                                             DIPROP_DEADZONE, &dilong.diph);
  1333         if (FAILED(result)) {
  1334             return DIENUM_CONTINUE;     /* don't use this axis */
  1335         }
  1336 
  1337         joystick->naxes++;
  1338     } else {
  1339         /* not supported at this time */
  1340         return DIENUM_CONTINUE;
  1341     }
  1342 
  1343     joystick->hwdata->NumInputs++;
  1344 
  1345     if (joystick->hwdata->NumInputs == MAX_INPUTS) {
  1346         return DIENUM_STOP;     /* too many */
  1347     }
  1348 
  1349     return DIENUM_CONTINUE;
  1350 }
  1351 
  1352 /* Function to update the state of a joystick - called as a device poll.
  1353  * This function shouldn't update the joystick structure directly,
  1354  * but instead should call SDL_PrivateJoystick*() to deliver events
  1355  * and update joystick device state.
  1356  */
  1357 void
  1358 SDL_SYS_JoystickUpdate_Polled(SDL_Joystick * joystick)
  1359 {
  1360     DIJOYSTATE2 state;
  1361     HRESULT result;
  1362     int i;
  1363 
  1364     result =
  1365         IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
  1366                                            sizeof(DIJOYSTATE2), &state);
  1367     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
  1368         IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
  1369         result =
  1370             IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
  1371                                                sizeof(DIJOYSTATE2), &state);
  1372     }
  1373 
  1374     if ( result != DI_OK )
  1375     {
  1376         joystick->hwdata->send_remove_event = 1;
  1377         joystick->hwdata->removed = 1;
  1378         return;
  1379     }
  1380 
  1381     /* Set each known axis, button and POV. */
  1382     for (i = 0; i < joystick->hwdata->NumInputs; ++i) {
  1383         const input_t *in = &joystick->hwdata->Inputs[i];
  1384 
  1385         switch (in->type) {
  1386         case AXIS:
  1387             switch (in->ofs) {
  1388             case DIJOFS_X:
  1389                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1390                                             (Sint16) state.lX);
  1391                 break;
  1392             case DIJOFS_Y:
  1393                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1394                                             (Sint16) state.lY);
  1395                 break;
  1396             case DIJOFS_Z:
  1397                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1398                                             (Sint16) state.lZ);
  1399                 break;
  1400             case DIJOFS_RX:
  1401                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1402                                             (Sint16) state.lRx);
  1403                 break;
  1404             case DIJOFS_RY:
  1405                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1406                                             (Sint16) state.lRy);
  1407                 break;
  1408             case DIJOFS_RZ:
  1409                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1410                                             (Sint16) state.lRz);
  1411                 break;
  1412             case DIJOFS_SLIDER(0):
  1413                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1414                                             (Sint16) state.rglSlider[0]);
  1415                 break;
  1416             case DIJOFS_SLIDER(1):
  1417                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1418                                             (Sint16) state.rglSlider[1]);
  1419                 break;
  1420             }
  1421 
  1422             break;
  1423 
  1424         case BUTTON:
  1425             SDL_PrivateJoystickButton_Int(joystick, in->num,
  1426                                           (Uint8) (state.
  1427                                                    rgbButtons[in->ofs -
  1428                                                               DIJOFS_BUTTON0]
  1429                                                    ? SDL_PRESSED :
  1430                                                    SDL_RELEASED));
  1431             break;
  1432         case HAT:
  1433             {
  1434                 Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs -
  1435                                                        DIJOFS_POV(0)]);
  1436                 SDL_PrivateJoystickHat_Int(joystick, in->num, pos);
  1437                 break;
  1438             }
  1439         }
  1440     }
  1441 }
  1442 
  1443 void
  1444 SDL_SYS_JoystickUpdate_Buffered(SDL_Joystick * joystick)
  1445 {
  1446     int i;
  1447     HRESULT result;
  1448     DWORD numevents;
  1449     DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
  1450 
  1451     numevents = INPUT_QSIZE;
  1452     result =
  1453         IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
  1454                                           sizeof(DIDEVICEOBJECTDATA), evtbuf,
  1455                                           &numevents, 0);
  1456     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
  1457         IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
  1458         result =
  1459             IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
  1460                                               sizeof(DIDEVICEOBJECTDATA),
  1461                                               evtbuf, &numevents, 0);
  1462     }
  1463 
  1464     /* Handle the events or punt */
  1465     if (FAILED(result))
  1466     {
  1467         joystick->hwdata->send_remove_event = 1;
  1468         joystick->hwdata->removed = 1;
  1469         return;
  1470     }
  1471 
  1472     for (i = 0; i < (int) numevents; ++i) {
  1473         int j;
  1474 
  1475         for (j = 0; j < joystick->hwdata->NumInputs; ++j) {
  1476             const input_t *in = &joystick->hwdata->Inputs[j];
  1477 
  1478             if (evtbuf[i].dwOfs != in->ofs)
  1479                 continue;
  1480 
  1481             switch (in->type) {
  1482             case AXIS:
  1483                 SDL_PrivateJoystickAxis(joystick, in->num,
  1484                                         (Sint16) evtbuf[i].dwData);
  1485                 break;
  1486             case BUTTON:
  1487                 SDL_PrivateJoystickButton(joystick, in->num,
  1488                                           (Uint8) (evtbuf[i].
  1489                                                    dwData ? SDL_PRESSED :
  1490                                                    SDL_RELEASED));
  1491                 break;
  1492             case HAT:
  1493                 {
  1494                     Uint8 pos = TranslatePOV(evtbuf[i].dwData);
  1495                     SDL_PrivateJoystickHat(joystick, in->num, pos);
  1496                 }
  1497             }
  1498         }
  1499     }
  1500 }
  1501 
  1502 
  1503 /* Function to return > 0 if a bit array of buttons differs after applying a mask
  1504 */
  1505 int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask )
  1506 {
  1507     return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask );
  1508 }
  1509 
  1510 /* Function to update the state of a XInput style joystick.
  1511 */
  1512 void
  1513 SDL_SYS_JoystickUpdate_XInput(SDL_Joystick * joystick)
  1514 {
  1515     HRESULT result;
  1516 
  1517     if ( !XINPUTGETSTATE )
  1518         return;
  1519 
  1520     result = XINPUTGETSTATE( joystick->hwdata->userid, &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot] );
  1521     if ( result == ERROR_DEVICE_NOT_CONNECTED )
  1522     {
  1523         joystick->hwdata->send_remove_event = 1;
  1524         joystick->hwdata->removed = 1;
  1525         return;
  1526     }
  1527 
  1528     /* only fire events if the data changed from last time */
  1529     if ( joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot].dwPacketNumber != 0
  1530         && joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot].dwPacketNumber != joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot^1].dwPacketNumber )
  1531     {
  1532         XINPUT_STATE_EX *pXInputState = &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot];
  1533         XINPUT_STATE_EX *pXInputStatePrev = &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot ^ 1];
  1534 
  1535         SDL_PrivateJoystickAxis( joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX );
  1536         SDL_PrivateJoystickAxis( joystick, 1, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbLY)) );
  1537         SDL_PrivateJoystickAxis( joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX );
  1538         SDL_PrivateJoystickAxis( joystick, 3, (Sint16)(-SDL_max(-32767, pXInputState->Gamepad.sThumbRY)) );
  1539         SDL_PrivateJoystickAxis( joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger*65535/255) - 32768));
  1540         SDL_PrivateJoystickAxis( joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger*65535/255) - 32768));
  1541 
  1542         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP ) )
  1543             SDL_PrivateJoystickButton(joystick, 0, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ? SDL_PRESSED :  SDL_RELEASED );
  1544         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN ) )
  1545             SDL_PrivateJoystickButton(joystick, 1, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ? SDL_PRESSED :    SDL_RELEASED );
  1546         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT ) )
  1547             SDL_PrivateJoystickButton(joystick, 2, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ? SDL_PRESSED :    SDL_RELEASED );
  1548         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ) )
  1549             SDL_PrivateJoystickButton(joystick, 3, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? SDL_PRESSED :   SDL_RELEASED );
  1550         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_START ) )
  1551             SDL_PrivateJoystickButton(joystick, 4, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_START ? SDL_PRESSED :    SDL_RELEASED );
  1552         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_BACK ) )
  1553             SDL_PrivateJoystickButton(joystick, 5, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_BACK ? SDL_PRESSED : SDL_RELEASED );
  1554         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB ) )
  1555             SDL_PrivateJoystickButton(joystick, 6, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ? SDL_PRESSED :   SDL_RELEASED );
  1556         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB ) )
  1557             SDL_PrivateJoystickButton(joystick, 7, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ? SDL_PRESSED :  SDL_RELEASED );
  1558         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ) )
  1559             SDL_PrivateJoystickButton(joystick, 8, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER ? SDL_PRESSED :    SDL_RELEASED );
  1560         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ) )
  1561             SDL_PrivateJoystickButton(joystick, 9, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? SDL_PRESSED :   SDL_RELEASED );
  1562         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_A ) )
  1563             SDL_PrivateJoystickButton(joystick, 10, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_A ? SDL_PRESSED :   SDL_RELEASED );
  1564         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_B ) )
  1565             SDL_PrivateJoystickButton(joystick, 11, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_B ? SDL_PRESSED :   SDL_RELEASED );
  1566         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_X ) )
  1567             SDL_PrivateJoystickButton(joystick, 12, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_X ? SDL_PRESSED :   SDL_RELEASED );
  1568         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_Y ) )
  1569             SDL_PrivateJoystickButton(joystick, 13, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_Y ? SDL_PRESSED :   SDL_RELEASED );
  1570         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons,  0x400 ) )
  1571             SDL_PrivateJoystickButton(joystick, 14, pXInputState->Gamepad.wButtons & 0x400 ? SDL_PRESSED :  SDL_RELEASED ); /* 0x400 is the undocumented code for the guide button */
  1572 
  1573         joystick->hwdata->currentXInputSlot ^= 1;
  1574 
  1575     }
  1576 }
  1577 
  1578 
  1579 static Uint8
  1580 TranslatePOV(DWORD value)
  1581 {
  1582     const int HAT_VALS[] = {
  1583         SDL_HAT_UP,
  1584         SDL_HAT_UP | SDL_HAT_RIGHT,
  1585         SDL_HAT_RIGHT,
  1586         SDL_HAT_DOWN | SDL_HAT_RIGHT,
  1587         SDL_HAT_DOWN,
  1588         SDL_HAT_DOWN | SDL_HAT_LEFT,
  1589         SDL_HAT_LEFT,
  1590         SDL_HAT_UP | SDL_HAT_LEFT
  1591     };
  1592 
  1593     if (LOWORD(value) == 0xFFFF)
  1594         return SDL_HAT_CENTERED;
  1595 
  1596     /* Round the value up: */
  1597     value += 4500 / 2;
  1598     value %= 36000;
  1599     value /= 4500;
  1600 
  1601     if (value >= 8)
  1602         return SDL_HAT_CENTERED;        /* shouldn't happen */
  1603 
  1604     return HAT_VALS[value];
  1605 }
  1606 
  1607 /* SDL_PrivateJoystick* doesn't discard duplicate events, so we need to
  1608  * do it. */
  1609 static int
  1610 SDL_PrivateJoystickAxis_Int(SDL_Joystick * joystick, Uint8 axis, Sint16 value)
  1611 {
  1612     if (joystick->axes[axis] != value)
  1613         return SDL_PrivateJoystickAxis(joystick, axis, value);
  1614     return 0;
  1615 }
  1616 
  1617 static int
  1618 SDL_PrivateJoystickHat_Int(SDL_Joystick * joystick, Uint8 hat, Uint8 value)
  1619 {
  1620     if (joystick->hats[hat] != value)
  1621         return SDL_PrivateJoystickHat(joystick, hat, value);
  1622     return 0;
  1623 }
  1624 
  1625 static int
  1626 SDL_PrivateJoystickButton_Int(SDL_Joystick * joystick, Uint8 button,
  1627                               Uint8 state)
  1628 {
  1629     if (joystick->buttons[button] != state)
  1630         return SDL_PrivateJoystickButton(joystick, button, state);
  1631     return 0;
  1632 }
  1633 
  1634 void
  1635 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
  1636 {
  1637     HRESULT result;
  1638 
  1639     if ( joystick->closed || !joystick->hwdata )
  1640         return;
  1641 
  1642     if (joystick->hwdata->bXInputDevice)
  1643     {
  1644         SDL_SYS_JoystickUpdate_XInput(joystick);
  1645     }
  1646     else
  1647     {
  1648         result = IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);
  1649         if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
  1650             IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
  1651             IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);
  1652         }
  1653 
  1654         if (joystick->hwdata->buffered)
  1655             SDL_SYS_JoystickUpdate_Buffered(joystick);
  1656         else
  1657             SDL_SYS_JoystickUpdate_Polled(joystick);
  1658     }
  1659 
  1660     if ( joystick->hwdata->removed )
  1661     {
  1662         joystick->closed = 1;
  1663         joystick->uncentered = 1;
  1664     }
  1665 }
  1666 
  1667 /* Function to close a joystick after use */
  1668 void
  1669 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
  1670 {
  1671     if ( joystick->hwdata->bXInputDevice )
  1672     {
  1673         JoyStick_DeviceData *joysticklist = SYS_Joystick;
  1674         /* scan the opened joysticks and clear the userid for this instance */
  1675         for( ; joysticklist; joysticklist = joysticklist->pNext)
  1676         {
  1677             if ( joysticklist->bXInputDevice && joysticklist->nInstanceID == joystick->instance_id )
  1678             {
  1679                 joysticklist->XInputUserId = INVALID_XINPUT_USERID;
  1680             }
  1681         }
  1682 
  1683     }
  1684     else
  1685     {
  1686         IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
  1687         IDirectInputDevice8_Release(joystick->hwdata->InputDevice);
  1688     }
  1689 
  1690     if (joystick->hwdata != NULL) {
  1691         /* free system specific hardware data */
  1692         SDL_free(joystick->hwdata);
  1693     }
  1694 
  1695     joystick->closed = 1;
  1696 }
  1697 
  1698 /* Function to perform any system-specific joystick related cleanup */
  1699 void
  1700 SDL_SYS_JoystickQuit(void)
  1701 {
  1702     JoyStick_DeviceData *device = SYS_Joystick;
  1703 
  1704     while ( device )
  1705     {
  1706         JoyStick_DeviceData *device_next = device->pNext;
  1707         SDL_free(device->joystickname);
  1708         SDL_free(device);
  1709         device = device_next;
  1710     }
  1711     SYS_Joystick = NULL;
  1712 
  1713     if ( s_threadJoystick )
  1714     {
  1715         SDL_LockMutex( s_mutexJoyStickEnum );
  1716         s_bJoystickThreadQuit = SDL_TRUE;
  1717         SDL_CondBroadcast( s_condJoystickThread ); /* signal the joystick thread to quit */
  1718         SDL_UnlockMutex( s_mutexJoyStickEnum );
  1719         SDL_WaitThread( s_threadJoystick, NULL ); /* wait for it to bugger off */
  1720 
  1721         SDL_DestroyMutex( s_mutexJoyStickEnum );
  1722         SDL_DestroyCond( s_condJoystickThread );
  1723         s_condJoystickThread= NULL;
  1724         s_mutexJoyStickEnum = NULL;
  1725         s_threadJoystick = NULL;
  1726     }
  1727 
  1728     if (dinput != NULL) {
  1729         IDirectInput8_Release(dinput);
  1730         dinput = NULL;
  1731     }
  1732 
  1733     if (coinitialized) {
  1734         WIN_CoUninitialize();
  1735         coinitialized = SDL_FALSE;
  1736     }
  1737 
  1738     if ( s_pKnownJoystickGUIDs )
  1739     {
  1740         SDL_free( s_pKnownJoystickGUIDs );
  1741         s_pKnownJoystickGUIDs = NULL;
  1742     }
  1743 
  1744     if (s_bXInputEnabled) {
  1745         WIN_UnloadXInputDLL();
  1746     }
  1747 }
  1748 
  1749 
  1750 /* return the stable device guid for this device index */
  1751 SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
  1752 {
  1753     JoyStick_DeviceData *device = SYS_Joystick;
  1754     int index;
  1755 
  1756     for (index = device_index; index > 0; index--)
  1757         device = device->pNext;
  1758 
  1759     return device->guid;
  1760 }
  1761 
  1762 SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
  1763 {
  1764     return joystick->hwdata->guid;
  1765 }
  1766 
  1767 /* return SDL_TRUE if this device is using XInput */
  1768 SDL_bool SDL_SYS_IsXInputDeviceIndex(int device_index)
  1769 {
  1770     JoyStick_DeviceData *device = SYS_Joystick;
  1771     int index;
  1772 
  1773     for (index = device_index; index > 0; index--)
  1774         device = device->pNext;
  1775 
  1776     return device->bXInputDevice;
  1777 }
  1778 
  1779 #endif /* SDL_JOYSTICK_DINPUT */
  1780 
  1781 /* vi: set ts=4 sw=4 expandtab: */