|
MythTV
0.25-pre
|
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 }
1.7.6.1