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_IOKIT
24 
25 #include "SDL_assert.h"
26 #include "SDL_stdinc.h"
27 #include "SDL_haptic.h"
28 #include "../SDL_syshaptic.h"
29 #include "SDL_joystick.h"
30 #include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */
31 #include "../../joystick/darwin/SDL_sysjoystick_c.h" /* For joystick hwdata */
32 #include "SDL_syshaptic_c.h"
33 
34 #include <IOKit/IOKitLib.h>
35 #include <IOKit/hid/IOHIDKeys.h>
36 #include <IOKit/hid/IOHIDUsageTables.h>
37 #include <ForceFeedback/ForceFeedback.h>
38 #include <ForceFeedback/ForceFeedbackConstants.h>
39 
40 #ifndef IO_OBJECT_NULL
41 #define IO_OBJECT_NULL ((io_service_t)0)
42 #endif
43 
44 /*
45  * List of available haptic devices.
46  */
47 typedef struct SDL_hapticlist_item
48 {
49  char name[256]; /* Name of the device. */
50 
51  io_service_t dev; /* Node we use to create the device. */
52  SDL_Haptic *haptic; /* Haptic currently associated with it. */
53 
54  /* Usage pages for determining if it's a mouse or not. */
55  long usage;
56  long usagePage;
57 
58  struct SDL_hapticlist_item *next;
60 
61 
62 /*
63  * Haptic system hardware data.
64  */
65 struct haptic_hwdata
66 {
67  FFDeviceObjectReference device; /* Hardware device. */
68  UInt8 axes[3];
69 };
70 
71 
72 /*
73  * Haptic system effect data.
74  */
75 struct haptic_hweffect
76 {
77  FFEffectObjectReference ref; /* Reference. */
78  struct FFEFFECT effect; /* Hardware effect. */
79 };
80 
81 /*
82  * Prototypes.
83  */
84 static void SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type);
85 static int HIDGetDeviceProduct(io_service_t dev, char *name);
86 
88 static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
89 static int numhaptics = -1;
90 
91 /*
92  * Like strerror but for force feedback errors.
93  */
94 static const char *
95 FFStrError(unsigned int err)
96 {
97  switch (err) {
98  case FFERR_DEVICEFULL:
99  return "device full";
100  /* This should be valid, but for some reason isn't defined... */
101  /* case FFERR_DEVICENOTREG:
102  return "device not registered"; */
103  case FFERR_DEVICEPAUSED:
104  return "device paused";
105  case FFERR_DEVICERELEASED:
106  return "device released";
107  case FFERR_EFFECTPLAYING:
108  return "effect playing";
109  case FFERR_EFFECTTYPEMISMATCH:
110  return "effect type mismatch";
111  case FFERR_EFFECTTYPENOTSUPPORTED:
112  return "effect type not supported";
113  case FFERR_GENERIC:
114  return "undetermined error";
115  case FFERR_HASEFFECTS:
116  return "device has effects";
117  case FFERR_INCOMPLETEEFFECT:
118  return "incomplete effect";
119  case FFERR_INTERNAL:
120  return "internal fault";
121  case FFERR_INVALIDDOWNLOADID:
122  return "invalid download id";
123  case FFERR_INVALIDPARAM:
124  return "invalid parameter";
125  case FFERR_MOREDATA:
126  return "more data";
127  case FFERR_NOINTERFACE:
128  return "interface not supported";
129  case FFERR_NOTDOWNLOADED:
130  return "effect is not downloaded";
131  case FFERR_NOTINITIALIZED:
132  return "object has not been initialized";
133  case FFERR_OUTOFMEMORY:
134  return "out of memory";
135  case FFERR_UNPLUGGED:
136  return "device is unplugged";
137  case FFERR_UNSUPPORTED:
138  return "function call unsupported";
139  case FFERR_UNSUPPORTEDAXIS:
140  return "axis unsupported";
141 
142  default:
143  return "unknown error";
144  }
145 }
146 
147 
148 /*
149  * Initializes the haptic subsystem.
150  */
151 int
152 SDL_SYS_HapticInit(void)
153 {
154  IOReturn result;
155  io_iterator_t iter;
156  CFDictionaryRef match;
157  io_service_t device;
158 
159  if (numhaptics != -1) {
160  return SDL_SetError("Haptic subsystem already initialized!");
161  }
162  numhaptics = 0;
163 
164  /* Get HID devices. */
165  match = IOServiceMatching(kIOHIDDeviceKey);
166  if (match == NULL) {
167  return SDL_SetError("Haptic: Failed to get IOServiceMatching.");
168  }
169 
170  /* Now search I/O Registry for matching devices. */
171  result = IOServiceGetMatchingServices(kIOMasterPortDefault, match, &iter);
172  if (result != kIOReturnSuccess) {
173  return SDL_SetError("Haptic: Couldn't create a HID object iterator.");
174  }
175  /* IOServiceGetMatchingServices consumes dictionary. */
176 
177  if (!IOIteratorIsValid(iter)) { /* No iterator. */
178  return 0;
179  }
180 
181  while ((device = IOIteratorNext(iter)) != IO_OBJECT_NULL) {
182  MacHaptic_MaybeAddDevice(device);
183  /* always release as the AddDevice will retain IF it's a forcefeedback device */
184  IOObjectRelease(device);
185  }
186  IOObjectRelease(iter);
187 
188  return numhaptics;
189 }
190 
191 int
192 SDL_SYS_NumHaptics(void)
193 {
194  return numhaptics;
195 }
196 
197 static SDL_hapticlist_item *
198 HapticByDevIndex(int device_index)
199 {
201 
202  if ((device_index < 0) || (device_index >= numhaptics)) {
203  return NULL;
204  }
205 
206  while (device_index > 0) {
207  SDL_assert(item != NULL);
208  --device_index;
209  item = item->next;
210  }
211 
212  return item;
213 }
214 
215 int
216 MacHaptic_MaybeAddDevice( io_object_t device )
217 {
218  IOReturn result;
219  CFMutableDictionaryRef hidProperties;
220  CFTypeRef refCF;
221  SDL_hapticlist_item *item;
222 
223  if (numhaptics == -1) {
224  return -1; /* not initialized. We'll pick these up on enumeration if we init later. */
225  }
226 
227  /* Check for force feedback. */
228  if (FFIsForceFeedback(device) != FF_OK) {
229  return -1;
230  }
231 
232  /* Make sure we don't already have it */
233  for (item = SDL_hapticlist; item ; item = item->next)
234  {
235  if (IOObjectIsEqualTo((io_object_t) item->dev, device)) {
236  /* Already added */
237  return -1;
238  }
239  }
240 
241  item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item));
242  if (item == NULL) {
243  return SDL_SetError("Could not allocate haptic storage");
244  }
245 
246  /* retain it as we are going to keep it around a while */
247  IOObjectRetain(device);
248 
249  /* Set basic device data. */
250  HIDGetDeviceProduct(device, item->name);
251  item->dev = device;
252 
253  /* Set usage pages. */
254  hidProperties = 0;
255  refCF = 0;
256  result = IORegistryEntryCreateCFProperties(device,
257  &hidProperties,
258  kCFAllocatorDefault,
259  kNilOptions);
260  if ((result == KERN_SUCCESS) && hidProperties) {
261  refCF = CFDictionaryGetValue(hidProperties,
262  CFSTR(kIOHIDPrimaryUsagePageKey));
263  if (refCF) {
264  if (!CFNumberGetValue(refCF, kCFNumberLongType, &item->usagePage)) {
265  SDL_SetError("Haptic: Receiving device's usage page.");
266  }
267  refCF = CFDictionaryGetValue(hidProperties,
268  CFSTR(kIOHIDPrimaryUsageKey));
269  if (refCF) {
270  if (!CFNumberGetValue(refCF, kCFNumberLongType, &item->usage)) {
271  SDL_SetError("Haptic: Receiving device's usage.");
272  }
273  }
274  }
275  CFRelease(hidProperties);
276  }
277 
278  if (SDL_hapticlist_tail == NULL) {
279  SDL_hapticlist = SDL_hapticlist_tail = item;
280  } else {
281  SDL_hapticlist_tail->next = item;
282  SDL_hapticlist_tail = item;
283  }
284 
285  /* Device has been added. */
286  ++numhaptics;
287 
288  return numhaptics;
289 }
290 
291 int
292 MacHaptic_MaybeRemoveDevice( io_object_t device )
293 {
294  SDL_hapticlist_item *item;
295  SDL_hapticlist_item *prev = NULL;
296 
297  if (numhaptics == -1) {
298  return -1; /* not initialized. ignore this. */
299  }
300 
301  for (item = SDL_hapticlist; item != NULL; item = item->next) {
302  /* found it, remove it. */
303  if (IOObjectIsEqualTo((io_object_t) item->dev, device)) {
304  const int retval = item->haptic ? item->haptic->index : -1;
305 
306  if (prev != NULL) {
307  prev->next = item->next;
308  } else {
309  SDL_assert(SDL_hapticlist == item);
310  SDL_hapticlist = item->next;
311  }
312  if (item == SDL_hapticlist_tail) {
313  SDL_hapticlist_tail = prev;
314  }
315 
316  /* Need to decrement the haptic count */
317  --numhaptics;
318  /* !!! TODO: Send a haptic remove event? */
319 
320  IOObjectRelease(item->dev);
321  SDL_free(item);
322  return retval;
323  }
324  prev = item;
325  }
326 
327  return -1;
328 }
329 
330 /*
331  * Return the name of a haptic device, does not need to be opened.
332  */
333 const char *
335 {
336  SDL_hapticlist_item *item;
337  item = HapticByDevIndex(index);
338  return item->name;
339 }
340 
341 /*
342  * Gets the device's product name.
343  */
344 static int
345 HIDGetDeviceProduct(io_service_t dev, char *name)
346 {
347  CFMutableDictionaryRef hidProperties, usbProperties;
348  io_registry_entry_t parent1, parent2;
349  kern_return_t ret;
350 
351  hidProperties = usbProperties = 0;
352 
353  ret = IORegistryEntryCreateCFProperties(dev, &hidProperties,
354  kCFAllocatorDefault, kNilOptions);
355  if ((ret != KERN_SUCCESS) || !hidProperties) {
356  return SDL_SetError("Haptic: Unable to create CFProperties.");
357  }
358 
359  /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
360  * get dictionary for USB properties: step up two levels and get CF dictionary for USB properties
361  */
362  if ((KERN_SUCCESS ==
363  IORegistryEntryGetParentEntry(dev, kIOServicePlane, &parent1))
364  && (KERN_SUCCESS ==
365  IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2))
366  && (KERN_SUCCESS ==
367  IORegistryEntryCreateCFProperties(parent2, &usbProperties,
368  kCFAllocatorDefault,
369  kNilOptions))) {
370  if (usbProperties) {
371  CFTypeRef refCF = 0;
372  /* get device info
373  * try hid dictionary first, if fail then go to USB dictionary
374  */
375 
376 
377  /* Get product name */
378  refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey));
379  if (!refCF) {
380  refCF = CFDictionaryGetValue(usbProperties,
381  CFSTR("USB Product Name"));
382  }
383  if (refCF) {
384  if (!CFStringGetCString(refCF, name, 256,
385  CFStringGetSystemEncoding())) {
386  return SDL_SetError("Haptic: CFStringGetCString error retrieving pDevice->product.");
387  }
388  }
389 
390  CFRelease(usbProperties);
391  } else {
392  return SDL_SetError("Haptic: IORegistryEntryCreateCFProperties failed to create usbProperties.");
393  }
394 
395  /* Release stuff. */
396  if (kIOReturnSuccess != IOObjectRelease(parent2)) {
397  SDL_SetError("Haptic: IOObjectRelease error with parent2.");
398  }
399  if (kIOReturnSuccess != IOObjectRelease(parent1)) {
400  SDL_SetError("Haptic: IOObjectRelease error with parent1.");
401  }
402  } else {
403  return SDL_SetError("Haptic: Error getting registry entries.");
404  }
405 
406  return 0;
407 }
408 
409 
410 #define FF_TEST(ff, s) \
411 if (features.supportedEffects & (ff)) supported |= (s)
412 /*
413  * Gets supported features.
414  */
415 static unsigned int
416 GetSupportedFeatures(SDL_Haptic * haptic)
417 {
418  HRESULT ret;
419  FFDeviceObjectReference device;
420  FFCAPABILITIES features;
421  unsigned int supported;
422  Uint32 val;
423 
424  device = haptic->hwdata->device;
425 
426  ret = FFDeviceGetForceFeedbackCapabilities(device, &features);
427  if (ret != FF_OK) {
428  return SDL_SetError("Haptic: Unable to get device's supported features.");
429  }
430 
431  supported = 0;
432 
433  /* Get maximum effects. */
434  haptic->neffects = features.storageCapacity;
435  haptic->nplaying = features.playbackCapacity;
436 
437  /* Test for effects. */
438  FF_TEST(FFCAP_ET_CONSTANTFORCE, SDL_HAPTIC_CONSTANT);
439  FF_TEST(FFCAP_ET_RAMPFORCE, SDL_HAPTIC_RAMP);
440  /* !!! FIXME: put this back when we have more bits in 2.1 */
441  /* FF_TEST(FFCAP_ET_SQUARE, SDL_HAPTIC_SQUARE); */
442  FF_TEST(FFCAP_ET_SINE, SDL_HAPTIC_SINE);
443  FF_TEST(FFCAP_ET_TRIANGLE, SDL_HAPTIC_TRIANGLE);
444  FF_TEST(FFCAP_ET_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHUP);
445  FF_TEST(FFCAP_ET_SAWTOOTHDOWN, SDL_HAPTIC_SAWTOOTHDOWN);
446  FF_TEST(FFCAP_ET_SPRING, SDL_HAPTIC_SPRING);
447  FF_TEST(FFCAP_ET_DAMPER, SDL_HAPTIC_DAMPER);
448  FF_TEST(FFCAP_ET_INERTIA, SDL_HAPTIC_INERTIA);
449  FF_TEST(FFCAP_ET_FRICTION, SDL_HAPTIC_FRICTION);
450  FF_TEST(FFCAP_ET_CUSTOMFORCE, SDL_HAPTIC_CUSTOM);
451 
452  /* Check if supports gain. */
453  ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_FFGAIN,
454  &val, sizeof(val));
455  if (ret == FF_OK) {
456  supported |= SDL_HAPTIC_GAIN;
457  } else if (ret != FFERR_UNSUPPORTED) {
458  return SDL_SetError("Haptic: Unable to get if device supports gain: %s.",
459  FFStrError(ret));
460  }
461 
462  /* Checks if supports autocenter. */
463  ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_AUTOCENTER,
464  &val, sizeof(val));
465  if (ret == FF_OK) {
466  supported |= SDL_HAPTIC_AUTOCENTER;
467  } else if (ret != FFERR_UNSUPPORTED) {
468  return SDL_SetError
469  ("Haptic: Unable to get if device supports autocenter: %s.",
470  FFStrError(ret));
471  }
472 
473  /* Check for axes, we have an artificial limit on axes */
474  haptic->naxes = ((features.numFfAxes) > 3) ? 3 : features.numFfAxes;
475  /* Actually store the axes we want to use */
476  SDL_memcpy(haptic->hwdata->axes, features.ffAxes,
477  haptic->naxes * sizeof(Uint8));
478 
479  /* Always supported features. */
480  supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE;
481 
482  haptic->supported = supported;
483  return 0;
484 }
485 
486 
487 /*
488  * Opens the haptic device from the file descriptor.
489  */
490 static int
491 SDL_SYS_HapticOpenFromService(SDL_Haptic * haptic, io_service_t service)
492 {
493  HRESULT ret;
494  int ret2;
495 
496  /* Allocate the hwdata */
497  haptic->hwdata = (struct haptic_hwdata *)
498  SDL_malloc(sizeof(*haptic->hwdata));
499  if (haptic->hwdata == NULL) {
500  SDL_OutOfMemory();
501  goto creat_err;
502  }
503  SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
504 
505  /* Open the device */
506  ret = FFCreateDevice(service, &haptic->hwdata->device);
507  if (ret != FF_OK) {
508  SDL_SetError("Haptic: Unable to create device from service: %s.",
509  FFStrError(ret));
510  goto creat_err;
511  }
512 
513  /* Get supported features. */
514  ret2 = GetSupportedFeatures(haptic);
515  if (ret2 < 0) {
516  goto open_err;
517  }
518 
519 
520  /* Reset and then enable actuators. */
521  ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
522  FFSFFC_RESET);
523  if (ret != FF_OK) {
524  SDL_SetError("Haptic: Unable to reset device: %s.", FFStrError(ret));
525  goto open_err;
526  }
527  ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
528  FFSFFC_SETACTUATORSON);
529  if (ret != FF_OK) {
530  SDL_SetError("Haptic: Unable to enable actuators: %s.",
531  FFStrError(ret));
532  goto open_err;
533  }
534 
535 
536  /* Allocate effects memory. */
537  haptic->effects = (struct haptic_effect *)
538  SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
539  if (haptic->effects == NULL) {
540  SDL_OutOfMemory();
541  goto open_err;
542  }
543  /* Clear the memory */
544  SDL_memset(haptic->effects, 0,
545  sizeof(struct haptic_effect) * haptic->neffects);
546 
547  return 0;
548 
549  /* Error handling */
550  open_err:
551  FFReleaseDevice(haptic->hwdata->device);
552  creat_err:
553  if (haptic->hwdata != NULL) {
554  SDL_free(haptic->hwdata);
555  haptic->hwdata = NULL;
556  }
557  return -1;
558 
559 }
560 
561 
562 /*
563  * Opens a haptic device for usage.
564  */
565 int
566 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
567 {
568  SDL_hapticlist_item *item;
569  item = HapticByDevIndex(haptic->index);
570 
571  return SDL_SYS_HapticOpenFromService(haptic, item->dev);
572 }
573 
574 
575 /*
576  * Opens a haptic device from first mouse it finds for usage.
577  */
578 int
580 {
581  int device_index = 0;
582  SDL_hapticlist_item *item;
583 
584  for (item = SDL_hapticlist; item; item = item->next) {
585  if ((item->usagePage == kHIDPage_GenericDesktop) &&
586  (item->usage == kHIDUsage_GD_Mouse)) {
587  return device_index;
588  }
589  ++device_index;
590  }
591 
592  return -1;
593 }
594 
595 
596 /*
597  * Checks to see if a joystick has haptic features.
598  */
599 int
600 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
601 {
602  if (joystick->hwdata->ffservice != 0) {
603  return SDL_TRUE;
604  }
605  return SDL_FALSE;
606 }
607 
608 
609 /*
610  * Checks to see if the haptic device and joystick are in reality the same.
611  */
612 int
613 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
614 {
615  if (IOObjectIsEqualTo((io_object_t) ((size_t)haptic->hwdata->device),
616  joystick->hwdata->ffservice)) {
617  return 1;
618  }
619  return 0;
620 }
621 
622 
623 /*
624  * Opens a SDL_Haptic from a SDL_Joystick.
625  */
626 int
627 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
628 {
629  int device_index = 0;
630  SDL_hapticlist_item *item;
631 
632  for (item = SDL_hapticlist; item; item = item->next) {
633  if (IOObjectIsEqualTo((io_object_t) item->dev,
634  joystick->hwdata->ffservice)) {
635  haptic->index = device_index;
636  break;
637  }
638  ++device_index;
639  }
640 
641  return SDL_SYS_HapticOpenFromService(haptic, joystick->hwdata->ffservice);
642 }
643 
644 
645 /*
646  * Closes the haptic device.
647  */
648 void
649 SDL_SYS_HapticClose(SDL_Haptic * haptic)
650 {
651  if (haptic->hwdata) {
652 
653  /* Free Effects. */
654  SDL_free(haptic->effects);
655  haptic->effects = NULL;
656  haptic->neffects = 0;
657 
658  /* Clean up */
659  FFReleaseDevice(haptic->hwdata->device);
660 
661  /* Free */
662  SDL_free(haptic->hwdata);
663  haptic->hwdata = NULL;
664  }
665 }
666 
667 
668 /*
669  * Clean up after system specific haptic stuff
670  */
671 void
672 SDL_SYS_HapticQuit(void)
673 {
674  SDL_hapticlist_item *item;
676 
677  for (item = SDL_hapticlist; item; item = next) {
678  next = item->next;
679  /* Opened and not closed haptics are leaked, this is on purpose.
680  * Close your haptic devices after usage. */
681 
682  /* Free the io_service_t */
683  IOObjectRelease(item->dev);
684  SDL_free(item);
685  }
686 
687  numhaptics = -1;
688  SDL_hapticlist = NULL;
689  SDL_hapticlist_tail = NULL;
690 }
691 
692 
693 /*
694  * Converts an SDL trigger button to an FFEFFECT trigger button.
695  */
696 static DWORD
697 FFGetTriggerButton(Uint16 button)
698 {
699  DWORD dwTriggerButton;
700 
701  dwTriggerButton = FFEB_NOTRIGGER;
702 
703  if (button != 0) {
704  dwTriggerButton = FFJOFS_BUTTON(button - 1);
705  }
706 
707  return dwTriggerButton;
708 }
709 
710 
711 /*
712  * Sets the direction.
713  */
714 static int
715 SDL_SYS_SetDirection(FFEFFECT * effect, SDL_HapticDirection * dir, int naxes)
716 {
717  LONG *rglDir;
718 
719  /* Handle no axes a part. */
720  if (naxes == 0) {
721  effect->dwFlags |= FFEFF_SPHERICAL; /* Set as default. */
722  effect->rglDirection = NULL;
723  return 0;
724  }
725 
726  /* Has axes. */
727  rglDir = SDL_malloc(sizeof(LONG) * naxes);
728  if (rglDir == NULL) {
729  return SDL_OutOfMemory();
730  }
731  SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
732  effect->rglDirection = rglDir;
733 
734  switch (dir->type) {
735  case SDL_HAPTIC_POLAR:
736  effect->dwFlags |= FFEFF_POLAR;
737  rglDir[0] = dir->dir[0];
738  return 0;
740  effect->dwFlags |= FFEFF_CARTESIAN;
741  rglDir[0] = dir->dir[0];
742  if (naxes > 1) {
743  rglDir[1] = dir->dir[1];
744  }
745  if (naxes > 2) {
746  rglDir[2] = dir->dir[2];
747  }
748  return 0;
750  effect->dwFlags |= FFEFF_SPHERICAL;
751  rglDir[0] = dir->dir[0];
752  if (naxes > 1) {
753  rglDir[1] = dir->dir[1];
754  }
755  if (naxes > 2) {
756  rglDir[2] = dir->dir[2];
757  }
758  return 0;
759 
760  default:
761  return SDL_SetError("Haptic: Unknown direction type.");
762  }
763 }
764 
765 
766 /* Clamps and converts. */
767 #define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
768 /* Just converts. */
769 #define CONVERT(x) (((x)*10000) / 0x7FFF)
770 /*
771  * Creates the FFEFFECT from a SDL_HapticEffect.
772  */
773 static int
774 SDL_SYS_ToFFEFFECT(SDL_Haptic * haptic, FFEFFECT * dest, SDL_HapticEffect * src)
775 {
776  int i;
777  FFCONSTANTFORCE *constant = NULL;
778  FFPERIODIC *periodic = NULL;
779  FFCONDITION *condition = NULL; /* Actually an array of conditions - one per axis. */
780  FFRAMPFORCE *ramp = NULL;
781  FFCUSTOMFORCE *custom = NULL;
782  FFENVELOPE *envelope = NULL;
783  SDL_HapticConstant *hap_constant = NULL;
784  SDL_HapticPeriodic *hap_periodic = NULL;
785  SDL_HapticCondition *hap_condition = NULL;
786  SDL_HapticRamp *hap_ramp = NULL;
787  SDL_HapticCustom *hap_custom = NULL;
788  DWORD *axes = NULL;
789 
790  /* Set global stuff. */
791  SDL_memset(dest, 0, sizeof(FFEFFECT));
792  dest->dwSize = sizeof(FFEFFECT); /* Set the structure size. */
793  dest->dwSamplePeriod = 0; /* Not used by us. */
794  dest->dwGain = 10000; /* Gain is set globally, not locally. */
795  dest->dwFlags = FFEFF_OBJECTOFFSETS; /* Seems obligatory. */
796 
797  /* Envelope. */
798  envelope = SDL_malloc(sizeof(FFENVELOPE));
799  if (envelope == NULL) {
800  return SDL_OutOfMemory();
801  }
802  SDL_memset(envelope, 0, sizeof(FFENVELOPE));
803  dest->lpEnvelope = envelope;
804  envelope->dwSize = sizeof(FFENVELOPE); /* Always should be this. */
805 
806  /* Axes. */
807  dest->cAxes = haptic->naxes;
808  if (dest->cAxes > 0) {
809  axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
810  if (axes == NULL) {
811  return SDL_OutOfMemory();
812  }
813  axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */
814  if (dest->cAxes > 1) {
815  axes[1] = haptic->hwdata->axes[1];
816  }
817  if (dest->cAxes > 2) {
818  axes[2] = haptic->hwdata->axes[2];
819  }
820  dest->rgdwAxes = axes;
821  }
822 
823 
824  /* The big type handling switch, even bigger then Linux's version. */
825  switch (src->type) {
826  case SDL_HAPTIC_CONSTANT:
827  hap_constant = &src->constant;
828  constant = SDL_malloc(sizeof(FFCONSTANTFORCE));
829  if (constant == NULL) {
830  return SDL_OutOfMemory();
831  }
832  SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE));
833 
834  /* Specifics */
835  constant->lMagnitude = CONVERT(hap_constant->level);
836  dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE);
837  dest->lpvTypeSpecificParams = constant;
838 
839  /* Generics */
840  dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
841  dest->dwTriggerButton = FFGetTriggerButton(hap_constant->button);
842  dest->dwTriggerRepeatInterval = hap_constant->interval;
843  dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */
844 
845  /* Direction. */
846  if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
847  < 0) {
848  return -1;
849  }
850 
851  /* Envelope */
852  if ((hap_constant->attack_length == 0)
853  && (hap_constant->fade_length == 0)) {
854  SDL_free(envelope);
855  dest->lpEnvelope = NULL;
856  } else {
857  envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level);
858  envelope->dwAttackTime = hap_constant->attack_length * 1000;
859  envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level);
860  envelope->dwFadeTime = hap_constant->fade_length * 1000;
861  }
862 
863  break;
864 
865  case SDL_HAPTIC_SINE:
866  /* !!! FIXME: put this back when we have more bits in 2.1 */
867  /* case SDL_HAPTIC_SQUARE: */
868  case SDL_HAPTIC_TRIANGLE:
871  hap_periodic = &src->periodic;
872  periodic = SDL_malloc(sizeof(FFPERIODIC));
873  if (periodic == NULL) {
874  return SDL_OutOfMemory();
875  }
876  SDL_memset(periodic, 0, sizeof(FFPERIODIC));
877 
878  /* Specifics */
879  periodic->dwMagnitude = CONVERT(SDL_abs(hap_periodic->magnitude));
880  periodic->lOffset = CONVERT(hap_periodic->offset);
881  periodic->dwPhase =
882  (hap_periodic->phase + (hap_periodic->magnitude < 0 ? 18000 : 0)) % 36000;
883  periodic->dwPeriod = hap_periodic->period * 1000;
884  dest->cbTypeSpecificParams = sizeof(FFPERIODIC);
885  dest->lpvTypeSpecificParams = periodic;
886 
887  /* Generics */
888  dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
889  dest->dwTriggerButton = FFGetTriggerButton(hap_periodic->button);
890  dest->dwTriggerRepeatInterval = hap_periodic->interval;
891  dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */
892 
893  /* Direction. */
894  if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
895  < 0) {
896  return -1;
897  }
898 
899  /* Envelope */
900  if ((hap_periodic->attack_length == 0)
901  && (hap_periodic->fade_length == 0)) {
902  SDL_free(envelope);
903  dest->lpEnvelope = NULL;
904  } else {
905  envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level);
906  envelope->dwAttackTime = hap_periodic->attack_length * 1000;
907  envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level);
908  envelope->dwFadeTime = hap_periodic->fade_length * 1000;
909  }
910 
911  break;
912 
913  case SDL_HAPTIC_SPRING:
914  case SDL_HAPTIC_DAMPER:
915  case SDL_HAPTIC_INERTIA:
916  case SDL_HAPTIC_FRICTION:
917  hap_condition = &src->condition;
918  if (dest->cAxes > 0) {
919  condition = SDL_malloc(sizeof(FFCONDITION) * dest->cAxes);
920  if (condition == NULL) {
921  return SDL_OutOfMemory();
922  }
923  SDL_memset(condition, 0, sizeof(FFCONDITION));
924 
925  /* Specifics */
926  for (i = 0; i < dest->cAxes; i++) {
927  condition[i].lOffset = CONVERT(hap_condition->center[i]);
928  condition[i].lPositiveCoefficient =
929  CONVERT(hap_condition->right_coeff[i]);
930  condition[i].lNegativeCoefficient =
931  CONVERT(hap_condition->left_coeff[i]);
932  condition[i].dwPositiveSaturation =
933  CCONVERT(hap_condition->right_sat[i] / 2);
934  condition[i].dwNegativeSaturation =
935  CCONVERT(hap_condition->left_sat[i] / 2);
936  condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i] / 2);
937  }
938  }
939 
940  dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes;
941  dest->lpvTypeSpecificParams = condition;
942 
943  /* Generics */
944  dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */
945  dest->dwTriggerButton = FFGetTriggerButton(hap_condition->button);
946  dest->dwTriggerRepeatInterval = hap_condition->interval;
947  dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */
948 
949  /* Direction. */
950  if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
951  < 0) {
952  return -1;
953  }
954 
955  /* Envelope - Not actually supported by most CONDITION implementations. */
956  SDL_free(dest->lpEnvelope);
957  dest->lpEnvelope = NULL;
958 
959  break;
960 
961  case SDL_HAPTIC_RAMP:
962  hap_ramp = &src->ramp;
963  ramp = SDL_malloc(sizeof(FFRAMPFORCE));
964  if (ramp == NULL) {
965  return SDL_OutOfMemory();
966  }
967  SDL_memset(ramp, 0, sizeof(FFRAMPFORCE));
968 
969  /* Specifics */
970  ramp->lStart = CONVERT(hap_ramp->start);
971  ramp->lEnd = CONVERT(hap_ramp->end);
972  dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE);
973  dest->lpvTypeSpecificParams = ramp;
974 
975  /* Generics */
976  dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */
977  dest->dwTriggerButton = FFGetTriggerButton(hap_ramp->button);
978  dest->dwTriggerRepeatInterval = hap_ramp->interval;
979  dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */
980 
981  /* Direction. */
982  if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
983  return -1;
984  }
985 
986  /* Envelope */
987  if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
988  SDL_free(envelope);
989  dest->lpEnvelope = NULL;
990  } else {
991  envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level);
992  envelope->dwAttackTime = hap_ramp->attack_length * 1000;
993  envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level);
994  envelope->dwFadeTime = hap_ramp->fade_length * 1000;
995  }
996 
997  break;
998 
999  case SDL_HAPTIC_CUSTOM:
1000  hap_custom = &src->custom;
1001  custom = SDL_malloc(sizeof(FFCUSTOMFORCE));
1002  if (custom == NULL) {
1003  return SDL_OutOfMemory();
1004  }
1005  SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE));
1006 
1007  /* Specifics */
1008  custom->cChannels = hap_custom->channels;
1009  custom->dwSamplePeriod = hap_custom->period * 1000;
1010  custom->cSamples = hap_custom->samples;
1011  custom->rglForceData =
1012  SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
1013  for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */
1014  custom->rglForceData[i] = CCONVERT(hap_custom->data[i]);
1015  }
1016  dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE);
1017  dest->lpvTypeSpecificParams = custom;
1018 
1019  /* Generics */
1020  dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */
1021  dest->dwTriggerButton = FFGetTriggerButton(hap_custom->button);
1022  dest->dwTriggerRepeatInterval = hap_custom->interval;
1023  dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */
1024 
1025  /* Direction. */
1026  if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
1027  0) {
1028  return -1;
1029  }
1030 
1031  /* Envelope */
1032  if ((hap_custom->attack_length == 0)
1033  && (hap_custom->fade_length == 0)) {
1034  SDL_free(envelope);
1035  dest->lpEnvelope = NULL;
1036  } else {
1037  envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level);
1038  envelope->dwAttackTime = hap_custom->attack_length * 1000;
1039  envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level);
1040  envelope->dwFadeTime = hap_custom->fade_length * 1000;
1041  }
1042 
1043  break;
1044 
1045 
1046  default:
1047  return SDL_SetError("Haptic: Unknown effect type.");
1048  }
1049 
1050  return 0;
1051 }
1052 
1053 
1054 /*
1055  * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT.
1056  */
1057 static void
1058 SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type)
1059 {
1060  FFCUSTOMFORCE *custom;
1061 
1062  SDL_free(effect->lpEnvelope);
1063  effect->lpEnvelope = NULL;
1064  SDL_free(effect->rgdwAxes);
1065  effect->rgdwAxes = NULL;
1066  if (effect->lpvTypeSpecificParams != NULL) {
1067  if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */
1068  custom = (FFCUSTOMFORCE *) effect->lpvTypeSpecificParams;
1069  SDL_free(custom->rglForceData);
1070  custom->rglForceData = NULL;
1071  }
1072  SDL_free(effect->lpvTypeSpecificParams);
1073  effect->lpvTypeSpecificParams = NULL;
1074  }
1075  SDL_free(effect->rglDirection);
1076  effect->rglDirection = NULL;
1077 }
1078 
1079 
1080 /*
1081  * Gets the effect type from the generic SDL haptic effect wrapper.
1082  */
1083 CFUUIDRef
1084 SDL_SYS_HapticEffectType(Uint16 type)
1085 {
1086  switch (type) {
1087  case SDL_HAPTIC_CONSTANT:
1088  return kFFEffectType_ConstantForce_ID;
1089 
1090  case SDL_HAPTIC_RAMP:
1091  return kFFEffectType_RampForce_ID;
1092 
1093  /* !!! FIXME: put this back when we have more bits in 2.1 */
1094  /* case SDL_HAPTIC_SQUARE:
1095  return kFFEffectType_Square_ID; */
1096 
1097  case SDL_HAPTIC_SINE:
1098  return kFFEffectType_Sine_ID;
1099 
1100  case SDL_HAPTIC_TRIANGLE:
1101  return kFFEffectType_Triangle_ID;
1102 
1103  case SDL_HAPTIC_SAWTOOTHUP:
1104  return kFFEffectType_SawtoothUp_ID;
1105 
1107  return kFFEffectType_SawtoothDown_ID;
1108 
1109  case SDL_HAPTIC_SPRING:
1110  return kFFEffectType_Spring_ID;
1111 
1112  case SDL_HAPTIC_DAMPER:
1113  return kFFEffectType_Damper_ID;
1114 
1115  case SDL_HAPTIC_INERTIA:
1116  return kFFEffectType_Inertia_ID;
1117 
1118  case SDL_HAPTIC_FRICTION:
1119  return kFFEffectType_Friction_ID;
1120 
1121  case SDL_HAPTIC_CUSTOM:
1122  return kFFEffectType_CustomForce_ID;
1123 
1124  default:
1125  SDL_SetError("Haptic: Unknown effect type.");
1126  return NULL;
1127  }
1128 }
1129 
1130 
1131 /*
1132  * Creates a new haptic effect.
1133  */
1134 int
1135 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1136  SDL_HapticEffect * base)
1137 {
1138  HRESULT ret;
1139  CFUUIDRef type;
1140 
1141  /* Alloc the effect. */
1142  effect->hweffect = (struct haptic_hweffect *)
1143  SDL_malloc(sizeof(struct haptic_hweffect));
1144  if (effect->hweffect == NULL) {
1145  SDL_OutOfMemory();
1146  goto err_hweffect;
1147  }
1148 
1149  /* Get the type. */
1150  type = SDL_SYS_HapticEffectType(base->type);
1151  if (type == NULL) {
1152  goto err_hweffect;
1153  }
1154 
1155  /* Get the effect. */
1156  if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
1157  goto err_effectdone;
1158  }
1159 
1160  /* Create the actual effect. */
1161  ret = FFDeviceCreateEffect(haptic->hwdata->device, type,
1162  &effect->hweffect->effect,
1163  &effect->hweffect->ref);
1164  if (ret != FF_OK) {
1165  SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret));
1166  goto err_effectdone;
1167  }
1168 
1169  return 0;
1170 
1171  err_effectdone:
1172  SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type);
1173  err_hweffect:
1174  SDL_free(effect->hweffect);
1175  effect->hweffect = NULL;
1176  return -1;
1177 }
1178 
1179 
1180 /*
1181  * Updates an effect.
1182  */
1183 int
1184 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
1185  struct haptic_effect *effect,
1187 {
1188  HRESULT ret;
1189  FFEffectParameterFlag flags;
1190  FFEFFECT temp;
1191 
1192  /* Get the effect. */
1193  SDL_memset(&temp, 0, sizeof(FFEFFECT));
1194  if (SDL_SYS_ToFFEFFECT(haptic, &temp, data) < 0) {
1195  goto err_update;
1196  }
1197 
1198  /* Set the flags. Might be worthwhile to diff temp with loaded effect and
1199  * only change those parameters. */
1200  flags = FFEP_DIRECTION |
1201  FFEP_DURATION |
1202  FFEP_ENVELOPE |
1203  FFEP_STARTDELAY |
1204  FFEP_TRIGGERBUTTON |
1205  FFEP_TRIGGERREPEATINTERVAL | FFEP_TYPESPECIFICPARAMS;
1206 
1207  /* Create the actual effect. */
1208  ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags);
1209  if (ret != FF_OK) {
1210  SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret));
1211  goto err_update;
1212  }
1213 
1214  /* Copy it over. */
1215  SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type);
1216  SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT));
1217 
1218  return 0;
1219 
1220  err_update:
1221  SDL_SYS_HapticFreeFFEFFECT(&temp, data->type);
1222  return -1;
1223 }
1224 
1225 
1226 /*
1227  * Runs an effect.
1228  */
1229 int
1230 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1232 {
1233  HRESULT ret;
1234  Uint32 iter;
1235 
1236  /* Check if it's infinite. */
1237  if (iterations == SDL_HAPTIC_INFINITY) {
1238  iter = FF_INFINITE;
1239  } else
1240  iter = iterations;
1241 
1242  /* Run the effect. */
1243  ret = FFEffectStart(effect->hweffect->ref, iter, 0);
1244  if (ret != FF_OK) {
1245  return SDL_SetError("Haptic: Unable to run the effect: %s.",
1246  FFStrError(ret));
1247  }
1248 
1249  return 0;
1250 }
1251 
1252 
1253 /*
1254  * Stops an effect.
1255  */
1256 int
1257 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1258 {
1259  HRESULT ret;
1260 
1261  ret = FFEffectStop(effect->hweffect->ref);
1262  if (ret != FF_OK) {
1263  return SDL_SetError("Haptic: Unable to stop the effect: %s.",
1264  FFStrError(ret));
1265  }
1266 
1267  return 0;
1268 }
1269 
1270 
1271 /*
1272  * Frees the effect.
1273  */
1274 void
1275 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1276 {
1277  HRESULT ret;
1278 
1279  ret = FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref);
1280  if (ret != FF_OK) {
1281  SDL_SetError("Haptic: Error removing the effect from the device: %s.",
1282  FFStrError(ret));
1283  }
1284  SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect,
1285  effect->effect.type);
1286  SDL_free(effect->hweffect);
1287  effect->hweffect = NULL;
1288 }
1289 
1290 
1291 /*
1292  * Gets the status of a haptic effect.
1293  */
1294 int
1295 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
1296  struct haptic_effect *effect)
1297 {
1298  HRESULT ret;
1299  FFEffectStatusFlag status;
1300 
1301  ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status);
1302  if (ret != FF_OK) {
1303  SDL_SetError("Haptic: Unable to get effect status: %s.",
1304  FFStrError(ret));
1305  return -1;
1306  }
1307 
1308  if (status == 0) {
1309  return SDL_FALSE;
1310  }
1311  return SDL_TRUE; /* Assume it's playing or emulated. */
1312 }
1313 
1314 
1315 /*
1316  * Sets the gain.
1317  */
1318 int
1319 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
1320 {
1321  HRESULT ret;
1322  Uint32 val;
1323 
1324  val = gain * 100; /* Mac OS X uses 0 to 10,000 */
1325  ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
1326  FFPROP_FFGAIN, &val);
1327  if (ret != FF_OK) {
1328  return SDL_SetError("Haptic: Error setting gain: %s.", FFStrError(ret));
1329  }
1330 
1331  return 0;
1332 }
1333 
1334 
1335 /*
1336  * Sets the autocentering.
1337  */
1338 int
1339 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
1340 {
1341  HRESULT ret;
1342  Uint32 val;
1343 
1344  /* Mac OS X only has 0 (off) and 1 (on) */
1345  if (autocenter == 0) {
1346  val = 0;
1347  } else {
1348  val = 1;
1349  }
1350 
1351  ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
1352  FFPROP_AUTOCENTER, &val);
1353  if (ret != FF_OK) {
1354  return SDL_SetError("Haptic: Error setting autocenter: %s.",
1355  FFStrError(ret));
1356  }
1357 
1358  return 0;
1359 }
1360 
1361 
1362 /*
1363  * Pauses the device.
1364  */
1365 int
1366 SDL_SYS_HapticPause(SDL_Haptic * haptic)
1367 {
1368  HRESULT ret;
1369 
1370  ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1371  FFSFFC_PAUSE);
1372  if (ret != FF_OK) {
1373  return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
1374  }
1375 
1376  return 0;
1377 }
1378 
1379 
1380 /*
1381  * Unpauses the device.
1382  */
1383 int
1384 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
1385 {
1386  HRESULT ret;
1387 
1388  ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1389  FFSFFC_CONTINUE);
1390  if (ret != FF_OK) {
1391  return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
1392  }
1393 
1394  return 0;
1395 }
1396 
1397 
1398 /*
1399  * Stops all currently playing effects.
1400  */
1401 int
1402 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
1403 {
1404  HRESULT ret;
1405 
1406  ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1407  FFSFFC_STOPALL);
1408  if (ret != FF_OK) {
1409  return SDL_SetError("Haptic: Error stopping device: %s.", FFStrError(ret));
1410  }
1411 
1412  return 0;
1413 }
1414 
1415 #endif /* SDL_HAPTIC_IOKIT */
1416 
1417 /* 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
#define SDL_abs
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
GLuint64EXT * result
#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
SDL_HapticDirection direction
Definition: SDL_haptic.h:596
int SDL_SYS_HapticOpen(SDL_Haptic *haptic)
SDL_HapticCustom custom
Definition: SDL_haptic.h:798
int SDL_SYS_HapticMouse(void)
int SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick)
#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 MacHaptic_MaybeRemoveDevice(io_object_t device)
GLenum GLint ref
int SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
const char * SDL_SYS_HapticName(int index)
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
Uint16 fade_length
Definition: SDL_haptic.h:716
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)
GLuint const GLchar * name
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
static SDL_AudioDeviceID device
Definition: loopwave.c:37
struct SDL_hapticlist_item * next
SDL_HapticCondition condition
Definition: SDL_haptic.h:795
SDL_bool retval
#define SDL_memcpy
GLsizeiptr const void GLenum usage
GLuint GLfloat * val
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)
uint8_t Uint8
Definition: SDL_stdinc.h:157
#define SDL_free
#define SDL_HAPTIC_POLAR
Uses polar coordinates for the direction.
Definition: SDL_haptic.h:312
SDL_HapticConstant constant
Definition: SDL_haptic.h:793
#define SDL_HAPTIC_PAUSE
Device can be paused.
Definition: SDL_haptic.h:299
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
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)
int MacHaptic_MaybeAddDevice(io_object_t device)
struct haptic_hweffect * hweffect
Definition: SDL_syshaptic.h:33
#define SDL_SetError
#define SDL_HAPTIC_STATUS
Device can be queried for effect status.
Definition: SDL_haptic.h:289
GLbitfield flags
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
Uint16 attack_level
Definition: SDL_haptic.h:715
#define SDL_HAPTIC_RAMP
Ramp effect supported.
Definition: SDL_haptic.h:211
#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
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
A structure containing a template for the SDL_HAPTIC_CUSTOM effect.
Definition: SDL_haptic.h:693
int SDL_SYS_HapticStopAll(SDL_Haptic *haptic)
#define SDL_malloc
#define SDL_HAPTIC_SAWTOOTHUP
Sawtoothup wave effect supported.
Definition: SDL_haptic.h:193
int SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain)
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
Uint16 attack_length
Definition: SDL_haptic.h:714
#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
#define SDL_memset
SDL_HapticDirection direction
Definition: SDL_haptic.h:461
SDL_HapticDirection direction
Definition: SDL_haptic.h:697
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