2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 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_internal.h"
23 #include "../SDL_sysjoystick.h"
25 #if SDL_JOYSTICK_DINPUT
27 #include "SDL_windowsjoystick_c.h"
28 #include "SDL_dinputjoystick_c.h"
29 #include "SDL_rawinputjoystick_c.h"
30 #include "SDL_xinputjoystick_c.h"
31 #include "../hidapi/SDL_hidapijoystick_c.h"
33 #ifndef DIDFT_OPTIONAL
34 #define DIDFT_OPTIONAL 0x80000000
37 #define INPUT_QSIZE 32 /* Buffer up to 32 input messages */
38 #define JOY_AXIS_THRESHOLD (((SDL_JOYSTICK_AXIS_MAX)-(SDL_JOYSTICK_AXIS_MIN))/100) /* 1% motion */
40 #define CONVERT_MAGNITUDE(x) (((x)*10000) / 0x7FFF)
42 /* external variables referenced. */
43 extern HWND SDL_HelperWindow;
46 static SDL_bool coinitialized = SDL_FALSE;
47 static LPDIRECTINPUT8 dinput = NULL;
48 static PRAWINPUTDEVICELIST SDL_RawDevList = NULL;
49 static UINT SDL_RawDevListCount = 0;
51 /* Taken from Wine - Thanks! */
52 static DIOBJECTDATAFORMAT dfDIJoystick2[] = {
53 { &GUID_XAxis, DIJOFS_X, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
54 { &GUID_YAxis, DIJOFS_Y, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
55 { &GUID_ZAxis, DIJOFS_Z, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
56 { &GUID_RxAxis, DIJOFS_RX, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
57 { &GUID_RyAxis, DIJOFS_RY, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
58 { &GUID_RzAxis, DIJOFS_RZ, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
59 { &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
60 { &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
61 { &GUID_POV, DIJOFS_POV(0), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
62 { &GUID_POV, DIJOFS_POV(1), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
63 { &GUID_POV, DIJOFS_POV(2), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
64 { &GUID_POV, DIJOFS_POV(3), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
65 { NULL, DIJOFS_BUTTON(0), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
66 { NULL, DIJOFS_BUTTON(1), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
67 { NULL, DIJOFS_BUTTON(2), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
68 { NULL, DIJOFS_BUTTON(3), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
69 { NULL, DIJOFS_BUTTON(4), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
70 { NULL, DIJOFS_BUTTON(5), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
71 { NULL, DIJOFS_BUTTON(6), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
72 { NULL, DIJOFS_BUTTON(7), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
73 { NULL, DIJOFS_BUTTON(8), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
74 { NULL, DIJOFS_BUTTON(9), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
75 { NULL, DIJOFS_BUTTON(10), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
76 { NULL, DIJOFS_BUTTON(11), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
77 { NULL, DIJOFS_BUTTON(12), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
78 { NULL, DIJOFS_BUTTON(13), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
79 { NULL, DIJOFS_BUTTON(14), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
80 { NULL, DIJOFS_BUTTON(15), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
81 { NULL, DIJOFS_BUTTON(16), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
82 { NULL, DIJOFS_BUTTON(17), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
83 { NULL, DIJOFS_BUTTON(18), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
84 { NULL, DIJOFS_BUTTON(19), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
85 { NULL, DIJOFS_BUTTON(20), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
86 { NULL, DIJOFS_BUTTON(21), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
87 { NULL, DIJOFS_BUTTON(22), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
88 { NULL, DIJOFS_BUTTON(23), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
89 { NULL, DIJOFS_BUTTON(24), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
90 { NULL, DIJOFS_BUTTON(25), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
91 { NULL, DIJOFS_BUTTON(26), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
92 { NULL, DIJOFS_BUTTON(27), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
93 { NULL, DIJOFS_BUTTON(28), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
94 { NULL, DIJOFS_BUTTON(29), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
95 { NULL, DIJOFS_BUTTON(30), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
96 { NULL, DIJOFS_BUTTON(31), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
97 { NULL, DIJOFS_BUTTON(32), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
98 { NULL, DIJOFS_BUTTON(33), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
99 { NULL, DIJOFS_BUTTON(34), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
100 { NULL, DIJOFS_BUTTON(35), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
101 { NULL, DIJOFS_BUTTON(36), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
102 { NULL, DIJOFS_BUTTON(37), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
103 { NULL, DIJOFS_BUTTON(38), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
104 { NULL, DIJOFS_BUTTON(39), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
105 { NULL, DIJOFS_BUTTON(40), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
106 { NULL, DIJOFS_BUTTON(41), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
107 { NULL, DIJOFS_BUTTON(42), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
108 { NULL, DIJOFS_BUTTON(43), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
109 { NULL, DIJOFS_BUTTON(44), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
110 { NULL, DIJOFS_BUTTON(45), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
111 { NULL, DIJOFS_BUTTON(46), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
112 { NULL, DIJOFS_BUTTON(47), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
113 { NULL, DIJOFS_BUTTON(48), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
114 { NULL, DIJOFS_BUTTON(49), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
115 { NULL, DIJOFS_BUTTON(50), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
116 { NULL, DIJOFS_BUTTON(51), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
117 { NULL, DIJOFS_BUTTON(52), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
118 { NULL, DIJOFS_BUTTON(53), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
119 { NULL, DIJOFS_BUTTON(54), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
120 { NULL, DIJOFS_BUTTON(55), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
121 { NULL, DIJOFS_BUTTON(56), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
122 { NULL, DIJOFS_BUTTON(57), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
123 { NULL, DIJOFS_BUTTON(58), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
124 { NULL, DIJOFS_BUTTON(59), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
125 { NULL, DIJOFS_BUTTON(60), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
126 { NULL, DIJOFS_BUTTON(61), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
127 { NULL, DIJOFS_BUTTON(62), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
128 { NULL, DIJOFS_BUTTON(63), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
129 { NULL, DIJOFS_BUTTON(64), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
130 { NULL, DIJOFS_BUTTON(65), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
131 { NULL, DIJOFS_BUTTON(66), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
132 { NULL, DIJOFS_BUTTON(67), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
133 { NULL, DIJOFS_BUTTON(68), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
134 { NULL, DIJOFS_BUTTON(69), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
135 { NULL, DIJOFS_BUTTON(70), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
136 { NULL, DIJOFS_BUTTON(71), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
137 { NULL, DIJOFS_BUTTON(72), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
138 { NULL, DIJOFS_BUTTON(73), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
139 { NULL, DIJOFS_BUTTON(74), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
140 { NULL, DIJOFS_BUTTON(75), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
141 { NULL, DIJOFS_BUTTON(76), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
142 { NULL, DIJOFS_BUTTON(77), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
143 { NULL, DIJOFS_BUTTON(78), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
144 { NULL, DIJOFS_BUTTON(79), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
145 { NULL, DIJOFS_BUTTON(80), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
146 { NULL, DIJOFS_BUTTON(81), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
147 { NULL, DIJOFS_BUTTON(82), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
148 { NULL, DIJOFS_BUTTON(83), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
149 { NULL, DIJOFS_BUTTON(84), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
150 { NULL, DIJOFS_BUTTON(85), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
151 { NULL, DIJOFS_BUTTON(86), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
152 { NULL, DIJOFS_BUTTON(87), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
153 { NULL, DIJOFS_BUTTON(88), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
154 { NULL, DIJOFS_BUTTON(89), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
155 { NULL, DIJOFS_BUTTON(90), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
156 { NULL, DIJOFS_BUTTON(91), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
157 { NULL, DIJOFS_BUTTON(92), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
158 { NULL, DIJOFS_BUTTON(93), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
159 { NULL, DIJOFS_BUTTON(94), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
160 { NULL, DIJOFS_BUTTON(95), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
161 { NULL, DIJOFS_BUTTON(96), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
162 { NULL, DIJOFS_BUTTON(97), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
163 { NULL, DIJOFS_BUTTON(98), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
164 { NULL, DIJOFS_BUTTON(99), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
165 { NULL, DIJOFS_BUTTON(100), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
166 { NULL, DIJOFS_BUTTON(101), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
167 { NULL, DIJOFS_BUTTON(102), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
168 { NULL, DIJOFS_BUTTON(103), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
169 { NULL, DIJOFS_BUTTON(104), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
170 { NULL, DIJOFS_BUTTON(105), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
171 { NULL, DIJOFS_BUTTON(106), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
172 { NULL, DIJOFS_BUTTON(107), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
173 { NULL, DIJOFS_BUTTON(108), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
174 { NULL, DIJOFS_BUTTON(109), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
175 { NULL, DIJOFS_BUTTON(110), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
176 { NULL, DIJOFS_BUTTON(111), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
177 { NULL, DIJOFS_BUTTON(112), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
178 { NULL, DIJOFS_BUTTON(113), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
179 { NULL, DIJOFS_BUTTON(114), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
180 { NULL, DIJOFS_BUTTON(115), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
181 { NULL, DIJOFS_BUTTON(116), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
182 { NULL, DIJOFS_BUTTON(117), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
183 { NULL, DIJOFS_BUTTON(118), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
184 { NULL, DIJOFS_BUTTON(119), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
185 { NULL, DIJOFS_BUTTON(120), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
186 { NULL, DIJOFS_BUTTON(121), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
187 { NULL, DIJOFS_BUTTON(122), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
188 { NULL, DIJOFS_BUTTON(123), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
189 { NULL, DIJOFS_BUTTON(124), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
190 { NULL, DIJOFS_BUTTON(125), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
191 { NULL, DIJOFS_BUTTON(126), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
192 { NULL, DIJOFS_BUTTON(127), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
193 { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lVX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
194 { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lVY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
195 { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lVZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
196 { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lVRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
197 { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lVRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
198 { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lVRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
199 { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
200 { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglVSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
201 { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lAX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
202 { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lAY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
203 { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lAZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
204 { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lARx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
205 { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lARy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
206 { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lARz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
207 { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
208 { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglASlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
209 { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lFX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
210 { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lFY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
211 { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lFZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
212 { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lFRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
213 { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lFRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
214 { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lFRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
215 { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[0]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
216 { &GUID_Slider, FIELD_OFFSET(DIJOYSTATE2, rglFSlider[1]), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0 },
219 const DIDATAFORMAT SDL_c_dfDIJoystick2 = {
220 sizeof(DIDATAFORMAT),
221 sizeof(DIOBJECTDATAFORMAT),
224 SDL_arraysize(dfDIJoystick2),
228 /* Convert a DirectInput return code to a text message */
230 SetDIerror(const char *function, HRESULT code)
232 return SDL_SetError("%s() DirectX error 0x%8.8lx", function, code);
235 #if 0 /* Microsoft recommended implementation, but slower than checking raw devices */
240 static const IID CLSID_WbemLocator = { 0x4590f811, 0x1d3a, 0x11d0,{ 0x89, 0x1f, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24 } };
241 static const IID IID_IWbemLocator = { 0xdc12a687, 0x737f, 0x11cf,{ 0x88, 0x4d, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24 } };
244 WIN_IsXInputDevice(const WCHAR *name, const GUID* pGuidProductFromDirectInput)
246 IWbemLocator* pIWbemLocator = NULL;
247 IEnumWbemClassObject* pEnumDevices = NULL;
248 IWbemClassObject* pDevices[20];
249 IWbemServices* pIWbemServices = NULL;
250 BSTR bstrNamespace = NULL;
251 BSTR bstrDeviceID = NULL;
252 BSTR bstrClassName = NULL;
254 SDL_bool bIsXinputDevice = SDL_FALSE;
259 if (!SDL_XINPUT_Enabled()) {
263 if (SDL_wcsstr(name, L" XINPUT ") != NULL) {
264 /* This is a duplicate interface for a controller that will show up with XInput,
265 e.g. Xbox One Elite Series 2 in Bluetooth mode.
273 hr = CoCreateInstance(&CLSID_WbemLocator,
275 CLSCTX_INPROC_SERVER,
277 (LPVOID*)&pIWbemLocator);
278 if (FAILED(hr) || pIWbemLocator == NULL)
281 bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); if (bstrNamespace == NULL) goto LCleanup;
282 bstrClassName = SysAllocString(L"Win32_PNPEntity"); if (bstrClassName == NULL) goto LCleanup;
283 bstrDeviceID = SysAllocString(L"DeviceID"); if (bstrDeviceID == NULL) goto LCleanup;
286 hr = IWbemLocator_ConnectServer(pIWbemLocator, bstrNamespace, NULL, NULL, 0L,
287 0L, NULL, NULL, &pIWbemServices);
288 if (FAILED(hr) || pIWbemServices == NULL) {
292 // Switch security level to IMPERSONATE.
293 CoSetProxyBlanket((IUnknown *)pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
294 RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
296 hr = IWbemServices_CreateInstanceEnum(pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices);
297 if (FAILED(hr) || pEnumDevices == NULL)
300 // Loop over all devices
303 hr = IEnumWbemClassObject_Next(pEnumDevices, 10000, SDL_arraysize(pDevices), pDevices, &uReturned);
307 if (uReturned == 0) {
311 for (iDevice = 0; iDevice < uReturned; iDevice++) {
312 // For each device, get its device ID
313 hr = IWbemClassObject_Get(pDevices[iDevice], bstrDeviceID, 0L, &var, NULL, NULL);
314 if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) {
315 // Check if the device ID contains "IG_". If it does, then it's an XInput device
316 // This information can not be found from DirectInput
317 if (SDL_wcsstr(var.bstrVal, L"IG_")) {
318 char *bstrVal = WIN_StringToUTF8(var.bstrVal);
320 // If it does, then get the VID/PID from var.bstrVal
321 DWORD dwPid = 0, dwVid = 0, dwVidPid;
322 const char *strVid, *strPid;
323 strVid = SDL_strstr(bstrVal, "VID_");
324 if (strVid && SDL_sscanf(strVid, "VID_%4X", &dwVid) != 1)
326 strPid = SDL_strstr(bstrVal, "PID_");
327 if (strPid && SDL_sscanf(strPid, "PID_%4X", &dwPid) != 1)
332 // Compare the VID/PID to the DInput device
333 dwVidPid = MAKELONG(dwVid, dwPid);
334 if (dwVidPid == pGuidProductFromDirectInput->Data1) {
335 bIsXinputDevice = SDL_TRUE;
340 IWbemClassObject_Release(pDevices[iDevice]);
346 SysFreeString(bstrNamespace);
349 SysFreeString(bstrDeviceID);
352 SysFreeString(bstrClassName);
354 for (iDevice = 0; iDevice < SDL_arraysize(pDevices); iDevice++) {
355 if (pDevices[iDevice]) {
356 IWbemClassObject_Release(pDevices[iDevice]);
360 IEnumWbemClassObject_Release(pEnumDevices);
363 IWbemLocator_Release(pIWbemLocator);
365 if (pIWbemServices) {
366 IWbemServices_Release(pIWbemServices);
369 return bIsXinputDevice;
374 SDL_IsXInputDevice(const WCHAR *name, const GUID* pGuidProductFromDirectInput)
378 if (!SDL_XINPUT_Enabled()) {
382 if (SDL_wcsstr(name, L" XINPUT ") != NULL) {
383 /* This is a duplicate interface for a controller that will show up with XInput,
384 e.g. Xbox One Elite Series 2 in Bluetooth mode.
389 if (SDL_memcmp(&pGuidProductFromDirectInput->Data4[2], "PIDVID", 6) == 0) {
390 Uint16 vendor_id = (Uint16)LOWORD(pGuidProductFromDirectInput->Data1);
391 Uint16 product_id = (Uint16)HIWORD(pGuidProductFromDirectInput->Data1);
392 SDL_GameControllerType type = SDL_GetJoystickGameControllerType("", vendor_id, product_id, -1, 0, 0, 0);
393 if (type == SDL_CONTROLLER_TYPE_XBOX360 ||
394 type == SDL_CONTROLLER_TYPE_XBOXONE ||
395 (vendor_id == 0x28DE && product_id == 0x11FF)) {
400 /* Go through RAWINPUT (WinXP and later) to find HID devices. */
401 /* Cache this if we end up using it. */
402 if (SDL_RawDevList == NULL) {
403 if ((GetRawInputDeviceList(NULL, &SDL_RawDevListCount, sizeof(RAWINPUTDEVICELIST)) == -1) || (!SDL_RawDevListCount)) {
404 return SDL_FALSE; /* oh well. */
407 SDL_RawDevList = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * SDL_RawDevListCount);
408 if (SDL_RawDevList == NULL) {
413 if (GetRawInputDeviceList(SDL_RawDevList, &SDL_RawDevListCount, sizeof(RAWINPUTDEVICELIST)) == -1) {
414 SDL_free(SDL_RawDevList);
415 SDL_RawDevList = NULL;
416 return SDL_FALSE; /* oh well. */
420 for (i = 0; i < SDL_RawDevListCount; i++) {
422 char devName[MAX_PATH];
423 UINT rdiSize = sizeof(rdi);
424 UINT nameSize = SDL_arraysize(devName);
426 rdi.cbSize = sizeof(rdi);
427 if ((SDL_RawDevList[i].dwType == RIM_TYPEHID) &&
428 (GetRawInputDeviceInfoA(SDL_RawDevList[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != ((UINT)-1)) &&
429 (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == ((LONG)pGuidProductFromDirectInput->Data1)) &&
430 (GetRawInputDeviceInfoA(SDL_RawDevList[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != ((UINT)-1)) &&
431 (SDL_strstr(devName, "IG_") != NULL)) {
439 void FreeRumbleEffectData(DIEFFECT *effect)
444 SDL_free(effect->rgdwAxes);
445 SDL_free(effect->rglDirection);
446 SDL_free(effect->lpvTypeSpecificParams);
450 DIEFFECT *CreateRumbleEffectData(Sint16 magnitude)
453 DIPERIODIC *periodic;
455 /* Create the effect */
456 effect = (DIEFFECT *)SDL_calloc(1, sizeof(*effect));
460 effect->dwSize = sizeof(*effect);
461 effect->dwGain = 10000;
462 effect->dwFlags = DIEFF_OBJECTOFFSETS;
463 effect->dwDuration = SDL_MAX_RUMBLE_DURATION_MS * 1000; /* In microseconds. */
464 effect->dwTriggerButton = DIEB_NOTRIGGER;
467 effect->rgdwAxes = (DWORD *)SDL_calloc(effect->cAxes, sizeof(DWORD));
468 if (!effect->rgdwAxes) {
469 FreeRumbleEffectData(effect);
473 effect->rglDirection = (LONG *)SDL_calloc(effect->cAxes, sizeof(LONG));
474 if (!effect->rglDirection) {
475 FreeRumbleEffectData(effect);
478 effect->dwFlags |= DIEFF_CARTESIAN;
480 periodic = (DIPERIODIC *)SDL_calloc(1, sizeof(*periodic));
482 FreeRumbleEffectData(effect);
485 periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
486 periodic->dwPeriod = 1000000;
488 effect->cbTypeSpecificParams = sizeof(*periodic);
489 effect->lpvTypeSpecificParams = periodic;
495 SDL_DINPUT_JoystickInit(void)
500 result = WIN_CoInitialize();
501 if (FAILED(result)) {
502 return SetDIerror("CoInitialize", result);
505 coinitialized = SDL_TRUE;
507 result = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER,
508 &IID_IDirectInput8, (LPVOID *)&dinput);
510 if (FAILED(result)) {
511 return SetDIerror("CoCreateInstance", result);
514 /* Because we used CoCreateInstance, we need to Initialize it, first. */
515 instance = GetModuleHandle(NULL);
516 if (instance == NULL) {
517 IDirectInput8_Release(dinput);
519 return SDL_SetError("GetModuleHandle() failed with error code %lu.", GetLastError());
521 result = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION);
523 if (FAILED(result)) {
524 IDirectInput8_Release(dinput);
526 return SetDIerror("IDirectInput::Initialize", result);
531 /* helper function for direct input, gets called for each connected joystick */
533 EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
535 JoyStick_DeviceData *pNewJoystick;
536 JoyStick_DeviceData *pPrevJoystick = NULL;
537 const DWORD devtype = (pdidInstance->dwDevType & 0xFF);
542 WCHAR hidPath[MAX_PATH];
545 if (devtype == DI8DEVTYPE_SUPPLEMENTAL) {
546 /* Add any supplemental devices that should be ignored here */
547 #define MAKE_TABLE_ENTRY(VID, PID) ((((DWORD)PID)<<16)|VID)
548 static DWORD ignored_devices[] = {
549 MAKE_TABLE_ENTRY(0, 0)
551 #undef MAKE_TABLE_ENTRY
554 for (i = 0; i < SDL_arraysize(ignored_devices); ++i) {
555 if (pdidInstance->guidProduct.Data1 == ignored_devices[i]) {
556 return DIENUM_CONTINUE;
561 if (SDL_IsXInputDevice(pdidInstance->tszProductName, &pdidInstance->guidProduct)) {
562 return DIENUM_CONTINUE; /* ignore XInput devices here, keep going. */
567 LPDIRECTINPUTDEVICE8 device;
568 LPDIRECTINPUTDEVICE8 InputDevice;
569 DIPROPGUIDANDPATH dipdw2;
571 result = IDirectInput8_CreateDevice(dinput, &(pdidInstance->guidInstance), &device, NULL);
572 if (FAILED(result)) {
573 return DIENUM_CONTINUE; /* better luck next time? */
576 /* Now get the IDirectInputDevice8 interface, instead. */
577 result = IDirectInputDevice8_QueryInterface(device, &IID_IDirectInputDevice8, (LPVOID *)&InputDevice);
578 /* We are done with this object. Use the stored one from now on. */
579 IDirectInputDevice8_Release(device);
580 if (FAILED(result)) {
581 return DIENUM_CONTINUE; /* better luck next time? */
583 dipdw2.diph.dwSize = sizeof(dipdw2);
584 dipdw2.diph.dwHeaderSize = sizeof(dipdw2.diph);
585 dipdw2.diph.dwObj = 0; // device property
586 dipdw2.diph.dwHow = DIPH_DEVICE;
588 result = IDirectInputDevice8_GetProperty(InputDevice, DIPROP_GUIDANDPATH, &dipdw2.diph);
589 IDirectInputDevice8_Release(InputDevice);
590 if (FAILED(result)) {
591 return DIENUM_CONTINUE; /* better luck next time? */
594 /* Get device path, compare that instead of GUID, additionally update GUIDs of joysticks with matching paths, in case they're not open yet. */
595 SDL_wcslcpy(hidPath, dipdw2.wszPath, SDL_arraysize(hidPath));
598 pNewJoystick = *(JoyStick_DeviceData **)pContext;
599 while (pNewJoystick) {
600 if (SDL_wcscmp(pNewJoystick->hidPath, hidPath) == 0) {
601 /* if we are replacing the front of the list then update it */
602 if (pNewJoystick == *(JoyStick_DeviceData **)pContext) {
603 *(JoyStick_DeviceData **)pContext = pNewJoystick->pNext;
604 } else if (pPrevJoystick) {
605 pPrevJoystick->pNext = pNewJoystick->pNext;
608 /* Update with new guid/etc, if it has changed */
609 SDL_memcpy(&pNewJoystick->dxdevice, pdidInstance, sizeof(DIDEVICEINSTANCE));
611 pNewJoystick->pNext = SYS_Joystick;
612 SYS_Joystick = pNewJoystick;
614 return DIENUM_CONTINUE; /* already have this joystick loaded, just keep going */
617 pPrevJoystick = pNewJoystick;
618 pNewJoystick = pNewJoystick->pNext;
621 pNewJoystick = (JoyStick_DeviceData *)SDL_malloc(sizeof(JoyStick_DeviceData));
623 return DIENUM_CONTINUE; /* better luck next time? */
626 SDL_zerop(pNewJoystick);
627 SDL_wcslcpy(pNewJoystick->hidPath, hidPath, SDL_arraysize(pNewJoystick->hidPath));
628 SDL_memcpy(&pNewJoystick->dxdevice, pdidInstance, sizeof(DIDEVICEINSTANCE));
629 SDL_memset(pNewJoystick->guid.data, 0, sizeof(pNewJoystick->guid.data));
631 if (SDL_memcmp(&pdidInstance->guidProduct.Data4[2], "PIDVID", 6) == 0) {
632 vendor = (Uint16)LOWORD(pdidInstance->guidProduct.Data1);
633 product = (Uint16)HIWORD(pdidInstance->guidProduct.Data1);
636 name = WIN_StringToUTF8(pdidInstance->tszProductName);
637 pNewJoystick->joystickname = SDL_CreateJoystickName(vendor, product, NULL, name);
640 if (!pNewJoystick->joystickname) {
641 SDL_free(pNewJoystick);
642 return DIENUM_CONTINUE; /* better luck next time? */
645 guid16 = (Uint16 *)pNewJoystick->guid.data;
646 if (SDL_memcmp(&pdidInstance->guidProduct.Data4[2], "PIDVID", 6) == 0) {
647 *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
649 *guid16++ = SDL_SwapLE16(vendor);
651 *guid16++ = SDL_SwapLE16(product);
653 *guid16++ = SDL_SwapLE16(version);
656 *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
658 SDL_strlcpy((char*)guid16, pNewJoystick->joystickname, sizeof(pNewJoystick->guid.data) - 4);
661 if (SDL_ShouldIgnoreJoystick(pNewJoystick->joystickname, pNewJoystick->guid)) {
662 SDL_free(pNewJoystick->joystickname);
663 SDL_free(pNewJoystick);
664 return DIENUM_CONTINUE;
667 #ifdef SDL_JOYSTICK_HIDAPI
668 if (HIDAPI_IsDevicePresent(vendor, product, 0, pNewJoystick->joystickname)) {
669 /* The HIDAPI driver is taking care of this device */
670 SDL_free(pNewJoystick->joystickname);
671 SDL_free(pNewJoystick);
672 return DIENUM_CONTINUE;
676 #ifdef SDL_JOYSTICK_RAWINPUT
677 if (RAWINPUT_IsDevicePresent(vendor, product, 0, pNewJoystick->joystickname)) {
678 /* The RAWINPUT driver is taking care of this device */
679 SDL_free(pNewJoystick);
680 return DIENUM_CONTINUE;
684 WINDOWS_AddJoystickDevice(pNewJoystick);
686 return DIENUM_CONTINUE; /* get next device, please */
690 SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
692 IDirectInput8_EnumDevices(dinput, DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, pContext, DIEDFL_ATTACHEDONLY);
694 if (SDL_RawDevList) {
695 SDL_free(SDL_RawDevList); /* in case we used this in DirectInput detection */
696 SDL_RawDevList = NULL;
698 SDL_RawDevListCount = 0;
707 } EnumJoystickPresentData;
710 EnumJoystickPresentCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
712 EnumJoystickPresentData *data = (EnumJoystickPresentData *)pContext;
717 if (SDL_memcmp(&pdidInstance->guidProduct.Data4[2], "PIDVID", 6) == 0) {
718 vendor = (Uint16)LOWORD(pdidInstance->guidProduct.Data1);
719 product = (Uint16)HIWORD(pdidInstance->guidProduct.Data1);
720 if (data->vendor == vendor && data->product == product && data->version == version) {
721 data->present = SDL_TRUE;
725 return DIENUM_CONTINUE;
729 SDL_DINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version)
731 EnumJoystickPresentData data;
733 if (dinput == NULL) {
737 data.vendor = vendor;
738 data.product = product;
739 data.version = version;
740 data.present = SDL_FALSE;
741 IDirectInput8_EnumDevices(dinput, DI8DEVCLASS_GAMECTRL, EnumJoystickPresentCallback, &data, DIEDFL_ATTACHEDONLY);
747 EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
749 SDL_Joystick *joystick = (SDL_Joystick *)pvRef;
751 input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs];
753 if (dev->dwType & DIDFT_BUTTON) {
755 in->num = joystick->nbuttons;
756 in->ofs = DIJOFS_BUTTON(in->num);
757 joystick->nbuttons++;
758 } else if (dev->dwType & DIDFT_POV) {
760 in->num = joystick->nhats;
761 in->ofs = DIJOFS_POV(in->num);
763 } else if (dev->dwType & DIDFT_AXIS) {
768 in->num = joystick->naxes;
769 if (!SDL_memcmp(&dev->guidType, &GUID_XAxis, sizeof(dev->guidType)))
771 else if (!SDL_memcmp(&dev->guidType, &GUID_YAxis, sizeof(dev->guidType)))
773 else if (!SDL_memcmp(&dev->guidType, &GUID_ZAxis, sizeof(dev->guidType)))
775 else if (!SDL_memcmp(&dev->guidType, &GUID_RxAxis, sizeof(dev->guidType)))
777 else if (!SDL_memcmp(&dev->guidType, &GUID_RyAxis, sizeof(dev->guidType)))
779 else if (!SDL_memcmp(&dev->guidType, &GUID_RzAxis, sizeof(dev->guidType)))
781 else if (!SDL_memcmp(&dev->guidType, &GUID_Slider, sizeof(dev->guidType))) {
782 in->ofs = DIJOFS_SLIDER(joystick->hwdata->NumSliders);
783 ++joystick->hwdata->NumSliders;
785 return DIENUM_CONTINUE; /* not an axis we can grok */
788 diprg.diph.dwSize = sizeof(diprg);
789 diprg.diph.dwHeaderSize = sizeof(diprg.diph);
790 diprg.diph.dwObj = dev->dwType;
791 diprg.diph.dwHow = DIPH_BYID;
792 diprg.lMin = SDL_JOYSTICK_AXIS_MIN;
793 diprg.lMax = SDL_JOYSTICK_AXIS_MAX;
796 IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
797 DIPROP_RANGE, &diprg.diph);
798 if (FAILED(result)) {
799 return DIENUM_CONTINUE; /* don't use this axis */
802 /* Set dead zone to 0. */
803 dilong.diph.dwSize = sizeof(dilong);
804 dilong.diph.dwHeaderSize = sizeof(dilong.diph);
805 dilong.diph.dwObj = dev->dwType;
806 dilong.diph.dwHow = DIPH_BYID;
809 IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
810 DIPROP_DEADZONE, &dilong.diph);
811 if (FAILED(result)) {
812 return DIENUM_CONTINUE; /* don't use this axis */
817 /* not supported at this time */
818 return DIENUM_CONTINUE;
821 joystick->hwdata->NumInputs++;
823 if (joystick->hwdata->NumInputs == MAX_INPUTS) {
824 return DIENUM_STOP; /* too many */
827 return DIENUM_CONTINUE;
830 /* Sort using the data offset into the DInput struct.
831 * This gives a reasonable ordering for the inputs.
834 SortDevFunc(const void *a, const void *b)
836 const input_t *inputA = (const input_t*)a;
837 const input_t *inputB = (const input_t*)b;
839 if (inputA->ofs < inputB->ofs)
841 if (inputA->ofs > inputB->ofs)
846 /* Sort the input objects and recalculate the indices for each input. */
848 SortDevObjects(SDL_Joystick *joystick)
850 input_t *inputs = joystick->hwdata->Inputs;
856 SDL_qsort(inputs, joystick->hwdata->NumInputs, sizeof(input_t), SortDevFunc);
858 for (n = 0; n < joystick->hwdata->NumInputs; n++) {
859 switch (inputs[n].type) {
861 inputs[n].num = nButtons;
866 inputs[n].num = nHats;
871 inputs[n].num = nAxis;
879 SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
882 LPDIRECTINPUTDEVICE8 device;
885 joystick->hwdata->buffered = SDL_TRUE;
886 joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS);
889 dipdw.diph.dwSize = sizeof(DIPROPDWORD);
890 dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
893 IDirectInput8_CreateDevice(dinput,
894 &(joystickdevice->dxdevice.guidInstance), &device, NULL);
895 if (FAILED(result)) {
896 return SetDIerror("IDirectInput::CreateDevice", result);
899 /* Now get the IDirectInputDevice8 interface, instead. */
900 result = IDirectInputDevice8_QueryInterface(device,
901 &IID_IDirectInputDevice8,
902 (LPVOID *)& joystick->
903 hwdata->InputDevice);
904 /* We are done with this object. Use the stored one from now on. */
905 IDirectInputDevice8_Release(device);
907 if (FAILED(result)) {
908 return SetDIerror("IDirectInputDevice8::QueryInterface", result);
911 /* Acquire shared access. Exclusive access is required for forces,
914 IDirectInputDevice8_SetCooperativeLevel(joystick->hwdata->
915 InputDevice, SDL_HelperWindow,
918 if (FAILED(result)) {
919 return SetDIerror("IDirectInputDevice8::SetCooperativeLevel", result);
922 /* Use the extended data structure: DIJOYSTATE2. */
924 IDirectInputDevice8_SetDataFormat(joystick->hwdata->InputDevice,
925 &SDL_c_dfDIJoystick2);
926 if (FAILED(result)) {
927 return SetDIerror("IDirectInputDevice8::SetDataFormat", result);
930 /* Get device capabilities */
932 IDirectInputDevice8_GetCapabilities(joystick->hwdata->InputDevice,
933 &joystick->hwdata->Capabilities);
934 if (FAILED(result)) {
935 return SetDIerror("IDirectInputDevice8::GetCapabilities", result);
939 if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
940 result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
941 if (FAILED(result)) {
942 return SetDIerror("IDirectInputDevice8::Acquire", result);
945 /* reset all actuators. */
947 IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->
951 /* Not necessarily supported, ignore if not supported.
952 if (FAILED(result)) {
953 return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand", result);
957 result = IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
959 if (FAILED(result)) {
960 return SetDIerror("IDirectInputDevice8::Unacquire", result);
963 /* Turn on auto-centering for a ForceFeedback device (until told
965 dipdw.diph.dwObj = 0;
966 dipdw.diph.dwHow = DIPH_DEVICE;
967 dipdw.dwData = DIPROPAUTOCENTER_ON;
970 IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
971 DIPROP_AUTOCENTER, &dipdw.diph);
973 /* Not necessarily supported, ignore if not supported.
974 if (FAILED(result)) {
975 return SetDIerror("IDirectInputDevice8::SetProperty", result);
980 /* What buttons and axes does it have? */
981 IDirectInputDevice8_EnumObjects(joystick->hwdata->InputDevice,
982 EnumDevObjectsCallback, joystick,
983 DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);
985 /* Reorder the input objects. Some devices do not report the X axis as
986 * the first axis, for example. */
987 SortDevObjects(joystick);
989 dipdw.diph.dwObj = 0;
990 dipdw.diph.dwHow = DIPH_DEVICE;
991 dipdw.dwData = INPUT_QSIZE;
993 /* Set the buffer size */
995 IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
996 DIPROP_BUFFERSIZE, &dipdw.diph);
998 if (result == DI_POLLEDDEVICE) {
999 /* This device doesn't support buffering, so we're forced
1000 * to use less reliable polling. */
1001 joystick->hwdata->buffered = SDL_FALSE;
1002 } else if (FAILED(result)) {
1003 return SetDIerror("IDirectInputDevice8::SetProperty", result);
1009 SDL_DINPUT_JoystickInitRumble(SDL_Joystick * joystick, Sint16 magnitude)
1013 /* Reset and then enable actuators */
1014 result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_RESET);
1015 if (result == DIERR_INPUTLOST || result == DIERR_NOTEXCLUSIVEACQUIRED) {
1016 result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1017 if (SUCCEEDED(result)) {
1018 result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_RESET);
1021 if (FAILED(result)) {
1022 return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_RESET)", result);
1025 result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_SETACTUATORSON);
1026 if (FAILED(result)) {
1027 return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_SETACTUATORSON)", result);
1030 /* Create the effect */
1031 joystick->hwdata->ffeffect = CreateRumbleEffectData(magnitude);
1032 if (!joystick->hwdata->ffeffect) {
1033 return SDL_OutOfMemory();
1036 result = IDirectInputDevice8_CreateEffect(joystick->hwdata->InputDevice, &GUID_Sine,
1037 joystick->hwdata->ffeffect, &joystick->hwdata->ffeffect_ref, NULL);
1038 if (FAILED(result)) {
1039 return SetDIerror("IDirectInputDevice8::CreateEffect", result);
1045 SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1049 /* Scale and average the two rumble strengths */
1050 Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
1052 if (!(joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK)) {
1053 return SDL_Unsupported();
1056 if (joystick->hwdata->ff_initialized) {
1057 DIPERIODIC *periodic = ((DIPERIODIC *)joystick->hwdata->ffeffect->lpvTypeSpecificParams);
1058 periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
1060 result = IDirectInputEffect_SetParameters(joystick->hwdata->ffeffect_ref, joystick->hwdata->ffeffect, (DIEP_DURATION | DIEP_TYPESPECIFICPARAMS));
1061 if (result == DIERR_INPUTLOST) {
1062 result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1063 if (SUCCEEDED(result)) {
1064 result = IDirectInputEffect_SetParameters(joystick->hwdata->ffeffect_ref, joystick->hwdata->ffeffect, (DIEP_DURATION | DIEP_TYPESPECIFICPARAMS));
1067 if (FAILED(result)) {
1068 return SetDIerror("IDirectInputDevice8::SetParameters", result);
1071 if (SDL_DINPUT_JoystickInitRumble(joystick, magnitude) < 0) {
1074 joystick->hwdata->ff_initialized = SDL_TRUE;
1077 result = IDirectInputEffect_Start(joystick->hwdata->ffeffect_ref, 1, 0);
1078 if (result == DIERR_INPUTLOST || result == DIERR_NOTEXCLUSIVEACQUIRED) {
1079 result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1080 if (SUCCEEDED(result)) {
1081 result = IDirectInputEffect_Start(joystick->hwdata->ffeffect_ref, 1, 0);
1084 if (FAILED(result)) {
1085 return SetDIerror("IDirectInputDevice8::Start", result);
1091 TranslatePOV(DWORD value)
1093 const int HAT_VALS[] = {
1095 SDL_HAT_UP | SDL_HAT_RIGHT,
1097 SDL_HAT_DOWN | SDL_HAT_RIGHT,
1099 SDL_HAT_DOWN | SDL_HAT_LEFT,
1101 SDL_HAT_UP | SDL_HAT_LEFT
1104 if (LOWORD(value) == 0xFFFF)
1105 return SDL_HAT_CENTERED;
1107 /* Round the value up: */
1113 return SDL_HAT_CENTERED; /* shouldn't happen */
1115 return HAT_VALS[value];
1119 UpdateDINPUTJoystickState_Buffered(SDL_Joystick * joystick)
1124 DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
1126 numevents = INPUT_QSIZE;
1128 IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
1129 sizeof(DIDEVICEOBJECTDATA), evtbuf,
1131 if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
1132 IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1134 IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
1135 sizeof(DIDEVICEOBJECTDATA),
1136 evtbuf, &numevents, 0);
1139 /* Handle the events or punt */
1140 if (FAILED(result)) {
1144 for (i = 0; i < (int)numevents; ++i) {
1147 for (j = 0; j < joystick->hwdata->NumInputs; ++j) {
1148 const input_t *in = &joystick->hwdata->Inputs[j];
1150 if (evtbuf[i].dwOfs != in->ofs)
1155 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)evtbuf[i].dwData);
1158 SDL_PrivateJoystickButton(joystick, in->num,
1159 (Uint8)(evtbuf[i].dwData ? SDL_PRESSED : SDL_RELEASED));
1163 Uint8 pos = TranslatePOV(evtbuf[i].dwData);
1164 SDL_PrivateJoystickHat(joystick, in->num, pos);
1172 /* Function to update the state of a joystick - called as a device poll.
1173 * This function shouldn't update the joystick structure directly,
1174 * but instead should call SDL_PrivateJoystick*() to deliver events
1175 * and update joystick device state.
1178 UpdateDINPUTJoystickState_Polled(SDL_Joystick * joystick)
1185 IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
1186 sizeof(DIJOYSTATE2), &state);
1187 if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
1188 IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1190 IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
1191 sizeof(DIJOYSTATE2), &state);
1194 if (result != DI_OK) {
1198 /* Set each known axis, button and POV. */
1199 for (i = 0; i < joystick->hwdata->NumInputs; ++i) {
1200 const input_t *in = &joystick->hwdata->Inputs[i];
1206 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lX);
1209 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lY);
1212 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lZ);
1215 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lRx);
1218 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lRy);
1221 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lRz);
1223 case DIJOFS_SLIDER(0):
1224 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.rglSlider[0]);
1226 case DIJOFS_SLIDER(1):
1227 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.rglSlider[1]);
1233 SDL_PrivateJoystickButton(joystick, in->num,
1234 (Uint8)(state.rgbButtons[in->ofs - DIJOFS_BUTTON0] ? SDL_PRESSED : SDL_RELEASED));
1238 Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs - DIJOFS_POV(0)]);
1239 SDL_PrivateJoystickHat(joystick, in->num, pos);
1247 SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick)
1251 result = IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);
1252 if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
1253 IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1254 IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);
1257 if (joystick->hwdata->buffered) {
1258 UpdateDINPUTJoystickState_Buffered(joystick);
1260 UpdateDINPUTJoystickState_Polled(joystick);
1265 SDL_DINPUT_JoystickClose(SDL_Joystick * joystick)
1267 if (joystick->hwdata->ffeffect_ref) {
1268 IDirectInputEffect_Unload(joystick->hwdata->ffeffect_ref);
1269 joystick->hwdata->ffeffect_ref = NULL;
1271 if (joystick->hwdata->ffeffect) {
1272 FreeRumbleEffectData(joystick->hwdata->ffeffect);
1273 joystick->hwdata->ffeffect = NULL;
1275 IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
1276 IDirectInputDevice8_Release(joystick->hwdata->InputDevice);
1277 joystick->hwdata->ff_initialized = SDL_FALSE;
1281 SDL_DINPUT_JoystickQuit(void)
1283 if (dinput != NULL) {
1284 IDirectInput8_Release(dinput);
1288 if (coinitialized) {
1289 WIN_CoUninitialize();
1290 coinitialized = SDL_FALSE;
1294 #else /* !SDL_JOYSTICK_DINPUT */
1296 typedef struct JoyStick_DeviceData JoyStick_DeviceData;
1299 SDL_DINPUT_JoystickInit(void)
1305 SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
1310 SDL_DINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version)
1316 SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
1318 return SDL_Unsupported();
1322 SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1324 return SDL_Unsupported();
1328 SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick)
1333 SDL_DINPUT_JoystickClose(SDL_Joystick * joystick)
1338 SDL_DINPUT_JoystickQuit(void)
1342 #endif /* SDL_JOYSTICK_DINPUT */
1344 /* vi: set ts=4 sw=4 expandtab: */