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