Implement new backend methods for haptic and hot plugging on OS X
authorEdward Rudd <urkle@outoforder.cc>
Tue, 04 Feb 2014 18:17:16 -0500
changeset 8173 23919d993046
parent 8172 a1174d5ac032
child 8174 a8e60169b648
Implement new backend methods for haptic and hot plugging on OS X
Xcode/SDL/SDL.xcodeproj/project.pbxproj
src/haptic/darwin/SDL_syshaptic.c
src/haptic/darwin/SDL_syshaptic_c.h
src/joystick/darwin/SDL_sysjoystick.c
--- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj	Tue Feb 04 18:16:52 2014 -0500
+++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj	Tue Feb 04 18:17:16 2014 -0500
@@ -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>"; };
@@ -1401,6 +1402,7 @@
 			isa = PBXGroup;
 			children = (
 				04BDFDF312E6671700899322 /* SDL_syshaptic.c */,
+				DB89958518A1A5C50092407C /* SDL_syshaptic_c.h */,
 			);
 			path = darwin;
 			sourceTree = "<group>";
--- a/src/haptic/darwin/SDL_syshaptic.c	Tue Feb 04 18:16:52 2014 -0500
+++ b/src/haptic/darwin/SDL_syshaptic.c	Tue Feb 04 18:17:16 2014 -0500
@@ -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 @@
     /* 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 @@
 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 @@
 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 @@
         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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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;
 }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/haptic/darwin/SDL_syshaptic_c.h	Tue Feb 04 18:17:16 2014 -0500
@@ -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 );
--- a/src/joystick/darwin/SDL_sysjoystick.c	Tue Feb 04 18:16:52 2014 -0500
+++ b/src/joystick/darwin/SDL_sysjoystick.c	Tue Feb 04 18:17:16 2014 -0500
@@ -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 @@
 {
     recDevice *device = (recDevice *) refcon;
     device->removed = 1;
+#if SDL_HAPTIC_IOKIT
+    PRIVATE_MaybeRemoveDevice(device->ffservice);
+#endif
     s_bDeviceRemoved = SDL_TRUE;
 }
 
@@ -134,6 +138,9 @@
     {
         recDevice *device = (recDevice *) refcon;
         device->removed = 1;
+#if SDL_HAPTIC_IOKIT
+        PRIVATE_MaybeRemoveDevice(device->ffservice);
+#endif
         s_bDeviceRemoved = SDL_TRUE;
     }
 }
@@ -679,6 +686,9 @@
      * SDL_HapticOpenFromJoystick */
     if (FFIsForceFeedback(ioHIDDeviceObject) == FF_OK) {
         device->ffservice = ioHIDDeviceObject;
+#if SDL_HAPTIC_IOKIT
+        PRIVATE_MaybeAddDevice(ioHIDDeviceObject);
+#endif
     } else {
         device->ffservice = 0;
     }