Back to index

texmacs  1.0.7.15
HIDRemote.m
Go to the documentation of this file.
00001 //
00002 //  HIDRemote.m
00003 //  HIDRemote V1.1.1 (14th December 2009)
00004 //
00005 //  Created by Felix Schwarz on 06.04.07.
00006 //  Copyright 2007-2009 IOSPIRIT GmbH. All rights reserved.
00007 //
00008 //  The latest version of this class is available at
00009 //     http://www.iospirit.com/developers/hidremote/
00010 //
00011 //  ** LICENSE *************************************************************************
00012 //
00013 //  Copyright (c) 2007-2009 IOSPIRIT GmbH (http://www.iospirit.com/)
00014 //  All rights reserved.
00015 //  
00016 //  Redistribution and use in source and binary forms, with or without modification,
00017 //  are permitted provided that the following conditions are met:
00018 //  
00019 //  * Redistributions of source code must retain the above copyright notice, this list
00020 //    of conditions and the following disclaimer.
00021 //  
00022 //  * Redistributions in binary form must reproduce the above copyright notice, this
00023 //    list of conditions and the following disclaimer in the documentation and/or other
00024 //    materials provided with the distribution.
00025 //  
00026 //  * Neither the name of IOSPIRIT GmbH nor the names of its contributors may be used to
00027 //    endorse or promote products derived from this software without specific prior
00028 //    written permission.
00029 //  
00030 //  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
00031 //  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00032 //  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00033 //  SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00034 //  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00035 //  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
00036 //  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00037 //  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00038 //  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
00039 //  DAMAGE.
00040 //
00041 //  ************************************************************************************
00042 
00043 //  ************************************************************************************
00044 //  ********************************** DOCUMENTATION ***********************************
00045 //  ************************************************************************************
00046 //
00047 //  - a reference is available at http://www.iospirit.com/developers/hidremote/reference/
00048 //  - for a guide, please see http://www.iospirit.com/developers/hidremote/guide/
00049 //
00050 //  ************************************************************************************
00051 
00052 #import "HIDRemote.h"
00053 
00054 // Callback Prototypes
00055 static void HIDEventCallback(      void * target, 
00056                             IOReturn result,
00057                             void * refcon,
00058                             void * sender);
00059 
00060 static void ServiceMatchingCallback(      void *refCon,
00061                                    io_iterator_t iterator);
00062 
00063 static void ServiceNotificationCallback(void *          refCon,
00064                                    io_service_t  service,
00065                                    natural_t     messageType,
00066                                    void *        messageArgument);
00067 
00068 static void SecureInputNotificationCallback(     void *        refCon,
00069                                           io_service_t  service,
00070                                           natural_t     messageType,
00071                                           void *        messageArgument);
00072 
00073 // Shared HIDRemote instance
00074 static HIDRemote *sHIDRemote = nil;
00075 
00076 @implementation HIDRemote
00077 
00078 #pragma mark -- Init, dealloc & shared instance --
00079 
00080 + (HIDRemote *)sharedHIDRemote
00081 {
00082        if (!sHIDRemote)
00083        {
00084               sHIDRemote = [[HIDRemote alloc] init];
00085        }
00086        
00087        return (sHIDRemote);
00088 }
00089 
00090 - (id)init
00091 {
00092        if ((self = [super init]) != nil)
00093        {
00094               // Detect application becoming active/inactive
00095               [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:)      name:NSApplicationDidBecomeActiveNotification   object:[NSApplication sharedApplication]];
00096               [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:)      name:NSApplicationWillResignActiveNotification object:[NSApplication sharedApplication]];
00097               [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:)      name:NSApplicationWillTerminateNotification     object:[NSApplication sharedApplication]];
00098 
00099               // Handle distributed notifications
00100               [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemotePing  object:nil];
00101               [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteRetry object:[NSString stringWithFormat:@"%d", getpid()]];
00102 
00103               // Enabled by default: simulate hold events for plus/minus
00104               _simulateHoldEvents = YES;
00105               
00106               // Enabled by default: work around for a locking issue introduced with Security Update 2008-004 / 10.4.9 and beyond (credit for finding this workaround goes to Martin Kahr)
00107               _secureEventInputWorkAround = YES;
00108               _secureInputNotification = 0;
00109               
00110               // Initialize instance variables
00111               _lastSeenRemoteID = -1;
00112               _lastSeenModel = kHIDRemoteModelUndetermined;
00113               _unusedButtonCodes = [[NSMutableArray alloc] init];
00114               _exclusiveLockLending = NO;
00115        }
00116 
00117        return (self);
00118 }
00119 
00120 - (void)dealloc
00121 {
00122        [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:[NSApplication sharedApplication]];
00123        [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillResignActiveNotification object:[NSApplication sharedApplication]];
00124        [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationDidBecomeActiveNotification object:[NSApplication sharedApplication]];
00125 
00126        [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemotePing object:nil];
00127        [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteRetry object:[NSString stringWithFormat:@"%d", getpid()]];
00128 
00129        [self stopRemoteControl];
00130        
00131        [self setExclusiveLockLendingEnabled:NO];
00132 
00133        [self setDelegate:nil];
00134        
00135        [_unusedButtonCodes release];
00136        _unusedButtonCodes = nil;
00137 
00138        [super dealloc];
00139 }
00140 
00141 #pragma mark -- PUBLIC: System Information --
00142 + (BOOL)isCandelairInstalled
00143 {
00144        mach_port_t   masterPort = 0;
00145        kern_return_t kernResult;
00146        io_service_t  matchingService = 0;
00147        BOOL isInstalled = NO;
00148 
00149        kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
00150        if (kernResult || !masterPort) { return(NO); }
00151 
00152        if ((matchingService = IOServiceGetMatchingService(masterPort, IOServiceMatching("IOSPIRITIRController"))) != 0)
00153        {
00154               isInstalled = YES;
00155               IOObjectRelease((io_object_t) matchingService);
00156        }
00157 
00158        mach_port_deallocate(mach_task_self(), masterPort);
00159 
00160        return (isInstalled);
00161 }
00162 
00163 + (BOOL)isCandelairInstallationRequiredForRemoteMode:(HIDRemoteMode)remoteMode
00164 {
00165        SInt32 systemVersion = 0;
00166        
00167        // Determine OS version
00168        if (Gestalt(gestaltSystemVersion, &systemVersion) == noErr)
00169        {
00170               switch (systemVersion)
00171               {
00172                      case 0x1060: // OS 10.6
00173                      case 0x1061: // OS 10.6.1
00174                             // OS X 10.6(.0) and OS X 10.6.1 require the Candelair driver for to be installed,
00175                             // so that third party apps can acquire an exclusive lock on the receiver HID Device
00176                             // via IOKit.
00177 
00178                             switch (remoteMode)
00179                             {
00180                             case kHIDRemoteModeExclusive:
00181                             case kHIDRemoteModeExclusiveAuto:
00182                               if (![self isCandelairInstalled])
00183                                 {
00184                                   return (YES);
00185                                 }
00186                               break;
00187                             default:
00188                               break;
00189                             }
00190                      break;
00191               }
00192        }
00193        
00194        return (NO);
00195 }
00196 
00197 - (HIDRemoteAluminumRemoteSupportLevel)aluminiumRemoteSystemSupportLevel
00198 {
00199        HIDRemoteAluminumRemoteSupportLevel supportLevel = kHIDRemoteAluminumRemoteSupportLevelNone;
00200        NSEnumerator *attribDictsEnum;
00201        NSDictionary *hidAttribsDict;
00202        
00203        attribDictsEnum = [_serviceAttribMap objectEnumerator];
00204        
00205        while ((hidAttribsDict = [attribDictsEnum nextObject]) != nil)
00206        {
00207               NSNumber *deviceSupportLevel;
00208               
00209               if ((deviceSupportLevel = [hidAttribsDict objectForKey:kHIDRemoteAluminumRemoteSupportLevel]) != nil)
00210               {
00211                      if ([deviceSupportLevel intValue] > supportLevel)
00212                      {
00213                             supportLevel = [deviceSupportLevel intValue];
00214                      }
00215               }
00216        }
00217        
00218        return (supportLevel);
00219 }
00220 
00221 #pragma mark -- PUBLIC: Interface / API --
00222 - (BOOL)startRemoteControl:(HIDRemoteMode)hidRemoteMode
00223 {
00224        if ((_mode == kHIDRemoteModeNone) && (hidRemoteMode != kHIDRemoteModeNone))
00225        {
00226               kern_return_t        kernReturn;
00227               CFMutableDictionaryRef      matchDict=NULL;
00228               io_service_t rootService;
00229               
00230               do
00231               {
00232                      // Get IOKit master port
00233                      kernReturn = IOMasterPort(bootstrap_port, &_masterPort);
00234                      if ((kernReturn!=kIOReturnSuccess) || (_masterPort==0)) { break; }
00235                                    
00236                      // Setup notification port
00237                      _notifyPort = IONotificationPortCreate(_masterPort);
00238                      
00239                      if ((_notifyRLSource = IONotificationPortGetRunLoopSource(_notifyPort)) != NULL)
00240                      {
00241                             CFRunLoopAddSource(  CFRunLoopGetCurrent(),
00242                                                  _notifyRLSource,
00243                                                  kCFRunLoopCommonModes);
00244                      }
00245                      else
00246                      {
00247                             break;
00248                      }
00249                      
00250                      // Setup SecureInput notification
00251                      if ((hidRemoteMode == kHIDRemoteModeExclusive) || (hidRemoteMode == kHIDRemoteModeExclusiveAuto))
00252                      {
00253                             if ((rootService = IORegistryEntryFromPath(_masterPort, kIOServicePlane ":/")) != 0)
00254                             {
00255                                    kernReturn = IOServiceAddInterestNotification(   _notifyPort,
00256                                                                              rootService,
00257                                                                              kIOBusyInterest,
00258                                                                              SecureInputNotificationCallback,
00259                                                                              (void *)self,
00260                                                                              &_secureInputNotification);
00261                                    if (kernReturn != kIOReturnSuccess) { break; }
00262                                    
00263                                    [self _updateSessionInformation];
00264                             }
00265                             else
00266                             {
00267                                    break;
00268                             }
00269                      }
00270 
00271                      // Setup notification matching dict
00272                      matchDict = IOServiceMatching(kIOHIDDeviceKey);
00273                      CFRetain(matchDict);
00274 
00275                      // Actually add notification
00276                      kernReturn = IOServiceAddMatchingNotification(   _notifyPort,
00277                                                                kIOFirstMatchNotification,
00278                                                                matchDict,                  // one reference count consumed by this call
00279                                                                ServiceMatchingCallback,
00280                                                                (void *) self,
00281                                                                &_matchingServicesIterator);
00282                      if (kernReturn != kIOReturnSuccess) { break; }
00283 
00284                      // Setup serviceAttribMap 
00285                      _serviceAttribMap = [[NSMutableDictionary alloc] init];
00286                      if (!_serviceAttribMap) { break; }
00287                      
00288                      // Phew .. everything went well!
00289                      _mode = hidRemoteMode;
00290                      CFRelease(matchDict);
00291                      
00292                      [self _serviceMatching:_matchingServicesIterator];
00293                      
00294                      [self _postStatusWithAction:kHIDRemoteDNStatusActionStart];
00295                      
00296                      return (YES);
00297 
00298               }while(0);
00299               
00300               // An error occured. Do necessary clean up.
00301               if (matchDict)
00302               {
00303                      CFRelease(matchDict);
00304                      matchDict = NULL;
00305               }
00306               
00307               [self stopRemoteControl];
00308        }
00309               
00310        return (NO);
00311 }
00312 
00313 - (void)stopRemoteControl
00314 {
00315        _autoRecover = NO;
00316        
00317        if (_autoRecoveryTimer)
00318        {
00319               [_autoRecoveryTimer invalidate];
00320               [_autoRecoveryTimer release];
00321               _autoRecoveryTimer = nil;
00322        }
00323 
00324        if (_serviceAttribMap)
00325        {
00326               NSDictionary *cloneDict = [[NSDictionary alloc] initWithDictionary:_serviceAttribMap];
00327        
00328               if (cloneDict)
00329               {
00330                      NSEnumerator *mapKeyEnum = [cloneDict keyEnumerator];
00331                      NSNumber *serviceValue;
00332                      
00333                      while ((serviceValue = [mapKeyEnum nextObject]) != nil)
00334                      {
00335                             [self _destructService:(io_object_t)[serviceValue unsignedIntValue]];
00336                      };
00337                      
00338                      [cloneDict release];
00339                      cloneDict = nil;
00340               }
00341        
00342               [_serviceAttribMap release];
00343               _serviceAttribMap = nil;
00344        }
00345 
00346        if (_matchingServicesIterator)
00347        {
00348               IOObjectRelease((io_object_t) _matchingServicesIterator);
00349               _matchingServicesIterator = 0;
00350        }
00351        
00352        if (_secureInputNotification)
00353        {
00354               IOObjectRelease((io_object_t) _secureInputNotification);
00355               _secureInputNotification = 0;
00356        }
00357 
00358        if (_notifyRLSource)
00359        {
00360               CFRunLoopSourceInvalidate(_notifyRLSource);
00361 
00362               _notifyRLSource = NULL;
00363        }
00364 
00365        if (_notifyPort)
00366        {
00367               IONotificationPortDestroy(_notifyPort);
00368               _notifyPort = NULL;
00369        }
00370 
00371        if (_masterPort)
00372        {
00373               mach_port_deallocate(mach_task_self(), _masterPort);
00374        }
00375 
00376        [self _postStatusWithAction:kHIDRemoteDNStatusActionStop];
00377 
00378        [_returnToPID release];
00379        _returnToPID = nil;
00380 
00381        _mode = kHIDRemoteModeNone;
00382 }
00383 
00384 - (BOOL)isStarted
00385 {
00386        return (_mode != kHIDRemoteModeNone);
00387 }
00388 
00389 - (unsigned)activeRemoteControlCount
00390 {
00391        return ([_serviceAttribMap count]);
00392 }
00393 
00394 - (SInt32)lastSeenRemoteControlID
00395 {
00396        return (_lastSeenRemoteID);
00397 }
00398 
00399 - (HIDRemoteModel)lastSeenModel
00400 {
00401        return (_lastSeenModel);
00402 }
00403 
00404 - (void)setLastSeenModel:(HIDRemoteModel)aModel
00405 {
00406        _lastSeenModel = aModel;
00407 }
00408 
00409 - (void)setSimulateHoldEvents:(BOOL)newSimulateHoldEvents
00410 {
00411        _simulateHoldEvents = newSimulateHoldEvents;
00412 }
00413 
00414 - (BOOL)simulateHoldEvents
00415 {
00416        return (_simulateHoldEvents);
00417 }
00418 
00419 - (NSArray *)unusedButtonCodes
00420 {
00421        return (_unusedButtonCodes);
00422 }
00423 
00424 - (void)setUnusedButtonCodes:(NSArray *)newArrayWithUnusedButtonCodesAsNSNumbers
00425 {
00426        [newArrayWithUnusedButtonCodesAsNSNumbers retain];
00427        [_unusedButtonCodes release];
00428        
00429        _unusedButtonCodes = newArrayWithUnusedButtonCodesAsNSNumbers;
00430 
00431        [self _postStatusWithAction:kHIDRemoteDNStatusActionUpdate];
00432 }
00433 
00434 - (void)setDelegate:(NSObject <HIDRemoteDelegate> *)newDelegate
00435 {
00436        _delegate = newDelegate;
00437 }
00438 
00439 - (NSObject <HIDRemoteDelegate> *)delegate
00440 {
00441        return (_delegate);
00442 }
00443 
00444 #pragma mark -- PUBLIC: Expert APIs --
00445 - (void)setEnableSecureEventInputWorkaround:(BOOL)newEnableSecureEventInputWorkaround
00446 {
00447        _secureEventInputWorkAround = newEnableSecureEventInputWorkaround;
00448 }
00449 
00450 - (BOOL)enableSecureEventInputWorkaround
00451 {
00452        return (_secureEventInputWorkAround);
00453 }
00454 
00455 - (void)setExclusiveLockLendingEnabled:(BOOL)newExclusiveLockLendingEnabled
00456 {
00457        if (newExclusiveLockLendingEnabled != _exclusiveLockLending)
00458        {
00459               _exclusiveLockLending = newExclusiveLockLendingEnabled;
00460               
00461               if (_exclusiveLockLending)
00462               {
00463                      [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteStatus object:nil];
00464               }
00465               else
00466               {
00467                      [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteStatus object:nil];
00468                      
00469                      [_waitForReturnByPID release];
00470                      _waitForReturnByPID = nil;
00471               }
00472        }
00473 }
00474 
00475 - (BOOL)exclusiveLockLendingEnabled
00476 {
00477        return (_exclusiveLockLending);
00478 }
00479 
00480 #pragma mark -- PRIVATE: Application becomes active / inactive handling for kHIDRemoteModeExclusiveAuto --
00481 - (void)_appStatusChanged:(NSNotification *)notification
00482 {
00483        if (notification)
00484        {
00485               if (_autoRecoveryTimer)
00486               {
00487                      [_autoRecoveryTimer invalidate];
00488                      [_autoRecoveryTimer release];
00489                      _autoRecoveryTimer = nil;
00490               }
00491 
00492               if ([[notification name] isEqual:NSApplicationDidBecomeActiveNotification])
00493               {
00494                      if (_autoRecover)
00495                      {
00496                             // Delay autorecover by 0.1 to avoid race conditions
00497                             if ((_autoRecoveryTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:0.1] interval:0.1 target:self selector:@selector(_delayedAutoRecovery:) userInfo:nil repeats:NO]) != nil)
00498                             {
00499                                    // Using CFRunLoopAddTimer instead of [[NSRunLoop currentRunLoop] addTimer:.. for consistency with run loop modes.
00500                                    // The kCFRunLoopCommonModes counterpart NSRunLoopCommonModes is only available in 10.5 and later, whereas this code
00501                                    // is designed to be also compatible with 10.4. CFRunLoopTimerRef is "toll-free-bridged" with NSTimer since 10.0.
00502                                    CFRunLoopAddTimer(CFRunLoopGetCurrent(), (CFRunLoopTimerRef)_autoRecoveryTimer, kCFRunLoopCommonModes);
00503                             }
00504                      }
00505               }
00506 
00507               if ([[notification name] isEqual:NSApplicationWillResignActiveNotification])
00508               {
00509                      if (_mode == kHIDRemoteModeExclusiveAuto)
00510                      {
00511                             [self stopRemoteControl];
00512                             _autoRecover = YES;
00513                      }
00514               }
00515               
00516               if ([[notification name] isEqual:NSApplicationWillTerminateNotification])
00517               {
00518                      if ([self isStarted])
00519                      {
00520                             [self stopRemoteControl];
00521                      }
00522               }
00523        }
00524 }
00525 
00526 - (void)_delayedAutoRecovery:(NSTimer *)aTimer
00527 {
00528        [_autoRecoveryTimer invalidate];
00529        [_autoRecoveryTimer release];
00530        _autoRecoveryTimer = nil;
00531 
00532        if (_autoRecover)
00533        {
00534               [self startRemoteControl:kHIDRemoteModeExclusiveAuto];
00535               _autoRecover = NO;
00536        }
00537 }
00538 
00539 
00540 #pragma mark -- PRIVATE: Distributed notifiations handling --
00541 - (void)_postStatusWithAction:(NSString *)action
00542 {
00543        [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kHIDRemoteDNHIDRemoteStatus
00544                                                                object:[NSString stringWithFormat:@"%d",getpid()]
00545                                                              userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
00546                                                                              [NSNumber numberWithInt:1],                                           kHIDRemoteDNStatusHIDRemoteVersionKey,
00547                                                                              [NSNumber numberWithUnsignedInt:(unsigned int)getpid()],                     kHIDRemoteDNStatusPIDKey,
00548                                                                              [NSNumber numberWithInt:(int)_mode],                                         kHIDRemoteDNStatusModeKey,
00549                                                                              [NSNumber numberWithUnsignedInt:(unsigned int)[self activeRemoteControlCount]], kHIDRemoteDNStatusRemoteControlCountKey,
00550                                                                              ((_unusedButtonCodes!=nil) ? _unusedButtonCodes : [NSArray array]),          kHIDRemoteDNStatusUnusedButtonCodesKey,
00551                                                                              action,                                                                      kHIDRemoteDNStatusActionKey,
00552                                                                              [[NSBundle mainBundle] bundleIdentifier],                             (NSString *)kCFBundleIdentifierKey,
00553                                                                              _returnToPID,                                                         kHIDRemoteDNStatusReturnToPIDKey,
00554                                                                      nil]
00555                                                     deliverImmediately:YES
00556        ];
00557 }
00558 
00559 - (void)_handleNotifications:(NSNotification *)notification
00560 {
00561        NSString *notificationName;
00562 
00563        if ((notification!=nil) && ((notificationName = [notification name]) != nil))
00564        {
00565               if ([notificationName isEqual:kHIDRemoteDNHIDRemotePing])
00566               {
00567                      [self _postStatusWithAction:kHIDRemoteDNStatusActionUpdate];
00568               }
00569 
00570               if ([notificationName isEqual:kHIDRemoteDNHIDRemoteRetry])
00571               {
00572                      if ([self isStarted])
00573                      {
00574                             BOOL retry = YES;
00575                      
00576                             if (([self delegate] != nil) &&
00577                                 ([[self delegate] respondsToSelector:@selector(hidRemote:shouldRetryExclusiveLockWithInfo:)]))
00578                             {
00579                                    retry = [[self delegate] hidRemote:self shouldRetryExclusiveLockWithInfo:[notification userInfo]];
00580                             }
00581                             
00582                             if (retry)
00583                             {
00584                                    HIDRemoteMode restartInMode = _mode;
00585                                    
00586                                    if (restartInMode != kHIDRemoteModeNone)
00587                                    {
00588                                           [self stopRemoteControl];
00589                                           
00590                                           [_returnToPID release];
00591                                           [self startRemoteControl:restartInMode];
00592                                           
00593                                           if (restartInMode != kHIDRemoteModeShared)
00594                                           {
00595                                                  _returnToPID = [[[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey] retain];
00596                                           }
00597                                    }
00598                             }
00599                             else
00600                             {
00601                                    NSNumber *cacheReturnPID = _returnToPID;
00602 
00603                                    _returnToPID = [[[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey] retain];
00604                                    [self _postStatusWithAction:kHIDRemoteDNStatusActionNoNeed];
00605                                    [_returnToPID release];
00606                                    
00607                                    _returnToPID = cacheReturnPID;
00608                             }
00609                      }
00610               }
00611               
00612               if (_exclusiveLockLending)
00613               {
00614                      if ([notificationName isEqual:kHIDRemoteDNHIDRemoteStatus])
00615                      {
00616                             NSString *action;
00617                             
00618                             if ((action = [[notification userInfo] objectForKey:kHIDRemoteDNStatusActionKey]) != nil)
00619                             {
00620                                    if ((_mode == kHIDRemoteModeNone) && _waitForReturnByPID)
00621                                    {
00622                                           NSNumber *pidNumber, *returnToPIDNumber;
00623 
00624                                           if ((pidNumber              = [[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey]) != nil)
00625                                           {
00626                                                  returnToPIDNumber = [[notification userInfo] objectForKey:kHIDRemoteDNStatusReturnToPIDKey];
00627                                           
00628                                                  if ([action isEqual:kHIDRemoteDNStatusActionStart])
00629                                                  {
00630                                                         if ([pidNumber isEqual:_waitForReturnByPID])
00631                                                         {
00632                                                                NSNumber *startMode;
00633                                                                
00634                                                                 if ((startMode = [[notification userInfo] objectForKey:kHIDRemoteDNStatusModeKey]) != nil)
00635                                                                 {
00636                                                                       if ([startMode intValue] == kHIDRemoteModeShared)
00637                                                                       {
00638                                                                              returnToPIDNumber = [NSNumber numberWithInt:getpid()];
00639                                                                              action = kHIDRemoteDNStatusActionNoNeed;
00640                                                                       }
00641                                                                 }
00642                                                         }
00643                                                  }
00644 
00645                                                  if (returnToPIDNumber != nil)
00646                                                  {
00647                                                         if ([action isEqual:kHIDRemoteDNStatusActionStop] || [action isEqual:kHIDRemoteDNStatusActionNoNeed])
00648                                                         {
00649                                                                if ([pidNumber isEqual:_waitForReturnByPID] && ([returnToPIDNumber intValue] == getpid()))
00650                                                                {
00651                                                                       [_waitForReturnByPID release];
00652                                                                       _waitForReturnByPID = nil;
00653                                                                
00654                                                                       if (([self delegate] != nil) &&
00655                                                                           ([[self delegate] respondsToSelector:@selector(hidRemote:exclusiveLockReleasedByApplicationWithInfo:)]))
00656                                                                       {
00657                                                                              [[self delegate] hidRemote:self exclusiveLockReleasedByApplicationWithInfo:[notification userInfo]];
00658                                                                       }
00659                                                                       else
00660                                                                       {
00661                                                                              [self startRemoteControl:kHIDRemoteModeExclusive];
00662                                                                       }
00663                                                                }
00664                                                         }
00665                                                  }
00666                                           }
00667                                    }
00668 
00669                                    if (_mode==kHIDRemoteModeExclusive)
00670                                    {
00671                                           if ([action isEqual:kHIDRemoteDNStatusActionStart])
00672                                           {
00673                                                  NSNumber *originPID = [[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey];
00674                                                  BOOL lendLock = YES;
00675                                           
00676                                                  if ([originPID intValue] != getpid())
00677                                                  {
00678                                                         if (([self delegate] != nil) &&
00679                                                             ([[self delegate] respondsToSelector:@selector(hidRemote:lendExclusiveLockToApplicationWithInfo:)]))
00680                                                         {
00681                                                                lendLock = [[self delegate] hidRemote:self lendExclusiveLockToApplicationWithInfo:[notification userInfo]];
00682                                                         }
00683                                                         
00684                                                         if (lendLock)
00685                                                         {
00686                                                                [_waitForReturnByPID release];
00687                                                                _waitForReturnByPID = [originPID retain];
00688                                                                
00689                                                                if (_waitForReturnByPID != nil)
00690                                                                {
00691                                                                       [self stopRemoteControl];
00692                                                                       
00693                                                                       [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kHIDRemoteDNHIDRemoteRetry
00694                                                                                                                               object:[NSString stringWithFormat:@"%d", [_waitForReturnByPID intValue]]
00695                                                                                                                             userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
00696                                                                                                                                             [NSNumber numberWithUnsignedInt:(unsigned int)getpid()], kHIDRemoteDNStatusPIDKey,
00697                                                                                                                                             [[NSBundle mainBundle] bundleIdentifier],         (NSString *)kCFBundleIdentifierKey,
00698                                                                                                                                     nil]
00699                                                                                                                    deliverImmediately:YES];
00700                                                                }
00701                                                         }
00702                                                  }
00703                                           }
00704                                    }
00705                             }
00706                      }
00707               }
00708        }
00709 }
00710 
00711 #pragma mark -- PRIVATE: Service setup and destruction --
00712 - (BOOL)_prematchService:(io_object_t)service
00713 {
00714        BOOL serviceMatches = NO;
00715        NSString *ioClass;
00716        NSNumber *candelairHIDRemoteCompatibilityMask;
00717        
00718        if (service != 0)
00719        {
00720               // IOClass matching
00721               if ((ioClass = (NSString *)IORegistryEntryCreateCFProperty((io_registry_entry_t)service,
00722                                                                   CFSTR(kIOClassKey),
00723                                                                   kCFAllocatorDefault,
00724                                                                   0)) != nil)
00725               {
00726                      // Match on Apple's AppleIRController and old versions of the Remote Buddy IR Controller
00727                      if ([ioClass isEqual:@"AppleIRController"] || [ioClass isEqual:@"RBIOKitAIREmu"])
00728                      {
00729                             CFTypeRef candelairHIDRemoteCompatibilityDevice;
00730 
00731                             serviceMatches = YES;
00732                             
00733                             if ((candelairHIDRemoteCompatibilityDevice = IORegistryEntryCreateCFProperty((io_registry_entry_t)service, CFSTR("CandelairHIDRemoteCompatibilityDevice"), kCFAllocatorDefault, 0)) != NULL)
00734                             {
00735                                    if (CFEqual(kCFBooleanTrue, candelairHIDRemoteCompatibilityDevice))
00736                                    {
00737                                           serviceMatches = NO;
00738                                    }
00739                                    
00740                                    CFRelease (candelairHIDRemoteCompatibilityDevice);
00741                             }
00742                      }
00743 
00744                      // Match on the virtual IOSPIRIT IR Controller
00745                      if ([ioClass isEqual:@"IOSPIRITIRController"])
00746                      {
00747                             serviceMatches = YES;
00748                      }
00749                      
00750                      CFRelease((CFTypeRef)ioClass);
00751               }
00752 
00753               // Match on services that claim compatibility with the HID Remote class (Candelair or third-party) by having a property of CandelairHIDRemoteCompatibilityMask = 1 <Type: Number>
00754               if ((candelairHIDRemoteCompatibilityMask = (NSNumber *)IORegistryEntryCreateCFProperty((io_registry_entry_t)service, CFSTR("CandelairHIDRemoteCompatibilityMask"), kCFAllocatorDefault, 0)) != nil)
00755               {
00756                      if ([candelairHIDRemoteCompatibilityMask isKindOfClass:[NSNumber class]])
00757                      {
00758                             if ([candelairHIDRemoteCompatibilityMask unsignedIntValue] & kHIDRemoteCompatibilityFlagsStandardHIDRemoteDevice)
00759                             {
00760                                    serviceMatches = YES;
00761                             }
00762                             else
00763                             {
00764                                    serviceMatches = NO;
00765                             }
00766                      }
00767                      
00768                      CFRelease((CFTypeRef)candelairHIDRemoteCompatibilityMask);
00769               }
00770        }
00771 
00772        if (([self delegate]!=nil) &&
00773            ([[self delegate] respondsToSelector:@selector(hidRemote:inspectNewHardwareWithService:prematchResult:)]))
00774        {
00775               serviceMatches = [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self inspectNewHardwareWithService:service prematchResult:serviceMatches];
00776        }
00777        
00778        return (serviceMatches);
00779 }
00780 
00781 - (HIDRemoteButtonCode)buttonCodeForUsage:(unsigned int)usage usagePage:(unsigned int)usagePage
00782 {
00783        HIDRemoteButtonCode buttonCode = kHIDRemoteButtonCodeNone;
00784 
00785        switch (usagePage)
00786        {
00787               case kHIDPage_Consumer:
00788                      switch (usage)
00789                      {
00790                             case kHIDUsage_Csmr_MenuPick:
00791                                    // Aluminum Remote: Center
00792                                    buttonCode = (kHIDRemoteButtonCodeCenter|kHIDRemoteButtonCodeAluminumMask);
00793                             break;
00794                             
00795                             case kHIDUsage_Csmr_ModeStep:
00796                                    // Aluminium Remote: Center Hold
00797                                    buttonCode = (kHIDRemoteButtonCodeCenterHold|kHIDRemoteButtonCodeAluminumMask);
00798                             break;
00799 
00800                             case kHIDUsage_Csmr_PlayOrPause:
00801                                    // Aluminum Remote: Play/Pause
00802                                    buttonCode = (kHIDRemoteButtonCodePlay|kHIDRemoteButtonCodeAluminumMask);
00803                             break;
00804                      
00805                             case kHIDUsage_Csmr_Rewind:
00806                                    buttonCode = kHIDRemoteButtonCodeLeftHold;
00807                             break;
00808                             
00809                             case kHIDUsage_Csmr_FastForward:
00810                                    buttonCode = kHIDRemoteButtonCodeRightHold;
00811                             break;
00812 
00813                             case kHIDUsage_Csmr_Menu:
00814                                    buttonCode = kHIDRemoteButtonCodeMenuHold;
00815                             break;
00816                      }
00817               break;
00818               
00819               case kHIDPage_GenericDesktop:
00820                      switch (usage)
00821                      {
00822                             case kHIDUsage_GD_SystemAppMenu:
00823                                    buttonCode = kHIDRemoteButtonCodeMenu;
00824                             break;
00825 
00826                             case kHIDUsage_GD_SystemMenu:
00827                                    buttonCode = kHIDRemoteButtonCodeCenter;
00828                             break;
00829 
00830                             case kHIDUsage_GD_SystemMenuRight:
00831                                    buttonCode = kHIDRemoteButtonCodeRight;
00832                             break;
00833 
00834                             case kHIDUsage_GD_SystemMenuLeft:
00835                                    buttonCode = kHIDRemoteButtonCodeLeft;
00836                             break;
00837 
00838                             case kHIDUsage_GD_SystemMenuUp:
00839                                    buttonCode = kHIDRemoteButtonCodeUp;
00840                             break;
00841 
00842                             case kHIDUsage_GD_SystemMenuDown:
00843                                    buttonCode = kHIDRemoteButtonCodeDown;
00844                             break;
00845                      }
00846               break;
00847               
00848               case 0x06: /* Reserved */
00849                      switch (usage)
00850                      {
00851                             case 0x22:
00852                                    buttonCode = kHIDRemoteButtonCodeIDChanged;
00853                             break;
00854                      }
00855               break;
00856               
00857               case 0xFF01: /* Vendor specific */
00858                      switch (usage)
00859                      {
00860                             case 0x23:
00861                                    buttonCode = kHIDRemoteButtonCodeCenterHold;
00862                             break;
00863 
00864                             #ifdef _HIDREMOTE_EXTENSIONS
00865                                    #define _HIDREMOTE_EXTENSIONS_SECTION 2
00866                                    #include "HIDRemoteAdditions.h"
00867                                    #undef _HIDREMOTE_EXTENSIONS_SECTION
00868                             #endif /* _HIDREMOTE_EXTENSIONS */
00869                      }
00870               break;
00871        }
00872        
00873        return (buttonCode);
00874 }
00875 
00876 - (BOOL)_setupService:(io_object_t)service
00877 {
00878        kern_return_t         kernResult;
00879        IOReturn              returnCode;
00880        HRESULT                      hResult;
00881        SInt32                score;
00882        BOOL                  opened = NO, queueStarted = NO;
00883        IOHIDDeviceInterface122      **hidDeviceInterface       = NULL;
00884        IOCFPlugInInterface   **cfPluginInterface = NULL;
00885        IOHIDQueueInterface   **hidQueueInterface = NULL;
00886        io_object_t           serviceNotification = 0;
00887        CFRunLoopSourceRef    queueEventSource    = NULL;
00888        NSMutableDictionary   *hidAttribsDict     = nil;
00889        CFArrayRef            hidElements         = NULL;
00890        NSError                      *error                     = nil;
00891        UInt32                errorCode           = 0;
00892 
00893        if (![self _prematchService:service])
00894        {
00895               return (NO);
00896        }
00897 
00898        do
00899        {
00900               // Create a plugin interface ..
00901               kernResult = IOCreatePlugInInterfaceForService(  service,
00902                                                         kIOHIDDeviceUserClientTypeID,
00903                                                         kIOCFPlugInInterfaceID,
00904                                                         &cfPluginInterface,
00905                                                         &score);
00906                                                         
00907               if (kernResult != kIOReturnSuccess)
00908               {
00909                      error = [NSError errorWithDomain:NSMachErrorDomain code:kernResult userInfo:nil];
00910                      errorCode = 1;
00911                      break; 
00912               }
00913 
00914               
00915               // .. use it to get the HID interface ..
00916               hResult = (*cfPluginInterface)->QueryInterface(  cfPluginInterface, 
00917                                                         CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID122),
00918                                                         (LPVOID)&hidDeviceInterface);
00919                                                                
00920               if ((hResult!=S_OK) || (hidDeviceInterface==NULL))
00921               {
00922                      error = [NSError errorWithDomain:NSMachErrorDomain code:hResult userInfo:nil];
00923                      errorCode = 2;
00924                      break; 
00925               }
00926 
00927               
00928               // .. then open it ..
00929               switch (_mode)
00930               {
00931                      case kHIDRemoteModeShared:
00932                             hResult = (*hidDeviceInterface)->open(hidDeviceInterface, kIOHIDOptionsTypeNone);
00933                      break;
00934                      
00935                      case kHIDRemoteModeExclusive:
00936                      case kHIDRemoteModeExclusiveAuto:
00937                             hResult = (*hidDeviceInterface)->open(hidDeviceInterface, kIOHIDOptionsTypeSeizeDevice);
00938                      break;
00939                      
00940                      default:
00941                             goto cleanUp; // Ugh! But there are no "double breaks" available in C AFAIK ..
00942                      break;
00943               }
00944               
00945               if (hResult!=S_OK)
00946               {
00947                      error = [NSError errorWithDomain:NSMachErrorDomain code:hResult userInfo:nil];
00948                      errorCode = 3;
00949                      break;
00950               }
00951               
00952               opened = YES;
00953 
00954               // .. query the HID elements ..
00955               returnCode = (*hidDeviceInterface)->copyMatchingElements(hidDeviceInterface,
00956                                                                 NULL,
00957                                                                 &hidElements);
00958               if ((returnCode != kIOReturnSuccess) || (hidElements==NULL))
00959               {
00960                      error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
00961                      errorCode = 4;
00962                      
00963                      break;
00964               }
00965 
00966               // Setup an event queue for HID events!
00967               hidQueueInterface = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
00968               if (hidQueueInterface == NULL)
00969               {
00970                      error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil];
00971                      errorCode = 5;
00972 
00973                      break;
00974               }
00975 
00976               returnCode = (*hidQueueInterface)->create(hidQueueInterface, 0, 32);
00977               if ((returnCode != kIOReturnSuccess) || (hidElements==NULL))
00978               {
00979                      error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
00980                      errorCode = 6;
00981 
00982                      break;
00983               }
00984 
00985 
00986               // Setup of attributes stored for this HID device
00987               hidAttribsDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
00988                                    [NSValue valueWithPointer:(const void *)cfPluginInterface],    kHIDRemoteCFPluginInterface,
00989                                    [NSValue valueWithPointer:(const void *)hidDeviceInterface],   kHIDRemoteHIDDeviceInterface,
00990                                    [NSValue valueWithPointer:(const void *)hidQueueInterface],    kHIDRemoteHIDQueueInterface,
00991                              nil];
00992 
00993               {
00994                      UInt32 i, hidElementCnt = CFArrayGetCount(hidElements);
00995                      NSMutableDictionary *cookieButtonCodeLUT = [[NSMutableDictionary alloc] init];
00996                      NSMutableDictionary *cookieCount   = [[NSMutableDictionary alloc] init];
00997                      
00998                      if ((cookieButtonCodeLUT==nil) || (cookieCount==nil))
00999                      {
01000                             [cookieButtonCodeLUT  release];
01001                             cookieButtonCodeLUT = nil;
01002 
01003                             [cookieCount  release];
01004                             cookieCount = nil;
01005 
01006                             error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil];
01007                             errorCode = 7;
01008 
01009                             break;
01010                      }
01011                      
01012                      // Analyze the HID elements and find matching elements
01013                      for (i=0;i<hidElementCnt;i++)
01014                      {
01015                             CFDictionaryRef             hidDict;
01016                             NSNumber             *usage, *usagePage, *cookie;
01017                             HIDRemoteButtonCode  buttonCode = kHIDRemoteButtonCodeNone;
01018                             
01019                             hidDict = CFArrayGetValueAtIndex(hidElements, i);
01020                             
01021                             usage    = (NSNumber *) CFDictionaryGetValue(hidDict, CFSTR(kIOHIDElementUsageKey));
01022                             usagePage = (NSNumber *) CFDictionaryGetValue(hidDict, CFSTR(kIOHIDElementUsagePageKey));
01023                             cookie    = (NSNumber *) CFDictionaryGetValue(hidDict, CFSTR(kIOHIDElementCookieKey));
01024                             
01025                             if (usage && usagePage && cookie)
01026                             {
01027                                    // Find the button codes for the ID combos
01028                                    buttonCode = [self buttonCodeForUsage:[usage unsignedIntValue] usagePage:[usagePage unsignedIntValue]];
01029 
01030                                    #ifdef _HIDREMOTE_EXTENSIONS
01031                                           // Debug logging code
01032                                           #define _HIDREMOTE_EXTENSIONS_SECTION 3
01033                                           #include "HIDRemoteAdditions.h"
01034                                           #undef _HIDREMOTE_EXTENSIONS_SECTION
01035                                    #endif /* _HIDREMOTE_EXTENSIONS */
01036                                    
01037                                    // Did record match?
01038                                    if (buttonCode != kHIDRemoteButtonCodeNone)
01039                                    {
01040                                           NSString *pairString     = [[NSString alloc] initWithFormat:@"%u_%u", [usagePage unsignedIntValue], [usage unsignedIntValue]];
01041                                           NSNumber *buttonCodeNumber  = [[NSNumber alloc] initWithUnsignedInt:(unsigned int)buttonCode];
01042                                           
01043                                           #ifdef _HIDREMOTE_EXTENSIONS
01044                                                  // Debug logging code
01045                                                  #define _HIDREMOTE_EXTENSIONS_SECTION 4
01046                                                  #include "HIDRemoteAdditions.h"
01047                                                  #undef _HIDREMOTE_EXTENSIONS_SECTION
01048                                           #endif /* _HIDREMOTE_EXTENSIONS */
01049                                           
01050                                           [cookieCount         setObject:buttonCodeNumber forKey:pairString];
01051                                           [cookieButtonCodeLUT setObject:buttonCodeNumber forKey:cookie];
01052                                           
01053                                           (*hidQueueInterface)->addElement(hidQueueInterface,
01054                                                                        (IOHIDElementCookie) [cookie unsignedIntValue],
01055                                                                        0);
01056                                           
01057                                           [buttonCodeNumber release];
01058                                           [pairString release];
01059                                    }
01060                             }
01061                      }
01062                      
01063                      // Compare number of *unique* matches (thus the cookieCount dictionary) with required minimum
01064                      if ([cookieCount count] < 10)
01065                      {
01066                             [cookieButtonCodeLUT  release];
01067                             cookieButtonCodeLUT = nil;
01068 
01069                             [cookieCount  release];
01070                             cookieCount = nil;
01071 
01072                             error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil];
01073                             errorCode = 8;
01074 
01075                             break;
01076                      }
01077 
01078                      [hidAttribsDict setObject:cookieButtonCodeLUT forKey:kHIDRemoteCookieButtonCodeLUT];
01079 
01080                      [cookieButtonCodeLUT  release];
01081                      cookieButtonCodeLUT = nil;
01082 
01083                      [cookieCount  release];
01084                      cookieCount = nil;
01085               }
01086               
01087               // Finish setup of IOHIDQueueInterface with CFRunLoop
01088               returnCode = (*hidQueueInterface)->createAsyncEventSource(hidQueueInterface, &queueEventSource);
01089               if ((returnCode != kIOReturnSuccess) || (queueEventSource == NULL))
01090               {
01091                      error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
01092                      errorCode = 9;
01093                      break; 
01094               }
01095               
01096               returnCode = (*hidQueueInterface)->setEventCallout(hidQueueInterface, HIDEventCallback, (void *)((intptr_t)service), (void *)self);
01097               if (returnCode != kIOReturnSuccess)
01098               {
01099                      error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
01100                      errorCode = 10;
01101                      break;
01102               }
01103               
01104               CFRunLoopAddSource(  CFRunLoopGetCurrent(),
01105                                    queueEventSource,
01106                                    kCFRunLoopCommonModes);
01107               [hidAttribsDict setObject:[NSValue valueWithPointer:(const void *)queueEventSource] forKey:kHIDRemoteCFRunLoopSource];
01108               
01109               returnCode = (*hidQueueInterface)->start(hidQueueInterface);
01110               if (returnCode != kIOReturnSuccess)
01111               {
01112                      error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
01113                      errorCode = 11;
01114                      break;
01115               }
01116               
01117               queueStarted = YES;
01118 
01119               // Setup device notifications
01120               returnCode = IOServiceAddInterestNotification(   _notifyPort,
01121                                                         service,
01122                                                         kIOGeneralInterest,
01123                                                         ServiceNotificationCallback,
01124                                                         self,
01125                                                         &serviceNotification);
01126               if ((returnCode != kIOReturnSuccess) || (serviceNotification==0))
01127               {
01128                      error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
01129                      errorCode = 12;
01130                      break;
01131               }
01132 
01133               [hidAttribsDict setObject:[NSNumber numberWithUnsignedInt:(unsigned int)serviceNotification] forKey:kHIDRemoteServiceNotification];
01134               
01135               // Retain service
01136               if (IOObjectRetain(service) != kIOReturnSuccess)
01137               {
01138                      error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil];
01139                      errorCode = 13;
01140                      break;
01141               }
01142               
01143               [hidAttribsDict setObject:[NSNumber numberWithUnsignedInt:(unsigned int)service] forKey:kHIDRemoteService];
01144               
01145               // Get some (somewhat optional) infos on the device
01146               {
01147                      CFStringRef product, manufacturer, transport;
01148               
01149                      if ((product = IORegistryEntryCreateCFProperty(  (io_registry_entry_t)service,
01150                                                                (CFStringRef) @"Product",
01151                                                                kCFAllocatorDefault,
01152                                                                0)) != NULL)
01153                      {
01154                             if (CFGetTypeID(product) == CFStringGetTypeID())
01155                             {
01156                                    [hidAttribsDict setObject:(NSString *)product forKey:kHIDRemoteProduct];
01157                             }
01158                             
01159                             CFRelease(product);
01160                      }
01161 
01162                      if ((manufacturer = IORegistryEntryCreateCFProperty(    (io_registry_entry_t)service,
01163                                                                       (CFStringRef) @"Manufacturer",
01164                                                                       kCFAllocatorDefault,
01165                                                                       0)) != NULL)
01166                      {
01167                             if (CFGetTypeID(manufacturer) == CFStringGetTypeID())
01168                             {
01169                                    [hidAttribsDict setObject:(NSString *)manufacturer forKey:kHIDRemoteManufacturer];
01170                             }
01171                             
01172                             CFRelease(manufacturer);
01173                      }
01174 
01175                      if ((transport = IORegistryEntryCreateCFProperty(       (io_registry_entry_t)service,
01176                                                                       (CFStringRef) @"Transport",
01177                                                                       kCFAllocatorDefault,
01178                                                                       0)) != NULL)
01179                      {
01180                             if (CFGetTypeID(transport) == CFStringGetTypeID())
01181                             {
01182                                    [hidAttribsDict setObject:(NSString *)transport forKey:kHIDRemoteTransport];
01183                             }
01184                             
01185                             CFRelease(transport);
01186                      }
01187               }
01188               
01189               // Determine Aluminum Remote support
01190               {
01191                      CFNumberRef aluSupport;
01192                      HIDRemoteAluminumRemoteSupportLevel supportLevel = kHIDRemoteAluminumRemoteSupportLevelNone;
01193                      
01194                      if ((_mode == kHIDRemoteModeExclusive) || (_mode == kHIDRemoteModeExclusiveAuto))
01195                      {
01196                             // Determine if this driver offers on-demand support for the Aluminum Remote (only relevant under OS versions < 10.6.2)
01197                             if ((aluSupport = IORegistryEntryCreateCFProperty((io_registry_entry_t)service,
01198                                                                         (CFStringRef) @"AluminumRemoteSupportLevelOnDemand",
01199                                                                         kCFAllocatorDefault,
01200                                                                         0)) != nil)
01201                             {
01202                                    // There is => request the driver to enable it for us
01203                                    if (IORegistryEntrySetCFProperty((io_registry_entry_t)service,
01204                                                                 CFSTR("EnableAluminumRemoteSupportForMe"),
01205                                                                 [NSDictionary dictionaryWithObjectsAndKeys:
01206                                                                       [NSNumber numberWithLongLong:(long long)getpid()],      @"pid",
01207                                                                       [NSNumber numberWithLongLong:(long long)getuid()],      @"uid",
01208                                                                 nil]) == kIOReturnSuccess)
01209                                    {
01210                                           if (CFGetTypeID(aluSupport) == CFNumberGetTypeID())
01211                                           {
01212                                                  supportLevel = (HIDRemoteAluminumRemoteSupportLevel) [(NSNumber *)aluSupport intValue];
01213                                           }
01214 
01215                                           [hidAttribsDict setObject:[NSNumber numberWithBool:YES] forKey:kHIDRemoteAluminumRemoteSupportOnDemand];
01216                                    }
01217                                    
01218                                    CFRelease(aluSupport);
01219                             }
01220                      }
01221                      
01222                      if (supportLevel == kHIDRemoteAluminumRemoteSupportLevelNone)
01223                      {
01224                             if ((aluSupport = IORegistryEntryCreateCFProperty((io_registry_entry_t)service,
01225                                                                         (CFStringRef) @"AluminumRemoteSupportLevel",
01226                                                                         kCFAllocatorDefault,
01227                                                                         0)) != nil)
01228                             {
01229                                    if (CFGetTypeID(aluSupport) == CFNumberGetTypeID())
01230                                    {
01231                                           supportLevel = (HIDRemoteAluminumRemoteSupportLevel) [(NSNumber *)aluSupport intValue];
01232                                    }
01233                                    
01234                                    CFRelease(aluSupport);
01235                             }
01236                             else
01237                             {
01238                                    CFStringRef ioKitClassName;
01239                             
01240                                    if ((ioKitClassName = IORegistryEntryCreateCFProperty(  (io_registry_entry_t)service,
01241                                                                                     CFSTR(kIOClassKey),
01242                                                                                     kCFAllocatorDefault,
01243                                                                                     0)) != nil)
01244                                    {
01245                                           if ([(NSString *)ioKitClassName isEqual:@"AppleIRController"])
01246                                           {
01247                                                  SInt32 systemVersion;
01248                                                  
01249                                                  if (Gestalt(gestaltSystemVersion, &systemVersion) == noErr)
01250                                                  {
01251                                                         if (systemVersion >= 0x1062)
01252                                                         {
01253                                                                // Support for the Aluminum Remote was added only with OS 10.6.2. Previous versions can not distinguish
01254                                                                // between the Center and the new, seperate Play/Pause button. They'll recognize both as presses of the
01255                                                                // "Center" button.
01256                                                                //
01257                                                                // You CAN, however, receive Aluminum Remote button presses even under OS 10.5 when using Remote Buddy's
01258                                                                // Virtual Remote. While Remote Buddy does support the Aluminum Remote across all OS releases it runs on,
01259                                                                // its Virtual Remote can only emulate Aluminum Remote button presses under OS 10.5 and up in order not to
01260                                                                // break compatibility with applications whose IR Remote code relies on driver internals. [13-Nov-09]
01261                                                                supportLevel = kHIDRemoteAluminumRemoteSupportLevelNative;
01262                                                         }
01263                                                  }
01264                                           }
01265                                           
01266                                           CFRelease(ioKitClassName);
01267                                    }
01268                             }
01269                      }
01270 
01271                      [hidAttribsDict setObject:(NSNumber *)[NSNumber numberWithInt:(int)supportLevel] forKey:kHIDRemoteAluminumRemoteSupportLevel];
01272               }
01273               
01274               // Add it to the serviceAttribMap
01275               [_serviceAttribMap setObject:hidAttribsDict forKey:[NSNumber numberWithUnsignedInt:(unsigned int)service]];
01276               
01277               // And we're done with setup ..
01278               if (([self delegate]!=nil) &&
01279                   ([[self delegate] respondsToSelector:@selector(hidRemote:foundNewHardwareWithAttributes:)]))
01280               {
01281                      [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self foundNewHardwareWithAttributes:hidAttribsDict];
01282               }
01283               
01284               [hidAttribsDict release];
01285               hidAttribsDict = nil;
01286               
01287               return(YES);
01288 
01289        }while(0);
01290        
01291        cleanUp:
01292 
01293        if (([self delegate]!=nil) &&
01294            ([[self delegate] respondsToSelector:@selector(hidRemote:failedNewHardwareWithError:)]))
01295        {
01296               if (error)
01297               {
01298                      error = [NSError errorWithDomain:[error domain] 
01299                                               code:[error code]
01300                                           userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:errorCode] forKey:@"InternalErrorCode"]
01301                             ];
01302               }
01303 
01304               [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self failedNewHardwareWithError:error];
01305        }
01306        
01307        // An error occured or this device is not of interest .. cleanup ..
01308        if (serviceNotification)
01309        {
01310               IOObjectRelease(serviceNotification);
01311               serviceNotification = 0;
01312        }
01313 
01314        if (queueEventSource)
01315        {
01316               CFRunLoopSourceInvalidate(queueEventSource);
01317               queueEventSource=NULL;
01318        }
01319        
01320        if (hidQueueInterface)
01321        {
01322               if (queueStarted)
01323               {
01324                      (*hidQueueInterface)->stop(hidQueueInterface);
01325               }
01326               (*hidQueueInterface)->dispose(hidQueueInterface);
01327               (*hidQueueInterface)->Release(hidQueueInterface);
01328               hidQueueInterface = NULL;
01329        }
01330 
01331        if (hidAttribsDict)
01332        {
01333               [hidAttribsDict release];
01334               hidAttribsDict = nil;
01335        }
01336        
01337        if (hidElements)
01338        {
01339               CFRelease(hidElements);
01340               hidElements = NULL;
01341        }
01342        
01343        if (hidDeviceInterface)
01344        {
01345               if (opened)
01346               {
01347                      (*hidDeviceInterface)->close(hidDeviceInterface);
01348               }
01349               (*hidDeviceInterface)->Release(hidDeviceInterface);
01350               // opened = NO;
01351               hidDeviceInterface = NULL;
01352        }
01353        
01354        if (cfPluginInterface)
01355        {
01356               IODestroyPlugInInterface(cfPluginInterface);
01357               cfPluginInterface = NULL;
01358        }
01359        
01360        return (NO);
01361 }
01362 
01363 - (void)_destructService:(io_object_t)service
01364 {
01365        NSNumber          *serviceValue;
01366        NSMutableDictionary *serviceDict = NULL;
01367        
01368        if ((serviceValue = [NSNumber numberWithUnsignedInt:(unsigned int)service]) == nil)
01369        {
01370               return;
01371        }
01372        
01373        serviceDict  = [_serviceAttribMap objectForKey:serviceValue];
01374        
01375        if (serviceDict)
01376        {
01377               IOHIDDeviceInterface122      **hidDeviceInterface       = NULL;
01378               IOCFPlugInInterface   **cfPluginInterface = NULL;
01379               IOHIDQueueInterface   **hidQueueInterface = NULL;
01380               io_object_t           serviceNotification = 0;
01381               CFRunLoopSourceRef    queueEventSource    = NULL;
01382               io_object_t           theService          = 0;
01383               NSMutableDictionary   *cookieButtonMap    = nil;
01384               NSTimer                      *simulateHoldTimer  = nil;
01385 
01386               serviceNotification = (io_object_t)                     ([serviceDict objectForKey:kHIDRemoteServiceNotification]      ? [[serviceDict objectForKey:kHIDRemoteServiceNotification] unsignedIntValue] :   0);
01387               theService        = (io_object_t)                ([serviceDict objectForKey:kHIDRemoteService]                  ? [[serviceDict objectForKey:kHIDRemoteService]             unsignedIntValue] :   0);
01388               queueEventSource    = (CFRunLoopSourceRef)              ([serviceDict objectForKey:kHIDRemoteCFRunLoopSource]          ? [[serviceDict objectForKey:kHIDRemoteCFRunLoopSource]     pointerValue]     : NULL);
01389               hidQueueInterface   = (IOHIDQueueInterface **)          ([serviceDict objectForKey:kHIDRemoteHIDQueueInterface]        ? [[serviceDict objectForKey:kHIDRemoteHIDQueueInterface]   pointerValue]     : NULL);
01390               hidDeviceInterface  = (IOHIDDeviceInterface122 **)      ([serviceDict objectForKey:kHIDRemoteHIDDeviceInterface]       ? [[serviceDict objectForKey:kHIDRemoteHIDDeviceInterface]  pointerValue]     : NULL);
01391               cfPluginInterface   = (IOCFPlugInInterface **)          ([serviceDict objectForKey:kHIDRemoteCFPluginInterface]        ? [[serviceDict objectForKey:kHIDRemoteCFPluginInterface]   pointerValue]     : NULL);
01392               cookieButtonMap          = (NSMutableDictionary *)              [serviceDict objectForKey:kHIDRemoteCookieButtonCodeLUT];
01393               simulateHoldTimer   = (NSTimer *)                 [serviceDict objectForKey:kHIDRemoteSimulateHoldEventsTimer];
01394               
01395               [serviceDict  retain];
01396               [_serviceAttribMap removeObjectForKey:serviceValue];
01397 
01398               if (([serviceDict objectForKey:kHIDRemoteAluminumRemoteSupportOnDemand]!=nil) && [[serviceDict objectForKey:kHIDRemoteAluminumRemoteSupportOnDemand] boolValue] && (theService != 0))
01399               {
01400                      // We previously requested the driver to enable Aluminum Remote support for us. Tell it to turn it off again - now that we no longer need it
01401                      IORegistryEntrySetCFProperty(      (io_registry_entry_t)theService,
01402                                                  CFSTR("DisableAluminumRemoteSupportForMe"),
01403                                                  [NSDictionary dictionaryWithObjectsAndKeys:
01404                                                         [NSNumber numberWithLongLong:(long long)getpid()],      @"pid",
01405                                                         [NSNumber numberWithLongLong:(long long)getuid()],      @"uid",
01406                                                  nil]);
01407               }
01408 
01409               if (([self delegate]!=nil) &&
01410                   ([[self delegate] respondsToSelector:@selector(hidRemote:releasedHardwareWithAttributes:)]))
01411               {
01412                      [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self releasedHardwareWithAttributes:serviceDict];
01413               }
01414               
01415               if (simulateHoldTimer)
01416               {
01417                      [simulateHoldTimer invalidate];
01418               }
01419 
01420               if (serviceNotification)
01421               {
01422                      IOObjectRelease(serviceNotification);
01423               }
01424 
01425               if (queueEventSource)
01426               {
01427                      CFRunLoopRemoveSource(      CFRunLoopGetCurrent(),
01428                                           queueEventSource,
01429                                           kCFRunLoopCommonModes);
01430               }
01431               
01432               if (hidQueueInterface && cookieButtonMap)
01433               {
01434                      NSEnumerator *cookieEnum = [cookieButtonMap keyEnumerator];
01435                      NSNumber *cookie;
01436                      
01437                      while ((cookie = [cookieEnum nextObject]) != nil)
01438                      {
01439                             if ((*hidQueueInterface)->hasElement(hidQueueInterface, (IOHIDElementCookie) [cookie unsignedIntValue]))
01440                             {
01441                                    (*hidQueueInterface)->removeElement(hidQueueInterface,
01442                                                                    (IOHIDElementCookie) [cookie unsignedIntValue]);
01443                             }
01444                      };
01445               }
01446               
01447               if (hidQueueInterface)
01448               {
01449                      (*hidQueueInterface)->stop(hidQueueInterface);
01450                      (*hidQueueInterface)->dispose(hidQueueInterface);
01451                      (*hidQueueInterface)->Release(hidQueueInterface);
01452               }
01453               
01454               if (hidDeviceInterface)
01455               {
01456                      (*hidDeviceInterface)->close(hidDeviceInterface);
01457                      (*hidDeviceInterface)->Release(hidDeviceInterface);
01458               }
01459               
01460               if (cfPluginInterface)
01461               {
01462                      IODestroyPlugInInterface(cfPluginInterface);
01463               }
01464               
01465               if (theService)
01466               {
01467                      IOObjectRelease(theService);
01468               }
01469 
01470               [serviceDict release];
01471        }
01472 }
01473 
01474 
01475 #pragma mark -- PRIVATE: HID Event handling --
01476 - (void)_simulateHoldEvent:(NSTimer *)aTimer
01477 {
01478        NSMutableDictionary *hidAttribsDict;
01479        NSTimer  *shTimer;
01480        NSNumber *shButtonCode;
01481        
01482        if ((hidAttribsDict = (NSMutableDictionary *)[aTimer userInfo]) != nil)
01483        {
01484               if (((shTimer    = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsTimer]) != nil) &&
01485                   ((shButtonCode = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsOriginButtonCode]) != nil)) 
01486               {
01487                      [shTimer invalidate];
01488                      [hidAttribsDict removeObjectForKey:kHIDRemoteSimulateHoldEventsTimer];
01489 
01490                      [self _sendButtonCode:(((HIDRemoteButtonCode)[shButtonCode unsignedIntValue])|kHIDRemoteButtonCodeHoldMask) isPressed:YES hidAttribsDict:hidAttribsDict];
01491               }
01492        }
01493 }
01494 
01495 - (void)_handleButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict
01496 {
01497        switch (buttonCode)
01498        {
01499               case kHIDRemoteButtonCodeIDChanged:
01500                      // Do nothing, this is handled seperately
01501               break;
01502 
01503               case kHIDRemoteButtonCodeUp:
01504               case kHIDRemoteButtonCodeDown:
01505                      if (_simulateHoldEvents)
01506                      {
01507                             NSTimer  *shTimer = nil;
01508                             NSNumber *shButtonCode = nil;
01509 
01510                             [[hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsTimer] invalidate];
01511 
01512                             if (isPressed)
01513                             {
01514                                    [hidAttribsDict setObject:[NSNumber numberWithUnsignedInt:buttonCode] forKey:kHIDRemoteSimulateHoldEventsOriginButtonCode];
01515                             
01516                                    if ((shTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:0.7] interval:0.1 target:self selector:@selector(_simulateHoldEvent:) userInfo:hidAttribsDict repeats:NO]) != nil)
01517                                    {
01518                                           [hidAttribsDict setObject:shTimer forKey:kHIDRemoteSimulateHoldEventsTimer];
01519                                    
01520                                           // Using CFRunLoopAddTimer instead of [[NSRunLoop currentRunLoop] addTimer:.. for consistency with run loop modes.
01521                                           // The kCFRunLoopCommonModes counterpart NSRunLoopCommonModes is only available in 10.5 and later, whereas this code
01522                                           // is designed to be also compatible with 10.4. CFRunLoopTimerRef is "toll-free-bridged" with NSTimer since 10.0.
01523                                           CFRunLoopAddTimer(CFRunLoopGetCurrent(), (CFRunLoopTimerRef)shTimer, kCFRunLoopCommonModes);
01524                                           
01525                                           [shTimer release];
01526 
01527                                           break;
01528                                    }
01529                             }
01530                             else
01531                             {
01532                                    shTimer            = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsTimer];
01533                                    shButtonCode = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsOriginButtonCode];
01534                             
01535                                    if (shTimer && shButtonCode)
01536                                    {
01537                                           [self _sendButtonCode:(HIDRemoteButtonCode)[shButtonCode unsignedIntValue] isPressed:YES hidAttribsDict:hidAttribsDict];
01538                                           [self _sendButtonCode:(HIDRemoteButtonCode)[shButtonCode unsignedIntValue] isPressed:NO hidAttribsDict:hidAttribsDict];
01539                                    }
01540                                    else
01541                                    {
01542                                           if (shButtonCode)
01543                                           {
01544                                                  [self _sendButtonCode:(((HIDRemoteButtonCode)[shButtonCode unsignedIntValue])|kHIDRemoteButtonCodeHoldMask) isPressed:NO hidAttribsDict:hidAttribsDict];
01545                                           }
01546                                    }
01547                             }
01548 
01549                             [hidAttribsDict removeObjectForKey:kHIDRemoteSimulateHoldEventsTimer];
01550                             [hidAttribsDict removeObjectForKey:kHIDRemoteSimulateHoldEventsOriginButtonCode];
01551                                    
01552                             break;
01553                      }
01554               
01555               default:
01556                      [self _sendButtonCode:buttonCode isPressed:isPressed hidAttribsDict:hidAttribsDict];
01557               break;
01558        }
01559 }
01560 
01561 - (void)_sendButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict
01562 {
01563        if (([self delegate]!=nil) &&
01564            ([[self delegate] respondsToSelector:@selector(hidRemote:eventWithButton:isPressed:fromHardwareWithAttributes:)]))
01565        {
01566               switch (buttonCode & (~kHIDRemoteButtonCodeAluminumMask))
01567               {
01568                      case kHIDRemoteButtonCodePlay:
01569                      case kHIDRemoteButtonCodeCenter:
01570                             if (buttonCode & kHIDRemoteButtonCodeAluminumMask)
01571                             {
01572                                    _lastSeenModel         = kHIDRemoteModelAluminum;
01573                                    _lastSeenModelRemoteID = _lastSeenRemoteID;
01574                             }
01575                             else
01576                             {
01577                                    switch ((HIDRemoteAluminumRemoteSupportLevel)[[hidAttribsDict objectForKey:kHIDRemoteAluminumRemoteSupportLevel] intValue])
01578                                    {
01579                                           case kHIDRemoteAluminumRemoteSupportLevelNone:
01580                                           case kHIDRemoteAluminumRemoteSupportLevelEmulation:
01581                                                  // Remote type can't be determined by just the Center button press
01582                                           break;
01583 
01584                                           case kHIDRemoteAluminumRemoteSupportLevelNative:
01585                                                  // Remote type can be safely determined by just the Center button press
01586                                                  if (((_lastSeenModel == kHIDRemoteModelAluminum) && (_lastSeenModelRemoteID != _lastSeenRemoteID)) ||
01587                                                       (_lastSeenModel == kHIDRemoteModelUndetermined))
01588                                                  {
01589                                                         _lastSeenModel = kHIDRemoteModelWhitePlastic;
01590                                                  }
01591                                           break;
01592                                    }
01593                             }
01594                      break;
01595               }
01596               
01597               // As soon as we have received a code that's unique to the Aluminum Remote, we can tell kHIDRemoteButtonCodePlayHold and kHIDRemoteButtonCodeCenterHold apart.
01598               // Prior to that, a long press of the new "Play" button will be submitted as a "kHIDRemoteButtonCodeCenterHold", not a "kHIDRemoteButtonCodePlayHold" code.
01599               if ((buttonCode == kHIDRemoteButtonCodeCenterHold) && (_lastSeenModel == kHIDRemoteModelAluminum))
01600               {
01601                      buttonCode = kHIDRemoteButtonCodePlayHold;
01602               }
01603        
01604               [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self eventWithButton:(buttonCode & (~kHIDRemoteButtonCodeAluminumMask)) isPressed:isPressed fromHardwareWithAttributes:hidAttribsDict];
01605        }
01606 }
01607 
01608 - (void)_hidEventFor:(io_service_t)hidDevice from:(IOHIDQueueInterface **)interface withResult:(IOReturn)result
01609 {
01610        NSMutableDictionary *hidAttribsDict = [_serviceAttribMap objectForKey:[NSNumber numberWithUnsignedInt:(unsigned int)hidDevice]];
01611        
01612        if (hidAttribsDict)
01613        {
01614               IOHIDQueueInterface **queueInterface  = NULL;
01615               
01616               queueInterface  = [[hidAttribsDict objectForKey:kHIDRemoteHIDQueueInterface] pointerValue];
01617               
01618               if (interface == queueInterface)
01619               {
01620                      NSNumber          *lastButtonPressedNumber = nil;
01621                      HIDRemoteButtonCode  lastButtonPressed = kHIDRemoteButtonCodeNone;
01622                      NSMutableDictionary *cookieButtonMap = nil;
01623                      
01624                      cookieButtonMap  = [hidAttribsDict objectForKey:kHIDRemoteCookieButtonCodeLUT];
01625 
01626                      if ((lastButtonPressedNumber = [hidAttribsDict objectForKey:kHIDRemoteLastButtonPressed]) != nil)
01627                      {
01628                             lastButtonPressed = [lastButtonPressedNumber unsignedIntValue];
01629                      }
01630 
01631                      while (result == kIOReturnSuccess)
01632                      {
01633                             IOHIDEventStruct hidEvent;
01634                             AbsoluteTime supportedTime = { 0,0 };
01635                      
01636                             result = (*queueInterface)->getNextEvent( queueInterface,
01637                                                                       &hidEvent,
01638                                                                       supportedTime,
01639                                                                       0);
01640                                                                
01641                             if (result == kIOReturnSuccess)
01642                             {
01643                                    NSNumber *buttonCodeNumber = [cookieButtonMap objectForKey:[NSNumber numberWithUnsignedInt:(unsigned int) hidEvent.elementCookie]];
01644                                    
01645                                    #ifdef _HIDREMOTE_EXTENSIONS
01646                                           // Debug logging code
01647                                           #define _HIDREMOTE_EXTENSIONS_SECTION 5
01648                                           #include "HIDRemoteAdditions.h"
01649                                           #undef _HIDREMOTE_EXTENSIONS_SECTION
01650                                    #endif /* _HIDREMOTE_EXTENSIONS */
01651                                    
01652                                    if (buttonCodeNumber)
01653                                    {
01654                                           HIDRemoteButtonCode buttonCode = [buttonCodeNumber unsignedIntValue];
01655                                    
01656                                           if (hidEvent.value == 0)
01657                                           {
01658                                                  if (buttonCode == lastButtonPressed)
01659                                                  {
01660                                                         [self _handleButtonCode:lastButtonPressed isPressed:NO hidAttribsDict:hidAttribsDict];
01661                                                         lastButtonPressed = kHIDRemoteButtonCodeNone;
01662                                                  }
01663                                           }
01664 
01665                                           if (hidEvent.value != 0)
01666                                           {
01667                                                  if (lastButtonPressed != kHIDRemoteButtonCodeNone)
01668                                                  {
01669                                                         [self _handleButtonCode:lastButtonPressed isPressed:NO hidAttribsDict:hidAttribsDict];
01670                                                         // lastButtonPressed = kHIDRemoteButtonCodeNone;
01671                                                  }
01672 
01673                                                  if (buttonCode == kHIDRemoteButtonCodeIDChanged)
01674                                                  {
01675                                                         if (([self delegate]!=nil) &&
01676                                                             ([[self delegate] respondsToSelector:@selector(hidRemote:remoteIDChangedOldID:newID:forHardwareWithAttributes:)]))
01677                                                         {
01678                                                                [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self remoteIDChangedOldID:_lastSeenRemoteID newID:hidEvent.value forHardwareWithAttributes:hidAttribsDict];
01679                                                         }
01680                                                  
01681                                                         _lastSeenRemoteID = hidEvent.value;
01682                                                         _lastSeenModel         = kHIDRemoteModelUndetermined;
01683                                                  }
01684                                                  
01685                                                  [self _handleButtonCode:buttonCode isPressed:YES hidAttribsDict:hidAttribsDict];
01686                                                  lastButtonPressed = buttonCode;
01687                                           }
01688                                    }
01689                             }
01690                      };
01691               
01692                      [hidAttribsDict setObject:[NSNumber numberWithUnsignedInt:lastButtonPressed] forKey:kHIDRemoteLastButtonPressed];
01693               }
01694               
01695               #ifdef _HIDREMOTE_EXTENSIONS
01696                      // Debug logging code
01697                      #define _HIDREMOTE_EXTENSIONS_SECTION 6
01698                      #include "HIDRemoteAdditions.h"
01699                      #undef _HIDREMOTE_EXTENSIONS_SECTION
01700               #endif /* _HIDREMOTE_EXTENSIONS */
01701        }
01702 }
01703 
01704 #pragma mark -- PRIVATE: Notification handling --
01705 - (void)_serviceMatching:(io_iterator_t)iterator
01706 {
01707        io_object_t matchingService = 0;
01708 
01709        while ((matchingService = IOIteratorNext(iterator)) != 0)
01710        {
01711               [self _setupService:matchingService];
01712 
01713               IOObjectRelease(matchingService);
01714        };
01715 }
01716 
01717 - (void)_serviceNotificationFor:(io_service_t)service messageType:(natural_t)messageType messageArgument:(void *)messageArgument
01718 {
01719        if (messageType == kIOMessageServiceIsTerminated)
01720        {
01721               [self _destructService:service];
01722        }
01723 }
01724 
01725 - (void)_updateSessionInformation
01726 {
01727        NSArray *consoleUsersArray;
01728        io_service_t rootService;
01729        
01730        if ((rootService = IORegistryGetRootEntry(_masterPort)) != 0)
01731        {
01732               if ((consoleUsersArray = (NSArray *)IORegistryEntryCreateCFProperty((io_registry_entry_t)rootService, CFSTR("IOConsoleUsers"), kCFAllocatorDefault, 0)) != nil)
01733               {
01734                      if ([consoleUsersArray isKindOfClass:[NSArray class]])  // Be careful - ensure this really is an array
01735                      {
01736                             NSEnumerator *consoleUsersEnum; // I *love* Obj-C2's fast enumerators, but we need to stay compatible with 10.4 :-/
01737                             
01738                             if ((consoleUsersEnum = [consoleUsersArray objectEnumerator]) != nil)
01739                             {
01740                                    UInt64 secureEventInputPIDSum = 0;
01741                                    uid_t frontUserSession = 0;
01742                                    NSDictionary *consoleUserDict;
01743                                    
01744                                    while ((consoleUserDict = [consoleUsersEnum nextObject]) != nil)
01745                                    {
01746                                           if ([consoleUserDict isKindOfClass:[NSDictionary class]]) // Be careful - ensure this really is a dictionary
01747                                           {
01748                                                  NSNumber *secureInputPID;
01749                                                  NSNumber *onConsole;
01750                                                  NSNumber *userID;
01751                                           
01752                                                  if ((secureInputPID = [consoleUserDict objectForKey:@"kCGSSessionSecureInputPID"]) != nil)
01753                                                  {
01754                                                         if ([secureInputPID isKindOfClass:[NSNumber class]])
01755                                                         {
01756                                                                secureEventInputPIDSum += ((UInt64) [secureInputPID intValue]);
01757                                                         }
01758                                                  }
01759                                                  
01760                                                  if (((onConsole = [consoleUserDict objectForKey:@"kCGSSessionOnConsoleKey"]) != nil) &&
01761                                                      ((userID    = [consoleUserDict objectForKey:@"kCGSSessionUserIDKey"]) != nil))
01762                                                  {
01763                                                         if ([onConsole isKindOfClass:[NSNumber class]] && [userID isKindOfClass:[NSNumber class]])
01764                                                         {
01765                                                                if ([onConsole boolValue])
01766                                                                {
01767                                                                       frontUserSession = (uid_t) [userID intValue];
01768                                                                }
01769                                                         }
01770                                                  }
01771                                           }
01772                                    }
01773 
01774                                    _lastSecureEventInputPIDSum = secureEventInputPIDSum;
01775                                    _lastFrontUserSession           = frontUserSession;
01776                             }
01777                      }
01778               
01779                      CFRelease((CFTypeRef)consoleUsersArray);
01780               }
01781               
01782               IOObjectRelease((io_object_t) rootService);
01783        }
01784 }
01785 
01786 - (void)_secureInputNotificationFor:(io_service_t)service messageType:(natural_t)messageType messageArgument:(void *)messageArgument
01787 {
01788        if (messageType == kIOMessageServiceBusyStateChange)
01789        {
01790               UInt64 old_lastSecureEventInputPIDSum = _lastSecureEventInputPIDSum;
01791               uid_t  old_lastFrontUserSession = _lastFrontUserSession;
01792               
01793               [self _updateSessionInformation];
01794               
01795               if (((old_lastSecureEventInputPIDSum != _lastSecureEventInputPIDSum) || (old_lastFrontUserSession != _lastFrontUserSession)) && _secureEventInputWorkAround)
01796               {
01797                      if ((_mode == kHIDRemoteModeExclusive) || (_mode == kHIDRemoteModeExclusiveAuto))
01798                      {
01799                             HIDRemoteMode restartInMode = _mode;
01800                      
01801                             [self stopRemoteControl];
01802                             [self startRemoteControl:restartInMode];
01803                      }
01804               }
01805        }
01806 }
01807 
01808 @end
01809 
01810 #pragma mark -- PRIVATE: IOKitLib Callbacks --
01811 
01812 static void HIDEventCallback(      void * target, 
01813                             IOReturn result,
01814                             void * refCon,
01815                             void * sender)
01816 {
01817        HIDRemote            *hidRemote = (HIDRemote *)refCon;
01818        NSAutoreleasePool    *pool     = [[NSAutoreleasePool alloc] init];
01819 
01820        [hidRemote _hidEventFor:(io_service_t)((intptr_t)target) from:(IOHIDQueueInterface**)sender withResult:(IOReturn)result];
01821 
01822        [pool release];
01823 }
01824 
01825 
01826 static void ServiceMatchingCallback(      void *refCon,
01827                                    io_iterator_t iterator)
01828 {
01829        HIDRemote            *hidRemote = (HIDRemote *)refCon;
01830        NSAutoreleasePool    *pool     = [[NSAutoreleasePool alloc] init];
01831 
01832        [hidRemote _serviceMatching:iterator];
01833 
01834        [pool release];
01835 }
01836 
01837 static void ServiceNotificationCallback(void *          refCon,
01838                                    io_service_t  service,
01839                                    natural_t     messageType,
01840                                    void *        messageArgument)
01841 {
01842        HIDRemote            *hidRemote = (HIDRemote *)refCon;
01843        NSAutoreleasePool    *pool     = [[NSAutoreleasePool alloc] init];
01844        
01845        [hidRemote _serviceNotificationFor:service
01846                             messageType:messageType
01847                         messageArgument:messageArgument];
01848 
01849        [pool release];
01850 }
01851 
01852 static void SecureInputNotificationCallback(     void *        refCon,
01853                                           io_service_t  service,
01854                                           natural_t     messageType,
01855                                           void *        messageArgument)
01856 {
01857        HIDRemote            *hidRemote = (HIDRemote *)refCon;
01858        NSAutoreleasePool    *pool     = [[NSAutoreleasePool alloc] init];
01859        
01860        [hidRemote _secureInputNotificationFor:service
01861                                messageType:messageType
01862                             messageArgument:messageArgument];
01863 
01864        [pool release];
01865 }
01866 
01867 // Attribute dictionary keys
01868 NSString *kHIDRemoteCFPluginInterface                   = @"CFPluginInterface";
01869 NSString *kHIDRemoteHIDDeviceInterface                  = @"HIDDeviceInterface";
01870 NSString *kHIDRemoteCookieButtonCodeLUT                 = @"CookieButtonCodeLUT";
01871 NSString *kHIDRemoteHIDQueueInterface                   = @"HIDQueueInterface";
01872 NSString *kHIDRemoteServiceNotification                 = @"ServiceNotification";
01873 NSString *kHIDRemoteCFRunLoopSource                     = @"CFRunLoopSource";
01874 NSString *kHIDRemoteLastButtonPressed                   = @"LastButtonPressed";
01875 NSString *kHIDRemoteService                      = @"Service";
01876 NSString *kHIDRemoteSimulateHoldEventsTimer             = @"SimulateHoldEventsTimer";
01877 NSString *kHIDRemoteSimulateHoldEventsOriginButtonCode  = @"SimulateHoldEventsOriginButtonCode";
01878 NSString *kHIDRemoteAluminumRemoteSupportLevel          = @"AluminumRemoteSupportLevel";
01879 NSString *kHIDRemoteAluminumRemoteSupportOnDemand       = @"AluminumRemoteSupportLevelOnDemand";
01880 
01881 NSString *kHIDRemoteManufacturer                 = @"Manufacturer";
01882 NSString *kHIDRemoteProduct                      = @"Product";
01883 NSString *kHIDRemoteTransport                           = @"Transport";
01884 
01885 // Distributed notifications
01886 NSString *kHIDRemoteDNHIDRemotePing                     = @"com.candelair.ping";
01887 NSString *kHIDRemoteDNHIDRemoteRetry                    = @"com.candelair.retry";
01888 NSString *kHIDRemoteDNHIDRemoteStatus                   = @"com.candelair.status";
01889 
01890 // Distributed notifications userInfo keys and values
01891 NSString *kHIDRemoteDNStatusHIDRemoteVersionKey         = @"HIDRemoteVersion";
01892 NSString *kHIDRemoteDNStatusPIDKey               = @"PID";
01893 NSString *kHIDRemoteDNStatusModeKey                     = @"Mode";
01894 NSString *kHIDRemoteDNStatusUnusedButtonCodesKey = @"UnusedButtonCodes";
01895 NSString *kHIDRemoteDNStatusActionKey                   = @"Action";
01896 NSString *kHIDRemoteDNStatusRemoteControlCountKey       = @"RemoteControlCount";
01897 NSString *kHIDRemoteDNStatusReturnToPIDKey              = @"ReturnToPID";
01898 NSString *kHIDRemoteDNStatusActionStart                 = @"start";
01899 NSString *kHIDRemoteDNStatusActionStop                  = @"stop";
01900 NSString *kHIDRemoteDNStatusActionUpdate         = @"update";
01901 NSString *kHIDRemoteDNStatusActionNoNeed         = @"noneed";
01902