Imported Upstream version 2.0.14
[platform/upstream/SDL.git] / src / joystick / windows / SDL_dinputjoystick.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22
23 #include "../SDL_sysjoystick.h"
24
25 #if SDL_JOYSTICK_DINPUT
26
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"
32
33 #ifndef DIDFT_OPTIONAL
34 #define DIDFT_OPTIONAL      0x80000000
35 #endif
36
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 */
39
40 #define CONVERT_MAGNITUDE(x)    (((x)*10000) / 0x7FFF)
41
42 /* external variables referenced. */
43 extern HWND SDL_HelperWindow;
44
45 /* local variables */
46 static SDL_bool coinitialized = SDL_FALSE;
47 static LPDIRECTINPUT8 dinput = NULL;
48 static PRAWINPUTDEVICELIST SDL_RawDevList = NULL;
49 static UINT SDL_RawDevListCount = 0;
50
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 },
217 };
218
219 const DIDATAFORMAT SDL_c_dfDIJoystick2 = {
220     sizeof(DIDATAFORMAT),
221     sizeof(DIOBJECTDATAFORMAT),
222     DIDF_ABSAXIS,
223     sizeof(DIJOYSTATE2),
224     SDL_arraysize(dfDIJoystick2),
225     dfDIJoystick2
226 };
227
228 /* Convert a DirectInput return code to a text message */
229 static int
230 SetDIerror(const char *function, HRESULT code)
231 {
232     return SDL_SetError("%s() DirectX error 0x%8.8lx", function, code);
233 }
234
235 #if 0 /* Microsoft recommended implementation, but slower than checking raw devices */
236 #define COBJMACROS
237 #include <wbemidl.h>
238 #include <oleauto.h>
239
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 } };
242
243 static SDL_bool
244 WIN_IsXInputDevice(const WCHAR *name, const GUID* pGuidProductFromDirectInput)
245 {
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;
253     DWORD                   uReturned = 0;
254     SDL_bool                bIsXinputDevice = SDL_FALSE;
255     UINT                    iDevice = 0;
256     VARIANT                 var;
257     HRESULT                 hr;
258
259     if (!SDL_XINPUT_Enabled()) {
260         return SDL_FALSE;
261     }
262
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.
266          */
267         return SDL_TRUE;
268     }
269
270     SDL_zeroa(pDevices);
271
272     // Create WMI
273     hr = CoCreateInstance(&CLSID_WbemLocator,
274         NULL,
275         CLSCTX_INPROC_SERVER,
276         &IID_IWbemLocator,
277         (LPVOID*)&pIWbemLocator);
278     if (FAILED(hr) || pIWbemLocator == NULL)
279         goto LCleanup;
280
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;
284
285     // Connect to WMI 
286     hr = IWbemLocator_ConnectServer(pIWbemLocator, bstrNamespace, NULL, NULL, 0L,
287         0L, NULL, NULL, &pIWbemServices);
288     if (FAILED(hr) || pIWbemServices == NULL) {
289         goto LCleanup;
290     }
291
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);
295
296     hr = IWbemServices_CreateInstanceEnum(pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices);
297     if (FAILED(hr) || pEnumDevices == NULL)
298         goto LCleanup;
299
300     // Loop over all devices
301     for (;;) {
302         // Get 20 at a time
303         hr = IEnumWbemClassObject_Next(pEnumDevices, 10000, SDL_arraysize(pDevices), pDevices, &uReturned);
304         if (FAILED(hr)) {
305             goto LCleanup;
306         }
307         if (uReturned == 0) {
308             break;
309         }
310
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);
319
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)
325                         dwVid = 0;
326                     strPid = SDL_strstr(bstrVal, "PID_");
327                     if (strPid && SDL_sscanf(strPid, "PID_%4X", &dwPid) != 1)
328                         dwPid = 0;
329
330                     SDL_free(bstrVal);
331
332                     // Compare the VID/PID to the DInput device
333                     dwVidPid = MAKELONG(dwVid, dwPid);
334                     if (dwVidPid == pGuidProductFromDirectInput->Data1) {
335                         bIsXinputDevice = SDL_TRUE;
336                         goto LCleanup;
337                     }
338                 }
339             }
340             IWbemClassObject_Release(pDevices[iDevice]);
341         }
342     }
343
344 LCleanup:
345     if (bstrNamespace) {
346         SysFreeString(bstrNamespace);
347     }
348     if (bstrDeviceID) {
349         SysFreeString(bstrDeviceID);
350     }
351     if (bstrClassName) {
352         SysFreeString(bstrClassName);
353     }
354     for (iDevice = 0; iDevice < SDL_arraysize(pDevices); iDevice++) {
355         if (pDevices[iDevice]) {
356             IWbemClassObject_Release(pDevices[iDevice]);
357         }
358     }
359     if (pEnumDevices) {
360         IEnumWbemClassObject_Release(pEnumDevices);
361     }
362     if (pIWbemLocator) {
363         IWbemLocator_Release(pIWbemLocator);
364     }
365     if (pIWbemServices) {
366         IWbemServices_Release(pIWbemServices);
367     }
368
369     return bIsXinputDevice;
370 }
371 #endif /* 0 */
372
373 static SDL_bool
374 SDL_IsXInputDevice(const WCHAR *name, const GUID* pGuidProductFromDirectInput)
375 {
376     UINT i;
377
378     if (!SDL_XINPUT_Enabled()) {
379         return SDL_FALSE;
380     }
381
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.
385          */
386         return SDL_TRUE;
387     }
388
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)) {
396             return SDL_TRUE;
397         }
398     }
399
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. */
405         }
406
407         SDL_RawDevList = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * SDL_RawDevListCount);
408         if (SDL_RawDevList == NULL) {
409             SDL_OutOfMemory();
410             return SDL_FALSE;
411         }
412
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. */
417         }
418     }
419
420     for (i = 0; i < SDL_RawDevListCount; i++) {
421         RID_DEVICE_INFO rdi;
422         char devName[MAX_PATH];
423         UINT rdiSize = sizeof(rdi);
424         UINT nameSize = SDL_arraysize(devName);
425
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)) {
432             return SDL_TRUE;
433         }
434     }
435
436     return SDL_FALSE;
437 }
438
439 void FreeRumbleEffectData(DIEFFECT *effect)
440 {
441     if (!effect) {
442         return;
443     }
444     SDL_free(effect->rgdwAxes);
445     SDL_free(effect->rglDirection);
446     SDL_free(effect->lpvTypeSpecificParams);
447     SDL_free(effect);
448 }
449
450 DIEFFECT *CreateRumbleEffectData(Sint16 magnitude)
451 {
452     DIEFFECT *effect;
453     DIPERIODIC *periodic;
454
455     /* Create the effect */
456     effect = (DIEFFECT *)SDL_calloc(1, sizeof(*effect));
457     if (!effect) {
458         return NULL;
459     }
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;
465
466     effect->cAxes = 2;
467     effect->rgdwAxes = (DWORD *)SDL_calloc(effect->cAxes, sizeof(DWORD));
468     if (!effect->rgdwAxes) {
469         FreeRumbleEffectData(effect);
470         return NULL;
471     }
472
473     effect->rglDirection = (LONG *)SDL_calloc(effect->cAxes, sizeof(LONG));
474     if (!effect->rglDirection) {
475         FreeRumbleEffectData(effect);
476         return NULL;
477     }
478     effect->dwFlags |= DIEFF_CARTESIAN;
479
480     periodic = (DIPERIODIC *)SDL_calloc(1, sizeof(*periodic));
481     if (!periodic) {
482         FreeRumbleEffectData(effect);
483         return NULL;
484     }
485     periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
486     periodic->dwPeriod = 1000000;
487
488     effect->cbTypeSpecificParams = sizeof(*periodic);
489     effect->lpvTypeSpecificParams = periodic;
490
491     return effect;
492 }
493
494 int
495 SDL_DINPUT_JoystickInit(void)
496 {
497     HRESULT result;
498     HINSTANCE instance;
499
500     result = WIN_CoInitialize();
501     if (FAILED(result)) {
502         return SetDIerror("CoInitialize", result);
503     }
504
505     coinitialized = SDL_TRUE;
506
507     result = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER,
508         &IID_IDirectInput8, (LPVOID *)&dinput);
509
510     if (FAILED(result)) {
511         return SetDIerror("CoCreateInstance", result);
512     }
513
514     /* Because we used CoCreateInstance, we need to Initialize it, first. */
515     instance = GetModuleHandle(NULL);
516     if (instance == NULL) {
517         IDirectInput8_Release(dinput);
518         dinput = NULL;
519         return SDL_SetError("GetModuleHandle() failed with error code %lu.", GetLastError());
520     }
521     result = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION);
522
523     if (FAILED(result)) {
524         IDirectInput8_Release(dinput);
525         dinput = NULL;
526         return SetDIerror("IDirectInput::Initialize", result);
527     }
528     return 0;
529 }
530
531 /* helper function for direct input, gets called for each connected joystick */
532 static BOOL CALLBACK
533 EnumJoysticksCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
534 {
535     JoyStick_DeviceData *pNewJoystick;
536     JoyStick_DeviceData *pPrevJoystick = NULL;
537     const DWORD devtype = (pdidInstance->dwDevType & 0xFF);
538     Uint16 *guid16;
539     Uint16 vendor = 0;
540     Uint16 product = 0;
541     Uint16 version = 0;
542     WCHAR hidPath[MAX_PATH];
543     char *name;
544
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)
550         };
551 #undef MAKE_TABLE_ENTRY
552         unsigned int i;
553
554         for (i = 0; i < SDL_arraysize(ignored_devices); ++i) {
555             if (pdidInstance->guidProduct.Data1 == ignored_devices[i]) {
556                 return DIENUM_CONTINUE;
557             }
558         }
559     }
560
561     if (SDL_IsXInputDevice(pdidInstance->tszProductName, &pdidInstance->guidProduct)) {
562         return DIENUM_CONTINUE;  /* ignore XInput devices here, keep going. */
563     }
564
565     {
566         HRESULT result;
567         LPDIRECTINPUTDEVICE8 device;
568         LPDIRECTINPUTDEVICE8 InputDevice;
569         DIPROPGUIDANDPATH dipdw2;
570
571         result = IDirectInput8_CreateDevice(dinput, &(pdidInstance->guidInstance), &device, NULL);
572         if (FAILED(result)) {
573             return DIENUM_CONTINUE; /* better luck next time? */
574         }
575
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? */
582         }
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;
587
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? */
592         }
593
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));
596     }
597
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;
606             }
607
608             /* Update with new guid/etc, if it has changed */
609             SDL_memcpy(&pNewJoystick->dxdevice, pdidInstance, sizeof(DIDEVICEINSTANCE));
610
611             pNewJoystick->pNext = SYS_Joystick;
612             SYS_Joystick = pNewJoystick;
613
614             return DIENUM_CONTINUE; /* already have this joystick loaded, just keep going */
615         }
616
617         pPrevJoystick = pNewJoystick;
618         pNewJoystick = pNewJoystick->pNext;
619     }
620
621     pNewJoystick = (JoyStick_DeviceData *)SDL_malloc(sizeof(JoyStick_DeviceData));
622     if (!pNewJoystick) {
623         return DIENUM_CONTINUE; /* better luck next time? */
624     }
625
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));
630
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);
634     }
635
636     name = WIN_StringToUTF8(pdidInstance->tszProductName);
637     pNewJoystick->joystickname = SDL_CreateJoystickName(vendor, product, NULL, name);
638     SDL_free(name);
639
640     if (!pNewJoystick->joystickname) {
641         SDL_free(pNewJoystick);
642         return DIENUM_CONTINUE; /* better luck next time? */
643     }
644
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);
648         *guid16++ = 0;
649         *guid16++ = SDL_SwapLE16(vendor);
650         *guid16++ = 0;
651         *guid16++ = SDL_SwapLE16(product);
652         *guid16++ = 0;
653         *guid16++ = SDL_SwapLE16(version);
654         *guid16++ = 0;
655     } else {
656         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
657         *guid16++ = 0;
658         SDL_strlcpy((char*)guid16, pNewJoystick->joystickname, sizeof(pNewJoystick->guid.data) - 4);
659     }
660
661     if (SDL_ShouldIgnoreJoystick(pNewJoystick->joystickname, pNewJoystick->guid)) {
662         SDL_free(pNewJoystick->joystickname);
663         SDL_free(pNewJoystick);
664         return DIENUM_CONTINUE;
665     }
666
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;
673     }
674 #endif
675
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;
681     }
682 #endif
683
684     WINDOWS_AddJoystickDevice(pNewJoystick);
685
686     return DIENUM_CONTINUE; /* get next device, please */
687 }
688
689 void
690 SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
691 {
692     IDirectInput8_EnumDevices(dinput, DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, pContext, DIEDFL_ATTACHEDONLY);
693
694     if (SDL_RawDevList) {
695         SDL_free(SDL_RawDevList);  /* in case we used this in DirectInput detection */
696         SDL_RawDevList = NULL;
697     }
698     SDL_RawDevListCount = 0;
699 }
700
701 typedef struct
702 {
703     Uint16 vendor;
704     Uint16 product;
705     Uint16 version;
706     SDL_bool present;
707 } EnumJoystickPresentData;
708
709 static BOOL CALLBACK
710 EnumJoystickPresentCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
711 {
712     EnumJoystickPresentData *data = (EnumJoystickPresentData *)pContext;
713     Uint16 vendor = 0;
714     Uint16 product = 0;
715     Uint16 version = 0;
716
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;
722             return DIENUM_STOP;
723         }
724     }
725     return DIENUM_CONTINUE;
726 }
727
728 SDL_bool
729 SDL_DINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version)
730 {
731     EnumJoystickPresentData data;
732
733     if (dinput == NULL) {
734         return SDL_FALSE;
735     }
736
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);
742
743     return data.present;
744 }
745
746 static BOOL CALLBACK
747 EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
748 {
749     SDL_Joystick *joystick = (SDL_Joystick *)pvRef;
750     HRESULT result;
751     input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs];
752
753     if (dev->dwType & DIDFT_BUTTON) {
754         in->type = BUTTON;
755         in->num = joystick->nbuttons;
756         in->ofs = DIJOFS_BUTTON(in->num);
757         joystick->nbuttons++;
758     } else if (dev->dwType & DIDFT_POV) {
759         in->type = HAT;
760         in->num = joystick->nhats;
761         in->ofs = DIJOFS_POV(in->num);
762         joystick->nhats++;
763     } else if (dev->dwType & DIDFT_AXIS) {
764         DIPROPRANGE diprg;
765         DIPROPDWORD dilong;
766
767         in->type = AXIS;
768         in->num = joystick->naxes;
769         if (!SDL_memcmp(&dev->guidType, &GUID_XAxis, sizeof(dev->guidType)))
770             in->ofs = DIJOFS_X;
771         else if (!SDL_memcmp(&dev->guidType, &GUID_YAxis, sizeof(dev->guidType)))
772             in->ofs = DIJOFS_Y;
773         else if (!SDL_memcmp(&dev->guidType, &GUID_ZAxis, sizeof(dev->guidType)))
774             in->ofs = DIJOFS_Z;
775         else if (!SDL_memcmp(&dev->guidType, &GUID_RxAxis, sizeof(dev->guidType)))
776             in->ofs = DIJOFS_RX;
777         else if (!SDL_memcmp(&dev->guidType, &GUID_RyAxis, sizeof(dev->guidType)))
778             in->ofs = DIJOFS_RY;
779         else if (!SDL_memcmp(&dev->guidType, &GUID_RzAxis, sizeof(dev->guidType)))
780             in->ofs = DIJOFS_RZ;
781         else if (!SDL_memcmp(&dev->guidType, &GUID_Slider, sizeof(dev->guidType))) {
782             in->ofs = DIJOFS_SLIDER(joystick->hwdata->NumSliders);
783             ++joystick->hwdata->NumSliders;
784         } else {
785             return DIENUM_CONTINUE; /* not an axis we can grok */
786         }
787
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;
794
795         result =
796             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
797             DIPROP_RANGE, &diprg.diph);
798         if (FAILED(result)) {
799             return DIENUM_CONTINUE;     /* don't use this axis */
800         }
801
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;
807         dilong.dwData = 0;
808         result =
809             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
810             DIPROP_DEADZONE, &dilong.diph);
811         if (FAILED(result)) {
812             return DIENUM_CONTINUE;     /* don't use this axis */
813         }
814
815         joystick->naxes++;
816     } else {
817         /* not supported at this time */
818         return DIENUM_CONTINUE;
819     }
820
821     joystick->hwdata->NumInputs++;
822
823     if (joystick->hwdata->NumInputs == MAX_INPUTS) {
824         return DIENUM_STOP;     /* too many */
825     }
826
827     return DIENUM_CONTINUE;
828 }
829
830 /* Sort using the data offset into the DInput struct.
831  * This gives a reasonable ordering for the inputs.
832  */
833 static int
834 SortDevFunc(const void *a, const void *b)
835 {
836     const input_t *inputA = (const input_t*)a;
837     const input_t *inputB = (const input_t*)b;
838
839     if (inputA->ofs < inputB->ofs)
840         return -1;
841     if (inputA->ofs > inputB->ofs)
842         return 1;
843     return 0;
844 }
845
846 /* Sort the input objects and recalculate the indices for each input. */
847 static void
848 SortDevObjects(SDL_Joystick *joystick)
849 {
850     input_t *inputs = joystick->hwdata->Inputs;
851     int nButtons = 0;
852     int nHats = 0;
853     int nAxis = 0;
854     int n;
855
856     SDL_qsort(inputs, joystick->hwdata->NumInputs, sizeof(input_t), SortDevFunc);
857
858     for (n = 0; n < joystick->hwdata->NumInputs; n++) {
859         switch (inputs[n].type) {
860         case BUTTON:
861             inputs[n].num = nButtons;
862             nButtons++;
863             break;
864
865         case HAT:
866             inputs[n].num = nHats;
867             nHats++;
868             break;
869
870         case AXIS:
871             inputs[n].num = nAxis;
872             nAxis++;
873             break;
874         }
875     }
876 }
877
878 int
879 SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
880 {
881     HRESULT result;
882     LPDIRECTINPUTDEVICE8 device;
883     DIPROPDWORD dipdw;
884
885     joystick->hwdata->buffered = SDL_TRUE;
886     joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS);
887
888     SDL_zero(dipdw);
889     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
890     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
891
892     result =
893         IDirectInput8_CreateDevice(dinput,
894         &(joystickdevice->dxdevice.guidInstance), &device, NULL);
895     if (FAILED(result)) {
896         return SetDIerror("IDirectInput::CreateDevice", result);
897     }
898
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);
906
907     if (FAILED(result)) {
908         return SetDIerror("IDirectInputDevice8::QueryInterface", result);
909     }
910
911     /* Acquire shared access. Exclusive access is required for forces,
912     * though. */
913     result =
914         IDirectInputDevice8_SetCooperativeLevel(joystick->hwdata->
915         InputDevice, SDL_HelperWindow,
916         DISCL_EXCLUSIVE |
917         DISCL_BACKGROUND);
918     if (FAILED(result)) {
919         return SetDIerror("IDirectInputDevice8::SetCooperativeLevel", result);
920     }
921
922     /* Use the extended data structure: DIJOYSTATE2. */
923     result =
924         IDirectInputDevice8_SetDataFormat(joystick->hwdata->InputDevice,
925         &SDL_c_dfDIJoystick2);
926     if (FAILED(result)) {
927         return SetDIerror("IDirectInputDevice8::SetDataFormat", result);
928     }
929
930     /* Get device capabilities */
931     result =
932         IDirectInputDevice8_GetCapabilities(joystick->hwdata->InputDevice,
933         &joystick->hwdata->Capabilities);
934     if (FAILED(result)) {
935         return SetDIerror("IDirectInputDevice8::GetCapabilities", result);
936     }
937
938     /* Force capable? */
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);
943         }
944
945         /* reset all actuators. */
946         result =
947             IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->
948             InputDevice,
949             DISFFC_RESET);
950
951         /* Not necessarily supported, ignore if not supported.
952         if (FAILED(result)) {
953         return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand", result);
954         }
955         */
956
957         result = IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
958
959         if (FAILED(result)) {
960             return SetDIerror("IDirectInputDevice8::Unacquire", result);
961         }
962
963         /* Turn on auto-centering for a ForceFeedback device (until told
964         * otherwise). */
965         dipdw.diph.dwObj = 0;
966         dipdw.diph.dwHow = DIPH_DEVICE;
967         dipdw.dwData = DIPROPAUTOCENTER_ON;
968
969         result =
970             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
971             DIPROP_AUTOCENTER, &dipdw.diph);
972
973         /* Not necessarily supported, ignore if not supported.
974         if (FAILED(result)) {
975         return SetDIerror("IDirectInputDevice8::SetProperty", result);
976         }
977         */
978     }
979
980     /* What buttons and axes does it have? */
981     IDirectInputDevice8_EnumObjects(joystick->hwdata->InputDevice,
982         EnumDevObjectsCallback, joystick,
983         DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);
984
985     /* Reorder the input objects. Some devices do not report the X axis as
986     * the first axis, for example. */
987     SortDevObjects(joystick);
988
989     dipdw.diph.dwObj = 0;
990     dipdw.diph.dwHow = DIPH_DEVICE;
991     dipdw.dwData = INPUT_QSIZE;
992
993     /* Set the buffer size */
994     result =
995         IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
996         DIPROP_BUFFERSIZE, &dipdw.diph);
997
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);
1004     }
1005     return 0;
1006 }
1007
1008 static int
1009 SDL_DINPUT_JoystickInitRumble(SDL_Joystick * joystick, Sint16 magnitude)
1010 {
1011     HRESULT result;
1012
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);
1019         }
1020     }
1021     if (FAILED(result)) {
1022         return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_RESET)", result);
1023     }
1024
1025     result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_SETACTUATORSON);
1026     if (FAILED(result)) {
1027         return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_SETACTUATORSON)", result);
1028     }
1029
1030     /* Create the effect */
1031     joystick->hwdata->ffeffect = CreateRumbleEffectData(magnitude);
1032     if (!joystick->hwdata->ffeffect) {
1033         return SDL_OutOfMemory();
1034     }
1035
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);
1040     }
1041     return 0;
1042 }
1043
1044 int
1045 SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1046 {
1047     HRESULT result;
1048
1049     /* Scale and average the two rumble strengths */
1050     Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
1051
1052     if (!(joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK)) {
1053         return SDL_Unsupported();
1054     }
1055
1056     if (joystick->hwdata->ff_initialized) {
1057         DIPERIODIC *periodic = ((DIPERIODIC *)joystick->hwdata->ffeffect->lpvTypeSpecificParams);
1058         periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
1059
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));
1065             }
1066         }
1067         if (FAILED(result)) {
1068             return SetDIerror("IDirectInputDevice8::SetParameters", result);
1069         }
1070     } else {
1071         if (SDL_DINPUT_JoystickInitRumble(joystick, magnitude) < 0) {
1072             return -1;
1073         }
1074         joystick->hwdata->ff_initialized = SDL_TRUE;
1075     }
1076
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);
1082         }
1083     }
1084     if (FAILED(result)) {
1085         return SetDIerror("IDirectInputDevice8::Start", result);
1086     }
1087     return 0;
1088 }
1089
1090 static Uint8
1091 TranslatePOV(DWORD value)
1092 {
1093     const int HAT_VALS[] = {
1094         SDL_HAT_UP,
1095         SDL_HAT_UP | SDL_HAT_RIGHT,
1096         SDL_HAT_RIGHT,
1097         SDL_HAT_DOWN | SDL_HAT_RIGHT,
1098         SDL_HAT_DOWN,
1099         SDL_HAT_DOWN | SDL_HAT_LEFT,
1100         SDL_HAT_LEFT,
1101         SDL_HAT_UP | SDL_HAT_LEFT
1102     };
1103
1104     if (LOWORD(value) == 0xFFFF)
1105         return SDL_HAT_CENTERED;
1106
1107     /* Round the value up: */
1108     value += 4500 / 2;
1109     value %= 36000;
1110     value /= 4500;
1111
1112     if (value >= 8)
1113         return SDL_HAT_CENTERED;        /* shouldn't happen */
1114
1115     return HAT_VALS[value];
1116 }
1117
1118 static void
1119 UpdateDINPUTJoystickState_Buffered(SDL_Joystick * joystick)
1120 {
1121     int i;
1122     HRESULT result;
1123     DWORD numevents;
1124     DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
1125
1126     numevents = INPUT_QSIZE;
1127     result =
1128         IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
1129         sizeof(DIDEVICEOBJECTDATA), evtbuf,
1130         &numevents, 0);
1131     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
1132         IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1133         result =
1134             IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
1135             sizeof(DIDEVICEOBJECTDATA),
1136             evtbuf, &numevents, 0);
1137     }
1138
1139     /* Handle the events or punt */
1140     if (FAILED(result)) {
1141         return;
1142     }
1143
1144     for (i = 0; i < (int)numevents; ++i) {
1145         int j;
1146
1147         for (j = 0; j < joystick->hwdata->NumInputs; ++j) {
1148             const input_t *in = &joystick->hwdata->Inputs[j];
1149
1150             if (evtbuf[i].dwOfs != in->ofs)
1151                 continue;
1152
1153             switch (in->type) {
1154             case AXIS:
1155                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)evtbuf[i].dwData);
1156                 break;
1157             case BUTTON:
1158                 SDL_PrivateJoystickButton(joystick, in->num,
1159                     (Uint8)(evtbuf[i].dwData ? SDL_PRESSED : SDL_RELEASED));
1160                 break;
1161             case HAT:
1162                 {
1163                     Uint8 pos = TranslatePOV(evtbuf[i].dwData);
1164                     SDL_PrivateJoystickHat(joystick, in->num, pos);
1165                 }
1166                 break;
1167             }
1168         }
1169     }
1170 }
1171
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.
1176  */
1177 static void
1178 UpdateDINPUTJoystickState_Polled(SDL_Joystick * joystick)
1179 {
1180     DIJOYSTATE2 state;
1181     HRESULT result;
1182     int i;
1183
1184     result =
1185         IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
1186         sizeof(DIJOYSTATE2), &state);
1187     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
1188         IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1189         result =
1190             IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
1191             sizeof(DIJOYSTATE2), &state);
1192     }
1193
1194     if (result != DI_OK) {
1195         return;
1196     }
1197
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];
1201
1202         switch (in->type) {
1203         case AXIS:
1204             switch (in->ofs) {
1205             case DIJOFS_X:
1206                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lX);
1207                 break;
1208             case DIJOFS_Y:
1209                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lY);
1210                 break;
1211             case DIJOFS_Z:
1212                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lZ);
1213                 break;
1214             case DIJOFS_RX:
1215                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lRx);
1216                 break;
1217             case DIJOFS_RY:
1218                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lRy);
1219                 break;
1220             case DIJOFS_RZ:
1221                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lRz);
1222                 break;
1223             case DIJOFS_SLIDER(0):
1224                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.rglSlider[0]);
1225                 break;
1226             case DIJOFS_SLIDER(1):
1227                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.rglSlider[1]);
1228                 break;
1229             }
1230             break;
1231
1232         case BUTTON:
1233             SDL_PrivateJoystickButton(joystick, in->num,
1234                 (Uint8)(state.rgbButtons[in->ofs - DIJOFS_BUTTON0] ? SDL_PRESSED : SDL_RELEASED));
1235             break;
1236         case HAT:
1237         {
1238             Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs - DIJOFS_POV(0)]);
1239             SDL_PrivateJoystickHat(joystick, in->num, pos);
1240             break;
1241         }
1242         }
1243     }
1244 }
1245
1246 void
1247 SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick)
1248 {
1249     HRESULT result;
1250
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);
1255     }
1256
1257     if (joystick->hwdata->buffered) {
1258         UpdateDINPUTJoystickState_Buffered(joystick);
1259     } else {
1260         UpdateDINPUTJoystickState_Polled(joystick);
1261     }
1262 }
1263
1264 void
1265 SDL_DINPUT_JoystickClose(SDL_Joystick * joystick)
1266 {
1267     if (joystick->hwdata->ffeffect_ref) {
1268         IDirectInputEffect_Unload(joystick->hwdata->ffeffect_ref);
1269         joystick->hwdata->ffeffect_ref = NULL;
1270     }
1271     if (joystick->hwdata->ffeffect) {
1272         FreeRumbleEffectData(joystick->hwdata->ffeffect);
1273         joystick->hwdata->ffeffect = NULL;
1274     }
1275     IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
1276     IDirectInputDevice8_Release(joystick->hwdata->InputDevice);
1277     joystick->hwdata->ff_initialized = SDL_FALSE;
1278 }
1279
1280 void
1281 SDL_DINPUT_JoystickQuit(void)
1282 {
1283     if (dinput != NULL) {
1284         IDirectInput8_Release(dinput);
1285         dinput = NULL;
1286     }
1287
1288     if (coinitialized) {
1289         WIN_CoUninitialize();
1290         coinitialized = SDL_FALSE;
1291     }
1292 }
1293
1294 #else /* !SDL_JOYSTICK_DINPUT */
1295
1296 typedef struct JoyStick_DeviceData JoyStick_DeviceData;
1297
1298 int
1299 SDL_DINPUT_JoystickInit(void)
1300 {
1301     return 0;
1302 }
1303
1304 void
1305 SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
1306 {
1307 }
1308
1309 SDL_bool
1310 SDL_DINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version)
1311 {
1312     return SDL_FALSE;
1313 }
1314
1315 int
1316 SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
1317 {
1318     return SDL_Unsupported();
1319 }
1320
1321 int
1322 SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1323 {
1324     return SDL_Unsupported();
1325 }
1326
1327 void
1328 SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick)
1329 {
1330 }
1331
1332 void
1333 SDL_DINPUT_JoystickClose(SDL_Joystick * joystick)
1334 {
1335 }
1336
1337 void
1338 SDL_DINPUT_JoystickQuit(void)
1339 {
1340 }
1341
1342 #endif /* SDL_JOYSTICK_DINPUT */
1343
1344 /* vi: set ts=4 sw=4 expandtab: */