2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../../SDL_internal.h"
23 #ifdef SDL_HAPTIC_IOKIT
25 #include "SDL_assert.h"
26 #include "SDL_stdinc.h"
27 #include "SDL_haptic.h"
28 #include "../SDL_syshaptic.h"
29 #include "SDL_joystick.h"
30 #include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */
31 #include "../../joystick/darwin/SDL_sysjoystick_c.h" /* For joystick hwdata */
32 #include "SDL_syshaptic_c.h"
34 #include <IOKit/IOKitLib.h>
35 #include <IOKit/hid/IOHIDKeys.h>
36 #include <IOKit/hid/IOHIDUsageTables.h>
37 #include <ForceFeedback/ForceFeedback.h>
38 #include <ForceFeedback/ForceFeedbackConstants.h>
40 #ifndef IO_OBJECT_NULL
41 #define IO_OBJECT_NULL ((io_service_t)0)
45 * List of available haptic devices.
47 typedef struct SDL_hapticlist_item
49 char name[256]; /* Name of the device. */
51 io_service_t dev; /* Node we use to create the device. */
52 SDL_Haptic *haptic; /* Haptic currently associated with it. */
54 /* Usage pages for determining if it's a mouse or not. */
58 struct SDL_hapticlist_item *next;
59 } SDL_hapticlist_item;
63 * Haptic system hardware data.
67 FFDeviceObjectReference device; /* Hardware device. */
73 * Haptic system effect data.
75 struct haptic_hweffect
77 FFEffectObjectReference ref; /* Reference. */
78 struct FFEFFECT effect; /* Hardware effect. */
84 static void SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type);
85 static int HIDGetDeviceProduct(io_service_t dev, char *name);
87 static SDL_hapticlist_item *SDL_hapticlist = NULL;
88 static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
89 static int numhaptics = -1;
92 * Like strerror but for force feedback errors.
95 FFStrError(unsigned int err)
98 case FFERR_DEVICEFULL:
100 /* This should be valid, but for some reason isn't defined... */
101 /* case FFERR_DEVICENOTREG:
102 return "device not registered"; */
103 case FFERR_DEVICEPAUSED:
104 return "device paused";
105 case FFERR_DEVICERELEASED:
106 return "device released";
107 case FFERR_EFFECTPLAYING:
108 return "effect playing";
109 case FFERR_EFFECTTYPEMISMATCH:
110 return "effect type mismatch";
111 case FFERR_EFFECTTYPENOTSUPPORTED:
112 return "effect type not supported";
114 return "undetermined error";
115 case FFERR_HASEFFECTS:
116 return "device has effects";
117 case FFERR_INCOMPLETEEFFECT:
118 return "incomplete effect";
120 return "internal fault";
121 case FFERR_INVALIDDOWNLOADID:
122 return "invalid download id";
123 case FFERR_INVALIDPARAM:
124 return "invalid parameter";
127 case FFERR_NOINTERFACE:
128 return "interface not supported";
129 case FFERR_NOTDOWNLOADED:
130 return "effect is not downloaded";
131 case FFERR_NOTINITIALIZED:
132 return "object has not been initialized";
133 case FFERR_OUTOFMEMORY:
134 return "out of memory";
135 case FFERR_UNPLUGGED:
136 return "device is unplugged";
137 case FFERR_UNSUPPORTED:
138 return "function call unsupported";
139 case FFERR_UNSUPPORTEDAXIS:
140 return "axis unsupported";
143 return "unknown error";
149 * Initializes the haptic subsystem.
152 SDL_SYS_HapticInit(void)
156 CFDictionaryRef match;
159 if (numhaptics != -1) {
160 return SDL_SetError("Haptic subsystem already initialized!");
164 /* Get HID devices. */
165 match = IOServiceMatching(kIOHIDDeviceKey);
167 return SDL_SetError("Haptic: Failed to get IOServiceMatching.");
170 /* Now search I/O Registry for matching devices. */
171 result = IOServiceGetMatchingServices(kIOMasterPortDefault, match, &iter);
172 if (result != kIOReturnSuccess) {
173 return SDL_SetError("Haptic: Couldn't create a HID object iterator.");
175 /* IOServiceGetMatchingServices consumes dictionary. */
177 if (!IOIteratorIsValid(iter)) { /* No iterator. */
181 while ((device = IOIteratorNext(iter)) != IO_OBJECT_NULL) {
182 MacHaptic_MaybeAddDevice(device);
183 /* always release as the AddDevice will retain IF it's a forcefeedback device */
184 IOObjectRelease(device);
186 IOObjectRelease(iter);
197 static SDL_hapticlist_item *
198 HapticByDevIndex(int device_index)
200 SDL_hapticlist_item *item = SDL_hapticlist;
202 if ((device_index < 0) || (device_index >= numhaptics)) {
206 while (device_index > 0) {
207 SDL_assert(item != NULL);
216 MacHaptic_MaybeAddDevice( io_object_t device )
219 CFMutableDictionaryRef hidProperties;
221 SDL_hapticlist_item *item;
223 if (numhaptics == -1) {
224 return -1; /* not initialized. We'll pick these up on enumeration if we init later. */
227 /* Check for force feedback. */
228 if (FFIsForceFeedback(device) != FF_OK) {
232 /* Make sure we don't already have it */
233 for (item = SDL_hapticlist; item ; item = item->next)
235 if (IOObjectIsEqualTo((io_object_t) item->dev, device)) {
241 item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item));
243 return SDL_SetError("Could not allocate haptic storage");
246 /* retain it as we are going to keep it around a while */
247 IOObjectRetain(device);
249 /* Set basic device data. */
250 HIDGetDeviceProduct(device, item->name);
253 /* Set usage pages. */
256 result = IORegistryEntryCreateCFProperties(device,
260 if ((result == KERN_SUCCESS) && hidProperties) {
261 refCF = CFDictionaryGetValue(hidProperties,
262 CFSTR(kIOHIDPrimaryUsagePageKey));
264 if (!CFNumberGetValue(refCF, kCFNumberLongType, &item->usagePage)) {
265 SDL_SetError("Haptic: Receiving device's usage page.");
267 refCF = CFDictionaryGetValue(hidProperties,
268 CFSTR(kIOHIDPrimaryUsageKey));
270 if (!CFNumberGetValue(refCF, kCFNumberLongType, &item->usage)) {
271 SDL_SetError("Haptic: Receiving device's usage.");
275 CFRelease(hidProperties);
278 if (SDL_hapticlist_tail == NULL) {
279 SDL_hapticlist = SDL_hapticlist_tail = item;
281 SDL_hapticlist_tail->next = item;
282 SDL_hapticlist_tail = item;
285 /* Device has been added. */
292 MacHaptic_MaybeRemoveDevice( io_object_t device )
294 SDL_hapticlist_item *item;
295 SDL_hapticlist_item *prev = NULL;
297 if (numhaptics == -1) {
298 return -1; /* not initialized. ignore this. */
301 for (item = SDL_hapticlist; item != NULL; item = item->next) {
302 /* found it, remove it. */
303 if (IOObjectIsEqualTo((io_object_t) item->dev, device)) {
304 const int retval = item->haptic ? item->haptic->index : -1;
307 prev->next = item->next;
309 SDL_assert(SDL_hapticlist == item);
310 SDL_hapticlist = item->next;
312 if (item == SDL_hapticlist_tail) {
313 SDL_hapticlist_tail = prev;
316 /* Need to decrement the haptic count */
318 /* !!! TODO: Send a haptic remove event? */
320 IOObjectRelease(item->dev);
331 * Return the name of a haptic device, does not need to be opened.
334 SDL_SYS_HapticName(int index)
336 SDL_hapticlist_item *item;
337 item = HapticByDevIndex(index);
342 * Gets the device's product name.
345 HIDGetDeviceProduct(io_service_t dev, char *name)
347 CFMutableDictionaryRef hidProperties, usbProperties;
348 io_registry_entry_t parent1, parent2;
351 hidProperties = usbProperties = 0;
353 ret = IORegistryEntryCreateCFProperties(dev, &hidProperties,
354 kCFAllocatorDefault, kNilOptions);
355 if ((ret != KERN_SUCCESS) || !hidProperties) {
356 return SDL_SetError("Haptic: Unable to create CFProperties.");
359 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
360 * get dictionary for USB properties: step up two levels and get CF dictionary for USB properties
363 IORegistryEntryGetParentEntry(dev, kIOServicePlane, &parent1))
365 IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2))
367 IORegistryEntryCreateCFProperties(parent2, &usbProperties,
373 * try hid dictionary first, if fail then go to USB dictionary
377 /* Get product name */
378 refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey));
380 refCF = CFDictionaryGetValue(usbProperties,
381 CFSTR("USB Product Name"));
384 if (!CFStringGetCString(refCF, name, 256,
385 CFStringGetSystemEncoding())) {
386 return SDL_SetError("Haptic: CFStringGetCString error retrieving pDevice->product.");
390 CFRelease(usbProperties);
392 return SDL_SetError("Haptic: IORegistryEntryCreateCFProperties failed to create usbProperties.");
396 if (kIOReturnSuccess != IOObjectRelease(parent2)) {
397 SDL_SetError("Haptic: IOObjectRelease error with parent2.");
399 if (kIOReturnSuccess != IOObjectRelease(parent1)) {
400 SDL_SetError("Haptic: IOObjectRelease error with parent1.");
403 return SDL_SetError("Haptic: Error getting registry entries.");
410 #define FF_TEST(ff, s) \
411 if (features.supportedEffects & (ff)) supported |= (s)
413 * Gets supported features.
416 GetSupportedFeatures(SDL_Haptic * haptic)
419 FFDeviceObjectReference device;
420 FFCAPABILITIES features;
421 unsigned int supported;
424 device = haptic->hwdata->device;
426 ret = FFDeviceGetForceFeedbackCapabilities(device, &features);
428 return SDL_SetError("Haptic: Unable to get device's supported features.");
433 /* Get maximum effects. */
434 haptic->neffects = features.storageCapacity;
435 haptic->nplaying = features.playbackCapacity;
437 /* Test for effects. */
438 FF_TEST(FFCAP_ET_CONSTANTFORCE, SDL_HAPTIC_CONSTANT);
439 FF_TEST(FFCAP_ET_RAMPFORCE, SDL_HAPTIC_RAMP);
440 /* !!! FIXME: put this back when we have more bits in 2.1 */
441 /* FF_TEST(FFCAP_ET_SQUARE, SDL_HAPTIC_SQUARE); */
442 FF_TEST(FFCAP_ET_SINE, SDL_HAPTIC_SINE);
443 FF_TEST(FFCAP_ET_TRIANGLE, SDL_HAPTIC_TRIANGLE);
444 FF_TEST(FFCAP_ET_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHUP);
445 FF_TEST(FFCAP_ET_SAWTOOTHDOWN, SDL_HAPTIC_SAWTOOTHDOWN);
446 FF_TEST(FFCAP_ET_SPRING, SDL_HAPTIC_SPRING);
447 FF_TEST(FFCAP_ET_DAMPER, SDL_HAPTIC_DAMPER);
448 FF_TEST(FFCAP_ET_INERTIA, SDL_HAPTIC_INERTIA);
449 FF_TEST(FFCAP_ET_FRICTION, SDL_HAPTIC_FRICTION);
450 FF_TEST(FFCAP_ET_CUSTOMFORCE, SDL_HAPTIC_CUSTOM);
452 /* Check if supports gain. */
453 ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_FFGAIN,
456 supported |= SDL_HAPTIC_GAIN;
457 } else if (ret != FFERR_UNSUPPORTED) {
458 return SDL_SetError("Haptic: Unable to get if device supports gain: %s.",
462 /* Checks if supports autocenter. */
463 ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_AUTOCENTER,
466 supported |= SDL_HAPTIC_AUTOCENTER;
467 } else if (ret != FFERR_UNSUPPORTED) {
469 ("Haptic: Unable to get if device supports autocenter: %s.",
473 /* Check for axes, we have an artificial limit on axes */
474 haptic->naxes = ((features.numFfAxes) > 3) ? 3 : features.numFfAxes;
475 /* Actually store the axes we want to use */
476 SDL_memcpy(haptic->hwdata->axes, features.ffAxes,
477 haptic->naxes * sizeof(Uint8));
479 /* Always supported features. */
480 supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE;
482 haptic->supported = supported;
488 * Opens the haptic device from the file descriptor.
491 SDL_SYS_HapticOpenFromService(SDL_Haptic * haptic, io_service_t service)
496 /* Allocate the hwdata */
497 haptic->hwdata = (struct haptic_hwdata *)
498 SDL_malloc(sizeof(*haptic->hwdata));
499 if (haptic->hwdata == NULL) {
503 SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
505 /* Open the device */
506 ret = FFCreateDevice(service, &haptic->hwdata->device);
508 SDL_SetError("Haptic: Unable to create device from service: %s.",
513 /* Get supported features. */
514 ret2 = GetSupportedFeatures(haptic);
520 /* Reset and then enable actuators. */
521 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
524 SDL_SetError("Haptic: Unable to reset device: %s.", FFStrError(ret));
527 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
528 FFSFFC_SETACTUATORSON);
530 SDL_SetError("Haptic: Unable to enable actuators: %s.",
536 /* Allocate effects memory. */
537 haptic->effects = (struct haptic_effect *)
538 SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
539 if (haptic->effects == NULL) {
543 /* Clear the memory */
544 SDL_memset(haptic->effects, 0,
545 sizeof(struct haptic_effect) * haptic->neffects);
551 FFReleaseDevice(haptic->hwdata->device);
553 if (haptic->hwdata != NULL) {
554 SDL_free(haptic->hwdata);
555 haptic->hwdata = NULL;
563 * Opens a haptic device for usage.
566 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
568 SDL_hapticlist_item *item;
569 item = HapticByDevIndex(haptic->index);
571 return SDL_SYS_HapticOpenFromService(haptic, item->dev);
576 * Opens a haptic device from first mouse it finds for usage.
579 SDL_SYS_HapticMouse(void)
581 int device_index = 0;
582 SDL_hapticlist_item *item;
584 for (item = SDL_hapticlist; item; item = item->next) {
585 if ((item->usagePage == kHIDPage_GenericDesktop) &&
586 (item->usage == kHIDUsage_GD_Mouse)) {
597 * Checks to see if a joystick has haptic features.
600 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
602 if (joystick->hwdata->ffservice != 0) {
610 * Checks to see if the haptic device and joystick are in reality the same.
613 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
615 if (IOObjectIsEqualTo((io_object_t) ((size_t)haptic->hwdata->device),
616 joystick->hwdata->ffservice)) {
624 * Opens a SDL_Haptic from a SDL_Joystick.
627 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
629 int device_index = 0;
630 SDL_hapticlist_item *item;
632 for (item = SDL_hapticlist; item; item = item->next) {
633 if (IOObjectIsEqualTo((io_object_t) item->dev,
634 joystick->hwdata->ffservice)) {
635 haptic->index = device_index;
641 return SDL_SYS_HapticOpenFromService(haptic, joystick->hwdata->ffservice);
646 * Closes the haptic device.
649 SDL_SYS_HapticClose(SDL_Haptic * haptic)
651 if (haptic->hwdata) {
654 SDL_free(haptic->effects);
655 haptic->effects = NULL;
656 haptic->neffects = 0;
659 FFReleaseDevice(haptic->hwdata->device);
662 SDL_free(haptic->hwdata);
663 haptic->hwdata = NULL;
669 * Clean up after system specific haptic stuff
672 SDL_SYS_HapticQuit(void)
674 SDL_hapticlist_item *item;
675 SDL_hapticlist_item *next = NULL;
677 for (item = SDL_hapticlist; item; item = next) {
679 /* Opened and not closed haptics are leaked, this is on purpose.
680 * Close your haptic devices after usage. */
682 /* Free the io_service_t */
683 IOObjectRelease(item->dev);
688 SDL_hapticlist = NULL;
689 SDL_hapticlist_tail = NULL;
694 * Converts an SDL trigger button to an FFEFFECT trigger button.
697 FFGetTriggerButton(Uint16 button)
699 DWORD dwTriggerButton;
701 dwTriggerButton = FFEB_NOTRIGGER;
704 dwTriggerButton = FFJOFS_BUTTON(button - 1);
707 return dwTriggerButton;
712 * Sets the direction.
715 SDL_SYS_SetDirection(FFEFFECT * effect, SDL_HapticDirection * dir, int naxes)
719 /* Handle no axes a part. */
721 effect->dwFlags |= FFEFF_SPHERICAL; /* Set as default. */
722 effect->rglDirection = NULL;
727 rglDir = SDL_malloc(sizeof(LONG) * naxes);
728 if (rglDir == NULL) {
729 return SDL_OutOfMemory();
731 SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
732 effect->rglDirection = rglDir;
735 case SDL_HAPTIC_POLAR:
736 effect->dwFlags |= FFEFF_POLAR;
737 rglDir[0] = dir->dir[0];
739 case SDL_HAPTIC_CARTESIAN:
740 effect->dwFlags |= FFEFF_CARTESIAN;
741 rglDir[0] = dir->dir[0];
743 rglDir[1] = dir->dir[1];
746 rglDir[2] = dir->dir[2];
749 case SDL_HAPTIC_SPHERICAL:
750 effect->dwFlags |= FFEFF_SPHERICAL;
751 rglDir[0] = dir->dir[0];
753 rglDir[1] = dir->dir[1];
756 rglDir[2] = dir->dir[2];
761 return SDL_SetError("Haptic: Unknown direction type.");
766 /* Clamps and converts. */
767 #define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
769 #define CONVERT(x) (((x)*10000) / 0x7FFF)
771 * Creates the FFEFFECT from a SDL_HapticEffect.
774 SDL_SYS_ToFFEFFECT(SDL_Haptic * haptic, FFEFFECT * dest, SDL_HapticEffect * src)
777 FFCONSTANTFORCE *constant = NULL;
778 FFPERIODIC *periodic = NULL;
779 FFCONDITION *condition = NULL; /* Actually an array of conditions - one per axis. */
780 FFRAMPFORCE *ramp = NULL;
781 FFCUSTOMFORCE *custom = NULL;
782 FFENVELOPE *envelope = NULL;
783 SDL_HapticConstant *hap_constant = NULL;
784 SDL_HapticPeriodic *hap_periodic = NULL;
785 SDL_HapticCondition *hap_condition = NULL;
786 SDL_HapticRamp *hap_ramp = NULL;
787 SDL_HapticCustom *hap_custom = NULL;
790 /* Set global stuff. */
791 SDL_memset(dest, 0, sizeof(FFEFFECT));
792 dest->dwSize = sizeof(FFEFFECT); /* Set the structure size. */
793 dest->dwSamplePeriod = 0; /* Not used by us. */
794 dest->dwGain = 10000; /* Gain is set globally, not locally. */
795 dest->dwFlags = FFEFF_OBJECTOFFSETS; /* Seems obligatory. */
798 envelope = SDL_malloc(sizeof(FFENVELOPE));
799 if (envelope == NULL) {
800 return SDL_OutOfMemory();
802 SDL_memset(envelope, 0, sizeof(FFENVELOPE));
803 dest->lpEnvelope = envelope;
804 envelope->dwSize = sizeof(FFENVELOPE); /* Always should be this. */
807 dest->cAxes = haptic->naxes;
808 if (dest->cAxes > 0) {
809 axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
811 return SDL_OutOfMemory();
813 axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */
814 if (dest->cAxes > 1) {
815 axes[1] = haptic->hwdata->axes[1];
817 if (dest->cAxes > 2) {
818 axes[2] = haptic->hwdata->axes[2];
820 dest->rgdwAxes = axes;
824 /* The big type handling switch, even bigger then Linux's version. */
826 case SDL_HAPTIC_CONSTANT:
827 hap_constant = &src->constant;
828 constant = SDL_malloc(sizeof(FFCONSTANTFORCE));
829 if (constant == NULL) {
830 return SDL_OutOfMemory();
832 SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE));
835 constant->lMagnitude = CONVERT(hap_constant->level);
836 dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE);
837 dest->lpvTypeSpecificParams = constant;
840 dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
841 dest->dwTriggerButton = FFGetTriggerButton(hap_constant->button);
842 dest->dwTriggerRepeatInterval = hap_constant->interval;
843 dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */
846 if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
852 if ((hap_constant->attack_length == 0)
853 && (hap_constant->fade_length == 0)) {
855 dest->lpEnvelope = NULL;
857 envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level);
858 envelope->dwAttackTime = hap_constant->attack_length * 1000;
859 envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level);
860 envelope->dwFadeTime = hap_constant->fade_length * 1000;
865 case SDL_HAPTIC_SINE:
866 /* !!! FIXME: put this back when we have more bits in 2.1 */
867 /* case SDL_HAPTIC_SQUARE: */
868 case SDL_HAPTIC_TRIANGLE:
869 case SDL_HAPTIC_SAWTOOTHUP:
870 case SDL_HAPTIC_SAWTOOTHDOWN:
871 hap_periodic = &src->periodic;
872 periodic = SDL_malloc(sizeof(FFPERIODIC));
873 if (periodic == NULL) {
874 return SDL_OutOfMemory();
876 SDL_memset(periodic, 0, sizeof(FFPERIODIC));
879 periodic->dwMagnitude = CONVERT(SDL_abs(hap_periodic->magnitude));
880 periodic->lOffset = CONVERT(hap_periodic->offset);
882 (hap_periodic->phase + (hap_periodic->magnitude < 0 ? 18000 : 0)) % 36000;
883 periodic->dwPeriod = hap_periodic->period * 1000;
884 dest->cbTypeSpecificParams = sizeof(FFPERIODIC);
885 dest->lpvTypeSpecificParams = periodic;
888 dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
889 dest->dwTriggerButton = FFGetTriggerButton(hap_periodic->button);
890 dest->dwTriggerRepeatInterval = hap_periodic->interval;
891 dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */
894 if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
900 if ((hap_periodic->attack_length == 0)
901 && (hap_periodic->fade_length == 0)) {
903 dest->lpEnvelope = NULL;
905 envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level);
906 envelope->dwAttackTime = hap_periodic->attack_length * 1000;
907 envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level);
908 envelope->dwFadeTime = hap_periodic->fade_length * 1000;
913 case SDL_HAPTIC_SPRING:
914 case SDL_HAPTIC_DAMPER:
915 case SDL_HAPTIC_INERTIA:
916 case SDL_HAPTIC_FRICTION:
917 hap_condition = &src->condition;
918 if (dest->cAxes > 0) {
919 condition = SDL_malloc(sizeof(FFCONDITION) * dest->cAxes);
920 if (condition == NULL) {
921 return SDL_OutOfMemory();
923 SDL_memset(condition, 0, sizeof(FFCONDITION));
926 for (i = 0; i < dest->cAxes; i++) {
927 condition[i].lOffset = CONVERT(hap_condition->center[i]);
928 condition[i].lPositiveCoefficient =
929 CONVERT(hap_condition->right_coeff[i]);
930 condition[i].lNegativeCoefficient =
931 CONVERT(hap_condition->left_coeff[i]);
932 condition[i].dwPositiveSaturation =
933 CCONVERT(hap_condition->right_sat[i] / 2);
934 condition[i].dwNegativeSaturation =
935 CCONVERT(hap_condition->left_sat[i] / 2);
936 condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i] / 2);
940 dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes;
941 dest->lpvTypeSpecificParams = condition;
944 dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */
945 dest->dwTriggerButton = FFGetTriggerButton(hap_condition->button);
946 dest->dwTriggerRepeatInterval = hap_condition->interval;
947 dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */
950 if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
955 /* Envelope - Not actually supported by most CONDITION implementations. */
956 SDL_free(dest->lpEnvelope);
957 dest->lpEnvelope = NULL;
961 case SDL_HAPTIC_RAMP:
962 hap_ramp = &src->ramp;
963 ramp = SDL_malloc(sizeof(FFRAMPFORCE));
965 return SDL_OutOfMemory();
967 SDL_memset(ramp, 0, sizeof(FFRAMPFORCE));
970 ramp->lStart = CONVERT(hap_ramp->start);
971 ramp->lEnd = CONVERT(hap_ramp->end);
972 dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE);
973 dest->lpvTypeSpecificParams = ramp;
976 dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */
977 dest->dwTriggerButton = FFGetTriggerButton(hap_ramp->button);
978 dest->dwTriggerRepeatInterval = hap_ramp->interval;
979 dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */
982 if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
987 if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
989 dest->lpEnvelope = NULL;
991 envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level);
992 envelope->dwAttackTime = hap_ramp->attack_length * 1000;
993 envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level);
994 envelope->dwFadeTime = hap_ramp->fade_length * 1000;
999 case SDL_HAPTIC_CUSTOM:
1000 hap_custom = &src->custom;
1001 custom = SDL_malloc(sizeof(FFCUSTOMFORCE));
1002 if (custom == NULL) {
1003 return SDL_OutOfMemory();
1005 SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE));
1008 custom->cChannels = hap_custom->channels;
1009 custom->dwSamplePeriod = hap_custom->period * 1000;
1010 custom->cSamples = hap_custom->samples;
1011 custom->rglForceData =
1012 SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
1013 for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */
1014 custom->rglForceData[i] = CCONVERT(hap_custom->data[i]);
1016 dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE);
1017 dest->lpvTypeSpecificParams = custom;
1020 dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */
1021 dest->dwTriggerButton = FFGetTriggerButton(hap_custom->button);
1022 dest->dwTriggerRepeatInterval = hap_custom->interval;
1023 dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */
1026 if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
1032 if ((hap_custom->attack_length == 0)
1033 && (hap_custom->fade_length == 0)) {
1035 dest->lpEnvelope = NULL;
1037 envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level);
1038 envelope->dwAttackTime = hap_custom->attack_length * 1000;
1039 envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level);
1040 envelope->dwFadeTime = hap_custom->fade_length * 1000;
1047 return SDL_SetError("Haptic: Unknown effect type.");
1055 * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT.
1058 SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type)
1060 FFCUSTOMFORCE *custom;
1062 SDL_free(effect->lpEnvelope);
1063 effect->lpEnvelope = NULL;
1064 SDL_free(effect->rgdwAxes);
1065 effect->rgdwAxes = NULL;
1066 if (effect->lpvTypeSpecificParams != NULL) {
1067 if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */
1068 custom = (FFCUSTOMFORCE *) effect->lpvTypeSpecificParams;
1069 SDL_free(custom->rglForceData);
1070 custom->rglForceData = NULL;
1072 SDL_free(effect->lpvTypeSpecificParams);
1073 effect->lpvTypeSpecificParams = NULL;
1075 SDL_free(effect->rglDirection);
1076 effect->rglDirection = NULL;
1081 * Gets the effect type from the generic SDL haptic effect wrapper.
1084 SDL_SYS_HapticEffectType(Uint16 type)
1087 case SDL_HAPTIC_CONSTANT:
1088 return kFFEffectType_ConstantForce_ID;
1090 case SDL_HAPTIC_RAMP:
1091 return kFFEffectType_RampForce_ID;
1093 /* !!! FIXME: put this back when we have more bits in 2.1 */
1094 /* case SDL_HAPTIC_SQUARE:
1095 return kFFEffectType_Square_ID; */
1097 case SDL_HAPTIC_SINE:
1098 return kFFEffectType_Sine_ID;
1100 case SDL_HAPTIC_TRIANGLE:
1101 return kFFEffectType_Triangle_ID;
1103 case SDL_HAPTIC_SAWTOOTHUP:
1104 return kFFEffectType_SawtoothUp_ID;
1106 case SDL_HAPTIC_SAWTOOTHDOWN:
1107 return kFFEffectType_SawtoothDown_ID;
1109 case SDL_HAPTIC_SPRING:
1110 return kFFEffectType_Spring_ID;
1112 case SDL_HAPTIC_DAMPER:
1113 return kFFEffectType_Damper_ID;
1115 case SDL_HAPTIC_INERTIA:
1116 return kFFEffectType_Inertia_ID;
1118 case SDL_HAPTIC_FRICTION:
1119 return kFFEffectType_Friction_ID;
1121 case SDL_HAPTIC_CUSTOM:
1122 return kFFEffectType_CustomForce_ID;
1125 SDL_SetError("Haptic: Unknown effect type.");
1132 * Creates a new haptic effect.
1135 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1136 SDL_HapticEffect * base)
1141 /* Alloc the effect. */
1142 effect->hweffect = (struct haptic_hweffect *)
1143 SDL_malloc(sizeof(struct haptic_hweffect));
1144 if (effect->hweffect == NULL) {
1150 type = SDL_SYS_HapticEffectType(base->type);
1155 /* Get the effect. */
1156 if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
1157 goto err_effectdone;
1160 /* Create the actual effect. */
1161 ret = FFDeviceCreateEffect(haptic->hwdata->device, type,
1162 &effect->hweffect->effect,
1163 &effect->hweffect->ref);
1165 SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret));
1166 goto err_effectdone;
1172 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type);
1174 SDL_free(effect->hweffect);
1175 effect->hweffect = NULL;
1181 * Updates an effect.
1184 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
1185 struct haptic_effect *effect,
1186 SDL_HapticEffect * data)
1189 FFEffectParameterFlag flags;
1192 /* Get the effect. */
1193 SDL_memset(&temp, 0, sizeof(FFEFFECT));
1194 if (SDL_SYS_ToFFEFFECT(haptic, &temp, data) < 0) {
1198 /* Set the flags. Might be worthwhile to diff temp with loaded effect and
1199 * only change those parameters. */
1200 flags = FFEP_DIRECTION |
1204 FFEP_TRIGGERBUTTON |
1205 FFEP_TRIGGERREPEATINTERVAL | FFEP_TYPESPECIFICPARAMS;
1207 /* Create the actual effect. */
1208 ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags);
1210 SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret));
1215 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type);
1216 SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT));
1221 SDL_SYS_HapticFreeFFEFFECT(&temp, data->type);
1230 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1236 /* Check if it's infinite. */
1237 if (iterations == SDL_HAPTIC_INFINITY) {
1242 /* Run the effect. */
1243 ret = FFEffectStart(effect->hweffect->ref, iter, 0);
1245 return SDL_SetError("Haptic: Unable to run the effect: %s.",
1257 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1261 ret = FFEffectStop(effect->hweffect->ref);
1263 return SDL_SetError("Haptic: Unable to stop the effect: %s.",
1275 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1279 ret = FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref);
1281 SDL_SetError("Haptic: Error removing the effect from the device: %s.",
1284 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect,
1285 effect->effect.type);
1286 SDL_free(effect->hweffect);
1287 effect->hweffect = NULL;
1292 * Gets the status of a haptic effect.
1295 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
1296 struct haptic_effect *effect)
1299 FFEffectStatusFlag status;
1301 ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status);
1303 SDL_SetError("Haptic: Unable to get effect status: %s.",
1311 return SDL_TRUE; /* Assume it's playing or emulated. */
1319 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
1324 val = gain * 100; /* Mac OS X uses 0 to 10,000 */
1325 ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
1326 FFPROP_FFGAIN, &val);
1328 return SDL_SetError("Haptic: Error setting gain: %s.", FFStrError(ret));
1336 * Sets the autocentering.
1339 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
1344 /* Mac OS X only has 0 (off) and 1 (on) */
1345 if (autocenter == 0) {
1351 ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
1352 FFPROP_AUTOCENTER, &val);
1354 return SDL_SetError("Haptic: Error setting autocenter: %s.",
1363 * Pauses the device.
1366 SDL_SYS_HapticPause(SDL_Haptic * haptic)
1370 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1373 return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
1381 * Unpauses the device.
1384 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
1388 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1391 return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
1399 * Stops all currently playing effects.
1402 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
1406 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1409 return SDL_SetError("Haptic: Error stopping device: %s.", FFStrError(ret));
1415 #endif /* SDL_HAPTIC_IOKIT */
1417 /* vi: set ts=4 sw=4 expandtab: */