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