src/haptic/darwin/SDL_syshaptic.c
author Manuel Alfayate Corchete
Thu, 22 Oct 2020 19:51:57 +0200
changeset 14191 7bb6793cd70f
parent 13860 d16844f83bfd
permissions -rw-r--r--
kmsdrm: Add comment about KMSDRM_VideoQuit() changes.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2020 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(void)
   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 #ifdef SDL_JOYSTICK_IOKIT
   603     if (joystick->driver != &SDL_DARWIN_JoystickDriver) {
   604         return SDL_FALSE;
   605     }
   606     if (joystick->hwdata->ffservice != 0) {
   607         return SDL_TRUE;
   608     }
   609 #endif
   610     return SDL_FALSE;
   611 }
   612 
   613 
   614 /*
   615  * Checks to see if the haptic device and joystick are in reality the same.
   616  */
   617 int
   618 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
   619 {
   620 #ifdef SDL_JOYSTICK_IOKIT
   621     if (joystick->driver != &SDL_DARWIN_JoystickDriver) {
   622         return 0;
   623     }
   624     if (IOObjectIsEqualTo((io_object_t) ((size_t)haptic->hwdata->device),
   625                           joystick->hwdata->ffservice)) {
   626         return 1;
   627     }
   628 #endif
   629     return 0;
   630 }
   631 
   632 
   633 /*
   634  * Opens a SDL_Haptic from a SDL_Joystick.
   635  */
   636 int
   637 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
   638 {
   639 #ifdef SDL_JOYSTICK_IOKIT
   640     int device_index = 0;
   641     SDL_hapticlist_item *item;
   642     
   643     if (joystick->driver != &SDL_DARWIN_JoystickDriver) {
   644         return -1;
   645     }
   646     for (item = SDL_hapticlist; item; item = item->next) {
   647         if (IOObjectIsEqualTo((io_object_t) item->dev,
   648                              joystick->hwdata->ffservice)) {
   649            haptic->index = device_index;
   650            break;
   651         }
   652         ++device_index;
   653     }
   654 
   655     return SDL_SYS_HapticOpenFromService(haptic, joystick->hwdata->ffservice);
   656 #else
   657 	return -1;
   658 #endif
   659 }
   660 
   661 
   662 /*
   663  * Closes the haptic device.
   664  */
   665 void
   666 SDL_SYS_HapticClose(SDL_Haptic * haptic)
   667 {
   668     if (haptic->hwdata) {
   669 
   670         /* Free Effects. */
   671         SDL_free(haptic->effects);
   672         haptic->effects = NULL;
   673         haptic->neffects = 0;
   674 
   675         /* Clean up */
   676         FFReleaseDevice(haptic->hwdata->device);
   677 
   678         /* Free */
   679         SDL_free(haptic->hwdata);
   680         haptic->hwdata = NULL;
   681     }
   682 }
   683 
   684 
   685 /*
   686  * Clean up after system specific haptic stuff
   687  */
   688 void
   689 SDL_SYS_HapticQuit(void)
   690 {
   691     SDL_hapticlist_item *item;
   692     SDL_hapticlist_item *next = NULL;
   693 
   694     for (item = SDL_hapticlist; item; item = next) {
   695         next = item->next;
   696         /* Opened and not closed haptics are leaked, this is on purpose.
   697          * Close your haptic devices after usage. */
   698 
   699         /* Free the io_service_t */
   700         IOObjectRelease(item->dev);
   701         SDL_free(item);
   702     }
   703 
   704     numhaptics = -1;
   705     SDL_hapticlist = NULL;
   706     SDL_hapticlist_tail = NULL;
   707 }
   708 
   709 
   710 /*
   711  * Converts an SDL trigger button to an FFEFFECT trigger button.
   712  */
   713 static DWORD
   714 FFGetTriggerButton(Uint16 button)
   715 {
   716     DWORD dwTriggerButton;
   717 
   718     dwTriggerButton = FFEB_NOTRIGGER;
   719 
   720     if (button != 0) {
   721         dwTriggerButton = FFJOFS_BUTTON(button - 1);
   722     }
   723 
   724     return dwTriggerButton;
   725 }
   726 
   727 
   728 /*
   729  * Sets the direction.
   730  */
   731 static int
   732 SDL_SYS_SetDirection(FFEFFECT * effect, SDL_HapticDirection * dir, int naxes)
   733 {
   734     LONG *rglDir;
   735 
   736     /* Handle no axes a part. */
   737     if (naxes == 0) {
   738         effect->dwFlags |= FFEFF_SPHERICAL;     /* Set as default. */
   739         effect->rglDirection = NULL;
   740         return 0;
   741     }
   742 
   743     /* Has axes. */
   744     rglDir = SDL_malloc(sizeof(LONG) * naxes);
   745     if (rglDir == NULL) {
   746         return SDL_OutOfMemory();
   747     }
   748     SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
   749     effect->rglDirection = rglDir;
   750 
   751     switch (dir->type) {
   752     case SDL_HAPTIC_POLAR:
   753         effect->dwFlags |= FFEFF_POLAR;
   754         rglDir[0] = dir->dir[0];
   755         return 0;
   756     case SDL_HAPTIC_CARTESIAN:
   757         effect->dwFlags |= FFEFF_CARTESIAN;
   758         rglDir[0] = dir->dir[0];
   759         if (naxes > 1) {
   760             rglDir[1] = dir->dir[1];
   761         }
   762         if (naxes > 2) {
   763             rglDir[2] = dir->dir[2];
   764         }
   765         return 0;
   766     case SDL_HAPTIC_SPHERICAL:
   767         effect->dwFlags |= FFEFF_SPHERICAL;
   768         rglDir[0] = dir->dir[0];
   769         if (naxes > 1) {
   770             rglDir[1] = dir->dir[1];
   771         }
   772         if (naxes > 2) {
   773             rglDir[2] = dir->dir[2];
   774         }
   775         return 0;
   776     case SDL_HAPTIC_STEERING_AXIS:
   777         effect->dwFlags |= FFEFF_CARTESIAN;
   778         rglDir[0] = 0;
   779         return 0;
   780 
   781     default:
   782         return SDL_SetError("Haptic: Unknown direction type.");
   783     }
   784 }
   785 
   786 
   787 /* Clamps and converts. */
   788 #define CCONVERT(x)   (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
   789 /* Just converts. */
   790 #define CONVERT(x)    (((x)*10000) / 0x7FFF)
   791 /*
   792  * Creates the FFEFFECT from a SDL_HapticEffect.
   793  */
   794 static int
   795 SDL_SYS_ToFFEFFECT(SDL_Haptic * haptic, FFEFFECT * dest, SDL_HapticEffect * src)
   796 {
   797     int i;
   798     FFCONSTANTFORCE *constant = NULL;
   799     FFPERIODIC *periodic = NULL;
   800     FFCONDITION *condition = NULL;     /* Actually an array of conditions - one per axis. */
   801     FFRAMPFORCE *ramp = NULL;
   802     FFCUSTOMFORCE *custom = NULL;
   803     FFENVELOPE *envelope = NULL;
   804     SDL_HapticConstant *hap_constant = NULL;
   805     SDL_HapticPeriodic *hap_periodic = NULL;
   806     SDL_HapticCondition *hap_condition = NULL;
   807     SDL_HapticRamp *hap_ramp = NULL;
   808     SDL_HapticCustom *hap_custom = NULL;
   809     DWORD *axes = NULL;
   810 
   811     /* Set global stuff. */
   812     SDL_memset(dest, 0, sizeof(FFEFFECT));
   813     dest->dwSize = sizeof(FFEFFECT);    /* Set the structure size. */
   814     dest->dwSamplePeriod = 0;   /* Not used by us. */
   815     dest->dwGain = 10000;       /* Gain is set globally, not locally. */
   816     dest->dwFlags = FFEFF_OBJECTOFFSETS;        /* Seems obligatory. */
   817 
   818     /* Envelope. */
   819     envelope = SDL_malloc(sizeof(FFENVELOPE));
   820     if (envelope == NULL) {
   821         return SDL_OutOfMemory();
   822     }
   823     SDL_memset(envelope, 0, sizeof(FFENVELOPE));
   824     dest->lpEnvelope = envelope;
   825     envelope->dwSize = sizeof(FFENVELOPE);      /* Always should be this. */
   826 
   827     /* Axes. */
   828     if (src->constant.direction.type == SDL_HAPTIC_STEERING_AXIS) {
   829         dest->cAxes = 1;
   830     } else {
   831         dest->cAxes = haptic->naxes;
   832     }
   833     if (dest->cAxes > 0) {
   834         axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
   835         if (axes == NULL) {
   836             return SDL_OutOfMemory();
   837         }
   838         axes[0] = haptic->hwdata->axes[0];      /* Always at least one axis. */
   839         if (dest->cAxes > 1) {
   840             axes[1] = haptic->hwdata->axes[1];
   841         }
   842         if (dest->cAxes > 2) {
   843             axes[2] = haptic->hwdata->axes[2];
   844         }
   845         dest->rgdwAxes = axes;
   846     }
   847 
   848 
   849     /* The big type handling switch, even bigger then Linux's version. */
   850     switch (src->type) {
   851     case SDL_HAPTIC_CONSTANT:
   852         hap_constant = &src->constant;
   853         constant = SDL_malloc(sizeof(FFCONSTANTFORCE));
   854         if (constant == NULL) {
   855             return SDL_OutOfMemory();
   856         }
   857         SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE));
   858 
   859         /* Specifics */
   860         constant->lMagnitude = CONVERT(hap_constant->level);
   861         dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE);
   862         dest->lpvTypeSpecificParams = constant;
   863 
   864         /* Generics */
   865         dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
   866         dest->dwTriggerButton = FFGetTriggerButton(hap_constant->button);
   867         dest->dwTriggerRepeatInterval = hap_constant->interval;
   868         dest->dwStartDelay = hap_constant->delay * 1000;        /* In microseconds. */
   869 
   870         /* Direction. */
   871         if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
   872             < 0) {
   873             return -1;
   874         }
   875 
   876         /* Envelope */
   877         if ((hap_constant->attack_length == 0)
   878             && (hap_constant->fade_length == 0)) {
   879             SDL_free(envelope);
   880             dest->lpEnvelope = NULL;
   881         } else {
   882             envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level);
   883             envelope->dwAttackTime = hap_constant->attack_length * 1000;
   884             envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level);
   885             envelope->dwFadeTime = hap_constant->fade_length * 1000;
   886         }
   887 
   888         break;
   889 
   890     case SDL_HAPTIC_SINE:
   891     /* !!! FIXME: put this back when we have more bits in 2.1 */
   892     /* case SDL_HAPTIC_SQUARE: */
   893     case SDL_HAPTIC_TRIANGLE:
   894     case SDL_HAPTIC_SAWTOOTHUP:
   895     case SDL_HAPTIC_SAWTOOTHDOWN:
   896         hap_periodic = &src->periodic;
   897         periodic = SDL_malloc(sizeof(FFPERIODIC));
   898         if (periodic == NULL) {
   899             return SDL_OutOfMemory();
   900         }
   901         SDL_memset(periodic, 0, sizeof(FFPERIODIC));
   902 
   903         /* Specifics */
   904         periodic->dwMagnitude = CONVERT(SDL_abs(hap_periodic->magnitude));
   905         periodic->lOffset = CONVERT(hap_periodic->offset);
   906         periodic->dwPhase = 
   907                 (hap_periodic->phase + (hap_periodic->magnitude < 0 ? 18000 : 0)) % 36000;
   908         periodic->dwPeriod = hap_periodic->period * 1000;
   909         dest->cbTypeSpecificParams = sizeof(FFPERIODIC);
   910         dest->lpvTypeSpecificParams = periodic;
   911 
   912         /* Generics */
   913         dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
   914         dest->dwTriggerButton = FFGetTriggerButton(hap_periodic->button);
   915         dest->dwTriggerRepeatInterval = hap_periodic->interval;
   916         dest->dwStartDelay = hap_periodic->delay * 1000;        /* In microseconds. */
   917 
   918         /* Direction. */
   919         if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
   920             < 0) {
   921             return -1;
   922         }
   923 
   924         /* Envelope */
   925         if ((hap_periodic->attack_length == 0)
   926             && (hap_periodic->fade_length == 0)) {
   927             SDL_free(envelope);
   928             dest->lpEnvelope = NULL;
   929         } else {
   930             envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level);
   931             envelope->dwAttackTime = hap_periodic->attack_length * 1000;
   932             envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level);
   933             envelope->dwFadeTime = hap_periodic->fade_length * 1000;
   934         }
   935 
   936         break;
   937 
   938     case SDL_HAPTIC_SPRING:
   939     case SDL_HAPTIC_DAMPER:
   940     case SDL_HAPTIC_INERTIA:
   941     case SDL_HAPTIC_FRICTION:
   942         hap_condition = &src->condition;
   943         if (dest->cAxes > 0) {
   944             condition = SDL_malloc(sizeof(FFCONDITION) * dest->cAxes);
   945             if (condition == NULL) {
   946                 return SDL_OutOfMemory();
   947             }
   948             SDL_memset(condition, 0, sizeof(FFCONDITION));
   949 
   950             /* Specifics */
   951             for (i = 0; i < dest->cAxes; i++) {
   952                 condition[i].lOffset = CONVERT(hap_condition->center[i]);
   953                 condition[i].lPositiveCoefficient =
   954                     CONVERT(hap_condition->right_coeff[i]);
   955                 condition[i].lNegativeCoefficient =
   956                     CONVERT(hap_condition->left_coeff[i]);
   957                 condition[i].dwPositiveSaturation =
   958                     CCONVERT(hap_condition->right_sat[i] / 2);
   959                 condition[i].dwNegativeSaturation =
   960                     CCONVERT(hap_condition->left_sat[i] / 2);
   961                 condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i] / 2);
   962             }
   963         }
   964 
   965         dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes;
   966         dest->lpvTypeSpecificParams = condition;
   967 
   968         /* Generics */
   969         dest->dwDuration = hap_condition->length * 1000;        /* In microseconds. */
   970         dest->dwTriggerButton = FFGetTriggerButton(hap_condition->button);
   971         dest->dwTriggerRepeatInterval = hap_condition->interval;
   972         dest->dwStartDelay = hap_condition->delay * 1000;       /* In microseconds. */
   973 
   974         /* Direction. */
   975         if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
   976             < 0) {
   977             return -1;
   978         }
   979 
   980         /* Envelope - Not actually supported by most CONDITION implementations. */
   981         SDL_free(dest->lpEnvelope);
   982         dest->lpEnvelope = NULL;
   983 
   984         break;
   985 
   986     case SDL_HAPTIC_RAMP:
   987         hap_ramp = &src->ramp;
   988         ramp = SDL_malloc(sizeof(FFRAMPFORCE));
   989         if (ramp == NULL) {
   990             return SDL_OutOfMemory();
   991         }
   992         SDL_memset(ramp, 0, sizeof(FFRAMPFORCE));
   993 
   994         /* Specifics */
   995         ramp->lStart = CONVERT(hap_ramp->start);
   996         ramp->lEnd = CONVERT(hap_ramp->end);
   997         dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE);
   998         dest->lpvTypeSpecificParams = ramp;
   999 
  1000         /* Generics */
  1001         dest->dwDuration = hap_ramp->length * 1000;     /* In microseconds. */
  1002         dest->dwTriggerButton = FFGetTriggerButton(hap_ramp->button);
  1003         dest->dwTriggerRepeatInterval = hap_ramp->interval;
  1004         dest->dwStartDelay = hap_ramp->delay * 1000;    /* In microseconds. */
  1005 
  1006         /* Direction. */
  1007         if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
  1008             return -1;
  1009         }
  1010 
  1011         /* Envelope */
  1012         if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
  1013             SDL_free(envelope);
  1014             dest->lpEnvelope = NULL;
  1015         } else {
  1016             envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level);
  1017             envelope->dwAttackTime = hap_ramp->attack_length * 1000;
  1018             envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level);
  1019             envelope->dwFadeTime = hap_ramp->fade_length * 1000;
  1020         }
  1021 
  1022         break;
  1023 
  1024     case SDL_HAPTIC_CUSTOM:
  1025         hap_custom = &src->custom;
  1026         custom = SDL_malloc(sizeof(FFCUSTOMFORCE));
  1027         if (custom == NULL) {
  1028             return SDL_OutOfMemory();
  1029         }
  1030         SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE));
  1031 
  1032         /* Specifics */
  1033         custom->cChannels = hap_custom->channels;
  1034         custom->dwSamplePeriod = hap_custom->period * 1000;
  1035         custom->cSamples = hap_custom->samples;
  1036         custom->rglForceData =
  1037             SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
  1038         for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) {      /* Copy data. */
  1039             custom->rglForceData[i] = CCONVERT(hap_custom->data[i]);
  1040         }
  1041         dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE);
  1042         dest->lpvTypeSpecificParams = custom;
  1043 
  1044         /* Generics */
  1045         dest->dwDuration = hap_custom->length * 1000;   /* In microseconds. */
  1046         dest->dwTriggerButton = FFGetTriggerButton(hap_custom->button);
  1047         dest->dwTriggerRepeatInterval = hap_custom->interval;
  1048         dest->dwStartDelay = hap_custom->delay * 1000;  /* In microseconds. */
  1049 
  1050         /* Direction. */
  1051         if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
  1052             0) {
  1053             return -1;
  1054         }
  1055 
  1056         /* Envelope */
  1057         if ((hap_custom->attack_length == 0)
  1058             && (hap_custom->fade_length == 0)) {
  1059             SDL_free(envelope);
  1060             dest->lpEnvelope = NULL;
  1061         } else {
  1062             envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level);
  1063             envelope->dwAttackTime = hap_custom->attack_length * 1000;
  1064             envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level);
  1065             envelope->dwFadeTime = hap_custom->fade_length * 1000;
  1066         }
  1067 
  1068         break;
  1069 
  1070 
  1071     default:
  1072         return SDL_SetError("Haptic: Unknown effect type.");
  1073     }
  1074 
  1075     return 0;
  1076 }
  1077 
  1078 
  1079 /*
  1080  * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT.
  1081  */
  1082 static void
  1083 SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type)
  1084 {
  1085     FFCUSTOMFORCE *custom;
  1086 
  1087     SDL_free(effect->lpEnvelope);
  1088     effect->lpEnvelope = NULL;
  1089     SDL_free(effect->rgdwAxes);
  1090     effect->rgdwAxes = NULL;
  1091     if (effect->lpvTypeSpecificParams != NULL) {
  1092         if (type == SDL_HAPTIC_CUSTOM) {        /* Must free the custom data. */
  1093             custom = (FFCUSTOMFORCE *) effect->lpvTypeSpecificParams;
  1094             SDL_free(custom->rglForceData);
  1095             custom->rglForceData = NULL;
  1096         }
  1097         SDL_free(effect->lpvTypeSpecificParams);
  1098         effect->lpvTypeSpecificParams = NULL;
  1099     }
  1100     SDL_free(effect->rglDirection);
  1101     effect->rglDirection = NULL;
  1102 }
  1103 
  1104 
  1105 /*
  1106  * Gets the effect type from the generic SDL haptic effect wrapper.
  1107  */
  1108 CFUUIDRef
  1109 SDL_SYS_HapticEffectType(Uint16 type)
  1110 {
  1111     switch (type) {
  1112     case SDL_HAPTIC_CONSTANT:
  1113         return kFFEffectType_ConstantForce_ID;
  1114 
  1115     case SDL_HAPTIC_RAMP:
  1116         return kFFEffectType_RampForce_ID;
  1117 
  1118     /* !!! FIXME: put this back when we have more bits in 2.1 */
  1119     /* case SDL_HAPTIC_SQUARE:
  1120         return kFFEffectType_Square_ID; */
  1121 
  1122     case SDL_HAPTIC_SINE:
  1123         return kFFEffectType_Sine_ID;
  1124 
  1125     case SDL_HAPTIC_TRIANGLE:
  1126         return kFFEffectType_Triangle_ID;
  1127 
  1128     case SDL_HAPTIC_SAWTOOTHUP:
  1129         return kFFEffectType_SawtoothUp_ID;
  1130 
  1131     case SDL_HAPTIC_SAWTOOTHDOWN:
  1132         return kFFEffectType_SawtoothDown_ID;
  1133 
  1134     case SDL_HAPTIC_SPRING:
  1135         return kFFEffectType_Spring_ID;
  1136 
  1137     case SDL_HAPTIC_DAMPER:
  1138         return kFFEffectType_Damper_ID;
  1139 
  1140     case SDL_HAPTIC_INERTIA:
  1141         return kFFEffectType_Inertia_ID;
  1142 
  1143     case SDL_HAPTIC_FRICTION:
  1144         return kFFEffectType_Friction_ID;
  1145 
  1146     case SDL_HAPTIC_CUSTOM:
  1147         return kFFEffectType_CustomForce_ID;
  1148 
  1149     default:
  1150         SDL_SetError("Haptic: Unknown effect type.");
  1151         return NULL;
  1152     }
  1153 }
  1154 
  1155 
  1156 /*
  1157  * Creates a new haptic effect.
  1158  */
  1159 int
  1160 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
  1161                         SDL_HapticEffect * base)
  1162 {
  1163     HRESULT ret;
  1164     CFUUIDRef type;
  1165 
  1166     /* Alloc the effect. */
  1167     effect->hweffect = (struct haptic_hweffect *)
  1168         SDL_malloc(sizeof(struct haptic_hweffect));
  1169     if (effect->hweffect == NULL) {
  1170         SDL_OutOfMemory();
  1171         goto err_hweffect;
  1172     }
  1173 
  1174     /* Get the type. */
  1175     type = SDL_SYS_HapticEffectType(base->type);
  1176     if (type == NULL) {
  1177         goto err_hweffect;
  1178     }
  1179 
  1180     /* Get the effect. */
  1181     if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
  1182         goto err_effectdone;
  1183     }
  1184 
  1185     /* Create the actual effect. */
  1186     ret = FFDeviceCreateEffect(haptic->hwdata->device, type,
  1187                                &effect->hweffect->effect,
  1188                                &effect->hweffect->ref);
  1189     if (ret != FF_OK) {
  1190         SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret));
  1191         goto err_effectdone;
  1192     }
  1193 
  1194     return 0;
  1195 
  1196   err_effectdone:
  1197     SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type);
  1198   err_hweffect:
  1199     SDL_free(effect->hweffect);
  1200     effect->hweffect = NULL;
  1201     return -1;
  1202 }
  1203 
  1204 
  1205 /*
  1206  * Updates an effect.
  1207  */
  1208 int
  1209 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
  1210                            struct haptic_effect *effect,
  1211                            SDL_HapticEffect * data)
  1212 {
  1213     HRESULT ret;
  1214     FFEffectParameterFlag flags;
  1215     FFEFFECT temp;
  1216 
  1217     /* Get the effect. */
  1218     SDL_memset(&temp, 0, sizeof(FFEFFECT));
  1219     if (SDL_SYS_ToFFEFFECT(haptic, &temp, data) < 0) {
  1220         goto err_update;
  1221     }
  1222 
  1223     /* Set the flags.  Might be worthwhile to diff temp with loaded effect and
  1224      *  only change those parameters. */
  1225     flags = FFEP_DIRECTION |
  1226         FFEP_DURATION |
  1227         FFEP_ENVELOPE |
  1228         FFEP_STARTDELAY |
  1229         FFEP_TRIGGERBUTTON |
  1230         FFEP_TRIGGERREPEATINTERVAL | FFEP_TYPESPECIFICPARAMS;
  1231 
  1232     /* Create the actual effect. */
  1233     ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags);
  1234     if (ret != FF_OK) {
  1235         SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret));
  1236         goto err_update;
  1237     }
  1238 
  1239     /* Copy it over. */
  1240     SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type);
  1241     SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT));
  1242 
  1243     return 0;
  1244 
  1245   err_update:
  1246     SDL_SYS_HapticFreeFFEFFECT(&temp, data->type);
  1247     return -1;
  1248 }
  1249 
  1250 
  1251 /*
  1252  * Runs an effect.
  1253  */
  1254 int
  1255 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
  1256                         Uint32 iterations)
  1257 {
  1258     HRESULT ret;
  1259     Uint32 iter;
  1260 
  1261     /* Check if it's infinite. */
  1262     if (iterations == SDL_HAPTIC_INFINITY) {
  1263         iter = FF_INFINITE;
  1264     } else
  1265         iter = iterations;
  1266 
  1267     /* Run the effect. */
  1268     ret = FFEffectStart(effect->hweffect->ref, iter, 0);
  1269     if (ret != FF_OK) {
  1270         return SDL_SetError("Haptic: Unable to run the effect: %s.",
  1271                             FFStrError(ret));
  1272     }
  1273 
  1274     return 0;
  1275 }
  1276 
  1277 
  1278 /*
  1279  * Stops an effect.
  1280  */
  1281 int
  1282 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1283 {
  1284     HRESULT ret;
  1285 
  1286     ret = FFEffectStop(effect->hweffect->ref);
  1287     if (ret != FF_OK) {
  1288         return SDL_SetError("Haptic: Unable to stop the effect: %s.",
  1289                             FFStrError(ret));
  1290     }
  1291 
  1292     return 0;
  1293 }
  1294 
  1295 
  1296 /*
  1297  * Frees the effect.
  1298  */
  1299 void
  1300 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1301 {
  1302     HRESULT ret;
  1303 
  1304     ret = FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref);
  1305     if (ret != FF_OK) {
  1306         SDL_SetError("Haptic: Error removing the effect from the device: %s.",
  1307                      FFStrError(ret));
  1308     }
  1309     SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect,
  1310                                effect->effect.type);
  1311     SDL_free(effect->hweffect);
  1312     effect->hweffect = NULL;
  1313 }
  1314 
  1315 
  1316 /*
  1317  * Gets the status of a haptic effect.
  1318  */
  1319 int
  1320 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
  1321                               struct haptic_effect *effect)
  1322 {
  1323     HRESULT ret;
  1324     FFEffectStatusFlag status;
  1325 
  1326     ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status);
  1327     if (ret != FF_OK) {
  1328         SDL_SetError("Haptic: Unable to get effect status: %s.",
  1329                      FFStrError(ret));
  1330         return -1;
  1331     }
  1332 
  1333     if (status == 0) {
  1334         return SDL_FALSE;
  1335     }
  1336     return SDL_TRUE;            /* Assume it's playing or emulated. */
  1337 }
  1338 
  1339 
  1340 /*
  1341  * Sets the gain.
  1342  */
  1343 int
  1344 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
  1345 {
  1346     HRESULT ret;
  1347     Uint32 val;
  1348 
  1349     val = gain * 100;           /* Mac OS X uses 0 to 10,000 */
  1350     ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
  1351                                            FFPROP_FFGAIN, &val);
  1352     if (ret != FF_OK) {
  1353         return SDL_SetError("Haptic: Error setting gain: %s.", FFStrError(ret));
  1354     }
  1355 
  1356     return 0;
  1357 }
  1358 
  1359 
  1360 /*
  1361  * Sets the autocentering.
  1362  */
  1363 int
  1364 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
  1365 {
  1366     HRESULT ret;
  1367     Uint32 val;
  1368 
  1369     /* Mac OS X only has 0 (off) and 1 (on) */
  1370     if (autocenter == 0) {
  1371         val = 0;
  1372     } else {
  1373         val = 1;
  1374     }
  1375 
  1376     ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
  1377                                            FFPROP_AUTOCENTER, &val);
  1378     if (ret != FF_OK) {
  1379         return SDL_SetError("Haptic: Error setting autocenter: %s.",
  1380                             FFStrError(ret));
  1381     }
  1382 
  1383     return 0;
  1384 }
  1385 
  1386 
  1387 /*
  1388  * Pauses the device.
  1389  */
  1390 int
  1391 SDL_SYS_HapticPause(SDL_Haptic * haptic)
  1392 {
  1393     HRESULT ret;
  1394 
  1395     ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
  1396                                            FFSFFC_PAUSE);
  1397     if (ret != FF_OK) {
  1398         return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
  1399     }
  1400 
  1401     return 0;
  1402 }
  1403 
  1404 
  1405 /*
  1406  * Unpauses the device.
  1407  */
  1408 int
  1409 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
  1410 {
  1411     HRESULT ret;
  1412 
  1413     ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
  1414                                            FFSFFC_CONTINUE);
  1415     if (ret != FF_OK) {
  1416         return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
  1417     }
  1418 
  1419     return 0;
  1420 }
  1421 
  1422 
  1423 /*
  1424  * Stops all currently playing effects.
  1425  */
  1426 int
  1427 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
  1428 {
  1429     HRESULT ret;
  1430 
  1431     ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
  1432                                            FFSFFC_STOPALL);
  1433     if (ret != FF_OK) {
  1434         return SDL_SetError("Haptic: Error stopping device: %s.", FFStrError(ret));
  1435     }
  1436 
  1437     return 0;
  1438 }
  1439 
  1440 #endif /* SDL_HAPTIC_IOKIT */
  1441 
  1442 /* vi: set ts=4 sw=4 expandtab: */