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#ifdef SDL_HAPTIC_LINUX
24
25#include "../SDL_syshaptic.h"
26#include "../../joystick/SDL_sysjoystick.h" // For the real SDL_Joystick
27#include "../../joystick/linux/SDL_sysjoystick_c.h" // For joystick hwdata
28#include "../../core/linux/SDL_evdev_capabilities.h"
29#include "../../core/linux/SDL_udev.h"
30
31#include <unistd.h> // close
32#include <linux/input.h> // Force feedback linux stuff.
33#include <fcntl.h> // O_RDWR
34#include <limits.h> // INT_MAX
35#include <errno.h> // errno
36#include <string.h> // strerror
37#include <sys/stat.h> // stat
38
39#define MAX_HAPTICS 32 // It's doubtful someone has more then 32 evdev
40
41static bool MaybeAddDevice(const char *path);
42#ifdef SDL_USE_LIBUDEV
43static bool MaybeRemoveDevice(const char *path);
44static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);
45#endif // SDL_USE_LIBUDEV
46
47/*
48 * List of available haptic devices.
49 */
50typedef struct SDL_hapticlist_item
51{
52 SDL_HapticID instance_id;
53 char *fname; // Dev path name (like /dev/input/event1)
54 SDL_Haptic *haptic; // Associated haptic.
55 dev_t dev_num;
56 struct SDL_hapticlist_item *next;
57} SDL_hapticlist_item;
58
59/*
60 * Haptic system hardware data.
61 */
62struct haptic_hwdata
63{
64 int fd; // File descriptor of the device.
65 char *fname; // Points to the name in SDL_hapticlist.
66};
67
68/*
69 * Haptic system effect data.
70 */
71struct haptic_hweffect
72{
73 struct ff_effect effect; // The linux kernel effect structure.
74};
75
76static SDL_hapticlist_item *SDL_hapticlist = NULL;
77static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
78static int numhaptics = 0;
79
80#define EV_TEST(ev, f) \
81 if (test_bit((ev), features)) { \
82 ret |= (f); \
83 }
84/*
85 * Test whether a device has haptic properties.
86 * Returns available properties or 0 if there are none.
87 */
88static Uint32 EV_IsHaptic(int fd)
89{
90 unsigned long features[1 + FF_MAX / sizeof(unsigned long)];
91 Uint32 ret = 0;
92
93 // Ask device for what it has.
94 if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(features)), features) < 0) {
95 SDL_SetError("Haptic: Unable to get device's features: %s", strerror(errno));
96 return 0;
97 }
98
99 // Convert supported features to SDL_HAPTIC platform-neutral features.
100 EV_TEST(FF_CONSTANT, SDL_HAPTIC_CONSTANT);
101 EV_TEST(FF_SINE, SDL_HAPTIC_SINE);
102 EV_TEST(FF_SQUARE, SDL_HAPTIC_SQUARE);
103 EV_TEST(FF_TRIANGLE, SDL_HAPTIC_TRIANGLE);
104 EV_TEST(FF_SAW_UP, SDL_HAPTIC_SAWTOOTHUP);
105 EV_TEST(FF_SAW_DOWN, SDL_HAPTIC_SAWTOOTHDOWN);
106 EV_TEST(FF_RAMP, SDL_HAPTIC_RAMP);
107 EV_TEST(FF_SPRING, SDL_HAPTIC_SPRING);
108 EV_TEST(FF_FRICTION, SDL_HAPTIC_FRICTION);
109 EV_TEST(FF_DAMPER, SDL_HAPTIC_DAMPER);
110 EV_TEST(FF_INERTIA, SDL_HAPTIC_INERTIA);
111 EV_TEST(FF_CUSTOM, SDL_HAPTIC_CUSTOM);
112 EV_TEST(FF_GAIN, SDL_HAPTIC_GAIN);
113 EV_TEST(FF_AUTOCENTER, SDL_HAPTIC_AUTOCENTER);
114 EV_TEST(FF_RUMBLE, SDL_HAPTIC_LEFTRIGHT);
115
116 // Return what it supports.
117 return ret;
118}
119
120/*
121 * Tests whether a device is a mouse or not.
122 */
123static bool EV_IsMouse(int fd)
124{
125 unsigned long argp[40];
126
127 // Ask for supported features.
128 if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(argp)), argp) < 0) {
129 return false;
130 }
131
132 // Currently we only test for BTN_MOUSE which can give fake positives.
133 if (test_bit(BTN_MOUSE, argp) != 0) {
134 return true;
135 }
136
137 return true;
138}
139
140/*
141 * Initializes the haptic subsystem by finding available devices.
142 */
143bool SDL_SYS_HapticInit(void)
144{
145 const char joydev_pattern[] = "/dev/input/event%d";
146 char path[PATH_MAX];
147 int i, j;
148
149 /*
150 * Limit amount of checks to MAX_HAPTICS since we may or may not have
151 * permission to some or all devices.
152 */
153 i = 0;
154 for (j = 0; j < MAX_HAPTICS; ++j) {
155 (void)SDL_snprintf(path, PATH_MAX, joydev_pattern, i++);
156 MaybeAddDevice(path);
157 }
158
159#ifdef SDL_USE_LIBUDEV
160 if (!SDL_UDEV_Init()) {
161 return SDL_SetError("Could not initialize UDEV");
162 }
163
164 if (!SDL_UDEV_AddCallback(haptic_udev_callback)) {
165 SDL_UDEV_Quit();
166 return SDL_SetError("Could not setup haptic <-> udev callback");
167 }
168
169 // Force a scan to build the initial device list
170 SDL_UDEV_Scan();
171#endif // SDL_USE_LIBUDEV
172
173 return true;
174}
175
176int SDL_SYS_NumHaptics(void)
177{
178 return numhaptics;
179}
180
181static SDL_hapticlist_item *HapticByDevIndex(int device_index)
182{
183 SDL_hapticlist_item *item = SDL_hapticlist;
184
185 if ((device_index < 0) || (device_index >= numhaptics)) {
186 return NULL;
187 }
188
189 while (device_index > 0) {
190 SDL_assert(item != NULL);
191 --device_index;
192 item = item->next;
193 }
194
195 return item;
196}
197
198static SDL_hapticlist_item *HapticByInstanceID(SDL_HapticID instance_id)
199{
200 SDL_hapticlist_item *item;
201 for (item = SDL_hapticlist; item; item = item->next) {
202 if (instance_id == item->instance_id) {
203 return item;
204 }
205 }
206 return NULL;
207}
208
209#ifdef SDL_USE_LIBUDEV
210static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
211{
212 if (!devpath || !(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) {
213 return;
214 }
215
216 switch (udev_type) {
217 case SDL_UDEV_DEVICEADDED:
218 MaybeAddDevice(devpath);
219 break;
220
221 case SDL_UDEV_DEVICEREMOVED:
222 MaybeRemoveDevice(devpath);
223 break;
224
225 default:
226 break;
227 }
228}
229#endif // SDL_USE_LIBUDEV
230
231static bool MaybeAddDevice(const char *path)
232{
233 struct stat sb;
234 int fd;
235 Uint32 supported;
236 SDL_hapticlist_item *item;
237
238 if (!path) {
239 return false;
240 }
241
242 // try to open
243 fd = open(path, O_RDWR | O_CLOEXEC, 0);
244 if (fd < 0) {
245 return false;
246 }
247
248 // get file status
249 if (fstat(fd, &sb) != 0) {
250 close(fd);
251 return false;
252 }
253
254 // check for duplicates
255 for (item = SDL_hapticlist; item; item = item->next) {
256 if (item->dev_num == sb.st_rdev) {
257 close(fd);
258 return false; // duplicate.
259 }
260 }
261
262#ifdef DEBUG_INPUT_EVENTS
263 printf("Checking %s\n", path);
264#endif
265
266 // see if it works
267 supported = EV_IsHaptic(fd);
268 close(fd);
269 if (!supported) {
270 return false;
271 }
272
273 item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item));
274 if (!item) {
275 return false;
276 }
277
278 item->instance_id = SDL_GetNextObjectID();
279 item->fname = SDL_strdup(path);
280 if (!item->fname) {
281 SDL_free(item);
282 return false;
283 }
284
285 item->dev_num = sb.st_rdev;
286
287 // TODO: should we add instance IDs?
288 if (!SDL_hapticlist_tail) {
289 SDL_hapticlist = SDL_hapticlist_tail = item;
290 } else {
291 SDL_hapticlist_tail->next = item;
292 SDL_hapticlist_tail = item;
293 }
294
295 ++numhaptics;
296
297 // !!! TODO: Send a haptic add event?
298
299 return true;
300}
301
302#ifdef SDL_USE_LIBUDEV
303static bool MaybeRemoveDevice(const char *path)
304{
305 SDL_hapticlist_item *item;
306 SDL_hapticlist_item *prev = NULL;
307
308 if (!path) {
309 return false;
310 }
311
312 for (item = SDL_hapticlist; item; item = item->next) {
313 // found it, remove it.
314 if (SDL_strcmp(path, item->fname) == 0) {
315 const bool result = item->haptic ? true : false;
316
317 if (prev) {
318 prev->next = item->next;
319 } else {
320 SDL_assert(SDL_hapticlist == item);
321 SDL_hapticlist = item->next;
322 }
323 if (item == SDL_hapticlist_tail) {
324 SDL_hapticlist_tail = prev;
325 }
326
327 // Need to decrement the haptic count
328 --numhaptics;
329 // !!! TODO: Send a haptic remove event?
330
331 SDL_free(item->fname);
332 SDL_free(item);
333 return result;
334 }
335 prev = item;
336 }
337
338 return false;
339}
340#endif // SDL_USE_LIBUDEV
341
342/*
343 * Return the instance ID of a haptic device, does not need to be opened.
344 */
345SDL_HapticID SDL_SYS_HapticInstanceID(int index)
346{
347 SDL_hapticlist_item *item;
348
349 item = HapticByDevIndex(index);
350 if (item) {
351 return item->instance_id;
352 }
353 return 0;
354}
355
356/*
357 * Gets the name from a file descriptor.
358 */
359static const char *SDL_SYS_HapticNameFromFD(int fd)
360{
361 static char namebuf[128];
362
363 // We use the evdev name ioctl.
364 if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) <= 0) {
365 return NULL;
366 }
367
368 return namebuf;
369}
370
371/*
372 * Return the name of a haptic device, does not need to be opened.
373 */
374const char *SDL_SYS_HapticName(int index)
375{
376 SDL_hapticlist_item *item;
377 int fd;
378 const char *name = NULL;
379
380 item = HapticByDevIndex(index);
381 if (item) {
382 // Open the haptic device.
383 fd = open(item->fname, O_RDONLY | O_CLOEXEC, 0);
384
385 if (fd >= 0) {
386
387 name = SDL_SYS_HapticNameFromFD(fd);
388 if (!name) {
389 // No name found, return device character device
390 name = item->fname;
391 }
392 close(fd);
393 }
394 }
395 return name;
396}
397
398/*
399 * Opens the haptic device from the file descriptor.
400 */
401static bool SDL_SYS_HapticOpenFromFD(SDL_Haptic *haptic, int fd)
402{
403 // Allocate the hwdata
404 haptic->hwdata = (struct haptic_hwdata *)
405 SDL_calloc(1, sizeof(*haptic->hwdata));
406 if (!haptic->hwdata) {
407 goto open_err;
408 }
409
410 // Set the data.
411 haptic->hwdata->fd = fd;
412 haptic->supported = EV_IsHaptic(fd);
413 haptic->naxes = 2; // Hardcoded for now, not sure if it's possible to find out.
414
415 // Set the effects
416 if (ioctl(fd, EVIOCGEFFECTS, &haptic->neffects) < 0) {
417 SDL_SetError("Haptic: Unable to query device memory: %s",
418 strerror(errno));
419 goto open_err;
420 }
421 haptic->nplaying = haptic->neffects; // Linux makes no distinction.
422 haptic->effects = (struct haptic_effect *)
423 SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
424 if (!haptic->effects) {
425 goto open_err;
426 }
427 // Clear the memory
428 SDL_memset(haptic->effects, 0,
429 sizeof(struct haptic_effect) * haptic->neffects);
430
431 return true;
432
433 // Error handling
434open_err:
435 close(fd);
436 if (haptic->hwdata) {
437 SDL_free(haptic->hwdata);
438 haptic->hwdata = NULL;
439 }
440 return false;
441}
442
443/*
444 * Opens a haptic device for usage.
445 */
446bool SDL_SYS_HapticOpen(SDL_Haptic *haptic)
447{
448 int fd;
449 SDL_hapticlist_item *item;
450
451 item = HapticByInstanceID(haptic->instance_id);
452 // Open the character device
453 fd = open(item->fname, O_RDWR | O_CLOEXEC, 0);
454 if (fd < 0) {
455 return SDL_SetError("Haptic: Unable to open %s: %s",
456 item->fname, strerror(errno));
457 }
458
459 // Try to create the haptic.
460 if (!SDL_SYS_HapticOpenFromFD(haptic, fd)) {
461 // Already closes on error.
462 return false;
463 }
464
465 // Set the fname.
466 haptic->hwdata->fname = SDL_strdup(item->fname);
467 return true;
468}
469
470/*
471 * Opens a haptic device from first mouse it finds for usage.
472 */
473int SDL_SYS_HapticMouse(void)
474{
475 int fd;
476 int device_index = 0;
477 SDL_hapticlist_item *item;
478
479 for (item = SDL_hapticlist; item; item = item->next) {
480 // Open the device.
481 fd = open(item->fname, O_RDWR | O_CLOEXEC, 0);
482 if (fd < 0) {
483 return SDL_SetError("Haptic: Unable to open %s: %s",
484 item->fname, strerror(errno));
485 }
486
487 // Is it a mouse?
488 if (EV_IsMouse(fd)) {
489 close(fd);
490 return device_index;
491 }
492
493 close(fd);
494
495 ++device_index;
496 }
497
498 return -1;
499}
500
501/*
502 * Checks to see if a joystick has haptic features.
503 */
504bool SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick)
505{
506#ifdef SDL_JOYSTICK_LINUX
507 SDL_AssertJoysticksLocked();
508
509 if (joystick->driver != &SDL_LINUX_JoystickDriver) {
510 return false;
511 }
512 if (EV_IsHaptic(joystick->hwdata->fd)) {
513 return true;
514 }
515#endif
516 return false;
517}
518
519/*
520 * Checks to see if the haptic device and joystick are in reality the same.
521 */
522bool SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick)
523{
524#ifdef SDL_JOYSTICK_LINUX
525 SDL_AssertJoysticksLocked();
526
527 if (joystick->driver != &SDL_LINUX_JoystickDriver) {
528 return false;
529 }
530 /* We are assuming Linux is using evdev which should trump the old
531 * joystick methods. */
532 if (SDL_strcmp(joystick->hwdata->fname, haptic->hwdata->fname) == 0) {
533 return true;
534 }
535#endif
536 return false;
537}
538
539/*
540 * Opens a SDL_Haptic from a SDL_Joystick.
541 */
542bool SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
543{
544#ifdef SDL_JOYSTICK_LINUX
545 int fd;
546 SDL_hapticlist_item *item;
547 const char *name;
548
549 SDL_AssertJoysticksLocked();
550
551 if (joystick->driver != &SDL_LINUX_JoystickDriver) {
552 return false;
553 }
554 // Find the joystick in the haptic list.
555 for (item = SDL_hapticlist; item; item = item->next) {
556 if (SDL_strcmp(item->fname, joystick->hwdata->fname) == 0) {
557 haptic->instance_id = item->instance_id;
558 break;
559 }
560 }
561
562 fd = open(joystick->hwdata->fname, O_RDWR | O_CLOEXEC, 0);
563 if (fd < 0) {
564 return SDL_SetError("Haptic: Unable to open %s: %s",
565 joystick->hwdata->fname, strerror(errno));
566 }
567 if (!SDL_SYS_HapticOpenFromFD(haptic, fd)) {
568 // Already closes on error.
569 return false;
570 }
571
572 haptic->hwdata->fname = SDL_strdup(joystick->hwdata->fname);
573
574 name = SDL_SYS_HapticNameFromFD(fd);
575 if (name) {
576 haptic->name = SDL_strdup(name);
577 }
578 return true;
579#else
580 return false;
581#endif
582}
583
584/*
585 * Closes the haptic device.
586 */
587void SDL_SYS_HapticClose(SDL_Haptic *haptic)
588{
589 if (haptic->hwdata) {
590
591 // Free effects.
592 SDL_free(haptic->effects);
593 haptic->effects = NULL;
594 haptic->neffects = 0;
595
596 // Clean up
597 close(haptic->hwdata->fd);
598
599 // Free
600 SDL_free(haptic->hwdata->fname);
601 SDL_free(haptic->hwdata);
602 haptic->hwdata = NULL;
603 }
604
605 // Clear the rest.
606 SDL_memset(haptic, 0, sizeof(SDL_Haptic));
607}
608
609/*
610 * Clean up after system specific haptic stuff
611 */
612void SDL_SYS_HapticQuit(void)
613{
614 SDL_hapticlist_item *item = NULL;
615 SDL_hapticlist_item *next = NULL;
616
617 for (item = SDL_hapticlist; item; item = next) {
618 next = item->next;
619 /* Opened and not closed haptics are leaked, this is on purpose.
620 * Close your haptic devices after usage. */
621 SDL_free(item->fname);
622 SDL_free(item);
623 }
624
625#ifdef SDL_USE_LIBUDEV
626 SDL_UDEV_DelCallback(haptic_udev_callback);
627 SDL_UDEV_Quit();
628#endif // SDL_USE_LIBUDEV
629
630 numhaptics = 0;
631 SDL_hapticlist = NULL;
632 SDL_hapticlist_tail = NULL;
633}
634
635/*
636 * Converts an SDL button to a ff_trigger button.
637 */
638static Uint16 SDL_SYS_ToButton(Uint16 button)
639{
640 Uint16 ff_button;
641
642 ff_button = 0;
643
644 /*
645 * Not sure what the proper syntax is because this actually isn't implemented
646 * in the current kernel from what I've seen (2.6.26).
647 */
648 if (button != 0) {
649 ff_button = BTN_GAMEPAD + button - 1;
650 }
651
652 return ff_button;
653}
654
655/*
656 * Initializes the ff_effect usable direction from a SDL_HapticDirection.
657 */
658static bool SDL_SYS_ToDirection(Uint16 *dest, const SDL_HapticDirection *src)
659{
660 Uint32 tmp;
661
662 switch (src->type) {
663 case SDL_HAPTIC_POLAR:
664 tmp = ((src->dir[0] % 36000) * 0x8000) / 18000; // convert to range [0,0xFFFF]
665 *dest = (Uint16)tmp;
666 break;
667
668 case SDL_HAPTIC_SPHERICAL:
669 /*
670 We convert to polar, because that's the only supported direction on Linux.
671 The first value of a spherical direction is practically the same as a
672 Polar direction, except that we have to add 90 degrees. It is the angle
673 from EAST {1,0} towards SOUTH {0,1}.
674 --> add 9000
675 --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
676 */
677 tmp = ((src->dir[0]) + 9000) % 36000; // Convert to polars
678 tmp = (tmp * 0x8000) / 18000; // convert to range [0,0xFFFF]
679 *dest = (Uint16)tmp;
680 break;
681
682 case SDL_HAPTIC_CARTESIAN:
683 if (!src->dir[1]) {
684 *dest = (src->dir[0] >= 0 ? 0x4000 : 0xC000);
685 } else if (!src->dir[0]) {
686 *dest = (src->dir[1] >= 0 ? 0x8000 : 0);
687 } else {
688 float f = SDL_atan2f(src->dir[1], src->dir[0]); // Ideally we'd use fixed point math instead of floats...
689 /*
690 SDL_atan2 takes the parameters: Y-axis-value and X-axis-value (in that order)
691 - Y-axis-value is the second coordinate (from center to SOUTH)
692 - X-axis-value is the first coordinate (from center to EAST)
693 We add 36000, because SDL_atan2 also returns negative values. Then we practically
694 have the first spherical value. Therefore we proceed as in case
695 SDL_HAPTIC_SPHERICAL and add another 9000 to get the polar value.
696 --> add 45000 in total
697 --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
698 */
699 tmp = (((Sint32)(f * 18000.0 / SDL_PI_D)) + 45000) % 36000;
700 tmp = (tmp * 0x8000) / 18000; // convert to range [0,0xFFFF]
701 *dest = (Uint16)tmp;
702 }
703 break;
704 case SDL_HAPTIC_STEERING_AXIS:
705 *dest = 0x4000;
706 break;
707 default:
708 return SDL_SetError("Haptic: Unsupported direction type.");
709 }
710
711 return true;
712}
713
714#define CLAMP(x) (((x) > 32767) ? 32767 : x)
715/*
716 * Initializes the Linux effect struct from a haptic_effect.
717 * Values above 32767 (for unsigned) are unspecified so we must clamp.
718 */
719static bool SDL_SYS_ToFFEffect(struct ff_effect *dest, const SDL_HapticEffect *src)
720{
721 const SDL_HapticConstant *constant;
722 const SDL_HapticPeriodic *periodic;
723 const SDL_HapticCondition *condition;
724 const SDL_HapticRamp *ramp;
725 const SDL_HapticLeftRight *leftright;
726
727 // Clear up
728 SDL_memset(dest, 0, sizeof(struct ff_effect));
729
730 switch (src->type) {
731 case SDL_HAPTIC_CONSTANT:
732 constant = &src->constant;
733
734 // Header
735 dest->type = FF_CONSTANT;
736 if (!SDL_SYS_ToDirection(&dest->direction, &constant->direction)) {
737 return false;
738 }
739
740 // Replay
741 dest->replay.length = (constant->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(constant->length);
742 dest->replay.delay = CLAMP(constant->delay);
743
744 // Trigger
745 dest->trigger.button = SDL_SYS_ToButton(constant->button);
746 dest->trigger.interval = CLAMP(constant->interval);
747
748 // Constant
749 dest->u.constant.level = constant->level;
750
751 // Envelope
752 dest->u.constant.envelope.attack_length =
753 CLAMP(constant->attack_length);
754 dest->u.constant.envelope.attack_level =
755 CLAMP(constant->attack_level);
756 dest->u.constant.envelope.fade_length = CLAMP(constant->fade_length);
757 dest->u.constant.envelope.fade_level = CLAMP(constant->fade_level);
758
759 break;
760
761 case SDL_HAPTIC_SINE:
762 case SDL_HAPTIC_SQUARE:
763 case SDL_HAPTIC_TRIANGLE:
764 case SDL_HAPTIC_SAWTOOTHUP:
765 case SDL_HAPTIC_SAWTOOTHDOWN:
766 periodic = &src->periodic;
767
768 // Header
769 dest->type = FF_PERIODIC;
770 if (!SDL_SYS_ToDirection(&dest->direction, &periodic->direction)) {
771 return false;
772 }
773
774 // Replay
775 dest->replay.length = (periodic->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(periodic->length);
776 dest->replay.delay = CLAMP(periodic->delay);
777
778 // Trigger
779 dest->trigger.button = SDL_SYS_ToButton(periodic->button);
780 dest->trigger.interval = CLAMP(periodic->interval);
781
782 // Periodic
783 if (periodic->type == SDL_HAPTIC_SINE) {
784 dest->u.periodic.waveform = FF_SINE;
785 } else if (periodic->type == SDL_HAPTIC_SQUARE) {
786 dest->u.periodic.waveform = FF_SQUARE;
787 } else if (periodic->type == SDL_HAPTIC_TRIANGLE) {
788 dest->u.periodic.waveform = FF_TRIANGLE;
789 } else if (periodic->type == SDL_HAPTIC_SAWTOOTHUP) {
790 dest->u.periodic.waveform = FF_SAW_UP;
791 } else if (periodic->type == SDL_HAPTIC_SAWTOOTHDOWN) {
792 dest->u.periodic.waveform = FF_SAW_DOWN;
793 }
794 dest->u.periodic.period = CLAMP(periodic->period);
795 dest->u.periodic.magnitude = periodic->magnitude;
796 dest->u.periodic.offset = periodic->offset;
797 // Linux phase is defined in interval "[0x0000, 0x10000[", corresponds with "[0deg, 360deg[" phase shift.
798 dest->u.periodic.phase = ((Uint32)periodic->phase * 0x10000U) / 36000;
799
800 // Envelope
801 dest->u.periodic.envelope.attack_length =
802 CLAMP(periodic->attack_length);
803 dest->u.periodic.envelope.attack_level =
804 CLAMP(periodic->attack_level);
805 dest->u.periodic.envelope.fade_length = CLAMP(periodic->fade_length);
806 dest->u.periodic.envelope.fade_level = CLAMP(periodic->fade_level);
807
808 break;
809
810 case SDL_HAPTIC_SPRING:
811 case SDL_HAPTIC_DAMPER:
812 case SDL_HAPTIC_INERTIA:
813 case SDL_HAPTIC_FRICTION:
814 condition = &src->condition;
815
816 // Header
817 if (condition->type == SDL_HAPTIC_SPRING) {
818 dest->type = FF_SPRING;
819 } else if (condition->type == SDL_HAPTIC_DAMPER) {
820 dest->type = FF_DAMPER;
821 } else if (condition->type == SDL_HAPTIC_INERTIA) {
822 dest->type = FF_INERTIA;
823 } else if (condition->type == SDL_HAPTIC_FRICTION) {
824 dest->type = FF_FRICTION;
825 }
826
827 if (!SDL_SYS_ToDirection(&dest->direction, &condition->direction)) {
828 return false;
829 }
830
831 // Replay
832 dest->replay.length = (condition->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(condition->length);
833 dest->replay.delay = CLAMP(condition->delay);
834
835 // Trigger
836 dest->trigger.button = SDL_SYS_ToButton(condition->button);
837 dest->trigger.interval = CLAMP(condition->interval);
838
839 // Condition
840 // X axis
841 dest->u.condition[0].right_saturation = condition->right_sat[0];
842 dest->u.condition[0].left_saturation = condition->left_sat[0];
843 dest->u.condition[0].right_coeff = condition->right_coeff[0];
844 dest->u.condition[0].left_coeff = condition->left_coeff[0];
845 dest->u.condition[0].deadband = condition->deadband[0];
846 dest->u.condition[0].center = condition->center[0];
847 // Y axis
848 dest->u.condition[1].right_saturation = condition->right_sat[1];
849 dest->u.condition[1].left_saturation = condition->left_sat[1];
850 dest->u.condition[1].right_coeff = condition->right_coeff[1];
851 dest->u.condition[1].left_coeff = condition->left_coeff[1];
852 dest->u.condition[1].deadband = condition->deadband[1];
853 dest->u.condition[1].center = condition->center[1];
854
855 /*
856 * There is no envelope in the linux force feedback api for conditions.
857 */
858
859 break;
860
861 case SDL_HAPTIC_RAMP:
862 ramp = &src->ramp;
863
864 // Header
865 dest->type = FF_RAMP;
866 if (!SDL_SYS_ToDirection(&dest->direction, &ramp->direction)) {
867 return false;
868 }
869
870 // Replay
871 dest->replay.length = (ramp->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(ramp->length);
872 dest->replay.delay = CLAMP(ramp->delay);
873
874 // Trigger
875 dest->trigger.button = SDL_SYS_ToButton(ramp->button);
876 dest->trigger.interval = CLAMP(ramp->interval);
877
878 // Ramp
879 dest->u.ramp.start_level = ramp->start;
880 dest->u.ramp.end_level = ramp->end;
881
882 // Envelope
883 dest->u.ramp.envelope.attack_length = CLAMP(ramp->attack_length);
884 dest->u.ramp.envelope.attack_level = CLAMP(ramp->attack_level);
885 dest->u.ramp.envelope.fade_length = CLAMP(ramp->fade_length);
886 dest->u.ramp.envelope.fade_level = CLAMP(ramp->fade_level);
887
888 break;
889
890 case SDL_HAPTIC_LEFTRIGHT:
891 leftright = &src->leftright;
892
893 // Header
894 dest->type = FF_RUMBLE;
895 dest->direction = 0x4000;
896
897 // Replay
898 dest->replay.length = (leftright->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(leftright->length);
899
900 // Trigger
901 dest->trigger.button = 0;
902 dest->trigger.interval = 0;
903
904 // Rumble (Linux expects 0-65535, so multiply by 2)
905 dest->u.rumble.strong_magnitude = CLAMP(leftright->large_magnitude) * 2;
906 dest->u.rumble.weak_magnitude = CLAMP(leftright->small_magnitude) * 2;
907
908 break;
909
910 default:
911 return SDL_SetError("Haptic: Unknown effect type.");
912 }
913
914 return true;
915}
916
917/*
918 * Creates a new haptic effect.
919 */
920bool SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect,
921 const SDL_HapticEffect *base)
922{
923 struct ff_effect *linux_effect;
924
925 // Allocate the hardware effect
926 effect->hweffect = (struct haptic_hweffect *)
927 SDL_calloc(1, sizeof(struct haptic_hweffect));
928 if (!effect->hweffect) {
929 return false;
930 }
931
932 // Prepare the ff_effect
933 linux_effect = &effect->hweffect->effect;
934 if (!SDL_SYS_ToFFEffect(linux_effect, base)) {
935 goto new_effect_err;
936 }
937 linux_effect->id = -1; // Have the kernel give it an id
938
939 // Upload the effect
940 if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) {
941 SDL_SetError("Haptic: Error uploading effect to the device: %s",
942 strerror(errno));
943 goto new_effect_err;
944 }
945
946 return true;
947
948new_effect_err:
949 SDL_free(effect->hweffect);
950 effect->hweffect = NULL;
951 return false;
952}
953
954/*
955 * Updates an effect.
956 *
957 * Note: Dynamically updating the direction can in some cases force
958 * the effect to restart and run once.
959 */
960bool SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic,
961 struct haptic_effect *effect,
962 const SDL_HapticEffect *data)
963{
964 struct ff_effect linux_effect;
965
966 // Create the new effect
967 if (!SDL_SYS_ToFFEffect(&linux_effect, data)) {
968 return false;
969 }
970 linux_effect.id = effect->hweffect->effect.id;
971
972 // See if it can be uploaded.
973 if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) {
974 return SDL_SetError("Haptic: Error updating the effect: %s",
975 strerror(errno));
976 }
977
978 // Copy the new effect into memory.
979 SDL_memcpy(&effect->hweffect->effect, &linux_effect,
980 sizeof(struct ff_effect));
981
982 return true;
983}
984
985/*
986 * Runs an effect.
987 */
988bool SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect,
989 Uint32 iterations)
990{
991 struct input_event run;
992
993 // Prepare to run the effect
994 run.type = EV_FF;
995 run.code = effect->hweffect->effect.id;
996 // We don't actually have infinity here, so we just do INT_MAX which is pretty damn close.
997 run.value = (iterations > INT_MAX) ? INT_MAX : iterations;
998
999 if (write(haptic->hwdata->fd, (const void *)&run, sizeof(run)) < 0) {
1000 return SDL_SetError("Haptic: Unable to run the effect: %s", strerror(errno));
1001 }
1002
1003 return true;
1004}
1005
1006/*
1007 * Stops an effect.
1008 */
1009bool SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
1010{
1011 struct input_event stop;
1012
1013 stop.type = EV_FF;
1014 stop.code = effect->hweffect->effect.id;
1015 stop.value = 0;
1016
1017 if (write(haptic->hwdata->fd, (const void *)&stop, sizeof(stop)) < 0) {
1018 return SDL_SetError("Haptic: Unable to stop the effect: %s",
1019 strerror(errno));
1020 }
1021
1022 return true;
1023}
1024
1025/*
1026 * Frees the effect.
1027 */
1028void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
1029{
1030 if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->hweffect->effect.id) < 0) {
1031 SDL_SetError("Haptic: Error removing the effect from the device: %s",
1032 strerror(errno));
1033 }
1034 SDL_free(effect->hweffect);
1035 effect->hweffect = NULL;
1036}
1037
1038/*
1039 * Gets the status of a haptic effect.
1040 */
1041int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic,
1042 struct haptic_effect *effect)
1043{
1044#if 0 // Not supported atm.
1045 struct input_event ie;
1046
1047 ie.type = EV_FF;
1048 ie.type = EV_FF_STATUS;
1049 ie.code = effect->hweffect->effect.id;
1050
1051 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1052 SDL_SetError("Haptic: Error getting device status.");
1053 return -1;
1054 }
1055
1056 return 1;
1057#endif
1058
1059 SDL_Unsupported();
1060 return -1;
1061}
1062
1063/*
1064 * Sets the gain.
1065 */
1066bool SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain)
1067{
1068 struct input_event ie;
1069
1070 ie.type = EV_FF;
1071 ie.code = FF_GAIN;
1072 ie.value = (0xFFFFUL * gain) / 100;
1073
1074 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1075 return SDL_SetError("Haptic: Error setting gain: %s", strerror(errno));
1076 }
1077
1078 return true;
1079}
1080
1081/*
1082 * Sets the autocentering.
1083 */
1084bool SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
1085{
1086 struct input_event ie;
1087
1088 ie.type = EV_FF;
1089 ie.code = FF_AUTOCENTER;
1090 ie.value = (0xFFFFUL * autocenter) / 100;
1091
1092 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1093 return SDL_SetError("Haptic: Error setting autocenter: %s", strerror(errno));
1094 }
1095
1096 return true;
1097}
1098
1099/*
1100 * Pausing is not supported atm by linux.
1101 */
1102bool SDL_SYS_HapticPause(SDL_Haptic *haptic)
1103{
1104 return SDL_Unsupported();
1105}
1106
1107/*
1108 * Unpausing is not supported atm by linux.
1109 */
1110bool SDL_SYS_HapticResume(SDL_Haptic *haptic)
1111{
1112 return SDL_Unsupported();
1113}
1114
1115/*
1116 * Stops all the currently playing effects.
1117 */
1118bool SDL_SYS_HapticStopAll(SDL_Haptic *haptic)
1119{
1120 int i, ret;
1121
1122 // Linux does not support this natively so we have to loop.
1123 for (i = 0; i < haptic->neffects; i++) {
1124 if (haptic->effects[i].hweffect != NULL) {
1125 ret = SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i]);
1126 if (ret < 0) {
1127 return SDL_SetError("Haptic: Error while trying to stop all playing effects.");
1128 }
1129 }
1130 }
1131 return true;
1132}
1133
1134#endif // SDL_HAPTIC_LINUX
1135