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