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