Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

270 lines
8.0KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. AppleRemoteDevice::AppleRemoteDevice()
  22. : device (nullptr),
  23. queue (nullptr),
  24. remoteId (0)
  25. {
  26. }
  27. AppleRemoteDevice::~AppleRemoteDevice()
  28. {
  29. stop();
  30. }
  31. namespace
  32. {
  33. io_object_t getAppleRemoteDevice()
  34. {
  35. CFMutableDictionaryRef dict = IOServiceMatching ("AppleIRController");
  36. io_iterator_t iter = 0;
  37. io_object_t iod = 0;
  38. if (IOServiceGetMatchingServices (kIOMasterPortDefault, dict, &iter) == kIOReturnSuccess
  39. && iter != 0)
  40. {
  41. iod = IOIteratorNext (iter);
  42. }
  43. IOObjectRelease (iter);
  44. return iod;
  45. }
  46. bool createAppleRemoteInterface (io_object_t iod, void** device)
  47. {
  48. jassert (*device == nullptr);
  49. io_name_t classname;
  50. if (IOObjectGetClass (iod, classname) == kIOReturnSuccess)
  51. {
  52. IOCFPlugInInterface** cfPlugInInterface = nullptr;
  53. SInt32 score = 0;
  54. if (IOCreatePlugInInterfaceForService (iod,
  55. kIOHIDDeviceUserClientTypeID,
  56. kIOCFPlugInInterfaceID,
  57. &cfPlugInInterface,
  58. &score) == kIOReturnSuccess)
  59. {
  60. HRESULT hr = (*cfPlugInInterface)->QueryInterface (cfPlugInInterface,
  61. CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID),
  62. device);
  63. ignoreUnused (hr);
  64. (*cfPlugInInterface)->Release (cfPlugInInterface);
  65. }
  66. }
  67. return *device != nullptr;
  68. }
  69. void appleRemoteQueueCallback (void* const target, const IOReturn result, void*, void*)
  70. {
  71. if (result == kIOReturnSuccess)
  72. ((AppleRemoteDevice*) target)->handleCallbackInternal();
  73. }
  74. }
  75. bool AppleRemoteDevice::start (const bool inExclusiveMode)
  76. {
  77. if (queue != nullptr)
  78. return true;
  79. stop();
  80. bool result = false;
  81. io_object_t iod = getAppleRemoteDevice();
  82. if (iod != 0)
  83. {
  84. if (createAppleRemoteInterface (iod, &device) && open (inExclusiveMode))
  85. result = true;
  86. else
  87. stop();
  88. IOObjectRelease (iod);
  89. }
  90. return result;
  91. }
  92. void AppleRemoteDevice::stop()
  93. {
  94. if (queue != nullptr)
  95. {
  96. (*(IOHIDQueueInterface**) queue)->stop ((IOHIDQueueInterface**) queue);
  97. (*(IOHIDQueueInterface**) queue)->dispose ((IOHIDQueueInterface**) queue);
  98. (*(IOHIDQueueInterface**) queue)->Release ((IOHIDQueueInterface**) queue);
  99. queue = nullptr;
  100. }
  101. if (device != nullptr)
  102. {
  103. (*(IOHIDDeviceInterface**) device)->close ((IOHIDDeviceInterface**) device);
  104. (*(IOHIDDeviceInterface**) device)->Release ((IOHIDDeviceInterface**) device);
  105. device = nullptr;
  106. }
  107. }
  108. bool AppleRemoteDevice::isActive() const
  109. {
  110. return queue != nullptr;
  111. }
  112. bool AppleRemoteDevice::open (const bool openInExclusiveMode)
  113. {
  114. Array <int> cookies;
  115. CFArrayRef elements;
  116. IOHIDDeviceInterface122** const device122 = (IOHIDDeviceInterface122**) device;
  117. if ((*device122)->copyMatchingElements (device122, 0, &elements) != kIOReturnSuccess)
  118. return false;
  119. for (int i = 0; i < CFArrayGetCount (elements); ++i)
  120. {
  121. CFDictionaryRef element = (CFDictionaryRef) CFArrayGetValueAtIndex (elements, i);
  122. // get the cookie
  123. CFTypeRef object = CFDictionaryGetValue (element, CFSTR (kIOHIDElementCookieKey));
  124. if (object == 0 || CFGetTypeID (object) != CFNumberGetTypeID())
  125. continue;
  126. long number;
  127. if (! CFNumberGetValue ((CFNumberRef) object, kCFNumberLongType, &number))
  128. continue;
  129. cookies.add ((int) number);
  130. }
  131. CFRelease (elements);
  132. if ((*(IOHIDDeviceInterface**) device)
  133. ->open ((IOHIDDeviceInterface**) device,
  134. openInExclusiveMode ? kIOHIDOptionsTypeSeizeDevice
  135. : kIOHIDOptionsTypeNone) == KERN_SUCCESS)
  136. {
  137. queue = (*(IOHIDDeviceInterface**) device)->allocQueue ((IOHIDDeviceInterface**) device);
  138. if (queue != 0)
  139. {
  140. (*(IOHIDQueueInterface**) queue)->create ((IOHIDQueueInterface**) queue, 0, 12);
  141. for (int i = 0; i < cookies.size(); ++i)
  142. {
  143. IOHIDElementCookie cookie = (IOHIDElementCookie) cookies.getUnchecked(i);
  144. (*(IOHIDQueueInterface**) queue)->addElement ((IOHIDQueueInterface**) queue, cookie, 0);
  145. }
  146. CFRunLoopSourceRef eventSource;
  147. if ((*(IOHIDQueueInterface**) queue)
  148. ->createAsyncEventSource ((IOHIDQueueInterface**) queue, &eventSource) == KERN_SUCCESS)
  149. {
  150. if ((*(IOHIDQueueInterface**) queue)->setEventCallout ((IOHIDQueueInterface**) queue,
  151. appleRemoteQueueCallback, this, 0) == KERN_SUCCESS)
  152. {
  153. CFRunLoopAddSource (CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
  154. (*(IOHIDQueueInterface**) queue)->start ((IOHIDQueueInterface**) queue);
  155. return true;
  156. }
  157. }
  158. }
  159. }
  160. return false;
  161. }
  162. void AppleRemoteDevice::handleCallbackInternal()
  163. {
  164. int totalValues = 0;
  165. AbsoluteTime nullTime = { 0, 0 };
  166. char cookies [12];
  167. int numCookies = 0;
  168. while (numCookies < numElementsInArray (cookies))
  169. {
  170. IOHIDEventStruct e;
  171. if ((*(IOHIDQueueInterface**) queue)->getNextEvent ((IOHIDQueueInterface**) queue, &e, nullTime, 0) != kIOReturnSuccess)
  172. break;
  173. if ((int) e.elementCookie == 19)
  174. {
  175. remoteId = e.value;
  176. buttonPressed (switched, false);
  177. }
  178. else
  179. {
  180. totalValues += e.value;
  181. cookies [numCookies++] = (char) (pointer_sized_int) e.elementCookie;
  182. }
  183. }
  184. cookies [numCookies++] = 0;
  185. static const char buttonPatterns[] =
  186. {
  187. 0x1f, 0x14, 0x12, 0x1f, 0x14, 0x12, 0,
  188. 0x1f, 0x15, 0x12, 0x1f, 0x15, 0x12, 0,
  189. 0x1f, 0x1d, 0x1c, 0x12, 0,
  190. 0x1f, 0x1e, 0x1c, 0x12, 0,
  191. 0x1f, 0x16, 0x12, 0x1f, 0x16, 0x12, 0,
  192. 0x1f, 0x17, 0x12, 0x1f, 0x17, 0x12, 0,
  193. 0x1f, 0x12, 0x04, 0x02, 0,
  194. 0x1f, 0x12, 0x03, 0x02, 0,
  195. 0x1f, 0x12, 0x1f, 0x12, 0,
  196. 0x23, 0x1f, 0x12, 0x23, 0x1f, 0x12, 0,
  197. 19, 0
  198. };
  199. int buttonNum = (int) menuButton;
  200. int i = 0;
  201. while (i < numElementsInArray (buttonPatterns))
  202. {
  203. if (strcmp (cookies, buttonPatterns + i) == 0)
  204. {
  205. buttonPressed ((ButtonType) buttonNum, totalValues > 0);
  206. break;
  207. }
  208. i += (int) strlen (buttonPatterns + i) + 1;
  209. ++buttonNum;
  210. }
  211. }
  212. } // namespace juce