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