SDL  2.0
SDL_hidapi_rumble.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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_assert.h"
28 #include "SDL_thread.h"
29 #include "SDL_hidapijoystick_c.h"
30 #include "SDL_hidapi_rumble.h"
31 #include "../../thread/SDL_systhread.h"
32 
33 
34 typedef struct SDL_HIDAPI_RumbleRequest
35 {
36  SDL_HIDAPI_Device *device;
37  Uint8 data[2*USB_PACKET_LENGTH]; /* need enough space for the biggest report: dualshock4 is 78 bytes */
38  int size;
39  struct SDL_HIDAPI_RumbleRequest *prev;
40 
41 } SDL_HIDAPI_RumbleRequest;
42 
43 typedef struct SDL_HIDAPI_RumbleContext
44 {
45  SDL_atomic_t initialized;
46  SDL_atomic_t running;
47  SDL_Thread *thread;
48  SDL_mutex *lock;
49  SDL_sem *request_sem;
50  SDL_HIDAPI_RumbleRequest *requests_head;
51  SDL_HIDAPI_RumbleRequest *requests_tail;
52 } SDL_HIDAPI_RumbleContext;
53 
54 static SDL_HIDAPI_RumbleContext rumble_context;
55 
56 static int SDL_HIDAPI_RumbleThread(void *data)
57 {
58  SDL_HIDAPI_RumbleContext *ctx = (SDL_HIDAPI_RumbleContext *)data;
59 
61 
62  while (SDL_AtomicGet(&ctx->running)) {
63  SDL_HIDAPI_RumbleRequest *request = NULL;
64 
65  SDL_SemWait(ctx->request_sem);
66 
67  SDL_LockMutex(ctx->lock);
68  request = ctx->requests_tail;
69  if (request) {
70  if (request == ctx->requests_head) {
71  ctx->requests_head = NULL;
72  }
73  ctx->requests_tail = request->prev;
74  }
75  SDL_UnlockMutex(ctx->lock);
76 
77  if (request) {
78  SDL_LockMutex(request->device->dev_lock);
79  if (request->device->dev) {
80  hid_write( request->device->dev, request->data, request->size );
81  }
82  SDL_UnlockMutex(request->device->dev_lock);
83  (void)SDL_AtomicDecRef(&request->device->rumble_pending);
84  SDL_free(request);
85  }
86  }
87  return 0;
88 }
89 
90 static void
91 SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
92 {
93  SDL_AtomicSet(&ctx->running, SDL_FALSE);
94 
95  if (ctx->thread) {
96  int result;
97 
98  SDL_SemPost(ctx->request_sem);
99  SDL_WaitThread(ctx->thread, &result);
100  ctx->thread = NULL;
101  }
102 
103  /* This should always be called with an empty queue */
104  SDL_assert(!ctx->requests_head);
105  SDL_assert(!ctx->requests_tail);
106 
107  if (ctx->request_sem) {
108  SDL_DestroySemaphore(ctx->request_sem);
109  ctx->request_sem = NULL;
110  }
111 
112  if (ctx->lock) {
113  SDL_DestroyMutex(ctx->lock);
114  ctx->lock = NULL;
115  }
116 
117  SDL_AtomicSet(&ctx->initialized, SDL_FALSE);
118 }
119 
120 static int
121 SDL_HIDAPI_StartRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
122 {
123  ctx->lock = SDL_CreateMutex();
124  if (!ctx->lock) {
125  SDL_HIDAPI_StopRumbleThread(ctx);
126  return -1;
127  }
128 
129  ctx->request_sem = SDL_CreateSemaphore(0);
130  if (!ctx->request_sem) {
131  SDL_HIDAPI_StopRumbleThread(ctx);
132  return -1;
133  }
134 
135  SDL_AtomicSet(&ctx->running, SDL_TRUE);
136  ctx->thread = SDL_CreateThreadInternal(SDL_HIDAPI_RumbleThread, "HIDAPI Rumble", 0, ctx);
137  if (!ctx->thread) {
138  SDL_HIDAPI_StopRumbleThread(ctx);
139  return -1;
140  }
141  return 0;
142 }
143 
144 int SDL_HIDAPI_LockRumble(void)
145 {
146  SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
147 
148  if (SDL_AtomicCAS(&ctx->initialized, SDL_FALSE, SDL_TRUE)) {
149  if (SDL_HIDAPI_StartRumbleThread(ctx) < 0) {
150  return -1;
151  }
152  }
153 
154  return SDL_LockMutex(ctx->lock);
155 }
156 
157 SDL_bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size)
158 {
159  SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
160  SDL_HIDAPI_RumbleRequest *request;
161 
162  for (request = ctx->requests_tail; request; request = request->prev) {
163  if (request->device == device) {
164  *data = request->data;
165  *size = &request->size;
166  *maximum_size = sizeof(request->data);
167  return SDL_TRUE;
168  }
169  }
170  return SDL_FALSE;
171 }
172 
173 int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
174 {
175  SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
176  SDL_HIDAPI_RumbleRequest *request;
177 
178  if (size > sizeof(request->data)) {
179  SDL_HIDAPI_UnlockRumble();
180  return SDL_SetError("Couldn't send rumble, size %d is greater than %d", size, (int)sizeof(request->data));
181  }
182 
183  request = (SDL_HIDAPI_RumbleRequest *)SDL_calloc(1, sizeof(*request));
184  if (!request) {
185  SDL_HIDAPI_UnlockRumble();
186  return SDL_OutOfMemory();
187  }
188  request->device = device;
189  SDL_memcpy(request->data, data, size);
190  request->size = size;
191 
192  SDL_AtomicIncRef(&device->rumble_pending);
193 
194  if (ctx->requests_head) {
195  ctx->requests_head->prev = request;
196  } else {
197  ctx->requests_tail = request;
198  }
199  ctx->requests_head = request;
200 
201  /* Make sure we unlock before posting the semaphore so the rumble thread can run immediately */
202  SDL_HIDAPI_UnlockRumble();
203 
204  SDL_SemPost(ctx->request_sem);
205 
206  return size;
207 }
208 
209 void SDL_HIDAPI_UnlockRumble(void)
210 {
211  SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
212 
213  SDL_UnlockMutex(ctx->lock);
214 }
215 
216 int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
217 {
218  Uint8 *pending_data;
219  int *pending_size;
220  int maximum_size;
221 
222  if (SDL_HIDAPI_LockRumble() < 0) {
223  return -1;
224  }
225 
226  /* check if there is a pending request for the device and update it */
227  if (SDL_HIDAPI_GetPendingRumbleLocked(device, &pending_data, &pending_size, &maximum_size)) {
228  if (size > maximum_size) {
229  SDL_HIDAPI_UnlockRumble();
230  return SDL_SetError("Couldn't send rumble, size %d is greater than %d", size, maximum_size);
231  }
232 
233  SDL_memcpy(pending_data, data, size);
234  *pending_size = size;
235  SDL_HIDAPI_UnlockRumble();
236  return size;
237  }
238 
239  return SDL_HIDAPI_SendRumbleAndUnlock(device, data, size);
240 }
241 
242 void SDL_HIDAPI_QuitRumble(void)
243 {
244  SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
245 
246  if (SDL_AtomicGet(&ctx->running)) {
247  SDL_HIDAPI_StopRumbleThread(ctx);
248  }
249 }
250 
251 #endif /* SDL_JOYSTICK_HIDAPI */
252 
253 /* vi: set ts=4 sw=4 expandtab: */
SDL_SetThreadPriority
#define SDL_SetThreadPriority
Definition: SDL_dynapi_overrides.h:477
hid_write
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length)
Write an Output report to a HID device.
SDL_CreateSemaphore
#define SDL_CreateSemaphore
Definition: SDL_dynapi_overrides.h:264
SDL_AtomicCAS
#define SDL_AtomicCAS
Definition: SDL_dynapi_overrides.h:66
SDL_LockMutex
#define SDL_LockMutex
Definition: SDL_dynapi_overrides.h:260
NULL
#define NULL
Definition: begin_code.h:167
SDL_mutex
Definition: SDL_sysmutex.c:29
SDL_hidapi_rumble.h
SDL_THREAD_PRIORITY_HIGH
@ SDL_THREAD_PRIORITY_HIGH
Definition: SDL_thread.h:62
SDL_AtomicIncRef
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:252
SDL_CreateMutex
#define SDL_CreateMutex
Definition: SDL_dynapi_overrides.h:259
SDL_Thread
Definition: SDL_thread_c.h:54
ctx
EGLContext ctx
Definition: eglext.h:208
SDL_SemPost
#define SDL_SemPost
Definition: SDL_dynapi_overrides.h:269
result
GLuint64EXT * result
Definition: SDL_opengl_glext.h:9435
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
SDL_hidapijoystick_c.h
SDL_AtomicDecRef
#define SDL_AtomicDecRef(a)
Decrement an atomic variable used as a reference count.
Definition: SDL_atomic.h:262
SDL_memcpy
#define SDL_memcpy
Definition: SDL_dynapi_overrides.h:387
SDL_FALSE
@ SDL_FALSE
Definition: SDL_stdinc.h:163
SDL_thread.h
SDL_CreateThreadInternal
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:435
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
SDL_assert.h
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:169
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
size
GLsizeiptr size
Definition: SDL_opengl_glext.h:540
SDL_calloc
#define SDL_calloc
Definition: SDL_dynapi_overrides.h:375
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_SemWait
#define SDL_SemWait
Definition: SDL_dynapi_overrides.h:266
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
USB_PACKET_LENGTH
#define USB_PACKET_LENGTH
Definition: SDL_hidapijoystick_c.h:58
SDL_atomic_t
A type representing an atomic integer value. It is a struct so people don't accidentally use numeric ...
Definition: SDL_atomic.h:216
SDL_DestroyMutex
#define SDL_DestroyMutex
Definition: SDL_dynapi_overrides.h:263
SDL_DestroySemaphore
#define SDL_DestroySemaphore
Definition: SDL_dynapi_overrides.h:265
SDL_AtomicSet
#define SDL_AtomicSet
Definition: SDL_dynapi_overrides.h:67
SDL_AtomicGet
#define SDL_AtomicGet
Definition: SDL_dynapi_overrides.h:68
void
const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char const char const SDL_SCANF_FORMAT_STRING char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
Definition: SDL_dynapi_procs.h:89
SDL_UnlockMutex
#define SDL_UnlockMutex
Definition: SDL_dynapi_overrides.h:262
device
static SDL_AudioDeviceID device
Definition: loopwave.c:37
SDL_WaitThread
#define SDL_WaitThread
Definition: SDL_dynapi_overrides.h:478
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:161
lock
SDL_mutex * lock
Definition: SDL_events.c:83
Uint8
uint8_t Uint8
Definition: SDL_stdinc.h:179