Path: blob/master/thirdparty/sdl/joystick/hidapi/SDL_hidapijoystick.c
9906 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#ifdef SDL_JOYSTICK_HIDAPI2324#include "../SDL_sysjoystick.h"25#include "SDL_hidapijoystick_c.h"26#include "SDL_hidapi_rumble.h"27#include "../../SDL_hints_c.h"2829#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)30#include "../windows/SDL_rawinputjoystick_c.h"31#endif323334struct joystick_hwdata35{36SDL_HIDAPI_Device *device;37};3839static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {40#ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE41&SDL_HIDAPI_DriverGameCube,42#endif43#ifdef SDL_JOYSTICK_HIDAPI_LUNA44&SDL_HIDAPI_DriverLuna,45#endif46#ifdef SDL_JOYSTICK_HIDAPI_SHIELD47&SDL_HIDAPI_DriverShield,48#endif49#ifdef SDL_JOYSTICK_HIDAPI_PS350&SDL_HIDAPI_DriverPS3,51&SDL_HIDAPI_DriverPS3ThirdParty,52&SDL_HIDAPI_DriverPS3SonySixaxis,53#endif54#ifdef SDL_JOYSTICK_HIDAPI_PS455&SDL_HIDAPI_DriverPS4,56#endif57#ifdef SDL_JOYSTICK_HIDAPI_PS558&SDL_HIDAPI_DriverPS5,59#endif60#ifdef SDL_JOYSTICK_HIDAPI_STADIA61&SDL_HIDAPI_DriverStadia,62#endif63#ifdef SDL_JOYSTICK_HIDAPI_STEAM64&SDL_HIDAPI_DriverSteam,65#endif66#ifdef SDL_JOYSTICK_HIDAPI_STEAM_HORI67&SDL_HIDAPI_DriverSteamHori,68#endif69#ifdef SDL_JOYSTICK_HIDAPI_STEAMDECK70&SDL_HIDAPI_DriverSteamDeck,71#endif72#ifdef SDL_JOYSTICK_HIDAPI_SWITCH73&SDL_HIDAPI_DriverNintendoClassic,74&SDL_HIDAPI_DriverJoyCons,75&SDL_HIDAPI_DriverSwitch,76#endif77#ifdef SDL_JOYSTICK_HIDAPI_WII78&SDL_HIDAPI_DriverWii,79#endif80#ifdef SDL_JOYSTICK_HIDAPI_XBOX36081&SDL_HIDAPI_DriverXbox360,82&SDL_HIDAPI_DriverXbox360W,83#endif84#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE85&SDL_HIDAPI_DriverXboxOne,86#endif87};88static int SDL_HIDAPI_numdrivers = 0;89static SDL_AtomicInt SDL_HIDAPI_updating_devices;90static bool SDL_HIDAPI_hints_changed = false;91static Uint32 SDL_HIDAPI_change_count = 0;92static SDL_HIDAPI_Device *SDL_HIDAPI_devices SDL_GUARDED_BY(SDL_joystick_lock);93static int SDL_HIDAPI_numjoysticks = 0;94static bool SDL_HIDAPI_combine_joycons = true;95static bool initialized = false;96static bool shutting_down = false;9798static char *HIDAPI_ConvertString(const wchar_t *wide_string)99{100char *string = NULL;101102if (wide_string) {103string = SDL_iconv_string("UTF-8", "WCHAR_T", (char *)wide_string, (SDL_wcslen(wide_string) + 1) * sizeof(wchar_t));104if (!string) {105switch (sizeof(wchar_t)) {106case 2:107string = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char *)wide_string, (SDL_wcslen(wide_string) + 1) * sizeof(wchar_t));108break;109case 4:110string = SDL_iconv_string("UTF-8", "UCS-4-INTERNAL", (char *)wide_string, (SDL_wcslen(wide_string) + 1) * sizeof(wchar_t));111break;112}113}114}115return string;116}117118void HIDAPI_DumpPacket(const char *prefix, const Uint8 *data, int size)119{120int i;121char *buffer;122size_t length = SDL_strlen(prefix) + 11 * (size / 8) + (5 * size * 2) + 1 + 1;123int start = 0, amount = size;124size_t current_len;125126buffer = (char *)SDL_malloc(length);127current_len = SDL_snprintf(buffer, length, prefix, size);128for (i = start; i < start + amount; ++i) {129if ((i % 8) == 0) {130current_len += SDL_snprintf(&buffer[current_len], length - current_len, "\n%.2d: ", i);131}132current_len += SDL_snprintf(&buffer[current_len], length - current_len, " 0x%.2x", data[i]);133}134SDL_strlcat(buffer, "\n", length);135SDL_Log("%s", buffer);136SDL_free(buffer);137}138139bool HIDAPI_SupportsPlaystationDetection(Uint16 vendor, Uint16 product)140{141/* If we already know the controller is a different type, don't try to detect it.142* This fixes a hang with the HORIPAD for Nintendo Switch (0x0f0d/0x00c1)143*/144if (SDL_GetGamepadTypeFromVIDPID(vendor, product, NULL, false) != SDL_GAMEPAD_TYPE_STANDARD) {145return false;146}147148switch (vendor) {149case USB_VENDOR_DRAGONRISE:150return true;151case USB_VENDOR_HORI:152return true;153case USB_VENDOR_LOGITECH:154/* Most Logitech devices are not PlayStation controllers, and some of them155* lock up or reset when we send them the Sony third-party query feature156* report, so don't include that vendor here. Instead add devices as157* appropriate to controller_list.h158*/159return false;160case USB_VENDOR_MADCATZ:161if (product == USB_PRODUCT_MADCATZ_SAITEK_SIDE_PANEL_CONTROL_DECK) {162// This is not a Playstation compatible device163return false;164}165return true;166case USB_VENDOR_MAYFLASH:167return true;168case USB_VENDOR_NACON:169case USB_VENDOR_NACON_ALT:170return true;171case USB_VENDOR_PDP:172return true;173case USB_VENDOR_POWERA:174return true;175case USB_VENDOR_POWERA_ALT:176return true;177case USB_VENDOR_QANBA:178return true;179case USB_VENDOR_RAZER:180/* Most Razer devices are not PlayStation controllers, and some of them181* lock up or reset when we send them the Sony third-party query feature182* report, so don't include that vendor here. Instead add devices as183* appropriate to controller_list.h184*185* Reference: https://github.com/libsdl-org/SDL/issues/6733186* https://github.com/libsdl-org/SDL/issues/6799187*/188return false;189case USB_VENDOR_SHANWAN:190return true;191case USB_VENDOR_SHANWAN_ALT:192return true;193case USB_VENDOR_THRUSTMASTER:194/* Most of these are wheels, don't have the full set of effects, and195* at least in the case of the T248 and T300 RS, the hid-tmff2 driver196* puts them in a non-standard report mode and they can't be read.197*198* If these should use the HIDAPI driver, add them to controller_list.h199*/200return false;201case USB_VENDOR_ZEROPLUS:202return true;203case 0x7545 /* SZ-MYPOWER */:204return true;205default:206return false;207}208}209210float HIDAPI_RemapVal(float val, float val_min, float val_max, float output_min, float output_max)211{212return output_min + (output_max - output_min) * (val - val_min) / (val_max - val_min);213}214215static void HIDAPI_UpdateDeviceList(void);216static void HIDAPI_JoystickClose(SDL_Joystick *joystick);217218static SDL_GamepadType SDL_GetJoystickGameControllerProtocol(const char *name, Uint16 vendor, Uint16 product, int interface_number, int interface_class, int interface_subclass, int interface_protocol)219{220static const int LIBUSB_CLASS_VENDOR_SPEC = 0xFF;221static const int XB360_IFACE_SUBCLASS = 93;222static const int XB360_IFACE_PROTOCOL = 1; // Wired223static const int XB360W_IFACE_PROTOCOL = 129; // Wireless224static const int XBONE_IFACE_SUBCLASS = 71;225static const int XBONE_IFACE_PROTOCOL = 208;226227SDL_GamepadType type = SDL_GAMEPAD_TYPE_STANDARD;228229// This code should match the checks in libusb/hid.c and HIDDeviceManager.java230if (interface_class == LIBUSB_CLASS_VENDOR_SPEC &&231interface_subclass == XB360_IFACE_SUBCLASS &&232(interface_protocol == XB360_IFACE_PROTOCOL ||233interface_protocol == XB360W_IFACE_PROTOCOL)) {234235static const int SUPPORTED_VENDORS[] = {2360x0079, // GPD Win 22370x044f, // Thrustmaster2380x045e, // Microsoft2390x046d, // Logitech2400x056e, // Elecom2410x06a3, // Saitek2420x0738, // Mad Catz2430x07ff, // Mad Catz2440x0e6f, // PDP2450x0f0d, // Hori2460x1038, // SteelSeries2470x11c9, // Nacon2480x12ab, // Unknown2490x1430, // RedOctane2500x146b, // BigBen2510x1532, // Razer2520x15e4, // Numark2530x162e, // Joytech2540x1689, // Razer Onza2550x1949, // Lab126, Inc.2560x1bad, // Harmonix2570x20d6, // PowerA2580x24c6, // PowerA2590x2c22, // Qanba2600x2dc8, // 8BitDo2610x9886, // ASTRO Gaming262};263264int i;265for (i = 0; i < SDL_arraysize(SUPPORTED_VENDORS); ++i) {266if (vendor == SUPPORTED_VENDORS[i]) {267type = SDL_GAMEPAD_TYPE_XBOX360;268break;269}270}271}272273if (interface_number == 0 &&274interface_class == LIBUSB_CLASS_VENDOR_SPEC &&275interface_subclass == XBONE_IFACE_SUBCLASS &&276interface_protocol == XBONE_IFACE_PROTOCOL) {277278static const int SUPPORTED_VENDORS[] = {2790x03f0, // HP2800x044f, // Thrustmaster2810x045e, // Microsoft2820x0738, // Mad Catz2830x0b05, // ASUS2840x0e6f, // PDP2850x0f0d, // Hori2860x10f5, // Turtle Beach2870x1532, // Razer2880x20d6, // PowerA2890x24c6, // PowerA2900x2dc8, // 8BitDo2910x2e24, // Hyperkin2920x3537, // GameSir293};294295int i;296for (i = 0; i < SDL_arraysize(SUPPORTED_VENDORS); ++i) {297if (vendor == SUPPORTED_VENDORS[i]) {298type = SDL_GAMEPAD_TYPE_XBOXONE;299break;300}301}302}303304if (type == SDL_GAMEPAD_TYPE_STANDARD) {305type = SDL_GetGamepadTypeFromVIDPID(vendor, product, name, false);306}307return type;308}309310static bool HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)311{312int i;313SDL_GamepadType type = SDL_GetJoystickGameControllerProtocol(name, vendor_id, product_id, -1, 0, 0, 0);314315for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {316SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];317if (driver->enabled && driver->IsSupportedDevice(NULL, name, type, vendor_id, product_id, version, -1, 0, 0, 0)) {318return true;319}320}321return false;322}323324static SDL_HIDAPI_DeviceDriver *HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)325{326const Uint16 USAGE_PAGE_GENERIC_DESKTOP = 0x0001;327const Uint16 USAGE_JOYSTICK = 0x0004;328const Uint16 USAGE_GAMEPAD = 0x0005;329const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008;330int i;331332if (device->num_children > 0) {333return &SDL_HIDAPI_DriverCombined;334}335336if (SDL_ShouldIgnoreJoystick(device->vendor_id, device->product_id, device->version, device->name)) {337return NULL;338}339340if (device->vendor_id != USB_VENDOR_VALVE) {341if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) {342return NULL;343}344if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) {345return NULL;346}347}348349for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {350SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];351if (driver->enabled && driver->IsSupportedDevice(device, device->name, device->type, device->vendor_id, device->product_id, device->version, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) {352return driver;353}354}355return NULL;356}357358static SDL_HIDAPI_Device *HIDAPI_GetDeviceByIndex(int device_index, SDL_JoystickID *pJoystickID)359{360SDL_HIDAPI_Device *device;361362SDL_AssertJoysticksLocked();363364for (device = SDL_HIDAPI_devices; device; device = device->next) {365if (device->parent || device->broken) {366continue;367}368if (device->driver) {369if (device_index < device->num_joysticks) {370if (pJoystickID) {371*pJoystickID = device->joysticks[device_index];372}373return device;374}375device_index -= device->num_joysticks;376}377}378return NULL;379}380381static SDL_HIDAPI_Device *HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id)382{383SDL_HIDAPI_Device *device;384385SDL_AssertJoysticksLocked();386387for (device = SDL_HIDAPI_devices; device; device = device->next) {388if (device->vendor_id == vendor_id && device->product_id == product_id &&389SDL_strcmp(device->path, path) == 0) {390break;391}392}393return device;394}395396static void HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device)397{398if (!device->driver) {399return; // Already cleaned up400}401402// Disconnect any joysticks403while (device->num_joysticks && device->joysticks) {404HIDAPI_JoystickDisconnected(device, device->joysticks[0]);405}406407device->driver->FreeDevice(device);408device->driver = NULL;409410SDL_LockMutex(device->dev_lock);411{412if (device->dev) {413SDL_hid_close(device->dev);414device->dev = NULL;415}416417if (device->context) {418SDL_free(device->context);419device->context = NULL;420}421}422SDL_UnlockMutex(device->dev_lock);423}424425static void HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device, bool *removed) SDL_NO_THREAD_SAFETY_ANALYSIS // We unlock the joystick lock to be able to open the HID device on Android426{427*removed = false;428429if (device->driver) {430bool enabled;431432if (device->vendor_id == USB_VENDOR_NINTENDO && device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR) {433enabled = SDL_HIDAPI_combine_joycons;434} else {435enabled = device->driver->enabled;436}437if (device->children) {438int i;439440for (i = 0; i < device->num_children; ++i) {441SDL_HIDAPI_Device *child = device->children[i];442if (!child->driver || !child->driver->enabled) {443enabled = false;444break;445}446}447}448if (!enabled) {449HIDAPI_CleanupDeviceDriver(device);450}451return; // Already setup452}453454if (HIDAPI_GetDeviceDriver(device)) {455// We might have a device driver for this device, try opening it and see456if (device->num_children == 0) {457SDL_hid_device *dev;458459// Wait a little bit for the device to initialize460SDL_Delay(10);461462dev = SDL_hid_open_path(device->path);463464if (dev == NULL) {465SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,466"HIDAPI_SetupDeviceDriver() couldn't open %s: %s",467device->path, SDL_GetError());468return;469}470SDL_hid_set_nonblocking(dev, 1);471472device->dev = dev;473}474475device->driver = HIDAPI_GetDeviceDriver(device);476477// Initialize the device, which may cause a connected event478if (device->driver && !device->driver->InitDevice(device)) {479HIDAPI_CleanupDeviceDriver(device);480}481482if (!device->driver && device->dev) {483// No driver claimed this device, go ahead and close it484SDL_hid_close(device->dev);485device->dev = NULL;486}487}488}489490static void SDL_HIDAPI_UpdateDrivers(void)491{492int i;493SDL_HIDAPI_Device *device;494bool removed;495496SDL_AssertJoysticksLocked();497498SDL_HIDAPI_numdrivers = 0;499for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {500SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];501driver->enabled = driver->IsEnabled();502if (driver->enabled && driver != &SDL_HIDAPI_DriverCombined) {503++SDL_HIDAPI_numdrivers;504}505}506507removed = false;508do {509for (device = SDL_HIDAPI_devices; device; device = device->next) {510HIDAPI_SetupDeviceDriver(device, &removed);511if (removed) {512break;513}514}515} while (removed);516}517518static void SDLCALL SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)519{520if (SDL_strcmp(name, SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS) == 0) {521SDL_HIDAPI_combine_joycons = SDL_GetStringBoolean(hint, true);522}523SDL_HIDAPI_hints_changed = true;524SDL_HIDAPI_change_count = 0;525}526527static bool HIDAPI_JoystickInit(void)528{529int i;530531if (initialized) {532return true;533}534535if (SDL_hid_init() < 0) {536return SDL_SetError("Couldn't initialize hidapi");537}538539for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {540SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];541driver->RegisterHints(SDL_HIDAPIDriverHintChanged, driver);542}543SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS,544SDL_HIDAPIDriverHintChanged, NULL);545SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI,546SDL_HIDAPIDriverHintChanged, NULL);547548SDL_HIDAPI_change_count = SDL_hid_device_change_count();549HIDAPI_UpdateDeviceList();550HIDAPI_UpdateDevices();551552initialized = true;553554return true;555}556557static bool HIDAPI_AddJoystickInstanceToDevice(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)558{559SDL_JoystickID *joysticks = (SDL_JoystickID *)SDL_realloc(device->joysticks, (device->num_joysticks + 1) * sizeof(*device->joysticks));560if (!joysticks) {561return false;562}563564device->joysticks = joysticks;565device->joysticks[device->num_joysticks++] = joystickID;566return true;567}568569static bool HIDAPI_DelJoystickInstanceFromDevice(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)570{571int i, size;572573for (i = 0; i < device->num_joysticks; ++i) {574if (device->joysticks[i] == joystickID) {575size = (device->num_joysticks - i - 1) * sizeof(SDL_JoystickID);576SDL_memmove(&device->joysticks[i], &device->joysticks[i + 1], size);577--device->num_joysticks;578if (device->num_joysticks == 0) {579SDL_free(device->joysticks);580device->joysticks = NULL;581}582return true;583}584}585return false;586}587588static bool HIDAPI_JoystickInstanceIsUnique(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)589{590if (device->parent && device->num_joysticks == 1 && device->parent->num_joysticks == 1 &&591device->joysticks[0] == device->parent->joysticks[0]) {592return false;593}594return true;595}596597void HIDAPI_SetDeviceName(SDL_HIDAPI_Device *device, const char *name)598{599if (name && *name && SDL_strcmp(name, device->name) != 0) {600SDL_free(device->name);601device->name = SDL_strdup(name);602SDL_SetJoystickGUIDCRC(&device->guid, SDL_crc16(0, name, SDL_strlen(name)));603}604}605606void HIDAPI_SetDeviceProduct(SDL_HIDAPI_Device *device, Uint16 vendor_id, Uint16 product_id)607{608// Don't set the device product ID directly, or we'll constantly re-enumerate this device609device->guid = SDL_CreateJoystickGUID(device->guid.data[0], vendor_id, product_id, device->version, device->manufacturer_string, device->product_string, 'h', 0);610}611612static void HIDAPI_UpdateJoystickSerial(SDL_HIDAPI_Device *device)613{614int i;615616SDL_AssertJoysticksLocked();617618for (i = 0; i < device->num_joysticks; ++i) {619SDL_Joystick *joystick = SDL_GetJoystickFromID(device->joysticks[i]);620if (joystick && device->serial) {621SDL_free(joystick->serial);622joystick->serial = SDL_strdup(device->serial);623}624}625}626627static bool HIDAPI_SerialIsEmpty(SDL_HIDAPI_Device *device)628{629bool all_zeroes = true;630631if (device->serial) {632const char *serial = device->serial;633for (serial = device->serial; *serial; ++serial) {634if (*serial != '0') {635all_zeroes = false;636break;637}638}639}640return all_zeroes;641}642643void HIDAPI_SetDeviceSerial(SDL_HIDAPI_Device *device, const char *serial)644{645if (serial && *serial && (!device->serial || SDL_strcmp(serial, device->serial) != 0)) {646SDL_free(device->serial);647device->serial = SDL_strdup(serial);648HIDAPI_UpdateJoystickSerial(device);649}650}651652static int wcstrcmp(const wchar_t *str1, const char *str2)653{654int result;655656while (1) {657result = (*str1 - *str2);658if (result != 0 || *str1 == 0) {659break;660}661++str1;662++str2;663}664return result;665}666667static void HIDAPI_SetDeviceSerialW(SDL_HIDAPI_Device *device, const wchar_t *serial)668{669if (serial && *serial && (!device->serial || wcstrcmp(serial, device->serial) != 0)) {670SDL_free(device->serial);671device->serial = HIDAPI_ConvertString(serial);672HIDAPI_UpdateJoystickSerial(device);673}674}675676bool HIDAPI_HasConnectedUSBDevice(const char *serial)677{678SDL_HIDAPI_Device *device;679680SDL_AssertJoysticksLocked();681682if (!serial) {683return false;684}685686for (device = SDL_HIDAPI_devices; device; device = device->next) {687if (!device->driver || device->broken) {688continue;689}690691if (device->is_bluetooth) {692continue;693}694695if (device->serial && SDL_strcmp(serial, device->serial) == 0) {696return true;697}698}699return false;700}701702void HIDAPI_DisconnectBluetoothDevice(const char *serial)703{704SDL_HIDAPI_Device *device;705706SDL_AssertJoysticksLocked();707708if (!serial) {709return;710}711712for (device = SDL_HIDAPI_devices; device; device = device->next) {713if (!device->driver || device->broken) {714continue;715}716717if (!device->is_bluetooth) {718continue;719}720721if (device->serial && SDL_strcmp(serial, device->serial) == 0) {722while (device->num_joysticks && device->joysticks) {723HIDAPI_JoystickDisconnected(device, device->joysticks[0]);724}725}726}727}728729bool HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID)730{731int i, j;732SDL_JoystickID joystickID;733734SDL_AssertJoysticksLocked();735736for (i = 0; i < device->num_children; ++i) {737SDL_HIDAPI_Device *child = device->children[i];738for (j = child->num_joysticks; j--;) {739HIDAPI_JoystickDisconnected(child, child->joysticks[j]);740}741}742743joystickID = SDL_GetNextObjectID();744HIDAPI_AddJoystickInstanceToDevice(device, joystickID);745746for (i = 0; i < device->num_children; ++i) {747SDL_HIDAPI_Device *child = device->children[i];748HIDAPI_AddJoystickInstanceToDevice(child, joystickID);749}750751++SDL_HIDAPI_numjoysticks;752753if (pJoystickID) {754*pJoystickID = joystickID;755}756757SDL_PrivateJoystickAdded(joystickID);758759return true;760}761762void HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)763{764int i, j;765766SDL_LockJoysticks();767768if (!HIDAPI_JoystickInstanceIsUnique(device, joystickID)) {769// Disconnecting a child always disconnects the parent770device = device->parent;771}772773for (i = 0; i < device->num_joysticks; ++i) {774if (device->joysticks[i] == joystickID) {775SDL_Joystick *joystick = SDL_GetJoystickFromID(joystickID);776if (joystick) {777HIDAPI_JoystickClose(joystick);778}779780HIDAPI_DelJoystickInstanceFromDevice(device, joystickID);781782for (j = 0; j < device->num_children; ++j) {783SDL_HIDAPI_Device *child = device->children[j];784HIDAPI_DelJoystickInstanceFromDevice(child, joystickID);785}786787--SDL_HIDAPI_numjoysticks;788789if (!shutting_down) {790SDL_PrivateJoystickRemoved(joystickID);791}792}793}794795// Rescan the device list in case device state has changed796SDL_HIDAPI_change_count = 0;797798SDL_UnlockJoysticks();799}800801static void HIDAPI_UpdateJoystickProperties(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)802{803SDL_PropertiesID props = SDL_GetJoystickProperties(joystick);804Uint32 caps = device->driver->GetJoystickCapabilities(device, joystick);805806if (caps & SDL_JOYSTICK_CAP_MONO_LED) {807SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_MONO_LED_BOOLEAN, true);808} else {809SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_MONO_LED_BOOLEAN, false);810}811if (caps & SDL_JOYSTICK_CAP_RGB_LED) {812SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, true);813} else {814SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, false);815}816if (caps & SDL_JOYSTICK_CAP_PLAYER_LED) {817SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_PLAYER_LED_BOOLEAN, true);818} else {819SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_PLAYER_LED_BOOLEAN, false);820}821if (caps & SDL_JOYSTICK_CAP_RUMBLE) {822SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);823} else {824SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, false);825}826if (caps & SDL_JOYSTICK_CAP_TRIGGER_RUMBLE) {827SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true);828} else {829SDL_SetBooleanProperty(props, SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, false);830}831}832833void HIDAPI_UpdateDeviceProperties(SDL_HIDAPI_Device *device)834{835int i;836837SDL_LockJoysticks();838839for (i = 0; i < device->num_joysticks; ++i) {840SDL_Joystick *joystick = SDL_GetJoystickFromID(device->joysticks[i]);841if (joystick) {842HIDAPI_UpdateJoystickProperties(device, joystick);843}844}845846SDL_UnlockJoysticks();847}848849static int HIDAPI_JoystickGetCount(void)850{851return SDL_HIDAPI_numjoysticks;852}853854static SDL_HIDAPI_Device *HIDAPI_AddDevice(const struct SDL_hid_device_info *info, int num_children, SDL_HIDAPI_Device **children)855{856SDL_HIDAPI_Device *device;857SDL_HIDAPI_Device *curr, *last = NULL;858bool removed;859Uint16 bus;860861SDL_AssertJoysticksLocked();862863for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {864}865866device = (SDL_HIDAPI_Device *)SDL_calloc(1, sizeof(*device));867if (!device) {868return NULL;869}870SDL_SetObjectValid(device, SDL_OBJECT_TYPE_HIDAPI_JOYSTICK, true);871if (info->path) {872device->path = SDL_strdup(info->path);873}874device->seen = true;875device->vendor_id = info->vendor_id;876device->product_id = info->product_id;877device->version = info->release_number;878device->interface_number = info->interface_number;879device->interface_class = info->interface_class;880device->interface_subclass = info->interface_subclass;881device->interface_protocol = info->interface_protocol;882device->usage_page = info->usage_page;883device->usage = info->usage;884device->is_bluetooth = (info->bus_type == SDL_HID_API_BUS_BLUETOOTH);885device->dev_lock = SDL_CreateMutex();886887// Need the device name before getting the driver to know whether to ignore this device888{889char *serial_number = HIDAPI_ConvertString(info->serial_number);890891device->manufacturer_string = HIDAPI_ConvertString(info->manufacturer_string);892device->product_string = HIDAPI_ConvertString(info->product_string);893device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, device->manufacturer_string, device->product_string);894895if (serial_number && *serial_number) {896device->serial = serial_number;897} else {898SDL_free(serial_number);899}900901if (!device->name) {902SDL_free(device->manufacturer_string);903SDL_free(device->product_string);904SDL_free(device->serial);905SDL_free(device->path);906SDL_free(device);907return NULL;908}909}910911if (info->bus_type == SDL_HID_API_BUS_BLUETOOTH) {912bus = SDL_HARDWARE_BUS_BLUETOOTH;913} else {914bus = SDL_HARDWARE_BUS_USB;915}916device->guid = SDL_CreateJoystickGUID(bus, device->vendor_id, device->product_id, device->version, device->manufacturer_string, device->product_string, 'h', 0);917device->joystick_type = SDL_JOYSTICK_TYPE_GAMEPAD;918device->type = SDL_GetJoystickGameControllerProtocol(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);919device->steam_virtual_gamepad_slot = -1;920921if (num_children > 0) {922int i;923924device->num_children = num_children;925device->children = children;926for (i = 0; i < num_children; ++i) {927children[i]->parent = device;928}929}930931// Add it to the list932if (last) {933last->next = device;934} else {935SDL_HIDAPI_devices = device;936}937938removed = false;939HIDAPI_SetupDeviceDriver(device, &removed);940if (removed) {941return NULL;942}943944SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, bluetooth %d, version %d, serial %s, interface %d, interface_class %d, interface_subclass %d, interface_protocol %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s (%s)", device->name, device->vendor_id, device->product_id, device->is_bluetooth, device->version,945device->serial ? device->serial : "NONE", device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage,946device->path, device->driver ? device->driver->name : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED");947948return device;949}950951static void HIDAPI_DelDevice(SDL_HIDAPI_Device *device)952{953SDL_HIDAPI_Device *curr, *last;954int i;955956SDL_AssertJoysticksLocked();957958SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "Removing HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, bluetooth %d, version %d, serial %s, interface %d, interface_class %d, interface_subclass %d, interface_protocol %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s (%s)", device->name, device->vendor_id, device->product_id, device->is_bluetooth, device->version,959device->serial ? device->serial : "NONE", device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage,960device->path, device->driver ? device->driver->name : "NONE", device->driver && device->driver->enabled ? "ENABLED" : "DISABLED");961962for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {963if (curr == device) {964if (last) {965last->next = curr->next;966} else {967SDL_HIDAPI_devices = curr->next;968}969970HIDAPI_CleanupDeviceDriver(device);971972// Make sure the rumble thread is done with this device973while (SDL_GetAtomicInt(&device->rumble_pending) > 0) {974SDL_Delay(10);975}976977for (i = 0; i < device->num_children; ++i) {978device->children[i]->parent = NULL;979}980981SDL_SetObjectValid(device, SDL_OBJECT_TYPE_HIDAPI_JOYSTICK, false);982SDL_DestroyMutex(device->dev_lock);983SDL_free(device->manufacturer_string);984SDL_free(device->product_string);985SDL_free(device->serial);986SDL_free(device->name);987SDL_free(device->path);988SDL_free(device->children);989SDL_free(device);990return;991}992}993}994995static bool HIDAPI_CreateCombinedJoyCons(void)996{997SDL_HIDAPI_Device *device, *combined;998SDL_HIDAPI_Device *joycons[2] = { NULL, NULL };9991000SDL_AssertJoysticksLocked();10011002if (!SDL_HIDAPI_combine_joycons) {1003return false;1004}10051006for (device = SDL_HIDAPI_devices; device; device = device->next) {1007Uint16 vendor, product;10081009if (!device->driver) {1010// Unsupported device1011continue;1012}1013if (device->parent) {1014// This device is already part of a combined device1015continue;1016}1017if (device->broken) {1018// This device can't be used1019continue;1020}10211022SDL_GetJoystickGUIDInfo(device->guid, &vendor, &product, NULL, NULL);10231024if (!joycons[0] &&1025(SDL_IsJoystickNintendoSwitchJoyConLeft(vendor, product) ||1026(SDL_IsJoystickNintendoSwitchJoyConGrip(vendor, product) &&1027SDL_strstr(device->name, "(L)") != NULL))) {1028joycons[0] = device;1029}1030if (!joycons[1] &&1031(SDL_IsJoystickNintendoSwitchJoyConRight(vendor, product) ||1032(SDL_IsJoystickNintendoSwitchJoyConGrip(vendor, product) &&1033SDL_strstr(device->name, "(R)") != NULL))) {1034joycons[1] = device;1035}1036if (joycons[0] && joycons[1]) {1037SDL_hid_device_info info;1038SDL_HIDAPI_Device **children = (SDL_HIDAPI_Device **)SDL_malloc(2 * sizeof(SDL_HIDAPI_Device *));1039if (!children) {1040return false;1041}1042children[0] = joycons[0];1043children[1] = joycons[1];10441045SDL_zero(info);1046info.path = "nintendo_joycons_combined";1047info.vendor_id = USB_VENDOR_NINTENDO;1048info.product_id = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR;1049info.interface_number = -1;1050info.usage_page = USB_USAGEPAGE_GENERIC_DESKTOP;1051info.usage = USB_USAGE_GENERIC_GAMEPAD;1052info.manufacturer_string = L"Nintendo";1053info.product_string = L"Switch Joy-Con (L/R)";1054if (children[0]->is_bluetooth || children[1]->is_bluetooth) {1055info.bus_type = SDL_HID_API_BUS_BLUETOOTH;1056} else {1057info.bus_type = SDL_HID_API_BUS_USB;1058}10591060combined = HIDAPI_AddDevice(&info, 2, children);1061if (combined && combined->driver) {1062return true;1063} else {1064if (combined) {1065HIDAPI_DelDevice(combined);1066} else {1067SDL_free(children);1068}1069return false;1070}1071}1072}1073return false;1074}10751076static void HIDAPI_UpdateDeviceList(void)1077{1078SDL_HIDAPI_Device *device;1079struct SDL_hid_device_info *devs, *info;10801081SDL_LockJoysticks();10821083if (SDL_HIDAPI_hints_changed) {1084SDL_HIDAPI_UpdateDrivers();1085SDL_HIDAPI_hints_changed = false;1086}10871088// Prepare the existing device list1089for (device = SDL_HIDAPI_devices; device; device = device->next) {1090if (device->children) {1091continue;1092}1093device->seen = false;1094}10951096// Enumerate the devices1097if (SDL_HIDAPI_numdrivers > 0) {1098devs = SDL_hid_enumerate(0, 0);1099if (devs) {1100for (info = devs; info; info = info->next) {1101device = HIDAPI_GetJoystickByInfo(info->path, info->vendor_id, info->product_id);1102if (device) {1103device->seen = true;11041105// Check to see if the serial number is available now1106if(HIDAPI_SerialIsEmpty(device)) {1107HIDAPI_SetDeviceSerialW(device, info->serial_number);1108}1109} else {1110HIDAPI_AddDevice(info, 0, NULL);1111}1112}1113SDL_hid_free_enumeration(devs);1114}1115}11161117// Remove any devices that weren't seen or have been disconnected due to read errors1118check_removed:1119device = SDL_HIDAPI_devices;1120while (device) {1121SDL_HIDAPI_Device *next = device->next;11221123if (!device->seen ||1124((device->driver || device->children) && device->num_joysticks == 0 && !device->dev)) {1125if (device->parent) {1126// When a child device goes away, so does the parent1127int i;1128device = device->parent;1129for (i = 0; i < device->num_children; ++i) {1130HIDAPI_DelDevice(device->children[i]);1131}1132HIDAPI_DelDevice(device);11331134// Update the device list again to pick up any children left1135SDL_HIDAPI_change_count = 0;11361137// We deleted more than one device here, restart the loop1138goto check_removed;1139} else {1140HIDAPI_DelDevice(device);1141device = NULL;11421143// Update the device list again in case this device comes back1144SDL_HIDAPI_change_count = 0;1145}1146}1147if (device && device->broken && device->parent) {1148HIDAPI_DelDevice(device->parent);11491150// We deleted a different device here, restart the loop1151goto check_removed;1152}1153device = next;1154}11551156// See if we can create any combined Joy-Con controllers1157while (HIDAPI_CreateCombinedJoyCons()) {1158}11591160SDL_UnlockJoysticks();1161}11621163static bool HIDAPI_IsEquivalentToDevice(Uint16 vendor_id, Uint16 product_id, SDL_HIDAPI_Device *device)1164{1165if (vendor_id == device->vendor_id && product_id == device->product_id) {1166return true;1167}11681169if (vendor_id == USB_VENDOR_MICROSOFT) {1170// If we're looking for the wireless XBox 360 controller, also look for the dongle1171if (product_id == USB_PRODUCT_XBOX360_XUSB_CONTROLLER && device->product_id == USB_PRODUCT_XBOX360_WIRELESS_RECEIVER) {1172return true;1173}11741175// If we're looking for the raw input Xbox One controller, match it against any other Xbox One controller1176if (product_id == USB_PRODUCT_XBOX_ONE_XBOXGIP_CONTROLLER &&1177device->type == SDL_GAMEPAD_TYPE_XBOXONE) {1178return true;1179}11801181// If we're looking for an XInput controller, match it against any other Xbox controller1182if (product_id == USB_PRODUCT_XBOX360_XUSB_CONTROLLER) {1183if (device->type == SDL_GAMEPAD_TYPE_XBOX360 || device->type == SDL_GAMEPAD_TYPE_XBOXONE) {1184return true;1185}1186}1187}11881189if (vendor_id == USB_VENDOR_NVIDIA) {1190// If we're looking for the NVIDIA SHIELD controller Xbox interface, match it against any NVIDIA SHIELD controller1191if (product_id == 0xb400 &&1192SDL_IsJoystickNVIDIASHIELDController(vendor_id, product_id)) {1193return true;1194}1195}1196return false;1197}11981199static bool HIDAPI_StartUpdatingDevices(void)1200{1201return SDL_CompareAndSwapAtomicInt(&SDL_HIDAPI_updating_devices, false, true);1202}12031204static void HIDAPI_FinishUpdatingDevices(void)1205{1206SDL_SetAtomicInt(&SDL_HIDAPI_updating_devices, false);1207}12081209bool HIDAPI_IsDeviceTypePresent(SDL_GamepadType type)1210{1211SDL_HIDAPI_Device *device;1212bool result = false;12131214// Make sure we're initialized, as this could be called from other drivers during startup1215if (!HIDAPI_JoystickInit()) {1216return false;1217}12181219if (HIDAPI_StartUpdatingDevices()) {1220HIDAPI_UpdateDeviceList();1221HIDAPI_FinishUpdatingDevices();1222}12231224SDL_LockJoysticks();1225for (device = SDL_HIDAPI_devices; device; device = device->next) {1226if (device->driver && device->type == type) {1227result = true;1228break;1229}1230}1231SDL_UnlockJoysticks();12321233#ifdef DEBUG_HIDAPI1234SDL_Log("HIDAPI_IsDeviceTypePresent() returning %s for %d", result ? "true" : "false", type);1235#endif1236return result;1237}12381239bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)1240{1241SDL_HIDAPI_Device *device;1242bool supported = false;1243bool result = false;12441245// Make sure we're initialized, as this could be called from other drivers during startup1246if (!HIDAPI_JoystickInit()) {1247return false;1248}12491250/* Only update the device list for devices we know might be supported.1251If we did this for every device, it would hit the USB driver too hard and potentially1252lock up the system. This won't catch devices that we support but can only detect using1253USB interface details, like Xbox controllers, but hopefully the device list update is1254responsive enough to catch those.1255*/1256supported = HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name);1257#if defined(SDL_JOYSTICK_HIDAPI_XBOX360) || defined(SDL_JOYSTICK_HIDAPI_XBOXONE)1258if (!supported &&1259(SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX"))) {1260supported = true;1261}1262#endif // SDL_JOYSTICK_HIDAPI_XBOX360 || SDL_JOYSTICK_HIDAPI_XBOXONE1263if (supported) {1264if (HIDAPI_StartUpdatingDevices()) {1265HIDAPI_UpdateDeviceList();1266HIDAPI_FinishUpdatingDevices();1267}1268}12691270/* Note that this isn't a perfect check - there may be multiple devices with 0 VID/PID,1271or a different name than we have it listed here, etc, but if we support the device1272and we have something similar in our device list, mark it as present.1273*/1274SDL_LockJoysticks();1275for (device = SDL_HIDAPI_devices; device; device = device->next) {1276if (device->driver &&1277HIDAPI_IsEquivalentToDevice(vendor_id, product_id, device)) {1278result = true;1279break;1280}1281}1282SDL_UnlockJoysticks();12831284#ifdef DEBUG_HIDAPI1285SDL_Log("HIDAPI_IsDevicePresent() returning %s for 0x%.4x / 0x%.4x", result ? "true" : "false", vendor_id, product_id);1286#endif1287return result;1288}12891290char *HIDAPI_GetDeviceProductName(Uint16 vendor_id, Uint16 product_id)1291{1292SDL_HIDAPI_Device *device;1293char *name = NULL;12941295SDL_LockJoysticks();1296for (device = SDL_HIDAPI_devices; device; device = device->next) {1297if (vendor_id == device->vendor_id && product_id == device->product_id) {1298if (device->product_string) {1299name = SDL_strdup(device->product_string);1300}1301break;1302}1303}1304SDL_UnlockJoysticks();13051306return name;1307}13081309char *HIDAPI_GetDeviceManufacturerName(Uint16 vendor_id, Uint16 product_id)1310{1311SDL_HIDAPI_Device *device;1312char *name = NULL;13131314SDL_LockJoysticks();1315for (device = SDL_HIDAPI_devices; device; device = device->next) {1316if (vendor_id == device->vendor_id && product_id == device->product_id) {1317if (device->manufacturer_string) {1318name = SDL_strdup(device->manufacturer_string);1319}1320break;1321}1322}1323SDL_UnlockJoysticks();13241325return name;1326}13271328SDL_JoystickType HIDAPI_GetJoystickTypeFromGUID(SDL_GUID guid)1329{1330SDL_HIDAPI_Device *device;1331SDL_JoystickType type = SDL_JOYSTICK_TYPE_UNKNOWN;13321333SDL_LockJoysticks();1334for (device = SDL_HIDAPI_devices; device; device = device->next) {1335if (SDL_memcmp(&guid, &device->guid, sizeof(guid)) == 0) {1336type = device->joystick_type;1337break;1338}1339}1340SDL_UnlockJoysticks();13411342return type;1343}13441345SDL_GamepadType HIDAPI_GetGamepadTypeFromGUID(SDL_GUID guid)1346{1347SDL_HIDAPI_Device *device;1348SDL_GamepadType type = SDL_GAMEPAD_TYPE_STANDARD;13491350SDL_LockJoysticks();1351for (device = SDL_HIDAPI_devices; device; device = device->next) {1352if (SDL_memcmp(&guid, &device->guid, sizeof(guid)) == 0) {1353type = device->type;1354break;1355}1356}1357SDL_UnlockJoysticks();13581359return type;1360}13611362static void HIDAPI_JoystickDetect(void)1363{1364if (HIDAPI_StartUpdatingDevices()) {1365Uint32 count = SDL_hid_device_change_count();1366if (SDL_HIDAPI_change_count != count) {1367SDL_HIDAPI_change_count = count;1368HIDAPI_UpdateDeviceList();1369}1370HIDAPI_FinishUpdatingDevices();1371}1372}13731374void HIDAPI_UpdateDevices(void)1375{1376SDL_HIDAPI_Device *device;13771378SDL_AssertJoysticksLocked();13791380// Update the devices, which may change connected joysticks and send events13811382// Prepare the existing device list1383if (HIDAPI_StartUpdatingDevices()) {1384for (device = SDL_HIDAPI_devices; device; device = device->next) {1385if (device->parent) {1386continue;1387}1388if (device->driver) {1389if (SDL_TryLockMutex(device->dev_lock)) {1390device->updating = true;1391device->driver->UpdateDevice(device);1392device->updating = false;1393SDL_UnlockMutex(device->dev_lock);1394}1395}1396}1397HIDAPI_FinishUpdatingDevices();1398}1399}14001401static const char *HIDAPI_JoystickGetDeviceName(int device_index)1402{1403SDL_HIDAPI_Device *device;1404const char *name = NULL;14051406device = HIDAPI_GetDeviceByIndex(device_index, NULL);1407if (device) {1408// FIXME: The device could be freed after this name is returned...1409name = device->name;1410}14111412return name;1413}14141415static const char *HIDAPI_JoystickGetDevicePath(int device_index)1416{1417SDL_HIDAPI_Device *device;1418const char *path = NULL;14191420device = HIDAPI_GetDeviceByIndex(device_index, NULL);1421if (device) {1422// FIXME: The device could be freed after this path is returned...1423path = device->path;1424}14251426return path;1427}14281429static int HIDAPI_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)1430{1431SDL_HIDAPI_Device *device;14321433device = HIDAPI_GetDeviceByIndex(device_index, NULL);1434if (device) {1435return device->steam_virtual_gamepad_slot;1436}1437return -1;1438}14391440static int HIDAPI_JoystickGetDevicePlayerIndex(int device_index)1441{1442SDL_HIDAPI_Device *device;1443SDL_JoystickID instance_id;1444int player_index = -1;14451446device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);1447if (device) {1448player_index = device->driver->GetDevicePlayerIndex(device, instance_id);1449}14501451return player_index;1452}14531454static void HIDAPI_JoystickSetDevicePlayerIndex(int device_index, int player_index)1455{1456SDL_HIDAPI_Device *device;1457SDL_JoystickID instance_id;14581459device = HIDAPI_GetDeviceByIndex(device_index, &instance_id);1460if (device) {1461device->driver->SetDevicePlayerIndex(device, instance_id, player_index);1462}1463}14641465static SDL_GUID HIDAPI_JoystickGetDeviceGUID(int device_index)1466{1467SDL_HIDAPI_Device *device;1468SDL_GUID guid;14691470device = HIDAPI_GetDeviceByIndex(device_index, NULL);1471if (device) {1472SDL_memcpy(&guid, &device->guid, sizeof(guid));1473} else {1474SDL_zero(guid);1475}14761477return guid;1478}14791480static SDL_JoystickID HIDAPI_JoystickGetDeviceInstanceID(int device_index)1481{1482SDL_JoystickID joystickID = 0;1483HIDAPI_GetDeviceByIndex(device_index, &joystickID);1484return joystickID;1485}14861487static bool HIDAPI_JoystickOpen(SDL_Joystick *joystick, int device_index)1488{1489SDL_JoystickID joystickID = 0;1490SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index, &joystickID);1491struct joystick_hwdata *hwdata;14921493SDL_AssertJoysticksLocked();14941495if (!device || !device->driver || device->broken) {1496// This should never happen - validated before being called1497return SDL_SetError("Couldn't find HIDAPI device at index %d", device_index);1498}14991500hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));1501if (!hwdata) {1502return false;1503}1504hwdata->device = device;15051506// Process any pending reports before opening the device1507SDL_LockMutex(device->dev_lock);1508device->updating = true;1509device->driver->UpdateDevice(device);1510device->updating = false;1511SDL_UnlockMutex(device->dev_lock);15121513// UpdateDevice() may have called HIDAPI_JoystickDisconnected() if the device went away1514if (device->num_joysticks == 0) {1515SDL_free(hwdata);1516return SDL_SetError("HIDAPI device disconnected while opening");1517}15181519// Set the default connection state, can be overridden below1520if (device->is_bluetooth) {1521joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;1522} else {1523joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED;1524}15251526if (!device->driver->OpenJoystick(device, joystick)) {1527// The open failed, mark this device as disconnected and update devices1528HIDAPI_JoystickDisconnected(device, joystickID);1529SDL_free(hwdata);1530return false;1531}15321533HIDAPI_UpdateJoystickProperties(device, joystick);15341535if (device->serial) {1536joystick->serial = SDL_strdup(device->serial);1537}15381539joystick->hwdata = hwdata;1540return true;1541}15421543static bool HIDAPI_GetJoystickDevice(SDL_Joystick *joystick, SDL_HIDAPI_Device **device)1544{1545SDL_AssertJoysticksLocked();15461547if (joystick && joystick->hwdata) {1548*device = joystick->hwdata->device;1549if (SDL_ObjectValid(*device, SDL_OBJECT_TYPE_HIDAPI_JOYSTICK) && (*device)->driver != NULL) {1550return true;1551}1552}1553return false;1554}15551556static bool HIDAPI_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)1557{1558bool result;1559SDL_HIDAPI_Device *device = NULL;15601561if (HIDAPI_GetJoystickDevice(joystick, &device)) {1562result = device->driver->RumbleJoystick(device, joystick, low_frequency_rumble, high_frequency_rumble);1563} else {1564result = SDL_SetError("Rumble failed, device disconnected");1565}15661567return result;1568}15691570static bool HIDAPI_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)1571{1572bool result;1573SDL_HIDAPI_Device *device = NULL;15741575if (HIDAPI_GetJoystickDevice(joystick, &device)) {1576result = device->driver->RumbleJoystickTriggers(device, joystick, left_rumble, right_rumble);1577} else {1578result = SDL_SetError("Rumble failed, device disconnected");1579}15801581return result;1582}15831584static bool HIDAPI_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)1585{1586bool result;1587SDL_HIDAPI_Device *device = NULL;15881589if (HIDAPI_GetJoystickDevice(joystick, &device)) {1590result = device->driver->SetJoystickLED(device, joystick, red, green, blue);1591} else {1592result = SDL_SetError("SetLED failed, device disconnected");1593}15941595return result;1596}15971598static bool HIDAPI_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)1599{1600bool result;1601SDL_HIDAPI_Device *device = NULL;16021603if (HIDAPI_GetJoystickDevice(joystick, &device)) {1604result = device->driver->SendJoystickEffect(device, joystick, data, size);1605} else {1606result = SDL_SetError("SendEffect failed, device disconnected");1607}16081609return result;1610}16111612static bool HIDAPI_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled)1613{1614bool result;1615SDL_HIDAPI_Device *device = NULL;16161617if (HIDAPI_GetJoystickDevice(joystick, &device)) {1618result = device->driver->SetJoystickSensorsEnabled(device, joystick, enabled);1619} else {1620result = SDL_SetError("SetSensorsEnabled failed, device disconnected");1621}16221623return result;1624}16251626static void HIDAPI_JoystickUpdate(SDL_Joystick *joystick)1627{1628// This is handled in SDL_HIDAPI_UpdateDevices()1629}16301631static void HIDAPI_JoystickClose(SDL_Joystick *joystick) SDL_NO_THREAD_SAFETY_ANALYSIS // We unlock the device lock so rumble can complete1632{1633SDL_AssertJoysticksLocked();16341635if (joystick->hwdata) {1636SDL_HIDAPI_Device *device = joystick->hwdata->device;1637int i;16381639// Wait up to 30 ms for pending rumble to complete1640if (device->updating) {1641// Unlock the device so rumble can complete1642SDL_UnlockMutex(device->dev_lock);1643}1644for (i = 0; i < 3; ++i) {1645if (SDL_GetAtomicInt(&device->rumble_pending) > 0) {1646SDL_Delay(10);1647}1648}1649if (device->updating) {1650// Relock the device1651SDL_LockMutex(device->dev_lock);1652}16531654device->driver->CloseJoystick(device, joystick);16551656SDL_free(joystick->hwdata);1657joystick->hwdata = NULL;1658}1659}16601661static void HIDAPI_JoystickQuit(void)1662{1663int i;16641665SDL_AssertJoysticksLocked();16661667shutting_down = true;16681669SDL_HIDAPI_QuitRumble();16701671while (SDL_HIDAPI_devices) {1672SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;1673if (device->parent) {1674// When a child device goes away, so does the parent1675device = device->parent;1676for (i = 0; i < device->num_children; ++i) {1677HIDAPI_DelDevice(device->children[i]);1678}1679HIDAPI_DelDevice(device);1680} else {1681HIDAPI_DelDevice(device);1682}1683}16841685// Make sure the drivers cleaned up properly1686SDL_assert(SDL_HIDAPI_numjoysticks == 0);16871688for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {1689SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];1690driver->UnregisterHints(SDL_HIDAPIDriverHintChanged, driver);1691}1692SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS,1693SDL_HIDAPIDriverHintChanged, NULL);1694SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI,1695SDL_HIDAPIDriverHintChanged, NULL);16961697SDL_hid_exit();16981699SDL_HIDAPI_change_count = 0;1700shutting_down = false;1701initialized = false;1702}17031704static bool HIDAPI_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)1705{1706return false;1707}17081709SDL_JoystickDriver SDL_HIDAPI_JoystickDriver = {1710HIDAPI_JoystickInit,1711HIDAPI_JoystickGetCount,1712HIDAPI_JoystickDetect,1713HIDAPI_IsDevicePresent,1714HIDAPI_JoystickGetDeviceName,1715HIDAPI_JoystickGetDevicePath,1716HIDAPI_JoystickGetDeviceSteamVirtualGamepadSlot,1717HIDAPI_JoystickGetDevicePlayerIndex,1718HIDAPI_JoystickSetDevicePlayerIndex,1719HIDAPI_JoystickGetDeviceGUID,1720HIDAPI_JoystickGetDeviceInstanceID,1721HIDAPI_JoystickOpen,1722HIDAPI_JoystickRumble,1723HIDAPI_JoystickRumbleTriggers,1724HIDAPI_JoystickSetLED,1725HIDAPI_JoystickSendEffect,1726HIDAPI_JoystickSetSensorsEnabled,1727HIDAPI_JoystickUpdate,1728HIDAPI_JoystickClose,1729HIDAPI_JoystickQuit,1730HIDAPI_JoystickGetGamepadMapping1731};17321733#endif // SDL_JOYSTICK_HIDAPI173417351736