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