MythTV  master
AppleRemote.cpp
Go to the documentation of this file.
1 
2 #include "AppleRemote.h"
3 
4 #include <cctype>
5 #include <cstdio>
6 #include <cstdlib>
7 #include <sys/errno.h>
8 #include <sys/sysctl.h> // for sysctlbyname
9 #include <sysexits.h>
10 #include <unistd.h>
11 
12 #include <mach/mach.h>
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> // for Gestalt
20 
21 #include <sstream>
22 
23 #include "mythlogging.h"
24 
26 
27 
28 #define REMOTE_SWITCH_COOKIE 19
29 #define REMOTE_COOKIE_STR "19_"
30 #define ATV_COOKIE_STR "17_9_280_"
31 #define LONG_PRESS_COUNT 10
32 #define KEY_RESPONSE_TIME 150 /* msecs before we send a key up event */
33 
34 #define LOC QString("AppleRemote::")
35 
36 typedef struct _ATV_IR_EVENT
37 {
38  UInt32 time_ms32;
39  UInt32 time_ls32; // units of microsecond
40  UInt32 unknown1;
41  UInt32 keycode;
42  UInt32 unknown2;
43 } ATV_IR_EVENT;
44 
45 static io_object_t _findAppleRemoteDevice(const char *devName);
46 
48 {
49  if (_instance == nullptr)
50  _instance = new AppleRemote();
51 
52  return _instance;
53 }
54 
56 {
57  stopListening();
58 
59  if (mUsingNewAtv)
60  delete mCallbackTimer;
61 
62  if (isRunning())
63  {
64  exit(0);
65  }
66  if (this == _instance)
67  {
68  _instance = nullptr;
69  }
70 }
71 
73 {
74  return (hidDeviceInterface != nullptr && !cookies.empty() && queue != nullptr);
75 }
76 
78 {
80 }
81 
83 {
84  if (queue != nullptr) // already listening
85  return;
86 
87  io_object_t hidDevice = _findAppleRemoteDevice("AppleIRController");
88 
89  if (!hidDevice)
90  hidDevice = _findAppleRemoteDevice("AppleTVIRReceiver");
91 
92  if (!hidDevice ||
93  !_createDeviceInterface(hidDevice) ||
94  !_initCookies() || !_openDevice())
95  {
96  LOG(VB_GENERAL, LOG_ERR, LOC + "startListening() failed");
97  stopListening();
98  return;
99  }
100 
101  IOObjectRelease(hidDevice);
102 }
103 
105 {
106  if (queue != nullptr)
107  {
108  (*queue)->stop(queue);
109  (*queue)->dispose(queue);
110  (*queue)->Release(queue);
111  queue = nullptr;
112  }
113 
114  if (!cookies.empty())
115  cookies.clear();
116 
117  if (hidDeviceInterface != nullptr)
118  {
119  (*hidDeviceInterface)->close(hidDeviceInterface);
120  (*hidDeviceInterface)->Release(hidDeviceInterface);
121  hidDeviceInterface = nullptr;
122  }
123 }
124 
126 {
127  RunProlog();
128  CFRunLoopRun();
129  exec(); // prevent QThread exiting, by entering its run loop
130  CFRunLoopStop(CFRunLoopGetCurrent());
131  RunEpilog();
132 }
133 
134 // Figure out if we're running on the Apple TV, and what version
135 static float GetATVversion()
136 {
137  SInt32 macVersion;
138  size_t len = 512;
139  char hw_model[512] = "unknown";
140 
141 
142  Gestalt(gestaltSystemVersion, &macVersion);
143 
144  if ( macVersion > 0x1040 ) // Mac OS 10.5 or greater is
145  return 0.0; // definitely not an Apple TV
146 
147  sysctlbyname("hw.model", &hw_model, &len, nullptr, 0);
148 
149  float version = 0.0;
150  if ( strstr(hw_model,"AppleTV1,1") )
151  {
152  // Find the build version of the AppleTV OS
153  FILE *inpipe = popen("sw_vers -buildVersion", "r");
154  char linebuf[1000];
155  if (inpipe && fgets(linebuf, sizeof(linebuf) - 1, inpipe) )
156  {
157  if ( strstr(linebuf,"8N5107") ) version = 1.0;
158  else if (strstr(linebuf,"8N5239") ) version = 1.1;
159  else if (strstr(linebuf,"8N5400") ) version = 2.0;
160  else if (strstr(linebuf,"8N5455") ) version = 2.01;
161  else if (strstr(linebuf,"8N5461") ) version = 2.02;
162  else if (strstr(linebuf,"8N5519") ) version = 2.1;
163  else if (strstr(linebuf,"8N5622") ) version = 2.2;
164  else
165  version = 2.3;
166  }
167  if (inpipe)
168  pclose(inpipe);
169  }
170  return version;
171 }
172 
173 // protected
175  openInExclusiveMode(true),
176  hidDeviceInterface(0),
177  queue(0),
178  remoteId(0),
179  _listener(nullptr),
180  mUsingNewAtv(false),
181  mLastEvent(AppleRemote::Undefined),
182  mEventCount(0),
183  mKeyIsDown(false)
184 {
185  if ( GetATVversion() > 2.2 )
186  {
187  LOG(VB_GENERAL, LOG_INFO,
188  LOC + "AppleRemote() detected Apple TV > v2.3");
189  mUsingNewAtv = true;
190  mCallbackTimer = new QTimer();
191  QObject::connect(mCallbackTimer, SIGNAL(timeout()),
192  (const QObject*)this, SLOT(TimeoutHandler()));
193  mCallbackTimer->setSingleShot(true);
194  mCallbackTimer->setInterval(KEY_RESPONSE_TIME);
195  }
196 
197  _initCookieMap();
198 }
199 
210 {
211  // 10.4 sequences:
212  cookieToButtonMapping["14_12_11_6_5_"] = Up;
213  cookieToButtonMapping["14_13_11_6_5_"] = Down;
214  cookieToButtonMapping["14_7_6_5_14_7_6_5_"] = Menu;
215  cookieToButtonMapping["14_8_6_5_14_8_6_5_"] = Select;
216  cookieToButtonMapping["14_9_6_5_14_9_6_5_"] = Right;
217  cookieToButtonMapping["14_10_6_5_14_10_6_5_"] = Left;
218  cookieToButtonMapping["14_6_5_4_2_"] = RightHold;
219  cookieToButtonMapping["14_6_5_3_2_"] = LeftHold;
220  cookieToButtonMapping["14_6_5_14_6_5_"] = MenuHold;
221  cookieToButtonMapping["18_14_6_5_18_14_6_5_"] = PlayHold;
223 
224  // 10.5 sequences:
225  cookieToButtonMapping["31_29_28_18_"] = Up;
226  cookieToButtonMapping["31_30_28_18_"] = Down;
227  cookieToButtonMapping["31_20_18_31_20_18_"] = Menu;
228  cookieToButtonMapping["31_21_18_31_21_18_"] = Select;
229  cookieToButtonMapping["31_22_18_31_22_18_"] = Right;
230  cookieToButtonMapping["31_23_18_31_23_18_"] = Left;
231  cookieToButtonMapping["31_18_4_2_"] = RightHold;
232  cookieToButtonMapping["31_18_3_2_"] = LeftHold;
233  cookieToButtonMapping["31_18_31_18_"] = MenuHold;
234  cookieToButtonMapping["35_31_18_35_31_18_"] = PlayHold;
236 
237  // ATV 1.0, 2.0-2.02
238  cookieToButtonMapping["14_12_11_6_"] = Up;
239  cookieToButtonMapping["14_13_11_6_"] = Down;
240  cookieToButtonMapping["14_7_6_14_7_6_"] = Menu;
241  cookieToButtonMapping["14_8_6_14_8_6_"] = Select;
242  cookieToButtonMapping["14_9_6_14_9_6_"] = Right;
243  cookieToButtonMapping["14_10_6_14_10_6_"] = Left;
244  cookieToButtonMapping["14_6_4_2_"] = RightHold;
245  cookieToButtonMapping["14_6_3_2_"] = LeftHold;
246  cookieToButtonMapping["14_6_14_6_"] = MenuHold;
247  cookieToButtonMapping["18_14_6_18_14_6_"] = PlayHold;
248 
249  // ATV 1.0, 2.1-2.2
250  cookieToButtonMapping["15_13_12_"] = Up;
251  cookieToButtonMapping["15_14_12_"] = Down;
252  cookieToButtonMapping["15_8_15_8_"] = Menu;
253  cookieToButtonMapping["15_9_15_9_"] = Select;
254  cookieToButtonMapping["15_10_15_10_"] = Right;
255  cookieToButtonMapping["15_11_15_11_"] = Left;
256  cookieToButtonMapping["15_5_3_"] = RightHold;
257  cookieToButtonMapping["15_4_3_"] = LeftHold;
258  cookieToButtonMapping["15_6_15_6_"] = MenuHold;
259  cookieToButtonMapping["19_15_19_15_"] = PlayHold;
260 
261  // ATV 2.30
262  cookieToButtonMapping["17_9_280_80"] = Up;
263  cookieToButtonMapping["17_9_280_48"] = Down;
264  cookieToButtonMapping["17_9_280_64"] = Menu;
265  cookieToButtonMapping["17_9_280_32"] = Select;
266  cookieToButtonMapping["17_9_280_96"] = Right;
267  cookieToButtonMapping["17_9_280_16"] = Left;
268 
269  // 10.6 sequences:
270  cookieToButtonMapping["33_31_30_21_20_2_"] = Up;
271  cookieToButtonMapping["33_32_30_21_20_2_"] = Down;
272  cookieToButtonMapping["33_22_21_20_2_33_22_21_20_2_"] = Menu;
273  cookieToButtonMapping["33_23_21_20_2_33_23_21_20_2_"] = Select;
274  cookieToButtonMapping["33_24_21_20_2_33_24_21_20_2_"] = Right;
275  cookieToButtonMapping["33_25_21_20_2_33_25_21_20_2_"] = Left;
276  cookieToButtonMapping["33_21_20_14_12_2_"] = RightHold;
277  cookieToButtonMapping["33_21_20_13_12_2_"] = LeftHold;
278  cookieToButtonMapping["33_21_20_2_33_21_20_2_"] = MenuHold;
279  cookieToButtonMapping["37_33_21_20_2_37_33_21_20_2_"] = PlayHold;
280 
281  // Aluminium remote which has an extra button:
282  cookieToButtonMapping["33_21_20_8_2_33_21_20_8_2_"] = PlayPause;
283  cookieToButtonMapping["33_21_20_3_2_33_21_20_3_2_"] = Select;
284  cookieToButtonMapping["33_21_20_11_2_33_21_20_11_2_"] = PlayHold;
285 }
286 
287 static io_object_t _findAppleRemoteDevice(const char *devName)
288 {
289  CFMutableDictionaryRef hidMatchDictionary = 0;
290  io_iterator_t hidObjectIterator = 0;
291  io_object_t hidDevice = 0;
292  IOReturn ioReturnValue;
293 
294 
295  hidMatchDictionary = IOServiceMatching(devName);
296 
297  // check for matching devices
298  ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault,
299  hidMatchDictionary,
300  &hidObjectIterator);
301 
302  if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0))
303  hidDevice = IOIteratorNext(hidObjectIterator);
304  else
305  LOG(VB_GENERAL, LOG_ERR, LOC +
306  QString("_findAppleRemoteDevice(%1) failed").arg(devName));
307 
308  // IOServiceGetMatchingServices consumes a reference to the dictionary,
309  // so we don't need to release the dictionary ref.
310  hidMatchDictionary = 0;
311  return hidDevice;
312 }
313 
315 {
316  IOHIDDeviceInterface122** handle;
317  CFArrayRef elements;
318  IOReturn success;
319 
320  handle = (IOHIDDeviceInterface122**)hidDeviceInterface;
321  success = (*handle)->copyMatchingElements(handle,
322  nullptr,
323  (CFArrayRef*)&elements);
324 
325  if (success == kIOReturnSuccess)
326  {
327  for (CFIndex i = 0; i < CFArrayGetCount(elements); ++i)
328  {
329  CFDictionaryRef element;
330  CFTypeRef object;
331  long number;
332  IOHIDElementCookie cookie;
333 
334  element = (CFDictionaryRef)CFArrayGetValueAtIndex(elements, i);
335  object = CFDictionaryGetValue(element,
336  CFSTR(kIOHIDElementCookieKey));
337 
338  if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
339  continue;
340 
341  if (!CFNumberGetValue((CFNumberRef)object,
342  kCFNumberLongType, &number))
343  continue;
344 
345  cookie = (IOHIDElementCookie)number;
346 
347  cookies.push_back((int)cookie);
348  }
349  return true;
350  }
351  return false;
352 }
353 
354 bool AppleRemote::_createDeviceInterface(io_object_t hidDevice)
355 {
356  IOReturn ioReturnValue;
357  IOCFPlugInInterface** plugInInterface = nullptr;
358  SInt32 score = 0;
359 
360 
361  ioReturnValue
362  = IOCreatePlugInInterfaceForService(hidDevice,
363  kIOHIDDeviceUserClientTypeID,
364  kIOCFPlugInInterfaceID,
365  &plugInInterface, &score);
366 
367  if ((kIOReturnSuccess == ioReturnValue) &&
368  plugInInterface && *plugInInterface)
369  {
370  HRESULT plugInResult = (*plugInInterface)->QueryInterface
371  (plugInInterface,
372  CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
373  (LPVOID*) (&hidDeviceInterface));
374 
375  if (plugInResult != S_OK)
376  LOG(VB_GENERAL, LOG_ERR, LOC + "_createDeviceInterface() failed");
377 
378  (*plugInInterface)->Release(plugInInterface);
379  }
380  return hidDeviceInterface != 0;
381 }
382 
384 {
385  CFRunLoopSourceRef eventSource;
386  IOReturn ioReturnValue;
387  IOHIDOptionsType openMode;
388 
389 
391  openMode = kIOHIDOptionsTypeSeizeDevice;
392  else
393  openMode = kIOHIDOptionsTypeNone;
394 
395  ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, openMode);
396 
397  if (ioReturnValue != KERN_SUCCESS)
398  {
399  LOG(VB_GENERAL, LOG_ERR, LOC + "_openDevice() failed");
400  return false;
401  }
402  queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
403  if (!queue)
404  {
405  LOG(VB_GENERAL, LOG_ERR, LOC +
406  "_openDevice() - error allocating queue");
407  return false;
408  }
409 
410  HRESULT result = (*queue)->create(queue, 0, 12);
411  if (result != S_OK || !queue)
412  LOG(VB_GENERAL, LOG_ERR, LOC + "_openDevice() - error creating queue");
413 
414  for (std::vector<int>::iterator iter = cookies.begin();
415  iter != cookies.end();
416  ++iter)
417  {
418  IOHIDElementCookie cookie = (IOHIDElementCookie)(*iter);
419  (*queue)->addElement(queue, cookie, 0);
420  }
421 
422  ioReturnValue = (*queue)->createAsyncEventSource(queue, &eventSource);
423  if (ioReturnValue != KERN_SUCCESS)
424  {
425  LOG(VB_GENERAL, LOG_ERR, LOC +
426  "_openDevice() - failed to create async event source");
427  return false;
428  }
429 
430  ioReturnValue = (*queue)->setEventCallout(queue, QueueCallbackFunction,
431  this, nullptr);
432  if (ioReturnValue != KERN_SUCCESS)
433  {
434  LOG(VB_GENERAL, LOG_ERR, LOC +
435  "_openDevice() - error registering callback");
436  return false;
437  }
438 
439  CFRunLoopAddSource(CFRunLoopGetCurrent(),
440  eventSource, kCFRunLoopDefaultMode);
441  (*queue)->start(queue);
442  return true;
443 }
444 
445 void AppleRemote::QueueCallbackFunction(void* target, IOReturn result,
446  void* refcon, void* sender)
447 {
448  AppleRemote* remote = static_cast<AppleRemote*>(target);
449 
450  if (remote->mUsingNewAtv)
451  remote->_queueCallbackATV23(result);
452  else
453  remote->_queueCallbackFunction(result, refcon, sender);
454 }
455 
457  void* /*refcon*/, void* /*sender*/)
458 {
459  AbsoluteTime zeroTime = {0,0};
460  SInt32 sumOfValues = 0;
461  std::stringstream cookieString;
462 
463  while (result == kIOReturnSuccess)
464  {
465  IOHIDEventStruct event;
466 
467  result = (*queue)->getNextEvent(queue, &event, zeroTime, 0);
468  if (result != kIOReturnSuccess)
469  break;
470 
471  if (REMOTE_SWITCH_COOKIE == (int)event.elementCookie)
472  {
473  remoteId=event.value;
475  }
476  else
477  {
478  sumOfValues+=event.value;
479  cookieString << std::dec << (int)event.elementCookie << "_";
480  }
481  }
482 
483  _handleEventWithCookieString(cookieString.str(), sumOfValues);
484 }
485 
486 void AppleRemote::_queueCallbackATV23(IOReturn result)
487 {
488  AbsoluteTime zeroTime = {0,0};
489  SInt32 sumOfValues = 0;
490  std::stringstream cookieString;
491  UInt32 key_code = 0;
492 
493 
494  if (mCallbackTimer->isActive())
495  {
496  mCallbackTimer->stop();
497  }
498 
499  while (result == kIOReturnSuccess)
500  {
501  IOHIDEventStruct event;
502 
503  result = (*queue)->getNextEvent(queue, &event, zeroTime, 0);
504  if (result != kIOReturnSuccess)
505  continue;
506 
507  if ( ((int)event.elementCookie == 280) && (event.longValueSize == 20))
508  {
509  ATV_IR_EVENT* atv_ir_event = (ATV_IR_EVENT*)event.longValue;
510  key_code = atv_ir_event->keycode;
511  }
512 
513  if (((int)event.elementCookie) != 5 )
514  {
515  sumOfValues += event.value;
516  cookieString << std::dec << (int)event.elementCookie << "_";
517  }
518  }
519 
520  if (strcmp(cookieString.str().c_str(), ATV_COOKIE_STR) == 0)
521  {
522  cookieString << std::dec << (int) ( (key_code & 0x00007F00) >> 8);
523 
524  sumOfValues = 1;
525  _handleEventATV23(cookieString.str(), sumOfValues);
526  }
527 }
528 
529 void AppleRemote::_handleEventWithCookieString(std::string cookieString,
530  SInt32 sumOfValues)
531 {
532  std::map<std::string,AppleRemote::Event>::iterator ii;
533 
534  ii = cookieToButtonMapping.find(cookieString);
535  if (ii != cookieToButtonMapping.end() && _listener)
536  {
537  AppleRemote::Event buttonid = ii->second;
538  if (_listener)
539  _listener->appleRemoteButton(buttonid, sumOfValues>0);
540  }
541 }
542 
543 // With the ATV from 2.3 onwards, we just get IR events.
544 // We need to simulate the key up and hold events
545 
546 void AppleRemote::_handleEventATV23(std::string cookieString,
547  SInt32 sumOfValues)
548 {
549  std::map<std::string,AppleRemote::Event>::iterator ii;
550  ii = cookieToButtonMapping.find(cookieString);
551 
552  if (ii != cookieToButtonMapping.end() )
553  {
554  AppleRemote::Event event = ii->second;
555 
556  if (mLastEvent == Undefined) // new event
557  {
558  mEventCount = 1;
559  // Need to figure out if this is a long press or a short press,
560  // so can't just send a key down event right now. It will be
561  // scheduled to run
562  }
563  else if (event != mLastEvent) // a new event, faster than timer
564  {
565  mEventCount = 1;
566  mKeyIsDown = true;
567 
568  if (_listener)
569  {
570  // Only send key up events for events that have separateRelease
571  // defined as true in AppleRemoteListener.cpp
572  if (mLastEvent == Up || mLastEvent == Down ||
574  {
576  /*pressedDown*/false);
577  }
579  }
580  }
581  else // Same event again
582  {
583  AppleRemote::Event newEvent = Undefined;
584 
585  ++mEventCount;
586 
587  // Can the event have a hold state?
588  switch (event)
589  {
590  case Right:
591  newEvent = RightHold;
592  break;
593  case Left:
594  newEvent = LeftHold;
595  break;
596  case Menu:
597  newEvent = MenuHold;
598  break;
599  case Select:
600  newEvent = PlayHold;
601  break;
602  default:
603  newEvent = event;
604  }
605 
606  if (newEvent == event) // Doesn't have a long press
607  {
608  if (mKeyIsDown)
609  {
610  if (_listener)
611  {
612  // Only send key up events for events that have separateRelease
613  // defined as true in AppleRemoteListener.cpp
614  if (mLastEvent == Up || mLastEvent == Down ||
616  {
617  _listener->appleRemoteButton(mLastEvent, /*pressedDown*/false);
618  }
619  }
620  }
621 
622  mKeyIsDown = true;
623  if (_listener)
624  {
626  }
627  }
628  else if (mEventCount == LONG_PRESS_COUNT)
629  {
630  mKeyIsDown = true;
631  if (_listener)
632  {
634  }
635  }
636  }
637 
638  mLastEvent = event;
639  mCallbackTimer->start();
640  }
641 }
642 
643 // Calls key down / up events on the ATV > v2.3
645 {
646  if (_listener)
647  {
649  }
650 
652 
653  if (!mKeyIsDown)
654  {
655  mEventCount = 0;
657  }
658  else
659  {
660  // Schedule a key up event for events that have separateRelease
661  // defined as true in AppleRemoteListener.cpp
662 
663  if (mLastEvent == Up || mLastEvent == Down ||
665  {
666  mCallbackTimer->start();
667  }
668  else
669  {
670  mKeyIsDown = false;
671  mEventCount = 0;
673  }
674 
675  }
676 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:216
bool mKeyIsDown
Definition: AppleRemote.h:78
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
bool isListeningToRemote()
Definition: AppleRemote.cpp:72
#define REMOTE_SWITCH_COOKIE
Definition: AppleRemote.cpp:28
Listener * _listener
Definition: AppleRemote.h:73
void run()
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:91
void _handleEventWithCookieString(std::string cookieString, SInt32 sumOfValues)
struct _ATV_IR_EVENT ATV_IR_EVENT
UInt32 time_ms32
Definition: AppleRemote.cpp:38
static AppleRemote * Get()
Definition: AppleRemote.cpp:47
void _initCookieMap()
Apple keeps changing the "interface" between the remote and the OS.
static void QueueCallbackFunction(void *target, IOReturn result, void *refcon, void *sender)
AppleRemote::Event mLastEvent
Definition: AppleRemote.h:76
static io_object_t _findAppleRemoteDevice(const char *devName)
#define ATV_COOKIE_STR
Definition: AppleRemote.cpp:30
bool _openDevice()
QTimer * mCallbackTimer
Definition: AppleRemote.h:79
void _handleEventATV23(std::string cookieString, SInt32 sumOfValues)
#define LONG_PRESS_COUNT
Definition: AppleRemote.cpp:31
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
void _queueCallbackFunction(IOReturn result, void *refcon, void *sender)
#define REMOTE_COOKIE_STR
Definition: AppleRemote.cpp:29
virtual void appleRemoteButton(Event button, bool pressedDown)=0
bool mUsingNewAtv
Definition: AppleRemote.h:75
void startListening()
Definition: AppleRemote.cpp:82
void exit(int retcode=0)
Use this to exit from the thread if you are using a Qt event loop.
Definition: mthread.cpp:290
bool isRunning(void) const
Definition: mthread.cpp:275
#define KEY_RESPONSE_TIME
Definition: AppleRemote.cpp:32
UInt32 time_ls32
Definition: AppleRemote.cpp:39
int FILE
Definition: mythburn.py:110
static AppleRemote * _instance
Definition: AppleRemote.h:63
int mEventCount
Definition: AppleRemote.h:77
std::vector< int > cookies
Definition: AppleRemote.h:70
IOHIDDeviceInterface ** hidDeviceInterface
Definition: AppleRemote.h:68
void TimeoutHandler()
Listener * listener()
Definition: AppleRemote.h:53
bool openInExclusiveMode
Definition: AppleRemote.h:67
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void stopListening()
bool _createDeviceInterface(io_object_t hidDevice)
std::map< std::string, Event > cookieToButtonMapping
Definition: AppleRemote.h:71
#define LOC
Definition: AppleRemote.cpp:34
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:203
static float GetATVversion()
void setListener(Listener *listener)
Definition: AppleRemote.cpp:77
IOHIDQueueInterface ** queue
Definition: AppleRemote.h:69
void _queueCallbackATV23(IOReturn result)
bool _initCookies()
int exec(void)
Enters the qt event loop. call exit or quit to exit thread.
Definition: mthread.cpp:329