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