diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj index fa8e49347..0222d835c 100755 --- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -1083,6 +1083,7 @@ D55A1B7F179F262300625D7C /* SDL_cocoamousetap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_cocoamousetap.h; sourceTree = ""; }; D55A1B80179F262300625D7C /* SDL_cocoamousetap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_cocoamousetap.m; sourceTree = ""; }; 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 = ""; }; F59C70FF00D5CB5801000001 /* ReadMe.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ReadMe.txt; sourceTree = ""; }; F59C710000D5CB5801000001 /* Welcome.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = Welcome.txt; sourceTree = ""; }; F59C710300D5CB5801000001 /* ReadMe.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = ReadMe.txt; sourceTree = ""; }; @@ -1401,6 +1402,7 @@ isa = PBXGroup; children = ( 04BDFDF312E6671700899322 /* SDL_syshaptic.c */, + DB89958518A1A5C50092407C /* SDL_syshaptic_c.h */, ); path = darwin; sourceTree = ""; diff --git a/src/haptic/darwin/SDL_syshaptic.c b/src/haptic/darwin/SDL_syshaptic.c index b939802f6..6df950b12 100644 --- a/src/haptic/darwin/SDL_syshaptic.c +++ b/src/haptic/darwin/SDL_syshaptic.c @@ -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 #include @@ -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; inext) { + 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; } diff --git a/src/haptic/darwin/SDL_syshaptic_c.h b/src/haptic/darwin/SDL_syshaptic_c.h new file mode 100644 index 000000000..0e8625313 --- /dev/null +++ b/src/haptic/darwin/SDL_syshaptic_c.h @@ -0,0 +1,26 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2014 Sam Lantinga + + 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 ); diff --git a/src/joystick/darwin/SDL_sysjoystick.c b/src/joystick/darwin/SDL_sysjoystick.c index 6961f0fb2..f57bcf80f 100644 --- a/src/joystick/darwin/SDL_sysjoystick.c +++ b/src/joystick/darwin/SDL_sysjoystick.c @@ -48,6 +48,7 @@ #include "../SDL_joystick_c.h" #include "SDL_sysjoystick_c.h" #include "SDL_events.h" +#include "../../haptic/darwin/SDL_syshaptic_c.h" /* For haptic hot plugging */ #if !SDL_EVENTS_DISABLED #include "../../events/SDL_events_c.h" #endif @@ -122,6 +123,9 @@ HIDRemovalCallback(void *target, IOReturn result, void *refcon, void *sender) { recDevice *device = (recDevice *) refcon; device->removed = 1; +#if SDL_HAPTIC_IOKIT + PRIVATE_MaybeRemoveDevice(device->ffservice); +#endif s_bDeviceRemoved = SDL_TRUE; } @@ -134,6 +138,9 @@ void JoystickDeviceWasRemovedCallback( void * refcon, io_service_t service, natu { recDevice *device = (recDevice *) refcon; device->removed = 1; +#if SDL_HAPTIC_IOKIT + PRIVATE_MaybeRemoveDevice(device->ffservice); +#endif s_bDeviceRemoved = SDL_TRUE; } } @@ -679,6 +686,9 @@ AddDeviceHelper( io_object_t ioHIDDeviceObject ) * SDL_HapticOpenFromJoystick */ if (FFIsForceFeedback(ioHIDDeviceObject) == FF_OK) { device->ffservice = ioHIDDeviceObject; +#if SDL_HAPTIC_IOKIT + PRIVATE_MaybeAddDevice(ioHIDDeviceObject); +#endif } else { device->ffservice = 0; }