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