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