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