It's better not to have the full range of the axis (by 1 on the negative side) than turn 0 value into -1
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "SDL_config.h"
23 #ifdef SDL_JOYSTICK_DINPUT
25 /* DirectInput joystick driver; written by Glenn Maynard, based on Andrei de
26 * A. Formiga's WINMM driver.
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.
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. */
35 #include "SDL_error.h"
36 #include "SDL_assert.h"
37 #include "SDL_events.h"
38 #include "SDL_thread.h"
39 #include "SDL_timer.h"
40 #include "SDL_mutex.h"
41 #include "SDL_events.h"
42 #include "SDL_hints.h"
43 #include "SDL_joystick.h"
44 #include "../SDL_sysjoystick.h"
45 #if !SDL_EVENTS_DISABLED
46 #include "../../events/SDL_events_c.h"
49 /* The latest version of mingw-w64 defines IID_IWbemLocator in wbemcli.h
50 instead of declaring it like Visual Studio and other mingw32 compilers.
51 So, we need to take care of this here before we define INITGUID.
54 #define __IWbemLocator_INTERFACE_DEFINED__
55 #endif /* __MINGW32__ */
57 #define INITGUID /* Only set here, if set twice will cause mingw32 to break. */
58 #include "SDL_dxjoystick_c.h"
61 /* And now that we've included wbemcli.h we need to declare these interfaces */
62 typedef struct IWbemLocatorVtbl {
64 HRESULT (WINAPI *QueryInterface)(IWbemLocator *This,REFIID riid,void **ppvObject);
65 ULONG (WINAPI *AddRef)(IWbemLocator *This);
66 ULONG (WINAPI *Release)(IWbemLocator *This);
67 HRESULT (WINAPI *ConnectServer)(IWbemLocator *This,const BSTR strNetworkResource,const BSTR strUser,const BSTR strPassword,const BSTR strLocale,LONG lSecurityFlags,const BSTR strAuthority,IWbemContext *pCtx,IWbemServices **ppNamespace);
71 CONST_VTBL struct IWbemLocatorVtbl *lpVtbl;
73 #define IWbemLocator_ConnectServer(This,strNetworkResource,strUser,strPassword,strLocale,lSecurityFlags,strAuthority,pCtx,ppNamespace) (This)->lpVtbl->ConnectServer(This,strNetworkResource,strUser,strPassword,strLocale,lSecurityFlags,strAuthority,pCtx,ppNamespace)
74 #endif /* __MINGW32__ */
76 #ifndef DIDFT_OPTIONAL
77 #define DIDFT_OPTIONAL 0x80000000
81 #define INPUT_QSIZE 32 /* Buffer up to 32 input messages */
82 #define MAX_JOYSTICKS 8
83 #define AXIS_MIN -32768 /* minimum value for axis coordinate */
84 #define AXIS_MAX 32767 /* maximum value for axis coordinate */
85 #define JOY_AXIS_THRESHOLD (((AXIS_MAX)-(AXIS_MIN))/100) /* 1% motion */
87 /* external variables referenced. */
88 extern HWND SDL_HelperWindow;
92 static SDL_bool coinitialized = SDL_FALSE;
93 static LPDIRECTINPUT8 dinput = NULL;
94 static SDL_bool s_bDeviceAdded = SDL_FALSE;
95 static SDL_bool s_bDeviceRemoved = SDL_FALSE;
96 static SDL_JoystickID s_nInstanceID = -1;
97 static GUID *s_pKnownJoystickGUIDs = NULL;
98 static SDL_cond *s_condJoystickThread = NULL;
99 static SDL_mutex *s_mutexJoyStickEnum = NULL;
100 static SDL_Thread *s_threadJoystick = NULL;
101 static SDL_bool s_bJoystickThreadQuit = SDL_FALSE;
102 static SDL_bool s_bXInputEnabled = SDL_TRUE;
104 XInputGetState_t SDL_XInputGetState = NULL;
105 XInputSetState_t SDL_XInputSetState = NULL;
106 XInputGetCapabilities_t SDL_XInputGetCapabilities = NULL;
107 DWORD SDL_XInputVersion = 0;
109 static HANDLE s_pXInputDLL = 0;
110 static int s_XInputDLLRefCount = 0;
113 WIN_LoadXInputDLL(void)
118 SDL_assert(s_XInputDLLRefCount > 0);
119 s_XInputDLLRefCount++;
120 return 0; /* already loaded */
123 version = (1 << 16) | 4;
124 s_pXInputDLL = LoadLibrary( L"XInput1_4.dll" ); /* 1.4 Ships with Windows 8. */
126 version = (1 << 16) | 3;
127 s_pXInputDLL = LoadLibrary( L"XInput1_3.dll" ); /* 1.3 Ships with Vista and Win7, can be installed as a redistributable component. */
130 s_pXInputDLL = LoadLibrary( L"bin\\XInput1_3.dll" );
136 SDL_assert(s_XInputDLLRefCount == 0);
137 SDL_XInputVersion = version;
138 s_XInputDLLRefCount = 1;
140 /* 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... */
141 SDL_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, (LPCSTR)100 );
142 SDL_XInputSetState = (XInputSetState_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputSetState" );
143 SDL_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress( (HMODULE)s_pXInputDLL, "XInputGetCapabilities" );
144 if ( !SDL_XInputGetState || !SDL_XInputSetState || !SDL_XInputGetCapabilities ) {
145 WIN_UnloadXInputDLL();
153 WIN_UnloadXInputDLL(void)
155 if ( s_pXInputDLL ) {
156 SDL_assert(s_XInputDLLRefCount > 0);
157 if (--s_XInputDLLRefCount == 0) {
158 FreeLibrary( s_pXInputDLL );
162 SDL_assert(s_XInputDLLRefCount == 0);
167 extern HRESULT(WINAPI * DInputCreate) (HINSTANCE hinst, DWORD dwVersion,
168 LPDIRECTINPUT * ppDI,
169 LPUNKNOWN punkOuter);
170 struct JoyStick_DeviceData_
172 SDL_JoystickGUID guid;
173 DIDEVICEINSTANCE dxdevice;
175 Uint8 send_add_event;
176 SDL_JoystickID nInstanceID;
177 SDL_bool bXInputDevice;
179 struct JoyStick_DeviceData_ *pNext;
182 typedef struct JoyStick_DeviceData_ JoyStick_DeviceData;
184 static JoyStick_DeviceData *SYS_Joystick; /* array to hold joystick ID values */
186 /* local prototypes */
187 static int SetDIerror(const char *function, HRESULT code);
188 static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE *
189 pdidInstance, VOID * pContext);
190 static BOOL CALLBACK EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev,
192 static void SortDevObjects(SDL_Joystick *joystick);
193 static Uint8 TranslatePOV(DWORD value);
194 static int SDL_PrivateJoystickAxis_Int(SDL_Joystick * joystick, Uint8 axis,
196 static int SDL_PrivateJoystickHat_Int(SDL_Joystick * joystick, Uint8 hat,
198 static int SDL_PrivateJoystickButton_Int(SDL_Joystick * joystick,
199 Uint8 button, Uint8 state);
201 /* Taken from Wine - Thanks! */
202 DIOBJECTDATAFORMAT dfDIJoystick2[] = {
203 { &GUID_XAxis,DIJOFS_X,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
204 { &GUID_YAxis,DIJOFS_Y,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
205 { &GUID_ZAxis,DIJOFS_Z,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
206 { &GUID_RxAxis,DIJOFS_RX,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
207 { &GUID_RyAxis,DIJOFS_RY,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
208 { &GUID_RzAxis,DIJOFS_RZ,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
209 { &GUID_Slider,DIJOFS_SLIDER(0),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
210 { &GUID_Slider,DIJOFS_SLIDER(1),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
211 { &GUID_POV,DIJOFS_POV(0),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE,0},
212 { &GUID_POV,DIJOFS_POV(1),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE,0},
213 { &GUID_POV,DIJOFS_POV(2),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE,0},
214 { &GUID_POV,DIJOFS_POV(3),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE,0},
215 { NULL,DIJOFS_BUTTON(0),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
216 { NULL,DIJOFS_BUTTON(1),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
217 { NULL,DIJOFS_BUTTON(2),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
218 { NULL,DIJOFS_BUTTON(3),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
219 { NULL,DIJOFS_BUTTON(4),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
220 { NULL,DIJOFS_BUTTON(5),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
221 { NULL,DIJOFS_BUTTON(6),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
222 { NULL,DIJOFS_BUTTON(7),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
223 { NULL,DIJOFS_BUTTON(8),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
224 { NULL,DIJOFS_BUTTON(9),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
225 { NULL,DIJOFS_BUTTON(10),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
226 { NULL,DIJOFS_BUTTON(11),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
227 { NULL,DIJOFS_BUTTON(12),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
228 { NULL,DIJOFS_BUTTON(13),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
229 { NULL,DIJOFS_BUTTON(14),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
230 { NULL,DIJOFS_BUTTON(15),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
231 { NULL,DIJOFS_BUTTON(16),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
232 { NULL,DIJOFS_BUTTON(17),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
233 { NULL,DIJOFS_BUTTON(18),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
234 { NULL,DIJOFS_BUTTON(19),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
235 { NULL,DIJOFS_BUTTON(20),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
236 { NULL,DIJOFS_BUTTON(21),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
237 { NULL,DIJOFS_BUTTON(22),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
238 { NULL,DIJOFS_BUTTON(23),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
239 { NULL,DIJOFS_BUTTON(24),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
240 { NULL,DIJOFS_BUTTON(25),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
241 { NULL,DIJOFS_BUTTON(26),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
242 { NULL,DIJOFS_BUTTON(27),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
243 { NULL,DIJOFS_BUTTON(28),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
244 { NULL,DIJOFS_BUTTON(29),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
245 { NULL,DIJOFS_BUTTON(30),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
246 { NULL,DIJOFS_BUTTON(31),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
247 { NULL,DIJOFS_BUTTON(32),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
248 { NULL,DIJOFS_BUTTON(33),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
249 { NULL,DIJOFS_BUTTON(34),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
250 { NULL,DIJOFS_BUTTON(35),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
251 { NULL,DIJOFS_BUTTON(36),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
252 { NULL,DIJOFS_BUTTON(37),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
253 { NULL,DIJOFS_BUTTON(38),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
254 { NULL,DIJOFS_BUTTON(39),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
255 { NULL,DIJOFS_BUTTON(40),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
256 { NULL,DIJOFS_BUTTON(41),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
257 { NULL,DIJOFS_BUTTON(42),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
258 { NULL,DIJOFS_BUTTON(43),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
259 { NULL,DIJOFS_BUTTON(44),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
260 { NULL,DIJOFS_BUTTON(45),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
261 { NULL,DIJOFS_BUTTON(46),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
262 { NULL,DIJOFS_BUTTON(47),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
263 { NULL,DIJOFS_BUTTON(48),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
264 { NULL,DIJOFS_BUTTON(49),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
265 { NULL,DIJOFS_BUTTON(50),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
266 { NULL,DIJOFS_BUTTON(51),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
267 { NULL,DIJOFS_BUTTON(52),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
268 { NULL,DIJOFS_BUTTON(53),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
269 { NULL,DIJOFS_BUTTON(54),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
270 { NULL,DIJOFS_BUTTON(55),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
271 { NULL,DIJOFS_BUTTON(56),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
272 { NULL,DIJOFS_BUTTON(57),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
273 { NULL,DIJOFS_BUTTON(58),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
274 { NULL,DIJOFS_BUTTON(59),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
275 { NULL,DIJOFS_BUTTON(60),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
276 { NULL,DIJOFS_BUTTON(61),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
277 { NULL,DIJOFS_BUTTON(62),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
278 { NULL,DIJOFS_BUTTON(63),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
279 { NULL,DIJOFS_BUTTON(64),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
280 { NULL,DIJOFS_BUTTON(65),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
281 { NULL,DIJOFS_BUTTON(66),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
282 { NULL,DIJOFS_BUTTON(67),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
283 { NULL,DIJOFS_BUTTON(68),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
284 { NULL,DIJOFS_BUTTON(69),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
285 { NULL,DIJOFS_BUTTON(70),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
286 { NULL,DIJOFS_BUTTON(71),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
287 { NULL,DIJOFS_BUTTON(72),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
288 { NULL,DIJOFS_BUTTON(73),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
289 { NULL,DIJOFS_BUTTON(74),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
290 { NULL,DIJOFS_BUTTON(75),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
291 { NULL,DIJOFS_BUTTON(76),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
292 { NULL,DIJOFS_BUTTON(77),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
293 { NULL,DIJOFS_BUTTON(78),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
294 { NULL,DIJOFS_BUTTON(79),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
295 { NULL,DIJOFS_BUTTON(80),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
296 { NULL,DIJOFS_BUTTON(81),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
297 { NULL,DIJOFS_BUTTON(82),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
298 { NULL,DIJOFS_BUTTON(83),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
299 { NULL,DIJOFS_BUTTON(84),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
300 { NULL,DIJOFS_BUTTON(85),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
301 { NULL,DIJOFS_BUTTON(86),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
302 { NULL,DIJOFS_BUTTON(87),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
303 { NULL,DIJOFS_BUTTON(88),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
304 { NULL,DIJOFS_BUTTON(89),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
305 { NULL,DIJOFS_BUTTON(90),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
306 { NULL,DIJOFS_BUTTON(91),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
307 { NULL,DIJOFS_BUTTON(92),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
308 { NULL,DIJOFS_BUTTON(93),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
309 { NULL,DIJOFS_BUTTON(94),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
310 { NULL,DIJOFS_BUTTON(95),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
311 { NULL,DIJOFS_BUTTON(96),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
312 { NULL,DIJOFS_BUTTON(97),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
313 { NULL,DIJOFS_BUTTON(98),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
314 { NULL,DIJOFS_BUTTON(99),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
315 { NULL,DIJOFS_BUTTON(100),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
316 { NULL,DIJOFS_BUTTON(101),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
317 { NULL,DIJOFS_BUTTON(102),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
318 { NULL,DIJOFS_BUTTON(103),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
319 { NULL,DIJOFS_BUTTON(104),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
320 { NULL,DIJOFS_BUTTON(105),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
321 { NULL,DIJOFS_BUTTON(106),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
322 { NULL,DIJOFS_BUTTON(107),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
323 { NULL,DIJOFS_BUTTON(108),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
324 { NULL,DIJOFS_BUTTON(109),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
325 { NULL,DIJOFS_BUTTON(110),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
326 { NULL,DIJOFS_BUTTON(111),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
327 { NULL,DIJOFS_BUTTON(112),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
328 { NULL,DIJOFS_BUTTON(113),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
329 { NULL,DIJOFS_BUTTON(114),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
330 { NULL,DIJOFS_BUTTON(115),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
331 { NULL,DIJOFS_BUTTON(116),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
332 { NULL,DIJOFS_BUTTON(117),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
333 { NULL,DIJOFS_BUTTON(118),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
334 { NULL,DIJOFS_BUTTON(119),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
335 { NULL,DIJOFS_BUTTON(120),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
336 { NULL,DIJOFS_BUTTON(121),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
337 { NULL,DIJOFS_BUTTON(122),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
338 { NULL,DIJOFS_BUTTON(123),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
339 { NULL,DIJOFS_BUTTON(124),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
340 { NULL,DIJOFS_BUTTON(125),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
341 { NULL,DIJOFS_BUTTON(126),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
342 { NULL,DIJOFS_BUTTON(127),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
343 { &GUID_XAxis,FIELD_OFFSET(DIJOYSTATE2,lVX),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
344 { &GUID_YAxis,FIELD_OFFSET(DIJOYSTATE2,lVY),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
345 { &GUID_ZAxis,FIELD_OFFSET(DIJOYSTATE2,lVZ),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
346 { &GUID_RxAxis,FIELD_OFFSET(DIJOYSTATE2,lVRx),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
347 { &GUID_RyAxis,FIELD_OFFSET(DIJOYSTATE2,lVRy),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
348 { &GUID_RzAxis,FIELD_OFFSET(DIJOYSTATE2,lVRz),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
349 { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglVSlider[0]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
350 { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglVSlider[1]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
351 { &GUID_XAxis,FIELD_OFFSET(DIJOYSTATE2,lAX),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
352 { &GUID_YAxis,FIELD_OFFSET(DIJOYSTATE2,lAY),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
353 { &GUID_ZAxis,FIELD_OFFSET(DIJOYSTATE2,lAZ),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
354 { &GUID_RxAxis,FIELD_OFFSET(DIJOYSTATE2,lARx),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
355 { &GUID_RyAxis,FIELD_OFFSET(DIJOYSTATE2,lARy),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
356 { &GUID_RzAxis,FIELD_OFFSET(DIJOYSTATE2,lARz),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
357 { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglASlider[0]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
358 { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglASlider[1]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
359 { &GUID_XAxis,FIELD_OFFSET(DIJOYSTATE2,lFX),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
360 { &GUID_YAxis,FIELD_OFFSET(DIJOYSTATE2,lFY),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
361 { &GUID_ZAxis,FIELD_OFFSET(DIJOYSTATE2,lFZ),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
362 { &GUID_RxAxis,FIELD_OFFSET(DIJOYSTATE2,lFRx),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
363 { &GUID_RyAxis,FIELD_OFFSET(DIJOYSTATE2,lFRy),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
364 { &GUID_RzAxis,FIELD_OFFSET(DIJOYSTATE2,lFRz),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
365 { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglFSlider[0]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
366 { &GUID_Slider,FIELD_OFFSET(DIJOYSTATE2,rglFSlider[1]),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
369 const DIDATAFORMAT c_dfDIJoystick2 = {
370 sizeof(DIDATAFORMAT),
371 sizeof(DIOBJECTDATAFORMAT),
374 SDL_arraysize(dfDIJoystick2),
379 /* Convert a DirectInput return code to a text message */
381 SetDIerror(const char *function, HRESULT code)
384 return SDL_SetError("%s() [%s]: %s", function,
385 DXGetErrorString9A(code), DXGetErrorDescription9A(code));
387 return SDL_SetError("%s() DirectX error %d", function, code);
391 #define SAFE_RELEASE(p) \
394 (p)->lpVtbl->Release((p)); \
400 DEFINE_GUID(CLSID_WbemLocator, 0x4590f811,0x1d3a,0x11d0,0x89,0x1F,0x00,0xaa,0x00,0x4b,0x2e,0x24);
401 /* The Windows SDK doesn't define this GUID */
402 DEFINE_GUID(IID_IWbemLocator, 0xdc12a687,0x737f,0x11cf,0x88,0x4d,0x00,0xaa,0x00,0x4b,0x2e,0x24);
404 /*-----------------------------------------------------------------------------
406 * code from MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx
408 * Enum each PNP device using WMI and check each device ID to see if it contains
409 * "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it's an XInput device
410 * Unfortunately this information can not be found by just using DirectInput
411 *-----------------------------------------------------------------------------*/
412 BOOL IsXInputDevice( const GUID* pGuidProductFromDirectInput )
414 IWbemLocator* pIWbemLocator = NULL;
415 IEnumWbemClassObject* pEnumDevices = NULL;
416 IWbemClassObject* pDevices[20];
417 IWbemServices* pIWbemServices = NULL;
419 BSTR bstrNamespace = NULL;
420 BSTR bstrDeviceID = NULL;
421 BSTR bstrClassName = NULL;
422 SDL_bool bIsXinputDevice= SDL_FALSE;
428 if (!s_bXInputEnabled)
433 SDL_memset( pDevices, 0x0, sizeof(pDevices) );
435 /* CoInit if needed */
436 hr = CoInitialize(NULL);
437 bCleanupCOM = SUCCEEDED(hr);
440 hr = CoCreateInstance( &CLSID_WbemLocator,
442 CLSCTX_INPROC_SERVER,
444 (LPVOID*) &pIWbemLocator);
445 if( FAILED(hr) || pIWbemLocator == NULL )
448 bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup;
449 bstrClassName = SysAllocString( L"Win32_PNPEntity" ); if( bstrClassName == NULL ) goto LCleanup;
450 bstrDeviceID = SysAllocString( L"DeviceID" ); if( bstrDeviceID == NULL ) goto LCleanup;
453 hr = IWbemLocator_ConnectServer( pIWbemLocator, bstrNamespace, NULL, NULL, 0L,
454 0L, NULL, NULL, &pIWbemServices );
455 if( FAILED(hr) || pIWbemServices == NULL )
458 /* Switch security level to IMPERSONATE. */
459 CoSetProxyBlanket( (IUnknown *)pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
460 RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE );
462 hr = IWbemServices_CreateInstanceEnum( pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices );
463 if( FAILED(hr) || pEnumDevices == NULL )
466 /* Loop over all devices */
469 /* Get 20 at a time */
470 hr = IEnumWbemClassObject_Next( pEnumDevices, 10000, 20, pDevices, &uReturned );
476 for( iDevice=0; iDevice<uReturned; iDevice++ )
478 /* For each device, get its device ID */
479 hr = IWbemClassObject_Get( pDevices[iDevice], bstrDeviceID, 0L, &var, NULL, NULL );
480 if( SUCCEEDED( hr ) && var.vt == VT_BSTR && var.bstrVal != NULL )
482 /* Check if the device ID contains "IG_". If it does, then it's an XInput device */
483 /* This information can not be found from DirectInput */
484 char *pDeviceString = WIN_StringToUTF8( var.bstrVal );
485 if( SDL_strstr( pDeviceString, "IG_" ) )
487 /* If it does, then get the VID/PID from var.bstrVal */
488 long dwPid = 0, dwVid = 0;
489 char * strPid = NULL;
491 char * strVid = SDL_strstr( pDeviceString, "VID_" );
494 dwVid = SDL_strtol( strVid + 4, NULL, 16 );
496 strPid = SDL_strstr( pDeviceString, "PID_" );
499 dwPid = SDL_strtol( strPid + 4, NULL, 16 );
502 /* Compare the VID/PID to the DInput device */
503 dwVidPid = MAKELONG( dwVid, dwPid );
504 if( dwVidPid == pGuidProductFromDirectInput->Data1 )
506 bIsXinputDevice = SDL_TRUE;
510 SDL_free( pDeviceString );
512 if ( bIsXinputDevice )
515 SAFE_RELEASE( pDevices[iDevice] );
521 for( iDevice=0; iDevice<20; iDevice++ )
522 SAFE_RELEASE( pDevices[iDevice] );
523 SAFE_RELEASE( pEnumDevices );
524 SAFE_RELEASE( pIWbemLocator );
525 SAFE_RELEASE( pIWbemServices );
528 SysFreeString( bstrNamespace );
530 SysFreeString( bstrClassName );
532 SysFreeString( bstrDeviceID );
537 return bIsXinputDevice;
541 static SDL_bool s_bWindowsDeviceChanged = SDL_FALSE;
543 /* windowproc for our joystick detect thread message only window, to detect any USB device addition/removal
545 LRESULT CALLBACK SDL_PrivateJoystickDetectProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
547 case WM_DEVICECHANGE:
549 case DBT_DEVICEARRIVAL:
550 if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
551 s_bWindowsDeviceChanged = SDL_TRUE;
554 case DBT_DEVICEREMOVECOMPLETE:
555 if (((DEV_BROADCAST_HDR*)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
556 s_bWindowsDeviceChanged = SDL_TRUE;
563 return DefWindowProc (hwnd, message, wParam, lParam);
567 DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, \
568 0xC0, 0x4F, 0xB9, 0x51, 0xED);
570 /* Function/thread to scan the system for joysticks.
573 SDL_JoystickThread(void *_data)
575 HWND messageWindow = 0;
576 HDEVNOTIFY hNotify = 0;
577 DEV_BROADCAST_DEVICEINTERFACE dbh;
578 SDL_bool bOpenedXInputDevices[4];
581 SDL_memset( bOpenedXInputDevices, 0x0, sizeof(bOpenedXInputDevices) );
585 SDL_memset( &wincl, 0x0, sizeof(wincl) );
586 wincl.hInstance = GetModuleHandle( NULL );
587 wincl.lpszClassName = L"Message";
588 wincl.lpfnWndProc = SDL_PrivateJoystickDetectProc; /* This function is called by windows */
589 wincl.cbSize = sizeof (WNDCLASSEX);
591 if (!RegisterClassEx (&wincl))
593 return SDL_SetError("Failed to create register class for joystick autodetect.", GetLastError());
596 messageWindow = (HWND)CreateWindowEx( 0, L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL );
597 if ( !messageWindow )
599 return SDL_SetError("Failed to create message window for joystick autodetect.", GetLastError());
602 SDL_memset(&dbh, 0x0, sizeof(dbh));
604 dbh.dbcc_size = sizeof(dbh);
605 dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
606 dbh.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
608 hNotify = RegisterDeviceNotification( messageWindow, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE );
611 return SDL_SetError("Failed to create notify device for joystick autodetect.", GetLastError());
614 SDL_LockMutex( s_mutexJoyStickEnum );
615 while ( s_bJoystickThreadQuit == SDL_FALSE )
619 int nCurrentOpenedXInputDevices = 0;
620 int nNewOpenedXInputDevices = 0;
621 SDL_CondWaitTimeout( s_condJoystickThread, s_mutexJoyStickEnum, 300 );
623 while ( s_bJoystickThreadQuit == SDL_FALSE && PeekMessage(&messages, messageWindow, 0, 0, PM_NOREMOVE) )
625 if ( GetMessage(&messages, messageWindow, 0, 0) != 0 ) {
626 TranslateMessage(&messages);
627 DispatchMessage(&messages);
631 if ( s_bXInputEnabled && XINPUTGETCAPABILITIES )
633 /* scan for any change in XInput devices */
634 for ( userId = 0; userId < 4; userId++ )
636 XINPUT_CAPABILITIES capabilities;
639 if ( bOpenedXInputDevices[userId] == SDL_TRUE )
640 nCurrentOpenedXInputDevices++;
642 result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities );
643 if ( result == ERROR_SUCCESS )
645 bOpenedXInputDevices[userId] = SDL_TRUE;
646 nNewOpenedXInputDevices++;
650 bOpenedXInputDevices[userId] = SDL_FALSE;
655 if ( s_pKnownJoystickGUIDs && ( s_bWindowsDeviceChanged || nNewOpenedXInputDevices != nCurrentOpenedXInputDevices ) )
657 SDL_Delay( 300 ); /* wait for direct input to find out about this device */
659 s_bDeviceRemoved = SDL_TRUE;
660 s_bDeviceAdded = SDL_TRUE;
661 s_bWindowsDeviceChanged = SDL_FALSE;
664 SDL_UnlockMutex( s_mutexJoyStickEnum );
667 UnregisterDeviceNotification( hNotify );
670 DestroyWindow( messageWindow );
672 UnregisterClass( wincl.lpszClassName, wincl.hInstance );
674 WIN_CoUninitialize();
679 /* Function to scan the system for joysticks.
680 * This function should set SDL_numjoysticks to the number of available
681 * joysticks. Joystick 0 should be the system default joystick.
682 * It should return 0, or -1 on an unrecoverable fatal error.
685 SDL_SYS_JoystickInit(void)
689 const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
690 if (env && !SDL_atoi(env)) {
691 s_bXInputEnabled = SDL_FALSE;
694 result = WIN_CoInitialize();
695 if (FAILED(result)) {
696 return SetDIerror("CoInitialize", result);
699 coinitialized = SDL_TRUE;
701 result = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER,
702 &IID_IDirectInput8, (LPVOID)&dinput);
704 if (FAILED(result)) {
705 SDL_SYS_JoystickQuit();
706 return SetDIerror("CoCreateInstance", result);
709 /* Because we used CoCreateInstance, we need to Initialize it, first. */
710 instance = GetModuleHandle(NULL);
711 if (instance == NULL) {
712 SDL_SYS_JoystickQuit();
713 return SDL_SetError("GetModuleHandle() failed with error code %d.", GetLastError());
715 result = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION);
717 if (FAILED(result)) {
718 SDL_SYS_JoystickQuit();
719 return SetDIerror("IDirectInput::Initialize", result);
722 s_mutexJoyStickEnum = SDL_CreateMutex();
723 s_condJoystickThread = SDL_CreateCond();
724 s_bDeviceAdded = SDL_TRUE; /* force a scan of the system for joysticks this first time */
725 SDL_SYS_JoystickDetect();
727 if ((s_bXInputEnabled) && (WIN_LoadXInputDLL() == -1)) {
728 s_bXInputEnabled = SDL_FALSE; /* oh well. */
731 if ( !s_threadJoystick )
733 s_bJoystickThreadQuit = SDL_FALSE;
734 /* spin up the thread to detect hotplug of devices */
735 #if defined(__WIN32__) && !defined(HAVE_LIBC)
736 #undef SDL_CreateThread
737 s_threadJoystick= SDL_CreateThread( SDL_JoystickThread, "SDL_joystick", NULL, NULL, NULL );
739 s_threadJoystick = SDL_CreateThread( SDL_JoystickThread, "SDL_joystick", NULL );
742 return SDL_SYS_NumJoysticks();
745 /* return the number of joysticks that are connected right now */
746 int SDL_SYS_NumJoysticks()
749 JoyStick_DeviceData *device = SYS_Joystick;
753 device = device->pNext;
759 static int s_iNewGUID = 0;
761 /* helper function for direct input, gets called for each connected joystick */
763 EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
765 JoyStick_DeviceData *pNewJoystick;
766 JoyStick_DeviceData *pPrevJoystick = NULL;
767 SDL_bool bXInputDevice;
768 pNewJoystick = *(JoyStick_DeviceData **)pContext;
769 while ( pNewJoystick )
771 if ( !SDL_memcmp( &pNewJoystick->dxdevice.guidInstance, &pdidInstance->guidInstance, sizeof(pNewJoystick->dxdevice.guidInstance) ) )
773 /* if we are replacing the front of the list then update it */
774 if ( pNewJoystick == *(JoyStick_DeviceData **)pContext )
776 *(JoyStick_DeviceData **)pContext = pNewJoystick->pNext;
778 else if ( pPrevJoystick )
780 pPrevJoystick->pNext = pNewJoystick->pNext;
783 pNewJoystick->pNext = SYS_Joystick;
784 SYS_Joystick = pNewJoystick;
786 s_pKnownJoystickGUIDs[ s_iNewGUID ] = pdidInstance->guidInstance;
788 if ( s_iNewGUID < MAX_JOYSTICKS )
789 return DIENUM_CONTINUE; /* already have this joystick loaded, just keep going */
794 pPrevJoystick = pNewJoystick;
795 pNewJoystick = pNewJoystick->pNext;
798 s_bDeviceAdded = SDL_TRUE;
800 bXInputDevice = IsXInputDevice( &pdidInstance->guidProduct );
802 pNewJoystick = (JoyStick_DeviceData *)SDL_malloc( sizeof(JoyStick_DeviceData) );
806 pNewJoystick->bXInputDevice = SDL_TRUE;
807 pNewJoystick->XInputUserId = INVALID_XINPUT_USERID;
811 pNewJoystick->bXInputDevice = SDL_FALSE;
814 SDL_memcpy(&(pNewJoystick->dxdevice), pdidInstance,
815 sizeof(DIDEVICEINSTANCE));
817 pNewJoystick->joystickname = WIN_StringToUTF8(pdidInstance->tszProductName);
818 pNewJoystick->send_add_event = 1;
819 pNewJoystick->nInstanceID = ++s_nInstanceID;
820 SDL_memcpy( &pNewJoystick->guid, &pdidInstance->guidProduct, sizeof(pNewJoystick->guid) );
821 pNewJoystick->pNext = NULL;
825 pNewJoystick->pNext = SYS_Joystick;
827 SYS_Joystick = pNewJoystick;
829 s_pKnownJoystickGUIDs[ s_iNewGUID ] = pdidInstance->guidInstance;
832 if ( s_iNewGUID < MAX_JOYSTICKS )
833 return DIENUM_CONTINUE; /* already have this joystick loaded, just keep going */
838 /* detect any new joysticks being inserted into the system */
839 void SDL_SYS_JoystickDetect()
841 JoyStick_DeviceData *pCurList = NULL;
842 /* only enum the devices if the joystick thread told us something changed */
843 if ( s_bDeviceAdded || s_bDeviceRemoved )
845 s_bDeviceAdded = SDL_FALSE;
846 s_bDeviceRemoved = SDL_FALSE;
848 pCurList = SYS_Joystick;
851 SDL_LockMutex( s_mutexJoyStickEnum );
853 if ( !s_pKnownJoystickGUIDs )
854 s_pKnownJoystickGUIDs = SDL_malloc( sizeof(GUID)*MAX_JOYSTICKS );
856 SDL_memset( s_pKnownJoystickGUIDs, 0x0, sizeof(GUID)*MAX_JOYSTICKS );
858 /* Look for joysticks, wheels, head trackers, gamepads, etc.. */
859 IDirectInput8_EnumDevices(dinput,
860 DI8DEVCLASS_GAMECTRL,
861 EnumJoysticksCallback,
862 &pCurList, DIEDFL_ATTACHEDONLY);
864 SDL_UnlockMutex( s_mutexJoyStickEnum );
871 JoyStick_DeviceData *pListNext = NULL;
872 #if !SDL_EVENTS_DISABLED
874 event.type = SDL_JOYDEVICEREMOVED;
876 if (SDL_GetEventState(event.type) == SDL_ENABLE) {
877 event.jdevice.which = pCurList->nInstanceID;
878 if ((SDL_EventOK == NULL)
879 || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
880 SDL_PushEvent(&event);
883 #endif /* !SDL_EVENTS_DISABLED */
885 pListNext = pCurList->pNext;
886 SDL_free(pCurList->joystickname);
887 SDL_free( pCurList );
888 pCurList = pListNext;
893 if ( s_bDeviceAdded )
895 JoyStick_DeviceData *pNewJoystick;
896 int device_index = 0;
897 s_bDeviceAdded = SDL_FALSE;
898 pNewJoystick = SYS_Joystick;
899 while ( pNewJoystick )
901 if ( pNewJoystick->send_add_event )
903 #if !SDL_EVENTS_DISABLED
905 event.type = SDL_JOYDEVICEADDED;
907 if (SDL_GetEventState(event.type) == SDL_ENABLE) {
908 event.jdevice.which = device_index;
909 if ((SDL_EventOK == NULL)
910 || (*SDL_EventOK) (SDL_EventOKParam, &event)) {
911 SDL_PushEvent(&event);
914 #endif /* !SDL_EVENTS_DISABLED */
915 pNewJoystick->send_add_event = 0;
918 pNewJoystick = pNewJoystick->pNext;
923 /* we need to poll if we have pending hotplug device changes or connected devices */
924 SDL_bool SDL_SYS_JoystickNeedsPolling()
926 /* we have a new device or one was pulled, we need to think this frame please */
927 if ( s_bDeviceAdded || s_bDeviceRemoved )
933 /* Function to get the device-dependent name of a joystick */
935 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
937 JoyStick_DeviceData *device = SYS_Joystick;
939 for (; device_index > 0; device_index--)
940 device = device->pNext;
942 return device->joystickname;
945 /* Function to perform the mapping between current device instance and this joysticks instance id */
946 SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
948 JoyStick_DeviceData *device = SYS_Joystick;
951 for (index = device_index; index > 0; index--)
952 device = device->pNext;
954 return device->nInstanceID;
957 /* Function to open a joystick for use.
958 The joystick to open is specified by the index field of the joystick.
959 This should fill the nbuttons and naxes fields of the joystick structure.
960 It returns 0, or -1 if there is an error.
963 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
966 LPDIRECTINPUTDEVICE8 device;
968 JoyStick_DeviceData *joystickdevice = SYS_Joystick;
970 for (; device_index > 0; device_index--)
971 joystickdevice = joystickdevice->pNext;
973 SDL_memset(&dipdw, 0, sizeof(DIPROPDWORD));
974 dipdw.diph.dwSize = sizeof(DIPROPDWORD);
975 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
977 /* allocate memory for system specific hardware data */
978 joystick->instance_id = joystickdevice->nInstanceID;
979 joystick->closed = 0;
981 (struct joystick_hwdata *) SDL_malloc(sizeof(struct joystick_hwdata));
982 if (joystick->hwdata == NULL) {
983 return SDL_OutOfMemory();
985 SDL_memset(joystick->hwdata, 0, sizeof(struct joystick_hwdata));
986 joystick->hwdata->buffered = 1;
987 joystick->hwdata->removed = 0;
988 joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS);
989 joystick->hwdata->guid = joystickdevice->guid;
991 if ( joystickdevice->bXInputDevice )
993 XINPUT_CAPABILITIES capabilities;
996 JoyStick_DeviceData *joysticklist = SYS_Joystick;
997 /* scan the opened joysticks and pick the next free xinput userid for this one */
998 for( ; joysticklist; joysticklist = joysticklist->pNext)
1000 if ( joysticklist->bXInputDevice && joysticklist->XInputUserId == userId )
1004 if ( s_bXInputEnabled && XINPUTGETCAPABILITIES )
1006 result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities );
1007 if ( result == ERROR_SUCCESS )
1009 const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
1010 SDL_bool bIsSupported = SDL_FALSE;
1011 /* Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad. */
1012 bIsSupported = ( capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD );
1014 if ( !bIsSupported )
1016 joystickdevice->bXInputDevice = SDL_FALSE;
1021 joystick->hwdata->bXInputDevice = SDL_TRUE;
1022 if ((!bIs14OrLater) || (capabilities.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
1023 joystick->hwdata->bXInputHaptic = SDL_TRUE;
1025 SDL_memset( joystick->hwdata->XInputState, 0x0, sizeof(joystick->hwdata->XInputState) );
1026 joystickdevice->XInputUserId = userId;
1027 joystick->hwdata->userid = userId;
1028 joystick->hwdata->currentXInputSlot = 0;
1029 /* The XInput API has a hard coded button/axis mapping, so we just match it */
1030 joystick->naxes = 6;
1031 joystick->nbuttons = 15;
1032 joystick->nballs = 0;
1033 joystick->nhats = 0;
1038 joystickdevice->bXInputDevice = SDL_FALSE;
1043 joystickdevice->bXInputDevice = SDL_FALSE;
1047 if ( joystickdevice->bXInputDevice == SDL_FALSE )
1049 joystick->hwdata->bXInputDevice = SDL_FALSE;
1052 IDirectInput8_CreateDevice(dinput,
1053 &(joystickdevice->dxdevice.guidInstance), &device, NULL);
1054 if (FAILED(result)) {
1055 return SetDIerror("IDirectInput::CreateDevice", result);
1058 /* Now get the IDirectInputDevice8 interface, instead. */
1059 result = IDirectInputDevice8_QueryInterface(device,
1060 &IID_IDirectInputDevice8,
1061 (LPVOID *) & joystick->
1062 hwdata->InputDevice);
1063 /* We are done with this object. Use the stored one from now on. */
1064 IDirectInputDevice8_Release(device);
1066 if (FAILED(result)) {
1067 return SetDIerror("IDirectInputDevice8::QueryInterface", result);
1070 /* Acquire shared access. Exclusive access is required for forces,
1073 IDirectInputDevice8_SetCooperativeLevel(joystick->hwdata->
1074 InputDevice, SDL_HelperWindow,
1075 DISCL_NONEXCLUSIVE |
1077 if (FAILED(result)) {
1078 return SetDIerror("IDirectInputDevice8::SetCooperativeLevel", result);
1081 /* Use the extended data structure: DIJOYSTATE2. */
1083 IDirectInputDevice8_SetDataFormat(joystick->hwdata->InputDevice,
1085 if (FAILED(result)) {
1086 return SetDIerror("IDirectInputDevice8::SetDataFormat", result);
1089 /* Get device capabilities */
1091 IDirectInputDevice8_GetCapabilities(joystick->hwdata->InputDevice,
1092 &joystick->hwdata->Capabilities);
1094 if (FAILED(result)) {
1095 return SetDIerror("IDirectInputDevice8::GetCapabilities", result);
1098 /* Force capable? */
1099 if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
1101 result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1103 if (FAILED(result)) {
1104 return SetDIerror("IDirectInputDevice8::Acquire", result);
1107 /* reset all accuators. */
1109 IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->
1113 /* Not necessarily supported, ignore if not supported.
1114 if (FAILED(result)) {
1115 return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand", result);
1119 result = IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
1121 if (FAILED(result)) {
1122 return SetDIerror("IDirectInputDevice8::Unacquire", result);
1125 /* Turn on auto-centering for a ForceFeedback device (until told
1127 dipdw.diph.dwObj = 0;
1128 dipdw.diph.dwHow = DIPH_DEVICE;
1129 dipdw.dwData = DIPROPAUTOCENTER_ON;
1132 IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
1133 DIPROP_AUTOCENTER, &dipdw.diph);
1135 /* Not necessarily supported, ignore if not supported.
1136 if (FAILED(result)) {
1137 return SetDIerror("IDirectInputDevice8::SetProperty", result);
1142 /* What buttons and axes does it have? */
1143 IDirectInputDevice8_EnumObjects(joystick->hwdata->InputDevice,
1144 EnumDevObjectsCallback, joystick,
1145 DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);
1147 /* Reorder the input objects. Some devices do not report the X axis as
1148 * the first axis, for example. */
1149 SortDevObjects(joystick);
1151 dipdw.diph.dwObj = 0;
1152 dipdw.diph.dwHow = DIPH_DEVICE;
1153 dipdw.dwData = INPUT_QSIZE;
1155 /* Set the buffer size */
1157 IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
1158 DIPROP_BUFFERSIZE, &dipdw.diph);
1160 if (result == DI_POLLEDDEVICE) {
1161 /* This device doesn't support buffering, so we're forced
1162 * to use less reliable polling. */
1163 joystick->hwdata->buffered = 0;
1164 } else if (FAILED(result)) {
1165 return SetDIerror("IDirectInputDevice8::SetProperty", result);
1171 /* return true if this joystick is plugged in right now */
1172 SDL_bool SDL_SYS_JoystickAttached( SDL_Joystick * joystick )
1174 return joystick->closed == 0 && joystick->hwdata->removed == 0;
1178 /* Sort using the data offset into the DInput struct.
1179 * This gives a reasonable ordering for the inputs. */
1181 SortDevFunc(const void *a, const void *b)
1183 const input_t *inputA = (const input_t*)a;
1184 const input_t *inputB = (const input_t*)b;
1186 if (inputA->ofs < inputB->ofs)
1188 if (inputA->ofs > inputB->ofs)
1193 /* Sort the input objects and recalculate the indices for each input. */
1195 SortDevObjects(SDL_Joystick *joystick)
1197 input_t *inputs = joystick->hwdata->Inputs;
1203 SDL_qsort(inputs, joystick->hwdata->NumInputs, sizeof(input_t), SortDevFunc);
1205 for (n = 0; n < joystick->hwdata->NumInputs; n++)
1207 switch (inputs[n].type)
1210 inputs[n].num = nButtons;
1215 inputs[n].num = nHats;
1220 inputs[n].num = nAxis;
1227 static BOOL CALLBACK
1228 EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
1230 SDL_Joystick *joystick = (SDL_Joystick *) pvRef;
1232 input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs];
1234 if (dev->dwType & DIDFT_BUTTON) {
1236 in->num = joystick->nbuttons;
1237 in->ofs = DIJOFS_BUTTON( in->num );
1238 joystick->nbuttons++;
1239 } else if (dev->dwType & DIDFT_POV) {
1241 in->num = joystick->nhats;
1242 in->ofs = DIJOFS_POV( in->num );
1244 } else if (dev->dwType & DIDFT_AXIS) {
1249 in->num = joystick->naxes;
1250 /* work our the axis this guy maps too, thanks for the code icculus! */
1251 if ( !SDL_memcmp( &dev->guidType, &GUID_XAxis, sizeof(dev->guidType) ) )
1253 else if ( !SDL_memcmp( &dev->guidType, &GUID_YAxis, sizeof(dev->guidType) ) )
1255 else if ( !SDL_memcmp( &dev->guidType, &GUID_ZAxis, sizeof(dev->guidType) ) )
1257 else if ( !SDL_memcmp( &dev->guidType, &GUID_RxAxis, sizeof(dev->guidType) ) )
1258 in->ofs = DIJOFS_RX;
1259 else if ( !SDL_memcmp( &dev->guidType, &GUID_RyAxis, sizeof(dev->guidType) ) )
1260 in->ofs = DIJOFS_RY;
1261 else if ( !SDL_memcmp( &dev->guidType, &GUID_RzAxis, sizeof(dev->guidType) ) )
1262 in->ofs = DIJOFS_RZ;
1263 else if ( !SDL_memcmp( &dev->guidType, &GUID_Slider, sizeof(dev->guidType) ) )
1265 in->ofs = DIJOFS_SLIDER( joystick->hwdata->NumSliders );
1266 ++joystick->hwdata->NumSliders;
1270 return DIENUM_CONTINUE; /* not an axis we can grok */
1273 diprg.diph.dwSize = sizeof(diprg);
1274 diprg.diph.dwHeaderSize = sizeof(diprg.diph);
1275 diprg.diph.dwObj = dev->dwType;
1276 diprg.diph.dwHow = DIPH_BYID;
1277 diprg.lMin = AXIS_MIN;
1278 diprg.lMax = AXIS_MAX;
1281 IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
1282 DIPROP_RANGE, &diprg.diph);
1283 if (FAILED(result)) {
1284 return DIENUM_CONTINUE; /* don't use this axis */
1287 /* Set dead zone to 0. */
1288 dilong.diph.dwSize = sizeof(dilong);
1289 dilong.diph.dwHeaderSize = sizeof(dilong.diph);
1290 dilong.diph.dwObj = dev->dwType;
1291 dilong.diph.dwHow = DIPH_BYID;
1294 IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
1295 DIPROP_DEADZONE, &dilong.diph);
1296 if (FAILED(result)) {
1297 return DIENUM_CONTINUE; /* don't use this axis */
1302 /* not supported at this time */
1303 return DIENUM_CONTINUE;
1306 joystick->hwdata->NumInputs++;
1308 if (joystick->hwdata->NumInputs == MAX_INPUTS) {
1309 return DIENUM_STOP; /* too many */
1312 return DIENUM_CONTINUE;
1315 /* Function to update the state of a joystick - called as a device poll.
1316 * This function shouldn't update the joystick structure directly,
1317 * but instead should call SDL_PrivateJoystick*() to deliver events
1318 * and update joystick device state.
1321 SDL_SYS_JoystickUpdate_Polled(SDL_Joystick * joystick)
1328 IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
1329 sizeof(DIJOYSTATE2), &state);
1330 if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
1331 IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1333 IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
1334 sizeof(DIJOYSTATE2), &state);
1337 if ( result != DI_OK )
1339 joystick->hwdata->send_remove_event = 1;
1340 joystick->hwdata->removed = 1;
1344 /* Set each known axis, button and POV. */
1345 for (i = 0; i < joystick->hwdata->NumInputs; ++i) {
1346 const input_t *in = &joystick->hwdata->Inputs[i];
1352 SDL_PrivateJoystickAxis_Int(joystick, in->num,
1356 SDL_PrivateJoystickAxis_Int(joystick, in->num,
1360 SDL_PrivateJoystickAxis_Int(joystick, in->num,
1364 SDL_PrivateJoystickAxis_Int(joystick, in->num,
1365 (Sint16) state.lRx);
1368 SDL_PrivateJoystickAxis_Int(joystick, in->num,
1369 (Sint16) state.lRy);
1372 SDL_PrivateJoystickAxis_Int(joystick, in->num,
1373 (Sint16) state.lRz);
1375 case DIJOFS_SLIDER(0):
1376 SDL_PrivateJoystickAxis_Int(joystick, in->num,
1377 (Sint16) state.rglSlider[0]);
1379 case DIJOFS_SLIDER(1):
1380 SDL_PrivateJoystickAxis_Int(joystick, in->num,
1381 (Sint16) state.rglSlider[1]);
1388 SDL_PrivateJoystickButton_Int(joystick, in->num,
1390 rgbButtons[in->ofs -
1397 Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs -
1399 SDL_PrivateJoystickHat_Int(joystick, in->num, pos);
1407 SDL_SYS_JoystickUpdate_Buffered(SDL_Joystick * joystick)
1412 DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
1414 numevents = INPUT_QSIZE;
1416 IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
1417 sizeof(DIDEVICEOBJECTDATA), evtbuf,
1419 if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
1420 IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1422 IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
1423 sizeof(DIDEVICEOBJECTDATA),
1424 evtbuf, &numevents, 0);
1427 /* Handle the events or punt */
1430 joystick->hwdata->send_remove_event = 1;
1431 joystick->hwdata->removed = 1;
1435 for (i = 0; i < (int) numevents; ++i) {
1438 for (j = 0; j < joystick->hwdata->NumInputs; ++j) {
1439 const input_t *in = &joystick->hwdata->Inputs[j];
1441 if (evtbuf[i].dwOfs != in->ofs)
1446 SDL_PrivateJoystickAxis(joystick, in->num,
1447 (Sint16) evtbuf[i].dwData);
1450 SDL_PrivateJoystickButton(joystick, in->num,
1452 dwData ? SDL_PRESSED :
1457 Uint8 pos = TranslatePOV(evtbuf[i].dwData);
1458 SDL_PrivateJoystickHat(joystick, in->num, pos);
1466 /* Function to return > 0 if a bit array of buttons differs after applying a mask
1468 int ButtonChanged( int ButtonsNow, int ButtonsPrev, int ButtonMask )
1470 return ( ButtonsNow & ButtonMask ) != ( ButtonsPrev & ButtonMask );
1473 /* Function to update the state of a XInput style joystick.
1476 SDL_SYS_JoystickUpdate_XInput(SDL_Joystick * joystick)
1480 if ( !XINPUTGETSTATE )
1483 result = XINPUTGETSTATE( joystick->hwdata->userid, &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot] );
1484 if ( result == ERROR_DEVICE_NOT_CONNECTED )
1486 joystick->hwdata->send_remove_event = 1;
1487 joystick->hwdata->removed = 1;
1491 /* only fire events if the data changed from last time */
1492 if ( joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot].dwPacketNumber != 0
1493 && joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot].dwPacketNumber != joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot^1].dwPacketNumber )
1495 XINPUT_STATE_EX *pXInputState = &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot];
1496 XINPUT_STATE_EX *pXInputStatePrev = &joystick->hwdata->XInputState[joystick->hwdata->currentXInputSlot ^ 1];
1498 SDL_PrivateJoystickAxis( joystick, 0, (Sint16)pXInputState->Gamepad.sThumbLX );
1499 SDL_PrivateJoystickAxis( joystick, 1, (Sint16)(-pXInputState->Gamepad.sThumbLY) );
1500 SDL_PrivateJoystickAxis( joystick, 2, (Sint16)pXInputState->Gamepad.sThumbRX );
1501 SDL_PrivateJoystickAxis( joystick, 3, (Sint16)(-pXInputState->Gamepad.sThumbRY) );
1502 SDL_PrivateJoystickAxis( joystick, 4, (Sint16)(((int)pXInputState->Gamepad.bLeftTrigger*65535/255) - 32768));
1503 SDL_PrivateJoystickAxis( joystick, 5, (Sint16)(((int)pXInputState->Gamepad.bRightTrigger*65535/255) - 32768));
1505 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_UP ) )
1506 SDL_PrivateJoystickButton(joystick, 0, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ? SDL_PRESSED : SDL_RELEASED );
1507 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_DOWN ) )
1508 SDL_PrivateJoystickButton(joystick, 1, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ? SDL_PRESSED : SDL_RELEASED );
1509 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_LEFT ) )
1510 SDL_PrivateJoystickButton(joystick, 2, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ? SDL_PRESSED : SDL_RELEASED );
1511 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT ) )
1512 SDL_PrivateJoystickButton(joystick, 3, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ? SDL_PRESSED : SDL_RELEASED );
1513 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_START ) )
1514 SDL_PrivateJoystickButton(joystick, 4, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_START ? SDL_PRESSED : SDL_RELEASED );
1515 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_BACK ) )
1516 SDL_PrivateJoystickButton(joystick, 5, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_BACK ? SDL_PRESSED : SDL_RELEASED );
1517 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_THUMB ) )
1518 SDL_PrivateJoystickButton(joystick, 6, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ? SDL_PRESSED : SDL_RELEASED );
1519 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB ) )
1520 SDL_PrivateJoystickButton(joystick, 7, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ? SDL_PRESSED : SDL_RELEASED );
1521 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER ) )
1522 SDL_PrivateJoystickButton(joystick, 8, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER ? SDL_PRESSED : SDL_RELEASED );
1523 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER ) )
1524 SDL_PrivateJoystickButton(joystick, 9, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER ? SDL_PRESSED : SDL_RELEASED );
1525 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_A ) )
1526 SDL_PrivateJoystickButton(joystick, 10, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_A ? SDL_PRESSED : SDL_RELEASED );
1527 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_B ) )
1528 SDL_PrivateJoystickButton(joystick, 11, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_B ? SDL_PRESSED : SDL_RELEASED );
1529 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_X ) )
1530 SDL_PrivateJoystickButton(joystick, 12, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_X ? SDL_PRESSED : SDL_RELEASED );
1531 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, XINPUT_GAMEPAD_Y ) )
1532 SDL_PrivateJoystickButton(joystick, 13, pXInputState->Gamepad.wButtons & XINPUT_GAMEPAD_Y ? SDL_PRESSED : SDL_RELEASED );
1533 if ( ButtonChanged( pXInputState->Gamepad.wButtons, pXInputStatePrev->Gamepad.wButtons, 0x400 ) )
1534 SDL_PrivateJoystickButton(joystick, 14, pXInputState->Gamepad.wButtons & 0x400 ? SDL_PRESSED : SDL_RELEASED ); /* 0x400 is the undocumented code for the guide button */
1536 joystick->hwdata->currentXInputSlot ^= 1;
1543 TranslatePOV(DWORD value)
1545 const int HAT_VALS[] = {
1547 SDL_HAT_UP | SDL_HAT_RIGHT,
1549 SDL_HAT_DOWN | SDL_HAT_RIGHT,
1551 SDL_HAT_DOWN | SDL_HAT_LEFT,
1553 SDL_HAT_UP | SDL_HAT_LEFT
1556 if (LOWORD(value) == 0xFFFF)
1557 return SDL_HAT_CENTERED;
1559 /* Round the value up: */
1565 return SDL_HAT_CENTERED; /* shouldn't happen */
1567 return HAT_VALS[value];
1570 /* SDL_PrivateJoystick* doesn't discard duplicate events, so we need to
1573 SDL_PrivateJoystickAxis_Int(SDL_Joystick * joystick, Uint8 axis, Sint16 value)
1575 if (joystick->axes[axis] != value)
1576 return SDL_PrivateJoystickAxis(joystick, axis, value);
1581 SDL_PrivateJoystickHat_Int(SDL_Joystick * joystick, Uint8 hat, Uint8 value)
1583 if (joystick->hats[hat] != value)
1584 return SDL_PrivateJoystickHat(joystick, hat, value);
1589 SDL_PrivateJoystickButton_Int(SDL_Joystick * joystick, Uint8 button,
1592 if (joystick->buttons[button] != state)
1593 return SDL_PrivateJoystickButton(joystick, button, state);
1598 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
1602 if ( joystick->closed || !joystick->hwdata )
1605 if (joystick->hwdata->bXInputDevice)
1607 SDL_SYS_JoystickUpdate_XInput(joystick);
1611 result = IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);
1612 if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
1613 IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1614 IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);
1617 if (joystick->hwdata->buffered)
1618 SDL_SYS_JoystickUpdate_Buffered(joystick);
1620 SDL_SYS_JoystickUpdate_Polled(joystick);
1623 if ( joystick->hwdata->removed )
1625 joystick->closed = 1;
1626 joystick->uncentered = 1;
1630 /* Function to close a joystick after use */
1632 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
1634 if ( joystick->hwdata->bXInputDevice )
1636 JoyStick_DeviceData *joysticklist = SYS_Joystick;
1637 /* scan the opened joysticks and clear the userid for this instance */
1638 for( ; joysticklist; joysticklist = joysticklist->pNext)
1640 if ( joysticklist->bXInputDevice && joysticklist->nInstanceID == joystick->instance_id )
1642 joysticklist->XInputUserId = INVALID_XINPUT_USERID;
1649 IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
1650 IDirectInputDevice8_Release(joystick->hwdata->InputDevice);
1653 if (joystick->hwdata != NULL) {
1654 /* free system specific hardware data */
1655 SDL_free(joystick->hwdata);
1658 joystick->closed = 1;
1661 /* Function to perform any system-specific joystick related cleanup */
1663 SDL_SYS_JoystickQuit(void)
1665 JoyStick_DeviceData *device = SYS_Joystick;
1669 JoyStick_DeviceData *device_next = device->pNext;
1670 SDL_free(device->joystickname);
1672 device = device_next;
1674 SYS_Joystick = NULL;
1676 if ( s_threadJoystick )
1678 SDL_LockMutex( s_mutexJoyStickEnum );
1679 s_bJoystickThreadQuit = SDL_TRUE;
1680 SDL_CondBroadcast( s_condJoystickThread ); /* signal the joystick thread to quit */
1681 SDL_UnlockMutex( s_mutexJoyStickEnum );
1682 SDL_WaitThread( s_threadJoystick, NULL ); /* wait for it to bugger off */
1684 SDL_DestroyMutex( s_mutexJoyStickEnum );
1685 SDL_DestroyCond( s_condJoystickThread );
1686 s_condJoystickThread= NULL;
1687 s_mutexJoyStickEnum = NULL;
1688 s_threadJoystick = NULL;
1691 if (dinput != NULL) {
1692 IDirectInput8_Release(dinput);
1696 if (coinitialized) {
1697 WIN_CoUninitialize();
1698 coinitialized = SDL_FALSE;
1701 if ( s_pKnownJoystickGUIDs )
1703 SDL_free( s_pKnownJoystickGUIDs );
1704 s_pKnownJoystickGUIDs = NULL;
1707 if (s_bXInputEnabled) {
1708 WIN_UnloadXInputDLL();
1713 /* return the stable device guid for this device index */
1714 SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )
1716 JoyStick_DeviceData *device = SYS_Joystick;
1719 for (index = device_index; index > 0; index--)
1720 device = device->pNext;
1722 return device->guid;
1725 SDL_JoystickGUID SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
1727 return joystick->hwdata->guid;
1730 /* return SDL_TRUE if this device is using XInput */
1731 SDL_bool SDL_SYS_IsXInputDeviceIndex(int device_index)
1733 JoyStick_DeviceData *device = SYS_Joystick;
1736 for (index = device_index; index > 0; index--)
1737 device = device->pNext;
1739 return device->bXInputDevice;
1742 #endif /* SDL_JOYSTICK_DINPUT */
1744 /* vi: set ts=4 sw=4 expandtab: */