Darwin haptic: Fixed a static analysis warning if axes==0.
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2015 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);
691 * Converts an SDL trigger button to an FFEFFECT trigger button.
694 FFGetTriggerButton(Uint16 button)
696 DWORD dwTriggerButton;
698 dwTriggerButton = FFEB_NOTRIGGER;
701 dwTriggerButton = FFJOFS_BUTTON(button - 1);
704 return dwTriggerButton;
709 * Sets the direction.
712 SDL_SYS_SetDirection(FFEFFECT * effect, SDL_HapticDirection * dir, int naxes)
716 /* Handle no axes a part. */
718 effect->dwFlags |= FFEFF_SPHERICAL; /* Set as default. */
719 effect->rglDirection = NULL;
724 rglDir = SDL_malloc(sizeof(LONG) * naxes);
725 if (rglDir == NULL) {
726 return SDL_OutOfMemory();
728 SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
729 effect->rglDirection = rglDir;
732 case SDL_HAPTIC_POLAR:
733 effect->dwFlags |= FFEFF_POLAR;
734 rglDir[0] = dir->dir[0];
736 case SDL_HAPTIC_CARTESIAN:
737 effect->dwFlags |= FFEFF_CARTESIAN;
738 rglDir[0] = dir->dir[0];
740 rglDir[1] = dir->dir[1];
743 rglDir[2] = dir->dir[2];
746 case SDL_HAPTIC_SPHERICAL:
747 effect->dwFlags |= FFEFF_SPHERICAL;
748 rglDir[0] = dir->dir[0];
750 rglDir[1] = dir->dir[1];
753 rglDir[2] = dir->dir[2];
758 return SDL_SetError("Haptic: Unknown direction type.");
763 /* Clamps and converts. */
764 #define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
766 #define CONVERT(x) (((x)*10000) / 0x7FFF)
768 * Creates the FFEFFECT from a SDL_HapticEffect.
771 SDL_SYS_ToFFEFFECT(SDL_Haptic * haptic, FFEFFECT * dest, SDL_HapticEffect * src)
774 FFCONSTANTFORCE *constant = NULL;
775 FFPERIODIC *periodic = NULL;
776 FFCONDITION *condition = NULL; /* Actually an array of conditions - one per axis. */
777 FFRAMPFORCE *ramp = NULL;
778 FFCUSTOMFORCE *custom = NULL;
779 FFENVELOPE *envelope = NULL;
780 SDL_HapticConstant *hap_constant = NULL;
781 SDL_HapticPeriodic *hap_periodic = NULL;
782 SDL_HapticCondition *hap_condition = NULL;
783 SDL_HapticRamp *hap_ramp = NULL;
784 SDL_HapticCustom *hap_custom = NULL;
787 /* Set global stuff. */
788 SDL_memset(dest, 0, sizeof(FFEFFECT));
789 dest->dwSize = sizeof(FFEFFECT); /* Set the structure size. */
790 dest->dwSamplePeriod = 0; /* Not used by us. */
791 dest->dwGain = 10000; /* Gain is set globally, not locally. */
792 dest->dwFlags = FFEFF_OBJECTOFFSETS; /* Seems obligatory. */
795 envelope = SDL_malloc(sizeof(FFENVELOPE));
796 if (envelope == NULL) {
797 return SDL_OutOfMemory();
799 SDL_memset(envelope, 0, sizeof(FFENVELOPE));
800 dest->lpEnvelope = envelope;
801 envelope->dwSize = sizeof(FFENVELOPE); /* Always should be this. */
804 dest->cAxes = haptic->naxes;
805 if (dest->cAxes > 0) {
806 axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
808 return SDL_OutOfMemory();
810 axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */
811 if (dest->cAxes > 1) {
812 axes[1] = haptic->hwdata->axes[1];
814 if (dest->cAxes > 2) {
815 axes[2] = haptic->hwdata->axes[2];
817 dest->rgdwAxes = axes;
821 /* The big type handling switch, even bigger then Linux's version. */
823 case SDL_HAPTIC_CONSTANT:
824 hap_constant = &src->constant;
825 constant = SDL_malloc(sizeof(FFCONSTANTFORCE));
826 if (constant == NULL) {
827 return SDL_OutOfMemory();
829 SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE));
832 constant->lMagnitude = CONVERT(hap_constant->level);
833 dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE);
834 dest->lpvTypeSpecificParams = constant;
837 dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
838 dest->dwTriggerButton = FFGetTriggerButton(hap_constant->button);
839 dest->dwTriggerRepeatInterval = hap_constant->interval;
840 dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */
843 if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
849 if ((hap_constant->attack_length == 0)
850 && (hap_constant->fade_length == 0)) {
852 dest->lpEnvelope = NULL;
854 envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level);
855 envelope->dwAttackTime = hap_constant->attack_length * 1000;
856 envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level);
857 envelope->dwFadeTime = hap_constant->fade_length * 1000;
862 case SDL_HAPTIC_SINE:
863 /* !!! FIXME: put this back when we have more bits in 2.1 */
864 /* case SDL_HAPTIC_SQUARE: */
865 case SDL_HAPTIC_TRIANGLE:
866 case SDL_HAPTIC_SAWTOOTHUP:
867 case SDL_HAPTIC_SAWTOOTHDOWN:
868 hap_periodic = &src->periodic;
869 periodic = SDL_malloc(sizeof(FFPERIODIC));
870 if (periodic == NULL) {
871 return SDL_OutOfMemory();
873 SDL_memset(periodic, 0, sizeof(FFPERIODIC));
876 periodic->dwMagnitude = CONVERT(SDL_abs(hap_periodic->magnitude));
877 periodic->lOffset = CONVERT(hap_periodic->offset);
879 (hap_periodic->phase + (hap_periodic->magnitude < 0 ? 18000 : 0)) % 36000;
880 periodic->dwPeriod = hap_periodic->period * 1000;
881 dest->cbTypeSpecificParams = sizeof(FFPERIODIC);
882 dest->lpvTypeSpecificParams = periodic;
885 dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
886 dest->dwTriggerButton = FFGetTriggerButton(hap_periodic->button);
887 dest->dwTriggerRepeatInterval = hap_periodic->interval;
888 dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */
891 if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
897 if ((hap_periodic->attack_length == 0)
898 && (hap_periodic->fade_length == 0)) {
900 dest->lpEnvelope = NULL;
902 envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level);
903 envelope->dwAttackTime = hap_periodic->attack_length * 1000;
904 envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level);
905 envelope->dwFadeTime = hap_periodic->fade_length * 1000;
910 case SDL_HAPTIC_SPRING:
911 case SDL_HAPTIC_DAMPER:
912 case SDL_HAPTIC_INERTIA:
913 case SDL_HAPTIC_FRICTION:
914 if (dest->cAxes > 0) {
915 hap_condition = &src->condition;
916 condition = SDL_malloc(sizeof(FFCONDITION) * dest->cAxes);
917 if (condition == NULL) {
918 return SDL_OutOfMemory();
920 SDL_memset(condition, 0, sizeof(FFCONDITION));
923 for (i = 0; i < dest->cAxes; i++) {
924 condition[i].lOffset = CONVERT(hap_condition->center[i]);
925 condition[i].lPositiveCoefficient =
926 CONVERT(hap_condition->right_coeff[i]);
927 condition[i].lNegativeCoefficient =
928 CONVERT(hap_condition->left_coeff[i]);
929 condition[i].dwPositiveSaturation =
930 CCONVERT(hap_condition->right_sat[i] / 2);
931 condition[i].dwNegativeSaturation =
932 CCONVERT(hap_condition->left_sat[i] / 2);
933 condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i] / 2);
937 dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes;
938 dest->lpvTypeSpecificParams = condition;
941 dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */
942 dest->dwTriggerButton = FFGetTriggerButton(hap_condition->button);
943 dest->dwTriggerRepeatInterval = hap_condition->interval;
944 dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */
947 if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
952 /* Envelope - Not actually supported by most CONDITION implementations. */
953 SDL_free(dest->lpEnvelope);
954 dest->lpEnvelope = NULL;
958 case SDL_HAPTIC_RAMP:
959 hap_ramp = &src->ramp;
960 ramp = SDL_malloc(sizeof(FFRAMPFORCE));
962 return SDL_OutOfMemory();
964 SDL_memset(ramp, 0, sizeof(FFRAMPFORCE));
967 ramp->lStart = CONVERT(hap_ramp->start);
968 ramp->lEnd = CONVERT(hap_ramp->end);
969 dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE);
970 dest->lpvTypeSpecificParams = ramp;
973 dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */
974 dest->dwTriggerButton = FFGetTriggerButton(hap_ramp->button);
975 dest->dwTriggerRepeatInterval = hap_ramp->interval;
976 dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */
979 if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
984 if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
986 dest->lpEnvelope = NULL;
988 envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level);
989 envelope->dwAttackTime = hap_ramp->attack_length * 1000;
990 envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level);
991 envelope->dwFadeTime = hap_ramp->fade_length * 1000;
996 case SDL_HAPTIC_CUSTOM:
997 hap_custom = &src->custom;
998 custom = SDL_malloc(sizeof(FFCUSTOMFORCE));
999 if (custom == NULL) {
1000 return SDL_OutOfMemory();
1002 SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE));
1005 custom->cChannels = hap_custom->channels;
1006 custom->dwSamplePeriod = hap_custom->period * 1000;
1007 custom->cSamples = hap_custom->samples;
1008 custom->rglForceData =
1009 SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
1010 for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */
1011 custom->rglForceData[i] = CCONVERT(hap_custom->data[i]);
1013 dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE);
1014 dest->lpvTypeSpecificParams = custom;
1017 dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */
1018 dest->dwTriggerButton = FFGetTriggerButton(hap_custom->button);
1019 dest->dwTriggerRepeatInterval = hap_custom->interval;
1020 dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */
1023 if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
1029 if ((hap_custom->attack_length == 0)
1030 && (hap_custom->fade_length == 0)) {
1032 dest->lpEnvelope = NULL;
1034 envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level);
1035 envelope->dwAttackTime = hap_custom->attack_length * 1000;
1036 envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level);
1037 envelope->dwFadeTime = hap_custom->fade_length * 1000;
1044 return SDL_SetError("Haptic: Unknown effect type.");
1052 * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT.
1055 SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type)
1057 FFCUSTOMFORCE *custom;
1059 SDL_free(effect->lpEnvelope);
1060 effect->lpEnvelope = NULL;
1061 SDL_free(effect->rgdwAxes);
1062 effect->rgdwAxes = NULL;
1063 if (effect->lpvTypeSpecificParams != NULL) {
1064 if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */
1065 custom = (FFCUSTOMFORCE *) effect->lpvTypeSpecificParams;
1066 SDL_free(custom->rglForceData);
1067 custom->rglForceData = NULL;
1069 SDL_free(effect->lpvTypeSpecificParams);
1070 effect->lpvTypeSpecificParams = NULL;
1072 SDL_free(effect->rglDirection);
1073 effect->rglDirection = NULL;
1078 * Gets the effect type from the generic SDL haptic effect wrapper.
1081 SDL_SYS_HapticEffectType(Uint16 type)
1084 case SDL_HAPTIC_CONSTANT:
1085 return kFFEffectType_ConstantForce_ID;
1087 case SDL_HAPTIC_RAMP:
1088 return kFFEffectType_RampForce_ID;
1090 /* !!! FIXME: put this back when we have more bits in 2.1 */
1091 /* case SDL_HAPTIC_SQUARE:
1092 return kFFEffectType_Square_ID; */
1094 case SDL_HAPTIC_SINE:
1095 return kFFEffectType_Sine_ID;
1097 case SDL_HAPTIC_TRIANGLE:
1098 return kFFEffectType_Triangle_ID;
1100 case SDL_HAPTIC_SAWTOOTHUP:
1101 return kFFEffectType_SawtoothUp_ID;
1103 case SDL_HAPTIC_SAWTOOTHDOWN:
1104 return kFFEffectType_SawtoothDown_ID;
1106 case SDL_HAPTIC_SPRING:
1107 return kFFEffectType_Spring_ID;
1109 case SDL_HAPTIC_DAMPER:
1110 return kFFEffectType_Damper_ID;
1112 case SDL_HAPTIC_INERTIA:
1113 return kFFEffectType_Inertia_ID;
1115 case SDL_HAPTIC_FRICTION:
1116 return kFFEffectType_Friction_ID;
1118 case SDL_HAPTIC_CUSTOM:
1119 return kFFEffectType_CustomForce_ID;
1122 SDL_SetError("Haptic: Unknown effect type.");
1129 * Creates a new haptic effect.
1132 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1133 SDL_HapticEffect * base)
1138 /* Alloc the effect. */
1139 effect->hweffect = (struct haptic_hweffect *)
1140 SDL_malloc(sizeof(struct haptic_hweffect));
1141 if (effect->hweffect == NULL) {
1147 type = SDL_SYS_HapticEffectType(base->type);
1152 /* Get the effect. */
1153 if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
1154 goto err_effectdone;
1157 /* Create the actual effect. */
1158 ret = FFDeviceCreateEffect(haptic->hwdata->device, type,
1159 &effect->hweffect->effect,
1160 &effect->hweffect->ref);
1162 SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret));
1163 goto err_effectdone;
1169 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type);
1171 SDL_free(effect->hweffect);
1172 effect->hweffect = NULL;
1178 * Updates an effect.
1181 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
1182 struct haptic_effect *effect,
1183 SDL_HapticEffect * data)
1186 FFEffectParameterFlag flags;
1189 /* Get the effect. */
1190 SDL_memset(&temp, 0, sizeof(FFEFFECT));
1191 if (SDL_SYS_ToFFEFFECT(haptic, &temp, data) < 0) {
1195 /* Set the flags. Might be worthwhile to diff temp with loaded effect and
1196 * only change those parameters. */
1197 flags = FFEP_DIRECTION |
1201 FFEP_TRIGGERBUTTON |
1202 FFEP_TRIGGERREPEATINTERVAL | FFEP_TYPESPECIFICPARAMS;
1204 /* Create the actual effect. */
1205 ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags);
1207 SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret));
1212 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type);
1213 SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT));
1218 SDL_SYS_HapticFreeFFEFFECT(&temp, data->type);
1227 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1233 /* Check if it's infinite. */
1234 if (iterations == SDL_HAPTIC_INFINITY) {
1239 /* Run the effect. */
1240 ret = FFEffectStart(effect->hweffect->ref, iter, 0);
1242 return SDL_SetError("Haptic: Unable to run the effect: %s.",
1254 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1258 ret = FFEffectStop(effect->hweffect->ref);
1260 return SDL_SetError("Haptic: Unable to stop the effect: %s.",
1272 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1276 ret = FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref);
1278 SDL_SetError("Haptic: Error removing the effect from the device: %s.",
1281 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect,
1282 effect->effect.type);
1283 SDL_free(effect->hweffect);
1284 effect->hweffect = NULL;
1289 * Gets the status of a haptic effect.
1292 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
1293 struct haptic_effect *effect)
1296 FFEffectStatusFlag status;
1298 ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status);
1300 SDL_SetError("Haptic: Unable to get effect status: %s.",
1308 return SDL_TRUE; /* Assume it's playing or emulated. */
1316 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
1321 val = gain * 100; /* Mac OS X uses 0 to 10,000 */
1322 ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
1323 FFPROP_FFGAIN, &val);
1325 return SDL_SetError("Haptic: Error setting gain: %s.", FFStrError(ret));
1333 * Sets the autocentering.
1336 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
1341 /* Mac OS X only has 0 (off) and 1 (on) */
1342 if (autocenter == 0) {
1348 ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
1349 FFPROP_AUTOCENTER, &val);
1351 return SDL_SetError("Haptic: Error setting autocenter: %s.",
1360 * Pauses the device.
1363 SDL_SYS_HapticPause(SDL_Haptic * haptic)
1367 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1370 return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
1378 * Unpauses the device.
1381 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
1385 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1388 return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
1396 * Stops all currently playing effects.
1399 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
1403 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1406 return SDL_SetError("Haptic: Error stopping device: %s.", FFStrError(ret));
1413 #endif /* SDL_HAPTIC_IOKIT */