Explorar el Código

Merge pull request #438 from bslack/next

Next
Sean Paul Taylor hace 13 años
padre
commit
481bf4e324
Se han modificado 2 ficheros con 807 adiciones y 33 borrados
  1. 4 0
      gameplay/gameplay.xcodeproj/project.pbxproj
  2. 803 33
      gameplay/src/PlatformMacOSX.mm

+ 4 - 0
gameplay/gameplay.xcodeproj/project.pbxproj

@@ -1622,6 +1622,7 @@
 		5B04C5C614BFCFE100EB0071 /* MeshBatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 4201818E14A41B18008C3F56 /* MeshBatch.h */; };
 		5B04C5CD14BFD48500EB0071 /* gameplay-main-ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B04C5CB14BFD48500EB0071 /* gameplay-main-ios.mm */; };
 		5B04C5CE14BFD48500EB0071 /* PlatformiOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B04C5CC14BFD48500EB0071 /* PlatformiOS.mm */; };
+		5B21E99616153890006EBEAC /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B21E99516153890006EBEAC /* IOKit.framework */; };
 		5B2BC75F1512514500D176CD /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B2BC75D1512514500D176CD /* OpenAL.framework */; };
 		5B2BC7601512514500D176CD /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B2BC75E1512514500D176CD /* OpenGL.framework */; };
 		5B2BC7621512514D00D176CD /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B2BC7611512514D00D176CD /* QuartzCore.framework */; };
@@ -2549,6 +2550,7 @@
 		5B04C5CA14BFCFE100EB0071 /* libgameplay.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libgameplay.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		5B04C5CB14BFD48500EB0071 /* gameplay-main-ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "gameplay-main-ios.mm"; path = "src/gameplay-main-ios.mm"; sourceTree = SOURCE_ROOT; };
 		5B04C5CC14BFD48500EB0071 /* PlatformiOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = PlatformiOS.mm; path = src/PlatformiOS.mm; sourceTree = SOURCE_ROOT; };
+		5B21E99516153890006EBEAC /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
 		5B2BC75D1512514500D176CD /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = System/Library/Frameworks/OpenAL.framework; sourceTree = SDKROOT; };
 		5B2BC75E1512514500D176CD /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
 		5B2BC7611512514D00D176CD /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
@@ -2617,6 +2619,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				5B21E99616153890006EBEAC /* IOKit.framework in Frameworks */,
 				5B2BC7641512516B00D176CD /* libz.dylib in Frameworks */,
 				5B2BC7621512514D00D176CD /* QuartzCore.framework in Frameworks */,
 				5B2BC75F1512514500D176CD /* OpenAL.framework in Frameworks */,
@@ -3582,6 +3585,7 @@
 		42CCD4AF146D811D00353661 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				5B21E99516153890006EBEAC /* IOKit.framework */,
 				5B04C5FD14BFE52300EB0071 /* iOS */,
 				5B04C5FE14BFE52F00EB0071 /* MacOSX */,
 			);

+ 803 - 33
gameplay/src/PlatformMacOSX.mm

@@ -7,14 +7,24 @@
 #include "Form.h"
 #include "ScriptController.h"
 #include <unistd.h>
+#include <IOKit/hid/IOHIDLib.h>
 #import <Cocoa/Cocoa.h>
 #import <QuartzCore/CVDisplayLink.h>
 #import <OpenGL/OpenGL.h>
 #import <mach/mach_time.h>
+#import <Foundation/Foundation.h>
+
+// These should probably be moved to a platform common file
+#define SONY_USB_VENDOR_ID          0x54c
+#define SONY_USB_PS3_PRODUCT_ID     0x268
+
 
 using namespace std;
 using namespace gameplay;
 
+@class View;
+@class OSXGamepad;
+
 // Default to 720p
 static int __width = 1280;
 static int __height = 720;
@@ -40,6 +50,29 @@ static void* __attachToWindow = NULL;
 static bool __mouseCaptured = false;
 static CGPoint __mouseCapturePoint;
 static bool __cursorVisible = true;
+static View* __view = NULL;
+
+static NSMutableDictionary *__activeGamepads = NULL;
+static NSMutableArray *__gamepads = NULL;
+static IOHIDManagerRef __hidManagerRef = NULL;
+
+// Gamepad Helper Function
+OSXGamepad *gamepadForLocationID(NSNumber *locationID);
+OSXGamepad *gamepadForLocationIDValue(unsigned int locationIDValue);
+OSXGamepad *gamepadForGameHandle(int gameHandle);
+
+
+// IOHid Helper Functions
+CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage);
+CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key);
+int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key);
+
+// IOHid Callbacks
+static void hidDeviceDiscoveredCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
+static void hidDeviceRemovalCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
+static void hidDeviceValueAvailableCallback(void *inContext, IOReturn inResult,  void *inSender);
+
+
 
 double getMachTimeInMilliseconds()
 {
@@ -54,7 +87,537 @@ double getMachTimeInMilliseconds()
     return ((double)mach_absolute_time() * (double)s_timebase_info.numer) / (kOneMillion * (double)s_timebase_info.denom);
 }
 
