Path: blob/master/thirdparty/sdl/joystick/linux/SDL_sysjoystick.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_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 whatever152char *name; // "SideWinder 3D Pro" or whatever153SDL_GUID guid;154dev_t devnum;155int steam_virtual_gamepad_slot;156struct joystick_hwdata *hwdata;157struct SDL_joylist_item *next;158159bool checked_mapping;160SDL_GamepadMapping *mapping;161} SDL_joylist_item;162163// A linked list of available gamepad sensors164typedef struct SDL_sensorlist_item165{166char *path; // "/dev/input/event2" or whatever167dev_t devnum;168struct joystick_hwdata *hwdata;169struct SDL_sensorlist_item *next;170} SDL_sensorlist_item;171172static bool SDL_classic_joysticks = false;173static SDL_joylist_item *SDL_joylist SDL_GUARDED_BY(SDL_joystick_lock) = NULL;174static SDL_joylist_item *SDL_joylist_tail SDL_GUARDED_BY(SDL_joystick_lock) = NULL;175static int numjoysticks SDL_GUARDED_BY(SDL_joystick_lock) = 0;176static SDL_sensorlist_item *SDL_sensorlist SDL_GUARDED_BY(SDL_joystick_lock) = NULL;177static int inotify_fd = -1;178179static Uint64 last_joy_detect_time;180static time_t last_input_dir_mtime;181182static void FixupDeviceInfoForMapping(int fd, struct input_id *inpid)183{184if (inpid->vendor == 0x045e && inpid->product == 0x0b05 && inpid->version == 0x0903) {185// This is a Microsoft Xbox One Elite Series 2 controller186unsigned long keybit[NBITS(KEY_MAX)] = { 0 };187188// The first version of the firmware duplicated all the inputs189if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&190test_bit(0x2c0, keybit)) {191// Change the version to 0x0902, so we can map it differently192inpid->version = 0x0902;193}194}195196/* For Atari vcs modern and classic controllers have the version reflecting197* firmware version, but the mapping stays stable so ignore198* version information */199if (inpid->vendor == 0x3250 && (inpid->product == 0x1001 || inpid->product == 0x1002)) {200inpid->version = 0;201}202}203204#ifdef SDL_JOYSTICK_HIDAPI205static bool IsVirtualJoystick(Uint16 vendor, Uint16 product, Uint16 version, const char *name)206{207if (vendor == USB_VENDOR_MICROSOFT && product == USB_PRODUCT_XBOX_ONE_S && version == 0 &&208SDL_strcmp(name, "Xbox One S Controller") == 0) {209// This is the virtual device created by the xow driver210return true;211}212return false;213}214#else215static bool IsVirtualJoystick(Uint16 vendor, Uint16 product, Uint16 version, const char *name)216{217return false;218}219#endif // SDL_JOYSTICK_HIDAPI220221static bool GetSteamVirtualGamepadSlot(int fd, int *slot)222{223char name[128];224225if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) > 0) {226const char *digits = SDL_strstr(name, "pad ");227if (digits) {228digits += 4;229if (SDL_isdigit(*digits)) {230*slot = SDL_atoi(digits);231return true;232}233}234}235return false;236}237238static int GuessDeviceClass(int fd)239{240unsigned long propbit[NBITS(INPUT_PROP_MAX)] = { 0 };241unsigned long evbit[NBITS(EV_MAX)] = { 0 };242unsigned long keybit[NBITS(KEY_MAX)] = { 0 };243unsigned long absbit[NBITS(ABS_MAX)] = { 0 };244unsigned long relbit[NBITS(REL_MAX)] = { 0 };245246if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||247(ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||248(ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) < 0) ||249(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {250return 0;251}252253/* This is a newer feature, so it's allowed to fail - if so, then the254* device just doesn't have any properties. */255(void) ioctl(fd, EVIOCGPROP(sizeof(propbit)), propbit);256257return SDL_EVDEV_GuessDeviceClass(propbit, evbit, absbit, keybit, relbit);258}259260static bool GuessIsJoystick(int fd)261{262if (GuessDeviceClass(fd) & SDL_UDEV_DEVICE_JOYSTICK) {263return true;264}265return false;266}267268static bool GuessIsSensor(int fd)269{270if (GuessDeviceClass(fd) & SDL_UDEV_DEVICE_ACCELEROMETER) {271return true;272}273return false;274}275276static bool IsJoystick(const char *path, int *fd, char **name_return, Uint16 *vendor_return, Uint16 *product_return, SDL_GUID *guid)277{278struct input_id inpid;279char *name;280char product_string[128];281int class = 0;282283SDL_zero(inpid);284#ifdef SDL_USE_LIBUDEV285// Opening input devices can generate synchronous device I/O, so avoid it if we can286if (SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version, &class) &&287!(class & SDL_UDEV_DEVICE_JOYSTICK)) {288return false;289}290#endif291292if (fd && *fd < 0) {293*fd = open(path, O_RDONLY | O_CLOEXEC, 0);294}295if (!fd || *fd < 0) {296return false;297}298299if (ioctl(*fd, JSIOCGNAME(sizeof(product_string)), product_string) <= 0) {300// When udev enumeration or classification, we only got joysticks here, so no need to test301if (enumeration_method != ENUMERATION_LIBUDEV && !class && !GuessIsJoystick(*fd)) {302return false;303}304305// Could have vendor and product already from udev, but should agree with evdev306if (ioctl(*fd, EVIOCGID, &inpid) < 0) {307return false;308}309310if (ioctl(*fd, EVIOCGNAME(sizeof(product_string)), product_string) < 0) {311return false;312}313}314315name = SDL_CreateJoystickName(inpid.vendor, inpid.product, NULL, product_string);316if (!name) {317return false;318}319320if (!IsVirtualJoystick(inpid.vendor, inpid.product, inpid.version, name) &&321SDL_JoystickHandledByAnotherDriver(&SDL_LINUX_JoystickDriver, inpid.vendor, inpid.product, inpid.version, name)) {322SDL_free(name);323return false;324}325326FixupDeviceInfoForMapping(*fd, &inpid);327328#ifdef DEBUG_JOYSTICK329SDL_Log("Joystick: %s, bustype = %d, vendor = 0x%.4x, product = 0x%.4x, version = %d", name, inpid.bustype, inpid.vendor, inpid.product, inpid.version);330#endif331332if (SDL_ShouldIgnoreJoystick(inpid.vendor, inpid.product, inpid.version, name)) {333SDL_free(name);334return false;335}336*name_return = name;337*vendor_return = inpid.vendor;338*product_return = inpid.product;339*guid = SDL_CreateJoystickGUID(inpid.bustype, inpid.vendor, inpid.product, inpid.version, NULL, product_string, 0, 0);340return true;341}342343static bool IsSensor(const char *path, int *fd)344{345struct input_id inpid;346int class = 0;347348SDL_zero(inpid);349#ifdef SDL_USE_LIBUDEV350// Opening input devices can generate synchronous device I/O, so avoid it if we can351if (SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version, &class) &&352!(class & SDL_UDEV_DEVICE_ACCELEROMETER)) {353return false;354}355#endif356357if (fd && *fd < 0) {358*fd = open(path, O_RDONLY | O_CLOEXEC, 0);359}360if (!fd || *fd < 0) {361return false;362}363364if (!class && !GuessIsSensor(*fd)) {365return false;366}367368if (ioctl(*fd, EVIOCGID, &inpid) < 0) {369return false;370}371372if (inpid.vendor == USB_VENDOR_NINTENDO && inpid.product == USB_PRODUCT_NINTENDO_WII_REMOTE) {373// Wii extension controls374// These may create 3 sensor devices but we only support reading from 1: ignore them375return false;376}377378return true;379}380381#ifdef SDL_USE_LIBUDEV382static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)383{384if (!devpath) {385return;386}387388switch (udev_type) {389case SDL_UDEV_DEVICEADDED:390if (!(udev_class & (SDL_UDEV_DEVICE_JOYSTICK | SDL_UDEV_DEVICE_ACCELEROMETER))) {391return;392}393if (SDL_classic_joysticks) {394if (!IsJoystickJSNode(devpath)) {395return;396}397} else {398if (IsJoystickJSNode(devpath)) {399return;400}401}402403// Wait a bit for the hidraw udev node to initialize404SDL_Delay(10);405406MaybeAddDevice(devpath);407break;408409case SDL_UDEV_DEVICEREMOVED:410MaybeRemoveDevice(devpath);411break;412413default:414break;415}416}417#endif // SDL_USE_LIBUDEV418419static void FreeJoylistItem(SDL_joylist_item *item)420{421SDL_free(item->mapping);422SDL_free(item->path);423SDL_free(item->name);424SDL_free(item);425}426427static void FreeSensorlistItem(SDL_sensorlist_item *item)428{429SDL_free(item->path);430SDL_free(item);431}432433static void MaybeAddDevice(const char *path)434{435struct stat sb;436int fd = -1;437char *name = NULL;438Uint16 vendor, product;439SDL_GUID guid;440SDL_joylist_item *item;441SDL_sensorlist_item *item_sensor;442443if (!path) {444return;445}446447fd = open(path, O_RDONLY | O_CLOEXEC, 0);448if (fd < 0) {449return;450}451452if (fstat(fd, &sb) == -1) {453close(fd);454return;455}456457SDL_LockJoysticks();458459// Check to make sure it's not already in list.460for (item = SDL_joylist; item; item = item->next) {461if (sb.st_rdev == item->devnum) {462goto done; // already have this one463}464}465for (item_sensor = SDL_sensorlist; item_sensor; item_sensor = item_sensor->next) {466if (sb.st_rdev == item_sensor->devnum) {467goto done; // already have this one468}469}470471#ifdef DEBUG_INPUT_EVENTS472SDL_Log("Checking %s", path);473#endif474475if (IsJoystick(path, &fd, &name, &vendor, &product, &guid)) {476#ifdef DEBUG_INPUT_EVENTS477SDL_Log("found joystick: %s", path);478#endif479item = (SDL_joylist_item *)SDL_calloc(1, sizeof(SDL_joylist_item));480if (!item) {481SDL_free(name);482goto done;483}484485item->devnum = sb.st_rdev;486item->steam_virtual_gamepad_slot = -1;487item->path = SDL_strdup(path);488item->name = name;489item->guid = guid;490491if (vendor == USB_VENDOR_VALVE &&492product == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD) {493GetSteamVirtualGamepadSlot(fd, &item->steam_virtual_gamepad_slot);494}495496if ((!item->path) || (!item->name)) {497FreeJoylistItem(item);498goto done;499}500501item->device_instance = SDL_GetNextObjectID();502if (!SDL_joylist_tail) {503SDL_joylist = SDL_joylist_tail = item;504} else {505SDL_joylist_tail->next = item;506SDL_joylist_tail = item;507}508509// Need to increment the joystick count before we post the event510++numjoysticks;511512SDL_PrivateJoystickAdded(item->device_instance);513goto done;514}515516if (IsSensor(path, &fd)) {517#ifdef DEBUG_INPUT_EVENTS518SDL_Log("found sensor: %s", path);519#endif520item_sensor = (SDL_sensorlist_item *)SDL_calloc(1, sizeof(SDL_sensorlist_item));521if (!item_sensor) {522goto done;523}524item_sensor->devnum = sb.st_rdev;525item_sensor->path = SDL_strdup(path);526527if (!item_sensor->path) {528FreeSensorlistItem(item_sensor);529goto done;530}531532item_sensor->next = SDL_sensorlist;533SDL_sensorlist = item_sensor;534goto done;535}536537done:538close(fd);539SDL_UnlockJoysticks();540}541542static void RemoveJoylistItem(SDL_joylist_item *item, SDL_joylist_item *prev)543{544SDL_AssertJoysticksLocked();545546if (item->hwdata) {547item->hwdata->item = NULL;548}549550if (prev) {551prev->next = item->next;552} else {553SDL_assert(SDL_joylist == item);554SDL_joylist = item->next;555}556557if (item == SDL_joylist_tail) {558SDL_joylist_tail = prev;559}560561// Need to decrement the joystick count before we post the event562--numjoysticks;563564SDL_PrivateJoystickRemoved(item->device_instance);565FreeJoylistItem(item);566}567568static void RemoveSensorlistItem(SDL_sensorlist_item *item, SDL_sensorlist_item *prev)569{570SDL_AssertJoysticksLocked();571572if (item->hwdata) {573item->hwdata->item_sensor = NULL;574}575576if (prev) {577prev->next = item->next;578} else {579SDL_assert(SDL_sensorlist == item);580SDL_sensorlist = item->next;581}582583/* Do not call SDL_PrivateJoystickRemoved here as RemoveJoylistItem will do it,584* assuming both sensor and joy item are removed at the same time */585FreeSensorlistItem(item);586}587588static void MaybeRemoveDevice(const char *path)589{590SDL_joylist_item *item;591SDL_joylist_item *prev = NULL;592SDL_sensorlist_item *item_sensor;593SDL_sensorlist_item *prev_sensor = NULL;594595if (!path) {596return;597}598599SDL_LockJoysticks();600for (item = SDL_joylist; item; item = item->next) {601// found it, remove it.602if (SDL_strcmp(path, item->path) == 0) {603RemoveJoylistItem(item, prev);604goto done;605}606prev = item;607}608for (item_sensor = SDL_sensorlist; item_sensor; item_sensor = item_sensor->next) {609// found it, remove it.610if (SDL_strcmp(path, item_sensor->path) == 0) {611RemoveSensorlistItem(item_sensor, prev_sensor);612goto done;613}614prev_sensor = item_sensor;615}616done:617SDL_UnlockJoysticks();618}619620static void HandlePendingRemovals(void)621{622SDL_joylist_item *prev = NULL;623SDL_joylist_item *item = NULL;624SDL_sensorlist_item *prev_sensor = NULL;625SDL_sensorlist_item *item_sensor = NULL;626627SDL_AssertJoysticksLocked();628629item = SDL_joylist;630while (item) {631if (item->hwdata && item->hwdata->gone) {632RemoveJoylistItem(item, prev);633634if (prev) {635item = prev->next;636} else {637item = SDL_joylist;638}639} else {640prev = item;641item = item->next;642}643}644645item_sensor = SDL_sensorlist;646while (item_sensor) {647if (item_sensor->hwdata && item_sensor->hwdata->sensor_gone) {648RemoveSensorlistItem(item_sensor, prev_sensor);649650if (prev_sensor) {651item_sensor = prev_sensor->next;652} else {653item_sensor = SDL_sensorlist;654}655} else {656prev_sensor = item_sensor;657item_sensor = item_sensor->next;658}659}660}661662static bool StrIsInteger(const char *string)663{664const char *p;665666if (*string == '\0') {667return false;668}669670for (p = string; *p != '\0'; p++) {671if (*p < '0' || *p > '9') {672return false;673}674}675676return true;677}678679static bool IsJoystickJSNode(const char *node)680{681const char *last_slash = SDL_strrchr(node, '/');682if (last_slash) {683node = last_slash + 1;684}685return SDL_startswith(node, "js") && StrIsInteger(node + 2);686}687688static bool IsJoystickEventNode(const char *node)689{690const char *last_slash = SDL_strrchr(node, '/');691if (last_slash) {692node = last_slash + 1;693}694return SDL_startswith(node, "event") && StrIsInteger(node + 5);695}696697static bool IsJoystickDeviceNode(const char *node)698{699if (SDL_classic_joysticks) {700return IsJoystickJSNode(node);701} else {702return IsJoystickEventNode(node);703}704}705706#ifdef HAVE_INOTIFY707#ifdef HAVE_INOTIFY_INIT1708static int SDL_inotify_init1(void)709{710return inotify_init1(IN_NONBLOCK | IN_CLOEXEC);711}712#else713static int SDL_inotify_init1(void)714{715int fd = inotify_init();716if (fd < 0) {717return -1;718}719fcntl(fd, F_SETFL, O_NONBLOCK);720fcntl(fd, F_SETFD, FD_CLOEXEC);721return fd;722}723#endif724725static void LINUX_InotifyJoystickDetect(void)726{727union728{729struct inotify_event event;730char storage[4096];731char enough_for_inotify[sizeof(struct inotify_event) + NAME_MAX + 1];732} buf;733ssize_t bytes;734size_t remain = 0;735size_t len;736char path[PATH_MAX];737738bytes = read(inotify_fd, &buf, sizeof(buf));739740if (bytes > 0) {741remain = (size_t)bytes;742}743744while (remain > 0) {745if (buf.event.len > 0) {746if (IsJoystickDeviceNode(buf.event.name)) {747(void)SDL_snprintf(path, SDL_arraysize(path), "/dev/input/%s", buf.event.name);748749if (buf.event.mask & (IN_CREATE | IN_MOVED_TO | IN_ATTRIB)) {750MaybeAddDevice(path);751} else if (buf.event.mask & (IN_DELETE | IN_MOVED_FROM)) {752MaybeRemoveDevice(path);753}754}755}756757len = sizeof(struct inotify_event) + buf.event.len;758remain -= len;759760if (remain != 0) {761SDL_memmove(&buf.storage[0], &buf.storage[len], remain);762}763}764}765#endif // HAVE_INOTIFY766767static int get_event_joystick_index(int event)768{769int joystick_index = -1;770int i, count;771struct dirent **entries = NULL;772char path[PATH_MAX];773774(void)SDL_snprintf(path, SDL_arraysize(path), "/sys/class/input/event%d/device", event);775count = scandir(path, &entries, NULL, alphasort);776for (i = 0; i < count; ++i) {777if (SDL_strncmp(entries[i]->d_name, "js", 2) == 0) {778joystick_index = SDL_atoi(entries[i]->d_name + 2);779}780free(entries[i]); // This should NOT be SDL_free()781}782free(entries); // This should NOT be SDL_free()783784return joystick_index;785}786787/* Detect devices by reading /dev/input. In the inotify code path we788* have to do this the first time, to detect devices that already existed789* before we started; in the non-inotify code path we do this repeatedly790* (polling). */791static int filter_entries(const struct dirent *entry)792{793return IsJoystickDeviceNode(entry->d_name);794}795static int SDLCALL sort_entries(const void *_a, const void *_b)796{797const struct dirent **a = (const struct dirent **)_a;798const struct dirent **b = (const struct dirent **)_b;799int numA, numB;800int offset;801802if (SDL_classic_joysticks) {803offset = 2; // strlen("js")804numA = SDL_atoi((*a)->d_name + offset);805numB = SDL_atoi((*b)->d_name + offset);806} else {807offset = 5; // strlen("event")808numA = SDL_atoi((*a)->d_name + offset);809numB = SDL_atoi((*b)->d_name + offset);810811// See if we can get the joystick ordering812{813int jsA = get_event_joystick_index(numA);814int jsB = get_event_joystick_index(numB);815if (jsA >= 0 && jsB >= 0) {816numA = jsA;817numB = jsB;818} else if (jsA >= 0) {819return -1;820} else if (jsB >= 0) {821return 1;822}823}824}825return numA - numB;826}827828typedef struct829{830char *path;831int slot;832} VirtualGamepadEntry;833834static int SDLCALL sort_virtual_gamepads(const void *_a, const void *_b)835{836const VirtualGamepadEntry *a = (const VirtualGamepadEntry *)_a;837const VirtualGamepadEntry *b = (const VirtualGamepadEntry *)_b;838return a->slot - b->slot;839}840841static void LINUX_ScanSteamVirtualGamepads(void)842{843int i, count;844int fd;845struct dirent **entries = NULL;846char path[PATH_MAX];847struct input_id inpid;848int num_virtual_gamepads = 0;849int virtual_gamepad_slot;850VirtualGamepadEntry *virtual_gamepads = NULL;851#ifdef SDL_USE_LIBUDEV852int class;853#endif854855count = scandir("/dev/input", &entries, filter_entries, NULL);856for (i = 0; i < count; ++i) {857(void)SDL_snprintf(path, SDL_arraysize(path), "/dev/input/%s", entries[i]->d_name);858859#ifdef SDL_USE_LIBUDEV860// Opening input devices can generate synchronous device I/O, so avoid it if we can861class = 0;862SDL_zero(inpid);863if (SDL_UDEV_GetProductInfo(path, &inpid.vendor, &inpid.product, &inpid.version, &class) &&864(inpid.vendor != USB_VENDOR_VALVE || inpid.product != USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD)) {865free(entries[i]); // This should NOT be SDL_free()866continue;867}868#endif869fd = open(path, O_RDONLY | O_CLOEXEC, 0);870if (fd >= 0) {871if (ioctl(fd, EVIOCGID, &inpid) == 0 &&872inpid.vendor == USB_VENDOR_VALVE &&873inpid.product == USB_PRODUCT_STEAM_VIRTUAL_GAMEPAD &&874GetSteamVirtualGamepadSlot(fd, &virtual_gamepad_slot)) {875VirtualGamepadEntry *new_virtual_gamepads = (VirtualGamepadEntry *)SDL_realloc(virtual_gamepads, (num_virtual_gamepads + 1) * sizeof(*virtual_gamepads));876if (new_virtual_gamepads) {877VirtualGamepadEntry *entry = &new_virtual_gamepads[num_virtual_gamepads];878entry->path = SDL_strdup(path);879entry->slot = virtual_gamepad_slot;880if (entry->path) {881virtual_gamepads = new_virtual_gamepads;882++num_virtual_gamepads;883} else {884SDL_free(entry->path);885SDL_free(new_virtual_gamepads);886}887}888}889close(fd);890}891free(entries[i]); // This should NOT be SDL_free()892}893free(entries); // This should NOT be SDL_free()894895if (num_virtual_gamepads > 1) {896SDL_qsort(virtual_gamepads, num_virtual_gamepads, sizeof(*virtual_gamepads), sort_virtual_gamepads);897}898for (i = 0; i < num_virtual_gamepads; ++i) {899MaybeAddDevice(virtual_gamepads[i].path);900SDL_free(virtual_gamepads[i].path);901}902SDL_free(virtual_gamepads);903}904905static void LINUX_ScanInputDevices(void)906{907int i, count;908struct dirent **entries = NULL;909char path[PATH_MAX];910911count = scandir("/dev/input", &entries, filter_entries, NULL);912if (count > 1) {913SDL_qsort(entries, count, sizeof(*entries), sort_entries);914}915for (i = 0; i < count; ++i) {916(void)SDL_snprintf(path, SDL_arraysize(path), "/dev/input/%s", entries[i]->d_name);917MaybeAddDevice(path);918919free(entries[i]); // This should NOT be SDL_free()920}921free(entries); // This should NOT be SDL_free()922}923924static void LINUX_FallbackJoystickDetect(void)925{926const Uint32 SDL_JOY_DETECT_INTERVAL_MS = 3000; // Update every 3 seconds927Uint64 now = SDL_GetTicks();928929if (!last_joy_detect_time || now >= (last_joy_detect_time + SDL_JOY_DETECT_INTERVAL_MS)) {930struct stat sb;931932// Opening input devices can generate synchronous device I/O, so avoid it if we can933if (stat("/dev/input", &sb) == 0 && sb.st_mtime != last_input_dir_mtime) {934// Look for Steam virtual gamepads first, and sort by Steam controller slot935LINUX_ScanSteamVirtualGamepads();936937LINUX_ScanInputDevices();938939last_input_dir_mtime = sb.st_mtime;940}941942last_joy_detect_time = now;943}944}945946static void LINUX_JoystickDetect(void)947{948#ifdef SDL_USE_LIBUDEV949if (enumeration_method == ENUMERATION_LIBUDEV) {950SDL_UDEV_Poll();951} else952#endif953#ifdef HAVE_INOTIFY954if (inotify_fd >= 0 && last_joy_detect_time != 0) {955LINUX_InotifyJoystickDetect();956} else957#endif958{959LINUX_FallbackJoystickDetect();960}961962HandlePendingRemovals();963}964965static bool LINUX_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)966{967// We don't override any other drivers968return false;969}970971static bool LINUX_JoystickInit(void)972{973const char *devices = SDL_GetHint(SDL_HINT_JOYSTICK_DEVICE);974#ifdef SDL_USE_LIBUDEV975bool udev_initialized = SDL_UDEV_Init();976#endif977978SDL_classic_joysticks = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_LINUX_CLASSIC, false);979980enumeration_method = ENUMERATION_UNSET;981982// First see if the user specified one or more joysticks to use983if (devices) {984char *envcopy, *envpath, *delim;985envcopy = SDL_strdup(devices);986envpath = envcopy;987while (envpath) {988delim = SDL_strchr(envpath, ':');989if (delim) {990*delim++ = '\0';991}992MaybeAddDevice(envpath);993envpath = delim;994}995SDL_free(envcopy);996}997998// Force immediate joystick detection if using fallback999last_joy_detect_time = 0;1000last_input_dir_mtime = 0;10011002// Manually scan first, since we sort by device number and udev doesn't1003LINUX_JoystickDetect();10041005#ifdef SDL_USE_LIBUDEV1006if (enumeration_method == ENUMERATION_UNSET) {1007if (SDL_GetHintBoolean("SDL_JOYSTICK_DISABLE_UDEV", false)) {1008SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,1009"udev disabled by SDL_JOYSTICK_DISABLE_UDEV");1010enumeration_method = ENUMERATION_FALLBACK;1011} else if (SDL_GetSandbox() != SDL_SANDBOX_NONE) {1012SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,1013"Container detected, disabling udev integration");1014enumeration_method = ENUMERATION_FALLBACK;10151016} else {1017SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,1018"Using udev for joystick device discovery");1019enumeration_method = ENUMERATION_LIBUDEV;1020}1021}10221023if (enumeration_method == ENUMERATION_LIBUDEV) {1024if (udev_initialized) {1025// Set up the udev callback1026if (!SDL_UDEV_AddCallback(joystick_udev_callback)) {1027SDL_UDEV_Quit();1028return SDL_SetError("Could not set up joystick <-> udev callback");1029}10301031// Force a scan to build the initial device list1032SDL_UDEV_Scan();1033} else {1034SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,1035"udev init failed, disabling udev integration");1036enumeration_method = ENUMERATION_FALLBACK;1037}1038} else {1039if (udev_initialized) {1040SDL_UDEV_Quit();1041}1042}1043#endif10441045if (enumeration_method != ENUMERATION_LIBUDEV) {1046#ifdef HAVE_INOTIFY1047inotify_fd = SDL_inotify_init1();10481049if (inotify_fd < 0) {1050SDL_LogWarn(SDL_LOG_CATEGORY_INPUT,1051"Unable to initialize inotify, falling back to polling: %s",1052strerror(errno));1053} else {1054/* We need to watch for attribute changes in addition to1055* creation, because when a device is first created, it has1056* permissions that we can't read. When udev chmods it to1057* something that we maybe *can* read, we'll get an1058* IN_ATTRIB event to tell us. */1059if (inotify_add_watch(inotify_fd, "/dev/input",1060IN_CREATE | IN_DELETE | IN_MOVE | IN_ATTRIB) < 0) {1061close(inotify_fd);1062inotify_fd = -1;1063SDL_LogWarn(SDL_LOG_CATEGORY_INPUT,1064"Unable to add inotify watch, falling back to polling: %s",1065strerror(errno));1066}1067}1068#endif // HAVE_INOTIFY1069}10701071return true;1072}10731074static int LINUX_JoystickGetCount(void)1075{1076SDL_AssertJoysticksLocked();10771078return numjoysticks;1079}10801081static SDL_joylist_item *GetJoystickByDevIndex(int device_index)1082{1083SDL_joylist_item *item;10841085SDL_AssertJoysticksLocked();10861087if ((device_index < 0) || (device_index >= numjoysticks)) {1088return NULL;1089}10901091item = SDL_joylist;1092while (device_index > 0) {1093SDL_assert(item != NULL);1094device_index--;1095item = item->next;1096}10971098return item;1099}11001101static const char *LINUX_JoystickGetDeviceName(int device_index)1102{1103return GetJoystickByDevIndex(device_index)->name;1104}11051106static const char *LINUX_JoystickGetDevicePath(int device_index)1107{1108return GetJoystickByDevIndex(device_index)->path;1109}11101111static int LINUX_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)1112{1113return GetJoystickByDevIndex(device_index)->steam_virtual_gamepad_slot;1114}11151116static int LINUX_JoystickGetDevicePlayerIndex(int device_index)1117{1118return -1;1119}11201121static void LINUX_JoystickSetDevicePlayerIndex(int device_index, int player_index)1122{1123}11241125static SDL_GUID LINUX_JoystickGetDeviceGUID(int device_index)1126{1127return GetJoystickByDevIndex(device_index)->guid;1128}11291130// Function to perform the mapping from device index to the instance id for this index1131static SDL_JoystickID LINUX_JoystickGetDeviceInstanceID(int device_index)1132{1133return GetJoystickByDevIndex(device_index)->device_instance;1134}11351136static bool allocate_balldata(SDL_Joystick *joystick)1137{1138joystick->hwdata->balls =1139(struct hwdata_ball *)SDL_calloc(joystick->nballs, sizeof(struct hwdata_ball));1140if (joystick->hwdata->balls == NULL) {1141return false;1142}1143return true;1144}11451146static bool allocate_hatdata(SDL_Joystick *joystick)1147{1148int i;11491150SDL_AssertJoysticksLocked();11511152joystick->hwdata->hats =1153(struct hwdata_hat *)SDL_malloc(joystick->nhats *1154sizeof(struct hwdata_hat));1155if (!joystick->hwdata->hats) {1156return false;1157}1158for (i = 0; i < joystick->nhats; ++i) {1159joystick->hwdata->hats[i].axis[0] = 1;1160joystick->hwdata->hats[i].axis[1] = 1;1161}1162return true;1163}11641165static bool GuessIfAxesAreDigitalHat(struct input_absinfo *absinfo_x, struct input_absinfo *absinfo_y)1166{1167/* A "hat" is assumed to be a digital input with at most 9 possible states1168* (3 per axis: negative/zero/positive), as opposed to a true "axis" which1169* can report a continuous range of possible values. Unfortunately the Linux1170* joystick interface makes no distinction between digital hat axes and any1171* other continuous analog axis, so we have to guess. */11721173// If both axes are missing, they're not anything.1174if (!absinfo_x && !absinfo_y) {1175return false;1176}11771178// If the hint says so, treat all hats as digital.1179if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_LINUX_DIGITAL_HATS, false)) {1180return true;1181}11821183// If both axes have ranges constrained between -1 and 1, they're definitely digital.1184if ((!absinfo_x || (absinfo_x->minimum == -1 && absinfo_x->maximum == 1)) && (!absinfo_y || (absinfo_y->minimum == -1 && absinfo_y->maximum == 1))) {1185return true;1186}11871188// If both axes lack fuzz, flat, and resolution values, they're probably digital.1189if ((!absinfo_x || (!absinfo_x->fuzz && !absinfo_x->flat && !absinfo_x->resolution)) && (!absinfo_y || (!absinfo_y->fuzz && !absinfo_y->flat && !absinfo_y->resolution))) {1190return true;1191}11921193// Otherwise, treat them as analog.1194return false;1195}11961197static void ConfigJoystick(SDL_Joystick *joystick, int fd, int fd_sensor)1198{1199int i, t;1200unsigned long keybit[NBITS(KEY_MAX)] = { 0 };1201unsigned long absbit[NBITS(ABS_MAX)] = { 0 };1202unsigned long relbit[NBITS(REL_MAX)] = { 0 };1203unsigned long ffbit[NBITS(FF_MAX)] = { 0 };1204Uint8 key_pam_size, abs_pam_size;1205bool use_deadzones = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_LINUX_DEADZONES, false);1206bool use_hat_deadzones = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_LINUX_HAT_DEADZONES, true);12071208SDL_AssertJoysticksLocked();12091210// See if this device uses the new unified event API1211if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&1212(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) &&1213(ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0)) {12141215// Get the number of buttons, axes, and other thingamajigs1216for (i = BTN_JOYSTICK; i < KEY_MAX; ++i) {1217if (test_bit(i, keybit)) {1218#ifdef DEBUG_INPUT_EVENTS1219SDL_Log("Joystick has button: 0x%x", i);1220#endif1221joystick->hwdata->key_map[i] = joystick->nbuttons;1222joystick->hwdata->has_key[i] = true;1223++joystick->nbuttons;1224}1225}1226for (i = 0; i < BTN_JOYSTICK; ++i) {1227if (test_bit(i, keybit)) {1228#ifdef DEBUG_INPUT_EVENTS1229SDL_Log("Joystick has button: 0x%x", i);1230#endif1231joystick->hwdata->key_map[i] = joystick->nbuttons;1232joystick->hwdata->has_key[i] = true;1233++joystick->nbuttons;1234}1235}1236for (i = ABS_HAT0X; i <= ABS_HAT3Y; i += 2) {1237int hat_x = -1;1238int hat_y = -1;1239struct input_absinfo absinfo_x;1240struct input_absinfo absinfo_y;1241if (test_bit(i, absbit)) {1242hat_x = ioctl(fd, EVIOCGABS(i), &absinfo_x);1243}1244if (test_bit(i + 1, absbit)) {1245hat_y = ioctl(fd, EVIOCGABS(i + 1), &absinfo_y);1246}1247if (GuessIfAxesAreDigitalHat((hat_x < 0 ? (void *)0 : &absinfo_x),1248(hat_y < 0 ? (void *)0 : &absinfo_y))) {1249const int hat_index = (i - ABS_HAT0X) / 2;1250struct hat_axis_correct *correct = &joystick->hwdata->hat_correct[hat_index];1251#ifdef DEBUG_INPUT_EVENTS1252SDL_Log("Joystick has digital hat: #%d", hat_index);1253if (hat_x >= 0) {1254SDL_Log("X Values = { val:%d, min:%d, max:%d, fuzz:%d, flat:%d, res:%d }",1255absinfo_x.value, absinfo_x.minimum, absinfo_x.maximum,1256absinfo_x.fuzz, absinfo_x.flat, absinfo_x.resolution);1257}1258if (hat_y >= 0) {1259SDL_Log("Y Values = { val:%d, min:%d, max:%d, fuzz:%d, flat:%d, res:%d }",1260absinfo_y.value, absinfo_y.minimum, absinfo_y.maximum,1261absinfo_y.fuzz, absinfo_y.flat, absinfo_y.resolution);1262}1263#endif // DEBUG_INPUT_EVENTS1264joystick->hwdata->hats_indices[hat_index] = joystick->nhats;1265joystick->hwdata->has_hat[hat_index] = true;1266correct->use_deadzones = use_hat_deadzones;1267correct->minimum[0] = (hat_x < 0) ? -1 : absinfo_x.minimum;1268correct->maximum[0] = (hat_x < 0) ? 1 : absinfo_x.maximum;1269correct->minimum[1] = (hat_y < 0) ? -1 : absinfo_y.minimum;1270correct->maximum[1] = (hat_y < 0) ? 1 : absinfo_y.maximum;1271++joystick->nhats;1272}1273}1274for (i = 0; i < ABS_MAX; ++i) {1275// Skip digital hats1276if (i >= ABS_HAT0X && i <= ABS_HAT3Y && joystick->hwdata->has_hat[(i - ABS_HAT0X) / 2]) {1277continue;1278}1279if (test_bit(i, absbit)) {1280struct input_absinfo absinfo;1281struct axis_correct *correct = &joystick->hwdata->abs_correct[i];12821283if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {1284continue;1285}1286#ifdef DEBUG_INPUT_EVENTS1287SDL_Log("Joystick has absolute axis: 0x%.2x", i);1288SDL_Log("Values = { val:%d, min:%d, max:%d, fuzz:%d, flat:%d, res:%d }",1289absinfo.value, absinfo.minimum, absinfo.maximum,1290absinfo.fuzz, absinfo.flat, absinfo.resolution);1291#endif // DEBUG_INPUT_EVENTS1292joystick->hwdata->abs_map[i] = joystick->naxes;1293joystick->hwdata->has_abs[i] = true;12941295correct->minimum = absinfo.minimum;1296correct->maximum = absinfo.maximum;1297if (correct->minimum != correct->maximum) {1298if (use_deadzones) {1299correct->use_deadzones = true;1300correct->coef[0] = (absinfo.maximum + absinfo.minimum) - 2 * absinfo.flat;1301correct->coef[1] = (absinfo.maximum + absinfo.minimum) + 2 * absinfo.flat;1302t = ((absinfo.maximum - absinfo.minimum) - 4 * absinfo.flat);1303if (t != 0) {1304correct->coef[2] = (1 << 28) / t;1305} else {1306correct->coef[2] = 0;1307}1308} else {1309float value_range = (correct->maximum - correct->minimum);1310float output_range = (SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN);13111312correct->scale = (output_range / value_range);1313}1314}1315++joystick->naxes;1316}1317}1318if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) {1319++joystick->nballs;1320}13211322} else if ((ioctl(fd, JSIOCGBUTTONS, &key_pam_size, sizeof(key_pam_size)) >= 0) &&1323(ioctl(fd, JSIOCGAXES, &abs_pam_size, sizeof(abs_pam_size)) >= 0)) {1324size_t len;13251326joystick->hwdata->classic = true;13271328len = (KEY_MAX - BTN_MISC + 1) * sizeof(*joystick->hwdata->key_pam);1329joystick->hwdata->key_pam = (Uint16 *)SDL_calloc(1, len);1330if (joystick->hwdata->key_pam) {1331if (ioctl(fd, JSIOCGBTNMAP, joystick->hwdata->key_pam, len) < 0) {1332SDL_free(joystick->hwdata->key_pam);1333joystick->hwdata->key_pam = NULL;1334key_pam_size = 0;1335}1336} else {1337key_pam_size = 0;1338}1339for (i = 0; i < key_pam_size; ++i) {1340Uint16 code = joystick->hwdata->key_pam[i];1341#ifdef DEBUG_INPUT_EVENTS1342SDL_Log("Joystick has button: 0x%x", code);1343#endif1344joystick->hwdata->key_map[code] = joystick->nbuttons;1345joystick->hwdata->has_key[code] = true;1346++joystick->nbuttons;1347}13481349len = ABS_CNT * sizeof(*joystick->hwdata->abs_pam);1350joystick->hwdata->abs_pam = (Uint8 *)SDL_calloc(1, len);1351if (joystick->hwdata->abs_pam) {1352if (ioctl(fd, JSIOCGAXMAP, joystick->hwdata->abs_pam, len) < 0) {1353SDL_free(joystick->hwdata->abs_pam);1354joystick->hwdata->abs_pam = NULL;1355abs_pam_size = 0;1356}1357} else {1358abs_pam_size = 0;1359}1360for (i = 0; i < abs_pam_size; ++i) {1361Uint8 code = joystick->hwdata->abs_pam[i];13621363// TODO: is there any way to detect analog hats in advance via this API?1364if (code >= ABS_HAT0X && code <= ABS_HAT3Y) {1365int hat_index = (code - ABS_HAT0X) / 2;1366if (!joystick->hwdata->has_hat[hat_index]) {1367#ifdef DEBUG_INPUT_EVENTS1368SDL_Log("Joystick has digital hat: #%d", hat_index);1369#endif1370joystick->hwdata->hats_indices[hat_index] = joystick->nhats++;1371joystick->hwdata->has_hat[hat_index] = true;1372joystick->hwdata->hat_correct[hat_index].minimum[0] = -1;1373joystick->hwdata->hat_correct[hat_index].maximum[0] = 1;1374joystick->hwdata->hat_correct[hat_index].minimum[1] = -1;1375joystick->hwdata->hat_correct[hat_index].maximum[1] = 1;1376}1377} else {1378#ifdef DEBUG_INPUT_EVENTS1379SDL_Log("Joystick has absolute axis: 0x%.2x", code);1380#endif1381joystick->hwdata->abs_map[code] = joystick->naxes;1382joystick->hwdata->has_abs[code] = true;1383++joystick->naxes;1384}1385}1386}13871388// Sensors are only available through the new unified event API1389if (fd_sensor >= 0 && (ioctl(fd_sensor, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0)) {1390if (test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit) && test_bit(ABS_Z, absbit)) {1391joystick->hwdata->has_accelerometer = true;1392for (i = 0; i < 3; ++i) {1393struct input_absinfo absinfo;1394if (ioctl(fd_sensor, EVIOCGABS(ABS_X + i), &absinfo) < 0) {1395joystick->hwdata->has_accelerometer = false;1396break; // do not report an accelerometer if we can't read all axes1397}1398joystick->hwdata->accelerometer_scale[i] = absinfo.resolution;1399#ifdef DEBUG_INPUT_EVENTS1400SDL_Log("Joystick has accelerometer axis: 0x%.2x", ABS_X + i);1401SDL_Log("Values = { val:%d, min:%d, max:%d, fuzz:%d, flat:%d, res:%d }",1402absinfo.value, absinfo.minimum, absinfo.maximum,1403absinfo.fuzz, absinfo.flat, absinfo.resolution);1404#endif // DEBUG_INPUT_EVENTS1405}1406}14071408if (test_bit(ABS_RX, absbit) && test_bit(ABS_RY, absbit) && test_bit(ABS_RZ, absbit)) {1409joystick->hwdata->has_gyro = true;1410for (i = 0; i < 3; ++i) {1411struct input_absinfo absinfo;1412if (ioctl(fd_sensor, EVIOCGABS(ABS_RX + i), &absinfo) < 0) {1413joystick->hwdata->has_gyro = false;1414break; // do not report a gyro if we can't read all axes1415}1416joystick->hwdata->gyro_scale[i] = absinfo.resolution;1417#ifdef DEBUG_INPUT_EVENTS1418SDL_Log("Joystick has gyro axis: 0x%.2x", ABS_RX + i);1419SDL_Log("Values = { val:%d, min:%d, max:%d, fuzz:%d, flat:%d, res:%d }",1420absinfo.value, absinfo.minimum, absinfo.maximum,1421absinfo.fuzz, absinfo.flat, absinfo.resolution);1422#endif // DEBUG_INPUT_EVENTS1423}1424}1425}14261427// Allocate data to keep track of these thingamajigs1428if (joystick->nballs > 0) {1429if (!allocate_balldata(joystick)) {1430joystick->nballs = 0;1431}1432}1433if (joystick->nhats > 0) {1434if (!allocate_hatdata(joystick)) {1435joystick->nhats = 0;1436}1437}14381439if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) >= 0) {1440if (test_bit(FF_RUMBLE, ffbit)) {1441joystick->hwdata->ff_rumble = true;1442}1443if (test_bit(FF_SINE, ffbit)) {1444joystick->hwdata->ff_sine = true;1445}1446}1447}14481449/* This is used to do the heavy lifting for LINUX_JoystickOpen and1450also LINUX_JoystickGetGamepadMapping, so we can query the hardware1451without adding an opened SDL_Joystick object to the system.1452This expects `joystick->hwdata` to be allocated and will not free it1453on error. Returns -1 on error, 0 on success. */1454static bool PrepareJoystickHwdata(SDL_Joystick *joystick, SDL_joylist_item *item, SDL_sensorlist_item *item_sensor)1455{1456SDL_AssertJoysticksLocked();14571458joystick->hwdata->item = item;1459joystick->hwdata->item_sensor = item_sensor;1460joystick->hwdata->guid = item->guid;1461joystick->hwdata->effect.id = -1;1462SDL_memset(joystick->hwdata->key_map, 0xFF, sizeof(joystick->hwdata->key_map));1463SDL_memset(joystick->hwdata->abs_map, 0xFF, sizeof(joystick->hwdata->abs_map));14641465int fd = -1, fd_sensor = -1;1466// Try read-write first, so we can do rumble1467fd = open(item->path, O_RDWR | O_CLOEXEC, 0);1468if (fd < 0) {1469// Try read-only again, at least we'll get events in this case1470fd = open(item->path, O_RDONLY | O_CLOEXEC, 0);1471}1472if (fd < 0) {1473return SDL_SetError("Unable to open %s", item->path);1474}1475// If opening sensor fail, continue with buttons and axes only1476if (item_sensor) {1477fd_sensor = open(item_sensor->path, O_RDONLY | O_CLOEXEC, 0);1478}14791480joystick->hwdata->fd = fd;1481joystick->hwdata->fd_sensor = fd_sensor;1482joystick->hwdata->fname = SDL_strdup(item->path);1483if (!joystick->hwdata->fname) {1484close(fd);1485if (fd_sensor >= 0) {1486close(fd_sensor);1487}1488return false;1489}14901491// Set the joystick to non-blocking read mode1492fcntl(fd, F_SETFL, O_NONBLOCK);1493if (fd_sensor >= 0) {1494fcntl(fd_sensor, F_SETFL, O_NONBLOCK);1495}14961497// Get the number of buttons and axes on the joystick1498ConfigJoystick(joystick, fd, fd_sensor);1499return true;1500}15011502static SDL_sensorlist_item *GetSensor(SDL_joylist_item *item)1503{1504SDL_sensorlist_item *item_sensor;1505char uniq_item[128];1506int fd_item = -1;15071508SDL_AssertJoysticksLocked();15091510if (!item || !SDL_sensorlist) {1511return NULL;1512}15131514SDL_memset(uniq_item, 0, sizeof(uniq_item));1515fd_item = open(item->path, O_RDONLY | O_CLOEXEC, 0);1516if (fd_item < 0) {1517return NULL;1518}1519if (ioctl(fd_item, EVIOCGUNIQ(sizeof(uniq_item) - 1), &uniq_item) < 0) {1520close(fd_item);1521return NULL;1522}1523close(fd_item);1524#ifdef DEBUG_INPUT_EVENTS1525SDL_Log("Joystick UNIQ: %s", uniq_item);1526#endif // DEBUG_INPUT_EVENTS15271528for (item_sensor = SDL_sensorlist; item_sensor; item_sensor = item_sensor->next) {1529char uniq_sensor[128];1530int fd_sensor = -1;1531if (item_sensor->hwdata) {1532// already associated with another joystick1533continue;1534}15351536SDL_memset(uniq_sensor, 0, sizeof(uniq_sensor));1537fd_sensor = open(item_sensor->path, O_RDONLY | O_CLOEXEC, 0);1538if (fd_sensor < 0) {1539continue;1540}1541if (ioctl(fd_sensor, EVIOCGUNIQ(sizeof(uniq_sensor) - 1), &uniq_sensor) < 0) {1542close(fd_sensor);1543continue;1544}1545close(fd_sensor);1546#ifdef DEBUG_INPUT_EVENTS1547SDL_Log("Sensor UNIQ: %s", uniq_sensor);1548#endif // DEBUG_INPUT_EVENTS15491550if (SDL_strcmp(uniq_item, uniq_sensor) == 0) {1551return item_sensor;1552}1553}1554return NULL;1555}15561557static bool LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)1558{1559SDL_joylist_item *item;1560SDL_sensorlist_item *item_sensor;15611562SDL_AssertJoysticksLocked();15631564item = GetJoystickByDevIndex(device_index);1565if (!item) {1566return SDL_SetError("No such device");1567}15681569joystick->hwdata = (struct joystick_hwdata *)1570SDL_calloc(1, sizeof(*joystick->hwdata));1571if (!joystick->hwdata) {1572return false;1573}15741575item_sensor = GetSensor(item);1576if (!PrepareJoystickHwdata(joystick, item, item_sensor)) {1577SDL_free(joystick->hwdata);1578joystick->hwdata = NULL;1579return false; // SDL_SetError will already have been called1580}15811582SDL_assert(item->hwdata == NULL);1583SDL_assert(!item_sensor || item_sensor->hwdata == NULL);1584item->hwdata = joystick->hwdata;1585if (item_sensor) {1586item_sensor->hwdata = joystick->hwdata;1587}15881589// mark joystick as fresh and ready1590joystick->hwdata->fresh = true;15911592if (joystick->hwdata->has_gyro) {1593SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 0.0f);1594}1595if (joystick->hwdata->has_accelerometer) {1596SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 0.0f);1597}1598if (joystick->hwdata->fd_sensor >= 0) {1599// Don't keep fd_sensor opened while sensor is disabled1600close(joystick->hwdata->fd_sensor);1601joystick->hwdata->fd_sensor = -1;1602}16031604if (joystick->hwdata->ff_rumble || joystick->hwdata->ff_sine) {1605SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true);1606}1607return true;1608}16091610static bool LINUX_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)1611{1612struct input_event event;16131614SDL_AssertJoysticksLocked();16151616if (joystick->hwdata->ff_rumble) {1617struct ff_effect *effect = &joystick->hwdata->effect;16181619effect->type = FF_RUMBLE;1620effect->replay.length = SDL_MAX_RUMBLE_DURATION_MS;1621effect->u.rumble.strong_magnitude = low_frequency_rumble;1622effect->u.rumble.weak_magnitude = high_frequency_rumble;1623} else if (joystick->hwdata->ff_sine) {1624// Scale and average the two rumble strengths1625Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);1626struct ff_effect *effect = &joystick->hwdata->effect;16271628effect->type = FF_PERIODIC;1629effect->replay.length = SDL_MAX_RUMBLE_DURATION_MS;1630effect->u.periodic.waveform = FF_SINE;1631effect->u.periodic.magnitude = magnitude;1632} else {1633return SDL_Unsupported();1634}16351636if (ioctl(joystick->hwdata->fd, EVIOCSFF, &joystick->hwdata->effect) < 0) {1637// The kernel may have lost this effect, try to allocate a new one1638joystick->hwdata->effect.id = -1;1639if (ioctl(joystick->hwdata->fd, EVIOCSFF, &joystick->hwdata->effect) < 0) {1640return SDL_SetError("Couldn't update rumble effect: %s", strerror(errno));1641}1642}16431644event.type = EV_FF;1645event.code = joystick->hwdata->effect.id;1646event.value = 1;1647if (write(joystick->hwdata->fd, &event, sizeof(event)) < 0) {1648return SDL_SetError("Couldn't start rumble effect: %s", strerror(errno));1649}1650return true;1651}16521653static bool LINUX_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)1654{1655return SDL_Unsupported();1656}16571658static bool LINUX_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)1659{1660return SDL_Unsupported();1661}16621663static bool LINUX_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)1664{1665return SDL_Unsupported();1666}16671668static bool LINUX_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled)1669{1670SDL_AssertJoysticksLocked();16711672if (!joystick->hwdata->has_accelerometer && !joystick->hwdata->has_gyro) {1673return SDL_Unsupported();1674}1675if (enabled == joystick->hwdata->report_sensor) {1676return true;1677}16781679if (enabled) {1680if (!joystick->hwdata->item_sensor) {1681return SDL_SetError("Sensors unplugged.");1682}1683joystick->hwdata->fd_sensor = open(joystick->hwdata->item_sensor->path, O_RDONLY | O_CLOEXEC, 0);1684if (joystick->hwdata->fd_sensor < 0) {1685return SDL_SetError("Couldn't open sensor file %s.", joystick->hwdata->item_sensor->path);1686}1687fcntl(joystick->hwdata->fd_sensor, F_SETFL, O_NONBLOCK);1688} else {1689SDL_assert(joystick->hwdata->fd_sensor >= 0);1690close(joystick->hwdata->fd_sensor);1691joystick->hwdata->fd_sensor = -1;1692}16931694joystick->hwdata->report_sensor = enabled;1695return true;1696}16971698static void HandleHat(Uint64 timestamp, SDL_Joystick *stick, int hatidx, int axis, int value)1699{1700int hatnum;1701struct hwdata_hat *the_hat;1702struct hat_axis_correct *correct;1703const Uint8 position_map[3][3] = {1704{ SDL_HAT_LEFTUP, SDL_HAT_UP, SDL_HAT_RIGHTUP },1705{ SDL_HAT_LEFT, SDL_HAT_CENTERED, SDL_HAT_RIGHT },1706{ SDL_HAT_LEFTDOWN, SDL_HAT_DOWN, SDL_HAT_RIGHTDOWN }1707};17081709SDL_AssertJoysticksLocked();17101711hatnum = stick->hwdata->hats_indices[hatidx];1712the_hat = &stick->hwdata->hats[hatnum];1713correct = &stick->hwdata->hat_correct[hatidx];1714/* Hopefully we detected any analog axes and left them as is rather than trying1715* to use them as digital hats, but just in case, the deadzones here will1716* prevent the slightest of twitches on an analog axis from registering as a hat1717* movement. If the axes really are digital, this won't hurt since they should1718* only ever be sending min, 0, or max anyway. */1719if (value < 0) {1720if (value <= correct->minimum[axis]) {1721correct->minimum[axis] = value;1722value = 0;1723} else if (!correct->use_deadzones || value < correct->minimum[axis] / 3) {1724value = 0;1725} else {1726value = 1;1727}1728} else if (value > 0) {1729if (value >= correct->maximum[axis]) {1730correct->maximum[axis] = value;1731value = 2;1732} else if (!correct->use_deadzones || value > correct->maximum[axis] / 3) {1733value = 2;1734} else {1735value = 1;1736}1737} else { // value == 01738value = 1;1739}1740if (value != the_hat->axis[axis]) {1741the_hat->axis[axis] = value;1742SDL_SendJoystickHat(timestamp, stick, hatnum,1743position_map[the_hat->axis[1]][the_hat->axis[0]]);1744}1745}17461747static void HandleBall(SDL_Joystick *stick, Uint8 ball, int axis, int value)1748{1749stick->hwdata->balls[ball].axis[axis] += value;1750}17511752static int AxisCorrect(SDL_Joystick *joystick, int which, int value)1753{1754struct axis_correct *correct;17551756SDL_AssertJoysticksLocked();17571758correct = &joystick->hwdata->abs_correct[which];1759if (correct->minimum != correct->maximum) {1760if (correct->use_deadzones) {1761value *= 2;1762if (value > correct->coef[0]) {1763if (value < correct->coef[1]) {1764return 0;1765}1766value -= correct->coef[1];1767} else {1768value -= correct->coef[0];1769}1770value *= correct->coef[2];1771value >>= 13;1772} else {1773value = (int)SDL_floorf((value - correct->minimum) * correct->scale + SDL_JOYSTICK_AXIS_MIN + 0.5f);1774}1775}17761777// Clamp and return1778if (value < SDL_JOYSTICK_AXIS_MIN) {1779return SDL_JOYSTICK_AXIS_MIN;1780}1781if (value > SDL_JOYSTICK_AXIS_MAX) {1782return SDL_JOYSTICK_AXIS_MAX;1783}1784return value;1785}17861787static void PollAllValues(Uint64 timestamp, SDL_Joystick *joystick)1788{1789struct input_absinfo absinfo;1790unsigned long keyinfo[NBITS(KEY_MAX)];1791int i;17921793SDL_AssertJoysticksLocked();17941795// Poll all axis1796for (i = ABS_X; i < ABS_MAX; i++) {1797// We don't need to test for digital hats here, they won't have has_abs[] set1798if (joystick->hwdata->has_abs[i]) {1799if (ioctl(joystick->hwdata->fd, EVIOCGABS(i), &absinfo) >= 0) {1800absinfo.value = AxisCorrect(joystick, i, absinfo.value);18011802#ifdef DEBUG_INPUT_EVENTS1803SDL_Log("Joystick : Re-read Axis %d (%d) val= %d",1804joystick->hwdata->abs_map[i], i, absinfo.value);1805#endif1806SDL_SendJoystickAxis(timestamp, joystick,1807joystick->hwdata->abs_map[i],1808absinfo.value);1809}1810}1811}18121813// Poll all digital hats1814for (i = ABS_HAT0X; i <= ABS_HAT3Y; i++) {1815const int baseaxis = i - ABS_HAT0X;1816const int hatidx = baseaxis / 2;1817SDL_assert(hatidx < SDL_arraysize(joystick->hwdata->has_hat));1818// We don't need to test for analog axes here, they won't have has_hat[] set1819if (joystick->hwdata->has_hat[hatidx]) {1820if (ioctl(joystick->hwdata->fd, EVIOCGABS(i), &absinfo) >= 0) {1821const int hataxis = baseaxis % 2;1822HandleHat(timestamp, joystick, hatidx, hataxis, absinfo.value);1823}1824}1825}18261827// Poll all buttons1828SDL_zeroa(keyinfo);1829if (ioctl(joystick->hwdata->fd, EVIOCGKEY(sizeof(keyinfo)), keyinfo) >= 0) {1830for (i = 0; i < KEY_MAX; i++) {1831if (joystick->hwdata->has_key[i]) {1832bool down = test_bit(i, keyinfo);1833#ifdef DEBUG_INPUT_EVENTS1834SDL_Log("Joystick : Re-read Button %d (%d) val= %d",1835joystick->hwdata->key_map[i], i, down);1836#endif1837SDL_SendJoystickButton(timestamp, joystick,1838joystick->hwdata->key_map[i], down);1839}1840}1841}18421843// Joyballs are relative input, so there's no poll state. Events only!1844}18451846static void PollAllSensors(Uint64 timestamp, SDL_Joystick *joystick)1847{1848struct input_absinfo absinfo;1849int i;18501851SDL_AssertJoysticksLocked();18521853SDL_assert(joystick->hwdata->fd_sensor >= 0);18541855if (joystick->hwdata->has_gyro) {1856float data[3] = {0.0f, 0.0f, 0.0f};1857for (i = 0; i < 3; i++) {1858if (ioctl(joystick->hwdata->fd_sensor, EVIOCGABS(ABS_RX + i), &absinfo) >= 0) {1859data[i] = absinfo.value * (SDL_PI_F / 180.f) / joystick->hwdata->gyro_scale[i];1860#ifdef DEBUG_INPUT_EVENTS1861SDL_Log("Joystick : Re-read Gyro (axis %d) val= %f", i, data[i]);1862#endif1863}1864}1865SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, SDL_US_TO_NS(joystick->hwdata->sensor_tick), data, 3);1866}1867if (joystick->hwdata->has_accelerometer) {1868float data[3] = {0.0f, 0.0f, 0.0f};1869for (i = 0; i < 3; i++) {1870if (ioctl(joystick->hwdata->fd_sensor, EVIOCGABS(ABS_X + i), &absinfo) >= 0) {1871data[i] = absinfo.value * SDL_STANDARD_GRAVITY / joystick->hwdata->accelerometer_scale[i];1872#ifdef DEBUG_INPUT_EVENTS1873SDL_Log("Joystick : Re-read Accelerometer (axis %d) val= %f", i, data[i]);1874#endif1875}1876}1877SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, SDL_US_TO_NS(joystick->hwdata->sensor_tick), data, 3);1878}1879}18801881static void HandleInputEvents(SDL_Joystick *joystick)1882{1883struct input_event events[32];1884int i, len, code, hat_index;18851886SDL_AssertJoysticksLocked();18871888if (joystick->hwdata->fresh) {1889Uint64 ticks = SDL_GetTicksNS();1890PollAllValues(ticks, joystick);1891if (joystick->hwdata->report_sensor) {1892PollAllSensors(ticks, joystick);1893}1894joystick->hwdata->fresh = false;1895}18961897errno = 0;18981899while ((len = read(joystick->hwdata->fd, events, sizeof(events))) > 0) {1900len /= sizeof(events[0]);1901for (i = 0; i < len; ++i) {1902struct input_event *event = &events[i];19031904code = event->code;19051906/* If the kernel sent a SYN_DROPPED, we are supposed to ignore the1907rest of the packet (the end of it signified by a SYN_REPORT) */1908if (joystick->hwdata->recovering_from_dropped &&1909((event->type != EV_SYN) || (code != SYN_REPORT))) {1910continue;1911}19121913switch (event->type) {1914case EV_KEY:1915#ifdef DEBUG_INPUT_EVENTS1916SDL_Log("Key 0x%.2x %s", code, event->value ? "PRESSED" : "RELEASED");1917#endif1918SDL_SendJoystickButton(SDL_EVDEV_GetEventTimestamp(event), joystick,1919joystick->hwdata->key_map[code],1920(event->value != 0));1921break;1922case EV_ABS:1923switch (code) {1924case ABS_HAT0X:1925case ABS_HAT0Y:1926case ABS_HAT1X:1927case ABS_HAT1Y:1928case ABS_HAT2X:1929case ABS_HAT2Y:1930case ABS_HAT3X:1931case ABS_HAT3Y:1932hat_index = (code - ABS_HAT0X) / 2;1933if (joystick->hwdata->has_hat[hat_index]) {1934#ifdef DEBUG_INPUT_EVENTS1935SDL_Log("Axis 0x%.2x = %d", code, event->value);1936#endif1937HandleHat(SDL_EVDEV_GetEventTimestamp(event), joystick, hat_index, code % 2, event->value);1938break;1939}1940SDL_FALLTHROUGH;1941default:1942#ifdef DEBUG_INPUT_EVENTS1943SDL_Log("Axis 0x%.2x = %d", code, event->value);1944#endif1945event->value = AxisCorrect(joystick, code, event->value);1946SDL_SendJoystickAxis(SDL_EVDEV_GetEventTimestamp(event), joystick,1947joystick->hwdata->abs_map[code],1948event->value);1949break;1950}1951break;1952case EV_REL:1953switch (code) {1954case REL_X:1955case REL_Y:1956code -= REL_X;1957HandleBall(joystick, code / 2, code % 2, event->value);1958break;1959default:1960break;1961}1962break;1963case EV_SYN:1964switch (code) {1965case SYN_DROPPED:1966#ifdef DEBUG_INPUT_EVENTS1967SDL_Log("Event SYN_DROPPED detected");1968#endif1969joystick->hwdata->recovering_from_dropped = true;1970break;1971case SYN_REPORT:1972if (joystick->hwdata->recovering_from_dropped) {1973joystick->hwdata->recovering_from_dropped = false;1974PollAllValues(SDL_GetTicksNS(), joystick); // try to sync up to current state now1975}1976break;1977default:1978break;1979}1980break;1981default:1982break;1983}1984}1985}19861987if (errno == ENODEV) {1988// We have to wait until the JoystickDetect callback to remove this1989joystick->hwdata->gone = true;1990errno = 0;1991}19921993if (joystick->hwdata->report_sensor) {1994SDL_assert(joystick->hwdata->fd_sensor >= 0);19951996while ((len = read(joystick->hwdata->fd_sensor, events, sizeof(events))) > 0) {1997len /= sizeof(events[0]);1998for (i = 0; i < len; ++i) {1999unsigned int j;2000struct input_event *event = &events[i];20012002code = event->code;20032004/* If the kernel sent a SYN_DROPPED, we are supposed to ignore the2005rest of the packet (the end of it signified by a SYN_REPORT) */2006if (joystick->hwdata->recovering_from_dropped_sensor &&2007((event->type != EV_SYN) || (code != SYN_REPORT))) {2008continue;2009}20102011switch (event->type) {2012case EV_KEY:2013SDL_assert(0);2014break;2015case EV_ABS:2016switch (code) {2017case ABS_X:2018case ABS_Y:2019case ABS_Z:2020j = code - ABS_X;2021joystick->hwdata->accel_data[j] = event->value * SDL_STANDARD_GRAVITY2022/ joystick->hwdata->accelerometer_scale[j];2023break;2024case ABS_RX:2025case ABS_RY:2026case ABS_RZ:2027j = code - ABS_RX;2028joystick->hwdata->gyro_data[j] = event->value * (SDL_PI_F / 180.f)2029/ joystick->hwdata->gyro_scale[j];2030break;2031}2032break;2033case EV_MSC:2034if (code == MSC_TIMESTAMP) {2035Sint32 tick = event->value;2036Sint32 delta;2037if (joystick->hwdata->last_tick < tick) {2038delta = (tick - joystick->hwdata->last_tick);2039} else {2040delta = (SDL_MAX_SINT32 - joystick->hwdata->last_tick + tick + 1);2041}2042joystick->hwdata->sensor_tick += delta;2043joystick->hwdata->last_tick = tick;2044}2045break;2046case EV_SYN:2047switch (code) {2048case SYN_DROPPED:2049#ifdef DEBUG_INPUT_EVENTS2050SDL_Log("Event SYN_DROPPED detected");2051#endif2052joystick->hwdata->recovering_from_dropped_sensor = true;2053break;2054case SYN_REPORT:2055if (joystick->hwdata->recovering_from_dropped_sensor) {2056joystick->hwdata->recovering_from_dropped_sensor = false;2057PollAllSensors(SDL_GetTicksNS(), joystick); // try to sync up to current state now2058} else {2059Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);2060SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO,2061SDL_US_TO_NS(joystick->hwdata->sensor_tick),2062joystick->hwdata->gyro_data, 3);2063SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL,2064SDL_US_TO_NS(joystick->hwdata->sensor_tick),2065joystick->hwdata->accel_data, 3);2066}2067break;2068default:2069break;2070}2071break;2072default:2073break;2074}2075}2076}2077}20782079if (errno == ENODEV) {2080// We have to wait until the JoystickDetect callback to remove this2081joystick->hwdata->sensor_gone = true;2082}2083}20842085static void HandleClassicEvents(SDL_Joystick *joystick)2086{2087struct js_event events[32];2088int i, len, code, hat_index;2089Uint64 timestamp = SDL_GetTicksNS();20902091SDL_AssertJoysticksLocked();20922093joystick->hwdata->fresh = false;2094while ((len = read(joystick->hwdata->fd, events, sizeof(events))) > 0) {2095len /= sizeof(events[0]);2096for (i = 0; i < len; ++i) {2097switch (events[i].type) {2098case JS_EVENT_BUTTON:2099code = joystick->hwdata->key_pam[events[i].number];2100SDL_SendJoystickButton(timestamp, joystick,2101joystick->hwdata->key_map[code],2102(events[i].value != 0));2103break;2104case JS_EVENT_AXIS:2105code = joystick->hwdata->abs_pam[events[i].number];2106switch (code) {2107case ABS_HAT0X:2108case ABS_HAT0Y:2109case ABS_HAT1X:2110case ABS_HAT1Y:2111case ABS_HAT2X:2112case ABS_HAT2Y:2113case ABS_HAT3X:2114case ABS_HAT3Y:2115hat_index = (code - ABS_HAT0X) / 2;2116if (joystick->hwdata->has_hat[hat_index]) {2117HandleHat(timestamp, joystick, hat_index, code % 2, events[i].value);2118break;2119}2120SDL_FALLTHROUGH;2121default:2122SDL_SendJoystickAxis(timestamp, joystick,2123joystick->hwdata->abs_map[code],2124events[i].value);2125break;2126}2127}2128}2129}2130}21312132static void LINUX_JoystickUpdate(SDL_Joystick *joystick)2133{2134int i;21352136SDL_AssertJoysticksLocked();21372138if (joystick->hwdata->classic) {2139HandleClassicEvents(joystick);2140} else {2141HandleInputEvents(joystick);2142}21432144// Deliver ball motion updates2145for (i = 0; i < joystick->nballs; ++i) {2146int xrel, yrel;21472148xrel = joystick->hwdata->balls[i].axis[0];2149yrel = joystick->hwdata->balls[i].axis[1];2150if (xrel || yrel) {2151joystick->hwdata->balls[i].axis[0] = 0;2152joystick->hwdata->balls[i].axis[1] = 0;2153SDL_SendJoystickBall(0, joystick, (Uint8)i, xrel, yrel);2154}2155}2156}21572158// Function to close a joystick after use2159static void LINUX_JoystickClose(SDL_Joystick *joystick)2160{2161SDL_AssertJoysticksLocked();21622163if (joystick->hwdata) {2164if (joystick->hwdata->effect.id >= 0) {2165ioctl(joystick->hwdata->fd, EVIOCRMFF, joystick->hwdata->effect.id);2166joystick->hwdata->effect.id = -1;2167}2168if (joystick->hwdata->fd >= 0) {2169close(joystick->hwdata->fd);2170}2171if (joystick->hwdata->fd_sensor >= 0) {2172close(joystick->hwdata->fd_sensor);2173}2174if (joystick->hwdata->item) {2175joystick->hwdata->item->hwdata = NULL;2176}2177if (joystick->hwdata->item_sensor) {2178joystick->hwdata->item_sensor->hwdata = NULL;2179}2180SDL_free(joystick->hwdata->key_pam);2181SDL_free(joystick->hwdata->abs_pam);2182SDL_free(joystick->hwdata->hats);2183SDL_free(joystick->hwdata->balls);2184SDL_free(joystick->hwdata->fname);2185SDL_free(joystick->hwdata);2186}2187}21882189// Function to perform any system-specific joystick related cleanup2190static void LINUX_JoystickQuit(void)2191{2192SDL_joylist_item *item = NULL;2193SDL_joylist_item *next = NULL;2194SDL_sensorlist_item *item_sensor = NULL;2195SDL_sensorlist_item *next_sensor = NULL;21962197SDL_AssertJoysticksLocked();21982199if (inotify_fd >= 0) {2200close(inotify_fd);2201inotify_fd = -1;2202}22032204for (item = SDL_joylist; item; item = next) {2205next = item->next;2206FreeJoylistItem(item);2207}2208for (item_sensor = SDL_sensorlist; item_sensor; item_sensor = next_sensor) {2209next_sensor = item_sensor->next;2210FreeSensorlistItem(item_sensor);2211}22122213SDL_joylist = SDL_joylist_tail = NULL;2214SDL_sensorlist = NULL;22152216numjoysticks = 0;22172218#ifdef SDL_USE_LIBUDEV2219if (enumeration_method == ENUMERATION_LIBUDEV) {2220SDL_UDEV_DelCallback(joystick_udev_callback);2221SDL_UDEV_Quit();2222}2223#endif2224}22252226/*2227This is based on the Linux Gamepad Specification2228available at: https://www.kernel.org/doc/html/v4.15/input/gamepad.html2229and the Android gamepad documentation,2230https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input2231*/2232static bool LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)2233{2234SDL_Joystick *joystick;2235SDL_joylist_item *item = GetJoystickByDevIndex(device_index);2236enum {2237MAPPED_TRIGGER_LEFT = 0x1,2238MAPPED_TRIGGER_RIGHT = 0x2,2239MAPPED_TRIGGER_BOTH = 0x3,22402241MAPPED_DPAD_UP = 0x1,2242MAPPED_DPAD_DOWN = 0x2,2243MAPPED_DPAD_LEFT = 0x4,2244MAPPED_DPAD_RIGHT = 0x8,2245MAPPED_DPAD_ALL = 0xF,2246};2247unsigned int mapped;2248bool result = false;22492250SDL_AssertJoysticksLocked();22512252if (item->checked_mapping) {2253if (item->mapping) {2254SDL_memcpy(out, item->mapping, sizeof(*out));2255#ifdef DEBUG_GAMEPAD_MAPPING2256SDL_Log("Prior mapping for device %d", device_index);2257#endif2258return true;2259} else {2260return false;2261}2262}22632264/* We temporarily open the device to check how it's configured. Make2265a fake SDL_Joystick object to do so. */2266joystick = (SDL_Joystick *)SDL_calloc(1, sizeof(*joystick));2267if (!joystick) {2268return false;2269}2270SDL_memcpy(&joystick->guid, &item->guid, sizeof(item->guid));22712272joystick->hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*joystick->hwdata));2273if (!joystick->hwdata) {2274SDL_free(joystick);2275return false;2276}2277SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, true);22782279item->checked_mapping = true;22802281if (!PrepareJoystickHwdata(joystick, item, NULL)) {2282goto done; // SDL_SetError will already have been called2283}22842285// don't assign `item->hwdata` so it's not in any global state.22862287// it is now safe to call LINUX_JoystickClose on this fake joystick.22882289if (!joystick->hwdata->has_key[BTN_GAMEPAD]) {2290// Not a gamepad according to the specs.2291goto done;2292}22932294// We have a gamepad, start filling out the mappings22952296#ifdef DEBUG_GAMEPAD_MAPPING2297SDL_Log("Mapping %s (VID/PID 0x%.4x/0x%.4x)", item->name, SDL_GetJoystickVendor(joystick), SDL_GetJoystickProduct(joystick));2298#endif22992300if (joystick->hwdata->has_key[BTN_A]) {2301out->a.kind = EMappingKind_Button;2302out->a.target = joystick->hwdata->key_map[BTN_A];2303#ifdef DEBUG_GAMEPAD_MAPPING2304SDL_Log("Mapped A to button %d (BTN_A)", out->a.target);2305#endif2306}23072308if (joystick->hwdata->has_key[BTN_B]) {2309out->b.kind = EMappingKind_Button;2310out->b.target = joystick->hwdata->key_map[BTN_B];2311#ifdef DEBUG_GAMEPAD_MAPPING2312SDL_Log("Mapped B to button %d (BTN_B)", out->b.target);2313#endif2314}23152316// Xbox controllers use BTN_X and BTN_Y, and PS4 controllers use BTN_WEST and BTN_NORTH2317if (SDL_GetJoystickVendor(joystick) == USB_VENDOR_SONY) {2318if (joystick->hwdata->has_key[BTN_WEST]) {2319out->x.kind = EMappingKind_Button;2320out->x.target = joystick->hwdata->key_map[BTN_WEST];2321#ifdef DEBUG_GAMEPAD_MAPPING2322SDL_Log("Mapped X to button %d (BTN_WEST)", out->x.target);2323#endif2324}23252326if (joystick->hwdata->has_key[BTN_NORTH]) {2327out->y.kind = EMappingKind_Button;2328out->y.target = joystick->hwdata->key_map[BTN_NORTH];2329#ifdef DEBUG_GAMEPAD_MAPPING2330SDL_Log("Mapped Y to button %d (BTN_NORTH)", out->y.target);2331#endif2332}2333} else {2334if (joystick->hwdata->has_key[BTN_X]) {2335out->x.kind = EMappingKind_Button;2336out->x.target = joystick->hwdata->key_map[BTN_X];2337#ifdef DEBUG_GAMEPAD_MAPPING2338SDL_Log("Mapped X to button %d (BTN_X)", out->x.target);2339#endif2340}23412342if (joystick->hwdata->has_key[BTN_Y]) {2343out->y.kind = EMappingKind_Button;2344out->y.target = joystick->hwdata->key_map[BTN_Y];2345#ifdef DEBUG_GAMEPAD_MAPPING2346SDL_Log("Mapped Y to button %d (BTN_Y)", out->y.target);2347#endif2348}2349}23502351if (joystick->hwdata->has_key[BTN_SELECT]) {2352out->back.kind = EMappingKind_Button;2353out->back.target = joystick->hwdata->key_map[BTN_SELECT];2354#ifdef DEBUG_GAMEPAD_MAPPING2355SDL_Log("Mapped BACK to button %d (BTN_SELECT)", out->back.target);2356#endif2357}23582359if (joystick->hwdata->has_key[BTN_START]) {2360out->start.kind = EMappingKind_Button;2361out->start.target = joystick->hwdata->key_map[BTN_START];2362#ifdef DEBUG_GAMEPAD_MAPPING2363SDL_Log("Mapped START to button %d (BTN_START)", out->start.target);2364#endif2365}23662367if (joystick->hwdata->has_key[BTN_THUMBL]) {2368out->leftstick.kind = EMappingKind_Button;2369out->leftstick.target = joystick->hwdata->key_map[BTN_THUMBL];2370#ifdef DEBUG_GAMEPAD_MAPPING2371SDL_Log("Mapped LEFTSTICK to button %d (BTN_THUMBL)", out->leftstick.target);2372#endif2373}23742375if (joystick->hwdata->has_key[BTN_THUMBR]) {2376out->rightstick.kind = EMappingKind_Button;2377out->rightstick.target = joystick->hwdata->key_map[BTN_THUMBR];2378#ifdef DEBUG_GAMEPAD_MAPPING2379SDL_Log("Mapped RIGHTSTICK to button %d (BTN_THUMBR)", out->rightstick.target);2380#endif2381}23822383if (joystick->hwdata->has_key[BTN_MODE]) {2384out->guide.kind = EMappingKind_Button;2385out->guide.target = joystick->hwdata->key_map[BTN_MODE];2386#ifdef DEBUG_GAMEPAD_MAPPING2387SDL_Log("Mapped GUIDE to button %d (BTN_MODE)", out->guide.target);2388#endif2389}23902391/*2392According to the specs the D-Pad, the shoulder buttons and the triggers2393can be digital, or analog, or both at the same time.2394*/23952396// Prefer digital shoulder buttons, but settle for digital or analog hat.2397mapped = 0;23982399if (joystick->hwdata->has_key[BTN_TL]) {2400out->leftshoulder.kind = EMappingKind_Button;2401out->leftshoulder.target = joystick->hwdata->key_map[BTN_TL];2402mapped |= 0x1;2403#ifdef DEBUG_GAMEPAD_MAPPING2404SDL_Log("Mapped LEFTSHOULDER to button %d (BTN_TL)", out->leftshoulder.target);2405#endif2406}24072408if (joystick->hwdata->has_key[BTN_TR]) {2409out->rightshoulder.kind = EMappingKind_Button;2410out->rightshoulder.target = joystick->hwdata->key_map[BTN_TR];2411mapped |= 0x2;2412#ifdef DEBUG_GAMEPAD_MAPPING2413SDL_Log("Mapped RIGHTSHOULDER to button %d (BTN_TR)", out->rightshoulder.target);2414#endif2415}24162417if (mapped != 0x3 && joystick->hwdata->has_hat[1]) {2418int hat = joystick->hwdata->hats_indices[1] << 4;2419out->leftshoulder.kind = EMappingKind_Hat;2420out->rightshoulder.kind = EMappingKind_Hat;2421out->leftshoulder.target = hat | 0x4;2422out->rightshoulder.target = hat | 0x2;2423mapped |= 0x3;2424#ifdef DEBUG_GAMEPAD_MAPPING2425SDL_Log("Mapped LEFT+RIGHTSHOULDER to hat 1 (ABS_HAT1X, ABS_HAT1Y)");2426#endif2427}24282429if (!(mapped & 0x1) && joystick->hwdata->has_abs[ABS_HAT1Y]) {2430out->leftshoulder.kind = EMappingKind_Axis;2431out->leftshoulder.target = joystick->hwdata->abs_map[ABS_HAT1Y];2432mapped |= 0x1;2433#ifdef DEBUG_GAMEPAD_MAPPING2434SDL_Log("Mapped LEFTSHOULDER to axis %d (ABS_HAT1Y)", out->leftshoulder.target);2435#endif2436}24372438if (!(mapped & 0x2) && joystick->hwdata->has_abs[ABS_HAT1X]) {2439out->rightshoulder.kind = EMappingKind_Axis;2440out->rightshoulder.target = joystick->hwdata->abs_map[ABS_HAT1X];2441mapped |= 0x2;2442#ifdef DEBUG_GAMEPAD_MAPPING2443SDL_Log("Mapped RIGHTSHOULDER to axis %d (ABS_HAT1X)", out->rightshoulder.target);2444#endif2445}24462447// Prefer analog triggers, but settle for digital hat or buttons.2448mapped = 0;24492450/* Unfortunately there are several conventions for how analog triggers2451* are represented as absolute axes:2452*2453* - Linux Gamepad Specification:2454* LT = ABS_HAT2Y, RT = ABS_HAT2X2455* - Android (and therefore many Bluetooth controllers):2456* LT = ABS_BRAKE, RT = ABS_GAS2457* - De facto standard for older Xbox and Playstation controllers:2458* LT = ABS_Z, RT = ABS_RZ2459*2460* We try each one in turn. */2461if (joystick->hwdata->has_abs[ABS_HAT2Y]) {2462// Linux Gamepad Specification2463out->lefttrigger.kind = EMappingKind_Axis;2464out->lefttrigger.target = joystick->hwdata->abs_map[ABS_HAT2Y];2465mapped |= MAPPED_TRIGGER_LEFT;2466#ifdef DEBUG_GAMEPAD_MAPPING2467SDL_Log("Mapped LEFTTRIGGER to axis %d (ABS_HAT2Y)", out->lefttrigger.target);2468#endif2469} else if (joystick->hwdata->has_abs[ABS_BRAKE]) {2470// Android convention2471out->lefttrigger.kind = EMappingKind_Axis;2472out->lefttrigger.target = joystick->hwdata->abs_map[ABS_BRAKE];2473mapped |= MAPPED_TRIGGER_LEFT;2474#ifdef DEBUG_GAMEPAD_MAPPING2475SDL_Log("Mapped LEFTTRIGGER to axis %d (ABS_BRAKE)", out->lefttrigger.target);2476#endif2477} else if (joystick->hwdata->has_abs[ABS_Z]) {2478// De facto standard for Xbox 360 and Playstation gamepads2479out->lefttrigger.kind = EMappingKind_Axis;2480out->lefttrigger.target = joystick->hwdata->abs_map[ABS_Z];2481mapped |= MAPPED_TRIGGER_LEFT;2482#ifdef DEBUG_GAMEPAD_MAPPING2483SDL_Log("Mapped LEFTTRIGGER to axis %d (ABS_Z)", out->lefttrigger.target);2484#endif2485}24862487if (joystick->hwdata->has_abs[ABS_HAT2X]) {2488// Linux Gamepad Specification2489out->righttrigger.kind = EMappingKind_Axis;2490out->righttrigger.target = joystick->hwdata->abs_map[ABS_HAT2X];2491mapped |= MAPPED_TRIGGER_RIGHT;2492#ifdef DEBUG_GAMEPAD_MAPPING2493SDL_Log("Mapped RIGHTTRIGGER to axis %d (ABS_HAT2X)", out->righttrigger.target);2494#endif2495} else if (joystick->hwdata->has_abs[ABS_GAS]) {2496// Android convention2497out->righttrigger.kind = EMappingKind_Axis;2498out->righttrigger.target = joystick->hwdata->abs_map[ABS_GAS];2499mapped |= MAPPED_TRIGGER_RIGHT;2500#ifdef DEBUG_GAMEPAD_MAPPING2501SDL_Log("Mapped RIGHTTRIGGER to axis %d (ABS_GAS)", out->righttrigger.target);2502#endif2503} else if (joystick->hwdata->has_abs[ABS_RZ]) {2504// De facto standard for Xbox 360 and Playstation gamepads2505out->righttrigger.kind = EMappingKind_Axis;2506out->righttrigger.target = joystick->hwdata->abs_map[ABS_RZ];2507mapped |= MAPPED_TRIGGER_RIGHT;2508#ifdef DEBUG_GAMEPAD_MAPPING2509SDL_Log("Mapped RIGHTTRIGGER to axis %d (ABS_RZ)", out->righttrigger.target);2510#endif2511}25122513if (mapped != MAPPED_TRIGGER_BOTH && joystick->hwdata->has_hat[2]) {2514int hat = joystick->hwdata->hats_indices[2] << 4;2515out->lefttrigger.kind = EMappingKind_Hat;2516out->righttrigger.kind = EMappingKind_Hat;2517out->lefttrigger.target = hat | 0x4;2518out->righttrigger.target = hat | 0x2;2519mapped |= MAPPED_TRIGGER_BOTH;2520#ifdef DEBUG_GAMEPAD_MAPPING2521SDL_Log("Mapped LEFT+RIGHTTRIGGER to hat 2 (ABS_HAT2X, ABS_HAT2Y)");2522#endif2523}25242525if (!(mapped & MAPPED_TRIGGER_LEFT) && joystick->hwdata->has_key[BTN_TL2]) {2526out->lefttrigger.kind = EMappingKind_Button;2527out->lefttrigger.target = joystick->hwdata->key_map[BTN_TL2];2528mapped |= MAPPED_TRIGGER_LEFT;2529#ifdef DEBUG_GAMEPAD_MAPPING2530SDL_Log("Mapped LEFTTRIGGER to button %d (BTN_TL2)", out->lefttrigger.target);2531#endif2532}25332534if (!(mapped & MAPPED_TRIGGER_RIGHT) && joystick->hwdata->has_key[BTN_TR2]) {2535out->righttrigger.kind = EMappingKind_Button;2536out->righttrigger.target = joystick->hwdata->key_map[BTN_TR2];2537mapped |= MAPPED_TRIGGER_RIGHT;2538#ifdef DEBUG_GAMEPAD_MAPPING2539SDL_Log("Mapped RIGHTTRIGGER to button %d (BTN_TR2)", out->righttrigger.target);2540#endif2541}25422543// Prefer digital D-Pad buttons, but settle for digital or analog hat.2544mapped = 0;25452546if (joystick->hwdata->has_key[BTN_DPAD_UP]) {2547out->dpup.kind = EMappingKind_Button;2548out->dpup.target = joystick->hwdata->key_map[BTN_DPAD_UP];2549mapped |= MAPPED_DPAD_UP;2550#ifdef DEBUG_GAMEPAD_MAPPING2551SDL_Log("Mapped DPUP to button %d (BTN_DPAD_UP)", out->dpup.target);2552#endif2553}25542555if (joystick->hwdata->has_key[BTN_DPAD_DOWN]) {2556out->dpdown.kind = EMappingKind_Button;2557out->dpdown.target = joystick->hwdata->key_map[BTN_DPAD_DOWN];2558mapped |= MAPPED_DPAD_DOWN;2559#ifdef DEBUG_GAMEPAD_MAPPING2560SDL_Log("Mapped DPDOWN to button %d (BTN_DPAD_DOWN)", out->dpdown.target);2561#endif2562}25632564if (joystick->hwdata->has_key[BTN_DPAD_LEFT]) {2565out->dpleft.kind = EMappingKind_Button;2566out->dpleft.target = joystick->hwdata->key_map[BTN_DPAD_LEFT];2567mapped |= MAPPED_DPAD_LEFT;2568#ifdef DEBUG_GAMEPAD_MAPPING2569SDL_Log("Mapped DPLEFT to button %d (BTN_DPAD_LEFT)", out->dpleft.target);2570#endif2571}25722573if (joystick->hwdata->has_key[BTN_DPAD_RIGHT]) {2574out->dpright.kind = EMappingKind_Button;2575out->dpright.target = joystick->hwdata->key_map[BTN_DPAD_RIGHT];2576mapped |= MAPPED_DPAD_RIGHT;2577#ifdef DEBUG_GAMEPAD_MAPPING2578SDL_Log("Mapped DPRIGHT to button %d (BTN_DPAD_RIGHT)", out->dpright.target);2579#endif2580}25812582if (mapped != MAPPED_DPAD_ALL) {2583if (joystick->hwdata->has_hat[0]) {2584int hat = joystick->hwdata->hats_indices[0] << 4;2585out->dpleft.kind = EMappingKind_Hat;2586out->dpright.kind = EMappingKind_Hat;2587out->dpup.kind = EMappingKind_Hat;2588out->dpdown.kind = EMappingKind_Hat;2589out->dpleft.target = hat | 0x8;2590out->dpright.target = hat | 0x2;2591out->dpup.target = hat | 0x1;2592out->dpdown.target = hat | 0x4;2593mapped |= MAPPED_DPAD_ALL;2594#ifdef DEBUG_GAMEPAD_MAPPING2595SDL_Log("Mapped DPUP+DOWN+LEFT+RIGHT to hat 0 (ABS_HAT0X, ABS_HAT0Y)");2596#endif2597} else if (joystick->hwdata->has_abs[ABS_HAT0X] && joystick->hwdata->has_abs[ABS_HAT0Y]) {2598out->dpleft.kind = EMappingKind_Axis;2599out->dpright.kind = EMappingKind_Axis;2600out->dpup.kind = EMappingKind_Axis;2601out->dpdown.kind = EMappingKind_Axis;2602out->dpleft.target = joystick->hwdata->abs_map[ABS_HAT0X];2603out->dpright.target = joystick->hwdata->abs_map[ABS_HAT0X];2604out->dpup.target = joystick->hwdata->abs_map[ABS_HAT0Y];2605out->dpdown.target = joystick->hwdata->abs_map[ABS_HAT0Y];2606mapped |= MAPPED_DPAD_ALL;2607#ifdef DEBUG_GAMEPAD_MAPPING2608SDL_Log("Mapped DPUP+DOWN to axis %d (ABS_HAT0Y)", out->dpup.target);2609SDL_Log("Mapped DPLEFT+RIGHT to axis %d (ABS_HAT0X)", out->dpleft.target);2610#endif2611}2612}26132614if (joystick->hwdata->has_abs[ABS_X] && joystick->hwdata->has_abs[ABS_Y]) {2615out->leftx.kind = EMappingKind_Axis;2616out->lefty.kind = EMappingKind_Axis;2617out->leftx.target = joystick->hwdata->abs_map[ABS_X];2618out->lefty.target = joystick->hwdata->abs_map[ABS_Y];2619#ifdef DEBUG_GAMEPAD_MAPPING2620SDL_Log("Mapped LEFTX to axis %d (ABS_X)", out->leftx.target);2621SDL_Log("Mapped LEFTY to axis %d (ABS_Y)", out->lefty.target);2622#endif2623}26242625/* The Linux Gamepad Specification uses the RX and RY axes,2626* originally intended to represent X and Y rotation, as a second2627* joystick. This is common for USB gamepads, and also many Bluetooth2628* gamepads, particularly older ones.2629*2630* The Android mapping convention used by many Bluetooth controllers2631* instead uses the Z axis as a secondary X axis, and the RZ axis as2632* a secondary Y axis. */2633if (joystick->hwdata->has_abs[ABS_RX] && joystick->hwdata->has_abs[ABS_RY]) {2634// Linux Gamepad Specification, Xbox 360, Playstation etc.2635out->rightx.kind = EMappingKind_Axis;2636out->righty.kind = EMappingKind_Axis;2637out->rightx.target = joystick->hwdata->abs_map[ABS_RX];2638out->righty.target = joystick->hwdata->abs_map[ABS_RY];2639#ifdef DEBUG_GAMEPAD_MAPPING2640SDL_Log("Mapped RIGHTX to axis %d (ABS_RX)", out->rightx.target);2641SDL_Log("Mapped RIGHTY to axis %d (ABS_RY)", out->righty.target);2642#endif2643} else if (joystick->hwdata->has_abs[ABS_Z] && joystick->hwdata->has_abs[ABS_RZ]) {2644// Android convention2645out->rightx.kind = EMappingKind_Axis;2646out->righty.kind = EMappingKind_Axis;2647out->rightx.target = joystick->hwdata->abs_map[ABS_Z];2648out->righty.target = joystick->hwdata->abs_map[ABS_RZ];2649#ifdef DEBUG_GAMEPAD_MAPPING2650SDL_Log("Mapped RIGHTX to axis %d (ABS_Z)", out->rightx.target);2651SDL_Log("Mapped RIGHTY to axis %d (ABS_RZ)", out->righty.target);2652#endif2653}26542655if (SDL_GetJoystickVendor(joystick) == USB_VENDOR_MICROSOFT) {2656// The Xbox Elite controllers have the paddles as BTN_TRIGGER_HAPPY5 - BTN_TRIGGER_HAPPY82657if (joystick->hwdata->has_key[BTN_TRIGGER_HAPPY5] &&2658joystick->hwdata->has_key[BTN_TRIGGER_HAPPY6] &&2659joystick->hwdata->has_key[BTN_TRIGGER_HAPPY7] &&2660joystick->hwdata->has_key[BTN_TRIGGER_HAPPY8]) {2661out->right_paddle1.kind = EMappingKind_Button;2662out->right_paddle1.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY5];2663out->left_paddle1.kind = EMappingKind_Button;2664out->left_paddle1.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY7];2665out->right_paddle2.kind = EMappingKind_Button;2666out->right_paddle2.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY6];2667out->left_paddle2.kind = EMappingKind_Button;2668out->left_paddle2.target = joystick->hwdata->key_map[BTN_TRIGGER_HAPPY8];2669#ifdef DEBUG_GAMEPAD_MAPPING2670SDL_Log("Mapped RIGHT_PADDLE1 to button %d (BTN_TRIGGER_HAPPY5)", out->right_paddle1.target);2671SDL_Log("Mapped LEFT_PADDLE1 to button %d (BTN_TRIGGER_HAPPY7)", out->left_paddle1.target);2672SDL_Log("Mapped RIGHT_PADDLE2 to button %d (BTN_TRIGGER_HAPPY6)", out->right_paddle2.target);2673SDL_Log("Mapped LEFT_PADDLE2 to button %d (BTN_TRIGGER_HAPPY8)", out->left_paddle2.target);2674#endif2675}26762677// The Xbox Series X controllers have the Share button as KEY_RECORD2678if (joystick->hwdata->has_key[KEY_RECORD]) {2679out->misc1.kind = EMappingKind_Button;2680out->misc1.target = joystick->hwdata->key_map[KEY_RECORD];2681#ifdef DEBUG_GAMEPAD_MAPPING2682SDL_Log("Mapped MISC1 to button %d (KEY_RECORD)", out->misc1.target);2683#endif2684}2685}26862687// Cache the mapping for later2688item->mapping = (SDL_GamepadMapping *)SDL_malloc(sizeof(*item->mapping));2689if (item->mapping) {2690SDL_memcpy(item->mapping, out, sizeof(*out));2691}2692#ifdef DEBUG_GAMEPAD_MAPPING2693SDL_Log("Generated mapping for device %d", device_index);2694#endif2695result = true;26962697done:2698LINUX_JoystickClose(joystick);2699SDL_SetObjectValid(joystick, SDL_OBJECT_TYPE_JOYSTICK, false);2700SDL_free(joystick);27012702return result;2703}27042705SDL_JoystickDriver SDL_LINUX_JoystickDriver = {2706LINUX_JoystickInit,2707LINUX_JoystickGetCount,2708LINUX_JoystickDetect,2709LINUX_JoystickIsDevicePresent,2710LINUX_JoystickGetDeviceName,2711LINUX_JoystickGetDevicePath,2712LINUX_JoystickGetDeviceSteamVirtualGamepadSlot,2713LINUX_JoystickGetDevicePlayerIndex,2714LINUX_JoystickSetDevicePlayerIndex,2715LINUX_JoystickGetDeviceGUID,2716LINUX_JoystickGetDeviceInstanceID,2717LINUX_JoystickOpen,2718LINUX_JoystickRumble,2719LINUX_JoystickRumbleTriggers,2720LINUX_JoystickSetLED,2721LINUX_JoystickSendEffect,2722LINUX_JoystickSetSensorsEnabled,2723LINUX_JoystickUpdate,2724LINUX_JoystickClose,2725LINUX_JoystickQuit,2726LINUX_JoystickGetGamepadMapping2727};27282729#endif // SDL_JOYSTICK_LINUX273027312732