src/joystick/windows/SDL_dxjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 06 Jun 2013 17:59:01 -0700
changeset 7293 341d22fe9044
parent 7265 3bb309bb6bfe
child 7296 a8145f734ad3
permissions -rw-r--r--
The triggers should be expanded out to the full range to match DirectInput behavior.
The game controller code will scale them back to 0 - 32767 when it converts the triggers axes.
     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 
   400 DEFINE_GUID(CLSID_WbemLocator,   0x4590f811,0x1d3a,0x11d0,0x89,0x1F,0x00,0xaa,0x00,0x4b,0x2e,0x24);
   401 /* The Windows SDK doesn't define this GUID */
   402 DEFINE_GUID(IID_IWbemLocator,    0xdc12a687,0x737f,0x11cf,0x88,0x4d,0x00,0xaa,0x00,0x4b,0x2e,0x24);
   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     IWbemLocator*           pIWbemLocator  = NULL;
   415     IEnumWbemClassObject*   pEnumDevices   = NULL;
   416     IWbemClassObject*       pDevices[20];
   417     IWbemServices*          pIWbemServices = NULL;
   418     DWORD                   uReturned      = 0;
   419     BSTR                    bstrNamespace  = NULL;
   420     BSTR                    bstrDeviceID   = NULL;
   421     BSTR                    bstrClassName  = NULL;
   422     SDL_bool                bIsXinputDevice= SDL_FALSE;
   423     UINT                    iDevice        = 0;
   424     VARIANT                 var;
   425     HRESULT                 hr;
   426     DWORD bCleanupCOM;
   427 
   428     if (!s_bXInputEnabled)
   429     {
   430         return SDL_FALSE;
   431     }
   432 
   433     SDL_memset( pDevices, 0x0, sizeof(pDevices) );
   434 
   435     /* CoInit if needed */
   436     hr = CoInitialize(NULL);
   437     bCleanupCOM = SUCCEEDED(hr);
   438 
   439     /* Create WMI */
   440     hr = CoCreateInstance( &CLSID_WbemLocator,
   441         NULL,
   442         CLSCTX_INPROC_SERVER,
   443         &IID_IWbemLocator,
   444         (LPVOID*) &pIWbemLocator);
   445     if( FAILED(hr) || pIWbemLocator == NULL )
   446         goto LCleanup;
   447 
   448     bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup;
   449     bstrClassName = SysAllocString( L"Win32_PNPEntity" );   if( bstrClassName == NULL ) goto LCleanup;
   450     bstrDeviceID  = SysAllocString( L"DeviceID" );          if( bstrDeviceID == NULL )  goto LCleanup;
   451 
   452     /* Connect to WMI */
   453     hr = IWbemLocator_ConnectServer( pIWbemLocator, bstrNamespace, NULL, NULL, 0L,
   454         0L, NULL, NULL, &pIWbemServices );
   455     if( FAILED(hr) || pIWbemServices == NULL )
   456         goto LCleanup;
   457 
   458     /* Switch security level to IMPERSONATE. */
   459     CoSetProxyBlanket( (IUnknown *)pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
   460         RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE );
   461 
   462     hr = IWbemServices_CreateInstanceEnum( pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices );
   463     if( FAILED(hr) || pEnumDevices == NULL )
   464         goto LCleanup;
   465 
   466     /* Loop over all devices */
   467     for( ;; )
   468     {
   469         /* Get 20 at a time */
   470         hr = IEnumWbemClassObject_Next( pEnumDevices, 10000, 20, pDevices, &uReturned );
   471         if( FAILED(hr) )
   472             goto LCleanup;
   473         if( uReturned == 0 )
   474             break;
   475 
   476         for( iDevice=0; iDevice<uReturned; iDevice++ )
   477         {
   478             /* For each device, get its device ID */
   479             hr = IWbemClassObject_Get( pDevices[iDevice], bstrDeviceID, 0L, &var, NULL, NULL );
   480             if(  SUCCEEDED( hr ) && var.vt == VT_BSTR && var.bstrVal != NULL )
   481             {
   482                 /* Check if the device ID contains "IG_".  If it does, then it's an XInput device */
   483                 /* This information can not be found from DirectInput */
   484                 char *pDeviceString = WIN_StringToUTF8( var.bstrVal );
   485                 if( SDL_strstr( pDeviceString, "IG_" ) )
   486                 {
   487                     /* If it does, then get the VID/PID from var.bstrVal */
   488                     long dwPid = 0, dwVid = 0;
   489                     char * strPid = NULL;
   490                     DWORD dwVidPid = 0;
   491                     char * strVid = SDL_strstr( pDeviceString, "VID_" );
   492                     if( strVid )
   493                     {
   494                         dwVid = SDL_strtol( strVid + 4, NULL, 16 );
   495                     }
   496                     strPid = SDL_strstr( pDeviceString, "PID_" );
   497                     if( strPid  )
   498                     {
   499                         dwPid = SDL_strtol( strPid + 4, NULL, 16 );
   500                     }
   501 
   502                     /* Compare the VID/PID to the DInput device */
   503                     dwVidPid = MAKELONG( dwVid, dwPid );
   504                     if( dwVidPid == pGuidProductFromDirectInput->Data1 )
   505                     {
   506                         bIsXinputDevice = SDL_TRUE;
   507                     }
   508                 }
   509                 if ( pDeviceString )
   510                     SDL_free( pDeviceString );
   511 
   512                 if ( bIsXinputDevice )
   513                     break;
   514             }
   515             SAFE_RELEASE( pDevices[iDevice] );
   516         }
   517     }
   518 
   519 LCleanup:
   520 
   521     for( iDevice=0; iDevice<20; iDevice++ )
   522         SAFE_RELEASE( pDevices[iDevice] );
   523     SAFE_RELEASE( pEnumDevices );
   524     SAFE_RELEASE( pIWbemLocator );
   525     SAFE_RELEASE( pIWbemServices );
   526 
   527     if ( bstrNamespace )
   528         SysFreeString( bstrNamespace );
   529     if ( bstrClassName )
   530         SysFreeString( bstrClassName );
   531     if ( bstrDeviceID )
   532         SysFreeString( bstrDeviceID );
   533 
   534     if( bCleanupCOM )
   535         CoUninitialize();
   536 
   537     return bIsXinputDevice;
   538 }
   539 
   540 
   541 static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE;
   542 
   543 /* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal
   544  */
   545 LRESULT CALLBACK SDL_PrivateJoystickDetectProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)    {
   546     switch (message)    {
   547     case WM_DEVICECHANGE:
   548         switch (wParam) {
   549         case DBT_DEVICEARRIVAL:
   550             if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)    {
   551                 s_bWindowsDeviceChanged = SDL_TRUE;
   552             }
   553             break;
   554         case DBT_DEVICEREMOVECOMPLETE:
   555             if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)    {
   556                 s_bWindowsDeviceChanged = SDL_TRUE;
   557             }
   558             break;
   559         }
   560         return 0;
   561     }
   562 
   563     return DefWindowProc (hwnd, message, wParam, lParam);
   564 }
   565 
   566 
   567 DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, \
   568     0xC0, 0x4F, 0xB9, 0x51, 0xED);
   569 
   570 /* Function/thread to scan the system for joysticks.
   571  */
   572 static int
   573 SDL_JoystickThread(void *_data)
   574 {
   575     HWND messageWindow = 0;
   576     HDEVNOTIFY hNotify = 0;
   577     DEV_BROADCAST_DEVICEINTERFACE dbh;
   578     SDL_bool bOpenedXInputDevices[4];
   579     WNDCLASSEX wincl;
   580 
   581     SDL_memset( bOpenedXInputDevices, 0x0, sizeof(bOpenedXInputDevices) );
   582 
   583     WIN_CoInitialize();
   584 
   585     SDL_memset( &wincl, 0x0, sizeof(wincl) );
   586     wincl.hInstance = GetModuleHandle( NULL );
   587     wincl.lpszClassName = L"Message";
   588     wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc;      /* This function is called by windows */
   589     wincl.cbSize = sizeof (WNDCLASSEX);
   590 
   591     if (!RegisterClassEx (&wincl))
   592     {
   593         return SDL_SetError("Failed to create register class for joystick autodetect.", GetLastError());
   594     }
   595 
   596     messageWindow = (HWND)CreateWindowEx( 0,  L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL );
   597     if ( !messageWindow )
   598     {
   599         return SDL_SetError("Failed to create message window for joystick autodetect.", GetLastError());
   600     }
   601 
   602     SDL_memset(&dbh, 0x0, sizeof(dbh));
   603 
   604     dbh.dbcc_size = sizeof(dbh);
   605     dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
   606     dbh.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
   607 
   608     hNotify = RegisterDeviceNotification( messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE );
   609     if ( !hNotify )
   610     {
   611         return SDL_SetError("Failed to create notify device for joystick autodetect.", GetLastError());
   612     }
   613 
   614     SDL_LockMutex( s_mutexJoyStickEnum );
   615     while ( s_bJoystickThreadQuit == SDL_FALSE )
   616     {
   617         MSG messages;
   618         Uint8 userId;
   619         int nCurrentOpenedXInputDevices = 0;
   620         int nNewOpenedXInputDevices = 0;
   621         SDL_CondWaitTimeout( s_condJoystickThread, s_mutexJoyStickEnum, 300 );
   622 
   623         while ( s_bJoystickThreadQuit == SDL_FALSE && PeekMessage(&messages, messageWindow, 0, 0, PM_NOREMOVE) )
   624         {
   625             if ( GetMessage(&messages, messageWindow, 0, 0) != 0 )  {
   626                 TranslateMessage(&messages);
   627                 DispatchMessage(&messages);
   628             }
   629         }
   630 
   631         if ( s_bXInputEnabled && XINPUTGETCAPABILITIES )
   632         {
   633             /* scan for any change in XInput devices */
   634             for ( userId = 0; userId < 4; userId++ )
   635             {
   636                 XINPUT_CAPABILITIES capabilities;
   637                 DWORD result;
   638 
   639                 if ( bOpenedXInputDevices[userId] == SDL_TRUE )
   640                     nCurrentOpenedXInputDevices++;
   641 
   642                 result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities );
   643                 if ( result == ERROR_SUCCESS )
   644                 {
   645                     bOpenedXInputDevices[userId] = SDL_TRUE;
   646                     nNewOpenedXInputDevices++;
   647                 }
   648                 else
   649                 {
   650                     bOpenedXInputDevices[userId] = SDL_FALSE;
   651                 }
   652             }
   653         }
   654 
   655         if ( s_pKnownJoystickGUIDs && ( s_bWindowsDeviceChanged || nNewOpenedXInputDevices != nCurrentOpenedXInputDevices ) )
   656         {
   657             SDL_Delay( 300 ); /* wait for direct input to find out about this device */
   658 
   659             s_bDeviceRemoved = SDL_TRUE;
   660             s_bDeviceAdded = SDL_TRUE;
   661             s_bWindowsDeviceChanged = SDL_FALSE;
   662         }
   663     }
   664     SDL_UnlockMutex( s_mutexJoyStickEnum );
   665 
   666     if ( hNotify )
   667         UnregisterDeviceNotification( hNotify );
   668 
   669     if ( messageWindow )
   670         DestroyWindow( messageWindow );
   671 
   672     UnregisterClass( wincl.lpszClassName, wincl.hInstance );
   673     messageWindow = 0;
   674     WIN_CoUninitialize();
   675     return 1;
   676 }
   677 
   678 
   679 /* Function to scan the system for joysticks.
   680  * This function should set SDL_numjoysticks to the number of available
   681  * joysticks.  Joystick 0 should be the system default joystick.
   682  * It should return 0, or -1 on an unrecoverable fatal error.
   683  */
   684 int
   685 SDL_SYS_JoystickInit(void)
   686 {
   687     HRESULT result;
   688     HINSTANCE instance;
   689     const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
   690     if (env && !SDL_atoi(env)) {
   691         s_bXInputEnabled = SDL_FALSE;
   692     }
   693 
   694     result = WIN_CoInitialize();
   695     if (FAILED(result)) {
   696         return SetDIerror("CoInitialize", result);
   697     }
   698 
   699     coinitialized = SDL_TRUE;
   700 
   701     result = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER,
   702                               &IID_IDirectInput8, (LPVOID)&dinput);
   703 
   704     if (FAILED(result)) {
   705         SDL_SYS_JoystickQuit();
   706         return SetDIerror("CoCreateInstance", result);
   707     }
   708 
   709     /* Because we used CoCreateInstance, we need to Initialize it, first. */
   710     instance = GetModuleHandle(NULL);
   711     if (instance == NULL) {
   712         SDL_SYS_JoystickQuit();
   713         return SDL_SetError("GetModuleHandle() failed with error code %d.", GetLastError());
   714     }
   715     result = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION);
   716 
   717     if (FAILED(result)) {
   718         SDL_SYS_JoystickQuit();
   719         return SetDIerror("IDirectInput::Initialize", result);
   720     }
   721 
   722     s_mutexJoyStickEnum = SDL_CreateMutex();
   723     s_condJoystickThread = SDL_CreateCond();
   724     s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */
   725     SDL_SYS_JoystickDetect();
   726 
   727     if ((s_bXInputEnabled) && (WIN_LoadXInputDLL() == -1)) {
   728         s_bXInputEnabled = SDL_FALSE;  /* oh well. */
   729     }
   730 
   731     if ( !s_threadJoystick )
   732     {
   733         s_bJoystickThreadQuit = SDL_FALSE;
   734         /* spin up the thread to detect hotplug of devices */
   735 #if defined(__WIN32__) && !defined(HAVE_LIBC)
   736 #undef SDL_CreateThread
   737         s_threadJoystick= SDL_CreateThread( SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL );
   738 #else
   739         s_threadJoystick = SDL_CreateThread( SDL_JoystickThread, "SDL_joystick", NULL );
   740 #endif
   741     }
   742         return SDL_SYS_NumJoysticks();
   743 }
   744 
   745 /* return the number of joysticks that are connected right now */
   746 int SDL_SYS_NumJoysticks()
   747 {
   748     int nJoysticks = 0;
   749     JoyStick_DeviceData *device = SYS_Joystick;
   750     while ( device )
   751     {
   752         nJoysticks++;
   753         device = device->pNext;
   754     }
   755 
   756     return nJoysticks;
   757 }
   758 
   759 static int s_iNewGUID = 0;
   760 
   761 /* helper function for direct input, gets called for each connected joystick */
   762 static BOOL CALLBACK
   763     EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
   764 {
   765     JoyStick_DeviceData *pNewJoystick;
   766     JoyStick_DeviceData *pPrevJoystick = NULL;
   767     SDL_bool bXInputDevice;
   768     pNewJoystick = *(JoyStick_DeviceData **)pContext;
   769     while ( pNewJoystick )
   770     {
   771         if ( !SDL_memcmp( &pNewJoystick->dxdevice.guidInstance, &pdidInstance->guidInstance, sizeof(pNewJoystick->dxdevice.guidInstance) ) )
   772         {
   773             /* if we are replacing the front of the list then update it */
   774             if ( pNewJoystick == *(JoyStick_DeviceData **)pContext )
   775             {
   776                 *(JoyStick_DeviceData **)pContext = pNewJoystick->pNext;
   777             }
   778             else if ( pPrevJoystick )
   779             {
   780                 pPrevJoystick->pNext = pNewJoystick->pNext;
   781             }
   782 
   783             pNewJoystick->pNext = SYS_Joystick;
   784             SYS_Joystick = pNewJoystick;
   785 
   786             s_pKnownJoystickGUIDs[ s_iNewGUID ] = pdidInstance->guidInstance;
   787             s_iNewGUID++;
   788             if ( s_iNewGUID < MAX_JOYSTICKS )
   789                 return DIENUM_CONTINUE; /* already have this joystick loaded, just keep going */
   790             else
   791                 return DIENUM_STOP;
   792         }
   793 
   794         pPrevJoystick = pNewJoystick;
   795         pNewJoystick = pNewJoystick->pNext;
   796     }
   797 
   798     s_bDeviceAdded = SDL_TRUE;
   799 
   800     bXInputDevice = IsXInputDevice( &pdidInstance->guidProduct );
   801 
   802     pNewJoystick = (JoyStick_DeviceData *)SDL_malloc( sizeof(JoyStick_DeviceData) );
   803 
   804     if ( bXInputDevice )
   805     {
   806         pNewJoystick->bXInputDevice = SDL_TRUE;
   807         pNewJoystick->XInputUserId = INVALID_XINPUT_USERID;
   808     }
   809     else
   810     {
   811         pNewJoystick->bXInputDevice = SDL_FALSE;
   812     }
   813 
   814     SDL_memcpy(&(pNewJoystick->dxdevice), pdidInstance,
   815         sizeof(DIDEVICEINSTANCE));
   816 
   817     pNewJoystick->joystickname = WIN_StringToUTF8(pdidInstance->tszProductName);
   818     pNewJoystick->send_add_event = 1;
   819     pNewJoystick->nInstanceID = ++s_nInstanceID;
   820     SDL_memcpy( &pNewJoystick->guid, &pdidInstance->guidProduct, sizeof(pNewJoystick->guid) );
   821     pNewJoystick->pNext = NULL;
   822 
   823     if ( SYS_Joystick )
   824     {
   825         pNewJoystick->pNext = SYS_Joystick;
   826     }
   827     SYS_Joystick = pNewJoystick;
   828 
   829     s_pKnownJoystickGUIDs[ s_iNewGUID ] = pdidInstance->guidInstance;
   830     s_iNewGUID++;
   831 
   832     if ( s_iNewGUID < MAX_JOYSTICKS )
   833         return DIENUM_CONTINUE; /* already have this joystick loaded, just keep going */
   834     else
   835         return DIENUM_STOP;
   836 }
   837 
   838 /* detect any new joysticks being inserted into the system */
   839 void SDL_SYS_JoystickDetect()
   840 {
   841     JoyStick_DeviceData *pCurList = NULL;
   842     /* only enum the devices if the joystick thread told us something changed */
   843     if ( s_bDeviceAdded || s_bDeviceRemoved )
   844     {
   845         s_bDeviceAdded = SDL_FALSE;
   846         s_bDeviceRemoved = SDL_FALSE;
   847 
   848         pCurList = SYS_Joystick;
   849         SYS_Joystick = NULL;
   850         s_iNewGUID = 0;
   851         SDL_LockMutex( s_mutexJoyStickEnum );
   852 
   853         if ( !s_pKnownJoystickGUIDs )
   854             s_pKnownJoystickGUIDs = SDL_malloc( sizeof(GUID)*MAX_JOYSTICKS );
   855 
   856         SDL_memset( s_pKnownJoystickGUIDs, 0x0, sizeof(GUID)*MAX_JOYSTICKS );
   857 
   858         /* Look for joysticks, wheels, head trackers, gamepads, etc.. */
   859         IDirectInput8_EnumDevices(dinput,
   860             DI8DEVCLASS_GAMECTRL,
   861             EnumJoysticksCallback,
   862             &pCurList, DIEDFL_ATTACHEDONLY);
   863 
   864         SDL_UnlockMutex( s_mutexJoyStickEnum );
   865     }
   866 
   867     if ( pCurList )
   868     {
   869         while ( pCurList )
   870         {
   871             JoyStick_DeviceData *pListNext = NULL;
   872 #if !SDL_EVENTS_DISABLED
   873             SDL_Event event;
   874             event.type = SDL_JOYDEVICEREMOVED;
   875 
   876             if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   877                 event.jdevice.which = pCurList->nInstanceID;
   878                 if ((SDL_EventOK == NULL)
   879                     || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   880                         SDL_PushEvent(&event);
   881                 }
   882             }
   883 #endif /* !SDL_EVENTS_DISABLED */
   884 
   885             pListNext = pCurList->pNext;
   886             SDL_free(pCurList->joystickname);
   887             SDL_free( pCurList );
   888             pCurList = pListNext;
   889         }
   890 
   891     }
   892 
   893     if ( s_bDeviceAdded )
   894     {
   895         JoyStick_DeviceData *pNewJoystick;
   896         int device_index = 0;
   897         s_bDeviceAdded = SDL_FALSE;
   898         pNewJoystick = SYS_Joystick;
   899         while ( pNewJoystick )
   900         {
   901             if ( pNewJoystick->send_add_event )
   902             {
   903 #if !SDL_EVENTS_DISABLED
   904                 SDL_Event event;
   905                 event.type = SDL_JOYDEVICEADDED;
   906 
   907                 if (SDL_GetEventState(event.type) == SDL_ENABLE) {
   908                     event.jdevice.which = device_index;
   909                     if ((SDL_EventOK == NULL)
   910                         || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
   911                             SDL_PushEvent(&event);
   912                     }
   913                 }
   914 #endif /* !SDL_EVENTS_DISABLED */
   915                 pNewJoystick->send_add_event = 0;
   916             }
   917             device_index++;
   918             pNewJoystick = pNewJoystick->pNext;
   919         }
   920     }
   921 }
   922 
   923 /* we need to poll if we have pending hotplug device changes or connected devices */
   924 SDL_bool SDL_SYS_JoystickNeedsPolling()
   925 {
   926     /* we have a new device or one was pulled, we need to think this frame please */
   927     if ( s_bDeviceAdded || s_bDeviceRemoved )
   928         return SDL_TRUE;
   929 
   930     return SDL_FALSE;
   931 }
   932 
   933 /* Function to get the device-dependent name of a joystick */
   934 const char *
   935 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
   936 {
   937     JoyStick_DeviceData *device = SYS_Joystick;
   938 
   939     for (; device_index > 0; device_index--)
   940         device = device->pNext;
   941 
   942     return device->joystickname;
   943 }
   944 
   945 /* Function to perform the mapping between current device instance and this joysticks instance id */
   946 SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
   947 {
   948     JoyStick_DeviceData *device = SYS_Joystick;
   949     int index;
   950 
   951     for (index = device_index; index > 0; index--)
   952         device = device->pNext;
   953 
   954     return device->nInstanceID;
   955 }
   956 
   957 /* Function to open a joystick for use.
   958    The joystick to open is specified by the index field of the joystick.
   959    This should fill the nbuttons and naxes fields of the joystick structure.
   960    It returns 0, or -1 if there is an error.
   961  */
   962 int
   963 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
   964 {
   965     HRESULT result;
   966     LPDIRECTINPUTDEVICE8 device;
   967     DIPROPDWORD dipdw;
   968     JoyStick_DeviceData *joystickdevice = SYS_Joystick;
   969 
   970     for (; device_index > 0; device_index--)
   971         joystickdevice = joystickdevice->pNext;
   972 
   973     SDL_memset(&dipdw, 0, sizeof(DIPROPDWORD));
   974     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
   975     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
   976 
   977     /* allocate memory for system specific hardware data */
   978     joystick->instance_id = joystickdevice->nInstanceID;
   979     joystick->closed = 0;
   980     joystick->hwdata =
   981         (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
   982     if (joystick->hwdata == NULL) {
   983         return SDL_OutOfMemory();
   984     }
   985     SDL_memset(joystick->hwdata, 0, sizeof(struct joystick_hwdata));
   986     joystick->hwdata->buffered = 1;
   987     joystick->hwdata->removed = 0;
   988     joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS);
   989     joystick->hwdata->guid = joystickdevice->guid;
   990 
   991     if ( joystickdevice->bXInputDevice )
   992     {
   993         XINPUT_CAPABILITIES capabilities;
   994         Uint8 userId = 0;
   995         DWORD result;
   996         JoyStick_DeviceData *joysticklist = SYS_Joystick;
   997         /* scan the opened joysticks and pick the next free xinput userid for this one */
   998         for( ; joysticklist; joysticklist = joysticklist->pNext)
   999         {
  1000             if ( joysticklist->bXInputDevice && joysticklist->XInputUserId == userId )
  1001                 userId++;
  1002         }
  1003 
  1004         if ( s_bXInputEnabled && XINPUTGETCAPABILITIES )
  1005         {
  1006             result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities );
  1007             if ( result == ERROR_SUCCESS )
  1008             {
  1009                 const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
  1010                 SDL_bool bIsSupported = SDL_FALSE;
  1011                 /* Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad. */
  1012                 bIsSupported = ( capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD );
  1013 
  1014                 if ( !bIsSupported )
  1015                 {
  1016                     joystickdevice->bXInputDevice = SDL_FALSE;
  1017                 }
  1018                 else
  1019                 {
  1020                     /* valid */
  1021                     joystick->hwdata->bXInputDevice = SDL_TRUE;
  1022                     if ((!bIs14OrLater) || (capabilities.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
  1023                         joystick->hwdata->bXInputHaptic = SDL_TRUE;
  1024                     }
  1025                     SDL_memset( joystick->hwdata->XInputState, 0x0, sizeof(joystick->hwdata->XInputState) );
  1026                     joystickdevice->XInputUserId = userId;
  1027                     joystick->hwdata->userid = userId;
  1028                     joystick->hwdata->currentXInputSlot = 0;
  1029                     /* The XInput API has a hard coded button/axis mapping, so we just match it */
  1030                     joystick->naxes = 6;
  1031                     joystick->nbuttons = 15;
  1032                     joystick->nballs = 0;
  1033                     joystick->nhats = 0;
  1034                 }
  1035             }
  1036             else
  1037             {
  1038                 joystickdevice->bXInputDevice = SDL_FALSE;
  1039             }
  1040         }
  1041         else
  1042         {
  1043             joystickdevice->bXInputDevice = SDL_FALSE;
  1044         }
  1045     }
  1046 
  1047     if ( joystickdevice->bXInputDevice == SDL_FALSE )
  1048     {
  1049         joystick->hwdata->bXInputDevice = SDL_FALSE;
  1050 
  1051         result =
  1052             IDirectInput8_CreateDevice(dinput,
  1053                                       &(joystickdevice->dxdevice.guidInstance), &device, NULL);
  1054         if (FAILED(result)) {
  1055             return SetDIerror("IDirectInput::CreateDevice", result);
  1056         }
  1057 
  1058         /* Now get the IDirectInputDevice8 interface, instead. */
  1059         result = IDirectInputDevice8_QueryInterface(device,
  1060                                                    &IID_IDirectInputDevice8,
  1061                                                    (LPVOID *) & joystick->
  1062                                                    hwdata->InputDevice);
  1063         /* We are done with this object.  Use the stored one from now on. */
  1064         IDirectInputDevice8_Release(device);
  1065 
  1066         if (FAILED(result)) {
  1067             return SetDIerror("IDirectInputDevice8::QueryInterface", result);
  1068         }
  1069 
  1070         /* Acquire shared access. Exclusive access is required for forces,
  1071          * though. */
  1072         result =
  1073             IDirectInputDevice8_SetCooperativeLevel(joystick->hwdata->
  1074                                                     InputDevice, SDL_HelperWindow,
  1075                                                     DISCL_NONEXCLUSIVE |
  1076                                                     DISCL_BACKGROUND);
  1077         if (FAILED(result)) {
  1078             return SetDIerror("IDirectInputDevice8::SetCooperativeLevel", result);
  1079         }
  1080 
  1081         /* Use the extended data structure: DIJOYSTATE2. */
  1082         result =
  1083             IDirectInputDevice8_SetDataFormat(joystick->hwdata->InputDevice,
  1084                                               &c_dfDIJoystick2);
  1085         if (FAILED(result)) {
  1086             return SetDIerror("IDirectInputDevice8::SetDataFormat", result);
  1087         }
  1088 
  1089         /* Get device capabilities */
  1090         result =
  1091             IDirectInputDevice8_GetCapabilities(joystick->hwdata->InputDevice,
  1092                                                 &joystick->hwdata->Capabilities);
  1093 
  1094         if (FAILED(result)) {
  1095             return SetDIerror("IDirectInputDevice8::GetCapabilities", result);
  1096         }
  1097 
  1098         /* Force capable? */
  1099         if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
  1100 
  1101             result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
  1102 
  1103             if (FAILED(result)) {
  1104                 return SetDIerror("IDirectInputDevice8::Acquire", result);
  1105             }
  1106 
  1107             /* reset all accuators. */
  1108             result =
  1109                 IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->
  1110                                                              InputDevice,
  1111                                                              DISFFC_RESET);
  1112 
  1113             /* Not necessarily supported, ignore if not supported.
  1114             if (FAILED(result)) {
  1115                 return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand", result);
  1116             }
  1117             */
  1118 
  1119             result = IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
  1120 
  1121             if (FAILED(result)) {
  1122                 return SetDIerror("IDirectInputDevice8::Unacquire", result);
  1123             }
  1124 
  1125             /* Turn on auto-centering for a ForceFeedback device (until told
  1126              * otherwise). */
  1127             dipdw.diph.dwObj = 0;
  1128             dipdw.diph.dwHow = DIPH_DEVICE;
  1129             dipdw.dwData = DIPROPAUTOCENTER_ON;
  1130 
  1131             result =
  1132                 IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
  1133                                                 DIPROP_AUTOCENTER, &dipdw.diph);
  1134 
  1135             /* Not necessarily supported, ignore if not supported.
  1136             if (FAILED(result)) {
  1137                 return SetDIerror("IDirectInputDevice8::SetProperty", result);
  1138             }
  1139             */
  1140         }
  1141 
  1142         /* What buttons and axes does it have? */
  1143         IDirectInputDevice8_EnumObjects(joystick->hwdata->InputDevice,
  1144                                         EnumDevObjectsCallback, joystick,
  1145                                         DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);
  1146 
  1147         /* Reorder the input objects. Some devices do not report the X axis as
  1148          * the first axis, for example. */
  1149         SortDevObjects(joystick);
  1150 
  1151         dipdw.diph.dwObj = 0;
  1152         dipdw.diph.dwHow = DIPH_DEVICE;
  1153         dipdw.dwData = INPUT_QSIZE;
  1154 
  1155         /* Set the buffer size */
  1156         result =
  1157             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
  1158                                             DIPROP_BUFFERSIZE, &dipdw.diph);
  1159 
  1160         if (result == DI_POLLEDDEVICE) {
  1161             /* This device doesn't support buffering, so we're forced
  1162              * to use less reliable polling. */
  1163             joystick->hwdata->buffered = 0;
  1164         } else if (FAILED(result)) {
  1165             return SetDIerror("IDirectInputDevice8::SetProperty", result);
  1166         }
  1167     }
  1168     return (0);
  1169 }
  1170 
  1171 /* return true if this joystick is plugged in right now */
  1172 SDL_bool SDL_SYS_JoystickAttached( SDL_Joystick * joystick )
  1173 {
  1174     return joystick->closed == 0 && joystick->hwdata->removed == 0;
  1175 }
  1176 
  1177 
  1178 /* Sort using the data offset into the DInput struct.
  1179  * This gives a reasonable ordering for the inputs. */
  1180 static int
  1181 SortDevFunc(const void *a, const void *b)
  1182 {
  1183     const input_t *inputA = (const input_t*)a;
  1184     const input_t *inputB = (const input_t*)b;
  1185 
  1186     if (inputA->ofs < inputB->ofs)
  1187         return -1;
  1188     if (inputA->ofs > inputB->ofs)
  1189         return 1;
  1190     return 0;
  1191 }
  1192 
  1193 /* Sort the input objects and recalculate the indices for each input. */
  1194 static void
  1195 SortDevObjects(SDL_Joystick *joystick)
  1196 {
  1197     input_t *inputs = joystick->hwdata->Inputs;
  1198     int nButtons = 0;
  1199     int nHats = 0;
  1200     int nAxis = 0;
  1201     int n;
  1202 
  1203     SDL_qsort(inputs, joystick->hwdata->NumInputs, sizeof(input_t), SortDevFunc);
  1204 
  1205     for (n = 0; n < joystick->hwdata->NumInputs; n++)
  1206     {
  1207         switch (inputs[n].type)
  1208         {
  1209         case BUTTON:
  1210             inputs[n].num = nButtons;
  1211             nButtons++;
  1212             break;
  1213 
  1214         case HAT:
  1215             inputs[n].num = nHats;
  1216             nHats++;
  1217             break;
  1218 
  1219         case AXIS:
  1220             inputs[n].num = nAxis;
  1221             nAxis++;
  1222             break;
  1223         }
  1224     }
  1225 }
  1226 
  1227 static BOOL CALLBACK
  1228 EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
  1229 {
  1230     SDL_Joystick *joystick = (SDL_Joystick *) pvRef;
  1231     HRESULT result;
  1232     input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs];
  1233 
  1234     if (dev->dwType & DIDFT_BUTTON) {
  1235         in->type = BUTTON;
  1236         in->num = joystick->nbuttons;
  1237         in->ofs = DIJOFS_BUTTON( in->num );
  1238         joystick->nbuttons++;
  1239     } else if (dev->dwType & DIDFT_POV) {
  1240         in->type = HAT;
  1241         in->num = joystick->nhats;
  1242         in->ofs = DIJOFS_POV( in->num );
  1243         joystick->nhats++;
  1244     } else if (dev->dwType & DIDFT_AXIS) {
  1245         DIPROPRANGE diprg;
  1246         DIPROPDWORD dilong;
  1247 
  1248         in->type = AXIS;
  1249         in->num = joystick->naxes;
  1250         /* work our the axis this guy maps too, thanks for the code icculus! */
  1251         if ( !SDL_memcmp( &dev->guidType, &GUID_XAxis, sizeof(dev->guidType) ) )
  1252             in->ofs = DIJOFS_X;
  1253         else if ( !SDL_memcmp( &dev->guidType, &GUID_YAxis, sizeof(dev->guidType) ) )
  1254             in->ofs = DIJOFS_Y;
  1255         else if ( !SDL_memcmp( &dev->guidType, &GUID_ZAxis, sizeof(dev->guidType) ) )
  1256             in->ofs = DIJOFS_Z;
  1257         else if ( !SDL_memcmp( &dev->guidType, &GUID_RxAxis, sizeof(dev->guidType) ) )
  1258             in->ofs = DIJOFS_RX;
  1259         else if ( !SDL_memcmp( &dev->guidType, &GUID_RyAxis, sizeof(dev->guidType) ) )
  1260             in->ofs = DIJOFS_RY;
  1261         else if ( !SDL_memcmp( &dev->guidType, &GUID_RzAxis, sizeof(dev->guidType) ) )
  1262             in->ofs = DIJOFS_RZ;
  1263         else if ( !SDL_memcmp( &dev->guidType, &GUID_Slider, sizeof(dev->guidType) ) )
  1264         {
  1265             in->ofs = DIJOFS_SLIDER( joystick->hwdata->NumSliders );
  1266             ++joystick->hwdata->NumSliders;
  1267         }
  1268         else
  1269         {
  1270              return DIENUM_CONTINUE; /* not an axis we can grok */
  1271         }
  1272 
  1273         diprg.diph.dwSize = sizeof(diprg);
  1274         diprg.diph.dwHeaderSize = sizeof(diprg.diph);
  1275         diprg.diph.dwObj = dev->dwType;
  1276         diprg.diph.dwHow = DIPH_BYID;
  1277         diprg.lMin = AXIS_MIN;
  1278         diprg.lMax = AXIS_MAX;
  1279 
  1280         result =
  1281             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
  1282                                             DIPROP_RANGE, &diprg.diph);
  1283         if (FAILED(result)) {
  1284             return DIENUM_CONTINUE;     /* don't use this axis */
  1285         }
  1286 
  1287         /* Set dead zone to 0. */
  1288         dilong.diph.dwSize = sizeof(dilong);
  1289         dilong.diph.dwHeaderSize = sizeof(dilong.diph);
  1290         dilong.diph.dwObj = dev->dwType;
  1291         dilong.diph.dwHow = DIPH_BYID;
  1292         dilong.dwData = 0;
  1293         result =
  1294             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
  1295                                             DIPROP_DEADZONE, &dilong.diph);
  1296         if (FAILED(result)) {
  1297             return DIENUM_CONTINUE;     /* don't use this axis */
  1298         }
  1299 
  1300         joystick->naxes++;
  1301     } else {
  1302         /* not supported at this time */
  1303         return DIENUM_CONTINUE;
  1304     }
  1305 
  1306     joystick->hwdata->NumInputs++;
  1307 
  1308     if (joystick->hwdata->NumInputs == MAX_INPUTS) {
  1309         return DIENUM_STOP;     /* too many */
  1310     }
  1311 
  1312     return DIENUM_CONTINUE;
  1313 }
  1314 
  1315 /* Function to update the state of a joystick - called as a device poll.
  1316  * This function shouldn't update the joystick structure directly,
  1317  * but instead should call SDL_PrivateJoystick*() to deliver events
  1318  * and update joystick device state.
  1319  */
  1320 void
  1321 SDL_SYS_JoystickUpdate_Polled(SDL_Joystick * joystick)
  1322 {
  1323     DIJOYSTATE2 state;
  1324     HRESULT result;
  1325     int i;
  1326 
  1327     result =
  1328         IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
  1329                                            sizeof(DIJOYSTATE2), &state);
  1330     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
  1331         IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
  1332         result =
  1333             IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
  1334                                                sizeof(DIJOYSTATE2), &state);
  1335     }
  1336 
  1337     if ( result != DI_OK )
  1338     {
  1339         joystick->hwdata->send_remove_event = 1;
  1340         joystick->hwdata->removed = 1;
  1341         return;
  1342     }
  1343 
  1344     /* Set each known axis, button and POV. */
  1345     for (i = 0; i < joystick->hwdata->NumInputs; ++i) {
  1346         const input_t *in = &joystick->hwdata->Inputs[i];
  1347 
  1348         switch (in->type) {
  1349         case AXIS:
  1350             switch (in->ofs) {
  1351             case DIJOFS_X:
  1352                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1353                                             (Sint16) state.lX);
  1354                 break;
  1355             case DIJOFS_Y:
  1356                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1357                                             (Sint16) state.lY);
  1358                 break;
  1359             case DIJOFS_Z:
  1360                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1361                                             (Sint16) state.lZ);
  1362                 break;
  1363             case DIJOFS_RX:
  1364                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1365                                             (Sint16) state.lRx);
  1366                 break;
  1367             case DIJOFS_RY:
  1368                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1369                                             (Sint16) state.lRy);
  1370                 break;
  1371             case DIJOFS_RZ:
  1372                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1373                                             (Sint16) state.lRz);
  1374                 break;
  1375             case DIJOFS_SLIDER(0):
  1376                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1377                                             (Sint16) state.rglSlider[0]);
  1378                 break;
  1379             case DIJOFS_SLIDER(1):
  1380                 SDL_PrivateJoystickAxis_Int(joystick, in->num,
  1381                                             (Sint16) state.rglSlider[1]);
  1382                 break;
  1383             }
  1384 
  1385             break;
  1386 
  1387         case BUTTON:
  1388             SDL_PrivateJoystickButton_Int(joystick, in->num,
  1389                                           (Uint8) (state.
  1390                                                    rgbButtons[in->ofs -
  1391                                                               DIJOFS_BUTTON0]
  1392                                                    ? SDL_PRESSED :
  1393                                                    SDL_RELEASED));
  1394             break;
  1395         case HAT:
  1396             {
  1397                 Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs -
  1398                                                        DIJOFS_POV(0)]);
  1399                 SDL_PrivateJoystickHat_Int(joystick, in->num, pos);
  1400                 break;
  1401             }
  1402         }
  1403     }
  1404 }
  1405 
  1406 void
  1407 SDL_SYS_JoystickUpdate_Buffered(SDL_Joystick * joystick)
  1408 {
  1409     int i;
  1410     HRESULT result;
  1411     DWORD numevents;
  1412     DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
  1413 
  1414     numevents = INPUT_QSIZE;
  1415     result =
  1416         IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
  1417                                           sizeof(DIDEVICEOBJECTDATA), evtbuf,
  1418                                           &numevents, 0);
  1419     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
  1420         IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
  1421         result =
  1422             IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
  1423                                               sizeof(DIDEVICEOBJECTDATA),
  1424                                               evtbuf, &numevents, 0);
  1425     }
  1426 
  1427     /* Handle the events or punt */
  1428     if (FAILED(result))
  1429     {
  1430         joystick->hwdata->send_remove_event = 1;
  1431         joystick->hwdata->removed = 1;
  1432         return;
  1433     }
  1434 
  1435     for (i = 0; i < (int) numevents; ++i) {
  1436         int j;
  1437 
  1438         for (j = 0; j < joystick->hwdata->NumInputs; ++j) {
  1439             const input_t *in = &joystick->hwdata->Inputs[j];
  1440 
  1441             if (evtbuf[i].dwOfs != in->ofs)
  1442                 continue;
  1443 
  1444             switch (in->type) {
  1445             case AXIS:
  1446                 SDL_PrivateJoystickAxis(joystick, in->num,
  1447                                         (Sint16) evtbuf[i].dwData);
  1448                 break;
  1449             case BUTTON:
  1450                 SDL_PrivateJoystickButton(joystick, in->num,
  1451                                           (Uint8) (evtbuf[i].
  1452                                                    dwData ? SDL_PRESSED :
  1453                                                    SDL_RELEASED));
  1454                 break;
  1455             case HAT:
  1456                 {
  1457                     Uint8 pos = TranslatePOV(evtbuf[i].dwData);
  1458                     SDL_PrivateJoystickHat(joystick, in->num, pos);
  1459                 }
  1460             }
  1461         }
  1462     }
  1463 }
  1464 
  1465 
  1466 /* Function to return > 0 if a bit array of buttons differs after applying a mask
  1467 */
  1468 int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask )
  1469 {
  1470     return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask );
  1471 }
  1472 
  1473 /* Function to update the state of a XInput style joystick.
  1474 */
  1475 void
  1476 SDL_SYS_JoystickUpdate_XInput(SDL_Joystick * joystick)
  1477 {
  1478     HRESULT result;
  1479 
  1480     if ( !XINPUTGETSTATE )
  1481         return;
  1482 
  1483     result = XINPUTGETSTATE( joystick->hwdata->userid, &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot] );
  1484     if ( result == ERROR_DEVICE_NOT_CONNECTED )
  1485     {
  1486         joystick->hwdata->send_remove_event = 1;
  1487         joystick->hwdata->removed = 1;
  1488         return;
  1489     }
  1490 
  1491     /* only fire events if the data changed from last time */
  1492     if ( joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot].dwPacketNumber != 0
  1493         && joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot].dwPacketNumber != joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot^1].dwPacketNumber )
  1494     {
  1495         XINPUT_STATE_EX *pXInputState = &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot];
  1496         XINPUT_STATE_EX *pXInputStatePrev = &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot ^ 1];
  1497 
  1498         SDL_PrivateJoystickAxis(joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX );
  1499         SDL_PrivateJoystickAxis(joystick, 1, (Sint16)(-1*pXInputState->Gamepad.sThumbLY-1) );
  1500         SDL_PrivateJoystickAxis(joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX );
  1501         SDL_PrivateJoystickAxis(joystick, 3, (Sint16)(-1*pXInputState->Gamepad.sThumbRY-1) );
  1502         SDL_PrivateJoystickAxis(joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger*65535/255) - 32768));
  1503         SDL_PrivateJoystickAxis(joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger*65535/255) - 32768));
  1504 
  1505         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP ) )
  1506             SDL_PrivateJoystickButton(joystick, 0, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ? SDL_PRESSED :  SDL_RELEASED );
  1507         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN ) )
  1508             SDL_PrivateJoystickButton(joystick, 1, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ? SDL_PRESSED :    SDL_RELEASED );
  1509         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT ) )
  1510             SDL_PrivateJoystickButton(joystick, 2, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ? SDL_PRESSED :    SDL_RELEASED );
  1511         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ) )
  1512             SDL_PrivateJoystickButton(joystick, 3, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? SDL_PRESSED :   SDL_RELEASED );
  1513         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_START ) )
  1514             SDL_PrivateJoystickButton(joystick, 4, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_START ? SDL_PRESSED :    SDL_RELEASED );
  1515         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_BACK ) )
  1516             SDL_PrivateJoystickButton(joystick, 5, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_BACK ? SDL_PRESSED : SDL_RELEASED );
  1517         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB ) )
  1518             SDL_PrivateJoystickButton(joystick, 6, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ? SDL_PRESSED :   SDL_RELEASED );
  1519         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB ) )
  1520             SDL_PrivateJoystickButton(joystick, 7, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ? SDL_PRESSED :  SDL_RELEASED );
  1521         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ) )
  1522             SDL_PrivateJoystickButton(joystick, 8, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER ? SDL_PRESSED :    SDL_RELEASED );
  1523         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ) )
  1524             SDL_PrivateJoystickButton(joystick, 9, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? SDL_PRESSED :   SDL_RELEASED );
  1525         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_A ) )
  1526             SDL_PrivateJoystickButton(joystick, 10, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_A ? SDL_PRESSED :   SDL_RELEASED );
  1527         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_B ) )
  1528             SDL_PrivateJoystickButton(joystick, 11, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_B ? SDL_PRESSED :   SDL_RELEASED );
  1529         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_X ) )
  1530             SDL_PrivateJoystickButton(joystick, 12, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_X ? SDL_PRESSED :   SDL_RELEASED );
  1531         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_Y ) )
  1532             SDL_PrivateJoystickButton(joystick, 13, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_Y ? SDL_PRESSED :   SDL_RELEASED );
  1533         if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons,  0x400 ) )
  1534             SDL_PrivateJoystickButton(joystick, 14, pXInputState->Gamepad.wButtons & 0x400 ? SDL_PRESSED :  SDL_RELEASED ); /* 0x400 is the undocumented code for the guide button */
  1535 
  1536         joystick->hwdata->currentXInputSlot ^= 1;
  1537 
  1538     }
  1539 }
  1540 
  1541 
  1542 static Uint8
  1543 TranslatePOV(DWORD value)
  1544 {
  1545     const int HAT_VALS[] = {
  1546         SDL_HAT_UP,
  1547         SDL_HAT_UP | SDL_HAT_RIGHT,
  1548         SDL_HAT_RIGHT,
  1549         SDL_HAT_DOWN | SDL_HAT_RIGHT,
  1550         SDL_HAT_DOWN,
  1551         SDL_HAT_DOWN | SDL_HAT_LEFT,
  1552         SDL_HAT_LEFT,
  1553         SDL_HAT_UP | SDL_HAT_LEFT
  1554     };
  1555 
  1556     if (LOWORD(value) == 0xFFFF)
  1557         return SDL_HAT_CENTERED;
  1558 
  1559     /* Round the value up: */
  1560     value += 4500 / 2;
  1561     value %= 36000;
  1562     value /= 4500;
  1563 
  1564     if (value >= 8)
  1565         return SDL_HAT_CENTERED;        /* shouldn't happen */
  1566 
  1567     return HAT_VALS[value];
  1568 }
  1569 
  1570 /* SDL_PrivateJoystick* doesn't discard duplicate events, so we need to
  1571  * do it. */
  1572 static int
  1573 SDL_PrivateJoystickAxis_Int(SDL_Joystick * joystick, Uint8 axis, Sint16 value)
  1574 {
  1575     if (joystick->axes[axis] != value)
  1576         return SDL_PrivateJoystickAxis(joystick, axis, value);
  1577     return 0;
  1578 }
  1579 
  1580 static int
  1581 SDL_PrivateJoystickHat_Int(SDL_Joystick * joystick, Uint8 hat, Uint8 value)
  1582 {
  1583     if (joystick->hats[hat] != value)
  1584         return SDL_PrivateJoystickHat(joystick, hat, value);
  1585     return 0;
  1586 }
  1587 
  1588 static int
  1589 SDL_PrivateJoystickButton_Int(SDL_Joystick * joystick, Uint8 button,
  1590                               Uint8 state)
  1591 {
  1592     if (joystick->buttons[button] != state)
  1593         return SDL_PrivateJoystickButton(joystick, button, state);
  1594     return 0;
  1595 }
  1596 
  1597 void
  1598 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
  1599 {
  1600     HRESULT result;
  1601 
  1602     if ( joystick->closed || !joystick->hwdata )
  1603         return;
  1604 
  1605     if (joystick->hwdata->bXInputDevice)
  1606     {
  1607         SDL_SYS_JoystickUpdate_XInput(joystick);
  1608     }
  1609     else
  1610     {
  1611         result = IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);
  1612         if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
  1613             IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
  1614             IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);
  1615         }
  1616 
  1617         if (joystick->hwdata->buffered)
  1618             SDL_SYS_JoystickUpdate_Buffered(joystick);
  1619         else
  1620             SDL_SYS_JoystickUpdate_Polled(joystick);
  1621     }
  1622 
  1623     if ( joystick->hwdata->removed )
  1624     {
  1625         joystick->closed = 1;
  1626         joystick->uncentered = 1;
  1627     }
  1628 }
  1629 
  1630 /* Function to close a joystick after use */
  1631 void
  1632 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
  1633 {
  1634     if ( joystick->hwdata->bXInputDevice )
  1635     {
  1636         JoyStick_DeviceData *joysticklist = SYS_Joystick;
  1637         /* scan the opened joysticks and clear the userid for this instance */
  1638         for( ; joysticklist; joysticklist = joysticklist->pNext)
  1639         {
  1640             if ( joysticklist->bXInputDevice && joysticklist->nInstanceID == joystick->instance_id )
  1641             {
  1642                 joysticklist->XInputUserId = INVALID_XINPUT_USERID;
  1643             }
  1644         }
  1645 
  1646     }
  1647     else
  1648     {
  1649         IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
  1650         IDirectInputDevice8_Release(joystick->hwdata->InputDevice);
  1651     }
  1652 
  1653     if (joystick->hwdata != NULL) {
  1654         /* free system specific hardware data */
  1655         SDL_free(joystick->hwdata);
  1656     }
  1657 
  1658     joystick->closed = 1;
  1659 }
  1660 
  1661 /* Function to perform any system-specific joystick related cleanup */
  1662 void
  1663 SDL_SYS_JoystickQuit(void)
  1664 {
  1665     JoyStick_DeviceData *device = SYS_Joystick;
  1666 
  1667     while ( device )
  1668     {
  1669         JoyStick_DeviceData *device_next = device->pNext;
  1670         SDL_free(device->joystickname);
  1671         SDL_free(device);
  1672         device = device_next;
  1673     }
  1674     SYS_Joystick = NULL;
  1675 
  1676     if ( s_threadJoystick )
  1677     {
  1678         SDL_LockMutex( s_mutexJoyStickEnum );
  1679         s_bJoystickThreadQuit = SDL_TRUE;
  1680         SDL_CondBroadcast( s_condJoystickThread ); /* signal the joystick thread to quit */
  1681         SDL_UnlockMutex( s_mutexJoyStickEnum );
  1682         SDL_WaitThread( s_threadJoystick, NULL ); /* wait for it to bugger off */
  1683 
  1684         SDL_DestroyMutex( s_mutexJoyStickEnum );
  1685         SDL_DestroyCond( s_condJoystickThread );
  1686         s_condJoystickThread= NULL;
  1687         s_mutexJoyStickEnum = NULL;
  1688         s_threadJoystick = NULL;
  1689     }
  1690 
  1691     if (dinput != NULL) {
  1692         IDirectInput8_Release(dinput);
  1693         dinput = NULL;
  1694     }
  1695 
  1696     if (coinitialized) {
  1697         WIN_CoUninitialize();
  1698         coinitialized = SDL_FALSE;
  1699     }
  1700 
  1701     if ( s_pKnownJoystickGUIDs )
  1702     {
  1703         SDL_free( s_pKnownJoystickGUIDs );
  1704         s_pKnownJoystickGUIDs = NULL;
  1705     }
  1706 
  1707     if (s_bXInputEnabled) {
  1708         WIN_UnloadXInputDLL();
  1709     }
  1710 }
  1711 
  1712 
  1713 /* return the stable device guid for this device index */
  1714 SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
  1715 {
  1716     JoyStick_DeviceData *device = SYS_Joystick;
  1717     int index;
  1718 
  1719     for (index = device_index; index > 0; index--)
  1720         device = device->pNext;
  1721 
  1722     return device->guid;
  1723 }
  1724 
  1725 SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
  1726 {
  1727     return joystick->hwdata->guid;
  1728 }
  1729 
  1730 /* return SDL_TRUE if this device is using XInput */
  1731 SDL_bool SDL_SYS_IsXInputDeviceIndex(int device_index)
  1732 {
  1733     JoyStick_DeviceData *device = SYS_Joystick;
  1734     int index;
  1735 
  1736     for (index = device_index; index > 0; index--)
  1737         device = device->pNext;
  1738 
  1739     return device->bXInputDevice;
  1740 }
  1741 
  1742 #endif /* SDL_JOYSTICK_DINPUT */
  1743 
  1744 /* vi: set ts=4 sw=4 expandtab: */