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