src/joystick/windows/SDL_dxjoystick.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 02 Feb 2014 00:53:27 -0800
changeset 8149 681eb46b8ac4
parent 8094 9efaae827924
child 8179 48c90b62af13
permissions -rw-r--r--
Fixed bug 2374 - Update copyright for 2014...

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