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