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