|
|
@@ -0,0 +1,188 @@
|
|
|
+/*
|
|
|
+ Simple DirectMedia Layer
|
|
|
+ Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
|
|
|
+
|
|
|
+ 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: */
|