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