Atlas - SDL_hidapi_rumble.c
Home / ext / SDL / src / joystick / hidapi Lines: 1 | Size: 8260 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_JOYSTICK_HIDAPI 24 25// Handle rumble on a separate thread so it doesn't block the application 26 27#include "SDL_hidapijoystick_c.h" 28#include "SDL_hidapi_rumble.h" 29#include "../../thread/SDL_systhread.h" 30 31typedef struct SDL_HIDAPI_RumbleRequest 32{ 33 SDL_HIDAPI_Device *device; 34 Uint8 data[2 * USB_PACKET_LENGTH]; // need enough space for the biggest report: dualshock4 is 78 bytes 35 int size; 36 SDL_HIDAPI_RumbleSentCallback callback; 37 void *userdata; 38 struct SDL_HIDAPI_RumbleRequest *prev; 39 40} SDL_HIDAPI_RumbleRequest; 41 42typedef struct SDL_HIDAPI_RumbleContext 43{ 44 SDL_AtomicInt initialized; 45 SDL_AtomicInt running; 46 SDL_Thread *thread; 47 SDL_Semaphore *request_sem; 48 SDL_HIDAPI_RumbleRequest *requests_head; 49 SDL_HIDAPI_RumbleRequest *requests_tail; 50} SDL_HIDAPI_RumbleContext; 51 52#ifndef SDL_THREAD_SAFETY_ANALYSIS 53static 54#endif 55SDL_Mutex *SDL_HIDAPI_rumble_lock; 56static SDL_HIDAPI_RumbleContext rumble_context SDL_GUARDED_BY(SDL_HIDAPI_rumble_lock); 57 58static int SDLCALL SDL_HIDAPI_RumbleThread(void *data) 59{ 60 SDL_HIDAPI_RumbleContext *ctx = (SDL_HIDAPI_RumbleContext *)data; 61 62 SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_HIGH); 63 64 while (SDL_GetAtomicInt(&ctx->running)) { 65 SDL_HIDAPI_RumbleRequest *request = NULL; 66 67 SDL_WaitSemaphore(ctx->request_sem); 68 69 SDL_LockMutex(SDL_HIDAPI_rumble_lock); 70 request = ctx->requests_tail; 71 if (request) { 72 if (request == ctx->requests_head) { 73 ctx->requests_head = NULL; 74 } 75 ctx->requests_tail = request->prev; 76 } 77 SDL_UnlockMutex(SDL_HIDAPI_rumble_lock); 78 79 if (request) { 80 if (request->device->dev) { 81#ifdef DEBUG_RUMBLE 82 HIDAPI_DumpPacket("Rumble packet: size = %d", request->data, request->size); 83#endif 84 SDL_hid_write(request->device->dev, request->data, request->size); 85 } 86 if (request->callback) { 87 request->callback(request->userdata); 88 } 89 (void)SDL_AtomicDecRef(&request->device->rumble_pending); 90 SDL_free(request); 91 92 // Make sure we're not starving report reads when there's lots of rumble 93 SDL_Delay(10); 94 } 95 } 96 return 0; 97} 98 99static void SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx) 100{ 101 SDL_HIDAPI_RumbleRequest *request; 102 103 SDL_SetAtomicInt(&ctx->running, false); 104 105 if (ctx->thread) { 106 int result; 107 108 SDL_SignalSemaphore(ctx->request_sem); 109 SDL_WaitThread(ctx->thread, &result); 110 ctx->thread = NULL; 111 } 112 113 SDL_LockMutex(SDL_HIDAPI_rumble_lock); 114 while (ctx->requests_tail) { 115 request = ctx->requests_tail; 116 if (request == ctx->requests_head) { 117 ctx->requests_head = NULL; 118 } 119 ctx->requests_tail = request->prev; 120 121 if (request->callback) { 122 request->callback(request->userdata); 123 } 124 (void)SDL_AtomicDecRef(&request->device->rumble_pending); 125 SDL_free(request); 126 } 127 SDL_UnlockMutex(SDL_HIDAPI_rumble_lock); 128 129 if (ctx->request_sem) { 130 SDL_DestroySemaphore(ctx->request_sem); 131 ctx->request_sem = NULL; 132 } 133 134 if (SDL_HIDAPI_rumble_lock) { 135 SDL_DestroyMutex(SDL_HIDAPI_rumble_lock); 136 SDL_HIDAPI_rumble_lock = NULL; 137 } 138 139 SDL_SetAtomicInt(&ctx->initialized, false); 140} 141 142static bool SDL_HIDAPI_StartRumbleThread(SDL_HIDAPI_RumbleContext *ctx) 143{ 144 SDL_HIDAPI_rumble_lock = SDL_CreateMutex(); 145 if (!SDL_HIDAPI_rumble_lock) { 146 SDL_HIDAPI_StopRumbleThread(ctx); 147 return false; 148 } 149 150 ctx->request_sem = SDL_CreateSemaphore(0); 151 if (!ctx->request_sem) { 152 SDL_HIDAPI_StopRumbleThread(ctx); 153 return false; 154 } 155 156 SDL_SetAtomicInt(&ctx->running, true); 157 ctx->thread = SDL_CreateThread(SDL_HIDAPI_RumbleThread, "HIDAPI Rumble", ctx); 158 if (!ctx->thread) { 159 SDL_HIDAPI_StopRumbleThread(ctx); 160 return false; 161 } 162 return true; 163} 164 165bool SDL_HIDAPI_LockRumble(void) 166{ 167 SDL_HIDAPI_RumbleContext *ctx = &rumble_context; 168 169 if (SDL_CompareAndSwapAtomicInt(&ctx->initialized, false, true)) { 170 if (!SDL_HIDAPI_StartRumbleThread(ctx)) { 171 return false; 172 } 173 } 174 175 SDL_LockMutex(SDL_HIDAPI_rumble_lock); 176 return true; 177} 178 179bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size) 180{ 181 SDL_HIDAPI_RumbleContext *ctx = &rumble_context; 182 SDL_HIDAPI_RumbleRequest *request, *found; 183 184 found = NULL; 185 for (request = ctx->requests_tail; request; request = request->prev) { 186 if (request->device == device) { 187 found = request; 188 } 189 } 190 if (found) { 191 *data = found->data; 192 *size = &found->size; 193 *maximum_size = sizeof(found->data); 194 return true; 195 } 196 return false; 197} 198 199int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size) 200{ 201 return SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(device, data, size, NULL, NULL); 202} 203 204int SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size, SDL_HIDAPI_RumbleSentCallback callback, void *userdata) 205{ 206 SDL_HIDAPI_RumbleContext *ctx = &rumble_context; 207 SDL_HIDAPI_RumbleRequest *request; 208 209 if (size > sizeof(request->data)) { 210 SDL_HIDAPI_UnlockRumble(); 211 SDL_SetError("Couldn't send rumble, size %d is greater than %d", size, (int)sizeof(request->data)); 212 return -1; 213 } 214 215 request = (SDL_HIDAPI_RumbleRequest *)SDL_calloc(1, sizeof(*request)); 216 if (!request) { 217 SDL_HIDAPI_UnlockRumble(); 218 return -1; 219 } 220 request->device = device; 221 SDL_memcpy(request->data, data, size); 222 request->size = size; 223 request->callback = callback; 224 request->userdata = userdata; 225 226 SDL_AtomicIncRef(&device->rumble_pending); 227 228 if (ctx->requests_head) { 229 ctx->requests_head->prev = request; 230 } else { 231 ctx->requests_tail = request; 232 } 233 ctx->requests_head = request; 234 235 // Make sure we unlock before posting the semaphore so the rumble thread can run immediately 236 SDL_HIDAPI_UnlockRumble(); 237 238 SDL_SignalSemaphore(ctx->request_sem); 239 240 return size; 241} 242 243void SDL_HIDAPI_UnlockRumble(void) 244{ 245 SDL_UnlockMutex(SDL_HIDAPI_rumble_lock); 246} 247 248int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size) 249{ 250 Uint8 *pending_data; 251 int *pending_size; 252 int maximum_size; 253 254 if (size <= 0) { 255 SDL_SetError("Tried to send rumble with invalid size"); 256 return -1; 257 } 258 259 if (!SDL_HIDAPI_LockRumble()) { 260 return -1; 261 } 262 263 // check if there is a pending request for the device and update it 264 if (SDL_HIDAPI_GetPendingRumbleLocked(device, &pending_data, &pending_size, &maximum_size) && 265 size == *pending_size && data[0] == pending_data[0]) { 266 SDL_memcpy(pending_data, data, size); 267 SDL_HIDAPI_UnlockRumble(); 268 return size; 269 } 270 271 return SDL_HIDAPI_SendRumbleAndUnlock(device, data, size); 272} 273 274void SDL_HIDAPI_QuitRumble(void) 275{ 276 SDL_HIDAPI_RumbleContext *ctx = &rumble_context; 277 278 if (SDL_GetAtomicInt(&ctx->running)) { 279 SDL_HIDAPI_StopRumbleThread(ctx); 280 } 281} 282 283#endif // SDL_JOYSTICK_HIDAPI 284[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.