forked from dolphin-emu/dolphin
		
	
		
			
				
	
	
		
			286 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			286 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| // Copyright 2013 Dolphin Emulator Project
 | |
| // Licensed under GPLv2+
 | |
| // Refer to the license.txt file included.
 | |
| 
 | |
| #include <sstream>
 | |
| 
 | |
| #include <Foundation/Foundation.h>
 | |
| #include <IOKit/hid/IOHIDLib.h>
 | |
| 
 | |
| #include "Common/StringUtil.h"
 | |
| #include "InputCommon/ControllerInterface/OSX/OSXJoystick.h"
 | |
| 
 | |
| namespace ciface
 | |
| {
 | |
| namespace OSX
 | |
| {
 | |
| Joystick::Joystick(IOHIDDeviceRef device, std::string name)
 | |
|     : m_device(device), m_device_name(name), m_ff_device(nullptr)
 | |
| {
 | |
|   // Buttons
 | |
|   NSDictionary* buttonDict = @{
 | |
|     @kIOHIDElementTypeKey : @(kIOHIDElementTypeInput_Button),
 | |
|     @kIOHIDElementUsagePageKey : @(kHIDPage_Button)
 | |
|   };
 | |
| 
 | |
|   CFArrayRef buttons =
 | |
|       IOHIDDeviceCopyMatchingElements(m_device, (CFDictionaryRef)buttonDict, kIOHIDOptionsTypeNone);
 | |
| 
 | |
|   if (buttons)
 | |
|   {
 | |
|     for (int i = 0; i < CFArrayGetCount(buttons); i++)
 | |
|     {
 | |
|       IOHIDElementRef e = (IOHIDElementRef)CFArrayGetValueAtIndex(buttons, i);
 | |
|       // DeviceElementDebugPrint(e, nullptr);
 | |
| 
 | |
|       AddInput(new Button(e, m_device));
 | |
|     }
 | |
|     CFRelease(buttons);
 | |
|   }
 | |
| 
 | |
|   // Axes
 | |
|   NSDictionary* axisDict = @{ @kIOHIDElementTypeKey : @(kIOHIDElementTypeInput_Misc) };
 | |
| 
 | |
|   CFArrayRef axes =
 | |
|       IOHIDDeviceCopyMatchingElements(m_device, (CFDictionaryRef)axisDict, kIOHIDOptionsTypeNone);
 | |
| 
 | |
|   if (axes)
 | |
|   {
 | |
|     for (int i = 0; i < CFArrayGetCount(axes); i++)
 | |
|     {
 | |
|       IOHIDElementRef e = (IOHIDElementRef)CFArrayGetValueAtIndex(axes, i);
 | |
|       // DeviceElementDebugPrint(e, nullptr);
 | |
| 
 | |
|       if (IOHIDElementGetUsage(e) == kHIDUsage_GD_Hatswitch)
 | |
|       {
 | |
|         AddInput(new Hat(e, m_device, Hat::up));
 | |
|         AddInput(new Hat(e, m_device, Hat::right));
 | |
|         AddInput(new Hat(e, m_device, Hat::down));
 | |
|         AddInput(new Hat(e, m_device, Hat::left));
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         AddAnalogInputs(new Axis(e, m_device, Axis::negative),
 | |
|                         new Axis(e, m_device, Axis::positive));
 | |
|       }
 | |
|     }
 | |
|     CFRelease(axes);
 | |
|   }
 | |
| 
 | |
|   // Force Feedback
 | |
|   FFCAPABILITIES ff_caps;
 | |
|   if (SUCCEEDED(
 | |
|           ForceFeedback::FFDeviceAdapter::Create(IOHIDDeviceGetService(m_device), &m_ff_device)) &&
 | |
|       SUCCEEDED(FFDeviceGetForceFeedbackCapabilities(m_ff_device->m_device, &ff_caps)))
 | |
|   {
 | |
|     InitForceFeedback(m_ff_device, ff_caps.numFfAxes);
 | |
|   }
 | |
| }
 | |
| 
 | |
| Joystick::~Joystick()
 | |
| {
 | |
|   if (m_ff_device)
 | |
|     m_ff_device->Release();
 | |
| }
 | |
| 
 | |
| std::string Joystick::GetName() const
 | |
| {
 | |
|   return m_device_name;
 | |
| }
 | |
| 
 | |
| std::string Joystick::GetSource() const
 | |
| {
 | |
|   return "Input";
 | |
| }
 | |
| 
 | |
| ControlState Joystick::Button::GetState() const
 | |
| {
 | |
|   IOHIDValueRef value;
 | |
|   if (IOHIDDeviceGetValue(m_device, m_element, &value) == kIOReturnSuccess)
 | |
|     return IOHIDValueGetIntegerValue(value);
 | |
|   else
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| std::string Joystick::Button::GetName() const
 | |
| {
 | |
|   std::ostringstream s;
 | |
|   s << IOHIDElementGetUsage(m_element);
 | |
|   return std::string("Button ") + StripSpaces(s.str());
 | |
| }
 | |
| 
 | |
| Joystick::Axis::Axis(IOHIDElementRef element, IOHIDDeviceRef device, direction dir)
 | |
|     : m_element(element), m_device(device), m_direction(dir)
 | |
| {
 | |
|   // Need to parse the element a bit first
 | |
|   std::string description("unk");
 | |
| 
 | |
|   int const usage = IOHIDElementGetUsage(m_element);
 | |
|   switch (usage)
 | |
|   {
 | |
|   case kHIDUsage_GD_X:
 | |
|     description = "X";
 | |
|     break;
 | |
|   case kHIDUsage_GD_Y:
 | |
|     description = "Y";
 | |
|     break;
 | |
|   case kHIDUsage_GD_Z:
 | |
|     description = "Z";
 | |
|     break;
 | |
|   case kHIDUsage_GD_Rx:
 | |
|     description = "Rx";
 | |
|     break;
 | |
|   case kHIDUsage_GD_Ry:
 | |
|     description = "Ry";
 | |
|     break;
 | |
|   case kHIDUsage_GD_Rz:
 | |
|     description = "Rz";
 | |
|     break;
 | |
|   case kHIDUsage_GD_Wheel:
 | |
|     description = "Wheel";
 | |
|     break;
 | |
|   case kHIDUsage_Csmr_ACPan:
 | |
|     description = "Pan";
 | |
|     break;
 | |
|   default:
 | |
|   {
 | |
|     IOHIDElementCookie elementCookie = IOHIDElementGetCookie(m_element);
 | |
|     // This axis isn't a 'well-known' one so cook a descriptive and uniquely
 | |
|     // identifiable name. macOS provides a 'cookie' for each element that
 | |
|     // will persist between sessions and identify the same physical controller
 | |
|     // element so we can use that as a component of the axis name
 | |
|     std::ostringstream s;
 | |
|     s << "CK-";
 | |
|     s << elementCookie;
 | |
|     description = StripSpaces(s.str());
 | |
|     break;
 | |
|   }
 | |
|   }
 | |
| 
 | |
|   m_name = std::string("Axis ") + description;
 | |
|   m_name.append((m_direction == positive) ? "+" : "-");
 | |
| 
 | |
|   m_neutral = (IOHIDElementGetLogicalMax(m_element) + IOHIDElementGetLogicalMin(m_element)) / 2.;
 | |
|   m_scale = 1 / fabs(IOHIDElementGetLogicalMax(m_element) - m_neutral);
 | |
| }
 | |
| 
 | |
| ControlState Joystick::Axis::GetState() const
 | |
| {
 | |
|   IOHIDValueRef value;
 | |
| 
 | |
|   if (IOHIDDeviceGetValue(m_device, m_element, &value) == kIOReturnSuccess)
 | |
|   {
 | |
|     // IOHIDValueGetIntegerValue() crashes when trying
 | |
|     // to convert unusually large element values.
 | |
|     if (IOHIDValueGetLength(value) > 2)
 | |
|       return 0;
 | |
| 
 | |
|     float position = IOHIDValueGetIntegerValue(value);
 | |
| 
 | |
|     if (m_direction == positive && position > m_neutral)
 | |
|       return (position - m_neutral) * m_scale;
 | |
|     if (m_direction == negative && position < m_neutral)
 | |
|       return (m_neutral - position) * m_scale;
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| std::string Joystick::Axis::GetName() const
 | |
| {
 | |
|   return m_name;
 | |
| }
 | |
| 
 | |
| Joystick::Hat::Hat(IOHIDElementRef element, IOHIDDeviceRef device, direction dir)
 | |
|     : m_element(element), m_device(device), m_direction(dir)
 | |
| {
 | |
|   switch (dir)
 | |
|   {
 | |
|   case up:
 | |
|     m_name = "Up";
 | |
|     break;
 | |
|   case right:
 | |
|     m_name = "Right";
 | |
|     break;
 | |
|   case down:
 | |
|     m_name = "Down";
 | |
|     break;
 | |
|   case left:
 | |
|     m_name = "Left";
 | |
|     break;
 | |
|   default:
 | |
|     m_name = "unk";
 | |
|   }
 | |
| }
 | |
| 
 | |
| ControlState Joystick::Hat::GetState() const
 | |
| {
 | |
|   IOHIDValueRef value;
 | |
| 
 | |
|   if (IOHIDDeviceGetValue(m_device, m_element, &value) == kIOReturnSuccess)
 | |
|   {
 | |
|     int position = IOHIDValueGetIntegerValue(value);
 | |
|     int min = IOHIDElementGetLogicalMin(m_element);
 | |
|     int max = IOHIDElementGetLogicalMax(m_element);
 | |
| 
 | |
|     // if the position is outside the min or max, don't register it as a valid button press
 | |
|     if (position < min || position > max)
 | |
|     {
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     // normalize the position so that its lowest value is 0
 | |
|     position -= min;
 | |
| 
 | |
|     switch (position)
 | |
|     {
 | |
|     case 0:
 | |
|       if (m_direction == up)
 | |
|         return 1;
 | |
|       break;
 | |
|     case 1:
 | |
|       if (m_direction == up || m_direction == right)
 | |
|         return 1;
 | |
|       break;
 | |
|     case 2:
 | |
|       if (m_direction == right)
 | |
|         return 1;
 | |
|       break;
 | |
|     case 3:
 | |
|       if (m_direction == right || m_direction == down)
 | |
|         return 1;
 | |
|       break;
 | |
|     case 4:
 | |
|       if (m_direction == down)
 | |
|         return 1;
 | |
|       break;
 | |
|     case 5:
 | |
|       if (m_direction == down || m_direction == left)
 | |
|         return 1;
 | |
|       break;
 | |
|     case 6:
 | |
|       if (m_direction == left)
 | |
|         return 1;
 | |
|       break;
 | |
|     case 7:
 | |
|       if (m_direction == left || m_direction == up)
 | |
|         return 1;
 | |
|       break;
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| std::string Joystick::Hat::GetName() const
 | |
| {
 | |
|   return m_name;
 | |
| }
 | |
| 
 | |
| bool Joystick::IsSameDevice(const IOHIDDeviceRef other_device) const
 | |
| {
 | |
|   return m_device == other_device;
 | |
| }
 | |
| }
 | |
| }
 |