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