MythTV  0.25-pre
AppleRemote.cpp
Go to the documentation of this file.
00001 
00002 #include "AppleRemote.h"
00003 
00004 #include <stdio.h>
00005 #include <unistd.h>
00006 #include <stdlib.h>
00007 #include <ctype.h>
00008 #include <sys/errno.h>
00009 #include <sys/sysctl.h>  // for sysctlbyname
00010 #include <sysexits.h>
00011 #include <mach/mach.h>
00012 #include <mach/mach_error.h>
00013 #include <IOKit/IOKitLib.h>
00014 #include <IOKit/IOCFPlugIn.h>
00015 #include <IOKit/hid/IOHIDLib.h>
00016 #include <IOKit/hid/IOHIDKeys.h>
00017 #include <CoreFoundation/CoreFoundation.h>
00018 #include <CoreServices/CoreServices.h>     // for Gestalt
00019 
00020 #include <sstream>
00021 
00022 #include "mythlogging.h"
00023 
00024 AppleRemote*    AppleRemote::_instance = 0;
00025 
00026 
00027 #define REMOTE_SWITCH_COOKIE 19
00028 #define REMOTE_COOKIE_STR    "19_"
00029 #define ATV_COOKIE_STR       "17_9_280_"
00030 #define LONG_PRESS_COUNT     10
00031 #define KEY_RESPONSE_TIME    150  /* msecs before we send a key up event */
00032 
00033 #define LOC QString("AppleRemote::")
00034 
00035 typedef struct _ATV_IR_EVENT
00036 {
00037     UInt32  time_ms32;
00038     UInt32  time_ls32;  // units of microsecond
00039     UInt32  unknown1;
00040     UInt32  keycode;
00041     UInt32  unknown2;
00042 } ATV_IR_EVENT;
00043 
00044 static io_object_t _findAppleRemoteDevice(const char *devName);
00045 
00046 AppleRemote::Listener::~Listener()
00047 {
00048 }
00049 
00050 AppleRemote * AppleRemote::Get()
00051 {
00052     if (_instance == 0)
00053         _instance = new AppleRemote();
00054 
00055     return _instance;
00056 }
00057 
00058 AppleRemote::~AppleRemote()
00059 {
00060     stopListening();
00061     if (mUsingNewAtv)
00062         delete mCallbackTimer;
00063 }
00064 
00065 bool AppleRemote::isListeningToRemote()
00066 {
00067     return (hidDeviceInterface != NULL && !cookies.empty() && queue != NULL);
00068 }
00069 
00070 void AppleRemote::setListener(AppleRemote::Listener* listener)
00071 {
00072     _listener = listener;
00073 }
00074 
00075 void AppleRemote::startListening()
00076 {
00077     if (queue != NULL)   // already listening
00078         return;
00079 
00080     io_object_t hidDevice = _findAppleRemoteDevice("AppleIRController");
00081 
00082     if (!hidDevice)
00083         hidDevice = _findAppleRemoteDevice("AppleTVIRReceiver");
00084 
00085     if (!hidDevice ||
00086         !_createDeviceInterface(hidDevice) ||
00087         !_initCookies() || !_openDevice())
00088     {
00089         LOG(VB_GENERAL, LOG_ERR, LOC + "startListening() failed");
00090         stopListening();
00091         return;
00092     }
00093 
00094     IOObjectRelease(hidDevice);
00095 }
00096 
00097 void AppleRemote::stopListening()
00098 {
00099     if (queue != NULL)
00100     {
00101         (*queue)->stop(queue);
00102         (*queue)->dispose(queue);
00103         (*queue)->Release(queue);
00104         queue = NULL;
00105     }
00106 
00107     if (!cookies.empty())
00108         cookies.clear();
00109 
00110     if (hidDeviceInterface != NULL)
00111     {
00112         (*hidDeviceInterface)->close(hidDeviceInterface);
00113         (*hidDeviceInterface)->Release(hidDeviceInterface);
00114         hidDeviceInterface = NULL;
00115     }
00116 }
00117 
00118 void AppleRemote::run()
00119 {
00120     RunProlog();
00121     CFRunLoopRun();
00122     exec();  // prevent QThread exiting, by entering its run loop
00123     CFRunLoopStop(CFRunLoopGetCurrent());
00124     RunEpilog();
00125 }
00126 
00127 // Figure out if we're running on the Apple TV, and what version
00128 static float GetATVversion()
00129 {
00130     SInt32 macVersion;
00131     size_t len = 512;
00132     char   hw_model[512] = "unknown";
00133 
00134 
00135     Gestalt(gestaltSystemVersion, &macVersion);
00136 
00137     if ( macVersion > 0x1040 )  // Mac OS 10.5 or greater is
00138         return 0.0;             // definitely not an Apple TV
00139 
00140     sysctlbyname("hw.model", &hw_model, &len, NULL, 0);
00141 
00142     float  version = 0.0;
00143     if ( strstr(hw_model,"AppleTV1,1") )
00144     {
00145         // Find the build version of the AppleTV OS
00146         FILE *inpipe = popen("sw_vers -buildVersion", "r");
00147         char linebuf[1000];
00148         if (inpipe && fgets(linebuf, sizeof(linebuf) - 1, inpipe) )
00149         {
00150             if (     strstr(linebuf,"8N5107") ) version = 1.0;
00151             else if (strstr(linebuf,"8N5239") ) version = 1.1;
00152             else if (strstr(linebuf,"8N5400") ) version = 2.0;
00153             else if (strstr(linebuf,"8N5455") ) version = 2.01;
00154             else if (strstr(linebuf,"8N5461") ) version = 2.02;
00155             else if (strstr(linebuf,"8N5519") ) version = 2.1;
00156             else if (strstr(linebuf,"8N5622") ) version = 2.2;
00157             else
00158                 version = 2.3;
00159         }
00160         if (inpipe)
00161             pclose(inpipe);
00162     }
00163     return version;
00164 }
00165 
00166 // protected
00167 AppleRemote::AppleRemote() : MThread("AppleRemote"),
00168                              openInExclusiveMode(true),
00169                              hidDeviceInterface(0),
00170                              queue(0),
00171                              remoteId(0),
00172                              _listener(0),
00173                              mUsingNewAtv(false),
00174                              mLastEvent(AppleRemote::Undefined),
00175                              mEventCount(0),
00176                              mKeyIsDown(false)
00177 {
00178     if ( GetATVversion() > 2.2 )
00179     {
00180         LOG(VB_GENERAL, LOG_INFO,
00181             LOC + "AppleRemote() detected Apple TV > v2.3");
00182         mUsingNewAtv = true;
00183         mCallbackTimer = new QTimer();
00184         QObject::connect(mCallbackTimer,       SIGNAL(timeout()),
00185                          (const QObject*)this, SLOT(TimeoutHandler()));
00186         mCallbackTimer->setSingleShot(true);
00187         mCallbackTimer->setInterval(KEY_RESPONSE_TIME);
00188     }
00189 
00190     _initCookieMap();
00191 }
00192 
00202 void AppleRemote::_initCookieMap()
00203 {
00204     // 10.4 sequences:
00205     cookieToButtonMapping["14_12_11_6_5_"]        = Up;
00206     cookieToButtonMapping["14_13_11_6_5_"]        = Down;
00207     cookieToButtonMapping["14_7_6_5_14_7_6_5_"]   = Menu;
00208     cookieToButtonMapping["14_8_6_5_14_8_6_5_"]   = Select;
00209     cookieToButtonMapping["14_9_6_5_14_9_6_5_"]   = Right;
00210     cookieToButtonMapping["14_10_6_5_14_10_6_5_"] = Left;
00211     cookieToButtonMapping["14_6_5_4_2_"]          = RightHold;
00212     cookieToButtonMapping["14_6_5_3_2_"]          = LeftHold;
00213     cookieToButtonMapping["14_6_5_14_6_5_"]       = MenuHold;
00214     cookieToButtonMapping["18_14_6_5_18_14_6_5_"] = PlayHold;
00215     cookieToButtonMapping["19_"]                  = ControlSwitched;
00216 
00217     // 10.5 sequences:
00218     cookieToButtonMapping["31_29_28_18_"]         = Up;
00219     cookieToButtonMapping["31_30_28_18_"]         = Down;
00220     cookieToButtonMapping["31_20_18_31_20_18_"]   = Menu;
00221     cookieToButtonMapping["31_21_18_31_21_18_"]   = Select;
00222     cookieToButtonMapping["31_22_18_31_22_18_"]   = Right;
00223     cookieToButtonMapping["31_23_18_31_23_18_"]   = Left;
00224     cookieToButtonMapping["31_18_4_2_"]           = RightHold;
00225     cookieToButtonMapping["31_18_3_2_"]           = LeftHold;
00226     cookieToButtonMapping["31_18_31_18_"]         = MenuHold;
00227     cookieToButtonMapping["35_31_18_35_31_18_"]   = PlayHold;
00228     cookieToButtonMapping["39_"]                  = ControlSwitched;
00229 
00230     // ATV 1.0, 2.0-2.02
00231     cookieToButtonMapping["14_12_11_6_"]          = Up;
00232     cookieToButtonMapping["14_13_11_6_"]          = Down;
00233     cookieToButtonMapping["14_7_6_14_7_6_"]       = Menu;
00234     cookieToButtonMapping["14_8_6_14_8_6_"]       = Select;
00235     cookieToButtonMapping["14_9_6_14_9_6_"]       = Right;
00236     cookieToButtonMapping["14_10_6_14_10_6_"]     = Left;
00237     cookieToButtonMapping["14_6_4_2_"]            = RightHold;
00238     cookieToButtonMapping["14_6_3_2_"]            = LeftHold;
00239     cookieToButtonMapping["14_6_14_6_"]           = MenuHold;
00240     cookieToButtonMapping["18_14_6_18_14_6_"]     = PlayHold;
00241 
00242     // ATV 1.0, 2.1-2.2
00243     cookieToButtonMapping["15_13_12_"]            = Up;
00244     cookieToButtonMapping["15_14_12_"]            = Down;
00245     cookieToButtonMapping["15_8_15_8_"]           = Menu;
00246     cookieToButtonMapping["15_9_15_9_"]           = Select;
00247     cookieToButtonMapping["15_10_15_10_"]         = Right;
00248     cookieToButtonMapping["15_11_15_11_"]         = Left;
00249     cookieToButtonMapping["15_5_3_"]              = RightHold;
00250     cookieToButtonMapping["15_4_3_"]              = LeftHold;
00251     cookieToButtonMapping["15_6_15_6_"]           = MenuHold;
00252     cookieToButtonMapping["19_15_19_15_"]         = PlayHold;
00253 
00254     // ATV 2.30
00255     cookieToButtonMapping["17_9_280_80"]          = Up;
00256     cookieToButtonMapping["17_9_280_48"]          = Down;
00257     cookieToButtonMapping["17_9_280_64"]          = Menu;
00258     cookieToButtonMapping["17_9_280_32"]          = Select;
00259     cookieToButtonMapping["17_9_280_96"]          = Right;
00260     cookieToButtonMapping["17_9_280_16"]          = Left;
00261 
00262     // 10.6 sequences:
00263     cookieToButtonMapping["33_31_30_21_20_2_"]            = Up;
00264     cookieToButtonMapping["33_32_30_21_20_2_"]            = Down;
00265     cookieToButtonMapping["33_22_21_20_2_33_22_21_20_2_"] = Menu;
00266     cookieToButtonMapping["33_23_21_20_2_33_23_21_20_2_"] = Select;
00267     cookieToButtonMapping["33_24_21_20_2_33_24_21_20_2_"] = Right;
00268     cookieToButtonMapping["33_25_21_20_2_33_25_21_20_2_"] = Left;
00269     cookieToButtonMapping["33_21_20_14_12_2_"]            = RightHold;
00270     cookieToButtonMapping["33_21_20_13_12_2_"]            = LeftHold;
00271     cookieToButtonMapping["33_21_20_2_33_21_20_2_"]       = MenuHold;
00272     cookieToButtonMapping["37_33_21_20_2_37_33_21_20_2_"] = PlayHold;
00273 
00274     // Aluminium remote which has an extra button:
00275     cookieToButtonMapping["33_21_20_8_2_33_21_20_8_2_"]   = PlayPause;
00276     cookieToButtonMapping["33_21_20_3_2_33_21_20_3_2_"]   = Select;
00277     cookieToButtonMapping["33_21_20_11_2_33_21_20_11_2_"] = PlayHold;
00278 }
00279 
00280 static io_object_t _findAppleRemoteDevice(const char *devName)
00281 {
00282     CFMutableDictionaryRef hidMatchDictionary = 0;
00283     io_iterator_t          hidObjectIterator = 0;
00284     io_object_t            hidDevice = 0;
00285     IOReturn               ioReturnValue;
00286 
00287 
00288     hidMatchDictionary = IOServiceMatching(devName);
00289 
00290     // check for matching devices
00291     ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault,
00292                                                  hidMatchDictionary,
00293                                                  &hidObjectIterator);
00294 
00295     if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0))
00296         hidDevice = IOIteratorNext(hidObjectIterator);
00297     else
00298         LOG(VB_GENERAL, LOG_ERR, LOC +
00299             QString("_findAppleRemoteDevice(%1) failed").arg(devName));
00300 
00301     // IOServiceGetMatchingServices consumes a reference to the dictionary,
00302     // so we don't need to release the dictionary ref.
00303     hidMatchDictionary = 0;
00304     return hidDevice;
00305 }
00306 
00307 bool AppleRemote::_initCookies()
00308 {
00309     IOHIDDeviceInterface122** handle;
00310     CFArrayRef                elements;
00311     IOReturn                  success;
00312 
00313     handle  = (IOHIDDeviceInterface122**)hidDeviceInterface;
00314     success = (*handle)->copyMatchingElements(handle,
00315                                               NULL,
00316                                               (CFArrayRef*)&elements);
00317 
00318     if (success == kIOReturnSuccess)
00319     {
00320         for (CFIndex i = 0; i < CFArrayGetCount(elements); ++i)
00321         {
00322             CFDictionaryRef    element;
00323             CFTypeRef          object;
00324             long               number;
00325             IOHIDElementCookie cookie;
00326 
00327             element = (CFDictionaryRef)CFArrayGetValueAtIndex(elements, i);
00328             object  = CFDictionaryGetValue(element,
00329                                            CFSTR(kIOHIDElementCookieKey));
00330 
00331             if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
00332                 continue;
00333 
00334             if (!CFNumberGetValue((CFNumberRef)object,
00335                                   kCFNumberLongType, &number))
00336                 continue;
00337 
00338             cookie = (IOHIDElementCookie)number;
00339 
00340             cookies.push_back((int)cookie);
00341         }
00342         return true;
00343     }
00344     return false;
00345 }
00346 
00347 bool AppleRemote::_createDeviceInterface(io_object_t hidDevice)
00348 {
00349     IOReturn              ioReturnValue;
00350     IOCFPlugInInterface** plugInInterface = NULL;
00351     SInt32                score = 0;
00352 
00353 
00354     ioReturnValue
00355         = IOCreatePlugInInterfaceForService(hidDevice,
00356                                             kIOHIDDeviceUserClientTypeID,
00357                                             kIOCFPlugInInterfaceID,
00358                                             &plugInInterface, &score);
00359 
00360     if ((kIOReturnSuccess == ioReturnValue) &&
00361         plugInInterface && *plugInInterface)
00362     {
00363         HRESULT plugInResult = (*plugInInterface)->QueryInterface
00364                                 (plugInInterface,
00365                                  CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
00366                                  (LPVOID*) (&hidDeviceInterface));
00367 
00368         if (plugInResult != S_OK)
00369             LOG(VB_GENERAL, LOG_ERR, LOC + "_createDeviceInterface() failed");
00370 
00371         (*plugInInterface)->Release(plugInInterface);
00372     }
00373     return hidDeviceInterface != 0;
00374 }
00375 
00376 bool AppleRemote::_openDevice()
00377 {
00378     CFRunLoopSourceRef eventSource;
00379     IOReturn           ioReturnValue;
00380     IOHIDOptionsType   openMode;
00381 
00382 
00383     if (openInExclusiveMode)
00384         openMode = kIOHIDOptionsTypeSeizeDevice;
00385     else
00386         openMode = kIOHIDOptionsTypeNone;
00387 
00388     ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, openMode);
00389 
00390     if (ioReturnValue != KERN_SUCCESS)
00391     {
00392         LOG(VB_GENERAL, LOG_ERR, LOC + "_openDevice() failed");
00393         return false;
00394     }
00395     queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
00396     if (!queue)
00397     {
00398         LOG(VB_GENERAL, LOG_ERR, LOC + 
00399             "_openDevice() - error allocating queue");
00400         return false;
00401     }
00402 
00403     HRESULT result = (*queue)->create(queue, 0, 12);
00404     if (result != S_OK || !queue)
00405         LOG(VB_GENERAL, LOG_ERR, LOC + "_openDevice() - error creating queue");
00406 
00407     for (std::vector<int>::iterator iter = cookies.begin();
00408          iter != cookies.end();
00409          ++iter)
00410     {
00411         IOHIDElementCookie cookie = (IOHIDElementCookie)(*iter);
00412         (*queue)->addElement(queue, cookie, 0);
00413     }
00414 
00415     ioReturnValue = (*queue)->createAsyncEventSource(queue, &eventSource);
00416     if (ioReturnValue != KERN_SUCCESS)
00417     {
00418         LOG(VB_GENERAL, LOG_ERR, LOC +
00419                 "_openDevice() - failed to create async event source");
00420         return false;
00421     }
00422 
00423     ioReturnValue = (*queue)->setEventCallout(queue, QueueCallbackFunction,
00424                                               this, NULL);
00425     if (ioReturnValue != KERN_SUCCESS)
00426     {
00427         LOG(VB_GENERAL, LOG_ERR, LOC + 
00428             "_openDevice() - error registering callback");
00429         return false;
00430     }
00431 
00432     CFRunLoopAddSource(CFRunLoopGetCurrent(),
00433                        eventSource, kCFRunLoopDefaultMode);
00434     (*queue)->start(queue);
00435     return true;
00436 }
00437 
00438 void AppleRemote::QueueCallbackFunction(void* target, IOReturn result,
00439                                         void* refcon, void* sender)
00440 {
00441     AppleRemote* remote = static_cast<AppleRemote*>(target);
00442 
00443     if (remote->mUsingNewAtv)
00444         remote->_queueCallbackATV23(result);
00445     else
00446         remote->_queueCallbackFunction(result, refcon, sender);
00447 }
00448 
00449 void AppleRemote::_queueCallbackFunction(IOReturn result,
00450                                          void* /*refcon*/, void* /*sender*/)
00451 {
00452     AbsoluteTime      zeroTime = {0,0};
00453     SInt32            sumOfValues = 0;
00454     std::stringstream cookieString;
00455 
00456     while (result == kIOReturnSuccess)
00457     {
00458         IOHIDEventStruct event;
00459 
00460         result = (*queue)->getNextEvent(queue, &event, zeroTime, 0);
00461         if (result != kIOReturnSuccess)
00462             break;
00463 
00464         if (REMOTE_SWITCH_COOKIE == (int)event.elementCookie)
00465         {
00466             remoteId=event.value;
00467             _handleEventWithCookieString(REMOTE_COOKIE_STR, 0);
00468         }
00469         else
00470         {
00471             sumOfValues+=event.value;
00472             cookieString << std::dec << (int)event.elementCookie << "_";
00473         }
00474     }
00475 
00476     _handleEventWithCookieString(cookieString.str(), sumOfValues);
00477 }
00478 
00479 void AppleRemote::_queueCallbackATV23(IOReturn result)
00480 {
00481     AbsoluteTime      zeroTime = {0,0};
00482     SInt32            sumOfValues = 0;
00483     std::stringstream cookieString;
00484     UInt32            key_code = 0;
00485 
00486 
00487     if (mCallbackTimer->isActive())
00488     {
00489         mCallbackTimer->stop();
00490     }
00491 
00492     while (result == kIOReturnSuccess)
00493     {
00494         IOHIDEventStruct  event;
00495 
00496         result = (*queue)->getNextEvent(queue, &event, zeroTime, 0);
00497         if (result != kIOReturnSuccess)
00498             continue;
00499 
00500         if ( ((int)event.elementCookie == 280) && (event.longValueSize == 20))
00501         {
00502             ATV_IR_EVENT* atv_ir_event = (ATV_IR_EVENT*)event.longValue;
00503             key_code = atv_ir_event->keycode;
00504         }
00505 
00506         if (((int)event.elementCookie) != 5 )
00507         {
00508             sumOfValues += event.value;
00509             cookieString << std::dec << (int)event.elementCookie << "_";
00510         }
00511     }
00512 
00513     if (strcmp(cookieString.str().c_str(), ATV_COOKIE_STR) == 0)
00514     {
00515         cookieString << std::dec << (int) ( (key_code & 0x00007F00) >> 8);
00516 
00517         sumOfValues = 1;
00518         _handleEventATV23(cookieString.str(), sumOfValues);
00519     }
00520 }
00521 
00522 void AppleRemote::_handleEventWithCookieString(std::string cookieString,
00523                                                SInt32 sumOfValues)
00524 {
00525     std::map<std::string,AppleRemote::Event>::iterator ii;
00526 
00527     ii = cookieToButtonMapping.find(cookieString);
00528     if (ii != cookieToButtonMapping.end() && _listener)
00529     {
00530         AppleRemote::Event buttonid = ii->second;
00531         if (_listener)
00532             _listener->appleRemoteButton(buttonid, sumOfValues>0);
00533     }
00534 }
00535 
00536 // With the ATV from 2.3 onwards, we just get IR events.
00537 // We need to simulate the key up and hold events
00538 
00539 void AppleRemote::_handleEventATV23(std::string cookieString,
00540                                     SInt32      sumOfValues)
00541 {
00542     std::map<std::string,AppleRemote::Event>::iterator ii;
00543     ii = cookieToButtonMapping.find(cookieString);
00544 
00545     if (ii != cookieToButtonMapping.end() )
00546     {
00547         AppleRemote::Event event = ii->second;
00548 
00549         if (mLastEvent == Undefined)    // new event
00550         {
00551             mEventCount = 1;
00552             // Need to figure out if this is a long press or a short press,
00553             // so can't just send a key down event right now. It will be
00554             // scheduled to run
00555         }
00556         else if (event != mLastEvent)    // a new event, faster than timer
00557         {
00558             mEventCount = 1;
00559             mKeyIsDown = true;
00560 
00561             if (_listener)
00562             {
00563                 // Only send key up events for events that have separateRelease
00564                 // defined as true in AppleRemoteListener.cpp
00565                 if (mLastEvent == Up       || mLastEvent == Down ||
00566                     mLastEvent == LeftHold || mLastEvent == RightHold)
00567                 {
00568                     _listener->appleRemoteButton(mLastEvent,
00569                                                  /*pressedDown*/false);
00570                 }
00571                 _listener->appleRemoteButton(event, mKeyIsDown);
00572             }
00573         }
00574         else // Same event again
00575         {
00576             AppleRemote::Event newEvent = Undefined;
00577 
00578             ++mEventCount;
00579 
00580             // Can the event have a hold state?
00581             switch (event)
00582             {
00583                 case Right:
00584                     newEvent = RightHold;
00585                     break;
00586                 case Left:
00587                     newEvent = LeftHold;
00588                     break;
00589                 case Menu:
00590                     newEvent = MenuHold;
00591                     break;
00592                 case Select:
00593                     newEvent = PlayHold;
00594                     break;
00595                 default:
00596                     newEvent = event;
00597             }
00598 
00599             if (newEvent == event) // Doesn't have a long press
00600             {
00601                 if (mKeyIsDown)
00602                 {
00603                     if (_listener)
00604                     {
00605                         // Only send key up events for events that have separateRelease
00606                         // defined as true in AppleRemoteListener.cpp
00607                         if (mLastEvent == Up || mLastEvent == Down ||
00608                             mLastEvent == LeftHold || mLastEvent == RightHold)
00609                         {
00610                             _listener->appleRemoteButton(mLastEvent, /*pressedDown*/false);
00611                         }
00612                     }
00613                 }
00614 
00615                 mKeyIsDown = true;
00616                 if (_listener)
00617                 {
00618                     _listener->appleRemoteButton(newEvent, mKeyIsDown);
00619                 }
00620             }
00621             else if (mEventCount == LONG_PRESS_COUNT)
00622             {
00623                 mKeyIsDown = true;
00624                 if (_listener)
00625                 {
00626                     _listener->appleRemoteButton(newEvent, mKeyIsDown);
00627                 }
00628             }
00629         }
00630 
00631         mLastEvent = event;
00632         mCallbackTimer->start();
00633     }
00634 }
00635 
00636 // Calls key down / up events on the ATV > v2.3
00637 void AppleRemote::TimeoutHandler()
00638 {
00639     if (_listener)
00640     {
00641         _listener->appleRemoteButton(mLastEvent, !mKeyIsDown);
00642     }
00643 
00644     mKeyIsDown = !mKeyIsDown;
00645 
00646     if (!mKeyIsDown)
00647     {
00648         mEventCount = 0;
00649         mLastEvent = Undefined;
00650     }
00651     else
00652     {
00653         // Schedule a key up event for events that have separateRelease
00654         // defined as true in AppleRemoteListener.cpp
00655 
00656         if (mLastEvent == Up       || mLastEvent == Down ||
00657             mLastEvent == LeftHold || mLastEvent == RightHold)
00658         {
00659             mCallbackTimer->start();
00660         }
00661         else
00662         {
00663             mKeyIsDown = false;
00664             mEventCount = 0;
00665             mLastEvent = Undefined;
00666         }
00667 
00668     }
00669 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends