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