|
@@ -22,11 +22,13 @@
|
|
|
|
|
|
#ifdef SDL_HAPTIC_IOKIT
|
|
|
|
|
|
+#include "SDL_assert.h"
|
|
|
#include "SDL_haptic.h"
|
|
|
#include "../SDL_syshaptic.h"
|
|
|
#include "SDL_joystick.h"
|
|
|
#include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */
|
|
|
#include "../../joystick/darwin/SDL_sysjoystick_c.h" /* For joystick hwdata */
|
|
|
+#include "SDL_syshaptic_c.h"
|
|
|
|
|
|
#include <IOKit/IOKitLib.h>
|
|
|
#include <IOKit/hid/IOHIDKeys.h>
|
|
@@ -38,13 +40,10 @@
|
|
|
#define IO_OBJECT_NULL ((io_service_t)0)
|
|
|
#endif
|
|
|
|
|
|
-#define MAX_HAPTICS 32
|
|
|
-
|
|
|
-
|
|
|
/*
|
|
|
* List of available haptic devices.
|
|
|
*/
|
|
|
-static struct
|
|
|
+typedef struct SDL_hapticlist_item
|
|
|
{
|
|
|
char name[256]; /* Name of the device. */
|
|
|
|
|
@@ -54,7 +53,9 @@ static struct
|
|
|
/* Usage pages for determining if it's a mouse or not. */
|
|
|
long usage;
|
|
|
long usagePage;
|
|
|
-} SDL_hapticlist[MAX_HAPTICS];
|
|
|
+
|
|
|
+ struct SDL_hapticlist_item *next;
|
|
|
+} SDL_hapticlist_item;
|
|
|
|
|
|
|
|
|
/*
|
|
@@ -82,6 +83,9 @@ struct haptic_hweffect
|
|
|
static void SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type);
|
|
|
static int HIDGetDeviceProduct(io_service_t dev, char *name);
|
|
|
|
|
|
+static SDL_hapticlist_item *SDL_hapticlist = NULL;
|
|
|
+static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
|
|
|
+static int numhaptics = 0;
|
|
|
|
|
|
/*
|
|
|
* Like strerror but for force feedback errors.
|
|
@@ -146,16 +150,10 @@ FFStrError(HRESULT err)
|
|
|
int
|
|
|
SDL_SYS_HapticInit(void)
|
|
|
{
|
|
|
- int numhaptics;
|
|
|
IOReturn result;
|
|
|
io_iterator_t iter;
|
|
|
CFDictionaryRef match;
|
|
|
io_service_t device;
|
|
|
- CFMutableDictionaryRef hidProperties;
|
|
|
- CFTypeRef refCF;
|
|
|
-
|
|
|
- /* Clear all the memory. */
|
|
|
- SDL_memset(SDL_hapticlist, 0, sizeof(SDL_hapticlist));
|
|
|
|
|
|
/* Get HID devices. */
|
|
|
match = IOServiceMatching(kIOHIDDeviceKey);
|
|
@@ -174,62 +172,150 @@ SDL_SYS_HapticInit(void)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- numhaptics = 0;
|
|
|
while ((device = IOIteratorNext(iter)) != IO_OBJECT_NULL) {
|
|
|
+ PRIVATE_MaybeAddDevice(device);
|
|
|
+ /* always release as the AddDevice will retain IF it's a forcefeedback device */
|
|
|
+ IOObjectRelease(device);
|
|
|
+ }
|
|
|
+ IOObjectRelease(iter);
|
|
|
|
|
|
- /* Check for force feedback. */
|
|
|
- if (FFIsForceFeedback(device) == FF_OK) {
|
|
|
-
|
|
|
- /* Set basic device data. */
|
|
|
- HIDGetDeviceProduct(device, SDL_hapticlist[numhaptics].name);
|
|
|
- SDL_hapticlist[numhaptics].dev = device;
|
|
|
- SDL_hapticlist[numhaptics].haptic = NULL;
|
|
|
-
|
|
|
- /* Set usage pages. */
|
|
|
- hidProperties = 0;
|
|
|
- refCF = 0;
|
|
|
- result = IORegistryEntryCreateCFProperties(device,
|
|
|
- &hidProperties,
|
|
|
- kCFAllocatorDefault,
|
|
|
- kNilOptions);
|
|
|
- if ((result == KERN_SUCCESS) && hidProperties) {
|
|
|
- refCF =
|
|
|
- CFDictionaryGetValue(hidProperties,
|
|
|
- CFSTR(kIOHIDPrimaryUsagePageKey));
|
|
|
- if (refCF) {
|
|
|
- if (!CFNumberGetValue(refCF, kCFNumberLongType,
|
|
|
- &SDL_hapticlist[numhaptics].
|
|
|
- usagePage))
|
|
|
- SDL_SetError
|
|
|
- ("Haptic: Recieving device's usage page.");
|
|
|
- refCF =
|
|
|
- CFDictionaryGetValue(hidProperties,
|
|
|
- CFSTR(kIOHIDPrimaryUsageKey));
|
|
|
- if (refCF) {
|
|
|
- if (!CFNumberGetValue(refCF, kCFNumberLongType,
|
|
|
- &SDL_hapticlist[numhaptics].
|
|
|
- usage))
|
|
|
- SDL_SetError("Haptic: Recieving device's usage.");
|
|
|
- }
|
|
|
- }
|
|
|
- CFRelease(hidProperties);
|
|
|
- }
|
|
|
+ return numhaptics;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+SDL_SYS_NumHaptics()
|
|
|
+{
|
|
|
+ return numhaptics;
|
|
|
+}
|
|
|
+
|
|
|
+static SDL_hapticlist_item *
|
|
|
+HapticByDevIndex(int device_index)
|
|
|
+{
|
|
|
+ SDL_hapticlist_item *item = SDL_hapticlist;
|
|
|
+
|
|
|
+ if ((device_index < 0) || (device_index >= numhaptics)) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ while (device_index > 0) {
|
|
|
+ SDL_assert(item != NULL);
|
|
|
+ device_index--;
|
|
|
+ item = item->next;
|
|
|
+ }
|
|
|
+
|
|
|
+ return item;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+PRIVATE_MaybeAddDevice( io_object_t device )
|
|
|
+{
|
|
|
+ IOReturn result;
|
|
|
+ CFMutableDictionaryRef hidProperties;
|
|
|
+ CFTypeRef refCF;
|
|
|
+ SDL_hapticlist_item *item;
|
|
|
|
|
|
- /* Device has been added. */
|
|
|
- numhaptics++;
|
|
|
- } else { /* Free the unused device. */
|
|
|
- IOObjectRelease(device);
|
|
|
+ /* Check for force feedback. */
|
|
|
+ if (FFIsForceFeedback(device) != FF_OK) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Make sure we don't already have it */
|
|
|
+ for (item = SDL_hapticlist; item ; item = item->next)
|
|
|
+ {
|
|
|
+ if (IOObjectIsEqualTo((io_object_t) item->dev, device)) {
|
|
|
+ /* Already added */
|
|
|
+ return -1;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- /* Reached haptic limit. */
|
|
|
- if (numhaptics >= MAX_HAPTICS)
|
|
|
- break;
|
|
|
+ item = (SDL_hapticlist_item *)SDL_malloc( sizeof(SDL_hapticlist_item));
|
|
|
+ if (item == NULL) {
|
|
|
+ return SDL_SetError("Could not allocate haptic storage");
|
|
|
}
|
|
|
- IOObjectRelease(iter);
|
|
|
+
|
|
|
+ /* retain it as we are going to keep it around a while */
|
|
|
+ IOObjectRetain(device);
|
|
|
+
|
|
|
+ /* Set basic device data. */
|
|
|
+ HIDGetDeviceProduct(device, item->name);
|
|
|
+ item->dev = device;
|
|
|
+ item->haptic = NULL;
|
|
|
+
|
|
|
+ /* Set usage pages. */
|
|
|
+ hidProperties = 0;
|
|
|
+ refCF = 0;
|
|
|
+ result = IORegistryEntryCreateCFProperties(device,
|
|
|
+ &hidProperties,
|
|
|
+ kCFAllocatorDefault,
|
|
|
+ kNilOptions);
|
|
|
+ if ((result == KERN_SUCCESS) && hidProperties) {
|
|
|
+ refCF =
|
|
|
+ CFDictionaryGetValue(hidProperties,
|
|
|
+ CFSTR(kIOHIDPrimaryUsagePageKey));
|
|
|
+ if (refCF) {
|
|
|
+ if (!CFNumberGetValue(refCF, kCFNumberLongType,
|
|
|
+ &item->usagePage))
|
|
|
+ SDL_SetError
|
|
|
+ ("Haptic: Recieving device's usage page.");
|
|
|
+ refCF =
|
|
|
+ CFDictionaryGetValue(hidProperties,
|
|
|
+ CFSTR(kIOHIDPrimaryUsageKey));
|
|
|
+ if (refCF) {
|
|
|
+ if (!CFNumberGetValue(refCF, kCFNumberLongType,
|
|
|
+ &item->usage))
|
|
|
+ SDL_SetError("Haptic: Recieving device's usage.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ CFRelease(hidProperties);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (SDL_hapticlist_tail == NULL) {
|
|
|
+ SDL_hapticlist = SDL_hapticlist_tail = item;
|
|
|
+ } else {
|
|
|
+ SDL_hapticlist_tail->next = item;
|
|
|
+ SDL_hapticlist_tail = item;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Device has been added. */
|
|
|
+ ++numhaptics;
|
|
|
|
|
|
return numhaptics;
|
|
|
}
|
|
|
|
|
|
+int
|
|
|
+PRIVATE_MaybeRemoveDevice( io_object_t device )
|
|
|
+{
|
|
|
+ SDL_hapticlist_item *item;
|
|
|
+ SDL_hapticlist_item *prev = NULL;
|
|
|
+
|
|
|
+ for (item = SDL_hapticlist; item != NULL; item = item->next) {
|
|
|
+ /* found it, remove it. */
|
|
|
+ if (IOObjectIsEqualTo((io_object_t) item->dev, device)) {
|
|
|
+ const int retval = item->haptic ? item->haptic->index : -1;
|
|
|
+
|
|
|
+ if (prev != NULL) {
|
|
|
+ prev->next = item->next;
|
|
|
+ } else {
|
|
|
+ SDL_assert(SDL_hapticlist == item);
|
|
|
+ SDL_hapticlist = item->next;
|
|
|
+ }
|
|
|
+ if (item == SDL_hapticlist_tail) {
|
|
|
+ SDL_hapticlist_tail = prev;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Need to decrement the haptic count */
|
|
|
+ --numhaptics;
|
|
|
+ /* !!! TODO: Send a haptic remove event? */
|
|
|
+
|
|
|
+ IOObjectRelease(item->dev);
|
|
|
+ SDL_free(item);
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+ prev = item;
|
|
|
+ }
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
* Return the name of a haptic device, does not need to be opened.
|
|
@@ -237,7 +323,9 @@ SDL_SYS_HapticInit(void)
|
|
|
const char *
|
|
|
SDL_SYS_HapticName(int index)
|
|
|
{
|
|
|
- return SDL_hapticlist[index].name;
|
|
|
+ SDL_hapticlist_item *item;
|
|
|
+ item = HapticByDevIndex(index);
|
|
|
+ return item->name;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -470,8 +558,10 @@ SDL_SYS_HapticOpenFromService(SDL_Haptic * haptic, io_service_t service)
|
|
|
int
|
|
|
SDL_SYS_HapticOpen(SDL_Haptic * haptic)
|
|
|
{
|
|
|
- return SDL_SYS_HapticOpenFromService(haptic,
|
|
|
- SDL_hapticlist[haptic->index].dev);
|
|
|
+ SDL_hapticlist_item *item;
|
|
|
+ item = HapticByDevIndex(haptic->index);
|
|
|
+
|
|
|
+ return SDL_SYS_HapticOpenFromService(haptic, item->dev);
|
|
|
}
|
|
|
|
|
|
|
|
@@ -481,12 +571,15 @@ SDL_SYS_HapticOpen(SDL_Haptic * haptic)
|
|
|
int
|
|
|
SDL_SYS_HapticMouse(void)
|
|
|
{
|
|
|
- int i;
|
|
|
+ int device_index = 0;
|
|
|
+ SDL_hapticlist_item *item;
|
|
|
|
|
|
- for (i = 0; i < SDL_numhaptics; i++) {
|
|
|
- if ((SDL_hapticlist[i].usagePage == kHIDPage_GenericDesktop) &&
|
|
|
- (SDL_hapticlist[i].usage == kHIDUsage_GD_Mouse))
|
|
|
- return i;
|
|
|
+ for (item = SDL_hapticlist; item; item = item->next) {
|
|
|
+ if ((item->usagePage == kHIDPage_GenericDesktop) &&
|
|
|
+ (item->usage == kHIDUsage_GD_Mouse))
|
|
|
+ return device_index;
|
|
|
+
|
|
|
+ ++device_index;
|
|
|
}
|
|
|
|
|
|
return -1;
|
|
@@ -524,16 +617,16 @@ SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
|
|
|
int
|
|
|
SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
|
|
|
{
|
|
|
- int i;
|
|
|
- for (i=0; i<SDL_numhaptics; i++) {
|
|
|
- if (IOObjectIsEqualTo((io_object_t) SDL_hapticlist[i].dev,
|
|
|
+ int device_index = 0;
|
|
|
+ SDL_hapticlist_item *item;
|
|
|
+
|
|
|
+ for (item = SDL_hapticlist; item; item = item->next) {
|
|
|
+ if (IOObjectIsEqualTo((io_object_t) item->dev,
|
|
|
joystick->hwdata->ffservice)) {
|
|
|
- haptic->index = i;
|
|
|
+ haptic->index = device_index;
|
|
|
break;
|
|
|
- }
|
|
|
- }
|
|
|
- if (i >= SDL_numhaptics) {
|
|
|
- return -1;
|
|
|
+ }
|
|
|
+ ++device_index;
|
|
|
}
|
|
|
|
|
|
return SDL_SYS_HapticOpenFromService(haptic, joystick->hwdata->ffservice);
|
|
@@ -569,15 +662,18 @@ SDL_SYS_HapticClose(SDL_Haptic * haptic)
|
|
|
void
|
|
|
SDL_SYS_HapticQuit(void)
|
|
|
{
|
|
|
- int i;
|
|
|
+ SDL_hapticlist_item *item;
|
|
|
+ SDL_hapticlist_item *next = NULL;
|
|
|
|
|
|
- for (i = 0; i < SDL_numhaptics; i++) {
|
|
|
+ for (item = SDL_hapticlist; item; item = next) {
|
|
|
+ next = item->next;
|
|
|
/* Opened and not closed haptics are leaked, this is on purpose.
|
|
|
* Close your haptic devices after usage. */
|
|
|
|
|
|
/* Free the io_service_t */
|
|
|
- IOObjectRelease(SDL_hapticlist[i].dev);
|
|
|
+ IOObjectRelease(item->dev);
|
|
|
}
|
|
|
+ numhaptics = 0;
|
|
|
}
|
|
|
|
|
|
|