#include "SDL_internal.h"
#include "SDL_sysjoystick.h"
#include "../SDL_hints_c.h"
#include "SDL_gamepad_c.h"
#include "SDL_joystick_c.h"
#include "SDL_steam_virtual_gamepad.h"
#include "../events/SDL_events_c.h"
#include "../video/SDL_sysvideo.h"
#include "../sensor/SDL_sensor_c.h"
#include "hidapi/SDL_hidapijoystick_c.h"
#include "controller_type.h"
#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
#include "../core/windows/SDL_windows.h"
#undef UNICODE
#include <tlhelp32.h>
#endif
#ifdef SDL_JOYSTICK_VIRTUAL
#include "./virtual/SDL_virtualjoystick_c.h"
#endif
static SDL_JoystickDriver *SDL_joystick_drivers[] = {
#ifdef SDL_JOYSTICK_HIDAPI
&SDL_HIDAPI_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_PRIVATE
&SDL_PRIVATE_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_GAMEINPUT
&SDL_GAMEINPUT_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_RAWINPUT
&SDL_RAWINPUT_JoystickDriver,
#endif
#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
&SDL_WINDOWS_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_WGI
&SDL_WGI_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_WINMM
&SDL_WINMM_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_LINUX
&SDL_LINUX_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_IOKIT
&SDL_DARWIN_JoystickDriver,
#endif
#if (defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)) && !defined(SDL_JOYSTICK_DISABLED)
&SDL_IOS_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_ANDROID
&SDL_ANDROID_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_EMSCRIPTEN
&SDL_EMSCRIPTEN_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_HAIKU
&SDL_HAIKU_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_USBHID
&SDL_BSD_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_PS2
&SDL_PS2_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_PSP
&SDL_PSP_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_VIRTUAL
&SDL_VIRTUAL_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_VITA
&SDL_VITA_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_N3DS
&SDL_N3DS_JoystickDriver,
#endif
#if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED)
&SDL_DUMMY_JoystickDriver
#endif
};
#ifndef SDL_THREAD_SAFETY_ANALYSIS
static
#endif
SDL_Mutex *SDL_joystick_lock = NULL; static SDL_AtomicInt SDL_joystick_lock_pending;
static int SDL_joysticks_locked;
static bool SDL_joysticks_initialized;
static bool SDL_joysticks_quitting;
static bool SDL_joystick_being_added;
static SDL_Joystick *SDL_joysticks SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static int SDL_joystick_player_count SDL_GUARDED_BY(SDL_joystick_lock) = 0;
static SDL_JoystickID *SDL_joystick_players SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static SDL_HashTable *SDL_joystick_names SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static bool SDL_joystick_allows_background_events = false;
static Uint32 initial_old_xboxone_controllers[] = {
MAKE_VIDPID(0x0000, 0x6686),
MAKE_VIDPID(0x0079, 0x18a1),
MAKE_VIDPID(0x0079, 0x18c2),
MAKE_VIDPID(0x0079, 0x18c8),
MAKE_VIDPID(0x0079, 0x18cf),
MAKE_VIDPID(0x03f0, 0x0495),
MAKE_VIDPID(0x045e, 0x02d1),
MAKE_VIDPID(0x045e, 0x02dd),
MAKE_VIDPID(0x045e, 0x02e0),
MAKE_VIDPID(0x045e, 0x02e3),
MAKE_VIDPID(0x045e, 0x02ea),
MAKE_VIDPID(0x045e, 0x02fd),
MAKE_VIDPID(0x045e, 0x02ff),
MAKE_VIDPID(0x045e, 0x0867),
MAKE_VIDPID(0x045e, 0x0b00),
MAKE_VIDPID(0x045e, 0x0b05),
MAKE_VIDPID(0x045e, 0x0b0a),
MAKE_VIDPID(0x045e, 0x0b0c),
MAKE_VIDPID(0x045e, 0x0b20),
MAKE_VIDPID(0x045e, 0x0b21),
MAKE_VIDPID(0x045e, 0x0b22),
MAKE_VIDPID(0x046d, 0x0000),
MAKE_VIDPID(0x046d, 0x1004),
MAKE_VIDPID(0x046d, 0x1007),
MAKE_VIDPID(0x046d, 0x1008),
MAKE_VIDPID(0x046d, 0xf301),
MAKE_VIDPID(0x0738, 0x02a0),
MAKE_VIDPID(0x0738, 0x4a01),
MAKE_VIDPID(0x0738, 0x7263),
MAKE_VIDPID(0x0738, 0xb738),
MAKE_VIDPID(0x0738, 0xcb29),
MAKE_VIDPID(0x0738, 0xf401),
MAKE_VIDPID(0x0c12, 0x0e17),
MAKE_VIDPID(0x0c12, 0x0e1c),
MAKE_VIDPID(0x0c12, 0x0e22),
MAKE_VIDPID(0x0c12, 0x0e30),
MAKE_VIDPID(0x0d62, 0x9a1a),
MAKE_VIDPID(0x0d62, 0x9a1b),
MAKE_VIDPID(0x0e00, 0x0e00),
MAKE_VIDPID(0x0e6f, 0x012a),
MAKE_VIDPID(0x0e6f, 0x0139),
MAKE_VIDPID(0x0e6f, 0x013B),
MAKE_VIDPID(0x0e6f, 0x013a),
MAKE_VIDPID(0x0e6f, 0x0145),
MAKE_VIDPID(0x0e6f, 0x0146),
MAKE_VIDPID(0x0e6f, 0x0152),
MAKE_VIDPID(0x0e6f, 0x015b),
MAKE_VIDPID(0x0e6f, 0x015c),
MAKE_VIDPID(0x0e6f, 0x015d),
MAKE_VIDPID(0x0e6f, 0x015f),
MAKE_VIDPID(0x0e6f, 0x0160),
MAKE_VIDPID(0x0e6f, 0x0161),
MAKE_VIDPID(0x0e6f, 0x0162),
MAKE_VIDPID(0x0e6f, 0x0163),
MAKE_VIDPID(0x0e6f, 0x0164),
MAKE_VIDPID(0x0e6f, 0x0165),
MAKE_VIDPID(0x0e6f, 0x0166),
MAKE_VIDPID(0x0e6f, 0x0167),
MAKE_VIDPID(0x0e6f, 0x0205),
MAKE_VIDPID(0x0e6f, 0x0206),
MAKE_VIDPID(0x0e6f, 0x0246),
MAKE_VIDPID(0x0e6f, 0x0261),
MAKE_VIDPID(0x0e6f, 0x0262),
MAKE_VIDPID(0x0e6f, 0x02a0),
MAKE_VIDPID(0x0e6f, 0x02a1),
MAKE_VIDPID(0x0e6f, 0x02a2),
MAKE_VIDPID(0x0e6f, 0x02a3),
MAKE_VIDPID(0x0e6f, 0x02a4),
MAKE_VIDPID(0x0e6f, 0x02a5),
MAKE_VIDPID(0x0e6f, 0x02a6),
MAKE_VIDPID(0x0e6f, 0x02a7),
MAKE_VIDPID(0x0e6f, 0x02a8),
MAKE_VIDPID(0x0e6f, 0x02a9),
MAKE_VIDPID(0x0e6f, 0x02aa),
MAKE_VIDPID(0x0e6f, 0x02ab),
MAKE_VIDPID(0x0e6f, 0x02ac),
MAKE_VIDPID(0x0e6f, 0x02ad),
MAKE_VIDPID(0x0e6f, 0x02ae),
MAKE_VIDPID(0x0e6f, 0x02af),
MAKE_VIDPID(0x0e6f, 0x02b0),
MAKE_VIDPID(0x0e6f, 0x02b1),
MAKE_VIDPID(0x0e6f, 0x02b2),
MAKE_VIDPID(0x0e6f, 0x02b3),
MAKE_VIDPID(0x0e6f, 0x02b5),
MAKE_VIDPID(0x0e6f, 0x02b6),
MAKE_VIDPID(0x0e6f, 0x02b8),
MAKE_VIDPID(0x0e6f, 0x02bd),
MAKE_VIDPID(0x0e6f, 0x02be),
MAKE_VIDPID(0x0e6f, 0x02bf),
MAKE_VIDPID(0x0e6f, 0x02c0),
MAKE_VIDPID(0x0e6f, 0x02c1),
MAKE_VIDPID(0x0e6f, 0x02c2),
MAKE_VIDPID(0x0e6f, 0x02c3),
MAKE_VIDPID(0x0e6f, 0x02c4),
MAKE_VIDPID(0x0e6f, 0x02c5),
MAKE_VIDPID(0x0e6f, 0x02c6),
MAKE_VIDPID(0x0e6f, 0x02c7),
MAKE_VIDPID(0x0e6f, 0x02c8),
MAKE_VIDPID(0x0e6f, 0x02c9),
MAKE_VIDPID(0x0e6f, 0x02ca),
MAKE_VIDPID(0x0e6f, 0x02cb),
MAKE_VIDPID(0x0e6f, 0x02cd),
MAKE_VIDPID(0x0e6f, 0x02ce),
MAKE_VIDPID(0x0e6f, 0x02cf),
MAKE_VIDPID(0x0e6f, 0x02d5),
MAKE_VIDPID(0x0e6f, 0x0346),
MAKE_VIDPID(0x0e6f, 0x0446),
MAKE_VIDPID(0x0e6f, 0xf501),
MAKE_VIDPID(0x0f0d, 0x0063),
MAKE_VIDPID(0x0f0d, 0x0067),
MAKE_VIDPID(0x0f0d, 0x0078),
MAKE_VIDPID(0x0f0d, 0x0097),
MAKE_VIDPID(0x0f0d, 0x00ba),
MAKE_VIDPID(0x0f0d, 0x00c0),
MAKE_VIDPID(0x0f0d, 0x00c5),
MAKE_VIDPID(0x0f0d, 0x00d8),
MAKE_VIDPID(0x0f0d, 0x00ed),
MAKE_VIDPID(0x0fff, 0x02a1),
MAKE_VIDPID(0x12ab, 0x0304),
MAKE_VIDPID(0x1430, 0x0291),
MAKE_VIDPID(0x1430, 0x02a9),
MAKE_VIDPID(0x1430, 0x070b),
MAKE_VIDPID(0x1430, 0x0719),
MAKE_VIDPID(0x146b, 0x0611),
MAKE_VIDPID(0x1532, 0x0a00),
MAKE_VIDPID(0x1532, 0x0a03),
MAKE_VIDPID(0x1532, 0x0a14),
MAKE_VIDPID(0x1532, 0x0a15),
MAKE_VIDPID(0x16d0, 0x0f3f),
MAKE_VIDPID(0x1bad, 0x028e),
MAKE_VIDPID(0x1bad, 0x02a0),
MAKE_VIDPID(0x1bad, 0x5500),
MAKE_VIDPID(0x20ab, 0x55ef),
MAKE_VIDPID(0x24c6, 0x541a),
MAKE_VIDPID(0x24c6, 0x542a),
MAKE_VIDPID(0x24c6, 0x543a),
MAKE_VIDPID(0x24c6, 0x5509),
MAKE_VIDPID(0x24c6, 0x551a),
MAKE_VIDPID(0x24c6, 0x561a),
MAKE_VIDPID(0x24c6, 0x581a),
MAKE_VIDPID(0x24c6, 0x591a),
MAKE_VIDPID(0x24c6, 0x592a),
MAKE_VIDPID(0x24c6, 0x791a),
MAKE_VIDPID(0x2516, 0x0069),
MAKE_VIDPID(0x25b1, 0x0360),
MAKE_VIDPID(0x2c22, 0x2203),
MAKE_VIDPID(0x2e24, 0x0652),
MAKE_VIDPID(0x2e24, 0x1618),
MAKE_VIDPID(0x2e24, 0x1688),
MAKE_VIDPID(0x2f24, 0x0011),
MAKE_VIDPID(0x2f24, 0x002e),
MAKE_VIDPID(0x2f24, 0x0050),
MAKE_VIDPID(0x2f24, 0x0053),
MAKE_VIDPID(0x2f24, 0x008f),
MAKE_VIDPID(0x2f24, 0x0091),
MAKE_VIDPID(0x2f24, 0x00b7),
MAKE_VIDPID(0xd2d2, 0xd2d2),
};
static SDL_vidpid_list old_xboxone_controllers = {
"SDL_JOYSTICK_OLD_XBOXONE_CONTROLLERS", 0, 0, NULL,
"SDL_JOYSTICK_OLD_XBOXONE_CONTROLLERS_EXCLUDED", 0, 0, NULL,
SDL_arraysize(initial_old_xboxone_controllers), initial_old_xboxone_controllers,
false
};
static Uint32 initial_arcadestick_devices[] = {
MAKE_VIDPID(0x0079, 0x181a), MAKE_VIDPID(0x0079, 0x181b), MAKE_VIDPID(0x0c12, 0x0ef6), MAKE_VIDPID(0x0e6f, 0x0109), MAKE_VIDPID(0x0f0d, 0x0016), MAKE_VIDPID(0x0f0d, 0x001b), MAKE_VIDPID(0x0f0d, 0x0063), MAKE_VIDPID(0x0f0d, 0x006a), MAKE_VIDPID(0x0f0d, 0x0078), MAKE_VIDPID(0x0f0d, 0x008a), MAKE_VIDPID(0x0f0d, 0x008c), MAKE_VIDPID(0x0f0d, 0x00aa), MAKE_VIDPID(0x0f0d, 0x00ed), MAKE_VIDPID(0x0f0d, 0x011c), MAKE_VIDPID(0x0f0d, 0x011e), MAKE_VIDPID(0x0f0d, 0x0184), MAKE_VIDPID(0x146b, 0x0604), MAKE_VIDPID(0x1532, 0x0a00), MAKE_VIDPID(0x1bad, 0xf03d), MAKE_VIDPID(0x1bad, 0xf502), MAKE_VIDPID(0x1bad, 0xf504), MAKE_VIDPID(0x1bad, 0xf506), MAKE_VIDPID(0x20d6, 0xa715), MAKE_VIDPID(0x24c6, 0x5000), MAKE_VIDPID(0x24c6, 0x5501), MAKE_VIDPID(0x24c6, 0x550e), MAKE_VIDPID(0x2c22, 0x2300), MAKE_VIDPID(0x2c22, 0x2302), MAKE_VIDPID(0x2c22, 0x2303), MAKE_VIDPID(0x2c22, 0x2500), MAKE_VIDPID(0x2c22, 0x2502), MAKE_VIDPID(0x2c22, 0x2503), };
static SDL_vidpid_list arcadestick_devices = {
SDL_HINT_JOYSTICK_ARCADESTICK_DEVICES, 0, 0, NULL,
SDL_HINT_JOYSTICK_ARCADESTICK_DEVICES_EXCLUDED, 0, 0, NULL,
SDL_arraysize(initial_arcadestick_devices), initial_arcadestick_devices,
false
};
static Uint32 initial_blacklist_devices[] = {
MAKE_VIDPID(0x045e, 0x009d),
MAKE_VIDPID(0x045e, 0x00b0),
MAKE_VIDPID(0x045e, 0x00b4),
MAKE_VIDPID(0x045e, 0x0730),
MAKE_VIDPID(0x045e, 0x0745),
MAKE_VIDPID(0x045e, 0x0748),
MAKE_VIDPID(0x045e, 0x0750),
MAKE_VIDPID(0x045e, 0x0768),
MAKE_VIDPID(0x045e, 0x0773),
MAKE_VIDPID(0x045e, 0x07a5),
MAKE_VIDPID(0x045e, 0x07b2),
MAKE_VIDPID(0x045e, 0x0800),
MAKE_VIDPID(0x046d, 0xc30a),
MAKE_VIDPID(0x04d9, 0xa0df),
MAKE_VIDPID(0x056a, 0x0010), MAKE_VIDPID(0x056a, 0x0011), MAKE_VIDPID(0x056a, 0x0012), MAKE_VIDPID(0x056a, 0x0013), MAKE_VIDPID(0x056a, 0x0014), MAKE_VIDPID(0x056a, 0x0015), MAKE_VIDPID(0x056a, 0x0016), MAKE_VIDPID(0x056a, 0x0017), MAKE_VIDPID(0x056a, 0x0018), MAKE_VIDPID(0x056a, 0x0019), MAKE_VIDPID(0x056a, 0x00d1), MAKE_VIDPID(0x056a, 0x030e),
MAKE_VIDPID(0x09da, 0x054f), MAKE_VIDPID(0x09da, 0x1410), MAKE_VIDPID(0x09da, 0x3043), MAKE_VIDPID(0x09da, 0x31b5), MAKE_VIDPID(0x09da, 0x3997), MAKE_VIDPID(0x09da, 0x3f8b), MAKE_VIDPID(0x09da, 0x51f4), MAKE_VIDPID(0x09da, 0x5589), MAKE_VIDPID(0x09da, 0x7b22), MAKE_VIDPID(0x09da, 0x7f2d), MAKE_VIDPID(0x09da, 0x8090), MAKE_VIDPID(0x09da, 0x9033), MAKE_VIDPID(0x09da, 0x9066), MAKE_VIDPID(0x09da, 0x9090), MAKE_VIDPID(0x09da, 0x90c0), MAKE_VIDPID(0x09da, 0xf012), MAKE_VIDPID(0x09da, 0xf32a), MAKE_VIDPID(0x09da, 0xf613), MAKE_VIDPID(0x09da, 0xf624),
MAKE_VIDPID(0x1b1c, 0x1b3c),
MAKE_VIDPID(0x1d57, 0xad03),
MAKE_VIDPID(0x1e7d, 0x2e4a),
MAKE_VIDPID(0x20a0, 0x422d),
MAKE_VIDPID(0x2516, 0x001f), MAKE_VIDPID(0x2516, 0x0028),
MAKE_VIDPID(0x04d9, 0x8008), MAKE_VIDPID(0x04d9, 0x8009), MAKE_VIDPID(0x04d9, 0xa292), MAKE_VIDPID(0x04d9, 0xa293), MAKE_VIDPID(0x04f2, 0xa13c), MAKE_VIDPID(0x0e6f, 0x018a), MAKE_VIDPID(0x1532, 0x0266), MAKE_VIDPID(0x1532, 0x0282), MAKE_VIDPID(0x26ce, 0x01a2), MAKE_VIDPID(0x20d6, 0x0002), MAKE_VIDPID(0x31e3, 0x1310), MAKE_VIDPID(0x3297, 0x1969), MAKE_VIDPID(0x3434, 0x0211), MAKE_VIDPID(0x3434, 0x0353), MAKE_VIDPID(0x3434, 0xd030), };
static SDL_vidpid_list blacklist_devices = {
SDL_HINT_JOYSTICK_BLACKLIST_DEVICES, 0, 0, NULL,
SDL_HINT_JOYSTICK_BLACKLIST_DEVICES_EXCLUDED, 0, 0, NULL,
SDL_arraysize(initial_blacklist_devices), initial_blacklist_devices,
false
};
static Uint32 initial_flightstick_devices[] = {
MAKE_VIDPID(0x044f, 0x0402), MAKE_VIDPID(0x044f, 0xb10a), MAKE_VIDPID(0x046d, 0xc215), MAKE_VIDPID(0x0583, 0x6258), MAKE_VIDPID(0x0583, 0x688f), MAKE_VIDPID(0x0583, 0x7070), MAKE_VIDPID(0x0583, 0xa019), MAKE_VIDPID(0x0583, 0xa131), MAKE_VIDPID(0x0583, 0xa209), MAKE_VIDPID(0x0583, 0xb010), MAKE_VIDPID(0x0583, 0xb012), MAKE_VIDPID(0x0583, 0xb013), MAKE_VIDPID(0x0738, 0x2221), MAKE_VIDPID(0x10f5, 0x7084), MAKE_VIDPID(0x231d, 0x0126), MAKE_VIDPID(0x231d, 0x0127), MAKE_VIDPID(0x362c, 0x0001), };
static SDL_vidpid_list flightstick_devices = {
SDL_HINT_JOYSTICK_FLIGHTSTICK_DEVICES, 0, 0, NULL,
SDL_HINT_JOYSTICK_FLIGHTSTICK_DEVICES_EXCLUDED, 0, 0, NULL,
SDL_arraysize(initial_flightstick_devices), initial_flightstick_devices,
false
};
static Uint32 initial_gamecube_devices[] = {
MAKE_VIDPID(0x0079, 0x1843), MAKE_VIDPID(0x0079, 0x1844), MAKE_VIDPID(0x0079, 0x1846), MAKE_VIDPID(0x057e, 0x0337), MAKE_VIDPID(0x057e, 0x2073), MAKE_VIDPID(0x0926, 0x8888), MAKE_VIDPID(0x0e6f, 0x0185), MAKE_VIDPID(0x1a34, 0xf705), MAKE_VIDPID(0x20d6, 0xa711), };
static SDL_vidpid_list gamecube_devices = {
SDL_HINT_JOYSTICK_GAMECUBE_DEVICES, 0, 0, NULL,
SDL_HINT_JOYSTICK_GAMECUBE_DEVICES_EXCLUDED, 0, 0, NULL,
SDL_arraysize(initial_gamecube_devices), initial_gamecube_devices,
false
};
static Uint32 initial_rog_gamepad_mice[] = {
MAKE_VIDPID(0x0b05, 0x18e3), MAKE_VIDPID(0x0b05, 0x18e5), MAKE_VIDPID(0x0b05, 0x1906), MAKE_VIDPID(0x0b05, 0x1958), MAKE_VIDPID(0x0b05, 0x1a18), MAKE_VIDPID(0x0b05, 0x1a1a), MAKE_VIDPID(0x0b05, 0x1a1c), };
static SDL_vidpid_list rog_gamepad_mice = {
SDL_HINT_ROG_GAMEPAD_MICE, 0, 0, NULL,
SDL_HINT_ROG_GAMEPAD_MICE_EXCLUDED, 0, 0, NULL,
SDL_arraysize(initial_rog_gamepad_mice), initial_rog_gamepad_mice,
false
};
static Uint32 initial_throttle_devices[] = {
MAKE_VIDPID(0x044f, 0x0404), MAKE_VIDPID(0x0738, 0xa221), MAKE_VIDPID(0x10f5, 0x7085), };
static SDL_vidpid_list throttle_devices = {
SDL_HINT_JOYSTICK_THROTTLE_DEVICES, 0, 0, NULL,
SDL_HINT_JOYSTICK_THROTTLE_DEVICES_EXCLUDED, 0, 0, NULL,
SDL_arraysize(initial_throttle_devices), initial_throttle_devices,
false
};
static Uint32 initial_wheel_devices[] = {
MAKE_VIDPID(0x0079, 0x1864), MAKE_VIDPID(0x044f, 0xb65d), MAKE_VIDPID(0x044f, 0xb65e), MAKE_VIDPID(0x044f, 0xb664), MAKE_VIDPID(0x044f, 0xb669), MAKE_VIDPID(0x044f, 0xb66d), MAKE_VIDPID(0x044f, 0xb66d), MAKE_VIDPID(0x044f, 0xb66e), MAKE_VIDPID(0x044f, 0xb66f), MAKE_VIDPID(0x044f, 0xb677), MAKE_VIDPID(0x044f, 0xb67f), MAKE_VIDPID(0x044f, 0xb691), MAKE_VIDPID(0x044f, 0xb692), MAKE_VIDPID(0x044f, 0xb696), MAKE_VIDPID(0x046d, 0xc24f), MAKE_VIDPID(0x046d, 0xc260), MAKE_VIDPID(0x046d, 0xc261), MAKE_VIDPID(0x046d, 0xc262), MAKE_VIDPID(0x046d, 0xc266), MAKE_VIDPID(0x046d, 0xc267), MAKE_VIDPID(0x046d, 0xc268), MAKE_VIDPID(0x046d, 0xc269), MAKE_VIDPID(0x046d, 0xc26d), MAKE_VIDPID(0x046d, 0xc26e), MAKE_VIDPID(0x046d, 0xc272), MAKE_VIDPID(0x046d, 0xc294), MAKE_VIDPID(0x046d, 0xc295), MAKE_VIDPID(0x046d, 0xc298), MAKE_VIDPID(0x046d, 0xc299), MAKE_VIDPID(0x046d, 0xc29a), MAKE_VIDPID(0x046d, 0xc29b), MAKE_VIDPID(0x046d, 0xca03), MAKE_VIDPID(0x0483, 0x0522), MAKE_VIDPID(0x0483, 0xa355), MAKE_VIDPID(0x0583, 0xa132), MAKE_VIDPID(0x0583, 0xa133), MAKE_VIDPID(0x0583, 0xa202), MAKE_VIDPID(0x0583, 0xb002), MAKE_VIDPID(0x0583, 0xb005), MAKE_VIDPID(0x0583, 0xb008), MAKE_VIDPID(0x0583, 0xb009), MAKE_VIDPID(0x0583, 0xb018), MAKE_VIDPID(0x0eb7, 0x0001), MAKE_VIDPID(0x0eb7, 0x0004), MAKE_VIDPID(0x0eb7, 0x0005), MAKE_VIDPID(0x0eb7, 0x0006), MAKE_VIDPID(0x0eb7, 0x0007), MAKE_VIDPID(0x0eb7, 0x0011), MAKE_VIDPID(0x0eb7, 0x0020), MAKE_VIDPID(0x0eb7, 0x0197), MAKE_VIDPID(0x0eb7, 0x038e), MAKE_VIDPID(0x0eb7, 0x0e03), MAKE_VIDPID(0x11ff, 0x0511), MAKE_VIDPID(0x1209, 0xffb0), MAKE_VIDPID(0x16d0, 0x0d5a), MAKE_VIDPID(0x16d0, 0x0d5f), MAKE_VIDPID(0x16d0, 0x0d60), MAKE_VIDPID(0x16d0, 0x0d61), MAKE_VIDPID(0x2433, 0xf300), MAKE_VIDPID(0x2433, 0xf301), MAKE_VIDPID(0x2433, 0xf303), MAKE_VIDPID(0x2433, 0xf306), MAKE_VIDPID(0x3416, 0x0301), MAKE_VIDPID(0x3416, 0x0302), MAKE_VIDPID(0x346e, 0x0000), MAKE_VIDPID(0x346e, 0x0002), MAKE_VIDPID(0x346e, 0x0004), MAKE_VIDPID(0x346e, 0x0005), MAKE_VIDPID(0x346e, 0x0006), MAKE_VIDPID(0x36e6, 0x400f), };
static SDL_vidpid_list wheel_devices = {
SDL_HINT_JOYSTICK_WHEEL_DEVICES, 0, 0, NULL,
SDL_HINT_JOYSTICK_WHEEL_DEVICES_EXCLUDED, 0, 0, NULL,
SDL_arraysize(initial_wheel_devices), initial_wheel_devices,
false
};
static Uint32 initial_zero_centered_devices[] = {
MAKE_VIDPID(0x05a0, 0x3232), MAKE_VIDPID(0x0e8f, 0x3013), };
static SDL_vidpid_list zero_centered_devices = {
SDL_HINT_JOYSTICK_ZERO_CENTERED_DEVICES, 0, 0, NULL,
NULL, 0, 0, NULL,
SDL_arraysize(initial_zero_centered_devices), initial_zero_centered_devices,
false
};
#define CHECK_JOYSTICK_MAGIC(joystick, result) \
CHECK_PARAM(!SDL_ObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK)) { \
SDL_InvalidParamError("joystick"); \
SDL_UnlockJoysticks(); \
return result; \
}
#define CHECK_JOYSTICK_VIRTUAL(joystick, result) \
CHECK_PARAM(!joystick->is_virtual) { \
SDL_SetError("joystick isn't virtual"); \
SDL_UnlockJoysticks(); \
return result; \
}
bool SDL_JoysticksInitialized(void)
{
return SDL_joysticks_initialized;
}
bool SDL_JoysticksQuitting(void)
{
return SDL_joysticks_quitting;
}
void SDL_LockJoysticks(void)
{
(void)SDL_AtomicIncRef(&SDL_joystick_lock_pending);
SDL_LockMutex(SDL_joystick_lock);
(void)SDL_AtomicDecRef(&SDL_joystick_lock_pending);
++SDL_joysticks_locked;
}
void SDL_UnlockJoysticks(void)
{
bool last_unlock = false;
--SDL_joysticks_locked;
if (!SDL_joysticks_initialized) {
if (!SDL_joysticks_locked && SDL_GetAtomicInt(&SDL_joystick_lock_pending) == 0) {
last_unlock = true;
}
}
if (last_unlock) {
SDL_Mutex *joystick_lock = SDL_joystick_lock;
SDL_LockMutex(joystick_lock);
{
SDL_UnlockMutex(SDL_joystick_lock);
SDL_joystick_lock = NULL;
}
SDL_UnlockMutex(joystick_lock);
SDL_DestroyMutex(joystick_lock);
} else {
SDL_UnlockMutex(SDL_joystick_lock);
}
}
bool SDL_JoysticksLocked(void)
{
return (SDL_joysticks_locked > 0);
}
void SDL_AssertJoysticksLocked(void)
{
SDL_assert(SDL_JoysticksLocked());
}
static bool SDL_GetDriverAndJoystickIndex(SDL_JoystickID instance_id, SDL_JoystickDriver **driver, int *driver_index)
{
int i, num_joysticks, device_index;
SDL_AssertJoysticksLocked();
if (instance_id > 0) {
for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
num_joysticks = SDL_joystick_drivers[i]->GetCount();
for (device_index = 0; device_index < num_joysticks; ++device_index) {
SDL_JoystickID joystick_id = SDL_joystick_drivers[i]->GetDeviceInstanceID(device_index);
if (joystick_id == instance_id) {
*driver = SDL_joystick_drivers[i];
*driver_index = device_index;
return true;
}
}
}
}
SDL_SetError("Joystick %" SDL_PRIu32 " not found", instance_id);
return false;
}
static int SDL_FindFreePlayerIndex(void)
{
int player_index;
SDL_AssertJoysticksLocked();
for (player_index = 0; player_index < SDL_joystick_player_count; ++player_index) {
if (SDL_joystick_players[player_index] == 0) {
break;
}
}
return player_index;
}
static int SDL_GetPlayerIndexForJoystickID(SDL_JoystickID instance_id)
{
int player_index;
SDL_AssertJoysticksLocked();
for (player_index = 0; player_index < SDL_joystick_player_count; ++player_index) {
if (instance_id == SDL_joystick_players[player_index]) {
break;
}
}
if (player_index == SDL_joystick_player_count) {
player_index = -1;
}
return player_index;
}
static SDL_JoystickID SDL_GetJoystickIDForPlayerIndex(int player_index)
{
SDL_AssertJoysticksLocked();
if (player_index < 0 || player_index >= SDL_joystick_player_count) {
return 0;
}
return SDL_joystick_players[player_index];
}
static bool SDL_SetJoystickIDForPlayerIndex(int player_index, SDL_JoystickID instance_id)
{
SDL_JoystickID existing_instance = SDL_GetJoystickIDForPlayerIndex(player_index);
SDL_JoystickDriver *driver;
int device_index;
int existing_player_index;
SDL_AssertJoysticksLocked();
if (player_index >= SDL_joystick_player_count) {
SDL_JoystickID *new_players = (SDL_JoystickID *)SDL_realloc(SDL_joystick_players, (player_index + 1) * sizeof(*SDL_joystick_players));
if (!new_players) {
return false;
}
SDL_joystick_players = new_players;
SDL_memset(&SDL_joystick_players[SDL_joystick_player_count], 0, (player_index - SDL_joystick_player_count + 1) * sizeof(SDL_joystick_players[0]));
SDL_joystick_player_count = player_index + 1;
} else if (player_index >= 0 && SDL_joystick_players[player_index] == instance_id) {
return true;
}
existing_player_index = SDL_GetPlayerIndexForJoystickID(instance_id);
if (existing_player_index >= 0) {
SDL_joystick_players[existing_player_index] = 0;
}
if (player_index >= 0) {
SDL_joystick_players[player_index] = instance_id;
}
if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
driver->SetDevicePlayerIndex(device_index, player_index);
}
if (existing_instance > 0) {
SDL_SetJoystickIDForPlayerIndex(SDL_FindFreePlayerIndex(), existing_instance);
}
return true;
}
static void SDLCALL SDL_JoystickAllowBackgroundEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
if (SDL_GetStringBoolean(hint, false)) {
SDL_joystick_allows_background_events = true;
} else {
SDL_joystick_allows_background_events = false;
}
}
bool SDL_InitJoysticks(void)
{
int i;
bool result = false;
if (SDL_joystick_lock == NULL) {
SDL_joystick_lock = SDL_CreateMutex();
}
if (!SDL_InitSubSystem(SDL_INIT_EVENTS)) {
return false;
}
SDL_LockJoysticks();
SDL_joysticks_initialized = true;
SDL_joystick_names = SDL_CreateHashTable(0, false, SDL_HashID, SDL_KeyMatchID, SDL_DestroyHashValue, NULL);
SDL_LoadVIDPIDList(&old_xboxone_controllers);
SDL_LoadVIDPIDList(&arcadestick_devices);
SDL_LoadVIDPIDList(&blacklist_devices);
SDL_LoadVIDPIDList(&flightstick_devices);
SDL_LoadVIDPIDList(&gamecube_devices);
SDL_LoadVIDPIDList(&rog_gamepad_mice);
SDL_LoadVIDPIDList(&throttle_devices);
SDL_LoadVIDPIDList(&wheel_devices);
SDL_LoadVIDPIDList(&zero_centered_devices);
SDL_InitGamepadMappings();
SDL_AddHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
SDL_JoystickAllowBackgroundEventsChanged, NULL);
SDL_InitSteamVirtualGamepadInfo();
for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
if (SDL_joystick_drivers[i]->Init()) {
result = true;
}
}
SDL_UnlockJoysticks();
if (!result) {
SDL_QuitJoysticks();
}
return result;
}
bool SDL_JoysticksOpened(void)
{
bool opened;
SDL_LockJoysticks();
{
if (SDL_joysticks != NULL) {
opened = true;
} else {
opened = false;
}
}
SDL_UnlockJoysticks();
return opened;
}
bool SDL_JoystickHandledByAnotherDriver(struct SDL_JoystickDriver *driver, Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
{
int i;
bool result = false;
SDL_LockJoysticks();
{
for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
if (driver == SDL_joystick_drivers[i]) {
break;
}
if (SDL_joystick_drivers[i]->IsDevicePresent(vendor_id, product_id, version, name)) {
result = true;
break;
}
}
}
SDL_UnlockJoysticks();
return result;
}
bool SDL_HasJoystick(void)
{
int i;
int total_joysticks = 0;
SDL_LockJoysticks();
{
for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
total_joysticks += SDL_joystick_drivers[i]->GetCount();
}
}
SDL_UnlockJoysticks();
if (total_joysticks > 0) {
return true;
}
return false;
}
SDL_JoystickID *SDL_GetJoysticks(int *count)
{
int i, num_joysticks, device_index;
int joystick_index = 0, total_joysticks = 0;
SDL_JoystickID *joysticks;
SDL_LockJoysticks();
{
for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
total_joysticks += SDL_joystick_drivers[i]->GetCount();
}
joysticks = (SDL_JoystickID *)SDL_malloc((total_joysticks + 1) * sizeof(*joysticks));
if (joysticks) {
if (count) {
*count = total_joysticks;
}
for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
num_joysticks = SDL_joystick_drivers[i]->GetCount();
for (device_index = 0; device_index < num_joysticks; ++device_index) {
SDL_assert(joystick_index < total_joysticks);
joysticks[joystick_index] = SDL_joystick_drivers[i]->GetDeviceInstanceID(device_index);
SDL_assert(joysticks[joystick_index] > 0);
++joystick_index;
}
}
SDL_assert(joystick_index == total_joysticks);
joysticks[joystick_index] = 0;
} else {
if (count) {
*count = 0;
}
}
}
SDL_UnlockJoysticks();
return joysticks;
}
const SDL_SteamVirtualGamepadInfo *SDL_GetJoystickVirtualGamepadInfoForID(SDL_JoystickID instance_id)
{
SDL_JoystickDriver *driver;
int device_index;
const SDL_SteamVirtualGamepadInfo *info = NULL;
if (SDL_SteamVirtualGamepadEnabled() &&
SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
info = SDL_GetSteamVirtualGamepadInfo(driver->GetDeviceSteamVirtualGamepadSlot(device_index));
}
return info;
}
static const char *SDL_UpdateJoystickNameForID(SDL_JoystickID instance_id)
{
SDL_JoystickDriver *driver;
int device_index;
const char *current_name = NULL;
const SDL_SteamVirtualGamepadInfo *info;
SDL_AssertJoysticksLocked();
info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);
if (info) {
current_name = info->name;
} else if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
current_name = driver->GetDeviceName(device_index);
}
if (!SDL_joystick_names) {
return SDL_GetPersistentString(current_name);
}
char *name = NULL;
bool found = SDL_FindInHashTable(SDL_joystick_names, (const void *)(uintptr_t)instance_id, (const void **)&name);
if (!current_name) {
if (!found) {
SDL_SetError("Joystick %" SDL_PRIu32 " not found", instance_id);
return NULL;
}
if (!name) {
SDL_OutOfMemory();
return NULL;
}
return name;
}
if (!name || SDL_strcmp(name, current_name) != 0) {
name = SDL_strdup(current_name);
SDL_InsertIntoHashTable(SDL_joystick_names, (const void *)(uintptr_t)instance_id, name, true);
}
return name;
}
const char *SDL_GetJoystickNameForID(SDL_JoystickID instance_id)
{
const char *name;
SDL_LockJoysticks();
name = SDL_UpdateJoystickNameForID(instance_id);
SDL_UnlockJoysticks();
return name;
}
const char *SDL_GetJoystickPathForID(SDL_JoystickID instance_id)
{
SDL_JoystickDriver *driver;
int device_index;
const char *path = NULL;
SDL_LockJoysticks();
if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
path = SDL_GetPersistentString(driver->GetDevicePath(device_index));
}
SDL_UnlockJoysticks();
if (!path) {
SDL_Unsupported();
}
return path;
}
int SDL_GetJoystickPlayerIndexForID(SDL_JoystickID instance_id)
{
int player_index;
SDL_LockJoysticks();
player_index = SDL_GetPlayerIndexForJoystickID(instance_id);
SDL_UnlockJoysticks();
return player_index;
}
static bool SDL_JoystickAxesCenteredAtZero(SDL_Joystick *joystick)
{
SDL_AssertJoysticksLocked();
if (joystick->naxes == 2) {
return true;
}
return SDL_VIDPIDInList(SDL_GetJoystickVendor(joystick), SDL_GetJoystickProduct(joystick), &zero_centered_devices);
}
static bool IsROGAlly(SDL_Joystick *joystick)
{
Uint16 vendor, product;
SDL_GUID guid = SDL_GetJoystickGUID(joystick);
SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
if (vendor == USB_VENDOR_MICROSOFT && product == USB_PRODUCT_XBOX360_WIRED_CONTROLLER) {
bool has_ally_accel = false;
bool has_ally_gyro = false;
if (SDL_InitSubSystem(SDL_INIT_SENSOR)) {
SDL_SensorID *sensors = SDL_GetSensors(NULL);
if (sensors) {
int i;
for (i = 0; sensors[i]; ++i) {
SDL_SensorID sensor = sensors[i];
if (!has_ally_accel && SDL_GetSensorTypeForID(sensor) == SDL_SENSOR_ACCEL) {
const char *sensor_name = SDL_GetSensorNameForID(sensor);
if (sensor_name && SDL_strcmp(sensor_name, "Sensor BMI320 Acc") == 0) {
has_ally_accel = true;
}
}
if (!has_ally_gyro && SDL_GetSensorTypeForID(sensor) == SDL_SENSOR_GYRO) {
const char *sensor_name = SDL_GetSensorNameForID(sensor);
if (sensor_name && SDL_strcmp(sensor_name, "Sensor BMI320 Gyr") == 0) {
has_ally_gyro = true;
}
}
}
SDL_free(sensors);
}
SDL_QuitSubSystem(SDL_INIT_SENSOR);
}
if (has_ally_accel && has_ally_gyro) {
return true;
}
}
return false;
}
static bool ShouldAttemptSensorFusion(SDL_Joystick *joystick, bool *invert_sensors)
{
SDL_AssertJoysticksLocked();
*invert_sensors = false;
if (!SDL_IsGamepad(joystick->instance_id)) {
return false;
}
if (joystick->nsensors > 0) {
return false;
}
const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION);
if (hint && *hint) {
if (*hint == '@' || SDL_strncmp(hint, "0x", 2) == 0) {
SDL_vidpid_list gamepads;
SDL_GUID guid;
Uint16 vendor, product;
bool enabled;
SDL_zero(gamepads);
guid = SDL_GetJoystickGUID(joystick);
SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
SDL_LoadVIDPIDListFromHints(&gamepads, hint, NULL);
enabled = SDL_VIDPIDInList(vendor, product, &gamepads);
SDL_FreeVIDPIDList(&gamepads);
if (enabled) {
return true;
}
} else {
return SDL_GetStringBoolean(hint, false);
}
}
if (joystick->name &&
(SDL_strstr(joystick->name, "Backbone One") ||
SDL_strstr(joystick->name, "Kishi"))) {
return true;
}
if (IsROGAlly(joystick)) {
*invert_sensors = true;
return true;
}
return false;
}
static void AttemptSensorFusion(SDL_Joystick *joystick, bool invert_sensors)
{
SDL_SensorID *sensors;
unsigned int i, j;
SDL_AssertJoysticksLocked();
if (!SDL_InitSubSystem(SDL_INIT_SENSOR)) {
return;
}
sensors = SDL_GetSensors(NULL);
if (sensors) {
for (i = 0; sensors[i]; ++i) {
SDL_SensorID sensor = sensors[i];
if (!joystick->accel_sensor && SDL_GetSensorTypeForID(sensor) == SDL_SENSOR_ACCEL) {
SDL_InitSubSystem(SDL_INIT_SENSOR);
joystick->accel_sensor = sensor;
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 0.0f);
}
if (!joystick->gyro_sensor && SDL_GetSensorTypeForID(sensor) == SDL_SENSOR_GYRO) {
SDL_InitSubSystem(SDL_INIT_SENSOR);
joystick->gyro_sensor = sensor;
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 0.0f);
}
}
SDL_free(sensors);
}
SDL_QuitSubSystem(SDL_INIT_SENSOR);
if (SDL_GetNaturalDisplayOrientation(SDL_GetPrimaryDisplay()) == SDL_ORIENTATION_LANDSCAPE) {
joystick->sensor_transform[0][0] = 1.0f;
joystick->sensor_transform[1][2] = 1.0f;
joystick->sensor_transform[2][1] = -1.0f;
} else {
joystick->sensor_transform[0][1] = -1.0f;
joystick->sensor_transform[1][2] = 1.0f;
joystick->sensor_transform[2][0] = -1.0f;
}
if (invert_sensors) {
for (i = 0; i < SDL_arraysize(joystick->sensor_transform); ++i) {
for (j = 0; j < SDL_arraysize(joystick->sensor_transform[i]); ++j) {
joystick->sensor_transform[i][j] *= -1.0f;
}
}
}
}
static void CleanupSensorFusion(SDL_Joystick *joystick)
{
SDL_AssertJoysticksLocked();
if (joystick->accel_sensor || joystick->gyro_sensor) {
if (joystick->accel_sensor) {
if (joystick->accel) {
SDL_CloseSensor(joystick->accel);
joystick->accel = NULL;
}
joystick->accel_sensor = 0;
SDL_QuitSubSystem(SDL_INIT_SENSOR);
}
if (joystick->gyro_sensor) {
if (joystick->gyro) {
SDL_CloseSensor(joystick->gyro);
joystick->gyro = NULL;
}
joystick->gyro_sensor = 0;
SDL_QuitSubSystem(SDL_INIT_SENSOR);
}
}
}
static bool ShouldSwapFaceButtons(const SDL_SteamVirtualGamepadInfo *info)
{
if (info &&
(info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO ||
info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT ||
info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT ||
info->type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR)) {
return true;
}
return false;
}
SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id)
{
SDL_JoystickDriver *driver;
int device_index;
SDL_Joystick *joystick;
SDL_Joystick *joysticklist;
const char *joystickname = NULL;
const char *joystickpath = NULL;
bool invert_sensors = false;
const SDL_SteamVirtualGamepadInfo *info;
SDL_LockJoysticks();
if (!SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
SDL_UnlockJoysticks();
return NULL;
}
joysticklist = SDL_joysticks;
while (joysticklist) {
if (instance_id == joysticklist->instance_id) {
joystick = joysticklist;
++joystick->ref_count;
SDL_UnlockJoysticks();
return joystick;
}
joysticklist = joysticklist->next;
}
joystick = (SDL_Joystick *)SDL_calloc(1, sizeof(*joystick));
if (!joystick) {
SDL_UnlockJoysticks();
return NULL;
}
SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, true);
joystick->driver = driver;
joystick->instance_id = instance_id;
joystick->attached = true;
joystick->led_expiration = SDL_GetTicks();
joystick->battery_percent = -1;
#ifdef SDL_JOYSTICK_VIRTUAL
joystick->is_virtual = (driver == &SDL_VIRTUAL_JoystickDriver);
#endif
if (!driver->Open(joystick, device_index)) {
SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, false);
SDL_free(joystick);
SDL_UnlockJoysticks();
return NULL;
}
joystickname = driver->GetDeviceName(device_index);
if (joystickname) {
joystick->name = SDL_strdup(joystickname);
}
joystickpath = driver->GetDevicePath(device_index);
if (joystickpath) {
joystick->path = SDL_strdup(joystickpath);
}
joystick->guid = driver->GetDeviceGUID(device_index);
if (joystick->naxes > 0) {
joystick->axes = (SDL_JoystickAxisInfo *)SDL_calloc(joystick->naxes, sizeof(*joystick->axes));
}
if (joystick->nballs > 0) {
joystick->balls = (SDL_JoystickBallData *)SDL_calloc(joystick->nballs, sizeof(*joystick->balls));
}
if (joystick->nhats > 0) {
joystick->hats = (Uint8 *)SDL_calloc(joystick->nhats, sizeof(*joystick->hats));
}
if (joystick->nbuttons > 0) {
joystick->buttons = (bool *)SDL_calloc(joystick->nbuttons, sizeof(*joystick->buttons));
}
if (((joystick->naxes > 0) && !joystick->axes) ||
((joystick->nballs > 0) && !joystick->balls) ||
((joystick->nhats > 0) && !joystick->hats) ||
((joystick->nbuttons > 0) && !joystick->buttons)) {
SDL_CloseJoystick(joystick);
SDL_UnlockJoysticks();
return NULL;
}
if (SDL_JoystickAxesCenteredAtZero(joystick)) {
for (int i = 0; i < joystick->naxes; ++i) {
joystick->axes[i].has_initial_value = true;
}
}
if ((SDL_IsJoystickHIDAPI(joystick->guid) ||
SDL_IsJoystickXInput(joystick->guid) ||
SDL_IsJoystickRAWINPUT(joystick->guid) ||
SDL_IsJoystickWGI(joystick->guid)) &&
joystick->naxes >= SDL_GAMEPAD_AXIS_COUNT) {
int left_trigger, right_trigger;
if (SDL_IsJoystickXInput(joystick->guid)) {
left_trigger = 2;
right_trigger = 5;
} else {
left_trigger = SDL_GAMEPAD_AXIS_LEFT_TRIGGER;
right_trigger = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER;
}
for (int i = 0; i < SDL_GAMEPAD_AXIS_COUNT; ++i) {
int initial_value;
if (i == left_trigger || i == right_trigger) {
initial_value = SDL_MIN_SINT16;
} else {
initial_value = 0;
}
joystick->axes[i].value = initial_value;
joystick->axes[i].zero = initial_value;
joystick->axes[i].initial_value = initial_value;
joystick->axes[i].has_initial_value = true;
}
}
info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);
if (info) {
joystick->steam_handle = info->handle;
joystick->swap_face_buttons = ShouldSwapFaceButtons(info);
}
if (ShouldAttemptSensorFusion(joystick, &invert_sensors)) {
AttemptSensorFusion(joystick, invert_sensors);
}
++joystick->ref_count;
joystick->next = SDL_joysticks;
SDL_joysticks = joystick;
driver->Update(joystick);
SDL_UnlockJoysticks();
return joystick;
}
SDL_JoystickID SDL_AttachVirtualJoystick(const SDL_VirtualJoystickDesc *desc)
{
#ifdef SDL_JOYSTICK_VIRTUAL
SDL_JoystickID result;
SDL_LockJoysticks();
result = SDL_JoystickAttachVirtualInner(desc);
SDL_UnlockJoysticks();
return result;
#else
SDL_SetError("SDL not built with virtual-joystick support");
return 0;
#endif
}
bool SDL_DetachVirtualJoystick(SDL_JoystickID instance_id)
{
#ifdef SDL_JOYSTICK_VIRTUAL
bool result;
SDL_LockJoysticks();
result = SDL_JoystickDetachVirtualInner(instance_id);
SDL_UnlockJoysticks();
return result;
#else
return SDL_SetError("SDL not built with virtual-joystick support");
#endif
}
bool SDL_IsJoystickVirtual(SDL_JoystickID instance_id)
{
#ifdef SDL_JOYSTICK_VIRTUAL
SDL_JoystickDriver *driver;
int device_index;
bool is_virtual = false;
SDL_LockJoysticks();
if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
if (driver == &SDL_VIRTUAL_JoystickDriver) {
is_virtual = true;
}
}
SDL_UnlockJoysticks();
return is_virtual;
#else
return false;
#endif
}
bool SDL_SetJoystickVirtualAxis(SDL_Joystick *joystick, int axis, Sint16 value)
{
bool result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
CHECK_JOYSTICK_VIRTUAL(joystick, false);
#ifdef SDL_JOYSTICK_VIRTUAL
result = SDL_SetJoystickVirtualAxisInner(joystick, axis, value);
#else
result = SDL_SetError("SDL not built with virtual-joystick support");
#endif
}
SDL_UnlockJoysticks();
return result;
}
bool SDL_SetJoystickVirtualBall(SDL_Joystick *joystick, int ball, Sint16 xrel, Sint16 yrel)
{
bool result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
CHECK_JOYSTICK_VIRTUAL(joystick, false);
#ifdef SDL_JOYSTICK_VIRTUAL
result = SDL_SetJoystickVirtualBallInner(joystick, ball, xrel, yrel);
#else
result = SDL_SetError("SDL not built with virtual-joystick support");
#endif
}
SDL_UnlockJoysticks();
return result;
}
bool SDL_SetJoystickVirtualButton(SDL_Joystick *joystick, int button, bool down)
{
bool result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
CHECK_JOYSTICK_VIRTUAL(joystick, false);
#ifdef SDL_JOYSTICK_VIRTUAL
result = SDL_SetJoystickVirtualButtonInner(joystick, button, down);
#else
result = SDL_SetError("SDL not built with virtual-joystick support");
#endif
}
SDL_UnlockJoysticks();
return result;
}
bool SDL_SetJoystickVirtualHat(SDL_Joystick *joystick, int hat, Uint8 value)
{
bool result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
CHECK_JOYSTICK_VIRTUAL(joystick, false);
#ifdef SDL_JOYSTICK_VIRTUAL
result = SDL_SetJoystickVirtualHatInner(joystick, hat, value);
#else
result = SDL_SetError("SDL not built with virtual-joystick support");
#endif
}
SDL_UnlockJoysticks();
return result;
}
bool SDL_SetJoystickVirtualTouchpad(SDL_Joystick *joystick, int touchpad, int finger, bool down, float x, float y, float pressure)
{
bool result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
CHECK_JOYSTICK_VIRTUAL(joystick, false);
#ifdef SDL_JOYSTICK_VIRTUAL
result = SDL_SetJoystickVirtualTouchpadInner(joystick, touchpad, finger, down, x, y, pressure);
#else
result = SDL_SetError("SDL not built with virtual-joystick support");
#endif
}
SDL_UnlockJoysticks();
return result;
}
bool SDL_SendJoystickVirtualSensorData(SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values)
{
bool result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
CHECK_JOYSTICK_VIRTUAL(joystick, false);
#ifdef SDL_JOYSTICK_VIRTUAL
result = SDL_SendJoystickVirtualSensorDataInner(joystick, type, sensor_timestamp, data, num_values);
#else
result = SDL_SetError("SDL not built with virtual-joystick support");
#endif
}
SDL_UnlockJoysticks();
return result;
}
bool SDL_IsJoystickValid(SDL_Joystick *joystick)
{
SDL_AssertJoysticksLocked();
return SDL_ObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK);
}
bool SDL_PrivateJoystickGetAutoGamepadMapping(SDL_JoystickID instance_id, SDL_GamepadMapping *out)
{
SDL_JoystickDriver *driver;
int device_index;
bool is_ok = false;
SDL_LockJoysticks();
if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
is_ok = driver->GetGamepadMapping(device_index, out);
}
SDL_UnlockJoysticks();
return is_ok;
}
int SDL_GetNumJoystickAxes(SDL_Joystick *joystick)
{
int result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
result = joystick->naxes;
}
SDL_UnlockJoysticks();
return result;
}
int SDL_GetNumJoystickHats(SDL_Joystick *joystick)
{
int result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
result = joystick->nhats;
}
SDL_UnlockJoysticks();
return result;
}
int SDL_GetNumJoystickBalls(SDL_Joystick *joystick)
{
int result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
result = joystick->nballs;
}
SDL_UnlockJoysticks();
return result;
}
int SDL_GetNumJoystickButtons(SDL_Joystick *joystick)
{
int result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
result = joystick->nbuttons;
}
SDL_UnlockJoysticks();
return result;
}
Sint16 SDL_GetJoystickAxis(SDL_Joystick *joystick, int axis)
{
Sint16 state;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, 0);
if (axis < joystick->naxes) {
state = joystick->axes[axis].value;
} else {
SDL_SetError("Joystick only has %d axes", joystick->naxes);
state = 0;
}
}
SDL_UnlockJoysticks();
return state;
}
bool SDL_GetJoystickAxisInitialState(SDL_Joystick *joystick, int axis, Sint16 *state)
{
bool result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
if (axis >= joystick->naxes) {
SDL_SetError("Joystick only has %d axes", joystick->naxes);
result = false;
} else {
if (state) {
*state = joystick->axes[axis].initial_value;
}
result = joystick->axes[axis].has_initial_value;
}
}
SDL_UnlockJoysticks();
return result;
}
Uint8 SDL_GetJoystickHat(SDL_Joystick *joystick, int hat)
{
Uint8 state;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, 0);
if (hat < joystick->nhats) {
state = joystick->hats[hat];
} else {
SDL_SetError("Joystick only has %d hats", joystick->nhats);
state = 0;
}
}
SDL_UnlockJoysticks();
return state;
}
bool SDL_GetJoystickBall(SDL_Joystick *joystick, int ball, int *dx, int *dy)
{
bool result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
if (ball < joystick->nballs) {
if (dx) {
*dx = joystick->balls[ball].dx;
}
if (dy) {
*dy = joystick->balls[ball].dy;
}
joystick->balls[ball].dx = 0;
joystick->balls[ball].dy = 0;
result = true;
} else {
result = SDL_SetError("Joystick only has %d balls", joystick->nballs);
}
}
SDL_UnlockJoysticks();
return result;
}
bool SDL_GetJoystickButton(SDL_Joystick *joystick, int button)
{
bool down = false;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
if (button < joystick->nbuttons) {
down = joystick->buttons[button];
} else {
SDL_SetError("Joystick only has %d buttons", joystick->nbuttons);
}
}
SDL_UnlockJoysticks();
return down;
}
bool SDL_JoystickConnected(SDL_Joystick *joystick)
{
bool result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
result = joystick->attached;
}
SDL_UnlockJoysticks();
return result;
}
SDL_JoystickID SDL_GetJoystickID(SDL_Joystick *joystick)
{
SDL_JoystickID result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, 0);
result = joystick->instance_id;
}
SDL_UnlockJoysticks();
return result;
}
SDL_Joystick *SDL_GetJoystickFromID(SDL_JoystickID instance_id)
{
SDL_Joystick *joystick;
SDL_LockJoysticks();
for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
if (joystick->instance_id == instance_id) {
break;
}
}
SDL_UnlockJoysticks();
return joystick;
}
SDL_Joystick *SDL_GetJoystickFromPlayerIndex(int player_index)
{
SDL_JoystickID instance_id;
SDL_Joystick *joystick;
SDL_LockJoysticks();
instance_id = SDL_GetJoystickIDForPlayerIndex(player_index);
for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
if (joystick->instance_id == instance_id) {
break;
}
}
SDL_UnlockJoysticks();
return joystick;
}
SDL_PropertiesID SDL_GetJoystickProperties(SDL_Joystick *joystick)
{
SDL_PropertiesID result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, 0);
if (joystick->props == 0) {
joystick->props = SDL_CreateProperties();
}
result = joystick->props;
}
SDL_UnlockJoysticks();
return result;
}
const char *SDL_GetJoystickName(SDL_Joystick *joystick)
{
const char *result;
const SDL_SteamVirtualGamepadInfo *info;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, NULL);
info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);
if (info) {
result = SDL_GetPersistentString(info->name);
} else {
result = SDL_GetPersistentString(joystick->name);
}
}
SDL_UnlockJoysticks();
return result;
}
const char *SDL_GetJoystickPath(SDL_Joystick *joystick)
{
const char *result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, NULL);
if (joystick->path) {
result = SDL_GetPersistentString(joystick->path);
} else {
SDL_Unsupported();
result = NULL;
}
}
SDL_UnlockJoysticks();
return result;
}
int SDL_GetJoystickPlayerIndex(SDL_Joystick *joystick)
{
int result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
result = SDL_GetPlayerIndexForJoystickID(joystick->instance_id);
}
SDL_UnlockJoysticks();
return result;
}
bool SDL_SetJoystickPlayerIndex(SDL_Joystick *joystick, int player_index)
{
bool result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
result = SDL_SetJoystickIDForPlayerIndex(player_index, joystick->instance_id);
}
SDL_UnlockJoysticks();
return result;
}
bool SDL_RumbleJoystick(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
{
bool result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
if (low_frequency_rumble == joystick->low_frequency_rumble &&
high_frequency_rumble == joystick->high_frequency_rumble) {
result = true;
} else {
result = joystick->driver->Rumble(joystick, low_frequency_rumble, high_frequency_rumble);
if (result) {
joystick->rumble_resend = SDL_GetTicks() + SDL_RUMBLE_RESEND_MS;
if (joystick->rumble_resend == 0) {
joystick->rumble_resend = 1;
}
} else {
joystick->rumble_resend = 0;
}
}
if (result) {
joystick->low_frequency_rumble = low_frequency_rumble;
joystick->high_frequency_rumble = high_frequency_rumble;
if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
joystick->rumble_expiration = SDL_GetTicks() + SDL_min(duration_ms, SDL_MAX_RUMBLE_DURATION_MS);
if (!joystick->rumble_expiration) {
joystick->rumble_expiration = 1;
}
} else {
joystick->rumble_expiration = 0;
joystick->rumble_resend = 0;
}
}
}
SDL_UnlockJoysticks();
return result;
}
bool SDL_RumbleJoystickTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms)
{
bool result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
if (left_rumble == joystick->left_trigger_rumble && right_rumble == joystick->right_trigger_rumble) {
result = true;
} else {
result = joystick->driver->RumbleTriggers(joystick, left_rumble, right_rumble);
if (result) {
joystick->trigger_rumble_resend = SDL_GetTicks() + SDL_RUMBLE_RESEND_MS;
if (joystick->trigger_rumble_resend == 0) {
joystick->trigger_rumble_resend = 1;
}
} else {
joystick->trigger_rumble_resend = 0;
}
}
if (result) {
joystick->left_trigger_rumble = left_rumble;
joystick->right_trigger_rumble = right_rumble;
if ((left_rumble || right_rumble) && duration_ms) {
joystick->trigger_rumble_expiration = SDL_GetTicks() + SDL_min(duration_ms, SDL_MAX_RUMBLE_DURATION_MS);
} else {
joystick->trigger_rumble_expiration = 0;
joystick->trigger_rumble_resend = 0;
}
}
}
SDL_UnlockJoysticks();
return result;
}
bool SDL_SetJoystickLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
{
bool result;
bool isfreshvalue;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
isfreshvalue = red != joystick->led_red ||
green != joystick->led_green ||
blue != joystick->led_blue;
if (isfreshvalue || SDL_GetTicks() >= joystick->led_expiration) {
result = joystick->driver->SetLED(joystick, red, green, blue);
joystick->led_expiration = SDL_GetTicks() + SDL_LED_MIN_REPEAT_MS;
} else {
result = true;
}
joystick->led_red = red;
joystick->led_green = green;
joystick->led_blue = blue;
}
SDL_UnlockJoysticks();
return result;
}
bool SDL_SendJoystickEffect(SDL_Joystick *joystick, const void *data, int size)
{
bool result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, false);
result = joystick->driver->SendEffect(joystick, data, size);
}
SDL_UnlockJoysticks();
return result;
}
void SDL_CloseJoystick(SDL_Joystick *joystick)
{
SDL_Joystick *joysticklist;
SDL_Joystick *joysticklistprev;
int i;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick,);
if (--joystick->ref_count > 0) {
SDL_UnlockJoysticks();
return;
}
SDL_DestroyProperties(joystick->props);
if (joystick->rumble_expiration) {
SDL_RumbleJoystick(joystick, 0, 0, 0);
}
if (joystick->trigger_rumble_expiration) {
SDL_RumbleJoystickTriggers(joystick, 0, 0, 0);
}
CleanupSensorFusion(joystick);
joystick->driver->Close(joystick);
joystick->hwdata = NULL;
SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, false);
joysticklist = SDL_joysticks;
joysticklistprev = NULL;
while (joysticklist) {
if (joystick == joysticklist) {
if (joysticklistprev) {
joysticklistprev->next = joysticklist->next;
} else {
SDL_joysticks = joystick->next;
}
break;
}
joysticklistprev = joysticklist;
joysticklist = joysticklist->next;
}
SDL_free(joystick->name);
SDL_free(joystick->path);
SDL_free(joystick->serial);
SDL_free(joystick->axes);
SDL_free(joystick->balls);
SDL_free(joystick->hats);
SDL_free(joystick->buttons);
for (i = 0; i < joystick->ntouchpads; i++) {
SDL_JoystickTouchpadInfo *touchpad = &joystick->touchpads[i];
SDL_free(touchpad->fingers);
}
SDL_free(joystick->touchpads);
SDL_free(joystick->sensors);
SDL_free(joystick);
}
SDL_UnlockJoysticks();
}
void SDL_QuitJoysticks(void)
{
int i;
SDL_JoystickID *joysticks;
SDL_LockJoysticks();
SDL_joysticks_quitting = true;
joysticks = SDL_GetJoysticks(NULL);
if (joysticks) {
for (i = 0; joysticks[i]; ++i) {
SDL_PrivateJoystickRemoved(joysticks[i]);
}
SDL_free(joysticks);
}
while (SDL_joysticks) {
SDL_joysticks->ref_count = 1;
SDL_CloseJoystick(SDL_joysticks);
}
for (i = SDL_arraysize(SDL_joystick_drivers) - 1; i >= 0; --i) {
SDL_joystick_drivers[i]->Quit();
}
if (SDL_joystick_players) {
SDL_free(SDL_joystick_players);
SDL_joystick_players = NULL;
SDL_joystick_player_count = 0;
}
SDL_QuitSubSystem(SDL_INIT_EVENTS);
SDL_QuitSteamVirtualGamepadInfo();
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
SDL_JoystickAllowBackgroundEventsChanged, NULL);
SDL_FreeVIDPIDList(&old_xboxone_controllers);
SDL_FreeVIDPIDList(&arcadestick_devices);
SDL_FreeVIDPIDList(&blacklist_devices);
SDL_FreeVIDPIDList(&flightstick_devices);
SDL_FreeVIDPIDList(&gamecube_devices);
SDL_FreeVIDPIDList(&rog_gamepad_mice);
SDL_FreeVIDPIDList(&throttle_devices);
SDL_FreeVIDPIDList(&wheel_devices);
SDL_FreeVIDPIDList(&zero_centered_devices);
SDL_QuitGamepadMappings();
if (SDL_joystick_names) {
SDL_DestroyHashTable(SDL_joystick_names);
SDL_joystick_names = NULL;
}
SDL_joysticks_quitting = false;
SDL_joysticks_initialized = false;
SDL_UnlockJoysticks();
}
static bool SDL_PrivateJoystickShouldIgnoreEvent(void)
{
if (SDL_joystick_allows_background_events) {
return false;
}
if (SDL_HasWindows() && SDL_GetKeyboardFocus() == NULL) {
return true;
}
return false;
}
void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers)
{
int ntouchpads;
SDL_JoystickTouchpadInfo *touchpads;
SDL_AssertJoysticksLocked();
ntouchpads = joystick->ntouchpads + 1;
touchpads = (SDL_JoystickTouchpadInfo *)SDL_realloc(joystick->touchpads, (ntouchpads * sizeof(SDL_JoystickTouchpadInfo)));
if (touchpads) {
SDL_JoystickTouchpadInfo *touchpad = &touchpads[ntouchpads - 1];
SDL_JoystickTouchpadFingerInfo *fingers = (SDL_JoystickTouchpadFingerInfo *)SDL_calloc(nfingers, sizeof(SDL_JoystickTouchpadFingerInfo));
if (fingers) {
touchpad->nfingers = nfingers;
touchpad->fingers = fingers;
} else {
touchpad->nfingers = 0;
touchpad->fingers = NULL;
}
joystick->ntouchpads = ntouchpads;
joystick->touchpads = touchpads;
}
}
void SDL_PrivateJoystickAddSensor(SDL_Joystick *joystick, SDL_SensorType type, float rate)
{
int nsensors;
SDL_JoystickSensorInfo *sensors;
SDL_AssertJoysticksLocked();
nsensors = joystick->nsensors + 1;
sensors = (SDL_JoystickSensorInfo *)SDL_realloc(joystick->sensors, (nsensors * sizeof(SDL_JoystickSensorInfo)));
if (sensors) {
SDL_JoystickSensorInfo *sensor = &sensors[nsensors - 1];
SDL_zerop(sensor);
sensor->type = type;
sensor->rate = rate;
joystick->nsensors = nsensors;
joystick->sensors = sensors;
}
}
void SDL_PrivateJoystickSensorRate(SDL_Joystick *joystick, SDL_SensorType type, float rate)
{
int i;
SDL_AssertJoysticksLocked();
for (i = 0; i < joystick->nsensors; ++i) {
if (joystick->sensors[i].type == type) {
joystick->sensors[i].rate = rate;
}
}
}
void SDL_PrivateJoystickAdded(SDL_JoystickID instance_id)
{
SDL_JoystickDriver *driver;
int device_index;
int player_index = -1;
bool is_gamepad;
SDL_AssertJoysticksLocked();
if (SDL_JoysticksQuitting()) {
return;
}
SDL_joystick_being_added = true;
if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
player_index = driver->GetDeviceSteamVirtualGamepadSlot(device_index);
if (player_index < 0) {
player_index = driver->GetDevicePlayerIndex(device_index);
}
}
if (player_index < 0 && SDL_IsGamepad(instance_id)) {
player_index = SDL_FindFreePlayerIndex();
}
if (player_index >= 0) {
SDL_SetJoystickIDForPlayerIndex(player_index, instance_id);
}
SDL_UpdateJoystickNameForID(instance_id);
{
SDL_Event event;
event.type = SDL_EVENT_JOYSTICK_ADDED;
event.common.timestamp = 0;
if (SDL_EventEnabled(event.type)) {
event.jdevice.which = instance_id;
SDL_PushEvent(&event);
}
}
is_gamepad = SDL_IsGamepad(instance_id);
SDL_joystick_being_added = false;
if (is_gamepad) {
SDL_PrivateGamepadAdded(instance_id);
}
}
bool SDL_IsJoystickBeingAdded(void)
{
return SDL_joystick_being_added;
}
void SDL_PrivateJoystickForceRecentering(SDL_Joystick *joystick)
{
int i, j;
Uint64 timestamp = SDL_GetTicksNS();
SDL_AssertJoysticksLocked();
for (i = 0; i < joystick->naxes; i++) {
if (joystick->axes[i].has_initial_value) {
SDL_SendJoystickAxis(timestamp, joystick, i, joystick->axes[i].zero);
}
}
for (i = 0; i < joystick->nbuttons; i++) {
SDL_SendJoystickButton(timestamp, joystick, i, false);
}
for (i = 0; i < joystick->nhats; i++) {
SDL_SendJoystickHat(timestamp, joystick, i, SDL_HAT_CENTERED);
}
for (i = 0; i < joystick->ntouchpads; i++) {
SDL_JoystickTouchpadInfo *touchpad = &joystick->touchpads[i];
for (j = 0; j < touchpad->nfingers; ++j) {
SDL_SendJoystickTouchpad(timestamp, joystick, i, j, false, 0.0f, 0.0f, 0.0f);
}
}
}
void SDL_PrivateJoystickRemoved(SDL_JoystickID instance_id)
{
SDL_Joystick *joystick = NULL;
int player_index;
SDL_Event event;
SDL_AssertJoysticksLocked();
for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
if (joystick->instance_id == instance_id) {
SDL_PrivateJoystickForceRecentering(joystick);
joystick->attached = false;
break;
}
}
if (SDL_IsGamepad(instance_id)) {
SDL_PrivateGamepadRemoved(instance_id);
}
event.type = SDL_EVENT_JOYSTICK_REMOVED;
event.common.timestamp = 0;
if (SDL_EventEnabled(event.type)) {
event.jdevice.which = instance_id;
SDL_PushEvent(&event);
}
player_index = SDL_GetPlayerIndexForJoystickID(instance_id);
if (player_index >= 0) {
SDL_joystick_players[player_index] = 0;
}
}
void SDL_SendJoystickAxis(Uint64 timestamp, SDL_Joystick *joystick, Uint8 axis, Sint16 value)
{
SDL_JoystickAxisInfo *info;
SDL_AssertJoysticksLocked();
if (axis >= joystick->naxes) {
return;
}
info = &joystick->axes[axis];
if (!info->has_initial_value ||
(!info->has_second_value && (info->initial_value <= -32767 || info->initial_value == 32767) && SDL_abs(value) < (SDL_JOYSTICK_AXIS_MAX / 4))) {
info->initial_value = value;
info->value = value;
info->zero = value;
info->has_initial_value = true;
} else if (value == info->value && !info->sending_initial_value) {
return;
} else {
info->has_second_value = true;
}
if (!info->sent_initial_value) {
const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; if (SDL_abs(value - info->value) <= MAX_ALLOWED_JITTER &&
!SDL_IsJoystickVIRTUAL(joystick->guid)) {
return;
}
info->sent_initial_value = true;
info->sending_initial_value = true;
SDL_SendJoystickAxis(timestamp, joystick, axis, info->initial_value);
info->sending_initial_value = false;
}
if (SDL_PrivateJoystickShouldIgnoreEvent()) {
if (info->sending_initial_value ||
(value > info->zero && value >= info->value) ||
(value < info->zero && value <= info->value)) {
return;
}
}
SDL_assert(timestamp != 0);
info->value = value;
joystick->update_complete = timestamp;
if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_AXIS_MOTION)) {
SDL_Event event;
event.type = SDL_EVENT_JOYSTICK_AXIS_MOTION;
event.common.timestamp = timestamp;
event.jaxis.which = joystick->instance_id;
event.jaxis.axis = axis;
event.jaxis.value = value;
SDL_PushEvent(&event);
}
}
void SDL_SendJoystickBall(Uint64 timestamp, SDL_Joystick *joystick, Uint8 ball, Sint16 xrel, Sint16 yrel)
{
SDL_AssertJoysticksLocked();
if (ball >= joystick->nballs) {
return;
}
if (SDL_PrivateJoystickShouldIgnoreEvent()) {
return;
}
joystick->balls[ball].dx += xrel;
joystick->balls[ball].dy += yrel;
if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_BALL_MOTION)) {
SDL_Event event;
event.type = SDL_EVENT_JOYSTICK_BALL_MOTION;
event.common.timestamp = timestamp;
event.jball.which = joystick->instance_id;
event.jball.ball = ball;
event.jball.xrel = xrel;
event.jball.yrel = yrel;
SDL_PushEvent(&event);
}
}
void SDL_SendJoystickHat(Uint64 timestamp, SDL_Joystick *joystick, Uint8 hat, Uint8 value)
{
SDL_AssertJoysticksLocked();
if (hat >= joystick->nhats) {
return;
}
if (value == joystick->hats[hat]) {
return;
}
if (SDL_PrivateJoystickShouldIgnoreEvent()) {
if (value != SDL_HAT_CENTERED) {
return;
}
}
SDL_assert(timestamp != 0);
joystick->hats[hat] = value;
joystick->update_complete = timestamp;
if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_HAT_MOTION)) {
SDL_Event event;
event.type = SDL_EVENT_JOYSTICK_HAT_MOTION;
event.common.timestamp = timestamp;
event.jhat.which = joystick->instance_id;
event.jhat.hat = hat;
event.jhat.value = value;
SDL_PushEvent(&event);
}
}
void SDL_SendJoystickButton(Uint64 timestamp, SDL_Joystick *joystick, Uint8 button, bool down)
{
SDL_Event event;
SDL_AssertJoysticksLocked();
if (down) {
event.type = SDL_EVENT_JOYSTICK_BUTTON_DOWN;
} else {
event.type = SDL_EVENT_JOYSTICK_BUTTON_UP;
}
if (joystick->swap_face_buttons) {
switch (button) {
case 0:
button = 1;
break;
case 1:
button = 0;
break;
case 2:
button = 3;
break;
case 3:
button = 2;
break;
default:
break;
}
}
if (button >= joystick->nbuttons) {
return;
}
if (down == joystick->buttons[button]) {
return;
}
if (SDL_PrivateJoystickShouldIgnoreEvent()) {
if (down) {
return;
}
}
SDL_assert(timestamp != 0);
joystick->buttons[button] = down;
joystick->update_complete = timestamp;
if (SDL_EventEnabled(event.type)) {
event.common.timestamp = timestamp;
event.jbutton.which = joystick->instance_id;
event.jbutton.button = button;
event.jbutton.down = down;
SDL_PushEvent(&event);
}
}
static void SendSteamHandleUpdateEvents(void)
{
SDL_Joystick *joystick;
const SDL_SteamVirtualGamepadInfo *info;
SDL_AssertJoysticksLocked();
for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
bool changed = false;
if (!SDL_IsGamepad(joystick->instance_id)) {
continue;
}
info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);
if (info) {
if (joystick->steam_handle != info->handle) {
joystick->steam_handle = info->handle;
joystick->swap_face_buttons = ShouldSwapFaceButtons(info);
changed = true;
}
} else {
if (joystick->steam_handle != 0) {
joystick->steam_handle = 0;
joystick->swap_face_buttons = false;
changed = true;
}
}
if (changed) {
SDL_Event event;
SDL_zero(event);
event.type = SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED;
event.common.timestamp = 0;
event.gdevice.which = joystick->instance_id;
SDL_PushEvent(&event);
}
}
}
void SDL_UpdateJoysticks(void)
{
int i;
Uint64 now;
SDL_Joystick *joystick;
if (!SDL_joysticks_initialized) {
return;
}
SDL_LockJoysticks();
if (SDL_UpdateSteamVirtualGamepadInfo()) {
SendSteamHandleUpdateEvents();
}
#ifdef SDL_JOYSTICK_HIDAPI
HIDAPI_UpdateDevices();
#endif
for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
if (!joystick->attached) {
continue;
}
joystick->driver->Update(joystick);
if (joystick->delayed_guide_button) {
SDL_GamepadHandleDelayedGuideButton(joystick);
}
now = SDL_GetTicks();
if (joystick->rumble_expiration && now >= joystick->rumble_expiration) {
SDL_RumbleJoystick(joystick, 0, 0, 0);
joystick->rumble_resend = 0;
}
if (joystick->rumble_resend && now >= joystick->rumble_resend) {
joystick->driver->Rumble(joystick, joystick->low_frequency_rumble, joystick->high_frequency_rumble);
joystick->rumble_resend = now + SDL_RUMBLE_RESEND_MS;
if (joystick->rumble_resend == 0) {
joystick->rumble_resend = 1;
}
}
if (joystick->trigger_rumble_expiration && now >= joystick->trigger_rumble_expiration) {
SDL_RumbleJoystickTriggers(joystick, 0, 0, 0);
joystick->trigger_rumble_resend = 0;
}
if (joystick->trigger_rumble_resend && now >= joystick->trigger_rumble_resend) {
joystick->driver->RumbleTriggers(joystick, joystick->left_trigger_rumble, joystick->right_trigger_rumble);
joystick->trigger_rumble_resend = now + SDL_RUMBLE_RESEND_MS;
if (joystick->trigger_rumble_resend == 0) {
joystick->trigger_rumble_resend = 1;
}
}
}
if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_UPDATE_COMPLETE)) {
for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
if (joystick->update_complete) {
SDL_Event event;
event.type = SDL_EVENT_JOYSTICK_UPDATE_COMPLETE;
event.common.timestamp = joystick->update_complete;
event.jdevice.which = joystick->instance_id;
SDL_PushEvent(&event);
joystick->update_complete = 0;
}
}
}
for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
SDL_joystick_drivers[i]->Detect();
}
SDL_UnlockJoysticks();
}
static const Uint32 SDL_joystick_event_list[] = {
SDL_EVENT_JOYSTICK_AXIS_MOTION,
SDL_EVENT_JOYSTICK_BALL_MOTION,
SDL_EVENT_JOYSTICK_HAT_MOTION,
SDL_EVENT_JOYSTICK_BUTTON_DOWN,
SDL_EVENT_JOYSTICK_BUTTON_UP,
SDL_EVENT_JOYSTICK_ADDED,
SDL_EVENT_JOYSTICK_REMOVED,
SDL_EVENT_JOYSTICK_BATTERY_UPDATED
};
void SDL_SetJoystickEventsEnabled(bool enabled)
{
unsigned int i;
for (i = 0; i < SDL_arraysize(SDL_joystick_event_list); ++i) {
SDL_SetEventEnabled(SDL_joystick_event_list[i], enabled);
}
}
bool SDL_JoystickEventsEnabled(void)
{
bool enabled = false;
unsigned int i;
for (i = 0; i < SDL_arraysize(SDL_joystick_event_list); ++i) {
enabled = SDL_EventEnabled(SDL_joystick_event_list[i]);
if (enabled) {
break;
}
}
return enabled;
}
void SDL_GetJoystickGUIDInfo(SDL_GUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version, Uint16 *crc16)
{
Uint16 *guid16 = (Uint16 *)guid.data;
Uint16 bus = SDL_Swap16LE(guid16[0]);
if ((bus < ' ' || bus == SDL_HARDWARE_BUS_VIRTUAL) && guid16[3] == 0x0000 && guid16[5] == 0x0000) {
if (vendor) {
*vendor = SDL_Swap16LE(guid16[2]);
}
if (product) {
*product = SDL_Swap16LE(guid16[4]);
}
if (version) {
*version = SDL_Swap16LE(guid16[6]);
}
if (crc16) {
*crc16 = SDL_Swap16LE(guid16[1]);
}
} else if (bus < ' ' || bus == SDL_HARDWARE_BUS_VIRTUAL) {
if (vendor) {
*vendor = 0;
}
if (product) {
*product = 0;
}
if (version) {
*version = 0;
}
if (crc16) {
*crc16 = SDL_Swap16LE(guid16[1]);
}
} else {
if (vendor) {
*vendor = 0;
}
if (product) {
*product = 0;
}
if (version) {
*version = 0;
}
if (crc16) {
*crc16 = 0;
}
}
}
char *SDL_CreateJoystickName(Uint16 vendor, Uint16 product, const char *vendor_name, const char *product_name)
{
const char *custom_name = GuessControllerName(vendor, product);
if (custom_name) {
return SDL_strdup(custom_name);
}
return SDL_CreateDeviceName(vendor, product, vendor_name, product_name, "Controller");
}
SDL_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)
{
SDL_GUID guid;
Uint16 *guid16 = (Uint16 *)guid.data;
Uint16 crc = 0;
SDL_zero(guid);
if (vendor_name && *vendor_name && product_name && *product_name) {
crc = SDL_crc16(crc, vendor_name, SDL_strlen(vendor_name));
crc = SDL_crc16(crc, " ", 1);
crc = SDL_crc16(crc, product_name, SDL_strlen(product_name));
} else if (product_name) {
crc = SDL_crc16(crc, product_name, SDL_strlen(product_name));
}
*guid16++ = SDL_Swap16LE(bus);
*guid16++ = SDL_Swap16LE(crc);
if (vendor) {
*guid16++ = SDL_Swap16LE(vendor);
*guid16++ = 0;
*guid16++ = SDL_Swap16LE(product);
*guid16++ = 0;
*guid16++ = SDL_Swap16LE(version);
guid.data[14] = driver_signature;
guid.data[15] = driver_data;
} else {
size_t available_space = sizeof(guid.data) - 4;
if (driver_signature) {
available_space -= 2;
guid.data[14] = driver_signature;
guid.data[15] = driver_data;
}
if (product_name) {
SDL_strlcpy((char *)guid16, product_name, available_space);
}
}
return guid;
}
SDL_GUID SDL_CreateJoystickGUIDForName(const char *name)
{
return SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_UNKNOWN, 0, 0, 0, NULL, name, 0, 0);
}
void SDL_SetJoystickGUIDVendor(SDL_GUID *guid, Uint16 vendor)
{
Uint16 *guid16 = (Uint16 *)guid->data;
guid16[2] = SDL_Swap16LE(vendor);
}
void SDL_SetJoystickGUIDProduct(SDL_GUID *guid, Uint16 product)
{
Uint16 *guid16 = (Uint16 *)guid->data;
guid16[4] = SDL_Swap16LE(product);
}
void SDL_SetJoystickGUIDVersion(SDL_GUID *guid, Uint16 version)
{
Uint16 *guid16 = (Uint16 *)guid->data;
guid16[6] = SDL_Swap16LE(version);
}
void SDL_SetJoystickGUIDCRC(SDL_GUID *guid, Uint16 crc)
{
Uint16 *guid16 = (Uint16 *)guid->data;
guid16[1] = SDL_Swap16LE(crc);
}
SDL_GamepadType SDL_GetGamepadTypeFromVIDPID(Uint16 vendor, Uint16 product, const char *name, bool forUI)
{
SDL_GamepadType type = SDL_GAMEPAD_TYPE_STANDARD;
if (vendor == 0x0000 && product == 0x0000) {
if (name &&
(SDL_strcmp(name, "Lic Pro Controller") == 0 ||
SDL_strcmp(name, "Nintendo Wireless Gamepad") == 0 ||
SDL_strcmp(name, "Wireless Gamepad") == 0)) {
type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
}
} else if (vendor == 0x0001 && product == 0x0001) {
type = SDL_GAMEPAD_TYPE_STANDARD;
} else if (vendor == USB_VENDOR_NINTENDO &&
(product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT ||
product == USB_PRODUCT_NINTENDO_SWITCH2_JOYCON_LEFT)) {
type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
} else if (vendor == USB_VENDOR_NINTENDO &&
(product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT ||
product == USB_PRODUCT_NINTENDO_SWITCH2_JOYCON_RIGHT)) {
if (name && SDL_strstr(name, "NES Controller") != NULL) {
type = SDL_GAMEPAD_TYPE_STANDARD;
} else {
type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
}
} else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
if (name && SDL_strstr(name, "(L)") != NULL) {
type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
} else {
type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
}
} else if (vendor == USB_VENDOR_NINTENDO &&
(product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR ||
product == USB_PRODUCT_NINTENDO_SWITCH2_JOYCON_PAIR)) {
type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR;
} else if (forUI && SDL_IsJoystickGameCube(vendor, product)) {
type = SDL_GAMEPAD_TYPE_GAMECUBE;
} else {
switch (GuessControllerType(vendor, product)) {
case k_eControllerType_XBox360Controller:
type = SDL_GAMEPAD_TYPE_XBOX360;
break;
case k_eControllerType_XBoxOneController:
type = SDL_GAMEPAD_TYPE_XBOXONE;
break;
case k_eControllerType_PS3Controller:
type = SDL_GAMEPAD_TYPE_PS3;
break;
case k_eControllerType_PS4Controller:
type = SDL_GAMEPAD_TYPE_PS4;
break;
case k_eControllerType_PS5Controller:
type = SDL_GAMEPAD_TYPE_PS5;
break;
case k_eControllerType_XInputPS4Controller:
if (forUI) {
type = SDL_GAMEPAD_TYPE_PS4;
} else {
type = SDL_GAMEPAD_TYPE_STANDARD;
}
break;
case k_eControllerType_SwitchProController:
case k_eControllerType_SwitchInputOnlyController:
type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
break;
case k_eControllerType_XInputSwitchController:
if (forUI) {
type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
} else {
type = SDL_GAMEPAD_TYPE_STANDARD;
}
break;
default:
break;
}
}
return type;
}
SDL_GamepadType SDL_GetGamepadTypeFromGUID(SDL_GUID guid, const char *name)
{
SDL_GamepadType type;
Uint16 vendor, product;
SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
type = SDL_GetGamepadTypeFromVIDPID(vendor, product, name, true);
if (type == SDL_GAMEPAD_TYPE_STANDARD) {
if (SDL_IsJoystickXInput(guid)) {
return SDL_GAMEPAD_TYPE_XBOXONE;
}
#ifdef SDL_JOYSTICK_HIDAPI
if (SDL_IsJoystickHIDAPI(guid)) {
return HIDAPI_GetGamepadTypeFromGUID(guid);
}
#endif }
return type;
}
bool SDL_JoystickGUIDUsesVersion(SDL_GUID guid)
{
Uint16 vendor, product;
if (SDL_IsJoystickMFI(guid)) {
return false;
}
SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
if (vendor && product) {
return true;
}
return false;
}
bool SDL_IsJoystickXboxOne(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_XBoxOneController;
}
bool SDL_IsJoystickXboxOneElite(Uint16 vendor_id, Uint16 product_id)
{
if (vendor_id == USB_VENDOR_MICROSOFT) {
if (product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_1 ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2 ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLE) {
return true;
}
}
return false;
}
bool SDL_IsJoystickXboxSeriesX(Uint16 vendor_id, Uint16 product_id)
{
if (SDL_VIDPIDInList(vendor_id, product_id, &old_xboxone_controllers)) {
return false;
}
return true;
}
bool SDL_IsJoystickBluetoothXboxOne(Uint16 vendor_id, Uint16 product_id)
{
if (vendor_id == USB_VENDOR_MICROSOFT) {
if (product_id == USB_PRODUCT_XBOX_ONE_ADAPTIVE_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_ADAPTIVE_BLE ||
product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLE ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLE ||
product_id == USB_PRODUCT_XBOX_SERIES_X_BLE) {
return true;
}
}
return false;
}
bool SDL_IsJoystickPS4(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_PS4Controller;
}
bool SDL_IsJoystickPS5(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_PS5Controller;
}
bool SDL_IsJoystickDualSenseEdge(Uint16 vendor_id, Uint16 product_id)
{
if (vendor_id == USB_VENDOR_SONY) {
if (product_id == USB_PRODUCT_SONY_DS5_EDGE) {
return true;
}
}
return false;
}
bool SDL_IsJoystickNintendoSwitchPro(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_SwitchProController || eType == k_eControllerType_SwitchInputOnlyController;
}
bool SDL_IsJoystickNintendoSwitchProInputOnly(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_SwitchInputOnlyController;
}
bool SDL_IsJoystickNintendoSwitchJoyCon(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_SwitchJoyConLeft || eType == k_eControllerType_SwitchJoyConRight;
}
bool SDL_IsJoystickNintendoSwitchJoyConLeft(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_SwitchJoyConLeft;
}
bool SDL_IsJoystickNintendoSwitchJoyConRight(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_SwitchJoyConRight;
}
bool SDL_IsJoystickNintendoSwitchJoyConGrip(Uint16 vendor_id, Uint16 product_id)
{
return vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP;
}
bool SDL_IsJoystickNintendoSwitchJoyConPair(Uint16 vendor_id, Uint16 product_id)
{
return vendor_id == USB_VENDOR_NINTENDO &&
(product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR ||
product_id == USB_PRODUCT_NINTENDO_SWITCH2_JOYCON_PAIR);
}
bool SDL_IsJoystickGameCube(Uint16 vendor_id, Uint16 product_id)
{
return SDL_VIDPIDInList(vendor_id, product_id, &gamecube_devices);
}
bool SDL_IsJoystickAmazonLunaController(Uint16 vendor_id, Uint16 product_id)
{
return ((vendor_id == USB_VENDOR_AMAZON && product_id == USB_PRODUCT_AMAZON_LUNA_CONTROLLER) ||
(vendor_id == BLUETOOTH_VENDOR_AMAZON && product_id == BLUETOOTH_PRODUCT_LUNA_CONTROLLER));
}
bool SDL_IsJoystickGoogleStadiaController(Uint16 vendor_id, Uint16 product_id)
{
return vendor_id == USB_VENDOR_GOOGLE && product_id == USB_PRODUCT_GOOGLE_STADIA_CONTROLLER;
}
bool SDL_IsJoystickNVIDIASHIELDController(Uint16 vendor_id, Uint16 product_id)
{
return (vendor_id == USB_VENDOR_NVIDIA &&
(product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103 ||
product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V104));
}
bool SDL_IsJoystickSteamVirtualGamepad(Uint16 vendor_id, Uint16 product_id, Uint16 version)
{
#ifdef SDL_PLATFORM_MACOS
return (vendor_id == USB_VENDOR_MICROSOFT && product_id == USB_PRODUCT_XBOX360_WIRED_CONTROLLER && version == 0);
#else
return (vendor_id == USB_VENDOR_VALVE && product_id == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD);
#endif
}
bool SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_SteamController || eType == k_eControllerType_SteamControllerV2;
}
bool SDL_IsJoystickHoriSteamController(Uint16 vendor_id, Uint16 product_id)
{
return vendor_id == USB_VENDOR_HORI && (product_id == USB_PRODUCT_HORI_STEAM_CONTROLLER || product_id == USB_PRODUCT_HORI_STEAM_CONTROLLER_BT);
}
bool SDL_IsJoystickSInputController(Uint16 vendor_id, Uint16 product_id)
{
if (vendor_id == USB_VENDOR_RASPBERRYPI) {
if (product_id == USB_PRODUCT_HANDHELDLEGEND_SINPUT_GENERIC ||
product_id == USB_PRODUCT_HANDHELDLEGEND_PROGCC ||
product_id == USB_PRODUCT_HANDHELDLEGEND_GCULTIMATE ||
product_id == USB_PRODUCT_BONZIRICHANNEL_FIREBIRD ||
product_id == USB_PRODUCT_VOIDGAMING_PS4FIREBIRD) {
return true;
}
}
return false;
}
bool SDL_IsJoystickFlydigiController(Uint16 vendor_id, Uint16 product_id)
{
if (vendor_id == USB_VENDOR_FLYDIGI_V1) {
if (product_id == USB_PRODUCT_FLYDIGI_V1_GAMEPAD) {
return true;
}
}
if (vendor_id == USB_VENDOR_FLYDIGI_V2) {
if (product_id == USB_PRODUCT_FLYDIGI_V2_APEX || product_id == USB_PRODUCT_FLYDIGI_V2_VADER) {
return true;
}
}
return false;
}
bool SDL_IsJoystickSteamDeck(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_SteamControllerNeptune;
}
bool SDL_IsJoystickSteamTriton(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return eType == k_eControllerType_SteamControllerTriton;
}
bool SDL_IsJoystickXInput(SDL_GUID guid)
{
return (guid.data[14] == 'x') ? true : false;
}
bool SDL_IsJoystickWGI(SDL_GUID guid)
{
return (guid.data[14] == 'w') ? true : false;
}
bool SDL_IsJoystickGameInput(SDL_GUID guid)
{
return (guid.data[14] == 'g') ? true : false;
}
bool SDL_IsJoystickHIDAPI(SDL_GUID guid)
{
return (guid.data[14] == 'h') ? true : false;
}
bool SDL_IsJoystickMFI(SDL_GUID guid)
{
return (guid.data[14] == 'm') ? true : false;
}
bool SDL_IsJoystickRAWINPUT(SDL_GUID guid)
{
return (guid.data[14] == 'r') ? true : false;
}
bool SDL_IsJoystickVIRTUAL(SDL_GUID guid)
{
return (guid.data[14] == 'v') ? true : false;
}
bool SDL_IsJoystickWheel(Uint16 vendor_id, Uint16 product_id)
{
return SDL_VIDPIDInList(vendor_id, product_id, &wheel_devices);
}
static bool SDL_IsJoystickArcadeStick(Uint16 vendor_id, Uint16 product_id)
{
return SDL_VIDPIDInList(vendor_id, product_id, &arcadestick_devices);
}
static bool SDL_IsJoystickFlightStick(Uint16 vendor_id, Uint16 product_id)
{
return SDL_VIDPIDInList(vendor_id, product_id, &flightstick_devices);
}
static bool SDL_IsJoystickThrottle(Uint16 vendor_id, Uint16 product_id)
{
return SDL_VIDPIDInList(vendor_id, product_id, &throttle_devices);
}
static SDL_JoystickType SDL_GetJoystickGUIDType(SDL_GUID guid)
{
Uint16 vendor;
Uint16 product;
SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
if (SDL_IsJoystickWheel(vendor, product)) {
return SDL_JOYSTICK_TYPE_WHEEL;
}
if (SDL_IsJoystickArcadeStick(vendor, product)) {
return SDL_JOYSTICK_TYPE_ARCADE_STICK;
}
if (SDL_IsJoystickFlightStick(vendor, product)) {
return SDL_JOYSTICK_TYPE_FLIGHT_STICK;
}
if (SDL_IsJoystickThrottle(vendor, product)) {
return SDL_JOYSTICK_TYPE_THROTTLE;
}
if (SDL_IsJoystickXInput(guid)) {
switch (guid.data[15]) {
case 0x01: return SDL_JOYSTICK_TYPE_GAMEPAD;
case 0x02: return SDL_JOYSTICK_TYPE_WHEEL;
case 0x03: return SDL_JOYSTICK_TYPE_ARCADE_STICK;
case 0x04: return SDL_JOYSTICK_TYPE_FLIGHT_STICK;
case 0x05: return SDL_JOYSTICK_TYPE_DANCE_PAD;
case 0x06: case 0x07: case 0x0B: return SDL_JOYSTICK_TYPE_GUITAR;
case 0x08: return SDL_JOYSTICK_TYPE_DRUM_KIT;
case 0x13: return SDL_JOYSTICK_TYPE_ARCADE_PAD;
default:
return SDL_JOYSTICK_TYPE_UNKNOWN;
}
}
if (SDL_IsJoystickWGI(guid)) {
return (SDL_JoystickType)guid.data[15];
}
if (SDL_IsJoystickGameInput(guid)) {
return (SDL_JoystickType)guid.data[15];
}
if (SDL_IsJoystickVIRTUAL(guid)) {
return (SDL_JoystickType)guid.data[15];
}
#ifdef SDL_JOYSTICK_HIDAPI
if (SDL_IsJoystickHIDAPI(guid)) {
return HIDAPI_GetJoystickTypeFromGUID(guid);
}
#endif
if (GuessControllerType(vendor, product) != k_eControllerType_UnknownNonSteamController) {
return SDL_JOYSTICK_TYPE_GAMEPAD;
}
return SDL_JOYSTICK_TYPE_UNKNOWN;
}
bool SDL_ShouldIgnoreJoystick(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
{
if (SDL_VIDPIDInList(vendor_id, product_id, &blacklist_devices)) {
return true;
}
if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_ROG_CHAKRAM, false)) {
if (SDL_VIDPIDInList(vendor_id, product_id, &rog_gamepad_mice)) {
return true;
}
}
if (SDL_ShouldIgnoreGamepad(vendor_id, product_id, version, name)) {
return true;
}
return false;
}
SDL_GUID SDL_GetJoystickGUIDForID(SDL_JoystickID instance_id)
{
SDL_JoystickDriver *driver;
int device_index;
SDL_GUID guid;
SDL_LockJoysticks();
if (SDL_GetDriverAndJoystickIndex(instance_id, &driver, &device_index)) {
guid = driver->GetDeviceGUID(device_index);
} else {
SDL_zero(guid);
}
SDL_UnlockJoysticks();
return guid;
}
Uint16 SDL_GetJoystickVendorForID(SDL_JoystickID instance_id)
{
Uint16 vendor;
const SDL_SteamVirtualGamepadInfo *info;
SDL_LockJoysticks();
info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);
if (info) {
vendor = info->vendor_id;
} else {
SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
}
SDL_UnlockJoysticks();
return vendor;
}
Uint16 SDL_GetJoystickProductForID(SDL_JoystickID instance_id)
{
Uint16 product;
const SDL_SteamVirtualGamepadInfo *info;
SDL_LockJoysticks();
info = SDL_GetJoystickVirtualGamepadInfoForID(instance_id);
if (info) {
product = info->product_id;
} else {
SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
}
SDL_UnlockJoysticks();
return product;
}
Uint16 SDL_GetJoystickProductVersionForID(SDL_JoystickID instance_id)
{
Uint16 version;
SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version, NULL);
return version;
}
SDL_JoystickType SDL_GetJoystickTypeForID(SDL_JoystickID instance_id)
{
SDL_JoystickType type;
SDL_GUID guid = SDL_GetJoystickGUIDForID(instance_id);
type = SDL_GetJoystickGUIDType(guid);
if (type == SDL_JOYSTICK_TYPE_UNKNOWN) {
if (SDL_IsGamepad(instance_id)) {
type = SDL_JOYSTICK_TYPE_GAMEPAD;
}
}
return type;
}
SDL_GUID SDL_GetJoystickGUID(SDL_Joystick *joystick)
{
SDL_GUID result;
SDL_LockJoysticks();
{
static SDL_GUID emptyGUID;
CHECK_JOYSTICK_MAGIC(joystick, emptyGUID);
result = joystick->guid;
}
SDL_UnlockJoysticks();
return result;
}
Uint16 SDL_GetJoystickVendor(SDL_Joystick *joystick)
{
Uint16 vendor;
const SDL_SteamVirtualGamepadInfo *info;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, 0);
info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);
if (info) {
vendor = info->vendor_id;
} else {
SDL_GUID guid = SDL_GetJoystickGUID(joystick);
SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
}
}
SDL_UnlockJoysticks();
return vendor;
}
Uint16 SDL_GetJoystickProduct(SDL_Joystick *joystick)
{
Uint16 product;
const SDL_SteamVirtualGamepadInfo *info;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, 0);
info = SDL_GetJoystickVirtualGamepadInfoForID(joystick->instance_id);
if (info) {
product = info->product_id;
} else {
SDL_GUID guid = SDL_GetJoystickGUID(joystick);
SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
}
}
SDL_UnlockJoysticks();
return product;
}
Uint16 SDL_GetJoystickProductVersion(SDL_Joystick *joystick)
{
Uint16 version;
SDL_GUID guid = SDL_GetJoystickGUID(joystick);
SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version, NULL);
return version;
}
Uint16 SDL_GetJoystickFirmwareVersion(SDL_Joystick *joystick)
{
Uint16 result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, 0);
result = joystick->firmware_version;
}
SDL_UnlockJoysticks();
return result;
}
const char *SDL_GetJoystickSerial(SDL_Joystick *joystick)
{
const char *result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, NULL);
result = SDL_GetPersistentString(joystick->serial);
}
SDL_UnlockJoysticks();
return result;
}
SDL_JoystickType SDL_GetJoystickType(SDL_Joystick *joystick)
{
SDL_JoystickType type;
SDL_GUID guid = SDL_GetJoystickGUID(joystick);
type = SDL_GetJoystickGUIDType(guid);
if (type == SDL_JOYSTICK_TYPE_UNKNOWN) {
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, SDL_JOYSTICK_TYPE_UNKNOWN);
if (SDL_IsGamepad(joystick->instance_id)) {
type = SDL_JOYSTICK_TYPE_GAMEPAD;
}
}
SDL_UnlockJoysticks();
}
return type;
}
void SDL_SendJoystickPowerInfo(SDL_Joystick *joystick, SDL_PowerState state, int percent)
{
SDL_AssertJoysticksLocked();
if (state != joystick->battery_state || percent != joystick->battery_percent) {
joystick->battery_state = state;
joystick->battery_percent = percent;
if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_BATTERY_UPDATED)) {
SDL_Event event;
event.type = SDL_EVENT_JOYSTICK_BATTERY_UPDATED;
event.common.timestamp = 0;
event.jbattery.which = joystick->instance_id;
event.jbattery.state = state;
event.jbattery.percent = percent;
SDL_PushEvent(&event);
}
}
}
SDL_JoystickConnectionState SDL_GetJoystickConnectionState(SDL_Joystick *joystick)
{
SDL_JoystickConnectionState result;
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, SDL_JOYSTICK_CONNECTION_INVALID);
result = joystick->connection_state;
}
SDL_UnlockJoysticks();
return result;
}
SDL_PowerState SDL_GetJoystickPowerInfo(SDL_Joystick *joystick, int *percent)
{
SDL_PowerState result;
if (percent) {
*percent = -1;
}
SDL_LockJoysticks();
{
CHECK_JOYSTICK_MAGIC(joystick, SDL_POWERSTATE_ERROR);
result = joystick->battery_state;
if (percent) {
*percent = joystick->battery_percent;
}
}
SDL_UnlockJoysticks();
return result;
}
void SDL_SendJoystickTouchpad(Uint64 timestamp, SDL_Joystick *joystick, int touchpad, int finger, bool down, float x, float y, float pressure)
{
SDL_JoystickTouchpadInfo *touchpad_info;
SDL_JoystickTouchpadFingerInfo *finger_info;
Uint32 event_type;
SDL_AssertJoysticksLocked();
if (touchpad < 0 || touchpad >= joystick->ntouchpads) {
return;
}
touchpad_info = &joystick->touchpads[touchpad];
if (finger < 0 || finger >= touchpad_info->nfingers) {
return;
}
finger_info = &touchpad_info->fingers[finger];
if (!down) {
if (x == 0.0f && y == 0.0f) {
x = finger_info->x;
y = finger_info->y;
}
pressure = 0.0f;
}
if (x < 0.0f) {
x = 0.0f;
} else if (x > 1.0f) {
x = 1.0f;
}
if (y < 0.0f) {
y = 0.0f;
} else if (y > 1.0f) {
y = 1.0f;
}
if (pressure < 0.0f) {
pressure = 0.0f;
} else if (pressure > 1.0f) {
pressure = 1.0f;
}
if (down == finger_info->down) {
if (!down ||
(x == finger_info->x && y == finger_info->y && pressure == finger_info->pressure)) {
return;
}
}
if (down == finger_info->down) {
event_type = SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION;
} else if (down) {
event_type = SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN;
} else {
event_type = SDL_EVENT_GAMEPAD_TOUCHPAD_UP;
}
if (SDL_PrivateJoystickShouldIgnoreEvent()) {
if (event_type != SDL_EVENT_GAMEPAD_TOUCHPAD_UP) {
return;
}
}
SDL_assert(timestamp != 0);
finger_info->down = down;
finger_info->x = x;
finger_info->y = y;
finger_info->pressure = pressure;
joystick->update_complete = timestamp;
if (SDL_EventEnabled(event_type)) {
SDL_Event event;
event.type = event_type;
event.common.timestamp = timestamp;
event.gtouchpad.which = joystick->instance_id;
event.gtouchpad.touchpad = touchpad;
event.gtouchpad.finger = finger;
event.gtouchpad.x = x;
event.gtouchpad.y = y;
event.gtouchpad.pressure = pressure;
SDL_PushEvent(&event);
}
}
void SDL_SendJoystickSensor(Uint64 timestamp, SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values)
{
SDL_AssertJoysticksLocked();
if (SDL_PrivateJoystickShouldIgnoreEvent()) {
return;
}
for (int i = 0; i < joystick->nsensors; ++i) {
SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
if (sensor->type == type) {
if (sensor->enabled) {
num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
SDL_memcpy(sensor->data, data, num_values * sizeof(*data));
joystick->update_complete = timestamp;
if (SDL_EventEnabled(SDL_EVENT_GAMEPAD_SENSOR_UPDATE)) {
SDL_Event event;
event.type = SDL_EVENT_GAMEPAD_SENSOR_UPDATE;
event.common.timestamp = timestamp;
event.gsensor.which = joystick->instance_id;
event.gsensor.sensor = type;
num_values = SDL_min(num_values,
SDL_arraysize(event.gsensor.data));
SDL_memset(event.gsensor.data, 0,
sizeof(event.gsensor.data));
SDL_memcpy(event.gsensor.data, data,
num_values * sizeof(*data));
event.gsensor.sensor_timestamp = sensor_timestamp;
SDL_PushEvent(&event);
}
}
break;
}
}
}
static void SDL_LoadVIDPIDListFromHint(const char *hint, int *num_entries, int *max_entries, Uint32 **entries)
{
Uint32 entry;
char *spot;
char *file = NULL;
if (hint && *hint == '@') {
spot = file = (char *)SDL_LoadFile(hint + 1, NULL);
} else {
spot = (char *)hint;
}
if (!spot) {
return;
}
while ((spot = SDL_strstr(spot, "0x")) != NULL) {
entry = (Uint16)SDL_strtol(spot, &spot, 0);
entry <<= 16;
spot = SDL_strstr(spot, "0x");
if (!spot) {
break;
}
entry |= (Uint16)SDL_strtol(spot, &spot, 0);
if (*num_entries == *max_entries) {
int new_max_entries = *max_entries + 16;
Uint32 *new_entries = (Uint32 *)SDL_realloc(*entries, new_max_entries * sizeof(**entries));
if (!new_entries) {
break;
}
*entries = new_entries;
*max_entries = new_max_entries;
}
(*entries)[(*num_entries)++] = entry;
}
SDL_free(file);
}
void SDL_LoadVIDPIDListFromHints(SDL_vidpid_list *list, const char *included_list, const char *excluded_list)
{
list->num_included_entries = 0;
list->num_excluded_entries = 0;
if (list->num_initial_entries > 0) {
if (list->num_included_entries < list->num_initial_entries) {
Uint32 *entries = (Uint32 *)SDL_malloc(list->num_initial_entries * sizeof(*entries));
if (entries) {
SDL_memcpy(entries, list->initial_entries, list->num_initial_entries * sizeof(*entries));
list->included_entries = entries;
list->num_included_entries = list->num_initial_entries;
list->max_included_entries = list->num_initial_entries;
}
}
}
SDL_LoadVIDPIDListFromHint(included_list, &list->num_included_entries, &list->max_included_entries, &list->included_entries);
SDL_LoadVIDPIDListFromHint(excluded_list, &list->num_excluded_entries, &list->max_excluded_entries, &list->excluded_entries);
}
static void SDLCALL SDL_VIDPIDIncludedHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_vidpid_list *list = (SDL_vidpid_list *)userdata;
const char *included_list = hint;
const char *excluded_list = NULL;
if (!list->initialized) {
return;
}
if (list->excluded_hint_name) {
excluded_list = SDL_GetHint(list->excluded_hint_name);
}
SDL_LoadVIDPIDListFromHints(list, included_list, excluded_list);
}
static void SDLCALL SDL_VIDPIDExcludedHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_vidpid_list *list = (SDL_vidpid_list *)userdata;
const char *included_list = NULL;
const char *excluded_list = hint;
if (!list->initialized) {
return;
}
if (list->included_hint_name) {
included_list = SDL_GetHint(list->included_hint_name);
}
SDL_LoadVIDPIDListFromHints(list, included_list, excluded_list);
}
void SDL_LoadVIDPIDList(SDL_vidpid_list *list)
{
const char *included_list = NULL;
const char *excluded_list = NULL;
if (list->included_hint_name) {
SDL_AddHintCallback(list->included_hint_name, SDL_VIDPIDIncludedHintChanged, list);
}
if (list->excluded_hint_name) {
SDL_AddHintCallback(list->excluded_hint_name, SDL_VIDPIDExcludedHintChanged, list);
}
list->initialized = true;
if (list->included_hint_name) {
included_list = SDL_GetHint(list->included_hint_name);
if (!included_list) {
included_list = SDL_getenv_unsafe(list->included_hint_name);
}
}
if (list->excluded_hint_name) {
excluded_list = SDL_GetHint(list->excluded_hint_name);
if (!excluded_list) {
excluded_list = SDL_getenv_unsafe(list->excluded_hint_name);
}
}
SDL_LoadVIDPIDListFromHints(list, included_list, excluded_list);
}
bool SDL_VIDPIDInList(Uint16 vendor_id, Uint16 product_id, const SDL_vidpid_list *list)
{
int i;
Uint32 vidpid = MAKE_VIDPID(vendor_id, product_id);
for (i = 0; i < list->num_excluded_entries; ++i) {
if (vidpid == list->excluded_entries[i]) {
return false;
}
}
for (i = 0; i < list->num_included_entries; ++i) {
if (vidpid == list->included_entries[i]) {
return true;
}
}
return false;
}
void SDL_FreeVIDPIDList(SDL_vidpid_list *list)
{
if (list->included_hint_name) {
SDL_RemoveHintCallback(list->included_hint_name, SDL_VIDPIDIncludedHintChanged, list);
}
if (list->excluded_hint_name) {
SDL_RemoveHintCallback(list->excluded_hint_name, SDL_VIDPIDExcludedHintChanged, list);
}
if (list->included_entries) {
SDL_free(list->included_entries);
list->included_entries = NULL;
list->num_included_entries = 0;
list->max_included_entries = 0;
}
if (list->excluded_entries) {
SDL_free(list->excluded_entries);
list->excluded_entries = NULL;
list->num_excluded_entries = 0;
list->max_excluded_entries = 0;
}
list->initialized = false;
}