joystick: Linux joysticks now recover better from dropped events. Fixes Bugzilla #4830.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
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 @@ 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;
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 @@ 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;
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 @@ 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;