Skip to content

Commit

Permalink
Implement new backend methods for haptic and hot plugging on OS X
Browse files Browse the repository at this point in the history
  • Loading branch information
urkle committed Feb 4, 2014
1 parent c9f6e4e commit b352698
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 76 deletions.
2 changes: 2 additions & 0 deletions Xcode/SDL/SDL.xcodeproj/project.pbxproj
Expand Up @@ -1083,6 +1083,7 @@
D55A1B7F179F262300625D7C /* SDL_cocoamousetap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_cocoamousetap.h; sourceTree = "<group>"; };
D55A1B80179F262300625D7C /* SDL_cocoamousetap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_cocoamousetap.m; sourceTree = "<group>"; };
DB31407717554B71006C0E22 /* libSDL2.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libSDL2.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
DB89958518A1A5C50092407C /* SDL_syshaptic_c.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDL_syshaptic_c.h; sourceTree = "<group>"; };
F59C70FF00D5CB5801000001 /* ReadMe.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ReadMe.txt; sourceTree = "<group>"; };
F59C710000D5CB5801000001 /* Welcome.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = Welcome.txt; sourceTree = "<group>"; };
F59C710300D5CB5801000001 /* ReadMe.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ReadMe.txt; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1401,6 +1402,7 @@
isa = PBXGroup;
children = (
04BDFDF312E6671700899322 /* SDL_syshaptic.c */,
DB89958518A1A5C50092407C /* SDL_syshaptic_c.h */,
);
path = darwin;
sourceTree = "<group>";
Expand Down
248 changes: 172 additions & 76 deletions src/haptic/darwin/SDL_syshaptic.c
Expand Up @@ -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>
Expand All @@ -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. */

Expand All @@ -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;


/*
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand All @@ -174,70 +172,160 @@ 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.
*/
const char *
SDL_SYS_HapticName(int index)
{
return SDL_hapticlist[index].name;
SDL_hapticlist_item *item;
item = HapticByDevIndex(index);
return item->name;
}

/*
Expand Down Expand Up @@ -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);
}


Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}


Expand Down
26 changes: 26 additions & 0 deletions src/haptic/darwin/SDL_syshaptic_c.h
@@ -0,0 +1,26 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

int
PRIVATE_MaybeAddDevice( io_object_t device );

int
PRIVATE_MaybeRemoveDevice( io_object_t device );

0 comments on commit b352698

Please sign in to comment.