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
1046static bool ShouldSwapFaceButtons(const SDL_SteamVirtualGamepadInfo *info)
1047{
1048 // When "Use Nintendo Button Layout" is enabled under Steam (the default)
1049 // it will send button 0 for the A (east) button and button 1 for the
1050 // B (south) button. This is done so that games that interpret the
1051 // buttons as Xbox input will get button 0 for "A" as they expect.
1052 //
1053 // However, SDL reports positional buttons, so we need to swap
1054 // the buttons so they show up in the correct position. This provides
1055 // consistent behavior regardless of whether we're running under Steam,
1056 // under the default settings.
1057 if (info &&
1058 (info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO ||
1059 info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT ||
1060 info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT ||
1061 info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR)) {
1062 return true;
1063 }
1064 return false;
1065}
1066
1067/*
1068 * Open a joystick for use - the index passed as an argument refers to
1069 * the N'th joystick on the system. This index is the value which will
1070 * identify this joystick in future joystick events.
1071 *
1072 * This function returns a joystick identifier, or NULL if an error occurred.
1073 */
1074SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id)
1075{
1076 SDL_JoystickDriver *driver;
1077 int device_index;
1078 SDL_Joystick *joystick;
1079 SDL_Joystick *joysticklist;
1080 const char *joystickname = NULL;
1081 const char *joystickpath = NULL;
1082 bool invert_sensors = false;
1083 const SDL_SteamVirtualGamepadInfo *info;
1084
1085 SDL_LockJoysticks();
1086
1087 if (!SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
1088 SDL_UnlockJoysticks();
1089 return NULL;
1090 }
1091
1092 joysticklist = SDL_joysticks;
1093 /* If the joystick is already open, return it
1094 * it is important that we have a single joystick for each instance id
1095 */
1096 while (joysticklist) {
1097 if (instance_id == joysticklist->instance_id) {
1098 joystick = joysticklist;
1099 ++joystick->ref_count;
1100 SDL_UnlockJoysticks();
1101 return joystick;
1102 }
1103 joysticklist = joysticklist->next;
1104 }
1105
1106 // Create and initialize the joystick
1107 joystick = (SDL_Joystick *)SDL_calloc(1, sizeof(*joystick));
1108 if (!joystick) {
1109 SDL_UnlockJoysticks();
1110 return NULL;
1111 }
1112 SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, true);
1113 joystick->driver = driver;
1114 joystick->instance_id = instance_id;
1115 joystick->attached = true;
1116 joystick->led_expiration = SDL_GetTicks();
1117 joystick->battery_percent = -1;
1118
1119 if (!driver->Open(joystick, device_index)) {
1120 SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, false);
1121 SDL_free(joystick);
1122 SDL_UnlockJoysticks();
1123 return NULL;
1124 }
1125
1126 joystickname = driver->GetDeviceName(device_index);
1127 if (joystickname) {
1128 joystick->name = SDL_strdup(joystickname);
1129 }
1130
1131 joystickpath = driver->GetDevicePath(device_index);
1132 if (joystickpath) {
1133 joystick->path = SDL_strdup(joystickpath);
1134 }
1135
1136 joystick->guid = driver->GetDeviceGUID(device_index);
1137
1138 if (joystick->naxes > 0) {
1139 joystick->axes = (SDL_JoystickAxisInfo *)SDL_calloc(joystick->naxes, sizeof(*joystick->axes));
1140 }
1141 if (joystick->nballs > 0) {
1142 joystick->balls = (SDL_JoystickBallData *)SDL_calloc(joystick->nballs, sizeof(*joystick->balls));
1143 }
1144 if (joystick->nhats > 0) {
1145 joystick->hats = (Uint8 *)SDL_calloc(joystick->nhats, sizeof(*joystick->hats));
1146 }
1147 if (joystick->nbuttons > 0) {
1148 joystick->buttons = (bool *)SDL_calloc(joystick->nbuttons, sizeof(*joystick->buttons));
1149 }
1150 if (((joystick->naxes > 0) && !joystick->axes) ||
1151 ((joystick->nballs > 0) && !joystick->balls) ||
1152 ((joystick->nhats > 0) && !joystick->hats) ||
1153 ((joystick->nbuttons > 0) && !joystick->buttons)) {
1154 SDL_CloseJoystick(joystick);
1155 SDL_UnlockJoysticks();
1156 return NULL;
1157 }
1158
1159 // If this joystick is known to have all zero centered axes, skip the auto-centering code
1160 if (SDL_JoystickAxesCenteredAtZero(joystick)) {
1161 int i;
1162
1163 for (i = 0; i < joystick->naxes; ++i) {
1164 joystick->axes[i].has_initial_value = true;
1165 }
1166 }
1167
1168 // Get the Steam Input API handle
1169 info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);
1170 if (info) {
1171 joystick->steam_handle = info->handle;
1172 joystick->swap_face_buttons = ShouldSwapFaceButtons(info);
1173 }
1174
1175 // Use system gyro and accelerometer if the gamepad doesn't have built-in sensors
1176 if (ShouldAttemptSensorFusion(joystick, &invert_sensors)) {
1177 AttemptSensorFusion(joystick, invert_sensors);
1178 }
1179
1180 // Add joystick to list
1181 ++joystick->ref_count;
1182 // Link the joystick in the list
1183 joystick->next = SDL_joysticks;
1184 SDL_joysticks = joystick;
1185
1186 driver->Update(joystick);
1187
1188 SDL_UnlockJoysticks();
1189
1190 return joystick;
1191}
1192
1193SDL_JoystickID SDL_AttachVirtualJoystick(const SDL_VirtualJoystickDesc *desc)
1194{
1195#ifdef SDL_JOYSTICK_VIRTUAL
1196 SDL_JoystickID result;
1197
1198 SDL_LockJoysticks();
1199 result = SDL_JoystickAttachVirtualInner(desc);
1200 SDL_UnlockJoysticks();
1201 return result;
1202#else
1203 SDL_SetError("SDL not built with virtual-joystick support");
1204 return 0;
1205#endif
1206}
1207
1208bool SDL_DetachVirtualJoystick(SDL_JoystickID instance_id)
1209{
1210#ifdef SDL_JOYSTICK_VIRTUAL
1211 bool result;
1212
1213 SDL_LockJoysticks();
1214 result = SDL_JoystickDetachVirtualInner(instance_id);
1215 SDL_UnlockJoysticks();
1216 return result;
1217#else
1218 return SDL_SetError("SDL not built with virtual-joystick support");
1219#endif
1220}
1221
1222bool SDL_IsJoystickVirtual(SDL_JoystickID instance_id)
1223{
1224#ifdef SDL_JOYSTICK_VIRTUAL
1225 SDL_JoystickDriver *driver;
1226 int device_index;
1227 bool is_virtual = false;
1228
1229 SDL_LockJoysticks();
1230 if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
1231 if (driver == &SDL_VIRTUAL_JoystickDriver) {
1232 is_virtual = true;
1233 }
1234 }
1235 SDL_UnlockJoysticks();
1236
1237 return is_virtual;
1238#else
1239 return false;
1240#endif
1241}
1242
1243bool SDL_SetJoystickVirtualAxis(SDL_Joystick *joystick, int axis, Sint16 value)
1244{
1245 bool result;
1246
1247 SDL_LockJoysticks();
1248 {
1249 CHECK_JOYSTICK_MAGIC(joystick, false);
1250
1251#ifdef SDL_JOYSTICK_VIRTUAL
1252 result = SDL_SetJoystickVirtualAxisInner(joystick, axis, value);
1253#else
1254 result = SDL_SetError("SDL not built with virtual-joystick support");
1255#endif
1256 }
1257 SDL_UnlockJoysticks();
1258
1259 return result;
1260}
1261
1262bool SDL_SetJoystickVirtualBall(SDL_Joystick *joystick, int ball, Sint16 xrel, Sint16 yrel)
1263{
1264 bool result;
1265
1266 SDL_LockJoysticks();
1267 {
1268 CHECK_JOYSTICK_MAGIC(joystick, false);
1269
1270#ifdef SDL_JOYSTICK_VIRTUAL
1271 result = SDL_SetJoystickVirtualBallInner(joystick, ball, xrel, yrel);
1272#else
1273 result = SDL_SetError("SDL not built with virtual-joystick support");
1274#endif
1275 }
1276 SDL_UnlockJoysticks();
1277
1278 return result;
1279}
1280
1281bool SDL_SetJoystickVirtualButton(SDL_Joystick *joystick, int button, bool down)
1282{
1283 bool result;
1284
1285 SDL_LockJoysticks();
1286 {
1287 CHECK_JOYSTICK_MAGIC(joystick, false);
1288
1289#ifdef SDL_JOYSTICK_VIRTUAL
1290 result = SDL_SetJoystickVirtualButtonInner(joystick, button, down);
1291#else
1292 result = SDL_SetError("SDL not built with virtual-joystick support");
1293#endif
1294 }
1295 SDL_UnlockJoysticks();
1296
1297 return result;
1298}
1299
1300bool SDL_SetJoystickVirtualHat(SDL_Joystick *joystick, int hat, Uint8 value)
1301{
1302 bool result;
1303
1304 SDL_LockJoysticks();
1305 {
1306 CHECK_JOYSTICK_MAGIC(joystick, false);
1307
1308#ifdef SDL_JOYSTICK_VIRTUAL
1309 result = SDL_SetJoystickVirtualHatInner(joystick, hat, value);
1310#else
1311 result = SDL_SetError("SDL not built with virtual-joystick support");
1312#endif
1313 }
1314 SDL_UnlockJoysticks();
1315
1316 return result;
1317}
1318
1319bool SDL_SetJoystickVirtualTouchpad(SDL_Joystick *joystick, int touchpad, int finger, bool down, float x, float y, float pressure)
1320{
1321 bool result;
1322
1323 SDL_LockJoysticks();
1324 {
1325 CHECK_JOYSTICK_MAGIC(joystick, false);
1326
1327#ifdef SDL_JOYSTICK_VIRTUAL
1328 result = SDL_SetJoystickVirtualTouchpadInner(joystick, touchpad, finger, down, x, y, pressure);
1329#else
1330 result = SDL_SetError("SDL not built with virtual-joystick support");
1331#endif
1332 }
1333 SDL_UnlockJoysticks();
1334
1335 return result;
1336}
1337
1338bool SDL_SendJoystickVirtualSensorData(SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values)
1339{
1340 bool result;
1341
1342 SDL_LockJoysticks();
1343 {
1344 CHECK_JOYSTICK_MAGIC(joystick, false);
1345
1346#ifdef SDL_JOYSTICK_VIRTUAL
1347 result = SDL_SendJoystickVirtualSensorDataInner(joystick, type, sensor_timestamp, data, num_values);
1348#else
1349 result = SDL_SetError("SDL not built with virtual-joystick support");
1350#endif
1351 }
1352 SDL_UnlockJoysticks();
1353
1354 return result;
1355}
1356
1357/*
1358 * Checks to make sure the joystick is valid.
1359 */
1360bool SDL_IsJoystickValid(SDL_Joystick *joystick)
1361{
1362 SDL_AssertJoysticksLocked();
1363 return SDL_ObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK);
1364}
1365
1366bool SDL_PrivateJoystickGetAutoGamepadMapping(SDL_JoystickID instance_id, SDL_GamepadMapping *out)
1367{
1368 SDL_JoystickDriver *driver;
1369 int device_index;
1370 bool is_ok = false;
1371
1372 SDL_LockJoysticks();
1373 if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
1374 is_ok = driver->GetGamepadMapping(device_index, out);
1375 }
1376 SDL_UnlockJoysticks();
1377
1378 return is_ok;
1379}
1380
1381/*
1382 * Get the number of multi-dimensional axis controls on a joystick
1383 */
1384int SDL_GetNumJoystickAxes(SDL_Joystick *joystick)
1385{
1386 int result;
1387
1388 SDL_LockJoysticks();
1389 {
1390 CHECK_JOYSTICK_MAGIC(joystick, -1);
1391
1392 result = joystick->naxes;
1393 }
1394 SDL_UnlockJoysticks();
1395
1396 return result;
1397}
1398
1399/*
1400 * Get the number of hats on a joystick
1401 */
1402int SDL_GetNumJoystickHats(SDL_Joystick *joystick)
1403{
1404 int result;
1405
1406 SDL_LockJoysticks();
1407 {
1408 CHECK_JOYSTICK_MAGIC(joystick, -1);
1409
1410 result = joystick->nhats;
1411 }
1412 SDL_UnlockJoysticks();
1413
1414 return result;
1415}
1416
1417/*
1418 * Get the number of trackballs on a joystick
1419 */
1420int SDL_GetNumJoystickBalls(SDL_Joystick *joystick)
1421{
1422 CHECK_JOYSTICK_MAGIC(joystick, -1);
1423
1424 return joystick->nballs;
1425}
1426
1427/*
1428 * Get the number of buttons on a joystick
1429 */
1430int SDL_GetNumJoystickButtons(SDL_Joystick *joystick)
1431{
1432 int result;
1433
1434 SDL_LockJoysticks();
1435 {
1436 CHECK_JOYSTICK_MAGIC(joystick, -1);
1437
1438 result = joystick->nbuttons;
1439 }
1440 SDL_UnlockJoysticks();
1441
1442 return result;
1443}
1444
1445/*
1446 * Get the current state of an axis control on a joystick
1447 */
1448Sint16 SDL_GetJoystickAxis(SDL_Joystick *joystick, int axis)
1449{
1450 Sint16 state;
1451
1452 SDL_LockJoysticks();
1453 {
1454 CHECK_JOYSTICK_MAGIC(joystick, 0);
1455
1456 if (axis < joystick->naxes) {
1457 state = joystick->axes[axis].value;
1458 } else {
1459 SDL_SetError("Joystick only has %d axes", joystick->naxes);
1460 state = 0;
1461 }
1462 }
1463 SDL_UnlockJoysticks();
1464
1465 return state;
1466}
1467
1468/*
1469 * Get the initial state of an axis control on a joystick
1470 */
1471bool SDL_GetJoystickAxisInitialState(SDL_Joystick *joystick, int axis, Sint16 *state)
1472{
1473 bool result;
1474
1475 SDL_LockJoysticks();
1476 {
1477 CHECK_JOYSTICK_MAGIC(joystick, false);
1478
1479 if (axis >= joystick->naxes) {
1480 SDL_SetError("Joystick only has %d axes", joystick->naxes);
1481 result = false;
1482 } else {
1483 if (state) {
1484 *state = joystick->axes[axis].initial_value;
1485 }
1486 result = joystick->axes[axis].has_initial_value;
1487 }
1488 }
1489 SDL_UnlockJoysticks();
1490
1491 return result;
1492}
1493
1494/*
1495 * Get the current state of a hat on a joystick
1496 */
1497Uint8 SDL_GetJoystickHat(SDL_Joystick *joystick, int hat)
1498{
1499 Uint8 state;
1500
1501 SDL_LockJoysticks();
1502 {
1503 CHECK_JOYSTICK_MAGIC(joystick, 0);
1504
1505 if (hat < joystick->nhats) {
1506 state = joystick->hats[hat];
1507 } else {
1508 SDL_SetError("Joystick only has %d hats", joystick->nhats);
1509 state = 0;
1510 }
1511 }
1512 SDL_UnlockJoysticks();
1513
1514 return state;
1515}
1516
1517/*
1518 * Get the ball axis change since the last poll
1519 */
1520bool SDL_GetJoystickBall(SDL_Joystick *joystick, int ball, int *dx, int *dy)
1521{
1522 bool result;
1523
1524 SDL_LockJoysticks();
1525 {
1526 CHECK_JOYSTICK_MAGIC(joystick, false);
1527
1528 if (ball < joystick->nballs) {
1529 if (dx) {
1530 *dx = joystick->balls[ball].dx;
1531 }
1532 if (dy) {
1533 *dy = joystick->balls[ball].dy;
1534 }
1535 joystick->balls[ball].dx = 0;
1536 joystick->balls[ball].dy = 0;
1537 result = true;
1538 } else {
1539 result = SDL_SetError("Joystick only has %d balls", joystick->nballs);
1540 }
1541 }
1542 SDL_UnlockJoysticks();
1543
1544 return result;
1545}
1546
1547/*
1548 * Get the current state of a button on a joystick
1549 */
1550bool SDL_GetJoystickButton(SDL_Joystick *joystick, int button)
1551{
1552 bool down = false;
1553
1554 SDL_LockJoysticks();
1555 {
1556 CHECK_JOYSTICK_MAGIC(joystick, false);
1557
1558 if (button < joystick->nbuttons) {
1559 down = joystick->buttons[button];
1560 } else {
1561 SDL_SetError("Joystick only has %d buttons", joystick->nbuttons);
1562 }
1563 }
1564 SDL_UnlockJoysticks();
1565
1566 return down;
1567}
1568
1569/*
1570 * Return if the joystick in question is currently attached to the system,
1571 * \return false if not plugged in, true if still present.
1572 */
1573bool SDL_JoystickConnected(SDL_Joystick *joystick)
1574{
1575 bool result;
1576
1577 SDL_LockJoysticks();
1578 {
1579 CHECK_JOYSTICK_MAGIC(joystick, false);
1580
1581 result = joystick->attached;
1582 }
1583 SDL_UnlockJoysticks();
1584
1585 return result;
1586}
1587
1588/*
1589 * Get the instance id for this opened joystick
1590 */
1591SDL_JoystickID SDL_GetJoystickID(SDL_Joystick *joystick)
1592{
1593 SDL_JoystickID result;
1594
1595 SDL_LockJoysticks();
1596 {
1597 CHECK_JOYSTICK_MAGIC(joystick, 0);
1598
1599 result = joystick->instance_id;
1600 }
1601 SDL_UnlockJoysticks();
1602
1603 return result;
1604}
1605
1606/*
1607 * Return the SDL_Joystick associated with an instance id.
1608 */
1609SDL_Joystick *SDL_GetJoystickFromID(SDL_JoystickID instance_id)
1610{
1611 SDL_Joystick *joystick;
1612
1613 SDL_LockJoysticks();
1614 for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
1615 if (joystick->instance_id == instance_id) {
1616 break;
1617 }
1618 }
1619 SDL_UnlockJoysticks();
1620 return joystick;
1621}
1622
1623/**
1624 * Return the SDL_Joystick associated with a player index.
1625 */
1626SDL_Joystick *SDL_GetJoystickFromPlayerIndex(int player_index)
1627{
1628 SDL_JoystickID instance_id;
1629 SDL_Joystick *joystick;
1630
1631 SDL_LockJoysticks();
1632 instance_id = SDL_GetJoystickIDForPlayerIndex(player_index);
1633 for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
1634 if (joystick->instance_id == instance_id) {
1635 break;
1636 }
1637 }
1638 SDL_UnlockJoysticks();
1639 return joystick;
1640}
1641
1642/*
1643 * Get the properties associated with a joystick
1644 */
1645SDL_PropertiesID SDL_GetJoystickProperties(SDL_Joystick *joystick)
1646{
1647 SDL_PropertiesID result;
1648
1649 SDL_LockJoysticks();
1650 {
1651 CHECK_JOYSTICK_MAGIC(joystick, 0);
1652
1653 if (joystick->props == 0) {
1654 joystick->props = SDL_CreateProperties();
1655 }
1656 result = joystick->props;
1657 }
1658 SDL_UnlockJoysticks();
1659
1660 return result;
1661}
1662
1663/*
1664 * Get the friendly name of this joystick
1665 */
1666const char *SDL_GetJoystickName(SDL_Joystick *joystick)
1667{
1668 const char *result;
1669 const SDL_SteamVirtualGamepadInfo *info;
1670
1671 SDL_LockJoysticks();
1672 {
1673 CHECK_JOYSTICK_MAGIC(joystick, NULL);
1674
1675 info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);
1676 if (info) {
1677 result = SDL_GetPersistentString(info->name);
1678 } else {
1679 result = SDL_GetPersistentString(joystick->name);
1680 }
1681 }
1682 SDL_UnlockJoysticks();
1683
1684 return result;
1685}
1686
1687/*
1688 * Get the implementation dependent path of this joystick
1689 */
1690const char *SDL_GetJoystickPath(SDL_Joystick *joystick)
1691{
1692 const char *result;
1693
1694 SDL_LockJoysticks();
1695 {
1696 CHECK_JOYSTICK_MAGIC(joystick, NULL);
1697
1698 if (joystick->path) {
1699 result = SDL_GetPersistentString(joystick->path);
1700 } else {
1701 SDL_Unsupported();
1702 result = NULL;
1703 }
1704 }
1705 SDL_UnlockJoysticks();
1706
1707 return result;
1708}
1709
1710/**
1711 * Get the player index of an opened joystick, or -1 if it's not available
1712 */
1713int SDL_GetJoystickPlayerIndex(SDL_Joystick *joystick)
1714{
1715 int result;
1716
1717 SDL_LockJoysticks();
1718 {
1719 CHECK_JOYSTICK_MAGIC(joystick, -1);
1720
1721 result = SDL_GetPlayerIndexForJoystickID(joystick->instance_id);
1722 }
1723 SDL_UnlockJoysticks();
1724
1725 return result;
1726}
1727
1728/**
1729 * Set the player index of an opened joystick
1730 */
1731bool SDL_SetJoystickPlayerIndex(SDL_Joystick *joystick, int player_index)
1732{
1733 bool result;
1734
1735 SDL_LockJoysticks();
1736 {
1737 CHECK_JOYSTICK_MAGIC(joystick, false);
1738
1739 result = SDL_SetJoystickIDForPlayerIndex(player_index, joystick->instance_id);
1740 }
1741 SDL_UnlockJoysticks();
1742
1743 return result;
1744}
1745
1746bool SDL_RumbleJoystick(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
1747{
1748 bool result;
1749
1750 SDL_LockJoysticks();
1751 {
1752 CHECK_JOYSTICK_MAGIC(joystick, false);
1753
1754 if (low_frequency_rumble == joystick->low_frequency_rumble &&
1755 high_frequency_rumble == joystick->high_frequency_rumble) {
1756 // Just update the expiration
1757 result = true;
1758 } else {
1759 result = joystick->driver->Rumble(joystick, low_frequency_rumble, high_frequency_rumble);
1760 if (result) {
1761 joystick->rumble_resend = SDL_GetTicks() + SDL_RUMBLE_RESEND_MS;
1762 if (joystick->rumble_resend == 0) {
1763 joystick->rumble_resend = 1;
1764 }
1765 } else {
1766 joystick->rumble_resend = 0;
1767 }
1768 }
1769
1770 if (result) {
1771 joystick->low_frequency_rumble = low_frequency_rumble;
1772 joystick->high_frequency_rumble = high_frequency_rumble;
1773
1774 if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
1775 joystick->rumble_expiration = SDL_GetTicks() + SDL_min(duration_ms, SDL_MAX_RUMBLE_DURATION_MS);
1776 if (!joystick->rumble_expiration) {
1777 joystick->rumble_expiration = 1;
1778 }
1779 } else {
1780 joystick->rumble_expiration = 0;
1781 joystick->rumble_resend = 0;
1782 }
1783 }
1784 }
1785 SDL_UnlockJoysticks();
1786
1787 return result;
1788}
1789
1790bool SDL_RumbleJoystickTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms)
1791{
1792 bool result;
1793
1794 SDL_LockJoysticks();
1795 {
1796 CHECK_JOYSTICK_MAGIC(joystick, false);
1797
1798 if (left_rumble == joystick->left_trigger_rumble && right_rumble == joystick->right_trigger_rumble) {
1799 // Just update the expiration
1800 result = true;
1801 } else {
1802 result = joystick->driver->RumbleTriggers(joystick, left_rumble, right_rumble);
1803 }
1804
1805 if (result) {
1806 joystick->left_trigger_rumble = left_rumble;
1807 joystick->right_trigger_rumble = right_rumble;
1808
1809 if ((left_rumble || right_rumble) && duration_ms) {
1810 joystick->trigger_rumble_expiration = SDL_GetTicks() + SDL_min(duration_ms, SDL_MAX_RUMBLE_DURATION_MS);
1811 } else {
1812 joystick->trigger_rumble_expiration = 0;
1813 }
1814 }
1815 }
1816 SDL_UnlockJoysticks();
1817
1818 return result;
1819}
1820
1821bool SDL_SetJoystickLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
1822{
1823 bool result;
1824 bool isfreshvalue;
1825
1826 SDL_LockJoysticks();
1827 {
1828 CHECK_JOYSTICK_MAGIC(joystick, false);
1829
1830 isfreshvalue = red != joystick->led_red ||
1831 green != joystick->led_green ||
1832 blue != joystick->led_blue;
1833
1834 if (isfreshvalue || SDL_GetTicks() >= joystick->led_expiration) {
1835 result = joystick->driver->SetLED(joystick, red, green, blue);
1836 joystick->led_expiration = SDL_GetTicks() + SDL_LED_MIN_REPEAT_MS;
1837 } else {
1838 // Avoid spamming the driver
1839 result = true;
1840 }
1841
1842 // Save the LED value regardless of success, so we don't spam the driver
1843 joystick->led_red = red;
1844 joystick->led_green = green;
1845 joystick->led_blue = blue;
1846 }
1847 SDL_UnlockJoysticks();
1848
1849 return result;
1850}
1851
1852bool SDL_SendJoystickEffect(SDL_Joystick *joystick, const void *data, int size)
1853{
1854 bool result;
1855
1856 SDL_LockJoysticks();
1857 {
1858 CHECK_JOYSTICK_MAGIC(joystick, false);
1859
1860 result = joystick->driver->SendEffect(joystick, data, size);
1861 }
1862 SDL_UnlockJoysticks();
1863
1864 return result;
1865}
1866
1867/*
1868 * Close a joystick previously opened with SDL_OpenJoystick()
1869 */
1870void SDL_CloseJoystick(SDL_Joystick *joystick)
1871{
1872 SDL_Joystick *joysticklist;
1873 SDL_Joystick *joysticklistprev;
1874 int i;
1875
1876 SDL_LockJoysticks();
1877 {
1878 CHECK_JOYSTICK_MAGIC(joystick,);
1879
1880 // First decrement ref count
1881 if (--joystick->ref_count > 0) {
1882 SDL_UnlockJoysticks();
1883 return;
1884 }
1885
1886 SDL_DestroyProperties(joystick->props);
1887
1888 if (joystick->rumble_expiration) {
1889 SDL_RumbleJoystick(joystick, 0, 0, 0);
1890 }
1891 if (joystick->trigger_rumble_expiration) {
1892 SDL_RumbleJoystickTriggers(joystick, 0, 0, 0);
1893 }
1894
1895 CleanupSensorFusion(joystick);
1896
1897 joystick->driver->Close(joystick);
1898 joystick->hwdata = NULL;
1899 SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, false);
1900
1901 joysticklist = SDL_joysticks;
1902 joysticklistprev = NULL;
1903 while (joysticklist) {
1904 if (joystick == joysticklist) {
1905 if (joysticklistprev) {
1906 // unlink this entry
1907 joysticklistprev->next = joysticklist->next;
1908 } else {
1909 SDL_joysticks = joystick->next;
1910 }
1911 break;
1912 }
1913 joysticklistprev = joysticklist;
1914 joysticklist = joysticklist->next;
1915 }
1916
1917 // Free the data associated with this joystick
1918 SDL_free(joystick->name);
1919 SDL_free(joystick->path);
1920 SDL_free(joystick->serial);
1921 SDL_free(joystick->axes);
1922 SDL_free(joystick->balls);
1923 SDL_free(joystick->hats);
1924 SDL_free(joystick->buttons);
1925 for (i = 0; i < joystick->ntouchpads; i++) {
1926 SDL_JoystickTouchpadInfo *touchpad = &joystick->touchpads[i];
1927 SDL_free(touchpad->fingers);
1928 }
1929 SDL_free(joystick->touchpads);
1930 SDL_free(joystick->sensors);
1931 SDL_free(joystick);
1932 }
1933 SDL_UnlockJoysticks();
1934}
1935
1936void SDL_QuitJoysticks(void)
1937{
1938 int i;
1939 SDL_JoystickID *joysticks;
1940
1941 SDL_LockJoysticks();
1942
1943 SDL_joysticks_quitting = true;
1944
1945 joysticks = SDL_GetJoysticks(NULL);
1946 if (joysticks) {
1947 for (i = 0; joysticks[i]; ++i) {
1948 SDL_PrivateJoystickRemoved(joysticks[i]);
1949 }
1950 SDL_free(joysticks);
1951 }
1952
1953 while (SDL_joysticks) {
1954 SDL_joysticks->ref_count = 1;
1955 SDL_CloseJoystick(SDL_joysticks);
1956 }
1957
1958 // Quit drivers in reverse order to avoid breaking dependencies between drivers
1959 for (i = SDL_arraysize(SDL_joystick_drivers) - 1; i >= 0; --i) {
1960 SDL_joystick_drivers[i]->Quit();
1961 }
1962
1963 if (SDL_joystick_players) {
1964 SDL_free(SDL_joystick_players);
1965 SDL_joystick_players = NULL;
1966 SDL_joystick_player_count = 0;
1967 }
1968
1969 SDL_QuitSubSystem(SDL_INIT_EVENTS);
1970
1971 SDL_QuitSteamVirtualGamepadInfo();
1972
1973 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
1974 SDL_JoystickAllowBackgroundEventsChanged, NULL);
1975
1976 SDL_FreeVIDPIDList(&arcadestick_devices);
1977 SDL_FreeVIDPIDList(&blacklist_devices);
1978 SDL_FreeVIDPIDList(&flightstick_devices);
1979 SDL_FreeVIDPIDList(&gamecube_devices);
1980 SDL_FreeVIDPIDList(&rog_gamepad_mice);
1981 SDL_FreeVIDPIDList(&throttle_devices);
1982 SDL_FreeVIDPIDList(&wheel_devices);
1983 SDL_FreeVIDPIDList(&zero_centered_devices);
1984
1985 SDL_QuitGamepadMappings();
1986
1987 SDL_joysticks_quitting = false;
1988 SDL_joysticks_initialized = false;
1989
1990 SDL_UnlockJoysticks();
1991}
1992
1993static bool SDL_PrivateJoystickShouldIgnoreEvent(void)
1994{
1995 if (SDL_joystick_allows_background_events) {
1996 return false;
1997 }
1998
1999 if (SDL_HasWindows() && SDL_GetKeyboardFocus() == NULL) {
2000 // We have windows but we don't have focus, ignore the event.
2001 return true;
2002 }
2003 return false;
2004}
2005
2006// These are global for SDL_sysjoystick.c and SDL_events.c
2007
2008void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers)
2009{
2010 int ntouchpads;
2011 SDL_JoystickTouchpadInfo *touchpads;
2012
2013 SDL_AssertJoysticksLocked();
2014
2015 ntouchpads = joystick->ntouchpads + 1;
2016 touchpads = (SDL_JoystickTouchpadInfo *)SDL_realloc(joystick->touchpads, (ntouchpads * sizeof(SDL_JoystickTouchpadInfo)));
2017 if (touchpads) {
2018 SDL_JoystickTouchpadInfo *touchpad = &touchpads[ntouchpads - 1];
2019 SDL_JoystickTouchpadFingerInfo *fingers = (SDL_JoystickTouchpadFingerInfo *)SDL_calloc(nfingers, sizeof(SDL_JoystickTouchpadFingerInfo));
2020
2021 if (fingers) {
2022 touchpad->nfingers = nfingers;
2023 touchpad->fingers = fingers;
2024 } else {
2025 // Out of memory, this touchpad won't be active
2026 touchpad->nfingers = 0;
2027 touchpad->fingers = NULL;
2028 }
2029
2030 joystick->ntouchpads = ntouchpads;
2031 joystick->touchpads = touchpads;
2032 }
2033}
2034
2035void SDL_PrivateJoystickAddSensor(SDL_Joystick *joystick, SDL_SensorType type, float rate)
2036{
2037 int nsensors;
2038 SDL_JoystickSensorInfo *sensors;
2039
2040 SDL_AssertJoysticksLocked();
2041
2042 nsensors = joystick->nsensors + 1;
2043 sensors = (SDL_JoystickSensorInfo *)SDL_realloc(joystick->sensors, (nsensors * sizeof(SDL_JoystickSensorInfo)));
2044 if (sensors) {
2045 SDL_JoystickSensorInfo *sensor = &sensors[nsensors - 1];
2046
2047 SDL_zerop(sensor);
2048 sensor->type = type;
2049 sensor->rate = rate;
2050
2051 joystick->nsensors = nsensors;
2052 joystick->sensors = sensors;
2053 }
2054}
2055
2056void SDL_PrivateJoystickSensorRate(SDL_Joystick *joystick, SDL_SensorType type, float rate)
2057{
2058 int i;
2059 SDL_AssertJoysticksLocked();
2060
2061 for (i = 0; i < joystick->nsensors; ++i) {
2062 if (joystick->sensors[i].type == type) {
2063 joystick->sensors[i].rate = rate;
2064 }
2065 }
2066}
2067
2068void SDL_PrivateJoystickAdded(SDL_JoystickID instance_id)
2069{
2070 SDL_JoystickDriver *driver;
2071 int device_index;
2072 int player_index = -1;
2073
2074 SDL_AssertJoysticksLocked();
2075
2076 if (SDL_JoysticksQuitting()) {
2077 return;
2078 }
2079
2080 SDL_joystick_being_added = true;
2081
2082 if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
2083 player_index = driver->GetDeviceSteamVirtualGamepadSlot(device_index);
2084 if (player_index < 0) {
2085 player_index = driver->GetDevicePlayerIndex(device_index);
2086 }
2087 }
2088 if (player_index < 0 && SDL_IsGamepad(instance_id)) {
2089 player_index = SDL_FindFreePlayerIndex();
2090 }
2091 if (player_index >= 0) {
2092 SDL_SetJoystickIDForPlayerIndex(player_index, instance_id);
2093 }
2094
2095 {
2096 SDL_Event event;
2097
2098 event.type = SDL_EVENT_JOYSTICK_ADDED;
2099 event.common.timestamp = 0;
2100
2101 if (SDL_EventEnabled(event.type)) {
2102 event.jdevice.which = instance_id;
2103 SDL_PushEvent(&event);
2104 }
2105 }
2106
2107 SDL_joystick_being_added = false;
2108
2109 if (SDL_IsGamepad(instance_id)) {
2110 SDL_PrivateGamepadAdded(instance_id);
2111 }
2112}
2113
2114bool SDL_IsJoystickBeingAdded(void)
2115{
2116 return SDL_joystick_being_added;
2117}
2118
2119void SDL_PrivateJoystickForceRecentering(SDL_Joystick *joystick)
2120{
2121 Uint8 i, j;
2122 Uint64 timestamp = SDL_GetTicksNS();
2123
2124 SDL_AssertJoysticksLocked();
2125
2126 // Tell the app that everything is centered/unpressed...
2127 for (i = 0; i < joystick->naxes; i++) {
2128 if (joystick->axes[i].has_initial_value) {
2129 SDL_SendJoystickAxis(timestamp, joystick, i, joystick->axes[i].zero);
2130 }
2131 }
2132
2133 for (i = 0; i < joystick->nbuttons; i++) {
2134 SDL_SendJoystickButton(timestamp, joystick, i, false);
2135 }
2136
2137 for (i = 0; i < joystick->nhats; i++) {
2138 SDL_SendJoystickHat(timestamp, joystick, i, SDL_HAT_CENTERED);
2139 }
2140
2141 for (i = 0; i < joystick->ntouchpads; i++) {
2142 SDL_JoystickTouchpadInfo *touchpad = &joystick->touchpads[i];
2143
2144 for (j = 0; j < touchpad->nfingers; ++j) {
2145 SDL_SendJoystickTouchpad(timestamp, joystick, i, j, false, 0.0f, 0.0f, 0.0f);
2146 }
2147 }
2148}
2149
2150void SDL_PrivateJoystickRemoved(SDL_JoystickID instance_id)
2151{
2152 SDL_Joystick *joystick = NULL;
2153 int player_index;
2154 SDL_Event event;
2155
2156 SDL_AssertJoysticksLocked();
2157
2158 // Find this joystick...
2159 for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
2160 if (joystick->instance_id == instance_id) {
2161 SDL_PrivateJoystickForceRecentering(joystick);
2162 joystick->attached = false;
2163 break;
2164 }
2165 }
2166
2167 if (SDL_IsGamepad(instance_id)) {
2168 SDL_PrivateGamepadRemoved(instance_id);
2169 }
2170
2171 event.type = SDL_EVENT_JOYSTICK_REMOVED;
2172 event.common.timestamp = 0;
2173
2174 if (SDL_EventEnabled(event.type)) {
2175 event.jdevice.which = instance_id;
2176 SDL_PushEvent(&event);
2177 }
2178
2179 player_index = SDL_GetPlayerIndexForJoystickID(instance_id);
2180 if (player_index >= 0) {
2181 SDL_joystick_players[player_index] = 0;
2182 }
2183}
2184
2185void SDL_SendJoystickAxis(Uint64 timestamp, SDL_Joystick *joystick, Uint8 axis, Sint16 value)
2186{
2187 SDL_JoystickAxisInfo *info;
2188
2189 SDL_AssertJoysticksLocked();
2190
2191 // Make sure we're not getting garbage or duplicate events
2192 if (axis >= joystick->naxes) {
2193 return;
2194 }
2195
2196 info = &joystick->axes[axis];
2197 if (!info->has_initial_value ||
2198 (!info->has_second_value && (info->initial_value <= -32767 || info->initial_value == 32767) && SDL_abs(value) < (SDL_JOYSTICK_AXIS_MAX / 4))) {
2199 info->initial_value = value;
2200 info->value = value;
2201 info->zero = value;
2202 info->has_initial_value = true;
2203 } else if (value == info->value && !info->sending_initial_value) {
2204 return;
2205 } else {
2206 info->has_second_value = true;
2207 }
2208 if (!info->sent_initial_value) {
2209 // Make sure we don't send motion until there's real activity on this axis
2210 const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; // ShanWan PS3 controller needed 96
2211 if (SDL_abs(value - info->value) <= MAX_ALLOWED_JITTER &&
2212 !SDL_IsJoystickVIRTUAL(joystick->guid)) {
2213 return;
2214 }
2215 info->sent_initial_value = true;
2216 info->sending_initial_value = true;
2217 SDL_SendJoystickAxis(timestamp, joystick, axis, info->initial_value);
2218 info->sending_initial_value = false;
2219 }
2220
2221 /* We ignore events if we don't have keyboard focus, except for centering
2222 * events.
2223 */
2224 if (SDL_PrivateJoystickShouldIgnoreEvent()) {
2225 if (info->sending_initial_value ||
2226 (value > info->zero && value >= info->value) ||
2227 (value < info->zero && value <= info->value)) {
2228 return;
2229 }
2230 }
2231
2232 // Update internal joystick state
2233 SDL_assert(timestamp != 0);
2234 info->value = value;
2235 joystick->update_complete = timestamp;
2236
2237 // Post the event, if desired
2238 if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_AXIS_MOTION)) {
2239 SDL_Event event;
2240 event.type = SDL_EVENT_JOYSTICK_AXIS_MOTION;
2241 event.common.timestamp = timestamp;
2242 event.jaxis.which = joystick->instance_id;
2243 event.jaxis.axis = axis;
2244 event.jaxis.value = value;
2245 SDL_PushEvent(&event);
2246 }
2247}
2248
2249void SDL_SendJoystickBall(Uint64 timestamp, SDL_Joystick *joystick, Uint8 ball, Sint16 xrel, Sint16 yrel)
2250{
2251 SDL_AssertJoysticksLocked();
2252
2253 // Make sure we're not getting garbage events
2254 if (ball >= joystick->nballs) {
2255 return;
2256 }
2257
2258 // We ignore events if we don't have keyboard focus.
2259 if (SDL_PrivateJoystickShouldIgnoreEvent()) {
2260 return;
2261 }
2262
2263 // Update internal mouse state
2264 joystick->balls[ball].dx += xrel;
2265 joystick->balls[ball].dy += yrel;
2266
2267 // Post the event, if desired
2268 if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_BALL_MOTION)) {
2269 SDL_Event event;
2270 event.type = SDL_EVENT_JOYSTICK_BALL_MOTION;
2271 event.common.timestamp = timestamp;
2272 event.jball.which = joystick->instance_id;
2273 event.jball.ball = ball;
2274 event.jball.xrel = xrel;
2275 event.jball.yrel = yrel;
2276 SDL_PushEvent(&event);
2277 }
2278}
2279
2280void SDL_SendJoystickHat(Uint64 timestamp, SDL_Joystick *joystick, Uint8 hat, Uint8 value)
2281{
2282 SDL_AssertJoysticksLocked();
2283
2284 // Make sure we're not getting garbage or duplicate events
2285 if (hat >= joystick->nhats) {
2286 return;
2287 }
2288 if (value == joystick->hats[hat]) {
2289 return;
2290 }
2291
2292 /* We ignore events if we don't have keyboard focus, except for centering
2293 * events.
2294 */
2295 if (SDL_PrivateJoystickShouldIgnoreEvent()) {
2296 if (value != SDL_HAT_CENTERED) {
2297 return;
2298 }
2299 }
2300
2301 // Update internal joystick state
2302 SDL_assert(timestamp != 0);
2303 joystick->hats[hat] = value;
2304 joystick->update_complete = timestamp;
2305
2306 // Post the event, if desired
2307 if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_HAT_MOTION)) {
2308 SDL_Event event;
2309 event.type = SDL_EVENT_JOYSTICK_HAT_MOTION;
2310 event.common.timestamp = timestamp;
2311 event.jhat.which = joystick->instance_id;
2312 event.jhat.hat = hat;
2313 event.jhat.value = value;
2314 SDL_PushEvent(&event);
2315 }
2316}
2317
2318void SDL_SendJoystickButton(Uint64 timestamp, SDL_Joystick *joystick, Uint8 button, bool down)
2319{
2320 SDL_Event event;
2321
2322 SDL_AssertJoysticksLocked();
2323
2324 if (down) {
2325 event.type = SDL_EVENT_JOYSTICK_BUTTON_DOWN;
2326 } else {
2327 event.type = SDL_EVENT_JOYSTICK_BUTTON_UP;
2328 }
2329
2330 if (joystick->swap_face_buttons) {
2331 switch (button) {
2332 case 0:
2333 button = 1;
2334 break;
2335 case 1:
2336 button = 0;
2337 break;
2338 case 2:
2339 button = 3;
2340 break;
2341 case 3:
2342 button = 2;
2343 break;
2344 default:
2345 break;
2346 }
2347 }
2348
2349 // Make sure we're not getting garbage or duplicate events
2350 if (button >= joystick->nbuttons) {
2351 return;
2352 }
2353 if (down == joystick->buttons[button]) {
2354 return;
2355 }
2356
2357 /* We ignore events if we don't have keyboard focus, except for button
2358 * release. */
2359 if (SDL_PrivateJoystickShouldIgnoreEvent()) {
2360 if (down) {
2361 return;
2362 }
2363 }
2364
2365 // Update internal joystick state
2366 SDL_assert(timestamp != 0);
2367 joystick->buttons[button] = down;
2368 joystick->update_complete = timestamp;
2369
2370 // Post the event, if desired
2371 if (SDL_EventEnabled(event.type)) {
2372 event.common.timestamp = timestamp;
2373 event.jbutton.which = joystick->instance_id;
2374 event.jbutton.button = button;
2375 event.jbutton.down = down;
2376 SDL_PushEvent(&event);
2377 }
2378}
2379
2380static void SendSteamHandleUpdateEvents(void)
2381{
2382 SDL_Joystick *joystick;
2383 const SDL_SteamVirtualGamepadInfo *info;
2384
2385 // Check to see if any Steam handles changed
2386 for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
2387 bool changed = false;
2388
2389 if (!SDL_IsGamepad(joystick->instance_id)) {
2390 continue;
2391 }
2392
2393 info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);
2394 if (info) {
2395 if (joystick->steam_handle != info->handle) {
2396 joystick->steam_handle = info->handle;
2397 joystick->swap_face_buttons = ShouldSwapFaceButtons(info);
2398 changed = true;
2399 }
2400 } else {
2401 if (joystick->steam_handle != 0) {
2402 joystick->steam_handle = 0;
2403 joystick->swap_face_buttons = false;
2404 changed = true;
2405 }
2406 }
2407 if (changed) {
2408 SDL_Event event;
2409
2410 SDL_zero(event);
2411 event.type = SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED;
2412 event.common.timestamp = 0;
2413 event.gdevice.which = joystick->instance_id;
2414 SDL_PushEvent(&event);
2415 }
2416 }
2417}
2418
2419void SDL_UpdateJoysticks(void)
2420{
2421 int i;
2422 Uint64 now;
2423 SDL_Joystick *joystick;
2424
2425 if (!SDL_WasInit(SDL_INIT_JOYSTICK)) {
2426 return;
2427 }
2428
2429 SDL_LockJoysticks();
2430
2431 if (SDL_UpdateSteamVirtualGamepadInfo()) {
2432 SendSteamHandleUpdateEvents();
2433 }
2434
2435#ifdef SDL_JOYSTICK_HIDAPI
2436 // Special function for HIDAPI devices, as a single device can provide multiple SDL_Joysticks
2437 HIDAPI_UpdateDevices();
2438#endif // SDL_JOYSTICK_HIDAPI
2439
2440 for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
2441 if (!joystick->attached) {
2442 continue;
2443 }
2444
2445 joystick->driver->Update(joystick);
2446
2447 if (joystick->delayed_guide_button) {
2448 SDL_GamepadHandleDelayedGuideButton(joystick);
2449 }
2450
2451 now = SDL_GetTicks();
2452 if (joystick->rumble_expiration && now >= joystick->rumble_expiration) {
2453 SDL_RumbleJoystick(joystick, 0, 0, 0);
2454 joystick->rumble_resend = 0;
2455 }
2456
2457 if (joystick->rumble_resend && now >= joystick->rumble_resend) {
2458 joystick->driver->Rumble(joystick, joystick->low_frequency_rumble, joystick->high_frequency_rumble);
2459 joystick->rumble_resend = now + SDL_RUMBLE_RESEND_MS;
2460 if (joystick->rumble_resend == 0) {
2461 joystick->rumble_resend = 1;
2462 }
2463 }
2464
2465 if (joystick->trigger_rumble_expiration && now >= joystick->trigger_rumble_expiration) {
2466 SDL_RumbleJoystickTriggers(joystick, 0, 0, 0);
2467 }
2468 }
2469
2470 if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_UPDATE_COMPLETE)) {
2471 for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
2472 if (joystick->update_complete) {
2473 SDL_Event event;
2474
2475 event.type = SDL_EVENT_JOYSTICK_UPDATE_COMPLETE;
2476 event.common.timestamp = joystick->update_complete;
2477 event.jdevice.which = joystick->instance_id;
2478 SDL_PushEvent(&event);
2479
2480 joystick->update_complete = 0;
2481 }
2482 }
2483 }
2484
2485 /* this needs to happen AFTER walking the joystick list above, so that any
2486 dangling hardware data from removed devices can be free'd
2487 */
2488 for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
2489 SDL_joystick_drivers[i]->Detect();
2490 }
2491
2492 SDL_UnlockJoysticks();
2493}
2494
2495static const Uint32 SDL_joystick_event_list[] = {
2496 SDL_EVENT_JOYSTICK_AXIS_MOTION,
2497 SDL_EVENT_JOYSTICK_BALL_MOTION,
2498 SDL_EVENT_JOYSTICK_HAT_MOTION,
2499 SDL_EVENT_JOYSTICK_BUTTON_DOWN,
2500 SDL_EVENT_JOYSTICK_BUTTON_UP,
2501 SDL_EVENT_JOYSTICK_ADDED,
2502 SDL_EVENT_JOYSTICK_REMOVED,
2503 SDL_EVENT_JOYSTICK_BATTERY_UPDATED
2504};
2505
2506void SDL_SetJoystickEventsEnabled(bool enabled)
2507{
2508 unsigned int i;
2509
2510 for (i = 0; i < SDL_arraysize(SDL_joystick_event_list); ++i) {
2511 SDL_SetEventEnabled(SDL_joystick_event_list[i], enabled);
2512 }
2513}
2514
2515bool SDL_JoystickEventsEnabled(void)
2516{
2517 bool enabled = false;
2518 unsigned int i;
2519
2520 for (i = 0; i < SDL_arraysize(SDL_joystick_event_list); ++i) {
2521 enabled = SDL_EventEnabled(SDL_joystick_event_list[i]);
2522 if (enabled) {
2523 break;
2524 }
2525 }
2526 return enabled;
2527}
2528
2529void SDL_GetJoystickGUIDInfo(SDL_GUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version, Uint16 *crc16)
2530{
2531 Uint16 *guid16 = (Uint16 *)guid.data;
2532 Uint16 bus = SDL_Swap16LE(guid16[0]);
2533
2534 if ((bus < ' ' || bus == SDL_HARDWARE_BUS_VIRTUAL) && guid16[3] == 0x0000 && guid16[5] == 0x0000) {
2535 /* This GUID fits the standard form:
2536 * 16-bit bus
2537 * 16-bit CRC16 of the joystick name (can be zero)
2538 * 16-bit vendor ID
2539 * 16-bit zero
2540 * 16-bit product ID
2541 * 16-bit zero
2542 * 16-bit version
2543 * 8-bit driver identifier ('h' for HIDAPI, 'x' for XInput, etc.)
2544 * 8-bit driver-dependent type info
2545 */
2546 if (vendor) {
2547 *vendor = SDL_Swap16LE(guid16[2]);
2548 }
2549 if (product) {
2550 *product = SDL_Swap16LE(guid16[4]);
2551 }
2552 if (version) {
2553 *version = SDL_Swap16LE(guid16[6]);
2554 }
2555 if (crc16) {
2556 *crc16 = SDL_Swap16LE(guid16[1]);
2557 }
2558 } else if (bus < ' ' || bus == SDL_HARDWARE_BUS_VIRTUAL) {
2559 /* This GUID fits the unknown VID/PID form:
2560 * 16-bit bus
2561 * 16-bit CRC16 of the joystick name (can be zero)
2562 * 11 characters of the joystick name, null terminated
2563 */
2564 if (vendor) {
2565 *vendor = 0;
2566 }
2567 if (product) {
2568 *product = 0;
2569 }
2570 if (version) {
2571 *version = 0;
2572 }
2573 if (crc16) {
2574 *crc16 = SDL_Swap16LE(guid16[1]);
2575 }
2576 } else {
2577 if (vendor) {
2578 *vendor = 0;
2579 }
2580 if (product) {
2581 *product = 0;
2582 }
2583 if (version) {
2584 *version = 0;
2585 }
2586 if (crc16) {
2587 *crc16 = 0;
2588 }
2589 }
2590}
2591
2592char *SDL_CreateJoystickName(Uint16 vendor, Uint16 product, const char *vendor_name, const char *product_name)
2593{
2594 const char *custom_name = GuessControllerName(vendor, product);
2595 if (custom_name) {
2596 return SDL_strdup(custom_name);
2597 }
2598
2599 return SDL_CreateDeviceName(vendor, product, vendor_name, product_name, "Controller");
2600}
2601
2602SDL_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)
2603{
2604 SDL_GUID guid;
2605 Uint16 *guid16 = (Uint16 *)guid.data;
2606 Uint16 crc = 0;
2607
2608 SDL_zero(guid);
2609
2610 if (vendor_name && *vendor_name && product_name && *product_name) {
2611 crc = SDL_crc16(crc, vendor_name, SDL_strlen(vendor_name));
2612 crc = SDL_crc16(crc, " ", 1);
2613 crc = SDL_crc16(crc, product_name, SDL_strlen(product_name));
2614 } else if (product_name) {
2615 crc = SDL_crc16(crc, product_name, SDL_strlen(product_name));
2616 }
2617
2618 // We only need 16 bits for each of these; space them out to fill 128.
2619 // Byteswap so devices get same GUID on little/big endian platforms.
2620 *guid16++ = SDL_Swap16LE(bus);
2621 *guid16++ = SDL_Swap16LE(crc);
2622
2623 if (vendor) {
2624 *guid16++ = SDL_Swap16LE(vendor);
2625 *guid16++ = 0;
2626 *guid16++ = SDL_Swap16LE(product);
2627 *guid16++ = 0;
2628 *guid16++ = SDL_Swap16LE(version);
2629 guid.data[14] = driver_signature;
2630 guid.data[15] = driver_data;
2631 } else {
2632 size_t available_space = sizeof(guid.data) - 4;
2633
2634 if (driver_signature) {
2635 available_space -= 2;
2636 guid.data[14] = driver_signature;
2637 guid.data[15] = driver_data;
2638 }
2639 if (product_name) {
2640 SDL_strlcpy((char *)guid16, product_name, available_space);
2641 }
2642 }
2643 return guid;
2644}
2645
2646SDL_GUID SDL_CreateJoystickGUIDForName(const char *name)
2647{
2648 return SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_UNKNOWN, 0, 0, 0, NULL, name, 0, 0);
2649}
2650
2651void SDL_SetJoystickGUIDVendor(SDL_GUID *guid, Uint16 vendor)
2652{
2653 Uint16 *guid16 = (Uint16 *)guid->data;
2654
2655 guid16[2] = SDL_Swap16LE(vendor);
2656}
2657
2658void SDL_SetJoystickGUIDProduct(SDL_GUID *guid, Uint16 product)
2659{
2660 Uint16 *guid16 = (Uint16 *)guid->data;
2661
2662 guid16[4] = SDL_Swap16LE(product);
2663}
2664
2665void SDL_SetJoystickGUIDVersion(SDL_GUID *guid, Uint16 version)
2666{
2667 Uint16 *guid16 = (Uint16 *)guid->data;
2668
2669 guid16[6] = SDL_Swap16LE(version);
2670}
2671
2672void SDL_SetJoystickGUIDCRC(SDL_GUID *guid, Uint16 crc)
2673{
2674 Uint16 *guid16 = (Uint16 *)guid->data;
2675
2676 guid16[1] = SDL_Swap16LE(crc);
2677}
2678
2679SDL_GamepadType SDL_GetGamepadTypeFromVIDPID(Uint16 vendor, Uint16 product, const char *name, bool forUI)
2680{
2681 SDL_GamepadType type = SDL_GAMEPAD_TYPE_STANDARD;
2682
2683 if (vendor == 0x0000 && product == 0x0000) {
2684 // Some devices are only identifiable by their name
2685 if (name &&
2686 (SDL_strcmp(name, "Lic Pro Controller") == 0 ||
2687 SDL_strcmp(name, "Nintendo Wireless Gamepad") == 0 ||
2688 SDL_strcmp(name, "Wireless Gamepad") == 0)) {
2689 // HORI or PowerA Switch Pro Controller clone
2690 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
2691 }
2692
2693 } else if (vendor == 0x0001 && product == 0x0001) {
2694 type = SDL_GAMEPAD_TYPE_STANDARD;
2695
2696 } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT) {
2697 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
2698
2699 } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) {
2700 if (name && SDL_strstr(name, "NES Controller") != NULL) {
2701 // We don't have a type for the Nintendo Online NES Controller
2702 type = SDL_GAMEPAD_TYPE_STANDARD;
2703 } else {
2704 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
2705 }
2706
2707 } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
2708 if (name && SDL_strstr(name, "(L)") != NULL) {
2709 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
2710 } else {
2711 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
2712 }
2713
2714 } else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR) {
2715 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR;
2716
2717 } else if (forUI && SDL_IsJoystickGameCube(vendor, product)) {
2718 // We don't have a type for the Nintendo GameCube controller
2719 type = SDL_GAMEPAD_TYPE_STANDARD;
2720
2721 } else {
2722 switch (GuessControllerType(vendor, product)) {
2723 case k_eControllerType_XBox360Controller:
2724 type = SDL_GAMEPAD_TYPE_XBOX360;
2725 break;
2726 case k_eControllerType_XBoxOneController:
2727 type = SDL_GAMEPAD_TYPE_XBOXONE;
2728 break;
2729 case k_eControllerType_PS3Controller:
2730 type = SDL_GAMEPAD_TYPE_PS3;
2731 break;
2732 case k_eControllerType_PS4Controller:
2733 type = SDL_GAMEPAD_TYPE_PS4;
2734 break;
2735 case k_eControllerType_PS5Controller:
2736 type = SDL_GAMEPAD_TYPE_PS5;
2737 break;
2738 case k_eControllerType_XInputPS4Controller:
2739 if (forUI) {
2740 type = SDL_GAMEPAD_TYPE_PS4;
2741 } else {
2742 type = SDL_GAMEPAD_TYPE_STANDARD;
2743 }
2744 break;
2745 case k_eControllerType_SwitchProController:
2746 case k_eControllerType_SwitchInputOnlyController:
2747 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
2748 break;
2749 case k_eControllerType_XInputSwitchController:
2750 if (forUI) {
2751 type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
2752 } else {
2753 type = SDL_GAMEPAD_TYPE_STANDARD;
2754 }
2755 break;
2756 default:
2757 break;
2758 }
2759 }
2760 return type;
2761}
2762
2763SDL_GamepadType SDL_GetGamepadTypeFromGUID(SDL_GUID guid, const char *name)
2764{
2765 SDL_GamepadType type;
2766 Uint16 vendor, product;
2767
2768 SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
2769 type = SDL_GetGamepadTypeFromVIDPID(vendor, product, name, true);
2770 if (type == SDL_GAMEPAD_TYPE_STANDARD) {
2771 if (SDL_IsJoystickXInput(guid)) {
2772 // This is probably an Xbox One controller
2773 return SDL_GAMEPAD_TYPE_XBOXONE;
2774 }
2775#ifdef SDL_JOYSTICK_HIDAPI
2776 if (SDL_IsJoystickHIDAPI(guid)) {
2777 return HIDAPI_GetGamepadTypeFromGUID(guid);
2778 }
2779#endif // SDL_JOYSTICK_HIDAPI
2780 }
2781 return type;
2782}
2783
2784bool SDL_JoystickGUIDUsesVersion(SDL_GUID guid)
2785{
2786 Uint16 vendor, product;
2787
2788 if (SDL_IsJoystickMFI(guid)) {
2789 // The version bits are used as button capability mask
2790 return false;
2791 }
2792
2793 SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
2794 if (vendor && product) {
2795 return true;
2796 }
2797 return false;
2798}
2799
2800bool SDL_IsJoystickXboxOne(Uint16 vendor_id, Uint16 product_id)
2801{
2802 EControllerType eType = GuessControllerType(vendor_id, product_id);
2803 return eType == k_eControllerType_XBoxOneController;
2804}
2805
2806bool SDL_IsJoystickXboxOneElite(Uint16 vendor_id, Uint16 product_id)
2807{
2808 if (vendor_id == USB_VENDOR_MICROSOFT) {
2809 if (product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_1 ||
2810 product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2 ||
2811 product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH ||
2812 product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLE) {
2813 return true;
2814 }
2815 }
2816 return false;
2817}
2818
2819bool SDL_IsJoystickXboxSeriesX(Uint16 vendor_id, Uint16 product_id)
2820{
2821 if (vendor_id == USB_VENDOR_MICROSOFT) {
2822 if (product_id == USB_PRODUCT_XBOX_SERIES_X ||
2823 product_id == USB_PRODUCT_XBOX_SERIES_X_BLE) {
2824 return true;
2825 }
2826 }
2827 if (vendor_id == USB_VENDOR_PDP) {
2828 if (product_id == USB_PRODUCT_XBOX_SERIES_X_VICTRIX_GAMBIT ||
2829 product_id == USB_PRODUCT_XBOX_SERIES_X_PDP_BLUE ||
2830 product_id == USB_PRODUCT_XBOX_SERIES_X_PDP_AFTERGLOW) {
2831 return true;
2832 }
2833 }
2834 if (vendor_id == USB_VENDOR_POWERA_ALT) {
2835 if ((product_id >= 0x2001 && product_id <= 0x201a) ||
2836 product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO2 ||
2837 product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO4 ||
2838 product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO_WIRELESS_USB ||
2839 product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO_WIRELESS_DONGLE ||
2840 product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_MOGA_XP_ULTRA ||
2841 product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_SPECTRA) {
2842 return true;
2843 }
2844 }
2845 if (vendor_id == USB_VENDOR_HORI) {
2846 if (product_id == USB_PRODUCT_HORI_FIGHTING_COMMANDER_OCTA_SERIES_X ||
2847 product_id == USB_PRODUCT_HORI_HORIPAD_PRO_SERIES_X) {
2848 return true;
2849 }
2850 }
2851 if (vendor_id == USB_VENDOR_HP) {
2852 if (product_id == USB_PRODUCT_XBOX_SERIES_X_HP_HYPERX ||
2853 product_id == USB_PRODUCT_XBOX_SERIES_X_HP_HYPERX_RGB) {
2854 return true;
2855 }
2856 }
2857 if (vendor_id == USB_VENDOR_RAZER) {
2858 if (product_id == USB_PRODUCT_RAZER_WOLVERINE_V2 ||
2859 product_id == USB_PRODUCT_RAZER_WOLVERINE_V2_CHROMA ||
2860 product_id == USB_PRODUCT_RAZER_WOLVERINE_V3_PRO) {
2861 return true;
2862 }
2863 }
2864 if (vendor_id == USB_VENDOR_THRUSTMASTER) {
2865 if (product_id == USB_PRODUCT_THRUSTMASTER_ESWAPX_PRO_SERIES_X) {
2866 return true;
2867 }
2868 }
2869 if (vendor_id == USB_VENDOR_TURTLE_BEACH) {
2870 if (product_id == USB_PRODUCT_TURTLE_BEACH_SERIES_X_REACT_R ||
2871 product_id == USB_PRODUCT_TURTLE_BEACH_SERIES_X_RECON) {
2872 return true;
2873 }
2874 }
2875 if (vendor_id == USB_VENDOR_8BITDO) {
2876 if (product_id == USB_PRODUCT_8BITDO_XBOX_CONTROLLER1 ||
2877 product_id == USB_PRODUCT_8BITDO_XBOX_CONTROLLER2) {
2878 return true;
2879 }
2880 }
2881 if (vendor_id == USB_VENDOR_GAMESIR) {
2882 if (product_id == USB_PRODUCT_GAMESIR_G7) {
2883 return true;
2884 }
2885 }
2886 if (vendor_id == USB_VENDOR_ASUS) {
2887 if (product_id == USB_PRODUCT_ROG_RAIKIRI) {
2888 return true;
2889 }
2890 }
2891 return false;
2892}
2893
2894bool SDL_IsJoystickBluetoothXboxOne(Uint16 vendor_id, Uint16 product_id)
2895{
2896 if (vendor_id == USB_VENDOR_MICROSOFT) {
2897 if (product_id == USB_PRODUCT_XBOX_ONE_ADAPTIVE_BLUETOOTH ||
2898 product_id == USB_PRODUCT_XBOX_ONE_ADAPTIVE_BLE ||
2899 product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH ||
2900 product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH ||
2901 product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLE ||
2902 product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH ||
2903 product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLE ||
2904 product_id == USB_PRODUCT_XBOX_SERIES_X_BLE) {
2905 return true;
2906 }
2907 }
2908 return false;
2909}
2910
2911bool SDL_IsJoystickPS4(Uint16 vendor_id, Uint16 product_id)
2912{
2913 EControllerType eType = GuessControllerType(vendor_id, product_id);
2914 return eType == k_eControllerType_PS4Controller;
2915}
2916
2917bool SDL_IsJoystickPS5(Uint16 vendor_id, Uint16 product_id)
2918{
2919 EControllerType eType = GuessControllerType(vendor_id, product_id);
2920 return eType == k_eControllerType_PS5Controller;
2921}
2922
2923bool SDL_IsJoystickDualSenseEdge(Uint16 vendor_id, Uint16 product_id)
2924{
2925 if (vendor_id == USB_VENDOR_SONY) {
2926 if (product_id == USB_PRODUCT_SONY_DS5_EDGE) {
2927 return true;
2928 }
2929 }
2930 return false;
2931}
2932
2933bool SDL_IsJoystickNintendoSwitchPro(Uint16 vendor_id, Uint16 product_id)
2934{
2935 EControllerType eType = GuessControllerType(vendor_id, product_id);
2936 return eType == k_eControllerType_SwitchProController || eType == k_eControllerType_SwitchInputOnlyController;
2937}
2938
2939bool SDL_IsJoystickNintendoSwitchProInputOnly(Uint16 vendor_id, Uint16 product_id)
2940{
2941 EControllerType eType = GuessControllerType(vendor_id, product_id);
2942 return eType == k_eControllerType_SwitchInputOnlyController;
2943}
2944
2945bool SDL_IsJoystickNintendoSwitchJoyCon(Uint16 vendor_id, Uint16 product_id)
2946{
2947 EControllerType eType = GuessControllerType(vendor_id, product_id);
2948 return eType == k_eControllerType_SwitchJoyConLeft || eType == k_eControllerType_SwitchJoyConRight;
2949}
2950
2951bool SDL_IsJoystickNintendoSwitchJoyConLeft(Uint16 vendor_id, Uint16 product_id)
2952{
2953 EControllerType eType = GuessControllerType(vendor_id, product_id);
2954 return eType == k_eControllerType_SwitchJoyConLeft;
2955}
2956
2957bool SDL_IsJoystickNintendoSwitchJoyConRight(Uint16 vendor_id, Uint16 product_id)
2958{
2959 EControllerType eType = GuessControllerType(vendor_id, product_id);
2960 return eType == k_eControllerType_SwitchJoyConRight;
2961}
2962
2963bool SDL_IsJoystickNintendoSwitchJoyConGrip(Uint16 vendor_id, Uint16 product_id)
2964{
2965 return vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP;
2966}
2967
2968bool SDL_IsJoystickNintendoSwitchJoyConPair(Uint16 vendor_id, Uint16 product_id)
2969{
2970 return vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR;
2971}
2972
2973bool SDL_IsJoystickGameCube(Uint16 vendor_id, Uint16 product_id)
2974{
2975 return SDL_VIDPIDInList(vendor_id, product_id, &gamecube_devices);
2976}
2977
2978bool SDL_IsJoystickAmazonLunaController(Uint16 vendor_id, Uint16 product_id)
2979{
2980 return ((vendor_id == USB_VENDOR_AMAZON && product_id == USB_PRODUCT_AMAZON_LUNA_CONTROLLER) ||
2981 (vendor_id == BLUETOOTH_VENDOR_AMAZON && product_id == BLUETOOTH_PRODUCT_LUNA_CONTROLLER));
2982}
2983
2984bool SDL_IsJoystickGoogleStadiaController(Uint16 vendor_id, Uint16 product_id)
2985{
2986 return vendor_id == USB_VENDOR_GOOGLE && product_id == USB_PRODUCT_GOOGLE_STADIA_CONTROLLER;
2987}
2988
2989bool SDL_IsJoystickNVIDIASHIELDController(Uint16 vendor_id, Uint16 product_id)
2990{
2991 return (vendor_id == USB_VENDOR_NVIDIA &&
2992 (product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103 ||
2993 product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V104));
2994}
2995
2996bool SDL_IsJoystickSteamVirtualGamepad(Uint16 vendor_id, Uint16 product_id, Uint16 version)
2997{
2998#ifdef SDL_PLATFORM_MACOS
2999 return (vendor_id == USB_VENDOR_MICROSOFT && product_id == USB_PRODUCT_XBOX360_WIRED_CONTROLLER && version == 0);
3000#else
3001 return (vendor_id == USB_VENDOR_VALVE && product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD);
3002#endif
3003}
3004
3005bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id)
3006{
3007 EControllerType eType = GuessControllerType(vendor_id, product_id);
3008 return eType == k_eControllerType_SteamController || eType == k_eControllerType_SteamControllerV2;
3009}
3010
3011bool SDL_IsJoystickHoriSteamController(Uint16 vendor_id, Uint16 product_id)
3012{
3013 return vendor_id == USB_VENDOR_HORI && (product_id == USB_PRODUCT_HORI_STEAM_CONTROLLER || product_id == USB_PRODUCT_HORI_STEAM_CONTROLLER_BT);
3014}
3015
3016bool SDL_IsJoystickSteamDeck(Uint16 vendor_id, Uint16 product_id)
3017{
3018 EControllerType eType = GuessControllerType(vendor_id, product_id);
3019 return eType == k_eControllerType_SteamControllerNeptune;
3020}
3021
3022bool SDL_IsJoystickXInput(SDL_GUID guid)
3023{
3024 return (guid.data[14] == 'x') ? true : false;
3025}
3026
3027bool SDL_IsJoystickWGI(SDL_GUID guid)
3028{
3029 return (guid.data[14] == 'w') ? true : false;
3030}
3031
3032bool SDL_IsJoystickHIDAPI(SDL_GUID guid)
3033{
3034 return (guid.data[14] == 'h') ? true : false;
3035}
3036
3037bool SDL_IsJoystickMFI(SDL_GUID guid)
3038{
3039 return (guid.data[14] == 'm') ? true : false;
3040}
3041
3042bool SDL_IsJoystickRAWINPUT(SDL_GUID guid)
3043{
3044 return (guid.data[14] == 'r') ? true : false;
3045}
3046
3047bool SDL_IsJoystickVIRTUAL(SDL_GUID guid)
3048{
3049 return (guid.data[14] == 'v') ? true : false;
3050}
3051
3052bool SDL_IsJoystickWheel(Uint16 vendor_id, Uint16 product_id)
3053{
3054 return SDL_VIDPIDInList(vendor_id, product_id, &wheel_devices);
3055}
3056
3057static bool SDL_IsJoystickArcadeStick(Uint16 vendor_id, Uint16 product_id)
3058{
3059 return SDL_VIDPIDInList(vendor_id, product_id, &arcadestick_devices);
3060}
3061
3062static bool SDL_IsJoystickFlightStick(Uint16 vendor_id, Uint16 product_id)
3063{
3064 return SDL_VIDPIDInList(vendor_id, product_id, &flightstick_devices);
3065}
3066
3067static bool SDL_IsJoystickThrottle(Uint16 vendor_id, Uint16 product_id)
3068{
3069 return SDL_VIDPIDInList(vendor_id, product_id, &throttle_devices);
3070}
3071
3072static SDL_JoystickType SDL_GetJoystickGUIDType(SDL_GUID guid)
3073{
3074 Uint16 vendor;
3075 Uint16 product;
3076
3077 SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
3078
3079 if (SDL_IsJoystickWheel(vendor, product)) {
3080 return SDL_JOYSTICK_TYPE_WHEEL;
3081 }
3082
3083 if (SDL_IsJoystickArcadeStick(vendor, product)) {
3084 return SDL_JOYSTICK_TYPE_ARCADE_STICK;
3085 }
3086
3087 if (SDL_IsJoystickFlightStick(vendor, product)) {
3088 return SDL_JOYSTICK_TYPE_FLIGHT_STICK;
3089 }
3090
3091 if (SDL_IsJoystickThrottle(vendor, product)) {
3092 return SDL_JOYSTICK_TYPE_THROTTLE;
3093 }
3094
3095 if (SDL_IsJoystickXInput(guid)) {
3096 // XInput GUID, get the type based on the XInput device subtype
3097 switch (guid.data[15]) {
3098 case 0x01: // XINPUT_DEVSUBTYPE_GAMEPAD
3099 return SDL_JOYSTICK_TYPE_GAMEPAD;
3100 case 0x02: // XINPUT_DEVSUBTYPE_WHEEL
3101 return SDL_JOYSTICK_TYPE_WHEEL;
3102 case 0x03: // XINPUT_DEVSUBTYPE_ARCADE_STICK
3103 return SDL_JOYSTICK_TYPE_ARCADE_STICK;
3104 case 0x04: // XINPUT_DEVSUBTYPE_FLIGHT_STICK
3105 return SDL_JOYSTICK_TYPE_FLIGHT_STICK;
3106 case 0x05: // XINPUT_DEVSUBTYPE_DANCE_PAD
3107 return SDL_JOYSTICK_TYPE_DANCE_PAD;
3108 case 0x06: // XINPUT_DEVSUBTYPE_GUITAR
3109 case 0x07: // XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE
3110 case 0x0B: // XINPUT_DEVSUBTYPE_GUITAR_BASS
3111 return SDL_JOYSTICK_TYPE_GUITAR;
3112 case 0x08: // XINPUT_DEVSUBTYPE_DRUM_KIT
3113 return SDL_JOYSTICK_TYPE_DRUM_KIT;
3114 case 0x13: // XINPUT_DEVSUBTYPE_ARCADE_PAD
3115 return SDL_JOYSTICK_TYPE_ARCADE_PAD;
3116 default:
3117 return SDL_JOYSTICK_TYPE_UNKNOWN;
3118 }
3119 }
3120
3121 if (SDL_IsJoystickWGI(guid)) {
3122 return (SDL_JoystickType)guid.data[15];
3123 }
3124
3125 if (SDL_IsJoystickVIRTUAL(guid)) {
3126 return (SDL_JoystickType)guid.data[15];
3127 }
3128
3129#ifdef SDL_JOYSTICK_HIDAPI
3130 if (SDL_IsJoystickHIDAPI(guid)) {
3131 return HIDAPI_GetJoystickTypeFromGUID(guid);
3132 }
3133#endif // SDL_JOYSTICK_HIDAPI
3134
3135 if (GuessControllerType(vendor, product) != k_eControllerType_UnknownNonSteamController) {
3136 return SDL_JOYSTICK_TYPE_GAMEPAD;
3137 }
3138
3139 return SDL_JOYSTICK_TYPE_UNKNOWN;
3140}
3141
3142bool SDL_ShouldIgnoreJoystick(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
3143{
3144 // Check the joystick blacklist
3145 if (SDL_VIDPIDInList(vendor_id, product_id, &blacklist_devices)) {
3146 return true;
3147 }
3148 if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_ROG_CHAKRAM, false)) {
3149 if (SDL_VIDPIDInList(vendor_id, product_id, &rog_gamepad_mice)) {
3150 return true;
3151 }
3152 }
3153
3154 if (SDL_ShouldIgnoreGamepad(vendor_id, product_id, version, name)) {
3155 return true;
3156 }
3157
3158 return false;
3159}
3160
3161// return the guid for this index
3162SDL_GUID SDL_GetJoystickGUIDForID(SDL_JoystickID instance_id)
3163{
3164 SDL_JoystickDriver *driver;
3165 int device_index;
3166 SDL_GUID guid;
3167
3168 SDL_LockJoysticks();
3169 if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
3170 guid = driver->GetDeviceGUID(device_index);
3171 } else {
3172 SDL_zero(guid);
3173 }
3174 SDL_UnlockJoysticks();
3175
3176 return guid;
3177}
3178
3179Uint16 SDL_GetJoystickVendorForID(SDL_JoystickID instance_id)
3180{
3181 Uint16 vendor;
3182 const SDL_SteamVirtualGamepadInfo *info;
3183
3184 SDL_LockJoysticks();
3185 info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);
3186 if (info) {
3187 vendor = info->vendor_id;
3188 } else {
3189 SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
3190
3191 SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
3192 }
3193 SDL_UnlockJoysticks();
3194
3195 return vendor;
3196}
3197
3198Uint16 SDL_GetJoystickProductForID(SDL_JoystickID instance_id)
3199{
3200 Uint16 product;
3201 const SDL_SteamVirtualGamepadInfo *info;
3202
3203 SDL_LockJoysticks();
3204 info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);
3205 if (info) {
3206 product = info->product_id;
3207 } else {
3208 SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
3209
3210 SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
3211 }
3212 SDL_UnlockJoysticks();
3213
3214 return product;
3215}
3216
3217Uint16 SDL_GetJoystickProductVersionForID(SDL_JoystickID instance_id)
3218{
3219 Uint16 version;
3220 SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
3221
3222 SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version, NULL);
3223 return version;
3224}
3225
3226SDL_JoystickType SDL_GetJoystickTypeForID(SDL_JoystickID instance_id)
3227{
3228 SDL_JoystickType type;
3229 SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
3230
3231 type = SDL_GetJoystickGUIDType(guid);
3232 if (type == SDL_JOYSTICK_TYPE_UNKNOWN) {
3233 if (SDL_IsGamepad(instance_id)) {
3234 type = SDL_JOYSTICK_TYPE_GAMEPAD;
3235 }
3236 }
3237 return type;
3238}
3239
3240SDL_GUID SDL_GetJoystickGUID(SDL_Joystick *joystick)
3241{
3242 SDL_GUID result;
3243
3244 SDL_LockJoysticks();
3245 {
3246 static SDL_GUID emptyGUID;
3247
3248 CHECK_JOYSTICK_MAGIC(joystick, emptyGUID);
3249
3250 result = joystick->guid;
3251 }
3252 SDL_UnlockJoysticks();
3253
3254 return result;
3255}
3256
3257Uint16 SDL_GetJoystickVendor(SDL_Joystick *joystick)
3258{
3259 Uint16 vendor;
3260 const SDL_SteamVirtualGamepadInfo *info;
3261
3262 SDL_LockJoysticks();
3263 {
3264 CHECK_JOYSTICK_MAGIC(joystick, 0);
3265
3266 info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);
3267 if (info) {
3268 vendor = info->vendor_id;
3269 } else {
3270 SDL_GUID guid = SDL_GetJoystickGUID(joystick);
3271
3272 SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
3273 }
3274 }
3275 SDL_UnlockJoysticks();
3276
3277 return vendor;
3278}
3279
3280Uint16 SDL_GetJoystickProduct(SDL_Joystick *joystick)
3281{
3282 Uint16 product;
3283 const SDL_SteamVirtualGamepadInfo *info;
3284
3285 SDL_LockJoysticks();
3286 {
3287 CHECK_JOYSTICK_MAGIC(joystick, 0);
3288
3289 info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);
3290 if (info) {
3291 product = info->product_id;
3292 } else {
3293 SDL_GUID guid = SDL_GetJoystickGUID(joystick);
3294
3295 SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
3296 }
3297 }
3298 SDL_UnlockJoysticks();
3299
3300 return product;
3301}
3302
3303Uint16 SDL_GetJoystickProductVersion(SDL_Joystick *joystick)
3304{
3305 Uint16 version;
3306 SDL_GUID guid = SDL_GetJoystickGUID(joystick);
3307
3308 SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version, NULL);
3309 return version;
3310}
3311
3312Uint16 SDL_GetJoystickFirmwareVersion(SDL_Joystick *joystick)
3313{
3314 Uint16 result;
3315
3316 SDL_LockJoysticks();
3317 {
3318 CHECK_JOYSTICK_MAGIC(joystick, 0);
3319
3320 result = joystick->firmware_version;
3321 }
3322 SDL_UnlockJoysticks();
3323
3324 return result;
3325}
3326
3327const char *SDL_GetJoystickSerial(SDL_Joystick *joystick)
3328{
3329 const char *result;
3330
3331 SDL_LockJoysticks();
3332 {
3333 CHECK_JOYSTICK_MAGIC(joystick, NULL);
3334
3335 result = SDL_GetPersistentString(joystick->serial);
3336 }
3337 SDL_UnlockJoysticks();
3338
3339 return result;
3340}
3341
3342SDL_JoystickType SDL_GetJoystickType(SDL_Joystick *joystick)
3343{
3344 SDL_JoystickType type;
3345 SDL_GUID guid = SDL_GetJoystickGUID(joystick);
3346
3347 type = SDL_GetJoystickGUIDType(guid);
3348 if (type == SDL_JOYSTICK_TYPE_UNKNOWN) {
3349 SDL_LockJoysticks();
3350 {
3351 CHECK_JOYSTICK_MAGIC(joystick, SDL_JOYSTICK_TYPE_UNKNOWN);
3352
3353 if (SDL_IsGamepad(joystick->instance_id)) {
3354 type = SDL_JOYSTICK_TYPE_GAMEPAD;
3355 }
3356 }
3357 SDL_UnlockJoysticks();
3358 }
3359 return type;
3360}
3361
3362void SDL_SendJoystickPowerInfo(SDL_Joystick *joystick, SDL_PowerState state, int percent)
3363{
3364 SDL_AssertJoysticksLocked();
3365
3366 if (state != joystick->battery_state || percent != joystick->battery_percent) {
3367 joystick->battery_state = state;
3368 joystick->battery_percent = percent;
3369
3370 if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_BATTERY_UPDATED)) {
3371 SDL_Event event;
3372 event.type = SDL_EVENT_JOYSTICK_BATTERY_UPDATED;
3373 event.common.timestamp = 0;
3374 event.jbattery.which = joystick->instance_id;
3375 event.jbattery.state = state;
3376 event.jbattery.percent = percent;
3377 SDL_PushEvent(&event);
3378 }
3379 }
3380}
3381
3382SDL_JoystickConnectionState SDL_GetJoystickConnectionState(SDL_Joystick *joystick)
3383{
3384 SDL_JoystickConnectionState result;
3385
3386 SDL_LockJoysticks();
3387 {
3388 CHECK_JOYSTICK_MAGIC(joystick, SDL_JOYSTICK_CONNECTION_INVALID);
3389
3390 result = joystick->connection_state;
3391 }
3392 SDL_UnlockJoysticks();
3393
3394 return result;
3395}
3396
3397SDL_PowerState SDL_GetJoystickPowerInfo(SDL_Joystick *joystick, int *percent)
3398{
3399 SDL_PowerState result;
3400
3401 if (percent) {
3402 *percent = -1;
3403 }
3404
3405 SDL_LockJoysticks();
3406 {
3407 CHECK_JOYSTICK_MAGIC(joystick, SDL_POWERSTATE_ERROR);
3408
3409 result = joystick->battery_state;
3410
3411 if (percent) {
3412 *percent = joystick->battery_percent;
3413 }
3414 }
3415 SDL_UnlockJoysticks();
3416
3417 return result;
3418}
3419
3420void SDL_SendJoystickTouchpad(Uint64 timestamp, SDL_Joystick *joystick, int touchpad, int finger, bool down, float x, float y, float pressure)
3421{
3422 SDL_JoystickTouchpadInfo *touchpad_info;
3423 SDL_JoystickTouchpadFingerInfo *finger_info;
3424 Uint32 event_type;
3425
3426 SDL_AssertJoysticksLocked();
3427
3428 if (touchpad < 0 || touchpad >= joystick->ntouchpads) {
3429 return;
3430 }
3431
3432 touchpad_info = &joystick->touchpads[touchpad];
3433 if (finger < 0 || finger >= touchpad_info->nfingers) {
3434 return;
3435 }
3436
3437 finger_info = &touchpad_info->fingers[finger];
3438
3439 if (!down) {
3440 if (x == 0.0f && y == 0.0f) {
3441 x = finger_info->x;
3442 y = finger_info->y;
3443 }
3444 pressure = 0.0f;
3445 }
3446
3447 if (x < 0.0f) {
3448 x = 0.0f;
3449 } else if (x > 1.0f) {
3450 x = 1.0f;
3451 }
3452 if (y < 0.0f) {
3453 y = 0.0f;
3454 } else if (y > 1.0f) {
3455 y = 1.0f;
3456 }
3457 if (pressure < 0.0f) {
3458 pressure = 0.0f;
3459 } else if (pressure > 1.0f) {
3460 pressure = 1.0f;
3461 }
3462
3463 if (down == finger_info->down) {
3464 if (!down ||
3465 (x == finger_info->x && y == finger_info->y && pressure == finger_info->pressure)) {
3466 return;
3467 }
3468 }
3469
3470 if (down == finger_info->down) {
3471 event_type = SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION;
3472 } else if (down) {
3473 event_type = SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN;
3474 } else {
3475 event_type = SDL_EVENT_GAMEPAD_TOUCHPAD_UP;
3476 }
3477
3478 // We ignore events if we don't have keyboard focus, except for touch release
3479 if (SDL_PrivateJoystickShouldIgnoreEvent()) {
3480 if (event_type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP) {
3481 return;
3482 }
3483 }
3484
3485 // Update internal joystick state
3486 SDL_assert(timestamp != 0);
3487 finger_info->down = down;
3488 finger_info->x = x;
3489 finger_info->y = y;
3490 finger_info->pressure = pressure;
3491 joystick->update_complete = timestamp;
3492
3493 // Post the event, if desired
3494 if (SDL_EventEnabled(event_type)) {
3495 SDL_Event event;
3496 event.type = event_type;
3497 event.common.timestamp = timestamp;
3498 event.gtouchpad.which = joystick->instance_id;
3499 event.gtouchpad.touchpad = touchpad;
3500 event.gtouchpad.finger = finger;
3501 event.gtouchpad.x = x;
3502 event.gtouchpad.y = y;
3503 event.gtouchpad.pressure = pressure;
3504 SDL_PushEvent(&event);
3505 }
3506}
3507
3508void SDL_SendJoystickSensor(Uint64 timestamp, SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values)
3509{
3510 SDL_AssertJoysticksLocked();
3511
3512 // We ignore events if we don't have keyboard focus
3513 if (SDL_PrivateJoystickShouldIgnoreEvent()) {
3514 return;
3515 }
3516
3517 for (int i = 0; i < joystick->nsensors; ++i) {
3518 SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
3519
3520 if (sensor->type == type) {
3521 if (sensor->enabled) {
3522 num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
3523
3524 // Update internal sensor state
3525 SDL_memcpy(sensor->data, data, num_values * sizeof(*data));
3526 joystick->update_complete = timestamp;
3527
3528 // Post the event, if desired
3529 if (SDL_EventEnabled(SDL_EVENT_GAMEPAD_SENSOR_UPDATE)) {
3530 SDL_Event event;
3531 event.type = SDL_EVENT_GAMEPAD_SENSOR_UPDATE;
3532 event.common.timestamp = timestamp;
3533 event.gsensor.which = joystick->instance_id;
3534 event.gsensor.sensor = type;
3535 num_values = SDL_min(num_values,
3536 SDL_arraysize(event.gsensor.data));
3537 SDL_memset(event.gsensor.data, 0,
3538 sizeof(event.gsensor.data));
3539 SDL_memcpy(event.gsensor.data, data,
3540 num_values * sizeof(*data));
3541 event.gsensor.sensor_timestamp = sensor_timestamp;
3542 SDL_PushEvent(&event);
3543 }
3544 }
3545 break;
3546 }
3547 }
3548}
3549
3550static void SDL_LoadVIDPIDListFromHint(const char *hint, int *num_entries, int *max_entries, Uint32 **entries)
3551{
3552 Uint32 entry;
3553 char *spot;
3554 char *file = NULL;
3555
3556 if (hint && *hint == '@') {
3557 spot = file = (char *)SDL_LoadFile(hint + 1, NULL);
3558 } else {
3559 spot = (char *)hint;
3560 }
3561
3562 if (!spot) {
3563 return;
3564 }
3565
3566 while ((spot = SDL_strstr(spot, "0x")) != NULL) {
3567 entry = (Uint16)SDL_strtol(spot, &spot, 0);
3568 entry <<= 16;
3569 spot = SDL_strstr(spot, "0x");
3570 if (!spot) {
3571 break;
3572 }
3573 entry |= (Uint16)SDL_strtol(spot, &spot, 0);
3574
3575 if (*num_entries == *max_entries) {
3576 int new_max_entries = *max_entries + 16;
3577 Uint32 *new_entries = (Uint32 *)SDL_realloc(*entries, new_max_entries * sizeof(**entries));
3578 if (!new_entries) {
3579 // Out of memory, go with what we have already
3580 break;
3581 }
3582 *entries = new_entries;
3583 *max_entries = new_max_entries;
3584 }
3585 (*entries)[(*num_entries)++] = entry;
3586 }
3587
3588 if (file) {
3589 SDL_free(file);
3590 }
3591}
3592
3593void SDL_LoadVIDPIDListFromHints(SDL_vidpid_list *list, const char *included_list, const char *excluded_list)
3594{
3595 // Empty the list
3596 list->num_included_entries = 0;
3597 list->num_excluded_entries = 0;
3598
3599 // Add the initial entries
3600 if (list->num_initial_entries > 0) {
3601 if (list->num_included_entries < list->num_initial_entries) {
3602 Uint32 *entries = (Uint32 *)SDL_malloc(list->num_initial_entries * sizeof(*entries));
3603 if (entries) {
3604 SDL_memcpy(entries, list->initial_entries, list->num_initial_entries * sizeof(*entries));
3605 list->included_entries = entries;
3606 list->num_included_entries = list->num_initial_entries;
3607 list->max_included_entries = list->num_initial_entries;
3608 }
3609 }
3610 }
3611
3612 // Add the included entries from the hint
3613 SDL_LoadVIDPIDListFromHint(included_list, &list->num_included_entries, &list->max_included_entries, &list->included_entries);
3614
3615 // Add the excluded entries from the hint
3616 SDL_LoadVIDPIDListFromHint(excluded_list, &list->num_excluded_entries, &list->max_excluded_entries, &list->excluded_entries);
3617}
3618
3619static void SDLCALL SDL_VIDPIDIncludedHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
3620{
3621 SDL_vidpid_list *list = (SDL_vidpid_list *)userdata;
3622 const char *included_list = hint;
3623 const char *excluded_list = NULL;
3624
3625 if (!list->initialized) {
3626 return;
3627 }
3628
3629 if (list->excluded_hint_name) {
3630 excluded_list = SDL_GetHint(list->excluded_hint_name);
3631 }
3632 SDL_LoadVIDPIDListFromHints(list, included_list, excluded_list);
3633}
3634
3635static void SDLCALL SDL_VIDPIDExcludedHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
3636{
3637 SDL_vidpid_list *list = (SDL_vidpid_list *)userdata;
3638 const char *included_list = NULL;
3639 const char *excluded_list = hint;
3640
3641 if (!list->initialized) {
3642 return;
3643 }
3644
3645 if (list->included_hint_name) {
3646 included_list = SDL_GetHint(list->included_hint_name);
3647 }
3648 SDL_LoadVIDPIDListFromHints(list, included_list, excluded_list);
3649}
3650
3651void SDL_LoadVIDPIDList(SDL_vidpid_list *list)
3652{
3653 const char *included_list = NULL;
3654 const char *excluded_list = NULL;
3655
3656 if (list->included_hint_name) {
3657 SDL_AddHintCallback(list->included_hint_name, SDL_VIDPIDIncludedHintChanged, list);
3658 }
3659
3660 if (list->excluded_hint_name) {
3661 SDL_AddHintCallback(list->excluded_hint_name, SDL_VIDPIDExcludedHintChanged, list);
3662 }
3663
3664 list->initialized = true;
3665
3666 if (list->included_hint_name) {
3667 included_list = SDL_GetHint(list->included_hint_name);
3668 }
3669 if (list->excluded_hint_name) {
3670 excluded_list = SDL_GetHint(list->excluded_hint_name);
3671 }
3672 SDL_LoadVIDPIDListFromHints(list, included_list, excluded_list);
3673}
3674
3675bool SDL_VIDPIDInList(Uint16 vendor_id, Uint16 product_id, const SDL_vidpid_list *list)
3676{
3677 int i;
3678 Uint32 vidpid = MAKE_VIDPID(vendor_id, product_id);
3679
3680 for (i = 0; i < list->num_excluded_entries; ++i) {
3681 if (vidpid == list->excluded_entries[i]) {
3682 return false;
3683 }
3684 }
3685 for (i = 0; i < list->num_included_entries; ++i) {
3686 if (vidpid == list->included_entries[i]) {
3687 return true;
3688 }
3689 }
3690 return false;
3691}
3692
3693void SDL_FreeVIDPIDList(SDL_vidpid_list *list)
3694{
3695 if (list->included_hint_name) {
3696 SDL_RemoveHintCallback(list->included_hint_name, SDL_VIDPIDIncludedHintChanged, list);
3697 }
3698
3699 if (list->excluded_hint_name) {
3700 SDL_RemoveHintCallback(list->excluded_hint_name, SDL_VIDPIDExcludedHintChanged, list);
3701 }
3702
3703 if (list->included_entries) {
3704 SDL_free(list->included_entries);
3705 list->included_entries = NULL;
3706 list->num_included_entries = 0;
3707 list->max_included_entries = 0;
3708 }
3709
3710 if (list->excluded_entries) {
3711 SDL_free(list->excluded_entries);
3712 list->excluded_entries = NULL;
3713 list->num_excluded_entries = 0;
3714 list->max_excluded_entries = 0;
3715 }
3716
3717 list->initialized = false;
3718}
3719