SDL 2.0
SDL_sysjoystick.c
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 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#include <sys/ioctl.h>
36#include <unistd.h>
37#include <dirent.h>
38#include <linux/joystick.h>
39
40#include "SDL_assert.h"
41#include "SDL_joystick.h"
42#include "SDL_endian.h"
43#include "SDL_timer.h"
44#include "../../events/SDL_events_c.h"
45#include "../SDL_sysjoystick.h"
46#include "../SDL_joystick_c.h"
47#include "../steam/SDL_steamcontroller.h"
48#include "SDL_sysjoystick_c.h"
49#include "../hidapi/SDL_hidapijoystick_c.h"
50
51/* This isn't defined in older Linux kernel headers */
52#ifndef SYN_DROPPED
53#define SYN_DROPPED 3
54#endif
55
56#include "../../core/linux/SDL_udev.h"
57
58static int MaybeAddDevice(const char *path);
59#if SDL_USE_LIBUDEV
60static int MaybeRemoveDevice(const char *path);
61#endif /* SDL_USE_LIBUDEV */
62
63/* A linked list of available joysticks */
64typedef struct SDL_joylist_item
65{
66 int device_instance;
67 char *path; /* "/dev/input/event2" or whatever */
68 char *name; /* "SideWinder 3D Pro" or whatever */
70 dev_t devnum;
71 struct joystick_hwdata *hwdata;
72 struct SDL_joylist_item *next;
73
74 /* Steam Controller support */
75 SDL_bool m_bSteamController;
76} SDL_joylist_item;
77
78static SDL_joylist_item *SDL_joylist = NULL;
79static SDL_joylist_item *SDL_joylist_tail = NULL;
80static int numjoysticks = 0;
81
82#if !SDL_USE_LIBUDEV
83static Uint32 last_joy_detect_time = 0;
84#endif
85
86#define test_bit(nr, addr) \
87 (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0)
88#define NBITS(x) ((((x)-1)/(sizeof(long) * 8))+1)
89
90static int
91IsJoystick(int fd, char *namebuf, const size_t namebuflen, SDL_JoystickGUID *guid)
92{
93 struct input_id inpid;
94 Uint16 *guid16 = (Uint16 *)guid->data;
95
96#if !SDL_USE_LIBUDEV
97 /* When udev is enabled we only get joystick devices here, so there's no need to test them */
98 unsigned long evbit[NBITS(EV_MAX)] = { 0 };
99 unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
100 unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
101
102 if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||
103 (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
104 (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {
105 return (0);
106 }
107
108 if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) &&
109 test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) {
110 return 0;
111 }
112#endif
113
114 if (ioctl(fd, EVIOCGNAME(namebuflen), namebuf) < 0) {
115 return 0;
116 }
117
118 if (ioctl(fd, EVIOCGID, &inpid) < 0) {
119 return 0;
120 }
121
122#ifdef SDL_JOYSTICK_HIDAPI
123 if (HIDAPI_IsDevicePresent(inpid.vendor, inpid.product, inpid.version)) {
124 /* The HIDAPI driver is taking care of this device */
125 return 0;
126 }
127#endif
128
129#ifdef DEBUG_JOYSTICK
130 printf("Joystick: %s, bustype = %d, vendor = 0x%.4x, product = 0x%.4x, version = %d\n", namebuf, inpid.bustype, inpid.vendor, inpid.product, inpid.version);
131#endif
132
133 SDL_memset(guid->data, 0, sizeof(guid->data));
134
135 /* We only need 16 bits for each of these; space them out to fill 128. */
136 /* Byteswap so devices get same GUID on little/big endian platforms. */
137 *guid16++ = SDL_SwapLE16(inpid.bustype);
138 *guid16++ = 0;
139
140 if (inpid.vendor && inpid.product) {
141 *guid16++ = SDL_SwapLE16(inpid.vendor);
142 *guid16++ = 0;
143 *guid16++ = SDL_SwapLE16(inpid.product);
144 *guid16++ = 0;
145 *guid16++ = SDL_SwapLE16(inpid.version);
146 *guid16++ = 0;
147 } else {
148 SDL_strlcpy((char*)guid16, namebuf, sizeof(guid->data) - 4);
149 }
150
151 if (SDL_ShouldIgnoreJoystick(namebuf, *guid)) {
152 return 0;
153 }
154 return 1;
155}
156
157#if SDL_USE_LIBUDEV
158static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
159{
160 if (devpath == NULL) {
161 return;
162 }
163
164 switch (udev_type) {
165 case SDL_UDEV_DEVICEADDED:
166 if (!(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) {
167 return;
168 }
169 MaybeAddDevice(devpath);
170 break;
171
172 case SDL_UDEV_DEVICEREMOVED:
173 MaybeRemoveDevice(devpath);
174 break;
175
176 default:
177 break;
178 }
179
180}
181#endif /* SDL_USE_LIBUDEV */
182
183static int
184MaybeAddDevice(const char *path)
185{
186 struct stat sb;
187 int fd = -1;
188 int isstick = 0;
189 char namebuf[128];
190 SDL_JoystickGUID guid;
191 SDL_joylist_item *item;
192
193 if (path == NULL) {
194 return -1;
195 }
196
197 if (stat(path, &sb) == -1) {
198 return -1;
199 }
200
201 /* Check to make sure it's not already in list. */
202 for (item = SDL_joylist; item != NULL; item = item->next) {
203 if (sb.st_rdev == item->devnum) {
204 return -1; /* already have this one */
205 }
206 }
207
208 fd = open(path, O_RDONLY, 0);
209 if (fd < 0) {
210 return -1;
211 }
212
213#ifdef DEBUG_INPUT_EVENTS
214 printf("Checking %s\n", path);
215#endif
216
217 isstick = IsJoystick(fd, namebuf, sizeof (namebuf), &guid);
218 close(fd);
219 if (!isstick) {
220 return -1;
221 }
222
223 item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item));
224 if (item == NULL) {
225 return -1;
226 }
227
228 SDL_zerop(item);
229 item->devnum = sb.st_rdev;
230 item->path = SDL_strdup(path);
231 item->name = SDL_strdup(namebuf);
232 item->guid = guid;
233
234 if ((item->path == NULL) || (item->name == NULL)) {
235 SDL_free(item->path);
236 SDL_free(item->name);
237 SDL_free(item);
238 return -1;
239 }
240
241 item->device_instance = SDL_GetNextJoystickInstanceID();
242 if (SDL_joylist_tail == NULL) {
243 SDL_joylist = SDL_joylist_tail = item;
244 } else {
245 SDL_joylist_tail->next = item;
246 SDL_joylist_tail = item;
247 }
248
249 /* Need to increment the joystick count before we post the event */
250 ++numjoysticks;
251
252 SDL_PrivateJoystickAdded(item->device_instance);
253
254 return numjoysticks;
255}
256
257#if SDL_USE_LIBUDEV
258static int
259MaybeRemoveDevice(const char *path)
260{
261 SDL_joylist_item *item;
262 SDL_joylist_item *prev = NULL;
263
264 if (path == NULL) {
265 return -1;
266 }
267
268 for (item = SDL_joylist; item != NULL; item = item->next) {
269 /* found it, remove it. */
270 if (SDL_strcmp(path, item->path) == 0) {
271 const int retval = item->device_instance;
272 if (item->hwdata) {
273 item->hwdata->item = NULL;
274 }
275 if (prev != NULL) {
276 prev->next = item->next;
277 } else {
278 SDL_assert(SDL_joylist == item);
279 SDL_joylist = item->next;
280 }
281 if (item == SDL_joylist_tail) {
282 SDL_joylist_tail = prev;
283 }
284
285 /* Need to decrement the joystick count before we post the event */
286 --numjoysticks;
287
288 SDL_PrivateJoystickRemoved(item->device_instance);
289
290 SDL_free(item->path);
291 SDL_free(item->name);
292 SDL_free(item);
293 return retval;
294 }
295 prev = item;
296 }
297
298 return -1;
299}
300#endif
301
302static void
303HandlePendingRemovals(void)
304{
305 SDL_joylist_item *prev = NULL;
306 SDL_joylist_item *item = SDL_joylist;
307
308 while (item != NULL) {
309 if (item->hwdata && item->hwdata->gone) {
310 item->hwdata->item = NULL;
311
312 if (prev != NULL) {
313 prev->next = item->next;
314 } else {
315 SDL_assert(SDL_joylist == item);
316 SDL_joylist = item->next;
317 }
318 if (item == SDL_joylist_tail) {
319 SDL_joylist_tail = prev;
320 }
321
322 /* Need to decrement the joystick count before we post the event */
323 --numjoysticks;
324
325 SDL_PrivateJoystickRemoved(item->device_instance);
326
327 SDL_free(item->path);
328 SDL_free(item->name);
329 SDL_free(item);
330
331 if (prev != NULL) {
332 item = prev->next;
333 } else {
334 item = SDL_joylist;
335 }
336 } else {
337 prev = item;
338 item = item->next;
339 }
340 }
341}
342
343static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickGUID guid, int *device_instance)
344{
345 SDL_joylist_item *item;
346
347 item = (SDL_joylist_item *) SDL_calloc(1, sizeof (SDL_joylist_item));
348 if (item == NULL) {
349 return SDL_FALSE;
350 }
351
352 item->path = SDL_strdup("");
353 item->name = SDL_strdup(name);
354 item->guid = guid;
355 item->m_bSteamController = SDL_TRUE;
356
357 if ((item->path == NULL) || (item->name == NULL)) {
358 SDL_free(item->path);
359 SDL_free(item->name);
360 SDL_free(item);
361 return SDL_FALSE;
362 }
363
364 *device_instance = item->device_instance = SDL_GetNextJoystickInstanceID();
365 if (SDL_joylist_tail == NULL) {
366 SDL_joylist = SDL_joylist_tail = item;
367 } else {
368 SDL_joylist_tail->next = item;
369 SDL_joylist_tail = item;
370 }
371
372 /* Need to increment the joystick count before we post the event */
373 ++numjoysticks;
374
375 SDL_PrivateJoystickAdded(item->device_instance);
376
377 return SDL_TRUE;
378}
379
380static void SteamControllerDisconnectedCallback(int device_instance)
381{
382 SDL_joylist_item *item;
383 SDL_joylist_item *prev = NULL;
384
385 for (item = SDL_joylist; item != NULL; item = item->next) {
386 /* found it, remove it. */
387 if (item->device_instance == device_instance) {
388 if (item->hwdata) {
389 item->hwdata->item = NULL;
390 }
391 if (prev != NULL) {
392 prev->next = item->next;
393 } else {
394 SDL_assert(SDL_joylist == item);
395 SDL_joylist = item->next;
396 }
397 if (item == SDL_joylist_tail) {
398 SDL_joylist_tail = prev;
399 }
400
401 /* Need to decrement the joystick count before we post the event */
402 --numjoysticks;
403
404 SDL_PrivateJoystickRemoved(item->device_instance);
405
406 SDL_free(item->name);
407 SDL_free(item);
408 return;
409 }
410 prev = item;
411 }
412}
413
414static void
415LINUX_JoystickDetect(void)
416{
417#if SDL_USE_LIBUDEV
418 SDL_UDEV_Poll();
419#else
420 const Uint32 SDL_JOY_DETECT_INTERVAL_MS = 3000; /* Update every 3 seconds */
421 Uint32 now = SDL_GetTicks();
422
423 if (!last_joy_detect_time || SDL_TICKS_PASSED(now, last_joy_detect_time + SDL_JOY_DETECT_INTERVAL_MS)) {
424 DIR *folder;
425 struct dirent *dent;
426
427 folder = opendir("/dev/input");
428 if (folder) {
429 while ((dent = readdir(folder))) {
430 int len = SDL_strlen(dent->d_name);
431 if (len > 5 && SDL_strncmp(dent->d_name, "event", 5) == 0) {
432 char path[PATH_MAX];
433 SDL_snprintf(path, SDL_arraysize(path), "/dev/input/%s", dent->d_name);
434 MaybeAddDevice(path);
435 }
436 }
437
438 closedir(folder);
439 }
440
441 last_joy_detect_time = now;
442 }
443#endif
444
445 HandlePendingRemovals();
446
448}
449
450static int
451LINUX_JoystickInit(void)
452{
453 /* First see if the user specified one or more joysticks to use */
454 if (SDL_getenv("SDL_JOYSTICK_DEVICE") != NULL) {
455 char *envcopy, *envpath, *delim;
456 envcopy = SDL_strdup(SDL_getenv("SDL_JOYSTICK_DEVICE"));
457 envpath = envcopy;
458 while (envpath != NULL) {
459 delim = SDL_strchr(envpath, ':');
460 if (delim != NULL) {
461 *delim++ = '\0';
462 }
463 MaybeAddDevice(envpath);
464 envpath = delim;
465 }
466 SDL_free(envcopy);
467 }
468
469 SDL_InitSteamControllers(SteamControllerConnectedCallback,
470 SteamControllerDisconnectedCallback);
471
472#if SDL_USE_LIBUDEV
473 if (SDL_UDEV_Init() < 0) {
474 return SDL_SetError("Could not initialize UDEV");
475 }
476
477 /* Set up the udev callback */
478 if (SDL_UDEV_AddCallback(joystick_udev_callback) < 0) {
479 SDL_UDEV_Quit();
480 return SDL_SetError("Could not set up joystick <-> udev callback");
481 }
482
483 /* Force a scan to build the initial device list */
484 SDL_UDEV_Scan();
485#else
486 /* Report all devices currently present */
487 LINUX_JoystickDetect();
488#endif
489
490 return 0;
491}
492
493static int
494LINUX_JoystickGetCount(void)
495{
496 return numjoysticks;
497}
498
499static SDL_joylist_item *
500JoystickByDevIndex(int device_index)
501{
502 SDL_joylist_item *item = SDL_joylist;
503
504 if ((device_index < 0) || (device_index >= numjoysticks)) {
505 return NULL;
506 }
507
508 while (device_index > 0) {
509 SDL_assert(item != NULL);
510 device_index--;
511 item = item->next;
512 }
513
514 return item;
515}
516
517/* Function to get the device-dependent name of a joystick */
518static const char *
519LINUX_JoystickGetDeviceName(int device_index)
520{
521 return JoystickByDevIndex(device_index)->name;
522}
523
524static int
525LINUX_JoystickGetDevicePlayerIndex(int device_index)
526{
527 return -1;
528}
529
530static SDL_JoystickGUID
531LINUX_JoystickGetDeviceGUID( int device_index )
532{
533 return JoystickByDevIndex(device_index)->guid;
534}
535
536/* Function to perform the mapping from device index to the instance id for this index */
537static SDL_JoystickID
538LINUX_JoystickGetDeviceInstanceID(int device_index)
539{
540 return JoystickByDevIndex(device_index)->device_instance;
541}
542
543static int
544allocate_hatdata(SDL_Joystick * joystick)
545{
546 int i;
547
548 joystick->hwdata->hats =
549 (struct hwdata_hat *) SDL_malloc(joystick->nhats *
550 sizeof(struct hwdata_hat));
551 if (joystick->hwdata->hats == NULL) {
552 return (-1);
553 }
554 for (i = 0; i < joystick->nhats; ++i) {
555 joystick->hwdata->hats[i].axis[0] = 1;
556 joystick->hwdata->hats[i].axis[1] = 1;
557 }
558 return (0);
559}
560
561static int
562allocate_balldata(SDL_Joystick * joystick)
563{
564 int i;
565
566 joystick->hwdata->balls =
567 (struct hwdata_ball *) SDL_malloc(joystick->nballs *
568 sizeof(struct hwdata_ball));
569 if (joystick->hwdata->balls == NULL) {
570 return (-1);
571 }
572 for (i = 0; i < joystick->nballs; ++i) {
573 joystick->hwdata->balls[i].axis[0] = 0;
574 joystick->hwdata->balls[i].axis[1] = 0;
575 }
576 return (0);
577}
578
579static void
580ConfigJoystick(SDL_Joystick * joystick, int fd)
581{
582 int i, t;
583 unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
584 unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
585 unsigned long relbit[NBITS(REL_MAX)] = { 0 };
586 unsigned long ffbit[NBITS(FF_MAX)] = { 0 };
587
588 /* See if this device uses the new unified event API */
589 if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&
590 (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) &&
591 (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0)) {
592
593 /* Get the number of buttons, axes, and other thingamajigs */
594 for (i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
595 if (test_bit(i, keybit)) {
596#ifdef DEBUG_INPUT_EVENTS
597 printf("Joystick has button: 0x%x\n", i);
598#endif
599 joystick->hwdata->key_map[i] = joystick->nbuttons;
600 ++joystick->nbuttons;
601 }
602 }
603 for (i = 0; i < BTN_JOYSTICK; ++i) {
604 if (test_bit(i, keybit)) {
605#ifdef DEBUG_INPUT_EVENTS
606 printf("Joystick has button: 0x%x\n", i);
607#endif
608 joystick->hwdata->key_map[i] = joystick->nbuttons;
609 ++joystick->nbuttons;
610 }
611 }
612 for (i = 0; i < ABS_MAX; ++i) {
613 /* Skip hats */
614 if (i == ABS_HAT0X) {
615 i = ABS_HAT3Y;
616 continue;
617 }
618 if (test_bit(i, absbit)) {
619 struct input_absinfo absinfo;
620
621 if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {
622 continue;
623 }
624#ifdef DEBUG_INPUT_EVENTS
625 printf("Joystick has absolute axis: 0x%.2x\n", i);
626 printf("Values = { %d, %d, %d, %d, %d }\n",
627 absinfo.value, absinfo.minimum, absinfo.maximum,
628 absinfo.fuzz, absinfo.flat);
629#endif /* DEBUG_INPUT_EVENTS */
630 joystick->hwdata->abs_map[i] = joystick->naxes;
631 if (absinfo.minimum == absinfo.maximum) {
632 joystick->hwdata->abs_correct[i].used = 0;
633 } else {
634 joystick->hwdata->abs_correct[i].used = 1;
635 joystick->hwdata->abs_correct[i].coef[0] =
636 (absinfo.maximum + absinfo.minimum) - 2 * absinfo.flat;
637 joystick->hwdata->abs_correct[i].coef[1] =
638 (absinfo.maximum + absinfo.minimum) + 2 * absinfo.flat;
639 t = ((absinfo.maximum - absinfo.minimum) - 4 * absinfo.flat);
640 if (t != 0) {
641 joystick->hwdata->abs_correct[i].coef[2] =
642 (1 << 28) / t;
643 } else {
644 joystick->hwdata->abs_correct[i].coef[2] = 0;
645 }
646 }
647 ++joystick->naxes;
648 }
649 }
650 for (i = ABS_HAT0X; i <= ABS_HAT3Y; i += 2) {
651 if (test_bit(i, absbit) || test_bit(i + 1, absbit)) {
652 struct input_absinfo absinfo;
653 int hat_index = (i - ABS_HAT0X) / 2;
654
655 if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {
656 continue;
657 }
658#ifdef DEBUG_INPUT_EVENTS
659 printf("Joystick has hat %d\n", hat_index);
660 printf("Values = { %d, %d, %d, %d, %d }\n",
661 absinfo.value, absinfo.minimum, absinfo.maximum,
662 absinfo.fuzz, absinfo.flat);
663#endif /* DEBUG_INPUT_EVENTS */
664 joystick->hwdata->hats_indices[joystick->nhats++] = hat_index;
665 }
666 }
667 if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) {
668 ++joystick->nballs;
669 }
670
671 /* Allocate data to keep track of these thingamajigs */
672 if (joystick->nhats > 0) {
673 if (allocate_hatdata(joystick) < 0) {
674 joystick->nhats = 0;
675 }
676 }
677 if (joystick->nballs > 0) {
678 if (allocate_balldata(joystick) < 0) {
679 joystick->nballs = 0;
680 }
681 }
682 }
683
684 if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) >= 0) {
685 if (test_bit(FF_RUMBLE, ffbit)) {
686 joystick->hwdata->ff_rumble = SDL_TRUE;
687 }
688 if (test_bit(FF_SINE, ffbit)) {
689 joystick->hwdata->ff_sine = SDL_TRUE;
690 }
691 }
692}
693
694
695/* Function to open a joystick for use.
696 The joystick to open is specified by the device index.
697 This should fill the nbuttons and naxes fields of the joystick structure.
698 It returns 0, or -1 if there is an error.
699 */
700static int
701LINUX_JoystickOpen(SDL_Joystick * joystick, int device_index)
702{
703 SDL_joylist_item *item = JoystickByDevIndex(device_index);
704
705 if (item == NULL) {
706 return SDL_SetError("No such device");
707 }
708
709 joystick->instance_id = item->device_instance;
710 joystick->hwdata = (struct joystick_hwdata *)
711 SDL_calloc(1, sizeof(*joystick->hwdata));
712 if (joystick->hwdata == NULL) {
713 return SDL_OutOfMemory();
714 }
715 joystick->hwdata->item = item;
716 joystick->hwdata->guid = item->guid;
717 joystick->hwdata->effect.id = -1;
718 joystick->hwdata->m_bSteamController = item->m_bSteamController;
719 SDL_memset(joystick->hwdata->abs_map, 0xFF, sizeof(joystick->hwdata->abs_map));
720
721 if (item->m_bSteamController) {
722 joystick->hwdata->fd = -1;
724 &joystick->naxes,
725 &joystick->nhats);
726 } else {
727 int fd = open(item->path, O_RDWR, 0);
728 if (fd < 0) {
729 SDL_free(joystick->hwdata);
730 joystick->hwdata = NULL;
731 return SDL_SetError("Unable to open %s", item->path);
732 }
733
734 joystick->hwdata->fd = fd;
735 joystick->hwdata->fname = SDL_strdup(item->path);
736 if (joystick->hwdata->fname == NULL) {
737 SDL_free(joystick->hwdata);
738 joystick->hwdata = NULL;
739 close(fd);
740 return SDL_OutOfMemory();
741 }
742
743 /* Set the joystick to non-blocking read mode */
744 fcntl(fd, F_SETFL, O_NONBLOCK);
745
746 /* Get the number of buttons and axes on the joystick */
747 ConfigJoystick(joystick, fd);
748 }
749
750 SDL_assert(item->hwdata == NULL);
751 item->hwdata = joystick->hwdata;
752
753 /* mark joystick as fresh and ready */
754 joystick->hwdata->fresh = 1;
755
756 return (0);
757}
758
759static int
760LINUX_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
761{
762 struct input_event event;
763
764 if (joystick->hwdata->ff_rumble) {
765 struct ff_effect *effect = &joystick->hwdata->effect;
766
767 effect->type = FF_RUMBLE;
768 effect->replay.length = SDL_min(duration_ms, 32767);
769 effect->u.rumble.strong_magnitude = low_frequency_rumble;
770 effect->u.rumble.weak_magnitude = high_frequency_rumble;
771 } else if (joystick->hwdata->ff_sine) {
772 /* Scale and average the two rumble strengths */
773 Sint16 magnitude = (Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
774 struct ff_effect *effect = &joystick->hwdata->effect;
775
776 effect->type = FF_PERIODIC;
777 effect->replay.length = SDL_min(duration_ms, 32767);
778 effect->u.periodic.waveform = FF_SINE;
779 effect->u.periodic.magnitude = magnitude;
780 } else {
781 return SDL_Unsupported();
782 }
783
784 if (ioctl(joystick->hwdata->fd, EVIOCSFF, &joystick->hwdata->effect) < 0) {
785 return SDL_SetError("Couldn't update rumble effect: %s", strerror(errno));
786 }
787
788 event.type = EV_FF;
789 event.code = joystick->hwdata->effect.id;
790 event.value = 1;
791 if (write(joystick->hwdata->fd, &event, sizeof(event)) < 0) {
792 return SDL_SetError("Couldn't start rumble effect: %s", strerror(errno));
793 }
794 return 0;
795}
796
797static SDL_INLINE void
798HandleHat(SDL_Joystick * stick, Uint8 hat, int axis, int value)
799{
800 struct hwdata_hat *the_hat;
801 const Uint8 position_map[3][3] = {
805 };
806
807 the_hat = &stick->hwdata->hats[hat];
808 if (value < 0) {
809 value = 0;
810 } else if (value == 0) {
811 value = 1;
812 } else if (value > 0) {
813 value = 2;
814 }
815 if (value != the_hat->axis[axis]) {
816 the_hat->axis[axis] = value;
817 SDL_PrivateJoystickHat(stick, hat,
818 position_map[the_hat->axis[1]][the_hat->axis[0]]);
819 }
820}
821
822static SDL_INLINE void
823HandleBall(SDL_Joystick * stick, Uint8 ball, int axis, int value)
824{
825 stick->hwdata->balls[ball].axis[axis] += value;
826}
827
828
829static SDL_INLINE int
830AxisCorrect(SDL_Joystick * joystick, int which, int value)
831{
832 struct axis_correct *correct;
833
834 correct = &joystick->hwdata->abs_correct[which];
835 if (correct->used) {
836 value *= 2;
837 if (value > correct->coef[0]) {
838 if (value < correct->coef[1]) {
839 return 0;
840 }
841 value -= correct->coef[1];
842 } else {
843 value -= correct->coef[0];
844 }
845 value *= correct->coef[2];
846 value >>= 13;
847 }
848
849 /* Clamp and return */
850 if (value < -32768)
851 return -32768;
852 if (value > 32767)
853 return 32767;
854
855 return value;
856}
857
858static SDL_INLINE void
859PollAllValues(SDL_Joystick * joystick)
860{
861 struct input_absinfo absinfo;
862 int i;
863
864 /* Poll all axis */
865 for (i = ABS_X; i < ABS_MAX; i++) {
866 if (i == ABS_HAT0X) {
867 i = ABS_HAT3Y;
868 continue;
869 }
870 if (joystick->hwdata->abs_correct[i].used) {
871 if (ioctl(joystick->hwdata->fd, EVIOCGABS(i), &absinfo) >= 0) {
872 absinfo.value = AxisCorrect(joystick, i, absinfo.value);
873
874#ifdef DEBUG_INPUT_EVENTS
875 printf("Joystick : Re-read Axis %d (%d) val= %d\n",
876 joystick->hwdata->abs_map[i], i, absinfo.value);
877#endif
879 joystick->hwdata->abs_map[i],
880 absinfo.value);
881 }
882 }
883 }
884}
885
886static SDL_INLINE void
887HandleInputEvents(SDL_Joystick * joystick)
888{
889 struct input_event events[32];
890 int i, len;
891 int code;
892
893 if (joystick->hwdata->fresh) {
894 PollAllValues(joystick);
895 joystick->hwdata->fresh = 0;
896 }
897
898 while ((len = read(joystick->hwdata->fd, events, (sizeof events))) > 0) {
899 len /= sizeof(events[0]);
900 for (i = 0; i < len; ++i) {
901 code = events[i].code;
902 switch (events[i].type) {
903 case EV_KEY:
905 joystick->hwdata->key_map[code],
906 events[i].value);
907 break;
908 case EV_ABS:
909 switch (code) {
910 case ABS_HAT0X:
911 case ABS_HAT0Y:
912 case ABS_HAT1X:
913 case ABS_HAT1Y:
914 case ABS_HAT2X:
915 case ABS_HAT2Y:
916 case ABS_HAT3X:
917 case ABS_HAT3Y:
918 code -= ABS_HAT0X;
919 HandleHat(joystick, joystick->hwdata->hats_indices[code / 2], code % 2, events[i].value);
920 break;
921 default:
922 if (joystick->hwdata->abs_map[code] != 0xFF) {
923 events[i].value =
924 AxisCorrect(joystick, code, events[i].value);
926 joystick->hwdata->abs_map[code],
927 events[i].value);
928 }
929 break;
930 }
931 break;
932 case EV_REL:
933 switch (code) {
934 case REL_X:
935 case REL_Y:
936 code -= REL_X;
937 HandleBall(joystick, code / 2, code % 2, events[i].value);
938 break;
939 default:
940 break;
941 }
942 break;
943 case EV_SYN:
944 switch (code) {
945 case SYN_DROPPED :
946#ifdef DEBUG_INPUT_EVENTS
947 printf("Event SYN_DROPPED detected\n");
948#endif
949 PollAllValues(joystick);
950 break;
951 default:
952 break;
953 }
954 default:
955 break;
956 }
957 }
958 }
959
960 if (errno == ENODEV) {
961 /* We have to wait until the JoystickDetect callback to remove this */
962 joystick->hwdata->gone = SDL_TRUE;
963 }
964}
965
966static void
967LINUX_JoystickUpdate(SDL_Joystick * joystick)
968{
969 int i;
970
971 if (joystick->hwdata->m_bSteamController) {
973 return;
974 }
975
976 HandleInputEvents(joystick);
977
978 /* Deliver ball motion updates */
979 for (i = 0; i < joystick->nballs; ++i) {
980 int xrel, yrel;
981
982 xrel = joystick->hwdata->balls[i].axis[0];
983 yrel = joystick->hwdata->balls[i].axis[1];
984 if (xrel || yrel) {
985 joystick->hwdata->balls[i].axis[0] = 0;
986 joystick->hwdata->balls[i].axis[1] = 0;
987 SDL_PrivateJoystickBall(joystick, (Uint8) i, xrel, yrel);
988 }
989 }
990}
991
992/* Function to close a joystick after use */
993static void
994LINUX_JoystickClose(SDL_Joystick * joystick)
995{
996 if (joystick->hwdata) {
997 if (joystick->hwdata->effect.id >= 0) {
998 ioctl(joystick->hwdata->fd, EVIOCRMFF, joystick->hwdata->effect.id);
999 joystick->hwdata->effect.id = -1;
1000 }
1001 if (joystick->hwdata->fd >= 0) {
1002 close(joystick->hwdata->fd);
1003 }
1004 if (joystick->hwdata->item) {
1005 joystick->hwdata->item->hwdata = NULL;
1006 }
1007 SDL_free(joystick->hwdata->hats);
1008 SDL_free(joystick->hwdata->balls);
1009 SDL_free(joystick->hwdata->fname);
1010 SDL_free(joystick->hwdata);
1011 }
1012}
1013
1014/* Function to perform any system-specific joystick related cleanup */
1015static void
1016LINUX_JoystickQuit(void)
1017{
1018 SDL_joylist_item *item = NULL;
1019 SDL_joylist_item *next = NULL;
1020
1021 for (item = SDL_joylist; item; item = next) {
1022 next = item->next;
1023 SDL_free(item->path);
1024 SDL_free(item->name);
1025 SDL_free(item);
1026 }
1027
1028 SDL_joylist = SDL_joylist_tail = NULL;
1029
1030 numjoysticks = 0;
1031
1032#if SDL_USE_LIBUDEV
1033 SDL_UDEV_DelCallback(joystick_udev_callback);
1034 SDL_UDEV_Quit();
1035#endif
1036
1038}
1039
1041{
1042 LINUX_JoystickInit,
1043 LINUX_JoystickGetCount,
1044 LINUX_JoystickDetect,
1045 LINUX_JoystickGetDeviceName,
1046 LINUX_JoystickGetDevicePlayerIndex,
1047 LINUX_JoystickGetDeviceGUID,
1048 LINUX_JoystickGetDeviceInstanceID,
1049 LINUX_JoystickOpen,
1050 LINUX_JoystickRumble,
1051 LINUX_JoystickUpdate,
1052 LINUX_JoystickClose,
1053 LINUX_JoystickQuit,
1054};
1055
1056#endif /* SDL_JOYSTICK_LINUX */
1057
1058/* vi: set ts=4 sw=4 expandtab: */
#define SDL_assert(condition)
Definition: SDL_assert.h:169
#define SDL_SetError
#define SDL_memset
#define SDL_strchr
#define SDL_strncmp
#define SDL_getenv
#define SDL_malloc
#define SDL_strlen
#define SDL_strlcpy
#define SDL_free
#define SDL_strdup
#define SDL_strcmp
#define SDL_snprintf
#define SDL_calloc
#define SDL_SwapLE16(X)
Definition: SDL_endian.h:232
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
#define SDL_Unsupported()
Definition: SDL_error.h:53
SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version)
int SDL_PrivateJoystickBall(SDL_Joystick *joystick, Uint8 ball, Sint16 xrel, Sint16 yrel)
Definition: SDL_joystick.c:930
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
Definition: SDL_joystick.c:890
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:805
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
Definition: SDL_joystick.c:833
SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid)
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:755
SDL_JoystickID SDL_GetNextJoystickInstanceID()
Definition: SDL_joystick.c:163
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
Definition: SDL_joystick.c:966
#define SDL_HAT_LEFTDOWN
Definition: SDL_joystick.h:337
#define SDL_HAT_LEFT
Definition: SDL_joystick.h:333
#define SDL_HAT_RIGHT
Definition: SDL_joystick.h:331
#define SDL_HAT_RIGHTUP
Definition: SDL_joystick.h:334
#define SDL_HAT_LEFTUP
Definition: SDL_joystick.h:336
Sint32 SDL_JoystickID
Definition: SDL_joystick.h:81
#define SDL_HAT_DOWN
Definition: SDL_joystick.h:332
#define SDL_HAT_RIGHTDOWN
Definition: SDL_joystick.h:335
#define SDL_HAT_UP
Definition: SDL_joystick.h:330
#define SDL_HAT_CENTERED
Definition: SDL_joystick.h:329
GLdouble GLdouble t
Definition: SDL_opengl.h:2071
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
struct _cl_event * event
GLenum GLsizei len
GLuint const GLchar * name
GLsizei const GLchar *const * path
GLsizei const GLfloat * value
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
uint32_t Uint32
Definition: SDL_stdinc.h:203
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
int16_t Sint16
Definition: SDL_stdinc.h:185
#define SDL_min(x, y)
Definition: SDL_stdinc.h:406
uint16_t Uint16
Definition: SDL_stdinc.h:191
uint8_t Uint8
Definition: SDL_stdinc.h:179
void SDL_InitSteamControllers(SteamControllerConnectedCallback_t connectedCallback, SteamControllerDisconnectedCallback_t disconnectedCallback)
void SDL_GetSteamControllerInputs(int *nbuttons, int *naxes, int *nhats)
void SDL_UpdateSteamControllers(void)
void SDL_UpdateSteamController(SDL_Joystick *joystick)
void SDL_QuitSteamControllers(void)
SDL_JoystickDriver SDL_LINUX_JoystickDriver
static int numjoysticks
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
#define SDL_TICKS_PASSED(A, B)
Compare SDL ticks values, and return true if A has passed B.
Definition: SDL_timer.h:56
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:167
#define SDL_INLINE
Definition: begin_code.h:134
GLuint64 GLenum GLint fd
Definition: gl2ext.h:1508
Uint8 data[16]
Definition: SDL_joystick.h:71
SDL_Joystick * joystick
struct SDL_joylist_item * item
SDL_Texture * axis
SDL_bool retval
static SDL_Event events[EVENT_BUF_SIZE]
Definition: testgesture.c:39