13#include <mach/mach_error.h>
14#include <IOKit/IOKitLib.h>
15#include <IOKit/IOCFPlugIn.h>
16#include <IOKit/hid/IOHIDLib.h>
17#include <IOKit/hid/IOHIDKeys.h>
18#include <CoreFoundation/CoreFoundation.h>
19#include <CoreServices/CoreServices.h>
20#include <AvailabilityMacros.h>
24#include "libmythbase/mythconfig.h"
28#define kIOMainPortDefault kIOMasterPortDefault
37#define LOC QString("AppleRemote::")
87 LOG(VB_GENERAL, LOG_ERR,
LOC +
"startListening() failed");
92 IOObjectRelease(hidDevice);
100 (*queue)->stop(
queue);
101 (*queue)->dispose(
queue);
102 (*queue)->Release(
queue);
123 CFRunLoopStop(CFRunLoopGetCurrent());
189 CFMutableDictionaryRef hidMatchDictionary =
nullptr;
190 io_iterator_t hidObjectIterator = 0;
191 io_object_t hidDevice = 0;
192 IOReturn ioReturnValue = 0;
195 hidMatchDictionary = IOServiceMatching(devName);
202 if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0))
203 hidDevice = IOIteratorNext(hidObjectIterator);
205 LOG(VB_GENERAL, LOG_ERR,
LOC +
206 QString(
"findAppleRemoteDevice(%1) failed").arg(devName));
210 hidMatchDictionary =
nullptr;
216 IOHIDDeviceInterface122** handle =
nullptr;
217 CFArrayRef elements =
nullptr;
218 IOReturn success = 0;
221 success = (*handle)->copyMatchingElements((
void*)handle,
nullptr, &elements);
223 if (success == kIOReturnSuccess)
225 for (CFIndex i = 0; i < CFArrayGetCount(elements); ++i)
227 CFDictionaryRef element =
nullptr;
228 CFTypeRef
object =
nullptr;
230 IOHIDElementCookie cookie = 0;
232 element = (CFDictionaryRef)CFArrayGetValueAtIndex(elements, i);
233 object = CFDictionaryGetValue(element,
234 CFSTR(kIOHIDElementCookieKey));
236 if (
object ==
nullptr || CFGetTypeID(
object) != CFNumberGetTypeID())
239 if (!CFNumberGetValue((CFNumberRef)
object,
240 kCFNumberLongType, &number))
243 cookie = (IOHIDElementCookie)number;
245 cookies.push_back((
int)cookie);
254 IOReturn ioReturnValue = 0;
255 IOCFPlugInInterface** plugInInterface =
nullptr;
260 = IOCreatePlugInInterfaceForService(hidDevice,
261 kIOHIDDeviceUserClientTypeID,
262 kIOCFPlugInInterfaceID,
263 &plugInInterface, &score);
265 if ((kIOReturnSuccess == ioReturnValue) &&
266 plugInInterface && *plugInInterface)
268 HRESULT plugInResult = (*plugInInterface)->QueryInterface
269 ((
void*)plugInInterface,
270 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
273 if (plugInResult != S_OK)
274 LOG(VB_GENERAL, LOG_ERR,
LOC +
"_createDeviceInterface() failed");
276 (*plugInInterface)->Release((
void*)plugInInterface);
284 CFRunLoopSourceRef eventSource =
nullptr;
285 IOReturn ioReturnValue = 0;
286 IOHIDOptionsType openMode = 0;
290 openMode = kIOHIDOptionsTypeSeizeDevice;
292 openMode = kIOHIDOptionsTypeNone;
296 if (ioReturnValue != KERN_SUCCESS)
298 LOG(VB_GENERAL, LOG_ERR,
LOC +
"_openDevice() failed");
304 LOG(VB_GENERAL, LOG_ERR,
LOC +
305 "_openDevice() - error allocating queue");
309 HRESULT result = (*queue)->create(
queue, 0, 12);
310 if (result != S_OK || !
queue)
311 LOG(VB_GENERAL, LOG_ERR,
LOC +
"_openDevice() - error creating queue");
315 auto cookie = (IOHIDElementCookie)iter;
316 (*queue)->addElement(
queue, cookie, 0);
319 ioReturnValue = (*queue)->createAsyncEventSource(
queue, &eventSource);
320 if (ioReturnValue != KERN_SUCCESS)
322 LOG(VB_GENERAL, LOG_ERR,
LOC +
323 "_openDevice() - failed to create async event source");
329 if (ioReturnValue != KERN_SUCCESS)
331 LOG(VB_GENERAL, LOG_ERR,
LOC +
332 "_openDevice() - error registering callback");
336 CFRunLoopAddSource(CFRunLoopGetCurrent(),
337 eventSource, kCFRunLoopDefaultMode);
338 (*queue)->start(
queue);
344 void* refcon,
void* sender)
354 AbsoluteTime zeroTime = {0,0};
355 SInt32 sumOfValues = 0;
356 std::stringstream cookieString;
358 while (result == kIOReturnSuccess)
360 IOHIDEventStruct event;
362 result = (*queue)->getNextEvent((
void*)
queue, &event, zeroTime, 0);
363 if (result != kIOReturnSuccess)
373 sumOfValues+=
event.value;
374 cookieString << std::dec << (int)event.elementCookie <<
"_";
384 std::map<std::string,AppleRemote::Event>::iterator ii;
static constexpr int REMOTE_SWITCH_COOKIE
static constexpr const char * REMOTE_COOKIE_STR
static io_object_t findAppleRemoteDevice(const char *devName)
#define kIOMainPortDefault
virtual void appleRemoteButton(Event button, bool pressedDown)=0
std::vector< int > cookies
IOHIDDeviceInterface ** hidDeviceInterface
void _initCookieMap()
Apple keeps changing the "interface" between the remote and the OS.
std::map< std::string, Event > cookieToButtonMapping
static AppleRemote * _instance
void setListener(Listener *listener)
bool _createDeviceInterface(io_object_t hidDevice)
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
static AppleRemote * Get()
IOHIDQueueInterface ** queue
void _queueCallbackFunction(IOReturn result, void *refcon, void *sender)
bool isListeningToRemote()
static void QueueCallbackFunction(void *target, IOReturn result, void *refcon, void *sender)
void _handleEventWithCookieString(const std::string &cookieString, SInt32 sumOfValues)
This is a wrapper around QThread that does several additional things.
bool isRunning(void) const
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
int exec(void)
Enters the qt event loop. call exit or quit to exit thread.
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
void exit(int retcode=0)
Use this to exit from the thread if you are using a Qt event loop.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)