-@class View;
+
+@interface OSXGamepadAxis : NSObject
+{
+    IOHIDElementRef e;
+    CFIndex v;
+    CFIndex logMin;
+    CFIndex logMax;
+}
++ gamepadAxisWithAxisElement:(IOHIDElementRef)element;
+- initWithAxisElement:(IOHIDElementRef)element;
+- (IOHIDElementRef)element;
+- (IOHIDElementCookie)cookie;
+- (uint32_t)usage;
+- (uint32_t)usagePage;
+- (CFIndex)logicalMinimum;
+- (CFIndex)logicalMaximum;
+
+- (float)calibratedValue;
+- (CFIndex)value;
+- (void)setValue:(CFIndex)value;
+@end
+@implementation OSXGamepadAxis
++ gamepadAxisWithAxisElement:(IOHIDElementRef)element
+{
+    return [[[[self class] alloc] initWithAxisElement:element] autorelease];
+}
+- initWithAxisElement:(IOHIDElementRef)element
+{
+    if((self = [super init]))
+    {
+        e = (IOHIDElementRef)CFRetain(element);
+    }
+    return self;
+}
+- (void)dealloc
+{
+    CFRelease(e);
+    [super dealloc];
+}
+- (IOHIDElementRef)element
+{
+    return e;
+}
+- (IOHIDElementCookie)cookie
+{
+    return IOHIDElementGetCookie(e);
+}
+- (bool)isHatSwitch {
+    return (IOHIDElementGetUsage(e) == kHIDUsage_GD_Hatswitch);
+}
+- (uint32_t)usage
+{
+    return IOHIDElementGetUsage(e);
+}
+- (uint32_t)usagePage
+{
+    return IOHIDElementGetUsagePage(e);
+}
+- (CFIndex)logicalMinimum
+{
+    return IOHIDElementGetLogicalMin(e);    
+}
+- (CFIndex)logicalMaximum
+{
+    return IOHIDElementGetLogicalMax(e);
+}
+- (float)calibratedValue
+{
+    float cmax = 2.0f;
+    float cmin = 0.0f;
+    return ((((v - [self logicalMinimum]) * (cmax - cmin)) / ([self logicalMaximum] - [self logicalMinimum])) + cmin - 1.0f);    
+}
+- (CFIndex)value
+{
+    return v;
+}
+- (void)setValue:(CFIndex)value
+{
+    v = value;
+}
+@end
+
+@interface OSXGamepadButton : NSObject
+{
+    IOHIDElementRef e;
+    IOHIDElementRef te;
+    bool state;
+    int triggerValue;
+}
++ gamepadButtonWithButtonElement:(IOHIDElementRef)element;
+- initWithButtonElement:(IOHIDElementRef)element;
+- (void)setTriggerElement:(IOHIDElementRef)element;
+- (IOHIDElementRef)element;
+- (IOHIDElementCookie)cookie;
+- (IOHIDElementRef)triggerElement;
+- (IOHIDElementCookie)triggerCookie;
+
+- (bool)isTriggerButton;
+- (uint32_t)usage;
+- (uint32_t)usagePage;
+- (int)stateValue;
+- (float)calibratedStateValue;
+- (void)setStateValue:(int)value;
+- (bool)state;
+- (void)setState:(bool)state;
+@end
+@implementation OSXGamepadButton
++ gamepadButtonWithButtonElement:(IOHIDElementRef)element
+{
+    return [[[[self class] alloc] initWithButtonElement:element] autorelease];
+}
+- initWithButtonElement:(IOHIDElementRef)element
+{
+    if((self = [super init]))
+    {
+        e = (IOHIDElementRef)CFRetain(element);
+        te = NULL;
+        state = false;
+    }
+    return self;
+}
+- (void)dealloc
+{
+    CFRelease(e);
+    if(te != NULL) CFRelease(te);
+    [super dealloc];
+}
+- (void)setTriggerElement:(IOHIDElementRef)element {
+    if(te)
+    {
+        CFRelease(te);
+        te = NULL;
+    }
+    if(element)
+    {
+        te = (IOHIDElementRef)CFRetain(element);
+    }
+}
+- (IOHIDElementRef)element
+{
+    return e;
+}
+- (IOHIDElementCookie)cookie
+{
+    return IOHIDElementGetCookie(e);
+}
+- (IOHIDElementRef)triggerElement
+{
+    return te;
+}
+- (IOHIDElementCookie)triggerCookie
+{
+    return IOHIDElementGetCookie(te);
+}
+- (bool)isTriggerButton
+{
+    return (te != NULL);
+}
+- (uint32_t)usage
+{
+    return IOHIDElementGetUsage(e);
+}
+- (uint32_t)usagePage
+{
+    return IOHIDElementGetUsagePage(e);
+}
+- (void)setStateValue:(int)value {
+    triggerValue = value;
+}
+- (int)stateValue
+{
+    return triggerValue;
+}
+- (float)calibratedStateValue
+{
+    return (float)triggerValue; // TODO: Need to figure out expected range
+}
+- (bool)state
+{
+    return state;
+}
+- (void)setState:(bool)s
+{
+    state = s;
+}
+@end
+
+@interface OSXGamepad : NSObject
+{
+    IOHIDDeviceRef hidDeviceRef;
+    IOHIDQueueRef queueRef;
+    NSMutableArray *buttons;
+    NSMutableArray *triggerButtons;
+    NSMutableArray *axes;
+}
+@property (assign) IOHIDDeviceRef hidDeviceRef;
+@property (assign) IOHIDQueueRef queueRef;
+@property (retain) NSMutableArray *buttons;
+@property (retain) NSMutableArray *triggerButtons;
+@property (retain) NSMutableArray *axes;
+
+- initWithDevice:(IOHIDDeviceRef)rawDevice;
+- (IOHIDDeviceRef)rawDevice;
+- (NSNumber*)locationID;
+
+- (void)initializeGamepadElements;
+- (OSXGamepadButton*)buttonWithCookie:(IOHIDElementCookie)cookie;
+
+- (bool)startListening;
+- (void)stopListening;
+
+- (NSString *)identifierName;
+- (NSString *)productName;
+- (NSString *)manufacturerName;
+- (NSString *)serialNumber;
+- (int)vendorID;
+- (int)productID;
+
+- (NSUInteger)numberOfAxes;
+- (NSUInteger)numberOfSticks;
+- (NSUInteger)numberOfButtons;
+- (NSUInteger)numberOfTriggerButtons;
+- (OSXGamepadAxis*)axisAtIndex:(NSUInteger)index;
+- (OSXGamepadButton*)buttonAtIndex:(NSUInteger)index;
+- (OSXGamepadButton*)triggerButtonAtIndex:(NSUInteger)index;
+@end
+
+@implementation OSXGamepad
+
+@synthesize hidDeviceRef;
+@synthesize queueRef;
+@synthesize buttons;
+@synthesize triggerButtons;
+@synthesize axes;
+
+- initWithDevice:(IOHIDDeviceRef)rawDevice
+{
+    if((self = [super init]))
+    {
+        [self setButtons:[NSMutableArray array]];
+        [self setTriggerButtons:[NSMutableArray array]];
+        [self setAxes:[NSMutableArray array]];
+
+        CFRetain(rawDevice);
+        IOHIDQueueRef queue = IOHIDQueueCreate(CFAllocatorGetDefault(), rawDevice, 10, kIOHIDOptionsTypeNone);
+        [self setHidDeviceRef:rawDevice];
+        [self setQueueRef:queue];
+        
+        [self initializeGamepadElements];
+        [self startListening];
+    }
+    return self;
+}
+- (void)dealloc
+{
+    [self stopListening];
+    
+    CFRelease([self rawDevice]);
+    CFRelease([self queueRef]);
+    [self setQueueRef:NULL];
+    [self setHidDeviceRef:NULL];
+    
+    [self setButtons:NULL];
+    [self setTriggerButtons:NULL];
+    [self setAxes:NULL];
+    [super dealloc];
+}
+- (IOHIDDeviceRef)rawDevice
+{
+    return [self hidDeviceRef];
+}
+- (NSNumber*)locationID
+{
+    return (NSNumber*)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDLocationIDKey));
+}
+
+- (void)initializeGamepadElements
+{
+    CFArrayRef elements = IOHIDDeviceCopyMatchingElements([self rawDevice], NULL, kIOHIDOptionsTypeNone);
+    for(int i = 0; i < CFArrayGetCount(elements); i++)
+    {
+        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
+        IOHIDElementType type = IOHIDElementGetType(hidElement);
+
+        if (type == kIOHIDElementTypeInput_Misc || type == kIOHIDElementTypeInput_Axis)
+        {
+            uint32_t pageUsage = IOHIDElementGetUsage(hidElement);
+            IOHIDElementCookie cookie = IOHIDElementGetCookie(hidElement);
+
+            switch(pageUsage)
+            {
+                case kHIDUsage_GD_X:
+                case kHIDUsage_GD_Y:
+                case kHIDUsage_GD_Z:
+                case kHIDUsage_GD_Rx:
+                case kHIDUsage_GD_Ry:
+                case kHIDUsage_GD_Rz:
+                {
+                    OSXGamepadAxis *axis = [OSXGamepadAxis gamepadAxisWithAxisElement:hidElement];
+                    [[self axes] addObject:axis];
+                }
+                    break;
+                default:
+                    // Ignore the pointers
+                    // Note: Some of the pointers are for the 6-axis accelerometer in a PS3 controller
+                    // Note: L2/R2 triggers are at cookie 39 and 40 base 10 tied to 9 and 10 button elements
+                    break;
+            }
+
+        }
+        if(type == kIOHIDElementTypeInput_Button)
+        {
+            OSXGamepadButton *button = [OSXGamepadButton gamepadButtonWithButtonElement:hidElement];
+            [[self buttons] addObject:button];
+        }
+    }
+    // Go back and get proprietary information (e.g. triggers) and asscoaite with appropriate values
+    // Example for other trigger buttons
+    uint32_t vendorID = [self vendorID];
+    uint32_t productID = [self productID];
+    for(int i = 0; i < CFArrayGetCount(elements); i++)
+    {
+        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
+        IOHIDElementType type = IOHIDElementGetType(hidElement);
+        IOHIDElementCookie cookie = IOHIDElementGetCookie(hidElement);
+        
+        // Gamepad specific code
+        // Not sure if there is a better way to associate buttons and misc hid elements :/
+        if(vendorID == SONY_USB_VENDOR_ID && productID == SONY_USB_PS3_PRODUCT_ID)
+        {
+            if((unsigned long)cookie == 39)
+            {
+                OSXGamepadButton *leftTrigger = [self buttonWithCookie:(IOHIDElementCookie)9];
+                if(leftTrigger)
+                {
+                    [leftTrigger setTriggerElement:hidElement];
+                    [[self triggerButtons] addObject:leftTrigger];
+                    //[[self buttons] removeObject:leftTrigger]; Defer to gamepad team on this line..  not sure how they intend to tackle
+                }
+            }
+            if((unsigned long)cookie == 40)
+            {
+                OSXGamepadButton *rightTrigger = [self buttonWithCookie:(IOHIDElementCookie)10];
+                if(rightTrigger)
+                {
+                    [rightTrigger setTriggerElement:hidElement];
+                    [[self triggerButtons] addObject:rightTrigger];
+                    //[[self buttons] removeObject:rightTrigger];
+                }
+            }
+        }
+    }
+    
+}
+- (OSXGamepadButton*)buttonWithCookie:(IOHIDElementCookie)cookie {
+    for(OSXGamepadButton *b in [self buttons]) {
+        if([b cookie] == cookie) return b;
+    }
+    return NULL;
+}
+
+- (bool)startListening
+{
+    IOReturn kr = IOHIDDeviceOpen([self hidDeviceRef], kIOHIDOptionsTypeNone);
+    if(kr != 0) {
+        return false;
+    }
+    IOHIDDeviceScheduleWithRunLoop([self hidDeviceRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    
+    IOHIDQueueStart([self queueRef]);
+    IOHIDQueueRegisterValueAvailableCallback([self queueRef], hidDeviceValueAvailableCallback, self);
+    
+    CFArrayRef elements = (CFArrayRef)[self watchedElements];
+    for(int i = 0; i < CFArrayGetCount(elements); i++)
+    {
+        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
+        IOHIDQueueAddElement([self queueRef], hidElement);
+    }
+    IOHIDQueueScheduleWithRunLoop([self queueRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    
+    return true;
+}
+- (void)stopListening
+{
+    IOHIDQueueUnscheduleFromRunLoop([self queueRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOHIDQueueStop([self queueRef]);
+    
+    IOHIDDeviceUnscheduleFromRunLoop([self hidDeviceRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOHIDDeviceClose([self hidDeviceRef], kIOHIDOptionsTypeNone);
+}
+
+- (NSString *)identifierName
+{
+    NSString *idName = NULL;
+    if(idName == NULL) idName = [self productName];
+    if(idName == NULL) idName = [self manufacturerName];
+    if(idName == NULL) idName = [self serialNumber];
+    if(idName == NULL) idName = [NSString stringWithFormat:@"%d-%d", [self vendorID], [self productID]];
+    return idName;
+}
+
+- (NSString *)productName {
+    CFStringRef productName = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDProductKey));
+    if(productName == NULL || CFGetTypeID(productName) != CFStringGetTypeID()) {
+        return NULL;
+    }
+    return (NSString*)productName;
+}
+- (NSString *)manufacturerName {
+    CFStringRef manufacturerName = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDManufacturerKey));
+    if(manufacturerName == NULL || CFGetTypeID(manufacturerName) != CFStringGetTypeID()) {
+        return NULL;
+    }
+    return (NSString*)manufacturerName;
+}
+- (NSString *)serialNumber {
+    CFStringRef serialNumber = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDSerialNumberKey));
+    if(serialNumber == NULL || CFGetTypeID(serialNumber) != CFStringGetTypeID()) {
+        return NULL;
+    }
+    return (NSString*)serialNumber;
+}
+
+
+- (int)vendorID
+{
+    return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDVendorIDKey));
+}
+- (int)productID
+{
+    return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDProductIDKey));
+}
+
+
+- (NSUInteger)numberOfAxes
+{
+    return [[self axes] count];
+}
+- (NSUInteger)numberOfSticks
+{
+    return ([[self axes] count] / 2);
+}
+- (NSUInteger)numberOfButtons
+{
+    return [[self buttons] count];
+}
+- (NSUInteger)numberOfTriggerButtons
+{
+    return [[self triggerButtons] count];
+}
+
+- (OSXGamepadButton*)triggerButtonAtIndex:(NSUInteger)index
+{
+    OSXGamepadButton *b = NULL;
+    if(index < [[self triggerButtons] count])
+    {
+        b = [[self triggerButtons] objectAtIndex:index];
+    }
+    return b;
+}
+
+- (OSXGamepadAxis*)axisAtIndex:(NSUInteger)index
+{
+    OSXGamepadAxis *a = NULL;
+    if(index < [[self axes] count])
+    {
+        a = [[self axes] objectAtIndex:index];
+    }
+    return a;
+}
+- (OSXGamepadButton*)buttonAtIndex:(NSUInteger)index
+{
+    OSXGamepadButton *b = NULL;
+    if(index < [[self buttons] count])
+    {
+        b = [[self buttons] objectAtIndex:index];
+    }
+    return b;
+}
+- (NSArray*)watchedElements
+{
+    NSMutableArray *r = [NSMutableArray array];
+    for(OSXGamepadButton *b in [self buttons])
+    {
+        [r addObject:(id)[b element]];
+    }
+    for(OSXGamepadAxis *a in [self axes])
+    {
+        [r addObject:(id)[a element]];
+    }
+    return [NSArray arrayWithArray:r];
+}
+- (void)hidValueAvailable:(IOHIDValueRef)value
+{
+    IOHIDElementRef element = IOHIDValueGetElement(value);
+	IOHIDElementCookie cookie = IOHIDElementGetCookie(element);
+    
+    if(IOHIDValueGetLength(value) > 4) return; // saftey precaution for PS3 cotroller
+    CFIndex integerValue = IOHIDValueGetIntegerValue(value);
+    
+    for(OSXGamepadAxis *a in [self axes])
+    {
+        if([a cookie] == cookie)
+        {
+            [a setValue:integerValue];
+        }
+    }
+    
+    for(OSXGamepadButton *b in [self buttons])
+    {
+        if([b cookie] == cookie)
+        {
+            [b setState:(bool)integerValue];
+            break;
+        }
+    }
+    
+    for(OSXGamepadButton *b in [self triggerButtons])
+    {
+        if([b triggerCookie] == cookie)
+        {
+            [b setStateValue:integerValue];
+            break;
+        }
+    }
+
+}
+
+
+@end
+
 
 @interface View : NSOpenGLView <NSWindowDelegate>
 {
@@ -66,10 +629,8 @@ double getMachTimeInMilliseconds()
     Game* _game;
     unsigned int _gestureEvents;    
 }
