Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/joystick/hidapi/SDL_hidapi_rumble.c
9906 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 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
31
typedef 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
42
typedef 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
53
static
54
#endif
55
SDL_Mutex *SDL_HIDAPI_rumble_lock;
56
static SDL_HIDAPI_RumbleContext rumble_context SDL_GUARDED_BY(SDL_HIDAPI_rumble_lock);
57
58
static 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
SDL_LockMutex(request->device->dev_lock);
81
if (request->device->dev) {
82
#ifdef DEBUG_RUMBLE
83
HIDAPI_DumpPacket("Rumble packet: size = %d", request->data, request->size);
84
#endif
85
SDL_hid_write(request->device->dev, request->data, request->size);
86
}
87
SDL_UnlockMutex(request->device->dev_lock);
88
if (request->callback) {
89
request->callback(request->userdata);
90
}
91
(void)SDL_AtomicDecRef(&request->device->rumble_pending);
92
SDL_free(request);
93
94
// Make sure we're not starving report reads when there's lots of rumble
95
SDL_Delay(10);
96
}
97
}
98
return 0;
99
}
100
101
static void SDL_HIDAPI_StopRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
102
{
103
SDL_HIDAPI_RumbleRequest *request;
104
105
SDL_SetAtomicInt(&ctx->running, false);
106
107
if (ctx->thread) {
108
int result;
109
110
SDL_SignalSemaphore(ctx->request_sem);
111
SDL_WaitThread(ctx->thread, &result);
112
ctx->thread = NULL;
113
}
114
115
SDL_LockMutex(SDL_HIDAPI_rumble_lock);
116
while (ctx->requests_tail) {
117
request = ctx->requests_tail;
118
if (request == ctx->requests_head) {
119
ctx->requests_head = NULL;
120
}
121
ctx->requests_tail = request->prev;
122
123
if (request->callback) {
124
request->callback(request->userdata);
125
}
126
(void)SDL_AtomicDecRef(&request->device->rumble_pending);
127
SDL_free(request);
128
}
129
SDL_UnlockMutex(SDL_HIDAPI_rumble_lock);
130
131
if (ctx->request_sem) {
132
SDL_DestroySemaphore(ctx->request_sem);
133
ctx->request_sem = NULL;
134
}
135
136
if (SDL_HIDAPI_rumble_lock) {
137
SDL_DestroyMutex(SDL_HIDAPI_rumble_lock);
138
SDL_HIDAPI_rumble_lock = NULL;
139
}
140
141
SDL_SetAtomicInt(&ctx->initialized, false);
142
}
143
144
static bool SDL_HIDAPI_StartRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
145
{
146
SDL_HIDAPI_rumble_lock = SDL_CreateMutex();
147
if (!SDL_HIDAPI_rumble_lock) {
148
SDL_HIDAPI_StopRumbleThread(ctx);
149
return false;
150
}
151
152
ctx->request_sem = SDL_CreateSemaphore(0);
153
if (!ctx->request_sem) {
154
SDL_HIDAPI_StopRumbleThread(ctx);
155
return false;
156
}
157
158
SDL_SetAtomicInt(&ctx->running, true);
159
ctx->thread = SDL_CreateThread(SDL_HIDAPI_RumbleThread, "HIDAPI Rumble", ctx);
160
if (!ctx->thread) {
161
SDL_HIDAPI_StopRumbleThread(ctx);
162
return false;
163
}
164
return true;
165
}
166
167
bool SDL_HIDAPI_LockRumble(void)
168
{
169
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
170
171
if (SDL_CompareAndSwapAtomicInt(&ctx->initialized, false, true)) {
172
if (!SDL_HIDAPI_StartRumbleThread(ctx)) {
173
return false;
174
}
175
}
176
177
SDL_LockMutex(SDL_HIDAPI_rumble_lock);
178
return true;
179
}
180
181
bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size)
182
{
183
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
184
SDL_HIDAPI_RumbleRequest *request, *found;
185
186
found = NULL;
187
for (request = ctx->requests_tail; request; request = request->prev) {
188
if (request->device == device) {
189
found = request;
190
}
191
}
192
if (found) {
193
*data = found->data;
194
*size = &found->size;
195
*maximum_size = sizeof(found->data);
196
return true;
197
}
198
return false;
199
}
200
201
int SDL_HIDAPI_SendRumbleAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
202
{
203
return SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(device, data, size, NULL, NULL);
204
}
205
206
int SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(SDL_HIDAPI_Device *device, const Uint8 *data, int size, SDL_HIDAPI_RumbleSentCallback callback, void *userdata)
207
{
208
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
209
SDL_HIDAPI_RumbleRequest *request;
210
211
if (size > sizeof(request->data)) {
212
SDL_HIDAPI_UnlockRumble();
213
SDL_SetError("Couldn't send rumble, size %d is greater than %d", size, (int)sizeof(request->data));
214
return -1;
215
}
216
217
request = (SDL_HIDAPI_RumbleRequest *)SDL_calloc(1, sizeof(*request));
218
if (!request) {
219
SDL_HIDAPI_UnlockRumble();
220
return -1;
221
}
222
request->device = device;
223
SDL_memcpy(request->data, data, size);
224
request->size = size;
225
request->callback = callback;
226
request->userdata = userdata;
227
228
SDL_AtomicIncRef(&device->rumble_pending);
229
230
if (ctx->requests_head) {
231
ctx->requests_head->prev = request;
232
} else {
233
ctx->requests_tail = request;
234
}
235
ctx->requests_head = request;
236
237
// Make sure we unlock before posting the semaphore so the rumble thread can run immediately
238
SDL_HIDAPI_UnlockRumble();
239
240
SDL_SignalSemaphore(ctx->request_sem);
241
242
return size;
243
}
244
245
void SDL_HIDAPI_UnlockRumble(void)
246
{
247
SDL_UnlockMutex(SDL_HIDAPI_rumble_lock);
248
}
249
250
int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
251
{
252
Uint8 *pending_data;
253
int *pending_size;
254
int maximum_size;
255
256
if (size <= 0) {
257
SDL_SetError("Tried to send rumble with invalid size");
258
return -1;
259
}
260
261
if (!SDL_HIDAPI_LockRumble()) {
262
return -1;
263
}
264
265
// check if there is a pending request for the device and update it
266
if (SDL_HIDAPI_GetPendingRumbleLocked(device, &pending_data, &pending_size, &maximum_size) &&
267
size == *pending_size && data[0] == pending_data[0]) {
268
SDL_memcpy(pending_data, data, size);
269
SDL_HIDAPI_UnlockRumble();
270
return size;
271
}
272
273
return SDL_HIDAPI_SendRumbleAndUnlock(device, data, size);
274
}
275
276
void SDL_HIDAPI_QuitRumble(void)
277
{
278
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
279
280
if (SDL_GetAtomicInt(&ctx->running)) {
281
SDL_HIDAPI_StopRumbleThread(ctx);
282
}
283
}
284
285
#endif // SDL_JOYSTICK_HIDAPI
286
287