// Copyright 2013 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #include #include #include #include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/ControllerInterface/OSX/OSX.h" #include "InputCommon/ControllerInterface/OSX/OSXJoystick.h" #include "InputCommon/ControllerInterface/OSX/OSXKeyboard.h" #include namespace ciface { namespace OSX { static IOHIDManagerRef HIDManager = nullptr; static CFStringRef OurRunLoop = CFSTR("DolphinOSXInput"); static std::map kbd_name_counts, joy_name_counts; void DeviceElementDebugPrint(const void* value, void* context) { 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); } } static void DeviceDebugPrint(IOHIDDeviceRef device) { #if 0 #define shortlog(x) NSLog(@"%s: %@", x, IOHIDDeviceGetProperty(device, CFSTR(x))); NSLog(@"-------------------------"); NSLog(@"Got Device: %@", IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey))); 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) #endif } static void* g_window; static void DeviceMatching_callback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef inIOHIDDeviceRef) { NSString* pName = (NSString*)IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDProductKey)); std::string name = (pName != nullptr) ? [pName UTF8String] : "Unknown device"; DeviceDebugPrint(inIOHIDDeviceRef); // Add a device if it's of a type we want if (IOHIDDeviceConformsTo(inIOHIDDeviceRef, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard)) g_controller_interface.AddDevice( std::make_shared(inIOHIDDeviceRef, name, kbd_name_counts[name]++, g_window)); #if 0 else if (IOHIDDeviceConformsTo(inIOHIDDeviceRef, kHIDPage_GenericDesktop, kHIDUsage_GD_Mouse)) g_controller_interface.AddDevice(new Mouse(inIOHIDDeviceRef, name, mouse_name_counts[name]++)); #endif else g_controller_interface.AddDevice( std::make_shared(inIOHIDDeviceRef, name, joy_name_counts[name]++)); } void Init(void* window) { HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); if (!HIDManager) NSLog(@"Failed to create HID Manager reference"); g_window = window; IOHIDManagerSetDeviceMatching(HIDManager, nullptr); // Callbacks for acquisition or loss of a matching device IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, DeviceMatching_callback, nullptr); // Match devices that are plugged in right now IOHIDManagerScheduleWithRunLoop(HIDManager, CFRunLoopGetCurrent(), OurRunLoop); if (IOHIDManagerOpen(HIDManager, kIOHIDOptionsTypeNone) != kIOReturnSuccess) NSLog(@"Failed to open HID Manager"); kbd_name_counts.clear(); joy_name_counts.clear(); // Wait while current devices are initialized while (CFRunLoopRunInMode(OurRunLoop, 0, TRUE) == kCFRunLoopRunHandledSource) { }; // Things should be configured now // Disable hotplugging and other scheduling IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, nullptr, nullptr); IOHIDManagerUnscheduleFromRunLoop(HIDManager, CFRunLoopGetCurrent(), OurRunLoop); } void DeInit() { // This closes all devices as well IOHIDManagerClose(HIDManager, kIOHIDOptionsTypeNone); CFRelease(HIDManager); } } }