diff options
author | Memphiz <memphis@machzwo.de> | 2016-07-09 21:51:18 +0200 |
---|---|---|
committer | Memphiz <memphis@machzwo.de> | 2016-08-13 10:42:37 +0200 |
commit | fbadb29dc42d1cb4d723eececbe9e74731681402 (patch) | |
tree | 7e579653ecdcaa8445319936b27d591b82a33eb8 /tools/EventClients | |
parent | 852f4d7a44e1092bf1969439b2f63b8a4df83b8a (diff) |
[XBMCHelper] - updated HIDRemote class to version 1.4
Diffstat (limited to 'tools/EventClients')
-rw-r--r-- | tools/EventClients/Clients/OSXRemote/HIDRemote/HIDRemote.h | 46 | ||||
-rw-r--r-- | tools/EventClients/Clients/OSXRemote/HIDRemote/HIDRemote.m | 524 |
2 files changed, 437 insertions, 133 deletions
diff --git a/tools/EventClients/Clients/OSXRemote/HIDRemote/HIDRemote.h b/tools/EventClients/Clients/OSXRemote/HIDRemote/HIDRemote.h index 8bbb54bb22..0c5b27373e 100644 --- a/tools/EventClients/Clients/OSXRemote/HIDRemote/HIDRemote.h +++ b/tools/EventClients/Clients/OSXRemote/HIDRemote/HIDRemote.h @@ -1,16 +1,16 @@ // // HIDRemote.h -// HIDRemote V1.1.1 +// HIDRemote V1.4 (18th February 2015) // // Created by Felix Schwarz on 06.04.07. -// Copyright 2007-2009 IOSPIRIT GmbH. All rights reserved. +// Copyright 2007-2015 IOSPIRIT GmbH. All rights reserved. // // The latest version of this class is available at // http://www.iospirit.com/developers/hidremote/ // // ** LICENSE ************************************************************************* // -// Copyright (c) 2007-2009 IOSPIRIT GmbH (http://www.iospirit.com/) +// Copyright (c) 2007-2014 IOSPIRIT GmbH (http://www.iospirit.com/) // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, @@ -41,7 +41,6 @@ // ************************************************************************************ - // ************************************************************************************ // ********************************** DOCUMENTATION *********************************** // ************************************************************************************ @@ -51,10 +50,22 @@ // // ************************************************************************************ - #import <Cocoa/Cocoa.h> +// For legacy SDKs +#ifndef MAC_OS_X_VERSION_10_9 +#define MAC_OS_X_VERSION_10_9 1090 +#endif /* MAC_OS_X_VERSION_10_9 */ + +#ifndef MAC_OS_X_VERSION_10_10 +#define MAC_OS_X_VERSION_10_10 101000 +#endif /* MAC_OS_X_VERSION_10_10 */ + +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 +// Carbon is only required on OS X versions prior to 10.10 (for getting the OS version via Gestalt() - +// replaced by [[NSProcessInfo processInfo] operatingSystemVersion] in 10.10) #include <Carbon/Carbon.h> +#endif #include <unistd.h> #include <mach/mach.h> @@ -241,11 +252,27 @@ typedef enum BOOL _secureEventInputWorkAround; UInt64 _lastSecureEventInputPIDSum; uid_t _lastFrontUserSession; + BOOL _lastScreenIsLocked; // Exclusive lock lending BOOL _exclusiveLockLending; + BOOL _sendExclusiveResourceReuseNotification; NSNumber *_waitForReturnByPID; NSNumber *_returnToPID; + BOOL _isRestarting; + + // Status notifications + BOOL _sendStatusNotifications; + NSString *_pidString; + + // Status + BOOL _applicationIsTerminating; + BOOL _isStopping; + + // Thread safety + #ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING /* #define HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING if you're running your HIDRemote instance on a background thread (requires OS X 10.5 or later) */ + NSThread *_runOnThread; + #endif } #pragma mark -- PUBLIC: Shared HID Remote -- @@ -254,6 +281,7 @@ typedef enum #pragma mark -- PUBLIC: System Information -- + (BOOL)isCandelairInstalled; + (BOOL)isCandelairInstallationRequiredForRemoteMode:(HIDRemoteMode)remoteMode; ++ (SInt32)OSXVersion; - (HIDRemoteAluminumRemoteSupportLevel)aluminiumRemoteSystemSupportLevel; #pragma mark -- PUBLIC: Interface / API -- @@ -261,6 +289,7 @@ typedef enum - (void)stopRemoteControl; - (BOOL)isStarted; +- (HIDRemoteMode)startedInMode; - (unsigned)activeRemoteControlCount; @@ -285,6 +314,9 @@ typedef enum - (void)setExclusiveLockLendingEnabled:(BOOL)newExclusiveLockLendingEnabled; - (BOOL)exclusiveLockLendingEnabled; +- (BOOL)isApplicationTerminating; +- (BOOL)isStopping; + #pragma mark -- PRIVATE: HID Event handling -- - (void)_handleButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict; - (void)_sendButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict; @@ -299,6 +331,8 @@ typedef enum #pragma mark -- PRIVATE: Distributed notifiations handling -- - (void)_postStatusWithAction:(NSString *)action; - (void)_handleNotifications:(NSNotification *)notification; +- (void)_setSendStatusNotifications:(BOOL)doSend; +- (BOOL)_sendStatusNotifications; #pragma mark -- PRIVATE: Application becomes active / inactive handling for kHIDRemoteModeExclusiveAuto -- - (void)_appStatusChanged:(NSNotification *)notification; @@ -336,6 +370,8 @@ extern NSString *kHIDRemoteDNHIDRemotePing; extern NSString *kHIDRemoteDNHIDRemoteRetry; extern NSString *kHIDRemoteDNHIDRemoteStatus; +extern NSString *kHIDRemoteDNHIDRemoteRetryGlobalObject; + #pragma mark -- Distributed notifications userInfo keys and values -- extern NSString *kHIDRemoteDNStatusHIDRemoteVersionKey; extern NSString *kHIDRemoteDNStatusPIDKey; diff --git a/tools/EventClients/Clients/OSXRemote/HIDRemote/HIDRemote.m b/tools/EventClients/Clients/OSXRemote/HIDRemote/HIDRemote.m index 31ba3be409..0f54b7553f 100644 --- a/tools/EventClients/Clients/OSXRemote/HIDRemote/HIDRemote.m +++ b/tools/EventClients/Clients/OSXRemote/HIDRemote/HIDRemote.m @@ -1,16 +1,16 @@ // // HIDRemote.m -// HIDRemote V1.1.1 (14th December 2009) +// HIDRemote V1.4 (18th February 2015) // // Created by Felix Schwarz on 06.04.07. -// Copyright 2007-2009 IOSPIRIT GmbH. All rights reserved. +// Copyright 2007-2015 IOSPIRIT GmbH. All rights reserved. // // The latest version of this class is available at // http://www.iospirit.com/developers/hidremote/ // // ** LICENSE ************************************************************************* // -// Copyright (c) 2007-2009 IOSPIRIT GmbH (http://www.iospirit.com/) +// Copyright (c) 2007-2014 IOSPIRIT GmbH (http://www.iospirit.com/) // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, @@ -75,11 +75,11 @@ static HIDRemote *sHIDRemote = nil; @implementation HIDRemote -#pragma mark -- Init, dealloc & shared instance -- +#pragma mark - Init, dealloc & shared instance + (HIDRemote *)sharedHIDRemote { - if (!sHIDRemote) + if (sHIDRemote==nil) { sHIDRemote = [[HIDRemote alloc] init]; } @@ -91,14 +91,21 @@ static HIDRemote *sHIDRemote = nil; { if ((self = [super init]) != nil) { + #ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING + _runOnThread = [[NSThread currentThread] retain]; + #endif + // Detect application becoming active/inactive [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:) name:NSApplicationDidBecomeActiveNotification object:[NSApplication sharedApplication]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:) name:NSApplicationWillResignActiveNotification object:[NSApplication sharedApplication]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:) name:NSApplicationWillTerminateNotification object:[NSApplication sharedApplication]]; // Handle distributed notifications + _pidString = [[NSString alloc] initWithFormat:@"%d", getpid()]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemotePing object:nil]; - [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteRetry object:[NSString stringWithFormat:@"%d", getpid()]]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteRetry object:kHIDRemoteDNHIDRemoteRetryGlobalObject]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteRetry object:_pidString]; // Enabled by default: simulate hold events for plus/minus _simulateHoldEvents = YES; @@ -112,6 +119,11 @@ static HIDRemote *sHIDRemote = nil; _lastSeenModel = kHIDRemoteModelUndetermined; _unusedButtonCodes = [[NSMutableArray alloc] init]; _exclusiveLockLending = NO; + _sendExclusiveResourceReuseNotification = YES; + _applicationIsTerminating = NO; + + // Send status notifications + _sendStatusNotifications = YES; } return (self); @@ -123,22 +135,35 @@ static HIDRemote *sHIDRemote = nil; [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillResignActiveNotification object:[NSApplication sharedApplication]]; [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationDidBecomeActiveNotification object:[NSApplication sharedApplication]]; - [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemotePing object:nil]; - [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteRetry object:[NSString stringWithFormat:@"%d", getpid()]]; - - [self stopRemoteControl]; + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemotePing object:nil]; + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteRetry object:kHIDRemoteDNHIDRemoteRetryGlobalObject]; + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteRetry object:_pidString]; + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:nil object:nil]; /* As demanded by the documentation for -[NSDistributedNotificationCenter removeObserver:name:object:] */ + [self stopRemoteControl]; + [self setExclusiveLockLendingEnabled:NO]; [self setDelegate:nil]; - - [_unusedButtonCodes release]; - _unusedButtonCodes = nil; + + if (_unusedButtonCodes != nil) + { + [_unusedButtonCodes release]; + _unusedButtonCodes = nil; + } + + #ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING + [_runOnThread release]; + _runOnThread = nil; + #endif + + [_pidString release]; + _pidString = nil; [super dealloc]; } -#pragma mark -- PUBLIC: System Information -- +#pragma mark - PUBLIC: System Information + (BOOL)isCandelairInstalled { mach_port_t masterPort = 0; @@ -147,7 +172,7 @@ static HIDRemote *sHIDRemote = nil; BOOL isInstalled = NO; kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort); - if (kernResult || !masterPort) { return(NO); } + if ((kernResult!=kIOReturnSuccess) || (masterPort==0)) { return(NO); } if ((matchingService = IOServiceGetMatchingService(masterPort, IOServiceMatching("IOSPIRITIRController"))) != 0) { @@ -162,36 +187,80 @@ static HIDRemote *sHIDRemote = nil; + (BOOL)isCandelairInstallationRequiredForRemoteMode:(HIDRemoteMode)remoteMode { - SInt32 systemVersion = 0; - // Determine OS version - if (Gestalt(gestaltSystemVersion, &systemVersion) == noErr) + switch ([self OSXVersion]) { - switch (systemVersion) - { - case 0x1060: // OS 10.6 - case 0x1061: // OS 10.6.1 - // OS X 10.6(.0) and OS X 10.6.1 require the Candelair driver for to be installed, - // so that third party apps can acquire an exclusive lock on the receiver HID Device - // via IOKit. + case 0x1060: // OS 10.6 + case 0x1061: // OS 10.6.1 + // OS X 10.6(.0) and OS X 10.6.1 require the Candelair driver for to be installed, + // so that third party apps can acquire an exclusive lock on the receiver HID Device + // via IOKit. - switch (remoteMode) - { - case kHIDRemoteModeExclusive: - case kHIDRemoteModeExclusiveAuto: - if (![self isCandelairInstalled]) - { - return (YES); - } - break; - } - break; - } + switch (remoteMode) + { + case kHIDRemoteModeExclusive: + case kHIDRemoteModeExclusiveAuto: + if (![self isCandelairInstalled]) + { + return (YES); + } + break; + + default: + return (NO); + break; + } + break; } return (NO); } +// Drop-in replacement for Gestalt(gestaltSystemVersion, &osXVersion) that avoids use of Gestalt for code targeting 10.10 or later ++ (SInt32)OSXVersion +{ + static SInt32 sHRGestaltOSXVersion = 0; + + if (sHRGestaltOSXVersion==0) + { + #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_9 + // Code for builds targeting OS X 10.10+ + NSOperatingSystemVersion osVersion; + + osVersion = [[NSProcessInfo processInfo] operatingSystemVersion]; + + sHRGestaltOSXVersion = (SInt32)(0x01000 | ((osVersion.majorVersion-10)<<8) | (osVersion.minorVersion<<4) | osVersion.patchVersion); + #else + #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_9 + // Code for builds using the OS X 10.10 SDK or later + NSOperatingSystemVersion osVersion; + + if ([[NSProcessInfo processInfo] respondsToSelector:@selector(operatingSystemVersion)]) + { + osVersion = [[NSProcessInfo processInfo] operatingSystemVersion]; + + sHRGestaltOSXVersion = (SInt32)(0x01000 | ((osVersion.majorVersion-10)<<8) | (osVersion.minorVersion<<4) | osVersion.patchVersion); + } + else + { + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + Gestalt (gestaltSystemVersion, &sHRGestaltOSXVersion); + #pragma clang diagnostic pop + } + #else /* MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_9 */ + // Code for builds using an SDK older than 10.10 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + Gestalt (gestaltSystemVersion, &sHRGestaltOSXVersion); + #pragma clang diagnostic pop + #endif /* MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_9 */ + #endif /* MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_9 */ + } + + return (sHRGestaltOSXVersion); +} + - (HIDRemoteAluminumRemoteSupportLevel)aluminiumRemoteSystemSupportLevel { HIDRemoteAluminumRemoteSupportLevel supportLevel = kHIDRemoteAluminumRemoteSupportLevelNone; @@ -206,7 +275,7 @@ static HIDRemote *sHIDRemote = nil; if ((deviceSupportLevel = [hidAttribsDict objectForKey:kHIDRemoteAluminumRemoteSupportLevel]) != nil) { - if ([deviceSupportLevel intValue] > supportLevel) + if ([deviceSupportLevel intValue] > (int)supportLevel) { supportLevel = [deviceSupportLevel intValue]; } @@ -216,7 +285,7 @@ static HIDRemote *sHIDRemote = nil; return (supportLevel); } -#pragma mark -- PUBLIC: Interface / API -- +#pragma mark - PUBLIC: Interface / API - (BOOL)startRemoteControl:(HIDRemoteMode)hidRemoteMode { if ((_mode == kHIDRemoteModeNone) && (hidRemoteMode != kHIDRemoteModeNone)) @@ -281,7 +350,7 @@ static HIDRemote *sHIDRemote = nil; // Setup serviceAttribMap _serviceAttribMap = [[NSMutableDictionary alloc] init]; - if (!_serviceAttribMap) { break; } + if (_serviceAttribMap==nil) { break; } // Phew .. everything went well! _mode = hidRemoteMode; @@ -291,12 +360,15 @@ static HIDRemote *sHIDRemote = nil; [self _postStatusWithAction:kHIDRemoteDNStatusActionStart]; + // Register for system wake notifications + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(_computerDidWake:) name:NSWorkspaceDidWakeNotification object:nil]; + return (YES); }while(0); // An error occured. Do necessary clean up. - if (matchDict) + if (matchDict!=NULL) { CFRelease(matchDict); matchDict = NULL; @@ -310,20 +382,23 @@ static HIDRemote *sHIDRemote = nil; - (void)stopRemoteControl { + UInt32 serviceCount = 0; + _autoRecover = NO; - - if (_autoRecoveryTimer) + _isStopping = YES; + + if (_autoRecoveryTimer!=nil) { [_autoRecoveryTimer invalidate]; [_autoRecoveryTimer release]; _autoRecoveryTimer = nil; } - if (_serviceAttribMap) + if (_serviceAttribMap!=nil) { NSDictionary *cloneDict = [[NSDictionary alloc] initWithDictionary:_serviceAttribMap]; - if (cloneDict) + if (cloneDict!=nil) { NSEnumerator *mapKeyEnum = [cloneDict keyEnumerator]; NSNumber *serviceValue; @@ -331,6 +406,7 @@ static HIDRemote *sHIDRemote = nil; while ((serviceValue = [mapKeyEnum nextObject]) != nil) { [self _destructService:(io_object_t)[serviceValue unsignedIntValue]]; + serviceCount++; }; [cloneDict release]; @@ -341,42 +417,73 @@ static HIDRemote *sHIDRemote = nil; _serviceAttribMap = nil; } - if (_matchingServicesIterator) + if (_matchingServicesIterator!=0) { IOObjectRelease((io_object_t) _matchingServicesIterator); _matchingServicesIterator = 0; } - if (_secureInputNotification) + if (_secureInputNotification!=0) { IOObjectRelease((io_object_t) _secureInputNotification); _secureInputNotification = 0; } - if (_notifyRLSource) + if (_notifyRLSource!=NULL) { CFRunLoopSourceInvalidate(_notifyRLSource); - _notifyRLSource = NULL; } - if (_notifyPort) + if (_notifyPort!=NULL) { IONotificationPortDestroy(_notifyPort); _notifyPort = NULL; } - if (_masterPort) + if (_masterPort!=0) { mach_port_deallocate(mach_task_self(), _masterPort); + _masterPort = 0; } - [self _postStatusWithAction:kHIDRemoteDNStatusActionStop]; + if (_returnToPID!=nil) + { + [_returnToPID release]; + _returnToPID = nil; + } - [_returnToPID release]; - _returnToPID = nil; + if (_mode!=kHIDRemoteModeNone) + { + // Post status + [self _postStatusWithAction:kHIDRemoteDNStatusActionStop]; + if (_sendStatusNotifications) + { + // In case we were not ready to lend it earlier, tell other HIDRemote apps that the resources (if any were used) are now again available for use by other applications + if (((_mode==kHIDRemoteModeExclusive) || (_mode==kHIDRemoteModeExclusiveAuto)) && (_sendExclusiveResourceReuseNotification==YES) && (_exclusiveLockLending==NO) && (serviceCount>0)) + { + _mode = kHIDRemoteModeNone; + + if (!_isRestarting) + { + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kHIDRemoteDNHIDRemoteRetry + object:kHIDRemoteDNHIDRemoteRetryGlobalObject + userInfo:[NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithUnsignedInt:(unsigned int)getpid()], kHIDRemoteDNStatusPIDKey, + [[NSBundle mainBundle] bundleIdentifier], (NSString *)kCFBundleIdentifierKey, + nil] + deliverImmediately:YES]; + } + } + } + + // Unregister from system wake notifications + [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceDidWakeNotification object:nil]; + } + _mode = kHIDRemoteModeNone; + _isStopping = NO; } - (BOOL)isStarted @@ -384,6 +491,11 @@ static HIDRemote *sHIDRemote = nil; return (_mode != kHIDRemoteModeNone); } +- (HIDRemoteMode)startedInMode +{ + return (_mode); +} + - (unsigned)activeRemoteControlCount { return ([_serviceAttribMap count]); @@ -439,7 +551,7 @@ static HIDRemote *sHIDRemote = nil; return (_delegate); } -#pragma mark -- PUBLIC: Expert APIs -- +#pragma mark - PUBLIC: Expert APIs - (void)setEnableSecureEventInputWorkaround:(BOOL)newEnableSecureEventInputWorkaround { _secureEventInputWorkAround = newEnableSecureEventInputWorkaround; @@ -475,12 +587,59 @@ static HIDRemote *sHIDRemote = nil; return (_exclusiveLockLending); } -#pragma mark -- PRIVATE: Application becomes active / inactive handling for kHIDRemoteModeExclusiveAuto -- +- (void)setSendExclusiveResourceReuseNotification:(BOOL)newSendExclusiveResourceReuseNotification +{ + _sendExclusiveResourceReuseNotification = newSendExclusiveResourceReuseNotification; +} + +- (BOOL)sendExclusiveResourceReuseNotification +{ + return (_sendExclusiveResourceReuseNotification); +} + +- (BOOL)isApplicationTerminating +{ + return (_applicationIsTerminating); +} + +- (BOOL)isStopping +{ + return (_isStopping); +} + +#pragma mark - PRIVATE: Application becomes active / inactive handling for kHIDRemoteModeExclusiveAuto - (void)_appStatusChanged:(NSNotification *)notification { - if (notification) + #ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING + if ([self respondsToSelector:@selector(performSelector:onThread:withObject:waitUntilDone:)]) // OS X 10.5+ only { - if (_autoRecoveryTimer) + if ([NSThread currentThread] != _runOnThread) + { + if ([[notification name] isEqual:NSApplicationDidBecomeActiveNotification]) + { + if (!_autoRecover) + { + return; + } + } + + if ([[notification name] isEqual:NSApplicationWillResignActiveNotification]) + { + if (_mode != kHIDRemoteModeExclusiveAuto) + { + return; + } + } + + [self performSelector:@selector(_appStatusChanged:) onThread:_runOnThread withObject:notification waitUntilDone:[[notification name] isEqual:NSApplicationWillTerminateNotification]]; + return; + } + } + #endif + + if (notification!=nil) + { + if (_autoRecoveryTimer!=nil) { [_autoRecoveryTimer invalidate]; [_autoRecoveryTimer release]; @@ -513,6 +672,8 @@ static HIDRemote *sHIDRemote = nil; if ([[notification name] isEqual:NSApplicationWillTerminateNotification]) { + _applicationIsTerminating = YES; + if ([self isStarted]) { [self stopRemoteControl]; @@ -535,29 +696,43 @@ static HIDRemote *sHIDRemote = nil; } -#pragma mark -- PRIVATE: Distributed notifiations handling -- +#pragma mark - PRIVATE: Distributed notifiations handling - (void)_postStatusWithAction:(NSString *)action { - [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kHIDRemoteDNHIDRemoteStatus - object:[NSString stringWithFormat:@"%d",getpid()] - userInfo:[NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInt:1], kHIDRemoteDNStatusHIDRemoteVersionKey, - [NSNumber numberWithUnsignedInt:(unsigned int)getpid()], kHIDRemoteDNStatusPIDKey, - [NSNumber numberWithInt:(int)_mode], kHIDRemoteDNStatusModeKey, - [NSNumber numberWithUnsignedInt:(unsigned int)[self activeRemoteControlCount]], kHIDRemoteDNStatusRemoteControlCountKey, - ((_unusedButtonCodes!=nil) ? _unusedButtonCodes : [NSArray array]), kHIDRemoteDNStatusUnusedButtonCodesKey, - action, kHIDRemoteDNStatusActionKey, - [[NSBundle mainBundle] bundleIdentifier], (NSString *)kCFBundleIdentifierKey, - _returnToPID, kHIDRemoteDNStatusReturnToPIDKey, - nil] - deliverImmediately:YES - ]; + if (_sendStatusNotifications) + { + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kHIDRemoteDNHIDRemoteStatus + object:((_pidString!=nil) ? _pidString : [NSString stringWithFormat:@"%d",getpid()]) + userInfo:[NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt:1], kHIDRemoteDNStatusHIDRemoteVersionKey, + [NSNumber numberWithUnsignedInt:(unsigned int)getpid()], kHIDRemoteDNStatusPIDKey, + [NSNumber numberWithInt:(int)_mode], kHIDRemoteDNStatusModeKey, + [NSNumber numberWithUnsignedInt:(unsigned int)[self activeRemoteControlCount]], kHIDRemoteDNStatusRemoteControlCountKey, + ((_unusedButtonCodes!=nil) ? _unusedButtonCodes : [NSArray array]), kHIDRemoteDNStatusUnusedButtonCodesKey, + action, kHIDRemoteDNStatusActionKey, + [[NSBundle mainBundle] bundleIdentifier], (NSString *)kCFBundleIdentifierKey, + _returnToPID, kHIDRemoteDNStatusReturnToPIDKey, + nil] + deliverImmediately:YES + ]; + } } - (void)_handleNotifications:(NSNotification *)notification { NSString *notificationName; + #ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING + if ([self respondsToSelector:@selector(performSelector:onThread:withObject:waitUntilDone:)]) // OS X 10.5+ only + { + if ([NSThread currentThread] != _runOnThread) + { + [self performSelector:@selector(_handleNotifications:) onThread:_runOnThread withObject:notification waitUntilDone:NO]; + return; + } + } + #endif + if ((notification!=nil) && ((notificationName = [notification name]) != nil)) { if ([notificationName isEqual:kHIDRemoteDNHIDRemotePing]) @@ -570,11 +745,28 @@ static HIDRemote *sHIDRemote = nil; if ([self isStarted]) { BOOL retry = YES; - - if (([self delegate] != nil) && - ([[self delegate] respondsToSelector:@selector(hidRemote:shouldRetryExclusiveLockWithInfo:)])) + + // Ignore our own global retry broadcasts + if ([[notification object] isEqual:kHIDRemoteDNHIDRemoteRetryGlobalObject]) + { + NSNumber *fromPID; + + if ((fromPID = [[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey]) != nil) + { + if (getpid() == (int)[fromPID unsignedIntValue]) + { + retry = NO; + } + } + } + + if (retry) { - retry = [[self delegate] hidRemote:self shouldRetryExclusiveLockWithInfo:[notification userInfo]]; + if (([self delegate] != nil) && + ([[self delegate] respondsToSelector:@selector(hidRemote:shouldRetryExclusiveLockWithInfo:)])) + { + retry = [[self delegate] hidRemote:self shouldRetryExclusiveLockWithInfo:[notification userInfo]]; + } } if (retry) @@ -583,10 +775,14 @@ static HIDRemote *sHIDRemote = nil; if (restartInMode != kHIDRemoteModeNone) { + _isRestarting = YES; [self stopRemoteControl]; [_returnToPID release]; + _returnToPID = nil; + [self startRemoteControl:restartInMode]; + _isRestarting = NO; if (restartInMode != kHIDRemoteModeShared) { @@ -615,7 +811,7 @@ static HIDRemote *sHIDRemote = nil; if ((action = [[notification userInfo] objectForKey:kHIDRemoteDNStatusActionKey]) != nil) { - if ((_mode == kHIDRemoteModeNone) && _waitForReturnByPID) + if ((_mode == kHIDRemoteModeNone) && (_waitForReturnByPID!=nil)) { NSNumber *pidNumber, *returnToPIDNumber; @@ -706,7 +902,17 @@ static HIDRemote *sHIDRemote = nil; } } -#pragma mark -- PRIVATE: Service setup and destruction -- +- (void)_setSendStatusNotifications:(BOOL)doSend +{ + _sendStatusNotifications = doSend; +} + +- (BOOL)_sendStatusNotifications +{ + return (_sendStatusNotifications); +} + +#pragma mark - PRIVATE: Service setup and destruction - (BOOL)_prematchService:(io_object_t)service { BOOL serviceMatches = NO; @@ -972,7 +1178,7 @@ static HIDRemote *sHIDRemote = nil; } returnCode = (*hidQueueInterface)->create(hidQueueInterface, 0, 32); - if ((returnCode != kIOReturnSuccess) || (hidElements==NULL)) + if (returnCode != kIOReturnSuccess) { error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil]; errorCode = 6; @@ -1020,7 +1226,7 @@ static HIDRemote *sHIDRemote = nil; usagePage = (NSNumber *) CFDictionaryGetValue(hidDict, CFSTR(kIOHIDElementUsagePageKey)); cookie = (NSNumber *) CFDictionaryGetValue(hidDict, CFSTR(kIOHIDElementCookieKey)); - if (usage && usagePage && cookie) + if ((usage!=nil) && (usagePage!=nil) && (cookie!=nil)) { // Find the button codes for the ID combos buttonCode = [self buttonCodeForUsage:[usage unsignedIntValue] usagePage:[usagePage unsignedIntValue]]; @@ -1051,6 +1257,13 @@ static HIDRemote *sHIDRemote = nil; (*hidQueueInterface)->addElement(hidQueueInterface, (IOHIDElementCookie) [cookie unsignedIntValue], 0); + + #ifdef _HIDREMOTE_EXTENSIONS + // Get current Apple Remote ID value + #define _HIDREMOTE_EXTENSIONS_SECTION 7 + #include "HIDRemoteAdditions.h" + #undef _HIDREMOTE_EXTENSIONS_SECTION + #endif /* _HIDREMOTE_EXTENSIONS */ [buttonCodeNumber release]; [pairString release]; @@ -1242,22 +1455,17 @@ static HIDRemote *sHIDRemote = nil; { if ([(NSString *)ioKitClassName isEqual:@"AppleIRController"]) { - SInt32 systemVersion; - - if (Gestalt(gestaltSystemVersion, &systemVersion) == noErr) + if ([HIDRemote OSXVersion] >= 0x1062) { - if (systemVersion >= 0x1062) - { - // Support for the Aluminum Remote was added only with OS 10.6.2. Previous versions can not distinguish - // between the Center and the new, seperate Play/Pause button. They'll recognize both as presses of the - // "Center" button. - // - // You CAN, however, receive Aluminum Remote button presses even under OS 10.5 when using Remote Buddy's - // Virtual Remote. While Remote Buddy does support the Aluminum Remote across all OS releases it runs on, - // its Virtual Remote can only emulate Aluminum Remote button presses under OS 10.5 and up in order not to - // break compatibility with applications whose IR Remote code relies on driver internals. [13-Nov-09] - supportLevel = kHIDRemoteAluminumRemoteSupportLevelNative; - } + // Support for the Aluminum Remote was added only with OS 10.6.2. Previous versions can not distinguish + // between the Center and the new, seperate Play/Pause button. They'll recognize both as presses of the + // "Center" button. + // + // You CAN, however, receive Aluminum Remote button presses even under OS 10.5 when using Remote Buddy's + // Virtual Remote. While Remote Buddy does support the Aluminum Remote across all OS releases it runs on, + // its Virtual Remote can only emulate Aluminum Remote button presses under OS 10.5 and up in order not to + // break compatibility with applications whose IR Remote code relies on driver internals. [13-Nov-09] + supportLevel = kHIDRemoteAluminumRemoteSupportLevelNative; } } @@ -1291,7 +1499,7 @@ static HIDRemote *sHIDRemote = nil; if (([self delegate]!=nil) && ([[self delegate] respondsToSelector:@selector(hidRemote:failedNewHardwareWithError:)])) { - if (error) + if (error!=nil) { error = [NSError errorWithDomain:[error domain] code:[error code] @@ -1303,19 +1511,19 @@ static HIDRemote *sHIDRemote = nil; } // An error occured or this device is not of interest .. cleanup .. - if (serviceNotification) + if (serviceNotification!=0) { IOObjectRelease(serviceNotification); serviceNotification = 0; } - if (queueEventSource) + if (queueEventSource!=NULL) { CFRunLoopSourceInvalidate(queueEventSource); queueEventSource=NULL; } - if (hidQueueInterface) + if (hidQueueInterface!=NULL) { if (queueStarted) { @@ -1326,19 +1534,19 @@ static HIDRemote *sHIDRemote = nil; hidQueueInterface = NULL; } - if (hidAttribsDict) + if (hidAttribsDict!=nil) { [hidAttribsDict release]; hidAttribsDict = nil; } - if (hidElements) + if (hidElements!=NULL) { CFRelease(hidElements); hidElements = NULL; } - if (hidDeviceInterface) + if (hidDeviceInterface!=NULL) { if (opened) { @@ -1349,7 +1557,7 @@ static HIDRemote *sHIDRemote = nil; hidDeviceInterface = NULL; } - if (cfPluginInterface) + if (cfPluginInterface!=NULL) { IODestroyPlugInInterface(cfPluginInterface); cfPluginInterface = NULL; @@ -1370,7 +1578,7 @@ static HIDRemote *sHIDRemote = nil; serviceDict = [_serviceAttribMap objectForKey:serviceValue]; - if (serviceDict) + if (serviceDict!=nil) { IOHIDDeviceInterface122 **hidDeviceInterface = NULL; IOCFPlugInInterface **cfPluginInterface = NULL; @@ -1410,24 +1618,24 @@ static HIDRemote *sHIDRemote = nil; [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self releasedHardwareWithAttributes:serviceDict]; } - if (simulateHoldTimer) + if (simulateHoldTimer!=nil) { [simulateHoldTimer invalidate]; } - if (serviceNotification) + if (serviceNotification!=0) { IOObjectRelease(serviceNotification); } - if (queueEventSource) + if (queueEventSource!=NULL) { CFRunLoopRemoveSource( CFRunLoopGetCurrent(), queueEventSource, kCFRunLoopCommonModes); } - if (hidQueueInterface && cookieButtonMap) + if ((hidQueueInterface!=NULL) && (cookieButtonMap!=nil)) { NSEnumerator *cookieEnum = [cookieButtonMap keyEnumerator]; NSNumber *cookie; @@ -1442,25 +1650,25 @@ static HIDRemote *sHIDRemote = nil; }; } - if (hidQueueInterface) + if (hidQueueInterface!=NULL) { (*hidQueueInterface)->stop(hidQueueInterface); (*hidQueueInterface)->dispose(hidQueueInterface); (*hidQueueInterface)->Release(hidQueueInterface); } - if (hidDeviceInterface) + if (hidDeviceInterface!=NULL) { (*hidDeviceInterface)->close(hidDeviceInterface); (*hidDeviceInterface)->Release(hidDeviceInterface); } - if (cfPluginInterface) + if (cfPluginInterface!=NULL) { IODestroyPlugInInterface(cfPluginInterface); } - if (theService) + if (theService!=0) { IOObjectRelease(theService); } @@ -1470,7 +1678,7 @@ static HIDRemote *sHIDRemote = nil; } -#pragma mark -- PRIVATE: HID Event handling -- +#pragma mark - PRIVATE: HID Event handling - (void)_simulateHoldEvent:(NSTimer *)aTimer { NSMutableDictionary *hidAttribsDict; @@ -1530,14 +1738,14 @@ static HIDRemote *sHIDRemote = nil; shTimer = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsTimer]; shButtonCode = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsOriginButtonCode]; - if (shTimer && shButtonCode) + if ((shTimer!=nil) && (shButtonCode!=nil)) { [self _sendButtonCode:(HIDRemoteButtonCode)[shButtonCode unsignedIntValue] isPressed:YES hidAttribsDict:hidAttribsDict]; [self _sendButtonCode:(HIDRemoteButtonCode)[shButtonCode unsignedIntValue] isPressed:NO hidAttribsDict:hidAttribsDict]; } else { - if (shButtonCode) + if (shButtonCode!=nil) { [self _sendButtonCode:(((HIDRemoteButtonCode)[shButtonCode unsignedIntValue])|kHIDRemoteButtonCodeHoldMask) isPressed:NO hidAttribsDict:hidAttribsDict]; } @@ -1605,9 +1813,9 @@ static HIDRemote *sHIDRemote = nil; - (void)_hidEventFor:(io_service_t)hidDevice from:(IOHIDQueueInterface **)interface withResult:(IOReturn)result { - NSMutableDictionary *hidAttribsDict = [_serviceAttribMap objectForKey:[NSNumber numberWithUnsignedInt:(unsigned int)hidDevice]]; + NSMutableDictionary *hidAttribsDict = [[[_serviceAttribMap objectForKey:[NSNumber numberWithUnsignedInt:(unsigned int)hidDevice]] retain] autorelease]; - if (hidAttribsDict) + if (hidAttribsDict!=nil) { IOHIDQueueInterface **queueInterface = NULL; @@ -1647,7 +1855,7 @@ static HIDRemote *sHIDRemote = nil; #undef _HIDREMOTE_EXTENSIONS_SECTION #endif /* _HIDREMOTE_EXTENSIONS */ - if (buttonCodeNumber) + if (buttonCodeNumber!=nil) { HIDRemoteButtonCode buttonCode = [buttonCodeNumber unsignedIntValue]; @@ -1699,7 +1907,7 @@ static HIDRemote *sHIDRemote = nil; } } -#pragma mark -- PRIVATE: Notification handling -- +#pragma mark - PRIVATE: Notification handling - (void)_serviceMatching:(io_iterator_t)iterator { io_object_t matchingService = 0; @@ -1725,6 +1933,8 @@ static HIDRemote *sHIDRemote = nil; NSArray *consoleUsersArray; io_service_t rootService; + if (_masterPort==0) { return; } + if ((rootService = IORegistryGetRootEntry(_masterPort)) != 0) { if ((consoleUsersArray = (NSArray *)IORegistryEntryCreateCFProperty((io_registry_entry_t)rootService, CFSTR("IOConsoleUsers"), kCFAllocatorDefault, 0)) != nil) @@ -1737,6 +1947,7 @@ static HIDRemote *sHIDRemote = nil; { UInt64 secureEventInputPIDSum = 0; uid_t frontUserSession = 0; + BOOL screenIsLocked = NO; NSDictionary *consoleUserDict; while ((consoleUserDict = [consoleUsersEnum nextObject]) != nil) @@ -1746,6 +1957,7 @@ static HIDRemote *sHIDRemote = nil; NSNumber *secureInputPID; NSNumber *onConsole; NSNumber *userID; + NSNumber *screenIsLockedBool; if ((secureInputPID = [consoleUserDict objectForKey:@"kCGSSessionSecureInputPID"]) != nil) { @@ -1766,11 +1978,20 @@ static HIDRemote *sHIDRemote = nil; } } } + + if ((screenIsLockedBool = [consoleUserDict objectForKey:@"CGSSessionScreenIsLocked"]) != nil) + { + if ([screenIsLockedBool isKindOfClass:[NSNumber class]]) + { + screenIsLocked = [screenIsLockedBool boolValue]; + } + } } } _lastSecureEventInputPIDSum = secureEventInputPIDSum; _lastFrontUserSession = frontUserSession; + _lastScreenIsLocked = screenIsLocked; } } @@ -1781,31 +2002,77 @@ static HIDRemote *sHIDRemote = nil; } } +- (void)_silentRestart +{ + if ((_mode == kHIDRemoteModeExclusive) || (_mode == kHIDRemoteModeExclusiveAuto)) + { + HIDRemoteMode restartInMode = _mode; + unsigned checkActiveRemoteControlCount = [self activeRemoteControlCount]; + + // Only restart when we already have active remote controls - to avoid race conditions with other applications using kHIDRemoteModeExclusive mode (new in V1.2.1) + if (checkActiveRemoteControlCount > 0) + { + _isRestarting = YES; + [self stopRemoteControl]; + [self startRemoteControl:restartInMode]; + _isRestarting = NO; + + // Check whether we lost a remote control due to restarting/secure input change notification handling (new in V1.2.1) + if (checkActiveRemoteControlCount != [self activeRemoteControlCount]) + { + // Log message + NSLog(@"Lost access (mode %d) to %d IR Remote Receiver(s) after handling SecureInput change notification - please quit other apps trying to use the Remote exclusively", restartInMode, checkActiveRemoteControlCount); + } + } + } +} + - (void)_secureInputNotificationFor:(io_service_t)service messageType:(natural_t)messageType messageArgument:(void *)messageArgument { if (messageType == kIOMessageServiceBusyStateChange) { UInt64 old_lastSecureEventInputPIDSum = _lastSecureEventInputPIDSum; uid_t old_lastFrontUserSession = _lastFrontUserSession; + BOOL old_lastScreenIsLocked = _lastScreenIsLocked; [self _updateSessionInformation]; - if (((old_lastSecureEventInputPIDSum != _lastSecureEventInputPIDSum) || (old_lastFrontUserSession != _lastFrontUserSession)) && _secureEventInputWorkAround) + if (((old_lastSecureEventInputPIDSum != _lastSecureEventInputPIDSum) || + (old_lastFrontUserSession != _lastFrontUserSession) || + (old_lastScreenIsLocked != _lastScreenIsLocked)) && _secureEventInputWorkAround) { - if ((_mode == kHIDRemoteModeExclusive) || (_mode == kHIDRemoteModeExclusiveAuto)) + [self _silentRestart]; + } + } +} + +- (void)_computerDidWake:(NSNotification *)aNotification +{ + // Work around for a bug in 10.8, where exclusive connections may be degraded to shared connections after a sleep/wakeup cycle (credit: Paul Duggan from Galaxy Software) + #ifdef NSAppKitVersionNumber10_8 + if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_8) + #else + if (NSAppKitVersionNumber >= 1187) + #endif + { + #ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING + if ([self respondsToSelector:@selector(performSelector:onThread:withObject:waitUntilDone:)]) // OS X 10.5+ only + { + if ([NSThread currentThread] != _runOnThread) { - HIDRemoteMode restartInMode = _mode; - - [self stopRemoteControl]; - [self startRemoteControl:restartInMode]; + [self performSelector:@selector(_computerDidWake:) onThread:_runOnThread withObject:aNotification waitUntilDone:NO]; + return; } } + #endif + + [self _silentRestart]; } } @end -#pragma mark -- PRIVATE: IOKitLib Callbacks -- +#pragma mark - PRIVATE: IOKitLib Callbacks static void HIDEventCallback( void * target, IOReturn result, @@ -1885,6 +2152,8 @@ NSString *kHIDRemoteDNHIDRemotePing = @"com.candelair.ping"; NSString *kHIDRemoteDNHIDRemoteRetry = @"com.candelair.retry"; NSString *kHIDRemoteDNHIDRemoteStatus = @"com.candelair.status"; +NSString *kHIDRemoteDNHIDRemoteRetryGlobalObject = @"global"; + // Distributed notifications userInfo keys and values NSString *kHIDRemoteDNStatusHIDRemoteVersionKey = @"HIDRemoteVersion"; NSString *kHIDRemoteDNStatusPIDKey = @"PID"; @@ -1897,4 +2166,3 @@ NSString *kHIDRemoteDNStatusActionStart = @"start"; NSString *kHIDRemoteDNStatusActionStop = @"stop"; NSString *kHIDRemoteDNStatusActionUpdate = @"update"; NSString *kHIDRemoteDNStatusActionNoNeed = @"noneed"; - |