-
 @end
 
-static View* __view = NULL;
 
 @implementation View
 
@@ -486,8 +1047,8 @@ int getKey(unsigned short keyCode, unsigned int modifierFlags)
             return Keyboard::KEY_F10;
         
         // MACOS reserved:
-        //return Keyboard::KEY_F11;
-        //return Keyboard::KEY_F12;
+        // return Keyboard::KEY_F11;
+        // return Keyboard::KEY_F12;
         // return Keyboard::KEY_PAUSE;
         // return Keyboard::KEY_SCROLL_LOCK;
             
@@ -680,25 +1241,6 @@ int getKey(unsigned short keyCode, unsigned int modifierFlags)
     [gameLock unlock];
 }
 
-- (void)swipeWithEvent:(NSEvent *)event
-{
-    if([self isGestureRegistered:Gesture::GESTURE_SWIPE] == false) return;
-    /**
-     * Gesture callback on Gesture::SWIPE events.
-     *
-     * @param x The x-coordinate of the start of the swipe.
-     * @param y The y-coordinate of the start of the swipe.
-     * @param direction The direction of the swipe
-     *
-     * @see Gesture::SWIPE_DIRECTION_UP
-     * @see Gesture::SWIPE_DIRECTION_DOWN
-     * @see Gesture::SWIPE_DIRECTION_LEFT
-     * @see Gesture::SWIPE_DIRECTION_RIGHT
-     */
-    //[gameLock lock];
-    //virtual void gestureSwipeEvent(int x, int y, int direction);
-    //[gameLock unlock];
-}
 
 @end
 
