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 {
176  if ( GetATVversion() > 2.2 )
177  {
178  LOG(VB_GENERAL, LOG_INFO,
179  LOC + "AppleRemote() detected Apple TV > v2.3");
180  mUsingNewAtv = true;
181  mCallbackTimer = new QTimer();
182  QObject::connect(mCallbackTimer, SIGNAL(timeout()),
183  (const QObject*)this, SLOT(TimeoutHandler()));
184  mCallbackTimer->setSingleShot(true);
185  mCallbackTimer->setInterval(KEY_RESPONSE_TIME);
186  }
187 
188  _initCookieMap();
189 }
190 
201 {
202  // 10.4 sequences:
203  cookieToButtonMapping["14_12_11_6_5_"] = Up;
204  cookieToButtonMapping["14_13_11_6_5_"] = Down;
205  cookieToButtonMapping["14_7_6_5_14_7_6_5_"] = Menu;
206  cookieToButtonMapping["14_8_6_5_14_8_6_5_"] = Select;
207  cookieToButtonMapping["14_9_6_5_14_9_6_5_"] = Right;
208  cookieToButtonMapping["14_10_6_5_14_10_6_5_"] = Left;
209  cookieToButtonMapping["14_6_5_4_2_"] = RightHold;
210  cookieToButtonMapping["14_6_5_3_2_"] = LeftHold;
211  cookieToButtonMapping["14_6_5_14_6_5_"] = MenuHold;
212  cookieToButtonMapping["18_14_6_5_18_14_6_5_"] = PlayHold;
214 
215  // 10.5 sequences:
216  cookieToButtonMapping["31_29_28_18_"] = Up;
217  cookieToButtonMapping["31_30_28_18_"] = Down;
218  cookieToButtonMapping["31_20_18_31_20_18_"] = Menu;
219  cookieToButtonMapping["31_21_18_31_21_18_"] = Select;
220  cookieToButtonMapping["31_22_18_31_22_18_"] = Right;
221  cookieToButtonMapping["31_23_18_31_23_18_"] = Left;
222  cookieToButtonMapping["31_18_4_2_"] = RightHold;
223  cookieToButtonMapping["31_18_3_2_"] = LeftHold;
224  cookieToButtonMapping["31_18_31_18_"] = MenuHold;
225  cookieToButtonMapping["35_31_18_35_31_18_"] = PlayHold;
227 
228  // ATV 1.0, 2.0-2.02
229  cookieToButtonMapping["14_12_11_6_"] = Up;
230  cookieToButtonMapping["14_13_11_6_"] = Down;
231  cookieToButtonMapping["14_7_6_14_7_6_"] = Menu;
232  cookieToButtonMapping["14_8_6_14_8_6_"] = Select;
233  cookieToButtonMapping["14_9_6_14_9_6_"] = Right;
234  cookieToButtonMapping["14_10_6_14_10_6_"] = Left;
235  cookieToButtonMapping["14_6_4_2_"] = RightHold;
236  cookieToButtonMapping["14_6_3_2_"] = LeftHold;
237  cookieToButtonMapping["14_6_14_6_"] = MenuHold;
238  cookieToButtonMapping["18_14_6_18_14_6_"] = PlayHold;
239 
240  // ATV 1.0, 2.1-2.2
241  cookieToButtonMapping["15_13_12_"] = Up;
242  cookieToButtonMapping["15_14_12_"] = Down;
243  cookieToButtonMapping["15_8_15_8_"] = Menu;
244  cookieToButtonMapping["15_9_15_9_"] = Select;
245  cookieToButtonMapping["15_10_15_10_"] = Right;
246  cookieToButtonMapping["15_11_15_11_"] = Left;
247  cookieToButtonMapping["15_5_3_"] = RightHold;
248  cookieToButtonMapping["15_4_3_"] = LeftHold;
249  cookieToButtonMapping["15_6_15_6_"] = MenuHold;
250  cookieToButtonMapping["19_15_19_15_"] = PlayHold;
251 
252  // ATV 2.30
253  cookieToButtonMapping["17_9_280_80"] = Up;
254  cookieToButtonMapping["17_9_280_48"] = Down;
255  cookieToButtonMapping["17_9_280_64"] = Menu;
256  cookieToButtonMapping["17_9_280_32"] = Select;
257  cookieToButtonMapping["17_9_280_96"] = Right;
258  cookieToButtonMapping["17_9_280_16"] = Left;
259 
260  // 10.6 sequences:
261  cookieToButtonMapping["33_31_30_21_20_2_"] = Up;
262  cookieToButtonMapping["33_32_30_21_20_2_"] = Down;
263  cookieToButtonMapping["33_22_21_20_2_33_22_21_20_2_"] = Menu;
264  cookieToButtonMapping["33_23_21_20_2_33_23_21_20_2_"] = Select;
265  cookieToButtonMapping["33_24_21_20_2_33_24_21_20_2_"] = Right;
266  cookieToButtonMapping["33_25_21_20_2_33_25_21_20_2_"] = Left;
267  cookieToButtonMapping["33_21_20_14_12_2_"] = RightHold;
268  cookieToButtonMapping["33_21_20_13_12_2_"] = LeftHold;
269  cookieToButtonMapping["33_21_20_2_33_21_20_2_"] = MenuHold;
270  cookieToButtonMapping["37_33_21_20_2_37_33_21_20_2_"] = PlayHold;
271 
272  // Aluminium remote which has an extra button:
273  cookieToButtonMapping["33_21_20_8_2_33_21_20_8_2_"] = PlayPause;
274  cookieToButtonMapping["33_21_20_3_2_33_21_20_3_2_"] = Select;
275  cookieToButtonMapping["33_21_20_11_2_33_21_20_11_2_"] = PlayHold;
276 }
277 
278 static io_object_t _findAppleRemoteDevice(const char *devName)
279 {
280  CFMutableDictionaryRef hidMatchDictionary = nullptr;
281  io_iterator_t hidObjectIterator = 0;
282  io_object_t hidDevice = 0;
283  IOReturn ioReturnValue;
284 
285 
286  hidMatchDictionary = IOServiceMatching(devName);
287 
288  // check for matching devices
289  ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault,
290  hidMatchDictionary,
291  &hidObjectIterator);
292 
293  if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0))
294  hidDevice = IOIteratorNext(hidObjectIterator);
295  else
296  LOG(VB_GENERAL, LOG_ERR, LOC +
297  QString("_findAppleRemoteDevice(%1) failed").arg(devName));
298 
299  // IOServiceGetMatchingServices consumes a reference to the dictionary,
300  // so we don't need to release the dictionary ref.
301  hidMatchDictionary = nullptr;
302  return hidDevice;
303 }
304 
306 {
307  IOHIDDeviceInterface122** handle;
308  CFArrayRef elements;
309  IOReturn success;
310 
311  handle = (IOHIDDeviceInterface122**)hidDeviceInterface;
312  success = (*handle)->copyMatchingElements(handle,
313  nullptr,
314  (CFArrayRef*)&elements);
315 
316  if (success == kIOReturnSuccess)
317  {
318  for (CFIndex i = 0; i < CFArrayGetCount(elements); ++i)
319  {
320  CFDictionaryRef element;
321  CFTypeRef object;
322  long number;
323  IOHIDElementCookie cookie;
324 
325  element = (CFDictionaryRef)CFArrayGetValueAtIndex(elements, i);
326  object = CFDictionaryGetValue(element,
327  CFSTR(kIOHIDElementCookieKey));
328 
329  if (object == nullptr || CFGetTypeID(object) != CFNumberGetTypeID())
330  continue;
331 
332  if (!CFNumberGetValue((CFNumberRef)object,
333  kCFNumberLongType, &number))
334  continue;
335 
336  cookie = (IOHIDElementCookie)number;
337 
338  cookies.push_back((int)cookie);
339  }
340  return true;
341  }
342  return false;
343 }
344 
345 bool AppleRemote::_createDeviceInterface(io_object_t hidDevice)
346 {
347  IOReturn ioReturnValue;
348  IOCFPlugInInterface** plugInInterface = nullptr;
349  SInt32 score = 0;
350 
351 
352  ioReturnValue
353  = IOCreatePlugInInterfaceForService(hidDevice,
354  kIOHIDDeviceUserClientTypeID,
355  kIOCFPlugInInterfaceID,
356  &plugInInterface, &score);
357 
358  if ((kIOReturnSuccess == ioReturnValue) &&
359  plugInInterface && *plugInInterface)
360  {
361  HRESULT plugInResult = (*plugInInterface)->QueryInterface
362  (plugInInterface,
363  CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
364  (LPVOID*) (&hidDeviceInterface));
365 
366  if (plugInResult != S_OK)
367  LOG(VB_GENERAL, LOG_ERR, LOC + "_createDeviceInterface() failed");
368 
369  (*plugInInterface)->Release(plugInInterface);
370  }
371  return hidDeviceInterface != nullptr;
372 }
373 
375 {
376  CFRunLoopSourceRef eventSource;
377  IOReturn ioReturnValue;
378  IOHIDOptionsType openMode;
379 
380 
382  openMode = kIOHIDOptionsTypeSeizeDevice;
383  else
384  openMode = kIOHIDOptionsTypeNone;
385 
386  ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, openMode);
387 
388  if (ioReturnValue != KERN_SUCCESS)
389  {
390  LOG(VB_GENERAL, LOG_ERR, LOC + "_openDevice() failed");
391  return false;
392  }
393  queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
394  if (!queue)
395  {
396  LOG(VB_GENERAL, LOG_ERR, LOC +
397  "_openDevice() - error allocating queue");
398  return false;
399  }
400 
401  HRESULT result = (*queue)->create(queue, 0, 12);
402  if (result != S_OK || !queue)
403  LOG(VB_GENERAL, LOG_ERR, LOC + "_openDevice() - error creating queue");
404 
405  for (std::vector<int>::iterator iter = cookies.begin();
406  iter != cookies.end();
407  ++iter)
408  {
409  IOHIDElementCookie cookie = (IOHIDElementCookie)(*iter);
410  (*queue)->addElement(queue, cookie, 0);
411  }
412 
413  ioReturnValue = (*queue)->createAsyncEventSource(queue, &eventSource);
414  if (ioReturnValue != KERN_SUCCESS)
415  {
416  LOG(VB_GENERAL, LOG_ERR, LOC +
417  "_openDevice() - failed to create async event source");
418  return false;
419  }
420 
421  ioReturnValue = (*queue)->setEventCallout(queue, QueueCallbackFunction,
422  this, nullptr);
423  if (ioReturnValue != KERN_SUCCESS)
424  {
425  LOG(VB_GENERAL, LOG_ERR, LOC +
426  "_openDevice() - error registering callback");
427  return false;
428  }
429 
430  CFRunLoopAddSource(CFRunLoopGetCurrent(),
431  eventSource, kCFRunLoopDefaultMode);
432  (*queue)->start(queue);
433  return true;
434 }
435 
436 void AppleRemote::QueueCallbackFunction(void* target, IOReturn result,
437  void* refcon, void* sender)
438 {
439  AppleRemote* remote = static_cast<AppleRemote*>(target);
440 
441  if (remote->mUsingNewAtv)
442  remote->_queueCallbackATV23(result);
443  else
444  remote->_queueCallbackFunction(result, refcon, sender);
445 }
446 
448  void* /*refcon*/, void* /*sender*/)
449 {
450  AbsoluteTime zeroTime = {0,0};
451  SInt32 sumOfValues = 0;
452  std::stringstream cookieString;
453 
454  while (result == kIOReturnSuccess)
455  {
456  IOHIDEventStruct event;
457 
458  result = (*queue)->getNextEvent(queue, &event, zeroTime, 0);
459  if (result != kIOReturnSuccess)
460  break;
461 
462  if (REMOTE_SWITCH_COOKIE == (int)event.elementCookie)
463  {
464  remoteId=event.value;
466  }
467  else
468  {
469  sumOfValues+=event.value;
470  cookieString << std::dec << (int)event.elementCookie << "_";
471  }
472  }
473 
474  _handleEventWithCookieString(cookieString.str(), sumOfValues);
475 }
476 
477 void AppleRemote::_queueCallbackATV23(IOReturn result)
478 {
479  AbsoluteTime zeroTime = {0,0};
480  SInt32 sumOfValues = 0;
481  std::stringstream cookieString;
482  UInt32 key_code = 0;
483 
484 
485  if (mCallbackTimer->isActive())
486  {
487  mCallbackTimer->stop();
488  }
489 
490  while (result == kIOReturnSuccess)
491  {
492  IOHIDEventStruct event;
493 
494  result = (*queue)->getNextEvent(queue, &event, zeroTime, 0);
495  if (result != kIOReturnSuccess)
496  continue;
497 
498  if ( ((int)event.elementCookie == 280) && (event.longValueSize == 20))
499  {
500  ATV_IR_EVENT* atv_ir_event = (ATV_IR_EVENT*)event.longValue;
501  key_code = atv_ir_event->keycode;
502  }
503 
504  if (((int)event.elementCookie) != 5 )
505  {
506  sumOfValues += event.value;
507  cookieString << std::dec << (int)event.elementCookie << "_";
508  }
509  }
510 
511  if (strcmp(cookieString.str().c_str(), ATV_COOKIE_STR) == 0)
512  {
513  cookieString << std::dec << (int) ( (key_code & 0x00007F00) >> 8);
514 
515  sumOfValues = 1;
516  _handleEventATV23(cookieString.str(), sumOfValues);
517  }
518 }
519 
520 void AppleRemote::_handleEventWithCookieString(std::string cookieString,
521  SInt32 sumOfValues)
522 {
523  std::map<std::string,AppleRemote::Event>::iterator ii;
524 
525  ii = cookieToButtonMapping.find(cookieString);
526  if (ii != cookieToButtonMapping.end() && _listener)
527  {
528  AppleRemote::Event buttonid = ii->second;
529  if (_listener)
530  _listener->appleRemoteButton(buttonid, sumOfValues>0);
531  }
532 }
533 
534 // With the ATV from 2.3 onwards, we just get IR events.
535 // We need to simulate the key up and hold events
536 
537 void AppleRemote::_handleEventATV23(std::string cookieString,
538  SInt32 /*sumOfValues*/)
539 {
540  std::map<std::string,AppleRemote::Event>::iterator ii;
541  ii = cookieToButtonMapping.find(cookieString);
542 
543  if (ii != cookieToButtonMapping.end() )
544  {
545  AppleRemote::Event event = ii->second;
546 
547  if (mLastEvent == Undefined) // new event
548  {
549  mEventCount = 1;
550  // Need to figure out if this is a long press or a short press,
551  // so can't just send a key down event right now. It will be
552  // scheduled to run
553  }
554  else if (event != mLastEvent) // a new event, faster than timer
555  {
556  mEventCount = 1;
557  mKeyIsDown = true;
558 
559  if (_listener)
560  {
561  // Only send key up events for events that have separateRelease
562  // defined as true in AppleRemoteListener.cpp
563  if (mLastEvent == Up || mLastEvent == Down ||
565  {
567  /*pressedDown*/false);
568  }
570  }
571  }
572  else // Same event again
573  {
574  AppleRemote::Event newEvent = Undefined;
575 
576  ++mEventCount;
577 
578  // Can the event have a hold state?
579  switch (event)
580  {
581  case Right:
582  newEvent = RightHold;
583  break;
584  case Left:
585  newEvent = LeftHold;
586  break;
587  case Menu:
588  newEvent = MenuHold;
589  break;
590  case Select:
591  newEvent = PlayHold;
592  break;
593  default:
594  newEvent = event;
595  }
596 
597  if (newEvent == event) // Doesn't have a long press
598  {
599  if (mKeyIsDown)
600  {
601  if (_listener)
602  {
603  // Only send key up events for events that have separateRelease
604  // defined as true in AppleRemoteListener.cpp
605  if (mLastEvent == Up || mLastEvent == Down ||
607  {
608  _listener->appleRemoteButton(mLastEvent, /*pressedDown*/false);
609  }
610  }
611  }
612 
613  mKeyIsDown = true;
614  if (_listener)
615  {
617  }
618  }
619  else if (mEventCount == LONG_PRESS_COUNT)
620  {
621  mKeyIsDown = true;
622  if (_listener)
623  {
625  }
626  }
627  }
628 
629  mLastEvent = event;
630  mCallbackTimer->start();
631  }
632 }
633 
634 // Calls key down / up events on the ATV > v2.3
636 {
637  if (_listener)
638  {
640  }
641 
643 
644  if (!mKeyIsDown)
645  {
646  mEventCount = 0;
648  }
649  else
650  {
651  // Schedule a key up event for events that have separateRelease
652  // defined as true in AppleRemoteListener.cpp
653 
654  if (mLastEvent == Up || mLastEvent == Down ||
656  {
657  mCallbackTimer->start();
658  }
659  else
660  {
661  mKeyIsDown = false;
662  mEventCount = 0;
664  }
665 
666  }
667 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
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 _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)
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
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
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:289
bool isRunning(void) const
Definition: mthread.cpp:274
#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:202
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:328