| 
									
										
										
										
											2014-02-10 13:54:46 -05:00
										 |  |  | // Copyright 2013 Dolphin Emulator Project | 
					
						
							| 
									
										
										
										
											2015-05-18 01:08:10 +02:00
										 |  |  | // Licensed under GPLv2+ | 
					
						
							| 
									
										
										
										
											2014-02-10 13:54:46 -05:00
										 |  |  | // Refer to the license.txt file included. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  | #include <thread> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #include <Cocoa/Cocoa.h> | 
					
						
							| 
									
										
										
										
											2010-06-26 13:03:25 +00:00
										 |  |  | #include <Foundation/Foundation.h> | 
					
						
							|  |  |  | #include <IOKit/hid/IOHIDLib.h> | 
					
						
							| 
									
										
										
										
											2010-04-25 18:04:55 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  | #include "Common/Logging/Log.h" | 
					
						
							| 
									
										
										
										
											2016-07-01 15:05:24 +12:00
										 |  |  | #include "Common/StringUtil.h" | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  | #include "Common/Thread.h" | 
					
						
							| 
									
										
										
										
											2016-07-01 15:05:24 +12:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-12 17:08:04 +02:00
										 |  |  | #include "InputCommon/ControllerInterface/ControllerInterface.h" | 
					
						
							| 
									
										
										
										
											2014-02-17 05:18:15 -05:00
										 |  |  | #include "InputCommon/ControllerInterface/OSX/OSX.h" | 
					
						
							|  |  |  | #include "InputCommon/ControllerInterface/OSX/OSXJoystick.h" | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #include "InputCommon/ControllerInterface/OSX/OSXKeyboard.h" | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  | #include "InputCommon/ControllerInterface/OSX/RunLoopStopper.h" | 
					
						
							| 
									
										
										
										
											2013-06-16 20:07:10 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-25 18:04:55 +00:00
										 |  |  | namespace ciface | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | namespace OSX | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  | constexpr CFTimeInterval FOREVER = 1e20; | 
					
						
							|  |  |  | static std::thread s_hotplug_thread; | 
					
						
							|  |  |  | static RunLoopStopper s_stopper; | 
					
						
							| 
									
										
										
										
											2014-03-09 21:14:26 +01:00
										 |  |  | static IOHIDManagerRef HIDManager = nullptr; | 
					
						
							| 
									
										
										
										
											2010-04-25 18:04:55 +00:00
										 |  |  | static CFStringRef OurRunLoop = CFSTR("DolphinOSXInput"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | void DeviceElementDebugPrint(const void* value, void* context) | 
					
						
							| 
									
										
										
										
											2010-04-27 15:22:23 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   IOHIDElementRef e = (IOHIDElementRef)value; | 
					
						
							|  |  |  |   bool recurse = false; | 
					
						
							|  |  |  |   if (context) | 
					
						
							|  |  |  |     recurse = *(bool*)context; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   std::string type = ""; | 
					
						
							|  |  |  |   switch (IOHIDElementGetType(e)) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |   case kIOHIDElementTypeInput_Axis: | 
					
						
							|  |  |  |     type = "axis"; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  |   case kIOHIDElementTypeInput_Button: | 
					
						
							|  |  |  |     type = "button"; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  |   case kIOHIDElementTypeInput_Misc: | 
					
						
							|  |  |  |     type = "misc"; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  |   case kIOHIDElementTypeInput_ScanCodes: | 
					
						
							|  |  |  |     type = "scancodes"; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  |   case kIOHIDElementTypeOutput: | 
					
						
							|  |  |  |     type = "output"; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  |   case kIOHIDElementTypeFeature: | 
					
						
							|  |  |  |     type = "feature"; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  |   case kIOHIDElementTypeCollection: | 
					
						
							|  |  |  |     type = "collection"; | 
					
						
							|  |  |  |     break; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   std::string c_type = ""; | 
					
						
							|  |  |  |   if (type == "collection") | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     switch (IOHIDElementGetCollectionType(e)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     case kIOHIDElementCollectionTypePhysical: | 
					
						
							|  |  |  |       c_type = "physical"; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     case kIOHIDElementCollectionTypeApplication: | 
					
						
							|  |  |  |       c_type = "application"; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     case kIOHIDElementCollectionTypeLogical: | 
					
						
							|  |  |  |       c_type = "logical"; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     case kIOHIDElementCollectionTypeReport: | 
					
						
							|  |  |  |       c_type = "report"; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     case kIOHIDElementCollectionTypeNamedArray: | 
					
						
							|  |  |  |       c_type = "namedArray"; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     case kIOHIDElementCollectionTypeUsageSwitch: | 
					
						
							|  |  |  |       c_type = "usageSwitch"; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     case kIOHIDElementCollectionTypeUsageModifier: | 
					
						
							|  |  |  |       c_type = "usageModifier"; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   c_type.append(" "); | 
					
						
							|  |  |  |   NSLog(@"%s%s%spage: 0x%x usage: 0x%x name: %@ " | 
					
						
							|  |  |  |          "lmin: %ld lmax: %ld pmin: %ld pmax: %ld", | 
					
						
							|  |  |  |         type.c_str(), type == "collection" ? ":" : "", type == "collection" ? c_type.c_str() : " ", | 
					
						
							|  |  |  |         IOHIDElementGetUsagePage(e), IOHIDElementGetUsage(e), | 
					
						
							|  |  |  |         IOHIDElementGetName(e),  // usually just nullptr | 
					
						
							|  |  |  |         IOHIDElementGetLogicalMin(e), IOHIDElementGetLogicalMax(e), IOHIDElementGetPhysicalMin(e), | 
					
						
							|  |  |  |         IOHIDElementGetPhysicalMax(e)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ((type == "collection") && recurse) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     CFArrayRef elements = IOHIDElementGetChildren(e); | 
					
						
							|  |  |  |     CFRange range = {0, CFArrayGetCount(elements)}; | 
					
						
							|  |  |  |     // this leaks...but it's just debug code, right? :D | 
					
						
							|  |  |  |     CFArrayApplyFunction(elements, range, DeviceElementDebugPrint, nullptr); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2010-04-27 15:22:23 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-28 08:36:00 -04:00
										 |  |  | static void DeviceDebugPrint(IOHIDDeviceRef device) | 
					
						
							| 
									
										
										
										
											2010-04-27 15:22:23 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2010-06-26 13:03:25 +00:00
										 |  |  | #if 0 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #define shortlog(x) NSLog(@"%s: %@", x, IOHIDDeviceGetProperty(device, CFSTR(x))); | 
					
						
							| 
									
										
										
										
											2010-04-27 15:22:23 +00:00
										 |  |  | 	NSLog(@"-------------------------"); | 
					
						
							| 
									
										
										
										
											2010-06-26 13:03:25 +00:00
										 |  |  | 	NSLog(@"Got Device: %@", | 
					
						
							|  |  |  | 		IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey))); | 
					
						
							| 
									
										
										
										
											2010-04-27 15:22:23 +00:00
										 |  |  | 	shortlog(kIOHIDTransportKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDVendorIDKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDVendorIDSourceKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDProductIDKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDVersionNumberKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDManufacturerKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDProductKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDSerialNumberKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDCountryCodeKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDLocationIDKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDDeviceUsageKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDDeviceUsagePageKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDDeviceUsagePairsKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDPrimaryUsageKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDPrimaryUsagePageKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDMaxInputReportSizeKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDMaxOutputReportSizeKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDMaxFeatureReportSizeKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDReportIntervalKey) | 
					
						
							|  |  |  | 	shortlog(kIOHIDReportDescriptorKey) | 
					
						
							| 
									
										
										
										
											2010-06-26 13:03:25 +00:00
										 |  |  | #endif | 
					
						
							| 
									
										
										
										
											2010-04-27 15:22:23 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | static void* g_window; | 
					
						
							| 
									
										
										
										
											2010-04-27 15:22:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  | static std::string GetDeviceRefName(IOHIDDeviceRef inIOHIDDeviceRef) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   const NSString* name = reinterpret_cast<const NSString*>( | 
					
						
							|  |  |  |       IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDProductKey))); | 
					
						
							|  |  |  |   return (name != nullptr) ? StripSpaces([name UTF8String]) : "Unknown device"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void DeviceRemovalCallback(void* inContext, IOReturn inResult, void* inSender, | 
					
						
							|  |  |  |                                   IOHIDDeviceRef inIOHIDDeviceRef) | 
					
						
							| 
									
										
										
										
											2010-04-25 23:41:01 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  |   g_controller_interface.RemoveDevice([&inIOHIDDeviceRef](const auto* device) { | 
					
						
							|  |  |  |     const Joystick* joystick = dynamic_cast<const Joystick*>(device); | 
					
						
							|  |  |  |     if (joystick && joystick->IsSameDevice(inIOHIDDeviceRef)) | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const Keyboard* keyboard = dynamic_cast<const Keyboard*>(device); | 
					
						
							|  |  |  |     if (keyboard && keyboard->IsSameDevice(inIOHIDDeviceRef)) | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   g_controller_interface.InvokeHotplugCallbacks(); | 
					
						
							|  |  |  |   NOTICE_LOG(SERIALINTERFACE, "Removed device: %s", GetDeviceRefName(inIOHIDDeviceRef).c_str()); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2011-01-30 05:46:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  | static void DeviceMatchingCallback(void* inContext, IOReturn inResult, void* inSender, | 
					
						
							|  |  |  |                                    IOHIDDeviceRef inIOHIDDeviceRef) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   DeviceDebugPrint(inIOHIDDeviceRef); | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  |   std::string name = GetDeviceRefName(inIOHIDDeviceRef); | 
					
						
							| 
									
										
										
										
											2010-06-26 13:03:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-12 17:08:04 +02:00
										 |  |  |   // Add a device if it's of a type we want | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (IOHIDDeviceConformsTo(inIOHIDDeviceRef, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard)) | 
					
						
							| 
									
										
										
										
											2016-07-14 10:25:52 +02:00
										 |  |  |     g_controller_interface.AddDevice(std::make_shared<Keyboard>(inIOHIDDeviceRef, name, g_window)); | 
					
						
							| 
									
										
										
										
											2011-01-30 05:46:19 +00:00
										 |  |  | #if 0 | 
					
						
							| 
									
										
										
										
											2016-07-14 10:25:52 +02:00
										 |  |  |   else if (IOHIDDeviceConformsTo(inIOHIDDeviceRef, kHIDPage_GenericDesktop, kHIDUsage_GD_Mouse)) | 
					
						
							|  |  |  |     g_controller_interface.AddDevice(new Mouse(inIOHIDDeviceRef, name)); | 
					
						
							| 
									
										
										
										
											2011-01-30 05:46:19 +00:00
										 |  |  | #endif | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   else | 
					
						
							| 
									
										
										
										
											2016-07-14 10:25:52 +02:00
										 |  |  |     g_controller_interface.AddDevice(std::make_shared<Joystick>(inIOHIDDeviceRef, name)); | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   NOTICE_LOG(SERIALINTERFACE, "Added device: %s", name.c_str()); | 
					
						
							|  |  |  |   g_controller_interface.InvokeHotplugCallbacks(); | 
					
						
							| 
									
										
										
										
											2010-04-25 18:04:55 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-12 17:08:04 +02:00
										 |  |  | void Init(void* window) | 
					
						
							| 
									
										
										
										
											2010-04-25 18:04:55 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); | 
					
						
							|  |  |  |   if (!HIDManager) | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  |     ERROR_LOG(SERIALINTERFACE, "Failed to create HID Manager reference"); | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   g_window = window; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   IOHIDManagerSetDeviceMatching(HIDManager, nullptr); | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  |   if (IOHIDManagerOpen(HIDManager, kIOHIDOptionsTypeNone) != kIOReturnSuccess) | 
					
						
							|  |  |  |     ERROR_LOG(SERIALINTERFACE, "Failed to open HID Manager"); | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Callbacks for acquisition or loss of a matching device | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  |   IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, DeviceMatchingCallback, nullptr); | 
					
						
							|  |  |  |   IOHIDManagerRegisterDeviceRemovalCallback(HIDManager, DeviceRemovalCallback, nullptr); | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Match devices that are plugged in right now | 
					
						
							|  |  |  |   IOHIDManagerScheduleWithRunLoop(HIDManager, CFRunLoopGetCurrent(), OurRunLoop); | 
					
						
							|  |  |  |   while (CFRunLoopRunInMode(OurRunLoop, 0, TRUE) == kCFRunLoopRunHandledSource) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  |   IOHIDManagerUnscheduleFromRunLoop(HIDManager, CFRunLoopGetCurrent(), OurRunLoop); | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Enable hotplugging | 
					
						
							|  |  |  |   s_hotplug_thread = std::thread([] { | 
					
						
							|  |  |  |     Common::SetCurrentThreadName("IOHIDManager Hotplug Thread"); | 
					
						
							|  |  |  |     NOTICE_LOG(SERIALINTERFACE, "IOHIDManager hotplug thread started"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IOHIDManagerScheduleWithRunLoop(HIDManager, CFRunLoopGetCurrent(), OurRunLoop); | 
					
						
							|  |  |  |     s_stopper.AddToRunLoop(CFRunLoopGetCurrent(), OurRunLoop); | 
					
						
							|  |  |  |     CFRunLoopRunInMode(OurRunLoop, FOREVER, FALSE); | 
					
						
							|  |  |  |     s_stopper.RemoveFromRunLoop(CFRunLoopGetCurrent(), OurRunLoop); | 
					
						
							|  |  |  |     IOHIDManagerUnscheduleFromRunLoop(HIDManager, CFRunLoopGetCurrent(), OurRunLoop); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NOTICE_LOG(SERIALINTERFACE, "IOHIDManager hotplug thread stopped"); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2010-04-25 18:04:55 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-16 13:39:05 -07:00
										 |  |  | void PopulateDevices(void* window) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   DeInit(); | 
					
						
							|  |  |  |   Init(window); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-25 18:04:55 +00:00
										 |  |  | void DeInit() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-10-17 23:00:11 -07:00
										 |  |  |   s_stopper.Signal(); | 
					
						
							|  |  |  |   s_hotplug_thread.join(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   // This closes all devices as well | 
					
						
							|  |  |  |   IOHIDManagerClose(HIDManager, kIOHIDOptionsTypeNone); | 
					
						
							|  |  |  |   CFRelease(HIDManager); | 
					
						
							| 
									
										
										
										
											2010-04-25 18:04:55 +00:00
										 |  |  | } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | } |