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