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