1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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#include "SDL_syshaptic.h"
24#ifdef SDL_JOYSTICK_HIDAPI
25#include "SDL_hidapihaptic.h"
26#endif
27#include "SDL_haptic_c.h"
28#include "../joystick/SDL_joystick_c.h" // For SDL_IsJoystickValid
29#include "../SDL_hints_c.h"
30
31typedef struct SDL_Haptic_VIDPID_Naxes {
32 Uint16 vid;
33 Uint16 pid;
34 Uint16 naxes;
35} SDL_Haptic_VIDPID_Naxes;
36
37static void SDL_Haptic_Load_Axes_List(SDL_Haptic_VIDPID_Naxes **entries, int *num_entries)
38{
39 SDL_Haptic_VIDPID_Naxes entry;
40 const char *spot;
41 int length = 0;
42
43 spot = SDL_GetHint(SDL_HINT_JOYSTICK_HAPTIC_AXES);
44 if (!spot)
45 return;
46
47 while (SDL_sscanf(spot, "0x%hx/0x%hx/%hu%n", &entry.vid, &entry.pid, &entry.naxes, &length) == 3) {
48 SDL_assert(length > 0);
49 spot += length;
50 length = 0;
51
52 if ((*num_entries % 8) == 0) {
53 int new_max = *num_entries + 8;
54 SDL_Haptic_VIDPID_Naxes *new_entries =
55 (SDL_Haptic_VIDPID_Naxes *)SDL_realloc(*entries, new_max * sizeof(**entries));
56
57 // Out of memory, go with what we have already
58 if (!new_entries)
59 break;
60
61 *entries = new_entries;
62 }
63 (*entries)[(*num_entries)++] = entry;
64
65 if (spot[0] == ',')
66 spot++;
67 }
68}
69
70// /* Return -1 if not found */
71static int SDL_Haptic_Naxes_List_Index(struct SDL_Haptic_VIDPID_Naxes *entries, int num_entries, Uint16 vid, Uint16 pid)
72{
73 if (!entries)
74 return -1;
75
76 int i;
77 for (i = 0; i < num_entries; ++i) {
78 if (entries[i].vid == vid && entries[i].pid == pid)
79 return i;
80 }
81
82 return -1;
83}
84
85// Check if device needs a custom number of naxes
86static int SDL_Haptic_Get_Naxes(Uint16 vid, Uint16 pid)
87{
88 int num_entries = 0, index = 0, naxes = -1;
89 SDL_Haptic_VIDPID_Naxes *naxes_list = NULL;
90
91 SDL_Haptic_Load_Axes_List(&naxes_list, &num_entries);
92 if (!num_entries || !naxes_list)
93 return -1;
94
95 // Perform "wildcard" pass
96 index = SDL_Haptic_Naxes_List_Index(naxes_list, num_entries, 0xffff, 0xffff);
97 if (index >= 0)
98 naxes = naxes_list[index].naxes;
99
100 index = SDL_Haptic_Naxes_List_Index(naxes_list, num_entries, vid, pid);
101 if (index >= 0)
102 naxes = naxes_list[index].naxes;
103
104 SDL_free(naxes_list);
105 return naxes;
106}
107
108static SDL_Haptic *SDL_haptics = NULL;
109
110#define CHECK_HAPTIC_MAGIC(haptic, result) \
111 if (!SDL_ObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC)) { \
112 SDL_InvalidParamError("haptic"); \
113 return result; \
114 }
115
116bool SDL_InitHaptics(void)
117{
118 if (!SDL_SYS_HapticInit()) {
119 return false;
120 }
121 #ifdef SDL_JOYSTICK_HIDAPI
122 if (!SDL_HIDAPI_HapticInit()) {
123 SDL_SYS_HapticQuit();
124 return false;
125 }
126 #endif
127
128 return true;
129}
130
131static bool SDL_GetHapticIndex(SDL_HapticID instance_id, int *driver_index)
132{
133 int num_haptics, device_index;
134
135 if (instance_id > 0) {
136 num_haptics = SDL_SYS_NumHaptics();
137 for (device_index = 0; device_index < num_haptics; ++device_index) {
138 SDL_HapticID haptic_id = SDL_SYS_HapticInstanceID(device_index);
139 if (haptic_id == instance_id) {
140 *driver_index = device_index;
141 return true;
142 }
143 }
144 }
145
146 SDL_SetError("Haptic device %" SDL_PRIu32 " not found", instance_id);
147 return false;
148}
149
150SDL_HapticID *SDL_GetHaptics(int *count)
151{
152 int device_index;
153 int haptic_index = 0, num_haptics = 0;
154 SDL_HapticID *haptics;
155
156 num_haptics = SDL_SYS_NumHaptics();
157
158 haptics = (SDL_HapticID *)SDL_malloc((num_haptics + 1) * sizeof(*haptics));
159 if (haptics) {
160 if (count) {
161 *count = num_haptics;
162 }
163
164 for (device_index = 0; device_index < num_haptics; ++device_index) {
165 haptics[haptic_index] = SDL_SYS_HapticInstanceID(device_index);
166 SDL_assert(haptics[haptic_index] > 0);
167 ++haptic_index;
168 }
169 haptics[haptic_index] = 0;
170 } else {
171 if (count) {
172 *count = 0;
173 }
174 }
175
176 return haptics;
177}
178
179const char *SDL_GetHapticNameForID(SDL_HapticID instance_id)
180{
181 int device_index;
182 const char *name = NULL;
183
184 if (SDL_GetHapticIndex(instance_id, &device_index)) {
185 name = SDL_GetPersistentString(SDL_SYS_HapticName(device_index));
186 }
187 return name;
188}
189
190SDL_Haptic *SDL_OpenHaptic(SDL_HapticID instance_id)
191{
192 SDL_Haptic *haptic;
193 SDL_Haptic *hapticlist;
194 const char *name;
195 int device_index = 0;
196
197 if (!SDL_GetHapticIndex(instance_id, &device_index)) {
198 return NULL;
199 }
200
201 hapticlist = SDL_haptics;
202 /* If the haptic device is already open, return it
203 * it is important that we have a single haptic device for each instance id
204 */
205 while (hapticlist) {
206 if (instance_id == hapticlist->instance_id) {
207 haptic = hapticlist;
208 ++haptic->ref_count;
209 return haptic;
210 }
211 hapticlist = hapticlist->next;
212 }
213
214 // Create the haptic device
215 haptic = (SDL_Haptic *)SDL_calloc(1, sizeof(*haptic));
216 if (!haptic) {
217 return NULL;
218 }
219
220 // Initialize the haptic device
221 haptic->instance_id = instance_id;
222 haptic->rumble_id = -1;
223 if (!SDL_SYS_HapticOpen(haptic)) {
224 SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
225 SDL_free(haptic);
226 return NULL;
227 }
228
229 if (!haptic->name) {
230 name = SDL_SYS_HapticName(device_index);
231 if (name) {
232 haptic->name = SDL_strdup(name);
233 }
234 }
235
236 // Add haptic to list
237 ++haptic->ref_count;
238 // Link the haptic in the list
239 haptic->next = SDL_haptics;
240 SDL_haptics = haptic;
241
242 SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
243
244 // Disable autocenter and set gain to max.
245 if (haptic->supported & SDL_HAPTIC_GAIN) {
246 SDL_SetHapticGain(haptic, 100);
247 }
248 if (haptic->supported & SDL_HAPTIC_AUTOCENTER) {
249 SDL_SetHapticAutocenter(haptic, 0);
250 }
251
252 return haptic;
253}
254
255SDL_Haptic *SDL_GetHapticFromID(SDL_HapticID instance_id)
256{
257 SDL_Haptic *haptic;
258
259 for (haptic = SDL_haptics; haptic; haptic = haptic->next) {
260 if (instance_id == haptic->instance_id) {
261 break;
262 }
263 }
264 return haptic;
265}
266
267SDL_HapticID SDL_GetHapticID(SDL_Haptic *haptic)
268{
269 CHECK_HAPTIC_MAGIC(haptic, 0);
270
271 return haptic->instance_id;
272}
273
274const char *SDL_GetHapticName(SDL_Haptic *haptic)
275{
276 CHECK_HAPTIC_MAGIC(haptic, NULL);
277
278 return SDL_GetPersistentString(haptic->name);
279}
280
281bool SDL_IsMouseHaptic(void)
282{
283 if (SDL_SYS_HapticMouse() < 0) {
284 return false;
285 }
286 return true;
287}
288
289SDL_Haptic *SDL_OpenHapticFromMouse(void)
290{
291 int device_index;
292
293 device_index = SDL_SYS_HapticMouse();
294
295 if (device_index < 0) {
296 SDL_SetError("Haptic: Mouse isn't a haptic device.");
297 return NULL;
298 }
299
300 return SDL_OpenHaptic(device_index);
301}
302
303bool SDL_IsJoystickHaptic(SDL_Joystick *joystick)
304{
305 bool result = false;
306
307 SDL_LockJoysticks();
308 {
309 // Must be a valid joystick
310 if (SDL_IsJoystickValid(joystick) &&
311 !SDL_IsGamepad(SDL_GetJoystickID(joystick))) {
312 #ifdef SDL_JOYSTICK_HIDAPI
313 result = SDL_SYS_JoystickIsHaptic(joystick) || SDL_HIDAPI_JoystickIsHaptic(joystick);
314 #else
315 result = SDL_SYS_JoystickIsHaptic(joystick);
316 #endif
317 }
318 }
319 SDL_UnlockJoysticks();
320
321 return result;
322}
323
324SDL_Haptic *SDL_OpenHapticFromJoystick(SDL_Joystick *joystick)
325{
326 SDL_Haptic *haptic;
327 SDL_Haptic *hapticlist;
328
329 SDL_LockJoysticks();
330 {
331 // Joystick must be valid and haptic
332 if (!SDL_IsJoystickHaptic(joystick)) {
333 SDL_SetError("Haptic: Joystick isn't a haptic device.");
334 SDL_UnlockJoysticks();
335 return NULL;
336 }
337
338 hapticlist = SDL_haptics;
339 // Check to see if joystick's haptic is already open
340 while (hapticlist) {
341 #ifdef SDL_JOYSTICK_HIDAPI
342 if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick) || SDL_HIDAPI_JoystickSameHaptic(hapticlist, joystick)) {
343 #else
344 if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick)) {
345 #endif
346 haptic = hapticlist;
347 ++haptic->ref_count;
348 SDL_UnlockJoysticks();
349 return haptic;
350 }
351 hapticlist = hapticlist->next;
352 }
353
354 // Create the haptic device
355 haptic = (SDL_Haptic *)SDL_calloc(1, sizeof(*haptic));
356 if (!haptic) {
357 SDL_UnlockJoysticks();
358 return NULL;
359 }
360
361 /* Initialize the haptic device
362 * This function should fill in the instance ID and name.
363 */
364 SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
365 haptic->rumble_id = -1;
366 #ifdef SDL_JOYSTICK_HIDAPI
367 if (SDL_HIDAPI_JoystickIsHaptic(joystick)) {
368 if (!SDL_HIDAPI_HapticOpenFromJoystick(haptic, joystick)) {
369 SDL_SetError("Haptic: SDL_HIDAPI_HapticOpenFromJoystick failed.");
370 SDL_free(haptic);
371 SDL_UnlockJoysticks();
372 return NULL;
373 }
374 } else
375 #endif
376 if (!SDL_SYS_HapticOpenFromJoystick(haptic, joystick)) {
377 SDL_SetError("Haptic: SDL_SYS_HapticOpenFromJoystick failed.");
378 SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
379 SDL_free(haptic);
380 SDL_UnlockJoysticks();
381 return NULL;
382 }
383 SDL_assert(haptic->instance_id != 0);
384 }
385 SDL_UnlockJoysticks();
386
387 // Check if custom number of haptic axes was defined
388 Uint16 vid = SDL_GetJoystickVendor(joystick);
389 Uint16 pid = SDL_GetJoystickProduct(joystick);
390 int general_axes = SDL_GetNumJoystickAxes(joystick);
391
392 int naxes = SDL_Haptic_Get_Naxes(vid, pid);
393 if (naxes > 0)
394 haptic->naxes = naxes;
395
396 // Limit to the actual number of axes found on the device
397 if (general_axes >= 0 && naxes > general_axes)
398 haptic->naxes = general_axes;
399
400 // Add haptic to list
401 ++haptic->ref_count;
402 // Link the haptic in the list
403 haptic->next = SDL_haptics;
404 SDL_haptics = haptic;
405
406 SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
407
408 // Disable autocenter and set gain to max.
409 if (haptic->supported & SDL_HAPTIC_GAIN) {
410 SDL_SetHapticGain(haptic, 100);
411 }
412 if (haptic->supported & SDL_HAPTIC_AUTOCENTER) {
413 SDL_SetHapticAutocenter(haptic, 0);
414 }
415
416 return haptic;
417}
418
419void SDL_CloseHaptic(SDL_Haptic *haptic)
420{
421 int i;
422 SDL_Haptic *hapticlist;
423 SDL_Haptic *hapticlistprev;
424
425 CHECK_HAPTIC_MAGIC(haptic,);
426
427 // Check if it's still in use
428 if (--haptic->ref_count > 0) {
429 return;
430 }
431
432 #ifdef SDL_JOYSTICK_HIDAPI
433 if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
434 SDL_HIDAPI_HapticClose(haptic);
435 } else
436 #endif
437 {
438 // Close it, properly removing effects if needed
439 for (i = 0; i < haptic->neffects; i++) {
440 if (haptic->effects[i].hweffect != NULL) {
441 SDL_DestroyHapticEffect(haptic, i);
442 }
443 }
444 SDL_SYS_HapticClose(haptic);
445 }
446 SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
447
448 // Remove from the list
449 hapticlist = SDL_haptics;
450 hapticlistprev = NULL;
451 while (hapticlist) {
452 if (haptic == hapticlist) {
453 if (hapticlistprev) {
454 // unlink this entry
455 hapticlistprev->next = hapticlist->next;
456 } else {
457 SDL_haptics = haptic->next;
458 }
459
460 break;
461 }
462 hapticlistprev = hapticlist;
463 hapticlist = hapticlist->next;
464 }
465
466 // Free the data associated with this device
467 SDL_free(haptic->name);
468 SDL_free(haptic);
469}
470
471void SDL_QuitHaptics(void)
472{
473 while (SDL_haptics) {
474 SDL_CloseHaptic(SDL_haptics);
475 }
476
477 #ifdef SDL_JOYSTICK_HIDAPI
478 SDL_HIDAPI_HapticQuit();
479 #endif
480 SDL_SYS_HapticQuit();
481}
482
483int SDL_GetMaxHapticEffects(SDL_Haptic *haptic)
484{
485 CHECK_HAPTIC_MAGIC(haptic, -1);
486
487 return haptic->neffects;
488}
489
490int SDL_GetMaxHapticEffectsPlaying(SDL_Haptic *haptic)
491{
492 CHECK_HAPTIC_MAGIC(haptic, -1);
493
494 return haptic->nplaying;
495}
496
497Uint32 SDL_GetHapticFeatures(SDL_Haptic *haptic)
498{
499 CHECK_HAPTIC_MAGIC(haptic, 0);
500
501 return haptic->supported;
502}
503
504int SDL_GetNumHapticAxes(SDL_Haptic *haptic)
505{
506 CHECK_HAPTIC_MAGIC(haptic, -1);
507
508 return haptic->naxes;
509}
510
511bool SDL_HapticEffectSupported(SDL_Haptic *haptic, const SDL_HapticEffect *effect)
512{
513 CHECK_HAPTIC_MAGIC(haptic, false);
514
515 if (!effect) {
516 return false;
517 }
518
519 if ((haptic->supported & effect->type) != 0) {
520 return true;
521 }
522 return false;
523}
524
525int SDL_CreateHapticEffect(SDL_Haptic *haptic, const SDL_HapticEffect *effect)
526{
527 int i;
528
529 CHECK_HAPTIC_MAGIC(haptic, -1);
530
531 if (!effect) {
532 SDL_InvalidParamError("effect");
533 return -1;
534 }
535
536 // Check to see if effect is supported
537 if (SDL_HapticEffectSupported(haptic, effect) == false) {
538 SDL_SetError("Haptic: Effect not supported by haptic device.");
539 return -1;
540 }
541
542 #ifdef SDL_JOYSTICK_HIDAPI
543 if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
544 return SDL_HIDAPI_HapticNewEffect(haptic, effect);
545 }
546 #endif
547
548 // See if there's a free slot
549 for (i = 0; i < haptic->neffects; i++) {
550 if (haptic->effects[i].hweffect == NULL) {
551
552 // Now let the backend create the real effect
553 if (!SDL_SYS_HapticNewEffect(haptic, &haptic->effects[i], effect)) {
554 return -1; // Backend failed to create effect
555 }
556
557 SDL_memcpy(&haptic->effects[i].effect, effect,
558 sizeof(SDL_HapticEffect));
559 return i;
560 }
561 }
562
563 SDL_SetError("Haptic: Device has no free space left.");
564 return -1;
565}
566
567static bool ValidEffect(SDL_Haptic *haptic, int effect)
568{
569 if ((effect < 0) || (effect >= haptic->neffects)) {
570 SDL_SetError("Haptic: Invalid effect identifier.");
571 return false;
572 }
573 return true;
574}
575
576bool SDL_UpdateHapticEffect(SDL_Haptic *haptic, int effect, const SDL_HapticEffect *data)
577{
578 CHECK_HAPTIC_MAGIC(haptic, false);
579
580 #ifdef SDL_JOYSTICK_HIDAPI
581 if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
582 return SDL_HIDAPI_HapticUpdateEffect(haptic, effect, data);
583 }
584 #endif
585
586 if (!ValidEffect(haptic, effect)) {
587 return false;
588 }
589
590 if (!data) {
591 return SDL_InvalidParamError("data");
592 }
593
594 // Can't change type dynamically.
595 if (data->type != haptic->effects[effect].effect.type) {
596 return SDL_SetError("Haptic: Updating effect type is illegal.");
597 }
598
599 // Updates the effect
600 if (!SDL_SYS_HapticUpdateEffect(haptic, &haptic->effects[effect], data)) {
601 return false;
602 }
603
604 SDL_memcpy(&haptic->effects[effect].effect, data,
605 sizeof(SDL_HapticEffect));
606 return true;
607}
608
609bool SDL_RunHapticEffect(SDL_Haptic *haptic, int effect, Uint32 iterations)
610{
611 CHECK_HAPTIC_MAGIC(haptic, false);
612
613 #ifdef SDL_JOYSTICK_HIDAPI
614 if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
615 return SDL_HIDAPI_HapticRunEffect(haptic, effect, iterations);
616 }
617 #endif
618
619 if (!ValidEffect(haptic, effect)) {
620 return false;
621 }
622
623 // Run the effect
624 if (!SDL_SYS_HapticRunEffect(haptic, &haptic->effects[effect], iterations)) {
625 return false;
626 }
627
628 return true;
629}
630
631bool SDL_StopHapticEffect(SDL_Haptic *haptic, int effect)
632{
633 CHECK_HAPTIC_MAGIC(haptic, false);
634
635 #ifdef SDL_JOYSTICK_HIDAPI
636 if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
637 return SDL_HIDAPI_HapticStopEffect(haptic, effect);
638 }
639 #endif
640
641 if (!ValidEffect(haptic, effect)) {
642 return false;
643 }
644
645 // Stop the effect
646 if (!SDL_SYS_HapticStopEffect(haptic, &haptic->effects[effect])) {
647 return false;
648 }
649
650 return true;
651}
652
653void SDL_DestroyHapticEffect(SDL_Haptic *haptic, int effect)
654{
655 CHECK_HAPTIC_MAGIC(haptic,);
656
657 #ifdef SDL_JOYSTICK_HIDAPI
658 if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
659 SDL_HIDAPI_HapticDestroyEffect(haptic, effect);
660 return;
661 }
662 #endif
663
664 if (!ValidEffect(haptic, effect)) {
665 return;
666 }
667
668 // Not allocated
669 if (haptic->effects[effect].hweffect == NULL) {
670 return;
671 }
672
673 SDL_SYS_HapticDestroyEffect(haptic, &haptic->effects[effect]);
674}
675
676bool SDL_GetHapticEffectStatus(SDL_Haptic *haptic, int effect)
677{
678 CHECK_HAPTIC_MAGIC(haptic, false);
679
680 #ifdef SDL_JOYSTICK_HIDAPI
681 if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
682 return SDL_HIDAPI_HapticGetEffectStatus(haptic, effect);
683 }
684 #endif
685
686 if (!ValidEffect(haptic, effect)) {
687 return false;
688 }
689
690 if (!(haptic->supported & SDL_HAPTIC_STATUS)) {
691 return SDL_SetError("Haptic: Device does not support status queries.");
692 }
693
694 SDL_ClearError();
695
696 return (SDL_SYS_HapticGetEffectStatus(haptic, &haptic->effects[effect]) > 0);
697}
698
699bool SDL_SetHapticGain(SDL_Haptic *haptic, int gain)
700{
701 const char *env;
702 int real_gain, max_gain;
703
704 CHECK_HAPTIC_MAGIC(haptic, false);
705
706 if (!(haptic->supported & SDL_HAPTIC_GAIN)) {
707 return SDL_SetError("Haptic: Device does not support setting gain.");
708 }
709
710 if ((gain < 0) || (gain > 100)) {
711 return SDL_SetError("Haptic: Gain must be between 0 and 100.");
712 }
713
714 // The user can use an environment variable to override the max gain.
715 env = SDL_getenv("SDL_HAPTIC_GAIN_MAX");
716 if (env) {
717 max_gain = SDL_atoi(env);
718
719 // Check for sanity.
720 if (max_gain < 0) {
721 max_gain = 0;
722 } else if (max_gain > 100) {
723 max_gain = 100;
724 }
725
726 // We'll scale it linearly with SDL_HAPTIC_GAIN_MAX
727 real_gain = (gain * max_gain) / 100;
728 } else {
729 real_gain = gain;
730 }
731
732 #ifdef SDL_JOYSTICK_HIDAPI
733 if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
734 return SDL_HIDAPI_HapticSetGain(haptic, real_gain);
735 }
736 #endif
737
738 return SDL_SYS_HapticSetGain(haptic, real_gain);
739}
740
741bool SDL_SetHapticAutocenter(SDL_Haptic *haptic, int autocenter)
742{
743 CHECK_HAPTIC_MAGIC(haptic, false);
744
745 if (!(haptic->supported & SDL_HAPTIC_AUTOCENTER)) {
746 return SDL_SetError("Haptic: Device does not support setting autocenter.");
747 }
748
749 if ((autocenter < 0) || (autocenter > 100)) {
750 return SDL_SetError("Haptic: Autocenter must be between 0 and 100.");
751 }
752
753 #ifdef SDL_JOYSTICK_HIDAPI
754 if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
755 return SDL_HIDAPI_HapticSetAutocenter(haptic, autocenter);
756 }
757 #endif
758
759 return SDL_SYS_HapticSetAutocenter(haptic, autocenter);
760}
761
762bool SDL_PauseHaptic(SDL_Haptic *haptic)
763{
764 CHECK_HAPTIC_MAGIC(haptic, false);
765
766 if (!(haptic->supported & SDL_HAPTIC_PAUSE)) {
767 return SDL_SetError("Haptic: Device does not support setting pausing.");
768 }
769
770 #ifdef SDL_JOYSTICK_HIDAPI
771 if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
772 return SDL_HIDAPI_HapticPause(haptic);
773 }
774 #endif
775
776 return SDL_SYS_HapticPause(haptic);
777}
778
779bool SDL_ResumeHaptic(SDL_Haptic *haptic)
780{
781 CHECK_HAPTIC_MAGIC(haptic, false);
782
783 if (!(haptic->supported & SDL_HAPTIC_PAUSE)) {
784 return true; // Not going to be paused, so we pretend it's unpaused.
785 }
786
787 #ifdef SDL_JOYSTICK_HIDAPI
788 if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
789 return SDL_HIDAPI_HapticResume(haptic);
790 }
791 #endif
792
793 return SDL_SYS_HapticResume(haptic);
794}
795
796bool SDL_StopHapticEffects(SDL_Haptic *haptic)
797{
798 CHECK_HAPTIC_MAGIC(haptic, false);
799
800 #ifdef SDL_JOYSTICK_HIDAPI
801 if (SDL_HIDAPI_HapticIsHidapi(haptic)) {
802 return SDL_HIDAPI_HapticStopAll(haptic);
803 }
804 #endif
805
806 return SDL_SYS_HapticStopAll(haptic);
807}
808
809bool SDL_HapticRumbleSupported(SDL_Haptic *haptic)
810{
811 CHECK_HAPTIC_MAGIC(haptic, false);
812
813 // Most things can use SINE, but XInput only has LEFTRIGHT.
814 return (haptic->supported & (SDL_HAPTIC_SINE | SDL_HAPTIC_LEFTRIGHT)) != 0;
815}
816
817bool SDL_InitHapticRumble(SDL_Haptic *haptic)
818{
819 SDL_HapticEffect *efx = &haptic->rumble_effect;
820
821 CHECK_HAPTIC_MAGIC(haptic, false);
822
823 // Already allocated.
824 if (haptic->rumble_id >= 0) {
825 return true;
826 }
827
828 SDL_zerop(efx);
829 if (haptic->supported & SDL_HAPTIC_SINE) {
830 efx->type = SDL_HAPTIC_SINE;
831 efx->periodic.direction.type = SDL_HAPTIC_CARTESIAN;
832 efx->periodic.period = 1000;
833 efx->periodic.magnitude = 0x4000;
834 efx->periodic.length = 5000;
835 efx->periodic.attack_length = 0;
836 efx->periodic.fade_length = 0;
837 } else if (haptic->supported & SDL_HAPTIC_LEFTRIGHT) { // XInput?
838 efx->type = SDL_HAPTIC_LEFTRIGHT;
839 efx->leftright.length = 5000;
840 efx->leftright.large_magnitude = 0x4000;
841 efx->leftright.small_magnitude = 0x4000;
842 } else {
843 return SDL_SetError("Device doesn't support rumble");
844 }
845
846 haptic->rumble_id = SDL_CreateHapticEffect(haptic, &haptic->rumble_effect);
847 if (haptic->rumble_id >= 0) {
848 return true;
849 }
850 return false;
851}
852
853bool SDL_PlayHapticRumble(SDL_Haptic *haptic, float strength, Uint32 length)
854{
855 SDL_HapticEffect *efx;
856 Sint16 magnitude;
857
858 CHECK_HAPTIC_MAGIC(haptic, false);
859
860 if (haptic->rumble_id < 0) {
861 return SDL_SetError("Haptic: Rumble effect not initialized on haptic device");
862 }
863
864 // Clamp strength.
865 if (strength > 1.0f) {
866 strength = 1.0f;
867 } else if (strength < 0.0f) {
868 strength = 0.0f;
869 }
870 magnitude = (Sint16)(32767.0f * strength);
871
872 efx = &haptic->rumble_effect;
873 if (efx->type == SDL_HAPTIC_SINE) {
874 efx->periodic.magnitude = magnitude;
875 efx->periodic.length = length;
876 } else if (efx->type == SDL_HAPTIC_LEFTRIGHT) {
877 efx->leftright.small_magnitude = efx->leftright.large_magnitude = magnitude;
878 efx->leftright.length = length;
879 } else {
880 SDL_assert(!"This should have been caught elsewhere");
881 }
882
883 if (!SDL_UpdateHapticEffect(haptic, haptic->rumble_id, &haptic->rumble_effect)) {
884 return false;
885 }
886
887 return SDL_RunHapticEffect(haptic, haptic->rumble_id, 1);
888}
889
890bool SDL_StopHapticRumble(SDL_Haptic *haptic)
891{
892 CHECK_HAPTIC_MAGIC(haptic, false);
893
894 if (haptic->rumble_id < 0) {
895 return SDL_SetError("Haptic: Rumble effect not initialized on haptic device");
896 }
897
898 return SDL_StopHapticEffect(haptic, haptic->rumble_id);
899}
900