2 ==============================================================================
\r
4 This file is part of the JUCE library.
\r
5 Copyright (c) 2022 - Raw Material Software Limited
\r
7 JUCE is an open source library subject to commercial or open-source
\r
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
\r
11 Agreement and JUCE Privacy Policy.
\r
13 End User License Agreement: www.juce.com/juce-7-licence
\r
14 Privacy Policy: www.juce.com/juce-privacy-policy
\r
16 Or: You may also use this code under the terms of the GPL v3 (see
\r
17 www.gnu.org/licenses).
\r
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
\r
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
\r
23 ==============================================================================
\r
29 AppleRemoteDevice::AppleRemoteDevice()
\r
36 AppleRemoteDevice::~AppleRemoteDevice()
\r
43 io_object_t getAppleRemoteDevice()
\r
45 CFMutableDictionaryRef dict = IOServiceMatching ("AppleIRController");
\r
47 io_iterator_t iter = 0;
\r
48 io_object_t iod = 0;
\r
50 const auto defaultPort = []
\r
52 #if defined (MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0
\r
53 if (@available (macOS 12.0, *))
\r
54 return kIOMainPortDefault;
\r
57 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
\r
58 return kIOMasterPortDefault;
\r
59 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
\r
62 if (IOServiceGetMatchingServices (defaultPort, dict, &iter) == kIOReturnSuccess
\r
65 iod = IOIteratorNext (iter);
\r
68 IOObjectRelease (iter);
\r
72 bool createAppleRemoteInterface (io_object_t iod, void** device)
\r
74 jassert (*device == nullptr);
\r
75 io_name_t classname;
\r
77 if (IOObjectGetClass (iod, classname) == kIOReturnSuccess)
\r
79 IOCFPlugInInterface** cfPlugInInterface = nullptr;
\r
82 if (IOCreatePlugInInterfaceForService (iod,
\r
83 kIOHIDDeviceUserClientTypeID,
\r
84 kIOCFPlugInInterfaceID,
\r
86 &score) == kIOReturnSuccess)
\r
88 HRESULT hr = (*cfPlugInInterface)->QueryInterface (cfPlugInInterface,
\r
89 CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID),
\r
94 (*cfPlugInInterface)->Release (cfPlugInInterface);
\r
98 return *device != nullptr;
\r
101 void appleRemoteQueueCallback (void* const target, const IOReturn result, void*, void*)
\r
103 if (result == kIOReturnSuccess)
\r
104 ((AppleRemoteDevice*) target)->handleCallbackInternal();
\r
108 bool AppleRemoteDevice::start (const bool inExclusiveMode)
\r
110 if (queue != nullptr)
\r
115 bool result = false;
\r
116 io_object_t iod = getAppleRemoteDevice();
\r
120 if (createAppleRemoteInterface (iod, &device) && open (inExclusiveMode))
\r
125 IOObjectRelease (iod);
\r
131 void AppleRemoteDevice::stop()
\r
133 if (queue != nullptr)
\r
135 (*(IOHIDQueueInterface**) queue)->stop ((IOHIDQueueInterface**) queue);
\r
136 (*(IOHIDQueueInterface**) queue)->dispose ((IOHIDQueueInterface**) queue);
\r
137 (*(IOHIDQueueInterface**) queue)->Release ((IOHIDQueueInterface**) queue);
\r
141 if (device != nullptr)
\r
143 (*(IOHIDDeviceInterface**) device)->close ((IOHIDDeviceInterface**) device);
\r
144 (*(IOHIDDeviceInterface**) device)->Release ((IOHIDDeviceInterface**) device);
\r
149 bool AppleRemoteDevice::isActive() const
\r
151 return queue != nullptr;
\r
154 bool AppleRemoteDevice::open (const bool openInExclusiveMode)
\r
156 Array<int> cookies;
\r
158 CFObjectHolder<CFArrayRef> elements;
\r
159 auto device122 = (IOHIDDeviceInterface122**) device;
\r
161 if ((*device122)->copyMatchingElements (device122, nullptr, &elements.object) != kIOReturnSuccess)
\r
164 for (int i = 0; i < CFArrayGetCount (elements.object); ++i)
\r
166 auto element = (CFDictionaryRef) CFArrayGetValueAtIndex (elements.object, i);
\r
169 CFTypeRef object = CFDictionaryGetValue (element, CFSTR (kIOHIDElementCookieKey));
\r
171 if (object == nullptr || CFGetTypeID (object) != CFNumberGetTypeID())
\r
175 if (! CFNumberGetValue ((CFNumberRef) object, kCFNumberLongType, &number))
\r
178 cookies.add ((int) number);
\r
181 if ((*(IOHIDDeviceInterface**) device)
\r
182 ->open ((IOHIDDeviceInterface**) device,
\r
183 openInExclusiveMode ? kIOHIDOptionsTypeSeizeDevice
\r
184 : kIOHIDOptionsTypeNone) == KERN_SUCCESS)
\r
186 queue = (*(IOHIDDeviceInterface**) device)->allocQueue ((IOHIDDeviceInterface**) device);
\r
188 if (queue != nullptr)
\r
190 (*(IOHIDQueueInterface**) queue)->create ((IOHIDQueueInterface**) queue, 0, 12);
\r
192 for (int i = 0; i < cookies.size(); ++i)
\r
194 IOHIDElementCookie cookie = (IOHIDElementCookie) cookies.getUnchecked(i);
\r
195 (*(IOHIDQueueInterface**) queue)->addElement ((IOHIDQueueInterface**) queue, cookie, 0);
\r
198 CFRunLoopSourceRef eventSource;
\r
200 if ((*(IOHIDQueueInterface**) queue)
\r
201 ->createAsyncEventSource ((IOHIDQueueInterface**) queue, &eventSource) == KERN_SUCCESS)
\r
203 if ((*(IOHIDQueueInterface**) queue)->setEventCallout ((IOHIDQueueInterface**) queue,
\r
204 appleRemoteQueueCallback, this, nullptr) == KERN_SUCCESS)
\r
206 CFRunLoopAddSource (CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
\r
208 (*(IOHIDQueueInterface**) queue)->start ((IOHIDQueueInterface**) queue);
\r
219 void AppleRemoteDevice::handleCallbackInternal()
\r
221 int totalValues = 0;
\r
222 AbsoluteTime nullTime = { 0, 0 };
\r
224 int numCookies = 0;
\r
226 while (numCookies < numElementsInArray (cookies))
\r
228 IOHIDEventStruct e;
\r
230 if ((*(IOHIDQueueInterface**) queue)->getNextEvent ((IOHIDQueueInterface**) queue, &e, nullTime, 0) != kIOReturnSuccess)
\r
233 if ((int) e.elementCookie == 19)
\r
235 remoteId = e.value;
\r
236 buttonPressed (switched, false);
\r
240 totalValues += e.value;
\r
241 cookies [numCookies++] = (char) (pointer_sized_int) e.elementCookie;
\r
245 cookies [numCookies++] = 0;
\r
247 static const char buttonPatterns[] =
\r
249 0x1f, 0x14, 0x12, 0x1f, 0x14, 0x12, 0,
\r
250 0x1f, 0x15, 0x12, 0x1f, 0x15, 0x12, 0,
\r
251 0x1f, 0x1d, 0x1c, 0x12, 0,
\r
252 0x1f, 0x1e, 0x1c, 0x12, 0,
\r
253 0x1f, 0x16, 0x12, 0x1f, 0x16, 0x12, 0,
\r
254 0x1f, 0x17, 0x12, 0x1f, 0x17, 0x12, 0,
\r
255 0x1f, 0x12, 0x04, 0x02, 0,
\r
256 0x1f, 0x12, 0x03, 0x02, 0,
\r
257 0x1f, 0x12, 0x1f, 0x12, 0,
\r
258 0x23, 0x1f, 0x12, 0x23, 0x1f, 0x12, 0,
\r
262 int buttonNum = (int) menuButton;
\r
265 while (i < numElementsInArray (buttonPatterns))
\r
267 if (strcmp (cookies, buttonPatterns + i) == 0)
\r
269 buttonPressed ((ButtonType) buttonNum, totalValues > 0);
\r
273 i += (int) strlen (buttonPatterns + i) + 1;
\r
278 } // namespace juce
\r