src/haptic/darwin/SDL_syshaptic.c
author Edgar Simo <bobbens@gmail.com>
Sat, 19 Jul 2008 15:57:07 +0000
branchgsoc2008_force_feedback
changeset 2535 f0ed8471497d
parent 2534 e597de8dccd5
child 2537 8d92ec01f92f
permissions -rw-r--r--
Added custom effect with support on darwin.
     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    io_service_t dev;
    47    SDL_Haptic *haptic;
    48 } SDL_hapticlist[MAX_HAPTICS];
    49 
    50 
    51 /*
    52  * Haptic system hardware data.
    53  */
    54 struct haptic_hwdata
    55 {
    56    FFDeviceObjectReference device; /* Hardware device. */
    57 };
    58 
    59 
    60 /*
    61  * Haptic system effect data.
    62  */
    63 struct haptic_hweffect
    64 {
    65    FFEffectObjectReference ref; /* Reference. */
    66    struct FFEFFECT effect; /* Hardware effect. */
    67 };
    68 
    69 /*
    70  * Prototypes.
    71  */
    72 static void SDL_SYS_HapticFreeFFEFFECT( FFEFFECT * effect, int type );
    73 
    74 
    75 /*
    76  * Initializes the haptic subsystem.
    77  */
    78 int
    79 SDL_SYS_HapticInit(void)
    80 {
    81    int numhaptics;
    82    IOReturn result;
    83    io_iterator_t iter;
    84    CFDictionaryRef match;
    85    io_service_t device;
    86 
    87    /* Get HID devices. */
    88    match = IOServiceMatching(kIOHIDDeviceKey);
    89    if (match == NULL) {
    90       SDL_SetError("Haptic: Failed to get IOServiceMatching.");
    91       return -1;
    92    }
    93 
    94    /* Now search I/O Registry for matching devices. */
    95    result = IOServiceGetMatchingServices(kIOMasterPortDefault, match, &iter);
    96    if (result != kIOReturnSuccess) {
    97       SDL_SetError("Haptic: Couldn't create a HID object iterator.");
    98       return -1;
    99    }
   100    /* IOServiceGetMatchingServices consumes dictionary. */
   101 
   102    numhaptics = 0;
   103    while ((device = IOIteratorNext(iter)) != IO_OBJECT_NULL) {
   104 
   105       /* Check for force feedback. */
   106       if (FFIsForceFeedback(device) == FF_OK) {
   107          SDL_hapticlist[numhaptics].dev = device;
   108          SDL_hapticlist[numhaptics].haptic = NULL;
   109          numhaptics++;
   110       }
   111 
   112       /* Reached haptic limit. */
   113       if (numhaptics >= MAX_HAPTICS)
   114          break;
   115    }
   116    IOObjectRelease(iter);
   117 
   118    return numhaptics;
   119 }
   120 
   121 
   122 /*
   123  * Return the name of a haptic device, does not need to be opened.
   124  */
   125 const char *
   126 SDL_SYS_HapticName(int index)
   127 {
   128    return NULL;
   129 }
   130 
   131 
   132 #define FF_TEST(ff, s) \
   133 if (features.supportedEffects & ff) supported |= s
   134 /*
   135  * Gets supported features.
   136  */
   137 static unsigned int
   138 GetSupportedFeatures(FFDeviceObjectReference device,
   139                      int *neffects, int *nplaying, int *naxes)
   140 {
   141    HRESULT ret;
   142    FFCAPABILITIES features;
   143    unsigned int supported;
   144    Uint32 val;
   145 
   146    ret = FFDeviceGetForceFeedbackCapabilities(device, &features);
   147    if (ret != FF_OK) {
   148       SDL_SetError("Haptic: Unable to get device's supported features.");
   149       return 0;
   150    }
   151 
   152    supported = 0;
   153 
   154    /* Get maximum effects. */
   155    *neffects = features.storageCapacity;
   156    *nplaying = features.playbackCapacity;
   157 
   158    /* Test for effects. */
   159    FF_TEST(FFCAP_ET_CONSTANTFORCE, SDL_HAPTIC_CONSTANT);
   160    FF_TEST(FFCAP_ET_RAMPFORCE,     SDL_HAPTIC_RAMP);
   161    FF_TEST(FFCAP_ET_SQUARE,        SDL_HAPTIC_SQUARE);
   162    FF_TEST(FFCAP_ET_SINE,          SDL_HAPTIC_SINE);
   163    FF_TEST(FFCAP_ET_TRIANGLE,      SDL_HAPTIC_TRIANGLE);
   164    FF_TEST(FFCAP_ET_SAWTOOTHUP,    SDL_HAPTIC_SAWTOOTHUP);
   165    FF_TEST(FFCAP_ET_SAWTOOTHDOWN,  SDL_HAPTIC_SAWTOOTHDOWN);
   166    FF_TEST(FFCAP_ET_SPRING,        SDL_HAPTIC_SPRING);
   167    FF_TEST(FFCAP_ET_DAMPER,        SDL_HAPTIC_DAMPER);
   168    FF_TEST(FFCAP_ET_INERTIA,       SDL_HAPTIC_INERTIA);
   169    FF_TEST(FFCAP_ET_FRICTION,      SDL_HAPTIC_FRICTION);
   170    FF_TEST(FFCAP_ET_CUSTOMFORCE,   SDL_HAPTIC_CUSTOM);
   171 
   172    /* Check if supports gain. */
   173    ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_FFGAIN,
   174                                           &val, sizeof(val));
   175    if (ret == FF_OK) supported |= SDL_HAPTIC_GAIN;
   176    else if (ret != FFERR_UNSUPPORTED) {
   177       SDL_SetError("Haptic: Unable to get if device supports gain.");
   178       return 0;
   179    }
   180 
   181    /* Checks if supports autocenter. */
   182    ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_AUTOCENTER,
   183                                           &val, sizeof(val));
   184    if (ret == FF_OK) supported |= SDL_HAPTIC_AUTOCENTER;
   185    else if (ret != FFERR_UNSUPPORTED) {
   186       SDL_SetError("Haptic: Unable to get if device supports autocenter.");
   187       return 0;
   188    }
   189 
   190    /* Check for axes, we have an artificial limit on axes */
   191    *naxes = ((features.numFfAxes) > 3) ?
   192          3 : features.numFfAxes;
   193 
   194    /* Always supported features. */
   195    supported |= SDL_HAPTIC_STATUS;
   196    return supported;
   197 }
   198 
   199 
   200 /*
   201  * Opens the haptic device from the file descriptor.
   202  */
   203 static int
   204 SDL_SYS_HapticOpenFromService(SDL_Haptic * haptic, io_service_t service)
   205 {
   206    /* Allocate the hwdata */
   207    haptic->hwdata = (struct haptic_hwdata *)
   208          SDL_malloc(sizeof(*haptic->hwdata));
   209    if (haptic->hwdata == NULL) {
   210       SDL_OutOfMemory();
   211       goto creat_err;
   212    }
   213    SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
   214   
   215    /* Open the device */
   216    if (FFCreateDevice( service, &haptic->hwdata->device ) != FF_OK) {
   217       SDL_SetError("Haptic: Unable to create device from service.");
   218       goto creat_err;
   219    }
   220 
   221    /* Get supported features. */
   222    haptic->supported = GetSupportedFeatures(haptic->hwdata->device,
   223                                             &haptic->neffects, &haptic->nplaying,
   224                                             &haptic->naxes);
   225    if (haptic->supported == 0) { /* Error since device supports nothing. */
   226       goto open_err;
   227    }
   228    haptic->effects = (struct haptic_effect *)
   229          SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
   230    if (haptic->effects == NULL) {
   231       SDL_OutOfMemory();
   232       goto open_err;
   233    }
   234    /* Clear the memory */
   235    SDL_memset(haptic->effects, 0,
   236          sizeof(struct haptic_effect) * haptic->neffects);
   237    
   238    return 0;
   239    
   240    /* Error handling */
   241 open_err:
   242    FFReleaseDevice(haptic->hwdata->device);
   243 creat_err:
   244    if (haptic->hwdata != NULL) {
   245       free(haptic->hwdata);
   246       haptic->hwdata = NULL;                                              
   247    }
   248    return -1;
   249 
   250 }
   251 
   252 
   253 /*
   254  * Opens a haptic device for usage.
   255  */
   256 int
   257 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
   258 {
   259    return SDL_SYS_HapticOpenFromService(haptic,
   260                 SDL_hapticlist[haptic->index].dev);
   261 }
   262 
   263 
   264 /*
   265  * Opens a haptic device from first mouse it finds for usage.
   266  */
   267 int
   268 SDL_SYS_HapticMouse(void)
   269 {
   270    return -1;
   271 }
   272 
   273 
   274 /*
   275  * Checks to see if a joystick has haptic features.
   276  */
   277 int
   278 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
   279 {
   280    return SDL_FALSE;
   281 }
   282 
   283 
   284 /*
   285  * Checks to see if the haptic device and joystick and in reality the same.
   286  */
   287 int
   288 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
   289 {
   290    return 0;
   291 }
   292 
   293 
   294 /*
   295  * Opens a SDL_Haptic from a SDL_Joystick.
   296  */
   297 int
   298 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
   299 {
   300    return -1;
   301 }
   302 
   303 
   304 /*
   305  * Closes the haptic device.
   306  */
   307 void
   308 SDL_SYS_HapticClose(SDL_Haptic * haptic)
   309 {
   310    int i;
   311 
   312    if (haptic->hwdata) {
   313 
   314       /* Free the effects. */
   315       for (i=0; i<haptic->neffects; i++) {        
   316          if (haptic->effects[i].hweffect != NULL) {
   317             SDL_SYS_HapticFreeFFEFFECT(&haptic->effects[i].hweffect->effect,
   318                                        haptic->effects[i].effect.type);
   319             SDL_free(haptic->effects[i].hweffect);
   320          } 
   321       }    
   322       SDL_free(haptic->effects);
   323       haptic->neffects = 0;
   324 
   325       /* Clean up */
   326       FFReleaseDevice(haptic->hwdata->device);
   327 
   328       /* Free */
   329       SDL_free(haptic->hwdata);
   330       haptic->hwdata = NULL;
   331    }
   332 }
   333 
   334 
   335 /* 
   336  * Clean up after system specific haptic stuff
   337  */
   338 void
   339 SDL_SYS_HapticQuit(void)
   340 {
   341    int i;
   342 
   343    for (i=0; i < SDL_numhaptics; i++) {
   344       /* Opened and not closed haptics are leaked, this is on purpose.
   345        * Close your haptic devices after usage. */
   346 
   347       /* Free the io_service_t */
   348       IOObjectRelease(SDL_hapticlist[i].dev);
   349    }
   350 }
   351 
   352 
   353 /*
   354  * Sets the direction.
   355  */
   356 static int
   357 SDL_SYS_SetDirection( FFEFFECT * effect, SDL_HapticDirection *dir, int naxes )
   358 {
   359    LONG *rglDir;
   360 
   361    /* Handle no axes a part. */
   362    if (naxes == 0) {
   363       effect->rglDirection = NULL;
   364       return 0;
   365    }
   366 
   367    /* Has axes. */
   368    rglDir = SDL_malloc( sizeof(LONG) * naxes );
   369    if (rglDir == NULL) {
   370       SDL_OutOfMemory();
   371       return -1;
   372    }
   373    SDL_memset( rglDir, 0, sizeof(LONG) * naxes );
   374    effect->rglDirection = rglDir;
   375 
   376    switch (dir->type) {
   377       case SDL_HAPTIC_POLAR:
   378          effect->dwFlags |= FFEFF_POLAR;
   379          rglDir[0] = dir->dir[0];
   380          return 0;
   381       case SDL_HAPTIC_CARTESIAN:
   382          effect->dwFlags |= FFEFF_CARTESIAN;
   383          rglDir[0] = dir->dir[0];
   384          rglDir[1] = dir->dir[1];
   385          rglDir[2] = dir->dir[2];
   386          return 0;
   387       case SDL_HAPTIC_SPHERICAL:
   388          effect->dwFlags |= FFEFF_SPHERICAL;
   389          rglDir[0] = dir->dir[0];
   390          rglDir[1] = dir->dir[1];
   391          rglDir[2] = dir->dir[2];
   392          return 0;
   393 
   394       default:
   395          SDL_SetError("Haptic: Unknown direction type.");
   396          return -1;
   397    }
   398 }
   399 
   400 #define CONVERT(x)   (((x)*10000) / 0xFFFF )
   401 /*
   402  * Creates the FFEFFECT from a SDL_HapticEffect.
   403  */
   404 static int
   405 SDL_SYS_ToFFEFFECT( SDL_Haptic * haptic, FFEFFECT * dest, SDL_HapticEffect * src )
   406 {
   407    int i;
   408    FFCONSTANTFORCE *constant;
   409    FFPERIODIC *periodic;
   410    FFCONDITION *condition; /* Actually an array of conditions - one per axis. */
   411    FFRAMPFORCE *ramp;
   412    FFCUSTOMFORCE *custom;
   413    FFENVELOPE *envelope;
   414    SDL_HapticConstant *hap_constant;
   415    SDL_HapticPeriodic *hap_periodic;
   416    SDL_HapticCondition *hap_condition;
   417    SDL_HapticRamp *hap_ramp;
   418    SDL_HapticCustom *hap_custom;
   419    DWORD *axes;
   420 
   421    /* Set global stuff. */
   422    SDL_memset(dest, 0, sizeof(FFEFFECT));
   423    dest->dwSize = sizeof(FFEFFECT); /* Set the structure size. */
   424    dest->dwSamplePeriod = 0; /* Not used by us. */
   425    dest->dwGain = 10000; /* Gain is set globally, not locally. */
   426 
   427    /* Envelope. */
   428    envelope = SDL_malloc( sizeof(FFENVELOPE) );
   429    if (envelope == NULL) {
   430       SDL_OutOfMemory();
   431       return -1;
   432    }
   433    SDL_memset(envelope, 0, sizeof(FFENVELOPE));
   434    dest->lpEnvelope = envelope;
   435    envelope->dwSize = sizeof(FFENVELOPE); /* Always should be this. */
   436 
   437    /* Axes. */
   438    dest->cAxes = haptic->naxes;
   439    if (dest->cAxes > 0) {
   440       axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
   441       if (axes == NULL) {
   442          SDL_OutOfMemory();
   443          return -1;
   444       }
   445       axes[0] = FFJOFS_X; /* Always at least one axis. */
   446       if (dest->cAxes > 1) {
   447          axes[1] = FFJOFS_Y;
   448       }
   449       if (dest->cAxes > 2) {
   450          axes[2] = FFJOFS_Z;
   451       }
   452       dest->rgdwAxes = axes;
   453    }
   454 
   455 
   456    /* The big type handling switch, even bigger then linux's version. */
   457    switch (src->type) {
   458       case SDL_HAPTIC_CONSTANT:
   459          hap_constant = &src->constant;
   460          constant = SDL_malloc( sizeof(FFCONSTANTFORCE) );
   461          if (constant == NULL) {
   462             SDL_OutOfMemory();
   463             return -1;
   464          }
   465          SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE));
   466 
   467          /* Specifics */
   468          constant->lMagnitude = CONVERT(hap_constant->level);
   469          dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE); 
   470          dest->lpvTypeSpecificParams = constant;
   471 
   472          /* Generics */
   473          dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
   474          dest->dwTriggerButton = FFJOFS_BUTTON(hap_constant->button);
   475          dest->dwTriggerRepeatInterval = hap_constant->interval;
   476          dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */
   477 
   478          /* Direction. */
   479          if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes) < 0) {
   480             return -1;
   481          }
   482          
   483          /* Envelope */
   484          envelope->dwAttackLevel = CONVERT(hap_constant->attack_level);
   485          envelope->dwAttackTime = hap_constant->attack_length * 1000;
   486          envelope->dwFadeLevel = CONVERT(hap_constant->fade_level);
   487          envelope->dwFadeTime = hap_constant->fade_length * 1000;
   488 
   489          break;
   490 
   491       case SDL_HAPTIC_SINE:
   492       case SDL_HAPTIC_SQUARE:
   493       case SDL_HAPTIC_TRIANGLE:
   494       case SDL_HAPTIC_SAWTOOTHUP:
   495       case SDL_HAPTIC_SAWTOOTHDOWN:
   496          hap_periodic = &src->periodic;
   497          periodic = SDL_malloc(sizeof(FFPERIODIC));
   498          if (periodic == NULL) {
   499             SDL_OutOfMemory();
   500             return -1;
   501          }
   502          SDL_memset(periodic, 0, sizeof(FFPERIODIC));
   503 
   504          /* Specifics */
   505          periodic->dwMagnitude = CONVERT(hap_periodic->magnitude);
   506          periodic->lOffset = CONVERT(hap_periodic->offset);
   507          periodic->dwPhase = hap_periodic->phase;
   508          periodic->dwPeriod = hap_periodic->period * 1000;
   509          dest->cbTypeSpecificParams = sizeof(FFPERIODIC);
   510          dest->lpvTypeSpecificParams = periodic;
   511 
   512          /* Generics */
   513          dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
   514          dest->dwTriggerButton = FFJOFS_BUTTON(hap_periodic->button);
   515          dest->dwTriggerRepeatInterval = hap_periodic->interval;
   516          dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */
   517          
   518          /* Direction. */
   519          if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes) < 0) {
   520             return -1;
   521          }
   522          
   523          /* Envelope */
   524          envelope->dwAttackLevel = CONVERT(hap_periodic->attack_level);
   525          envelope->dwAttackTime = hap_periodic->attack_length * 1000;
   526          envelope->dwFadeLevel = CONVERT(hap_periodic->fade_level);
   527          envelope->dwFadeTime = hap_periodic->fade_length * 1000;
   528 
   529          break;
   530 
   531       case SDL_HAPTIC_SPRING:
   532       case SDL_HAPTIC_DAMPER:
   533       case SDL_HAPTIC_INERTIA:
   534       case SDL_HAPTIC_FRICTION:
   535          hap_condition = &src->condition;
   536          condition = SDL_malloc(sizeof(FFCONDITION) * dest->cAxes);
   537          if (condition == NULL) {
   538             SDL_OutOfMemory();
   539             return -1;
   540          }
   541          SDL_memset(condition, 0, sizeof(FFCONDITION));
   542 
   543          /* Specifics */
   544          for (i=0; i<dest->cAxes; i++) {
   545             condition[i].lOffset = CONVERT(hap_condition->center[i]);
   546             condition[i].lPositiveCoefficient = CONVERT(hap_condition->right_coeff[i]);
   547             condition[i].lNegativeCoefficient = CONVERT(hap_condition->left_coeff[i]);
   548             condition[i].dwPositiveSaturation = CONVERT(hap_condition->right_sat[i]);
   549             condition[i].dwNegativeSaturation = CONVERT(hap_condition->left_sat[i]);
   550             condition[i].lDeadBand = CONVERT(hap_condition->deadband[i]);
   551          }
   552          dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes;
   553          dest->lpvTypeSpecificParams = condition;
   554 
   555          /* Generics */
   556          dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */
   557          dest->dwTriggerButton = FFJOFS_BUTTON(hap_condition->button);
   558          dest->dwTriggerRepeatInterval = hap_condition->interval;
   559          dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */
   560 
   561          /* Direction. */
   562          if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes) < 0) {
   563             return -1;                
   564          }                            
   565                                       
   566          /* Envelope */
   567 /* TODO Check is envelope actually used.
   568          envelope->dwAttackLevel = CONVERT(hap_condition->attack_level);
   569          envelope->dwAttackTime = hap_condition->attack_length * 1000;
   570          envelope->dwFadeLevel = CONVERT(hap_condition->fade_level);
   571          envelope->dwFadeTime = hap_condition->fade_length * 1000;
   572 */
   573 
   574          break;
   575 
   576       case SDL_HAPTIC_RAMP:
   577          hap_ramp = &src->ramp;
   578          ramp = SDL_malloc(sizeof(FFRAMPFORCE));
   579          if (ramp == NULL) {
   580             SDL_OutOfMemory();
   581             return -1;
   582          }
   583          SDL_memset(ramp, 0, sizeof(FFRAMPFORCE));
   584 
   585          /* Specifics */
   586          ramp->lStart = CONVERT(hap_ramp->start);
   587          ramp->lEnd = CONVERT(hap_ramp->end);
   588          dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE);
   589          dest->lpvTypeSpecificParams = ramp;
   590 
   591          /* Generics */
   592          dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */
   593          dest->dwTriggerButton = FFJOFS_BUTTON(hap_ramp->button);
   594          dest->dwTriggerRepeatInterval = hap_ramp->interval;
   595          dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */
   596 
   597          /* Direction. */
   598          if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
   599             return -1;
   600          }
   601 
   602          /* Envelope */
   603          envelope->dwAttackLevel = CONVERT(hap_ramp->attack_level);
   604          envelope->dwAttackTime = hap_ramp->attack_length * 1000;
   605          envelope->dwFadeLevel = CONVERT(hap_ramp->fade_level);
   606          envelope->dwFadeTime = hap_ramp->fade_length * 1000;
   607 
   608          break;
   609 
   610       case SDL_HAPTIC_CUSTOM:
   611          hap_custom = &src->custom;
   612          custom = SDL_malloc(sizeof(FFCUSTOMFORCE));
   613          if (custom == NULL) {
   614             SDL_OutOfMemory();
   615             return -1;
   616          }
   617          SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE));
   618 
   619          /* Specifics */
   620          custom->cChannels = hap_custom->channels;
   621          custom->dwSamplePeriod = hap_custom->period * 1000;
   622          custom->cSamples = hap_custom->samples;
   623          custom->rglForceData = SDL_malloc(sizeof(LONG)*custom->cSamples*custom->cChannels);
   624          for (i=0; i<hap_custom->samples*hap_custom->channels; i++) { /* Copy data. */
   625             custom->rglForceData[i] = CONVERT(hap_custom->data[i]);
   626          }
   627          dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE);
   628          dest->lpvTypeSpecificParams = custom;
   629 
   630          /* Generics */
   631          dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */
   632          dest->dwTriggerButton = FFJOFS_BUTTON(hap_custom->button);
   633          dest->dwTriggerRepeatInterval = hap_custom->interval;
   634          dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */
   635 
   636          /* Direction. */
   637          if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) < 0) {
   638             return -1;
   639          }
   640          
   641          /* Envelope */
   642          envelope->dwAttackLevel = CONVERT(hap_custom->attack_level);
   643          envelope->dwAttackTime = hap_custom->attack_length * 1000;
   644          envelope->dwFadeLevel = CONVERT(hap_custom->fade_level);
   645          envelope->dwFadeTime = hap_custom->fade_length * 1000;
   646 
   647          break;
   648 
   649 
   650       default:
   651          SDL_SetError("Haptic: Unknown effect type.");
   652          return -1;
   653    }
   654 
   655    return 0;
   656 }
   657 
   658 
   659 /*
   660  * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT.
   661  */
   662 static void
   663 SDL_SYS_HapticFreeFFEFFECT( FFEFFECT * effect, int type )
   664 {
   665    FFCUSTOMFORCE *custom;
   666 
   667    if (effect->lpEnvelope != NULL) {
   668       SDL_free(effect->lpEnvelope);
   669       effect->lpEnvelope = NULL;
   670    }
   671    if (effect->rgdwAxes != NULL) {
   672       SDL_free(effect->rgdwAxes);
   673       effect->rgdwAxes = NULL;
   674    }
   675    if (effect->lpvTypeSpecificParams != NULL) {
   676       if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */
   677          custom = (FFCUSTOMFORCE*) effect->lpvTypeSpecificParams;
   678          SDL_free(custom->rglForceData);
   679          custom->rglForceData = NULL;
   680       }
   681       SDL_free(effect->lpvTypeSpecificParams);
   682       effect->lpvTypeSpecificParams = NULL;
   683    }
   684    if (effect->rglDirection != NULL) {
   685       SDL_free(effect->rglDirection);
   686       effect->rglDirection = NULL;
   687    }
   688 }
   689 
   690 
   691 /*
   692  * Gets the effect type from the generic SDL haptic effect wrapper.
   693  */
   694 CFUUIDRef
   695 SDL_SYS_HapticEffectType(struct haptic_effect * effect)
   696 {
   697    switch (effect->effect.type) {
   698       case SDL_HAPTIC_CONSTANT:
   699          return kFFEffectType_ConstantForce_ID;
   700 
   701       case SDL_HAPTIC_RAMP:
   702          return kFFEffectType_RampForce_ID;
   703 
   704       case SDL_HAPTIC_SQUARE:
   705          return kFFEffectType_Square_ID;
   706 
   707       case SDL_HAPTIC_SINE:
   708          return kFFEffectType_Sine_ID;
   709 
   710       case SDL_HAPTIC_TRIANGLE:
   711          return kFFEffectType_Triangle_ID;
   712 
   713       case SDL_HAPTIC_SAWTOOTHUP:
   714          return kFFEffectType_SawtoothUp_ID;
   715 
   716       case SDL_HAPTIC_SAWTOOTHDOWN:
   717          return kFFEffectType_SawtoothDown_ID;
   718 
   719       case SDL_HAPTIC_SPRING:
   720          return kFFEffectType_Spring_ID;
   721 
   722       case SDL_HAPTIC_DAMPER:
   723          return kFFEffectType_Damper_ID;
   724 
   725       case SDL_HAPTIC_INERTIA:
   726          return kFFEffectType_Inertia_ID;
   727 
   728       case SDL_HAPTIC_FRICTION:
   729          return kFFEffectType_Friction_ID;
   730 
   731       case SDL_HAPTIC_CUSTOM:
   732          return kFFEffectType_CustomForce_ID;
   733 
   734       default:
   735          SDL_SetError("Haptic: Unknown effect type.");
   736          return NULL;
   737    }
   738 }
   739 
   740 
   741 /*
   742  * Creates a new haptic effect.
   743  */
   744 int
   745 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect * effect,
   746       SDL_HapticEffect * base)
   747 {
   748    HRESULT ret;
   749    CFUUIDRef type;
   750 
   751    /* Alloc the effect. */
   752    effect->hweffect = (struct haptic_hweffect *)
   753          SDL_malloc(sizeof(struct haptic_hweffect));
   754    if (effect->hweffect == NULL) {
   755       SDL_OutOfMemory();
   756       goto err_hweffect;
   757    }
   758 
   759    /* Get the type. */
   760    type = SDL_SYS_HapticEffectType(effect);
   761    if (type == NULL) {
   762       goto err_hweffect;
   763    }
   764 
   765    /* Get the effect. */
   766    if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, &effect->effect ) < 0) {
   767       goto err_effectdone;
   768    }
   769 
   770    /* Create the actual effect. */
   771    ret = FFDeviceCreateEffect( haptic->hwdata->device, type,
   772          &effect->hweffect->effect, &effect->hweffect->ref );
   773 
   774    if (ret != FF_OK) {
   775       SDL_SetError("Haptic: Unable to create effect.");
   776       goto err_effectdone;
   777    }
   778 
   779    return 0;
   780 
   781 err_effectdone:
   782    SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, effect->effect.type);
   783 err_hweffect:
   784    if (effect->hweffect != NULL) {
   785       SDL_free(effect->hweffect);
   786       effect->hweffect = NULL;
   787    }
   788    return -1;
   789 }
   790 
   791 
   792 /*
   793  * Updates an effect.
   794  */
   795 int
   796 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
   797       struct haptic_effect * effect, SDL_HapticEffect * data)
   798 {
   799    /* TODO */
   800    return -1;
   801 }
   802 
   803 
   804 /*
   805  * Runs an effect.
   806  */
   807 int
   808 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect * effect,
   809                         Uint32 iterations)
   810 {
   811    HRESULT ret;
   812    Uint32 iter;
   813 
   814    /* Check if it's infinite. */
   815    if (iterations == SDL_HAPTIC_INFINITY) {
   816       iter = FF_INFINITE;
   817    }
   818    else
   819       iter = iterations;
   820 
   821    /* Run the effect. */
   822    ret = FFEffectStart(effect->hweffect->ref, iter, 0);
   823    if (ret != FF_OK) {
   824       SDL_SetError("Haptic: Unable to run the effect.");
   825       return -1;
   826    }
   827 
   828    return 0;
   829 }
   830 
   831 
   832 /*
   833  * Stops an effect.
   834  */
   835 int
   836 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect * effect)
   837 {
   838    HRESULT ret;
   839 
   840    ret = FFEffectStop(effect->hweffect->ref);
   841    if (ret != FF_OK) {
   842       SDL_SetError("Haptic: Unable to stop the effect.");
   843       return -1;
   844    }
   845 
   846    return 0;
   847 }
   848 
   849 
   850 /*
   851  * Frees the effect.
   852  */
   853 void
   854 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect * effect)
   855 {
   856    HRESULT ret;
   857 
   858    ret = FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref);
   859    if (ret != FF_OK) {
   860       SDL_SetError("Haptic: Error removing the effect from the device.");
   861    }
   862    SDL_free(effect->hweffect->effect.lpvTypeSpecificParams);
   863    effect->hweffect->effect.lpvTypeSpecificParams = NULL;
   864    SDL_free(effect->hweffect);
   865    effect->hweffect = NULL;
   866 }
   867 
   868 
   869 /*
   870  * Gets the status of a haptic effect.
   871  */
   872 int
   873 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect * effect)
   874 {
   875    HRESULT ret;
   876    FFEffectStatusFlag status;
   877 
   878    ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status);
   879    if (ret != FF_OK) {
   880       SDL_SetError("Haptic: Unable to get effect status.");
   881       return -1;
   882    }
   883 
   884    if (status == 0) return SDL_FALSE;
   885    return SDL_TRUE; /* Assume it's playing or emulated. */
   886 }
   887 
   888 
   889 /*
   890  * Sets the gain.
   891  */
   892 int
   893 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
   894 {
   895    HRESULT ret;
   896    Uint32 val;
   897 
   898    val = gain * 100; /* Mac OS X uses 0 to 10,000 */
   899    ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device, FFPROP_FFGAIN, &val);
   900    if (ret != FF_OK) {
   901       SDL_SetError("Haptic: Error setting gain.");
   902       return -1;
   903    }
   904 
   905    return 0;
   906 }
   907 
   908 
   909 /*
   910  * Sets the autocentering.
   911  */
   912 int
   913 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
   914 {
   915    HRESULT ret;
   916    Uint32 val;
   917 
   918    /* Mac OS X only has 0 (off) and 1 (on) */
   919    if (autocenter == 0) val = 0;
   920    else val = 1;
   921 
   922    ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
   923                FFPROP_AUTOCENTER, &val);
   924    if (ret != FF_OK) {
   925       SDL_SetError("Haptic: Error setting autocenter.");
   926       return -1;
   927    }
   928   
   929    return 0;
   930 
   931 }
   932 
   933 
   934 #endif /* SDL_HAPTIC_LINUX */