joystick: Linux joysticks now recover better from dropped events.

Fixes Bugzilla #4830.
This commit is contained in:
Ryan C. Gordon 2020-06-28 16:23:05 -04:00
parent e594a6738a
commit 0e98040d43
3 changed files with 64 additions and 5 deletions

View File

@ -79,6 +79,10 @@
#include "../../core/linux/SDL_udev.h" #include "../../core/linux/SDL_udev.h"
#if 0
#define DEBUG_INPUT_EVENTS 1
#endif
static int MaybeAddDevice(const char *path); static int MaybeAddDevice(const char *path);
#if SDL_USE_LIBUDEV #if SDL_USE_LIBUDEV
static int MaybeRemoveDevice(const char *path); static int MaybeRemoveDevice(const char *path);
@ -838,7 +842,7 @@ LINUX_JoystickOpen(SDL_Joystick * joystick, int device_index)
item->hwdata = joystick->hwdata; item->hwdata = joystick->hwdata;
/* mark joystick as fresh and ready */ /* mark joystick as fresh and ready */
joystick->hwdata->fresh = 1; joystick->hwdata->fresh = SDL_TRUE;
return (0); return (0);
} }
@ -950,11 +954,12 @@ static SDL_INLINE void
PollAllValues(SDL_Joystick * joystick) PollAllValues(SDL_Joystick * joystick)
{ {
struct input_absinfo absinfo; struct input_absinfo absinfo;
unsigned long keyinfo[NBITS(KEY_MAX)];
int i; int i;
/* Poll all axis */ /* Poll all axis */
for (i = ABS_X; i < ABS_MAX; i++) { for (i = ABS_X; i < ABS_MAX; i++) {
if (i == ABS_HAT0X) { if (i == ABS_HAT0X) { /* we handle hats in the next loop, skip them for now. */
i = ABS_HAT3Y; i = ABS_HAT3Y;
continue; continue;
} }
@ -972,6 +977,37 @@ PollAllValues(SDL_Joystick * joystick)
} }
} }
} }
/* Poll all hats */
for (i = ABS_HAT0X; i <= ABS_HAT3Y; i++) {
const int baseaxis = i - ABS_HAT0X;
const int hatidx = baseaxis / 2;
SDL_assert(hatidx < SDL_arraysize(joystick->hwdata->has_hat));
if (joystick->hwdata->has_hat[hatidx]) {
if (ioctl(joystick->hwdata->fd, EVIOCGABS(i), &absinfo) >= 0) {
const int hataxis = baseaxis % 2;
HandleHat(joystick, joystick->hwdata->hats_indices[hatidx], hataxis, absinfo.value);
}
}
}
/* Poll all buttons */
SDL_zeroa(keyinfo);
if (ioctl(joystick->hwdata->fd, EVIOCGKEY(sizeof (keyinfo)), keyinfo) >= 0) {
for (i = 0; i < KEY_MAX; i++) {
if (joystick->hwdata->has_key[i]) {
const Uint8 value = test_bit(i, keyinfo) ? SDL_PRESSED : SDL_RELEASED;
#ifdef DEBUG_INPUT_EVENTS
printf("Joystick : Re-read Button %d (%d) val= %d\n",
joystick->hwdata->key_map[i], i, value);
#endif
SDL_PrivateJoystickButton(joystick,
joystick->hwdata->key_map[i], value);
}
}
}
/* Joyballs are relative input, so there's no poll state. Events only! */
} }
static SDL_INLINE void static SDL_INLINE void
@ -983,13 +1019,21 @@ HandleInputEvents(SDL_Joystick * joystick)
if (joystick->hwdata->fresh) { if (joystick->hwdata->fresh) {
PollAllValues(joystick); PollAllValues(joystick);
joystick->hwdata->fresh = 0; joystick->hwdata->fresh = SDL_FALSE;
} }
while ((len = read(joystick->hwdata->fd, events, (sizeof events))) > 0) { while ((len = read(joystick->hwdata->fd, events, (sizeof events))) > 0) {
len /= sizeof(events[0]); len /= sizeof(events[0]);
for (i = 0; i < len; ++i) { for (i = 0; i < len; ++i) {
code = events[i].code; code = events[i].code;
/* If the kernel sent a SYN_DROPPED, we are supposed to ignore the
rest of the packet (the end of it signified by a SYN_REPORT) */
if ( joystick->hwdata->recovering_from_dropped &&
((events[i].type != EV_SYN) || (code != SYN_REPORT)) ) {
continue;
}
switch (events[i].type) { switch (events[i].type) {
case EV_KEY: case EV_KEY:
SDL_PrivateJoystickButton(joystick, SDL_PrivateJoystickButton(joystick,
@ -1037,7 +1081,13 @@ HandleInputEvents(SDL_Joystick * joystick)
#ifdef DEBUG_INPUT_EVENTS #ifdef DEBUG_INPUT_EVENTS
printf("Event SYN_DROPPED detected\n"); printf("Event SYN_DROPPED detected\n");
#endif #endif
PollAllValues(joystick); joystick->hwdata->recovering_from_dropped = SDL_TRUE;
break;
case SYN_REPORT :
if (joystick->hwdata->recovering_from_dropped) {
joystick->hwdata->recovering_from_dropped = SDL_FALSE;
PollAllValues(joystick); /* try to sync up to current state now */
}
break; break;
default: default:
break; break;

View File

@ -62,7 +62,8 @@ struct joystick_hwdata
int coef[3]; int coef[3];
} abs_correct[ABS_MAX]; } abs_correct[ABS_MAX];
int fresh; SDL_bool fresh;
SDL_bool recovering_from_dropped;
/* Steam Controller support */ /* Steam Controller support */
SDL_bool m_bSteamController; SDL_bool m_bSteamController;

View File

@ -167,6 +167,14 @@ loop(void *arg)
event.jbutton.which, event.jbutton.button); event.jbutton.which, event.jbutton.button);
break; break;
case SDL_KEYDOWN: case SDL_KEYDOWN:
/* Press the L key to lag for 3 seconds, to see what happens
when SDL doesn't service the event loop quickly. */
if (event.key.keysym.sym == SDLK_l) {
SDL_Log("Lagging for 3 seconds...\n");
SDL_Delay(3000);
break;
}
if ((event.key.keysym.sym != SDLK_ESCAPE) && if ((event.key.keysym.sym != SDLK_ESCAPE) &&
(event.key.keysym.sym != SDLK_AC_BACK)) { (event.key.keysym.sym != SDLK_AC_BACK)) {
break; break;