forked from dolphin-emu/dolphin
		
	
		
			
				
	
	
		
			243 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			243 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
// Copyright 2013 Dolphin Emulator Project
 | 
						|
// Licensed under GPLv2+
 | 
						|
// Refer to the license.txt file included.
 | 
						|
 | 
						|
#include <thread>
 | 
						|
 | 
						|
#include <Cocoa/Cocoa.h>
 | 
						|
#include <Foundation/Foundation.h>
 | 
						|
#include <IOKit/hid/IOHIDLib.h>
 | 
						|
 | 
						|
#include "Common/Logging/Log.h"
 | 
						|
#include "Common/StringUtil.h"
 | 
						|
#include "Common/Thread.h"
 | 
						|
 | 
						|
#include "InputCommon/ControllerInterface/ControllerInterface.h"
 | 
						|
#include "InputCommon/ControllerInterface/OSX/OSX.h"
 | 
						|
#include "InputCommon/ControllerInterface/OSX/OSXJoystick.h"
 | 
						|
#include "InputCommon/ControllerInterface/OSX/OSXKeyboard.h"
 | 
						|
#include "InputCommon/ControllerInterface/OSX/RunLoopStopper.h"
 | 
						|
 | 
						|
namespace ciface
 | 
						|
{
 | 
						|
namespace OSX
 | 
						|
{
 | 
						|
constexpr CFTimeInterval FOREVER = 1e20;
 | 
						|
static std::thread s_hotplug_thread;
 | 
						|
static RunLoopStopper s_stopper;
 | 
						|
static IOHIDManagerRef HIDManager = nullptr;
 | 
						|
static CFStringRef OurRunLoop = CFSTR("DolphinOSXInput");
 | 
						|
 | 
						|
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 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)
 | 
						|
{
 | 
						|
  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());
 | 
						|
}
 | 
						|
 | 
						|
static void DeviceMatchingCallback(void* inContext, IOReturn inResult, void* inSender,
 | 
						|
                                   IOHIDDeviceRef inIOHIDDeviceRef)
 | 
						|
{
 | 
						|
  DeviceDebugPrint(inIOHIDDeviceRef);
 | 
						|
  std::string name = GetDeviceRefName(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<Keyboard>(inIOHIDDeviceRef, name, g_window));
 | 
						|
#if 0
 | 
						|
  else if (IOHIDDeviceConformsTo(inIOHIDDeviceRef, kHIDPage_GenericDesktop, kHIDUsage_GD_Mouse))
 | 
						|
    g_controller_interface.AddDevice(new Mouse(inIOHIDDeviceRef, name));
 | 
						|
#endif
 | 
						|
  else
 | 
						|
    g_controller_interface.AddDevice(std::make_shared<Joystick>(inIOHIDDeviceRef, name));
 | 
						|
 | 
						|
  NOTICE_LOG(SERIALINTERFACE, "Added device: %s", name.c_str());
 | 
						|
  g_controller_interface.InvokeHotplugCallbacks();
 | 
						|
}
 | 
						|
 | 
						|
void Init(void* window)
 | 
						|
{
 | 
						|
  HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
 | 
						|
  if (!HIDManager)
 | 
						|
    ERROR_LOG(SERIALINTERFACE, "Failed to create HID Manager reference");
 | 
						|
 | 
						|
  g_window = window;
 | 
						|
 | 
						|
  IOHIDManagerSetDeviceMatching(HIDManager, nullptr);
 | 
						|
  if (IOHIDManagerOpen(HIDManager, kIOHIDOptionsTypeNone) != kIOReturnSuccess)
 | 
						|
    ERROR_LOG(SERIALINTERFACE, "Failed to open HID Manager");
 | 
						|
 | 
						|
  // Callbacks for acquisition or loss of a matching device
 | 
						|
  IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, DeviceMatchingCallback, nullptr);
 | 
						|
  IOHIDManagerRegisterDeviceRemovalCallback(HIDManager, DeviceRemovalCallback, nullptr);
 | 
						|
 | 
						|
  // Match devices that are plugged in right now
 | 
						|
  IOHIDManagerScheduleWithRunLoop(HIDManager, CFRunLoopGetCurrent(), OurRunLoop);
 | 
						|
  while (CFRunLoopRunInMode(OurRunLoop, 0, TRUE) == kCFRunLoopRunHandledSource)
 | 
						|
  {
 | 
						|
  };
 | 
						|
  IOHIDManagerUnscheduleFromRunLoop(HIDManager, CFRunLoopGetCurrent(), OurRunLoop);
 | 
						|
 | 
						|
  // 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");
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
void PopulateDevices(void* window)
 | 
						|
{
 | 
						|
  DeInit();
 | 
						|
  Init(window);
 | 
						|
}
 | 
						|
 | 
						|
void DeInit()
 | 
						|
{
 | 
						|
  s_stopper.Signal();
 | 
						|
  s_hotplug_thread.join();
 | 
						|
 | 
						|
  // This closes all devices as well
 | 
						|
  IOHIDManagerClose(HIDManager, kIOHIDOptionsTypeNone);
 | 
						|
  CFRelease(HIDManager);
 | 
						|
}
 | 
						|
}
 | 
						|
}
 |