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