summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/joystick/SDL_joystick.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/SDL_joystick.c
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/joystick/SDL_joystick.c')
-rw-r--r--contrib/SDL-3.2.8/src/joystick/SDL_joystick.c3675
1 files changed, 3675 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/joystick/SDL_joystick.c b/contrib/SDL-3.2.8/src/joystick/SDL_joystick.c
new file mode 100644
index 0000000..7574adc
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/joystick/SDL_joystick.c
@@ -0,0 +1,3675 @@
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// This is the joystick API for Simple DirectMedia Layer
24
25#include "SDL_sysjoystick.h"
26#include "../SDL_hints_c.h"
27#include "SDL_gamepad_c.h"
28#include "SDL_joystick_c.h"
29#include "SDL_steam_virtual_gamepad.h"
30
31#include "../events/SDL_events_c.h"
32#include "../video/SDL_sysvideo.h"
33#include "../sensor/SDL_sensor_c.h"
34#include "hidapi/SDL_hidapijoystick_c.h"
35
36// This is included in only one place because it has a large static list of controllers
37#include "controller_type.h"
38
39#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
40// Needed for checking for input remapping programs
41#include "../core/windows/SDL_windows.h"
42
43#undef UNICODE // We want ASCII functions
44#include <tlhelp32.h>
45#endif
46
47#ifdef SDL_JOYSTICK_VIRTUAL
48#include "./virtual/SDL_virtualjoystick_c.h"
49#endif
50
51static SDL_JoystickDriver *SDL_joystick_drivers[] = {
52#ifdef SDL_JOYSTICK_HIDAPI // Highest priority driver for supported devices
53 &SDL_HIDAPI_JoystickDriver,
54#endif
55#ifdef SDL_JOYSTICK_PRIVATE
56 &SDL_PRIVATE_JoystickDriver,
57#endif
58#ifdef SDL_JOYSTICK_GAMEINPUT // Higher priority than other Windows drivers
59 &SDL_GAMEINPUT_JoystickDriver,
60#endif
61#ifdef SDL_JOYSTICK_RAWINPUT // Before WINDOWS driver, as WINDOWS wants to check if this driver is handling things
62 &SDL_RAWINPUT_JoystickDriver,
63#endif
64#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) // Before WGI driver, as WGI wants to check if this driver is handling things
65 &SDL_WINDOWS_JoystickDriver,
66#endif
67#ifdef SDL_JOYSTICK_WGI
68 &SDL_WGI_JoystickDriver,
69#endif
70#ifdef SDL_JOYSTICK_WINMM
71 &SDL_WINMM_JoystickDriver,
72#endif
73#ifdef SDL_JOYSTICK_LINUX
74 &SDL_LINUX_JoystickDriver,
75#endif
76#ifdef SDL_JOYSTICK_IOKIT
77 &SDL_DARWIN_JoystickDriver,
78#endif
79#if (defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)) && !defined(SDL_JOYSTICK_DISABLED)
80 &SDL_IOS_JoystickDriver,
81#endif
82#ifdef SDL_JOYSTICK_ANDROID
83 &SDL_ANDROID_JoystickDriver,
84#endif
85#ifdef SDL_JOYSTICK_EMSCRIPTEN
86 &SDL_EMSCRIPTEN_JoystickDriver,
87#endif
88#ifdef SDL_JOYSTICK_HAIKU
89 &SDL_HAIKU_JoystickDriver,
90#endif
91#ifdef SDL_JOYSTICK_USBHID /* !!! FIXME: "USBHID" is a generic name, and doubly-confusing with HIDAPI next to it. This is the *BSD interface, rename this. */
92 &SDL_BSD_JoystickDriver,
93#endif
94#ifdef SDL_JOYSTICK_PS2
95 &SDL_PS2_JoystickDriver,
96#endif
97#ifdef SDL_JOYSTICK_PSP
98 &SDL_PSP_JoystickDriver,
99#endif
100#ifdef SDL_JOYSTICK_VIRTUAL
101 &SDL_VIRTUAL_JoystickDriver,
102#endif
103#ifdef SDL_JOYSTICK_VITA
104 &SDL_VITA_JoystickDriver,
105#endif
106#ifdef SDL_JOYSTICK_N3DS
107 &SDL_N3DS_JoystickDriver,
108#endif
109#if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED)
110 &SDL_DUMMY_JoystickDriver
111#endif
112};
113
114#ifndef SDL_THREAD_SAFETY_ANALYSIS
115static
116#endif
117SDL_Mutex *SDL_joystick_lock = NULL; // This needs to support recursive locks
118static SDL_AtomicInt SDL_joystick_lock_pending;
119static int SDL_joysticks_locked;
120static bool SDL_joysticks_initialized;
121static bool SDL_joysticks_quitting;
122static bool SDL_joystick_being_added;
123static SDL_Joystick *SDL_joysticks SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
124static int SDL_joystick_player_count SDL_GUARDED_BY(SDL_joystick_lock) = 0;
125static SDL_JoystickID *SDL_joystick_players SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
126static bool SDL_joystick_allows_background_events = false;
127
128static Uint32 initial_arcadestick_devices[] = {
129 MAKE_VIDPID(0x0079, 0x181a), // Venom Arcade Stick
130 MAKE_VIDPID(0x0079, 0x181b), // Venom Arcade Stick
131 MAKE_VIDPID(0x0c12, 0x0ef6), // Hitbox Arcade Stick
132 MAKE_VIDPID(0x0e6f, 0x0109), // PDP Versus Fighting Pad
133 MAKE_VIDPID(0x0f0d, 0x0016), // Hori Real Arcade Pro.EX
134 MAKE_VIDPID(0x0f0d, 0x001b), // Hori Real Arcade Pro VX
135 MAKE_VIDPID(0x0f0d, 0x0063), // Hori Real Arcade Pro Hayabusa (USA) Xbox One
136 MAKE_VIDPID(0x0f0d, 0x006a), // Real Arcade Pro 4
137 MAKE_VIDPID(0x0f0d, 0x0078), // Hori Real Arcade Pro V Kai Xbox One
138 MAKE_VIDPID(0x0f0d, 0x008a), // HORI Real Arcade Pro 4
139 MAKE_VIDPID(0x0f0d, 0x008c), // Hori Real Arcade Pro 4
140 MAKE_VIDPID(0x0f0d, 0x00aa), // HORI Real Arcade Pro V Hayabusa in Switch Mode
141 MAKE_VIDPID(0x0f0d, 0x00ed), // Hori Fighting Stick mini 4 kai
142 MAKE_VIDPID(0x0f0d, 0x011c), // Hori Fighting Stick Alpha in PS4 Mode
143 MAKE_VIDPID(0x0f0d, 0x011e), // Hori Fighting Stick Alpha in PC Mode
144 MAKE_VIDPID(0x0f0d, 0x0184), // Hori Fighting Stick Alpha in PS5 Mode
145 MAKE_VIDPID(0x146b, 0x0604), // NACON Daija Arcade Stick
146 MAKE_VIDPID(0x1532, 0x0a00), // Razer Atrox Arcade Stick
147 MAKE_VIDPID(0x1bad, 0xf03d), // Street Fighter IV Arcade Stick TE - Chun Li
148 MAKE_VIDPID(0x1bad, 0xf502), // Hori Real Arcade Pro.VX SA
149 MAKE_VIDPID(0x1bad, 0xf504), // Hori Real Arcade Pro. EX
150 MAKE_VIDPID(0x1bad, 0xf506), // Hori Real Arcade Pro.EX Premium VLX
151 MAKE_VIDPID(0x20d6, 0xa715), // PowerA Nintendo Switch Fusion Arcade Stick
152 MAKE_VIDPID(0x24c6, 0x5000), // Razer Atrox Arcade Stick
153 MAKE_VIDPID(0x24c6, 0x5501), // Hori Real Arcade Pro VX-SA
154 MAKE_VIDPID(0x24c6, 0x550e), // Hori Real Arcade Pro V Kai 360
155 MAKE_VIDPID(0x2c22, 0x2300), // Qanba Obsidian Arcade Joystick in PS4 Mode
156 MAKE_VIDPID(0x2c22, 0x2302), // Qanba Obsidian Arcade Joystick in PS3 Mode
157 MAKE_VIDPID(0x2c22, 0x2303), // Qanba Obsidian Arcade Joystick in PC Mode
158 MAKE_VIDPID(0x2c22, 0x2500), // Qanba Dragon Arcade Joystick in PS4 Mode
159 MAKE_VIDPID(0x2c22, 0x2502), // Qanba Dragon Arcade Joystick in PS3 Mode
160 MAKE_VIDPID(0x2c22, 0x2503), // Qanba Dragon Arcade Joystick in PC Mode
161};
162static SDL_vidpid_list arcadestick_devices = {
163 SDL_HINT_JOYSTICK_ARCADESTICK_DEVICES, 0, 0, NULL,
164 SDL_HINT_JOYSTICK_ARCADESTICK_DEVICES_EXCLUDED, 0, 0, NULL,
165 SDL_arraysize(initial_arcadestick_devices), initial_arcadestick_devices,
166 false
167};
168
169/* This list is taken from:
170 https://raw.githubusercontent.com/denilsonsa/udev-joystick-blacklist/master/generate_rules.py
171 */
172static Uint32 initial_blacklist_devices[] = {
173 // Microsoft Microsoft Wireless Optical Desktop 2.10
174 // Microsoft Wireless Desktop - Comfort Edition
175 MAKE_VIDPID(0x045e, 0x009d),
176
177 // Microsoft Microsoft Digital Media Pro Keyboard
178 // Microsoft Corp. Digital Media Pro Keyboard
179 MAKE_VIDPID(0x045e, 0x00b0),
180
181 // Microsoft Microsoft Digital Media Keyboard
182 // Microsoft Corp. Digital Media Keyboard 1.0A
183 MAKE_VIDPID(0x045e, 0x00b4),
184
185 // Microsoft Microsoft Digital Media Keyboard 3000
186 MAKE_VIDPID(0x045e, 0x0730),
187
188 // Microsoft Microsoft 2.4GHz Transceiver v6.0
189 // Microsoft Microsoft 2.4GHz Transceiver v8.0
190 // Microsoft Corp. Nano Transceiver v1.0 for Bluetooth
191 // Microsoft Wireless Mobile Mouse 1000
192 // Microsoft Wireless Desktop 3000
193 MAKE_VIDPID(0x045e, 0x0745),
194
195 // Microsoft SideWinder(TM) 2.4GHz Transceiver
196 MAKE_VIDPID(0x045e, 0x0748),
197
198 // Microsoft Corp. Wired Keyboard 600
199 MAKE_VIDPID(0x045e, 0x0750),
200
201 // Microsoft Corp. Sidewinder X4 keyboard
202 MAKE_VIDPID(0x045e, 0x0768),
203
204 // Microsoft Corp. Arc Touch Mouse Transceiver
205 MAKE_VIDPID(0x045e, 0x0773),
206
207 // Microsoft 2.4GHz Transceiver v9.0
208 // Microsoft Nano Transceiver v2.1
209 // Microsoft Sculpt Ergonomic Keyboard (5KV-00001)
210 MAKE_VIDPID(0x045e, 0x07a5),
211
212 // Microsoft Nano Transceiver v1.0
213 // Microsoft Wireless Keyboard 800
214 MAKE_VIDPID(0x045e, 0x07b2),
215
216 // Microsoft Nano Transceiver v2.0
217 MAKE_VIDPID(0x045e, 0x0800),
218
219 MAKE_VIDPID(0x046d, 0xc30a), // Logitech, Inc. iTouch Composite keyboard
220
221 MAKE_VIDPID(0x04d9, 0xa0df), // Tek Syndicate Mouse (E-Signal USB Gaming Mouse)
222
223 // List of Wacom devices at: http://linuxwacom.sourceforge.net/wiki/index.php/Device_IDs
224 MAKE_VIDPID(0x056a, 0x0010), // Wacom ET-0405 Graphire
225 MAKE_VIDPID(0x056a, 0x0011), // Wacom ET-0405A Graphire2 (4x5)
226 MAKE_VIDPID(0x056a, 0x0012), // Wacom ET-0507A Graphire2 (5x7)
227 MAKE_VIDPID(0x056a, 0x0013), // Wacom CTE-430 Graphire3 (4x5)
228 MAKE_VIDPID(0x056a, 0x0014), // Wacom CTE-630 Graphire3 (6x8)
229 MAKE_VIDPID(0x056a, 0x0015), // Wacom CTE-440 Graphire4 (4x5)
230 MAKE_VIDPID(0x056a, 0x0016), // Wacom CTE-640 Graphire4 (6x8)
231 MAKE_VIDPID(0x056a, 0x0017), // Wacom CTE-450 Bamboo Fun (4x5)
232 MAKE_VIDPID(0x056a, 0x0018), // Wacom CTE-650 Bamboo Fun 6x8
233 MAKE_VIDPID(0x056a, 0x0019), // Wacom CTE-631 Bamboo One
234 MAKE_VIDPID(0x056a, 0x00d1), // Wacom Bamboo Pen and Touch CTH-460
235 MAKE_VIDPID(0x056a, 0x030e), // Wacom Intuos Pen (S) CTL-480
236
237 MAKE_VIDPID(0x09da, 0x054f), // A4 Tech Co., G7 750 mouse
238 MAKE_VIDPID(0x09da, 0x1410), // A4 Tech Co., Ltd Bloody AL9 mouse
239 MAKE_VIDPID(0x09da, 0x3043), // A4 Tech Co., Ltd Bloody R8A Gaming Mouse
240 MAKE_VIDPID(0x09da, 0x31b5), // A4 Tech Co., Ltd Bloody TL80 Terminator Laser Gaming Mouse
241 MAKE_VIDPID(0x09da, 0x3997), // A4 Tech Co., Ltd Bloody RT7 Terminator Wireless
242 MAKE_VIDPID(0x09da, 0x3f8b), // A4 Tech Co., Ltd Bloody V8 mouse
243 MAKE_VIDPID(0x09da, 0x51f4), // Modecom MC-5006 Keyboard
244 MAKE_VIDPID(0x09da, 0x5589), // A4 Tech Co., Ltd Terminator TL9 Laser Gaming Mouse
245 MAKE_VIDPID(0x09da, 0x7b22), // A4 Tech Co., Ltd Bloody V5
246 MAKE_VIDPID(0x09da, 0x7f2d), // A4 Tech Co., Ltd Bloody R3 mouse
247 MAKE_VIDPID(0x09da, 0x8090), // A4 Tech Co., Ltd X-718BK Oscar Optical Gaming Mouse
248 MAKE_VIDPID(0x09da, 0x9033), // A4 Tech Co., X7 X-705K
249 MAKE_VIDPID(0x09da, 0x9066), // A4 Tech Co., Sharkoon Fireglider Optical
250 MAKE_VIDPID(0x09da, 0x9090), // A4 Tech Co., Ltd XL-730K / XL-750BK / XL-755BK Laser Mouse
251 MAKE_VIDPID(0x09da, 0x90c0), // A4 Tech Co., Ltd X7 G800V keyboard
252 MAKE_VIDPID(0x09da, 0xf012), // A4 Tech Co., Ltd Bloody V7 mouse
253 MAKE_VIDPID(0x09da, 0xf32a), // A4 Tech Co., Ltd Bloody B540 keyboard
254 MAKE_VIDPID(0x09da, 0xf613), // A4 Tech Co., Ltd Bloody V2 mouse
255 MAKE_VIDPID(0x09da, 0xf624), // A4 Tech Co., Ltd Bloody B120 Keyboard
256
257 MAKE_VIDPID(0x1b1c, 0x1b3c), // Corsair Harpoon RGB gaming mouse
258
259 MAKE_VIDPID(0x1d57, 0xad03), // [T3] 2.4GHz and IR Air Mouse Remote Control
260
261 MAKE_VIDPID(0x1e7d, 0x2e4a), // Roccat Tyon Mouse
262
263 MAKE_VIDPID(0x20a0, 0x422d), // Winkeyless.kr Keyboards
264
265 MAKE_VIDPID(0x2516, 0x001f), // Cooler Master Storm Mizar Mouse
266 MAKE_VIDPID(0x2516, 0x0028), // Cooler Master Storm Alcor Mouse
267
268 /*****************************************************************/
269 // Additional entries
270 /*****************************************************************/
271
272 MAKE_VIDPID(0x04d9, 0x8008), // OBINLB USB-HID Keyboard (Anne Pro II)
273 MAKE_VIDPID(0x04d9, 0x8009), // OBINLB USB-HID Keyboard (Anne Pro II)
274 MAKE_VIDPID(0x04d9, 0xa292), // OBINLB USB-HID Keyboard (Anne Pro II)
275 MAKE_VIDPID(0x04d9, 0xa293), // OBINLB USB-HID Keyboard (Anne Pro II)
276 MAKE_VIDPID(0x1532, 0x0266), // Razer Huntsman V2 Analog, non-functional DInput device
277 MAKE_VIDPID(0x1532, 0x0282), // Razer Huntsman Mini Analog, non-functional DInput device
278 MAKE_VIDPID(0x26ce, 0x01a2), // ASRock LED Controller
279 MAKE_VIDPID(0x20d6, 0x0002), // PowerA Enhanced Wireless Controller for Nintendo Switch (charging port only)
280};
281static SDL_vidpid_list blacklist_devices = {
282 SDL_HINT_JOYSTICK_BLACKLIST_DEVICES, 0, 0, NULL,
283 SDL_HINT_JOYSTICK_BLACKLIST_DEVICES_EXCLUDED, 0, 0, NULL,
284 SDL_arraysize(initial_blacklist_devices), initial_blacklist_devices,
285 false
286};
287
288static Uint32 initial_flightstick_devices[] = {
289 MAKE_VIDPID(0x044f, 0x0402), // HOTAS Warthog Joystick
290 MAKE_VIDPID(0x044f, 0xb10a), // ThrustMaster, Inc. T.16000M Joystick
291 MAKE_VIDPID(0x046d, 0xc215), // Logitech Extreme 3D
292 MAKE_VIDPID(0x0738, 0x2221), // Saitek Pro Flight X-56 Rhino Stick
293 MAKE_VIDPID(0x231d, 0x0126), // Gunfighter Mk.III 'Space Combat Edition' (right)
294 MAKE_VIDPID(0x231d, 0x0127), // Gunfighter Mk.III 'Space Combat Edition' (left)
295 MAKE_VIDPID(0x362c, 0x0001), // Yawman Arrow
296};
297static SDL_vidpid_list flightstick_devices = {
298 SDL_HINT_JOYSTICK_FLIGHTSTICK_DEVICES, 0, 0, NULL,
299 SDL_HINT_JOYSTICK_FLIGHTSTICK_DEVICES_EXCLUDED, 0, 0, NULL,
300 SDL_arraysize(initial_flightstick_devices), initial_flightstick_devices,
301 false
302};
303
304static Uint32 initial_gamecube_devices[] = {
305 MAKE_VIDPID(0x0e6f, 0x0185), // PDP Wired Fight Pad Pro for Nintendo Switch
306 MAKE_VIDPID(0x20d6, 0xa711), // PowerA Wired Controller Nintendo GameCube Style
307};
308static SDL_vidpid_list gamecube_devices = {
309 SDL_HINT_JOYSTICK_GAMECUBE_DEVICES, 0, 0, NULL,
310 SDL_HINT_JOYSTICK_GAMECUBE_DEVICES_EXCLUDED, 0, 0, NULL,
311 SDL_arraysize(initial_gamecube_devices), initial_gamecube_devices,
312 false
313};
314
315static Uint32 initial_rog_gamepad_mice[] = {
316 MAKE_VIDPID(0x0b05, 0x18e3), // ROG Chakram (wired) Mouse
317 MAKE_VIDPID(0x0b05, 0x18e5), // ROG Chakram (wireless) Mouse
318 MAKE_VIDPID(0x0b05, 0x1906), // ROG Pugio II
319 MAKE_VIDPID(0x0b05, 0x1958), // ROG Chakram Core Mouse
320 MAKE_VIDPID(0x0b05, 0x1a18), // ROG Chakram X (wired) Mouse
321 MAKE_VIDPID(0x0b05, 0x1a1a), // ROG Chakram X (wireless) Mouse
322 MAKE_VIDPID(0x0b05, 0x1a1c), // ROG Chakram X (Bluetooth) Mouse
323};
324static SDL_vidpid_list rog_gamepad_mice = {
325 SDL_HINT_ROG_GAMEPAD_MICE, 0, 0, NULL,
326 SDL_HINT_ROG_GAMEPAD_MICE_EXCLUDED, 0, 0, NULL,
327 SDL_arraysize(initial_rog_gamepad_mice), initial_rog_gamepad_mice,
328 false
329};
330
331static Uint32 initial_throttle_devices[] = {
332 MAKE_VIDPID(0x044f, 0x0404), // HOTAS Warthog Throttle
333 MAKE_VIDPID(0x0738, 0xa221), // Saitek Pro Flight X-56 Rhino Throttle
334};
335static SDL_vidpid_list throttle_devices = {
336 SDL_HINT_JOYSTICK_THROTTLE_DEVICES, 0, 0, NULL,
337 SDL_HINT_JOYSTICK_THROTTLE_DEVICES_EXCLUDED, 0, 0, NULL,
338 SDL_arraysize(initial_throttle_devices), initial_throttle_devices,
339 false
340};
341
342static Uint32 initial_wheel_devices[] = {
343 MAKE_VIDPID(0x0079, 0x1864), // DragonRise Inc. Wired Wheel (active mode) (also known as PXN V900 (PS3), Superdrive SV-750, or a Genesis Seaborg 400)
344 MAKE_VIDPID(0x044f, 0xb65d), // Thrustmaster Wheel FFB
345 MAKE_VIDPID(0x044f, 0xb65e), // Thrustmaster T500RS
346 MAKE_VIDPID(0x044f, 0xb664), // Thrustmaster TX (initial mode)
347 MAKE_VIDPID(0x044f, 0xb669), // Thrustmaster TX (active mode)
348 MAKE_VIDPID(0x044f, 0xb66d), // Thrustmaster T300RS (PS4 mode)
349 MAKE_VIDPID(0x044f, 0xb66d), // Thrustmaster Wheel FFB
350 MAKE_VIDPID(0x044f, 0xb66e), // Thrustmaster T300RS (normal mode)
351 MAKE_VIDPID(0x044f, 0xb66f), // Thrustmaster T300RS (advanced mode)
352 MAKE_VIDPID(0x044f, 0xb677), // Thrustmaster T150
353 MAKE_VIDPID(0x044f, 0xb67f), // Thrustmaster TMX
354 MAKE_VIDPID(0x044f, 0xb691), // Thrustmaster TS-XW (initial mode)
355 MAKE_VIDPID(0x044f, 0xb692), // Thrustmaster TS-XW (active mode)
356 MAKE_VIDPID(0x044f, 0xb696), // Thrustmaster T248
357 MAKE_VIDPID(0x046d, 0xc24f), // Logitech G29 (PS3)
358 MAKE_VIDPID(0x046d, 0xc260), // Logitech G29 (PS4)
359 MAKE_VIDPID(0x046d, 0xc261), // Logitech G920 (initial mode)
360 MAKE_VIDPID(0x046d, 0xc262), // Logitech G920 (active mode)
361 MAKE_VIDPID(0x046d, 0xc266), // Logitech G923 for Playstation 4 and PC (PC mode)
362 MAKE_VIDPID(0x046d, 0xc267), // Logitech G923 for Playstation 4 and PC (PS4 mode)
363 MAKE_VIDPID(0x046d, 0xc268), // Logitech PRO Racing Wheel (PC mode)
364 MAKE_VIDPID(0x046d, 0xc269), // Logitech PRO Racing Wheel (PS4/PS5 mode)
365 MAKE_VIDPID(0x046d, 0xc26d), // Logitech G923 (Xbox)
366 MAKE_VIDPID(0x046d, 0xc26e), // Logitech G923
367 MAKE_VIDPID(0x046d, 0xc272), // Logitech PRO Racing Wheel for Xbox (PC mode)
368 MAKE_VIDPID(0x046d, 0xc294), // Logitech generic wheel
369 MAKE_VIDPID(0x046d, 0xc295), // Logitech Momo Force
370 MAKE_VIDPID(0x046d, 0xc298), // Logitech Driving Force Pro
371 MAKE_VIDPID(0x046d, 0xc299), // Logitech G25
372 MAKE_VIDPID(0x046d, 0xc29a), // Logitech Driving Force GT
373 MAKE_VIDPID(0x046d, 0xc29b), // Logitech G27
374 MAKE_VIDPID(0x046d, 0xca03), // Logitech Momo Racing
375 MAKE_VIDPID(0x0483, 0x0522), // Simagic Wheelbase (including M10, Alpha Mini, Alpha, Alpha U)
376 MAKE_VIDPID(0x0483, 0xa355), // VRS DirectForce Pro Wheel Base
377 MAKE_VIDPID(0x0eb7, 0x0001), // Fanatec ClubSport Wheel Base V2
378 MAKE_VIDPID(0x0eb7, 0x0004), // Fanatec ClubSport Wheel Base V2.5
379 MAKE_VIDPID(0x0eb7, 0x0005), // Fanatec CSL Elite Wheel Base+ (PS4)
380 MAKE_VIDPID(0x0eb7, 0x0006), // Fanatec Podium Wheel Base DD1
381 MAKE_VIDPID(0x0eb7, 0x0007), // Fanatec Podium Wheel Base DD2
382 MAKE_VIDPID(0x0eb7, 0x0011), // Fanatec Forza Motorsport (CSR Wheel / CSR Elite Wheel)
383 MAKE_VIDPID(0x0eb7, 0x0020), // Fanatec generic wheel / CSL DD / GT DD Pro
384 MAKE_VIDPID(0x0eb7, 0x0197), // Fanatec Porsche Wheel (Turbo / GT3 RS / Turbo S / GT3 V2 / GT2)
385 MAKE_VIDPID(0x0eb7, 0x038e), // Fanatec ClubSport Wheel Base V1
386 MAKE_VIDPID(0x0eb7, 0x0e03), // Fanatec CSL Elite Wheel Base
387 MAKE_VIDPID(0x11ff, 0x0511), // DragonRise Inc. Wired Wheel (initial mode) (also known as PXN V900 (PS3), Superdrive SV-750, or a Genesis Seaborg 400)
388 MAKE_VIDPID(0x1209, 0xffb0), // Generic FFBoard OpenFFBoard universal forcefeedback wheel
389 MAKE_VIDPID(0x16d0, 0x0d5a), // Simucube 1 Wheelbase
390 MAKE_VIDPID(0x16d0, 0x0d5f), // Simucube 2 Ultimate Wheelbase
391 MAKE_VIDPID(0x16d0, 0x0d60), // Simucube 2 Pro Wheelbase
392 MAKE_VIDPID(0x16d0, 0x0d61), // Simucube 2 Sport Wheelbase
393 MAKE_VIDPID(0x2433, 0xf300), // Asetek SimSports Invicta Wheelbase
394 MAKE_VIDPID(0x2433, 0xf301), // Asetek SimSports Forte Wheelbase
395 MAKE_VIDPID(0x2433, 0xf303), // Asetek SimSports La Prima Wheelbase
396 MAKE_VIDPID(0x2433, 0xf306), // Asetek SimSports Tony Kannan Wheelbase
397 MAKE_VIDPID(0x3416, 0x0301), // Cammus C5 Wheelbase
398 MAKE_VIDPID(0x3416, 0x0302), // Cammus C12 Wheelbase
399 MAKE_VIDPID(0x346e, 0x0000), // Moza R16/R21 Wheelbase
400 MAKE_VIDPID(0x346e, 0x0002), // Moza R9 Wheelbase
401 MAKE_VIDPID(0x346e, 0x0004), // Moza R5 Wheelbase
402 MAKE_VIDPID(0x346e, 0x0005), // Moza R3 Wheelbase
403 MAKE_VIDPID(0x346e, 0x0006), // Moza R12 Wheelbase
404};
405static SDL_vidpid_list wheel_devices = {
406 SDL_HINT_JOYSTICK_WHEEL_DEVICES, 0, 0, NULL,
407 SDL_HINT_JOYSTICK_WHEEL_DEVICES_EXCLUDED, 0, 0, NULL,
408 SDL_arraysize(initial_wheel_devices), initial_wheel_devices,
409 false
410};
411
412static Uint32 initial_zero_centered_devices[] = {
413 MAKE_VIDPID(0x05a0, 0x3232), // 8Bitdo Zero Gamepad
414 MAKE_VIDPID(0x0e8f, 0x3013), // HuiJia SNES USB adapter
415};
416static SDL_vidpid_list zero_centered_devices = {
417 SDL_HINT_JOYSTICK_ZERO_CENTERED_DEVICES, 0, 0, NULL,
418 NULL, 0, 0, NULL,
419 SDL_arraysize(initial_zero_centered_devices), initial_zero_centered_devices,
420 false
421};
422
423#define CHECK_JOYSTICK_MAGIC(joystick, result) \
424 if (!SDL_ObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK)) { \
425 SDL_InvalidParamError("joystick"); \
426 SDL_UnlockJoysticks(); \
427 return result; \
428 }
429
430bool SDL_JoysticksInitialized(void)
431{
432 return SDL_joysticks_initialized;
433}
434
435bool SDL_JoysticksQuitting(void)
436{
437 return SDL_joysticks_quitting;
438}
439
440void SDL_LockJoysticks(void)
441{
442 (void)SDL_AtomicIncRef(&SDL_joystick_lock_pending);
443 SDL_LockMutex(SDL_joystick_lock);
444 (void)SDL_AtomicDecRef(&SDL_joystick_lock_pending);
445
446 ++SDL_joysticks_locked;
447}
448
449void SDL_UnlockJoysticks(void)
450{
451 bool last_unlock = false;
452
453 --SDL_joysticks_locked;
454
455 if (!SDL_joysticks_initialized) {
456 // NOTE: There's a small window here where another thread could lock the mutex after we've checked for pending locks
457 if (!SDL_joysticks_locked && SDL_GetAtomicInt(&SDL_joystick_lock_pending) == 0) {
458 last_unlock = true;
459 }
460 }
461
462 /* The last unlock after joysticks are uninitialized will cleanup the mutex,
463 * allowing applications to lock joysticks while reinitializing the system.
464 */
465 if (last_unlock) {
466 SDL_Mutex *joystick_lock = SDL_joystick_lock;
467
468 SDL_LockMutex(joystick_lock);
469 {
470 SDL_UnlockMutex(SDL_joystick_lock);
471
472 SDL_joystick_lock = NULL;
473 }
474 SDL_UnlockMutex(joystick_lock);
475 SDL_DestroyMutex(joystick_lock);
476 } else {
477 SDL_UnlockMutex(SDL_joystick_lock);
478 }
479}
480
481bool SDL_JoysticksLocked(void)
482{
483 return (SDL_joysticks_locked > 0);
484}
485
486void SDL_AssertJoysticksLocked(void)
487{
488 SDL_assert(SDL_JoysticksLocked());
489}
490
491/*
492 * Get the driver and device index for a joystick instance ID
493 * This should be called while the joystick lock is held, to prevent another thread from updating the list
494 */
495static bool SDL_GetDriverAndJoystickIndex(SDL_JoystickID instance_id, SDL_JoystickDriver **driver, int *driver_index)
496{
497 int i, num_joysticks, device_index;
498
499 SDL_AssertJoysticksLocked();
500
501 if (instance_id > 0) {
502 for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
503 num_joysticks = SDL_joystick_drivers[i]->GetCount();
504 for (device_index = 0; device_index < num_joysticks; ++device_index) {
505 SDL_JoystickID joystick_id = SDL_joystick_drivers[i]->GetDeviceInstanceID(device_index);
506 if (joystick_id == instance_id) {
507 *driver = SDL_joystick_drivers[i];
508 *driver_index = device_index;
509 return true;
510 }
511 }
512 }
513 }
514
515 SDL_SetError("Joystick %" SDL_PRIu32 " not found", instance_id);
516 return false;
517}
518
519static int SDL_FindFreePlayerIndex(void)
520{
521 int player_index;
522
523 SDL_AssertJoysticksLocked();
524
525 for (player_index = 0; player_index < SDL_joystick_player_count; ++player_index) {
526 if (SDL_joystick_players[player_index] == 0) {
527 break;
528 }
529 }
530 return player_index;
531}
532
533static int SDL_GetPlayerIndexForJoystickID(SDL_JoystickID instance_id)
534{
535 int player_index;
536
537 SDL_AssertJoysticksLocked();
538
539 for (player_index = 0; player_index < SDL_joystick_player_count; ++player_index) {
540 if (instance_id == SDL_joystick_players[player_index]) {
541 break;
542 }
543 }
544 if (player_index == SDL_joystick_player_count) {
545 player_index = -1;
546 }
547 return player_index;
548}
549
550static SDL_JoystickID SDL_GetJoystickIDForPlayerIndex(int player_index)
551{
552 SDL_AssertJoysticksLocked();
553
554 if (player_index < 0 || player_index >= SDL_joystick_player_count) {
555 return 0;
556 }
557 return SDL_joystick_players[player_index];
558}
559
560static bool SDL_SetJoystickIDForPlayerIndex(int player_index, SDL_JoystickID instance_id)
561{
562 SDL_JoystickID existing_instance = SDL_GetJoystickIDForPlayerIndex(player_index);
563 SDL_JoystickDriver *driver;
564 int device_index;
565 int existing_player_index;
566
567 SDL_AssertJoysticksLocked();
568
569 if (player_index >= SDL_joystick_player_count) {
570 SDL_JoystickID *new_players = (SDL_JoystickID *)SDL_realloc(SDL_joystick_players, (player_index + 1) * sizeof(*SDL_joystick_players));
571 if (!new_players) {
572 return false;
573 }
574
575 SDL_joystick_players = new_players;
576 SDL_memset(&SDL_joystick_players[SDL_joystick_player_count], 0, (player_index - SDL_joystick_player_count + 1) * sizeof(SDL_joystick_players[0]));
577 SDL_joystick_player_count = player_index + 1;
578 } else if (player_index >= 0 && SDL_joystick_players[player_index] == instance_id) {
579 // Joystick is already assigned the requested player index
580 return true;
581 }
582
583 // Clear the old player index
584 existing_player_index = SDL_GetPlayerIndexForJoystickID(instance_id);
585 if (existing_player_index >= 0) {
586 SDL_joystick_players[existing_player_index] = 0;
587 }
588
589 if (player_index >= 0) {
590 SDL_joystick_players[player_index] = instance_id;
591 }
592
593 // Update the driver with the new index
594 if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
595 driver->SetDevicePlayerIndex(device_index, player_index);
596 }
597
598 // Move any existing joystick to another slot
599 if (existing_instance > 0) {
600 SDL_SetJoystickIDForPlayerIndex(SDL_FindFreePlayerIndex(), existing_instance);
601 }
602 return true;
603}
604
605static void SDLCALL SDL_JoystickAllowBackgroundEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
606{
607 if (SDL_GetStringBoolean(hint, false)) {
608 SDL_joystick_allows_background_events = true;
609 } else {
610 SDL_joystick_allows_background_events = false;
611 }
612}
613
614bool SDL_InitJoysticks(void)
615{
616 int i;
617 bool result = false;
618
619 // Create the joystick list lock
620 if (SDL_joystick_lock == NULL) {
621 SDL_joystick_lock = SDL_CreateMutex();
622 }
623
624 if (!SDL_InitSubSystem(SDL_INIT_EVENTS)) {
625 return false;
626 }
627
628 SDL_LockJoysticks();
629
630 SDL_joysticks_initialized = true;
631
632 SDL_InitGamepadMappings();
633
634 SDL_LoadVIDPIDList(&arcadestick_devices);
635 SDL_LoadVIDPIDList(&blacklist_devices);
636 SDL_LoadVIDPIDList(&flightstick_devices);
637 SDL_LoadVIDPIDList(&gamecube_devices);
638 SDL_LoadVIDPIDList(&rog_gamepad_mice);
639 SDL_LoadVIDPIDList(&throttle_devices);
640 SDL_LoadVIDPIDList(&wheel_devices);
641 SDL_LoadVIDPIDList(&zero_centered_devices);
642
643 // See if we should allow joystick events while in the background
644 SDL_AddHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
645 SDL_JoystickAllowBackgroundEventsChanged, NULL);
646
647 SDL_InitSteamVirtualGamepadInfo();
648
649 for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
650 if (SDL_joystick_drivers[i]->Init()) {
651 result = true;
652 }
653 }
654 SDL_UnlockJoysticks();
655
656 if (!result) {
657 SDL_QuitJoysticks();
658 }
659
660 return result;
661}
662
663bool SDL_JoysticksOpened(void)
664{
665 bool opened;
666
667 SDL_LockJoysticks();
668 {
669 if (SDL_joysticks != NULL) {
670 opened = true;
671 } else {
672 opened = false;
673 }
674 }
675 SDL_UnlockJoysticks();
676
677 return opened;
678}
679
680bool SDL_JoystickHandledByAnotherDriver(struct SDL_JoystickDriver *driver, Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
681{
682 int i;
683 bool result = false;
684
685 SDL_LockJoysticks();
686 {
687 for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
688 if (driver == SDL_joystick_drivers[i]) {
689 // Higher priority drivers do not have this device
690 break;
691 }
692 if (SDL_joystick_drivers[i]->IsDevicePresent(vendor_id, product_id, version, name)) {
693 result = true;
694 break;
695 }
696 }
697 }
698 SDL_UnlockJoysticks();
699
700 return result;
701}
702
703bool SDL_HasJoystick(void)
704{
705 int i;
706 int total_joysticks = 0;
707
708 SDL_LockJoysticks();
709 {
710 for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
711 total_joysticks += SDL_joystick_drivers[i]->GetCount();
712 }
713 }
714 SDL_UnlockJoysticks();
715
716 if (total_joysticks > 0) {
717 return true;
718 }
719 return false;
720}
721
722SDL_JoystickID *SDL_GetJoysticks(int *count)
723{
724 int i, num_joysticks, device_index;
725 int joystick_index = 0, total_joysticks = 0;
726 SDL_JoystickID *joysticks;
727
728 SDL_LockJoysticks();
729 {
730 for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
731 total_joysticks += SDL_joystick_drivers[i]->GetCount();
732 }
733
734 joysticks = (SDL_JoystickID *)SDL_malloc((total_joysticks + 1) * sizeof(*joysticks));
735 if (joysticks) {
736 if (count) {
737 *count = total_joysticks;
738 }
739
740 for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
741 num_joysticks = SDL_joystick_drivers[i]->GetCount();
742 for (device_index = 0; device_index < num_joysticks; ++device_index) {
743 SDL_assert(joystick_index < total_joysticks);
744 joysticks[joystick_index] = SDL_joystick_drivers[i]->GetDeviceInstanceID(device_index);
745 SDL_assert(joysticks[joystick_index] > 0);
746 ++joystick_index;
747 }
748 }
749 SDL_assert(joystick_index == total_joysticks);
750 joysticks[joystick_index] = 0;
751 } else {
752 if (count) {
753 *count = 0;
754 }
755 }
756 }
757 SDL_UnlockJoysticks();
758
759 return joysticks;
760}
761
762const SDL_SteamVirtualGamepadInfo *SDL_GetJoystickVirtualGamepadInfoForID(SDL_JoystickID instance_id)
763{
764 SDL_JoystickDriver *driver;
765 int device_index;
766 const SDL_SteamVirtualGamepadInfo *info = NULL;
767
768 if (SDL_SteamVirtualGamepadEnabled() &&
769 SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
770 info = SDL_GetSteamVirtualGamepadInfo(driver->GetDeviceSteamVirtualGamepadSlot(device_index));
771 }
772 return info;
773}
774
775/*
776 * Get the implementation dependent name of a joystick
777 */
778const char *SDL_GetJoystickNameForID(SDL_JoystickID instance_id)
779{
780 SDL_JoystickDriver *driver;
781 int device_index;
782 const char *name = NULL;
783 const SDL_SteamVirtualGamepadInfo *info;
784
785 SDL_LockJoysticks();
786 info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);
787 if (info) {
788 name = SDL_GetPersistentString(info->name);
789 } else if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
790 name = SDL_GetPersistentString(driver->GetDeviceName(device_index));
791 }
792 SDL_UnlockJoysticks();
793
794 return name;
795}
796
797/*
798 * Get the implementation dependent path of a joystick
799 */
800const char *SDL_GetJoystickPathForID(SDL_JoystickID instance_id)
801{
802 SDL_JoystickDriver *driver;
803 int device_index;
804 const char *path = NULL;
805
806 SDL_LockJoysticks();
807 if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
808 path = SDL_GetPersistentString(driver->GetDevicePath(device_index));
809 }
810 SDL_UnlockJoysticks();
811
812 if (!path) {
813 SDL_Unsupported();
814 }
815 return path;
816}
817
818/*
819 * Get the player index of a joystick, or -1 if it's not available
820 */
821int SDL_GetJoystickPlayerIndexForID(SDL_JoystickID instance_id)
822{
823 int player_index;
824
825 SDL_LockJoysticks();
826 player_index = SDL_GetPlayerIndexForJoystickID(instance_id);
827 SDL_UnlockJoysticks();
828
829 return player_index;
830}
831
832/*
833 * Return true if this joystick is known to have all axes centered at zero
834 * This isn't generally needed unless the joystick never generates an initial axis value near zero,
835 * e.g. it's emulating axes with digital buttons
836 */
837static bool SDL_JoystickAxesCenteredAtZero(SDL_Joystick *joystick)
838{
839 // printf("JOYSTICK '%s' VID/PID 0x%.4x/0x%.4x AXES: %d\n", joystick->name, vendor, product, joystick->naxes);
840
841 if (joystick->naxes == 2) {
842 // Assume D-pad or thumbstick style axes are centered at 0
843 return true;
844 }
845
846 return SDL_VIDPIDInList(SDL_GetJoystickVendor(joystick), SDL_GetJoystickProduct(joystick), &zero_centered_devices);
847}
848
849static bool IsROGAlly(SDL_Joystick *joystick)
850{
851 Uint16 vendor, product;
852 SDL_GUID guid = SDL_GetJoystickGUID(joystick);
853
854 // The ROG Ally controller spoofs an Xbox 360 controller
855 SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
856 if (vendor == USB_VENDOR_MICROSOFT && product == USB_PRODUCT_XBOX360_WIRED_CONTROLLER) {
857 // Check to see if this system has the expected sensors
858 bool has_ally_accel = false;
859 bool has_ally_gyro = false;
860
861 if (SDL_InitSubSystem(SDL_INIT_SENSOR)) {
862 SDL_SensorID *sensors = SDL_GetSensors(NULL);
863 if (sensors) {
864 int i;
865 for (i = 0; sensors[i]; ++i) {
866 SDL_SensorID sensor = sensors[i];
867
868 if (!has_ally_accel && SDL_GetSensorTypeForID(sensor) == SDL_SENSOR_ACCEL) {
869 const char *sensor_name = SDL_GetSensorNameForID(sensor);
870 if (sensor_name && SDL_strcmp(sensor_name, "Sensor BMI320 Acc") == 0) {
871 has_ally_accel = true;
872 }
873 }
874 if (!has_ally_gyro && SDL_GetSensorTypeForID(sensor) == SDL_SENSOR_GYRO) {
875 const char *sensor_name = SDL_GetSensorNameForID(sensor);
876 if (sensor_name && SDL_strcmp(sensor_name, "Sensor BMI320 Gyr") == 0) {
877 has_ally_gyro = true;
878 }
879 }
880 }
881 SDL_free(sensors);
882 }
883 SDL_QuitSubSystem(SDL_INIT_SENSOR);
884 }
885 if (has_ally_accel && has_ally_gyro) {
886 return true;
887 }
888 }
889 return false;
890}
891
892static bool ShouldAttemptSensorFusion(SDL_Joystick *joystick, bool *invert_sensors)
893{
894 SDL_AssertJoysticksLocked();
895
896 *invert_sensors = false;
897
898 // The SDL controller sensor API is only available for gamepads (at the moment)
899 if (!SDL_IsGamepad(joystick->instance_id)) {
900 return false;
901 }
902
903 // If the controller already has sensors, use those
904 if (joystick->nsensors > 0) {
905 return false;
906 }
907
908 const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION);
909 if (hint && *hint) {
910 if (*hint == '@' || SDL_strncmp(hint, "0x", 2) == 0) {
911 SDL_vidpid_list gamepads;
912 SDL_GUID guid;
913 Uint16 vendor, product;
914 bool enabled;
915 SDL_zero(gamepads);
916
917 // See if the gamepad is in our list of devices to enable
918 guid = SDL_GetJoystickGUID(joystick);
919 SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
920 SDL_LoadVIDPIDListFromHints(&gamepads, hint, NULL);
921 enabled = SDL_VIDPIDInList(vendor, product, &gamepads);
922 SDL_FreeVIDPIDList(&gamepads);
923 if (enabled) {
924 return true;
925 }
926 } else {
927 return SDL_GetStringBoolean(hint, false);
928 }
929 }
930
931 // See if this is another known wraparound gamepad
932 if (joystick->name &&
933 (SDL_strstr(joystick->name, "Backbone One") ||
934 SDL_strstr(joystick->name, "Kishi"))) {
935 return true;
936 }
937 if (IsROGAlly(joystick)) {
938 /* I'm not sure if this is a Windows thing, or a quirk for ROG Ally,
939 * but we need to invert the sensor data on all axes.
940 */
941 *invert_sensors = true;
942 return true;
943 }
944 return false;
945}
946
947static void AttemptSensorFusion(SDL_Joystick *joystick, bool invert_sensors)
948{
949 SDL_SensorID *sensors;
950 unsigned int i, j;
951
952 SDL_AssertJoysticksLocked();
953
954 if (!SDL_InitSubSystem(SDL_INIT_SENSOR)) {
955 return;
956 }
957
958 sensors = SDL_GetSensors(NULL);
959 if (sensors) {
960 for (i = 0; sensors[i]; ++i) {
961 SDL_SensorID sensor = sensors[i];
962
963 if (!joystick->accel_sensor && SDL_GetSensorTypeForID(sensor) == SDL_SENSOR_ACCEL) {
964 // Increment the sensor subsystem reference count
965 SDL_InitSubSystem(SDL_INIT_SENSOR);
966
967 joystick->accel_sensor = sensor;
968 SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 0.0f);
969 }
970 if (!joystick->gyro_sensor && SDL_GetSensorTypeForID(sensor) == SDL_SENSOR_GYRO) {
971 // Increment the sensor subsystem reference count
972 SDL_InitSubSystem(SDL_INIT_SENSOR);
973
974 joystick->gyro_sensor = sensor;
975 SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 0.0f);
976 }
977 }
978 SDL_free(sensors);
979 }
980 SDL_QuitSubSystem(SDL_INIT_SENSOR);
981
982 /* SDL defines sensor orientation for phones relative to the natural
983 orientation, and for gamepads relative to being held in front of you.
984 When a phone is being used as a gamepad, its orientation changes,
985 so adjust sensor axes to match.
986 */
987 if (SDL_GetNaturalDisplayOrientation(SDL_GetPrimaryDisplay()) == SDL_ORIENTATION_LANDSCAPE) {
988 /* When a device in landscape orientation is laid flat, the axes change
989 orientation as follows:
990 -X to +X becomes -X to +X
991 -Y to +Y becomes +Z to -Z
992 -Z to +Z becomes -Y to +Y
993 */
994 joystick->sensor_transform[0][0] = 1.0f;
995 joystick->sensor_transform[1][2] = 1.0f;
996 joystick->sensor_transform[2][1] = -1.0f;
997 } else {
998 /* When a device in portrait orientation is rotated left and laid flat,
999 the axes change orientation as follows:
1000 -X to +X becomes +Z to -Z
1001 -Y to +Y becomes +X to -X
1002 -Z to +Z becomes -Y to +Y
1003 */
1004 joystick->sensor_transform[0][1] = -1.0f;
1005 joystick->sensor_transform[1][2] = 1.0f;
1006 joystick->sensor_transform[2][0] = -1.0f;
1007 }
1008
1009 if (invert_sensors) {
1010 for (i = 0; i < SDL_arraysize(joystick->sensor_transform); ++i) {
1011 for (j = 0; j < SDL_arraysize(joystick->sensor_transform[i]); ++j) {
1012 joystick->sensor_transform[i][j] *= -1.0f;
1013 }
1014 }
1015 }
1016}
1017
1018static void CleanupSensorFusion(SDL_Joystick *joystick)
1019{
1020 SDL_AssertJoysticksLocked();
1021
1022 if (joystick->accel_sensor || joystick->gyro_sensor) {
1023 if (joystick->accel_sensor) {
1024 if (joystick->accel) {
1025 SDL_CloseSensor(joystick->accel);
1026 joystick->accel = NULL;
1027 }
1028 joystick->accel_sensor = 0;
1029
1030 // Decrement the sensor subsystem reference count
1031 SDL_QuitSubSystem(SDL_INIT_SENSOR);
1032 }
1033 if (joystick->gyro_sensor) {
1034 if (joystick->gyro) {
1035 SDL_CloseSensor(joystick->gyro);
1036 joystick->gyro = NULL;
1037 }
1038 joystick->gyro_sensor = 0;
1039
1040 // Decrement the sensor subsystem reference count
1041 SDL_QuitSubSystem(SDL_INIT_SENSOR);
1042 }
1043 }
1044}
1045
1046/*
1047 * Open a joystick for use - the index passed as an argument refers to
1048 * the N'th joystick on the system. This index is the value which will
1049 * identify this joystick in future joystick events.
1050 *
1051 * This function returns a joystick identifier, or NULL if an error occurred.
1052 */
1053SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id)
1054{
1055 SDL_JoystickDriver *driver;
1056 int device_index;
1057 SDL_Joystick *joystick;
1058 SDL_Joystick *joysticklist;
1059 const char *joystickname = NULL;
1060 const char *joystickpath = NULL;
1061 bool invert_sensors = false;
1062 const SDL_SteamVirtualGamepadInfo *info;
1063
1064 SDL_LockJoysticks();
1065
1066 if (!SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
1067 SDL_UnlockJoysticks();
1068 return NULL;
1069 }
1070
1071 joysticklist = SDL_joysticks;
1072 /* If the joystick is already open, return it
1073 * it is important that we have a single joystick for each instance id
1074 */
1075 while (joysticklist) {
1076 if (instance_id == joysticklist->instance_id) {
1077 joystick = joysticklist;
1078 ++joystick->ref_count;
1079 SDL_UnlockJoysticks();
1080 return joystick;
1081 }
1082 joysticklist = joysticklist->next;
1083 }
1084
1085 // Create and initialize the joystick
1086 joystick = (SDL_Joystick *)SDL_calloc(1, sizeof(*joystick));
1087 if (!joystick) {
1088 SDL_UnlockJoysticks();
1089 return NULL;
1090 }
1091 SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, true);
1092 joystick->driver = driver;
1093 joystick->instance_id = instance_id;
1094 joystick->attached = true;
1095 joystick->led_expiration = SDL_GetTicks();
1096 joystick->battery_percent = -1;
1097
1098 if (!driver->Open(joystick, device_index)) {
1099 SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, false);
1100 SDL_free(joystick);
1101 SDL_UnlockJoysticks();
1102 return NULL;
1103 }
1104
1105 joystickname = driver->GetDeviceName(device_index);
1106 if (joystickname) {
1107 joystick->name = SDL_strdup(joystickname);
1108 }
1109
1110 joystickpath = driver->GetDevicePath(device_index);
1111 if (joystickpath) {
1112 joystick->path = SDL_strdup(joystickpath);
1113 }
1114
1115 joystick->guid = driver->GetDeviceGUID(device_index);
1116
1117 if (joystick->naxes > 0) {
1118 joystick->axes = (SDL_JoystickAxisInfo *)SDL_calloc(joystick->naxes, sizeof(*joystick->axes));
1119 }
1120 if (joystick->nballs > 0) {
1121 joystick->balls = (SDL_JoystickBallData *)SDL_calloc(joystick->nballs, sizeof(*joystick->balls));
1122 }
1123 if (joystick->nhats > 0) {
1124 joystick->hats = (Uint8 *)SDL_calloc(joystick->nhats, sizeof(*joystick->hats));
1125 }
1126 if (joystick->nbuttons > 0) {
1127 joystick->buttons = (bool *)SDL_calloc(joystick->nbuttons, sizeof(*joystick->buttons));
1128 }
1129 if (((joystick->naxes > 0) && !joystick->axes) ||
1130 ((joystick->nballs > 0) && !joystick->balls) ||
1131 ((joystick->nhats > 0) && !joystick->hats) ||
1132 ((joystick->nbuttons > 0) && !joystick->buttons)) {
1133 SDL_CloseJoystick(joystick);
1134 SDL_UnlockJoysticks();
1135 return NULL;
1136 }
1137
1138 // If this joystick is known to have all zero centered axes, skip the auto-centering code
1139 if (SDL_JoystickAxesCenteredAtZero(joystick)) {
1140 int i;
1141
1142 for (i = 0; i < joystick->naxes; ++i) {
1143 joystick->axes[i].has_initial_value = true;
1144 }
1145 }
1146
1147 // Get the Steam Input API handle
1148 info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);
1149 if (info) {
1150 joystick->steam_handle = info->handle;
1151 }
1152
1153 // Use system gyro and accelerometer if the gamepad doesn't have built-in sensors
1154 if (ShouldAttemptSensorFusion(joystick, &invert_sensors)) {
1155 AttemptSensorFusion(joystick, invert_sensors);
1156 }
1157
1158 // Add joystick to list
1159 ++joystick->ref_count;
1160 // Link the joystick in the list
1161 joystick->next = SDL_joysticks;
1162 SDL_joysticks = joystick;
1163
1164 driver->Update(joystick);
1165
1166 SDL_UnlockJoysticks();
1167
1168 return joystick;
1169}
1170
1171SDL_JoystickID SDL_AttachVirtualJoystick(const SDL_VirtualJoystickDesc *desc)
1172{
1173#ifdef SDL_JOYSTICK_VIRTUAL
1174 SDL_JoystickID result;
1175
1176 SDL_LockJoysticks();
1177 result = SDL_JoystickAttachVirtualInner(desc);
1178 SDL_UnlockJoysticks();
1179 return result;
1180#else
1181 SDL_SetError("SDL not built with virtual-joystick support");
1182 return 0;
1183#endif
1184}
1185
1186bool SDL_DetachVirtualJoystick(SDL_JoystickID instance_id)
1187{
1188#ifdef SDL_JOYSTICK_VIRTUAL
1189 bool result;
1190
1191 SDL_LockJoysticks();
1192 result = SDL_JoystickDetachVirtualInner(instance_id);
1193 SDL_UnlockJoysticks();
1194 return result;
1195#else
1196 return SDL_SetError("SDL not built with virtual-joystick support");
1197#endif
1198}
1199
1200bool SDL_IsJoystickVirtual(SDL_JoystickID instance_id)
1201{
1202#ifdef SDL_JOYSTICK_VIRTUAL
1203 SDL_JoystickDriver *driver;
1204 int device_index;
1205 bool is_virtual = false;
1206
1207 SDL_LockJoysticks();
1208 if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
1209 if (driver == &SDL_VIRTUAL_JoystickDriver) {
1210 is_virtual = true;
1211 }
1212 }
1213 SDL_UnlockJoysticks();
1214
1215 return is_virtual;
1216#else
1217 return false;
1218#endif
1219}
1220
1221bool SDL_SetJoystickVirtualAxis(SDL_Joystick *joystick, int axis, Sint16 value)
1222{
1223 bool result;
1224
1225 SDL_LockJoysticks();
1226 {
1227 CHECK_JOYSTICK_MAGIC(joystick, false);
1228
1229#ifdef SDL_JOYSTICK_VIRTUAL
1230 result = SDL_SetJoystickVirtualAxisInner(joystick, axis, value);
1231#else
1232 result = SDL_SetError("SDL not built with virtual-joystick support");
1233#endif
1234 }
1235 SDL_UnlockJoysticks();
1236
1237 return result;
1238}
1239
1240bool SDL_SetJoystickVirtualBall(SDL_Joystick *joystick, int ball, Sint16 xrel, Sint16 yrel)
1241{
1242 bool result;
1243
1244 SDL_LockJoysticks();
1245 {
1246 CHECK_JOYSTICK_MAGIC(joystick, false);
1247
1248#ifdef SDL_JOYSTICK_VIRTUAL
1249 result = SDL_SetJoystickVirtualBallInner(joystick, ball, xrel, yrel);
1250#else
1251 result = SDL_SetError("SDL not built with virtual-joystick support");
1252#endif
1253 }
1254 SDL_UnlockJoysticks();
1255
1256 return result;
1257}
1258
1259bool SDL_SetJoystickVirtualButton(SDL_Joystick *joystick, int button, bool down)
1260{
1261 bool result;
1262
1263 SDL_LockJoysticks();
1264 {
1265 CHECK_JOYSTICK_MAGIC(joystick, false);
1266
1267#ifdef SDL_JOYSTICK_VIRTUAL
1268 result = SDL_SetJoystickVirtualButtonInner(joystick, button, down);
1269#else
1270 result = SDL_SetError("SDL not built with virtual-joystick support");
1271#endif
1272 }
1273 SDL_UnlockJoysticks();
1274
1275 return result;
1276}
1277
1278bool SDL_SetJoystickVirtualHat(SDL_Joystick *joystick, int hat, Uint8 value)
1279{
1280 bool result;
1281
1282 SDL_LockJoysticks();
1283 {
1284 CHECK_JOYSTICK_MAGIC(joystick, false);
1285
1286#ifdef SDL_JOYSTICK_VIRTUAL
1287 result = SDL_SetJoystickVirtualHatInner(joystick, hat, value);
1288#else
1289 result = SDL_SetError("SDL not built with virtual-joystick support");
1290#endif
1291 }
1292 SDL_UnlockJoysticks();
1293
1294 return result;
1295}
1296
1297bool SDL_SetJoystickVirtualTouchpad(SDL_Joystick *joystick, int touchpad, int finger, bool down, float x, float y, float pressure)
1298{
1299 bool result;
1300
1301 SDL_LockJoysticks();
1302 {
1303 CHECK_JOYSTICK_MAGIC(joystick, false);
1304
1305#ifdef SDL_JOYSTICK_VIRTUAL
1306 result = SDL_SetJoystickVirtualTouchpadInner(joystick, touchpad, finger, down, x, y, pressure);
1307#else
1308 result = SDL_SetError("SDL not built with virtual-joystick support");
1309#endif
1310 }
1311 SDL_UnlockJoysticks();
1312
1313 return result;
1314}
1315
1316bool SDL_SendJoystickVirtualSensorData(SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values)
1317{
1318 bool result;
1319
1320 SDL_LockJoysticks();
1321 {
1322 CHECK_JOYSTICK_MAGIC(joystick, false);
1323
1324#ifdef SDL_JOYSTICK_VIRTUAL
1325 result = SDL_SendJoystickVirtualSensorDataInner(joystick, type, sensor_timestamp, data, num_values);
1326#else
1327 result = SDL_SetError("SDL not built with virtual-joystick support");
1328#endif
1329 }
1330 SDL_UnlockJoysticks();
1331
1332 return result;
1333}
1334
1335/*
1336 * Checks to make sure the joystick is valid.
1337 */
1338bool SDL_IsJoystickValid(SDL_Joystick *joystick)
1339{
1340 SDL_AssertJoysticksLocked();
1341 return SDL_ObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK);
1342}
1343
1344bool SDL_PrivateJoystickGetAutoGamepadMapping(SDL_JoystickID instance_id, SDL_GamepadMapping *out)
1345{
1346 SDL_JoystickDriver *driver;
1347 int device_index;
1348 bool is_ok = false;
1349
1350 SDL_LockJoysticks();
1351 if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
1352 is_ok = driver->GetGamepadMapping(device_index, out);
1353 }
1354 SDL_UnlockJoysticks();
1355
1356 return is_ok;
1357}
1358
1359/*
1360 * Get the number of multi-dimensional axis controls on a joystick
1361 */
1362int SDL_GetNumJoystickAxes(SDL_Joystick *joystick)
1363{
1364 int result;
1365
1366 SDL_LockJoysticks();
1367 {
1368 CHECK_JOYSTICK_MAGIC(joystick, -1);
1369
1370 result = joystick->naxes;
1371 }
1372 SDL_UnlockJoysticks();
1373
1374 return result;
1375}
1376
1377/*
1378 * Get the number of hats on a joystick
1379 */
1380int SDL_GetNumJoystickHats(SDL_Joystick *joystick)
1381{
1382 int result;
1383
1384 SDL_LockJoysticks();
1385 {
1386 CHECK_JOYSTICK_MAGIC(joystick, -1);
1387
1388 result = joystick->nhats;
1389 }
1390 SDL_UnlockJoysticks();
1391
1392 return result;
1393}
1394
1395/*
1396 * Get the number of trackballs on a joystick
1397 */
1398int SDL_GetNumJoystickBalls(SDL_Joystick *joystick)
1399{
1400 CHECK_JOYSTICK_MAGIC(joystick, -1);
1401
1402 return joystick->nballs;
1403}
1404
1405/*
1406 * Get the number of buttons on a joystick
1407 */
1408int SDL_GetNumJoystickButtons(SDL_Joystick *joystick)
1409{
1410 int result;
1411
1412 SDL_LockJoysticks();
1413 {
1414 CHECK_JOYSTICK_MAGIC(joystick, -1);
1415
1416 result = joystick->nbuttons;
1417 }
1418 SDL_UnlockJoysticks();
1419
1420 return result;
1421}
1422
1423/*
1424 * Get the current state of an axis control on a joystick
1425 */
1426Sint16 SDL_GetJoystickAxis(SDL_Joystick *joystick, int axis)
1427{
1428 Sint16 state;
1429
1430 SDL_LockJoysticks();
1431 {
1432 CHECK_JOYSTICK_MAGIC(joystick, 0);
1433
1434 if (axis < joystick->naxes) {
1435 state = joystick->axes[axis].value;
1436 } else {
1437 SDL_SetError("Joystick only has %d axes", joystick->naxes);
1438 state = 0;
1439 }
1440 }
1441 SDL_UnlockJoysticks();
1442
1443 return state;
1444}
1445
1446/*
1447 * Get the initial state of an axis control on a joystick
1448 */
1449bool SDL_GetJoystickAxisInitialState(SDL_Joystick *joystick, int axis, Sint16 *state)
1450{
1451 bool result;
1452
1453 SDL_LockJoysticks();
1454 {
1455 CHECK_JOYSTICK_MAGIC(joystick, false);
1456
1457 if (axis >= joystick->naxes) {
1458 SDL_SetError("Joystick only has %d axes", joystick->naxes);
1459 result = false;
1460 } else {
1461 if (state) {
1462 *state = joystick->axes[axis].initial_value;
1463 }
1464 result = joystick->axes[axis].has_initial_value;
1465 }
1466 }
1467 SDL_UnlockJoysticks();
1468
1469 return result;
1470}
1471
1472/*
1473 * Get the current state of a hat on a joystick
1474 */
1475Uint8 SDL_GetJoystickHat(SDL_Joystick *joystick, int hat)
1476{
1477 Uint8 state;
1478
1479 SDL_LockJoysticks();
1480 {
1481 CHECK_JOYSTICK_MAGIC(joystick, 0);
1482
1483 if (hat < joystick->nhats) {
1484 state = joystick->hats[hat];
1485 } else {
1486 SDL_SetError("Joystick only has %d hats", joystick->nhats);
1487 state = 0;
1488 }
1489 }
1490 SDL_UnlockJoysticks();
1491
1492 return state;
1493}
1494
1495/*
1496 * Get the ball axis change since the last poll
1497 */
1498bool SDL_GetJoystickBall(SDL_Joystick *joystick, int ball, int *dx, int *dy)
1499{
1500 bool result;
1501
1502 SDL_LockJoysticks();
1503 {
1504 CHECK_JOYSTICK_MAGIC(joystick, false);
1505
1506 if (ball < joystick->nballs) {
1507 if (dx) {
1508 *dx = joystick->balls[ball].dx;
1509 }
1510 if (dy) {
1511 *dy = joystick->balls[ball].dy;
1512 }
1513 joystick->balls[ball].dx = 0;
1514 joystick->balls[ball].dy = 0;
1515 result = true;
1516 } else {
1517 result = SDL_SetError("Joystick only has %d balls", joystick->nballs);
1518 }
1519 }
1520 SDL_UnlockJoysticks();
1521
1522 return result;
1523}
1524
1525/*
1526 * Get the current state of a button on a joystick
1527 */
1528bool SDL_GetJoystickButton(SDL_Joystick *joystick, int button)
1529{
1530 bool down = false;
1531
1532 SDL_LockJoysticks();
1533 {
1534 CHECK_JOYSTICK_MAGIC(joystick, false);
1535
1536 if (button < joystick->nbuttons) {
1537 down = joystick->buttons[button];
1538 } else {
1539 SDL_SetError("Joystick only has %d buttons", joystick->nbuttons);
1540 }
1541 }
1542 SDL_UnlockJoysticks();
1543
1544 return down;
1545}
1546
1547/*
1548 * Return if the joystick in question is currently attached to the system,
1549 * \return false if not plugged in, true if still present.
1550 */
1551bool SDL_JoystickConnected(SDL_Joystick *joystick)
1552{
1553 bool result;
1554
1555 SDL_LockJoysticks();
1556 {
1557 CHECK_JOYSTICK_MAGIC(joystick, false);
1558
1559 result = joystick->attached;
1560 }
1561 SDL_UnlockJoysticks();
1562
1563 return result;
1564}
1565
1566/*
1567 * Get the instance id for this opened joystick
1568 */
1569SDL_JoystickID SDL_GetJoystickID(SDL_Joystick *joystick)
1570{
1571 SDL_JoystickID result;
1572
1573 SDL_LockJoysticks();
1574 {
1575 CHECK_JOYSTICK_MAGIC(joystick, 0);
1576
1577 result = joystick->instance_id;
1578 }
1579 SDL_UnlockJoysticks();
1580
1581 return result;
1582}
1583
1584/*
1585 * Return the SDL_Joystick associated with an instance id.
1586 */
1587SDL_Joystick *SDL_GetJoystickFromID(SDL_JoystickID instance_id)
1588{
1589 SDL_Joystick *joystick;
1590
1591 SDL_LockJoysticks();
1592 for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
1593 if (joystick->instance_id == instance_id) {
1594 break;
1595 }
1596 }
1597 SDL_UnlockJoysticks();
1598 return joystick;
1599}
1600
1601/**
1602 * Return the SDL_Joystick associated with a player index.
1603 */
1604SDL_Joystick *SDL_GetJoystickFromPlayerIndex(int player_index)
1605{
1606 SDL_JoystickID instance_id;
1607 SDL_Joystick *joystick;
1608
1609 SDL_LockJoysticks();
1610 instance_id = SDL_GetJoystickIDForPlayerIndex(player_index);
1611 for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
1612 if (joystick->instance_id == instance_id) {
1613 break;
1614 }
1615 }
1616 SDL_UnlockJoysticks();
1617 return joystick;
1618}
1619
1620/*
1621 * Get the properties associated with a joystick
1622 */
1623SDL_PropertiesID SDL_GetJoystickProperties(SDL_Joystick *joystick)
1624{
1625 SDL_PropertiesID result;
1626
1627 SDL_LockJoysticks();
1628 {
1629 CHECK_JOYSTICK_MAGIC(joystick, 0);
1630
1631 if (joystick->props == 0) {
1632 joystick->props = SDL_CreateProperties();
1633 }
1634 result = joystick->props;
1635 }
1636 SDL_UnlockJoysticks();
1637
1638 return result;
1639}
1640
1641/*
1642 * Get the friendly name of this joystick
1643 */
1644const char *SDL_GetJoystickName(SDL_Joystick *joystick)
1645{
1646 const char *result;
1647 const SDL_SteamVirtualGamepadInfo *info;
1648
1649 SDL_LockJoysticks();
1650 {
1651 CHECK_JOYSTICK_MAGIC(joystick, NULL);
1652
1653 info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);
1654 if (info) {
1655 result = SDL_GetPersistentString(info->name);
1656 } else {
1657 result = SDL_GetPersistentString(joystick->name);
1658 }
1659 }
1660 SDL_UnlockJoysticks();
1661
1662 return result;
1663}
1664
1665/*
1666 * Get the implementation dependent path of this joystick
1667 */
1668const char *SDL_GetJoystickPath(SDL_Joystick *joystick)
1669{
1670 const char *result;
1671
1672 SDL_LockJoysticks();
1673 {
1674 CHECK_JOYSTICK_MAGIC(joystick, NULL);
1675
1676 if (joystick->path) {
1677 result = SDL_GetPersistentString(joystick->path);
1678 } else {
1679 SDL_Unsupported();
1680 result = NULL;
1681 }
1682 }
1683 SDL_UnlockJoysticks();
1684
1685 return result;
1686}
1687
1688/**
1689 * Get the player index of an opened joystick, or -1 if it's not available
1690 */
1691int SDL_GetJoystickPlayerIndex(SDL_Joystick *joystick)
1692{
1693 int result;
1694
1695 SDL_LockJoysticks();
1696 {
1697 CHECK_JOYSTICK_MAGIC(joystick, -1);
1698
1699 result = SDL_GetPlayerIndexForJoystickID(joystick->instance_id);
1700 }
1701 SDL_UnlockJoysticks();
1702
1703 return result;
1704}
1705
1706/**
1707 * Set the player index of an opened joystick
1708 */
1709bool SDL_SetJoystickPlayerIndex(SDL_Joystick *joystick, int player_index)
1710{
1711 bool result;
1712
1713 SDL_LockJoysticks();
1714 {
1715 CHECK_JOYSTICK_MAGIC(joystick, false);
1716
1717 result = SDL_SetJoystickIDForPlayerIndex(player_index, joystick->instance_id);
1718 }
1719 SDL_UnlockJoysticks();
1720
1721 return result;
1722}
1723
1724bool SDL_RumbleJoystick(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
1725{
1726 bool result;
1727
1728 SDL_LockJoysticks();
1729 {
1730 CHECK_JOYSTICK_MAGIC(joystick, false);
1731
1732 if (low_frequency_rumble == joystick->low_frequency_rumble &&
1733 high_frequency_rumble == joystick->high_frequency_rumble) {
1734 // Just update the expiration
1735 result = true;
1736 } else {
1737 result = joystick->driver->Rumble(joystick, low_frequency_rumble, high_frequency_rumble);
1738 if (result) {
1739 joystick->rumble_resend = SDL_GetTicks() + SDL_RUMBLE_RESEND_MS;
1740 if (joystick->rumble_resend == 0) {
1741 joystick->rumble_resend = 1;
1742 }
1743 } else {
1744 joystick->rumble_resend = 0;
1745 }
1746 }
1747
1748 if (result) {
1749 joystick->low_frequency_rumble = low_frequency_rumble;
1750 joystick->high_frequency_rumble = high_frequency_rumble;
1751
1752 if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
1753 joystick->rumble_expiration = SDL_GetTicks() + SDL_min(duration_ms, SDL_MAX_RUMBLE_DURATION_MS);
1754 if (!joystick->rumble_expiration) {
1755 joystick->rumble_expiration = 1;
1756 }
1757 } else {
1758 joystick->rumble_expiration = 0;
1759 joystick->rumble_resend = 0;
1760 }
1761 }
1762 }
1763 SDL_UnlockJoysticks();
1764
1765 return result;
1766}
1767
1768bool SDL_RumbleJoystickTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms)
1769{
1770 bool result;
1771
1772 SDL_LockJoysticks();
1773 {
1774 CHECK_JOYSTICK_MAGIC(joystick, false);
1775
1776 if (left_rumble == joystick->left_trigger_rumble && right_rumble == joystick->right_trigger_rumble) {
1777 // Just update the expiration
1778 result = true;
1779 } else {
1780 result = joystick->driver->RumbleTriggers(joystick, left_rumble, right_rumble);
1781 }
1782
1783 if (result) {
1784 joystick->left_trigger_rumble = left_rumble;
1785 joystick->right_trigger_rumble = right_rumble;
1786
1787 if ((left_rumble || right_rumble) && duration_ms) {
1788 joystick->trigger_rumble_expiration = SDL_GetTicks() + SDL_min(duration_ms, SDL_MAX_RUMBLE_DURATION_MS);
1789 } else {
1790 joystick->trigger_rumble_expiration = 0;
1791 }
1792 }
1793 }
1794 SDL_UnlockJoysticks();
1795
1796 return result;
1797}
1798
1799bool SDL_SetJoystickLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
1800{
1801 bool result;
1802 bool isfreshvalue;
1803
1804 SDL_LockJoysticks();
1805 {
1806 CHECK_JOYSTICK_MAGIC(joystick, false);
1807
1808 isfreshvalue = red != joystick->led_red ||
1809 green != joystick->led_green ||
1810 blue != joystick->led_blue;
1811
1812 if (isfreshvalue || SDL_GetTicks() >= joystick->led_expiration) {
1813 result = joystick->driver->SetLED(joystick, red, green, blue);
1814 joystick->led_expiration = SDL_GetTicks() + SDL_LED_MIN_REPEAT_MS;
1815 } else {
1816 // Avoid spamming the driver
1817 result = true;
1818 }
1819
1820 // Save the LED value regardless of success, so we don't spam the driver
1821 joystick->led_red = red;
1822 joystick->led_green = green;
1823 joystick->led_blue = blue;
1824 }
1825 SDL_UnlockJoysticks();
1826
1827 return result;
1828}
1829
1830bool SDL_SendJoystickEffect(SDL_Joystick *joystick, const void *data, int size)
1831{
1832 bool result;
1833
1834 SDL_LockJoysticks();
1835 {
1836 CHECK_JOYSTICK_MAGIC(joystick, false);
1837
1838 result = joystick->driver->SendEffect(joystick, data, size);
1839 }
1840 SDL_UnlockJoysticks();
1841
1842 return result;
1843}
1844
1845/*
1846 * Close a joystick previously opened with SDL_OpenJoystick()
1847 */
1848void SDL_CloseJoystick(SDL_Joystick *joystick)
1849{
1850 SDL_Joystick *joysticklist;
1851 SDL_Joystick *joysticklistprev;
1852 int i;
1853
1854 SDL_LockJoysticks();
1855 {
1856 CHECK_JOYSTICK_MAGIC(joystick,);
1857
1858 // First decrement ref count
1859 if (--joystick->ref_count > 0) {
1860 SDL_UnlockJoysticks();
1861 return;
1862 }
1863
1864 SDL_DestroyProperties(joystick->props);
1865
1866 if (joystick->rumble_expiration) {
1867 SDL_RumbleJoystick(joystick, 0, 0, 0);
1868 }
1869 if (joystick->trigger_rumble_expiration) {
1870 SDL_RumbleJoystickTriggers(joystick, 0, 0, 0);
1871 }
1872
1873 CleanupSensorFusion(joystick);
1874
1875 joystick->driver->Close(joystick);
1876 joystick->hwdata = NULL;
1877 SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, false);
1878
1879 joysticklist = SDL_joysticks;
1880 joysticklistprev = NULL;
1881 while (joysticklist) {
1882 if (joystick == joysticklist) {
1883 if (joysticklistprev) {
1884 // unlink this entry
1885 joysticklistprev->next = joysticklist->next;
1886 } else {
1887 SDL_joysticks = joystick->next;
1888 }
1889 break;
1890 }
1891 joysticklistprev = joysticklist;
1892 joysticklist = joysticklist->next;
1893 }
1894
1895 // Free the data associated with this joystick
1896 SDL_free(joystick->name);
1897 SDL_free(joystick->path);
1898 SDL_free(joystick->serial);
1899 SDL_free(joystick->axes);
1900 SDL_free(joystick->balls);
1901 SDL_free(joystick->hats);
1902 SDL_free(joystick->buttons);
1903 for (i = 0; i < joystick->ntouchpads; i++) {
1904 SDL_JoystickTouchpadInfo *touchpad = &joystick->touchpads[i];
1905 SDL_free(touchpad->fingers);
1906 }
1907 SDL_free(joystick->touchpads);
1908 SDL_free(joystick->sensors);
1909 SDL_free(joystick);
1910 }
1911 SDL_UnlockJoysticks();
1912}
1913
1914void SDL_QuitJoysticks(void)
1915{
1916 int i;
1917 SDL_JoystickID *joysticks;
1918
1919 SDL_LockJoysticks();
1920
1921 SDL_joysticks_quitting = true;
1922
1923 joysticks = SDL_GetJoysticks(NULL);
1924 if (joysticks) {
1925 for (i = 0; joysticks[i]; ++i) {
1926 SDL_PrivateJoystickRemoved(joysticks[i]);
1927 }
1928 SDL_free(joysticks);
1929 }
1930
1931 while (SDL_joysticks) {
1932 SDL_joysticks->ref_count = 1;
1933 SDL_CloseJoystick(SDL_joysticks);
1934 }
1935
1936 // Quit drivers in reverse order to avoid breaking dependencies between drivers
1937 for (i = SDL_arraysize(SDL_joystick_drivers) - 1; i >= 0; --i) {
1938 SDL_joystick_drivers[i]->Quit();
1939 }
1940
1941 if (SDL_joystick_players) {
1942 SDL_free(SDL_joystick_players);
1943 SDL_joystick_players = NULL;
1944 SDL_joystick_player_count = 0;
1945 }
1946
1947 SDL_QuitSubSystem(SDL_INIT_EVENTS);
1948
1949 SDL_QuitSteamVirtualGamepadInfo();
1950
1951 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
1952 SDL_JoystickAllowBackgroundEventsChanged, NULL);
1953
1954 SDL_FreeVIDPIDList(&arcadestick_devices);
1955 SDL_FreeVIDPIDList(&blacklist_devices);
1956 SDL_FreeVIDPIDList(&flightstick_devices);
1957 SDL_FreeVIDPIDList(&gamecube_devices);
1958 SDL_FreeVIDPIDList(&rog_gamepad_mice);
1959 SDL_FreeVIDPIDList(&throttle_devices);
1960 SDL_FreeVIDPIDList(&wheel_devices);
1961 SDL_FreeVIDPIDList(&zero_centered_devices);
1962
1963 SDL_QuitGamepadMappings();
1964
1965 SDL_joysticks_quitting = false;
1966 SDL_joysticks_initialized = false;
1967
1968 SDL_UnlockJoysticks();
1969}
1970
1971static bool SDL_PrivateJoystickShouldIgnoreEvent(void)
1972{
1973 if (SDL_joystick_allows_background_events) {
1974 return false;
1975 }
1976
1977 if (SDL_HasWindows() && SDL_GetKeyboardFocus() == NULL) {
1978 // We have windows but we don't have focus, ignore the event.
1979 return true;
1980 }
1981 return false;
1982}
1983
1984// These are global for SDL_sysjoystick.c and SDL_events.c
1985
1986void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers)
1987{
1988 int ntouchpads;
1989 SDL_JoystickTouchpadInfo *touchpads;
1990
1991 SDL_AssertJoysticksLocked();
1992
1993 ntouchpads = joystick->ntouchpads + 1;
1994 touchpads = (SDL_JoystickTouchpadInfo *)SDL_realloc(joystick->touchpads, (ntouchpads * sizeof(SDL_JoystickTouchpadInfo)));
1995 if (touchpads) {
1996 SDL_JoystickTouchpadInfo *touchpad = &touchpads[ntouchpads - 1];
1997 SDL_JoystickTouchpadFingerInfo *fingers = (SDL_JoystickTouchpadFingerInfo *)SDL_calloc(nfingers, sizeof(SDL_JoystickTouchpadFingerInfo));
1998
1999 if (fingers) {
2000 touchpad->nfingers = nfingers;
2001 touchpad->fingers = fingers;
2002 } else {
2003 // Out of memory, this touchpad won't be active
2004 touchpad->nfingers = 0;
2005 touchpad->fingers = NULL;
2006 }
2007
2008 joystick->ntouchpads = ntouchpads;
2009 joystick->touchpads = touchpads;
2010 }
2011}
2012
2013void SDL_PrivateJoystickAddSensor(SDL_Joystick *joystick, SDL_SensorType type, float rate)
2014{
2015 int nsensors;
2016 SDL_JoystickSensorInfo *sensors;
2017
2018 SDL_AssertJoysticksLocked();
2019
2020 nsensors = joystick->nsensors + 1;
2021 sensors = (SDL_JoystickSensorInfo *)SDL_realloc(joystick->sensors, (nsensors * sizeof(SDL_JoystickSensorInfo)));
2022 if (sensors) {
2023 SDL_JoystickSensorInfo *sensor = &sensors[nsensors - 1];
2024
2025 SDL_zerop(sensor);
2026 sensor->type = type;
2027 sensor->rate = rate;
2028
2029 joystick->nsensors = nsensors;
2030 joystick->sensors = sensors;
2031 }
2032}
2033
2034void SDL_PrivateJoystickSensorRate(SDL_Joystick *joystick, SDL_SensorType type, float rate)
2035{
2036 int i;
2037 SDL_AssertJoysticksLocked();
2038
2039 for (i = 0; i < joystick->nsensors; ++i) {
2040 if (joystick->sensors[i].type == type) {
2041 joystick->sensors[i].rate = rate;
2042 }
2043 }
2044}
2045
2046void SDL_PrivateJoystickAdded(SDL_JoystickID instance_id)
2047{
2048 SDL_JoystickDriver *driver;
2049 int device_index;
2050 int player_index = -1;
2051
2052 SDL_AssertJoysticksLocked();
2053
2054 if (SDL_JoysticksQuitting()) {
2055 return;
2056 }
2057
2058 SDL_joystick_being_added = true;
2059
2060 if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
2061 player_index = driver->GetDeviceSteamVirtualGamepadSlot(device_index);
2062 if (player_index < 0) {
2063 player_index = driver->GetDevicePlayerIndex(device_index);
2064 }
2065 }
2066 if (player_index < 0 && SDL_IsGamepad(instance_id)) {
2067 player_index = SDL_FindFreePlayerIndex();
2068 }
2069 if (player_index >= 0) {
2070 SDL_SetJoystickIDForPlayerIndex(player_index, instance_id);
2071 }
2072
2073 {
2074 SDL_Event event;
2075
2076 event.type = SDL_EVENT_JOYSTICK_ADDED;
2077 event.common.timestamp = 0;
2078
2079 if (SDL_EventEnabled(event.type)) {
2080 event.jdevice.which = instance_id;
2081 SDL_PushEvent(&event);
2082 }
2083 }
2084
2085 SDL_joystick_being_added = false;
2086
2087 if (SDL_IsGamepad(instance_id)) {
2088 SDL_PrivateGamepadAdded(instance_id);
2089 }
2090}
2091
2092bool SDL_IsJoystickBeingAdded(void)
2093{
2094 return SDL_joystick_being_added;
2095}
2096
2097void SDL_PrivateJoystickForceRecentering(SDL_Joystick *joystick)
2098{
2099 Uint8 i, j;
2100 Uint64 timestamp = SDL_GetTicksNS();
2101
2102 SDL_AssertJoysticksLocked();
2103
2104 // Tell the app that everything is centered/unpressed...
2105 for (i = 0; i < joystick->naxes; i++) {
2106 if (joystick->axes[i].has_initial_value) {
2107 SDL_SendJoystickAxis(timestamp, joystick, i, joystick->axes[i].zero);
2108 }
2109 }
2110
2111 for (i = 0; i < joystick->nbuttons; i++) {
2112 SDL_SendJoystickButton(timestamp, joystick, i, false);
2113 }
2114
2115 for (i = 0; i < joystick->nhats; i++) {
2116 SDL_SendJoystickHat(timestamp, joystick, i, SDL_HAT_CENTERED);
2117 }
2118
2119 for (i = 0; i < joystick->ntouchpads; i++) {
2120 SDL_JoystickTouchpadInfo *touchpad = &joystick->touchpads[i];
2121
2122 for (j = 0; j < touchpad->nfingers; ++j) {
2123 SDL_SendJoystickTouchpad(timestamp, joystick, i, j, false, 0.0f, 0.0f, 0.0f);
2124 }
2125 }
2126}
2127
2128void SDL_PrivateJoystickRemoved(SDL_JoystickID instance_id)
2129{
2130 SDL_Joystick *joystick = NULL;
2131 int player_index;
2132 SDL_Event event;
2133
2134 SDL_AssertJoysticksLocked();
2135
2136 // Find this joystick...
2137 for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
2138 if (joystick->instance_id == instance_id) {
2139 SDL_PrivateJoystickForceRecentering(joystick);
2140 joystick->attached = false;
2141 break;
2142 }
2143 }
2144
2145 if (SDL_IsGamepad(instance_id)) {
2146 SDL_PrivateGamepadRemoved(instance_id);
2147 }
2148
2149 event.type = SDL_EVENT_JOYSTICK_REMOVED;
2150 event.common.timestamp = 0;
2151
2152 if (SDL_EventEnabled(event.type)) {
2153 event.jdevice.which = instance_id;
2154 SDL_PushEvent(&event);
2155 }
2156
2157 player_index = SDL_GetPlayerIndexForJoystickID(instance_id);
2158 if (player_index >= 0) {
2159 SDL_joystick_players[player_index] = 0;
2160 }
2161}
2162
2163void SDL_SendJoystickAxis(Uint64 timestamp, SDL_Joystick *joystick, Uint8 axis, Sint16 value)
2164{
2165 SDL_JoystickAxisInfo *info;
2166
2167 SDL_AssertJoysticksLocked();
2168
2169 // Make sure we're not getting garbage or duplicate events
2170 if (axis >= joystick->naxes) {
2171 return;
2172 }
2173
2174 info = &joystick->axes[axis];
2175 if (!info->has_initial_value ||
2176 (!info->has_second_value && (info->initial_value <= -32767 || info->initial_value == 32767) && SDL_abs(value) < (SDL_JOYSTICK_AXIS_MAX / 4))) {
2177 info->initial_value = value;
2178 info->value = value;
2179 info->zero = value;
2180 info->has_initial_value = true;
2181 } else if (value == info->value && !info->sending_initial_value) {
2182 return;
2183 } else {
2184 info->has_second_value = true;
2185 }
2186 if (!info->sent_initial_value) {
2187 // Make sure we don't send motion until there's real activity on this axis
2188 const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; // ShanWan PS3 controller needed 96
2189 if (SDL_abs(value - info->value) <= MAX_ALLOWED_JITTER &&
2190 !SDL_IsJoystickVIRTUAL(joystick->guid)) {
2191 return;
2192 }
2193 info->sent_initial_value = true;
2194 info->sending_initial_value = true;
2195 SDL_SendJoystickAxis(timestamp, joystick, axis, info->initial_value);
2196 info->sending_initial_value = false;
2197 }
2198
2199 /* We ignore events if we don't have keyboard focus, except for centering
2200 * events.
2201 */
2202 if (SDL_PrivateJoystickShouldIgnoreEvent()) {
2203 if (info->sending_initial_value ||
2204 (value > info->zero && value >= info->value) ||
2205 (value < info->zero && value <= info->value)) {
2206 return;
2207 }
2208 }
2209
2210 // Update internal joystick state
2211 SDL_assert(timestamp != 0);
2212 info->value = value;
2213 joystick->update_complete = timestamp;
2214
2215 // Post the event, if desired
2216 if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_AXIS_MOTION)) {
2217 SDL_Event event;
2218 event.type = SDL_EVENT_JOYSTICK_AXIS_MOTION;
2219 event.common.timestamp = timestamp;
2220 event.jaxis.which = joystick->instance_id;
2221 event.jaxis.axis = axis;
2222 event.jaxis.value = value;
2223 SDL_PushEvent(&event);
2224 }
2225}
2226
2227void SDL_SendJoystickBall(Uint64 timestamp, SDL_Joystick *joystick, Uint8 ball, Sint16 xrel, Sint16 yrel)
2228{
2229 SDL_AssertJoysticksLocked();
2230
2231 // Make sure we're not getting garbage events
2232 if (ball >= joystick->nballs) {
2233 return;
2234 }
2235
2236 // We ignore events if we don't have keyboard focus.
2237 if (SDL_PrivateJoystickShouldIgnoreEvent()) {
2238 return;
2239 }
2240
2241 // Update internal mouse state
2242 joystick->balls[ball].dx += xrel;
2243 joystick->balls[ball].dy += yrel;
2244
2245 // Post the event, if desired
2246 if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_BALL_MOTION)) {
2247 SDL_Event event;
2248 event.type = SDL_EVENT_JOYSTICK_BALL_MOTION;
2249 event.common.timestamp = timestamp;
2250 event.jball.which = joystick->instance_id;
2251 event.jball.ball = ball;
2252 event.jball.xrel = xrel;
2253 event.jball.yrel = yrel;
2254 SDL_PushEvent(&event);
2255 }
2256}
2257
2258void SDL_SendJoystickHat(Uint64 timestamp, SDL_Joystick *joystick, Uint8 hat, Uint8 value)
2259{
2260 SDL_AssertJoysticksLocked();
2261
2262 // Make sure we're not getting garbage or duplicate events
2263 if (hat >= joystick->nhats) {
2264 return;
2265 }
2266 if (value == joystick->hats[hat]) {
2267 return;
2268 }
2269
2270 /* We ignore events if we don't have keyboard focus, except for centering
2271 * events.
2272 */
2273 if (SDL_PrivateJoystickShouldIgnoreEvent()) {
2274 if (value != SDL_HAT_CENTERED) {
2275 return;
2276 }
2277 }
2278
2279 // Update internal joystick state
2280 SDL_assert(timestamp != 0);
2281 joystick->hats[hat] = value;
2282 joystick->update_complete = timestamp;
2283
2284 // Post the event, if desired
2285 if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_HAT_MOTION)) {
2286 SDL_Event event;
2287 event.type = SDL_EVENT_JOYSTICK_HAT_MOTION;
2288 event.common.timestamp = timestamp;
2289 event.jhat.which = joystick->instance_id;
2290 event.jhat.hat = hat;
2291 event.jhat.value = value;
2292 SDL_PushEvent(&event);
2293 }
2294}
2295
2296void SDL_SendJoystickButton(Uint64 timestamp, SDL_Joystick *joystick, Uint8 button, bool down)
2297{
2298 SDL_Event event;
2299
2300 SDL_AssertJoysticksLocked();
2301
2302 if (down) {
2303 event.type = SDL_EVENT_JOYSTICK_BUTTON_DOWN;
2304 } else {
2305 event.type = SDL_EVENT_JOYSTICK_BUTTON_UP;
2306 }
2307
2308 // Make sure we're not getting garbage or duplicate events
2309 if (button >= joystick->nbuttons) {
2310 return;
2311 }
2312 if (down == joystick->buttons[button]) {
2313 return;
2314 }
2315
2316 /* We ignore events if we don't have keyboard focus, except for button
2317 * release. */
2318 if (SDL_PrivateJoystickShouldIgnoreEvent()) {
2319 if (down) {
2320 return;
2321 }
2322 }
2323
2324 // Update internal joystick state
2325 SDL_assert(timestamp != 0);
2326 joystick->buttons[button] = down;
2327 joystick->update_complete = timestamp;
2328
2329 // Post the event, if desired
2330 if (SDL_EventEnabled(event.type)) {
2331 event.common.timestamp = timestamp;
2332 event.jbutton.which = joystick->instance_id;
2333 event.jbutton.button = button;
2334 event.jbutton.down = down;
2335 SDL_PushEvent(&event);
2336 }
2337}
2338
2339static void SendSteamHandleUpdateEvents(void)
2340{
2341 SDL_Joystick *joystick;
2342 const SDL_SteamVirtualGamepadInfo *info;
2343
2344 // Check to see if any Steam handles changed
2345 for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
2346 bool changed = false;
2347
2348 if (!SDL_IsGamepad(joystick->instance_id)) {
2349 continue;
2350 }
2351
2352 info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);
2353 if (info) {
2354 if (joystick->steam_handle != info->handle) {
2355 joystick->steam_handle = info->handle;
2356 changed = true;
2357 }
2358 } else {
2359 if (joystick->steam_handle != 0) {
2360 joystick->steam_handle = 0;
2361 changed = true;
2362 }
2363 }
2364 if (changed) {
2365 SDL_Event event;
2366
2367 SDL_zero(event);
2368 event.type = SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED;
2369 event.common.timestamp = 0;
2370 event.gdevice.which = joystick->instance_id;
2371 SDL_PushEvent(&event);
2372 }
2373 }
2374}
2375
2376void SDL_UpdateJoysticks(void)
2377{
2378 int i;
2379 Uint64 now;
2380 SDL_Joystick *joystick;
2381
2382 if (!SDL_WasInit(SDL_INIT_JOYSTICK)) {
2383 return;
2384 }
2385
2386 SDL_LockJoysticks();
2387
2388 if (SDL_UpdateSteamVirtualGamepadInfo()) {
2389 SendSteamHandleUpdateEvents();
2390 }
2391
2392#ifdef SDL_JOYSTICK_HIDAPI
2393 // Special function for HIDAPI devices, as a single device can provide multiple SDL_Joysticks
2394 HIDAPI_UpdateDevices();
2395#endif // SDL_JOYSTICK_HIDAPI
2396
2397 for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
2398 if (!joystick->attached) {
2399 continue;
2400 }
2401
2402 joystick->driver->Update(joystick);
2403
2404 if (joystick->delayed_guide_button) {
2405 SDL_GamepadHandleDelayedGuideButton(joystick);
2406 }
2407
2408 now = SDL_GetTicks();
2409 if (joystick->rumble_expiration && now >= joystick->rumble_expiration) {
2410 SDL_RumbleJoystick(joystick, 0, 0, 0);
2411 joystick->rumble_resend = 0;
2412 }
2413
2414 if (joystick->rumble_resend && now >= joystick->rumble_resend) {
2415 joystick->driver->Rumble(joystick, joystick->low_frequency_rumble, joystick->high_frequency_rumble);
2416 joystick->rumble_resend = now + SDL_RUMBLE_RESEND_MS;
2417 if (joystick->rumble_resend == 0) {
2418 joystick->rumble_resend = 1;
2419 }
2420 }
2421
2422 if (joystick->trigger_rumble_expiration && now >= joystick->trigger_rumble_expiration) {
2423 SDL_RumbleJoystickTriggers(joystick, 0, 0, 0);
2424 }
2425 }
2426
2427 if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_UPDATE_COMPLETE)) {
2428 for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
2429 if (joystick->update_complete) {
2430 SDL_Event event;
2431
2432 event.type = SDL_EVENT_JOYSTICK_UPDATE_COMPLETE;
2433 event.common.timestamp = joystick->update_complete;
2434 event.jdevice.which = joystick->instance_id;
2435 SDL_PushEvent(&event);
2436
2437 joystick->update_complete = 0;
2438 }
2439 }
2440 }
2441
2442 /* this needs to happen AFTER walking the joystick list above, so that any
2443 dangling hardware data from removed devices can be free'd
2444 */
2445 for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
2446 SDL_joystick_drivers[i]->Detect();
2447 }
2448
2449 SDL_UnlockJoysticks();
2450}
2451
2452static const Uint32 SDL_joystick_event_list[] = {
2453 SDL_EVENT_JOYSTICK_AXIS_MOTION,
2454 SDL_EVENT_JOYSTICK_BALL_MOTION,
2455 SDL_EVENT_JOYSTICK_HAT_MOTION,
2456 SDL_EVENT_JOYSTICK_BUTTON_DOWN,
2457 SDL_EVENT_JOYSTICK_BUTTON_UP,
2458 SDL_EVENT_JOYSTICK_ADDED,
2459 SDL_EVENT_JOYSTICK_REMOVED,
2460 SDL_EVENT_JOYSTICK_BATTERY_UPDATED
2461};
2462
2463void SDL_SetJoystickEventsEnabled(bool enabled)
2464{
2465 unsigned int i;
2466
2467 for (i = 0; i < SDL_arraysize(SDL_joystick_event_list); ++i) {
2468 SDL_SetEventEnabled(SDL_joystick_event_list[i], enabled);
2469 }
2470}
2471
2472bool SDL_JoystickEventsEnabled(void)
2473{
2474 bool enabled = false;
2475 unsigned int i;
2476
2477 for (i = 0; i < SDL_arraysize(SDL_joystick_event_list); ++i) {
2478 enabled = SDL_EventEnabled(SDL_joystick_event_list[i]);
2479 if (enabled) {
2480 break;
2481 }
2482 }
2483 return enabled;
2484}
2485
2486void SDL_GetJoystickGUIDInfo(SDL_GUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version, Uint16 *crc16)
2487{
2488 Uint16 *guid16 = (Uint16 *)guid.data;
2489 Uint16 bus = SDL_Swap16LE(guid16[0]);
2490
2491 if ((bus < ' ' || bus == SDL_HARDWARE_BUS_VIRTUAL) && guid16[3] == 0x0000 && guid16[5] == 0x0000) {
2492 /* This GUID fits the standard form:
2493 * 16-bit bus
2494 * 16-bit CRC16 of the joystick name (can be zero)
2495 * 16-bit vendor ID
2496 * 16-bit zero
2497 * 16-bit product ID
2498 * 16-bit zero
2499 * 16-bit version
2500 * 8-bit driver identifier ('h' for HIDAPI, 'x' for XInput, etc.)
2501 * 8-bit driver-dependent type info
2502 */
2503 if (vendor) {
2504 *vendor = SDL_Swap16LE(guid16[2]);
2505 }
2506 if (product) {
2507 *product = SDL_Swap16LE(guid16[4]);
2508 }
2509 if (version) {
2510 *version = SDL_Swap16LE(guid16[6]);
2511 }
2512 if (crc16) {
2513 *crc16 = SDL_Swap16LE(guid16[1]);
2514 }
2515 } else if (bus < ' ' || bus == SDL_HARDWARE_BUS_VIRTUAL) {
2516 /* This GUID fits the unknown VID/PID form:
2517 * 16-bit bus
2518 * 16-bit CRC16 of the joystick name (can be zero)
2519 * 11 characters of the joystick name, null terminated
2520 */
2521 if (vendor) {
2522 *vendor = 0;
2523 }
2524 if (product) {
2525 *product = 0;
2526 }
2527 if (version) {
2528 *version = 0;
2529 }
2530 if (crc16) {
2531 *crc16 = SDL_Swap16LE(guid16[1]);
2532 }
2533 } else {
2534 if (vendor) {
2535 *vendor = 0;
2536 }
2537 if (product) {
2538 *product = 0;
2539 }
2540 if (version) {
2541 *version = 0;
2542 }
2543 if (crc16) {
2544 *crc16 = 0;
2545 }
2546 }
2547}
2548
2549char *SDL_CreateJoystickName(Uint16 vendor, Uint16 product, const char *vendor_name, const char *product_name)
2550{
2551 const char *custom_name = GuessControllerName(vendor, product);
2552 if (custom_name) {
2553 return SDL_strdup(custom_name);
2554 }
2555
2556 return SDL_CreateDeviceName(vendor, product, vendor_name, product_name, "Controller");
2557}
2558
2559SDL_GUID SDL_CreateJoystickGUID(Uint16 bus, Uint16 vendor, Uint16 product, Uint16 version, const char *vendor_name, const char *product_name, Uint8 driver_signature, Uint8 driver_data)
2560{
2561 SDL_GUID guid;
2562 Uint16 *guid16 = (Uint16 *)guid.data;
2563 Uint16 crc = 0;
2564
2565 SDL_zero(guid);
2566
2567 if (vendor_name && *vendor_name && product_name && *product_name) {
2568 crc = SDL_crc16(crc, vendor_name, SDL_strlen(vendor_name));
2569 crc = SDL_crc16(crc, " ", 1);
2570 crc = SDL_crc16(crc, product_name, SDL_strlen(product_name));
2571 } else if (product_name) {
2572 crc = SDL_crc16(crc, product_name, SDL_strlen(product_name));
2573 }
2574
2575 // We only need 16 bits for each of these; space them out to fill 128.
2576 // Byteswap so devices get same GUID on little/big endian platforms.
2577 *guid16++ = SDL_Swap16LE(bus);
2578 *guid16++ = SDL_Swap16LE(crc);
2579
2580 if (vendor) {
2581 *guid16++ = SDL_Swap16LE(vendor);
2582 *guid16++ = 0;
2583 *guid16++ = SDL_Swap16LE(product);
2584 *guid16++ = 0;
2585 *guid16++ = SDL_Swap16LE(version);
2586 guid.data[14] = driver_signature;
2587 guid.data[15] = driver_data;
2588 } else {
2589 size_t available_space = sizeof(guid.data) - 4;
2590
2591 if (driver_signature) {
2592 available_space -= 2;
2593 guid.data[14] = driver_signature;
2594 guid.data[15] = driver_data;
2595 }
2596 if (product_name) {
2597 SDL_strlcpy((char *)guid16, product_name, available_space);
2598 }
2599 }
2600 return guid;
2601}
2602
2603SDL_GUID SDL_CreateJoystickGUIDForName(const char *name)
2604{
2605 return SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_UNKNOWN, 0, 0, 0, NULL, name, 0, 0);
2606}
2607
2608void SDL_SetJoystickGUIDVendor(SDL_GUID *guid, Uint16 vendor)
2609{
2610 Uint16 *guid16 = (Uint16 *)guid->data;
2611
2612 guid16[2] = SDL_Swap16LE(vendor);
2613}
2614
2615void SDL_SetJoystickGUIDProduct(SDL_GUID *guid, Uint16 product)
2616{
2617 Uint16 *guid16 = (Uint16 *)guid->data;
2618
2619 guid16[4] = SDL_Swap16LE(product);
2620}
2621
2622void SDL_SetJoystickGUIDVersion(SDL_GUID *guid, Uint16 version)
2623{
2624 Uint16 *guid16 = (Uint16 *)guid->data;
2625
2626 guid16[6] = SDL_Swap16LE(version);
2627}
2628
2629void SDL_SetJoystickGUIDCRC(SDL_GUID *guid, Uint16 crc)
2630{
2631 Uint16 *guid16 = (Uint16 *)guid->data;
2632
2633 guid16[1] = SDL_Swap16LE(crc);
2634}
2635
2636SDL_GamepadType SDL_GetGamepadTypeFromVIDPID(Uint16 vendor, Uint16 product, const char *name, bool forUI)
2637{
2638 SDL_GamepadType type = SDL_GAMEPAD_TYPE_STANDARD;
2639
2640 if (vendor == 0x0000 && product == 0x0000) {
2641 // Some devices are only identifiable by their name
2642 if (name &&
2643 (SDL_strcmp(name, "Lic Pro Controller") == 0 ||
2644 SDL_strcmp(name, "Nintendo Wireless Gamepad") == 0 ||
2645 SDL_strcmp(name, "Wireless Gamepad") == 0)) {
2646 // HORI or PowerA Switch Pro Controller clone
2647 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
2648 }
2649
2650 } else if (vendor == 0x0001 && product == 0x0001) {
2651 type = SDL_GAMEPAD_TYPE_STANDARD;
2652
2653 } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT) {
2654 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
2655
2656 } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) {
2657 if (name && SDL_strstr(name, "NES Controller") != NULL) {
2658 // We don't have a type for the Nintendo Online NES Controller
2659 type = SDL_GAMEPAD_TYPE_STANDARD;
2660 } else {
2661 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
2662 }
2663
2664 } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
2665 if (name && SDL_strstr(name, "(L)") != NULL) {
2666 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
2667 } else {
2668 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
2669 }
2670
2671 } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR) {
2672 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR;
2673
2674 } else if (forUI && SDL_IsJoystickGameCube(vendor, product)) {
2675 // We don't have a type for the Nintendo GameCube controller
2676 type = SDL_GAMEPAD_TYPE_STANDARD;
2677
2678 } else {
2679 switch (GuessControllerType(vendor, product)) {
2680 case k_eControllerType_XBox360Controller:
2681 type = SDL_GAMEPAD_TYPE_XBOX360;
2682 break;
2683 case k_eControllerType_XBoxOneController:
2684 type = SDL_GAMEPAD_TYPE_XBOXONE;
2685 break;
2686 case k_eControllerType_PS3Controller:
2687 type = SDL_GAMEPAD_TYPE_PS3;
2688 break;
2689 case k_eControllerType_PS4Controller:
2690 type = SDL_GAMEPAD_TYPE_PS4;
2691 break;
2692 case k_eControllerType_PS5Controller:
2693 type = SDL_GAMEPAD_TYPE_PS5;
2694 break;
2695 case k_eControllerType_XInputPS4Controller:
2696 if (forUI) {
2697 type = SDL_GAMEPAD_TYPE_PS4;
2698 } else {
2699 type = SDL_GAMEPAD_TYPE_STANDARD;
2700 }
2701 break;
2702 case k_eControllerType_SwitchProController:
2703 case k_eControllerType_SwitchInputOnlyController:
2704 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
2705 break;
2706 case k_eControllerType_XInputSwitchController:
2707 if (forUI) {
2708 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
2709 } else {
2710 type = SDL_GAMEPAD_TYPE_STANDARD;
2711 }
2712 break;
2713 default:
2714 break;
2715 }
2716 }
2717 return type;
2718}
2719
2720SDL_GamepadType SDL_GetGamepadTypeFromGUID(SDL_GUID guid, const char *name)
2721{
2722 SDL_GamepadType type;
2723 Uint16 vendor, product;
2724
2725 SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
2726 type = SDL_GetGamepadTypeFromVIDPID(vendor, product, name, true);
2727 if (type == SDL_GAMEPAD_TYPE_STANDARD) {
2728 if (SDL_IsJoystickXInput(guid)) {
2729 // This is probably an Xbox One controller
2730 return SDL_GAMEPAD_TYPE_XBOXONE;
2731 }
2732#ifdef SDL_JOYSTICK_HIDAPI
2733 if (SDL_IsJoystickHIDAPI(guid)) {
2734 return HIDAPI_GetGamepadTypeFromGUID(guid);
2735 }
2736#endif // SDL_JOYSTICK_HIDAPI
2737 }
2738 return type;
2739}
2740
2741bool SDL_JoystickGUIDUsesVersion(SDL_GUID guid)
2742{
2743 Uint16 vendor, product;
2744
2745 if (SDL_IsJoystickMFI(guid)) {
2746 // The version bits are used as button capability mask
2747 return false;
2748 }
2749
2750 SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
2751 if (vendor && product) {
2752 return true;
2753 }
2754 return false;
2755}
2756
2757bool SDL_IsJoystickXboxOne(Uint16 vendor_id, Uint16 product_id)
2758{
2759 EControllerType eType = GuessControllerType(vendor_id, product_id);
2760 return eType == k_eControllerType_XBoxOneController;
2761}
2762
2763bool SDL_IsJoystickXboxOneElite(Uint16 vendor_id, Uint16 product_id)
2764{
2765 if (vendor_id == USB_VENDOR_MICROSOFT) {
2766 if (product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_1 ||
2767 product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2 ||
2768 product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH ||
2769 product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLE) {
2770 return true;
2771 }
2772 }
2773 return false;
2774}
2775
2776bool SDL_IsJoystickXboxSeriesX(Uint16 vendor_id, Uint16 product_id)
2777{
2778 if (vendor_id == USB_VENDOR_MICROSOFT) {
2779 if (product_id == USB_PRODUCT_XBOX_SERIES_X ||
2780 product_id == USB_PRODUCT_XBOX_SERIES_X_BLE) {
2781 return true;
2782 }
2783 }
2784 if (vendor_id == USB_VENDOR_PDP) {
2785 if (product_id == USB_PRODUCT_XBOX_SERIES_X_VICTRIX_GAMBIT ||
2786 product_id == USB_PRODUCT_XBOX_SERIES_X_PDP_BLUE ||
2787 product_id == USB_PRODUCT_XBOX_SERIES_X_PDP_AFTERGLOW) {
2788 return true;
2789 }
2790 }
2791 if (vendor_id == USB_VENDOR_POWERA_ALT) {
2792 if ((product_id >= 0x2001 && product_id <= 0x201a) ||
2793 product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO2 ||
2794 product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO4 ||
2795 product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO_WIRELESS_USB ||
2796 product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO_WIRELESS_DONGLE ||
2797 product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_MOGA_XP_ULTRA ||
2798 product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_SPECTRA) {
2799 return true;
2800 }
2801 }
2802 if (vendor_id == USB_VENDOR_HORI) {
2803 if (product_id == USB_PRODUCT_HORI_FIGHTING_COMMANDER_OCTA_SERIES_X ||
2804 product_id == USB_PRODUCT_HORI_HORIPAD_PRO_SERIES_X) {
2805 return true;
2806 }
2807 }
2808 if (vendor_id == USB_VENDOR_HP) {
2809 if (product_id == USB_PRODUCT_XBOX_SERIES_X_HP_HYPERX ||
2810 product_id == USB_PRODUCT_XBOX_SERIES_X_HP_HYPERX_RGB) {
2811 return true;
2812 }
2813 }
2814 if (vendor_id == USB_VENDOR_RAZER) {
2815 if (product_id == USB_PRODUCT_RAZER_WOLVERINE_V2 ||
2816 product_id == USB_PRODUCT_RAZER_WOLVERINE_V2_CHROMA ||
2817 product_id == USB_PRODUCT_RAZER_WOLVERINE_V3_PRO) {
2818 return true;
2819 }
2820 }
2821 if (vendor_id == USB_VENDOR_THRUSTMASTER) {
2822 if (product_id == USB_PRODUCT_THRUSTMASTER_ESWAPX_PRO_SERIES_X) {
2823 return true;
2824 }
2825 }
2826 if (vendor_id == USB_VENDOR_TURTLE_BEACH) {
2827 if (product_id == USB_PRODUCT_TURTLE_BEACH_SERIES_X_REACT_R ||
2828 product_id == USB_PRODUCT_TURTLE_BEACH_SERIES_X_RECON) {
2829 return true;
2830 }
2831 }
2832 if (vendor_id == USB_VENDOR_8BITDO) {
2833 if (product_id == USB_PRODUCT_8BITDO_XBOX_CONTROLLER1 ||
2834 product_id == USB_PRODUCT_8BITDO_XBOX_CONTROLLER2) {
2835 return true;
2836 }
2837 }
2838 if (vendor_id == USB_VENDOR_GAMESIR) {
2839 if (product_id == USB_PRODUCT_GAMESIR_G7) {
2840 return true;
2841 }
2842 }
2843 if (vendor_id == USB_VENDOR_ASUS) {
2844 if (product_id == USB_PRODUCT_ROG_RAIKIRI) {
2845 return true;
2846 }
2847 }
2848 return false;
2849}
2850
2851bool SDL_IsJoystickBluetoothXboxOne(Uint16 vendor_id, Uint16 product_id)
2852{
2853 if (vendor_id == USB_VENDOR_MICROSOFT) {
2854 if (product_id == USB_PRODUCT_XBOX_ONE_ADAPTIVE_BLUETOOTH ||
2855 product_id == USB_PRODUCT_XBOX_ONE_ADAPTIVE_BLE ||
2856 product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH ||
2857 product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH ||
2858 product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLE ||
2859 product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH ||
2860 product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLE ||
2861 product_id == USB_PRODUCT_XBOX_SERIES_X_BLE) {
2862 return true;
2863 }
2864 }
2865 return false;
2866}
2867
2868bool SDL_IsJoystickPS4(Uint16 vendor_id, Uint16 product_id)
2869{
2870 EControllerType eType = GuessControllerType(vendor_id, product_id);
2871 return eType == k_eControllerType_PS4Controller;
2872}
2873
2874bool SDL_IsJoystickPS5(Uint16 vendor_id, Uint16 product_id)
2875{
2876 EControllerType eType = GuessControllerType(vendor_id, product_id);
2877 return eType == k_eControllerType_PS5Controller;
2878}
2879
2880bool SDL_IsJoystickDualSenseEdge(Uint16 vendor_id, Uint16 product_id)
2881{
2882 if (vendor_id == USB_VENDOR_SONY) {
2883 if (product_id == USB_PRODUCT_SONY_DS5_EDGE) {
2884 return true;
2885 }
2886 }
2887 return false;
2888}
2889
2890bool SDL_IsJoystickNintendoSwitchPro(Uint16 vendor_id, Uint16 product_id)
2891{
2892 EControllerType eType = GuessControllerType(vendor_id, product_id);
2893 return eType == k_eControllerType_SwitchProController || eType == k_eControllerType_SwitchInputOnlyController;
2894}
2895
2896bool SDL_IsJoystickNintendoSwitchProInputOnly(Uint16 vendor_id, Uint16 product_id)
2897{
2898 EControllerType eType = GuessControllerType(vendor_id, product_id);
2899 return eType == k_eControllerType_SwitchInputOnlyController;
2900}
2901
2902bool SDL_IsJoystickNintendoSwitchJoyCon(Uint16 vendor_id, Uint16 product_id)
2903{
2904 EControllerType eType = GuessControllerType(vendor_id, product_id);
2905 return eType == k_eControllerType_SwitchJoyConLeft || eType == k_eControllerType_SwitchJoyConRight;
2906}
2907
2908bool SDL_IsJoystickNintendoSwitchJoyConLeft(Uint16 vendor_id, Uint16 product_id)
2909{
2910 EControllerType eType = GuessControllerType(vendor_id, product_id);
2911 return eType == k_eControllerType_SwitchJoyConLeft;
2912}
2913
2914bool SDL_IsJoystickNintendoSwitchJoyConRight(Uint16 vendor_id, Uint16 product_id)
2915{
2916 EControllerType eType = GuessControllerType(vendor_id, product_id);
2917 return eType == k_eControllerType_SwitchJoyConRight;
2918}
2919
2920bool SDL_IsJoystickNintendoSwitchJoyConGrip(Uint16 vendor_id, Uint16 product_id)
2921{
2922 return vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP;
2923}
2924
2925bool SDL_IsJoystickNintendoSwitchJoyConPair(Uint16 vendor_id, Uint16 product_id)
2926{
2927 return vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR;
2928}
2929
2930bool SDL_IsJoystickGameCube(Uint16 vendor_id, Uint16 product_id)
2931{
2932 return SDL_VIDPIDInList(vendor_id, product_id, &gamecube_devices);
2933}
2934
2935bool SDL_IsJoystickAmazonLunaController(Uint16 vendor_id, Uint16 product_id)
2936{
2937 return ((vendor_id == USB_VENDOR_AMAZON && product_id == USB_PRODUCT_AMAZON_LUNA_CONTROLLER) ||
2938 (vendor_id == BLUETOOTH_VENDOR_AMAZON && product_id == BLUETOOTH_PRODUCT_LUNA_CONTROLLER));
2939}
2940
2941bool SDL_IsJoystickGoogleStadiaController(Uint16 vendor_id, Uint16 product_id)
2942{
2943 return vendor_id == USB_VENDOR_GOOGLE && product_id == USB_PRODUCT_GOOGLE_STADIA_CONTROLLER;
2944}
2945
2946bool SDL_IsJoystickNVIDIASHIELDController(Uint16 vendor_id, Uint16 product_id)
2947{
2948 return (vendor_id == USB_VENDOR_NVIDIA &&
2949 (product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103 ||
2950 product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V104));
2951}
2952
2953bool SDL_IsJoystickSteamVirtualGamepad(Uint16 vendor_id, Uint16 product_id, Uint16 version)
2954{
2955#ifdef SDL_PLATFORM_MACOS
2956 return (vendor_id == USB_VENDOR_MICROSOFT && product_id == USB_PRODUCT_XBOX360_WIRED_CONTROLLER && version == 0);
2957#else
2958 return (vendor_id == USB_VENDOR_VALVE && product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD);
2959#endif
2960}
2961
2962bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id)
2963{
2964 EControllerType eType = GuessControllerType(vendor_id, product_id);
2965 return eType == k_eControllerType_SteamController || eType == k_eControllerType_SteamControllerV2;
2966}
2967
2968bool SDL_IsJoystickHoriSteamController(Uint16 vendor_id, Uint16 product_id)
2969{
2970 return vendor_id == USB_VENDOR_HORI && (product_id == USB_PRODUCT_HORI_STEAM_CONTROLLER || product_id == USB_PRODUCT_HORI_STEAM_CONTROLLER_BT);
2971}
2972
2973bool SDL_IsJoystickSteamDeck(Uint16 vendor_id, Uint16 product_id)
2974{
2975 EControllerType eType = GuessControllerType(vendor_id, product_id);
2976 return eType == k_eControllerType_SteamControllerNeptune;
2977}
2978
2979bool SDL_IsJoystickXInput(SDL_GUID guid)
2980{
2981 return (guid.data[14] == 'x') ? true : false;
2982}
2983
2984bool SDL_IsJoystickWGI(SDL_GUID guid)
2985{
2986 return (guid.data[14] == 'w') ? true : false;
2987}
2988
2989bool SDL_IsJoystickHIDAPI(SDL_GUID guid)
2990{
2991 return (guid.data[14] == 'h') ? true : false;
2992}
2993
2994bool SDL_IsJoystickMFI(SDL_GUID guid)
2995{
2996 return (guid.data[14] == 'm') ? true : false;
2997}
2998
2999bool SDL_IsJoystickRAWINPUT(SDL_GUID guid)
3000{
3001 return (guid.data[14] == 'r') ? true : false;
3002}
3003
3004bool SDL_IsJoystickVIRTUAL(SDL_GUID guid)
3005{
3006 return (guid.data[14] == 'v') ? true : false;
3007}
3008
3009static bool SDL_IsJoystickWheel(Uint16 vendor_id, Uint16 product_id)
3010{
3011 return SDL_VIDPIDInList(vendor_id, product_id, &wheel_devices);
3012}
3013
3014static bool SDL_IsJoystickArcadeStick(Uint16 vendor_id, Uint16 product_id)
3015{
3016 return SDL_VIDPIDInList(vendor_id, product_id, &arcadestick_devices);
3017}
3018
3019static bool SDL_IsJoystickFlightStick(Uint16 vendor_id, Uint16 product_id)
3020{
3021 return SDL_VIDPIDInList(vendor_id, product_id, &flightstick_devices);
3022}
3023
3024static bool SDL_IsJoystickThrottle(Uint16 vendor_id, Uint16 product_id)
3025{
3026 return SDL_VIDPIDInList(vendor_id, product_id, &throttle_devices);
3027}
3028
3029static SDL_JoystickType SDL_GetJoystickGUIDType(SDL_GUID guid)
3030{
3031 Uint16 vendor;
3032 Uint16 product;
3033
3034 SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
3035
3036 if (SDL_IsJoystickWheel(vendor, product)) {
3037 return SDL_JOYSTICK_TYPE_WHEEL;
3038 }
3039
3040 if (SDL_IsJoystickArcadeStick(vendor, product)) {
3041 return SDL_JOYSTICK_TYPE_ARCADE_STICK;
3042 }
3043
3044 if (SDL_IsJoystickFlightStick(vendor, product)) {
3045 return SDL_JOYSTICK_TYPE_FLIGHT_STICK;
3046 }
3047
3048 if (SDL_IsJoystickThrottle(vendor, product)) {
3049 return SDL_JOYSTICK_TYPE_THROTTLE;
3050 }
3051
3052 if (SDL_IsJoystickXInput(guid)) {
3053 // XInput GUID, get the type based on the XInput device subtype
3054 switch (guid.data[15]) {
3055 case 0x01: // XINPUT_DEVSUBTYPE_GAMEPAD
3056 return SDL_JOYSTICK_TYPE_GAMEPAD;
3057 case 0x02: // XINPUT_DEVSUBTYPE_WHEEL
3058 return SDL_JOYSTICK_TYPE_WHEEL;
3059 case 0x03: // XINPUT_DEVSUBTYPE_ARCADE_STICK
3060 return SDL_JOYSTICK_TYPE_ARCADE_STICK;
3061 case 0x04: // XINPUT_DEVSUBTYPE_FLIGHT_STICK
3062 return SDL_JOYSTICK_TYPE_FLIGHT_STICK;
3063 case 0x05: // XINPUT_DEVSUBTYPE_DANCE_PAD
3064 return SDL_JOYSTICK_TYPE_DANCE_PAD;
3065 case 0x06: // XINPUT_DEVSUBTYPE_GUITAR
3066 case 0x07: // XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE
3067 case 0x0B: // XINPUT_DEVSUBTYPE_GUITAR_BASS
3068 return SDL_JOYSTICK_TYPE_GUITAR;
3069 case 0x08: // XINPUT_DEVSUBTYPE_DRUM_KIT
3070 return SDL_JOYSTICK_TYPE_DRUM_KIT;
3071 case 0x13: // XINPUT_DEVSUBTYPE_ARCADE_PAD
3072 return SDL_JOYSTICK_TYPE_ARCADE_PAD;
3073 default:
3074 return SDL_JOYSTICK_TYPE_UNKNOWN;
3075 }
3076 }
3077
3078 if (SDL_IsJoystickWGI(guid)) {
3079 return (SDL_JoystickType)guid.data[15];
3080 }
3081
3082 if (SDL_IsJoystickVIRTUAL(guid)) {
3083 return (SDL_JoystickType)guid.data[15];
3084 }
3085
3086#ifdef SDL_JOYSTICK_HIDAPI
3087 if (SDL_IsJoystickHIDAPI(guid)) {
3088 return HIDAPI_GetJoystickTypeFromGUID(guid);
3089 }
3090#endif // SDL_JOYSTICK_HIDAPI
3091
3092 if (GuessControllerType(vendor, product) != k_eControllerType_UnknownNonSteamController) {
3093 return SDL_JOYSTICK_TYPE_GAMEPAD;
3094 }
3095
3096 return SDL_JOYSTICK_TYPE_UNKNOWN;
3097}
3098
3099bool SDL_ShouldIgnoreJoystick(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
3100{
3101 // Check the joystick blacklist
3102 if (SDL_VIDPIDInList(vendor_id, product_id, &blacklist_devices)) {
3103 return true;
3104 }
3105 if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_ROG_CHAKRAM, false)) {
3106 if (SDL_VIDPIDInList(vendor_id, product_id, &rog_gamepad_mice)) {
3107 return true;
3108 }
3109 }
3110
3111 if (SDL_ShouldIgnoreGamepad(vendor_id, product_id, version, name)) {
3112 return true;
3113 }
3114
3115 return false;
3116}
3117
3118// return the guid for this index
3119SDL_GUID SDL_GetJoystickGUIDForID(SDL_JoystickID instance_id)
3120{
3121 SDL_JoystickDriver *driver;
3122 int device_index;
3123 SDL_GUID guid;
3124
3125 SDL_LockJoysticks();
3126 if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
3127 guid = driver->GetDeviceGUID(device_index);
3128 } else {
3129 SDL_zero(guid);
3130 }
3131 SDL_UnlockJoysticks();
3132
3133 return guid;
3134}
3135
3136Uint16 SDL_GetJoystickVendorForID(SDL_JoystickID instance_id)
3137{
3138 Uint16 vendor;
3139 const SDL_SteamVirtualGamepadInfo *info;
3140
3141 SDL_LockJoysticks();
3142 info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);
3143 if (info) {
3144 vendor = info->vendor_id;
3145 } else {
3146 SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
3147
3148 SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
3149 }
3150 SDL_UnlockJoysticks();
3151
3152 return vendor;
3153}
3154
3155Uint16 SDL_GetJoystickProductForID(SDL_JoystickID instance_id)
3156{
3157 Uint16 product;
3158 const SDL_SteamVirtualGamepadInfo *info;
3159
3160 SDL_LockJoysticks();
3161 info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);
3162 if (info) {
3163 product = info->product_id;
3164 } else {
3165 SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
3166
3167 SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
3168 }
3169 SDL_UnlockJoysticks();
3170
3171 return product;
3172}
3173
3174Uint16 SDL_GetJoystickProductVersionForID(SDL_JoystickID instance_id)
3175{
3176 Uint16 version;
3177 SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
3178
3179 SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version, NULL);
3180 return version;
3181}
3182
3183SDL_JoystickType SDL_GetJoystickTypeForID(SDL_JoystickID instance_id)
3184{
3185 SDL_JoystickType type;
3186 SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
3187
3188 type = SDL_GetJoystickGUIDType(guid);
3189 if (type == SDL_JOYSTICK_TYPE_UNKNOWN) {
3190 if (SDL_IsGamepad(instance_id)) {
3191 type = SDL_JOYSTICK_TYPE_GAMEPAD;
3192 }
3193 }
3194 return type;
3195}
3196
3197SDL_GUID SDL_GetJoystickGUID(SDL_Joystick *joystick)
3198{
3199 SDL_GUID result;
3200
3201 SDL_LockJoysticks();
3202 {
3203 static SDL_GUID emptyGUID;
3204
3205 CHECK_JOYSTICK_MAGIC(joystick, emptyGUID);
3206
3207 result = joystick->guid;
3208 }
3209 SDL_UnlockJoysticks();
3210
3211 return result;
3212}
3213
3214Uint16 SDL_GetJoystickVendor(SDL_Joystick *joystick)
3215{
3216 Uint16 vendor;
3217 const SDL_SteamVirtualGamepadInfo *info;
3218
3219 SDL_LockJoysticks();
3220 {
3221 CHECK_JOYSTICK_MAGIC(joystick, 0);
3222
3223 info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);
3224 if (info) {
3225 vendor = info->vendor_id;
3226 } else {
3227 SDL_GUID guid = SDL_GetJoystickGUID(joystick);
3228
3229 SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
3230 }
3231 }
3232 SDL_UnlockJoysticks();
3233
3234 return vendor;
3235}
3236
3237Uint16 SDL_GetJoystickProduct(SDL_Joystick *joystick)
3238{
3239 Uint16 product;
3240 const SDL_SteamVirtualGamepadInfo *info;
3241
3242 SDL_LockJoysticks();
3243 {
3244 CHECK_JOYSTICK_MAGIC(joystick, 0);
3245
3246 info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);
3247 if (info) {
3248 product = info->product_id;
3249 } else {
3250 SDL_GUID guid = SDL_GetJoystickGUID(joystick);
3251
3252 SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
3253 }
3254 }
3255 SDL_UnlockJoysticks();
3256
3257 return product;
3258}
3259
3260Uint16 SDL_GetJoystickProductVersion(SDL_Joystick *joystick)
3261{
3262 Uint16 version;
3263 SDL_GUID guid = SDL_GetJoystickGUID(joystick);
3264
3265 SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version, NULL);
3266 return version;
3267}
3268
3269Uint16 SDL_GetJoystickFirmwareVersion(SDL_Joystick *joystick)
3270{
3271 Uint16 result;
3272
3273 SDL_LockJoysticks();
3274 {
3275 CHECK_JOYSTICK_MAGIC(joystick, 0);
3276
3277 result = joystick->firmware_version;
3278 }
3279 SDL_UnlockJoysticks();
3280
3281 return result;
3282}
3283
3284const char *SDL_GetJoystickSerial(SDL_Joystick *joystick)
3285{
3286 const char *result;
3287
3288 SDL_LockJoysticks();
3289 {
3290 CHECK_JOYSTICK_MAGIC(joystick, NULL);
3291
3292 result = SDL_GetPersistentString(joystick->serial);
3293 }
3294 SDL_UnlockJoysticks();
3295
3296 return result;
3297}
3298
3299SDL_JoystickType SDL_GetJoystickType(SDL_Joystick *joystick)
3300{
3301 SDL_JoystickType type;
3302 SDL_GUID guid = SDL_GetJoystickGUID(joystick);
3303
3304 type = SDL_GetJoystickGUIDType(guid);
3305 if (type == SDL_JOYSTICK_TYPE_UNKNOWN) {
3306 SDL_LockJoysticks();
3307 {
3308 CHECK_JOYSTICK_MAGIC(joystick, SDL_JOYSTICK_TYPE_UNKNOWN);
3309
3310 if (SDL_IsGamepad(joystick->instance_id)) {
3311 type = SDL_JOYSTICK_TYPE_GAMEPAD;
3312 }
3313 }
3314 SDL_UnlockJoysticks();
3315 }
3316 return type;
3317}
3318
3319void SDL_SendJoystickPowerInfo(SDL_Joystick *joystick, SDL_PowerState state, int percent)
3320{
3321 SDL_AssertJoysticksLocked();
3322
3323 if (state != joystick->battery_state || percent != joystick->battery_percent) {
3324 joystick->battery_state = state;
3325 joystick->battery_percent = percent;
3326
3327 if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_BATTERY_UPDATED)) {
3328 SDL_Event event;
3329 event.type = SDL_EVENT_JOYSTICK_BATTERY_UPDATED;
3330 event.common.timestamp = 0;
3331 event.jbattery.which = joystick->instance_id;
3332 event.jbattery.state = state;
3333 event.jbattery.percent = percent;
3334 SDL_PushEvent(&event);
3335 }
3336 }
3337}
3338
3339SDL_JoystickConnectionState SDL_GetJoystickConnectionState(SDL_Joystick *joystick)
3340{
3341 SDL_JoystickConnectionState result;
3342
3343 SDL_LockJoysticks();
3344 {
3345 CHECK_JOYSTICK_MAGIC(joystick, SDL_JOYSTICK_CONNECTION_INVALID);
3346
3347 result = joystick->connection_state;
3348 }
3349 SDL_UnlockJoysticks();
3350
3351 return result;
3352}
3353
3354SDL_PowerState SDL_GetJoystickPowerInfo(SDL_Joystick *joystick, int *percent)
3355{
3356 SDL_PowerState result;
3357
3358 if (percent) {
3359 *percent = -1;
3360 }
3361
3362 SDL_LockJoysticks();
3363 {
3364 CHECK_JOYSTICK_MAGIC(joystick, SDL_POWERSTATE_ERROR);
3365
3366 result = joystick->battery_state;
3367
3368 if (percent) {
3369 *percent = joystick->battery_percent;
3370 }
3371 }
3372 SDL_UnlockJoysticks();
3373
3374 return result;
3375}
3376
3377void SDL_SendJoystickTouchpad(Uint64 timestamp, SDL_Joystick *joystick, int touchpad, int finger, bool down, float x, float y, float pressure)
3378{
3379 SDL_JoystickTouchpadInfo *touchpad_info;
3380 SDL_JoystickTouchpadFingerInfo *finger_info;
3381 Uint32 event_type;
3382
3383 SDL_AssertJoysticksLocked();
3384
3385 if (touchpad < 0 || touchpad >= joystick->ntouchpads) {
3386 return;
3387 }
3388
3389 touchpad_info = &joystick->touchpads[touchpad];
3390 if (finger < 0 || finger >= touchpad_info->nfingers) {
3391 return;
3392 }
3393
3394 finger_info = &touchpad_info->fingers[finger];
3395
3396 if (!down) {
3397 if (x == 0.0f && y == 0.0f) {
3398 x = finger_info->x;
3399 y = finger_info->y;
3400 }
3401 pressure = 0.0f;
3402 }
3403
3404 if (x < 0.0f) {
3405 x = 0.0f;
3406 } else if (x > 1.0f) {
3407 x = 1.0f;
3408 }
3409 if (y < 0.0f) {
3410 y = 0.0f;
3411 } else if (y > 1.0f) {
3412 y = 1.0f;
3413 }
3414 if (pressure < 0.0f) {
3415 pressure = 0.0f;
3416 } else if (pressure > 1.0f) {
3417 pressure = 1.0f;
3418 }
3419
3420 if (down == finger_info->down) {
3421 if (!down ||
3422 (x == finger_info->x && y == finger_info->y && pressure == finger_info->pressure)) {
3423 return;
3424 }
3425 }
3426
3427 if (down == finger_info->down) {
3428 event_type = SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION;
3429 } else if (down) {
3430 event_type = SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN;
3431 } else {
3432 event_type = SDL_EVENT_GAMEPAD_TOUCHPAD_UP;
3433 }
3434
3435 // We ignore events if we don't have keyboard focus, except for touch release
3436 if (SDL_PrivateJoystickShouldIgnoreEvent()) {
3437 if (event_type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP) {
3438 return;
3439 }
3440 }
3441
3442 // Update internal joystick state
3443 SDL_assert(timestamp != 0);
3444 finger_info->down = down;
3445 finger_info->x = x;
3446 finger_info->y = y;
3447 finger_info->pressure = pressure;
3448 joystick->update_complete = timestamp;
3449
3450 // Post the event, if desired
3451 if (SDL_EventEnabled(event_type)) {
3452 SDL_Event event;
3453 event.type = event_type;
3454 event.common.timestamp = timestamp;
3455 event.gtouchpad.which = joystick->instance_id;
3456 event.gtouchpad.touchpad = touchpad;
3457 event.gtouchpad.finger = finger;
3458 event.gtouchpad.x = x;
3459 event.gtouchpad.y = y;
3460 event.gtouchpad.pressure = pressure;
3461 SDL_PushEvent(&event);
3462 }
3463}
3464
3465void SDL_SendJoystickSensor(Uint64 timestamp, SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values)
3466{
3467 SDL_AssertJoysticksLocked();
3468
3469 // We ignore events if we don't have keyboard focus
3470 if (SDL_PrivateJoystickShouldIgnoreEvent()) {
3471 return;
3472 }
3473
3474 for (int i = 0; i < joystick->nsensors; ++i) {
3475 SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
3476
3477 if (sensor->type == type) {
3478 if (sensor->enabled) {
3479 num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
3480
3481 // Update internal sensor state
3482 SDL_memcpy(sensor->data, data, num_values * sizeof(*data));
3483 joystick->update_complete = timestamp;
3484
3485 // Post the event, if desired
3486 if (SDL_EventEnabled(SDL_EVENT_GAMEPAD_SENSOR_UPDATE)) {
3487 SDL_Event event;
3488 event.type = SDL_EVENT_GAMEPAD_SENSOR_UPDATE;
3489 event.common.timestamp = timestamp;
3490 event.gsensor.which = joystick->instance_id;
3491 event.gsensor.sensor = type;
3492 num_values = SDL_min(num_values,
3493 SDL_arraysize(event.gsensor.data));
3494 SDL_memset(event.gsensor.data, 0,
3495 sizeof(event.gsensor.data));
3496 SDL_memcpy(event.gsensor.data, data,
3497 num_values * sizeof(*data));
3498 event.gsensor.sensor_timestamp = sensor_timestamp;
3499 SDL_PushEvent(&event);
3500 }
3501 }
3502 break;
3503 }
3504 }
3505}
3506
3507static void SDL_LoadVIDPIDListFromHint(const char *hint, int *num_entries, int *max_entries, Uint32 **entries)
3508{
3509 Uint32 entry;
3510 char *spot;
3511 char *file = NULL;
3512
3513 if (hint && *hint == '@') {
3514 spot = file = (char *)SDL_LoadFile(hint + 1, NULL);
3515 } else {
3516 spot = (char *)hint;
3517 }
3518
3519 if (!spot) {
3520 return;
3521 }
3522
3523 while ((spot = SDL_strstr(spot, "0x")) != NULL) {
3524 entry = (Uint16)SDL_strtol(spot, &spot, 0);
3525 entry <<= 16;
3526 spot = SDL_strstr(spot, "0x");
3527 if (!spot) {
3528 break;
3529 }
3530 entry |= (Uint16)SDL_strtol(spot, &spot, 0);
3531
3532 if (*num_entries == *max_entries) {
3533 int new_max_entries = *max_entries + 16;
3534 Uint32 *new_entries = (Uint32 *)SDL_realloc(*entries, new_max_entries * sizeof(**entries));
3535 if (!new_entries) {
3536 // Out of memory, go with what we have already
3537 break;
3538 }
3539 *entries = new_entries;
3540 *max_entries = new_max_entries;
3541 }
3542 (*entries)[(*num_entries)++] = entry;
3543 }
3544
3545 if (file) {
3546 SDL_free(file);
3547 }
3548}
3549
3550void SDL_LoadVIDPIDListFromHints(SDL_vidpid_list *list, const char *included_list, const char *excluded_list)
3551{
3552 // Empty the list
3553 list->num_included_entries = 0;
3554 list->num_excluded_entries = 0;
3555
3556 // Add the initial entries
3557 if (list->num_initial_entries > 0) {
3558 if (list->num_included_entries < list->num_initial_entries) {
3559 Uint32 *entries = (Uint32 *)SDL_malloc(list->num_initial_entries * sizeof(*entries));
3560 if (entries) {
3561 SDL_memcpy(entries, list->initial_entries, list->num_initial_entries * sizeof(*entries));
3562 list->included_entries = entries;
3563 list->num_included_entries = list->num_initial_entries;
3564 list->max_included_entries = list->num_initial_entries;
3565 }
3566 }
3567 }
3568
3569 // Add the included entries from the hint
3570 SDL_LoadVIDPIDListFromHint(included_list, &list->num_included_entries, &list->max_included_entries, &list->included_entries);
3571
3572 // Add the excluded entries from the hint
3573 SDL_LoadVIDPIDListFromHint(excluded_list, &list->num_excluded_entries, &list->max_excluded_entries, &list->excluded_entries);
3574}
3575
3576static void SDLCALL SDL_VIDPIDIncludedHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
3577{
3578 SDL_vidpid_list *list = (SDL_vidpid_list *)userdata;
3579 const char *included_list = hint;
3580 const char *excluded_list = NULL;
3581
3582 if (!list->initialized) {
3583 return;
3584 }
3585
3586 if (list->excluded_hint_name) {
3587 excluded_list = SDL_GetHint(list->excluded_hint_name);
3588 }
3589 SDL_LoadVIDPIDListFromHints(list, included_list, excluded_list);
3590}
3591
3592static void SDLCALL SDL_VIDPIDExcludedHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
3593{
3594 SDL_vidpid_list *list = (SDL_vidpid_list *)userdata;
3595 const char *included_list = NULL;
3596 const char *excluded_list = hint;
3597
3598 if (!list->initialized) {
3599 return;
3600 }
3601
3602 if (list->included_hint_name) {
3603 included_list = SDL_GetHint(list->included_hint_name);
3604 }
3605 SDL_LoadVIDPIDListFromHints(list, included_list, excluded_list);
3606}
3607
3608void SDL_LoadVIDPIDList(SDL_vidpid_list *list)
3609{
3610 const char *included_list = NULL;
3611 const char *excluded_list = NULL;
3612
3613 if (list->included_hint_name) {
3614 SDL_AddHintCallback(list->included_hint_name, SDL_VIDPIDIncludedHintChanged, list);
3615 }
3616
3617 if (list->excluded_hint_name) {
3618 SDL_AddHintCallback(list->excluded_hint_name, SDL_VIDPIDExcludedHintChanged, list);
3619 }
3620
3621 list->initialized = true;
3622
3623 if (list->included_hint_name) {
3624 included_list = SDL_GetHint(list->included_hint_name);
3625 }
3626 if (list->excluded_hint_name) {
3627 excluded_list = SDL_GetHint(list->excluded_hint_name);
3628 }
3629 SDL_LoadVIDPIDListFromHints(list, included_list, excluded_list);
3630}
3631
3632bool SDL_VIDPIDInList(Uint16 vendor_id, Uint16 product_id, const SDL_vidpid_list *list)
3633{
3634 int i;
3635 Uint32 vidpid = MAKE_VIDPID(vendor_id, product_id);
3636
3637 for (i = 0; i < list->num_excluded_entries; ++i) {
3638 if (vidpid == list->excluded_entries[i]) {
3639 return false;
3640 }
3641 }
3642 for (i = 0; i < list->num_included_entries; ++i) {
3643 if (vidpid == list->included_entries[i]) {
3644 return true;
3645 }
3646 }
3647 return false;
3648}
3649
3650void SDL_FreeVIDPIDList(SDL_vidpid_list *list)
3651{
3652 if (list->included_hint_name) {
3653 SDL_RemoveHintCallback(list->included_hint_name, SDL_VIDPIDIncludedHintChanged, list);
3654 }
3655
3656 if (list->excluded_hint_name) {
3657 SDL_RemoveHintCallback(list->excluded_hint_name, SDL_VIDPIDExcludedHintChanged, list);
3658 }
3659
3660 if (list->included_entries) {
3661 SDL_free(list->included_entries);
3662 list->included_entries = NULL;
3663 list->num_included_entries = 0;
3664 list->max_included_entries = 0;
3665 }
3666
3667 if (list->excluded_entries) {
3668 SDL_free(list->excluded_entries);
3669 list->excluded_entries = NULL;
3670 list->num_excluded_entries = 0;
3671 list->max_excluded_entries = 0;
3672 }
3673
3674 list->initialized = false;
3675}