diff --git a/src/joystick/hidapi/SDL_hidapi_rumble.c b/src/joystick/hidapi/SDL_hidapi_rumble.c new file mode 100644 index 000000000..c15f1c142 --- /dev/null +++ b/src/joystick/hidapi/SDL_hidapi_rumble.c @@ -0,0 +1,188 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2020 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifdef SDL_JOYSTICK_HIDAPI + +/* Handle rumble on a separate thread so it doesn't block the application */ + +#include "SDL_assert.h" +#include "SDL_thread.h" +#include "SDL_hidapijoystick_c.h" +#include "SDL_hidapi_rumble.h" + + +typedef struct SDL_HIDAPI_RumbleRequest +{ + SDL_HIDAPI_Device *device; + Uint8 data[USB_PACKET_LENGTH]; + int size; + struct SDL_HIDAPI_RumbleRequest *prev; + +} SDL_HIDAPI_RumbleRequest; + +typedef struct SDL_HIDAPI_RumbleContext +{ + volatile SDL_bool running; + SDL_Thread *thread; + SDL_mutex *lock; + SDL_sem *request_sem; + SDL_HIDAPI_RumbleRequest *requests_head; + SDL_HIDAPI_RumbleRequest *requests_tail; +} SDL_HIDAPI_RumbleContext; + +static SDL_HIDAPI_RumbleContext rumble_context; + +static int SDL_HIDAPI_RumbleThread(void *data) +{ + SDL_HIDAPI_RumbleContext *ctx = (SDL_HIDAPI_RumbleContext *)data; + + SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); + + while (ctx->running) { + SDL_HIDAPI_RumbleRequest *request = NULL; + + SDL_SemWait(ctx->request_sem); + + SDL_LockMutex(ctx->lock); + request = ctx->requests_tail; + if (request) { + if (request == ctx->requests_head) { + ctx->requests_head = NULL; + } + ctx->requests_tail = request->prev; + } + SDL_UnlockMutex(ctx->lock); + + if (request) { + SDL_LockMutex(request->device->dev_lock); + hid_write(request->device->dev, request->data, request->size); + SDL_UnlockMutex(request->device->dev_lock); + (void)SDL_AtomicDecRef(&request->device->rumble_pending); + SDL_free(request); + } + } + return 0; +} + +static void +SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx) +{ + ctx->running = SDL_FALSE; + + if (ctx->thread) { + int result; + + SDL_SemPost(ctx->request_sem); + SDL_WaitThread(ctx->thread, &result); + ctx->thread = NULL; + } + + /* This should always be called with an empty queue */ + SDL_assert(!ctx->requests_head); + SDL_assert(!ctx->requests_tail); + + if (ctx->request_sem) { + SDL_DestroySemaphore(ctx->request_sem); + ctx->request_sem = NULL; + } + + if (ctx->lock) { + SDL_DestroyMutex(ctx->lock); + ctx->lock = NULL; + } +} + +static int +SDL_HIDAPI_StartRumbleThread(SDL_HIDAPI_RumbleContext *ctx) +{ + ctx->lock = SDL_CreateMutex(); + if (!ctx->lock) { + SDL_HIDAPI_StopRumbleThread(ctx); + return -1; + } + + ctx->request_sem = SDL_CreateSemaphore(0); + if (!ctx->request_sem) { + SDL_HIDAPI_StopRumbleThread(ctx); + return -1; + } + + ctx->running = SDL_TRUE; + ctx->thread = SDL_CreateThread(SDL_HIDAPI_RumbleThread, "HIDAPI Rumble", ctx); + if (!ctx->thread) { + SDL_HIDAPI_StopRumbleThread(ctx); + return -1; + } + return 0; +} + +int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size) +{ + 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, sizeof(request->data)); + } + + if (!ctx->running) { + if (SDL_HIDAPI_StartRumbleThread(ctx) < 0) { + return -1; + } + } + + request = (SDL_HIDAPI_RumbleRequest *)SDL_calloc(1, sizeof(*request)); + if (!request) { + return SDL_OutOfMemory(); + } + request->device = device; + SDL_memcpy(request->data, data, size); + request->size = 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); + + SDL_SemPost(ctx->request_sem); + + return size; +} + +void SDL_HIDAPI_QuitRumble() +{ + SDL_HIDAPI_RumbleContext *ctx = &rumble_context; + + if (ctx->running) { + SDL_HIDAPI_StopRumbleThread(ctx); + } +} + +#endif /* SDL_JOYSTICK_HIDAPI */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/hidapi/SDL_hidapi_rumble.h b/src/joystick/hidapi/SDL_hidapi_rumble.h new file mode 100644 index 000000000..1ce67ba66 --- /dev/null +++ b/src/joystick/hidapi/SDL_hidapi_rumble.h @@ -0,0 +1,31 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2020 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifdef SDL_JOYSTICK_HIDAPI + +/* Handle rumble on a separate thread so it doesn't block the application */ +int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size); +void SDL_HIDAPI_QuitRumble(); + +#endif /* SDL_JOYSTICK_HIDAPI */ + +/* vi: set ts=4 sw=4 expandtab: */