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. 35 #include <sys/ioctl.h> 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" 58 static int MaybeAddDevice(
const char *
path);
60 static int MaybeRemoveDevice(
const char *
path);
64 typedef struct SDL_joylist_item
72 struct SDL_joylist_item *next;
78 static SDL_joylist_item *SDL_joylist =
NULL;
79 static SDL_joylist_item *SDL_joylist_tail =
NULL;
83 static 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 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);
140 if (inpid.vendor && inpid.product) {
158 static 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);
184 MaybeAddDevice(
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;
259 MaybeRemoveDevice(
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;
303 HandlePendingRemovals(
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;
380 static 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;
415 LINUX_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))) {
431 if (len > 5 &&
SDL_strncmp(dent->d_name,
"event", 5) == 0) {
434 MaybeAddDevice(path);
441 last_joy_detect_time = now;
445 HandlePendingRemovals();
451 LINUX_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();
494 LINUX_JoystickGetCount(
void)
499 static SDL_joylist_item *
500 JoystickByDevIndex(
int device_index)
502 SDL_joylist_item *item = SDL_joylist;
504 if ((device_index < 0) || (device_index >=
numjoysticks)) {
508 while (device_index > 0) {
519 LINUX_JoystickGetDeviceName(
int device_index)
521 return JoystickByDevIndex(device_index)->name;
525 LINUX_JoystickGetDevicePlayerIndex(
int device_index)
531 LINUX_JoystickGetDeviceGUID(
int device_index )
533 return JoystickByDevIndex(device_index)->guid;
538 LINUX_JoystickGetDeviceInstanceID(
int device_index)
540 return JoystickByDevIndex(device_index)->device_instance;
544 allocate_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;
562 allocate_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;
580 ConfigJoystick(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;
701 LINUX_JoystickOpen(SDL_Joystick * joystick,
int device_index)
703 SDL_joylist_item *item = JoystickByDevIndex(device_index);
709 joystick->instance_id = item->device_instance;
712 if (joystick->hwdata ==
NULL) {
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));
721 if (item->m_bSteamController) {
722 joystick->hwdata->fd = -1;
727 int fd = open(item->path, O_RDWR, 0);
730 joystick->hwdata =
NULL;
734 joystick->hwdata->fd =
fd;
735 joystick->hwdata->fname =
SDL_strdup(item->path);
736 if (joystick->hwdata->fname ==
NULL) {
738 joystick->hwdata =
NULL;
744 fcntl(fd, F_SETFL, O_NONBLOCK);
747 ConfigJoystick(joystick, fd);
751 item->hwdata = joystick->hwdata;
754 joystick->hwdata->fresh = 1;
760 LINUX_JoystickRumble(SDL_Joystick * joystick,
Uint16 low_frequency_rumble,
Uint16 high_frequency_rumble,
Uint32 duration_ms)
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) {
815 if (value != the_hat->axis[axis]) {
818 position_map[the_hat->axis[1]][the_hat->axis[0]]);
823 HandleBall(SDL_Joystick * stick,
Uint8 ball,
int axis,
int value)
825 stick->hwdata->balls[ball].axis[
axis] +=
value;
830 AxisCorrect(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];
859 PollAllValues(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],
887 HandleInputEvents(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) {
924 AxisCorrect(joystick, code,
events[i].value);
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) {
967 LINUX_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;
994 LINUX_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;
1016 LINUX_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,
void SDL_UpdateSteamControllers(void)
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
void SDL_InitSteamControllers(SteamControllerConnectedCallback_t connectedCallback, SteamControllerDisconnectedCallback_t disconnectedCallback)
void SDL_QuitSteamControllers(void)
static SDL_Event events[EVENT_BUF_SIZE]
SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid)
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
GLuint const GLchar * name
int SDL_PrivateJoystickBall(SDL_Joystick *joystick, Uint8 ball, Sint16 xrel, Sint16 yrel)
#define SDL_HAT_RIGHTDOWN
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
GLsizei const GLfloat * value
SDL_JoystickDriver SDL_LINUX_JoystickDriver
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)
#define SDL_assert(condition)
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
void SDL_UpdateSteamController(SDL_Joystick *joystick)
#define SDL_OutOfMemory()
SDL_JoystickID SDL_GetNextJoystickInstanceID()
struct SDL_joylist_item * item
GLuint GLuint GLsizei GLenum type
void SDL_GetSteamControllerInputs(int *nbuttons, int *naxes, int *nhats)
#define SDL_arraysize(array)
GLsizei const GLchar *const * path
#define SDL_TICKS_PASSED(A, B)
Compare SDL ticks values, and return true if A has passed B.
SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version)
#define SDL_Unsupported()