Etterna 0.74.4
Loading...
Searching...
No Matches
HIDDevice.h
1#ifndef HIDDEVICE_H
2#define HIDDEVICE_H
3
4#include <CoreFoundation/CoreFoundation.h>
5#include <IOKit/hid/IOHIDLib.h>
6#include <IOKit/IOKitLib.h>
7#include <IOKit/IOCFPlugIn.h>
8#include <IOKit/hid/IOHIDUsageTables.h>
9#include <IOKit/usb/USB.h>
10#include <mach/mach.h>
11#include <mach/mach_error.h>
12#include <vector>
13#include <utility>
14#include <ext/hash_map>
15
16#include "Core/Services/Locator.hpp"
17#include "RageUtil/Misc/RageInputDevice.h"
18
19/* A few helper functions. */
20
21// The result needs to be released.
22inline CFNumberRef
23CFInt(int n)
24{
25 return CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &n);
26}
27
28inline void
29PrintIOErr(IOReturn err, const char* s)
30{
31 Locator::getLogger()->warn("{} - {}({:x},{})", s, mach_error_string(err), err, err & 0xFFFFFF);
32}
33
34inline Boolean
35IntValue(CFTypeRef o, int& n)
36{
37 if (!o || CFGetTypeID(o) != CFNumberGetTypeID())
38 return false;
39 return CFNumberGetValue(CFNumberRef(o), kCFNumberIntType, &n);
40}
41
42inline Boolean
43LongValue(CFTypeRef o, long& n)
44{
45 if (!o || CFGetTypeID(o) != CFNumberGetTypeID())
46 return false;
47 return CFNumberGetValue(CFNumberRef(o), kCFNumberLongType, &n);
48}
49
50namespace __gnu_cxx {
51#ifndef __LP64__
52template<>
53struct hash<IOHIDElementCookie> : private hash<uintptr_t>
54{
55 size_t operator()(const IOHIDElementCookie& cookie) const
56 {
57 return hash<unsigned long>::operator()(uintptr_t(cookie));
58 }
59};
60#endif
61}
62
63/* This is just awful, these aren't objects, treating them as such
64 * leads to: (*object)->function(object [, argument]...)
65 * Instead, do: CALL(object, function [, argument]...)
66 */
67#define CALL(o, f, ...) (*(o))->f((o), ##__VA_ARGS__)
68
70{
71 private:
72 IOHIDDeviceInterface** m_Interface;
73 IOHIDQueueInterface** m_Queue;
74 bool m_bRunning;
75 std::string m_sDescription;
76
77 static void AddLogicalDevice(const void* value, void* context);
78 static void AddElement(const void* value, void* context);
79
80 protected:
81 /* Each physical device has zero or more logical devices. If this device
82 * allows a logical device of type (usagePage, usage), then allocate storage
83 * as necessary and return true, otherwise, return false. */
84 virtual bool AddLogicalDevice(int usagePage, int usage) = 0;
85
86 /* If the most recently added logical device cares about the state of an
87 * element of type (usagePage, usage), store the cookie. */
88 virtual void AddElement(int usagePage,
89 int usage,
90 IOHIDElementCookie cookie,
91 const CFDictionaryRef properties) = 0;
92
93 // Add any elements to the queue by calling AddElementToQueue() with the
94 // stored cookies.
95 virtual void Open() = 0;
96
97 // Optional. Subclasses can initialize the device, if required.
98 virtual bool InitDevice(int vid, int pid) { return true; }
99
100 // This adds the element with the given cookie to the queue to be notified
101 // of state changes.
102 inline void AddElementToQueue(IOHIDElementCookie cookie)
103 {
104 IOReturn ret = CALL(m_Queue, addElement, cookie, 0);
105 if (ret != KERN_SUCCESS)
106 Locator::getLogger()->warn("Failed to add HID element with cookie %p to queue: {}",
107 static_cast<UInt32>(cookie),ret);
108 }
109
110 // Perform a synchronous set report on the HID interface.
111 inline IOReturn SetReport(IOHIDReportType type,
112 UInt32 reportID,
113 void* buffer,
114 UInt32 size,
115 UInt32 timeoutMS)
116 {
117 return CALL(m_Interface,
118 setReport,
119 type,
120 reportID,
121 buffer,
122 size,
123 timeoutMS,
124 NULL,
125 NULL,
126 NULL);
127 }
128
129 public:
130 HIDDevice();
131 virtual ~HIDDevice();
132
133 bool Open(io_object_t device);
134 void StartQueue(CFRunLoopRef loopRef,
135 IOHIDCallbackFunction callback,
136 void* target,
137 int refCon);
138 inline const std::string& GetDescription() const { return m_sDescription; }
139
140 /* Add button presses (or releases) to vPresses for the given cookie. More
141 * than one DeviceInput can be added at a time. For example, Two axes
142 * presses may be generated by a single element. The value of the element is
143 * passed to determine if this is a push or a release. The time is provided
144 * as an optimization. */
145 virtual void GetButtonPresses(
146 std::vector<DeviceInput>& vPresses,
147 IOHIDElementCookie cookie,
148 int value,
149 const std::chrono::time_point<std::chrono::steady_clock>& now) const = 0;
150
151 /* Returns the number of IDs assigned starting from startID. This is not
152 * meaningful for devices like keyboards that all share the same InputDevice
153 * id. If a particular device has multiple logical devices, then it must
154 * ensure that AssignIDs does not assign an ID outside of its range.
155 * Return -1 to indicate that the device does not share the same InputDevice
156 * and none could be assigned. */
157 virtual int AssignIDs(InputDevice startID) { return 0; }
158
159 // Add a device and a description for each logical device.
160 virtual void GetDevicesAndDescriptions(
161 std::vector<InputDeviceInfo>& vDevices) const = 0;
162};
163
164#endif
Definition HIDDevice.h:70