21#include "../../SDL_internal.h"
23#ifdef SDL_JOYSTICK_LINUX
25#ifndef SDL_INPUT_LINUXEV
26#error SDL now requires a Linux 2.4+ kernel with /dev/input/event support.
38#include <linux/joystick.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"
56#include "../../core/linux/SDL_udev.h"
58static int MaybeAddDevice(
const char *
path);
60static int MaybeRemoveDevice(
const char *
path);
64typedef struct SDL_joylist_item
72 struct SDL_joylist_item *next;
78static SDL_joylist_item *SDL_joylist =
NULL;
79static SDL_joylist_item *SDL_joylist_tail =
NULL;
83static Uint32 last_joy_detect_time = 0;
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)
93 struct input_id inpid;
98 unsigned long evbit[NBITS(EV_MAX)] = { 0 };
99 unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
100 unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
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)) {
108 if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) &&
109 test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) {
114 if (ioctl(
fd, EVIOCGNAME(namebuflen), namebuf) < 0) {
118 if (ioctl(
fd, EVIOCGID, &inpid) < 0) {
122#ifdef SDL_JOYSTICK_HIDAPI
130 printf(
"Joystick: %s, bustype = %d, vendor = 0x%.4x, product = 0x%.4x, version = %d\n", namebuf, inpid.bustype, inpid.vendor, inpid.product, inpid.version);
140 if (inpid.vendor && inpid.product) {
158static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type,
int udev_class,
const char *devpath)
160 if (devpath ==
NULL) {
165 case SDL_UDEV_DEVICEADDED:
166 if (!(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) {
169 MaybeAddDevice(devpath);
172 case SDL_UDEV_DEVICEREMOVED:
173 MaybeRemoveDevice(devpath);
184MaybeAddDevice(
const char *
path)
191 SDL_joylist_item *item;
197 if (stat(
path, &sb) == -1) {
202 for (item = SDL_joylist; item !=
NULL; item = item->next) {
203 if (sb.st_rdev == item->devnum) {
208 fd = open(
path, O_RDONLY, 0);
213#ifdef DEBUG_INPUT_EVENTS
214 printf(
"Checking %s\n",
path);
217 isstick = IsJoystick(
fd, namebuf,
sizeof (namebuf), &guid);
223 item = (SDL_joylist_item *)
SDL_malloc(
sizeof (SDL_joylist_item));
229 item->devnum = sb.st_rdev;
234 if ((item->path ==
NULL) || (item->name ==
NULL)) {
242 if (SDL_joylist_tail ==
NULL) {
243 SDL_joylist = SDL_joylist_tail = item;
245 SDL_joylist_tail->next = item;
246 SDL_joylist_tail = item;
259MaybeRemoveDevice(
const char *
path)
261 SDL_joylist_item *item;
262 SDL_joylist_item *prev =
NULL;
268 for (item = SDL_joylist; item !=
NULL; item = item->next) {
271 const int retval = item->device_instance;
273 item->hwdata->item =
NULL;
276 prev->next = item->next;
279 SDL_joylist = item->next;
281 if (item == SDL_joylist_tail) {
282 SDL_joylist_tail = prev;
303HandlePendingRemovals(
void)
305 SDL_joylist_item *prev =
NULL;
306 SDL_joylist_item *item = SDL_joylist;
308 while (item !=
NULL) {
309 if (item->hwdata && item->hwdata->gone) {
310 item->hwdata->item =
NULL;
313 prev->next = item->next;
316 SDL_joylist = item->next;
318 if (item == SDL_joylist_tail) {
319 SDL_joylist_tail = prev;
345 SDL_joylist_item *item;
347 item = (SDL_joylist_item *)
SDL_calloc(1,
sizeof (SDL_joylist_item));
355 item->m_bSteamController =
SDL_TRUE;
357 if ((item->path ==
NULL) || (item->name ==
NULL)) {
365 if (SDL_joylist_tail ==
NULL) {
366 SDL_joylist = SDL_joylist_tail = item;
368 SDL_joylist_tail->next = item;
369 SDL_joylist_tail = item;
380static void SteamControllerDisconnectedCallback(
int device_instance)
382 SDL_joylist_item *item;
383 SDL_joylist_item *prev =
NULL;
385 for (item = SDL_joylist; item !=
NULL; item = item->next) {
387 if (item->device_instance == device_instance) {
389 item->hwdata->item =
NULL;
392 prev->next = item->next;
395 SDL_joylist = item->next;
397 if (item == SDL_joylist_tail) {
398 SDL_joylist_tail = prev;
415LINUX_JoystickDetect(
void)
420 const Uint32 SDL_JOY_DETECT_INTERVAL_MS = 3000;
423 if (!last_joy_detect_time ||
SDL_TICKS_PASSED(now, last_joy_detect_time + SDL_JOY_DETECT_INTERVAL_MS)) {
427 folder = opendir(
"/dev/input");
429 while ((dent = readdir(folder))) {
434 MaybeAddDevice(
path);
441 last_joy_detect_time = now;
445 HandlePendingRemovals();
451LINUX_JoystickInit(
void)
455 char *envcopy, *envpath, *delim;
458 while (envpath !=
NULL) {
463 MaybeAddDevice(envpath);
470 SteamControllerDisconnectedCallback);
473 if (SDL_UDEV_Init() < 0) {
478 if (SDL_UDEV_AddCallback(joystick_udev_callback) < 0) {
480 return SDL_SetError(
"Could not set up joystick <-> udev callback");
487 LINUX_JoystickDetect();
494LINUX_JoystickGetCount(
void)
499static SDL_joylist_item *
500JoystickByDevIndex(
int device_index)
502 SDL_joylist_item *item = SDL_joylist;
504 if ((device_index < 0) || (device_index >=
numjoysticks)) {
508 while (device_index > 0) {
519LINUX_JoystickGetDeviceName(
int device_index)
521 return JoystickByDevIndex(device_index)->name;
525LINUX_JoystickGetDevicePlayerIndex(
int device_index)
531LINUX_JoystickGetDeviceGUID(
int device_index )
533 return JoystickByDevIndex(device_index)->guid;
538LINUX_JoystickGetDeviceInstanceID(
int device_index)
540 return JoystickByDevIndex(device_index)->device_instance;
544allocate_hatdata(SDL_Joystick * joystick)
548 joystick->hwdata->hats =
549 (
struct hwdata_hat *)
SDL_malloc(joystick->nhats *
550 sizeof(
struct hwdata_hat));
551 if (joystick->hwdata->hats ==
NULL) {
554 for (
i = 0;
i < joystick->nhats; ++
i) {
555 joystick->hwdata->hats[
i].axis[0] = 1;
556 joystick->hwdata->hats[
i].axis[1] = 1;
562allocate_balldata(SDL_Joystick * joystick)
566 joystick->hwdata->balls =
567 (
struct hwdata_ball *)
SDL_malloc(joystick->nballs *
568 sizeof(
struct hwdata_ball));
569 if (joystick->hwdata->balls ==
NULL) {
572 for (
i = 0;
i < joystick->nballs; ++
i) {
573 joystick->hwdata->balls[
i].axis[0] = 0;
574 joystick->hwdata->balls[
i].axis[1] = 0;
580ConfigJoystick(SDL_Joystick * joystick,
int fd)
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 };
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)) {
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);
599 joystick->hwdata->key_map[
i] = joystick->nbuttons;
600 ++joystick->nbuttons;
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);
608 joystick->hwdata->key_map[
i] = joystick->nbuttons;
609 ++joystick->nbuttons;
612 for (
i = 0;
i < ABS_MAX; ++
i) {
614 if (
i == ABS_HAT0X) {
618 if (test_bit(
i, absbit)) {
619 struct input_absinfo absinfo;
621 if (ioctl(
fd, EVIOCGABS(
i), &absinfo) < 0) {
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);
630 joystick->hwdata->abs_map[
i] = joystick->naxes;
631 if (absinfo.minimum == absinfo.maximum) {
632 joystick->hwdata->abs_correct[
i].used = 0;
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);
641 joystick->hwdata->abs_correct[
i].coef[2] =
644 joystick->hwdata->abs_correct[
i].coef[2] = 0;
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;
655 if (ioctl(
fd, EVIOCGABS(
i), &absinfo) < 0) {
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);
664 joystick->hwdata->hats_indices[joystick->nhats++] = hat_index;
667 if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) {
672 if (joystick->nhats > 0) {
673 if (allocate_hatdata(joystick) < 0) {
677 if (joystick->nballs > 0) {
678 if (allocate_balldata(joystick) < 0) {
679 joystick->nballs = 0;
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;
688 if (test_bit(FF_SINE, ffbit)) {
689 joystick->hwdata->ff_sine =
SDL_TRUE;
701LINUX_JoystickOpen(SDL_Joystick * joystick,
int device_index)
703 SDL_joylist_item *item = JoystickByDevIndex(device_index);
709 joystick->instance_id = item->device_instance;
718 joystick->hwdata->m_bSteamController =
item->m_bSteamController;
721 if (
item->m_bSteamController) {
727 int fd = open(
item->path, O_RDWR, 0);
744 fcntl(
fd, F_SETFL, O_NONBLOCK);
762 struct input_event
event;
764 if (joystick->hwdata->ff_rumble) {
765 struct ff_effect *effect = &joystick->hwdata->effect;
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) {
773 Sint16 magnitude = (
Sint16)(((low_frequency_rumble / 2) + (high_frequency_rumble / 2)) / 2);
774 struct ff_effect *effect = &joystick->hwdata->effect;
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;
784 if (ioctl(joystick->hwdata->fd, EVIOCSFF, &joystick->hwdata->effect) < 0) {
785 return SDL_SetError(
"Couldn't update rumble effect: %s", strerror(errno));
789 event.code = joystick->hwdata->effect.id;
791 if (write(joystick->hwdata->fd, &
event,
sizeof(
event)) < 0) {
792 return SDL_SetError(
"Couldn't start rumble effect: %s", strerror(errno));
800 struct hwdata_hat *the_hat;
801 const Uint8 position_map[3][3] = {
807 the_hat = &stick->hwdata->hats[hat];
810 }
else if (
value == 0) {
812 }
else if (
value > 0) {
818 position_map[the_hat->axis[1]][the_hat->axis[0]]);
825 stick->hwdata->balls[ball].axis[
axis] +=
value;
830AxisCorrect(SDL_Joystick * joystick,
int which,
int value)
832 struct axis_correct *correct;
834 correct = &joystick->hwdata->abs_correct[which];
837 if (
value > correct->coef[0]) {
841 value -= correct->coef[1];
843 value -= correct->coef[0];
845 value *= correct->coef[2];
859PollAllValues(SDL_Joystick * joystick)
861 struct input_absinfo absinfo;
865 for (
i = ABS_X;
i < ABS_MAX;
i++) {
866 if (
i == ABS_HAT0X) {
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);
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);
879 joystick->hwdata->abs_map[
i],
887HandleInputEvents(SDL_Joystick * joystick)
889 struct input_event
events[32];
893 if (joystick->hwdata->fresh) {
894 PollAllValues(joystick);
895 joystick->hwdata->fresh = 0;
898 while ((
len = read(joystick->hwdata->fd,
events, (
sizeof events))) > 0) {
900 for (
i = 0;
i <
len; ++
i) {
905 joystick->hwdata->key_map[code],
919 HandleHat(joystick, joystick->hwdata->hats_indices[code / 2], code % 2,
events[
i].value);
922 if (joystick->hwdata->abs_map[code] != 0xFF) {
926 joystick->hwdata->abs_map[code],
937 HandleBall(joystick, code / 2, code % 2,
events[
i].
value);
946#ifdef DEBUG_INPUT_EVENTS
947 printf(
"Event SYN_DROPPED detected\n");
949 PollAllValues(joystick);
960 if (errno == ENODEV) {
967LINUX_JoystickUpdate(SDL_Joystick * joystick)
971 if (joystick->hwdata->m_bSteamController) {
976 HandleInputEvents(joystick);
979 for (
i = 0;
i < joystick->nballs; ++
i) {
982 xrel = joystick->hwdata->balls[
i].axis[0];
983 yrel = joystick->hwdata->balls[
i].axis[1];
985 joystick->hwdata->balls[
i].axis[0] = 0;
986 joystick->hwdata->balls[
i].axis[1] = 0;
994LINUX_JoystickClose(SDL_Joystick * joystick)
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;
1001 if (joystick->hwdata->fd >= 0) {
1002 close(joystick->hwdata->fd);
1004 if (joystick->hwdata->item) {
1005 joystick->hwdata->item->hwdata =
NULL;
1016LINUX_JoystickQuit(
void)
1018 SDL_joylist_item *item =
NULL;
1019 SDL_joylist_item *next =
NULL;
1021 for (item = SDL_joylist; item; item = next) {
1028 SDL_joylist = SDL_joylist_tail =
NULL;
1033 SDL_UDEV_DelCallback(joystick_udev_callback);
1043 LINUX_JoystickGetCount,
1044 LINUX_JoystickDetect,
1045 LINUX_JoystickGetDeviceName,
1046 LINUX_JoystickGetDevicePlayerIndex,
1047 LINUX_JoystickGetDeviceGUID,
1048 LINUX_JoystickGetDeviceInstanceID,
1050 LINUX_JoystickRumble,
1051 LINUX_JoystickUpdate,
1052 LINUX_JoystickClose,
#define SDL_assert(condition)
#define SDL_OutOfMemory()
#define SDL_Unsupported()
SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version)
int SDL_PrivateJoystickBall(SDL_Joystick *joystick, Uint8 ball, Sint16 xrel, Sint16 yrel)
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid)
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
SDL_JoystickID SDL_GetNextJoystickInstanceID()
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
#define SDL_HAT_RIGHTDOWN
GLuint GLuint GLsizei GLenum type
GLuint const GLchar * name
GLsizei const GLchar *const * path
GLsizei const GLfloat * value
#define SDL_arraysize(array)
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
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.
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)
struct SDL_joylist_item * item
static SDL_Event events[EVENT_BUF_SIZE]