1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2024 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 dest->direction = 0; // Handled by the condition-specifics.
828
829 // Replay
830 dest->replay.length = (condition->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(condition->length);
831 dest->replay.delay = CLAMP(condition->delay);
832
833 // Trigger
834 dest->trigger.button = SDL_SYS_ToButton(condition->button);
835 dest->trigger.interval = CLAMP(condition->interval);
836
837 // Condition
838 // X axis
839 dest->u.condition[0].right_saturation = condition->right_sat[0];
840 dest->u.condition[0].left_saturation = condition->left_sat[0];
841 dest->u.condition[0].right_coeff = condition->right_coeff[0];
842 dest->u.condition[0].left_coeff = condition->left_coeff[0];
843 dest->u.condition[0].deadband = condition->deadband[0];
844 dest->u.condition[0].center = condition->center[0];
845 // Y axis
846 dest->u.condition[1].right_saturation = condition->right_sat[1];
847 dest->u.condition[1].left_saturation = condition->left_sat[1];
848 dest->u.condition[1].right_coeff = condition->right_coeff[1];
849 dest->u.condition[1].left_coeff = condition->left_coeff[1];
850 dest->u.condition[1].deadband = condition->deadband[1];
851 dest->u.condition[1].center = condition->center[1];
852
853 /*
854 * There is no envelope in the linux force feedback api for conditions.
855 */
856
857 break;
858
859 case SDL_HAPTIC_RAMP:
860 ramp = &src->ramp;
861
862 // Header
863 dest->type = FF_RAMP;
864 if (!SDL_SYS_ToDirection(&dest->direction, &ramp->direction)) {
865 return false;
866 }
867
868 // Replay
869 dest->replay.length = (ramp->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(ramp->length);
870 dest->replay.delay = CLAMP(ramp->delay);
871
872 // Trigger
873 dest->trigger.button = SDL_SYS_ToButton(ramp->button);
874 dest->trigger.interval = CLAMP(ramp->interval);
875
876 // Ramp
877 dest->u.ramp.start_level = ramp->start;
878 dest->u.ramp.end_level = ramp->end;
879
880 // Envelope
881 dest->u.ramp.envelope.attack_length = CLAMP(ramp->attack_length);
882 dest->u.ramp.envelope.attack_level = CLAMP(ramp->attack_level);
883 dest->u.ramp.envelope.fade_length = CLAMP(ramp->fade_length);
884 dest->u.ramp.envelope.fade_level = CLAMP(ramp->fade_level);
885
886 break;
887
888 case SDL_HAPTIC_LEFTRIGHT:
889 leftright = &src->leftright;
890
891 // Header
892 dest->type = FF_RUMBLE;
893 dest->direction = 0;
894
895 // Replay
896 dest->replay.length = (leftright->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(leftright->length);
897
898 // Trigger
899 dest->trigger.button = 0;
900 dest->trigger.interval = 0;
901
902 // Rumble (Linux expects 0-65535, so multiply by 2)
903 dest->u.rumble.strong_magnitude = CLAMP(leftright->large_magnitude) * 2;
904 dest->u.rumble.weak_magnitude = CLAMP(leftright->small_magnitude) * 2;
905
906 break;
907
908 default:
909 return SDL_SetError("Haptic: Unknown effect type.");
910 }
911
912 return true;
913}
914
915/*
916 * Creates a new haptic effect.
917 */
918bool SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect,
919 const SDL_HapticEffect *base)
920{
921 struct ff_effect *linux_effect;
922
923 // Allocate the hardware effect
924 effect->hweffect = (struct haptic_hweffect *)
925 SDL_calloc(1, sizeof(struct haptic_hweffect));
926 if (!effect->hweffect) {
927 return false;
928 }
929
930 // Prepare the ff_effect
931 linux_effect = &effect->hweffect->effect;
932 if (!SDL_SYS_ToFFEffect(linux_effect, base)) {
933 goto new_effect_err;
934 }
935 linux_effect->id = -1; // Have the kernel give it an id
936
937 // Upload the effect
938 if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) {
939 SDL_SetError("Haptic: Error uploading effect to the device: %s",
940 strerror(errno));
941 goto new_effect_err;
942 }
943
944 return true;
945
946new_effect_err:
947 SDL_free(effect->hweffect);
948 effect->hweffect = NULL;
949 return false;
950}
951
952/*
953 * Updates an effect.
954 *
955 * Note: Dynamically updating the direction can in some cases force
956 * the effect to restart and run once.
957 */
958bool SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic,
959 struct haptic_effect *effect,
960 const SDL_HapticEffect *data)
961{
962 struct ff_effect linux_effect;
963
964 // Create the new effect
965 if (!SDL_SYS_ToFFEffect(&linux_effect, data)) {
966 return false;
967 }
968 linux_effect.id = effect->hweffect->effect.id;
969
970 // See if it can be uploaded.
971 if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) {
972 return SDL_SetError("Haptic: Error updating the effect: %s",
973 strerror(errno));
974 }
975
976 // Copy the new effect into memory.
977 SDL_memcpy(&effect->hweffect->effect, &linux_effect,
978 sizeof(struct ff_effect));
979
980 return true;
981}
982
983/*
984 * Runs an effect.
985 */
986bool SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect,
987 Uint32 iterations)
988{
989 struct input_event run;
990
991 // Prepare to run the effect
992 run.type = EV_FF;
993 run.code = effect->hweffect->effect.id;
994 // We don't actually have infinity here, so we just do INT_MAX which is pretty damn close.
995 run.value = (iterations > INT_MAX) ? INT_MAX : iterations;
996
997 if (write(haptic->hwdata->fd, (const void *)&run, sizeof(run)) < 0) {
998 return SDL_SetError("Haptic: Unable to run the effect: %s", strerror(errno));
999 }
1000
1001 return true;
1002}
1003
1004/*
1005 * Stops an effect.
1006 */
1007bool SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
1008{
1009 struct input_event stop;
1010
1011 stop.type = EV_FF;
1012 stop.code = effect->hweffect->effect.id;
1013 stop.value = 0;
1014
1015 if (write(haptic->hwdata->fd, (const void *)&stop, sizeof(stop)) < 0) {
1016 return SDL_SetError("Haptic: Unable to stop the effect: %s",
1017 strerror(errno));
1018 }
1019
1020 return true;
1021}
1022
1023/*
1024 * Frees the effect.
1025 */
1026void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
1027{
1028 if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->hweffect->effect.id) < 0) {
1029 SDL_SetError("Haptic: Error removing the effect from the device: %s",
1030 strerror(errno));
1031 }
1032 SDL_free(effect->hweffect);
1033 effect->hweffect = NULL;
1034}
1035
1036/*
1037 * Gets the status of a haptic effect.
1038 */
1039int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic,
1040 struct haptic_effect *effect)
1041{
1042#if 0 // Not supported atm.
1043 struct input_event ie;
1044
1045 ie.type = EV_FF;
1046 ie.type = EV_FF_STATUS;
1047 ie.code = effect->hweffect->effect.id;
1048
1049 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1050 SDL_SetError("Haptic: Error getting device status.");
1051 return -1;
1052 }
1053
1054 return 1;
1055#endif
1056
1057 SDL_Unsupported();
1058 return -1;
1059}
1060
1061/*
1062 * Sets the gain.
1063 */
1064bool SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain)
1065{
1066 struct input_event ie;
1067
1068 ie.type = EV_FF;
1069 ie.code = FF_GAIN;
1070 ie.value = (0xFFFFUL * gain) / 100;
1071
1072 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1073 return SDL_SetError("Haptic: Error setting gain: %s", strerror(errno));
1074 }
1075
1076 return true;
1077}
1078
1079/*
1080 * Sets the autocentering.
1081 */
1082bool SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
1083{
1084 struct input_event ie;
1085
1086 ie.type = EV_FF;
1087 ie.code = FF_AUTOCENTER;
1088 ie.value = (0xFFFFUL * autocenter) / 100;
1089
1090 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1091 return SDL_SetError("Haptic: Error setting autocenter: %s", strerror(errno));
1092 }
1093
1094 return true;
1095}
1096
1097/*
1098 * Pausing is not supported atm by linux.
1099 */
1100bool SDL_SYS_HapticPause(SDL_Haptic *haptic)
1101{
1102 return SDL_Unsupported();
1103}
1104
1105/*
1106 * Unpausing is not supported atm by linux.
1107 */
1108bool SDL_SYS_HapticResume(SDL_Haptic *haptic)
1109{
1110 return SDL_Unsupported();
1111}
1112
1113/*
1114 * Stops all the currently playing effects.
1115 */
1116bool SDL_SYS_HapticStopAll(SDL_Haptic *haptic)
1117{
1118 int i, ret;
1119
1120 // Linux does not support this natively so we have to loop.
1121 for (i = 0; i < haptic->neffects; i++) {
1122 if (haptic->effects[i].hweffect != NULL) {
1123 ret = SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i]);
1124 if (ret < 0) {
1125 return SDL_SetError("Haptic: Error while trying to stop all playing effects.");
1126 }
1127 }
1128 }
1129 return true;
1130}
1131
1132#endif // SDL_HAPTIC_LINUX