2 SDL - Simple DirectMedia Layer
3 Copyright (C) 2008 Edgar Simo
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.
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.
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
22 #include "SDL_config.h"
24 #ifdef SDL_HAPTIC_IOKIT
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 */
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>
38 #ifndef IO_OBJECT_NULL
39 #define IO_OBJECT_NULL ((io_service_t)0)
42 #define MAX_HAPTICS 32
46 * List of available haptic devices.
50 char name[256]; /* Name of the device. */
52 io_service_t dev; /* Node we use to create the device. */
53 SDL_Haptic *haptic; /* Haptic currently assosciated with it. */
55 /* Usage pages for determining if it's a mouse or not. */
58 } SDL_hapticlist[MAX_HAPTICS];
62 * Haptic system hardware data.
66 FFDeviceObjectReference device; /* Hardware device. */
72 * Haptic system effect data.
74 struct haptic_hweffect
76 FFEffectObjectReference ref; /* Reference. */
77 struct FFEFFECT effect; /* Hardware effect. */
83 static void SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type);
84 static int HIDGetDeviceProduct(io_service_t dev, char *name);
88 * Like strerror but for force feedback errors.
91 FFStrError(HRESULT err)
94 case FFERR_DEVICEFULL:
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";
110 return "undetermined error";
111 case FFERR_HASEFFECTS:
112 return "device has effects";
113 case FFERR_INCOMPLETEEFFECT:
114 return "incomplete effect";
116 return "internal fault";
117 case FFERR_INVALIDDOWNLOADID:
118 return "invalid download id";
119 case FFERR_INVALIDPARAM:
120 return "invalid parameter";
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";
139 return "unknown error";
145 * Initializes the haptic subsystem.
148 SDL_SYS_HapticInit(void)
153 CFDictionaryRef match;
155 CFMutableDictionaryRef hidProperties;
158 /* Clear all the memory. */
159 SDL_memset(SDL_hapticlist, 0, sizeof(SDL_hapticlist));
161 /* Get HID devices. */
162 match = IOServiceMatching(kIOHIDDeviceKey);
164 SDL_SetError("Haptic: Failed to get IOServiceMatching.");
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.");
174 /* IOServiceGetMatchingServices consumes dictionary. */
176 if (!IOIteratorIsValid(iter)) { /* No iterator. */
182 while ((device = IOIteratorNext(iter)) != IO_OBJECT_NULL) {
184 /* Check for force feedback. */
185 if (FFIsForceFeedback(device) == FF_OK) {
187 /* Set basic device data. */
188 HIDGetDeviceProduct(device, SDL_hapticlist[numhaptics].name);
189 SDL_hapticlist[numhaptics].dev = device;
190 SDL_hapticlist[numhaptics].haptic = NULL;
192 /* Set usage pages. */
195 result = IORegistryEntryCreateCFProperties(device,
199 if ((result == KERN_SUCCESS) && hidProperties) {
201 CFDictionaryGetValue(hidProperties,
202 CFSTR(kIOHIDPrimaryUsagePageKey));
204 if (!CFNumberGetValue(refCF, kCFNumberLongType,
205 &SDL_hapticlist[numhaptics].
208 ("Haptic: Recieving device's usage page.");
210 CFDictionaryGetValue(hidProperties,
211 CFSTR(kIOHIDPrimaryUsageKey));
213 if (!CFNumberGetValue(refCF, kCFNumberLongType,
214 &SDL_hapticlist[numhaptics].
216 SDL_SetError("Haptic: Recieving device's usage.");
219 CFRelease(hidProperties);
222 /* Device has been added. */
224 } else { /* Free the unused device. */
225 IOObjectRelease(device);
228 /* Reached haptic limit. */
229 if (numhaptics >= MAX_HAPTICS)
232 IOObjectRelease(iter);
239 * Return the name of a haptic device, does not need to be opened.
242 SDL_SYS_HapticName(int index)
244 return SDL_hapticlist[index].name;
248 * Gets the device's product name.
251 HIDGetDeviceProduct(io_service_t dev, char *name)
253 CFMutableDictionaryRef hidProperties, usbProperties;
254 io_registry_entry_t parent1, parent2;
257 hidProperties = usbProperties = 0;
259 ret = IORegistryEntryCreateCFProperties(dev, &hidProperties,
260 kCFAllocatorDefault, kNilOptions);
261 if ((ret != KERN_SUCCESS) || !hidProperties) {
262 SDL_SetError("Haptic: Unable to create CFProperties.");
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
270 IORegistryEntryGetParentEntry(dev, kIOServicePlane, &parent1))
272 IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2))
274 IORegistryEntryCreateCFProperties(parent2, &usbProperties,
280 * try hid dictionary first, if fail then go to usb dictionary
284 /* Get product name */
286 CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey));
289 CFDictionaryGetValue(usbProperties,
290 CFSTR("USB Product Name"));
292 if (!CFStringGetCString(refCF, name, 256,
293 CFStringGetSystemEncoding())) {
295 ("Haptic: CFStringGetCString error retrieving pDevice->product.");
300 CFRelease(usbProperties);
303 ("Haptic: IORegistryEntryCreateCFProperties failed to create usbProperties.");
308 if (kIOReturnSuccess != IOObjectRelease(parent2)) {
309 SDL_SetError("Haptic: IOObjectRelease error with parent2.");
311 if (kIOReturnSuccess != IOObjectRelease(parent1)) {
312 SDL_SetError("Haptic: IOObjectRelease error with parent1.");
315 SDL_SetError("Haptic: Error getting registry entries.");
323 #define FF_TEST(ff, s) \
324 if (features.supportedEffects & (ff)) supported |= (s)
326 * Gets supported features.
329 GetSupportedFeatures(SDL_Haptic * haptic)
332 FFDeviceObjectReference device;
333 FFCAPABILITIES features;
334 unsigned int supported;
337 device = haptic->hwdata->device;
339 ret = FFDeviceGetForceFeedbackCapabilities(device, &features);
341 SDL_SetError("Haptic: Unable to get device's supported features.");
347 /* Get maximum effects. */
348 haptic->neffects = features.storageCapacity;
349 haptic->nplaying = features.playbackCapacity;
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);
365 /* Check if supports gain. */
366 ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_FFGAIN,
369 supported |= SDL_HAPTIC_GAIN;
370 else if (ret != FFERR_UNSUPPORTED) {
371 SDL_SetError("Haptic: Unable to get if device supports gain: %s.",
376 /* Checks if supports autocenter. */
377 ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_AUTOCENTER,
380 supported |= SDL_HAPTIC_AUTOCENTER;
381 else if (ret != FFERR_UNSUPPORTED) {
383 ("Haptic: Unable to get if device supports autocenter: %s.",
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));
394 /* Always supported features. */
395 supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE;
397 haptic->supported = supported;
403 * Opens the haptic device from the file descriptor.
406 SDL_SYS_HapticOpenFromService(SDL_Haptic * haptic, io_service_t service)
411 /* Allocate the hwdata */
412 haptic->hwdata = (struct haptic_hwdata *)
413 SDL_malloc(sizeof(*haptic->hwdata));
414 if (haptic->hwdata == NULL) {
418 SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
420 /* Open the device */
421 ret = FFCreateDevice(service, &haptic->hwdata->device);
423 SDL_SetError("Haptic: Unable to create device from service: %s.",
428 /* Get supported features. */
429 ret2 = GetSupportedFeatures(haptic);
430 if (haptic->supported < 0) {
435 /* Reset and then enable actuators. */
436 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
439 SDL_SetError("Haptic: Unable to reset device: %s.", FFStrError(ret));
442 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
443 FFSFFC_SETACTUATORSON);
445 SDL_SetError("Haptic: Unable to enable actuators: %s.",
451 /* Allocate effects memory. */
452 haptic->effects = (struct haptic_effect *)
453 SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
454 if (haptic->effects == NULL) {
458 /* Clear the memory */
459 SDL_memset(haptic->effects, 0,
460 sizeof(struct haptic_effect) * haptic->neffects);
466 FFReleaseDevice(haptic->hwdata->device);
468 if (haptic->hwdata != NULL) {
469 free(haptic->hwdata);
470 haptic->hwdata = NULL;
478 * Opens a haptic device for usage.
481 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
483 return SDL_SYS_HapticOpenFromService(haptic,
484 SDL_hapticlist[haptic->index].dev);
489 * Opens a haptic device from first mouse it finds for usage.
492 SDL_SYS_HapticMouse(void)
496 for (i = 0; i < SDL_numhaptics; i++) {
497 if ((SDL_hapticlist[i].usagePage == kHIDPage_GenericDesktop) &&
498 (SDL_hapticlist[i].usage == kHIDUsage_GD_Mouse))
507 * Checks to see if a joystick has haptic features.
510 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
512 if (joystick->hwdata->ffservice != 0)
519 * Checks to see if the haptic device and joystick and in reality the same.
522 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
524 if (IOObjectIsEqualTo((io_object_t) haptic->hwdata->device,
525 joystick->hwdata->ffservice))
532 * Opens a SDL_Haptic from a SDL_Joystick.
535 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
537 return SDL_SYS_HapticOpenFromService(haptic, joystick->hwdata->ffservice);
542 * Closes the haptic device.
545 SDL_SYS_HapticClose(SDL_Haptic * haptic)
547 if (haptic->hwdata) {
550 SDL_free(haptic->effects);
551 haptic->effects = NULL;
552 haptic->neffects = 0;
555 FFReleaseDevice(haptic->hwdata->device);
558 SDL_free(haptic->hwdata);
559 haptic->hwdata = NULL;
565 * Clean up after system specific haptic stuff
568 SDL_SYS_HapticQuit(void)
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. */
576 /* Free the io_service_t */
577 IOObjectRelease(SDL_hapticlist[i].dev);
583 * Converts an SDL trigger button to an FFEFFECT trigger button.
586 FFGetTriggerButton(Uint16 button)
588 DWORD dwTriggerButton;
590 dwTriggerButton = FFEB_NOTRIGGER;
593 dwTriggerButton = FFJOFS_BUTTON(button - 1);
596 return dwTriggerButton;
601 * Sets the direction.
604 SDL_SYS_SetDirection(FFEFFECT * effect, SDL_HapticDirection * dir, int naxes)
608 /* Handle no axes a part. */
610 effect->dwFlags |= FFEFF_SPHERICAL; /* Set as default. */
611 effect->rglDirection = NULL;
616 rglDir = SDL_malloc(sizeof(LONG) * naxes);
617 if (rglDir == NULL) {
621 SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
622 effect->rglDirection = rglDir;
625 case SDL_HAPTIC_POLAR:
626 effect->dwFlags |= FFEFF_POLAR;
627 rglDir[0] = dir->dir[0];
629 case SDL_HAPTIC_CARTESIAN:
630 effect->dwFlags |= FFEFF_CARTESIAN;
631 rglDir[0] = dir->dir[0];
633 rglDir[1] = dir->dir[1];
635 rglDir[2] = dir->dir[2];
637 case SDL_HAPTIC_SPHERICAL:
638 effect->dwFlags |= FFEFF_SPHERICAL;
639 rglDir[0] = dir->dir[0];
641 rglDir[1] = dir->dir[1];
643 rglDir[2] = dir->dir[2];
647 SDL_SetError("Haptic: Unknown direction type.");
653 /* Clamps and converts. */
654 #define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
656 #define CONVERT(x) (((x)*10000) / 0x7FFF)
658 * Creates the FFEFFECT from a SDL_HapticEffect.
661 SDL_SYS_ToFFEFFECT(SDL_Haptic * haptic, FFEFFECT * dest,
662 SDL_HapticEffect * src)
665 FFCONSTANTFORCE *constant;
666 FFPERIODIC *periodic;
667 FFCONDITION *condition; /* Actually an array of conditions - one per axis. */
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;
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. */
686 envelope = SDL_malloc(sizeof(FFENVELOPE));
687 if (envelope == NULL) {
691 SDL_memset(envelope, 0, sizeof(FFENVELOPE));
692 dest->lpEnvelope = envelope;
693 envelope->dwSize = sizeof(FFENVELOPE); /* Always should be this. */
696 dest->cAxes = haptic->naxes;
697 if (dest->cAxes > 0) {
698 axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
703 axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */
704 if (dest->cAxes > 1) {
705 axes[1] = haptic->hwdata->axes[1];
707 if (dest->cAxes > 2) {
708 axes[2] = haptic->hwdata->axes[2];
710 dest->rgdwAxes = axes;
714 /* The big type handling switch, even bigger then linux's version. */
716 case SDL_HAPTIC_CONSTANT:
717 hap_constant = &src->constant;
718 constant = SDL_malloc(sizeof(FFCONSTANTFORCE));
719 if (constant == NULL) {
723 SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE));
726 constant->lMagnitude = CONVERT(hap_constant->level);
727 dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE);
728 dest->lpvTypeSpecificParams = constant;
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. */
737 if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
743 if ((hap_constant->attack_length == 0)
744 && (hap_constant->fade_length == 0)) {
746 dest->lpEnvelope = NULL;
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;
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) {
767 SDL_memset(periodic, 0, sizeof(FFPERIODIC));
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;
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. */
784 if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
790 if ((hap_periodic->attack_length == 0)
791 && (hap_periodic->fade_length == 0)) {
793 dest->lpEnvelope = NULL;
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;
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) {
813 SDL_memset(condition, 0, sizeof(FFCONDITION));
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]);
828 dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes;
829 dest->lpvTypeSpecificParams = condition;
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. */
838 if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
843 /* Envelope - Not actually supported by most CONDITION implementations. */
844 SDL_free(dest->lpEnvelope);
845 dest->lpEnvelope = NULL;
849 case SDL_HAPTIC_RAMP:
850 hap_ramp = &src->ramp;
851 ramp = SDL_malloc(sizeof(FFRAMPFORCE));
856 SDL_memset(ramp, 0, sizeof(FFRAMPFORCE));
859 ramp->lStart = CONVERT(hap_ramp->start);
860 ramp->lEnd = CONVERT(hap_ramp->end);
861 dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE);
862 dest->lpvTypeSpecificParams = ramp;
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. */
871 if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
876 if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
878 dest->lpEnvelope = NULL;
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;
888 case SDL_HAPTIC_CUSTOM:
889 hap_custom = &src->custom;
890 custom = SDL_malloc(sizeof(FFCUSTOMFORCE));
891 if (custom == NULL) {
895 SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE));
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]);
906 dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE);
907 dest->lpvTypeSpecificParams = custom;
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. */
916 if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
922 if ((hap_custom->attack_length == 0)
923 && (hap_custom->fade_length == 0)) {
925 dest->lpEnvelope = NULL;
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;
937 SDL_SetError("Haptic: Unknown effect type.");
946 * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT.
949 SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type)
951 FFCUSTOMFORCE *custom;
953 if (effect->lpEnvelope != NULL) {
954 SDL_free(effect->lpEnvelope);
955 effect->lpEnvelope = NULL;
957 if (effect->rgdwAxes != NULL) {
958 SDL_free(effect->rgdwAxes);
959 effect->rgdwAxes = NULL;
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;
967 SDL_free(effect->lpvTypeSpecificParams);
968 effect->lpvTypeSpecificParams = NULL;
970 if (effect->rglDirection != NULL) {
971 SDL_free(effect->rglDirection);
972 effect->rglDirection = NULL;
978 * Gets the effect type from the generic SDL haptic effect wrapper.
981 SDL_SYS_HapticEffectType(Uint16 type)
984 case SDL_HAPTIC_CONSTANT:
985 return kFFEffectType_ConstantForce_ID;
987 case SDL_HAPTIC_RAMP:
988 return kFFEffectType_RampForce_ID;
990 case SDL_HAPTIC_SQUARE:
991 return kFFEffectType_Square_ID;
993 case SDL_HAPTIC_SINE:
994 return kFFEffectType_Sine_ID;
996 case SDL_HAPTIC_TRIANGLE:
997 return kFFEffectType_Triangle_ID;
999 case SDL_HAPTIC_SAWTOOTHUP:
1000 return kFFEffectType_SawtoothUp_ID;
1002 case SDL_HAPTIC_SAWTOOTHDOWN:
1003 return kFFEffectType_SawtoothDown_ID;
1005 case SDL_HAPTIC_SPRING:
1006 return kFFEffectType_Spring_ID;
1008 case SDL_HAPTIC_DAMPER:
1009 return kFFEffectType_Damper_ID;
1011 case SDL_HAPTIC_INERTIA:
1012 return kFFEffectType_Inertia_ID;
1014 case SDL_HAPTIC_FRICTION:
1015 return kFFEffectType_Friction_ID;
1017 case SDL_HAPTIC_CUSTOM:
1018 return kFFEffectType_CustomForce_ID;
1021 SDL_SetError("Haptic: Unknown effect type.");
1028 * Creates a new haptic effect.
1031 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1032 SDL_HapticEffect * base)
1037 /* Alloc the effect. */
1038 effect->hweffect = (struct haptic_hweffect *)
1039 SDL_malloc(sizeof(struct haptic_hweffect));
1040 if (effect->hweffect == NULL) {
1046 type = SDL_SYS_HapticEffectType(base->type);
1051 /* Get the effect. */
1052 if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
1053 goto err_effectdone;
1056 /* Create the actual effect. */
1057 ret = FFDeviceCreateEffect(haptic->hwdata->device, type,
1058 &effect->hweffect->effect,
1059 &effect->hweffect->ref);
1061 SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret));
1062 goto err_effectdone;
1068 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type);
1070 if (effect->hweffect != NULL) {
1071 SDL_free(effect->hweffect);
1072 effect->hweffect = NULL;
1079 * Updates an effect.
1082 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
1083 struct haptic_effect *effect,
1084 SDL_HapticEffect * data)
1087 FFEffectParameterFlag flags;
1090 /* Get the effect. */
1091 SDL_memset(&temp, 0, sizeof(FFEFFECT));
1092 if (SDL_SYS_ToFFEFFECT(haptic, &temp, data) < 0) {
1096 /* Set the flags. Might be worthwhile to diff temp with loaded effect and
1097 * only change those parameters. */
1098 flags = FFEP_DIRECTION |
1102 FFEP_TRIGGERBUTTON |
1103 FFEP_TRIGGERREPEATINTERVAL | FFEP_TYPESPECIFICPARAMS;
1105 /* Create the actual effect. */
1106 ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags);
1108 SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret));
1113 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type);
1114 SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT));
1119 SDL_SYS_HapticFreeFFEFFECT(&temp, data->type);
1128 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1134 /* Check if it's infinite. */
1135 if (iterations == SDL_HAPTIC_INFINITY) {
1140 /* Run the effect. */
1141 ret = FFEffectStart(effect->hweffect->ref, iter, 0);
1143 SDL_SetError("Haptic: Unable to run the effect: %s.",
1156 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1160 ret = FFEffectStop(effect->hweffect->ref);
1162 SDL_SetError("Haptic: Unable to stop the effect: %s.",
1175 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1180 FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref);
1182 SDL_SetError("Haptic: Error removing the effect from the device: %s.",
1185 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect,
1186 effect->effect.type);
1187 SDL_free(effect->hweffect);
1188 effect->hweffect = NULL;
1193 * Gets the status of a haptic effect.
1196 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
1197 struct haptic_effect *effect)
1200 FFEffectStatusFlag status;
1202 ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status);
1204 SDL_SetError("Haptic: Unable to get effect status: %s.",
1211 return SDL_TRUE; /* Assume it's playing or emulated. */
1219 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
1224 val = gain * 100; /* Mac OS X uses 0 to 10,000 */
1226 FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
1227 FFPROP_FFGAIN, &val);
1229 SDL_SetError("Haptic: Error setting gain: %s.", FFStrError(ret));
1238 * Sets the autocentering.
1241 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
1246 /* Mac OS X only has 0 (off) and 1 (on) */
1247 if (autocenter == 0)
1252 ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
1253 FFPROP_AUTOCENTER, &val);
1255 SDL_SetError("Haptic: Error setting autocenter: %s.",
1265 * Pauses the device.
1268 SDL_SYS_HapticPause(SDL_Haptic * haptic)
1272 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1275 SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
1284 * Unpauses the device.
1287 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
1291 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1294 SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
1303 * Stops all currently playing effects.
1306 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
1310 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1313 SDL_SetError("Haptic: Error stopping device: %s.", FFStrError(ret));
1321 #endif /* SDL_HAPTIC_IOKIT */