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