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