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 
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;
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 
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
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
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
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
193 SDL_SYS_NumHaptics(void)
194 {
195  return numhaptics;
196 }
197 
198 static SDL_hapticlist_item *
199 HapticByDevIndex(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
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
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
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 *
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 *
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
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
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
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
513 SDL_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  */
522 int
523 SDL_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  */
537 int
538 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
539 {
540  int device_index = 0;
541  int fd;
542  int ret;
543  SDL_hapticlist_item *item;
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  */
577 void
578 SDL_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  */
604 void
605 SDL_SYS_HapticQuit(void)
606 {
607  SDL_hapticlist_item *item = NULL;
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;
624  SDL_hapticlist = NULL;
625  SDL_hapticlist_tail = NULL;
626 }
627 
628 
629 /*
630  * Converts an SDL button to a ff_trigger button.
631  */
632 static Uint16
633 SDL_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  */
654 static int
655 SDL_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  */
726 static int
727 SDL_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) {
739  case SDL_HAPTIC_CONSTANT:
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: */
772  case SDL_HAPTIC_TRIANGLE:
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 
819  case SDL_HAPTIC_SPRING:
820  case SDL_HAPTIC_DAMPER:
821  case SDL_HAPTIC_INERTIA:
822  case SDL_HAPTIC_FRICTION:
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  */
929 int
930 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
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  */
971 int
972 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
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  */
1001 int
1002 SDL_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  */
1024 int
1025 SDL_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  */
1045 void
1046 SDL_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  */
1060 int
1061 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
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  */
1085 int
1086 SDL_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  */
1105 int
1106 SDL_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  */
1125 int
1126 SDL_SYS_HapticPause(SDL_Haptic * haptic)
1127 {
1128  return -1;
1129 }
1130 
1131 
1132 /*
1133  * Unpausing is not supported atm by linux.
1134  */
1135 int
1136 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
1137 {
1138  return -1;
1139 }
1140 
1141 
1142 /*
1143  * Stops all the currently playing effects.
1144  */
1145 int
1146 SDL_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: */
int SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
Uint16 deadband[3]
Definition: SDL_haptic.h:622
Structure that represents a haptic direction.
Definition: SDL_haptic.h:450
#define SDL_HAPTIC_SPHERICAL
Uses spherical coordinates for the direction.
Definition: SDL_haptic.h:337
SDL_Texture * button
#define SDL_HAPTIC_AUTOCENTER
Device can set autocenter.
Definition: SDL_haptic.h:291
A structure containing a template for a Periodic effect.
Definition: SDL_haptic.h:549
#define SDL_HAPTIC_GAIN
Device can set global gain.
Definition: SDL_haptic.h:282
#define SDL_HAPTIC_CUSTOM
Custom effect is supported.
Definition: SDL_haptic.h:269
#define SDL_HAPTIC_TRIANGLE
Triangle wave effect supported.
Definition: SDL_haptic.h:195
int SDL_SYS_HapticOpen(SDL_Haptic *haptic)
int SDL_SYS_HapticMouse(void)
int SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick)
#define SDL_atan2
#define SDL_HAPTIC_INERTIA
Inertia effect supported - uses axes acceleration.
Definition: SDL_haptic.h:252
Uint16 fade_level
Definition: SDL_haptic.h:661
SDL_HapticRamp ramp
Definition: SDL_haptic.h:807
uint16_t Uint16
Definition: SDL_stdinc.h:191
int SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
const char * SDL_SYS_HapticName(int index)
GLfloat f
A structure containing a template for a Condition effect.
Definition: SDL_haptic.h:602
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
The SDL haptic subsystem allows you to control haptic (force feedback) devices.
int SDL_SYS_HapticUnpause(SDL_Haptic *haptic)
static int iterations
Definition: testsprite2.c:45
Sint16 left_coeff[3]
Definition: SDL_haptic.h:621
Uint16 right_sat[3]
Definition: SDL_haptic.h:618
Sint16 right_coeff[3]
Definition: SDL_haptic.h:620
GLenum src
int SDL_SYS_NumHaptics(void)
#define SDL_HAPTIC_SINE
Sine wave effect supported.
Definition: SDL_haptic.h:172
int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic, struct haptic_effect *effect)
A structure containing a template for a Constant effect.
Definition: SDL_haptic.h:468
#define SDL_HAPTIC_INFINITY
Used to play a device an infinite number of times.
Definition: SDL_haptic.h:352
Uint16 interval
Definition: SDL_haptic.h:651
#define SDL_HAPTIC_CARTESIAN
Uses cartesian coordinates for the direction.
Definition: SDL_haptic.h:330
SDL_hapticlist_item * SDL_hapticlist
struct SDL_hapticlist_item * next
SDL_HapticCondition condition
Definition: SDL_haptic.h:806
A structure containing a template for a Left/Right effect.
Definition: SDL_haptic.h:676
SDL_bool retval
#define SDL_memcpy
The generic template for any haptic effect.
Definition: SDL_haptic.h:800
#define SDL_HAPTIC_CONSTANT
Constant effect supported.
Definition: SDL_haptic.h:163
int SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
#define SDL_free
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
#define SDL_HAPTIC_POLAR
Uses polar coordinates for the direction.
Definition: SDL_haptic.h:323
SDL_HapticConstant constant
Definition: SDL_haptic.h:804
int SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic, struct haptic_effect *effect, SDL_HapticEffect *data)
int SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick)
SDL_HapticDirection direction
Definition: SDL_haptic.h:643
int32_t Sint32
Definition: SDL_stdinc.h:197
A structure containing a template for a Ramp effect.
Definition: SDL_haptic.h:639
GLuint index
void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
Uint16 attack_length
Definition: SDL_haptic.h:658
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 SDL_assert(condition)
Definition: SDL_assert.h:169
Uint16 fade_length
Definition: SDL_haptic.h:660
#define NULL
Definition: begin_code.h:167
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_HapticEffect effect
Definition: SDL_syshaptic.h:32
int SDL_SYS_HapticInit(void)
void SDL_SYS_HapticQuit(void)
struct haptic_hweffect * hweffect
Definition: SDL_syshaptic.h:33
#define SDL_SetError
void SDL_SYS_HapticClose(SDL_Haptic *haptic)
int SDL_SYS_HapticPause(SDL_Haptic *haptic)
#define SDL_calloc
SDL_HapticDirection direction
Definition: SDL_haptic.h:555
SDL_HapticLeftRight leftright
Definition: SDL_haptic.h:808
#define SDL_HAPTIC_RAMP
Ramp effect supported.
Definition: SDL_haptic.h:222
#define SDL_strdup
#define SDL_HAPTIC_SPRING
Spring effect supported - uses axes position.
Definition: SDL_haptic.h:232
uint32_t Uint32
Definition: SDL_stdinc.h:203
GLenum condition
int SDL_SYS_HapticStopAll(SDL_Haptic *haptic)
#define SDL_malloc
#define SDL_HAPTIC_SAWTOOTHUP
Sawtoothup wave effect supported.
Definition: SDL_haptic.h:204
GLsizei const GLchar *const * path
#define SDL_strcmp
int SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain)
#define SDL_HAPTIC_LEFTRIGHT
Left/Right effect supported.
Definition: SDL_haptic.h:183
int SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, Uint32 iterations)
SDL_HapticPeriodic periodic
Definition: SDL_haptic.h:805
Uint16 attack_level
Definition: SDL_haptic.h:659
#define SDL_HAPTIC_FRICTION
Friction effect supported - uses axes movement.
Definition: SDL_haptic.h:262
#define SDL_HAPTIC_SAWTOOTHDOWN
Sawtoothdown wave effect supported.
Definition: SDL_haptic.h:213
#define INT_MAX
Definition: SDL_wave.c:31
GLuint64 GLenum GLint fd
Definition: gl2ext.h:1508
#define SDL_memset
SDL_HapticDirection direction
Definition: SDL_haptic.h:472
int SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect, SDL_HapticEffect *base)
#define SDL_HAPTIC_DAMPER
Damper effect supported - uses axes velocity.
Definition: SDL_haptic.h:242
Uint16 left_sat[3]
Definition: SDL_haptic.h:619