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
34#define REMOTE_SWITCH_COOKIE 19
35#define REMOTE_COOKIE_STR "19_"
36#define LONG_PRESS_COUNT 10
38#define LOC QString("AppleRemote::")
88 LOG(VB_GENERAL, LOG_ERR,
LOC +
"startListening() failed");
93 IOObjectRelease(hidDevice);
100 (*queue)->stop(
queue);
101 (*queue)->dispose(
queue);
102 (*queue)->Release(
queue);
122 CFRunLoopStop(CFRunLoopGetCurrent());
188 CFMutableDictionaryRef hidMatchDictionary =
nullptr;
189 io_iterator_t hidObjectIterator = 0;
190 io_object_t hidDevice = 0;
191 IOReturn ioReturnValue;
194 hidMatchDictionary = IOServiceMatching(devName);
201 if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0))
202 hidDevice = IOIteratorNext(hidObjectIterator);
204 LOG(VB_GENERAL, LOG_ERR,
LOC +
205 QString(
"_findAppleRemoteDevice(%1) failed").arg(devName));
209 hidMatchDictionary =
nullptr;
215 IOHIDDeviceInterface122** handle;
220 success = (*handle)->copyMatchingElements(handle,
222 (CFArrayRef*)&elements);
224 if (success == kIOReturnSuccess)
226 for (CFIndex i = 0; i < CFArrayGetCount(elements); ++i)
228 CFDictionaryRef element;
231 IOHIDElementCookie cookie;
233 element = (CFDictionaryRef)CFArrayGetValueAtIndex(elements, i);
234 object = CFDictionaryGetValue(element,
235 CFSTR(kIOHIDElementCookieKey));
237 if (
object ==
nullptr || CFGetTypeID(
object) != CFNumberGetTypeID())
240 if (!CFNumberGetValue((CFNumberRef)
object,
241 kCFNumberLongType, &number))
244 cookie = (IOHIDElementCookie)number;
246 cookies.push_back((
int)cookie);
255 IOReturn ioReturnValue;
256 IOCFPlugInInterface** plugInInterface =
nullptr;
261 = IOCreatePlugInInterfaceForService(hidDevice,
262 kIOHIDDeviceUserClientTypeID,
263 kIOCFPlugInInterfaceID,
264 &plugInInterface, &score);
266 if ((kIOReturnSuccess == ioReturnValue) &&
267 plugInInterface && *plugInInterface)
269 HRESULT plugInResult = (*plugInInterface)->QueryInterface
271 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
274 if (plugInResult != S_OK)
275 LOG(VB_GENERAL, LOG_ERR,
LOC +
"_createDeviceInterface() failed");
277 (*plugInInterface)->Release(plugInInterface);
284 CFRunLoopSourceRef eventSource;
285 IOReturn ioReturnValue;
286 IOHIDOptionsType openMode;
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);
343 void* refcon,
void* sender)
353 AbsoluteTime zeroTime = {0,0};
354 SInt32 sumOfValues = 0;
355 std::stringstream cookieString;
357 while (result == kIOReturnSuccess)
359 IOHIDEventStruct event;
361 result = (*queue)->getNextEvent(
queue, &event, zeroTime, 0);
362 if (result != kIOReturnSuccess)
372 sumOfValues+=
event.value;
373 cookieString << std::dec << (int)event.elementCookie <<
"_";
383 std::map<std::string,AppleRemote::Event>::iterator ii;
#define REMOTE_COOKIE_STR
static io_object_t _findAppleRemoteDevice(const char *devName)
#define REMOTE_SWITCH_COOKIE
#define kIOMainPortDefault
virtual void appleRemoteButton(Event button, bool pressedDown)=0
void _handleEventWithCookieString(std::string cookieString, SInt32 sumOfValues)
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)
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_)