Path: blob/master/thirdparty/sdl/joystick/SDL_joystick.c
9902 views
/*1Simple DirectMedia Layer2Copyright (C) 1997-2025 Sam Lantinga <[email protected]>34This software is provided 'as-is', without any express or implied5warranty. In no event will the authors be held liable for any damages6arising from the use of this software.78Permission is granted to anyone to use this software for any purpose,9including commercial applications, and to alter it and redistribute it10freely, subject to the following restrictions:11121. The origin of this software must not be misrepresented; you must not13claim that you wrote the original software. If you use this software14in a product, an acknowledgment in the product documentation would be15appreciated but is not required.162. Altered source versions must be plainly marked as such, and must not be17misrepresented as being the original software.183. This notice may not be removed or altered from any source distribution.19*/20#include "SDL_internal.h"2122// This is the joystick API for Simple DirectMedia Layer2324#include "SDL_sysjoystick.h"25#include "../SDL_hints_c.h"26#include "SDL_gamepad_c.h"27#include "SDL_joystick_c.h"28#include "SDL_steam_virtual_gamepad.h"2930#include "../events/SDL_events_c.h"31//#include "../video/SDL_sysvideo.h"32#include "../sensor/SDL_sensor_c.h"33#include "hidapi/SDL_hidapijoystick_c.h"3435// This is included in only one place because it has a large static list of controllers36#include "controller_type.h"3738#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)39// Needed for checking for input remapping programs40#include "../core/windows/SDL_windows.h"4142#undef UNICODE // We want ASCII functions43#include <tlhelp32.h>44#endif4546#ifdef SDL_JOYSTICK_VIRTUAL47#include "./virtual/SDL_virtualjoystick_c.h"48#endif4950static SDL_JoystickDriver *SDL_joystick_drivers[] = {51#ifdef SDL_JOYSTICK_HIDAPI // Highest priority driver for supported devices52&SDL_HIDAPI_JoystickDriver,53#endif54#ifdef SDL_JOYSTICK_PRIVATE55&SDL_PRIVATE_JoystickDriver,56#endif57#ifdef SDL_JOYSTICK_GAMEINPUT // Higher priority than other Windows drivers58&SDL_GAMEINPUT_JoystickDriver,59#endif60#ifdef SDL_JOYSTICK_RAWINPUT // Before WINDOWS driver, as WINDOWS wants to check if this driver is handling things61&SDL_RAWINPUT_JoystickDriver,62#endif63#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) // Before WGI driver, as WGI wants to check if this driver is handling things64&SDL_WINDOWS_JoystickDriver,65#endif66#ifdef SDL_JOYSTICK_WGI67&SDL_WGI_JoystickDriver,68#endif69#ifdef SDL_JOYSTICK_WINMM70&SDL_WINMM_JoystickDriver,71#endif72#ifdef SDL_JOYSTICK_LINUX73&SDL_LINUX_JoystickDriver,74#endif75#ifdef SDL_JOYSTICK_IOKIT76&SDL_DARWIN_JoystickDriver,77#endif78#if (defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)) && !defined(SDL_JOYSTICK_DISABLED)79&SDL_IOS_JoystickDriver,80#endif81#ifdef SDL_JOYSTICK_ANDROID82&SDL_ANDROID_JoystickDriver,83#endif84#ifdef SDL_JOYSTICK_EMSCRIPTEN85&SDL_EMSCRIPTEN_JoystickDriver,86#endif87#ifdef SDL_JOYSTICK_HAIKU88&SDL_HAIKU_JoystickDriver,89#endif90#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. */91&SDL_BSD_JoystickDriver,92#endif93#ifdef SDL_JOYSTICK_PS294&SDL_PS2_JoystickDriver,95#endif96#ifdef SDL_JOYSTICK_PSP97&SDL_PSP_JoystickDriver,98#endif99#ifdef SDL_JOYSTICK_VIRTUAL100&SDL_VIRTUAL_JoystickDriver,101#endif102#ifdef SDL_JOYSTICK_VITA103&SDL_VITA_JoystickDriver,104#endif105#ifdef SDL_JOYSTICK_N3DS106&SDL_N3DS_JoystickDriver,107#endif108#if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED)109&SDL_DUMMY_JoystickDriver110#endif111};112113#ifndef SDL_THREAD_SAFETY_ANALYSIS114static115#endif116SDL_Mutex *SDL_joystick_lock = NULL; // This needs to support recursive locks117static SDL_AtomicInt SDL_joystick_lock_pending;118static int SDL_joysticks_locked;119static bool SDL_joysticks_initialized;120static bool SDL_joysticks_quitting;121static bool SDL_joystick_being_added;122static SDL_Joystick *SDL_joysticks SDL_GUARDED_BY(SDL_joystick_lock) = NULL;123static int SDL_joystick_player_count SDL_GUARDED_BY(SDL_joystick_lock) = 0;124static SDL_JoystickID *SDL_joystick_players SDL_GUARDED_BY(SDL_joystick_lock) = NULL;125static bool SDL_joystick_allows_background_events = false;126127static Uint32 initial_arcadestick_devices[] = {128MAKE_VIDPID(0x0079, 0x181a), // Venom Arcade Stick129MAKE_VIDPID(0x0079, 0x181b), // Venom Arcade Stick130MAKE_VIDPID(0x0c12, 0x0ef6), // Hitbox Arcade Stick131MAKE_VIDPID(0x0e6f, 0x0109), // PDP Versus Fighting Pad132MAKE_VIDPID(0x0f0d, 0x0016), // Hori Real Arcade Pro.EX133MAKE_VIDPID(0x0f0d, 0x001b), // Hori Real Arcade Pro VX134MAKE_VIDPID(0x0f0d, 0x0063), // Hori Real Arcade Pro Hayabusa (USA) Xbox One135MAKE_VIDPID(0x0f0d, 0x006a), // Real Arcade Pro 4136MAKE_VIDPID(0x0f0d, 0x0078), // Hori Real Arcade Pro V Kai Xbox One137MAKE_VIDPID(0x0f0d, 0x008a), // HORI Real Arcade Pro 4138MAKE_VIDPID(0x0f0d, 0x008c), // Hori Real Arcade Pro 4139MAKE_VIDPID(0x0f0d, 0x00aa), // HORI Real Arcade Pro V Hayabusa in Switch Mode140MAKE_VIDPID(0x0f0d, 0x00ed), // Hori Fighting Stick mini 4 kai141MAKE_VIDPID(0x0f0d, 0x011c), // Hori Fighting Stick Alpha in PS4 Mode142MAKE_VIDPID(0x0f0d, 0x011e), // Hori Fighting Stick Alpha in PC Mode143MAKE_VIDPID(0x0f0d, 0x0184), // Hori Fighting Stick Alpha in PS5 Mode144MAKE_VIDPID(0x146b, 0x0604), // NACON Daija Arcade Stick145MAKE_VIDPID(0x1532, 0x0a00), // Razer Atrox Arcade Stick146MAKE_VIDPID(0x1bad, 0xf03d), // Street Fighter IV Arcade Stick TE - Chun Li147MAKE_VIDPID(0x1bad, 0xf502), // Hori Real Arcade Pro.VX SA148MAKE_VIDPID(0x1bad, 0xf504), // Hori Real Arcade Pro. EX149MAKE_VIDPID(0x1bad, 0xf506), // Hori Real Arcade Pro.EX Premium VLX150MAKE_VIDPID(0x20d6, 0xa715), // PowerA Nintendo Switch Fusion Arcade Stick151MAKE_VIDPID(0x24c6, 0x5000), // Razer Atrox Arcade Stick152MAKE_VIDPID(0x24c6, 0x5501), // Hori Real Arcade Pro VX-SA153MAKE_VIDPID(0x24c6, 0x550e), // Hori Real Arcade Pro V Kai 360154MAKE_VIDPID(0x2c22, 0x2300), // Qanba Obsidian Arcade Joystick in PS4 Mode155MAKE_VIDPID(0x2c22, 0x2302), // Qanba Obsidian Arcade Joystick in PS3 Mode156MAKE_VIDPID(0x2c22, 0x2303), // Qanba Obsidian Arcade Joystick in PC Mode157MAKE_VIDPID(0x2c22, 0x2500), // Qanba Dragon Arcade Joystick in PS4 Mode158MAKE_VIDPID(0x2c22, 0x2502), // Qanba Dragon Arcade Joystick in PS3 Mode159MAKE_VIDPID(0x2c22, 0x2503), // Qanba Dragon Arcade Joystick in PC Mode160};161static SDL_vidpid_list arcadestick_devices = {162SDL_HINT_JOYSTICK_ARCADESTICK_DEVICES, 0, 0, NULL,163SDL_HINT_JOYSTICK_ARCADESTICK_DEVICES_EXCLUDED, 0, 0, NULL,164SDL_arraysize(initial_arcadestick_devices), initial_arcadestick_devices,165false166};167168/* This list is taken from:169https://raw.githubusercontent.com/denilsonsa/udev-joystick-blacklist/master/generate_rules.py170*/171static Uint32 initial_blacklist_devices[] = {172// Microsoft Microsoft Wireless Optical Desktop 2.10173// Microsoft Wireless Desktop - Comfort Edition174MAKE_VIDPID(0x045e, 0x009d),175176// Microsoft Microsoft Digital Media Pro Keyboard177// Microsoft Corp. Digital Media Pro Keyboard178MAKE_VIDPID(0x045e, 0x00b0),179180// Microsoft Microsoft Digital Media Keyboard181// Microsoft Corp. Digital Media Keyboard 1.0A182MAKE_VIDPID(0x045e, 0x00b4),183184// Microsoft Microsoft Digital Media Keyboard 3000185MAKE_VIDPID(0x045e, 0x0730),186187// Microsoft Microsoft 2.4GHz Transceiver v6.0188// Microsoft Microsoft 2.4GHz Transceiver v8.0189// Microsoft Corp. Nano Transceiver v1.0 for Bluetooth190// Microsoft Wireless Mobile Mouse 1000191// Microsoft Wireless Desktop 3000192MAKE_VIDPID(0x045e, 0x0745),193194// Microsoft SideWinder(TM) 2.4GHz Transceiver195MAKE_VIDPID(0x045e, 0x0748),196197// Microsoft Corp. Wired Keyboard 600198MAKE_VIDPID(0x045e, 0x0750),199200// Microsoft Corp. Sidewinder X4 keyboard201MAKE_VIDPID(0x045e, 0x0768),202203// Microsoft Corp. Arc Touch Mouse Transceiver204MAKE_VIDPID(0x045e, 0x0773),205206// Microsoft 2.4GHz Transceiver v9.0207// Microsoft Nano Transceiver v2.1208// Microsoft Sculpt Ergonomic Keyboard (5KV-00001)209MAKE_VIDPID(0x045e, 0x07a5),210211// Microsoft Nano Transceiver v1.0212// Microsoft Wireless Keyboard 800213MAKE_VIDPID(0x045e, 0x07b2),214215// Microsoft Nano Transceiver v2.0216MAKE_VIDPID(0x045e, 0x0800),217218MAKE_VIDPID(0x046d, 0xc30a), // Logitech, Inc. iTouch Composite keyboard219220MAKE_VIDPID(0x04d9, 0xa0df), // Tek Syndicate Mouse (E-Signal USB Gaming Mouse)221222// List of Wacom devices at: http://linuxwacom.sourceforge.net/wiki/index.php/Device_IDs223MAKE_VIDPID(0x056a, 0x0010), // Wacom ET-0405 Graphire224MAKE_VIDPID(0x056a, 0x0011), // Wacom ET-0405A Graphire2 (4x5)225MAKE_VIDPID(0x056a, 0x0012), // Wacom ET-0507A Graphire2 (5x7)226MAKE_VIDPID(0x056a, 0x0013), // Wacom CTE-430 Graphire3 (4x5)227MAKE_VIDPID(0x056a, 0x0014), // Wacom CTE-630 Graphire3 (6x8)228MAKE_VIDPID(0x056a, 0x0015), // Wacom CTE-440 Graphire4 (4x5)229MAKE_VIDPID(0x056a, 0x0016), // Wacom CTE-640 Graphire4 (6x8)230MAKE_VIDPID(0x056a, 0x0017), // Wacom CTE-450 Bamboo Fun (4x5)231MAKE_VIDPID(0x056a, 0x0018), // Wacom CTE-650 Bamboo Fun 6x8232MAKE_VIDPID(0x056a, 0x0019), // Wacom CTE-631 Bamboo One233MAKE_VIDPID(0x056a, 0x00d1), // Wacom Bamboo Pen and Touch CTH-460234MAKE_VIDPID(0x056a, 0x030e), // Wacom Intuos Pen (S) CTL-480235236MAKE_VIDPID(0x09da, 0x054f), // A4 Tech Co., G7 750 mouse237MAKE_VIDPID(0x09da, 0x1410), // A4 Tech Co., Ltd Bloody AL9 mouse238MAKE_VIDPID(0x09da, 0x3043), // A4 Tech Co., Ltd Bloody R8A Gaming Mouse239MAKE_VIDPID(0x09da, 0x31b5), // A4 Tech Co., Ltd Bloody TL80 Terminator Laser Gaming Mouse240MAKE_VIDPID(0x09da, 0x3997), // A4 Tech Co., Ltd Bloody RT7 Terminator Wireless241MAKE_VIDPID(0x09da, 0x3f8b), // A4 Tech Co., Ltd Bloody V8 mouse242MAKE_VIDPID(0x09da, 0x51f4), // Modecom MC-5006 Keyboard243MAKE_VIDPID(0x09da, 0x5589), // A4 Tech Co., Ltd Terminator TL9 Laser Gaming Mouse244MAKE_VIDPID(0x09da, 0x7b22), // A4 Tech Co., Ltd Bloody V5245MAKE_VIDPID(0x09da, 0x7f2d), // A4 Tech Co., Ltd Bloody R3 mouse246MAKE_VIDPID(0x09da, 0x8090), // A4 Tech Co., Ltd X-718BK Oscar Optical Gaming Mouse247MAKE_VIDPID(0x09da, 0x9033), // A4 Tech Co., X7 X-705K248MAKE_VIDPID(0x09da, 0x9066), // A4 Tech Co., Sharkoon Fireglider Optical249MAKE_VIDPID(0x09da, 0x9090), // A4 Tech Co., Ltd XL-730K / XL-750BK / XL-755BK Laser Mouse250MAKE_VIDPID(0x09da, 0x90c0), // A4 Tech Co., Ltd X7 G800V keyboard251MAKE_VIDPID(0x09da, 0xf012), // A4 Tech Co., Ltd Bloody V7 mouse252MAKE_VIDPID(0x09da, 0xf32a), // A4 Tech Co., Ltd Bloody B540 keyboard253MAKE_VIDPID(0x09da, 0xf613), // A4 Tech Co., Ltd Bloody V2 mouse254MAKE_VIDPID(0x09da, 0xf624), // A4 Tech Co., Ltd Bloody B120 Keyboard255256MAKE_VIDPID(0x1b1c, 0x1b3c), // Corsair Harpoon RGB gaming mouse257258MAKE_VIDPID(0x1d57, 0xad03), // [T3] 2.4GHz and IR Air Mouse Remote Control259260MAKE_VIDPID(0x1e7d, 0x2e4a), // Roccat Tyon Mouse261262MAKE_VIDPID(0x20a0, 0x422d), // Winkeyless.kr Keyboards263264MAKE_VIDPID(0x2516, 0x001f), // Cooler Master Storm Mizar Mouse265MAKE_VIDPID(0x2516, 0x0028), // Cooler Master Storm Alcor Mouse266267/*****************************************************************/268// Additional entries269/*****************************************************************/270271MAKE_VIDPID(0x04d9, 0x8008), // OBINLB USB-HID Keyboard (Anne Pro II)272MAKE_VIDPID(0x04d9, 0x8009), // OBINLB USB-HID Keyboard (Anne Pro II)273MAKE_VIDPID(0x04d9, 0xa292), // OBINLB USB-HID Keyboard (Anne Pro II)274MAKE_VIDPID(0x04d9, 0xa293), // OBINLB USB-HID Keyboard (Anne Pro II)275MAKE_VIDPID(0x1532, 0x0266), // Razer Huntsman V2 Analog, non-functional DInput device276MAKE_VIDPID(0x1532, 0x0282), // Razer Huntsman Mini Analog, non-functional DInput device277MAKE_VIDPID(0x26ce, 0x01a2), // ASRock LED Controller278MAKE_VIDPID(0x20d6, 0x0002), // PowerA Enhanced Wireless Controller for Nintendo Switch (charging port only)279MAKE_VIDPID(0x3434, 0x0211), // Keychron K1 Pro System Control280};281static SDL_vidpid_list blacklist_devices = {282SDL_HINT_JOYSTICK_BLACKLIST_DEVICES, 0, 0, NULL,283SDL_HINT_JOYSTICK_BLACKLIST_DEVICES_EXCLUDED, 0, 0, NULL,284SDL_arraysize(initial_blacklist_devices), initial_blacklist_devices,285false286};287288static Uint32 initial_flightstick_devices[] = {289MAKE_VIDPID(0x044f, 0x0402), // HOTAS Warthog Joystick290MAKE_VIDPID(0x044f, 0xb10a), // ThrustMaster, Inc. T.16000M Joystick291MAKE_VIDPID(0x046d, 0xc215), // Logitech Extreme 3D292MAKE_VIDPID(0x0583, 0x6258), // Padix USB joystick with viewfinder293MAKE_VIDPID(0x0583, 0x688f), // Padix QF-688uv Windstorm Pro294MAKE_VIDPID(0x0583, 0x7070), // Padix QF-707u Bazooka295MAKE_VIDPID(0x0583, 0xa019), // Padix USB vibration joystick with viewfinder296MAKE_VIDPID(0x0583, 0xa131), // Padix USB Wireless 2.4GHz297MAKE_VIDPID(0x0583, 0xa209), // Padix MetalStrike ForceFeedback298MAKE_VIDPID(0x0583, 0xb010), // Padix MetalStrike Pro299MAKE_VIDPID(0x0583, 0xb012), // Padix Wireless MetalStrike300MAKE_VIDPID(0x0583, 0xb013), // Padix USB Wireless 2.4GHZ301MAKE_VIDPID(0x0738, 0x2221), // Saitek Pro Flight X-56 Rhino Stick302MAKE_VIDPID(0x10f5, 0x7084), // Turtle Beach VelocityOne303MAKE_VIDPID(0x231d, 0x0126), // Gunfighter Mk.III 'Space Combat Edition' (right)304MAKE_VIDPID(0x231d, 0x0127), // Gunfighter Mk.III 'Space Combat Edition' (left)305MAKE_VIDPID(0x362c, 0x0001), // Yawman Arrow306};307static SDL_vidpid_list flightstick_devices = {308SDL_HINT_JOYSTICK_FLIGHTSTICK_DEVICES, 0, 0, NULL,309SDL_HINT_JOYSTICK_FLIGHTSTICK_DEVICES_EXCLUDED, 0, 0, NULL,310SDL_arraysize(initial_flightstick_devices), initial_flightstick_devices,311false312};313314static Uint32 initial_gamecube_devices[] = {315MAKE_VIDPID(0x0e6f, 0x0185), // PDP Wired Fight Pad Pro for Nintendo Switch316MAKE_VIDPID(0x20d6, 0xa711), // PowerA Wired Controller Nintendo GameCube Style317};318static SDL_vidpid_list gamecube_devices = {319SDL_HINT_JOYSTICK_GAMECUBE_DEVICES, 0, 0, NULL,320SDL_HINT_JOYSTICK_GAMECUBE_DEVICES_EXCLUDED, 0, 0, NULL,321SDL_arraysize(initial_gamecube_devices), initial_gamecube_devices,322false323};324325static Uint32 initial_rog_gamepad_mice[] = {326MAKE_VIDPID(0x0b05, 0x18e3), // ROG Chakram (wired) Mouse327MAKE_VIDPID(0x0b05, 0x18e5), // ROG Chakram (wireless) Mouse328MAKE_VIDPID(0x0b05, 0x1906), // ROG Pugio II329MAKE_VIDPID(0x0b05, 0x1958), // ROG Chakram Core Mouse330MAKE_VIDPID(0x0b05, 0x1a18), // ROG Chakram X (wired) Mouse331MAKE_VIDPID(0x0b05, 0x1a1a), // ROG Chakram X (wireless) Mouse332MAKE_VIDPID(0x0b05, 0x1a1c), // ROG Chakram X (Bluetooth) Mouse333};334static SDL_vidpid_list rog_gamepad_mice = {335SDL_HINT_ROG_GAMEPAD_MICE, 0, 0, NULL,336SDL_HINT_ROG_GAMEPAD_MICE_EXCLUDED, 0, 0, NULL,337SDL_arraysize(initial_rog_gamepad_mice), initial_rog_gamepad_mice,338false339};340341static Uint32 initial_throttle_devices[] = {342MAKE_VIDPID(0x044f, 0x0404), // HOTAS Warthog Throttle343MAKE_VIDPID(0x0738, 0xa221), // Saitek Pro Flight X-56 Rhino Throttle344MAKE_VIDPID(0x10f5, 0x7085), // Turtle Beach VelocityOne Throttle345};346static SDL_vidpid_list throttle_devices = {347SDL_HINT_JOYSTICK_THROTTLE_DEVICES, 0, 0, NULL,348SDL_HINT_JOYSTICK_THROTTLE_DEVICES_EXCLUDED, 0, 0, NULL,349SDL_arraysize(initial_throttle_devices), initial_throttle_devices,350false351};352353static Uint32 initial_wheel_devices[] = {354MAKE_VIDPID(0x0079, 0x1864), // DragonRise Inc. Wired Wheel (active mode) (also known as PXN V900 (PS3), Superdrive SV-750, or a Genesis Seaborg 400)355MAKE_VIDPID(0x044f, 0xb65d), // Thrustmaster Wheel FFB356MAKE_VIDPID(0x044f, 0xb65e), // Thrustmaster T500RS357MAKE_VIDPID(0x044f, 0xb664), // Thrustmaster TX (initial mode)358MAKE_VIDPID(0x044f, 0xb669), // Thrustmaster TX (active mode)359MAKE_VIDPID(0x044f, 0xb66d), // Thrustmaster T300RS (PS4 mode)360MAKE_VIDPID(0x044f, 0xb66d), // Thrustmaster Wheel FFB361MAKE_VIDPID(0x044f, 0xb66e), // Thrustmaster T300RS (normal mode)362MAKE_VIDPID(0x044f, 0xb66f), // Thrustmaster T300RS (advanced mode)363MAKE_VIDPID(0x044f, 0xb677), // Thrustmaster T150364MAKE_VIDPID(0x044f, 0xb67f), // Thrustmaster TMX365MAKE_VIDPID(0x044f, 0xb691), // Thrustmaster TS-XW (initial mode)366MAKE_VIDPID(0x044f, 0xb692), // Thrustmaster TS-XW (active mode)367MAKE_VIDPID(0x044f, 0xb696), // Thrustmaster T248368MAKE_VIDPID(0x046d, 0xc24f), // Logitech G29 (PS3)369MAKE_VIDPID(0x046d, 0xc260), // Logitech G29 (PS4)370MAKE_VIDPID(0x046d, 0xc261), // Logitech G920 (initial mode)371MAKE_VIDPID(0x046d, 0xc262), // Logitech G920 (active mode)372MAKE_VIDPID(0x046d, 0xc266), // Logitech G923 for Playstation 4 and PC (PC mode)373MAKE_VIDPID(0x046d, 0xc267), // Logitech G923 for Playstation 4 and PC (PS4 mode)374MAKE_VIDPID(0x046d, 0xc268), // Logitech PRO Racing Wheel (PC mode)375MAKE_VIDPID(0x046d, 0xc269), // Logitech PRO Racing Wheel (PS4/PS5 mode)376MAKE_VIDPID(0x046d, 0xc26d), // Logitech G923 (Xbox)377MAKE_VIDPID(0x046d, 0xc26e), // Logitech G923378MAKE_VIDPID(0x046d, 0xc272), // Logitech PRO Racing Wheel for Xbox (PC mode)379MAKE_VIDPID(0x046d, 0xc294), // Logitech generic wheel380MAKE_VIDPID(0x046d, 0xc295), // Logitech Momo Force381MAKE_VIDPID(0x046d, 0xc298), // Logitech Driving Force Pro382MAKE_VIDPID(0x046d, 0xc299), // Logitech G25383MAKE_VIDPID(0x046d, 0xc29a), // Logitech Driving Force GT384MAKE_VIDPID(0x046d, 0xc29b), // Logitech G27385MAKE_VIDPID(0x046d, 0xca03), // Logitech Momo Racing386MAKE_VIDPID(0x0483, 0x0522), // Simagic Wheelbase (including M10, Alpha Mini, Alpha, Alpha U)387MAKE_VIDPID(0x0483, 0xa355), // VRS DirectForce Pro Wheel Base388MAKE_VIDPID(0x0583, 0xa132), // Padix USB Wireless 2.4GHz Wheelpad389MAKE_VIDPID(0x0583, 0xa133), // Padix USB Wireless 2.4GHz Wheel390MAKE_VIDPID(0x0583, 0xa202), // Padix Force Feedback Wheel391MAKE_VIDPID(0x0583, 0xb002), // Padix Vibration USB Wheel392MAKE_VIDPID(0x0583, 0xb005), // Padix USB Wheel393MAKE_VIDPID(0x0583, 0xb008), // Padix USB Wireless 2.4GHz Wheel394MAKE_VIDPID(0x0583, 0xb009), // Padix USB Wheel395MAKE_VIDPID(0x0583, 0xb018), // Padix TW6 Wheel396MAKE_VIDPID(0x0eb7, 0x0001), // Fanatec ClubSport Wheel Base V2397MAKE_VIDPID(0x0eb7, 0x0004), // Fanatec ClubSport Wheel Base V2.5398MAKE_VIDPID(0x0eb7, 0x0005), // Fanatec CSL Elite Wheel Base+ (PS4)399MAKE_VIDPID(0x0eb7, 0x0006), // Fanatec Podium Wheel Base DD1400MAKE_VIDPID(0x0eb7, 0x0007), // Fanatec Podium Wheel Base DD2401MAKE_VIDPID(0x0eb7, 0x0011), // Fanatec Forza Motorsport (CSR Wheel / CSR Elite Wheel)402MAKE_VIDPID(0x0eb7, 0x0020), // Fanatec generic wheel / CSL DD / GT DD Pro403MAKE_VIDPID(0x0eb7, 0x0197), // Fanatec Porsche Wheel (Turbo / GT3 RS / Turbo S / GT3 V2 / GT2)404MAKE_VIDPID(0x0eb7, 0x038e), // Fanatec ClubSport Wheel Base V1405MAKE_VIDPID(0x0eb7, 0x0e03), // Fanatec CSL Elite Wheel Base406MAKE_VIDPID(0x11ff, 0x0511), // DragonRise Inc. Wired Wheel (initial mode) (also known as PXN V900 (PS3), Superdrive SV-750, or a Genesis Seaborg 400)407MAKE_VIDPID(0x1209, 0xffb0), // Generic FFBoard OpenFFBoard universal forcefeedback wheel408MAKE_VIDPID(0x16d0, 0x0d5a), // Simucube 1 Wheelbase409MAKE_VIDPID(0x16d0, 0x0d5f), // Simucube 2 Ultimate Wheelbase410MAKE_VIDPID(0x16d0, 0x0d60), // Simucube 2 Pro Wheelbase411MAKE_VIDPID(0x16d0, 0x0d61), // Simucube 2 Sport Wheelbase412MAKE_VIDPID(0x2433, 0xf300), // Asetek SimSports Invicta Wheelbase413MAKE_VIDPID(0x2433, 0xf301), // Asetek SimSports Forte Wheelbase414MAKE_VIDPID(0x2433, 0xf303), // Asetek SimSports La Prima Wheelbase415MAKE_VIDPID(0x2433, 0xf306), // Asetek SimSports Tony Kannan Wheelbase416MAKE_VIDPID(0x3416, 0x0301), // Cammus C5 Wheelbase417MAKE_VIDPID(0x3416, 0x0302), // Cammus C12 Wheelbase418MAKE_VIDPID(0x346e, 0x0000), // Moza R16/R21 Wheelbase419MAKE_VIDPID(0x346e, 0x0002), // Moza R9 Wheelbase420MAKE_VIDPID(0x346e, 0x0004), // Moza R5 Wheelbase421MAKE_VIDPID(0x346e, 0x0005), // Moza R3 Wheelbase422MAKE_VIDPID(0x346e, 0x0006), // Moza R12 Wheelbase423};424static SDL_vidpid_list wheel_devices = {425SDL_HINT_JOYSTICK_WHEEL_DEVICES, 0, 0, NULL,426SDL_HINT_JOYSTICK_WHEEL_DEVICES_EXCLUDED, 0, 0, NULL,427SDL_arraysize(initial_wheel_devices), initial_wheel_devices,428false429};430431static Uint32 initial_zero_centered_devices[] = {432MAKE_VIDPID(0x05a0, 0x3232), // 8Bitdo Zero Gamepad433MAKE_VIDPID(0x0e8f, 0x3013), // HuiJia SNES USB adapter434};435static SDL_vidpid_list zero_centered_devices = {436SDL_HINT_JOYSTICK_ZERO_CENTERED_DEVICES, 0, 0, NULL,437NULL, 0, 0, NULL,438SDL_arraysize(initial_zero_centered_devices), initial_zero_centered_devices,439false440};441442#define CHECK_JOYSTICK_MAGIC(joystick, result) \443if (!SDL_ObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK)) { \444SDL_InvalidParamError("joystick"); \445SDL_UnlockJoysticks(); \446return result; \447}448449#define CHECK_JOYSTICK_VIRTUAL(joystick, result) \450if (!joystick->is_virtual) { \451SDL_SetError("joystick isn't virtual"); \452SDL_UnlockJoysticks(); \453return result; \454}455456bool SDL_JoysticksInitialized(void)457{458return SDL_joysticks_initialized;459}460461bool SDL_JoysticksQuitting(void)462{463return SDL_joysticks_quitting;464}465466void SDL_LockJoysticks(void)467{468(void)SDL_AtomicIncRef(&SDL_joystick_lock_pending);469SDL_LockMutex(SDL_joystick_lock);470(void)SDL_AtomicDecRef(&SDL_joystick_lock_pending);471472++SDL_joysticks_locked;473}474475void SDL_UnlockJoysticks(void)476{477bool last_unlock = false;478479--SDL_joysticks_locked;480481if (!SDL_joysticks_initialized) {482// NOTE: There's a small window here where another thread could lock the mutex after we've checked for pending locks483if (!SDL_joysticks_locked && SDL_GetAtomicInt(&SDL_joystick_lock_pending) == 0) {484last_unlock = true;485}486}487488/* The last unlock after joysticks are uninitialized will cleanup the mutex,489* allowing applications to lock joysticks while reinitializing the system.490*/491if (last_unlock) {492SDL_Mutex *joystick_lock = SDL_joystick_lock;493494SDL_LockMutex(joystick_lock);495{496SDL_UnlockMutex(SDL_joystick_lock);497498SDL_joystick_lock = NULL;499}500SDL_UnlockMutex(joystick_lock);501SDL_DestroyMutex(joystick_lock);502} else {503SDL_UnlockMutex(SDL_joystick_lock);504}505}506507bool SDL_JoysticksLocked(void)508{509return (SDL_joysticks_locked > 0);510}511512void SDL_AssertJoysticksLocked(void)513{514SDL_assert(SDL_JoysticksLocked());515}516517/*518* Get the driver and device index for a joystick instance ID519* This should be called while the joystick lock is held, to prevent another thread from updating the list520*/521static bool SDL_GetDriverAndJoystickIndex(SDL_JoystickID instance_id, SDL_JoystickDriver **driver, int *driver_index)522{523int i, num_joysticks, device_index;524525SDL_AssertJoysticksLocked();526527if (instance_id > 0) {528for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {529num_joysticks = SDL_joystick_drivers[i]->GetCount();530for (device_index = 0; device_index < num_joysticks; ++device_index) {531SDL_JoystickID joystick_id = SDL_joystick_drivers[i]->GetDeviceInstanceID(device_index);532if (joystick_id == instance_id) {533*driver = SDL_joystick_drivers[i];534*driver_index = device_index;535return true;536}537}538}539}540541SDL_SetError("Joystick %" SDL_PRIu32 " not found", instance_id);542return false;543}544545static int SDL_FindFreePlayerIndex(void)546{547int player_index;548549SDL_AssertJoysticksLocked();550551for (player_index = 0; player_index < SDL_joystick_player_count; ++player_index) {552if (SDL_joystick_players[player_index] == 0) {553break;554}555}556return player_index;557}558559static int SDL_GetPlayerIndexForJoystickID(SDL_JoystickID instance_id)560{561int player_index;562563SDL_AssertJoysticksLocked();564565for (player_index = 0; player_index < SDL_joystick_player_count; ++player_index) {566if (instance_id == SDL_joystick_players[player_index]) {567break;568}569}570if (player_index == SDL_joystick_player_count) {571player_index = -1;572}573return player_index;574}575576static SDL_JoystickID SDL_GetJoystickIDForPlayerIndex(int player_index)577{578SDL_AssertJoysticksLocked();579580if (player_index < 0 || player_index >= SDL_joystick_player_count) {581return 0;582}583return SDL_joystick_players[player_index];584}585586static bool SDL_SetJoystickIDForPlayerIndex(int player_index, SDL_JoystickID instance_id)587{588SDL_JoystickID existing_instance = SDL_GetJoystickIDForPlayerIndex(player_index);589SDL_JoystickDriver *driver;590int device_index;591int existing_player_index;592593SDL_AssertJoysticksLocked();594595if (player_index >= SDL_joystick_player_count) {596SDL_JoystickID *new_players = (SDL_JoystickID *)SDL_realloc(SDL_joystick_players, (player_index + 1) * sizeof(*SDL_joystick_players));597if (!new_players) {598return false;599}600601SDL_joystick_players = new_players;602SDL_memset(&SDL_joystick_players[SDL_joystick_player_count], 0, (player_index - SDL_joystick_player_count + 1) * sizeof(SDL_joystick_players[0]));603SDL_joystick_player_count = player_index + 1;604} else if (player_index >= 0 && SDL_joystick_players[player_index] == instance_id) {605// Joystick is already assigned the requested player index606return true;607}608609// Clear the old player index610existing_player_index = SDL_GetPlayerIndexForJoystickID(instance_id);611if (existing_player_index >= 0) {612SDL_joystick_players[existing_player_index] = 0;613}614615if (player_index >= 0) {616SDL_joystick_players[player_index] = instance_id;617}618619// Update the driver with the new index620if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {621driver->SetDevicePlayerIndex(device_index, player_index);622}623624// Move any existing joystick to another slot625if (existing_instance > 0) {626SDL_SetJoystickIDForPlayerIndex(SDL_FindFreePlayerIndex(), existing_instance);627}628return true;629}630631static void SDLCALL SDL_JoystickAllowBackgroundEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)632{633if (SDL_GetStringBoolean(hint, false)) {634SDL_joystick_allows_background_events = true;635} else {636SDL_joystick_allows_background_events = false;637}638}639640bool SDL_InitJoysticks(void)641{642int i;643bool result = false;644645// Create the joystick list lock646if (SDL_joystick_lock == NULL) {647SDL_joystick_lock = SDL_CreateMutex();648}649650if (!SDL_InitSubSystem(SDL_INIT_EVENTS)) {651return false;652}653654SDL_LockJoysticks();655656SDL_joysticks_initialized = true;657658SDL_InitGamepadMappings();659660SDL_LoadVIDPIDList(&arcadestick_devices);661SDL_LoadVIDPIDList(&blacklist_devices);662SDL_LoadVIDPIDList(&flightstick_devices);663SDL_LoadVIDPIDList(&gamecube_devices);664SDL_LoadVIDPIDList(&rog_gamepad_mice);665SDL_LoadVIDPIDList(&throttle_devices);666SDL_LoadVIDPIDList(&wheel_devices);667SDL_LoadVIDPIDList(&zero_centered_devices);668669// See if we should allow joystick events while in the background670SDL_AddHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,671SDL_JoystickAllowBackgroundEventsChanged, NULL);672673SDL_InitSteamVirtualGamepadInfo();674675for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {676if (SDL_joystick_drivers[i]->Init()) {677result = true;678}679}680SDL_UnlockJoysticks();681682if (!result) {683SDL_QuitJoysticks();684}685686return result;687}688689bool SDL_JoysticksOpened(void)690{691bool opened;692693SDL_LockJoysticks();694{695if (SDL_joysticks != NULL) {696opened = true;697} else {698opened = false;699}700}701SDL_UnlockJoysticks();702703return opened;704}705706bool SDL_JoystickHandledByAnotherDriver(struct SDL_JoystickDriver *driver, Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)707{708int i;709bool result = false;710711SDL_LockJoysticks();712{713for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {714if (driver == SDL_joystick_drivers[i]) {715// Higher priority drivers do not have this device716break;717}718if (SDL_joystick_drivers[i]->IsDevicePresent(vendor_id, product_id, version, name)) {719result = true;720break;721}722}723}724SDL_UnlockJoysticks();725726return result;727}728729bool SDL_HasJoystick(void)730{731int i;732int total_joysticks = 0;733734SDL_LockJoysticks();735{736for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {737total_joysticks += SDL_joystick_drivers[i]->GetCount();738}739}740SDL_UnlockJoysticks();741742if (total_joysticks > 0) {743return true;744}745return false;746}747748SDL_JoystickID *SDL_GetJoysticks(int *count)749{750int i, num_joysticks, device_index;751int joystick_index = 0, total_joysticks = 0;752SDL_JoystickID *joysticks;753754SDL_LockJoysticks();755{756for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {757total_joysticks += SDL_joystick_drivers[i]->GetCount();758}759760joysticks = (SDL_JoystickID *)SDL_malloc((total_joysticks + 1) * sizeof(*joysticks));761if (joysticks) {762if (count) {763*count = total_joysticks;764}765766for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {767num_joysticks = SDL_joystick_drivers[i]->GetCount();768for (device_index = 0; device_index < num_joysticks; ++device_index) {769SDL_assert(joystick_index < total_joysticks);770joysticks[joystick_index] = SDL_joystick_drivers[i]->GetDeviceInstanceID(device_index);771SDL_assert(joysticks[joystick_index] > 0);772++joystick_index;773}774}775SDL_assert(joystick_index == total_joysticks);776joysticks[joystick_index] = 0;777} else {778if (count) {779*count = 0;780}781}782}783SDL_UnlockJoysticks();784785return joysticks;786}787788const SDL_SteamVirtualGamepadInfo *SDL_GetJoystickVirtualGamepadInfoForID(SDL_JoystickID instance_id)789{790SDL_JoystickDriver *driver;791int device_index;792const SDL_SteamVirtualGamepadInfo *info = NULL;793794if (SDL_SteamVirtualGamepadEnabled() &&795SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {796info = SDL_GetSteamVirtualGamepadInfo(driver->GetDeviceSteamVirtualGamepadSlot(device_index));797}798return info;799}800801/*802* Get the implementation dependent name of a joystick803*/804const char *SDL_GetJoystickNameForID(SDL_JoystickID instance_id)805{806SDL_JoystickDriver *driver;807int device_index;808const char *name = NULL;809const SDL_SteamVirtualGamepadInfo *info;810811SDL_LockJoysticks();812info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);813if (info) {814name = SDL_GetPersistentString(info->name);815} else if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {816name = SDL_GetPersistentString(driver->GetDeviceName(device_index));817}818SDL_UnlockJoysticks();819820return name;821}822823/*824* Get the implementation dependent path of a joystick825*/826const char *SDL_GetJoystickPathForID(SDL_JoystickID instance_id)827{828SDL_JoystickDriver *driver;829int device_index;830const char *path = NULL;831832SDL_LockJoysticks();833if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {834path = SDL_GetPersistentString(driver->GetDevicePath(device_index));835}836SDL_UnlockJoysticks();837838if (!path) {839SDL_Unsupported();840}841return path;842}843844/*845* Get the player index of a joystick, or -1 if it's not available846*/847int SDL_GetJoystickPlayerIndexForID(SDL_JoystickID instance_id)848{849int player_index;850851SDL_LockJoysticks();852player_index = SDL_GetPlayerIndexForJoystickID(instance_id);853SDL_UnlockJoysticks();854855return player_index;856}857858/*859* Return true if this joystick is known to have all axes centered at zero860* This isn't generally needed unless the joystick never generates an initial axis value near zero,861* e.g. it's emulating axes with digital buttons862*/863static bool SDL_JoystickAxesCenteredAtZero(SDL_Joystick *joystick)864{865// printf("JOYSTICK '%s' VID/PID 0x%.4x/0x%.4x AXES: %d\n", joystick->name, vendor, product, joystick->naxes);866867if (joystick->naxes == 2) {868// Assume D-pad or thumbstick style axes are centered at 0869return true;870}871872return SDL_VIDPIDInList(SDL_GetJoystickVendor(joystick), SDL_GetJoystickProduct(joystick), &zero_centered_devices);873}874875static bool IsROGAlly(SDL_Joystick *joystick)876{877Uint16 vendor, product;878SDL_GUID guid = SDL_GetJoystickGUID(joystick);879880// The ROG Ally controller spoofs an Xbox 360 controller881SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);882if (vendor == USB_VENDOR_MICROSOFT && product == USB_PRODUCT_XBOX360_WIRED_CONTROLLER) {883// Check to see if this system has the expected sensors884bool has_ally_accel = false;885bool has_ally_gyro = false;886887if (SDL_InitSubSystem(SDL_INIT_SENSOR)) {888SDL_SensorID *sensors = SDL_GetSensors(NULL);889if (sensors) {890int i;891for (i = 0; sensors[i]; ++i) {892SDL_SensorID sensor = sensors[i];893894if (!has_ally_accel && SDL_GetSensorTypeForID(sensor) == SDL_SENSOR_ACCEL) {895const char *sensor_name = SDL_GetSensorNameForID(sensor);896if (sensor_name && SDL_strcmp(sensor_name, "Sensor BMI320 Acc") == 0) {897has_ally_accel = true;898}899}900if (!has_ally_gyro && SDL_GetSensorTypeForID(sensor) == SDL_SENSOR_GYRO) {901const char *sensor_name = SDL_GetSensorNameForID(sensor);902if (sensor_name && SDL_strcmp(sensor_name, "Sensor BMI320 Gyr") == 0) {903has_ally_gyro = true;904}905}906}907SDL_free(sensors);908}909SDL_QuitSubSystem(SDL_INIT_SENSOR);910}911if (has_ally_accel && has_ally_gyro) {912return true;913}914}915return false;916}917918static bool ShouldAttemptSensorFusion(SDL_Joystick *joystick, bool *invert_sensors)919{920SDL_AssertJoysticksLocked();921922*invert_sensors = false;923924// The SDL controller sensor API is only available for gamepads (at the moment)925if (!SDL_IsGamepad(joystick->instance_id)) {926return false;927}928929// If the controller already has sensors, use those930if (joystick->nsensors > 0) {931return false;932}933934const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION);935if (hint && *hint) {936if (*hint == '@' || SDL_strncmp(hint, "0x", 2) == 0) {937SDL_vidpid_list gamepads;938SDL_GUID guid;939Uint16 vendor, product;940bool enabled;941SDL_zero(gamepads);942943// See if the gamepad is in our list of devices to enable944guid = SDL_GetJoystickGUID(joystick);945SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);946SDL_LoadVIDPIDListFromHints(&gamepads, hint, NULL);947enabled = SDL_VIDPIDInList(vendor, product, &gamepads);948SDL_FreeVIDPIDList(&gamepads);949if (enabled) {950return true;951}952} else {953return SDL_GetStringBoolean(hint, false);954}955}956957// See if this is another known wraparound gamepad958if (joystick->name &&959(SDL_strstr(joystick->name, "Backbone One") ||960SDL_strstr(joystick->name, "Kishi"))) {961return true;962}963if (IsROGAlly(joystick)) {964/* I'm not sure if this is a Windows thing, or a quirk for ROG Ally,965* but we need to invert the sensor data on all axes.966*/967*invert_sensors = true;968return true;969}970return false;971}972973static void AttemptSensorFusion(SDL_Joystick *joystick, bool invert_sensors)974{975SDL_SensorID *sensors;976unsigned int i, j;977978SDL_AssertJoysticksLocked();979980if (!SDL_InitSubSystem(SDL_INIT_SENSOR)) {981return;982}983984sensors = SDL_GetSensors(NULL);985if (sensors) {986for (i = 0; sensors[i]; ++i) {987SDL_SensorID sensor = sensors[i];988989if (!joystick->accel_sensor && SDL_GetSensorTypeForID(sensor) == SDL_SENSOR_ACCEL) {990// Increment the sensor subsystem reference count991SDL_InitSubSystem(SDL_INIT_SENSOR);992993joystick->accel_sensor = sensor;994SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 0.0f);995}996if (!joystick->gyro_sensor && SDL_GetSensorTypeForID(sensor) == SDL_SENSOR_GYRO) {997// Increment the sensor subsystem reference count998SDL_InitSubSystem(SDL_INIT_SENSOR);9991000joystick->gyro_sensor = sensor;1001SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 0.0f);1002}1003}1004SDL_free(sensors);1005}1006SDL_QuitSubSystem(SDL_INIT_SENSOR);10071008/* SDL defines sensor orientation for phones relative to the natural1009orientation, and for gamepads relative to being held in front of you.1010When a phone is being used as a gamepad, its orientation changes,1011so adjust sensor axes to match.1012*/1013//if (SDL_GetNaturalDisplayOrientation(SDL_GetPrimaryDisplay()) == SDL_ORIENTATION_LANDSCAPE) {1014if (true) {1015/* When a device in landscape orientation is laid flat, the axes change1016orientation as follows:1017-X to +X becomes -X to +X1018-Y to +Y becomes +Z to -Z1019-Z to +Z becomes -Y to +Y1020*/1021joystick->sensor_transform[0][0] = 1.0f;1022joystick->sensor_transform[1][2] = 1.0f;1023joystick->sensor_transform[2][1] = -1.0f;1024} else {1025/* When a device in portrait orientation is rotated left and laid flat,1026the axes change orientation as follows:1027-X to +X becomes +Z to -Z1028-Y to +Y becomes +X to -X1029-Z to +Z becomes -Y to +Y1030*/1031joystick->sensor_transform[0][1] = -1.0f;1032joystick->sensor_transform[1][2] = 1.0f;1033joystick->sensor_transform[2][0] = -1.0f;1034}10351036if (invert_sensors) {1037for (i = 0; i < SDL_arraysize(joystick->sensor_transform); ++i) {1038for (j = 0; j < SDL_arraysize(joystick->sensor_transform[i]); ++j) {1039joystick->sensor_transform[i][j] *= -1.0f;1040}1041}1042}1043}10441045static void CleanupSensorFusion(SDL_Joystick *joystick)1046{1047SDL_AssertJoysticksLocked();10481049if (joystick->accel_sensor || joystick->gyro_sensor) {1050if (joystick->accel_sensor) {1051if (joystick->accel) {1052SDL_CloseSensor(joystick->accel);1053joystick->accel = NULL;1054}1055joystick->accel_sensor = 0;10561057// Decrement the sensor subsystem reference count1058SDL_QuitSubSystem(SDL_INIT_SENSOR);1059}1060if (joystick->gyro_sensor) {1061if (joystick->gyro) {1062SDL_CloseSensor(joystick->gyro);1063joystick->gyro = NULL;1064}1065joystick->gyro_sensor = 0;10661067// Decrement the sensor subsystem reference count1068SDL_QuitSubSystem(SDL_INIT_SENSOR);1069}1070}1071}10721073static bool ShouldSwapFaceButtons(const SDL_SteamVirtualGamepadInfo *info)1074{1075// When "Use Nintendo Button Layout" is enabled under Steam (the default)1076// it will send button 0 for the A (east) button and button 1 for the1077// B (south) button. This is done so that games that interpret the1078// buttons as Xbox input will get button 0 for "A" as they expect.1079//1080// However, SDL reports positional buttons, so we need to swap1081// the buttons so they show up in the correct position. This provides1082// consistent behavior regardless of whether we're running under Steam,1083// under the default settings.1084if (info &&1085(info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO ||1086info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT ||1087info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT ||1088info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR)) {1089return true;1090}1091return false;1092}10931094/*1095* Open a joystick for use - the index passed as an argument refers to1096* the N'th joystick on the system. This index is the value which will1097* identify this joystick in future joystick events.1098*1099* This function returns a joystick identifier, or NULL if an error occurred.1100*/1101SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id)1102{1103SDL_JoystickDriver *driver;1104int device_index;1105SDL_Joystick *joystick;1106SDL_Joystick *joysticklist;1107const char *joystickname = NULL;1108const char *joystickpath = NULL;1109bool invert_sensors = false;1110const SDL_SteamVirtualGamepadInfo *info;11111112SDL_LockJoysticks();11131114if (!SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {1115SDL_UnlockJoysticks();1116return NULL;1117}11181119joysticklist = SDL_joysticks;1120/* If the joystick is already open, return it1121* it is important that we have a single joystick for each instance id1122*/1123while (joysticklist) {1124if (instance_id == joysticklist->instance_id) {1125joystick = joysticklist;1126++joystick->ref_count;1127SDL_UnlockJoysticks();1128return joystick;1129}1130joysticklist = joysticklist->next;1131}11321133// Create and initialize the joystick1134joystick = (SDL_Joystick *)SDL_calloc(1, sizeof(*joystick));1135if (!joystick) {1136SDL_UnlockJoysticks();1137return NULL;1138}1139SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, true);1140joystick->driver = driver;1141joystick->instance_id = instance_id;1142joystick->attached = true;1143joystick->led_expiration = SDL_GetTicks();1144joystick->battery_percent = -1;1145#ifdef SDL_JOYSTICK_VIRTUAL1146joystick->is_virtual = (driver == &SDL_VIRTUAL_JoystickDriver);1147#else1148joystick->is_virtual = false;1149#endif11501151if (!driver->Open(joystick, device_index)) {1152SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, false);1153SDL_free(joystick);1154SDL_UnlockJoysticks();1155return NULL;1156}11571158joystickname = driver->GetDeviceName(device_index);1159if (joystickname) {1160joystick->name = SDL_strdup(joystickname);1161}11621163joystickpath = driver->GetDevicePath(device_index);1164if (joystickpath) {1165joystick->path = SDL_strdup(joystickpath);1166}11671168joystick->guid = driver->GetDeviceGUID(device_index);11691170if (joystick->naxes > 0) {1171joystick->axes = (SDL_JoystickAxisInfo *)SDL_calloc(joystick->naxes, sizeof(*joystick->axes));1172}1173if (joystick->nballs > 0) {1174joystick->balls = (SDL_JoystickBallData *)SDL_calloc(joystick->nballs, sizeof(*joystick->balls));1175}1176if (joystick->nhats > 0) {1177joystick->hats = (Uint8 *)SDL_calloc(joystick->nhats, sizeof(*joystick->hats));1178}1179if (joystick->nbuttons > 0) {1180joystick->buttons = (bool *)SDL_calloc(joystick->nbuttons, sizeof(*joystick->buttons));1181}1182if (((joystick->naxes > 0) && !joystick->axes) ||1183((joystick->nballs > 0) && !joystick->balls) ||1184((joystick->nhats > 0) && !joystick->hats) ||1185((joystick->nbuttons > 0) && !joystick->buttons)) {1186SDL_CloseJoystick(joystick);1187SDL_UnlockJoysticks();1188return NULL;1189}11901191// If this joystick is known to have all zero centered axes, skip the auto-centering code1192if (SDL_JoystickAxesCenteredAtZero(joystick)) {1193for (int i = 0; i < joystick->naxes; ++i) {1194joystick->axes[i].has_initial_value = true;1195}1196}11971198// We know the initial values for HIDAPI and XInput joysticks1199if ((SDL_IsJoystickHIDAPI(joystick->guid) ||1200SDL_IsJoystickXInput(joystick->guid) ||1201SDL_IsJoystickRAWINPUT(joystick->guid) ||1202SDL_IsJoystickWGI(joystick->guid)) &&1203joystick->naxes >= SDL_GAMEPAD_AXIS_COUNT) {1204int left_trigger, right_trigger;1205if (SDL_IsJoystickXInput(joystick->guid)) {1206left_trigger = 2;1207right_trigger = 5;1208} else {1209left_trigger = SDL_GAMEPAD_AXIS_LEFT_TRIGGER;1210right_trigger = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER;1211}1212for (int i = 0; i < SDL_GAMEPAD_AXIS_COUNT; ++i) {1213int initial_value;1214if (i == left_trigger || i == right_trigger) {1215initial_value = SDL_MIN_SINT16;1216} else {1217initial_value = 0;1218}1219joystick->axes[i].value = initial_value;1220joystick->axes[i].zero = initial_value;1221joystick->axes[i].initial_value = initial_value;1222joystick->axes[i].has_initial_value = true;1223}1224}12251226// Get the Steam Input API handle1227info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);1228if (info) {1229joystick->steam_handle = info->handle;1230joystick->swap_face_buttons = ShouldSwapFaceButtons(info);1231}12321233// Use system gyro and accelerometer if the gamepad doesn't have built-in sensors1234if (ShouldAttemptSensorFusion(joystick, &invert_sensors)) {1235AttemptSensorFusion(joystick, invert_sensors);1236}12371238// Add joystick to list1239++joystick->ref_count;1240// Link the joystick in the list1241joystick->next = SDL_joysticks;1242SDL_joysticks = joystick;12431244driver->Update(joystick);12451246SDL_UnlockJoysticks();12471248return joystick;1249}12501251SDL_JoystickID SDL_AttachVirtualJoystick(const SDL_VirtualJoystickDesc *desc)1252{1253#ifdef SDL_JOYSTICK_VIRTUAL1254SDL_JoystickID result;12551256SDL_LockJoysticks();1257result = SDL_JoystickAttachVirtualInner(desc);1258SDL_UnlockJoysticks();1259return result;1260#else1261SDL_SetError("SDL not built with virtual-joystick support");1262return 0;1263#endif1264}12651266bool SDL_DetachVirtualJoystick(SDL_JoystickID instance_id)1267{1268#ifdef SDL_JOYSTICK_VIRTUAL1269bool result;12701271SDL_LockJoysticks();1272result = SDL_JoystickDetachVirtualInner(instance_id);1273SDL_UnlockJoysticks();1274return result;1275#else1276return SDL_SetError("SDL not built with virtual-joystick support");1277#endif1278}12791280bool SDL_IsJoystickVirtual(SDL_JoystickID instance_id)1281{1282#ifdef SDL_JOYSTICK_VIRTUAL1283SDL_JoystickDriver *driver;1284int device_index;1285bool is_virtual = false;12861287SDL_LockJoysticks();1288if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {1289if (driver == &SDL_VIRTUAL_JoystickDriver) {1290is_virtual = true;1291}1292}1293SDL_UnlockJoysticks();12941295return is_virtual;1296#else1297return false;1298#endif1299}13001301bool SDL_SetJoystickVirtualAxis(SDL_Joystick *joystick, int axis, Sint16 value)1302{1303bool result;13041305SDL_LockJoysticks();1306{1307CHECK_JOYSTICK_MAGIC(joystick, false);1308CHECK_JOYSTICK_VIRTUAL(joystick, false);13091310#ifdef SDL_JOYSTICK_VIRTUAL1311result = SDL_SetJoystickVirtualAxisInner(joystick, axis, value);1312#else1313result = SDL_SetError("SDL not built with virtual-joystick support");1314#endif1315}1316SDL_UnlockJoysticks();13171318return result;1319}13201321bool SDL_SetJoystickVirtualBall(SDL_Joystick *joystick, int ball, Sint16 xrel, Sint16 yrel)1322{1323bool result;13241325SDL_LockJoysticks();1326{1327CHECK_JOYSTICK_MAGIC(joystick, false);1328CHECK_JOYSTICK_VIRTUAL(joystick, false);13291330#ifdef SDL_JOYSTICK_VIRTUAL1331result = SDL_SetJoystickVirtualBallInner(joystick, ball, xrel, yrel);1332#else1333result = SDL_SetError("SDL not built with virtual-joystick support");1334#endif1335}1336SDL_UnlockJoysticks();13371338return result;1339}13401341bool SDL_SetJoystickVirtualButton(SDL_Joystick *joystick, int button, bool down)1342{1343bool result;13441345SDL_LockJoysticks();1346{1347CHECK_JOYSTICK_MAGIC(joystick, false);1348CHECK_JOYSTICK_VIRTUAL(joystick, false);13491350#ifdef SDL_JOYSTICK_VIRTUAL1351result = SDL_SetJoystickVirtualButtonInner(joystick, button, down);1352#else1353result = SDL_SetError("SDL not built with virtual-joystick support");1354#endif1355}1356SDL_UnlockJoysticks();13571358return result;1359}13601361bool SDL_SetJoystickVirtualHat(SDL_Joystick *joystick, int hat, Uint8 value)1362{1363bool result;13641365SDL_LockJoysticks();1366{1367CHECK_JOYSTICK_MAGIC(joystick, false);1368CHECK_JOYSTICK_VIRTUAL(joystick, false);13691370#ifdef SDL_JOYSTICK_VIRTUAL1371result = SDL_SetJoystickVirtualHatInner(joystick, hat, value);1372#else1373result = SDL_SetError("SDL not built with virtual-joystick support");1374#endif1375}1376SDL_UnlockJoysticks();13771378return result;1379}13801381bool SDL_SetJoystickVirtualTouchpad(SDL_Joystick *joystick, int touchpad, int finger, bool down, float x, float y, float pressure)1382{1383bool result;13841385SDL_LockJoysticks();1386{1387CHECK_JOYSTICK_MAGIC(joystick, false);1388CHECK_JOYSTICK_VIRTUAL(joystick, false);13891390#ifdef SDL_JOYSTICK_VIRTUAL1391result = SDL_SetJoystickVirtualTouchpadInner(joystick, touchpad, finger, down, x, y, pressure);1392#else1393result = SDL_SetError("SDL not built with virtual-joystick support");1394#endif1395}1396SDL_UnlockJoysticks();13971398return result;1399}14001401bool SDL_SendJoystickVirtualSensorData(SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values)1402{1403bool result;14041405SDL_LockJoysticks();1406{1407CHECK_JOYSTICK_MAGIC(joystick, false);1408CHECK_JOYSTICK_VIRTUAL(joystick, false);14091410#ifdef SDL_JOYSTICK_VIRTUAL1411result = SDL_SendJoystickVirtualSensorDataInner(joystick, type, sensor_timestamp, data, num_values);1412#else1413result = SDL_SetError("SDL not built with virtual-joystick support");1414#endif1415}1416SDL_UnlockJoysticks();14171418return result;1419}14201421/*1422* Checks to make sure the joystick is valid.1423*/1424bool SDL_IsJoystickValid(SDL_Joystick *joystick)1425{1426SDL_AssertJoysticksLocked();1427return SDL_ObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK);1428}14291430bool SDL_PrivateJoystickGetAutoGamepadMapping(SDL_JoystickID instance_id, SDL_GamepadMapping *out)1431{1432SDL_JoystickDriver *driver;1433int device_index;1434bool is_ok = false;14351436SDL_LockJoysticks();1437if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {1438is_ok = driver->GetGamepadMapping(device_index, out);1439}1440SDL_UnlockJoysticks();14411442return is_ok;1443}14441445/*1446* Get the number of multi-dimensional axis controls on a joystick1447*/1448int SDL_GetNumJoystickAxes(SDL_Joystick *joystick)1449{1450int result;14511452SDL_LockJoysticks();1453{1454CHECK_JOYSTICK_MAGIC(joystick, -1);14551456result = joystick->naxes;1457}1458SDL_UnlockJoysticks();14591460return result;1461}14621463/*1464* Get the number of hats on a joystick1465*/1466int SDL_GetNumJoystickHats(SDL_Joystick *joystick)1467{1468int result;14691470SDL_LockJoysticks();1471{1472CHECK_JOYSTICK_MAGIC(joystick, -1);14731474result = joystick->nhats;1475}1476SDL_UnlockJoysticks();14771478return result;1479}14801481/*1482* Get the number of trackballs on a joystick1483*/1484int SDL_GetNumJoystickBalls(SDL_Joystick *joystick)1485{1486CHECK_JOYSTICK_MAGIC(joystick, -1);14871488return joystick->nballs;1489}14901491/*1492* Get the number of buttons on a joystick1493*/1494int SDL_GetNumJoystickButtons(SDL_Joystick *joystick)1495{1496int result;14971498SDL_LockJoysticks();1499{1500CHECK_JOYSTICK_MAGIC(joystick, -1);15011502result = joystick->nbuttons;1503}1504SDL_UnlockJoysticks();15051506return result;1507}15081509/*1510* Get the current state of an axis control on a joystick1511*/1512Sint16 SDL_GetJoystickAxis(SDL_Joystick *joystick, int axis)1513{1514Sint16 state;15151516SDL_LockJoysticks();1517{1518CHECK_JOYSTICK_MAGIC(joystick, 0);15191520if (axis < joystick->naxes) {1521state = joystick->axes[axis].value;1522} else {1523SDL_SetError("Joystick only has %d axes", joystick->naxes);1524state = 0;1525}1526}1527SDL_UnlockJoysticks();15281529return state;1530}15311532/*1533* Get the initial state of an axis control on a joystick1534*/1535bool SDL_GetJoystickAxisInitialState(SDL_Joystick *joystick, int axis, Sint16 *state)1536{1537bool result;15381539SDL_LockJoysticks();1540{1541CHECK_JOYSTICK_MAGIC(joystick, false);15421543if (axis >= joystick->naxes) {1544SDL_SetError("Joystick only has %d axes", joystick->naxes);1545result = false;1546} else {1547if (state) {1548*state = joystick->axes[axis].initial_value;1549}1550result = joystick->axes[axis].has_initial_value;1551}1552}1553SDL_UnlockJoysticks();15541555return result;1556}15571558/*1559* Get the current state of a hat on a joystick1560*/1561Uint8 SDL_GetJoystickHat(SDL_Joystick *joystick, int hat)1562{1563Uint8 state;15641565SDL_LockJoysticks();1566{1567CHECK_JOYSTICK_MAGIC(joystick, 0);15681569if (hat < joystick->nhats) {1570state = joystick->hats[hat];1571} else {1572SDL_SetError("Joystick only has %d hats", joystick->nhats);1573state = 0;1574}1575}1576SDL_UnlockJoysticks();15771578return state;1579}15801581/*1582* Get the ball axis change since the last poll1583*/1584bool SDL_GetJoystickBall(SDL_Joystick *joystick, int ball, int *dx, int *dy)1585{1586bool result;15871588SDL_LockJoysticks();1589{1590CHECK_JOYSTICK_MAGIC(joystick, false);15911592if (ball < joystick->nballs) {1593if (dx) {1594*dx = joystick->balls[ball].dx;1595}1596if (dy) {1597*dy = joystick->balls[ball].dy;1598}1599joystick->balls[ball].dx = 0;1600joystick->balls[ball].dy = 0;1601result = true;1602} else {1603result = SDL_SetError("Joystick only has %d balls", joystick->nballs);1604}1605}1606SDL_UnlockJoysticks();16071608return result;1609}16101611/*1612* Get the current state of a button on a joystick1613*/1614bool SDL_GetJoystickButton(SDL_Joystick *joystick, int button)1615{1616bool down = false;16171618SDL_LockJoysticks();1619{1620CHECK_JOYSTICK_MAGIC(joystick, false);16211622if (button < joystick->nbuttons) {1623down = joystick->buttons[button];1624} else {1625SDL_SetError("Joystick only has %d buttons", joystick->nbuttons);1626}1627}1628SDL_UnlockJoysticks();16291630return down;1631}16321633/*1634* Return if the joystick in question is currently attached to the system,1635* \return false if not plugged in, true if still present.1636*/1637bool SDL_JoystickConnected(SDL_Joystick *joystick)1638{1639bool result;16401641SDL_LockJoysticks();1642{1643CHECK_JOYSTICK_MAGIC(joystick, false);16441645result = joystick->attached;1646}1647SDL_UnlockJoysticks();16481649return result;1650}16511652/*1653* Get the instance id for this opened joystick1654*/1655SDL_JoystickID SDL_GetJoystickID(SDL_Joystick *joystick)1656{1657SDL_JoystickID result;16581659SDL_LockJoysticks();1660{1661CHECK_JOYSTICK_MAGIC(joystick, 0);16621663result = joystick->instance_id;1664}1665SDL_UnlockJoysticks();16661667return result;1668}16691670/*1671* Return the SDL_Joystick associated with an instance id.1672*/1673SDL_Joystick *SDL_GetJoystickFromID(SDL_JoystickID instance_id)1674{1675SDL_Joystick *joystick;16761677SDL_LockJoysticks();1678for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {1679if (joystick->instance_id == instance_id) {1680break;1681}1682}1683SDL_UnlockJoysticks();1684return joystick;1685}16861687/**1688* Return the SDL_Joystick associated with a player index.1689*/1690SDL_Joystick *SDL_GetJoystickFromPlayerIndex(int player_index)1691{1692SDL_JoystickID instance_id;1693SDL_Joystick *joystick;16941695SDL_LockJoysticks();1696instance_id = SDL_GetJoystickIDForPlayerIndex(player_index);1697for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {1698if (joystick->instance_id == instance_id) {1699break;1700}1701}1702SDL_UnlockJoysticks();1703return joystick;1704}17051706/*1707* Get the properties associated with a joystick1708*/1709SDL_PropertiesID SDL_GetJoystickProperties(SDL_Joystick *joystick)1710{1711SDL_PropertiesID result;17121713SDL_LockJoysticks();1714{1715CHECK_JOYSTICK_MAGIC(joystick, 0);17161717if (joystick->props == 0) {1718joystick->props = SDL_CreateProperties();1719}1720result = joystick->props;1721}1722SDL_UnlockJoysticks();17231724return result;1725}17261727/*1728* Get the friendly name of this joystick1729*/1730const char *SDL_GetJoystickName(SDL_Joystick *joystick)1731{1732const char *result;1733const SDL_SteamVirtualGamepadInfo *info;17341735SDL_LockJoysticks();1736{1737CHECK_JOYSTICK_MAGIC(joystick, NULL);17381739info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);1740if (info) {1741result = SDL_GetPersistentString(info->name);1742} else {1743result = SDL_GetPersistentString(joystick->name);1744}1745}1746SDL_UnlockJoysticks();17471748return result;1749}17501751/*1752* Get the implementation dependent path of this joystick1753*/1754const char *SDL_GetJoystickPath(SDL_Joystick *joystick)1755{1756const char *result;17571758SDL_LockJoysticks();1759{1760CHECK_JOYSTICK_MAGIC(joystick, NULL);17611762if (joystick->path) {1763result = SDL_GetPersistentString(joystick->path);1764} else {1765SDL_Unsupported();1766result = NULL;1767}1768}1769SDL_UnlockJoysticks();17701771return result;1772}17731774/**1775* Get the player index of an opened joystick, or -1 if it's not available1776*/1777int SDL_GetJoystickPlayerIndex(SDL_Joystick *joystick)1778{1779int result;17801781SDL_LockJoysticks();1782{1783CHECK_JOYSTICK_MAGIC(joystick, -1);17841785result = SDL_GetPlayerIndexForJoystickID(joystick->instance_id);1786}1787SDL_UnlockJoysticks();17881789return result;1790}17911792/**1793* Set the player index of an opened joystick1794*/1795bool SDL_SetJoystickPlayerIndex(SDL_Joystick *joystick, int player_index)1796{1797bool result;17981799SDL_LockJoysticks();1800{1801CHECK_JOYSTICK_MAGIC(joystick, false);18021803result = SDL_SetJoystickIDForPlayerIndex(player_index, joystick->instance_id);1804}1805SDL_UnlockJoysticks();18061807return result;1808}18091810bool SDL_RumbleJoystick(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)1811{1812bool result;18131814SDL_LockJoysticks();1815{1816CHECK_JOYSTICK_MAGIC(joystick, false);18171818if (low_frequency_rumble == joystick->low_frequency_rumble &&1819high_frequency_rumble == joystick->high_frequency_rumble) {1820// Just update the expiration1821result = true;1822} else {1823result = joystick->driver->Rumble(joystick, low_frequency_rumble, high_frequency_rumble);1824if (result) {1825joystick->rumble_resend = SDL_GetTicks() + SDL_RUMBLE_RESEND_MS;1826if (joystick->rumble_resend == 0) {1827joystick->rumble_resend = 1;1828}1829} else {1830joystick->rumble_resend = 0;1831}1832}18331834if (result) {1835joystick->low_frequency_rumble = low_frequency_rumble;1836joystick->high_frequency_rumble = high_frequency_rumble;18371838if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {1839joystick->rumble_expiration = SDL_GetTicks() + SDL_min(duration_ms, SDL_MAX_RUMBLE_DURATION_MS);1840if (!joystick->rumble_expiration) {1841joystick->rumble_expiration = 1;1842}1843} else {1844joystick->rumble_expiration = 0;1845joystick->rumble_resend = 0;1846}1847}1848}1849SDL_UnlockJoysticks();18501851return result;1852}18531854bool SDL_RumbleJoystickTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms)1855{1856bool result;18571858SDL_LockJoysticks();1859{1860CHECK_JOYSTICK_MAGIC(joystick, false);18611862if (left_rumble == joystick->left_trigger_rumble && right_rumble == joystick->right_trigger_rumble) {1863// Just update the expiration1864result = true;1865} else {1866result = joystick->driver->RumbleTriggers(joystick, left_rumble, right_rumble);1867if (result) {1868joystick->trigger_rumble_resend = SDL_GetTicks() + SDL_RUMBLE_RESEND_MS;1869if (joystick->trigger_rumble_resend == 0) {1870joystick->trigger_rumble_resend = 1;1871}1872} else {1873joystick->trigger_rumble_resend = 0;1874}1875}18761877if (result) {1878joystick->left_trigger_rumble = left_rumble;1879joystick->right_trigger_rumble = right_rumble;18801881if ((left_rumble || right_rumble) && duration_ms) {1882joystick->trigger_rumble_expiration = SDL_GetTicks() + SDL_min(duration_ms, SDL_MAX_RUMBLE_DURATION_MS);1883} else {1884joystick->trigger_rumble_expiration = 0;1885joystick->trigger_rumble_resend = 0;1886}1887}1888}1889SDL_UnlockJoysticks();18901891return result;1892}18931894bool SDL_SetJoystickLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)1895{1896bool result;1897bool isfreshvalue;18981899SDL_LockJoysticks();1900{1901CHECK_JOYSTICK_MAGIC(joystick, false);19021903isfreshvalue = red != joystick->led_red ||1904green != joystick->led_green ||1905blue != joystick->led_blue;19061907if (isfreshvalue || SDL_GetTicks() >= joystick->led_expiration) {1908result = joystick->driver->SetLED(joystick, red, green, blue);1909joystick->led_expiration = SDL_GetTicks() + SDL_LED_MIN_REPEAT_MS;1910} else {1911// Avoid spamming the driver1912result = true;1913}19141915// Save the LED value regardless of success, so we don't spam the driver1916joystick->led_red = red;1917joystick->led_green = green;1918joystick->led_blue = blue;1919}1920SDL_UnlockJoysticks();19211922return result;1923}19241925bool SDL_SendJoystickEffect(SDL_Joystick *joystick, const void *data, int size)1926{1927bool result;19281929SDL_LockJoysticks();1930{1931CHECK_JOYSTICK_MAGIC(joystick, false);19321933result = joystick->driver->SendEffect(joystick, data, size);1934}1935SDL_UnlockJoysticks();19361937return result;1938}19391940/*1941* Close a joystick previously opened with SDL_OpenJoystick()1942*/1943void SDL_CloseJoystick(SDL_Joystick *joystick)1944{1945SDL_Joystick *joysticklist;1946SDL_Joystick *joysticklistprev;1947int i;19481949SDL_LockJoysticks();1950{1951CHECK_JOYSTICK_MAGIC(joystick,);19521953// First decrement ref count1954if (--joystick->ref_count > 0) {1955SDL_UnlockJoysticks();1956return;1957}19581959SDL_DestroyProperties(joystick->props);19601961if (joystick->rumble_expiration) {1962SDL_RumbleJoystick(joystick, 0, 0, 0);1963}1964if (joystick->trigger_rumble_expiration) {1965SDL_RumbleJoystickTriggers(joystick, 0, 0, 0);1966}19671968CleanupSensorFusion(joystick);19691970joystick->driver->Close(joystick);1971joystick->hwdata = NULL;1972SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, false);19731974joysticklist = SDL_joysticks;1975joysticklistprev = NULL;1976while (joysticklist) {1977if (joystick == joysticklist) {1978if (joysticklistprev) {1979// unlink this entry1980joysticklistprev->next = joysticklist->next;1981} else {1982SDL_joysticks = joystick->next;1983}1984break;1985}1986joysticklistprev = joysticklist;1987joysticklist = joysticklist->next;1988}19891990// Free the data associated with this joystick1991SDL_free(joystick->name);1992SDL_free(joystick->path);1993SDL_free(joystick->serial);1994SDL_free(joystick->axes);1995SDL_free(joystick->balls);1996SDL_free(joystick->hats);1997SDL_free(joystick->buttons);1998for (i = 0; i < joystick->ntouchpads; i++) {1999SDL_JoystickTouchpadInfo *touchpad = &joystick->touchpads[i];2000SDL_free(touchpad->fingers);2001}2002SDL_free(joystick->touchpads);2003SDL_free(joystick->sensors);2004SDL_free(joystick);2005}2006SDL_UnlockJoysticks();2007}20082009void SDL_QuitJoysticks(void)2010{2011int i;2012SDL_JoystickID *joysticks;20132014SDL_LockJoysticks();20152016SDL_joysticks_quitting = true;20172018joysticks = SDL_GetJoysticks(NULL);2019if (joysticks) {2020for (i = 0; joysticks[i]; ++i) {2021SDL_PrivateJoystickRemoved(joysticks[i]);2022}2023SDL_free(joysticks);2024}20252026while (SDL_joysticks) {2027SDL_joysticks->ref_count = 1;2028SDL_CloseJoystick(SDL_joysticks);2029}20302031// Quit drivers in reverse order to avoid breaking dependencies between drivers2032for (i = SDL_arraysize(SDL_joystick_drivers) - 1; i >= 0; --i) {2033SDL_joystick_drivers[i]->Quit();2034}20352036if (SDL_joystick_players) {2037SDL_free(SDL_joystick_players);2038SDL_joystick_players = NULL;2039SDL_joystick_player_count = 0;2040}20412042SDL_QuitSubSystem(SDL_INIT_EVENTS);20432044SDL_QuitSteamVirtualGamepadInfo();20452046SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,2047SDL_JoystickAllowBackgroundEventsChanged, NULL);20482049SDL_FreeVIDPIDList(&arcadestick_devices);2050SDL_FreeVIDPIDList(&blacklist_devices);2051SDL_FreeVIDPIDList(&flightstick_devices);2052SDL_FreeVIDPIDList(&gamecube_devices);2053SDL_FreeVIDPIDList(&rog_gamepad_mice);2054SDL_FreeVIDPIDList(&throttle_devices);2055SDL_FreeVIDPIDList(&wheel_devices);2056SDL_FreeVIDPIDList(&zero_centered_devices);20572058SDL_QuitGamepadMappings();20592060SDL_joysticks_quitting = false;2061SDL_joysticks_initialized = false;20622063SDL_UnlockJoysticks();2064}20652066static bool SDL_PrivateJoystickShouldIgnoreEvent(void)2067{2068if (SDL_joystick_allows_background_events) {2069return false;2070}2071return false;2072}20732074// These are global for SDL_sysjoystick.c and SDL_events.c20752076void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers)2077{2078int ntouchpads;2079SDL_JoystickTouchpadInfo *touchpads;20802081SDL_AssertJoysticksLocked();20822083ntouchpads = joystick->ntouchpads + 1;2084touchpads = (SDL_JoystickTouchpadInfo *)SDL_realloc(joystick->touchpads, (ntouchpads * sizeof(SDL_JoystickTouchpadInfo)));2085if (touchpads) {2086SDL_JoystickTouchpadInfo *touchpad = &touchpads[ntouchpads - 1];2087SDL_JoystickTouchpadFingerInfo *fingers = (SDL_JoystickTouchpadFingerInfo *)SDL_calloc(nfingers, sizeof(SDL_JoystickTouchpadFingerInfo));20882089if (fingers) {2090touchpad->nfingers = nfingers;2091touchpad->fingers = fingers;2092} else {2093// Out of memory, this touchpad won't be active2094touchpad->nfingers = 0;2095touchpad->fingers = NULL;2096}20972098joystick->ntouchpads = ntouchpads;2099joystick->touchpads = touchpads;2100}2101}21022103void SDL_PrivateJoystickAddSensor(SDL_Joystick *joystick, SDL_SensorType type, float rate)2104{2105int nsensors;2106SDL_JoystickSensorInfo *sensors;21072108SDL_AssertJoysticksLocked();21092110nsensors = joystick->nsensors + 1;2111sensors = (SDL_JoystickSensorInfo *)SDL_realloc(joystick->sensors, (nsensors * sizeof(SDL_JoystickSensorInfo)));2112if (sensors) {2113SDL_JoystickSensorInfo *sensor = &sensors[nsensors - 1];21142115SDL_zerop(sensor);2116sensor->type = type;2117sensor->rate = rate;21182119joystick->nsensors = nsensors;2120joystick->sensors = sensors;2121}2122}21232124void SDL_PrivateJoystickSensorRate(SDL_Joystick *joystick, SDL_SensorType type, float rate)2125{2126int i;2127SDL_AssertJoysticksLocked();21282129for (i = 0; i < joystick->nsensors; ++i) {2130if (joystick->sensors[i].type == type) {2131joystick->sensors[i].rate = rate;2132}2133}2134}21352136void SDL_PrivateJoystickAdded(SDL_JoystickID instance_id)2137{2138SDL_JoystickDriver *driver;2139int device_index;2140int player_index = -1;21412142SDL_AssertJoysticksLocked();21432144if (SDL_JoysticksQuitting()) {2145return;2146}21472148SDL_joystick_being_added = true;21492150if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {2151player_index = driver->GetDeviceSteamVirtualGamepadSlot(device_index);2152if (player_index < 0) {2153player_index = driver->GetDevicePlayerIndex(device_index);2154}2155}2156if (player_index < 0 && SDL_IsGamepad(instance_id)) {2157player_index = SDL_FindFreePlayerIndex();2158}2159if (player_index >= 0) {2160SDL_SetJoystickIDForPlayerIndex(player_index, instance_id);2161}21622163{2164SDL_Event event;21652166event.type = SDL_EVENT_JOYSTICK_ADDED;2167event.common.timestamp = 0;21682169if (SDL_EventEnabled(event.type)) {2170event.jdevice.which = instance_id;2171SDL_PushEvent(&event);2172}2173}21742175SDL_joystick_being_added = false;21762177if (SDL_IsGamepad(instance_id)) {2178SDL_PrivateGamepadAdded(instance_id);2179}2180}21812182bool SDL_IsJoystickBeingAdded(void)2183{2184return SDL_joystick_being_added;2185}21862187void SDL_PrivateJoystickForceRecentering(SDL_Joystick *joystick)2188{2189Uint8 i, j;2190Uint64 timestamp = SDL_GetTicksNS();21912192SDL_AssertJoysticksLocked();21932194// Tell the app that everything is centered/unpressed...2195for (i = 0; i < joystick->naxes; i++) {2196if (joystick->axes[i].has_initial_value) {2197SDL_SendJoystickAxis(timestamp, joystick, i, joystick->axes[i].zero);2198}2199}22002201for (i = 0; i < joystick->nbuttons; i++) {2202SDL_SendJoystickButton(timestamp, joystick, i, false);2203}22042205for (i = 0; i < joystick->nhats; i++) {2206SDL_SendJoystickHat(timestamp, joystick, i, SDL_HAT_CENTERED);2207}22082209for (i = 0; i < joystick->ntouchpads; i++) {2210SDL_JoystickTouchpadInfo *touchpad = &joystick->touchpads[i];22112212for (j = 0; j < touchpad->nfingers; ++j) {2213SDL_SendJoystickTouchpad(timestamp, joystick, i, j, false, 0.0f, 0.0f, 0.0f);2214}2215}2216}22172218void SDL_PrivateJoystickRemoved(SDL_JoystickID instance_id)2219{2220SDL_Joystick *joystick = NULL;2221int player_index;2222SDL_Event event;22232224SDL_AssertJoysticksLocked();22252226// Find this joystick...2227for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {2228if (joystick->instance_id == instance_id) {2229SDL_PrivateJoystickForceRecentering(joystick);2230joystick->attached = false;2231break;2232}2233}22342235if (SDL_IsGamepad(instance_id)) {2236SDL_PrivateGamepadRemoved(instance_id);2237}22382239event.type = SDL_EVENT_JOYSTICK_REMOVED;2240event.common.timestamp = 0;22412242if (SDL_EventEnabled(event.type)) {2243event.jdevice.which = instance_id;2244SDL_PushEvent(&event);2245}22462247player_index = SDL_GetPlayerIndexForJoystickID(instance_id);2248if (player_index >= 0) {2249SDL_joystick_players[player_index] = 0;2250}2251}22522253void SDL_SendJoystickAxis(Uint64 timestamp, SDL_Joystick *joystick, Uint8 axis, Sint16 value)2254{2255SDL_JoystickAxisInfo *info;22562257SDL_AssertJoysticksLocked();22582259// Make sure we're not getting garbage or duplicate events2260if (axis >= joystick->naxes) {2261return;2262}22632264info = &joystick->axes[axis];2265if (!info->has_initial_value ||2266(!info->has_second_value && (info->initial_value <= -32767 || info->initial_value == 32767) && SDL_abs(value) < (SDL_JOYSTICK_AXIS_MAX / 4))) {2267info->initial_value = value;2268info->value = value;2269info->zero = value;2270info->has_initial_value = true;2271} else if (value == info->value && !info->sending_initial_value) {2272return;2273} else {2274info->has_second_value = true;2275}2276if (!info->sent_initial_value) {2277// Make sure we don't send motion until there's real activity on this axis2278const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; // ShanWan PS3 controller needed 962279if (SDL_abs(value - info->value) <= MAX_ALLOWED_JITTER &&2280!SDL_IsJoystickVIRTUAL(joystick->guid)) {2281return;2282}2283info->sent_initial_value = true;2284info->sending_initial_value = true;2285SDL_SendJoystickAxis(timestamp, joystick, axis, info->initial_value);2286info->sending_initial_value = false;2287}22882289/* We ignore events if we don't have keyboard focus, except for centering2290* events.2291*/2292if (SDL_PrivateJoystickShouldIgnoreEvent()) {2293if (info->sending_initial_value ||2294(value > info->zero && value >= info->value) ||2295(value < info->zero && value <= info->value)) {2296return;2297}2298}22992300// Update internal joystick state2301SDL_assert(timestamp != 0);2302info->value = value;2303joystick->update_complete = timestamp;23042305// Post the event, if desired2306if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_AXIS_MOTION)) {2307SDL_Event event;2308event.type = SDL_EVENT_JOYSTICK_AXIS_MOTION;2309event.common.timestamp = timestamp;2310event.jaxis.which = joystick->instance_id;2311event.jaxis.axis = axis;2312event.jaxis.value = value;2313SDL_PushEvent(&event);2314}2315}23162317void SDL_SendJoystickBall(Uint64 timestamp, SDL_Joystick *joystick, Uint8 ball, Sint16 xrel, Sint16 yrel)2318{2319SDL_AssertJoysticksLocked();23202321// Make sure we're not getting garbage events2322if (ball >= joystick->nballs) {2323return;2324}23252326// We ignore events if we don't have keyboard focus.2327if (SDL_PrivateJoystickShouldIgnoreEvent()) {2328return;2329}23302331// Update internal mouse state2332joystick->balls[ball].dx += xrel;2333joystick->balls[ball].dy += yrel;23342335// Post the event, if desired2336if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_BALL_MOTION)) {2337SDL_Event event;2338event.type = SDL_EVENT_JOYSTICK_BALL_MOTION;2339event.common.timestamp = timestamp;2340event.jball.which = joystick->instance_id;2341event.jball.ball = ball;2342event.jball.xrel = xrel;2343event.jball.yrel = yrel;2344SDL_PushEvent(&event);2345}2346}23472348void SDL_SendJoystickHat(Uint64 timestamp, SDL_Joystick *joystick, Uint8 hat, Uint8 value)2349{2350SDL_AssertJoysticksLocked();23512352// Make sure we're not getting garbage or duplicate events2353if (hat >= joystick->nhats) {2354return;2355}2356if (value == joystick->hats[hat]) {2357return;2358}23592360/* We ignore events if we don't have keyboard focus, except for centering2361* events.2362*/2363if (SDL_PrivateJoystickShouldIgnoreEvent()) {2364if (value != SDL_HAT_CENTERED) {2365return;2366}2367}23682369// Update internal joystick state2370SDL_assert(timestamp != 0);2371joystick->hats[hat] = value;2372joystick->update_complete = timestamp;23732374// Post the event, if desired2375if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_HAT_MOTION)) {2376SDL_Event event;2377event.type = SDL_EVENT_JOYSTICK_HAT_MOTION;2378event.common.timestamp = timestamp;2379event.jhat.which = joystick->instance_id;2380event.jhat.hat = hat;2381event.jhat.value = value;2382SDL_PushEvent(&event);2383}2384}23852386void SDL_SendJoystickButton(Uint64 timestamp, SDL_Joystick *joystick, Uint8 button, bool down)2387{2388SDL_Event event;23892390SDL_AssertJoysticksLocked();23912392if (down) {2393event.type = SDL_EVENT_JOYSTICK_BUTTON_DOWN;2394} else {2395event.type = SDL_EVENT_JOYSTICK_BUTTON_UP;2396}23972398if (joystick->swap_face_buttons) {2399switch (button) {2400case 0:2401button = 1;2402break;2403case 1:2404button = 0;2405break;2406case 2:2407button = 3;2408break;2409case 3:2410button = 2;2411break;2412default:2413break;2414}2415}24162417// Make sure we're not getting garbage or duplicate events2418if (button >= joystick->nbuttons) {2419return;2420}2421if (down == joystick->buttons[button]) {2422return;2423}24242425/* We ignore events if we don't have keyboard focus, except for button2426* release. */2427if (SDL_PrivateJoystickShouldIgnoreEvent()) {2428if (down) {2429return;2430}2431}24322433// Update internal joystick state2434SDL_assert(timestamp != 0);2435joystick->buttons[button] = down;2436joystick->update_complete = timestamp;24372438// Post the event, if desired2439if (SDL_EventEnabled(event.type)) {2440event.common.timestamp = timestamp;2441event.jbutton.which = joystick->instance_id;2442event.jbutton.button = button;2443event.jbutton.down = down;2444SDL_PushEvent(&event);2445}2446}24472448static void SendSteamHandleUpdateEvents(void)2449{2450SDL_Joystick *joystick;2451const SDL_SteamVirtualGamepadInfo *info;24522453// Check to see if any Steam handles changed2454for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {2455bool changed = false;24562457if (!SDL_IsGamepad(joystick->instance_id)) {2458continue;2459}24602461info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);2462if (info) {2463if (joystick->steam_handle != info->handle) {2464joystick->steam_handle = info->handle;2465joystick->swap_face_buttons = ShouldSwapFaceButtons(info);2466changed = true;2467}2468} else {2469if (joystick->steam_handle != 0) {2470joystick->steam_handle = 0;2471joystick->swap_face_buttons = false;2472changed = true;2473}2474}2475if (changed) {2476SDL_Event event;24772478SDL_zero(event);2479event.type = SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED;2480event.common.timestamp = 0;2481event.gdevice.which = joystick->instance_id;2482SDL_PushEvent(&event);2483}2484}2485}24862487void SDL_UpdateJoysticks(void)2488{2489int i;2490Uint64 now;2491SDL_Joystick *joystick;24922493if (!SDL_WasInit(SDL_INIT_JOYSTICK)) {2494return;2495}24962497SDL_LockJoysticks();24982499if (SDL_UpdateSteamVirtualGamepadInfo()) {2500SendSteamHandleUpdateEvents();2501}25022503#ifdef SDL_JOYSTICK_HIDAPI2504// Special function for HIDAPI devices, as a single device can provide multiple SDL_Joysticks2505HIDAPI_UpdateDevices();2506#endif // SDL_JOYSTICK_HIDAPI25072508for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {2509if (!joystick->attached) {2510continue;2511}25122513joystick->driver->Update(joystick);25142515if (joystick->delayed_guide_button) {2516SDL_GamepadHandleDelayedGuideButton(joystick);2517}25182519now = SDL_GetTicks();2520if (joystick->rumble_expiration && now >= joystick->rumble_expiration) {2521SDL_RumbleJoystick(joystick, 0, 0, 0);2522joystick->rumble_resend = 0;2523}25242525if (joystick->rumble_resend && now >= joystick->rumble_resend) {2526joystick->driver->Rumble(joystick, joystick->low_frequency_rumble, joystick->high_frequency_rumble);2527joystick->rumble_resend = now + SDL_RUMBLE_RESEND_MS;2528if (joystick->rumble_resend == 0) {2529joystick->rumble_resend = 1;2530}2531}25322533if (joystick->trigger_rumble_expiration && now >= joystick->trigger_rumble_expiration) {2534SDL_RumbleJoystickTriggers(joystick, 0, 0, 0);2535joystick->trigger_rumble_resend = 0;2536}25372538if (joystick->trigger_rumble_resend && now >= joystick->trigger_rumble_resend) {2539joystick->driver->RumbleTriggers(joystick, joystick->left_trigger_rumble, joystick->right_trigger_rumble);2540joystick->trigger_rumble_resend = now + SDL_RUMBLE_RESEND_MS;2541if (joystick->trigger_rumble_resend == 0) {2542joystick->trigger_rumble_resend = 1;2543}2544}2545}25462547if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_UPDATE_COMPLETE)) {2548for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {2549if (joystick->update_complete) {2550SDL_Event event;25512552event.type = SDL_EVENT_JOYSTICK_UPDATE_COMPLETE;2553event.common.timestamp = joystick->update_complete;2554event.jdevice.which = joystick->instance_id;2555SDL_PushEvent(&event);25562557joystick->update_complete = 0;2558}2559}2560}25612562/* this needs to happen AFTER walking the joystick list above, so that any2563dangling hardware data from removed devices can be free'd2564*/2565for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {2566SDL_joystick_drivers[i]->Detect();2567}25682569SDL_UnlockJoysticks();2570}25712572static const Uint32 SDL_joystick_event_list[] = {2573SDL_EVENT_JOYSTICK_AXIS_MOTION,2574SDL_EVENT_JOYSTICK_BALL_MOTION,2575SDL_EVENT_JOYSTICK_HAT_MOTION,2576SDL_EVENT_JOYSTICK_BUTTON_DOWN,2577SDL_EVENT_JOYSTICK_BUTTON_UP,2578SDL_EVENT_JOYSTICK_ADDED,2579SDL_EVENT_JOYSTICK_REMOVED,2580SDL_EVENT_JOYSTICK_BATTERY_UPDATED2581};25822583void SDL_SetJoystickEventsEnabled(bool enabled)2584{2585unsigned int i;25862587for (i = 0; i < SDL_arraysize(SDL_joystick_event_list); ++i) {2588SDL_SetEventEnabled(SDL_joystick_event_list[i], enabled);2589}2590}25912592bool SDL_JoystickEventsEnabled(void)2593{2594bool enabled = false;2595unsigned int i;25962597for (i = 0; i < SDL_arraysize(SDL_joystick_event_list); ++i) {2598enabled = SDL_EventEnabled(SDL_joystick_event_list[i]);2599if (enabled) {2600break;2601}2602}2603return enabled;2604}26052606void SDL_GetJoystickGUIDInfo(SDL_GUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version, Uint16 *crc16)2607{2608Uint16 *guid16 = (Uint16 *)guid.data;2609Uint16 bus = SDL_Swap16LE(guid16[0]);26102611if ((bus < ' ' || bus == SDL_HARDWARE_BUS_VIRTUAL) && guid16[3] == 0x0000 && guid16[5] == 0x0000) {2612/* This GUID fits the standard form:2613* 16-bit bus2614* 16-bit CRC16 of the joystick name (can be zero)2615* 16-bit vendor ID2616* 16-bit zero2617* 16-bit product ID2618* 16-bit zero2619* 16-bit version2620* 8-bit driver identifier ('h' for HIDAPI, 'x' for XInput, etc.)2621* 8-bit driver-dependent type info2622*/2623if (vendor) {2624*vendor = SDL_Swap16LE(guid16[2]);2625}2626if (product) {2627*product = SDL_Swap16LE(guid16[4]);2628}2629if (version) {2630*version = SDL_Swap16LE(guid16[6]);2631}2632if (crc16) {2633*crc16 = SDL_Swap16LE(guid16[1]);2634}2635} else if (bus < ' ' || bus == SDL_HARDWARE_BUS_VIRTUAL) {2636/* This GUID fits the unknown VID/PID form:2637* 16-bit bus2638* 16-bit CRC16 of the joystick name (can be zero)2639* 11 characters of the joystick name, null terminated2640*/2641if (vendor) {2642*vendor = 0;2643}2644if (product) {2645*product = 0;2646}2647if (version) {2648*version = 0;2649}2650if (crc16) {2651*crc16 = SDL_Swap16LE(guid16[1]);2652}2653} else {2654if (vendor) {2655*vendor = 0;2656}2657if (product) {2658*product = 0;2659}2660if (version) {2661*version = 0;2662}2663if (crc16) {2664*crc16 = 0;2665}2666}2667}26682669char *SDL_CreateJoystickName(Uint16 vendor, Uint16 product, const char *vendor_name, const char *product_name)2670{2671const char *custom_name = GuessControllerName(vendor, product);2672if (custom_name) {2673return SDL_strdup(custom_name);2674}26752676return SDL_CreateDeviceName(vendor, product, vendor_name, product_name, "Controller");2677}26782679SDL_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)2680{2681SDL_GUID guid;2682Uint16 *guid16 = (Uint16 *)guid.data;2683Uint16 crc = 0;26842685SDL_zero(guid);26862687if (vendor_name && *vendor_name && product_name && *product_name) {2688crc = SDL_crc16(crc, vendor_name, SDL_strlen(vendor_name));2689crc = SDL_crc16(crc, " ", 1);2690crc = SDL_crc16(crc, product_name, SDL_strlen(product_name));2691} else if (product_name) {2692crc = SDL_crc16(crc, product_name, SDL_strlen(product_name));2693}26942695// We only need 16 bits for each of these; space them out to fill 128.2696// Byteswap so devices get same GUID on little/big endian platforms.2697*guid16++ = SDL_Swap16LE(bus);2698*guid16++ = SDL_Swap16LE(crc);26992700if (vendor) {2701*guid16++ = SDL_Swap16LE(vendor);2702*guid16++ = 0;2703*guid16++ = SDL_Swap16LE(product);2704*guid16++ = 0;2705*guid16++ = SDL_Swap16LE(version);2706guid.data[14] = driver_signature;2707guid.data[15] = driver_data;2708} else {2709size_t available_space = sizeof(guid.data) - 4;27102711if (driver_signature) {2712available_space -= 2;2713guid.data[14] = driver_signature;2714guid.data[15] = driver_data;2715}2716if (product_name) {2717SDL_strlcpy((char *)guid16, product_name, available_space);2718}2719}2720return guid;2721}27222723SDL_GUID SDL_CreateJoystickGUIDForName(const char *name)2724{2725return SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_UNKNOWN, 0, 0, 0, NULL, name, 0, 0);2726}27272728void SDL_SetJoystickGUIDVendor(SDL_GUID *guid, Uint16 vendor)2729{2730Uint16 *guid16 = (Uint16 *)guid->data;27312732guid16[2] = SDL_Swap16LE(vendor);2733}27342735void SDL_SetJoystickGUIDProduct(SDL_GUID *guid, Uint16 product)2736{2737Uint16 *guid16 = (Uint16 *)guid->data;27382739guid16[4] = SDL_Swap16LE(product);2740}27412742void SDL_SetJoystickGUIDVersion(SDL_GUID *guid, Uint16 version)2743{2744Uint16 *guid16 = (Uint16 *)guid->data;27452746guid16[6] = SDL_Swap16LE(version);2747}27482749void SDL_SetJoystickGUIDCRC(SDL_GUID *guid, Uint16 crc)2750{2751Uint16 *guid16 = (Uint16 *)guid->data;27522753guid16[1] = SDL_Swap16LE(crc);2754}27552756SDL_GamepadType SDL_GetGamepadTypeFromVIDPID(Uint16 vendor, Uint16 product, const char *name, bool forUI)2757{2758SDL_GamepadType type = SDL_GAMEPAD_TYPE_STANDARD;27592760if (vendor == 0x0000 && product == 0x0000) {2761// Some devices are only identifiable by their name2762if (name &&2763(SDL_strcmp(name, "Lic Pro Controller") == 0 ||2764SDL_strcmp(name, "Nintendo Wireless Gamepad") == 0 ||2765SDL_strcmp(name, "Wireless Gamepad") == 0)) {2766// HORI or PowerA Switch Pro Controller clone2767type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;2768}27692770} else if (vendor == 0x0001 && product == 0x0001) {2771type = SDL_GAMEPAD_TYPE_STANDARD;27722773} else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT) {2774type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;27752776} else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) {2777if (name && SDL_strstr(name, "NES Controller") != NULL) {2778// We don't have a type for the Nintendo Online NES Controller2779type = SDL_GAMEPAD_TYPE_STANDARD;2780} else {2781type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;2782}27832784} else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {2785if (name && SDL_strstr(name, "(L)") != NULL) {2786type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;2787} else {2788type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;2789}27902791} else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR) {2792type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR;27932794} else if (forUI && SDL_IsJoystickGameCube(vendor, product)) {2795// We don't have a type for the Nintendo GameCube controller2796type = SDL_GAMEPAD_TYPE_STANDARD;27972798} else {2799switch (GuessControllerType(vendor, product)) {2800case k_eControllerType_XBox360Controller:2801type = SDL_GAMEPAD_TYPE_XBOX360;2802break;2803case k_eControllerType_XBoxOneController:2804type = SDL_GAMEPAD_TYPE_XBOXONE;2805break;2806case k_eControllerType_PS3Controller:2807type = SDL_GAMEPAD_TYPE_PS3;2808break;2809case k_eControllerType_PS4Controller:2810type = SDL_GAMEPAD_TYPE_PS4;2811break;2812case k_eControllerType_PS5Controller:2813type = SDL_GAMEPAD_TYPE_PS5;2814break;2815case k_eControllerType_XInputPS4Controller:2816if (forUI) {2817type = SDL_GAMEPAD_TYPE_PS4;2818} else {2819type = SDL_GAMEPAD_TYPE_STANDARD;2820}2821break;2822case k_eControllerType_SwitchProController:2823case k_eControllerType_SwitchInputOnlyController:2824type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;2825break;2826case k_eControllerType_XInputSwitchController:2827if (forUI) {2828type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;2829} else {2830type = SDL_GAMEPAD_TYPE_STANDARD;2831}2832break;2833default:2834break;2835}2836}2837return type;2838}28392840SDL_GamepadType SDL_GetGamepadTypeFromGUID(SDL_GUID guid, const char *name)2841{2842SDL_GamepadType type;2843Uint16 vendor, product;28442845SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);2846type = SDL_GetGamepadTypeFromVIDPID(vendor, product, name, true);2847if (type == SDL_GAMEPAD_TYPE_STANDARD) {2848if (SDL_IsJoystickXInput(guid)) {2849// This is probably an Xbox One controller2850return SDL_GAMEPAD_TYPE_XBOXONE;2851}2852#ifdef SDL_JOYSTICK_HIDAPI2853if (SDL_IsJoystickHIDAPI(guid)) {2854return HIDAPI_GetGamepadTypeFromGUID(guid);2855}2856#endif // SDL_JOYSTICK_HIDAPI2857}2858return type;2859}28602861bool SDL_JoystickGUIDUsesVersion(SDL_GUID guid)2862{2863Uint16 vendor, product;28642865if (SDL_IsJoystickMFI(guid)) {2866// The version bits are used as button capability mask2867return false;2868}28692870SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);2871if (vendor && product) {2872return true;2873}2874return false;2875}28762877bool SDL_IsJoystickXboxOne(Uint16 vendor_id, Uint16 product_id)2878{2879EControllerType eType = GuessControllerType(vendor_id, product_id);2880return eType == k_eControllerType_XBoxOneController;2881}28822883bool SDL_IsJoystickXboxOneElite(Uint16 vendor_id, Uint16 product_id)2884{2885if (vendor_id == USB_VENDOR_MICROSOFT) {2886if (product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_1 ||2887product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2 ||2888product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH ||2889product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLE) {2890return true;2891}2892}2893return false;2894}28952896bool SDL_IsJoystickXboxSeriesX(Uint16 vendor_id, Uint16 product_id)2897{2898if (vendor_id == USB_VENDOR_MICROSOFT) {2899if (product_id == USB_PRODUCT_XBOX_SERIES_X ||2900product_id == USB_PRODUCT_XBOX_SERIES_X_BLE) {2901return true;2902}2903}2904if (vendor_id == USB_VENDOR_PDP) {2905if (product_id == USB_PRODUCT_XBOX_SERIES_X_VICTRIX_GAMBIT ||2906product_id == USB_PRODUCT_XBOX_SERIES_X_PDP_BLUE ||2907product_id == USB_PRODUCT_XBOX_SERIES_X_PDP_AFTERGLOW) {2908return true;2909}2910}2911if (vendor_id == USB_VENDOR_POWERA_ALT) {2912if ((product_id >= 0x2001 && product_id <= 0x201a) ||2913product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO2 ||2914product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO4 ||2915product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO_WIRELESS_USB ||2916product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO_WIRELESS_DONGLE ||2917product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_MOGA_XP_ULTRA ||2918product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_SPECTRA) {2919return true;2920}2921}2922if (vendor_id == USB_VENDOR_HORI) {2923if (product_id == USB_PRODUCT_HORI_FIGHTING_COMMANDER_OCTA_SERIES_X ||2924product_id == USB_PRODUCT_HORI_HORIPAD_PRO_SERIES_X ||2925product_id == USB_PRODUCT_HORI_TAIKO_DRUM_CONTROLLER) {2926return true;2927}2928}2929if (vendor_id == USB_VENDOR_HP) {2930if (product_id == USB_PRODUCT_XBOX_SERIES_X_HP_HYPERX ||2931product_id == USB_PRODUCT_XBOX_SERIES_X_HP_HYPERX_RGB) {2932return true;2933}2934}2935if (vendor_id == USB_VENDOR_RAZER) {2936if (product_id == USB_PRODUCT_RAZER_WOLVERINE_V2 ||2937product_id == USB_PRODUCT_RAZER_WOLVERINE_V2_CHROMA ||2938product_id == USB_PRODUCT_RAZER_WOLVERINE_V3_PRO) {2939return true;2940}2941}2942if (vendor_id == USB_VENDOR_THRUSTMASTER) {2943if (product_id == USB_PRODUCT_THRUSTMASTER_ESWAPX_PRO_SERIES_X) {2944return true;2945}2946}2947if (vendor_id == USB_VENDOR_TURTLE_BEACH) {2948if (product_id == USB_PRODUCT_TURTLE_BEACH_SERIES_X_REACT_R ||2949product_id == USB_PRODUCT_TURTLE_BEACH_SERIES_X_RECON) {2950return true;2951}2952}2953if (vendor_id == USB_VENDOR_8BITDO) {2954if (product_id == USB_PRODUCT_8BITDO_XBOX_CONTROLLER1 ||2955product_id == USB_PRODUCT_8BITDO_XBOX_CONTROLLER2) {2956return true;2957}2958}2959if (vendor_id == USB_VENDOR_GAMESIR) {2960if (product_id == USB_PRODUCT_GAMESIR_G7) {2961return true;2962}2963}2964if (vendor_id == USB_VENDOR_ASUS) {2965if (product_id == USB_PRODUCT_ROG_RAIKIRI) {2966return true;2967}2968}2969return false;2970}29712972bool SDL_IsJoystickBluetoothXboxOne(Uint16 vendor_id, Uint16 product_id)2973{2974if (vendor_id == USB_VENDOR_MICROSOFT) {2975if (product_id == USB_PRODUCT_XBOX_ONE_ADAPTIVE_BLUETOOTH ||2976product_id == USB_PRODUCT_XBOX_ONE_ADAPTIVE_BLE ||2977product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH ||2978product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH ||2979product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLE ||2980product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH ||2981product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLE ||2982product_id == USB_PRODUCT_XBOX_SERIES_X_BLE) {2983return true;2984}2985}2986return false;2987}29882989bool SDL_IsJoystickPS4(Uint16 vendor_id, Uint16 product_id)2990{2991EControllerType eType = GuessControllerType(vendor_id, product_id);2992return eType == k_eControllerType_PS4Controller;2993}29942995bool SDL_IsJoystickPS5(Uint16 vendor_id, Uint16 product_id)2996{2997EControllerType eType = GuessControllerType(vendor_id, product_id);2998return eType == k_eControllerType_PS5Controller;2999}30003001bool SDL_IsJoystickDualSenseEdge(Uint16 vendor_id, Uint16 product_id)3002{3003if (vendor_id == USB_VENDOR_SONY) {3004if (product_id == USB_PRODUCT_SONY_DS5_EDGE) {3005return true;3006}3007}3008return false;3009}30103011bool SDL_IsJoystickNintendoSwitchPro(Uint16 vendor_id, Uint16 product_id)3012{3013EControllerType eType = GuessControllerType(vendor_id, product_id);3014return eType == k_eControllerType_SwitchProController || eType == k_eControllerType_SwitchInputOnlyController;3015}30163017bool SDL_IsJoystickNintendoSwitchProInputOnly(Uint16 vendor_id, Uint16 product_id)3018{3019EControllerType eType = GuessControllerType(vendor_id, product_id);3020return eType == k_eControllerType_SwitchInputOnlyController;3021}30223023bool SDL_IsJoystickNintendoSwitchJoyCon(Uint16 vendor_id, Uint16 product_id)3024{3025EControllerType eType = GuessControllerType(vendor_id, product_id);3026return eType == k_eControllerType_SwitchJoyConLeft || eType == k_eControllerType_SwitchJoyConRight;3027}30283029bool SDL_IsJoystickNintendoSwitchJoyConLeft(Uint16 vendor_id, Uint16 product_id)3030{3031EControllerType eType = GuessControllerType(vendor_id, product_id);3032return eType == k_eControllerType_SwitchJoyConLeft;3033}30343035bool SDL_IsJoystickNintendoSwitchJoyConRight(Uint16 vendor_id, Uint16 product_id)3036{3037EControllerType eType = GuessControllerType(vendor_id, product_id);3038return eType == k_eControllerType_SwitchJoyConRight;3039}30403041bool SDL_IsJoystickNintendoSwitchJoyConGrip(Uint16 vendor_id, Uint16 product_id)3042{3043return vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP;3044}30453046bool SDL_IsJoystickNintendoSwitchJoyConPair(Uint16 vendor_id, Uint16 product_id)3047{3048return vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR;3049}30503051bool SDL_IsJoystickGameCube(Uint16 vendor_id, Uint16 product_id)3052{3053return SDL_VIDPIDInList(vendor_id, product_id, &gamecube_devices);3054}30553056bool SDL_IsJoystickAmazonLunaController(Uint16 vendor_id, Uint16 product_id)3057{3058return ((vendor_id == USB_VENDOR_AMAZON && product_id == USB_PRODUCT_AMAZON_LUNA_CONTROLLER) ||3059(vendor_id == BLUETOOTH_VENDOR_AMAZON && product_id == BLUETOOTH_PRODUCT_LUNA_CONTROLLER));3060}30613062bool SDL_IsJoystickGoogleStadiaController(Uint16 vendor_id, Uint16 product_id)3063{3064return vendor_id == USB_VENDOR_GOOGLE && product_id == USB_PRODUCT_GOOGLE_STADIA_CONTROLLER;3065}30663067bool SDL_IsJoystickNVIDIASHIELDController(Uint16 vendor_id, Uint16 product_id)3068{3069return (vendor_id == USB_VENDOR_NVIDIA &&3070(product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103 ||3071product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V104));3072}30733074bool SDL_IsJoystickSteamVirtualGamepad(Uint16 vendor_id, Uint16 product_id, Uint16 version)3075{3076#ifdef SDL_PLATFORM_MACOS3077return (vendor_id == USB_VENDOR_MICROSOFT && product_id == USB_PRODUCT_XBOX360_WIRED_CONTROLLER && version == 0);3078#else3079return (vendor_id == USB_VENDOR_VALVE && product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD);3080#endif3081}30823083bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id)3084{3085EControllerType eType = GuessControllerType(vendor_id, product_id);3086return eType == k_eControllerType_SteamController || eType == k_eControllerType_SteamControllerV2;3087}30883089bool SDL_IsJoystickHoriSteamController(Uint16 vendor_id, Uint16 product_id)3090{3091return vendor_id == USB_VENDOR_HORI && (product_id == USB_PRODUCT_HORI_STEAM_CONTROLLER || product_id == USB_PRODUCT_HORI_STEAM_CONTROLLER_BT);3092}30933094bool SDL_IsJoystickSteamDeck(Uint16 vendor_id, Uint16 product_id)3095{3096EControllerType eType = GuessControllerType(vendor_id, product_id);3097return eType == k_eControllerType_SteamControllerNeptune;3098}30993100bool SDL_IsJoystickXInput(SDL_GUID guid)3101{3102return (guid.data[14] == 'x') ? true : false;3103}31043105bool SDL_IsJoystickWGI(SDL_GUID guid)3106{3107return (guid.data[14] == 'w') ? true : false;3108}31093110bool SDL_IsJoystickHIDAPI(SDL_GUID guid)3111{3112return (guid.data[14] == 'h') ? true : false;3113}31143115bool SDL_IsJoystickMFI(SDL_GUID guid)3116{3117return (guid.data[14] == 'm') ? true : false;3118}31193120bool SDL_IsJoystickRAWINPUT(SDL_GUID guid)3121{3122return (guid.data[14] == 'r') ? true : false;3123}31243125bool SDL_IsJoystickVIRTUAL(SDL_GUID guid)3126{3127return (guid.data[14] == 'v') ? true : false;3128}31293130static bool SDL_IsJoystickWheel(Uint16 vendor_id, Uint16 product_id)3131{3132return SDL_VIDPIDInList(vendor_id, product_id, &wheel_devices);3133}31343135static bool SDL_IsJoystickArcadeStick(Uint16 vendor_id, Uint16 product_id)3136{3137return SDL_VIDPIDInList(vendor_id, product_id, &arcadestick_devices);3138}31393140static bool SDL_IsJoystickFlightStick(Uint16 vendor_id, Uint16 product_id)3141{3142return SDL_VIDPIDInList(vendor_id, product_id, &flightstick_devices);3143}31443145static bool SDL_IsJoystickThrottle(Uint16 vendor_id, Uint16 product_id)3146{3147return SDL_VIDPIDInList(vendor_id, product_id, &throttle_devices);3148}31493150static SDL_JoystickType SDL_GetJoystickGUIDType(SDL_GUID guid)3151{3152Uint16 vendor;3153Uint16 product;31543155SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);31563157if (SDL_IsJoystickWheel(vendor, product)) {3158return SDL_JOYSTICK_TYPE_WHEEL;3159}31603161if (SDL_IsJoystickArcadeStick(vendor, product)) {3162return SDL_JOYSTICK_TYPE_ARCADE_STICK;3163}31643165if (SDL_IsJoystickFlightStick(vendor, product)) {3166return SDL_JOYSTICK_TYPE_FLIGHT_STICK;3167}31683169if (SDL_IsJoystickThrottle(vendor, product)) {3170return SDL_JOYSTICK_TYPE_THROTTLE;3171}31723173if (SDL_IsJoystickXInput(guid)) {3174// XInput GUID, get the type based on the XInput device subtype3175switch (guid.data[15]) {3176case 0x01: // XINPUT_DEVSUBTYPE_GAMEPAD3177return SDL_JOYSTICK_TYPE_GAMEPAD;3178case 0x02: // XINPUT_DEVSUBTYPE_WHEEL3179return SDL_JOYSTICK_TYPE_WHEEL;3180case 0x03: // XINPUT_DEVSUBTYPE_ARCADE_STICK3181return SDL_JOYSTICK_TYPE_ARCADE_STICK;3182case 0x04: // XINPUT_DEVSUBTYPE_FLIGHT_STICK3183return SDL_JOYSTICK_TYPE_FLIGHT_STICK;3184case 0x05: // XINPUT_DEVSUBTYPE_DANCE_PAD3185return SDL_JOYSTICK_TYPE_DANCE_PAD;3186case 0x06: // XINPUT_DEVSUBTYPE_GUITAR3187case 0x07: // XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE3188case 0x0B: // XINPUT_DEVSUBTYPE_GUITAR_BASS3189return SDL_JOYSTICK_TYPE_GUITAR;3190case 0x08: // XINPUT_DEVSUBTYPE_DRUM_KIT3191return SDL_JOYSTICK_TYPE_DRUM_KIT;3192case 0x13: // XINPUT_DEVSUBTYPE_ARCADE_PAD3193return SDL_JOYSTICK_TYPE_ARCADE_PAD;3194default:3195return SDL_JOYSTICK_TYPE_UNKNOWN;3196}3197}31983199if (SDL_IsJoystickWGI(guid)) {3200return (SDL_JoystickType)guid.data[15];3201}32023203if (SDL_IsJoystickVIRTUAL(guid)) {3204return (SDL_JoystickType)guid.data[15];3205}32063207#ifdef SDL_JOYSTICK_HIDAPI3208if (SDL_IsJoystickHIDAPI(guid)) {3209return HIDAPI_GetJoystickTypeFromGUID(guid);3210}3211#endif // SDL_JOYSTICK_HIDAPI32123213if (GuessControllerType(vendor, product) != k_eControllerType_UnknownNonSteamController) {3214return SDL_JOYSTICK_TYPE_GAMEPAD;3215}32163217return SDL_JOYSTICK_TYPE_UNKNOWN;3218}32193220bool SDL_ShouldIgnoreJoystick(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)3221{3222// Check the joystick blacklist3223if (SDL_VIDPIDInList(vendor_id, product_id, &blacklist_devices)) {3224return true;3225}3226if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_ROG_CHAKRAM, false)) {3227if (SDL_VIDPIDInList(vendor_id, product_id, &rog_gamepad_mice)) {3228return true;3229}3230}32313232if (SDL_ShouldIgnoreGamepad(vendor_id, product_id, version, name)) {3233return true;3234}32353236return false;3237}32383239// return the guid for this index3240SDL_GUID SDL_GetJoystickGUIDForID(SDL_JoystickID instance_id)3241{3242SDL_JoystickDriver *driver;3243int device_index;3244SDL_GUID guid;32453246SDL_LockJoysticks();3247if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {3248guid = driver->GetDeviceGUID(device_index);3249} else {3250SDL_zero(guid);3251}3252SDL_UnlockJoysticks();32533254return guid;3255}32563257Uint16 SDL_GetJoystickVendorForID(SDL_JoystickID instance_id)3258{3259Uint16 vendor;3260const SDL_SteamVirtualGamepadInfo *info;32613262SDL_LockJoysticks();3263info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);3264if (info) {3265vendor = info->vendor_id;3266} else {3267SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);32683269SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);3270}3271SDL_UnlockJoysticks();32723273return vendor;3274}32753276Uint16 SDL_GetJoystickProductForID(SDL_JoystickID instance_id)3277{3278Uint16 product;3279const SDL_SteamVirtualGamepadInfo *info;32803281SDL_LockJoysticks();3282info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);3283if (info) {3284product = info->product_id;3285} else {3286SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);32873288SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);3289}3290SDL_UnlockJoysticks();32913292return product;3293}32943295Uint16 SDL_GetJoystickProductVersionForID(SDL_JoystickID instance_id)3296{3297Uint16 version;3298SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);32993300SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version, NULL);3301return version;3302}33033304SDL_JoystickType SDL_GetJoystickTypeForID(SDL_JoystickID instance_id)3305{3306SDL_JoystickType type;3307SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);33083309type = SDL_GetJoystickGUIDType(guid);3310if (type == SDL_JOYSTICK_TYPE_UNKNOWN) {3311if (SDL_IsGamepad(instance_id)) {3312type = SDL_JOYSTICK_TYPE_GAMEPAD;3313}3314}3315return type;3316}33173318SDL_GUID SDL_GetJoystickGUID(SDL_Joystick *joystick)3319{3320SDL_GUID result;33213322SDL_LockJoysticks();3323{3324static SDL_GUID emptyGUID;33253326CHECK_JOYSTICK_MAGIC(joystick, emptyGUID);33273328result = joystick->guid;3329}3330SDL_UnlockJoysticks();33313332return result;3333}33343335Uint16 SDL_GetJoystickVendor(SDL_Joystick *joystick)3336{3337Uint16 vendor;3338const SDL_SteamVirtualGamepadInfo *info;33393340SDL_LockJoysticks();3341{3342CHECK_JOYSTICK_MAGIC(joystick, 0);33433344info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);3345if (info) {3346vendor = info->vendor_id;3347} else {3348SDL_GUID guid = SDL_GetJoystickGUID(joystick);33493350SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);3351}3352}3353SDL_UnlockJoysticks();33543355return vendor;3356}33573358Uint16 SDL_GetJoystickProduct(SDL_Joystick *joystick)3359{3360Uint16 product;3361const SDL_SteamVirtualGamepadInfo *info;33623363SDL_LockJoysticks();3364{3365CHECK_JOYSTICK_MAGIC(joystick, 0);33663367info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);3368if (info) {3369product = info->product_id;3370} else {3371SDL_GUID guid = SDL_GetJoystickGUID(joystick);33723373SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);3374}3375}3376SDL_UnlockJoysticks();33773378return product;3379}33803381Uint16 SDL_GetJoystickProductVersion(SDL_Joystick *joystick)3382{3383Uint16 version;3384SDL_GUID guid = SDL_GetJoystickGUID(joystick);33853386SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version, NULL);3387return version;3388}33893390Uint16 SDL_GetJoystickFirmwareVersion(SDL_Joystick *joystick)3391{3392Uint16 result;33933394SDL_LockJoysticks();3395{3396CHECK_JOYSTICK_MAGIC(joystick, 0);33973398result = joystick->firmware_version;3399}3400SDL_UnlockJoysticks();34013402return result;3403}34043405const char *SDL_GetJoystickSerial(SDL_Joystick *joystick)3406{3407const char *result;34083409SDL_LockJoysticks();3410{3411CHECK_JOYSTICK_MAGIC(joystick, NULL);34123413result = SDL_GetPersistentString(joystick->serial);3414}3415SDL_UnlockJoysticks();34163417return result;3418}34193420SDL_JoystickType SDL_GetJoystickType(SDL_Joystick *joystick)3421{3422SDL_JoystickType type;3423SDL_GUID guid = SDL_GetJoystickGUID(joystick);34243425type = SDL_GetJoystickGUIDType(guid);3426if (type == SDL_JOYSTICK_TYPE_UNKNOWN) {3427SDL_LockJoysticks();3428{3429CHECK_JOYSTICK_MAGIC(joystick, SDL_JOYSTICK_TYPE_UNKNOWN);34303431if (SDL_IsGamepad(joystick->instance_id)) {3432type = SDL_JOYSTICK_TYPE_GAMEPAD;3433}3434}3435SDL_UnlockJoysticks();3436}3437return type;3438}34393440void SDL_SendJoystickPowerInfo(SDL_Joystick *joystick, SDL_PowerState state, int percent)3441{3442SDL_AssertJoysticksLocked();34433444if (state != joystick->battery_state || percent != joystick->battery_percent) {3445joystick->battery_state = state;3446joystick->battery_percent = percent;34473448if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_BATTERY_UPDATED)) {3449SDL_Event event;3450event.type = SDL_EVENT_JOYSTICK_BATTERY_UPDATED;3451event.common.timestamp = 0;3452event.jbattery.which = joystick->instance_id;3453event.jbattery.state = state;3454event.jbattery.percent = percent;3455SDL_PushEvent(&event);3456}3457}3458}34593460SDL_JoystickConnectionState SDL_GetJoystickConnectionState(SDL_Joystick *joystick)3461{3462SDL_JoystickConnectionState result;34633464SDL_LockJoysticks();3465{3466CHECK_JOYSTICK_MAGIC(joystick, SDL_JOYSTICK_CONNECTION_INVALID);34673468result = joystick->connection_state;3469}3470SDL_UnlockJoysticks();34713472return result;3473}34743475SDL_PowerState SDL_GetJoystickPowerInfo(SDL_Joystick *joystick, int *percent)3476{3477SDL_PowerState result;34783479if (percent) {3480*percent = -1;3481}34823483SDL_LockJoysticks();3484{3485CHECK_JOYSTICK_MAGIC(joystick, SDL_POWERSTATE_ERROR);34863487result = joystick->battery_state;34883489if (percent) {3490*percent = joystick->battery_percent;3491}3492}3493SDL_UnlockJoysticks();34943495return result;3496}34973498void SDL_SendJoystickTouchpad(Uint64 timestamp, SDL_Joystick *joystick, int touchpad, int finger, bool down, float x, float y, float pressure)3499{3500SDL_JoystickTouchpadInfo *touchpad_info;3501SDL_JoystickTouchpadFingerInfo *finger_info;3502Uint32 event_type;35033504SDL_AssertJoysticksLocked();35053506if (touchpad < 0 || touchpad >= joystick->ntouchpads) {3507return;3508}35093510touchpad_info = &joystick->touchpads[touchpad];3511if (finger < 0 || finger >= touchpad_info->nfingers) {3512return;3513}35143515finger_info = &touchpad_info->fingers[finger];35163517if (!down) {3518if (x == 0.0f && y == 0.0f) {3519x = finger_info->x;3520y = finger_info->y;3521}3522pressure = 0.0f;3523}35243525if (x < 0.0f) {3526x = 0.0f;3527} else if (x > 1.0f) {3528x = 1.0f;3529}3530if (y < 0.0f) {3531y = 0.0f;3532} else if (y > 1.0f) {3533y = 1.0f;3534}3535if (pressure < 0.0f) {3536pressure = 0.0f;3537} else if (pressure > 1.0f) {3538pressure = 1.0f;3539}35403541if (down == finger_info->down) {3542if (!down ||3543(x == finger_info->x && y == finger_info->y && pressure == finger_info->pressure)) {3544return;3545}3546}35473548if (down == finger_info->down) {3549event_type = SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION;3550} else if (down) {3551event_type = SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN;3552} else {3553event_type = SDL_EVENT_GAMEPAD_TOUCHPAD_UP;3554}35553556// We ignore events if we don't have keyboard focus, except for touch release3557if (SDL_PrivateJoystickShouldIgnoreEvent()) {3558if (event_type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP) {3559return;3560}3561}35623563// Update internal joystick state3564SDL_assert(timestamp != 0);3565finger_info->down = down;3566finger_info->x = x;3567finger_info->y = y;3568finger_info->pressure = pressure;3569joystick->update_complete = timestamp;35703571// Post the event, if desired3572if (SDL_EventEnabled(event_type)) {3573SDL_Event event;3574event.type = event_type;3575event.common.timestamp = timestamp;3576event.gtouchpad.which = joystick->instance_id;3577event.gtouchpad.touchpad = touchpad;3578event.gtouchpad.finger = finger;3579event.gtouchpad.x = x;3580event.gtouchpad.y = y;3581event.gtouchpad.pressure = pressure;3582SDL_PushEvent(&event);3583}3584}35853586void SDL_SendJoystickSensor(Uint64 timestamp, SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values)3587{3588SDL_AssertJoysticksLocked();35893590// We ignore events if we don't have keyboard focus3591if (SDL_PrivateJoystickShouldIgnoreEvent()) {3592return;3593}35943595for (int i = 0; i < joystick->nsensors; ++i) {3596SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];35973598if (sensor->type == type) {3599if (sensor->enabled) {3600num_values = SDL_min(num_values, SDL_arraysize(sensor->data));36013602// Update internal sensor state3603SDL_memcpy(sensor->data, data, num_values * sizeof(*data));3604joystick->update_complete = timestamp;36053606// Post the event, if desired3607if (SDL_EventEnabled(SDL_EVENT_GAMEPAD_SENSOR_UPDATE)) {3608SDL_Event event;3609event.type = SDL_EVENT_GAMEPAD_SENSOR_UPDATE;3610event.common.timestamp = timestamp;3611event.gsensor.which = joystick->instance_id;3612event.gsensor.sensor = type;3613num_values = SDL_min(num_values,3614SDL_arraysize(event.gsensor.data));3615SDL_memset(event.gsensor.data, 0,3616sizeof(event.gsensor.data));3617SDL_memcpy(event.gsensor.data, data,3618num_values * sizeof(*data));3619event.gsensor.sensor_timestamp = sensor_timestamp;3620SDL_PushEvent(&event);3621}3622}3623break;3624}3625}3626}36273628static void SDL_LoadVIDPIDListFromHint(const char *hint, int *num_entries, int *max_entries, Uint32 **entries)3629{3630Uint32 entry;3631char *spot;3632char *file = NULL;36333634if (hint && *hint == '@') {3635spot = file = (char *)SDL_LoadFile(hint + 1, NULL);3636} else {3637spot = (char *)hint;3638}36393640if (!spot) {3641return;3642}36433644while ((spot = SDL_strstr(spot, "0x")) != NULL) {3645entry = (Uint16)SDL_strtol(spot, &spot, 0);3646entry <<= 16;3647spot = SDL_strstr(spot, "0x");3648if (!spot) {3649break;3650}3651entry |= (Uint16)SDL_strtol(spot, &spot, 0);36523653if (*num_entries == *max_entries) {3654int new_max_entries = *max_entries + 16;3655Uint32 *new_entries = (Uint32 *)SDL_realloc(*entries, new_max_entries * sizeof(**entries));3656if (!new_entries) {3657// Out of memory, go with what we have already3658break;3659}3660*entries = new_entries;3661*max_entries = new_max_entries;3662}3663(*entries)[(*num_entries)++] = entry;3664}36653666if (file) {3667SDL_free(file);3668}3669}36703671void SDL_LoadVIDPIDListFromHints(SDL_vidpid_list *list, const char *included_list, const char *excluded_list)3672{3673// Empty the list3674list->num_included_entries = 0;3675list->num_excluded_entries = 0;36763677// Add the initial entries3678if (list->num_initial_entries > 0) {3679if (list->num_included_entries < list->num_initial_entries) {3680Uint32 *entries = (Uint32 *)SDL_malloc(list->num_initial_entries * sizeof(*entries));3681if (entries) {3682SDL_memcpy(entries, list->initial_entries, list->num_initial_entries * sizeof(*entries));3683list->included_entries = entries;3684list->num_included_entries = list->num_initial_entries;3685list->max_included_entries = list->num_initial_entries;3686}3687}3688}36893690// Add the included entries from the hint3691SDL_LoadVIDPIDListFromHint(included_list, &list->num_included_entries, &list->max_included_entries, &list->included_entries);36923693// Add the excluded entries from the hint3694SDL_LoadVIDPIDListFromHint(excluded_list, &list->num_excluded_entries, &list->max_excluded_entries, &list->excluded_entries);3695}36963697static void SDLCALL SDL_VIDPIDIncludedHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)3698{3699SDL_vidpid_list *list = (SDL_vidpid_list *)userdata;3700const char *included_list = hint;3701const char *excluded_list = NULL;37023703if (!list->initialized) {3704return;3705}37063707if (list->excluded_hint_name) {3708excluded_list = SDL_GetHint(list->excluded_hint_name);3709}3710SDL_LoadVIDPIDListFromHints(list, included_list, excluded_list);3711}37123713static void SDLCALL SDL_VIDPIDExcludedHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)3714{3715SDL_vidpid_list *list = (SDL_vidpid_list *)userdata;3716const char *included_list = NULL;3717const char *excluded_list = hint;37183719if (!list->initialized) {3720return;3721}37223723if (list->included_hint_name) {3724included_list = SDL_GetHint(list->included_hint_name);3725}3726SDL_LoadVIDPIDListFromHints(list, included_list, excluded_list);3727}37283729void SDL_LoadVIDPIDList(SDL_vidpid_list *list)3730{3731const char *included_list = NULL;3732const char *excluded_list = NULL;37333734if (list->included_hint_name) {3735SDL_AddHintCallback(list->included_hint_name, SDL_VIDPIDIncludedHintChanged, list);3736}37373738if (list->excluded_hint_name) {3739SDL_AddHintCallback(list->excluded_hint_name, SDL_VIDPIDExcludedHintChanged, list);3740}37413742list->initialized = true;37433744if (list->included_hint_name) {3745included_list = SDL_GetHint(list->included_hint_name);3746}3747if (list->excluded_hint_name) {3748excluded_list = SDL_GetHint(list->excluded_hint_name);3749}3750SDL_LoadVIDPIDListFromHints(list, included_list, excluded_list);3751}37523753bool SDL_VIDPIDInList(Uint16 vendor_id, Uint16 product_id, const SDL_vidpid_list *list)3754{3755int i;3756Uint32 vidpid = MAKE_VIDPID(vendor_id, product_id);37573758for (i = 0; i < list->num_excluded_entries; ++i) {3759if (vidpid == list->excluded_entries[i]) {3760return false;3761}3762}3763for (i = 0; i < list->num_included_entries; ++i) {3764if (vidpid == list->included_entries[i]) {3765return true;3766}3767}3768return false;3769}37703771void SDL_FreeVIDPIDList(SDL_vidpid_list *list)3772{3773if (list->included_hint_name) {3774SDL_RemoveHintCallback(list->included_hint_name, SDL_VIDPIDIncludedHintChanged, list);3775}37763777if (list->excluded_hint_name) {3778SDL_RemoveHintCallback(list->excluded_hint_name, SDL_VIDPIDExcludedHintChanged, list);3779}37803781if (list->included_entries) {3782SDL_free(list->included_entries);3783list->included_entries = NULL;3784list->num_included_entries = 0;3785list->max_included_entries = 0;3786}37873788if (list->excluded_entries) {3789SDL_free(list->excluded_entries);3790list->excluded_entries = NULL;3791list->num_excluded_entries = 0;3792list->max_excluded_entries = 0;3793}37943795list->initialized = false;3796}379737983799