1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "../../SDL_internal.h"
22
23#ifdef SDL_JOYSTICK_LINUX
24
25#ifndef SDL_INPUT_LINUXEV
26#error SDL now requires a Linux 2.4+ kernel with /dev/input/event support.
27#endif
28
29/* This is the Linux implementation of the SDL joystick API */
30
31#include <sys/stat.h>
32#include <errno.h> /* errno, strerror */
33#include <fcntl.h>
34#include <limits.h> /* For the definition of PATH_MAX */
35#ifdef HAVE_INOTIFY
36#include <sys/inotify.h>
37#endif
38#include <sys/ioctl.h>
39#include <unistd.h>
40#include <dirent.h>
41#include <linux/joystick.h>
42
43#include "SDL_hints.h"
44#include "SDL_joystick.h"
45#include "SDL_log.h"
46#include "SDL_endian.h"
47#include "SDL_timer.h"
48#include "../../events/SDL_events_c.h"
49#include "../SDL_sysjoystick.h"
50#include "../SDL_joystick_c.h"
51#include "../steam/SDL_steamcontroller.h"
52#include "SDL_sysjoystick_c.h"
53#include "../hidapi/SDL_hidapijoystick_c.h"
54
55/* This isn't defined in older Linux kernel headers */
56#ifndef SYN_DROPPED
57#define SYN_DROPPED 3
58#endif
59#ifndef BTN_DPAD_UP
60#define BTN_DPAD_UP 0x220
61#endif
62#ifndef BTN_DPAD_DOWN
63#define BTN_DPAD_DOWN 0x221
64#endif
65#ifndef BTN_DPAD_LEFT
66#define BTN_DPAD_LEFT 0x222
67#endif
68#ifndef BTN_DPAD_RIGHT
69#define BTN_DPAD_RIGHT 0x223
70#endif
71
72#include "../../core/linux/SDL_evdev_capabilities.h"
73#include "../../core/linux/SDL_udev.h"
74
75#if 0
76#define DEBUG_INPUT_EVENTS 1
77#endif
78
79typedef enum
80{
81 ENUMERATION_UNSET,
82 ENUMERATION_LIBUDEV,
83 ENUMERATION_FALLBACK
84} EnumerationMethod;
85
86static EnumerationMethod enumeration_method = ENUMERATION_UNSET;
87
88static int MaybeAddDevice(const char *path);
89static int MaybeRemoveDevice(const char *path);
90
91/* A linked list of available joysticks */
92typedef struct SDL_joylist_item
93{
94 int device_instance;
95 char *path; /* "/dev/input/event2" or whatever */
96 char *name; /* "SideWinder 3D Pro" or whatever */
97 SDL_JoystickGUID guid;
98 dev_t devnum;
99 struct joystick_hwdata *hwdata;
100 struct SDL_joylist_item *next;
101
102 /* Steam Controller support */
103 SDL_bool m_bSteamController;
104
105 SDL_GamepadMapping *mapping;
106} SDL_joylist_item;
107
108static SDL_joylist_item *SDL_joylist = NULL;
109static SDL_joylist_item *SDL_joylist_tail = NULL;
110static int numjoysticks = 0;
111static int inotify_fd = -1;
112
113static Uint32 last_joy_detect_time;
114static time_t last_input_dir_mtime;
115
116static void
117FixupDeviceInfoForMapping(int fd, struct input_id *inpid)
118{
119 if (inpid->vendor == 0x045e && inpid->product == 0x0b05 && inpid->version == 0x0903) {
120 /* This is a Microsoft Xbox One Elite Series 2 controller */
121 unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
122
123 /* The first version of the firmware duplicated all the inputs */
124 if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&
125 test_bit(0x2c0, keybit)) {
126 /* Change the version to 0x0902, so we can map it differently */
127 inpid->version = 0x0902;
128 }
129 }
130}
131
132#ifdef SDL_JOYSTICK_HIDAPI
133static SDL_bool
134IsVirtualJoystick(Uint16 vendor, Uint16 product, Uint16 version, const char *name)
135{
136 if (vendor == USB_VENDOR_MICROSOFT && product == USB_PRODUCT_XBOX_ONE_S && version == 0 &&
137 SDL_strcmp(name, "Xbox One S Controller") == 0) {
138 /* This is the virtual device created by the xow driver */
139 return SDL_TRUE;
140 }
141 return SDL_FALSE;
142}
143#endif /* SDL_JOYSTICK_HIDAPI */
144
145static int
146GuessIsJoystick(int fd)
147{
148 unsigned long evbit[NBITS(EV_MAX)] = { 0 };
149 unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
150 unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
151 unsigned long relbit[NBITS(REL_MAX)] = { 0 };
152 int devclass;
153
154 if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||
155 (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
156 (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) < 0) ||
157 (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {
158 return (0);
159 }
160
161 devclass = SDL_EVDEV_GuessDeviceClass(evbit, absbit, keybit, relbit);
162
163 if (devclass & SDL_UDEV_DEVICE_JOYSTICK) {
164 return 1;
165 }
166
167 return 0;
168}
169
170static int
171IsJoystick(int fd, char **name_return, SDL_JoystickGUID *guid)
172{
173 struct input_id inpid;
174 Uint16 *guid16 = (Uint16 *)guid->data;
175 char *name;
176 char product_string[128];
177
178 /* When udev is enabled we only get joystick devices here, so there's no need to test them */
179 if (enumeration_method != ENUMERATION_LIBUDEV && !GuessIsJoystick(fd)) {
180 return 0;
181 }
182
183 if (ioctl(fd, EVIOCGID, &inpid) < 0) {
184 return 0;
185 }
186
187 if (ioctl(fd, EVIOCGNAME(sizeof(product_string)), product_string) < 0) {
188 return 0;
189 }
190
191 name = SDL_CreateJoystickName(inpid.vendor, inpid.product, NULL, product_string);
192 if (!name) {
193 return 0;
194 }
195
196#ifdef SDL_JOYSTICK_HIDAPI
197 if (!IsVirtualJoystick(inpid.vendor, inpid.product, inpid.version, name) &&
198 HIDAPI_IsDevicePresent(inpid.vendor, inpid.product, inpid.version, name)) {
199 /* The HIDAPI driver is taking care of this device */
200 SDL_free(name);
201 return 0;
202 }
203#endif
204
205 FixupDeviceInfoForMapping(fd, &inpid);
206
207#ifdef DEBUG_JOYSTICK
208 printf("Joystick: %s, bustype = %d, vendor = 0x%.4x, product = 0x%.4x, version = %d\n", name, inpid.bustype, inpid.vendor, inpid.product, inpid.version);
209#endif
210
211 SDL_memset(guid->data, 0, sizeof(guid->data));
212
213 /* We only need 16 bits for each of these; space them out to fill 128. */
214 /* Byteswap so devices get same GUID on little/big endian platforms. */
215 *guid16++ = SDL_SwapLE16(inpid.bustype);
216 *guid16++ = 0;
217
218 if (inpid.vendor && inpid.product) {
219 *guid16++ = SDL_SwapLE16(inpid.vendor);
220 *guid16++ = 0;
221 *guid16++ = SDL_SwapLE16(inpid.product);
222 *guid16++ = 0;
223 *guid16++ = SDL_SwapLE16(inpid.version);
224 *guid16++ = 0;
225 } else {
226 SDL_strlcpy((char*)guid16, name, sizeof(guid->data) - 4);
227 }
228
229 if (SDL_ShouldIgnoreJoystick(name, *guid)) {
230 SDL_free(name);
231 return 0;
232 }
233 *name_return = name;
234 return 1;
235}
236
237#if SDL_USE_LIBUDEV
238static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
239{
240 if (devpath == NULL) {
241 return;
242 }
243
244 switch (udev_type) {
245 case SDL_UDEV_DEVICEADDED:
246 if (!(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) {
247 return;
248 }
249 MaybeAddDevice(devpath);
250 break;
251
252 case SDL_UDEV_DEVICEREMOVED:
253 MaybeRemoveDevice(devpath);
254 break;
255
256 default:
257 break;
258 }
259
260}
261#endif /* SDL_USE_LIBUDEV */
262
263static int
264MaybeAddDevice(const char *path)
265{
266 struct stat sb;
267 int fd = -1;
268 int isstick = 0;
269 char *name = NULL;
270 SDL_JoystickGUID guid;
271 SDL_joylist_item *item;
272
273 if (path == NULL) {
274 return -1;
275 }
276
277 if (stat(path, &sb) == -1) {
278 return -1;
279 }
280
281 /* Check to make sure it's not already in list. */
282 for (item = SDL_joylist; item != NULL; item = item->next) {
283 if (sb.st_rdev == item->devnum) {
284 return -1; /* already have this one */
285 }
286 }
287
288 fd = open(path, O_RDONLY, 0);
289 if (fd < 0) {
290 return -1;
291 }
292
293#ifdef DEBUG_INPUT_EVENTS
294 printf("Checking %s\n", path);
295#endif
296
297 isstick = IsJoystick(fd, &name, &guid);
298 close(fd);
299 if (!isstick) {
300 return -1;
301 }
302
303 item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item));
304 if (item == NULL) {
305 return -1;
306 }
307
308 SDL_zerop(item);
309 item->devnum = sb.st_rdev;
310 item->path = SDL_strdup(path);
311 item->name = name;
312 item->guid = guid;
313
314 if ((item->path == NULL) || (item->name == NULL)) {
315 SDL_free(item->path);
316 SDL_free(item->name);
317 SDL_free(item);
318 return -1;
319 }
320
321 item->device_instance = SDL_GetNextJoystickInstanceID();
322 if (SDL_joylist_tail == NULL) {
323 SDL_joylist = SDL_joylist_tail = item;
324 } else {
325 SDL_joylist_tail->next = item;
326 SDL_joylist_tail = item;
327 }
328
329 /* Need to increment the joystick count before we post the event */
330 ++numjoysticks;
331
332 SDL_PrivateJoystickAdded(item->device_instance);
333
334 return numjoysticks;
335}
336
337static int
338MaybeRemoveDevice(const char *path)
339{
340 SDL_joylist_item *item;
341 SDL_joylist_item *prev = NULL;
342
343 if (path == NULL) {
344 return -1;
345 }
346
347 for (item = SDL_joylist; item != NULL; item = item->next) {
348 /* found it, remove it. */
349 if (SDL_strcmp(path, item->path) == 0) {
350 const int retval = item->device_instance;
351 if (item->hwdata) {
352 item->hwdata->item = NULL;
353 }
354 if (prev != NULL) {
355 prev->next = item->next;
356 } else {
357 SDL_assert(SDL_joylist == item);
358 SDL_joylist = item->next;
359 }
360 if (item == SDL_joylist_tail) {
361 SDL_joylist_tail = prev;
362 }
363
364 /* Need to decrement the joystick count before we post the event */
365 --numjoysticks;
366
367 SDL_PrivateJoystickRemoved(item->device_instance);
368
369 if (item->mapping) {
370 SDL_free(item->mapping);
371 }
372 SDL_free(item->path);
373 SDL_free(item->name);
374 SDL_free(item);
375 return retval;
376 }
377 prev = item;
378 }
379
380 return -1;
381}
382
383static void
384HandlePendingRemovals(void)
385{
386 SDL_joylist_item *prev = NULL;
387 SDL_joylist_item *item = SDL_joylist;
388
389 while (item != NULL) {
390 if (item->hwdata && item->hwdata->gone) {
391 item->hwdata->item = NULL;
392
393 if (prev != NULL) {
394 prev->next = item->next;
395 } else {
396 SDL_assert(SDL_joylist == item);
397 SDL_joylist = item->next;
398 }
399 if (item == SDL_joylist_tail) {
400 SDL_joylist_tail = prev;
401 }
402
403 /* Need to decrement the joystick count before we post the event */
404 --numjoysticks;
405
406 SDL_PrivateJoystickRemoved(item->device_instance);
407
408 SDL_free(item->path);
409 SDL_free(item->name);
410 SDL_free(item);
411
412 if (prev != NULL) {
413 item = prev->next;
414 } else {
415 item = SDL_joylist;
416 }
417 } else {
418 prev = item;
419 item = item->next;
420 }
421 }
422}
423
424static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance)
425{
426 SDL_joylist_item *item;
427
428 item = (SDL_joylist_item *) SDL_calloc(1, sizeof (SDL_joylist_item));
429 if (item == NULL) {
430 return SDL_FALSE;
431 }
432
433 item->path = SDL_strdup("");
434 item->name = SDL_strdup(name);
435 item->guid = guid;
436 item->m_bSteamController = SDL_TRUE;
437
438 if ((item->path == NULL) || (item->name == NULL)) {
439 SDL_free(item->path);
440 SDL_free(item->name);
441 SDL_free(item);
442 return SDL_FALSE;
443 }
444
445 *device_instance = item->device_instance = SDL_GetNextJoystickInstanceID();
446 if (SDL_joylist_tail == NULL) {
447 SDL_joylist = SDL_joylist_tail = item;
448 } else {
449 SDL_joylist_tail->next = item;
450 SDL_joylist_tail = item;
451 }
452
453 /* Need to increment the joystick count before we post the event */
454 ++numjoysticks;
455
456 SDL_PrivateJoystickAdded(item->device_instance);
457
458 return SDL_TRUE;
459}
460
461static void SteamControllerDisconnectedCallback(int device_instance)
462{
463 SDL_joylist_item *item;
464 SDL_joylist_item *prev = NULL;
465
466 for (item = SDL_joylist; item != NULL; item = item->next) {
467 /* found it, remove it. */
468 if (item->device_instance == device_instance) {
469 if (item->hwdata) {
470 item->hwdata->item = NULL;
471 }
472 if (prev != NULL) {
473 prev->next = item->next;
474 } else {
475 SDL_assert(SDL_joylist == item);
476 SDL_joylist = item->next;
477 }
478 if (item == SDL_joylist_tail) {
479 SDL_joylist_tail = prev;
480 }
481
482 /* Need to decrement the joystick count before we post the event */
483 --numjoysticks;
484
485 SDL_PrivateJoystickRemoved(item->device_instance);
486
487 SDL_free(item->name);
488 SDL_free(item);
489 return;
490 }
491 prev = item;
492 }
493}
494
495#ifdef HAVE_INOTIFY
496#ifdef HAVE_INOTIFY_INIT1
497static int SDL_inotify_init1(void) {
498 return inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
499}
500#else
501static int SDL_inotify_init1(void) {
502 int fd = inotify_init();
503 if (fd < 0) return -1;
504 fcntl(fd, F_SETFL, O_NONBLOCK);
505 fcntl(fd, F_SETFD, FD_CLOEXEC);
506 return fd;
507}
508#endif
509
510static int
511StrHasPrefix(const char *string, const char *prefix)
512{
513 return (SDL_strncmp(string, prefix, SDL_strlen(prefix)) == 0);
514}
515
516static int
517StrIsInteger(const char *string)
518{
519 const char *p;
520
521 if (*string == '\0') {
522 return 0;
523 }
524
525 for (p = string; *p != '\0'; p++) {
526 if (*p < '0' || *p > '9') {
527 return 0;
528 }
529 }
530
531 return 1;
532}
533
534static void
535LINUX_InotifyJoystickDetect(void)
536{
537 union
538 {
539 struct inotify_event event;
540 char storage[4096];
541 char enough_for_inotify[sizeof (struct inotify_event) + NAME_MAX + 1];
542 } buf;
543 ssize_t bytes;
544 size_t remain = 0;
545 size_t len;
546
547 bytes = read(inotify_fd, &buf, sizeof (buf));
548
549 if (bytes > 0) {
550 remain = (size_t) bytes;
551 }
552
553 while (remain > 0) {
554 if (buf.event.len > 0) {
555 if (StrHasPrefix(buf.event.name, "event") &&
556 StrIsInteger(buf.event.name + strlen ("event"))) {
557 char path[PATH_MAX];
558
559 SDL_snprintf(path, SDL_arraysize(path), "/dev/input/%s", buf.event.name);
560
561 if (buf.event.mask & (IN_CREATE | IN_MOVED_TO | IN_ATTRIB)) {
562 MaybeAddDevice(path);
563 }
564 else if (buf.event.mask & (IN_DELETE | IN_MOVED_FROM)) {
565 MaybeRemoveDevice(path);
566 }
567 }
568 }
569
570 len = sizeof (struct inotify_event) + buf.event.len;
571 remain -= len;
572
573 if (remain != 0) {
574 memmove (&buf.storage[0], &buf.storage[len], remain);
575 }
576 }
577}
578#endif /* HAVE_INOTIFY */
579
580/* Detect devices by reading /dev/input. In the inotify code path we
581 * have to do this the first time, to detect devices that already existed
582 * before we started; in the non-inotify code path we do this repeatedly
583 * (polling). */
584static void
585LINUX_FallbackJoystickDetect(void)
586{
587 const Uint32 SDL_JOY_DETECT_INTERVAL_MS = 3000; /* Update every 3 seconds */
588 Uint32 now = SDL_GetTicks();
589
590 if (!last_joy_detect_time || SDL_TICKS_PASSED(now, last_joy_detect_time + SDL_JOY_DETECT_INTERVAL_MS)) {
591 struct stat sb;
592
593 /* Opening input devices can generate synchronous device I/O, so avoid it if we can */
594 if (stat("/dev/input", &sb) == 0 && sb.st_mtime != last_input_dir_mtime) {
595 DIR *folder;
596 struct dirent *dent;
597
598 folder = opendir("/dev/input");
599 if (folder) {
600 while ((dent = readdir(folder))) {
601 int len = SDL_strlen(dent->d_name);
602 if (len > 5 && SDL_strncmp(dent->d_name, "event", 5) == 0) {
603 char path[PATH_MAX];
604 SDL_snprintf(path, SDL_arraysize(path), "/dev/input/%s", dent->d_name);
605 MaybeAddDevice(path);
606 }
607 }
608
609 closedir(folder);
610 }
611
612 last_input_dir_mtime = sb.st_mtime;
613 }
614
615 last_joy_detect_time = now;
616 }
617}
618
619static void
620LINUX_JoystickDetect(void)
621{
622#if SDL_USE_LIBUDEV
623 if (enumeration_method == ENUMERATION_LIBUDEV) {
624 SDL_UDEV_Poll();
625 }
626 else
627#endif
628#ifdef HAVE_INOTIFY
629 if (inotify_fd >= 0 && last_joy_detect_time != 0) {
630 LINUX_InotifyJoystickDetect();
631 }
632 else
633#endif
634 {
635 LINUX_FallbackJoystickDetect();
636 }
637
638 HandlePendingRemovals();
639
640 SDL_UpdateSteamControllers();
641}
642
643static int
644LINUX_JoystickInit(void)
645{
646#if SDL_USE_LIBUDEV
647 if (enumeration_method == ENUMERATION_UNSET) {
648 if (SDL_getenv("SDL_JOYSTICK_DISABLE_UDEV") != NULL) {
649 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
650 "udev disabled by SDL_JOYSTICK_DISABLE_UDEV");
651 enumeration_method = ENUMERATION_FALLBACK;
652 }
653 else if (access("/.flatpak-info", F_OK) == 0
654 || access("/run/host/container-manager", F_OK) == 0) {
655 /* Explicitly check `/.flatpak-info` because, for old versions of
656 * Flatpak, this was the only available way to tell if we were in
657 * a Flatpak container. */
658 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
659 "Container detected, disabling udev integration");
660 enumeration_method = ENUMERATION_FALLBACK;
661 }
662 else {
663 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
664 "Using udev for joystick device discovery");
665 enumeration_method = ENUMERATION_LIBUDEV;
666 }
667 }
668#endif
669
670 /* First see if the user specified one or more joysticks to use */
671 if (SDL_getenv("SDL_JOYSTICK_DEVICE") != NULL) {
672 char *envcopy, *envpath, *delim;
673 envcopy = SDL_strdup(SDL_getenv("SDL_JOYSTICK_DEVICE"));
674 envpath = envcopy;
675 while (envpath != NULL) {
676 delim = SDL_strchr(envpath, ':');
677 if (delim != NULL) {
678 *delim++ = '\0';
679 }
680 MaybeAddDevice(envpath);
681 envpath = delim;
682 }
683 SDL_free(envcopy);
684 }
685
686 SDL_InitSteamControllers(SteamControllerConnectedCallback,
687 SteamControllerDisconnectedCallback);
688
689 /* Force immediate joystick detection if using fallback */
690 last_joy_detect_time = 0;
691 last_input_dir_mtime = 0;
692
693#if SDL_USE_LIBUDEV
694 if (enumeration_method == ENUMERATION_LIBUDEV) {
695 if (SDL_UDEV_Init() < 0) {
696 return SDL_SetError("Could not initialize UDEV");
697 }
698
699 /* Set up the udev callback */
700 if (SDL_UDEV_AddCallback(joystick_udev_callback) < 0) {
701 SDL_UDEV_Quit();
702 return SDL_SetError("Could not set up joystick <-> udev callback");
703 }
704
705 /* Force a scan to build the initial device list */
706 SDL_UDEV_Scan();
707 }
708 else
709#endif
710 {
711#if defined(HAVE_INOTIFY)
712 inotify_fd = SDL_inotify_init1();
713
714 if (inotify_fd < 0) {
715 SDL_LogWarn(SDL_LOG_CATEGORY_INPUT,
716 "Unable to initialize inotify, falling back to polling: %s",
717 strerror (errno));
718 } else {
719 /* We need to watch for attribute changes in addition to
720 * creation, because when a device is first created, it has
721 * permissions that we can't read. When udev chmods it to
722 * something that we maybe *can* read, we'll get an
723 * IN_ATTRIB event to tell us. */
724 if (inotify_add_watch(inotify_fd, "/dev/input",
725 IN_CREATE | IN_DELETE | IN_MOVE | IN_ATTRIB) < 0) {
726 close(inotify_fd);
727 inotify_fd = -1;
728 SDL_LogWarn(SDL_LOG_CATEGORY_INPUT,
729 "Unable to add inotify watch, falling back to polling: %s",
730 strerror (errno));
731 }
732 }
733#endif /* HAVE_INOTIFY */
734
735 /* Report all devices currently present */
736 LINUX_JoystickDetect();
737 }
738
739 return 0;
740}
741
742static int
743LINUX_JoystickGetCount(void)
744{
745 return numjoysticks;
746}
747
748static SDL_joylist_item *
749JoystickByDevIndex(int device_index)
750{
751 SDL_joylist_item *item = SDL_joylist;
752
753 if ((device_index < 0) || (device_index >= numjoysticks)) {
754 return NULL;
755 }
756
757 while (device_index > 0) {
758 SDL_assert(item != NULL);
759 device_index--;
760 item = item->next;
761 }
762
763 return item;
764}
765
766/* Function to get the device-dependent name of a joystick */
767static const char *
768LINUX_JoystickGetDeviceName(int device_index)
769{
770 return JoystickByDevIndex(device_index)->name;
771}
772
773static int
774LINUX_JoystickGetDevicePlayerIndex(int device_index)
775{
776 return -1;
777}
778
779static void
780LINUX_JoystickSetDevicePlayerIndex(int device_index, int player_index)
781{
782}
783
784static SDL_JoystickGUID
785LINUX_JoystickGetDeviceGUID( int device_index )
786{
787 return JoystickByDevIndex(device_index)->guid;
788}
789
790/* Function to perform the mapping from device index to the instance id for this index */
791static SDL_JoystickID
792LINUX_JoystickGetDeviceInstanceID(int device_index)
793{
794 return JoystickByDevIndex(device_index)->device_instance;
795}
796
797static int
798allocate_hatdata(SDL_Joystick *joystick)
799{
800 int i;
801
802 joystick->hwdata->hats =
803 (struct hwdata_hat *) SDL_malloc(joystick->nhats *
804 sizeof(struct hwdata_hat));
805 if (joystick->hwdata->hats == NULL) {
806 return (-1);
807 }
808 for (i = 0; i < joystick->nhats; ++i) {
809 joystick->hwdata->hats[i].axis[0] = 1;
810 joystick->hwdata->hats[i].axis[1] = 1;
811 }
812 return (0);
813}
814
815static int
816allocate_balldata(SDL_Joystick *joystick)
817{
818 int i;
819
820 joystick->hwdata->balls =
821 (struct hwdata_ball *) SDL_malloc(joystick->nballs *
822 sizeof(struct hwdata_ball));
823 if (joystick->hwdata->balls == NULL) {
824 return (-1);
825 }
826 for (i = 0; i < joystick->nballs; ++i) {
827 joystick->hwdata->balls[i].axis[0] = 0;
828 joystick->hwdata->balls[i].axis[1] = 0;
829 }
830 return (0);
831}
832
833static void
834ConfigJoystick(SDL_Joystick *joystick, int fd)
835{
836 int i, t;
837 unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
838 unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
839 unsigned long relbit[NBITS(REL_MAX)] = { 0 };
840 unsigned long ffbit[NBITS(FF_MAX)] = { 0 };
841 SDL_bool use_deadzones = SDL_GetHintBoolean(SDL_HINT_LINUX_JOYSTICK_DEADZONES, SDL_FALSE);
842
843 /* See if this device uses the new unified event API */
844 if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&
845 (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) &&
846 (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0)) {
847
848 /* Get the number of buttons, axes, and other thingamajigs */
849 for (i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
850 if (test_bit(i, keybit)) {
851#ifdef DEBUG_INPUT_EVENTS
852 printf("Joystick has button: 0x%x\n", i);
853#endif
854 joystick->hwdata->key_map[i] = joystick->nbuttons;
855 joystick->hwdata->has_key[i] = SDL_TRUE;
856 ++joystick->nbuttons;
857 }
858 }
859 for (i = 0; i < BTN_JOYSTICK; ++i) {
860 if (test_bit(i, keybit)) {
861#ifdef DEBUG_INPUT_EVENTS
862 printf("Joystick has button: 0x%x\n", i);
863#endif
864 joystick->hwdata->key_map[i] = joystick->nbuttons;
865 joystick->hwdata->has_key[i] = SDL_TRUE;
866 ++joystick->nbuttons;
867 }
868 }
869 for (i = 0; i < ABS_MAX; ++i) {
870 /* Skip hats */
871 if (i == ABS_HAT0X) {
872 i = ABS_HAT3Y;
873 continue;
874 }
875 if (test_bit(i, absbit)) {
876 struct input_absinfo absinfo;
877 struct axis_correct *correct = &joystick->hwdata->abs_correct[i];
878
879 if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {
880 continue;
881 }
882#ifdef DEBUG_INPUT_EVENTS
883 printf("Joystick has absolute axis: 0x%.2x\n", i);
884 printf("Values = { %d, %d, %d, %d, %d }\n",
885 absinfo.value, absinfo.minimum, absinfo.maximum,
886 absinfo.fuzz, absinfo.flat);
887#endif /* DEBUG_INPUT_EVENTS */
888 joystick->hwdata->abs_map[i] = joystick->naxes;
889 joystick->hwdata->has_abs[i] = SDL_TRUE;
890
891 correct->minimum = absinfo.minimum;
892 correct->maximum = absinfo.maximum;
893 if (correct->minimum != correct->maximum) {
894 if (use_deadzones) {
895 correct->use_deadzones = SDL_TRUE;
896 correct->coef[0] = (absinfo.maximum + absinfo.minimum) - 2 * absinfo.flat;
897 correct->coef[1] = (absinfo.maximum + absinfo.minimum) + 2 * absinfo.flat;
898 t = ((absinfo.maximum - absinfo.minimum) - 4 * absinfo.flat);
899 if (t != 0) {
900 correct->coef[2] = (1 << 28) / t;
901 } else {
902 correct->coef[2] = 0;
903 }
904 } else {
905 float value_range = (correct->maximum - correct->minimum);
906 float output_range = (SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN);
907
908 correct->scale = (output_range / value_range);
909 }
910 }
911 ++joystick->naxes;
912 }
913 }
914 for (i = ABS_HAT0X; i <= ABS_HAT3Y; i += 2) {
915 if (test_bit(i, absbit) || test_bit(i + 1, absbit)) {
916 struct input_absinfo absinfo;
917 int hat_index = (i - ABS_HAT0X) / 2;
918
919 if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {
920 continue;
921 }
922#ifdef DEBUG_INPUT_EVENTS
923 printf("Joystick has hat %d\n", hat_index);
924 printf("Values = { %d, %d, %d, %d, %d }\n",
925 absinfo.value, absinfo.minimum, absinfo.maximum,
926 absinfo.fuzz, absinfo.flat);
927#endif /* DEBUG_INPUT_EVENTS */
928 joystick->hwdata->hats_indices[hat_index] = joystick->nhats++;
929 joystick->hwdata->has_hat[hat_index] = SDL_TRUE;
930 }
931 }
932 if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) {
933 ++joystick->nballs;
934 }
935
936 /* Allocate data to keep track of these thingamajigs */
937 if (joystick->nhats > 0) {
938 if (allocate_hatdata(joystick) < 0) {
939 joystick->nhats = 0;
940 }
941 }
942 if (joystick->nballs > 0) {
943 if (allocate_balldata(joystick) < 0) {
944 joystick->nballs = 0;
945 }
946 }
947 }
948
949 if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) >= 0) {
950 if (test_bit(FF_RUMBLE, ffbit)) {
951 joystick->hwdata->ff_rumble = SDL_TRUE;
952 }
953 if (test_bit(FF_SINE, ffbit)) {
954 joystick->hwdata->ff_sine = SDL_TRUE;
955 }
956 }
957}
958
959
960/* Function to open a joystick for use.
961 The joystick to open is specified by the device index.
962 This should fill the nbuttons and naxes fields of the joystick structure.
963 It returns 0, or -1 if there is an error.
964 */
965static int
966LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
967{
968 SDL_joylist_item *item = JoystickByDevIndex(device_index);
969
970 if (item == NULL) {
971 return SDL_SetError("No such device");
972 }
973
974 joystick->instance_id = item->device_instance;
975 joystick->hwdata = (struct joystick_hwdata *)
976 SDL_calloc(1, sizeof(*joystick->hwdata));
977 if (joystick->hwdata == NULL) {
978 return SDL_OutOfMemory();
979 }
980 joystick->hwdata->item = item;
981 joystick->hwdata->guid = item->guid;
982 joystick->hwdata->effect.id = -1;
983 joystick->hwdata->m_bSteamController = item->m_bSteamController;
984 SDL_memset(joystick->hwdata->abs_map, 0xFF, sizeof(joystick->hwdata->abs_map));
985
986 if (item->m_bSteamController) {
987 joystick->hwdata->fd = -1;
988 SDL_GetSteamControllerInputs(&joystick->nbuttons,
989 &joystick->naxes,
990 &joystick->nhats);
991 } else {
992 int fd = open(item->path, O_RDWR, 0);
993 if (fd < 0) {
994 SDL_free(joystick->hwdata);
995 joystick->hwdata = NULL;
996 return SDL_SetError("Unable to open %s", item->path);
997 }
998
999 joystick->hwdata->fd = fd;
1000 joystick->hwdata->fname = SDL_strdup(item->path);
1001 if (joystick->hwdata->fname == NULL) {
1002 SDL_free(joystick->hwdata);
1003 joystick->hwdata = NULL;
1004 close(fd);
1005 return SDL_OutOfMemory();
1006 }
1007
1008 /* Set the joystick to non-blocking read mode */
1009 fcntl(fd, F_SETFL, O_NONBLOCK);
1010
1011 /* Get the number of buttons and axes on the joystick */
1012 ConfigJoystick(joystick, fd);
1013 }
1014
1015 SDL_assert(item->hwdata == NULL);
1016 item->hwdata = joystick->hwdata;
1017
1018 /* mark joystick as fresh and ready */
1019 joystick->hwdata->fresh = SDL_TRUE;
1020
1021 return (0);
1022}
1023
1024static int
1025LINUX_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1026{
1027 struct input_event event;
1028
1029 if (joystick->hwdata->ff_rumble) {
1030 struct ff_effect *effect = &joystick->hwdata->effect;
1031
1032 effect->type = FF_RUMBLE;
1033 effect->replay.length = SDL_MAX_RUMBLE_DURATION_MS;
1034 effect->u.rumble.strong_magnitude = low_frequency_rumble;
1035 effect->u.rumble.weak_magnitude = high_frequency_rumble;
1036 } else if (joystick->hwdata->ff_sine) {
1037 /* Scale and average the two rumble strengths */
1038 Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
1039 struct ff_effect *effect = &joystick->hwdata->effect;
1040
1041 effect->type = FF_PERIODIC;
1042 effect->replay.length = SDL_MAX_RUMBLE_DURATION_MS;
1043 effect->u.periodic.waveform = FF_SINE;
1044 effect->u.periodic.magnitude = magnitude;
1045 } else {
1046 return SDL_Unsupported();
1047 }
1048
1049 if (ioctl(joystick->hwdata->fd, EVIOCSFF, &joystick->hwdata->effect) < 0) {
1050 /* The kernel may have lost this effect, try to allocate a new one */
1051 joystick->hwdata->effect.id = -1;
1052 if (ioctl(joystick->hwdata->fd, EVIOCSFF, &joystick->hwdata->effect) < 0) {
1053 return SDL_SetError("Couldn't update rumble effect: %s", strerror(errno));
1054 }
1055 }
1056
1057 event.type = EV_FF;
1058 event.code = joystick->hwdata->effect.id;
1059 event.value = 1;
1060 if (write(joystick->hwdata->fd, &event, sizeof(event)) < 0) {
1061 return SDL_SetError("Couldn't start rumble effect: %s", strerror(errno));
1062 }
1063 return 0;
1064}
1065
1066static int
1067LINUX_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
1068{
1069 return SDL_Unsupported();
1070}
1071
1072static SDL_bool
1073LINUX_JoystickHasLED(SDL_Joystick *joystick)
1074{
1075 return SDL_FALSE;
1076}
1077
1078static int
1079LINUX_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
1080{
1081 return SDL_Unsupported();
1082}
1083
1084static int
1085LINUX_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled)
1086{
1087 return SDL_Unsupported();
1088}
1089
1090static SDL_INLINE void
1091HandleHat(SDL_Joystick *stick, Uint8 hat, int axis, int value)
1092{
1093 struct hwdata_hat *the_hat;
1094 const Uint8 position_map[3][3] = {
1095 {SDL_HAT_LEFTUP, SDL_HAT_UP, SDL_HAT_RIGHTUP},
1096 {SDL_HAT_LEFT, SDL_HAT_CENTERED, SDL_HAT_RIGHT},
1097 {SDL_HAT_LEFTDOWN, SDL_HAT_DOWN, SDL_HAT_RIGHTDOWN}
1098 };
1099
1100 the_hat = &stick->hwdata->hats[hat];
1101 if (value < 0) {
1102 value = 0;
1103 } else if (value == 0) {
1104 value = 1;
1105 } else if (value > 0) {
1106 value = 2;
1107 }
1108 if (value != the_hat->axis[axis]) {
1109 the_hat->axis[axis] = value;
1110 SDL_PrivateJoystickHat(stick, hat,
1111 position_map[the_hat->axis[1]][the_hat->axis[0]]);
1112 }
1113}
1114
1115static SDL_INLINE void
1116HandleBall(SDL_Joystick *stick, Uint8 ball, int axis, int value)
1117{
1118 stick->hwdata->balls[ball].axis[axis] += value;
1119}
1120
1121
1122static SDL_INLINE int
1123AxisCorrect(SDL_Joystick *joystick, int which, int value)
1124{
1125 struct axis_correct *correct;
1126
1127 correct = &joystick->hwdata->abs_correct[which];
1128 if (correct->minimum != correct->maximum) {
1129 if (correct->use_deadzones) {
1130 value *= 2;
1131 if (value > correct->coef[0]) {
1132 if (value < correct->coef[1]) {
1133 return 0;
1134 }
1135 value -= correct->coef[1];
1136 } else {
1137 value -= correct->coef[0];
1138 }
1139 value *= correct->coef[2];
1140 value >>= 13;
1141 } else {
1142 value = (int)SDL_floorf((value - correct->minimum) * correct->scale + SDL_JOYSTICK_AXIS_MIN + 0.5f);
1143 }
1144 }
1145
1146 /* Clamp and return */
1147 if (value < SDL_JOYSTICK_AXIS_MIN) {
1148 return SDL_JOYSTICK_AXIS_MIN;
1149 }
1150 if (value > SDL_JOYSTICK_AXIS_MAX) {
1151 return SDL_JOYSTICK_AXIS_MAX;
1152 }
1153 return value;
1154}
1155
1156static SDL_INLINE void
1157PollAllValues(SDL_Joystick *joystick)
1158{
1159 struct input_absinfo absinfo;
1160 unsigned long keyinfo[NBITS(KEY_MAX)];
1161 int i;
1162
1163 /* Poll all axis */
1164 for (i = ABS_X; i < ABS_MAX; i++) {
1165 if (i == ABS_HAT0X) { /* we handle hats in the next loop, skip them for now. */
1166 i = ABS_HAT3Y;
1167 continue;
1168 }
1169 if (joystick->hwdata->has_abs[i]) {
1170 if (ioctl(joystick->hwdata->fd, EVIOCGABS(i), &absinfo) >= 0) {
1171 absinfo.value = AxisCorrect(joystick, i, absinfo.value);
1172
1173#ifdef DEBUG_INPUT_EVENTS
1174 printf("Joystick : Re-read Axis %d (%d) val= %d\n",
1175 joystick->hwdata->abs_map[i], i, absinfo.value);
1176#endif
1177 SDL_PrivateJoystickAxis(joystick,
1178 joystick->hwdata->abs_map[i],
1179 absinfo.value);
1180 }
1181 }
1182 }
1183
1184 /* Poll all hats */
1185 for (i = ABS_HAT0X; i <= ABS_HAT3Y; i++) {
1186 const int baseaxis = i - ABS_HAT0X;
1187 const int hatidx = baseaxis / 2;
1188 SDL_assert(hatidx < SDL_arraysize(joystick->hwdata->has_hat));
1189 if (joystick->hwdata->has_hat[hatidx]) {
1190 if (ioctl(joystick->hwdata->fd, EVIOCGABS(i), &absinfo) >= 0) {
1191 const int hataxis = baseaxis % 2;
1192 HandleHat(joystick, joystick->hwdata->hats_indices[hatidx], hataxis, absinfo.value);
1193 }
1194 }
1195 }
1196
1197 /* Poll all buttons */
1198 SDL_zeroa(keyinfo);
1199 if (ioctl(joystick->hwdata->fd, EVIOCGKEY(sizeof (keyinfo)), keyinfo) >= 0) {
1200 for (i = 0; i < KEY_MAX; i++) {
1201 if (joystick->hwdata->has_key[i]) {
1202 const Uint8 value = test_bit(i, keyinfo) ? SDL_PRESSED : SDL_RELEASED;
1203#ifdef DEBUG_INPUT_EVENTS
1204 printf("Joystick : Re-read Button %d (%d) val= %d\n",
1205 joystick->hwdata->key_map[i], i, value);
1206#endif
1207 SDL_PrivateJoystickButton(joystick,
1208 joystick->hwdata->key_map[i], value);
1209 }
1210 }
1211 }
1212
1213 /* Joyballs are relative input, so there's no poll state. Events only! */
1214}
1215
1216static SDL_INLINE void
1217HandleInputEvents(SDL_Joystick *joystick)
1218{
1219 struct input_event events[32];
1220 int i, len;
1221 int code;
1222
1223 if (joystick->hwdata->fresh) {
1224 PollAllValues(joystick);
1225 joystick->hwdata->fresh = SDL_FALSE;
1226 }
1227
1228 while ((len = read(joystick->hwdata->fd, events, (sizeof events))) > 0) {
1229 len /= sizeof(events[0]);
1230 for (i = 0; i < len; ++i) {
1231 code = events[i].code;
1232
1233 /* If the kernel sent a SYN_DROPPED, we are supposed to ignore the
1234 rest of the packet (the end of it signified by a SYN_REPORT) */
1235 if ( joystick->hwdata->recovering_from_dropped &&
1236 ((events[i].type != EV_SYN) || (code != SYN_REPORT)) ) {
1237 continue;
1238 }
1239
1240 switch (events[i].type) {
1241 case EV_KEY:
1242 SDL_PrivateJoystickButton(joystick,
1243 joystick->hwdata->key_map[code],
1244 events[i].value);
1245 break;
1246 case EV_ABS:
1247 switch (code) {
1248 case ABS_HAT0X:
1249 case ABS_HAT0Y:
1250 case ABS_HAT1X:
1251 case ABS_HAT1Y:
1252 case ABS_HAT2X:
1253 case ABS_HAT2Y:
1254 case ABS_HAT3X:
1255 case ABS_HAT3Y:
1256 code -= ABS_HAT0X;
1257 HandleHat(joystick, joystick->hwdata->hats_indices[code / 2], code % 2, events[i].value);
1258 break;
1259 default:
1260 if (joystick->hwdata->abs_map[code] != 0xFF) {
1261 events[i].value =
1262 AxisCorrect(joystick, code, events[i].value);
1263 SDL_PrivateJoystickAxis(joystick,
1264 joystick->hwdata->abs_map[code],
1265 events[i].value);
1266 }
1267 break;
1268 }
1269 break;
1270 case EV_REL:
1271 switch (code) {
1272 case REL_X:
1273 case REL_Y:
1274 code -= REL_X;
1275 HandleBall(joystick, code / 2, code % 2, events[i].value);
1276 break;
1277 default:
1278 break;
1279 }
1280 break;
1281 case EV_SYN:
1282 switch (code) {
1283 case SYN_DROPPED :
1284#ifdef DEBUG_INPUT_EVENTS
1285 printf("Event SYN_DROPPED detected\n");
1286#endif
1287 joystick->hwdata->recovering_from_dropped = SDL_TRUE;
1288 break;
1289 case SYN_REPORT :
1290 if (joystick->hwdata->recovering_from_dropped) {
1291 joystick->hwdata->recovering_from_dropped = SDL_FALSE;
1292 PollAllValues(joystick); /* try to sync up to current state now */
1293 }
1294 break;
1295 default:
1296 break;
1297 }
1298 default:
1299 break;
1300 }
1301 }
1302 }
1303
1304 if (errno == ENODEV) {
1305 /* We have to wait until the JoystickDetect callback to remove this */
1306 joystick->hwdata->gone = SDL_TRUE;
1307 }
1308}
1309
1310static void
1311LINUX_JoystickUpdate(SDL_Joystick *joystick)
1312{
1313 int i;
1314
1315 if (joystick->hwdata->m_bSteamController) {
1316 SDL_UpdateSteamController(joystick);
1317 return;
1318 }
1319
1320 HandleInputEvents(joystick);
1321
1322 /* Deliver ball motion updates */
1323 for (i = 0; i < joystick->nballs; ++i) {
1324 int xrel, yrel;
1325
1326 xrel = joystick->hwdata->balls[i].axis[0];
1327 yrel = joystick->hwdata->balls[i].axis[1];
1328 if (xrel || yrel) {
1329 joystick->hwdata->balls[i].axis[0] = 0;
1330 joystick->hwdata->balls[i].axis[1] = 0;
1331 SDL_PrivateJoystickBall(joystick, (Uint8) i, xrel, yrel);
1332 }
1333 }
1334}
1335
1336/* Function to close a joystick after use */
1337static void
1338LINUX_JoystickClose(SDL_Joystick *joystick)
1339{
1340 if (joystick->hwdata) {
1341 if (joystick->hwdata->effect.id >= 0) {
1342 ioctl(joystick->hwdata->fd, EVIOCRMFF, joystick->hwdata->effect.id);
1343 joystick->hwdata->effect.id = -1;
1344 }
1345 if (joystick->hwdata->fd >= 0) {
1346 close(joystick->hwdata->fd);
1347 }
1348 if (joystick->hwdata->item) {
1349 joystick->hwdata->item->hwdata = NULL;
1350 }
1351 SDL_free(joystick->hwdata->hats);
1352 SDL_free(joystick->hwdata->balls);
1353 SDL_free(joystick->hwdata->fname);
1354 SDL_free(joystick->hwdata);
1355 }
1356}
1357
1358/* Function to perform any system-specific joystick related cleanup */
1359static void
1360LINUX_JoystickQuit(void)
1361{
1362 SDL_joylist_item *item = NULL;
1363 SDL_joylist_item *next = NULL;
1364
1365 if (inotify_fd >= 0) {
1366 close(inotify_fd);
1367 inotify_fd = -1;
1368 }
1369
1370 for (item = SDL_joylist; item; item = next) {
1371 next = item->next;
1372 SDL_free(item->path);
1373 SDL_free(item->name);
1374 SDL_free(item);
1375 }
1376
1377 SDL_joylist = SDL_joylist_tail = NULL;
1378
1379 numjoysticks = 0;
1380
1381#if SDL_USE_LIBUDEV
1382 if (enumeration_method == ENUMERATION_LIBUDEV) {
1383 SDL_UDEV_DelCallback(joystick_udev_callback);
1384 SDL_UDEV_Quit();
1385 }
1386#endif
1387
1388 SDL_QuitSteamControllers();
1389}
1390
1391/*
1392 This is based on the Linux Gamepad Specification
1393 available at: https://www.kernel.org/doc/html/v4.15/input/gamepad.html
1394 */
1395static SDL_bool
1396LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
1397{
1398 SDL_Joystick *joystick;
1399 SDL_joylist_item *item = JoystickByDevIndex(device_index);
1400
1401 if (item->mapping) {
1402 SDL_memcpy(out, item->mapping, sizeof(*out));
1403 return SDL_TRUE;
1404 }
1405
1406 joystick = (SDL_Joystick *) SDL_calloc(sizeof(*joystick), 1);
1407 if (joystick == NULL) {
1408 SDL_OutOfMemory();
1409 return SDL_FALSE;
1410 }
1411
1412 /* We temporarily open the device to check how it's configured. */
1413 if (LINUX_JoystickOpen(joystick, device_index) < 0) {
1414 SDL_free(joystick);
1415 return SDL_FALSE;
1416 }
1417
1418 if (!joystick->hwdata->has_key[BTN_GAMEPAD]) {
1419 /* Not a gamepad according to the specs. */
1420 LINUX_JoystickClose(joystick);
1421 SDL_free(joystick);
1422 return SDL_FALSE;
1423 }
1424
1425 /* We have a gamepad, start filling out the mappings */
1426
1427 if (joystick->hwdata->has_key[BTN_A]) {
1428 out->a.kind = EMappingKind_Button;
1429 out->a.target = joystick->hwdata->key_map[BTN_A];
1430 }
1431
1432 if (joystick->hwdata->has_key[BTN_B]) {
1433 out->b.kind = EMappingKind_Button;
1434 out->b.target = joystick->hwdata->key_map[BTN_B];
1435 }
1436
1437 if (joystick->hwdata->has_key[BTN_X]) {
1438 out->x.kind = EMappingKind_Button;
1439 out->x.target = joystick->hwdata->key_map[BTN_X];
1440 }
1441
1442 if (joystick->hwdata->has_key[BTN_Y]) {
1443 out->y.kind = EMappingKind_Button;
1444 out->y.target = joystick->hwdata->key_map[BTN_Y];
1445 }
1446
1447 if (joystick->hwdata->has_key[BTN_SELECT]) {
1448 out->back.kind = EMappingKind_Button;
1449 out->back.target = joystick->hwdata->key_map[BTN_SELECT];
1450 }
1451
1452 if (joystick->hwdata->has_key[BTN_START]) {
1453 out->start.kind = EMappingKind_Button;
1454 out->start.target = joystick->hwdata->key_map[BTN_START];
1455 }
1456
1457 if (joystick->hwdata->has_key[BTN_THUMBL]) {
1458 out->leftstick.kind = EMappingKind_Button;
1459 out->leftstick.target = joystick->hwdata->key_map[BTN_THUMBL];
1460 }
1461
1462 if (joystick->hwdata->has_key[BTN_THUMBR]) {
1463 out->rightstick.kind = EMappingKind_Button;
1464 out->rightstick.target = joystick->hwdata->key_map[BTN_THUMBR];
1465 }
1466
1467 if (joystick->hwdata->has_key[BTN_MODE]) {
1468 out->guide.kind = EMappingKind_Button;
1469 out->guide.target = joystick->hwdata->key_map[BTN_MODE];
1470 }
1471
1472 /*
1473 According to the specs the D-Pad, the shoulder buttons and the triggers
1474 can be digital, or analog, or both at the same time.
1475 */
1476
1477 /* Prefer digital shoulder buttons, but settle for analog if missing. */
1478 if (joystick->hwdata->has_key[BTN_TL]) {
1479 out->leftshoulder.kind = EMappingKind_Button;
1480 out->leftshoulder.target = joystick->hwdata->key_map[BTN_TL];
1481 }
1482
1483 if (joystick->hwdata->has_key[BTN_TR]) {
1484 out->rightshoulder.kind = EMappingKind_Button;
1485 out->rightshoulder.target = joystick->hwdata->key_map[BTN_TR];
1486 }
1487
1488 if (joystick->hwdata->has_hat[1] && /* Check if ABS_HAT1{X, Y} is available. */
1489 (!joystick->hwdata->has_key[BTN_TL] || !joystick->hwdata->has_key[BTN_TR])) {
1490 int hat = joystick->hwdata->hats_indices[1] << 4;
1491 out->leftshoulder.kind = EMappingKind_Hat;
1492 out->rightshoulder.kind = EMappingKind_Hat;
1493 out->leftshoulder.target = hat | 0x4;
1494 out->rightshoulder.target = hat | 0x2;
1495 }
1496
1497 /* Prefer analog triggers, but settle for digital if missing. */
1498 if (joystick->hwdata->has_hat[2]) { /* Check if ABS_HAT2{X,Y} is available. */
1499 int hat = joystick->hwdata->hats_indices[2] << 4;
1500 out->lefttrigger.kind = EMappingKind_Hat;
1501 out->righttrigger.kind = EMappingKind_Hat;
1502 out->lefttrigger.target = hat | 0x4;
1503 out->righttrigger.target = hat | 0x2;
1504 } else {
1505 if (joystick->hwdata->has_key[BTN_TL2]) {
1506 out->lefttrigger.kind = EMappingKind_Button;
1507 out->lefttrigger.target = joystick->hwdata->key_map[BTN_TL2];
1508 } else if (joystick->hwdata->has_abs[ABS_Z]) {
1509 out->lefttrigger.kind = EMappingKind_Axis;
1510 out->lefttrigger.target = joystick->hwdata->abs_map[ABS_Z];
1511 }
1512
1513 if (joystick->hwdata->has_key[BTN_TR2]) {
1514 out->righttrigger.kind = EMappingKind_Button;
1515 out->righttrigger.target = joystick->hwdata->key_map[BTN_TR2];
1516 } else if (joystick->hwdata->has_abs[ABS_RZ]) {
1517 out->righttrigger.kind = EMappingKind_Axis;
1518 out->righttrigger.target = joystick->hwdata->abs_map[ABS_RZ];
1519 }
1520 }
1521
1522 /* Prefer digital D-Pad, but settle for analog if missing. */
1523 if (joystick->hwdata->has_key[BTN_DPAD_UP]) {
1524 out->dpup.kind = EMappingKind_Button;
1525 out->dpup.target = joystick->hwdata->key_map[BTN_DPAD_UP];
1526 }
1527
1528 if (joystick->hwdata->has_key[BTN_DPAD_DOWN]) {
1529 out->dpdown.kind = EMappingKind_Button;
1530 out->dpdown.target = joystick->hwdata->key_map[BTN_DPAD_DOWN];
1531 }
1532
1533 if (joystick->hwdata->has_key[BTN_DPAD_LEFT]) {
1534 out->dpleft.kind = EMappingKind_Button;
1535 out->dpleft.target = joystick->hwdata->key_map[BTN_DPAD_LEFT];
1536 }
1537
1538 if (joystick->hwdata->has_key[BTN_DPAD_RIGHT]) {
1539 out->dpright.kind = EMappingKind_Button;
1540 out->dpright.target = joystick->hwdata->key_map[BTN_DPAD_RIGHT];
1541 }
1542
1543 if (joystick->hwdata->has_hat[0] && /* Check if ABS_HAT0{X,Y} is available. */
1544 (!joystick->hwdata->has_key[BTN_DPAD_LEFT] || !joystick->hwdata->has_key[BTN_DPAD_RIGHT] ||
1545 !joystick->hwdata->has_key[BTN_DPAD_UP] || !joystick->hwdata->has_key[BTN_DPAD_DOWN])) {
1546 int hat = joystick->hwdata->hats_indices[0] << 4;
1547 out->dpleft.kind = EMappingKind_Hat;
1548 out->dpright.kind = EMappingKind_Hat;
1549 out->dpup.kind = EMappingKind_Hat;
1550 out->dpdown.kind = EMappingKind_Hat;
1551 out->dpleft.target = hat | 0x8;
1552 out->dpright.target = hat | 0x2;
1553 out->dpup.target = hat | 0x1;
1554 out->dpdown.target = hat | 0x4;
1555 }
1556
1557 if (joystick->hwdata->has_abs[ABS_X] && joystick->hwdata->has_abs[ABS_Y]) {
1558 out->leftx.kind = EMappingKind_Axis;
1559 out->lefty.kind = EMappingKind_Axis;
1560 out->leftx.target = joystick->hwdata->abs_map[ABS_X];
1561 out->lefty.target = joystick->hwdata->abs_map[ABS_Y];
1562 }
1563
1564 if (joystick->hwdata->has_abs[ABS_RX] && joystick->hwdata->has_abs[ABS_RY]) {
1565 out->rightx.kind = EMappingKind_Axis;
1566 out->righty.kind = EMappingKind_Axis;
1567 out->rightx.target = joystick->hwdata->abs_map[ABS_RX];
1568 out->righty.target = joystick->hwdata->abs_map[ABS_RY];
1569 }
1570
1571 LINUX_JoystickClose(joystick);
1572 SDL_free(joystick);
1573
1574 /* Cache the mapping for later */
1575 item->mapping = (SDL_GamepadMapping *)SDL_malloc(sizeof(*item->mapping));
1576 if (item->mapping) {
1577 SDL_memcpy(item->mapping, out, sizeof(*out));
1578 }
1579
1580 return SDL_TRUE;
1581}
1582
1583SDL_JoystickDriver SDL_LINUX_JoystickDriver =
1584{
1585 LINUX_JoystickInit,
1586 LINUX_JoystickGetCount,
1587 LINUX_JoystickDetect,
1588 LINUX_JoystickGetDeviceName,
1589 LINUX_JoystickGetDevicePlayerIndex,
1590 LINUX_JoystickSetDevicePlayerIndex,
1591 LINUX_JoystickGetDeviceGUID,
1592 LINUX_JoystickGetDeviceInstanceID,
1593 LINUX_JoystickOpen,
1594 LINUX_JoystickRumble,
1595 LINUX_JoystickRumbleTriggers,
1596 LINUX_JoystickHasLED,
1597 LINUX_JoystickSetLED,
1598 LINUX_JoystickSetSensorsEnabled,
1599 LINUX_JoystickUpdate,
1600 LINUX_JoystickClose,
1601 LINUX_JoystickQuit,
1602 LINUX_JoystickGetGamepadMapping
1603};
1604
1605#endif /* SDL_JOYSTICK_LINUX */
1606
1607/* vi: set ts=4 sw=4 expandtab: */
1608