Implement new backend methods for haptic and hot plugging on OS X

This commit is contained in:
Edward Rudd 2014-02-04 18:17:16 -05:00
parent 832dfd827e
commit 00970c2034
4 changed files with 213 additions and 79 deletions

View file

@ -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) {
/* 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);
}
/* Device has been added. */
numhaptics++;
} else { /* Free the unused device. */
IOObjectRelease(device);
}
/* Reached haptic limit. */
if (numhaptics >= MAX_HAPTICS)
break;
PRIVATE_MaybeAddDevice(device);
/* always release as the AddDevice will retain IF it's a forcefeedback device */
IOObjectRelease(device);
}
IOObjectRelease(iter);
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;
/* 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;
}
}
item = (SDL_hapticlist_item *)SDL_malloc( sizeof(SDL_hapticlist_item));
if (item == NULL) {
return SDL_SetError("Could not allocate haptic storage");
}
/* 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;
}