forked from LeenkxTeam/LNXSDK
141 lines
5.6 KiB
C
141 lines
5.6 KiB
C
#include <kinc/backend/HIDManager.h>
|
|
#include <kinc/log.h>
|
|
|
|
static int initHIDManager(struct HIDManager *manager);
|
|
static bool addMatchingArray(struct HIDManager *manager, CFMutableArrayRef matchingCFArrayRef, CFDictionaryRef matchingCFDictRef);
|
|
static CFMutableDictionaryRef createDeviceMatchingDictionary(struct HIDManager *manager, uint32_t inUsagePage, uint32_t inUsage);
|
|
|
|
static void deviceConnected(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
|
|
static void deviceRemoved(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
|
|
|
|
void HIDManager_init(struct HIDManager *manager) {
|
|
manager->managerRef = 0x0;
|
|
initHIDManager(manager);
|
|
}
|
|
|
|
void HIDManager_destroy(struct HIDManager *manager) {
|
|
if (manager->managerRef) {
|
|
IOHIDManagerUnscheduleFromRunLoop(manager->managerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
|
IOHIDManagerClose(manager->managerRef, kIOHIDOptionsTypeNone);
|
|
}
|
|
}
|
|
|
|
static int initHIDManager(struct HIDManager *manager) {
|
|
// Initialize the IOHIDManager
|
|
manager->managerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
|
|
if (CFGetTypeID(manager->managerRef) == IOHIDManagerGetTypeID()) {
|
|
|
|
// Create a matching dictionary for gamepads and joysticks
|
|
CFMutableArrayRef matchingCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
|
|
if (matchingCFArrayRef) {
|
|
// Create a device matching dictionary for joysticks
|
|
CFDictionaryRef matchingCFDictRef = createDeviceMatchingDictionary(manager, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
|
|
addMatchingArray(manager, matchingCFArrayRef, matchingCFDictRef);
|
|
|
|
// Create a device matching dictionary for game pads
|
|
matchingCFDictRef = createDeviceMatchingDictionary(manager, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
|
|
addMatchingArray(manager, matchingCFArrayRef, matchingCFDictRef);
|
|
}
|
|
else {
|
|
kinc_log(KINC_LOG_LEVEL_ERROR, "%s: CFArrayCreateMutable failed.", __PRETTY_FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
// Set the HID device matching array
|
|
IOHIDManagerSetDeviceMatchingMultiple(manager->managerRef, matchingCFArrayRef);
|
|
CFRelease(matchingCFArrayRef);
|
|
|
|
// Open manager
|
|
IOHIDManagerOpen(manager->managerRef, kIOHIDOptionsTypeNone);
|
|
|
|
// Register routines to be called when (matching) devices are connected or disconnected
|
|
IOHIDManagerRegisterDeviceMatchingCallback(manager->managerRef, deviceConnected, manager);
|
|
IOHIDManagerRegisterDeviceRemovalCallback(manager->managerRef, deviceRemoved, manager);
|
|
|
|
IOHIDManagerScheduleWithRunLoop(manager->managerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
|
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool addMatchingArray(struct HIDManager *manager, CFMutableArrayRef matchingCFArrayRef, CFDictionaryRef matchingCFDictRef) {
|
|
if (matchingCFDictRef) {
|
|
// Add it to the matching array
|
|
CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef);
|
|
CFRelease(matchingCFDictRef); // and release it
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
CFMutableDictionaryRef createDeviceMatchingDictionary(struct HIDManager *manager, uint32_t inUsagePage, uint32_t 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 {
|
|
kinc_log(KINC_LOG_LEVEL_ERROR, "%s: CFNumberCreate(usage) failed.", __PRETTY_FUNCTION__);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
kinc_log(KINC_LOG_LEVEL_ERROR, "%s: CFNumberCreate(usage page) failed.", __PRETTY_FUNCTION__);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
kinc_log(KINC_LOG_LEVEL_ERROR, "%s: CFDictionaryCreateMutable failed.", __PRETTY_FUNCTION__);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// HID device plugged callback
|
|
void deviceConnected(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) {
|
|
// Reference manager
|
|
struct HIDManager *manager = (struct HIDManager *)inContext;
|
|
|
|
// Find an empty slot in the devices list and add the new device there
|
|
// TODO: does this need to be made thread safe?
|
|
struct HIDManagerDeviceRecord *device = &manager->devices[0];
|
|
for (int i = 0; i < KINC_MAX_HID_DEVICES; ++i, ++device) {
|
|
if (!device->connected) {
|
|
device->connected = true;
|
|
device->device = inIOHIDDeviceRef;
|
|
HIDGamepad_bind(&device->pad, inIOHIDDeviceRef, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// HID device unplugged callback
|
|
void deviceRemoved(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) {
|
|
// Reference manager
|
|
struct HIDManager *manager = (struct HIDManager *)inContext;
|
|
|
|
// TODO: does this need to be made thread safe?
|
|
struct HIDManagerDeviceRecord *device = &manager->devices[0];
|
|
for (int i = 0; i < KINC_MAX_HID_DEVICES; ++i, ++device) {
|
|
// TODO: is comparing IOHIDDeviceRef to match devices safe? Is there a better way?
|
|
if (device->connected && device->device == inIOHIDDeviceRef) {
|
|
device->connected = false;
|
|
device->device = NULL;
|
|
HIDGamepad_unbind(&device->pad);
|
|
break;
|
|
}
|
|
}
|
|
}
|