src/joystick/windows/SDL_dxjoystick.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 28 Aug 2013 17:17:21 -0400
changeset 7709 edd2c14acf66
parent 7707 37e02f8fcfa8
child 7711 db9e27a52d77
permissions -rw-r--r--
Make XInput joystick names match the numbers on the device.

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