Mercurial > sdl-ios-xcode
view src/haptic/darwin/SDL_syshaptic.c @ 4426:1bceff8f008f
Fixed bug #943
Ozkan Sezer 2010-02-06 12:31:06 PST
Hi:
Here are some small fixes for compiling SDL against mingw-w64.
(see http://mingw-w64.sourceforge.net/ . Despite the name, it
supports both win32 and win64.)
src/audio/windx5/directx.h and src/video/windx5/directx.h (both
SDL-1.2 and SDL-1.3.) I get compilation errors about some union
not having a member named u1 and alike, because of other system
headers being included before this one and them already defining
DUMMYUNIONNAME and stuff. This header probably assumes that those
stuff are defined in windef.h, but mingw-w64 headers define them
in _mingw.h. Easily fixed by moving NONAMELESSUNION definition to
the top of the file.
src/thread/win32/SDL_systhread.c (both SDL-1.2 and SDL-1.3.) :
The __GNUC__ case for pfnSDL_CurrentBeginThread is 32-bit centric
because _beginthreadex returns uintptr_t, not unsigned long which
is 32 bits in win64. Changing the return type to uintptr_t fixes
it.
video/SDL_blit.h (and configure.in) (SDL-1.3-only) : MinGW-w64
uses msvcrt version of _aligned_malloc and _aligned_free and
they are defined in intrin.h (similar to VC). Adding proper
ifdefs fixes it. (Notes about macros to check: __MINGW32__ is
defined for both mingw.org and for mingw-w64 for both win32 and
win64, __MINGW64__ is only defined for _WIN64, so __MINGW64__
can't be used to detect mingw-w64: including _mingw.h and then
checking for __MINGW64_VERSION_MAJOR does the trick.)
SDL_win32video.h (SDL-1.3-only) : Tweaked the VINWER definition
and location in order to avoid multiple redefinition warnings.
Hope these are useful. Thanks.
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Wed, 10 Mar 2010 15:02:58 +0000 |
parents | b717ce4dfe36 |
children |
line wrap: on
line source
/* SDL - Simple DirectMedia Layer Copyright (C) 2008 Edgar Simo This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Sam Lantinga slouken@libsdl.org */ #include "SDL_config.h" #ifdef SDL_HAPTIC_IOKIT #include "SDL_haptic.h" #include "../SDL_syshaptic.h" #include "SDL_joystick.h" #include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */ #include "../../joystick/darwin/SDL_sysjoystick_c.h" /* For joystick hwdata */ #include <IOKit/IOKitLib.h> #include <IOKit/hid/IOHIDKeys.h> #include <IOKit/hid/IOHIDUsageTables.h> #include <ForceFeedback/ForceFeedback.h> #include <ForceFeedback/ForceFeedbackConstants.h> #ifndef IO_OBJECT_NULL #define IO_OBJECT_NULL ((io_service_t)0) #endif #define MAX_HAPTICS 32 /* * List of available haptic devices. */ static struct { char name[256]; /* Name of the device. */ io_service_t dev; /* Node we use to create the device. */ SDL_Haptic *haptic; /* Haptic currently assosciated with it. */ /* Usage pages for determining if it's a mouse or not. */ long usage; long usagePage; } SDL_hapticlist[MAX_HAPTICS]; /* * Haptic system hardware data. */ struct haptic_hwdata { FFDeviceObjectReference device; /* Hardware device. */ UInt8 axes[3]; }; /* * Haptic system effect data. */ struct haptic_hweffect { FFEffectObjectReference ref; /* Reference. */ struct FFEFFECT effect; /* Hardware effect. */ }; /* * Prototypes. */ static void SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type); static int HIDGetDeviceProduct(io_service_t dev, char *name); /* * Like strerror but for force feedback errors. */ static const char * FFStrError(HRESULT err) { switch (err) { case FFERR_DEVICEFULL: return "device full"; /* This should be valid, but for some reason isn't defined... */ /*case FFERR_DEVICENOTREG: return "device not registered"; */ case FFERR_DEVICEPAUSED: return "device paused"; case FFERR_DEVICERELEASED: return "device released"; case FFERR_EFFECTPLAYING: return "effect playing"; case FFERR_EFFECTTYPEMISMATCH: return "effect type mismatch"; case FFERR_EFFECTTYPENOTSUPPORTED: return "effect type not supported"; case FFERR_GENERIC: return "undetermined error"; case FFERR_HASEFFECTS: return "device has effects"; case FFERR_INCOMPLETEEFFECT: return "incomplete effect"; case FFERR_INTERNAL: return "internal fault"; case FFERR_INVALIDDOWNLOADID: return "invalid download id"; case FFERR_INVALIDPARAM: return "invalid parameter"; case FFERR_MOREDATA: return "more data"; case FFERR_NOINTERFACE: return "interface not supported"; case FFERR_NOTDOWNLOADED: return "effect is not downloaded"; case FFERR_NOTINITIALIZED: return "object has not been initialized"; case FFERR_OUTOFMEMORY: return "out of memory"; case FFERR_UNPLUGGED: return "device is unplugged"; case FFERR_UNSUPPORTED: return "function call unsupported"; case FFERR_UNSUPPORTEDAXIS: return "axis unsupported"; default: return "unknown error"; } } /* * Initializes the haptic subsystem. */ int SDL_SYS_HapticInit(void) { int numhaptics; IOReturn result; io_iterator_t iter; CFDictionaryRef match; io_service_t device; CFMutableDictionaryRef hidProperties; CFTypeRef refCF; /* Clear all the memory. */ SDL_memset(SDL_hapticlist, 0, sizeof(SDL_hapticlist)); /* Get HID devices. */ match = IOServiceMatching(kIOHIDDeviceKey); if (match == NULL) { SDL_SetError("Haptic: Failed to get IOServiceMatching."); return -1; } /* Now search I/O Registry for matching devices. */ result = IOServiceGetMatchingServices(kIOMasterPortDefault, match, &iter); if (result != kIOReturnSuccess) { SDL_SetError("Haptic: Couldn't create a HID object iterator."); return -1; } /* IOServiceGetMatchingServices consumes dictionary. */ if (!IOIteratorIsValid(iter)) { /* No iterator. */ numhaptics = 0; return 0; } numhaptics = 0; while ((device = IOIteratorNext(iter)) != IO_OBJECT_NULL) { /* Check for force feedback. */ if (FFIsForceFeedback(device) == FF_OK) { /* Set basic device data. */ HIDGetDeviceProduct(device, SDL_hapticlist[numhaptics].name); SDL_hapticlist[numhaptics].dev = device; SDL_hapticlist[numhaptics].haptic = NULL; /* Set usage pages. */ hidProperties = 0; refCF = 0; result = IORegistryEntryCreateCFProperties(device, &hidProperties, kCFAllocatorDefault, kNilOptions); if ((result == KERN_SUCCESS) && hidProperties) { refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDPrimaryUsagePageKey)); if (refCF) { if (!CFNumberGetValue(refCF, kCFNumberLongType, &SDL_hapticlist[numhaptics]. usagePage)) SDL_SetError ("Haptic: Recieving device's usage page."); refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDPrimaryUsageKey)); if (refCF) { if (!CFNumberGetValue(refCF, kCFNumberLongType, &SDL_hapticlist[numhaptics]. usage)) SDL_SetError("Haptic: Recieving device's usage."); } } CFRelease(hidProperties); } /* Device has been added. */ numhaptics++; } else { /* Free the unused device. */ IOObjectRelease(device); } /* Reached haptic limit. */ if (numhaptics >= MAX_HAPTICS) break; } IOObjectRelease(iter); return numhaptics; } /* * Return the name of a haptic device, does not need to be opened. */ const char * SDL_SYS_HapticName(int index) { return SDL_hapticlist[index].name; } /* * Gets the device's product name. */ static int HIDGetDeviceProduct(io_service_t dev, char *name) { CFMutableDictionaryRef hidProperties, usbProperties; io_registry_entry_t parent1, parent2; kern_return_t ret; hidProperties = usbProperties = 0; ret = IORegistryEntryCreateCFProperties(dev, &hidProperties, kCFAllocatorDefault, kNilOptions); if ((ret != KERN_SUCCESS) || !hidProperties) { SDL_SetError("Haptic: Unable to create CFProperties."); return -1; } /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties */ if ((KERN_SUCCESS == IORegistryEntryGetParentEntry(dev, kIOServicePlane, &parent1)) && (KERN_SUCCESS == IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2)) && (KERN_SUCCESS == IORegistryEntryCreateCFProperties(parent2, &usbProperties, kCFAllocatorDefault, kNilOptions))) { if (usbProperties) { CFTypeRef refCF = 0; /* get device info * try hid dictionary first, if fail then go to usb dictionary */ /* Get product name */ refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey)); if (!refCF) refCF = CFDictionaryGetValue(usbProperties, CFSTR("USB Product Name")); if (refCF) { if (!CFStringGetCString(refCF, name, 256, CFStringGetSystemEncoding())) { SDL_SetError ("Haptic: CFStringGetCString error retrieving pDevice->product."); return -1; } } CFRelease(usbProperties); } else { SDL_SetError ("Haptic: IORegistryEntryCreateCFProperties failed to create usbProperties."); return -1; } /* Release stuff. */ if (kIOReturnSuccess != IOObjectRelease(parent2)) { SDL_SetError("Haptic: IOObjectRelease error with parent2."); } if (kIOReturnSuccess != IOObjectRelease(parent1)) { SDL_SetError("Haptic: IOObjectRelease error with parent1."); } } else { SDL_SetError("Haptic: Error getting registry entries."); return -1; } return 0; } #define FF_TEST(ff, s) \ if (features.supportedEffects & (ff)) supported |= (s) /* * Gets supported features. */ static unsigned int GetSupportedFeatures(SDL_Haptic * haptic) { HRESULT ret; FFDeviceObjectReference device; FFCAPABILITIES features; unsigned int supported; Uint32 val; device = haptic->hwdata->device; ret = FFDeviceGetForceFeedbackCapabilities(device, &features); if (ret != FF_OK) { SDL_SetError("Haptic: Unable to get device's supported features."); return -1; } supported = 0; /* Get maximum effects. */ haptic->neffects = features.storageCapacity; haptic->nplaying = features.playbackCapacity; /* Test for effects. */ FF_TEST(FFCAP_ET_CONSTANTFORCE, SDL_HAPTIC_CONSTANT); FF_TEST(FFCAP_ET_RAMPFORCE, SDL_HAPTIC_RAMP); FF_TEST(FFCAP_ET_SQUARE, SDL_HAPTIC_SQUARE); FF_TEST(FFCAP_ET_SINE, SDL_HAPTIC_SINE); FF_TEST(FFCAP_ET_TRIANGLE, SDL_HAPTIC_TRIANGLE); FF_TEST(FFCAP_ET_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHUP); FF_TEST(FFCAP_ET_SAWTOOTHDOWN, SDL_HAPTIC_SAWTOOTHDOWN); FF_TEST(FFCAP_ET_SPRING, SDL_HAPTIC_SPRING); FF_TEST(FFCAP_ET_DAMPER, SDL_HAPTIC_DAMPER); FF_TEST(FFCAP_ET_INERTIA, SDL_HAPTIC_INERTIA); FF_TEST(FFCAP_ET_FRICTION, SDL_HAPTIC_FRICTION); FF_TEST(FFCAP_ET_CUSTOMFORCE, SDL_HAPTIC_CUSTOM); /* Check if supports gain. */ ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_FFGAIN, &val, sizeof(val)); if (ret == FF_OK) supported |= SDL_HAPTIC_GAIN; else if (ret != FFERR_UNSUPPORTED) { SDL_SetError("Haptic: Unable to get if device supports gain: %s.", FFStrError(ret)); return -1; } /* Checks if supports autocenter. */ ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_AUTOCENTER, &val, sizeof(val)); if (ret == FF_OK) supported |= SDL_HAPTIC_AUTOCENTER; else if (ret != FFERR_UNSUPPORTED) { SDL_SetError ("Haptic: Unable to get if device supports autocenter: %s.", FFStrError(ret)); return -1; } /* Check for axes, we have an artificial limit on axes */ haptic->naxes = ((features.numFfAxes) > 3) ? 3 : features.numFfAxes; /* Actually store the axes we want to use */ SDL_memcpy(haptic->hwdata->axes, features.ffAxes, haptic->naxes * sizeof(Uint8)); /* Always supported features. */ supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE; haptic->supported = supported; return 0;; } /* * Opens the haptic device from the file descriptor. */ static int SDL_SYS_HapticOpenFromService(SDL_Haptic * haptic, io_service_t service) { HRESULT ret; int ret2; /* Allocate the hwdata */ haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata)); if (haptic->hwdata == NULL) { SDL_OutOfMemory(); goto creat_err; } SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata)); /* Open the device */ ret = FFCreateDevice(service, &haptic->hwdata->device); if (ret != FF_OK) { SDL_SetError("Haptic: Unable to create device from service: %s.", FFStrError(ret)); goto creat_err; } /* Get supported features. */ ret2 = GetSupportedFeatures(haptic); if (haptic->supported < 0) { goto open_err; } /* Reset and then enable actuators. */ ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, FFSFFC_RESET); if (ret != FF_OK) { SDL_SetError("Haptic: Unable to reset device: %s.", FFStrError(ret)); goto open_err; } ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, FFSFFC_SETACTUATORSON); if (ret != FF_OK) { SDL_SetError("Haptic: Unable to enable actuators: %s.", FFStrError(ret)); goto open_err; } /* Allocate effects memory. */ haptic->effects = (struct haptic_effect *) SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects); if (haptic->effects == NULL) { SDL_OutOfMemory(); goto open_err; } /* Clear the memory */ SDL_memset(haptic->effects, 0, sizeof(struct haptic_effect) * haptic->neffects); return 0; /* Error handling */ open_err: FFReleaseDevice(haptic->hwdata->device); creat_err: if (haptic->hwdata != NULL) { free(haptic->hwdata); haptic->hwdata = NULL; } return -1; } /* * Opens a haptic device for usage. */ int SDL_SYS_HapticOpen(SDL_Haptic * haptic) { return SDL_SYS_HapticOpenFromService(haptic, SDL_hapticlist[haptic->index].dev); } /* * Opens a haptic device from first mouse it finds for usage. */ int SDL_SYS_HapticMouse(void) { int i; for (i = 0; i < SDL_numhaptics; i++) { if ((SDL_hapticlist[i].usagePage == kHIDPage_GenericDesktop) && (SDL_hapticlist[i].usage == kHIDUsage_GD_Mouse)) return i; } return -1; } /* * Checks to see if a joystick has haptic features. */ int SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick) { if (joystick->hwdata->ffservice != 0) return SDL_TRUE; return SDL_FALSE; } /* * Checks to see if the haptic device and joystick and in reality the same. */ int SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick) { if (IOObjectIsEqualTo((io_object_t) haptic->hwdata->device, joystick->hwdata->ffservice)) return 1; return 0; } /* * Opens a SDL_Haptic from a SDL_Joystick. */ int SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick) { return SDL_SYS_HapticOpenFromService(haptic, joystick->hwdata->ffservice); } /* * Closes the haptic device. */ void SDL_SYS_HapticClose(SDL_Haptic * haptic) { if (haptic->hwdata) { /* Free Effects. */ SDL_free(haptic->effects); haptic->effects = NULL; haptic->neffects = 0; /* Clean up */ FFReleaseDevice(haptic->hwdata->device); /* Free */ SDL_free(haptic->hwdata); haptic->hwdata = NULL; } } /* * Clean up after system specific haptic stuff */ void SDL_SYS_HapticQuit(void) { int i; for (i = 0; i < SDL_numhaptics; i++) { /* Opened and not closed haptics are leaked, this is on purpose. * Close your haptic devices after usage. */ /* Free the io_service_t */ IOObjectRelease(SDL_hapticlist[i].dev); } } /* * Converts an SDL trigger button to an FFEFFECT trigger button. */ static DWORD FFGetTriggerButton(Uint16 button) { DWORD dwTriggerButton; dwTriggerButton = FFEB_NOTRIGGER; if (button != 0) { dwTriggerButton = FFJOFS_BUTTON(button - 1); } return dwTriggerButton; } /* * Sets the direction. */ static int SDL_SYS_SetDirection(FFEFFECT * effect, SDL_HapticDirection * dir, int naxes) { LONG *rglDir; /* Handle no axes a part. */ if (naxes == 0) { effect->dwFlags |= FFEFF_SPHERICAL; /* Set as default. */ effect->rglDirection = NULL; return 0; } /* Has axes. */ rglDir = SDL_malloc(sizeof(LONG) * naxes); if (rglDir == NULL) { SDL_OutOfMemory(); return -1; } SDL_memset(rglDir, 0, sizeof(LONG) * naxes); effect->rglDirection = rglDir; switch (dir->type) { case SDL_HAPTIC_POLAR: effect->dwFlags |= FFEFF_POLAR; rglDir[0] = dir->dir[0]; return 0; case SDL_HAPTIC_CARTESIAN: effect->dwFlags |= FFEFF_CARTESIAN; rglDir[0] = dir->dir[0]; if (naxes > 1) rglDir[1] = dir->dir[1]; if (naxes > 2) rglDir[2] = dir->dir[2]; return 0; case SDL_HAPTIC_SPHERICAL: effect->dwFlags |= FFEFF_SPHERICAL; rglDir[0] = dir->dir[0]; if (naxes > 1) rglDir[1] = dir->dir[1]; if (naxes > 2) rglDir[2] = dir->dir[2]; return 0; default: SDL_SetError("Haptic: Unknown direction type."); return -1; } } /* Clamps and converts. */ #define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF) /* Just converts. */ #define CONVERT(x) (((x)*10000) / 0x7FFF) /* * Creates the FFEFFECT from a SDL_HapticEffect. */ static int SDL_SYS_ToFFEFFECT(SDL_Haptic * haptic, FFEFFECT * dest, SDL_HapticEffect * src) { int i; FFCONSTANTFORCE *constant; FFPERIODIC *periodic; FFCONDITION *condition; /* Actually an array of conditions - one per axis. */ FFRAMPFORCE *ramp; FFCUSTOMFORCE *custom; FFENVELOPE *envelope; SDL_HapticConstant *hap_constant; SDL_HapticPeriodic *hap_periodic; SDL_HapticCondition *hap_condition; SDL_HapticRamp *hap_ramp; SDL_HapticCustom *hap_custom; DWORD *axes; /* Set global stuff. */ SDL_memset(dest, 0, sizeof(FFEFFECT)); dest->dwSize = sizeof(FFEFFECT); /* Set the structure size. */ dest->dwSamplePeriod = 0; /* Not used by us. */ dest->dwGain = 10000; /* Gain is set globally, not locally. */ dest->dwFlags = FFEFF_OBJECTOFFSETS; /* Seems obligatory. */ /* Envelope. */ envelope = SDL_malloc(sizeof(FFENVELOPE)); if (envelope == NULL) { SDL_OutOfMemory(); return -1; } SDL_memset(envelope, 0, sizeof(FFENVELOPE)); dest->lpEnvelope = envelope; envelope->dwSize = sizeof(FFENVELOPE); /* Always should be this. */ /* Axes. */ dest->cAxes = haptic->naxes; if (dest->cAxes > 0) { axes = SDL_malloc(sizeof(DWORD) * dest->cAxes); if (axes == NULL) { SDL_OutOfMemory(); return -1; } axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */ if (dest->cAxes > 1) { axes[1] = haptic->hwdata->axes[1]; } if (dest->cAxes > 2) { axes[2] = haptic->hwdata->axes[2]; } dest->rgdwAxes = axes; } /* The big type handling switch, even bigger then linux's version. */ switch (src->type) { case SDL_HAPTIC_CONSTANT: hap_constant = &src->constant; constant = SDL_malloc(sizeof(FFCONSTANTFORCE)); if (constant == NULL) { SDL_OutOfMemory(); return -1; } SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE)); /* Specifics */ constant->lMagnitude = CONVERT(hap_constant->level); dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE); dest->lpvTypeSpecificParams = constant; /* Generics */ dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */ dest->dwTriggerButton = FFGetTriggerButton(hap_constant->button); dest->dwTriggerRepeatInterval = hap_constant->interval; dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */ /* Direction. */ if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes) < 0) { return -1; } /* Envelope */ if ((hap_constant->attack_length == 0) && (hap_constant->fade_length == 0)) { SDL_free(envelope); dest->lpEnvelope = NULL; } else { envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level); envelope->dwAttackTime = hap_constant->attack_length * 1000; envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level); envelope->dwFadeTime = hap_constant->fade_length * 1000; } break; case SDL_HAPTIC_SINE: case SDL_HAPTIC_SQUARE: case SDL_HAPTIC_TRIANGLE: case SDL_HAPTIC_SAWTOOTHUP: case SDL_HAPTIC_SAWTOOTHDOWN: hap_periodic = &src->periodic; periodic = SDL_malloc(sizeof(FFPERIODIC)); if (periodic == NULL) { SDL_OutOfMemory(); return -1; } SDL_memset(periodic, 0, sizeof(FFPERIODIC)); /* Specifics */ periodic->dwMagnitude = CONVERT(hap_periodic->magnitude); periodic->lOffset = CONVERT(hap_periodic->offset); periodic->dwPhase = hap_periodic->phase; periodic->dwPeriod = hap_periodic->period * 1000; dest->cbTypeSpecificParams = sizeof(FFPERIODIC); dest->lpvTypeSpecificParams = periodic; /* Generics */ dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */ dest->dwTriggerButton = FFGetTriggerButton(hap_periodic->button); dest->dwTriggerRepeatInterval = hap_periodic->interval; dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */ /* Direction. */ if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes) < 0) { return -1; } /* Envelope */ if ((hap_periodic->attack_length == 0) && (hap_periodic->fade_length == 0)) { SDL_free(envelope); dest->lpEnvelope = NULL; } else { envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level); envelope->dwAttackTime = hap_periodic->attack_length * 1000; envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level); envelope->dwFadeTime = hap_periodic->fade_length * 1000; } break; case SDL_HAPTIC_SPRING: case SDL_HAPTIC_DAMPER: case SDL_HAPTIC_INERTIA: case SDL_HAPTIC_FRICTION: hap_condition = &src->condition; condition = SDL_malloc(sizeof(FFCONDITION) * dest->cAxes); if (condition == NULL) { SDL_OutOfMemory(); return -1; } SDL_memset(condition, 0, sizeof(FFCONDITION)); /* Specifics */ for (i = 0; i < dest->cAxes; i++) { condition[i].lOffset = CONVERT(hap_condition->center[i]); condition[i].lPositiveCoefficient = CONVERT(hap_condition->right_coeff[i]); condition[i].lNegativeCoefficient = CONVERT(hap_condition->left_coeff[i]); condition[i].dwPositiveSaturation = CCONVERT(hap_condition->right_sat[i]); condition[i].dwNegativeSaturation = CCONVERT(hap_condition->left_sat[i]); condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i]); } dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes; dest->lpvTypeSpecificParams = condition; /* Generics */ dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */ dest->dwTriggerButton = FFGetTriggerButton(hap_condition->button); dest->dwTriggerRepeatInterval = hap_condition->interval; dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */ /* Direction. */ if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes) < 0) { return -1; } /* Envelope - Not actually supported by most CONDITION implementations. */ SDL_free(dest->lpEnvelope); dest->lpEnvelope = NULL; break; case SDL_HAPTIC_RAMP: hap_ramp = &src->ramp; ramp = SDL_malloc(sizeof(FFRAMPFORCE)); if (ramp == NULL) { SDL_OutOfMemory(); return -1; } SDL_memset(ramp, 0, sizeof(FFRAMPFORCE)); /* Specifics */ ramp->lStart = CONVERT(hap_ramp->start); ramp->lEnd = CONVERT(hap_ramp->end); dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE); dest->lpvTypeSpecificParams = ramp; /* Generics */ dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */ dest->dwTriggerButton = FFGetTriggerButton(hap_ramp->button); dest->dwTriggerRepeatInterval = hap_ramp->interval; dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */ /* Direction. */ if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) { return -1; } /* Envelope */ if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) { SDL_free(envelope); dest->lpEnvelope = NULL; } else { envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level); envelope->dwAttackTime = hap_ramp->attack_length * 1000; envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level); envelope->dwFadeTime = hap_ramp->fade_length * 1000; } break; case SDL_HAPTIC_CUSTOM: hap_custom = &src->custom; custom = SDL_malloc(sizeof(FFCUSTOMFORCE)); if (custom == NULL) { SDL_OutOfMemory(); return -1; } SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE)); /* Specifics */ custom->cChannels = hap_custom->channels; custom->dwSamplePeriod = hap_custom->period * 1000; custom->cSamples = hap_custom->samples; custom->rglForceData = SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels); for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */ custom->rglForceData[i] = CCONVERT(hap_custom->data[i]); } dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE); dest->lpvTypeSpecificParams = custom; /* Generics */ dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */ dest->dwTriggerButton = FFGetTriggerButton(hap_custom->button); dest->dwTriggerRepeatInterval = hap_custom->interval; dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */ /* Direction. */ if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) < 0) { return -1; } /* Envelope */ if ((hap_custom->attack_length == 0) && (hap_custom->fade_length == 0)) { SDL_free(envelope); dest->lpEnvelope = NULL; } else { envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level); envelope->dwAttackTime = hap_custom->attack_length * 1000; envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level); envelope->dwFadeTime = hap_custom->fade_length * 1000; } break; default: SDL_SetError("Haptic: Unknown effect type."); return -1; } return 0; } /* * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT. */ static void SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type) { FFCUSTOMFORCE *custom; if (effect->lpEnvelope != NULL) { SDL_free(effect->lpEnvelope); effect->lpEnvelope = NULL; } if (effect->rgdwAxes != NULL) { SDL_free(effect->rgdwAxes); effect->rgdwAxes = NULL; } if (effect->lpvTypeSpecificParams != NULL) { if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */ custom = (FFCUSTOMFORCE *) effect->lpvTypeSpecificParams; SDL_free(custom->rglForceData); custom->rglForceData = NULL; } SDL_free(effect->lpvTypeSpecificParams); effect->lpvTypeSpecificParams = NULL; } if (effect->rglDirection != NULL) { SDL_free(effect->rglDirection); effect->rglDirection = NULL; } } /* * Gets the effect type from the generic SDL haptic effect wrapper. */ CFUUIDRef SDL_SYS_HapticEffectType(Uint16 type) { switch (type) { case SDL_HAPTIC_CONSTANT: return kFFEffectType_ConstantForce_ID; case SDL_HAPTIC_RAMP: return kFFEffectType_RampForce_ID; case SDL_HAPTIC_SQUARE: return kFFEffectType_Square_ID; case SDL_HAPTIC_SINE: return kFFEffectType_Sine_ID; case SDL_HAPTIC_TRIANGLE: return kFFEffectType_Triangle_ID; case SDL_HAPTIC_SAWTOOTHUP: return kFFEffectType_SawtoothUp_ID; case SDL_HAPTIC_SAWTOOTHDOWN: return kFFEffectType_SawtoothDown_ID; case SDL_HAPTIC_SPRING: return kFFEffectType_Spring_ID; case SDL_HAPTIC_DAMPER: return kFFEffectType_Damper_ID; case SDL_HAPTIC_INERTIA: return kFFEffectType_Inertia_ID; case SDL_HAPTIC_FRICTION: return kFFEffectType_Friction_ID; case SDL_HAPTIC_CUSTOM: return kFFEffectType_CustomForce_ID; default: SDL_SetError("Haptic: Unknown effect type."); return NULL; } } /* * Creates a new haptic effect. */ int SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base) { HRESULT ret; CFUUIDRef type; /* Alloc the effect. */ effect->hweffect = (struct haptic_hweffect *) SDL_malloc(sizeof(struct haptic_hweffect)); if (effect->hweffect == NULL) { SDL_OutOfMemory(); goto err_hweffect; } /* Get the type. */ type = SDL_SYS_HapticEffectType(base->type); if (type == NULL) { goto err_hweffect; } /* Get the effect. */ if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base) < 0) { goto err_effectdone; } /* Create the actual effect. */ ret = FFDeviceCreateEffect(haptic->hwdata->device, type, &effect->hweffect->effect, &effect->hweffect->ref); if (ret != FF_OK) { SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret)); goto err_effectdone; } return 0; err_effectdone: SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type); err_hweffect: if (effect->hweffect != NULL) { SDL_free(effect->hweffect); effect->hweffect = NULL; } return -1; } /* * Updates an effect. */ int SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * data) { HRESULT ret; FFEffectParameterFlag flags; FFEFFECT temp; /* Get the effect. */ SDL_memset(&temp, 0, sizeof(FFEFFECT)); if (SDL_SYS_ToFFEFFECT(haptic, &temp, data) < 0) { goto err_update; } /* Set the flags. Might be worthwhile to diff temp with loaded effect and * only change those parameters. */ flags = FFEP_DIRECTION | FFEP_DURATION | FFEP_ENVELOPE | FFEP_STARTDELAY | FFEP_TRIGGERBUTTON | FFEP_TRIGGERREPEATINTERVAL | FFEP_TYPESPECIFICPARAMS; /* Create the actual effect. */ ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags); if (ret != FF_OK) { SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret)); goto err_update; } /* Copy it over. */ SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type); SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT)); return 0; err_update: SDL_SYS_HapticFreeFFEFFECT(&temp, data->type); return -1; } /* * Runs an effect. */ int SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect, Uint32 iterations) { HRESULT ret; Uint32 iter; /* Check if it's infinite. */ if (iterations == SDL_HAPTIC_INFINITY) { iter = FF_INFINITE; } else iter = iterations; /* Run the effect. */ ret = FFEffectStart(effect->hweffect->ref, iter, 0); if (ret != FF_OK) { SDL_SetError("Haptic: Unable to run the effect: %s.", FFStrError(ret)); return -1; } return 0; } /* * Stops an effect. */ int SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect) { HRESULT ret; ret = FFEffectStop(effect->hweffect->ref); if (ret != FF_OK) { SDL_SetError("Haptic: Unable to stop the effect: %s.", FFStrError(ret)); return -1; } return 0; } /* * Frees the effect. */ void SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect) { HRESULT ret; ret = FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref); if (ret != FF_OK) { SDL_SetError("Haptic: Error removing the effect from the device: %s.", FFStrError(ret)); } SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, effect->effect.type); SDL_free(effect->hweffect); effect->hweffect = NULL; } /* * Gets the status of a haptic effect. */ int SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect) { HRESULT ret; FFEffectStatusFlag status; ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status); if (ret != FF_OK) { SDL_SetError("Haptic: Unable to get effect status: %s.", FFStrError(ret)); return -1; } if (status == 0) return SDL_FALSE; return SDL_TRUE; /* Assume it's playing or emulated. */ } /* * Sets the gain. */ int SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain) { HRESULT ret; Uint32 val; val = gain * 100; /* Mac OS X uses 0 to 10,000 */ ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device, FFPROP_FFGAIN, &val); if (ret != FF_OK) { SDL_SetError("Haptic: Error setting gain: %s.", FFStrError(ret)); return -1; } return 0; } /* * Sets the autocentering. */ int SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter) { HRESULT ret; Uint32 val; /* Mac OS X only has 0 (off) and 1 (on) */ if (autocenter == 0) val = 0; else val = 1; ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device, FFPROP_AUTOCENTER, &val); if (ret != FF_OK) { SDL_SetError("Haptic: Error setting autocenter: %s.", FFStrError(ret)); return -1; } return 0; } /* * Pauses the device. */ int SDL_SYS_HapticPause(SDL_Haptic * haptic) { HRESULT ret; ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, FFSFFC_PAUSE); if (ret != FF_OK) { SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret)); return -1; } return 0; } /* * Unpauses the device. */ int SDL_SYS_HapticUnpause(SDL_Haptic * haptic) { HRESULT ret; ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, FFSFFC_CONTINUE); if (ret != FF_OK) { SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret)); return -1; } return 0; } /* * Stops all currently playing effects. */ int SDL_SYS_HapticStopAll(SDL_Haptic * haptic) { HRESULT ret; ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device, FFSFFC_STOPALL); if (ret != FF_OK) { SDL_SetError("Haptic: Error stopping device: %s.", FFStrError(ret)); return -1; } return 0; } #endif /* SDL_HAPTIC_IOKIT */