joystick: Linux joysticks now recover better from dropped events.

Fixes Bugzilla #4830.
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"
#if 0
#define DEBUG_INPUT_EVENTS 1
#endif
static int MaybeAddDevice(const char *path);
#if SDL_USE_LIBUDEV
static int MaybeRemoveDevice(const char *path);
@ -838,7 +842,7 @@ LINUX_JoystickOpen(SDL_Joystick * joystick, int device_index)
item->hwdata = joystick->hwdata;
/* mark joystick as fresh and ready */
joystick->hwdata->fresh = 1;
joystick->hwdata->fresh = SDL_TRUE;
return (0);
}
@ -950,11 +954,12 @@ static SDL_INLINE void
PollAllValues(SDL_Joystick * joystick)
{
struct input_absinfo absinfo;
unsigned long keyinfo[NBITS(KEY_MAX)];
int i;
/* Poll all axis */
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;
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
@ -983,13 +1019,21 @@ HandleInputEvents(SDL_Joystick * joystick)
if (joystick->hwdata->fresh) {
PollAllValues(joystick);
joystick->hwdata->fresh = 0;
joystick->hwdata->fresh = SDL_FALSE;
}
while ((len = read(joystick->hwdata->fd, events, (sizeof events))) > 0) {
len /= sizeof(events[0]);
for (i = 0; i < len; ++i) {
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) {
case EV_KEY:
SDL_PrivateJoystickButton(joystick,
@ -1037,7 +1081,13 @@ HandleInputEvents(SDL_Joystick * joystick)
#ifdef DEBUG_INPUT_EVENTS
printf("Event SYN_DROPPED detected\n");
#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;
default:
break;

View File

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

View File

@ -167,6 +167,14 @@ loop(void *arg)
event.jbutton.which, event.jbutton.button);
break;
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) &&
(event.key.keysym.sym != SDLK_AC_BACK)) {
break;