Vuo  2.1.2
VuoHidIo.cc
Go to the documentation of this file.
1 
10 #include "VuoHid.h"
11 #include "VuoTriggerSet.hh"
12 #include "VuoIoReturn.h"
13 
14 #include <IOKit/hid/IOHIDLib.h>
15 
16 extern "C"
17 {
18 
19 #ifdef VUO_COMPILER
21  "title" : "VuoHidIo",
22  "dependencies" : [
23  "VuoHidDevice",
24  "VuoHidDevices",
25  "VuoIoReturn",
26  "VuoList_VuoHidDevice"
27  ]
28  });
29 #endif
30 }
31 
32 
36 typedef struct _VuoHid_internal
37 {
38  VuoHidDevice device;
39  VuoBoolean exclusive;
40 
42  IOHIDManagerRef manager;
44 
45 
46 static dispatch_queue_t VuoHid_pendingDevicesQueue;
47 static std::set<VuoHid_internal> VuoHid_pendingDevices;
48 
49 
53 static void __attribute__((constructor)) VuoHidIo_init()
54 {
55  VuoHid_pendingDevicesQueue = dispatch_queue_create("org.vuo.hid.pendingDevicesQueue", NULL);
56 }
57 
61 static void VuoHid_inputValueCallback(void *context, IOReturn result, void *manager, IOHIDValueRef value)
62 {
63  if (result != kIOReturnSuccess)
64  {
65  VUserLog("Error: %s", VuoIoReturn_getText(result));
66  return;
67  }
68 
69  VuoHid_internal si = (VuoHid_internal)context;
70 
71  // IOHIDManager can't match on kIOHIDLocationIDKey, so we have to match on other criteria, then filter those results by location.
72  IOHIDElementRef element = IOHIDValueGetElement(value);
73  IOHIDDeviceRef device = IOHIDElementGetDevice(element);
74  if (VuoHid_getLocation(device) != si->device.location)
75  return;
76 
77  VuoHidControl control = VuoHid_getControlForElement(element);
78 
79 // if (!VuoHid_isElementValid(element))
80 // {
81 // VLog("Error: invalid element (%s)", control.name);
82 // return;
83 // }
84 
85  control.value = IOHIDValueGetIntegerValue(value);
86 
87  si->triggerSet.fire(control);
88 }
89 
94 {
95  si->manager = NULL;
96 
97 
98  VuoHidDevice realizedDevice;
99  if (!VuoHidDevice_realize(si->device, &realizedDevice))
100  return;
101 
102  VuoHidDevice_retain(realizedDevice);
103  VuoHidDevice_release(si->device);
104  si->device = realizedDevice;
105 
106 
107  si->manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
108  if (!si->manager)
109  {
110  VUserLog("Error: Couldn't initialize IOHIDManager.");
111  return;
112  }
113 
114 
115  CFMutableDictionaryRef matchesCF = CFDictionaryCreateMutable(NULL, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
116 
117  // IOHIDManager can't match on kIOHIDLocationIDKey, so we have to match on other criteria, then filter those results by location.
118 // uint32_t location = si->device.location >> 8;
119 // CFNumberRef locationCF = CFNumberCreate(NULL, kCFNumberSInt32Type, &location);
120 // CFDictionarySetValue(matchesCF, CFSTR(kIOHIDLocationIDKey), locationCF);
121 // CFRelease(locationCF);
122 
123  if (realizedDevice.vendorID)
124  {
125  CFNumberRef vendorCF = CFNumberCreate(NULL, kCFNumberSInt64Type, &realizedDevice.vendorID);
126  CFDictionarySetValue(matchesCF, CFSTR(kIOHIDVendorIDKey), vendorCF);
127  CFRelease(vendorCF);
128  }
129 
130  if (realizedDevice.productID)
131  {
132  CFNumberRef productCF = CFNumberCreate(NULL, kCFNumberSInt64Type, &realizedDevice.productID);
133  CFDictionarySetValue(matchesCF, CFSTR(kIOHIDProductIDKey), productCF);
134  CFRelease(productCF);
135  }
136 
137  if (realizedDevice.usagePage)
138  {
139  CFNumberRef usagePageCF = CFNumberCreate(NULL, kCFNumberSInt64Type, &realizedDevice.usagePage);
140  CFDictionarySetValue(matchesCF, CFSTR(kIOHIDDeviceUsagePageKey), usagePageCF);
141  CFRelease(usagePageCF);
142  }
143 
144  if (realizedDevice.usage)
145  {
146  CFNumberRef usageCF = CFNumberCreate(NULL, kCFNumberSInt64Type, &realizedDevice.usage);
147  CFDictionarySetValue(matchesCF, CFSTR(kIOHIDDeviceUsageKey), usageCF);
148  CFRelease(usageCF);
149  }
150 
151  IOHIDManagerSetDeviceMatching(si->manager, matchesCF);
152  CFRelease(matchesCF);
153 
154 
155  IOReturn ret = IOHIDManagerOpen(si->manager, si->exclusive ? kIOHIDOptionsTypeSeizeDevice : kIOHIDOptionsTypeNone);
156  if (ret != kIOReturnSuccess)
157  {
158  char *text = VuoIoReturn_getText(ret);
159  VUserLog("Error: Couldn't open IOHIDManager: %s", text);
160  free(text);
161  CFRelease(si->manager);
162  si->manager = NULL;
163  return;
164  }
165 
166 
167  CFSetRef deviceCF = IOHIDManagerCopyDevices(si->manager);
168  if (!deviceCF)
169  {
170  VUserLog("Error: Couldn't copy device list.");
171  CFRelease(si->manager);
172  si->manager = NULL;
173  return;
174  }
175  if (CFSetGetCount(deviceCF) == 0)
176  {
177  CFRelease(deviceCF);
178  CFRelease(si->manager);
179  si->manager = NULL;
180  return;
181  }
182  CFRelease(deviceCF);
183 
184 
185  IOHIDManagerRegisterInputValueCallback(si->manager, VuoHid_inputValueCallback, si);
186 
187  IOHIDManagerScheduleWithRunLoop(si->manager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
188 }
189 
196 {
197  dispatch_async(VuoHid_pendingDevicesQueue, ^{
198  for (std::set<VuoHid_internal>::iterator i = VuoHid_pendingDevices.begin(); i != VuoHid_pendingDevices.end();)
199  {
200  VuoHid_openDevice(*i);
201  if (!(*i)->manager)
202  {
203  ++i;
204  continue;
205  }
206 
207  VuoHid_pendingDevices.erase(i++);
208  }
209  });
210 }
211 
216 {
217  dispatch_sync(VuoHid_pendingDevicesQueue, ^{
218  VuoHid_pendingDevices.erase(si);
219  });
220 
221  if (si->manager)
222  {
223  VUOLOG_PROFILE_BEGIN(mainQueue);
224  dispatch_sync(dispatch_get_main_queue(), ^{
225  VUOLOG_PROFILE_END(mainQueue);
226  IOHIDManagerRegisterInputValueCallback(si->manager, NULL, NULL);
227  IOHIDManagerClose(si->manager, kIOHIDOptionsTypeNone);
228  CFRelease(si->manager);
229  });
230  }
231 
232  VuoHidDevice_release(si->device);
233  delete si;
234 }
235 
239 VuoHid VuoHid_make(const VuoHidDevice device, const VuoBoolean exclusive)
240 {
241  if (!device.name)
242  return NULL;
243 
244  VuoHid_internal si = new _VuoHid_internal;
246 
247  si->device = device;
248  VuoHidDevice_retain(si->device);
249 
250  si->exclusive = exclusive;
251 
252  VuoHid_openDevice(si);
253 
254  if (!si->manager)
255  {
256  VUserLog("Warning: HID device '%s' isn't currently available. I'll keep trying.", VuoHidDevice_getSummary(si->device));
257  dispatch_async(VuoHid_pendingDevicesQueue, ^{
258  VuoHid_pendingDevices.insert(si);
259  });
260  }
261 
262  return si;
263 }
264 
265 
266 
273 {
274  if (!device)
275  return;
276 
277  VuoHid_internal si = (VuoHid_internal)device;
278  si->triggerSet.addTrigger(receivedControl);
279 }
280 
281 
288 {
289  if (!device)
290  return;
291 
292  VuoHid_internal si = (VuoHid_internal)device;
293  si->triggerSet.removeTrigger(receivedControl);
294 }