SDL 2.0
SDL_syshaptic.c
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 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
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;
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
86static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
87static 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 */
97static int
98EV_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 */
136static int
137EV_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 */
157int
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
192int
194{
195 return numhaptics;
196}
197
198static SDL_hapticlist_item *
199HapticByDevIndex(int device_index)
200{
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
217static 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
240static int
241MaybeAddDevice(const char *path)
242{
243 struct stat sb;
244 int fd;
245 int success;
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
310static int
311MaybeRemoveDevice(const char* path)
312{
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 */
353static const char *
354SDL_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 */
370const char *
372{
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 */
399static int
400SDL_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) {
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) {
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 */
449int
450SDL_SYS_HapticOpen(SDL_Haptic * haptic)
451{
452 int fd;
453 int ret;
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 */
479int
481{
482 int fd;
483 int device_index = 0;
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 */
512int
513SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
514{
515 return EV_IsHaptic(joystick->hwdata->fd);
516}
517
518
519/*
520 * Checks to see if the haptic device and joystick are in reality the same.
521 */
522int
523SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
524{
525 /* We are assuming Linux is using evdev which should trump the old
526 * joystick methods. */
527 if (SDL_strcmp(joystick->hwdata->fname, haptic->hwdata->fname) == 0) {
528 return 1;
529 }
530 return 0;
531}
532
533
534/*
535 * Opens a SDL_Haptic from a SDL_Joystick.
536 */
537int
538SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
539{
540 int device_index = 0;
541 int fd;
542 int ret;
544
545 /* Find the joystick in the haptic list. */
546 for (item = SDL_hapticlist; item; item = item->next) {
547 if (SDL_strcmp(item->fname, joystick->hwdata->fname) == 0) {
548 break;
549 }
550 ++device_index;
551 }
552 haptic->index = device_index;
553
554 if (device_index >= MAX_HAPTICS) {
555 return SDL_SetError("Haptic: Joystick doesn't have Haptic capabilities");
556 }
557
558 fd = open(joystick->hwdata->fname, O_RDWR, 0);
559 if (fd < 0) {
560 return SDL_SetError("Haptic: Unable to open %s: %s",
561 joystick->hwdata->fname, strerror(errno));
562 }
563 ret = SDL_SYS_HapticOpenFromFD(haptic, fd); /* Already closes on error. */
564 if (ret < 0) {
565 return -1;
566 }
567
568 haptic->hwdata->fname = SDL_strdup( joystick->hwdata->fname );
569
570 return 0;
571}
572
573
574/*
575 * Closes the haptic device.
576 */
577void
578SDL_SYS_HapticClose(SDL_Haptic * haptic)
579{
580 if (haptic->hwdata) {
581
582 /* Free effects. */
583 SDL_free(haptic->effects);
584 haptic->effects = NULL;
585 haptic->neffects = 0;
586
587 /* Clean up */
588 close(haptic->hwdata->fd);
589
590 /* Free */
591 SDL_free(haptic->hwdata->fname);
592 SDL_free(haptic->hwdata);
593 haptic->hwdata = NULL;
594 }
595
596 /* Clear the rest. */
597 SDL_memset(haptic, 0, sizeof(SDL_Haptic));
598}
599
600
601/*
602 * Clean up after system specific haptic stuff
603 */
604void
606{
609
610 for (item = SDL_hapticlist; item; item = next) {
611 next = item->next;
612 /* Opened and not closed haptics are leaked, this is on purpose.
613 * Close your haptic devices after usage. */
614 SDL_free(item->fname);
615 SDL_free(item);
616 }
617
618#if SDL_USE_LIBUDEV
619 SDL_UDEV_DelCallback(haptic_udev_callback);
620 SDL_UDEV_Quit();
621#endif /* SDL_USE_LIBUDEV */
622
623 numhaptics = 0;
625 SDL_hapticlist_tail = NULL;
626}
627
628
629/*
630 * Converts an SDL button to a ff_trigger button.
631 */
632static Uint16
633SDL_SYS_ToButton(Uint16 button)
634{
635 Uint16 ff_button;
636
637 ff_button = 0;
638
639 /*
640 * Not sure what the proper syntax is because this actually isn't implemented
641 * in the current kernel from what I've seen (2.6.26).
642 */
643 if (button != 0) {
644 ff_button = BTN_GAMEPAD + button - 1;
645 }
646
647 return ff_button;
648}
649
650
651/*
652 * Initializes the ff_effect usable direction from a SDL_HapticDirection.
653 */
654static int
655SDL_SYS_ToDirection(Uint16 *dest, SDL_HapticDirection * src)
656{
657 Uint32 tmp;
658
659 switch (src->type) {
660 case SDL_HAPTIC_POLAR:
661 /* Linux directions start from south.
662 (and range from 0 to 0xFFFF)
663 Quoting include/linux/input.h, line 926:
664 Direction of the effect is encoded as follows:
665 0 deg -> 0x0000 (down)
666 90 deg -> 0x4000 (left)
667 180 deg -> 0x8000 (up)
668 270 deg -> 0xC000 (right)
669 The force pulls into the direction specified by Linux directions,
670 i.e. the opposite convention of SDL directions.
671 */
672 tmp = ((src->dir[0] % 36000) * 0x8000) / 18000; /* convert to range [0,0xFFFF] */
673 *dest = (Uint16) tmp;
674 break;
675
677 /*
678 We convert to polar, because that's the only supported direction on Linux.
679 The first value of a spherical direction is practically the same as a
680 Polar direction, except that we have to add 90 degrees. It is the angle
681 from EAST {1,0} towards SOUTH {0,1}.
682 --> add 9000
683 --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
684 */
685 tmp = ((src->dir[0]) + 9000) % 36000; /* Convert to polars */
686 tmp = (tmp * 0x8000) / 18000; /* convert to range [0,0xFFFF] */
687 *dest = (Uint16) tmp;
688 break;
689
691 if (!src->dir[1])
692 *dest = (src->dir[0] >= 0 ? 0x4000 : 0xC000);
693 else if (!src->dir[0])
694 *dest = (src->dir[1] >= 0 ? 0x8000 : 0);
695 else {
696 float f = SDL_atan2(src->dir[1], src->dir[0]); /* Ideally we'd use fixed point math instead of floats... */
697 /*
698 atan2 takes the parameters: Y-axis-value and X-axis-value (in that order)
699 - Y-axis-value is the second coordinate (from center to SOUTH)
700 - X-axis-value is the first coordinate (from center to EAST)
701 We add 36000, because atan2 also returns negative values. Then we practically
702 have the first spherical value. Therefore we proceed as in case
703 SDL_HAPTIC_SPHERICAL and add another 9000 to get the polar value.
704 --> add 45000 in total
705 --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
706 */
707 tmp = (((Sint32) (f * 18000. / M_PI)) + 45000) % 36000;
708 tmp = (tmp * 0x8000) / 18000; /* convert to range [0,0xFFFF] */
709 *dest = (Uint16) tmp;
710 }
711 break;
712
713 default:
714 return SDL_SetError("Haptic: Unsupported direction type.");
715 }
716
717 return 0;
718}
719
720
721#define CLAMP(x) (((x) > 32767) ? 32767 : x)
722/*
723 * Initializes the Linux effect struct from a haptic_effect.
724 * Values above 32767 (for unsigned) are unspecified so we must clamp.
725 */
726static int
727SDL_SYS_ToFFEffect(struct ff_effect *dest, SDL_HapticEffect * src)
728{
729 SDL_HapticConstant *constant;
730 SDL_HapticPeriodic *periodic;
732 SDL_HapticRamp *ramp;
733 SDL_HapticLeftRight *leftright;
734
735 /* Clear up */
736 SDL_memset(dest, 0, sizeof(struct ff_effect));
737
738 switch (src->type) {
740 constant = &src->constant;
741
742 /* Header */
743 dest->type = FF_CONSTANT;
744 if (SDL_SYS_ToDirection(&dest->direction, &constant->direction) == -1)
745 return -1;
746
747 /* Replay */
748 dest->replay.length = (constant->length == SDL_HAPTIC_INFINITY) ?
749 0 : CLAMP(constant->length);
750 dest->replay.delay = CLAMP(constant->delay);
751
752 /* Trigger */
753 dest->trigger.button = SDL_SYS_ToButton(constant->button);
754 dest->trigger.interval = CLAMP(constant->interval);
755
756 /* Constant */
757 dest->u.constant.level = constant->level;
758
759 /* Envelope */
760 dest->u.constant.envelope.attack_length =
761 CLAMP(constant->attack_length);
762 dest->u.constant.envelope.attack_level =
763 CLAMP(constant->attack_level);
764 dest->u.constant.envelope.fade_length = CLAMP(constant->fade_length);
765 dest->u.constant.envelope.fade_level = CLAMP(constant->fade_level);
766
767 break;
768
769 case SDL_HAPTIC_SINE:
770 /* !!! FIXME: put this back when we have more bits in 2.1 */
771 /* case SDL_HAPTIC_SQUARE: */
775 periodic = &src->periodic;
776
777 /* Header */
778 dest->type = FF_PERIODIC;
779 if (SDL_SYS_ToDirection(&dest->direction, &periodic->direction) == -1)
780 return -1;
781
782 /* Replay */
783 dest->replay.length = (periodic->length == SDL_HAPTIC_INFINITY) ?
784 0 : CLAMP(periodic->length);
785 dest->replay.delay = CLAMP(periodic->delay);
786
787 /* Trigger */
788 dest->trigger.button = SDL_SYS_ToButton(periodic->button);
789 dest->trigger.interval = CLAMP(periodic->interval);
790
791 /* Periodic */
792 if (periodic->type == SDL_HAPTIC_SINE)
793 dest->u.periodic.waveform = FF_SINE;
794 /* !!! FIXME: put this back when we have more bits in 2.1 */
795 /* else if (periodic->type == SDL_HAPTIC_SQUARE)
796 dest->u.periodic.waveform = FF_SQUARE; */
797 else if (periodic->type == SDL_HAPTIC_TRIANGLE)
798 dest->u.periodic.waveform = FF_TRIANGLE;
799 else if (periodic->type == SDL_HAPTIC_SAWTOOTHUP)
800 dest->u.periodic.waveform = FF_SAW_UP;
801 else if (periodic->type == SDL_HAPTIC_SAWTOOTHDOWN)
802 dest->u.periodic.waveform = FF_SAW_DOWN;
803 dest->u.periodic.period = CLAMP(periodic->period);
804 dest->u.periodic.magnitude = periodic->magnitude;
805 dest->u.periodic.offset = periodic->offset;
806 /* Linux phase is defined in interval "[0x0000, 0x10000[", corresponds with "[0deg, 360deg[" phase shift. */
807 dest->u.periodic.phase = ((Uint32)periodic->phase * 0x10000U) / 36000;
808
809 /* Envelope */
810 dest->u.periodic.envelope.attack_length =
811 CLAMP(periodic->attack_length);
812 dest->u.periodic.envelope.attack_level =
813 CLAMP(periodic->attack_level);
814 dest->u.periodic.envelope.fade_length = CLAMP(periodic->fade_length);
815 dest->u.periodic.envelope.fade_level = CLAMP(periodic->fade_level);
816
817 break;
818
823 condition = &src->condition;
824
825 /* Header */
826 if (condition->type == SDL_HAPTIC_SPRING)
827 dest->type = FF_SPRING;
828 else if (condition->type == SDL_HAPTIC_DAMPER)
829 dest->type = FF_DAMPER;
830 else if (condition->type == SDL_HAPTIC_INERTIA)
831 dest->type = FF_INERTIA;
832 else if (condition->type == SDL_HAPTIC_FRICTION)
833 dest->type = FF_FRICTION;
834 dest->direction = 0; /* Handled by the condition-specifics. */
835
836 /* Replay */
837 dest->replay.length = (condition->length == SDL_HAPTIC_INFINITY) ?
838 0 : CLAMP(condition->length);
839 dest->replay.delay = CLAMP(condition->delay);
840
841 /* Trigger */
842 dest->trigger.button = SDL_SYS_ToButton(condition->button);
843 dest->trigger.interval = CLAMP(condition->interval);
844
845 /* Condition */
846 /* X axis */
847 dest->u.condition[0].right_saturation = condition->right_sat[0];
848 dest->u.condition[0].left_saturation = condition->left_sat[0];
849 dest->u.condition[0].right_coeff = condition->right_coeff[0];
850 dest->u.condition[0].left_coeff = condition->left_coeff[0];
851 dest->u.condition[0].deadband = condition->deadband[0];
852 dest->u.condition[0].center = condition->center[0];
853 /* Y axis */
854 dest->u.condition[1].right_saturation = condition->right_sat[1];
855 dest->u.condition[1].left_saturation = condition->left_sat[1];
856 dest->u.condition[1].right_coeff = condition->right_coeff[1];
857 dest->u.condition[1].left_coeff = condition->left_coeff[1];
858 dest->u.condition[1].deadband = condition->deadband[1];
859 dest->u.condition[1].center = condition->center[1];
860
861 /*
862 * There is no envelope in the linux force feedback api for conditions.
863 */
864
865 break;
866
867 case SDL_HAPTIC_RAMP:
868 ramp = &src->ramp;
869
870 /* Header */
871 dest->type = FF_RAMP;
872 if (SDL_SYS_ToDirection(&dest->direction, &ramp->direction) == -1)
873 return -1;
874
875 /* Replay */
876 dest->replay.length = (ramp->length == SDL_HAPTIC_INFINITY) ?
877 0 : CLAMP(ramp->length);
878 dest->replay.delay = CLAMP(ramp->delay);
879
880 /* Trigger */
881 dest->trigger.button = SDL_SYS_ToButton(ramp->button);
882 dest->trigger.interval = CLAMP(ramp->interval);
883
884 /* Ramp */
885 dest->u.ramp.start_level = ramp->start;
886 dest->u.ramp.end_level = ramp->end;
887
888 /* Envelope */
889 dest->u.ramp.envelope.attack_length = CLAMP(ramp->attack_length);
890 dest->u.ramp.envelope.attack_level = CLAMP(ramp->attack_level);
891 dest->u.ramp.envelope.fade_length = CLAMP(ramp->fade_length);
892 dest->u.ramp.envelope.fade_level = CLAMP(ramp->fade_level);
893
894 break;
895
897 leftright = &src->leftright;
898
899 /* Header */
900 dest->type = FF_RUMBLE;
901 dest->direction = 0;
902
903 /* Replay */
904 dest->replay.length = (leftright->length == SDL_HAPTIC_INFINITY) ?
905 0 : CLAMP(leftright->length);
906
907 /* Trigger */
908 dest->trigger.button = 0;
909 dest->trigger.interval = 0;
910
911 /* Rumble (Linux expects 0-65535, so multiply by 2) */
912 dest->u.rumble.strong_magnitude = CLAMP(leftright->large_magnitude) * 2;
913 dest->u.rumble.weak_magnitude = CLAMP(leftright->small_magnitude) * 2;
914
915 break;
916
917
918 default:
919 return SDL_SetError("Haptic: Unknown effect type.");
920 }
921
922 return 0;
923}
924
925
926/*
927 * Creates a new haptic effect.
928 */
929int
931 SDL_HapticEffect * base)
932{
933 struct ff_effect *linux_effect;
934
935 /* Allocate the hardware effect */
936 effect->hweffect = (struct haptic_hweffect *)
937 SDL_malloc(sizeof(struct haptic_hweffect));
938 if (effect->hweffect == NULL) {
939 return SDL_OutOfMemory();
940 }
941
942 /* Prepare the ff_effect */
943 linux_effect = &effect->hweffect->effect;
944 if (SDL_SYS_ToFFEffect(linux_effect, base) != 0) {
945 goto new_effect_err;
946 }
947 linux_effect->id = -1; /* Have the kernel give it an id */
948
949 /* Upload the effect */
950 if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) {
951 SDL_SetError("Haptic: Error uploading effect to the device: %s",
952 strerror(errno));
953 goto new_effect_err;
954 }
955
956 return 0;
957
958 new_effect_err:
959 SDL_free(effect->hweffect);
960 effect->hweffect = NULL;
961 return -1;
962}
963
964
965/*
966 * Updates an effect.
967 *
968 * Note: Dynamically updating the direction can in some cases force
969 * the effect to restart and run once.
970 */
971int
973 struct haptic_effect *effect,
975{
976 struct ff_effect linux_effect;
977
978 /* Create the new effect */
979 if (SDL_SYS_ToFFEffect(&linux_effect, data) != 0) {
980 return -1;
981 }
982 linux_effect.id = effect->hweffect->effect.id;
983
984 /* See if it can be uploaded. */
985 if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) {
986 return SDL_SetError("Haptic: Error updating the effect: %s",
987 strerror(errno));
988 }
989
990 /* Copy the new effect into memory. */
991 SDL_memcpy(&effect->hweffect->effect, &linux_effect,
992 sizeof(struct ff_effect));
993
994 return effect->hweffect->effect.id;
995}
996
997
998/*
999 * Runs an effect.
1000 */
1001int
1002SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1004{
1005 struct input_event run;
1006
1007 /* Prepare to run the effect */
1008 run.type = EV_FF;
1009 run.code = effect->hweffect->effect.id;
1010 /* We don't actually have infinity here, so we just do INT_MAX which is pretty damn close. */
1011 run.value = (iterations > INT_MAX) ? INT_MAX : iterations;
1012
1013 if (write(haptic->hwdata->fd, (const void *) &run, sizeof(run)) < 0) {
1014 return SDL_SetError("Haptic: Unable to run the effect: %s", strerror(errno));
1015 }
1016
1017 return 0;
1018}
1019
1020
1021/*
1022 * Stops an effect.
1023 */
1024int
1025SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1026{
1027 struct input_event stop;
1028
1029 stop.type = EV_FF;
1030 stop.code = effect->hweffect->effect.id;
1031 stop.value = 0;
1032
1033 if (write(haptic->hwdata->fd, (const void *) &stop, sizeof(stop)) < 0) {
1034 return SDL_SetError("Haptic: Unable to stop the effect: %s",
1035 strerror(errno));
1036 }
1037
1038 return 0;
1039}
1040
1041
1042/*
1043 * Frees the effect.
1044 */
1045void
1046SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1047{
1048 if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->hweffect->effect.id) < 0) {
1049 SDL_SetError("Haptic: Error removing the effect from the device: %s",
1050 strerror(errno));
1051 }
1052 SDL_free(effect->hweffect);
1053 effect->hweffect = NULL;
1054}
1055
1056
1057/*
1058 * Gets the status of a haptic effect.
1059 */
1060int
1062 struct haptic_effect *effect)
1063{
1064#if 0 /* Not supported atm. */
1065 struct input_event ie;
1066
1067 ie.type = EV_FF;
1068 ie.type = EV_FF_STATUS;
1069 ie.code = effect->hweffect->effect.id;
1070
1071 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1072 return SDL_SetError("Haptic: Error getting device status.");
1073 }
1074
1075 return 0;
1076#endif
1077
1078 return -1;
1079}
1080
1081
1082/*
1083 * Sets the gain.
1084 */
1085int
1086SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
1087{
1088 struct input_event ie;
1089
1090 ie.type = EV_FF;
1091 ie.code = FF_GAIN;
1092 ie.value = (0xFFFFUL * gain) / 100;
1093
1094 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1095 return SDL_SetError("Haptic: Error setting gain: %s", strerror(errno));
1096 }
1097
1098 return 0;
1099}
1100
1101
1102/*
1103 * Sets the autocentering.
1104 */
1105int
1106SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
1107{
1108 struct input_event ie;
1109
1110 ie.type = EV_FF;
1111 ie.code = FF_AUTOCENTER;
1112 ie.value = (0xFFFFUL * autocenter) / 100;
1113
1114 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1115 return SDL_SetError("Haptic: Error setting autocenter: %s", strerror(errno));
1116 }
1117
1118 return 0;
1119}
1120
1121
1122/*
1123 * Pausing is not supported atm by linux.
1124 */
1125int
1126SDL_SYS_HapticPause(SDL_Haptic * haptic)
1127{
1128 return -1;
1129}
1130
1131
1132/*
1133 * Unpausing is not supported atm by linux.
1134 */
1135int
1136SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
1137{
1138 return -1;
1139}
1140
1141
1142/*
1143 * Stops all the currently playing effects.
1144 */
1145int
1146SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
1147{
1148 int i, ret;
1149
1150 /* Linux does not support this natively so we have to loop. */
1151 for (i = 0; i < haptic->neffects; i++) {
1152 if (haptic->effects[i].hweffect != NULL) {
1153 ret = SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i]);
1154 if (ret < 0) {
1155 return SDL_SetError
1156 ("Haptic: Error while trying to stop all playing effects.");
1157 }
1158 }
1159 }
1160 return 0;
1161}
1162
1163#endif /* SDL_HAPTIC_LINUX */
1164
1165/* vi: set ts=4 sw=4 expandtab: */
#define SDL_assert(condition)
Definition: SDL_assert.h:169
#define SDL_atan2
#define SDL_SetError
#define SDL_memset
#define SDL_malloc
#define SDL_free
#define SDL_strdup
#define SDL_strcmp
#define SDL_memcpy
#define SDL_calloc
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
The SDL haptic subsystem allows you to control haptic (force feedback) devices.
#define SDL_HAPTIC_INERTIA
Inertia effect supported - uses axes acceleration.
Definition: SDL_haptic.h:252
#define SDL_HAPTIC_SPHERICAL
Uses spherical coordinates for the direction.
Definition: SDL_haptic.h:337
#define SDL_HAPTIC_AUTOCENTER
Device can set autocenter.
Definition: SDL_haptic.h:291
#define SDL_HAPTIC_GAIN
Device can set global gain.
Definition: SDL_haptic.h:282
#define SDL_HAPTIC_SPRING
Spring effect supported - uses axes position.
Definition: SDL_haptic.h:232
#define SDL_HAPTIC_INFINITY
Used to play a device an infinite number of times.
Definition: SDL_haptic.h:352
#define SDL_HAPTIC_DAMPER
Damper effect supported - uses axes velocity.
Definition: SDL_haptic.h:242
#define SDL_HAPTIC_CUSTOM
Custom effect is supported.
Definition: SDL_haptic.h:269
#define SDL_HAPTIC_CONSTANT
Constant effect supported.
Definition: SDL_haptic.h:163
#define SDL_HAPTIC_FRICTION
Friction effect supported - uses axes movement.
Definition: SDL_haptic.h:262
#define SDL_HAPTIC_SINE
Sine wave effect supported.
Definition: SDL_haptic.h:172
#define SDL_HAPTIC_SAWTOOTHUP
Sawtoothup wave effect supported.
Definition: SDL_haptic.h:204
#define SDL_HAPTIC_POLAR
Uses polar coordinates for the direction.
Definition: SDL_haptic.h:323
#define SDL_HAPTIC_LEFTRIGHT
Left/Right effect supported.
Definition: SDL_haptic.h:183
#define SDL_HAPTIC_TRIANGLE
Triangle wave effect supported.
Definition: SDL_haptic.h:195
#define SDL_HAPTIC_RAMP
Ramp effect supported.
Definition: SDL_haptic.h:222
#define SDL_HAPTIC_CARTESIAN
Uses cartesian coordinates for the direction.
Definition: SDL_haptic.h:330
#define SDL_HAPTIC_SAWTOOTHDOWN
Sawtoothdown wave effect supported.
Definition: SDL_haptic.h:213
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLfloat f
GLenum src
GLenum condition
GLuint index
GLuint const GLchar * name
GLsizei const GLchar *const * path
uint32_t Uint32
Definition: SDL_stdinc.h:203
int32_t Sint32
Definition: SDL_stdinc.h:197
uint16_t Uint16
Definition: SDL_stdinc.h:191
void SDL_SYS_HapticClose(SDL_Haptic *haptic)
int SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
int SDL_SYS_HapticPause(SDL_Haptic *haptic)
int SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain)
void SDL_SYS_HapticQuit(void)
int SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick)
int SDL_SYS_HapticMouse(void)
int SDL_SYS_HapticUnpause(SDL_Haptic *haptic)
int SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
int SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic, struct haptic_effect *effect, SDL_HapticEffect *data)
int SDL_SYS_HapticStopAll(SDL_Haptic *haptic)
int SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect, SDL_HapticEffect *base)
int SDL_SYS_NumHaptics(void)
const char * SDL_SYS_HapticName(int index)
void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
int SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, Uint32 iterations)
int SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
int SDL_SYS_HapticOpen(SDL_Haptic *haptic)
int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic, struct haptic_effect *effect)
int SDL_SYS_HapticInit(void)
int SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick)
#define INT_MAX
Definition: SDL_wave.c:31
SDL_hapticlist_item * SDL_hapticlist
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int int in j)
Definition: SDL_x11sym.h:50
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:167
GLuint64 GLenum GLint fd
Definition: gl2ext.h:1508
A structure containing a template for a Condition effect.
Definition: SDL_haptic.h:603
A structure containing a template for a Constant effect.
Definition: SDL_haptic.h:469
SDL_HapticDirection direction
Definition: SDL_haptic.h:472
Structure that represents a haptic direction.
Definition: SDL_haptic.h:451
A structure containing a template for a Left/Right effect.
Definition: SDL_haptic.h:677
A structure containing a template for a Periodic effect.
Definition: SDL_haptic.h:550
SDL_HapticDirection direction
Definition: SDL_haptic.h:555
A structure containing a template for a Ramp effect.
Definition: SDL_haptic.h:640
Uint16 interval
Definition: SDL_haptic.h:651
Uint16 fade_level
Definition: SDL_haptic.h:661
SDL_HapticDirection direction
Definition: SDL_haptic.h:643
Uint16 attack_level
Definition: SDL_haptic.h:659
Uint16 fade_length
Definition: SDL_haptic.h:660
Uint16 attack_length
Definition: SDL_haptic.h:658
struct SDL_hapticlist_item * next
SDL_HapticEffect effect
Definition: SDL_syshaptic.h:32
struct haptic_hweffect * hweffect
Definition: SDL_syshaptic.h:33
SDL_Texture * button
SDL_bool retval
static SDL_Haptic * haptic
Definition: testhaptic.c:25
static int iterations
Definition: testsprite2.c:45
The generic template for any haptic effect.
Definition: SDL_haptic.h:801