Path: blob/master/thirdparty/sdl/joystick/linux/SDL_sysjoystick.c
21635 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_LINUX2324#ifndef SDL_INPUT_LINUXEV25#error SDL now requires a Linux 2.4+ kernel with /dev/input/event support.26#endif2728// This is the Linux implementation of the SDL joystick API2930#include <sys/stat.h>31#include <errno.h> // errno, strerror32#include <fcntl.h>33#include <limits.h> // For the definition of PATH_MAX34#ifdef HAVE_INOTIFY35#include <sys/inotify.h>36#include <string.h> // strerror37#endif38#include <sys/ioctl.h>39#include <unistd.h>40#include <dirent.h>41#include <linux/joystick.h>4243#include "../../events/SDL_events_c.h"44#include "../../core/linux/SDL_evdev.h"45#include "../SDL_sysjoystick.h"46#include "../SDL_joystick_c.h"47#include "../usb_ids.h"48#include "SDL_sysjoystick_c.h"49#include "../hidapi/SDL_hidapijoystick_c.h"5051// This isn't defined in older Linux kernel headers52#ifndef MSC_TIMESTAMP53#define MSC_TIMESTAMP 0x0554#endif5556#ifndef SYN_DROPPED57#define SYN_DROPPED 358#endif59#ifndef BTN_NORTH60#define BTN_NORTH 0x13361#endif62#ifndef BTN_WEST63#define BTN_WEST 0x13464#endif65#ifndef BTN_DPAD_UP66#define BTN_DPAD_UP 0x22067#endif68#ifndef BTN_DPAD_DOWN69#define BTN_DPAD_DOWN 0x22170#endif71#ifndef BTN_DPAD_LEFT72#define BTN_DPAD_LEFT 0x22273#endif74#ifndef BTN_DPAD_RIGHT75#define BTN_DPAD_RIGHT 0x22376#endif7778#ifndef BTN_TRIGGER_HAPPY79#define BTN_TRIGGER_HAPPY 0x2c080#define BTN_TRIGGER_HAPPY1 0x2c081#define BTN_TRIGGER_HAPPY2 0x2c182#define BTN_TRIGGER_HAPPY3 0x2c283#define BTN_TRIGGER_HAPPY4 0x2c384#define BTN_TRIGGER_HAPPY5 0x2c485#define BTN_TRIGGER_HAPPY6 0x2c586#define BTN_TRIGGER_HAPPY7 0x2c687#define BTN_TRIGGER_HAPPY8 0x2c788#define BTN_TRIGGER_HAPPY9 0x2c889#define BTN_TRIGGER_HAPPY10 0x2c990#define BTN_TRIGGER_HAPPY11 0x2ca91#define BTN_TRIGGER_HAPPY12 0x2cb92#define BTN_TRIGGER_HAPPY13 0x2cc93#define BTN_TRIGGER_HAPPY14 0x2cd94#define BTN_TRIGGER_HAPPY15 0x2ce95#define BTN_TRIGGER_HAPPY16 0x2cf96#define BTN_TRIGGER_HAPPY17 0x2d097#define BTN_TRIGGER_HAPPY18 0x2d198#define BTN_TRIGGER_HAPPY19 0x2d299#define BTN_TRIGGER_HAPPY20 0x2d3100#define BTN_TRIGGER_HAPPY21 0x2d4101#define BTN_TRIGGER_HAPPY22 0x2d5102#define BTN_TRIGGER_HAPPY23 0x2d6103#define BTN_TRIGGER_HAPPY24 0x2d7104#define BTN_TRIGGER_HAPPY25 0x2d8105#define BTN_TRIGGER_HAPPY26 0x2d9106#define BTN_TRIGGER_HAPPY27 0x2da107#define BTN_TRIGGER_HAPPY28 0x2db108#define BTN_TRIGGER_HAPPY29 0x2dc109#define BTN_TRIGGER_HAPPY30 0x2dd110#define BTN_TRIGGER_HAPPY31 0x2de111#define BTN_TRIGGER_HAPPY32 0x2df112#define BTN_TRIGGER_HAPPY33 0x2e0113#define BTN_TRIGGER_HAPPY34 0x2e1114#define BTN_TRIGGER_HAPPY35 0x2e2115#define BTN_TRIGGER_HAPPY36 0x2e3116#define BTN_TRIGGER_HAPPY37 0x2e4117#define BTN_TRIGGER_HAPPY38 0x2e5118#define BTN_TRIGGER_HAPPY39 0x2e6119#define BTN_TRIGGER_HAPPY40 0x2e7120#endif121122123#include "../../core/linux/SDL_evdev_capabilities.h"124#include "../../core/linux/SDL_udev.h"125126#if 0127#define DEBUG_INPUT_EVENTS 1128#endif129130#if 0131#define DEBUG_GAMEPAD_MAPPING 1132#endif133134typedef enum135{136ENUMERATION_UNSET,137ENUMERATION_LIBUDEV,138ENUMERATION_FALLBACK139} EnumerationMethod;140141static EnumerationMethod enumeration_method = ENUMERATION_UNSET;142143static bool IsJoystickJSNode(const char *node);144static void MaybeAddDevice(const char *path);145static void MaybeRemoveDevice(const char *path);146147// A linked list of available joysticks148typedef struct SDL_joylist_item149{150SDL_JoystickID device_instance;151char *path; // "/dev/input/event2" or whatever152Uint16 vendor;153Uint16 product;154char *name; // "SideWinder 3D Pro" or whatever155SDL_GUID guid;156dev_t devnum;157int steam_virtual_gamepad_slot;158struct joystick_hwdata *hwdata;159struct SDL_joylist_item *next;160161bool checked_mapping;162SDL_GamepadMapping *mapping;163} SDL_joylist_item;164165// A linked list of available gamepad sensors166typedef struct SDL_sensorlist_item167{168char *path; // "/dev/input/event2" or whatever169dev_t devnum;170struct joystick_hwdata *hwdata;171struct SDL_sensorlist_item *next;172} SDL_sensorlist_item;173174static bool SDL_classic_joysticks = false;175static SDL_joylist_item *SDL_joylist SDL_GUARDED_BY(SDL_joystick_lock) = NULL;176static SDL_joylist_item *SDL_joylist_tail SDL_GUARDED_BY(SDL_joystick_lock) = NULL;177static int numjoysticks SDL_GUARDED_BY(SDL_joystick_lock) = 0;178static SDL_sensorlist_item *SDL_sensorlist SDL_GUARDED_BY(SDL_joystick_lock) = NULL;179static int inotify_fd = -1;180181static Uint64 last_joy_detect_time;182static time_t last_input_dir_mtime;183184static void FixupDeviceInfoForMapping(int fd, struct input_id *inpid)185{186if (inpid->vendor == 0x045e && inpid->product == 0x0b05 && inpid->version == 0x0903) {187// This is a Microsoft Xbox One Elite Series 2 controller188unsigned long keybit[NBITS(KEY_MAX)] = { 0 };189190// The first version of the firmware duplicated all the inputs191if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&192test_bit(0x2c0, keybit)) {193// Change the version to 0x0902, so we can map it differently194inpid->version = 0x0902;195}196}197198/* For Atari vcs modern and classic controllers have the version reflecting199* firmware version, but the mapping stays stable so ignore200* version information */201if (inpid->vendor == 0x3250 && (inpid->product == 0x1001 || inpid->product == 0x1002)) {202inpid->version = 0;203}204}205206#ifdef SDL_JOYSTICK_HIDAPI207static bool IsVirtualJoystick(Uint16 vendor, Uint16 product, Uint16 version, const char *name)208{209if (vendor == USB_VENDOR_MICROSOFT && product == USB_PRODUCT_XBOX_ONE_S && version == 0 &&210SDL_strcmp(name, "Xbox One S Controller") == 0) {211// This is the virtual device created by the xow driver212return true;213}214return false;215}216#else217static bool IsVirtualJoystick(Uint16 vendor, Uint16 product, Uint16 version, const char *name)218{219return false;220}221#endif // SDL_JOYSTICK_HIDAPI222223static bool GetSteamVirtualGamepadSlot(int fd, int *slot)224{225char name[128];226227if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) > 0) {228const char *digits = SDL_strstr(name, "pad ");229if (digits) {230digits += 4;231if (SDL_isdigit(*digits)) {232*slot = SDL_atoi(digits);233return true;234}235}236}237return false;238}239240static int GuessDeviceClass(int fd)241{242unsigned long propbit[NBITS(INPUT_PROP_MAX)] = { 0 };243unsigned long evbit[NBITS(EV_MAX)] = { 0 };244unsigned long keybit[NBITS(KEY_MAX)] = { 0 };245unsigned long absbit[NBITS(ABS_MAX)] = { 0 };246unsigned long relbit[NBITS(REL_MAX)] = { 0 };247248if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||249(ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||250(ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) < 0) ||251(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {252return 0;253}254255/* This is a newer feature, so it's allowed to fail - if so, then the256* device just doesn't have any properties. */257(void) ioctl(fd, EVIOCGPROP(sizeof(propbit)), propbit);258259return SDL_EVDEV_GuessDeviceClass(propbit, evbit, absbit, keybit, relbit);260}261262static bool GuessIsJoystick(int fd)263{264if (GuessDeviceClass(fd) & SDL_UDEV_DEVICE_JOYSTICK) {265return true;266}267return false;268}269270static bool GuessIsSensor(int fd)271{272if (GuessDeviceClass(fd) & SDL_UDEV_DEVICE_ACCELEROMETER) {273return true;274}275return false;276}277278static bool IsJoystick(const char *path, int *fd, char **name_return, Uint16 *vendor_return, Uint16 *product_return, SDL_GUID *guid)279{280struct input_id inpid;281char *name;282char product_string[128];283int class = 0;284285SDL_zero(inpid);286#ifdef SDL_USE_LIBUDEV287// Opening input devices can generate synchronous device I/O, so avoid it if we can288if (SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version, &class) &&289!(class & SDL_UDEV_DEVICE_JOYSTICK)) {290return false;291}292#endif293294if (fd && *fd < 0) {295*fd = open(path, O_RDONLY | O_CLOEXEC, 0);296}297if (!fd || *fd < 0) {298return false;299}300301if (ioctl(*fd, JSIOCGNAME(sizeof(product_string)), product_string) <= 0) {302// When udev enumeration or classification, we only got joysticks here, so no need to test303if (enumeration_method != ENUMERATION_LIBUDEV && !class && !GuessIsJoystick(*fd)) {304return false;305}306307// Could have vendor and product already from udev, but should agree with evdev308if (ioctl(*fd, EVIOCGID, &inpid) < 0) {309return false;310}311312if (ioctl(*fd, EVIOCGNAME(sizeof(product_string)), product_string) < 0) {313return false;314}315}316317name = SDL_CreateJoystickName(inpid.vendor, inpid.product, NULL, product_string);318if (!name) {319return false;320}321322if (!IsVirtualJoystick(inpid.vendor, inpid.product, inpid.version, name) &&323SDL_JoystickHandledByAnotherDriver(&SDL_LINUX_JoystickDriver, inpid.vendor, inpid.product, inpid.version, name)) {324SDL_free(name);325return false;326}327328FixupDeviceInfoForMapping(*fd, &inpid);329330#ifdef DEBUG_JOYSTICK331SDL_Log("Joystick: %s, bustype = %d, vendor = 0x%.4x, product = 0x%.4x, version = %d", name, inpid.bustype, inpid.vendor, inpid.product, inpid.version);332#endif333334if (SDL_ShouldIgnoreJoystick(inpid.vendor, inpid.product, inpid.version, name)) {335SDL_free(name);336return false;337}338*name_return = name;339*vendor_return = inpid.vendor;340*product_return = inpid.product;341*guid = SDL_CreateJoystickGUID(inpid.bustype, inpid.vendor, inpid.product, inpid.version, NULL, product_string, 0, 0);342return true;343}344345static bool IsSensor(const char *path, int *fd)346{347struct input_id inpid;348int class = 0;349350SDL_zero(inpid);351#ifdef SDL_USE_LIBUDEV352// Opening input devices can generate synchronous device I/O, so avoid it if we can353if (SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version, &class) &&354!(class & SDL_UDEV_DEVICE_ACCELEROMETER)) {355return false;356}357#endif358359if (fd && *fd < 0) {360*fd = open(path, O_RDONLY | O_CLOEXEC, 0);361}362if (!fd || *fd < 0) {363return false;364}365366if (!class && !GuessIsSensor(*fd)) {367return false;368}369370if (ioctl(*fd, EVIOCGID, &inpid) < 0) {371return false;372}373374if (inpid.vendor == USB_VENDOR_NINTENDO && inpid.product == USB_PRODUCT_NINTENDO_WII_REMOTE) {375// Wii extension controls376// These may create 3 sensor devices but we only support reading from 1: ignore them377return false;378}379380return true;381}382383#ifdef SDL_USE_LIBUDEV384static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)385{386if (!devpath) {387return;388}389390switch (udev_type) {391case SDL_UDEV_DEVICEADDED:392if (!(udev_class & (SDL_UDEV_DEVICE_JOYSTICK | SDL_UDEV_DEVICE_ACCELEROMETER))) {393return;394}395if (SDL_classic_joysticks) {396if (!IsJoystickJSNode(devpath)) {397return;398}399} else {400if (IsJoystickJSNode(devpath)) {401return;402}403}404405// Wait a bit for the hidraw udev node to initialize406SDL_Delay(10);407408MaybeAddDevice(devpath);409break;410411case SDL_UDEV_DEVICEREMOVED:412MaybeRemoveDevice(devpath);413break;414415default:416break;417}418}419#endif // SDL_USE_LIBUDEV420421static void FreeJoylistItem(SDL_joylist_item *item)422{423SDL_free(item->mapping);424SDL_free(item->path);425SDL_free(item->name);426SDL_free(item);427}428429static void FreeSensorlistItem(SDL_sensorlist_item *item)430{431SDL_free(item->path);432SDL_free(item);433}434435static void MaybeAddDevice(const char *path)436{437struct stat sb;438int fd = -1;439char *name = NULL;440Uint16 vendor, product;441SDL_GUID guid;442SDL_joylist_item *item;443SDL_sensorlist_item *item_sensor;444445if (!path) {446return;447}448449fd = open(path, O_RDONLY | O_CLOEXEC, 0);450if (fd < 0) {451return;452}453454if (fstat(fd, &sb) == -1) {455close(fd);456return;457}458459SDL_LockJoysticks();460461// Check to make sure it's not already in list.462for (item = SDL_joylist; item; item = item->next) {463if (sb.st_rdev == item->devnum) {464goto done; // already have this one465}466}467for (item_sensor = SDL_sensorlist; item_sensor; item_sensor = item_sensor->next) {468if (sb.st_rdev == item_sensor->devnum) {469goto done; // already have this one470}471}472473#ifdef DEBUG_INPUT_EVENTS474SDL_Log("Checking %s", path);475#endif476477if (IsJoystick(path, &fd, &name, &vendor, &product, &guid)) {478#ifdef DEBUG_INPUT_EVENTS479SDL_Log("found joystick: %s", path);480#endif481item = (SDL_joylist_item *)SDL_calloc(1, sizeof(SDL_joylist_item));482if (!item) {483SDL_free(name);484goto done;485}486487item->devnum = sb.st_rdev;488item->steam_virtual_gamepad_slot = -1;489item->path = SDL_strdup(path);490item->vendor = vendor;491item->product = product;492item->name = name;493item->guid = guid;494495if (vendor == USB_VENDOR_VALVE &&496product == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) {497GetSteamVirtualGamepadSlot(fd, &item->steam_virtual_gamepad_slot);498}499500if ((!item->path) || (!item->name)) {501FreeJoylistItem(item);502goto done;503}504505item->device_instance = SDL_GetNextObjectID();506if (!SDL_joylist_tail) {507SDL_joylist = SDL_joylist_tail = item;508} else {509SDL_joylist_tail->next = item;510SDL_joylist_tail = item;511}512513// Need to increment the joystick count before we post the event514++numjoysticks;515516SDL_PrivateJoystickAdded(item->device_instance);517goto done;518}519520if (IsSensor(path, &fd)) {521#ifdef DEBUG_INPUT_EVENTS522SDL_Log("found sensor: %s", path);523#endif524item_sensor = (SDL_sensorlist_item *)SDL_calloc(1, sizeof(SDL_sensorlist_item));525if (!item_sensor) {526goto done;527}528item_sensor->devnum = sb.st_rdev;529item_sensor->path = SDL_strdup(path);530531if (!item_sensor->path) {532FreeSensorlistItem(item_sensor);533goto done;534}535536item_sensor->next = SDL_sensorlist;537SDL_sensorlist = item_sensor;538goto done;539}540541done:542close(fd);543SDL_UnlockJoysticks();544}545546static void RemoveJoylistItem(SDL_joylist_item *item, SDL_joylist_item *prev)547{548SDL_AssertJoysticksLocked();549550if (item->hwdata) {551item->hwdata->item = NULL;552}553554if (prev) {555prev->next = item->next;556} else {557SDL_assert(SDL_joylist == item);558SDL_joylist = item->next;559}560561if (item == SDL_joylist_tail) {562SDL_joylist_tail = prev;563}564565// Need to decrement the joystick count before we post the event566--numjoysticks;567568SDL_PrivateJoystickRemoved(item->device_instance);569FreeJoylistItem(item);570}571572static void RemoveSensorlistItem(SDL_sensorlist_item *item, SDL_sensorlist_item *prev)573{574SDL_AssertJoysticksLocked();575576if (item->hwdata) {577item->hwdata->item_sensor = NULL;578}579580if (prev) {581prev->next = item->next;582} else {583SDL_assert(SDL_sensorlist == item);584SDL_sensorlist = item->next;585}586587/* Do not call SDL_PrivateJoystickRemoved here as RemoveJoylistItem will do it,588* assuming both sensor and joy item are removed at the same time */589FreeSensorlistItem(item);590}591592static void MaybeRemoveDevice(const char *path)593{594SDL_joylist_item *item;595SDL_joylist_item *prev = NULL;596SDL_sensorlist_item *item_sensor;597SDL_sensorlist_item *prev_sensor = NULL;598599if (!path) {600return;601}602603SDL_LockJoysticks();604for (item = SDL_joylist; item; item = item->next) {605// found it, remove it.606if (SDL_strcmp(path, item->path) == 0) {607RemoveJoylistItem(item, prev);608goto done;609}610prev = item;611}612for (item_sensor = SDL_sensorlist; item_sensor; item_sensor = item_sensor->next) {613// found it, remove it.614if (SDL_strcmp(path, item_sensor->path) == 0) {615RemoveSensorlistItem(item_sensor, prev_sensor);616goto done;617}618prev_sensor = item_sensor;619}620done:621SDL_UnlockJoysticks();622}623624static void HandlePendingRemovals(void)625{626SDL_joylist_item *prev = NULL;627SDL_joylist_item *item = NULL;628SDL_sensorlist_item *prev_sensor = NULL;629SDL_sensorlist_item *item_sensor = NULL;630631SDL_AssertJoysticksLocked();632633item = SDL_joylist;634while (item) {635if (item->hwdata && item->hwdata->gone) {636RemoveJoylistItem(item, prev);637638if (prev) {639item = prev->next;640} else {641item = SDL_joylist;642}643} else {644prev = item;645item = item->next;646}647}648649item_sensor = SDL_sensorlist;650while (item_sensor) {651if (item_sensor->hwdata && item_sensor->hwdata->sensor_gone) {652RemoveSensorlistItem(item_sensor, prev_sensor);653654if (prev_sensor) {655item_sensor = prev_sensor->next;656} else {657item_sensor = SDL_sensorlist;658}659} else {660prev_sensor = item_sensor;661item_sensor = item_sensor->next;662}663}664}665666static bool StrIsInteger(const char *string)667{668const char *p;669670if (*string == '\0') {671return false;672}673674for (p = string; *p != '\0'; p++) {675if (*p < '0' || *p > '9') {676return false;677}678}679680return true;681}682683static bool IsJoystickJSNode(const char *node)684{685const char *last_slash = SDL_strrchr(node, '/');686if (last_slash) {687node = last_slash + 1;688}689return SDL_startswith(node, "js") && StrIsInteger(node + 2);690}691692static bool IsJoystickEventNode(const char *node)693{694const char *last_slash = SDL_strrchr(node, '/');695if (last_slash) {696node = last_slash + 1;697}698return SDL_startswith(node, "event") && StrIsInteger(node + 5);699}700701static bool IsJoystickDeviceNode(const char *node)702{703if (SDL_classic_joysticks) {704return IsJoystickJSNode(node);705} else {706return IsJoystickEventNode(node);707}708}709710#ifdef HAVE_INOTIFY711#ifdef HAVE_INOTIFY_INIT1712static int SDL_inotify_init1(void)713{714return inotify_init1(IN_NONBLOCK | IN_CLOEXEC);715}716#else717static int SDL_inotify_init1(void)718{719int fd = inotify_init();720if (fd < 0) {721return -1;722}723fcntl(fd, F_SETFL, O_NONBLOCK);724fcntl(fd, F_SETFD, FD_CLOEXEC);725return fd;726}727#endif728729static void LINUX_InotifyJoystickDetect(void)730{731union732{733struct inotify_event event;734char storage[4096];735char enough_for_inotify[sizeof(struct inotify_event) + NAME_MAX + 1];736} buf;737ssize_t bytes;738size_t remain = 0;739size_t len;740char path[PATH_MAX];741742bytes = read(inotify_fd, &buf, sizeof(buf));743744if (bytes > 0) {745remain = (size_t)bytes;746}747748while (remain > 0) {749if (buf.event.len > 0) {750if (IsJoystickDeviceNode(buf.event.name)) {751(void)SDL_snprintf(path, SDL_arraysize(path), "/dev/input/%s", buf.event.name);752753if (buf.event.mask & (IN_CREATE | IN_MOVED_TO | IN_ATTRIB)) {754MaybeAddDevice(path);755} else if (buf.event.mask & (IN_DELETE | IN_MOVED_FROM)) {756MaybeRemoveDevice(path);757}758}759}760761len = sizeof(struct inotify_event) + buf.event.len;762remain -= len;763764if (remain != 0) {765SDL_memmove(&buf.storage[0], &buf.storage[len], remain);766}767}768}769#endif // HAVE_INOTIFY770771static int get_event_joystick_index(int event)772{773int joystick_index = -1;774int i, count;775struct dirent **entries = NULL;776char path[PATH_MAX];777778(void)SDL_snprintf(path, SDL_arraysize(path), "/sys/class/input/event%d/device", event);779count = scandir(path, &entries, NULL, alphasort);780for (i = 0; i < count; ++i) {781if (SDL_strncmp(entries[i]->d_name, "js", 2) == 0) {782joystick_index = SDL_atoi(entries[i]->d_name + 2);783}784free(entries[i]); // This should NOT be SDL_free()785}786free(entries); // This should NOT be SDL_free()787788return joystick_index;789}790791/* Detect devices by reading /dev/input. In the inotify code path we792* have to do this the first time, to detect devices that already existed793* before we started; in the non-inotify code path we do this repeatedly794* (polling). */795static int filter_entries(const struct dirent *entry)796{797return IsJoystickDeviceNode(entry->d_name);798}799static int SDLCALL sort_entries(const void *_a, const void *_b)800{801const struct dirent **a = (const struct dirent **)_a;802const struct dirent **b = (const struct dirent **)_b;803int numA, numB;804int offset;805806if (SDL_classic_joysticks) {807offset = 2; // strlen("js")808numA = SDL_atoi((*a)->d_name + offset);809numB = SDL_atoi((*b)->d_name + offset);810} else {811offset = 5; // strlen("event")812numA = SDL_atoi((*a)->d_name + offset);813numB = SDL_atoi((*b)->d_name + offset);814815// See if we can get the joystick ordering816{817int jsA = get_event_joystick_index(numA);818int jsB = get_event_joystick_index(numB);819if (jsA >= 0 && jsB >= 0) {820numA = jsA;821numB = jsB;822} else if (jsA >= 0) {823return -1;824} else if (jsB >= 0) {825return 1;826}827}828}829return numA - numB;830}831832typedef struct833{834char *path;835int slot;836} VirtualGamepadEntry;837838static int SDLCALL sort_virtual_gamepads(const void *_a, const void *_b)839{840const VirtualGamepadEntry *a = (const VirtualGamepadEntry *)_a;841const VirtualGamepadEntry *b = (const VirtualGamepadEntry *)_b;842return a->slot - b->slot;843}844845static void LINUX_ScanSteamVirtualGamepads(void)846{847int i, count;848int fd;849struct dirent **entries = NULL;850char path[PATH_MAX];851struct input_id inpid;852int num_virtual_gamepads = 0;853int virtual_gamepad_slot;854VirtualGamepadEntry *virtual_gamepads = NULL;855#ifdef SDL_USE_LIBUDEV856int class;857#endif858859count = scandir("/dev/input", &entries, filter_entries, NULL);860for (i = 0; i < count; ++i) {861(void)SDL_snprintf(path, SDL_arraysize(path), "/dev/input/%s", entries[i]->d_name);862863#ifdef SDL_USE_LIBUDEV864// Opening input devices can generate synchronous device I/O, so avoid it if we can865class = 0;866SDL_zero(inpid);867if (SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version, &class) &&868(inpid.vendor != USB_VENDOR_VALVE || inpid.product != USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD)) {869free(entries[i]); // This should NOT be SDL_free()870continue;871}872#endif873fd = open(path, O_RDONLY | O_CLOEXEC, 0);874if (fd >= 0) {875if (ioctl(fd, EVIOCGID, &inpid) == 0 &&876inpid.vendor == USB_VENDOR_VALVE &&877inpid.product == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD &&878GetSteamVirtualGamepadSlot(fd, &virtual_gamepad_slot)) {879VirtualGamepadEntry *new_virtual_gamepads = (VirtualGamepadEntry *)SDL_realloc(virtual_gamepads, (num_virtual_gamepads + 1) * sizeof(*virtual_gamepads));880if (new_virtual_gamepads) {881VirtualGamepadEntry *entry = &new_virtual_gamepads[num_virtual_gamepads];882entry->path = SDL_strdup(path);883entry->slot = virtual_gamepad_slot;884if (entry->path) {885virtual_gamepads = new_virtual_gamepads;886++num_virtual_gamepads;887} else {888SDL_free(entry->path);889SDL_free(new_virtual_gamepads);890}891}892}893close(fd);894}895free(entries[i]); // This should NOT be SDL_free()896}897free(entries); // This should NOT be SDL_free()898899if (num_virtual_gamepads > 1) {900SDL_qsort(virtual_gamepads, num_virtual_gamepads, sizeof(*virtual_gamepads), sort_virtual_gamepads);901}902for (i = 0; i < num_virtual_gamepads; ++i) {903MaybeAddDevice(virtual_gamepads[i].path);904SDL_free(virtual_gamepads[i].path);905}906SDL_free(virtual_gamepads);907}908909static void LINUX_ScanInputDevices(void)910{911int i, count;912struct dirent **entries = NULL;913char path[PATH_MAX];914915count = scandir("/dev/input", &entries, filter_entries, NULL);916if (count > 1) {917SDL_qsort(entries, count, sizeof(*entries), sort_entries);918}919for (i = 0; i < count; ++i) {920(void)SDL_snprintf(path, SDL_arraysize(path), "/dev/input/%s", entries[i]->d_name);921MaybeAddDevice(path);922923free(entries[i]); // This should NOT be SDL_free()924}925free(entries); // This should NOT be SDL_free()926}927928static void LINUX_FallbackJoystickDetect(void)929{930const Uint32 SDL_JOY_DETECT_INTERVAL_MS = 3000; // Update every 3 seconds931Uint64 now = SDL_GetTicks();932933if (!last_joy_detect_time || now >= (last_joy_detect_time + SDL_JOY_DETECT_INTERVAL_MS)) {934struct stat sb;935936// Opening input devices can generate synchronous device I/O, so avoid it if we can937if (stat("/dev/input", &sb) == 0 && sb.st_mtime != last_input_dir_mtime) {938// Look for Steam virtual gamepads first, and sort by Steam controller slot939LINUX_ScanSteamVirtualGamepads();940941LINUX_ScanInputDevices();942943last_input_dir_mtime = sb.st_mtime;944}945946last_joy_detect_time = now;947}948}949950static void LINUX_JoystickDetect(void)951{952#ifdef SDL_USE_LIBUDEV953if (enumeration_method == ENUMERATION_LIBUDEV) {954SDL_UDEV_Poll();955} else956#endif957#ifdef HAVE_INOTIFY958if (inotify_fd >= 0 && last_joy_detect_time != 0) {959LINUX_InotifyJoystickDetect();960} else961#endif962{963LINUX_FallbackJoystickDetect();964}965966HandlePendingRemovals();967}968969static bool LINUX_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)970{971// We don't override any other drivers972return false;973}974975static bool LINUX_JoystickInit(void)976{977const char *devices = SDL_GetHint(SDL_HINT_JOYSTICK_DEVICE);978#ifdef SDL_USE_LIBUDEV979bool udev_initialized = SDL_UDEV_Init();980#endif981982SDL_classic_joysticks = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_LINUX_CLASSIC, false);983984enumeration_method = ENUMERATION_UNSET;985986// First see if the user specified one or more joysticks to use987if (devices) {988char *envcopy, *envpath, *delim;989envcopy = SDL_strdup(devices);990envpath = envcopy;991while (envpath) {992delim = SDL_strchr(envpath, ':');993if (delim) {994*delim++ = '\0';995}996MaybeAddDevice(envpath);997envpath = delim;998}999SDL_free(envcopy);1000}10011002// Force immediate joystick detection if using fallback1003last_joy_detect_time = 0;1004last_input_dir_mtime = 0;10051006// Manually scan first, since we sort by device number and udev doesn't1007LINUX_JoystickDetect();10081009#ifdef SDL_USE_LIBUDEV1010if (enumeration_method == ENUMERATION_UNSET) {1011if (SDL_GetHintBoolean("SDL_JOYSTICK_DISABLE_UDEV", false)) {1012SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,1013"udev disabled by SDL_JOYSTICK_DISABLE_UDEV");1014enumeration_method = ENUMERATION_FALLBACK;1015} else if (SDL_GetSandbox() != SDL_SANDBOX_NONE) {1016SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,1017"Container detected, disabling udev integration");1018enumeration_method = ENUMERATION_FALLBACK;10191020} else {1021SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,1022"Using udev for joystick device discovery");1023enumeration_method = ENUMERATION_LIBUDEV;1024}1025}10261027if (enumeration_method == ENUMERATION_LIBUDEV) {1028if (udev_initialized) {1029// Set up the udev callback1030if (!SDL_UDEV_AddCallback(joystick_udev_callback)) {1031SDL_UDEV_Quit();1032return SDL_SetError("Could not set up joystick <-> udev callback");1033}10341035// Force a scan to build the initial device list1036SDL_UDEV_Scan();1037} else {1038SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,1039"udev init failed, disabling udev integration");1040enumeration_method = ENUMERATION_FALLBACK;1041}1042} else {1043if (udev_initialized) {1044SDL_UDEV_Quit();1045}1046}1047#endif10481049if (enumeration_method != ENUMERATION_LIBUDEV) {1050#ifdef HAVE_INOTIFY1051inotify_fd = SDL_inotify_init1();10521053if (inotify_fd < 0) {1054SDL_LogWarn(SDL_LOG_CATEGORY_INPUT,1055"Unable to initialize inotify, falling back to polling: %s",1056strerror(errno));1057} else {1058/* We need to watch for attribute changes in addition to1059* creation, because when a device is first created, it has1060* permissions that we can't read. When udev chmods it to1061* something that we maybe *can* read, we'll get an1062* IN_ATTRIB event to tell us. */1063if (inotify_add_watch(inotify_fd, "/dev/input",1064IN_CREATE | IN_DELETE | IN_MOVE | IN_ATTRIB) < 0) {1065close(inotify_fd);1066inotify_fd = -1;1067SDL_LogWarn(SDL_LOG_CATEGORY_INPUT,1068"Unable to add inotify watch, falling back to polling: %s",1069strerror(errno));1070}1071}1072#endif // HAVE_INOTIFY1073}10741075return true;1076}10771078static int LINUX_JoystickGetCount(void)1079{1080SDL_AssertJoysticksLocked();10811082return numjoysticks;1083}10841085static SDL_joylist_item *GetJoystickByDevIndex(int device_index)1086{1087SDL_joylist_item *item;10881089SDL_AssertJoysticksLocked();10901091if ((device_index < 0) || (device_index >= numjoysticks)) {1092return NULL;1093}10941095item = SDL_joylist;1096while (device_index > 0) {1097SDL_assert(item != NULL);1098device_index--;1099item = item->next;1100}11011102return item;1103}11041105static const char *LINUX_JoystickGetDeviceName(int device_index)1106{1107return GetJoystickByDevIndex(device_index)->name;1108}11091110static const char *LINUX_JoystickGetDevicePath(int device_index)1111{1112return GetJoystickByDevIndex(device_index)->path;1113}11141115static int LINUX_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)1116{1117return GetJoystickByDevIndex(device_index)->steam_virtual_gamepad_slot;1118}11191120static int LINUX_JoystickGetDevicePlayerIndex(int device_index)1121{1122return -1;1123}11241125static void LINUX_JoystickSetDevicePlayerIndex(int device_index, int player_index)1126{1127}11281129static SDL_GUID LINUX_JoystickGetDeviceGUID(int device_index)1130{1131return GetJoystickByDevIndex(device_index)->guid;1132}11331134// Function to perform the mapping from device index to the instance id for this index1135static SDL_JoystickID LINUX_JoystickGetDeviceInstanceID(int device_index)1136{1137return GetJoystickByDevIndex(device_index)->device_instance;1138}11391140static bool allocate_balldata(SDL_Joystick *joystick)1141{1142joystick->hwdata->balls =1143(struct hwdata_ball *)SDL_calloc(joystick->nballs, sizeof(struct hwdata_ball));1144if (joystick->hwdata->balls == NULL) {1145return false;1146}1147return true;1148}11491150static bool allocate_hatdata(SDL_Joystick *joystick)1151{1152int i;11531154SDL_AssertJoysticksLocked();11551156joystick->hwdata->hats =1157(struct hwdata_hat *)SDL_malloc(joystick->nhats *1158sizeof(struct hwdata_hat));1159if (!joystick->hwdata->hats) {1160return false;1161}1162for (i = 0; i < joystick->nhats; ++i) {1163joystick->hwdata->hats[i].axis[0] = 1;1164joystick->hwdata->hats[i].axis[1] = 1;1165}1166return true;1167}11681169static bool GuessIfAxesAreDigitalHat(struct input_absinfo *absinfo_x, struct input_absinfo *absinfo_y)1170{1171/* A "hat" is assumed to be a digital input with at most 9 possible states1172* (3 per axis: negative/zero/positive), as opposed to a true "axis" which1173* can report a continuous range of possible values. Unfortunately the Linux1174* joystick interface makes no distinction between digital hat axes and any1175* other continuous analog axis, so we have to guess. */11761177// If both axes are missing, they're not anything.1178if (!absinfo_x && !absinfo_y) {1179return false;1180}11811182// If the hint says so, treat all hats as digital.1183if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_LINUX_DIGITAL_HATS, false)) {1184return true;1185}11861187// If both axes have ranges constrained between -1 and 1, they're definitely digital.1188if ((!absinfo_x || (absinfo_x->minimum == -1 && absinfo_x->maximum == 1)) && (!absinfo_y || (absinfo_y->minimum == -1 && absinfo_y->maximum == 1))) {1189return true;1190}11911192// If both axes lack fuzz, flat, and resolution values, they're probably digital.1193if ((!absinfo_x || (!absinfo_x->fuzz && !absinfo_x->flat && !absinfo_x->resolution)) && (!absinfo_y || (!absinfo_y->fuzz && !absinfo_y->flat && !absinfo_y->resolution))) {1194return true;1195}11961197// Otherwise, treat them as analog.1198return false;1199}12001201static void ConfigJoystick(SDL_Joystick *joystick, int fd, int fd_sensor)1202{1203int i, t;1204unsigned long keybit[NBITS(KEY_MAX)] = { 0 };1205unsigned long absbit[NBITS(ABS_MAX)] = { 0 };1206unsigned long relbit[NBITS(REL_MAX)] = { 0 };1207unsigned long ffbit[NBITS(FF_MAX)] = { 0 };1208Uint8 key_pam_size, abs_pam_size;1209bool use_deadzones = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_LINUX_DEADZONES, false);1210bool use_hat_deadzones = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_LINUX_HAT_DEADZONES, true);12111212SDL_AssertJoysticksLocked();12131214// See if this device uses the new unified event API1215if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&1216(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) &&1217(ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0)) {12181219// Get the number of buttons, axes, and other thingamajigs1220for (i = BTN_JOYSTICK; i < KEY_MAX; ++i) {1221if (test_bit(i, keybit)) {1222#ifdef DEBUG_INPUT_EVENTS1223SDL_Log("Joystick has button: 0x%x", i);1224#endif1225joystick->hwdata->key_map[i] = joystick->nbuttons;1226joystick->hwdata->has_key[i] = true;1227++joystick->nbuttons;1228}1229}1230for (i = 0; i < BTN_JOYSTICK; ++i) {1231if (test_bit(i, keybit)) {1232#ifdef DEBUG_INPUT_EVENTS1233SDL_Log("Joystick has button: 0x%x", i);1234#endif1235joystick->hwdata->key_map[i] = joystick->nbuttons;1236joystick->hwdata->has_key[i] = true;1237++joystick->nbuttons;1238}1239}1240for (i = ABS_HAT0X; i <= ABS_HAT3Y; i += 2) {1241int hat_x = -1;1242int hat_y = -1;1243struct input_absinfo absinfo_x;1244struct input_absinfo absinfo_y;1245if (test_bit(i, absbit)) {1246hat_x = ioctl(fd, EVIOCGABS(i), &absinfo_x);1247}1248if (test_bit(i + 1, absbit)) {1249hat_y = ioctl(fd, EVIOCGABS(i + 1), &absinfo_y);1250}1251if (GuessIfAxesAreDigitalHat((hat_x < 0 ? (void *)0 : &absinfo_x),1252(hat_y < 0 ? (void *)0 : &absinfo_y))) {1253const int hat_index = (i - ABS_HAT0X) / 2;1254struct hat_axis_correct *correct = &joystick->hwdata->hat_correct[hat_index];1255#ifdef DEBUG_INPUT_EVENTS1256SDL_Log("Joystick has digital hat: #%d", hat_index);1257if (hat_x >= 0) {1258SDL_Log("X Values = { val:%d, min:%d, max:%d, fuzz:%d, flat:%d, res:%d }",1259absinfo_x.value, absinfo_x.minimum, absinfo_x.maximum,1260absinfo_x.fuzz, absinfo_x.flat, absinfo_x.resolution);1261}1262if (hat_y >= 0) {1263SDL_Log("Y Values = { val:%d, min:%d, max:%d, fuzz:%d, flat:%d, res:%d }",1264absinfo_y.value, absinfo_y.minimum, absinfo_y.maximum,1265absinfo_y.fuzz, absinfo_y.flat, absinfo_y.resolution);1266}1267#endif // DEBUG_INPUT_EVENTS1268joystick->hwdata->hats_indices[hat_index] = joystick->nhats;1269joystick->hwdata->has_hat[hat_index] = true;1270correct->use_deadzones = use_hat_deadzones;1271correct->minimum[0] = (hat_x < 0) ? -1 : absinfo_x.minimum;1272correct->maximum[0] = (hat_x < 0) ? 1 : absinfo_x.maximum;1273correct->minimum[1] = (hat_y < 0) ? -1 : absinfo_y.minimum;1274correct->maximum[1] = (hat_y < 0) ? 1 : absinfo_y.maximum;1275++joystick->nhats;1276}1277}1278for (i = 0; i < ABS_MAX; ++i) {1279// Skip digital hats1280if (i >= ABS_HAT0X && i <= ABS_HAT3Y && joystick->hwdata->has_hat[(i - ABS_HAT0X) / 2]) {1281continue;1282}1283if (test_bit(i, absbit)) {1284struct input_absinfo absinfo;1285struct axis_correct *correct = &joystick->hwdata->abs_correct[i];12861287if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {1288continue;1289}1290#ifdef DEBUG_INPUT_EVENTS1291SDL_Log("Joystick has absolute axis: 0x%.2x", i);1292SDL_Log("Values = { val:%d, min:%d, max:%d, fuzz:%d, flat:%d, res:%d }",1293absinfo.value, absinfo.minimum, absinfo.maximum,1294absinfo.fuzz, absinfo.flat, absinfo.resolution);1295#endif // DEBUG_INPUT_EVENTS1296joystick->hwdata->abs_map[i] = joystick->naxes;1297joystick->hwdata->has_abs[i] = true;12981299correct->minimum = absinfo.minimum;1300correct->maximum = absinfo.maximum;1301if (correct->minimum != correct->maximum) {1302if (use_deadzones) {1303correct->use_deadzones = true;1304correct->coef[0] = (absinfo.maximum + absinfo.minimum) - 2 * absinfo.flat;1305correct->coef[1] = (absinfo.maximum + absinfo.minimum) + 2 * absinfo.flat;1306t = ((absinfo.maximum - absinfo.minimum) - 4 * absinfo.flat);1307if (t != 0) {1308correct->coef[2] = (1 << 28) / t;1309} else {1310correct->coef[2] = 0;1311}1312} else {1313float value_range = (correct->maximum - correct->minimum);1314float output_range = (SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN);13151316correct->scale = (output_range / value_range);1317}1318}1319++joystick->naxes;1320}1321}1322if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) {1323++joystick->nballs;1324}13251326} else if ((ioctl(fd, JSIOCGBUTTONS, &key_pam_size, sizeof(key_pam_size)) >= 0) &&1327(ioctl(fd, JSIOCGAXES, &abs_pam_size, sizeof(abs_pam_size)) >= 0)) {1328size_t len;13291330joystick->hwdata->classic = true;13311332len = (KEY_MAX - BTN_MISC + 1) * sizeof(*joystick->hwdata->key_pam);1333joystick->hwdata->key_pam = (Uint16 *)SDL_calloc(1, len);1334if (joystick->hwdata->key_pam) {1335if (ioctl(fd, JSIOCGBTNMAP, joystick->hwdata->key_pam, len) < 0) {1336SDL_free(joystick->hwdata->key_pam);1337joystick->hwdata->key_pam = NULL;1338key_pam_size = 0;1339}1340} else {1341key_pam_size = 0;1342}1343for (i = 0; i < key_pam_size; ++i) {1344Uint16 code = joystick->hwdata->key_pam[i];1345#ifdef DEBUG_INPUT_EVENTS1346SDL_Log("Joystick has button: 0x%x", code);1347#endif1348joystick->hwdata->key_map[code] = joystick->nbuttons;1349joystick->hwdata->has_key[code] = true;1350++joystick->nbuttons;1351}13521353len = ABS_CNT * sizeof(*joystick->hwdata->abs_pam);1354joystick->hwdata->abs_pam = (Uint8 *)SDL_calloc(1, len);1355if (joystick->hwdata->abs_pam) {1356if (ioctl(fd, JSIOCGAXMAP, joystick->hwdata->abs_pam, len) < 0) {1357SDL_free(joystick->hwdata->abs_pam);1358joystick->hwdata->abs_pam = NULL;1359abs_pam_size = 0;1360}1361} else {1362abs_pam_size = 0;1363}1364for (i = 0; i < abs_pam_size; ++i) {1365Uint8 code = joystick->hwdata->abs_pam[i];13661367// TODO: is there any way to detect analog hats in advance via this API?1368if (code >= ABS_HAT0X && code <= ABS_HAT3Y) {1369int hat_index = (code - ABS_HAT0X) / 2;1370if (!joystick->hwdata->has_hat[hat_index]) {1371#ifdef DEBUG_INPUT_EVENTS1372SDL_Log("Joystick has digital hat: #%d", hat_index);1373#endif1374joystick->hwdata->hats_indices[hat_index] = joystick->nhats++;1375joystick->hwdata->has_hat[hat_index] = true;1376joystick->hwdata->hat_correct[hat_index].minimum[0] = -1;1377joystick->hwdata->hat_correct[hat_index].maximum[0] = 1;1378joystick->hwdata->hat_correct[hat_index].minimum[1] = -1;1379joystick->hwdata->hat_correct[hat_index].maximum[1] = 1;1380}1381} else {1382#ifdef DEBUG_INPUT_EVENTS1383SDL_Log("Joystick has absolute axis: 0x%.2x", code);1384#endif1385joystick->hwdata->abs_map[code] = joystick->naxes;1386joystick->hwdata->has_abs[code] = true;1387++joystick->naxes;1388}1389}1390}13911392// Sensors are only available through the new unified event API1393if (fd_sensor >= 0 && (ioctl(fd_sensor, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0)) {1394if (test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit) && test_bit(ABS_Z, absbit)) {1395joystick->hwdata->has_accelerometer = true;1396for (i = 0; i < 3; ++i) {1397struct input_absinfo absinfo;1398if (ioctl(fd_sensor, EVIOCGABS(ABS_X + i), &absinfo) < 0) {1399joystick->hwdata->has_accelerometer = false;1400break; // do not report an accelerometer if we can't read all axes1401}1402joystick->hwdata->accelerometer_scale[i] = absinfo.resolution;1403#ifdef DEBUG_INPUT_EVENTS1404SDL_Log("Joystick has accelerometer axis: 0x%.2x", ABS_X + i);1405SDL_Log("Values = { val:%d, min:%d, max:%d, fuzz:%d, flat:%d, res:%d }",1406absinfo.value, absinfo.minimum, absinfo.maximum,1407absinfo.fuzz, absinfo.flat, absinfo.resolution);1408#endif // DEBUG_INPUT_EVENTS1409}1410}14111412if (test_bit(ABS_RX, absbit) && test_bit(ABS_RY, absbit) && test_bit(ABS_RZ, absbit)) {1413joystick->hwdata->has_gyro = true;1414for (i = 0; i < 3; ++i) {1415struct input_absinfo absinfo;1416if (ioctl(fd_sensor, EVIOCGABS(ABS_RX + i), &absinfo) < 0) {1417joystick->hwdata->has_gyro = false;1418break; // do not report a gyro if we can't read all axes1419}1420joystick->hwdata->gyro_scale[i] = absinfo.resolution;1421#ifdef DEBUG_INPUT_EVENTS1422SDL_Log("Joystick has gyro axis: 0x%.2x", ABS_RX + i);1423SDL_Log("Values = { val:%d, min:%d, max:%d, fuzz:%d, flat:%d, res:%d }",1424absinfo.value, absinfo.minimum, absinfo.maximum,1425absinfo.fuzz, absinfo.flat, absinfo.resolution);1426#endif // DEBUG_INPUT_EVENTS1427}1428}1429}14301431// Allocate data to keep track of these thingamajigs1432if (joystick->nballs > 0) {1433if (!allocate_balldata(joystick)) {1434joystick->nballs = 0;1435}1436}1437if (joystick->nhats > 0) {1438if (!allocate_hatdata(joystick)) {1439joystick->nhats = 0;1440}1441}14421443if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) >= 0) {1444if (test_bit(FF_RUMBLE, ffbit)) {1445joystick->hwdata->ff_rumble = true;1446}1447if (test_bit(FF_SINE, ffbit)) {1448joystick->hwdata->ff_sine = true;1449}1450}1451}14521453/* This is used to do the heavy lifting for LINUX_JoystickOpen and1454also LINUX_JoystickGetGamepadMapping, so we can query the hardware1455without adding an opened SDL_Joystick object to the system.1456This expects `joystick->hwdata` to be allocated and will not free it1457on error. Returns -1 on error, 0 on success. */1458static bool PrepareJoystickHwdata(SDL_Joystick *joystick, SDL_joylist_item *item, SDL_sensorlist_item *item_sensor)1459{1460SDL_AssertJoysticksLocked();14611462joystick->hwdata->item = item;1463joystick->hwdata->item_sensor = item_sensor;1464joystick->hwdata->guid = item->guid;1465joystick->hwdata->effect.id = -1;1466SDL_memset(joystick->hwdata->key_map, 0xFF, sizeof(joystick->hwdata->key_map));1467SDL_memset(joystick->hwdata->abs_map, 0xFF, sizeof(joystick->hwdata->abs_map));14681469int fd = -1, fd_sensor = -1;1470// Try read-write first, so we can do rumble1471fd = open(item->path, O_RDWR | O_CLOEXEC, 0);1472if (fd < 0) {1473// Try read-only again, at least we'll get events in this case1474fd = open(item->path, O_RDONLY | O_CLOEXEC, 0);1475}1476if (fd < 0) {1477return SDL_SetError("Unable to open %s", item->path);1478}1479// If opening sensor fail, continue with buttons and axes only1480if (item_sensor) {1481fd_sensor = open(item_sensor->path, O_RDONLY | O_CLOEXEC, 0);1482}14831484joystick->hwdata->fd = fd;1485joystick->hwdata->fd_sensor = fd_sensor;1486joystick->hwdata->fname = SDL_strdup(item->path);1487if (!joystick->hwdata->fname) {1488close(fd);1489if (fd_sensor >= 0) {1490close(fd_sensor);1491}1492return false;1493}14941495// Set the joystick to non-blocking read mode1496fcntl(fd, F_SETFL, O_NONBLOCK);1497if (fd_sensor >= 0) {1498fcntl(fd_sensor, F_SETFL, O_NONBLOCK);1499}15001501// Get the number of buttons and axes on the joystick1502ConfigJoystick(joystick, fd, fd_sensor);1503return true;1504}15051506static SDL_sensorlist_item *GetSensor(SDL_joylist_item *item)1507{1508SDL_sensorlist_item *item_sensor;1509char uniq_item[128];1510int fd_item = -1;15111512SDL_AssertJoysticksLocked();15131514if (!item || !SDL_sensorlist) {1515return NULL;1516}15171518SDL_memset(uniq_item, 0, sizeof(uniq_item));1519fd_item = open(item->path, O_RDONLY | O_CLOEXEC, 0);1520if (fd_item < 0) {1521return NULL;1522}1523if (ioctl(fd_item, EVIOCGUNIQ(sizeof(uniq_item) - 1), &uniq_item) < 0) {1524close(fd_item);1525return NULL;1526}1527close(fd_item);1528#ifdef DEBUG_INPUT_EVENTS1529SDL_Log("Joystick UNIQ: %s", uniq_item);1530#endif // DEBUG_INPUT_EVENTS15311532for (item_sensor = SDL_sensorlist; item_sensor; item_sensor = item_sensor->next) {1533char uniq_sensor[128];1534int fd_sensor = -1;1535if (item_sensor->hwdata) {1536// already associated with another joystick1537continue;1538}15391540SDL_memset(uniq_sensor, 0, sizeof(uniq_sensor));1541fd_sensor = open(item_sensor->path, O_RDONLY | O_CLOEXEC, 0);1542if (fd_sensor < 0) {1543continue;1544}1545if (ioctl(fd_sensor, EVIOCGUNIQ(sizeof(uniq_sensor) - 1), &uniq_sensor) < 0) {1546close(fd_sensor);1547continue;1548}1549close(fd_sensor);1550#ifdef DEBUG_INPUT_EVENTS1551SDL_Log("Sensor UNIQ: %s", uniq_sensor);1552#endif // DEBUG_INPUT_EVENTS15531554if (SDL_strcmp(uniq_item, uniq_sensor) == 0) {1555return item_sensor;1556}1557}1558return NULL;1559}15601561static bool LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)1562{1563SDL_joylist_item *item;1564SDL_sensorlist_item *item_sensor;15651566SDL_AssertJoysticksLocked();15671568item = GetJoystickByDevIndex(device_index);1569if (!item) {1570return SDL_SetError("No such device");1571}15721573joystick->hwdata = (struct joystick_hwdata *)1574SDL_calloc(1, sizeof(*joystick->hwdata));1575if (!joystick->hwdata) {1576return false;1577}15781579item_sensor = GetSensor(item);1580if (!PrepareJoystickHwdata(joystick, item, item_sensor)) {1581SDL_free(joystick->hwdata);1582joystick->hwdata = NULL;1583return false; // SDL_SetError will already have been called1584}15851586SDL_assert(item->hwdata == NULL);1587SDL_assert(!item_sensor || item_sensor->hwdata == NULL);1588item->hwdata = joystick->hwdata;1589if (item_sensor) {1590item_sensor->hwdata = joystick->hwdata;1591}15921593#ifdef SDL_USE_LIBUDEV1594const char *serial = NULL;1595if (SDL_UDEV_GetProductSerial(item->path, &serial)) {1596joystick->serial = SDL_strdup(serial);1597}1598#endif15991600// mark joystick as fresh and ready1601joystick->hwdata->fresh = true;16021603if (joystick->hwdata->has_gyro) {1604SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 0.0f);1605}1606if (joystick->hwdata->has_accelerometer) {1607SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 0.0f);1608}1609if (joystick->hwdata->fd_sensor >= 0) {1610// Don't keep fd_sensor opened while sensor is disabled1611close(joystick->hwdata->fd_sensor);1612joystick->hwdata->fd_sensor = -1;1613}16141615if (joystick->hwdata->ff_rumble || joystick->hwdata->ff_sine) {1616SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);1617}1618return true;1619}16201621static bool LINUX_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)1622{1623struct input_event event;16241625SDL_AssertJoysticksLocked();16261627if (joystick->hwdata->ff_rumble) {1628struct ff_effect *effect = &joystick->hwdata->effect;16291630effect->type = FF_RUMBLE;1631effect->replay.length = SDL_MAX_RUMBLE_DURATION_MS;1632effect->u.rumble.strong_magnitude = low_frequency_rumble;1633effect->u.rumble.weak_magnitude = high_frequency_rumble;1634} else if (joystick->hwdata->ff_sine) {1635// Scale and average the two rumble strengths1636Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);1637struct ff_effect *effect = &joystick->hwdata->effect;16381639effect->type = FF_PERIODIC;1640effect->replay.length = SDL_MAX_RUMBLE_DURATION_MS;1641effect->u.periodic.waveform = FF_SINE;1642effect->u.periodic.magnitude = magnitude;1643} else {1644return SDL_Unsupported();1645}16461647if (ioctl(joystick->hwdata->fd, EVIOCSFF, &joystick->hwdata->effect) < 0) {1648// The kernel may have lost this effect, try to allocate a new one1649joystick->hwdata->effect.id = -1;1650if (ioctl(joystick->hwdata->fd, EVIOCSFF, &joystick->hwdata->effect) < 0) {1651return SDL_SetError("Couldn't update rumble effect: %s", strerror(errno));1652}1653}16541655event.type = EV_FF;1656event.code = joystick->hwdata->effect.id;1657event.value = 1;1658if (write(joystick->hwdata->fd, &event, sizeof(event)) < 0) {1659return SDL_SetError("Couldn't start rumble effect: %s", strerror(errno));1660}1661return true;1662}16631664static bool LINUX_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)1665{1666return SDL_Unsupported();1667}16681669static bool LINUX_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)1670{1671return SDL_Unsupported();1672}16731674static bool LINUX_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)1675{1676return SDL_Unsupported();1677}16781679static bool LINUX_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled)1680{1681SDL_AssertJoysticksLocked();16821683if (!joystick->hwdata->has_accelerometer && !joystick->hwdata->has_gyro) {1684return SDL_Unsupported();1685}1686if (enabled == joystick->hwdata->report_sensor) {1687return true;1688}16891690if (enabled) {1691if (!joystick->hwdata->item_sensor) {1692return SDL_SetError("Sensors unplugged.");1693}1694joystick->hwdata->fd_sensor = open(joystick->hwdata->item_sensor->path, O_RDONLY | O_CLOEXEC, 0);1695if (joystick->hwdata->fd_sensor < 0) {1696return SDL_SetError("Couldn't open sensor file %s.", joystick->hwdata->item_sensor->path);1697}1698fcntl(joystick->hwdata->fd_sensor, F_SETFL, O_NONBLOCK);1699} else {1700SDL_assert(joystick->hwdata->fd_sensor >= 0);1701close(joystick->hwdata->fd_sensor);1702joystick->hwdata->fd_sensor = -1;1703}17041705joystick->hwdata->report_sensor = enabled;1706return true;1707}17081709static void HandleHat(Uint64 timestamp, SDL_Joystick *stick, int hatidx, int axis, int value)1710{1711int hatnum;1712struct hwdata_hat *the_hat;1713struct hat_axis_correct *correct;1714const Uint8 position_map[3][3] = {1715{ SDL_HAT_LEFTUP, SDL_HAT_UP, SDL_HAT_RIGHTUP },1716{ SDL_HAT_LEFT, SDL_HAT_CENTERED, SDL_HAT_RIGHT },1717{ SDL_HAT_LEFTDOWN, SDL_HAT_DOWN, SDL_HAT_RIGHTDOWN }1718};17191720SDL_AssertJoysticksLocked();17211722hatnum = stick->hwdata->hats_indices[hatidx];1723the_hat = &stick->hwdata->hats[hatnum];1724correct = &stick->hwdata->hat_correct[hatidx];1725/* Hopefully we detected any analog axes and left them as is rather than trying1726* to use them as digital hats, but just in case, the deadzones here will1727* prevent the slightest of twitches on an analog axis from registering as a hat1728* movement. If the axes really are digital, this won't hurt since they should1729* only ever be sending min, 0, or max anyway. */1730if (value < 0) {1731if (value <= correct->minimum[axis]) {1732correct->minimum[axis] = value;1733value = 0;1734} else if (!correct->use_deadzones || value < correct->minimum[axis] / 3) {1735value = 0;1736} else {1737value = 1;1738}1739} else if (value > 0) {1740if (value >= correct->maximum[axis]) {1741correct->maximum[axis] = value;1742value = 2;1743} else if (!correct->use_deadzones || value > correct->maximum[axis] / 3) {1744value = 2;1745} else {1746value = 1;1747}1748} else { // value == 01749value = 1;1750}1751if (value != the_hat->axis[axis]) {1752the_hat->axis[axis] = value;1753SDL_SendJoystickHat(timestamp, stick, hatnum,1754position_map[the_hat->axis[1]][the_hat->axis[0]]);1755}1756}17571758static void HandleBall(SDL_Joystick *stick, Uint8 ball, int axis, int value)1759{1760stick->hwdata->balls[ball].axis[axis] += value;1761}17621763static int AxisCorrect(SDL_Joystick *joystick, int which, int value)1764{1765struct axis_correct *correct;17661767SDL_AssertJoysticksLocked();17681769correct = &joystick->hwdata->abs_correct[which];1770if (correct->minimum != correct->maximum) {1771if (correct->use_deadzones) {1772value *= 2;1773if (value > correct->coef[0]) {1774if (value < correct->coef[1]) {1775return 0;1776}1777value -= correct->coef[1];1778} else {1779value -= correct->coef[0];1780}1781value *= correct->coef[2];1782value >>= 13;1783} else {1784value = (int)SDL_floorf((value - correct->minimum) * correct->scale + SDL_JOYSTICK_AXIS_MIN + 0.5f);1785}1786}17871788// Clamp and return1789if (value < SDL_JOYSTICK_AXIS_MIN) {1790return SDL_JOYSTICK_AXIS_MIN;1791}1792if (value > SDL_JOYSTICK_AXIS_MAX) {1793return SDL_JOYSTICK_AXIS_MAX;1794}1795return value;1796}17971798static void PollAllValues(Uint64 timestamp, SDL_Joystick *joystick)1799{1800struct input_absinfo absinfo;1801unsigned long keyinfo[NBITS(KEY_MAX)];1802int i;18031804SDL_AssertJoysticksLocked();18051806// Poll all axis1807for (i = ABS_X; i < ABS_MAX; i++) {1808// We don't need to test for digital hats here, they won't have has_abs[] set1809if (joystick->hwdata->has_abs[i]) {1810if (ioctl(joystick->hwdata->fd, EVIOCGABS(i), &absinfo) >= 0) {1811absinfo.value = AxisCorrect(joystick, i, absinfo.value);18121813#ifdef DEBUG_INPUT_EVENTS1814SDL_Log("Joystick : Re-read Axis %d (%d) val= %d",1815joystick->hwdata->abs_map[i], i, absinfo.value);1816#endif1817SDL_SendJoystickAxis(timestamp, joystick,1818joystick->hwdata->abs_map[i],1819absinfo.value);1820}1821}1822}18231824// Poll all digital hats1825for (i = ABS_HAT0X; i <= ABS_HAT3Y; i++) {1826const int baseaxis = i - ABS_HAT0X;1827const int hatidx = baseaxis / 2;1828SDL_assert(hatidx < SDL_arraysize(joystick->hwdata->has_hat));1829// We don't need to test for analog axes here, they won't have has_hat[] set1830if (joystick->hwdata->has_hat[hatidx]) {1831if (ioctl(joystick->hwdata->fd, EVIOCGABS(i), &absinfo) >= 0) {1832const int hataxis = baseaxis % 2;1833HandleHat(timestamp, joystick, hatidx, hataxis, absinfo.value);1834}1835}1836}18371838// Poll all buttons1839SDL_zeroa(keyinfo);1840if (ioctl(joystick->hwdata->fd, EVIOCGKEY(sizeof(keyinfo)), keyinfo) >= 0) {1841for (i = 0; i < KEY_MAX; i++) {1842if (joystick->hwdata->has_key[i]) {1843bool down = test_bit(i, keyinfo);1844#ifdef DEBUG_INPUT_EVENTS1845SDL_Log("Joystick : Re-read Button %d (%d) val= %d",1846joystick->hwdata->key_map[i], i, down);1847#endif1848SDL_SendJoystickButton(timestamp, joystick,1849joystick->hwdata->key_map[i], down);1850}1851}1852}18531854// Joyballs are relative input, so there's no poll state. Events only!1855}18561857static void CorrectSensorData(struct joystick_hwdata *hwdata, float *values, float *data)1858{1859if (hwdata->item->vendor == USB_VENDOR_NINTENDO) {1860// The Nintendo driver uses a different axis order than SDL1861data[0] = -values[1];1862data[1] = values[2];1863data[2] = -values[0];1864} else {1865data[0] = values[0];1866data[1] = values[1];1867data[2] = values[2];1868}1869}18701871static void PollAllSensors(Uint64 timestamp, SDL_Joystick *joystick)1872{1873struct input_absinfo absinfo;1874int i;18751876SDL_AssertJoysticksLocked();18771878SDL_assert(joystick->hwdata->fd_sensor >= 0);18791880if (joystick->hwdata->has_gyro) {1881float values[3] = {0.0f, 0.0f, 0.0f};1882for (i = 0; i < 3; i++) {1883if (ioctl(joystick->hwdata->fd_sensor, EVIOCGABS(ABS_RX + i), &absinfo) >= 0) {1884values[i] = absinfo.value * (SDL_PI_F / 180.f) / joystick->hwdata->gyro_scale[i];1885#ifdef DEBUG_INPUT_EVENTS1886SDL_Log("Joystick : Re-read Gyro (axis %d) val= %f", i, data[i]);1887#endif1888}1889}1890float data[3];1891CorrectSensorData(joystick->hwdata, values, data);1892SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, SDL_US_TO_NS(joystick->hwdata->sensor_tick), data, 3);1893}1894if (joystick->hwdata->has_accelerometer) {1895float values[3] = {0.0f, 0.0f, 0.0f};1896for (i = 0; i < 3; i++) {1897if (ioctl(joystick->hwdata->fd_sensor, EVIOCGABS(ABS_X + i), &absinfo) >= 0) {1898values[i] = absinfo.value * SDL_STANDARD_GRAVITY / joystick->hwdata->accelerometer_scale[i];1899#ifdef DEBUG_INPUT_EVENTS1900SDL_Log("Joystick : Re-read Accelerometer (axis %d) val= %f", i, data[i]);1901#endif1902}1903}1904float data[3];1905CorrectSensorData(joystick->hwdata, values, data);1906SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, SDL_US_TO_NS(joystick->hwdata->sensor_tick), data, 3);1907}1908}19091910static void HandleInputEvents(SDL_Joystick *joystick)1911{1912struct input_event events[32];1913int i, len, code, hat_index;19141915SDL_AssertJoysticksLocked();19161917if (joystick->hwdata->fresh) {1918Uint64 ticks = SDL_GetTicksNS();1919PollAllValues(ticks, joystick);1920if (joystick->hwdata->report_sensor) {1921PollAllSensors(ticks, joystick);1922}1923joystick->hwdata->fresh = false;1924}19251926errno = 0;19271928while ((len = read(joystick->hwdata->fd, events, sizeof(events))) > 0) {1929len /= sizeof(events[0]);1930for (i = 0; i < len; ++i) {1931struct input_event *event = &events[i];19321933code = event->code;19341935/* If the kernel sent a SYN_DROPPED, we are supposed to ignore the1936rest of the packet (the end of it signified by a SYN_REPORT) */1937if (joystick->hwdata->recovering_from_dropped &&1938((event->type != EV_SYN) || (code != SYN_REPORT))) {1939continue;1940}19411942switch (event->type) {1943case EV_KEY:1944#ifdef DEBUG_INPUT_EVENTS1945SDL_Log("Key 0x%.2x %s", code, event->value ? "PRESSED" : "RELEASED");1946#endif1947SDL_SendJoystickButton(SDL_EVDEV_GetEventTimestamp(event), joystick,1948joystick->hwdata->key_map[code],1949(event->value != 0));1950break;1951case EV_ABS:1952switch (code) {1953case ABS_HAT0X:1954case ABS_HAT0Y:1955case ABS_HAT1X:1956case ABS_HAT1Y:1957case ABS_HAT2X:1958case ABS_HAT2Y:1959case ABS_HAT3X:1960case ABS_HAT3Y:1961hat_index = (code - ABS_HAT0X) / 2;1962if (joystick->hwdata->has_hat[hat_index]) {1963#ifdef DEBUG_INPUT_EVENTS1964SDL_Log("Axis 0x%.2x = %d", code, event->value);1965#endif1966HandleHat(SDL_EVDEV_GetEventTimestamp(event), joystick, hat_index, code % 2, event->value);1967break;1968}1969SDL_FALLTHROUGH;1970default:1971#ifdef DEBUG_INPUT_EVENTS1972SDL_Log("Axis 0x%.2x = %d", code, event->value);1973#endif1974event->value = AxisCorrect(joystick, code, event->value);1975SDL_SendJoystickAxis(SDL_EVDEV_GetEventTimestamp(event), joystick,1976joystick->hwdata->abs_map[code],1977event->value);1978break;1979}1980break;1981case EV_REL:1982switch (code) {1983case REL_X:1984case REL_Y:1985code -= REL_X;1986HandleBall(joystick, code / 2, code % 2, event->value);1987break;1988default:1989break;1990}1991break;1992case EV_SYN:1993switch (code) {1994case SYN_DROPPED:1995#ifdef DEBUG_INPUT_EVENTS1996SDL_Log("Event SYN_DROPPED detected");1997#endif1998joystick->hwdata->recovering_from_dropped = true;1999break;2000case SYN_REPORT:2001if (joystick->hwdata->recovering_from_dropped) {2002joystick->hwdata->recovering_from_dropped = false;2003PollAllValues(SDL_GetTicksNS(), joystick); // try to sync up to current state now2004}2005break;2006default:2007break;2008}2009break;2010default:2011break;2012}2013}2014}20152016if (errno == ENODEV) {2017// We have to wait until the JoystickDetect callback to remove this2018joystick->hwdata->gone = true;2019errno = 0;2020}20212022if (joystick->hwdata->report_sensor) {2023SDL_assert(joystick->hwdata->fd_sensor >= 0);20242025while ((len = read(joystick->hwdata->fd_sensor, events, sizeof(events))) > 0) {2026len /= sizeof(events[0]);2027for (i = 0; i < len; ++i) {2028unsigned int j;2029struct input_event *event = &events[i];20302031code = event->code;20322033/* If the kernel sent a SYN_DROPPED, we are supposed to ignore the2034rest of the packet (the end of it signified by a SYN_REPORT) */2035if (joystick->hwdata->recovering_from_dropped_sensor &&2036((event->type != EV_SYN) || (code != SYN_REPORT))) {2037continue;2038}20392040switch (event->type) {2041case EV_KEY:2042SDL_assert(0);2043break;2044case EV_ABS:2045switch (code) {2046case ABS_X:2047case ABS_Y:2048case ABS_Z:2049j = code - ABS_X;2050joystick->hwdata->accel_data[j] = event->value * SDL_STANDARD_GRAVITY2051/ joystick->hwdata->accelerometer_scale[j];2052break;2053case ABS_RX:2054case ABS_RY:2055case ABS_RZ:2056j = code - ABS_RX;2057joystick->hwdata->gyro_data[j] = event->value * (SDL_PI_F / 180.f)2058/ joystick->hwdata->gyro_scale[j];2059break;2060}2061break;2062case EV_MSC:2063if (code == MSC_TIMESTAMP) {2064Sint32 tick = event->value;2065Sint32 delta;2066if (joystick->hwdata->last_tick < tick) {2067delta = (tick - joystick->hwdata->last_tick);2068} else {2069delta = (SDL_MAX_SINT32 - joystick->hwdata->last_tick + tick + 1);2070}2071joystick->hwdata->sensor_tick += delta;2072joystick->hwdata->last_tick = tick;2073}2074break;2075case EV_SYN:2076switch (code) {2077case SYN_DROPPED:2078#ifdef DEBUG_INPUT_EVENTS2079SDL_Log("Event SYN_DROPPED detected");2080#endif2081joystick->hwdata->recovering_from_dropped_sensor = true;2082break;2083case SYN_REPORT:2084if (joystick->hwdata->recovering_from_dropped_sensor) {2085joystick->hwdata->recovering_from_dropped_sensor = false;2086PollAllSensors(SDL_GetTicksNS(), joystick); // try to sync up to current state now2087} else {2088Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);2089float data[3];2090CorrectSensorData(joystick->hwdata, joystick->hwdata->gyro_data, data);2091SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO,2092SDL_US_TO_NS(joystick->hwdata->sensor_tick),2093data, 3);2094CorrectSensorData(joystick->hwdata, joystick->hwdata->accel_data, data);2095SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL,2096SDL_US_TO_NS(joystick->hwdata->sensor_tick),2097data, 3);2098}2099break;2100default:2101break;2102}2103break;2104default:2105break;2106}2107}2108}2109}21102111if (errno == ENODEV) {2112// We have to wait until the JoystickDetect callback to remove this2113joystick->hwdata->sensor_gone = true;2114}2115}21162117static void HandleClassicEvents(SDL_Joystick *joystick)2118{2119struct js_event events[32];2120int i, len, code, hat_index;2121Uint64 timestamp = SDL_GetTicksNS();21222123SDL_AssertJoysticksLocked();21242125joystick->hwdata->fresh = false;2126while ((len = read(joystick->hwdata->fd, events, sizeof(events))) > 0) {2127len /= sizeof(events[0]);2128for (i = 0; i < len; ++i) {2129switch (events[i].type) {2130case JS_EVENT_BUTTON:2131code = joystick->hwdata->key_pam[events[i].number];2132SDL_SendJoystickButton(timestamp, joystick,2133joystick->hwdata->key_map[code],2134(events[i].value != 0));2135break;2136case JS_EVENT_AXIS:2137code = joystick->hwdata->abs_pam[events[i].number];2138switch (code) {2139case ABS_HAT0X:2140case ABS_HAT0Y:2141case ABS_HAT1X:2142case ABS_HAT1Y:2143case ABS_HAT2X:2144case ABS_HAT2Y:2145case ABS_HAT3X:2146case ABS_HAT3Y:2147hat_index = (code - ABS_HAT0X) / 2;2148if (joystick->hwdata->has_hat[hat_index]) {2149HandleHat(timestamp, joystick, hat_index, code % 2, events[i].value);2150break;2151}2152SDL_FALLTHROUGH;2153default:2154SDL_SendJoystickAxis(timestamp, joystick,2155joystick->hwdata->abs_map[code],2156events[i].value);2157break;2158}2159}2160}2161}2162}21632164static void LINUX_JoystickUpdate(SDL_Joystick *joystick)2165{2166int i;21672168SDL_AssertJoysticksLocked();21692170if (joystick->hwdata->classic) {2171HandleClassicEvents(joystick);2172} else {2173HandleInputEvents(joystick);2174}21752176// Deliver ball motion updates2177for (i = 0; i < joystick->nballs; ++i) {2178int xrel, yrel;21792180xrel = joystick->hwdata->balls[i].axis[0];2181yrel = joystick->hwdata->balls[i].axis[1];2182if (xrel || yrel) {2183joystick->hwdata->balls[i].axis[0] = 0;2184joystick->hwdata->balls[i].axis[1] = 0;2185SDL_SendJoystickBall(0, joystick, (Uint8)i, xrel, yrel);2186}2187}2188}21892190// Function to close a joystick after use2191static void LINUX_JoystickClose(SDL_Joystick *joystick)2192{2193SDL_AssertJoysticksLocked();21942195if (joystick->hwdata) {2196if (joystick->hwdata->effect.id >= 0) {2197ioctl(joystick->hwdata->fd, EVIOCRMFF, joystick->hwdata->effect.id);2198joystick->hwdata->effect.id = -1;2199}2200if (joystick->hwdata->fd >= 0) {2201close(joystick->hwdata->fd);2202}2203if (joystick->hwdata->fd_sensor >= 0) {2204close(joystick->hwdata->fd_sensor);2205}2206if (joystick->hwdata->item) {2207joystick->hwdata->item->hwdata = NULL;2208}2209if (joystick->hwdata->item_sensor) {2210joystick->hwdata->item_sensor->hwdata = NULL;2211}2212SDL_free(joystick->hwdata->key_pam);2213SDL_free(joystick->hwdata->abs_pam);2214SDL_free(joystick->hwdata->hats);2215SDL_free(joystick->hwdata->balls);2216SDL_free(joystick->hwdata->fname);2217SDL_free(joystick->hwdata);2218}2219}22202221// Function to perform any system-specific joystick related cleanup2222static void LINUX_JoystickQuit(void)2223{2224SDL_joylist_item *item = NULL;2225SDL_joylist_item *next = NULL;2226SDL_sensorlist_item *item_sensor = NULL;2227SDL_sensorlist_item *next_sensor = NULL;22282229SDL_AssertJoysticksLocked();22302231if (inotify_fd >= 0) {2232close(inotify_fd);2233inotify_fd = -1;2234}22352236for (item = SDL_joylist; item; item = next) {2237next = item->next;2238FreeJoylistItem(item);2239}2240for (item_sensor = SDL_sensorlist; item_sensor; item_sensor = next_sensor) {2241next_sensor = item_sensor->next;2242FreeSensorlistItem(item_sensor);2243}22442245SDL_joylist = SDL_joylist_tail = NULL;2246SDL_sensorlist = NULL;22472248numjoysticks = 0;22492250#ifdef SDL_USE_LIBUDEV2251if (enumeration_method == ENUMERATION_LIBUDEV) {2252SDL_UDEV_DelCallback(joystick_udev_callback);2253SDL_UDEV_Quit();2254}2255#endif2256}22572258/*2259This is based on the Linux Gamepad Specification2260available at: https://www.kernel.org/doc/html/v4.15/input/gamepad.html2261and the Android gamepad documentation,2262https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input2263*/2264static bool LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)2265{2266SDL_Joystick *joystick;2267SDL_joylist_item *item = GetJoystickByDevIndex(device_index);2268enum {2269MAPPED_TRIGGER_LEFT = 0x1,2270MAPPED_TRIGGER_RIGHT = 0x2,2271MAPPED_TRIGGER_BOTH = 0x3,22722273MAPPED_DPAD_UP = 0x1,2274MAPPED_DPAD_DOWN = 0x2,2275MAPPED_DPAD_LEFT = 0x4,2276MAPPED_DPAD_RIGHT = 0x8,2277MAPPED_DPAD_ALL = 0xF,2278};2279unsigned int mapped;2280bool result = false;22812282SDL_AssertJoysticksLocked();22832284if (item->checked_mapping) {2285if (item->mapping) {2286SDL_memcpy(out, item->mapping, sizeof(*out));2287#ifdef DEBUG_GAMEPAD_MAPPING2288SDL_Log("Prior mapping for device %d", device_index);2289#endif2290return true;2291} else {2292return false;2293}2294}22952296/* We temporarily open the device to check how it's configured. Make2297a fake SDL_Joystick object to do so. */2298joystick = (SDL_Joystick *)SDL_calloc(1, sizeof(*joystick));2299if (!joystick) {2300return false;2301}2302SDL_memcpy(&joystick->guid, &item->guid, sizeof(item->guid));23032304joystick->hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*joystick->hwdata));2305if (!joystick->hwdata) {2306SDL_free(joystick);2307return false;2308}2309SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, true);23102311item->checked_mapping = true;23122313if (!PrepareJoystickHwdata(joystick, item, NULL)) {2314goto done; // SDL_SetError will already have been called2315}23162317// don't assign `item->hwdata` so it's not in any global state.23182319// it is now safe to call LINUX_JoystickClose on this fake joystick.23202321if (!joystick->hwdata->has_key[BTN_GAMEPAD]) {2322// Not a gamepad according to the specs.2323goto done;2324}23252326// We have a gamepad, start filling out the mappings23272328#ifdef DEBUG_GAMEPAD_MAPPING2329SDL_Log("Mapping %s (VID/PID 0x%.4x/0x%.4x)", item->name, SDL_GetJoystickVendor(joystick), SDL_GetJoystickProduct(joystick));2330#endif23312332if (joystick->hwdata->has_key[BTN_A]) {2333out->a.kind = EMappingKind_Button;2334out->a.target = joystick->hwdata->key_map[BTN_A];2335#ifdef DEBUG_GAMEPAD_MAPPING2336SDL_Log("Mapped A to button %d (BTN_A)", out->a.target);2337#endif2338}23392340if (joystick->hwdata->has_key[BTN_B]) {2341out->b.kind = EMappingKind_Button;2342out->b.target = joystick->hwdata->key_map[BTN_B];2343#ifdef DEBUG_GAMEPAD_MAPPING2344SDL_Log("Mapped B to button %d (BTN_B)", out->b.target);2345#endif2346}23472348// Xbox controllers use BTN_X and BTN_Y, and PS4 controllers use BTN_WEST and BTN_NORTH2349if (SDL_GetJoystickVendor(joystick) == USB_VENDOR_SONY) {2350if (joystick->hwdata->has_key[BTN_WEST]) {2351out->x.kind = EMappingKind_Button;2352out->x.target = joystick->hwdata->key_map[BTN_WEST];2353#ifdef DEBUG_GAMEPAD_MAPPING2354SDL_Log("Mapped X to button %d (BTN_WEST)", out->x.target);2355#endif2356}23572358if (joystick->hwdata->has_key[BTN_NORTH]) {2359out->y.kind = EMappingKind_Button;2360out->y.target = joystick->hwdata->key_map[BTN_NORTH];2361#ifdef DEBUG_GAMEPAD_MAPPING2362SDL_Log("Mapped Y to button %d (BTN_NORTH)", out->y.target);2363#endif2364}2365} else {2366if (joystick->hwdata->has_key[BTN_X]) {2367out->x.kind = EMappingKind_Button;2368out->x.target = joystick->hwdata->key_map[BTN_X];2369#ifdef DEBUG_GAMEPAD_MAPPING2370SDL_Log("Mapped X to button %d (BTN_X)", out->x.target);2371#endif2372}23732374if (joystick->hwdata->has_key[BTN_Y]) {2375out->y.kind = EMappingKind_Button;2376out->y.target = joystick->hwdata->key_map[BTN_Y];2377#ifdef DEBUG_GAMEPAD_MAPPING2378SDL_Log("Mapped Y to button %d (BTN_Y)", out->y.target);2379#endif2380}2381}23822383if (joystick->hwdata->has_key[BTN_SELECT]) {2384out->back.kind = EMappingKind_Button;2385out->back.target = joystick->hwdata->key_map[BTN_SELECT];2386#ifdef DEBUG_GAMEPAD_MAPPING2387SDL_Log("Mapped BACK to button %d (BTN_SELECT)", out->back.target);2388#endif2389}23902391if (joystick->hwdata->has_key[BTN_START]) {2392out->start.kind = EMappingKind_Button;2393out->start.target = joystick->hwdata->key_map[BTN_START];2394#ifdef DEBUG_GAMEPAD_MAPPING2395SDL_Log("Mapped START to button %d (BTN_START)", out->start.target);2396#endif2397}23982399if (joystick->hwdata->has_key[BTN_THUMBL]) {2400out->leftstick.kind = EMappingKind_Button;2401out->leftstick.target = joystick->hwdata->key_map[BTN_THUMBL];2402#ifdef DEBUG_GAMEPAD_MAPPING2403SDL_Log("Mapped LEFTSTICK to button %d (BTN_THUMBL)", out->leftstick.target);2404#endif2405}24062407if (joystick->hwdata->has_key[BTN_THUMBR]) {2408out->rightstick.kind = EMappingKind_Button;2409out->rightstick.target = joystick->hwdata->key_map[BTN_THUMBR];2410#ifdef DEBUG_GAMEPAD_MAPPING2411SDL_Log("Mapped RIGHTSTICK to button %d (BTN_THUMBR)", out->rightstick.target);2412#endif2413}24142415if (joystick->hwdata->has_key[BTN_MODE]) {2416out->guide.kind = EMappingKind_Button;2417out->guide.target = joystick->hwdata->key_map[BTN_MODE];2418#ifdef DEBUG_GAMEPAD_MAPPING2419SDL_Log("Mapped GUIDE to button %d (BTN_MODE)", out->guide.target);2420#endif2421}24222423/*2424According to the specs the D-Pad, the shoulder buttons and the triggers2425can be digital, or analog, or both at the same time.2426*/24272428// Prefer digital shoulder buttons, but settle for digital or analog hat.2429mapped = 0;24302431if (joystick->hwdata->has_key[BTN_TL]) {2432out->leftshoulder.kind = EMappingKind_Button;2433out->leftshoulder.target = joystick->hwdata->key_map[BTN_TL];2434mapped |= 0x1;2435#ifdef DEBUG_GAMEPAD_MAPPING2436SDL_Log("Mapped LEFTSHOULDER to button %d (BTN_TL)", out->leftshoulder.target);2437#endif2438}24392440if (joystick->hwdata->has_key[BTN_TR]) {2441out->rightshoulder.kind = EMappingKind_Button;2442out->rightshoulder.target = joystick->hwdata->key_map[BTN_TR];2443mapped |= 0x2;2444#ifdef DEBUG_GAMEPAD_MAPPING2445SDL_Log("Mapped RIGHTSHOULDER to button %d (BTN_TR)", out->rightshoulder.target);2446#endif2447}24482449if (mapped != 0x3 && joystick->hwdata->has_hat[1]) {2450int hat = joystick->hwdata->hats_indices[1] << 4;2451out->leftshoulder.kind = EMappingKind_Hat;2452out->rightshoulder.kind = EMappingKind_Hat;2453out->leftshoulder.target = hat | 0x4;2454out->rightshoulder.target = hat | 0x2;2455mapped |= 0x3;2456#ifdef DEBUG_GAMEPAD_MAPPING2457SDL_Log("Mapped LEFT+RIGHTSHOULDER to hat 1 (ABS_HAT1X, ABS_HAT1Y)");2458#endif2459}24602461if (!(mapped & 0x1) && joystick->hwdata->has_abs[ABS_HAT1Y]) {2462out->leftshoulder.kind = EMappingKind_Axis;2463out->leftshoulder.target = joystick->hwdata->abs_map[ABS_HAT1Y];2464mapped |= 0x1;2465#ifdef DEBUG_GAMEPAD_MAPPING2466SDL_Log("Mapped LEFTSHOULDER to axis %d (ABS_HAT1Y)", out->leftshoulder.target);2467#endif2468}24692470if (!(mapped & 0x2) && joystick->hwdata->has_abs[ABS_HAT1X]) {2471out->rightshoulder.kind = EMappingKind_Axis;2472out->rightshoulder.target = joystick->hwdata->abs_map[ABS_HAT1X];2473mapped |= 0x2;2474#ifdef DEBUG_GAMEPAD_MAPPING2475SDL_Log("Mapped RIGHTSHOULDER to axis %d (ABS_HAT1X)", out->rightshoulder.target);2476#endif2477}24782479// Prefer analog triggers, but settle for digital hat or buttons.2480mapped = 0;24812482/* Unfortunately there are several conventions for how analog triggers2483* are represented as absolute axes:2484*2485* - Linux Gamepad Specification:2486* LT = ABS_HAT2Y, RT = ABS_HAT2X2487* - Android (and therefore many Bluetooth controllers):2488* LT = ABS_BRAKE, RT = ABS_GAS2489* - De facto standard for older Xbox and Playstation controllers:2490* LT = ABS_Z, RT = ABS_RZ2491*2492* We try each one in turn. */2493if (joystick->hwdata->has_abs[ABS_HAT2Y]) {2494// Linux Gamepad Specification2495out->lefttrigger.kind = EMappingKind_Axis;2496out->lefttrigger.target = joystick->hwdata->abs_map[ABS_HAT2Y];2497mapped |= MAPPED_TRIGGER_LEFT;2498#ifdef DEBUG_GAMEPAD_MAPPING2499SDL_Log("Mapped LEFTTRIGGER to axis %d (ABS_HAT2Y)", out->lefttrigger.target);2500#endif2501} else if (joystick->hwdata->has_abs[ABS_BRAKE]) {2502// Android convention2503out->lefttrigger.kind = EMappingKind_Axis;2504out->lefttrigger.target = joystick->hwdata->abs_map[ABS_BRAKE];2505mapped |= MAPPED_TRIGGER_LEFT;2506#ifdef DEBUG_GAMEPAD_MAPPING2507SDL_Log("Mapped LEFTTRIGGER to axis %d (ABS_BRAKE)", out->lefttrigger.target);2508#endif2509} else if (joystick->hwdata->has_abs[ABS_Z]) {2510// De facto standard for Xbox 360 and Playstation gamepads2511out->lefttrigger.kind = EMappingKind_Axis;2512out->lefttrigger.target = joystick->hwdata->abs_map[ABS_Z];2513mapped |= MAPPED_TRIGGER_LEFT;2514#ifdef DEBUG_GAMEPAD_MAPPING2515SDL_Log("Mapped LEFTTRIGGER to axis %d (ABS_Z)", out->lefttrigger.target);2516#endif2517}25182519if (joystick->hwdata->has_abs[ABS_HAT2X]) {2520// Linux Gamepad Specification2521out->righttrigger.kind = EMappingKind_Axis;2522out->righttrigger.target = joystick->hwdata->abs_map[ABS_HAT2X];2523mapped |= MAPPED_TRIGGER_RIGHT;2524#ifdef DEBUG_GAMEPAD_MAPPING2525SDL_Log("Mapped RIGHTTRIGGER to axis %d (ABS_HAT2X)", out->righttrigger.target);2526#endif2527} else if (joystick->hwdata->has_abs[ABS_GAS]) {2528// Android convention2529out->righttrigger.kind = EMappingKind_Axis;2530out->righttrigger.target = joystick->hwdata->abs_map[ABS_GAS];2531mapped |= MAPPED_TRIGGER_RIGHT;2532#ifdef DEBUG_GAMEPAD_MAPPING2533SDL_Log("Mapped RIGHTTRIGGER to axis %d (ABS_GAS)", out->righttrigger.target);2534#endif2535} else if (joystick->hwdata->has_abs[ABS_RZ]) {2536// De facto standard for Xbox 360 and Playstation gamepads2537out->righttrigger.kind = EMappingKind_Axis;2538out->righttrigger.target = joystick->hwdata->abs_map[ABS_RZ];2539mapped |= MAPPED_TRIGGER_RIGHT;2540#ifdef DEBUG_GAMEPAD_MAPPING2541SDL_Log("Mapped RIGHTTRIGGER to axis %d (ABS_RZ)", out->righttrigger.target);2542#endif2543}25442545if (mapped != MAPPED_TRIGGER_BOTH && joystick->hwdata->has_hat[2]) {2546int hat = joystick->hwdata->hats_indices[2] << 4;2547out->lefttrigger.kind = EMappingKind_Hat;2548out->righttrigger.kind = EMappingKind_Hat;2549out->lefttrigger.target = hat | 0x4;2550out->righttrigger.target = hat | 0x2;2551mapped |= MAPPED_TRIGGER_BOTH;2552#ifdef DEBUG_GAMEPAD_MAPPING2553SDL_Log("Mapped LEFT+RIGHTTRIGGER to hat 2 (ABS_HAT2X, ABS_HAT2Y)");2554#endif2555}25562557if (!(mapped & MAPPED_TRIGGER_LEFT) && joystick->hwdata->has_key[BTN_TL2]) {2558out->lefttrigger.kind = EMappingKind_Button;2559out->lefttrigger.target = joystick->hwdata->key_map[BTN_TL2];2560mapped |= MAPPED_TRIGGER_LEFT;2561#ifdef DEBUG_GAMEPAD_MAPPING2562SDL_Log("Mapped LEFTTRIGGER to button %d (BTN_TL2)", out->lefttrigger.target);2563#endif2564}25652566if (!(mapped & MAPPED_TRIGGER_RIGHT) && joystick->hwdata->has_key[BTN_TR2]) {2567out->righttrigger.kind = EMappingKind_Button;2568out->righttrigger.target = joystick->hwdata->key_map[BTN_TR2];2569mapped |= MAPPED_TRIGGER_RIGHT;2570#ifdef DEBUG_GAMEPAD_MAPPING2571SDL_Log("Mapped RIGHTTRIGGER to button %d (BTN_TR2)", out->righttrigger.target);2572#endif2573}25742575// Prefer digital D-Pad buttons, but settle for digital or analog hat.2576mapped = 0;25772578if (joystick->hwdata->has_key[BTN_DPAD_UP]) {2579out->dpup.kind = EMappingKind_Button;2580out->dpup.target = joystick->hwdata->key_map[BTN_DPAD_UP];2581mapped |= MAPPED_DPAD_UP;2582#ifdef DEBUG_GAMEPAD_MAPPING2583SDL_Log("Mapped DPUP to button %d (BTN_DPAD_UP)", out->dpup.target);2584#endif2585}25862587if (joystick->hwdata->has_key[BTN_DPAD_DOWN]) {2588out->dpdown.kind = EMappingKind_Button;2589out->dpdown.target = joystick->hwdata->key_map[BTN_DPAD_DOWN];2590mapped |= MAPPED_DPAD_DOWN;2591#ifdef DEBUG_GAMEPAD_MAPPING2592SDL_Log("Mapped DPDOWN to button %d (BTN_DPAD_DOWN)", out->dpdown.target);2593#endif2594}25952596if (joystick->hwdata->has_key[BTN_DPAD_LEFT]) {2597out->dpleft.kind = EMappingKind_Button;2598out->dpleft.target = joystick->hwdata->key_map[BTN_DPAD_LEFT];2599mapped |= MAPPED_DPAD_LEFT;2600#ifdef DEBUG_GAMEPAD_MAPPING2601SDL_Log("Mapped DPLEFT to button %d (BTN_DPAD_LEFT)", out->dpleft.target);2602#endif2603}26042605if (joystick->hwdata->has_key[BTN_DPAD_RIGHT]) {2606out->dpright.kind = EMappingKind_Button;2607out->dpright.target = joystick->hwdata->key_map[BTN_DPAD_RIGHT];2608mapped |= MAPPED_DPAD_RIGHT;2609#ifdef DEBUG_GAMEPAD_MAPPING2610SDL_Log("Mapped DPRIGHT to button %d (BTN_DPAD_RIGHT)", out->dpright.target);2611#endif2612}26132614if (mapped != MAPPED_DPAD_ALL) {2615if (joystick->hwdata->has_hat[0]) {2616int hat = joystick->hwdata->hats_indices[0] << 4;2617out->dpleft.kind = EMappingKind_Hat;2618out->dpright.kind = EMappingKind_Hat;2619out->dpup.kind = EMappingKind_Hat;2620out->dpdown.kind = EMappingKind_Hat;2621out->dpleft.target = hat | 0x8;2622out->dpright.target = hat | 0x2;2623out->dpup.target = hat | 0x1;2624out->dpdown.target = hat | 0x4;2625mapped |= MAPPED_DPAD_ALL;2626#ifdef DEBUG_GAMEPAD_MAPPING2627SDL_Log("Mapped DPUP+DOWN+LEFT+RIGHT to hat 0 (ABS_HAT0X, ABS_HAT0Y)");2628#endif2629} else if (joystick->hwdata->has_abs[ABS_HAT0X] && joystick->hwdata->has_abs[ABS_HAT0Y]) {2630out->dpleft.kind = EMappingKind_Axis;2631out->dpright.kind = EMappingKind_Axis;2632out->dpup.kind = EMappingKind_Axis;2633out->dpdown.kind = EMappingKind_Axis;2634out->dpleft.target = joystick->hwdata->abs_map[ABS_HAT0X];2635out->dpright.target = joystick->hwdata->abs_map[ABS_HAT0X];2636out->dpup.target = joystick->hwdata->abs_map[ABS_HAT0Y];2637out->dpdown.target = joystick->hwdata->abs_map[ABS_HAT0Y];2638mapped |= MAPPED_DPAD_ALL;2639#ifdef DEBUG_GAMEPAD_MAPPING2640SDL_Log("Mapped DPUP+DOWN to axis %d (ABS_HAT0Y)", out->dpup.target);2641SDL_Log("Mapped DPLEFT+RIGHT to axis %d (ABS_HAT0X)", out->dpleft.target);2642#endif2643}2644}26452646if (joystick->hwdata->has_abs[ABS_X] && joystick->hwdata->has_abs[ABS_Y]) {2647out->leftx.kind = EMappingKind_Axis;2648out->lefty.kind = EMappingKind_Axis;2649out->leftx.target = joystick->hwdata->abs_map[ABS_X];2650out->lefty.target = joystick->hwdata->abs_map[ABS_Y];2651#ifdef DEBUG_GAMEPAD_MAPPING2652SDL_Log("Mapped LEFTX to axis %d (ABS_X)", out->leftx.target);2653SDL_Log("Mapped LEFTY to axis %d (ABS_Y)", out->lefty.target);2654#endif2655}26562657/* The Linux Gamepad Specification uses the RX and RY axes,2658* originally intended to represent X and Y rotation, as a second2659* joystick. This is common for USB gamepads, and also many Bluetooth2660* gamepads, particularly older ones.2661*2662* The Android mapping convention used by many Bluetooth controllers2663* instead uses the Z axis as a secondary X axis, and the RZ axis as2664* a secondary Y axis. */2665if (joystick->hwdata->has_abs[ABS_RX] && joystick->hwdata->has_abs[ABS_RY]) {2666// Linux Gamepad Specification, Xbox 360, Playstation etc.2667out->rightx.kind = EMappingKind_Axis;2668out->righty.kind = EMappingKind_Axis;2669out->rightx.target = joystick->hwdata->abs_map[ABS_RX];2670out->righty.target = joystick->hwdata->abs_map[ABS_RY];2671#ifdef DEBUG_GAMEPAD_MAPPING2672SDL_Log("Mapped RIGHTX to axis %d (ABS_RX)", out->rightx.target);2673SDL_Log("Mapped RIGHTY to axis %d (ABS_RY)", out->righty.target);2674#endif2675} else if (joystick->hwdata->has_abs[ABS_Z] && joystick->hwdata->has_abs[ABS_RZ]) {2676// Android convention2677out->rightx.kind = EMappingKind_Axis;2678out->righty.kind = EMappingKind_Axis;2679out->rightx.target = joystick->hwdata->abs_map[ABS_Z];2680out->righty.target = joystick->hwdata->abs_map[ABS_RZ];2681#ifdef DEBUG_GAMEPAD_MAPPING2682SDL_Log("Mapped RIGHTX to axis %d (ABS_Z)", out->rightx.target);2683SDL_Log("Mapped RIGHTY to axis %d (ABS_RZ)", out->righty.target);2684#endif2685}26862687if (SDL_GetJoystickVendor(joystick) == USB_VENDOR_MICROSOFT) {2688// The Xbox Elite controllers have the paddles as BTN_TRIGGER_HAPPY5 - BTN_TRIGGER_HAPPY82689if (joystick->hwdata->has_key[BTN_TRIGGER_HAPPY5] &&2690joystick->hwdata->has_key[BTN_TRIGGER_HAPPY6] &&2691joystick->hwdata->has_key[BTN_TRIGGER_HAPPY7] &&2692joystick->hwdata->has_key[BTN_TRIGGER_HAPPY8]) {2693out->right_paddle1.kind = EMappingKind_Button;2694out->right_paddle1.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY5];2695out->left_paddle1.kind = EMappingKind_Button;2696out->left_paddle1.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY7];2697out->right_paddle2.kind = EMappingKind_Button;2698out->right_paddle2.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY6];2699out->left_paddle2.kind = EMappingKind_Button;2700out->left_paddle2.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY8];2701#ifdef DEBUG_GAMEPAD_MAPPING2702SDL_Log("Mapped RIGHT_PADDLE1 to button %d (BTN_TRIGGER_HAPPY5)", out->right_paddle1.target);2703SDL_Log("Mapped LEFT_PADDLE1 to button %d (BTN_TRIGGER_HAPPY7)", out->left_paddle1.target);2704SDL_Log("Mapped RIGHT_PADDLE2 to button %d (BTN_TRIGGER_HAPPY6)", out->right_paddle2.target);2705SDL_Log("Mapped LEFT_PADDLE2 to button %d (BTN_TRIGGER_HAPPY8)", out->left_paddle2.target);2706#endif2707}27082709// The Xbox Series X controllers have the Share button as KEY_RECORD2710if (joystick->hwdata->has_key[KEY_RECORD]) {2711out->misc1.kind = EMappingKind_Button;2712out->misc1.target = joystick->hwdata->key_map[KEY_RECORD];2713#ifdef DEBUG_GAMEPAD_MAPPING2714SDL_Log("Mapped MISC1 to button %d (KEY_RECORD)", out->misc1.target);2715#endif2716}2717}27182719// Cache the mapping for later2720item->mapping = (SDL_GamepadMapping *)SDL_malloc(sizeof(*item->mapping));2721if (item->mapping) {2722SDL_memcpy(item->mapping, out, sizeof(*out));2723}2724#ifdef DEBUG_GAMEPAD_MAPPING2725SDL_Log("Generated mapping for device %d", device_index);2726#endif2727result = true;27282729done:2730LINUX_JoystickClose(joystick);2731SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, false);2732SDL_free(joystick);27332734return result;2735}27362737SDL_JoystickDriver SDL_LINUX_JoystickDriver = {2738LINUX_JoystickInit,2739LINUX_JoystickGetCount,2740LINUX_JoystickDetect,2741LINUX_JoystickIsDevicePresent,2742LINUX_JoystickGetDeviceName,2743LINUX_JoystickGetDevicePath,2744LINUX_JoystickGetDeviceSteamVirtualGamepadSlot,2745LINUX_JoystickGetDevicePlayerIndex,2746LINUX_JoystickSetDevicePlayerIndex,2747LINUX_JoystickGetDeviceGUID,2748LINUX_JoystickGetDeviceInstanceID,2749LINUX_JoystickOpen,2750LINUX_JoystickRumble,2751LINUX_JoystickRumbleTriggers,2752LINUX_JoystickSetLED,2753LINUX_JoystickSendEffect,2754LINUX_JoystickSetSensorsEnabled,2755LINUX_JoystickUpdate,2756LINUX_JoystickClose,2757LINUX_JoystickQuit,2758LINUX_JoystickGetGamepadMapping2759};27602761#endif // SDL_JOYSTICK_LINUX276227632764