Update for bug 4923 - Calling SDL_GameControllerRumble() often takes 8 ms meyraud705 Dualshock4 on bluetooth need 78 bytes for the rumble data while SDL_HIDAPI_RumbleRequest can only hold 64 bytes. 'volatile' is not meant for thread synchronization. The list of rumble request could grow infinitely if user call SDL_JoystickRumble too much. The documentation says "Each call to this function cancels any previous rumble effect", so overwriting pending request seem like a good idea.
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 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
diff --git a/src/joystick/hidapi/SDL_hidapi_rumble.c b/src/joystick/hidapi/SDL_hidapi_rumble.c
index 860bd06..a4f05e4 100644
--- a/src/joystick/hidapi/SDL_hidapi_rumble.c
+++ b/src/joystick/hidapi/SDL_hidapi_rumble.c
@@ -34,7 +34,7 @@
typedef struct SDL_HIDAPI_RumbleRequest
{
SDL_HIDAPI_Device *device;
- Uint8 data[USB_PACKET_LENGTH];
+ Uint8 data[2*USB_PACKET_LENGTH]; /* need enough space for the biggest report: dualshock4 is 78 bytes */
int size;
struct SDL_HIDAPI_RumbleRequest *prev;
@@ -42,7 +42,8 @@ typedef struct SDL_HIDAPI_RumbleRequest
typedef struct SDL_HIDAPI_RumbleContext
{
- volatile SDL_bool running;
+ SDL_atomic_t initialized;
+ SDL_atomic_t running;
SDL_Thread *thread;
SDL_mutex *lock;
SDL_sem *request_sem;
@@ -58,7 +59,7 @@ static int SDL_HIDAPI_RumbleThread(void *data)
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
- while (ctx->running) {
+ while (SDL_AtomicGet(&ctx->running)) {
SDL_HIDAPI_RumbleRequest *request = NULL;
SDL_SemWait(ctx->request_sem);
@@ -87,7 +88,7 @@ static int SDL_HIDAPI_RumbleThread(void *data)
static void
SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
{
- ctx->running = SDL_FALSE;
+ SDL_AtomicSet(&ctx->running, SDL_FALSE);
if (ctx->thread) {
int result;
@@ -110,6 +111,8 @@ SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
SDL_DestroyMutex(ctx->lock);
ctx->lock = NULL;
}
+
+ SDL_AtomicSet(&ctx->initialized, SDL_FALSE);
}
static int
@@ -127,7 +130,7 @@ SDL_HIDAPI_StartRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
return -1;
}
- ctx->running = SDL_TRUE;
+ SDL_AtomicSet(&ctx->running, SDL_TRUE);
ctx->thread = SDL_CreateThreadInternal(SDL_HIDAPI_RumbleThread, "HIDAPI Rumble", 0, ctx);
if (!ctx->thread) {
SDL_HIDAPI_StopRumbleThread(ctx);
@@ -136,23 +139,48 @@ SDL_HIDAPI_StartRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
return 0;
}
-int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
+int SDL_HIDAPI_LockRumble(void)
{
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
- SDL_HIDAPI_RumbleRequest *request;
-
- if (size > sizeof(request->data)) {
- return SDL_SetError("Couldn't send rumble, size %d is greater than %d", size, (int)sizeof(request->data));
- }
- if (!ctx->running) {
+ if (SDL_AtomicCAS(&ctx->initialized, SDL_FALSE, SDL_TRUE)) {
if (SDL_HIDAPI_StartRumbleThread(ctx) < 0) {
return -1;
}
}
+ return SDL_LockMutex(ctx->lock);
+}
+
+SDL_bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size)
+{
+ SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
+ SDL_HIDAPI_RumbleRequest *request;
+
+ for (request = ctx->requests_tail; request; request = request->prev) {
+ if (request->device == device) {
+ *data = request->data;
+ *size = &request->size;
+ *maximum_size = sizeof(request->data);
+ return SDL_TRUE;
+ }
+ }
+ return SDL_FALSE;
+}
+
+int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
+{
+ SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
+ SDL_HIDAPI_RumbleRequest *request;
+
+ if (size > sizeof(request->data)) {
+ SDL_HIDAPI_UnlockRumble();
+ return SDL_SetError("Couldn't send rumble, size %d is greater than %d", size, (int)sizeof(request->data));
+ }
+
request = (SDL_HIDAPI_RumbleRequest *)SDL_calloc(1, sizeof(*request));
if (!request) {
+ SDL_HIDAPI_UnlockRumble();
return SDL_OutOfMemory();
}
request->device = device;
@@ -161,25 +189,59 @@ int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size
SDL_AtomicIncRef(&device->rumble_pending);
- SDL_LockMutex(ctx->lock);
if (ctx->requests_head) {
ctx->requests_head->prev = request;
} else {
ctx->requests_tail = request;
}
ctx->requests_head = request;
- SDL_UnlockMutex(ctx->lock);
+
+ /* Make sure we unlock before posting the semaphore so the rumble thread can run immediately */
+ SDL_HIDAPI_UnlockRumble();
SDL_SemPost(ctx->request_sem);
return size;
}
+void SDL_HIDAPI_UnlockRumble(void)
+{
+ SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
+
+ SDL_UnlockMutex(ctx->lock);
+}
+
+int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
+{
+ Uint8 *pending_data;
+ int *pending_size;
+ int maximum_size;
+
+ if (SDL_HIDAPI_LockRumble() < 0) {
+ return -1;
+ }
+
+ /* check if there is a pending request for the device and update it */
+ if (SDL_HIDAPI_GetPendingRumbleLocked(device, &pending_data, &pending_size, &maximum_size)) {
+ if (size > maximum_size) {
+ SDL_HIDAPI_UnlockRumble();
+ return SDL_SetError("Couldn't send rumble, size %d is greater than %d", size, maximum_size);
+ }
+
+ SDL_memcpy(pending_data, data, size);
+ *pending_size = size;
+ SDL_HIDAPI_UnlockRumble();
+ return size;
+ }
+
+ return SDL_HIDAPI_SendRumbleAndUnlock(device, data, size);
+}
+
void SDL_HIDAPI_QuitRumble(void)
{
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
- if (ctx->running) {
+ if (SDL_AtomicGet(&ctx->running)) {
SDL_HIDAPI_StopRumbleThread(ctx);
}
}
diff --git a/src/joystick/hidapi/SDL_hidapi_rumble.h b/src/joystick/hidapi/SDL_hidapi_rumble.h
index dde3dbd..9b14da0 100644
--- a/src/joystick/hidapi/SDL_hidapi_rumble.h
+++ b/src/joystick/hidapi/SDL_hidapi_rumble.h
@@ -23,6 +23,14 @@
#ifdef SDL_JOYSTICK_HIDAPI
/* Handle rumble on a separate thread so it doesn't block the application */
+
+/* Advanced API */
+int SDL_HIDAPI_LockRumble(void);
+SDL_bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size);
+int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size);
+void SDL_HIDAPI_UnlockRumble(void);
+
+/* Simple API, will replace any pending rumble with the new data */
int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size);
void SDL_HIDAPI_QuitRumble(void);
diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c
index d964351..2e109c9 100644
--- a/src/joystick/hidapi/SDL_hidapi_xboxone.c
+++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c
@@ -118,6 +118,7 @@ typedef struct {
Uint8 sequence;
Uint8 last_state[USB_PACKET_LENGTH];
SDL_bool rumble_synchronized;
+ SDL_bool rumble_synchronization_complete;
SDL_bool has_paddles;
} SDL_DriverXboxOne_Context;
@@ -194,7 +195,10 @@ SynchronizeRumbleSequence(SDL_HIDAPI_Device *device, SDL_DriverXboxOne_Context *
SDL_memcpy(init_packet, xboxone_rumble_reset, sizeof(xboxone_rumble_reset));
for (i = 0; i < 255; ++i) {
init_packet[2] = ((ctx->sequence + i) % 255);
- if (SDL_HIDAPI_SendRumble(device, init_packet, sizeof(xboxone_rumble_reset)) != sizeof(xboxone_rumble_reset)) {
+ if (SDL_HIDAPI_LockRumble() < 0) {
+ return SDL_FALSE;
+ }
+ if (SDL_HIDAPI_SendRumbleAndUnlock(device, init_packet, sizeof(xboxone_rumble_reset)) != sizeof(xboxone_rumble_reset)) {
SDL_SetError("Couldn't write Xbox One initialization packet");
return SDL_FALSE;
}
@@ -371,16 +375,39 @@ HIDAPI_DriverXboxOne_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joy
{
SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
Uint8 rumble_packet[] = { 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF };
+ Uint8 *pending_rumble;
+ int *pending_size;
+ int maximum_size;
SynchronizeRumbleSequence(device, ctx);
- /* Magnitude is 1..100 so scale the 16-bit input here */
- rumble_packet[2] = ctx->sequence++;
- rumble_packet[8] = low_frequency_rumble / 655;
- rumble_packet[9] = high_frequency_rumble / 655;
+ if (SDL_HIDAPI_LockRumble() < 0) {
+ return -1;
+ }
- if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
- return SDL_SetError("Couldn't send rumble packet");
+ if (!ctx->rumble_synchronization_complete) {
+ if (!SDL_HIDAPI_GetPendingRumbleLocked(device, &pending_rumble, &pending_size, &maximum_size)) {
+ /* Rumble synchronization has drained */
+ ctx->rumble_synchronization_complete = SDL_TRUE;
+ }
+ }
+
+ /* Try to overwrite any pending rumble with the new value */
+ if (ctx->rumble_synchronization_complete &&
+ SDL_HIDAPI_GetPendingRumbleLocked(device, &pending_rumble, &pending_size, &maximum_size)) {
+ /* Magnitude is 1..100 so scale the 16-bit input here */
+ pending_rumble[8] = low_frequency_rumble / 655;
+ pending_rumble[9] = high_frequency_rumble / 655;
+ SDL_HIDAPI_UnlockRumble();
+ } else {
+ /* Magnitude is 1..100 so scale the 16-bit input here */
+ rumble_packet[2] = ctx->sequence++;
+ rumble_packet[8] = low_frequency_rumble / 655;
+ rumble_packet[9] = high_frequency_rumble / 655;
+
+ if (SDL_HIDAPI_SendRumbleAndUnlock(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
+ return SDL_SetError("Couldn't send rumble packet");
+ }
}
return 0;
}