1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2021 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 #include "SDL_haptic_c.h"
25 #include "../joystick/SDL_joystick_c.h" /* For SDL_PrivateJoystickValid */
26 
27 /* Global for SDL_windowshaptic.c */
28 #if (defined(SDL_HAPTIC_DINPUT) && SDL_HAPTIC_DINPUT) || (defined(SDL_HAPTIC_XINPUT) && SDL_HAPTIC_XINPUT)
29 SDL_Haptic *SDL_haptics = NULL;
30 #else
31 static SDL_Haptic *SDL_haptics = NULL;
32 #endif
33 
34 /*
35  * Initializes the Haptic devices.
36  */
37 int
SDL_HapticInit(void)38 SDL_HapticInit(void)
39 {
40     int status;
41 
42     status = SDL_SYS_HapticInit();
43     if (status >= 0) {
44         status = 0;
45     }
46 
47     return status;
48 }
49 
50 
51 /*
52  * Checks to see if the haptic device is valid
53  */
54 static int
ValidHaptic(SDL_Haptic * haptic)55 ValidHaptic(SDL_Haptic * haptic)
56 {
57     int valid;
58     SDL_Haptic *hapticlist;
59 
60     valid = 0;
61     if (haptic != NULL) {
62         hapticlist = SDL_haptics;
63         while ( hapticlist )
64         {
65             if (hapticlist == haptic) {
66                 valid = 1;
67                 break;
68             }
69             hapticlist = hapticlist->next;
70         }
71     }
72 
73     /* Create the error here. */
74     if (valid == 0) {
75         SDL_SetError("Haptic: Invalid haptic device identifier");
76     }
77 
78     return valid;
79 }
80 
81 
82 /*
83  * Returns the number of available devices.
84  */
85 int
SDL_NumHaptics(void)86 SDL_NumHaptics(void)
87 {
88     return SDL_SYS_NumHaptics();
89 }
90 
91 
92 /*
93  * Gets the name of a Haptic device by index.
94  */
95 const char *
SDL_HapticName(int device_index)96 SDL_HapticName(int device_index)
97 {
98     if ((device_index < 0) || (device_index >= SDL_NumHaptics())) {
99         SDL_SetError("Haptic: There are %d haptic devices available",
100                      SDL_NumHaptics());
101         return NULL;
102     }
103     return SDL_SYS_HapticName(device_index);
104 }
105 
106 
107 /*
108  * Opens a Haptic device.
109  */
110 SDL_Haptic *
SDL_HapticOpen(int device_index)111 SDL_HapticOpen(int device_index)
112 {
113     SDL_Haptic *haptic;
114     SDL_Haptic *hapticlist;
115 
116     if ((device_index < 0) || (device_index >= SDL_NumHaptics())) {
117         SDL_SetError("Haptic: There are %d haptic devices available",
118                      SDL_NumHaptics());
119         return NULL;
120     }
121 
122     hapticlist = SDL_haptics;
123     /* If the haptic is already open, return it
124     * TODO: Should we create haptic instance IDs like the Joystick API?
125     */
126     while ( hapticlist )
127     {
128         if (device_index == hapticlist->index) {
129             haptic = hapticlist;
130             ++haptic->ref_count;
131             return haptic;
132         }
133         hapticlist = hapticlist->next;
134     }
135 
136     /* Create the haptic device */
137     haptic = (SDL_Haptic *) SDL_malloc((sizeof *haptic));
138     if (haptic == NULL) {
139         SDL_OutOfMemory();
140         return NULL;
141     }
142 
143     /* Initialize the haptic device */
144     SDL_memset(haptic, 0, (sizeof *haptic));
145     haptic->rumble_id = -1;
146     haptic->index = device_index;
147     if (SDL_SYS_HapticOpen(haptic) < 0) {
148         SDL_free(haptic);
149         return NULL;
150     }
151 
152     /* Add haptic to list */
153     ++haptic->ref_count;
154     /* Link the haptic in the list */
155     haptic->next = SDL_haptics;
156     SDL_haptics = haptic;
157 
158     /* Disable autocenter and set gain to max. */
159     if (haptic->supported & SDL_HAPTIC_GAIN)
160         SDL_HapticSetGain(haptic, 100);
161     if (haptic->supported & SDL_HAPTIC_AUTOCENTER)
162         SDL_HapticSetAutocenter(haptic, 0);
163 
164     return haptic;
165 }
166 
167 
168 /*
169  * Returns 1 if the device has been opened.
170  */
171 int
SDL_HapticOpened(int device_index)172 SDL_HapticOpened(int device_index)
173 {
174     int opened;
175     SDL_Haptic *hapticlist;
176 
177     /* Make sure it's valid. */
178     if ((device_index < 0) || (device_index >= SDL_NumHaptics())) {
179         SDL_SetError("Haptic: There are %d haptic devices available",
180                      SDL_NumHaptics());
181         return 0;
182     }
183 
184     opened = 0;
185     hapticlist = SDL_haptics;
186     /* TODO Should this use an instance ID? */
187     while ( hapticlist )
188     {
189         if (hapticlist->index == (Uint8) device_index) {
190             opened = 1;
191             break;
192         }
193         hapticlist = hapticlist->next;
194     }
195     return opened;
196 }
197 
198 
199 /*
200  * Returns the index to a haptic device.
201  */
202 int
SDL_HapticIndex(SDL_Haptic * haptic)203 SDL_HapticIndex(SDL_Haptic * haptic)
204 {
205     if (!ValidHaptic(haptic)) {
206         return -1;
207     }
208 
209     return haptic->index;
210 }
211 
212 
213 /*
214  * Returns SDL_TRUE if mouse is haptic, SDL_FALSE if it isn't.
215  */
216 int
SDL_MouseIsHaptic(void)217 SDL_MouseIsHaptic(void)
218 {
219     if (SDL_SYS_HapticMouse() < 0)
220         return SDL_FALSE;
221     return SDL_TRUE;
222 }
223 
224 
225 /*
226  * Returns the haptic device if mouse is haptic or NULL elsewise.
227  */
228 SDL_Haptic *
SDL_HapticOpenFromMouse(void)229 SDL_HapticOpenFromMouse(void)
230 {
231     int device_index;
232 
233     device_index = SDL_SYS_HapticMouse();
234 
235     if (device_index < 0) {
236         SDL_SetError("Haptic: Mouse isn't a haptic device.");
237         return NULL;
238     }
239 
240     return SDL_HapticOpen(device_index);
241 }
242 
243 
244 /*
245  * Returns SDL_TRUE if joystick has haptic features.
246  */
247 int
SDL_JoystickIsHaptic(SDL_Joystick * joystick)248 SDL_JoystickIsHaptic(SDL_Joystick * joystick)
249 {
250     int ret;
251 
252     /* Must be a valid joystick */
253     if (!SDL_PrivateJoystickValid(joystick)) {
254         return -1;
255     }
256 
257     ret = SDL_SYS_JoystickIsHaptic(joystick);
258 
259     if (ret > 0)
260         return SDL_TRUE;
261     else if (ret == 0)
262         return SDL_FALSE;
263     else
264         return -1;
265 }
266 
267 
268 /*
269  * Opens a haptic device from a joystick.
270  */
271 SDL_Haptic *
SDL_HapticOpenFromJoystick(SDL_Joystick * joystick)272 SDL_HapticOpenFromJoystick(SDL_Joystick * joystick)
273 {
274     SDL_Haptic *haptic;
275     SDL_Haptic *hapticlist;
276 
277     /* Make sure there is room. */
278     if (SDL_NumHaptics() <= 0) {
279         SDL_SetError("Haptic: There are %d haptic devices available",
280                      SDL_NumHaptics());
281         return NULL;
282     }
283 
284     /* Must be a valid joystick */
285     if (!SDL_PrivateJoystickValid(joystick)) {
286         SDL_SetError("Haptic: Joystick isn't valid.");
287         return NULL;
288     }
289 
290     /* Joystick must be haptic */
291     if (SDL_SYS_JoystickIsHaptic(joystick) <= 0) {
292         SDL_SetError("Haptic: Joystick isn't a haptic device.");
293         return NULL;
294     }
295 
296     hapticlist = SDL_haptics;
297     /* Check to see if joystick's haptic is already open */
298     while ( hapticlist )
299     {
300         if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick)) {
301             haptic = hapticlist;
302             ++haptic->ref_count;
303             return haptic;
304         }
305         hapticlist = hapticlist->next;
306     }
307 
308     /* Create the haptic device */
309     haptic = (SDL_Haptic *) SDL_malloc((sizeof *haptic));
310     if (haptic == NULL) {
311         SDL_OutOfMemory();
312         return NULL;
313     }
314 
315     /* Initialize the haptic device */
316     SDL_memset(haptic, 0, sizeof(SDL_Haptic));
317     haptic->rumble_id = -1;
318     if (SDL_SYS_HapticOpenFromJoystick(haptic, joystick) < 0) {
319         SDL_SetError("Haptic: SDL_SYS_HapticOpenFromJoystick failed.");
320         SDL_free(haptic);
321         return NULL;
322     }
323 
324     /* Add haptic to list */
325     ++haptic->ref_count;
326     /* Link the haptic in the list */
327     haptic->next = SDL_haptics;
328     SDL_haptics = haptic;
329 
330     return haptic;
331 }
332 
333 
334 /*
335  * Closes a SDL_Haptic device.
336  */
337 void
SDL_HapticClose(SDL_Haptic * haptic)338 SDL_HapticClose(SDL_Haptic * haptic)
339 {
340     int i;
341     SDL_Haptic *hapticlist;
342     SDL_Haptic *hapticlistprev;
343 
344     /* Must be valid */
345     if (!ValidHaptic(haptic)) {
346         return;
347     }
348 
349     /* Check if it's still in use */
350     if (--haptic->ref_count > 0) {
351         return;
352     }
353 
354     /* Close it, properly removing effects if needed */
355     for (i = 0; i < haptic->neffects; i++) {
356         if (haptic->effects[i].hweffect != NULL) {
357             SDL_HapticDestroyEffect(haptic, i);
358         }
359     }
360     SDL_SYS_HapticClose(haptic);
361 
362     /* Remove from the list */
363     hapticlist = SDL_haptics;
364     hapticlistprev = NULL;
365     while ( hapticlist )
366     {
367         if (haptic == hapticlist)
368         {
369             if ( hapticlistprev )
370             {
371                 /* unlink this entry */
372                 hapticlistprev->next = hapticlist->next;
373             }
374             else
375             {
376                 SDL_haptics = haptic->next;
377             }
378 
379             break;
380         }
381         hapticlistprev = hapticlist;
382         hapticlist = hapticlist->next;
383     }
384 
385     /* Free */
386     SDL_free(haptic);
387 }
388 
389 /*
390  * Cleans up after the subsystem.
391  */
392 void
SDL_HapticQuit(void)393 SDL_HapticQuit(void)
394 {
395     while (SDL_haptics) {
396         SDL_HapticClose(SDL_haptics);
397     }
398 
399     SDL_SYS_HapticQuit();
400 }
401 
402 /*
403  * Returns the number of effects a haptic device has.
404  */
405 int
SDL_HapticNumEffects(SDL_Haptic * haptic)406 SDL_HapticNumEffects(SDL_Haptic * haptic)
407 {
408     if (!ValidHaptic(haptic)) {
409         return -1;
410     }
411 
412     return haptic->neffects;
413 }
414 
415 
416 /*
417  * Returns the number of effects a haptic device can play.
418  */
419 int
SDL_HapticNumEffectsPlaying(SDL_Haptic * haptic)420 SDL_HapticNumEffectsPlaying(SDL_Haptic * haptic)
421 {
422     if (!ValidHaptic(haptic)) {
423         return -1;
424     }
425 
426     return haptic->nplaying;
427 }
428 
429 
430 /*
431  * Returns supported effects by the device.
432  */
433 unsigned int
SDL_HapticQuery(SDL_Haptic * haptic)434 SDL_HapticQuery(SDL_Haptic * haptic)
435 {
436     if (!ValidHaptic(haptic)) {
437         return 0; /* same as if no effects were supported */
438     }
439 
440     return haptic->supported;
441 }
442 
443 
444 /*
445  * Returns the number of axis on the device.
446  */
447 int
SDL_HapticNumAxes(SDL_Haptic * haptic)448 SDL_HapticNumAxes(SDL_Haptic * haptic)
449 {
450     if (!ValidHaptic(haptic)) {
451         return -1;
452     }
453 
454     return haptic->naxes;
455 }
456 
457 /*
458  * Checks to see if the device can support the effect.
459  */
460 int
SDL_HapticEffectSupported(SDL_Haptic * haptic,SDL_HapticEffect * effect)461 SDL_HapticEffectSupported(SDL_Haptic * haptic, SDL_HapticEffect * effect)
462 {
463     if (!ValidHaptic(haptic)) {
464         return -1;
465     }
466 
467     if ((haptic->supported & effect->type) != 0)
468         return SDL_TRUE;
469     return SDL_FALSE;
470 }
471 
472 /*
473  * Creates a new haptic effect.
474  */
475 int
SDL_HapticNewEffect(SDL_Haptic * haptic,SDL_HapticEffect * effect)476 SDL_HapticNewEffect(SDL_Haptic * haptic, SDL_HapticEffect * effect)
477 {
478     int i;
479 
480     /* Check for device validity. */
481     if (!ValidHaptic(haptic)) {
482         return -1;
483     }
484 
485     /* Check to see if effect is supported */
486     if (SDL_HapticEffectSupported(haptic, effect) == SDL_FALSE) {
487         return SDL_SetError("Haptic: Effect not supported by haptic device.");
488     }
489 
490     /* See if there's a free slot */
491     for (i = 0; i < haptic->neffects; i++) {
492         if (haptic->effects[i].hweffect == NULL) {
493 
494             /* Now let the backend create the real effect */
495             if (SDL_SYS_HapticNewEffect(haptic, &haptic->effects[i], effect)
496                 != 0) {
497                 return -1;      /* Backend failed to create effect */
498             }
499 
500             SDL_memcpy(&haptic->effects[i].effect, effect,
501                        sizeof(SDL_HapticEffect));
502             return i;
503         }
504     }
505 
506     return SDL_SetError("Haptic: Device has no free space left.");
507 }
508 
509 /*
510  * Checks to see if an effect is valid.
511  */
512 static int
ValidEffect(SDL_Haptic * haptic,int effect)513 ValidEffect(SDL_Haptic * haptic, int effect)
514 {
515     if ((effect < 0) || (effect >= haptic->neffects)) {
516         SDL_SetError("Haptic: Invalid effect identifier.");
517         return 0;
518     }
519     return 1;
520 }
521 
522 /*
523  * Updates an effect.
524  */
525 int
SDL_HapticUpdateEffect(SDL_Haptic * haptic,int effect,SDL_HapticEffect * data)526 SDL_HapticUpdateEffect(SDL_Haptic * haptic, int effect,
527                        SDL_HapticEffect * data)
528 {
529     if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
530         return -1;
531     }
532 
533     /* Can't change type dynamically. */
534     if (data->type != haptic->effects[effect].effect.type) {
535         return SDL_SetError("Haptic: Updating effect type is illegal.");
536     }
537 
538     /* Updates the effect */
539     if (SDL_SYS_HapticUpdateEffect(haptic, &haptic->effects[effect], data) <
540         0) {
541         return -1;
542     }
543 
544     SDL_memcpy(&haptic->effects[effect].effect, data,
545                sizeof(SDL_HapticEffect));
546     return 0;
547 }
548 
549 
550 /*
551  * Runs the haptic effect on the device.
552  */
553 int
SDL_HapticRunEffect(SDL_Haptic * haptic,int effect,Uint32 iterations)554 SDL_HapticRunEffect(SDL_Haptic * haptic, int effect, Uint32 iterations)
555 {
556     if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
557         return -1;
558     }
559 
560     /* Run the effect */
561     if (SDL_SYS_HapticRunEffect(haptic, &haptic->effects[effect], iterations)
562         < 0) {
563         return -1;
564     }
565 
566     return 0;
567 }
568 
569 /*
570  * Stops the haptic effect on the device.
571  */
572 int
SDL_HapticStopEffect(SDL_Haptic * haptic,int effect)573 SDL_HapticStopEffect(SDL_Haptic * haptic, int effect)
574 {
575     if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
576         return -1;
577     }
578 
579     /* Stop the effect */
580     if (SDL_SYS_HapticStopEffect(haptic, &haptic->effects[effect]) < 0) {
581         return -1;
582     }
583 
584     return 0;
585 }
586 
587 /*
588  * Gets rid of a haptic effect.
589  */
590 void
SDL_HapticDestroyEffect(SDL_Haptic * haptic,int effect)591 SDL_HapticDestroyEffect(SDL_Haptic * haptic, int effect)
592 {
593     if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
594         return;
595     }
596 
597     /* Not allocated */
598     if (haptic->effects[effect].hweffect == NULL) {
599         return;
600     }
601 
602     SDL_SYS_HapticDestroyEffect(haptic, &haptic->effects[effect]);
603 }
604 
605 /*
606  * Gets the status of a haptic effect.
607  */
608 int
SDL_HapticGetEffectStatus(SDL_Haptic * haptic,int effect)609 SDL_HapticGetEffectStatus(SDL_Haptic * haptic, int effect)
610 {
611     if (!ValidHaptic(haptic) || !ValidEffect(haptic, effect)) {
612         return -1;
613     }
614 
615     if ((haptic->supported & SDL_HAPTIC_STATUS) == 0) {
616         return SDL_SetError("Haptic: Device does not support status queries.");
617     }
618 
619     return SDL_SYS_HapticGetEffectStatus(haptic, &haptic->effects[effect]);
620 }
621 
622 /*
623  * Sets the global gain of the device.
624  */
625 int
SDL_HapticSetGain(SDL_Haptic * haptic,int gain)626 SDL_HapticSetGain(SDL_Haptic * haptic, int gain)
627 {
628     const char *env;
629     int real_gain, max_gain;
630 
631     if (!ValidHaptic(haptic)) {
632         return -1;
633     }
634 
635     if ((haptic->supported & SDL_HAPTIC_GAIN) == 0) {
636         return SDL_SetError("Haptic: Device does not support setting gain.");
637     }
638 
639     if ((gain < 0) || (gain > 100)) {
640         return SDL_SetError("Haptic: Gain must be between 0 and 100.");
641     }
642 
643     /* We use the envvar to get the maximum gain. */
644     env = SDL_getenv("SDL_HAPTIC_GAIN_MAX");
645     if (env != NULL) {
646         max_gain = SDL_atoi(env);
647 
648         /* Check for sanity. */
649         if (max_gain < 0)
650             max_gain = 0;
651         else if (max_gain > 100)
652             max_gain = 100;
653 
654         /* We'll scale it linearly with SDL_HAPTIC_GAIN_MAX */
655         real_gain = (gain * max_gain) / 100;
656     } else {
657         real_gain = gain;
658     }
659 
660     if (SDL_SYS_HapticSetGain(haptic, real_gain) < 0) {
661         return -1;
662     }
663 
664     return 0;
665 }
666 
667 /*
668  * Makes the device autocenter, 0 disables.
669  */
670 int
SDL_HapticSetAutocenter(SDL_Haptic * haptic,int autocenter)671 SDL_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
672 {
673     if (!ValidHaptic(haptic)) {
674         return -1;
675     }
676 
677     if ((haptic->supported & SDL_HAPTIC_AUTOCENTER) == 0) {
678         return SDL_SetError("Haptic: Device does not support setting autocenter.");
679     }
680 
681     if ((autocenter < 0) || (autocenter > 100)) {
682         return SDL_SetError("Haptic: Autocenter must be between 0 and 100.");
683     }
684 
685     if (SDL_SYS_HapticSetAutocenter(haptic, autocenter) < 0) {
686         return -1;
687     }
688 
689     return 0;
690 }
691 
692 /*
693  * Pauses the haptic device.
694  */
695 int
SDL_HapticPause(SDL_Haptic * haptic)696 SDL_HapticPause(SDL_Haptic * haptic)
697 {
698     if (!ValidHaptic(haptic)) {
699         return -1;
700     }
701 
702     if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) {
703         return SDL_SetError("Haptic: Device does not support setting pausing.");
704     }
705 
706     return SDL_SYS_HapticPause(haptic);
707 }
708 
709 /*
710  * Unpauses the haptic device.
711  */
712 int
SDL_HapticUnpause(SDL_Haptic * haptic)713 SDL_HapticUnpause(SDL_Haptic * haptic)
714 {
715     if (!ValidHaptic(haptic)) {
716         return -1;
717     }
718 
719     if ((haptic->supported & SDL_HAPTIC_PAUSE) == 0) {
720         return 0;               /* Not going to be paused, so we pretend it's unpaused. */
721     }
722 
723     return SDL_SYS_HapticUnpause(haptic);
724 }
725 
726 /*
727  * Stops all the currently playing effects.
728  */
729 int
SDL_HapticStopAll(SDL_Haptic * haptic)730 SDL_HapticStopAll(SDL_Haptic * haptic)
731 {
732     if (!ValidHaptic(haptic)) {
733         return -1;
734     }
735 
736     return SDL_SYS_HapticStopAll(haptic);
737 }
738 
739 /*
740  * Checks to see if rumble is supported.
741  */
742 int
SDL_HapticRumbleSupported(SDL_Haptic * haptic)743 SDL_HapticRumbleSupported(SDL_Haptic * haptic)
744 {
745     if (!ValidHaptic(haptic)) {
746         return -1;
747     }
748 
749     /* Most things can use SINE, but XInput only has LEFTRIGHT. */
750     return ((haptic->supported & (SDL_HAPTIC_SINE|SDL_HAPTIC_LEFTRIGHT)) != 0);
751 }
752 
753 /*
754  * Initializes the haptic device for simple rumble playback.
755  */
756 int
SDL_HapticRumbleInit(SDL_Haptic * haptic)757 SDL_HapticRumbleInit(SDL_Haptic * haptic)
758 {
759     SDL_HapticEffect *efx = &haptic->rumble_effect;
760 
761     if (!ValidHaptic(haptic)) {
762         return -1;
763     }
764 
765     /* Already allocated. */
766     if (haptic->rumble_id >= 0) {
767         return 0;
768     }
769 
770     SDL_zerop(efx);
771     if (haptic->supported & SDL_HAPTIC_SINE) {
772         efx->type = SDL_HAPTIC_SINE;
773         efx->periodic.direction.type = SDL_HAPTIC_CARTESIAN;
774         efx->periodic.period = 1000;
775         efx->periodic.magnitude = 0x4000;
776         efx->periodic.length = 5000;
777         efx->periodic.attack_length = 0;
778         efx->periodic.fade_length = 0;
779     } else if (haptic->supported & SDL_HAPTIC_LEFTRIGHT) {  /* XInput? */
780         efx->type = SDL_HAPTIC_LEFTRIGHT;
781         efx->leftright.length = 5000;
782         efx->leftright.large_magnitude = 0x4000;
783         efx->leftright.small_magnitude = 0x4000;
784     } else {
785         return SDL_SetError("Device doesn't support rumble");
786     }
787 
788     haptic->rumble_id = SDL_HapticNewEffect(haptic, &haptic->rumble_effect);
789     if (haptic->rumble_id >= 0) {
790         return 0;
791     }
792     return -1;
793 }
794 
795 /*
796  * Runs simple rumble on a haptic device
797  */
798 int
SDL_HapticRumblePlay(SDL_Haptic * haptic,float strength,Uint32 length)799 SDL_HapticRumblePlay(SDL_Haptic * haptic, float strength, Uint32 length)
800 {
801     SDL_HapticEffect *efx;
802     Sint16 magnitude;
803 
804     if (!ValidHaptic(haptic)) {
805         return -1;
806     }
807 
808     if (haptic->rumble_id < 0) {
809         return SDL_SetError("Haptic: Rumble effect not initialized on haptic device");
810     }
811 
812     /* Clamp strength. */
813     if (strength > 1.0f) {
814         strength = 1.0f;
815     } else if (strength < 0.0f) {
816         strength = 0.0f;
817     }
818     magnitude = (Sint16)(32767.0f*strength);
819 
820     efx = &haptic->rumble_effect;
821     if (efx->type == SDL_HAPTIC_SINE) {
822         efx->periodic.magnitude = magnitude;
823         efx->periodic.length = length;
824     } else if (efx->type == SDL_HAPTIC_LEFTRIGHT) {
825         efx->leftright.small_magnitude = efx->leftright.large_magnitude = magnitude;
826         efx->leftright.length = length;
827     } else {
828         SDL_assert(0 && "This should have been caught elsewhere");
829     }
830 
831     if (SDL_HapticUpdateEffect(haptic, haptic->rumble_id, &haptic->rumble_effect) < 0) {
832         return -1;
833     }
834 
835     return SDL_HapticRunEffect(haptic, haptic->rumble_id, 1);
836 }
837 
838 /*
839  * Stops the simple rumble on a haptic device.
840  */
841 int
SDL_HapticRumbleStop(SDL_Haptic * haptic)842 SDL_HapticRumbleStop(SDL_Haptic * haptic)
843 {
844     if (!ValidHaptic(haptic)) {
845         return -1;
846     }
847 
848     if (haptic->rumble_id < 0) {
849         return SDL_SetError("Haptic: Rumble effect not initialized on haptic device");
850     }
851 
852     return SDL_HapticStopEffect(haptic, haptic->rumble_id);
853 }
854 
855 /* vi: set ts=4 sw=4 expandtab: */
856