1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2021 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 128      /* Buffer up to 128 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 
49 /* Taken from Wine - Thanks! */
50 static DIOBJECTDATAFORMAT dfDIJoystick2[] = {
51     { &GUID_XAxis, DIJOFS_X, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },
52     { &GUID_YAxis, DIJOFS_Y, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },
53     { &GUID_ZAxis, DIJOFS_Z, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },
54     { &GUID_RxAxis, DIJOFS_RX, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },
55     { &GUID_RyAxis, DIJOFS_RY, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },
56     { &GUID_RzAxis, DIJOFS_RZ, DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },
57     { &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },
58     { &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTPOSITION },
59     { &GUID_POV, DIJOFS_POV(0), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
60     { &GUID_POV, DIJOFS_POV(1), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
61     { &GUID_POV, DIJOFS_POV(2), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
62     { &GUID_POV, DIJOFS_POV(3), DIDFT_OPTIONAL | DIDFT_POV | DIDFT_ANYINSTANCE, 0 },
63     { NULL, DIJOFS_BUTTON(0), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
64     { NULL, DIJOFS_BUTTON(1), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
65     { NULL, DIJOFS_BUTTON(2), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
66     { NULL, DIJOFS_BUTTON(3), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
67     { NULL, DIJOFS_BUTTON(4), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
68     { NULL, DIJOFS_BUTTON(5), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
69     { NULL, DIJOFS_BUTTON(6), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
70     { NULL, DIJOFS_BUTTON(7), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
71     { NULL, DIJOFS_BUTTON(8), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
72     { NULL, DIJOFS_BUTTON(9), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
73     { NULL, DIJOFS_BUTTON(10), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
74     { NULL, DIJOFS_BUTTON(11), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
75     { NULL, DIJOFS_BUTTON(12), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
76     { NULL, DIJOFS_BUTTON(13), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
77     { NULL, DIJOFS_BUTTON(14), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
78     { NULL, DIJOFS_BUTTON(15), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
79     { NULL, DIJOFS_BUTTON(16), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
80     { NULL, DIJOFS_BUTTON(17), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
81     { NULL, DIJOFS_BUTTON(18), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
82     { NULL, DIJOFS_BUTTON(19), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
83     { NULL, DIJOFS_BUTTON(20), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
84     { NULL, DIJOFS_BUTTON(21), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
85     { NULL, DIJOFS_BUTTON(22), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
86     { NULL, DIJOFS_BUTTON(23), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
87     { NULL, DIJOFS_BUTTON(24), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
88     { NULL, DIJOFS_BUTTON(25), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
89     { NULL, DIJOFS_BUTTON(26), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
90     { NULL, DIJOFS_BUTTON(27), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
91     { NULL, DIJOFS_BUTTON(28), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
92     { NULL, DIJOFS_BUTTON(29), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
93     { NULL, DIJOFS_BUTTON(30), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
94     { NULL, DIJOFS_BUTTON(31), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
95     { NULL, DIJOFS_BUTTON(32), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
96     { NULL, DIJOFS_BUTTON(33), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
97     { NULL, DIJOFS_BUTTON(34), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
98     { NULL, DIJOFS_BUTTON(35), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
99     { NULL, DIJOFS_BUTTON(36), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
100     { NULL, DIJOFS_BUTTON(37), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
101     { NULL, DIJOFS_BUTTON(38), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
102     { NULL, DIJOFS_BUTTON(39), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
103     { NULL, DIJOFS_BUTTON(40), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
104     { NULL, DIJOFS_BUTTON(41), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
105     { NULL, DIJOFS_BUTTON(42), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
106     { NULL, DIJOFS_BUTTON(43), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
107     { NULL, DIJOFS_BUTTON(44), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
108     { NULL, DIJOFS_BUTTON(45), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
109     { NULL, DIJOFS_BUTTON(46), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
110     { NULL, DIJOFS_BUTTON(47), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
111     { NULL, DIJOFS_BUTTON(48), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
112     { NULL, DIJOFS_BUTTON(49), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
113     { NULL, DIJOFS_BUTTON(50), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
114     { NULL, DIJOFS_BUTTON(51), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
115     { NULL, DIJOFS_BUTTON(52), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
116     { NULL, DIJOFS_BUTTON(53), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
117     { NULL, DIJOFS_BUTTON(54), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
118     { NULL, DIJOFS_BUTTON(55), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
119     { NULL, DIJOFS_BUTTON(56), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
120     { NULL, DIJOFS_BUTTON(57), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
121     { NULL, DIJOFS_BUTTON(58), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
122     { NULL, DIJOFS_BUTTON(59), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
123     { NULL, DIJOFS_BUTTON(60), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
124     { NULL, DIJOFS_BUTTON(61), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
125     { NULL, DIJOFS_BUTTON(62), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
126     { NULL, DIJOFS_BUTTON(63), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
127     { NULL, DIJOFS_BUTTON(64), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
128     { NULL, DIJOFS_BUTTON(65), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
129     { NULL, DIJOFS_BUTTON(66), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
130     { NULL, DIJOFS_BUTTON(67), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
131     { NULL, DIJOFS_BUTTON(68), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
132     { NULL, DIJOFS_BUTTON(69), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
133     { NULL, DIJOFS_BUTTON(70), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
134     { NULL, DIJOFS_BUTTON(71), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
135     { NULL, DIJOFS_BUTTON(72), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
136     { NULL, DIJOFS_BUTTON(73), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
137     { NULL, DIJOFS_BUTTON(74), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
138     { NULL, DIJOFS_BUTTON(75), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
139     { NULL, DIJOFS_BUTTON(76), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
140     { NULL, DIJOFS_BUTTON(77), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
141     { NULL, DIJOFS_BUTTON(78), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
142     { NULL, DIJOFS_BUTTON(79), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
143     { NULL, DIJOFS_BUTTON(80), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
144     { NULL, DIJOFS_BUTTON(81), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
145     { NULL, DIJOFS_BUTTON(82), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
146     { NULL, DIJOFS_BUTTON(83), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
147     { NULL, DIJOFS_BUTTON(84), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
148     { NULL, DIJOFS_BUTTON(85), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
149     { NULL, DIJOFS_BUTTON(86), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
150     { NULL, DIJOFS_BUTTON(87), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
151     { NULL, DIJOFS_BUTTON(88), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
152     { NULL, DIJOFS_BUTTON(89), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
153     { NULL, DIJOFS_BUTTON(90), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
154     { NULL, DIJOFS_BUTTON(91), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
155     { NULL, DIJOFS_BUTTON(92), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
156     { NULL, DIJOFS_BUTTON(93), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
157     { NULL, DIJOFS_BUTTON(94), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
158     { NULL, DIJOFS_BUTTON(95), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
159     { NULL, DIJOFS_BUTTON(96), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
160     { NULL, DIJOFS_BUTTON(97), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
161     { NULL, DIJOFS_BUTTON(98), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
162     { NULL, DIJOFS_BUTTON(99), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
163     { NULL, DIJOFS_BUTTON(100), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
164     { NULL, DIJOFS_BUTTON(101), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
165     { NULL, DIJOFS_BUTTON(102), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
166     { NULL, DIJOFS_BUTTON(103), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
167     { NULL, DIJOFS_BUTTON(104), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
168     { NULL, DIJOFS_BUTTON(105), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
169     { NULL, DIJOFS_BUTTON(106), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
170     { NULL, DIJOFS_BUTTON(107), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
171     { NULL, DIJOFS_BUTTON(108), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
172     { NULL, DIJOFS_BUTTON(109), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
173     { NULL, DIJOFS_BUTTON(110), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
174     { NULL, DIJOFS_BUTTON(111), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
175     { NULL, DIJOFS_BUTTON(112), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
176     { NULL, DIJOFS_BUTTON(113), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
177     { NULL, DIJOFS_BUTTON(114), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
178     { NULL, DIJOFS_BUTTON(115), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
179     { NULL, DIJOFS_BUTTON(116), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
180     { NULL, DIJOFS_BUTTON(117), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
181     { NULL, DIJOFS_BUTTON(118), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
182     { NULL, DIJOFS_BUTTON(119), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
183     { NULL, DIJOFS_BUTTON(120), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
184     { NULL, DIJOFS_BUTTON(121), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
185     { NULL, DIJOFS_BUTTON(122), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
186     { NULL, DIJOFS_BUTTON(123), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
187     { NULL, DIJOFS_BUTTON(124), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
188     { NULL, DIJOFS_BUTTON(125), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
189     { NULL, DIJOFS_BUTTON(126), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
190     { NULL, DIJOFS_BUTTON(127), DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0 },
191     { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lVX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },
192     { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lVY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },
193     { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lVZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },
194     { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lVRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },
195     { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lVRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },
196     { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lVRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },
197     /* note: dwOfs value matches Windows */
198     { &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },
199     { &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTVELOCITY },
200     { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lAX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },
201     { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lAY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },
202     { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lAZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },
203     { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lARx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },
204     { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lARy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },
205     { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lARz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },
206     /* note: dwOfs value matches Windows */
207     { &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },
208     { &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTACCEL },
209     { &GUID_XAxis, FIELD_OFFSET(DIJOYSTATE2, lFX), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },
210     { &GUID_YAxis, FIELD_OFFSET(DIJOYSTATE2, lFY), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },
211     { &GUID_ZAxis, FIELD_OFFSET(DIJOYSTATE2, lFZ), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },
212     { &GUID_RxAxis, FIELD_OFFSET(DIJOYSTATE2, lFRx), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },
213     { &GUID_RyAxis, FIELD_OFFSET(DIJOYSTATE2, lFRy), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },
214     { &GUID_RzAxis, FIELD_OFFSET(DIJOYSTATE2, lFRz), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },
215     /* note: dwOfs value matches Windows */
216     { &GUID_Slider, DIJOFS_SLIDER(0), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },
217     { &GUID_Slider, DIJOFS_SLIDER(1), DIDFT_OPTIONAL | DIDFT_AXIS | DIDFT_ANYINSTANCE, DIDOI_ASPECTFORCE },
218 };
219 
220 const DIDATAFORMAT SDL_c_dfDIJoystick2 = {
221     sizeof(DIDATAFORMAT),
222     sizeof(DIOBJECTDATAFORMAT),
223     DIDF_ABSAXIS,
224     sizeof(DIJOYSTATE2),
225     SDL_arraysize(dfDIJoystick2),
226     dfDIJoystick2
227 };
228 
229 /* Convert a DirectInput return code to a text message */
230 static int
SetDIerror(const char * function,HRESULT code)231 SetDIerror(const char *function, HRESULT code)
232 {
233     return SDL_SetError("%s() DirectX error 0x%8.8lx", function, code);
234 }
235 
236 static SDL_bool
SDL_IsXInputDevice(Uint16 vendor_id,Uint16 product_id,const char * hidPath)237 SDL_IsXInputDevice(Uint16 vendor_id, Uint16 product_id, const char* hidPath)
238 {
239     SDL_GameControllerType type;
240 
241     /* XInput and RawInput backends will pick up XInput-compatible devices */
242     if (!SDL_XINPUT_Enabled()
243 #ifdef SDL_JOYSTICK_RAWINPUT
244         && !RAWINPUT_IsEnabled()
245 #endif
246         ) {
247         return SDL_FALSE;
248     }
249 
250     /* If device path contains "IG_" then its an XInput device */
251     /* See: https://docs.microsoft.com/windows/win32/xinput/xinput-and-directinput */
252     if (SDL_strstr(hidPath, "IG_") != NULL) {
253         return SDL_TRUE;
254     }
255 
256     type = SDL_GetJoystickGameControllerType("", vendor_id, product_id, -1, 0, 0, 0);
257     if (type == SDL_CONTROLLER_TYPE_XBOX360 ||
258         type == SDL_CONTROLLER_TYPE_XBOXONE ||
259         (vendor_id == USB_VENDOR_VALVE && product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD)) {
260         return SDL_TRUE;
261     }
262 
263     return SDL_FALSE;
264 }
265 
266 static SDL_bool
QueryDeviceName(LPDIRECTINPUTDEVICE8 device,char ** device_name)267 QueryDeviceName(LPDIRECTINPUTDEVICE8 device, char** device_name)
268 {
269     DIPROPSTRING dipstr;
270 
271     if (!device || !device_name) {
272         return SDL_FALSE;
273     }
274 
275     dipstr.diph.dwSize = sizeof(dipstr);
276     dipstr.diph.dwHeaderSize = sizeof(dipstr.diph);
277     dipstr.diph.dwObj = 0;
278     dipstr.diph.dwHow = DIPH_DEVICE;
279 
280     if (FAILED(IDirectInputDevice8_GetProperty(device, DIPROP_PRODUCTNAME, &dipstr.diph))) {
281         return SDL_FALSE;
282     }
283 
284     *device_name = WIN_StringToUTF8(dipstr.wsz);
285 
286     return SDL_TRUE;
287 }
288 
289 static SDL_bool
QueryDevicePath(LPDIRECTINPUTDEVICE8 device,char ** device_path)290 QueryDevicePath(LPDIRECTINPUTDEVICE8 device, char** device_path)
291 {
292     DIPROPGUIDANDPATH dippath;
293 
294     if (!device || !device_path) {
295         return SDL_FALSE;
296     }
297 
298     dippath.diph.dwSize = sizeof(dippath);
299     dippath.diph.dwHeaderSize = sizeof(dippath.diph);
300     dippath.diph.dwObj = 0;
301     dippath.diph.dwHow = DIPH_DEVICE;
302 
303     if (FAILED(IDirectInputDevice8_GetProperty(device, DIPROP_GUIDANDPATH, &dippath.diph))) {
304         return SDL_FALSE;
305     }
306 
307     *device_path = WIN_StringToUTF8W(dippath.wszPath);
308 
309     /* Normalize path to upper case. */
310     SDL_strupr(*device_path);
311 
312     return SDL_TRUE;
313 }
314 
315 static SDL_bool
QueryDeviceInfo(LPDIRECTINPUTDEVICE8 device,Uint16 * vendor_id,Uint16 * product_id)316 QueryDeviceInfo(LPDIRECTINPUTDEVICE8 device, Uint16* vendor_id, Uint16* product_id)
317 {
318     DIPROPDWORD dipdw;
319 
320     if (!device || !vendor_id || !product_id) {
321         return SDL_FALSE;
322     }
323 
324     dipdw.diph.dwSize = sizeof(dipdw);
325     dipdw.diph.dwHeaderSize = sizeof(dipdw.diph);
326     dipdw.diph.dwObj = 0;
327     dipdw.diph.dwHow = DIPH_DEVICE;
328 
329     if (FAILED(IDirectInputDevice8_GetProperty(device, DIPROP_VIDPID, &dipdw.diph))) {
330         return SDL_FALSE;
331     }
332 
333     *vendor_id = LOWORD(dipdw.dwData);
334     *product_id = HIWORD(dipdw.dwData);
335 
336     return SDL_TRUE;
337 }
338 
FreeRumbleEffectData(DIEFFECT * effect)339 void FreeRumbleEffectData(DIEFFECT *effect)
340 {
341     if (!effect) {
342         return;
343     }
344     SDL_free(effect->rgdwAxes);
345     SDL_free(effect->rglDirection);
346     SDL_free(effect->lpvTypeSpecificParams);
347     SDL_free(effect);
348 }
349 
CreateRumbleEffectData(Sint16 magnitude)350 DIEFFECT *CreateRumbleEffectData(Sint16 magnitude)
351 {
352     DIEFFECT *effect;
353     DIPERIODIC *periodic;
354 
355     /* Create the effect */
356     effect = (DIEFFECT *)SDL_calloc(1, sizeof(*effect));
357     if (!effect) {
358         return NULL;
359     }
360     effect->dwSize = sizeof(*effect);
361     effect->dwGain = 10000;
362     effect->dwFlags = DIEFF_OBJECTOFFSETS;
363     effect->dwDuration = SDL_MAX_RUMBLE_DURATION_MS * 1000; /* In microseconds. */
364     effect->dwTriggerButton = DIEB_NOTRIGGER;
365 
366     effect->cAxes = 2;
367     effect->rgdwAxes = (DWORD *)SDL_calloc(effect->cAxes, sizeof(DWORD));
368     if (!effect->rgdwAxes) {
369         FreeRumbleEffectData(effect);
370         return NULL;
371     }
372 
373     effect->rglDirection = (LONG *)SDL_calloc(effect->cAxes, sizeof(LONG));
374     if (!effect->rglDirection) {
375         FreeRumbleEffectData(effect);
376         return NULL;
377     }
378     effect->dwFlags |= DIEFF_CARTESIAN;
379 
380     periodic = (DIPERIODIC *)SDL_calloc(1, sizeof(*periodic));
381     if (!periodic) {
382         FreeRumbleEffectData(effect);
383         return NULL;
384     }
385     periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
386     periodic->dwPeriod = 1000000;
387 
388     effect->cbTypeSpecificParams = sizeof(*periodic);
389     effect->lpvTypeSpecificParams = periodic;
390 
391     return effect;
392 }
393 
394 int
SDL_DINPUT_JoystickInit(void)395 SDL_DINPUT_JoystickInit(void)
396 {
397     HRESULT result;
398     HINSTANCE instance;
399 
400     result = WIN_CoInitialize();
401     if (FAILED(result)) {
402         return SetDIerror("CoInitialize", result);
403     }
404 
405     coinitialized = SDL_TRUE;
406 
407     result = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER,
408         &IID_IDirectInput8, (LPVOID *)&dinput);
409 
410     if (FAILED(result)) {
411         return SetDIerror("CoCreateInstance", result);
412     }
413 
414     /* Because we used CoCreateInstance, we need to Initialize it, first. */
415     instance = GetModuleHandle(NULL);
416     if (instance == NULL) {
417         IDirectInput8_Release(dinput);
418         dinput = NULL;
419         return SDL_SetError("GetModuleHandle() failed with error code %lu.", GetLastError());
420     }
421     result = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION);
422 
423     if (FAILED(result)) {
424         IDirectInput8_Release(dinput);
425         dinput = NULL;
426         return SetDIerror("IDirectInput::Initialize", result);
427     }
428     return 0;
429 }
430 
431 /* helper function for direct input, gets called for each connected joystick */
432 static BOOL CALLBACK
EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInstance,LPVOID pContext)433 EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInstance, LPVOID pContext)
434 {
435 #define CHECK(expression) { if(!(expression)) goto err; }
436     JoyStick_DeviceData *pNewJoystick = NULL;
437     JoyStick_DeviceData *pPrevJoystick = NULL;
438     Uint16 *guid16;
439     Uint16 vendor = 0;
440     Uint16 product = 0;
441     Uint16 version = 0;
442     char *hidPath = NULL;
443     char *name = NULL;
444     LPDIRECTINPUTDEVICE8 device = NULL;
445 
446     /* We are only supporting HID devices. */
447     CHECK((pDeviceInstance->dwDevType & DIDEVTYPE_HID) != 0);
448 
449     CHECK(SUCCEEDED(IDirectInput8_CreateDevice(dinput, &pDeviceInstance->guidInstance, &device, NULL)));
450     CHECK(QueryDeviceName(device, &name));
451     CHECK(QueryDevicePath(device, &hidPath));
452     CHECK(QueryDeviceInfo(device, &vendor, &product));
453 
454     CHECK(!SDL_IsXInputDevice(vendor, product, hidPath));
455 
456     pNewJoystick = *(JoyStick_DeviceData**)pContext;
457     while (pNewJoystick) {
458         /* update GUIDs of joysticks with matching paths, in case they're not open yet */
459         if (SDL_strcmp(pNewJoystick->hidPath, hidPath) == 0) {
460             /* if we are replacing the front of the list then update it */
461             if (pNewJoystick == *(JoyStick_DeviceData**)pContext) {
462                 *(JoyStick_DeviceData**)pContext = pNewJoystick->pNext;
463             }
464             else if (pPrevJoystick) {
465                 pPrevJoystick->pNext = pNewJoystick->pNext;
466             }
467 
468             /* Update with new guid/etc, if it has changed */
469             SDL_memcpy(&pNewJoystick->dxdevice, pDeviceInstance, sizeof(DIDEVICEINSTANCE));
470 
471             pNewJoystick->pNext = SYS_Joystick;
472             SYS_Joystick = pNewJoystick;
473 
474             pNewJoystick = NULL;
475             CHECK(FALSE);
476         }
477 
478         pPrevJoystick = pNewJoystick;
479         pNewJoystick = pNewJoystick->pNext;
480     }
481 
482     pNewJoystick = (JoyStick_DeviceData *)SDL_malloc(sizeof(JoyStick_DeviceData));
483     CHECK(pNewJoystick);
484 
485     SDL_zerop(pNewJoystick);
486     SDL_strlcpy(pNewJoystick->hidPath, hidPath, SDL_arraysize(pNewJoystick->hidPath));
487     SDL_memcpy(&pNewJoystick->dxdevice, pDeviceInstance, sizeof(DIDEVICEINSTANCE));
488     SDL_memset(pNewJoystick->guid.data, 0, sizeof(pNewJoystick->guid.data));
489 
490     pNewJoystick->joystickname = SDL_CreateJoystickName(vendor, product, NULL, name);
491     CHECK(pNewJoystick->joystickname);
492 
493     guid16 = (Uint16 *)pNewJoystick->guid.data;
494     if (vendor && product) {
495         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
496         *guid16++ = 0;
497         *guid16++ = SDL_SwapLE16(vendor);
498         *guid16++ = 0;
499         *guid16++ = SDL_SwapLE16(product);
500         *guid16++ = 0;
501         *guid16++ = SDL_SwapLE16(version);
502         *guid16++ = 0;
503     } else {
504         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
505         *guid16++ = 0;
506         SDL_strlcpy((char*)guid16, pNewJoystick->joystickname, sizeof(pNewJoystick->guid.data) - 4);
507     }
508 
509     CHECK(!SDL_ShouldIgnoreJoystick(pNewJoystick->joystickname, pNewJoystick->guid));
510 
511 #ifdef SDL_JOYSTICK_HIDAPI
512     CHECK(!HIDAPI_IsDevicePresent(vendor, product, version, pNewJoystick->joystickname));
513 #endif
514 
515 #ifdef SDL_JOYSTICK_RAWINPUT
516     CHECK(!RAWINPUT_IsDevicePresent(vendor, product, version, pNewJoystick->joystickname));
517 #endif
518 
519     WINDOWS_AddJoystickDevice(pNewJoystick);
520     pNewJoystick = NULL;
521 
522 err:
523     if (pNewJoystick) {
524         SDL_free(pNewJoystick->joystickname);
525         SDL_free(pNewJoystick);
526     }
527 
528     SDL_free(hidPath);
529     SDL_free(name);
530 
531     if (device) {
532         IDirectInputDevice8_Release(device);
533     }
534 
535     return DIENUM_CONTINUE; /* get next device, please */
536 #undef CHECK
537 }
538 
539 void
SDL_DINPUT_JoystickDetect(JoyStick_DeviceData ** pContext)540 SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
541 {
542     IDirectInput8_EnumDevices(dinput, DI8DEVCLASS_GAMECTRL, EnumJoystickDetectCallback, pContext, DIEDFL_ATTACHEDONLY);
543 }
544 
545 /* helper function for direct input, gets called for each connected joystick */
546 typedef struct
547 {
548     Uint16 vendor;
549     Uint16 product;
550     SDL_bool present;
551 } Joystick_PresentData;
552 
553 static BOOL CALLBACK
EnumJoystickPresentCallback(LPCDIDEVICEINSTANCE pDeviceInstance,LPVOID pContext)554 EnumJoystickPresentCallback(LPCDIDEVICEINSTANCE pDeviceInstance, LPVOID pContext)
555 {
556 #define CHECK(expression) { if(!(expression)) goto err; }
557     Joystick_PresentData *pData = (Joystick_PresentData *)pContext;
558     Uint16 vendor = 0;
559     Uint16 product = 0;
560     LPDIRECTINPUTDEVICE8 device = NULL;
561     BOOL result = DIENUM_CONTINUE;
562 
563     /* We are only supporting HID devices. */
564     CHECK((pDeviceInstance->dwDevType & DIDEVTYPE_HID) != 0);
565 
566     CHECK(SUCCEEDED(IDirectInput8_CreateDevice(dinput, &pDeviceInstance->guidInstance, &device, NULL)));
567     CHECK(QueryDeviceInfo(device, &vendor, &product));
568 
569     if (vendor == pData->vendor && product == pData->product) {
570         pData->present = SDL_TRUE;
571         result = DIENUM_STOP; /* found it */
572     }
573 
574 err:
575     if (device) {
576         IDirectInputDevice8_Release(device);
577     }
578 
579     return result;
580 #undef CHECK
581 }
582 
583 SDL_bool
SDL_DINPUT_JoystickPresent(Uint16 vendor_id,Uint16 product_id,Uint16 version_number)584 SDL_DINPUT_JoystickPresent(Uint16 vendor_id, Uint16 product_id, Uint16 version_number)
585 {
586     Joystick_PresentData data;
587 
588     data.vendor = vendor_id;
589     data.product = product_id;
590     data.present = SDL_FALSE;
591     IDirectInput8_EnumDevices(dinput, DI8DEVCLASS_GAMECTRL, EnumJoystickPresentCallback, &data, DIEDFL_ATTACHEDONLY);
592     return data.present;
593 }
594 
595 static BOOL CALLBACK
EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE pDeviceObject,LPVOID pContext)596 EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE pDeviceObject, LPVOID pContext)
597 {
598     SDL_Joystick *joystick = (SDL_Joystick *)pContext;
599     HRESULT result;
600     input_t *in = &joystick->hwdata->Inputs[joystick->hwdata->NumInputs];
601 
602     if (pDeviceObject->dwType & DIDFT_BUTTON) {
603         in->type = BUTTON;
604         in->num = joystick->nbuttons;
605         in->ofs = DIJOFS_BUTTON(in->num);
606         joystick->nbuttons++;
607     } else if (pDeviceObject->dwType & DIDFT_POV) {
608         in->type = HAT;
609         in->num = joystick->nhats;
610         in->ofs = DIJOFS_POV(in->num);
611         joystick->nhats++;
612     } else if (pDeviceObject->dwType & DIDFT_AXIS) {
613         DIPROPRANGE diprg;
614         DIPROPDWORD dilong;
615 
616         in->type = AXIS;
617         in->num = joystick->naxes;
618         if (!SDL_memcmp(&pDeviceObject->guidType, &GUID_XAxis, sizeof(pDeviceObject->guidType)))
619             in->ofs = DIJOFS_X;
620         else if (!SDL_memcmp(&pDeviceObject->guidType, &GUID_YAxis, sizeof(pDeviceObject->guidType)))
621             in->ofs = DIJOFS_Y;
622         else if (!SDL_memcmp(&pDeviceObject->guidType, &GUID_ZAxis, sizeof(pDeviceObject->guidType)))
623             in->ofs = DIJOFS_Z;
624         else if (!SDL_memcmp(&pDeviceObject->guidType, &GUID_RxAxis, sizeof(pDeviceObject->guidType)))
625             in->ofs = DIJOFS_RX;
626         else if (!SDL_memcmp(&pDeviceObject->guidType, &GUID_RyAxis, sizeof(pDeviceObject->guidType)))
627             in->ofs = DIJOFS_RY;
628         else if (!SDL_memcmp(&pDeviceObject->guidType, &GUID_RzAxis, sizeof(pDeviceObject->guidType)))
629             in->ofs = DIJOFS_RZ;
630         else if (!SDL_memcmp(&pDeviceObject->guidType, &GUID_Slider, sizeof(pDeviceObject->guidType))) {
631             in->ofs = DIJOFS_SLIDER(joystick->hwdata->NumSliders);
632             ++joystick->hwdata->NumSliders;
633         } else {
634             return DIENUM_CONTINUE; /* not an axis we can grok */
635         }
636 
637         diprg.diph.dwSize = sizeof(diprg);
638         diprg.diph.dwHeaderSize = sizeof(diprg.diph);
639         diprg.diph.dwObj = pDeviceObject->dwType;
640         diprg.diph.dwHow = DIPH_BYID;
641         diprg.lMin = SDL_JOYSTICK_AXIS_MIN;
642         diprg.lMax = SDL_JOYSTICK_AXIS_MAX;
643 
644         result =
645             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
646             DIPROP_RANGE, &diprg.diph);
647         if (FAILED(result)) {
648             return DIENUM_CONTINUE;     /* don't use this axis */
649         }
650 
651         /* Set dead zone to 0. */
652         dilong.diph.dwSize = sizeof(dilong);
653         dilong.diph.dwHeaderSize = sizeof(dilong.diph);
654         dilong.diph.dwObj = pDeviceObject->dwType;
655         dilong.diph.dwHow = DIPH_BYID;
656         dilong.dwData = 0;
657         result =
658             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
659             DIPROP_DEADZONE, &dilong.diph);
660         if (FAILED(result)) {
661             return DIENUM_CONTINUE;     /* don't use this axis */
662         }
663 
664         joystick->naxes++;
665     } else {
666         /* not supported at this time */
667         return DIENUM_CONTINUE;
668     }
669 
670     joystick->hwdata->NumInputs++;
671 
672     if (joystick->hwdata->NumInputs == MAX_INPUTS) {
673         return DIENUM_STOP;     /* too many */
674     }
675 
676     return DIENUM_CONTINUE;
677 }
678 
679 /* Sort using the data offset into the DInput struct.
680  * This gives a reasonable ordering for the inputs.
681  */
682 static int
SortDevFunc(const void * a,const void * b)683 SortDevFunc(const void *a, const void *b)
684 {
685     const input_t *inputA = (const input_t*)a;
686     const input_t *inputB = (const input_t*)b;
687 
688     if (inputA->ofs < inputB->ofs)
689         return -1;
690     if (inputA->ofs > inputB->ofs)
691         return 1;
692     return 0;
693 }
694 
695 /* Sort the input objects and recalculate the indices for each input. */
696 static void
SortDevObjects(SDL_Joystick * joystick)697 SortDevObjects(SDL_Joystick *joystick)
698 {
699     input_t *inputs = joystick->hwdata->Inputs;
700     int nButtons = 0;
701     int nHats = 0;
702     int nAxis = 0;
703     int n;
704 
705     SDL_qsort(inputs, joystick->hwdata->NumInputs, sizeof(input_t), SortDevFunc);
706 
707     for (n = 0; n < joystick->hwdata->NumInputs; n++) {
708         switch (inputs[n].type) {
709         case BUTTON:
710             inputs[n].num = nButtons;
711             nButtons++;
712             break;
713 
714         case HAT:
715             inputs[n].num = nHats;
716             nHats++;
717             break;
718 
719         case AXIS:
720             inputs[n].num = nAxis;
721             nAxis++;
722             break;
723         }
724     }
725 }
726 
727 int
SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick,JoyStick_DeviceData * joystickdevice)728 SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
729 {
730     HRESULT result;
731     DIPROPDWORD dipdw;
732 
733     joystick->hwdata->buffered = SDL_TRUE;
734     joystick->hwdata->Capabilities.dwSize = sizeof(DIDEVCAPS);
735 
736     SDL_zero(dipdw);
737     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
738     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
739 
740     result =
741         IDirectInput8_CreateDevice(dinput,
742             &joystickdevice->dxdevice.guidInstance,
743             &joystick->hwdata->InputDevice,
744             NULL);
745     if (FAILED(result)) {
746         return SetDIerror("IDirectInput::CreateDevice", result);
747     }
748 
749     /* Acquire shared access. Exclusive access is required for forces,
750     * though. */
751     result =
752         IDirectInputDevice8_SetCooperativeLevel(joystick->hwdata->
753         InputDevice, SDL_HelperWindow,
754         DISCL_EXCLUSIVE |
755         DISCL_BACKGROUND);
756     if (FAILED(result)) {
757         return SetDIerror("IDirectInputDevice8::SetCooperativeLevel", result);
758     }
759 
760     /* Use the extended data structure: DIJOYSTATE2. */
761     result =
762         IDirectInputDevice8_SetDataFormat(joystick->hwdata->InputDevice,
763         &SDL_c_dfDIJoystick2);
764     if (FAILED(result)) {
765         return SetDIerror("IDirectInputDevice8::SetDataFormat", result);
766     }
767 
768     /* Get device capabilities */
769     result =
770         IDirectInputDevice8_GetCapabilities(joystick->hwdata->InputDevice,
771         &joystick->hwdata->Capabilities);
772     if (FAILED(result)) {
773         return SetDIerror("IDirectInputDevice8::GetCapabilities", result);
774     }
775 
776     /* Force capable? */
777     if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
778         result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
779         if (FAILED(result)) {
780             return SetDIerror("IDirectInputDevice8::Acquire", result);
781         }
782 
783         /* reset all actuators. */
784         result =
785             IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->
786             InputDevice,
787             DISFFC_RESET);
788 
789         /* Not necessarily supported, ignore if not supported.
790         if (FAILED(result)) {
791         return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand", result);
792         }
793         */
794 
795         result = IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
796 
797         if (FAILED(result)) {
798             return SetDIerror("IDirectInputDevice8::Unacquire", result);
799         }
800 
801         /* Turn on auto-centering for a ForceFeedback device (until told
802         * otherwise). */
803         dipdw.diph.dwObj = 0;
804         dipdw.diph.dwHow = DIPH_DEVICE;
805         dipdw.dwData = DIPROPAUTOCENTER_ON;
806 
807         result =
808             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
809             DIPROP_AUTOCENTER, &dipdw.diph);
810 
811         /* Not necessarily supported, ignore if not supported.
812         if (FAILED(result)) {
813         return SetDIerror("IDirectInputDevice8::SetProperty", result);
814         }
815         */
816     }
817 
818     /* What buttons and axes does it have? */
819     IDirectInputDevice8_EnumObjects(joystick->hwdata->InputDevice,
820         EnumDevObjectsCallback, joystick,
821         DIDFT_BUTTON | DIDFT_AXIS | DIDFT_POV);
822 
823     /* Reorder the input objects. Some devices do not report the X axis as
824     * the first axis, for example. */
825     SortDevObjects(joystick);
826 
827     dipdw.diph.dwObj = 0;
828     dipdw.diph.dwHow = DIPH_DEVICE;
829     dipdw.dwData = INPUT_QSIZE;
830 
831     /* Set the buffer size */
832     result =
833         IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
834         DIPROP_BUFFERSIZE, &dipdw.diph);
835 
836     if (result == DI_POLLEDDEVICE) {
837         /* This device doesn't support buffering, so we're forced
838          * to use less reliable polling. */
839         joystick->hwdata->buffered = SDL_FALSE;
840     } else if (FAILED(result)) {
841         return SetDIerror("IDirectInputDevice8::SetProperty", result);
842     }
843     return 0;
844 }
845 
846 static int
SDL_DINPUT_JoystickInitRumble(SDL_Joystick * joystick,Sint16 magnitude)847 SDL_DINPUT_JoystickInitRumble(SDL_Joystick * joystick, Sint16 magnitude)
848 {
849     HRESULT result;
850 
851     /* Reset and then enable actuators */
852     result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_RESET);
853     if (result == DIERR_INPUTLOST || result == DIERR_NOTEXCLUSIVEACQUIRED) {
854         result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
855         if (SUCCEEDED(result)) {
856             result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_RESET);
857         }
858     }
859     if (FAILED(result)) {
860         return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_RESET)", result);
861     }
862 
863     result = IDirectInputDevice8_SendForceFeedbackCommand(joystick->hwdata->InputDevice, DISFFC_SETACTUATORSON);
864     if (FAILED(result)) {
865         return SetDIerror("IDirectInputDevice8::SendForceFeedbackCommand(DISFFC_SETACTUATORSON)", result);
866     }
867 
868     /* Create the effect */
869     joystick->hwdata->ffeffect = CreateRumbleEffectData(magnitude);
870     if (!joystick->hwdata->ffeffect) {
871         return SDL_OutOfMemory();
872     }
873 
874     result = IDirectInputDevice8_CreateEffect(joystick->hwdata->InputDevice, &GUID_Sine,
875                                               joystick->hwdata->ffeffect, &joystick->hwdata->ffeffect_ref, NULL);
876     if (FAILED(result)) {
877         return SetDIerror("IDirectInputDevice8::CreateEffect", result);
878     }
879     return 0;
880 }
881 
882 int
SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick,Uint16 low_frequency_rumble,Uint16 high_frequency_rumble)883 SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
884 {
885     HRESULT result;
886 
887     /* Scale and average the two rumble strengths */
888     Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
889 
890     if (!(joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK)) {
891         return SDL_Unsupported();
892     }
893 
894     if (joystick->hwdata->ff_initialized) {
895         DIPERIODIC *periodic = ((DIPERIODIC *)joystick->hwdata->ffeffect->lpvTypeSpecificParams);
896         periodic->dwMagnitude = CONVERT_MAGNITUDE(magnitude);
897 
898         result = IDirectInputEffect_SetParameters(joystick->hwdata->ffeffect_ref, joystick->hwdata->ffeffect, (DIEP_DURATION | DIEP_TYPESPECIFICPARAMS));
899         if (result == DIERR_INPUTLOST) {
900             result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
901             if (SUCCEEDED(result)) {
902                 result = IDirectInputEffect_SetParameters(joystick->hwdata->ffeffect_ref, joystick->hwdata->ffeffect, (DIEP_DURATION | DIEP_TYPESPECIFICPARAMS));
903             }
904         }
905         if (FAILED(result)) {
906             return SetDIerror("IDirectInputDevice8::SetParameters", result);
907         }
908     } else {
909         if (SDL_DINPUT_JoystickInitRumble(joystick, magnitude) < 0) {
910             return -1;
911         }
912         joystick->hwdata->ff_initialized = SDL_TRUE;
913     }
914 
915     result = IDirectInputEffect_Start(joystick->hwdata->ffeffect_ref, 1, 0);
916     if (result == DIERR_INPUTLOST || result == DIERR_NOTEXCLUSIVEACQUIRED) {
917         result = IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
918         if (SUCCEEDED(result)) {
919             result = IDirectInputEffect_Start(joystick->hwdata->ffeffect_ref, 1, 0);
920         }
921     }
922     if (FAILED(result)) {
923         return SetDIerror("IDirectInputDevice8::Start", result);
924     }
925     return 0;
926 }
927 
928 Uint32
SDL_DINPUT_JoystickGetCapabilities(SDL_Joystick * joystick)929 SDL_DINPUT_JoystickGetCapabilities(SDL_Joystick * joystick)
930 {
931     Uint32 result = 0;
932 
933     if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
934         result |= SDL_JOYCAP_RUMBLE;
935     }
936 
937     return result;
938 }
939 
940 static Uint8
TranslatePOV(DWORD value)941 TranslatePOV(DWORD value)
942 {
943     const int HAT_VALS[] = {
944         SDL_HAT_UP,
945         SDL_HAT_UP | SDL_HAT_RIGHT,
946         SDL_HAT_RIGHT,
947         SDL_HAT_DOWN | SDL_HAT_RIGHT,
948         SDL_HAT_DOWN,
949         SDL_HAT_DOWN | SDL_HAT_LEFT,
950         SDL_HAT_LEFT,
951         SDL_HAT_UP | SDL_HAT_LEFT
952     };
953 
954     if (LOWORD(value) == 0xFFFF)
955         return SDL_HAT_CENTERED;
956 
957     /* Round the value up: */
958     value += 4500 / 2;
959     value %= 36000;
960     value /= 4500;
961 
962     if (value >= 8)
963         return SDL_HAT_CENTERED;        /* shouldn't happen */
964 
965     return HAT_VALS[value];
966 }
967 
968 /* Function to update the state of a joystick - called as a device poll.
969  * This function shouldn't update the joystick structure directly,
970  * but instead should call SDL_PrivateJoystick*() to deliver events
971  * and update joystick device state.
972  */
973 static void
UpdateDINPUTJoystickState_Polled(SDL_Joystick * joystick)974 UpdateDINPUTJoystickState_Polled(SDL_Joystick * joystick)
975 {
976     DIJOYSTATE2 state;
977     HRESULT result;
978     int i;
979 
980     result =
981         IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
982         sizeof(DIJOYSTATE2), &state);
983     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
984         IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
985         result =
986             IDirectInputDevice8_GetDeviceState(joystick->hwdata->InputDevice,
987             sizeof(DIJOYSTATE2), &state);
988     }
989 
990     if (result != DI_OK) {
991         return;
992     }
993 
994     /* Set each known axis, button and POV. */
995     for (i = 0; i < joystick->hwdata->NumInputs; ++i) {
996         const input_t *in = &joystick->hwdata->Inputs[i];
997 
998         switch (in->type) {
999         case AXIS:
1000             switch (in->ofs) {
1001             case DIJOFS_X:
1002                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lX);
1003                 break;
1004             case DIJOFS_Y:
1005                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lY);
1006                 break;
1007             case DIJOFS_Z:
1008                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lZ);
1009                 break;
1010             case DIJOFS_RX:
1011                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lRx);
1012                 break;
1013             case DIJOFS_RY:
1014                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lRy);
1015                 break;
1016             case DIJOFS_RZ:
1017                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.lRz);
1018                 break;
1019             case DIJOFS_SLIDER(0):
1020                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.rglSlider[0]);
1021                 break;
1022             case DIJOFS_SLIDER(1):
1023                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)state.rglSlider[1]);
1024                 break;
1025             }
1026             break;
1027 
1028         case BUTTON:
1029             SDL_PrivateJoystickButton(joystick, in->num,
1030                 (Uint8)(state.rgbButtons[in->ofs - DIJOFS_BUTTON0] ? SDL_PRESSED : SDL_RELEASED));
1031             break;
1032         case HAT:
1033         {
1034             Uint8 pos = TranslatePOV(state.rgdwPOV[in->ofs - DIJOFS_POV(0)]);
1035             SDL_PrivateJoystickHat(joystick, in->num, pos);
1036             break;
1037         }
1038         }
1039     }
1040 }
1041 
1042 static void
UpdateDINPUTJoystickState_Buffered(SDL_Joystick * joystick)1043 UpdateDINPUTJoystickState_Buffered(SDL_Joystick * joystick)
1044 {
1045     int i;
1046     HRESULT result;
1047     DWORD numevents;
1048     DIDEVICEOBJECTDATA evtbuf[INPUT_QSIZE];
1049 
1050     numevents = INPUT_QSIZE;
1051     result =
1052         IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
1053         sizeof(DIDEVICEOBJECTDATA), evtbuf,
1054         &numevents, 0);
1055     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
1056         IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1057         result =
1058             IDirectInputDevice8_GetDeviceData(joystick->hwdata->InputDevice,
1059             sizeof(DIDEVICEOBJECTDATA),
1060             evtbuf, &numevents, 0);
1061     }
1062 
1063     /* Handle the events or punt */
1064     if (FAILED(result)) {
1065         return;
1066     }
1067 
1068     for (i = 0; i < (int)numevents; ++i) {
1069         int j;
1070 
1071         for (j = 0; j < joystick->hwdata->NumInputs; ++j) {
1072             const input_t *in = &joystick->hwdata->Inputs[j];
1073 
1074             if (evtbuf[i].dwOfs != in->ofs)
1075                 continue;
1076 
1077             switch (in->type) {
1078             case AXIS:
1079                 SDL_PrivateJoystickAxis(joystick, in->num, (Sint16)evtbuf[i].dwData);
1080                 break;
1081             case BUTTON:
1082                 SDL_PrivateJoystickButton(joystick, in->num,
1083                     (Uint8)(evtbuf[i].dwData ? SDL_PRESSED : SDL_RELEASED));
1084                 break;
1085             case HAT:
1086                 {
1087                     Uint8 pos = TranslatePOV(evtbuf[i].dwData);
1088                     SDL_PrivateJoystickHat(joystick, in->num, pos);
1089                 }
1090                 break;
1091             }
1092         }
1093     }
1094 
1095     if (result == DI_BUFFEROVERFLOW) {
1096         /* Our buffer wasn't big enough to hold all the queued events,
1097          * so poll the device to make sure we have the complete state.
1098          */
1099         UpdateDINPUTJoystickState_Polled(joystick);
1100     }
1101 }
1102 
1103 void
SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick)1104 SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick)
1105 {
1106     HRESULT result;
1107 
1108     result = IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);
1109     if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) {
1110         IDirectInputDevice8_Acquire(joystick->hwdata->InputDevice);
1111         IDirectInputDevice8_Poll(joystick->hwdata->InputDevice);
1112     }
1113 
1114     if (joystick->hwdata->buffered) {
1115         UpdateDINPUTJoystickState_Buffered(joystick);
1116     } else {
1117         UpdateDINPUTJoystickState_Polled(joystick);
1118     }
1119 }
1120 
1121 void
SDL_DINPUT_JoystickClose(SDL_Joystick * joystick)1122 SDL_DINPUT_JoystickClose(SDL_Joystick * joystick)
1123 {
1124     if (joystick->hwdata->ffeffect_ref) {
1125         IDirectInputEffect_Unload(joystick->hwdata->ffeffect_ref);
1126         joystick->hwdata->ffeffect_ref = NULL;
1127     }
1128     if (joystick->hwdata->ffeffect) {
1129         FreeRumbleEffectData(joystick->hwdata->ffeffect);
1130         joystick->hwdata->ffeffect = NULL;
1131     }
1132     IDirectInputDevice8_Unacquire(joystick->hwdata->InputDevice);
1133     IDirectInputDevice8_Release(joystick->hwdata->InputDevice);
1134     joystick->hwdata->ff_initialized = SDL_FALSE;
1135 }
1136 
1137 void
SDL_DINPUT_JoystickQuit(void)1138 SDL_DINPUT_JoystickQuit(void)
1139 {
1140     if (dinput != NULL) {
1141         IDirectInput8_Release(dinput);
1142         dinput = NULL;
1143     }
1144 
1145     if (coinitialized) {
1146         WIN_CoUninitialize();
1147         coinitialized = SDL_FALSE;
1148     }
1149 }
1150 
1151 #else /* !SDL_JOYSTICK_DINPUT */
1152 
1153 typedef struct JoyStick_DeviceData JoyStick_DeviceData;
1154 
1155 int
SDL_DINPUT_JoystickInit(void)1156 SDL_DINPUT_JoystickInit(void)
1157 {
1158     return 0;
1159 }
1160 
1161 void
SDL_DINPUT_JoystickDetect(JoyStick_DeviceData ** pContext)1162 SDL_DINPUT_JoystickDetect(JoyStick_DeviceData **pContext)
1163 {
1164 }
1165 
1166 SDL_bool
SDL_DINPUT_JoystickPresent(Uint16 vendor,Uint16 product,Uint16 version)1167 SDL_DINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version)
1168 {
1169     return SDL_FALSE;
1170 }
1171 
1172 int
SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick,JoyStick_DeviceData * joystickdevice)1173 SDL_DINPUT_JoystickOpen(SDL_Joystick * joystick, JoyStick_DeviceData *joystickdevice)
1174 {
1175     return SDL_Unsupported();
1176 }
1177 
1178 int
SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick,Uint16 low_frequency_rumble,Uint16 high_frequency_rumble)1179 SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1180 {
1181     return SDL_Unsupported();
1182 }
1183 
1184 Uint32
SDL_DINPUT_JoystickGetCapabilities(SDL_Joystick * joystick)1185 SDL_DINPUT_JoystickGetCapabilities(SDL_Joystick * joystick)
1186 {
1187     return 0;
1188 }
1189 
1190 void
SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick)1191 SDL_DINPUT_JoystickUpdate(SDL_Joystick * joystick)
1192 {
1193 }
1194 
1195 void
SDL_DINPUT_JoystickClose(SDL_Joystick * joystick)1196 SDL_DINPUT_JoystickClose(SDL_Joystick * joystick)
1197 {
1198 }
1199 
1200 void
SDL_DINPUT_JoystickQuit(void)1201 SDL_DINPUT_JoystickQuit(void)
1202 {
1203 }
1204 
1205 #endif /* SDL_JOYSTICK_DINPUT */
1206 
1207 /* vi: set ts=4 sw=4 expandtab: */
1208