@@ -730,12 +1272,36 @@ extern void print(const char* format, ...)
 Platform::Platform(Game* game)
 : _game(game)
 {
+    __activeGamepads = [[NSMutableDictionary alloc] init];
+    __gamepads = [[NSMutableArray alloc] init];
+    __hidManagerRef = IOHIDManagerCreate(CFAllocatorGetDefault(), kIOHIDOptionsTypeNone);
+    IOHIDManagerRegisterDeviceMatchingCallback(__hidManagerRef, hidDeviceDiscoveredCallback, NULL);
+    IOHIDManagerRegisterDeviceRemovalCallback(__hidManagerRef, hidDeviceRemovalCallback, NULL);
+    
+    CFDictionaryRef matchingCFDictRef = IOHIDCreateDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
+    if (matchingCFDictRef) IOHIDManagerSetDeviceMatching(__hidManagerRef, matchingCFDictRef);
+    CFRelease(matchingCFDictRef);
+    
+    IOHIDManagerScheduleWithRunLoop(__hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOReturn kr = IOHIDManagerOpen(__hidManagerRef, kIOHIDOptionsTypeNone);
+    assert(kr == 0);
 }
 
+    
 Platform::~Platform()
 {
+    IOHIDManagerUnscheduleFromRunLoop(__hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOHIDManagerClose(__hidManagerRef, kIOHIDOptionsTypeNone);
+    
+    CFRelease(__hidManagerRef);
+    __hidManagerRef = NULL;
+    [__activeGamepads release];
+    __activeGamepads = NULL;
+    [__gamepads release];
+    __gamepads = NULL;
 }
 
+    
 Platform* Platform::create(Game* game, void* attachToWindow)
 {
     __attachToWindow = attachToWindow;
@@ -999,7 +1565,11 @@ bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheel
 
 bool Platform::isGestureSupported(Gesture::GestureEvent evt)
 {
-    // TODO: Support Swipe and Tap
+    // Swipe unsupported as it is considered moving mouse cursor
+    // Two fingers is scrolling
+    // Three fingers is swipe, but is not always enabled on users system
+    // Tap not supported as it is considered a mouse click/button click
+    // on some systems making it difficult to differentiate 
     switch(evt)
     {
         case Gesture::GESTURE_PINCH:
@@ -1012,12 +1582,12 @@ bool Platform::isGestureSupported(Gesture::GestureEvent evt)
 
 void Platform::registerGesture(Gesture::GestureEvent evt)
 {
-    [__view registerGesture:evt];
+    [__view registerGesture:evt];   
 }
 
 void Platform::unregisterGesture(Gesture::GestureEvent evt)
 {
-    [__view unregisterGesture:evt];
+    [__view unregisterGesture:evt];        
 }
   
 bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
@@ -1027,63 +1597,263 @@ bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
 
 unsigned int Platform::getGamepadsConnected()
 {
-    return 0;
+    Game* game = Game::getInstance();
+    
+    if(game->isInitialized())
+    {
+        // Locate any newly connected devices
+        for(OSXGamepad *gamepad in __gamepads)
+        {
+            NSNumber *locationID = [gamepad locationID];
+            if([__activeGamepads objectForKey:locationID] == NULL)
+            {
+                unsigned int handle = game->createGamepad([[gamepad identifierName] cStringUsingEncoding:NSASCIIStringEncoding],
+                                                          [locationID unsignedIntValue],
+                                                          [gamepad numberOfButtons],
+                                                          [gamepad numberOfSticks],
+                                                          [gamepad numberOfTriggerButtons]);
+                NSNumber *handleObj = [NSNumber numberWithUnsignedInt:handle];
+                [__activeGamepads setObject:handleObj forKey:locationID];
+                game->gamepadEvent(Gamepad::CONNECTED_EVENT, game->getGamepad(handle));
+            }
+        }
+        
+        // Detect any disconnected gamepads
+        NSMutableArray *deadGamepads = [NSMutableArray array];
+        for(NSNumber *locationID in __activeGamepads)
+        {
+            OSXGamepad *gamepad = gamepadForLocationID(locationID);
+            if(gamepad == NULL)
+            {
+                NSNumber *gameHandle = [__activeGamepads objectForKey:locationID];
+                game->gamepadEvent(Gamepad::DISCONNECTED_EVENT, game->getGamepad([gameHandle unsignedIntValue]));
+                [deadGamepads addObject:locationID];
+            }
+        }
+        [__activeGamepads removeObjectsForKeys:deadGamepads];
+    }
+    return [__gamepads count];
 }
 
 bool Platform::isGamepadConnected(unsigned int gamepadHandle)
 {
-    return false;
+    OSXGamepad *gamepad = gamepadForLocationIDValue(gamepadHandle);
+    return (gamepad != NULL);
 }
 
 const char* Platform::getGamepadId(unsigned int gamepadHandle)
 {
+    OSXGamepad *gamepad = gamepadForLocationIDValue(gamepadHandle);
+    if(gamepad)
+    {
+        return [[gamepad productName] cStringUsingEncoding:NSASCIIStringEncoding];
+    }
     return NULL;
 }
 
 unsigned int Platform::getGamepadButtonCount(unsigned int gamepadHandle)
 {
+    OSXGamepad *gamepad = gamepadForLocationIDValue(gamepadHandle);
+    if(gamepad)
+    {
+        return [gamepad numberOfButtons];
+    }
     return 0;
 }
 
 bool Platform::getGamepadButtonState(unsigned int gamepadHandle, unsigned int buttonIndex)
 {
+    OSXGamepad *gamepad = gamepadForLocationIDValue(gamepadHandle);
+    OSXGamepadButton *button = [gamepad buttonAtIndex:buttonIndex];
+    if(button)
+    {
+        return [button state];
+    }
     return false;
 }
 
 unsigned int Platform::getGamepadJoystickCount(unsigned int gamepadHandle)
 {
+    OSXGamepad *gamepad = gamepadForLocationIDValue(gamepadHandle);
+    if(gamepad)
+    {
+        return [gamepad numberOfSticks];
+    }
     return 0;
 }
 
 bool Platform::isGamepadJoystickActive(unsigned int gamepadHandle, unsigned int joystickIndex)
 {
-    return false;
+    return true; // when are they not active??
 }
 
 float Platform::getGamepadJoystickAxisX(unsigned int gamepadHandle, unsigned int joystickIndex)
 {
-    return 0.0f;
+    OSXGamepad *gamepad = gamepadForLocationIDValue(gamepadHandle);
+    OSXGamepadAxis *xAxis = [gamepad axisAtIndex:(joystickIndex*2)];
+    return [xAxis calibratedValue];
 }
 
 float Platform::getGamepadJoystickAxisY(unsigned int gamepadHandle, unsigned int joystickIndex)
 {
-    return 0.0f;
+    OSXGamepad *gamepad = gamepadForLocationIDValue(gamepadHandle);
+    OSXGamepadAxis *yAxis = [gamepad axisAtIndex:((joystickIndex*2)+1)];
+    return [yAxis calibratedValue];
 }
 
 void Platform::getGamepadJoystickAxisValues(unsigned int gamepadHandle, unsigned int joystickIndex, Vector2* outValue)
 {
+    OSXGamepad *gamepad = gamepadForLocationIDValue(gamepadHandle);
+    OSXGamepadAxis *xAxis = [gamepad axisAtIndex:(joystickIndex*2)];
+    OSXGamepadAxis *yAxis = [gamepad axisAtIndex:((joystickIndex*2)+1)];
+    if(outValue)
+    {
+        outValue->x = [xAxis calibratedValue];
+        outValue->y = [yAxis calibratedValue];
+    }
 }
 
 unsigned int Platform::getGamepadTriggerCount(unsigned int gamepadHandle)
 {
-    return 0;
+    OSXGamepad *gamepad = gamepadForLocationIDValue(gamepadHandle);
+    return [gamepad numberOfTriggerButtons];
 }
 
 float Platform::getGamepadTriggerValue(unsigned int gamepadHandle, unsigned int triggerIndex)
 {
+    OSXGamepad *gamepad = gamepadForLocationIDValue(gamepadHandle);
+    OSXGamepadButton *button = [gamepad triggerButtonAtIndex:triggerIndex];
+    if(button)
+    {
+        return [button stateValue];
+    }
     return 0.0f;
 }
+}
+    
+OSXGamepad *gamepadForLocationID(NSNumber *locationID)
+{
+    OSXGamepad *fgamepad = NULL;
+    for(OSXGamepad *gamepad in __gamepads)
+    {
+        if([[gamepad locationID] isEqual:locationID])
+        {
+            fgamepad = gamepad;
+            break;
+        }
+    }
+    return fgamepad;
+}
 
+OSXGamepad *gamepadForLocationIDValue(unsigned int locationIDValue)
+{
+    return gamepadForLocationID([NSNumber numberWithUnsignedInt:locationIDValue]);
 }
+OSXGamepad *gamepadForGameHandle(int gameHandle)
+{
+    OSXGamepad *gamepad = NULL;
+    for(NSNumber *locationID in __activeGamepads)
+    {
+        NSNumber *handleID = [__activeGamepads objectForKey:locationID];
+        if([handleID integerValue] == gameHandle)
+        {
+            gamepad = gamepadForLocationID(locationID);
+            break;
+        }
+    }
+    return gamepad;
+}
+
+
+CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary( UInt32 inUsagePage, UInt32 inUsage ) {
+    // create a dictionary to add usage page/usages to
+    CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    if (result) {
+        if (inUsagePage) {
+            // Add key for device type to refine the matching dictionary.
+            CFNumberRef pageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsagePage );
+            if (pageCFNumberRef) {
+                CFDictionarySetValue(result, CFSTR( kIOHIDDeviceUsagePageKey ), pageCFNumberRef);
+                CFRelease(pageCFNumberRef);
+                
+                // note: the usage is only valid if the usage page is also defined
+                if (inUsage) {
+                    CFNumberRef usageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage );
+                    if (usageCFNumberRef) {
+                        CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsageKey), usageCFNumberRef);
+                        CFRelease(usageCFNumberRef);
+                    } else {
+                        fprintf(stderr, "%s: CFNumberCreate( usage ) failed.", __PRETTY_FUNCTION__ );
+                    }
+                }
+            } else {
+                fprintf( stderr, "%s: CFNumberCreate( usage page ) failed.", __PRETTY_FUNCTION__ );
+            }
+        }
+    } else {
+        fprintf( stderr, "%s: CFDictionaryCreateMutable failed.", __PRETTY_FUNCTION__ );
+    }
+    return result;
+}
+
+CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key) {
+	CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
+	if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) {
+		return NULL;
+	}
+    return (CFStringRef)typeRef;
+}
+int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key) {
+	CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
+	if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) {
+		return 0;
+	}
+    
+    int value;
+	CFNumberGetValue((CFNumberRef) typeRef, kCFNumberSInt32Type, &value);
+	return value;
+}
+
 
+static void hidDeviceDiscoveredCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) {
+    CFNumberRef locID = (CFNumberRef)IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDLocationIDKey));
+    if(locID)
+    {
+        OSXGamepad *gamepad = [[OSXGamepad alloc] initWithDevice:inIOHIDDeviceRef];
+        [__gamepads addObject:gamepad];
+    }
+    
+}
+static void hidDeviceRemovalCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) {
+    int removeIndex = -1;
+    NSNumber *locID = (NSNumber*)IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDLocationIDKey));
+    if(locID)
+    {
+        for(int i = 0; i < [__gamepads count]; i++)
+        {
+            OSXGamepad *gamepad = [__gamepads objectAtIndex:i];
+            if([[gamepad locationID] isEqual:locID])
+            {
+                removeIndex = i;
+                break;
+            }
+        }
+    }
+    if(removeIndex >= 0)
+    {
+        [__gamepads removeObjectAtIndex:removeIndex];
+    }
+}
+
+static void hidDeviceValueAvailableCallback(void *inContext, IOReturn inResult,  void *inSender)
+{
+    OSXGamepad *d = (OSXGamepad*)inContext;
+    do
+    {
+        IOHIDValueRef valueRef = IOHIDQueueCopyNextValueWithTimeout( ( IOHIDQueueRef ) inSender, 0. );
+        if (!valueRef) break;
+        [d hidValueAvailable:valueRef];
+        CFRelease(valueRef); // Don't forget to release our HID value reference
+    } while (1);
+}
 #endif