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