Skip to content

Instantly share code, notes, and snippets.

@dhleong

dhleong/main.m Secret

Created December 20, 2014 16:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dhleong/6a4834185ab259d2a962 to your computer and use it in GitHub Desktop.
Save dhleong/6a4834185ab259d2a962 to your computer and use it in GitHub Desktop.
OSX JoyTest
//
// main.m
// JoyTest
//
// Created by Daniel Leong on 12/18/14.
// Copyright (c) 2014 Daniel Leong. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <IOKit/hid/IOHIDManager.h>
enum
{
kJoystickGammaTableSize = 256
};
static void HandleDeviceMatchingCallback(void * inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef inIOHIDDeviceRef);
static void HandleInputValueCallback(void * inContext, IOReturn inResult, void* inSender, IOHIDValueRef inIOHIDValueRef);
static void HandleDeviceRemovalCallback(void * inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef inIOHIDDeviceRef);
static void HandleDevice(IOHIDDeviceRef inIOHIDDeviceRef);
static int AxisIndex(uint32_t page, uint32_t usage);
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
IOHIDManagerRef hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
IOHIDManagerSetDeviceMatching(hidManager, NULL);
IOHIDManagerRegisterDeviceMatchingCallback(hidManager, HandleDeviceMatchingCallback, NULL);
IOHIDManagerRegisterDeviceRemovalCallback(hidManager, HandleDeviceRemovalCallback, NULL);
IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
IOReturn iores = IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone);
if (iores != kIOReturnSuccess) {
NSLog(@"Cannot open HID manager; joystick support will not function.");
}
else {
NSLog(@"Opened!");
}
// CFSetRef set = IOHIDManagerCopyDevices(hidManager);
// CFIndex count = CFSetGetCount(set);
// const IOHIDDeviceRef results[count];
//
// CFSetGetValues(set, (const void **)&results);
// for (int i=0; i < count; i++) {
//// NSLog(@"%s", results[i]);
// CFStringRef ref = IOHIDDeviceGetProperty(results[i],
// CFSTR(kIOHIDManufacturerKey));
// NSString *manufacturer = (__bridge NSString *) ref;
//// long len = CFStringGetLength(ref);
//// char str[len];
//// CFStringGetCString(ref, str, len, )
//
// if ([manufacturer containsString:@"ustmaster"]) {
// HandleDevice(results[i]);
// }
// }
CFRunLoopRun();
}
return 0;
}
static void HandleDevice(IOHIDDeviceRef device) {
NSLog(@"Discovered device!: %@", device);
IOOptionBits options = kIOHIDOptionsTypeNone;
IOReturn result = IOHIDDeviceOpen(device, options);
if (result != kIOReturnSuccess) {
NSLog(@"result: %d", result);
return;
}
NSDictionary *dict = [[NSDictionary alloc] init];
IOHIDDeviceRegisterInputValueCallback(device, HandleInputValueCallback, (__bridge void *)(dict));
CFArrayRef elementList = IOHIDDeviceCopyMatchingElements(device, NULL, 0L);
if (elementList != NULL)
{
CFIndex idx, count = CFArrayGetCount(elementList);
for (idx = 0; idx < count; idx++)
{
IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elementList, idx);
IOHIDElementType elementType = IOHIDElementGetType(element);
if (elementType > kIOHIDElementTypeInput_ScanCodes)
{
continue;
}
IOHIDElementCookie elementCookie = IOHIDElementGetCookie(element);
uint32_t usagePage = IOHIDElementGetUsagePage(element);
uint32_t usage = IOHIDElementGetUsage(element);
uint32_t min = (uint32_t)IOHIDElementGetPhysicalMin(element);
uint32_t max = (uint32_t)IOHIDElementGetPhysicalMax(element);
NSString *name = (__bridge NSString *)IOHIDElementGetProperty(element, CFSTR(kIOHIDElementNameKey)) ?: @"unnamed";
IOHIDValueRef ref;
IOHIDDeviceGetValue(device, element, &ref);
if (ref) {
CFIndex intValue = IOHIDValueGetIntegerValue(ref);
if (intValue) {
NSLog(@"#%ld, %@ - usage %d:%d, cookie %d, range %d-%d", idx, name, usagePage, usage, (int) elementCookie, min, max);
NSLog(@" --> value=%ld", intValue);
}
}
}
CFRelease(elementList);
}
}
static void HandleDeviceMatchingCallback(void * inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef device)
{
CFStringRef ref = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManufacturerKey));
NSString *manufacturer = (__bridge NSString *) ref;
// long len = CFStringGetLength(ref);
// char str[len];
// CFStringGetCString(ref, str, len, )
if ([manufacturer containsString:@"ustmaster"]) {
HandleDevice(device);
}
}
static void HandleInputValueCallback(void * inContext, IOReturn inResult, void* inSender, IOHIDValueRef value)
{
IOHIDElementRef element = IOHIDValueGetElement(value);
IOHIDDeviceRef device = IOHIDElementGetDevice(element);
NSString *deviceName = (__bridge NSString *)(IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)));
NSString *name = (__bridge NSString *)(IOHIDElementGetName(element));
IOHIDElementCookie cookie = IOHIDElementGetCookie(element);
uint32_t usagePage = IOHIDElementGetUsagePage(element);
uint32_t usage = IOHIDElementGetUsage(element);
int axisIndex = AxisIndex(usagePage, usage);
if (axisIndex >= 0) {
long intValue = IOHIDValueGetIntegerValue(value);
NSLog(@">> (%d) %@.%@%u -> %ld", axisIndex, deviceName, name, cookie, intValue);
{
CFIndex intValue = IOHIDValueGetIntegerValue(value);
CFIndex min = IOHIDElementGetLogicalMin(element);
CFIndex max = IOHIDElementGetLogicalMax(element);
float axisValue = (float)(intValue - min) / (float)(max + 1 - min);
// Note: this is designed so gammaIndex == intValue if min == 0 and max == kJoystickGammaTableSize - 1 (the common case).
unsigned gammaIndex = floor(axisValue * kJoystickGammaTableSize);
NSLog(@"value=%f -> %d", axisValue, gammaIndex);
// evt.value = gammaTable[gammaIndex];
}
// {
// CFTypeRef o = IOHIDDeviceGetProperty(
// device, CFSTR(kIOHIDDeviceUsagePairsKey));
// printf("\tkIOHIDDeviceUsagePairsKey = %p\n", o);
// CFShow(o);
// // uint32_t usagePage = (IOHIDDeviceGetProperty(device, CFSTR(kIOHIDDeviceUsagePageKey)));
// // if (usagePage == kHIDPage_GenericDesktop) {
// // NSLog(@"GEneric desktop!");
// // } else {
// // NSLog(@"GEneric desktop? %d", usagePage);
// // }
// }
}
}
static int AxisIndex(uint32_t page, uint32_t usage)
{
/*
Map axis-like HID usages to SDL-like axis indices. These are all the
commonly used joystick axes according to Microsoft's DirectInput
documentation (hey, you've got to get your info somewhere).
GD_Slider, GD_Dial, DG_Wheel and Sim_Throttle are actually distinct;
unlike the others, they're uncentered. (By implication,
IOHIDElementHasPreferredState() should be false.) This should, in
particular, be considered when mapping to the throttle: centered axes
should provide relative input (for gamepads), while uncentered ones
should provide absolute input. Since that festering pool of pus, SDL,
can't make this distinction, OOJoystickManager can't either (yet).
-- Ahruman 2011-01-04
*/
switch (page)
{
case kHIDPage_GenericDesktop:
switch (usage)
{
case kHIDUsage_GD_X: return 0;
case kHIDUsage_GD_Y: return 1;
case kHIDUsage_GD_Z: return 2;
case kHIDUsage_GD_Rx: return 3;
case kHIDUsage_GD_Ry: return 4;
case kHIDUsage_GD_Rz: return 5;
case kHIDUsage_GD_Slider: return 6;
case kHIDUsage_GD_Dial: return 7;
case kHIDUsage_GD_Wheel: return 8;
}
break;
case kHIDPage_Simulation:
switch (usage)
{
case kHIDUsage_Sim_Throttle:
return 9;
}
}
// Negative numbers indicate non-axis.
return -1;
}
static void HandleDeviceRemovalCallback(void * inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef inIOHIDDeviceRef)
{
NSLog(@"Removed");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment