joystick: Linux joysticks now recover better from dropped events.
Fixes Bugzilla #4830.
--HG--
extra : amend_source : 8b3872b83701beffe529a9d3788d5ebfd74d6513
diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c
index 45b7960..58471d4 100644
--- a/src/joystick/linux/SDL_sysjoystick.c
+++ b/src/joystick/linux/SDL_sysjoystick.c
@@ -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 @@
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 @@
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 @@
}
}
}
+
+ /* 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 @@
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 @@
#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;
diff --git a/src/joystick/linux/SDL_sysjoystick_c.h b/src/joystick/linux/SDL_sysjoystick_c.h
index 6f162ae..bcbc255 100644
--- a/src/joystick/linux/SDL_sysjoystick_c.h
+++ b/src/joystick/linux/SDL_sysjoystick_c.h
@@ -62,7 +62,8 @@
int coef[3];
} abs_correct[ABS_MAX];
- int fresh;
+ SDL_bool fresh;
+ SDL_bool recovering_from_dropped;
/* Steam Controller support */
SDL_bool m_bSteamController;
diff --git a/test/testjoystick.c b/test/testjoystick.c
index 2d8d2e1..f838af7 100644
--- a/test/testjoystick.c
+++ b/test/testjoystick.c
@@ -167,6 +167,14 @@
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;