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; | ||
|  | 		} | ||
|  | 	} | ||
|  | } |