1/*
2 Simple DirectMedia Layer
3 Copyright (C) 2025 Katharine Chui <katharine.chui@gmail.com>
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#include "SDL_hidapihaptic_c.h"
26#include "SDL3/SDL_mutex.h"
27#include "SDL3/SDL_error.h"
28
29extern struct SDL_JoystickDriver SDL_HIDAPI_JoystickDriver;
30
31typedef struct haptic_list_node
32{
33 SDL_Haptic *haptic;
34 struct haptic_list_node *next;
35} haptic_list_node;
36
37static haptic_list_node *haptic_list_head = NULL;
38static SDL_Mutex *haptic_list_mutex = NULL;
39
40static SDL_HIDAPI_HapticDriver *drivers[] = {
41 #ifdef SDL_HAPTIC_HIDAPI_LG4FF
42 &SDL_HIDAPI_HapticDriverLg4ff,
43 #endif
44 NULL
45};
46
47bool SDL_HIDAPI_HapticInit()
48{
49 haptic_list_head = NULL;
50 haptic_list_mutex = SDL_CreateMutex();
51 if (haptic_list_mutex == NULL) {
52 return SDL_OutOfMemory();
53 }
54 return true;
55}
56
57bool SDL_HIDAPI_HapticIsHidapi(SDL_Haptic *haptic)
58{
59 haptic_list_node *cur;
60 bool ret = false;
61
62 SDL_LockMutex(haptic_list_mutex);
63 cur = haptic_list_head;
64 while (cur != NULL) {
65 if (cur->haptic == haptic) {
66 ret = true;
67 break;
68 }
69 cur = cur->next;
70 }
71
72 SDL_UnlockMutex(haptic_list_mutex);
73
74 return ret;
75}
76
77
78bool SDL_HIDAPI_JoystickIsHaptic(SDL_Joystick *joystick)
79{
80 const int numdrivers = SDL_arraysize(drivers) - 1;
81 int i;
82
83 SDL_AssertJoysticksLocked();
84
85 if (joystick->driver != &SDL_HIDAPI_JoystickDriver) {
86 return false;
87 }
88
89 for (i = 0; i < numdrivers; ++i) {
90 if (drivers[i]->JoystickSupported(joystick)) {
91 return true;
92 }
93 }
94 return false;
95}
96
97bool SDL_HIDAPI_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
98{
99 const int numdrivers = SDL_arraysize(drivers) - 1;
100 int i;
101
102 SDL_AssertJoysticksLocked();
103
104 if (joystick->driver != &SDL_HIDAPI_JoystickDriver) {
105 return SDL_SetError("Cannot open hidapi haptic from non hidapi joystick");
106 }
107
108 for (i = 0; i < numdrivers; ++i) {
109 if (drivers[i]->JoystickSupported(joystick)) {
110 SDL_HIDAPI_HapticDevice *device;
111 haptic_list_node *list_node;
112 // the driver is responsible for calling SDL_SetError
113 void *ctx = drivers[i]->Open(joystick);
114 if (ctx == NULL) {
115 return false;
116 }
117
118 device = SDL_malloc(sizeof(SDL_HIDAPI_HapticDevice));
119 if (device == NULL) {
120 SDL_HIDAPI_HapticDevice temp;
121 temp.ctx = ctx;
122 temp.driver = drivers[i];
123 temp.joystick = joystick;
124 temp.driver->Close(&temp);
125 return SDL_OutOfMemory();
126 }
127
128 device->driver = drivers[i];
129 device->haptic = haptic;
130 device->joystick = joystick;
131 device->ctx = ctx;
132
133 list_node = SDL_malloc(sizeof(haptic_list_node));
134 if (list_node == NULL) {
135 device->driver->Close(device);
136 SDL_free(device);
137 return SDL_OutOfMemory();
138 }
139
140 haptic->hwdata = (struct haptic_hwdata *)device;
141
142 // this is outside of the syshaptic driver
143
144 haptic->neffects = device->driver->NumEffects(device);
145 haptic->nplaying = device->driver->NumEffectsPlaying(device);
146 haptic->supported = device->driver->GetFeatures(device);
147 haptic->naxes = device->driver->NumAxes(device);
148
149 // outside of SYS_HAPTIC
150 haptic->instance_id = 255;
151
152 list_node->haptic = haptic;
153 list_node->next = NULL;
154
155 // grab a joystick ref so that it doesn't get fully destroyed before the haptic is closed
156 SDL_OpenJoystick(SDL_GetJoystickID(joystick));
157
158 SDL_LockMutex(haptic_list_mutex);
159 if (haptic_list_head == NULL) {
160 haptic_list_head = list_node;
161 } else {
162 haptic_list_node *cur = haptic_list_head;
163 while(cur->next != NULL) {
164 cur = cur->next;
165 }
166 cur->next = list_node;
167 }
168 SDL_UnlockMutex(haptic_list_mutex);
169
170 return true;
171 }
172 }
173
174 return SDL_SetError("No supported HIDAPI haptic driver found for joystick");
175}
176
177bool SDL_HIDAPI_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick)
178{
179 SDL_HIDAPI_HapticDevice *device;
180
181 SDL_AssertJoysticksLocked();
182 if (joystick->driver != &SDL_HIDAPI_JoystickDriver) {
183 return false;
184 }
185
186 device = (SDL_HIDAPI_HapticDevice *)haptic->hwdata;
187
188 if (joystick == device->joystick) {
189 return true;
190 }
191 return false;
192}
193
194void SDL_HIDAPI_HapticClose(SDL_Haptic *haptic)
195{
196 haptic_list_node *cur, *last;
197
198 SDL_LockMutex(haptic_list_mutex);
199
200 cur = haptic_list_head;
201 last = NULL;
202 while (cur != NULL) {
203 if (cur->haptic == haptic) {
204 SDL_HIDAPI_HapticDevice *device = (SDL_HIDAPI_HapticDevice *)haptic->hwdata;
205
206 device->driver->Close(device);
207
208 // a reference was grabbed during open, now release it
209 SDL_CloseJoystick(device->joystick);
210
211 if (cur == haptic_list_head) {
212 haptic_list_head = cur->next;
213 } else {
214 last->next = cur->next;
215 }
216
217 SDL_free(device->ctx);
218 SDL_free(device);
219 SDL_free(cur);
220 SDL_UnlockMutex(haptic_list_mutex);
221 return;
222 }
223 last = cur;
224 cur = cur->next;
225 }
226
227 SDL_UnlockMutex(haptic_list_mutex);
228}
229
230void SDL_HIDAPI_HapticQuit(void)
231{
232 // the list is cleared in SDL_haptic.c
233 if (haptic_list_mutex != NULL) {
234 SDL_DestroyMutex(haptic_list_mutex);
235 haptic_list_mutex = NULL;
236 }
237}
238
239int SDL_HIDAPI_HapticNewEffect(SDL_Haptic *haptic, const SDL_HapticEffect *base)
240{
241 SDL_HIDAPI_HapticDevice *device = (SDL_HIDAPI_HapticDevice *)haptic->hwdata;
242 return device->driver->CreateEffect(device, base);
243}
244
245bool SDL_HIDAPI_HapticUpdateEffect(SDL_Haptic *haptic, int id, const SDL_HapticEffect *data)
246{
247 SDL_HIDAPI_HapticDevice *device = (SDL_HIDAPI_HapticDevice *)haptic->hwdata;
248 return device->driver->UpdateEffect(device, id, data);
249}
250
251bool SDL_HIDAPI_HapticRunEffect(SDL_Haptic *haptic, int id, Uint32 iterations)
252{
253 SDL_HIDAPI_HapticDevice *device = (SDL_HIDAPI_HapticDevice *)haptic->hwdata;
254 return device->driver->RunEffect(device, id, iterations);
255}
256
257bool SDL_HIDAPI_HapticStopEffect(SDL_Haptic *haptic, int id)
258{
259 SDL_HIDAPI_HapticDevice *device = (SDL_HIDAPI_HapticDevice *)haptic->hwdata;
260 return device->driver->StopEffect(device, id);
261}
262
263void SDL_HIDAPI_HapticDestroyEffect(SDL_Haptic *haptic, int id)
264{
265 SDL_HIDAPI_HapticDevice *device = (SDL_HIDAPI_HapticDevice *)haptic->hwdata;
266 device->driver->DestroyEffect(device, id);
267}
268
269bool SDL_HIDAPI_HapticGetEffectStatus(SDL_Haptic *haptic, int id)
270{
271 SDL_HIDAPI_HapticDevice *device = (SDL_HIDAPI_HapticDevice *)haptic->hwdata;
272 return device->driver->GetEffectStatus(device, id);
273}
274
275bool SDL_HIDAPI_HapticSetGain(SDL_Haptic *haptic, int gain)
276{
277 SDL_HIDAPI_HapticDevice *device = (SDL_HIDAPI_HapticDevice *)haptic->hwdata;
278 return device->driver->SetGain(device, gain);
279}
280
281bool SDL_HIDAPI_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
282{
283 SDL_HIDAPI_HapticDevice *device = (SDL_HIDAPI_HapticDevice *)haptic->hwdata;
284 return device->driver->SetAutocenter(device, autocenter);
285}
286
287bool SDL_HIDAPI_HapticPause(SDL_Haptic *haptic)
288{
289 SDL_HIDAPI_HapticDevice *device = (SDL_HIDAPI_HapticDevice *)haptic->hwdata;
290 return device->driver->Pause(device);
291}
292
293bool SDL_HIDAPI_HapticResume(SDL_Haptic *haptic)
294{
295 SDL_HIDAPI_HapticDevice *device = (SDL_HIDAPI_HapticDevice *)haptic->hwdata;
296 return device->driver->Resume(device);
297}
298
299bool SDL_HIDAPI_HapticStopAll(SDL_Haptic *haptic)
300{
301 SDL_HIDAPI_HapticDevice *device = (SDL_HIDAPI_HapticDevice *)haptic->hwdata;
302 return device->driver->StopEffects(device);
303}
304
305#endif //SDL_JOYSTICK_HIDAPI