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