Steph Thirion
Steph Thirion

Reputation: 9423

How to detect a gamepad button press on OSX 10.5 and higher?

How do I detect a button press on a USB gamepad on OSX 10.5 and higher?

I can't wrap my head around the ridiculously complex HID Manager (even though apparently it was simplified with 10.5), and the code samples at Apple have thousands of lines of code that would take days to understand and isolate what I need, so I'd appreciate if someone posts a simple, and fully coded solution for this isolated problem.


EDIT: so far all answers are links to source code or semi obscure libraries for all kinds of HID devices, which will require more research time than what I'd like to invest on this. I am starting a bounty to get an actual snippet of code that solves this simple problem (using an external library or not).


EDIT POS BOUNTY: thanks to all for you help; but unfortunately the answer that has been automatically selected by the system is not working for me, can't figure out why; and the author has not yet replied to my comments. Any insight would be appreciated, but until a fix is found, anyone looking for resources on this topic should take this answer with a pinch of salt.

Upvotes: 12

Views: 7202

Answers (7)

vgrimmer
vgrimmer

Reputation: 165

Rob's example code just needs one more thing to make it work.

if([joySticks count] > 0)
{
    DDHidJoyStick *myJoyStick = [joySticks objectAtIdex:0];
    [myJoyStick startListening];
}

Put this in your awakeFromNib function, after call startWatchingJoySticks.

BTW, mac doesn't register all gamepad (at least not 360 Gamepad) as a HID device, hence you will need to download a driver for it. I tested with this driver and it works. http://tattiebogle.net/index.php/ProjectRoot/Xbox360Controller/OsxDriver

Upvotes: 0

funkensturm
funkensturm

Reputation: 111

First, import the IOKit Framework and include it like this in your header and implementation files:

#import <IOKit/hid/IOHIDLib.h>

In your header file you might want to have this variable:

IOHIDManagerRef hidManager;

Then add this to your implementation:

void gamepadWasAdded(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef device) {
  NSLog(@"Gamepad was plugged in");
}

void gamepadWasRemoved(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef device) {
  NSLog(@"Gamepad was unplugged");
}

void gamepadAction(void* inContext, IOReturn inResult, void* inSender, IOHIDValueRef value) {
  NSLog(@"Gamepad talked!");
  IOHIDElementRef element = IOHIDValueGetElement(value);
  NSLog(@"Element: %@", element);
  int elementValue = IOHIDValueGetIntegerValue(value);
  NSLog(@"Element value: %i", elementValue);
}

-(void) setupGamepad {
  hidManager = IOHIDManagerCreate( kCFAllocatorDefault, kIOHIDOptionsTypeNone);
  NSMutableDictionary* criterion = [[NSMutableDictionary alloc] init];
  [criterion setObject: [NSNumber numberWithInt: kHIDPage_GenericDesktop] forKey: (NSString*)CFSTR(kIOHIDDeviceUsagePageKey)];
  [criterion setObject: [NSNumber numberWithInt: kHIDUsage_GD_GamePad] forKey: (NSString*)CFSTR(kIOHIDDeviceUsageKey)];
  IOHIDManagerSetDeviceMatching(hidManager, criterion);
  IOHIDManagerRegisterDeviceMatchingCallback(hidManager, gamepadWasAdded, (void*)self);
  IOHIDManagerRegisterDeviceRemovalCallback(hidManager, gamepadWasRemoved, (void*)self);
  IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  IOReturn tIOReturn = IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone);
  IOHIDManagerRegisterInputValueCallback(hidManager, gamepadAction, (void*)self);
}

And call setupGamepad from your awakeFromNib. You can modify the variable "kHIDUsage_GD_GamePad" to register other devices as well. I coded the stuff above by reading the http://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/HID/hid.pdf document examples step by step and comparing it to the "real" source code at http://abstractable.net/enjoy

Good luck.

Upvotes: 11

bmac6502
bmac6502

Reputation: 171

I believe I have solved the problem stated by the original poster with regards to the sample code given:

DDHidJoystick* currentJoystick = [joySticks objectAtIndex: 0]; [currentJoystick startListening];

You must select a joystick and start listening to it. Simple as that! Note that you can replace the 0 with any valid index into the joySticks array, in order to select which joystick you listen to, and I am guessing that you can listen to more than one at a time if you like.

Also note that this (and the above) initialization must be done from a thread which has a run loop. If you are working on native mac os x apps, this isn't a problem, but if you are writing portable code and hope to use POSIX threads, you are going to find that many cocoa objects don't function when created inside a POSIX thread. I have found that you can generally call them safely from any thread once created, but creation must occur on a thread with a run loop. Almost all cocoa objects which work via callback style events, fire those events from the run loop of the thread which creates them.

Upvotes: 1

Rob Keniger
Rob Keniger

Reputation: 46020

Peter's suggestion of the DDHidLib framework is exactly what you are looking for. The library is well designed and the sample code that comes with the library is pretty self-explanatory.

An implementation of an object that gets all the joysticks/gamepads attached to the system and watches for button presses using DDHidLib would look something like this:

#import <Cocoa/Cocoa.h>
#import <DDHidLib/DDHidLib.h>

@interface JoyStickWatcher : NSObject
{
    NSArray* joySticks;
}
- (void)startWatchingJoysticks;
@end


@implementation JoyStickWatcher
- (void)startWatchingJoysticks
{
    //get an array of all joystick objects
    joySticks = [[DDHidJoystick allJoysticks] retain];

    //become the delegate of all available joystick objects
    [joySticks makeObjectsPerformSelector:@selector(setDelegate:) withObject:self];
}

- (void)dealloc
{
    [joySticks release];
    [super dealloc];
}

//these are the DDHidLib joystick delegate methods related to buttons

- (void)ddhidJoystick:(DDHidJoystick *)joystick buttonDown:(unsigned)buttonNumber
{
    NSLog(@"button number %ld of joystick %p went down",joystick,buttonNumber);
}

- (void)ddhidJoystick:(DDHidJoystick *)joystick buttonUp:(unsigned)buttonNumber
{
    NSLog(@"button number %ld of joystick %p went up",joystick,buttonNumber);
}

@end

Upvotes: 8

Peter Hosey
Peter Hosey

Reputation: 96363

You might try Dave Dribin's DDHidLib.

Upvotes: 4

Ken
Ken

Reputation: 13003

Perhaps you could look at the source code for MAME OS X? It has good gamepad support.

Upvotes: 1

Nathan
Nathan

Reputation: 6225

I use Procontroll, but thats a Java library. It's good though.

Upvotes: 1

Related Questions