1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188 |
- //
- // HIDRemote.m
- // HIDRemote V1.6 (27th September 2017)
- //
- // Created by Felix Schwarz on 06.04.07.
- // Copyright 2007-2017 IOSPIRIT GmbH. All rights reserved.
- //
- // The latest version of this class is available at
- // http://www.iospirit.com/developers/hidremote/
- //
- // ** LICENSE *************************************************************************
- //
- // Copyright (c) 2007-2017 IOSPIRIT GmbH (http://www.iospirit.com/)
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without modification,
- // are permitted provided that the following conditions are met:
- //
- // * Redistributions of source code must retain the above copyright notice, this list
- // of conditions and the following disclaimer.
- //
- // * Redistributions in binary form must reproduce the above copyright notice, this
- // list of conditions and the following disclaimer in the documentation and/or other
- // materials provided with the distribution.
- //
- // * Neither the name of IOSPIRIT GmbH nor the names of its contributors may be used to
- // endorse or promote products derived from this software without specific prior
- // written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
- // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- // BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
- // DAMAGE.
- //
- // ************************************************************************************
- // ************************************************************************************
- // ********************************** DOCUMENTATION ***********************************
- // ************************************************************************************
- //
- // - a reference is available at http://www.iospirit.com/developers/hidremote/reference/
- // - for a guide, please see http://www.iospirit.com/developers/hidremote/guide/
- //
- // ************************************************************************************
- #import "HIDRemote.h"
- // ARC support
- #if !__has_feature(objc_arc)
- #define HIDRemoteRetain(object) [object retain]
- #define HIDRemoteRetained(object) [object retain]
- #define HIDRemoteRelease(object) [object release]
- #define HIDRemoteReleaseNil(object) [object release]; object=nil
- #define HIDRemoteAutoreleased(object) [object autorelease]
- #define HIDRemoteSuperDealloc(object) [super dealloc]
- #define HIDRemoteAutoreleasePoolOpen() NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- #define HIDRemoteAutoreleasePoolClose() [pool release];
- #define __HIDRemoteBridge
- #define HIDRemoteBridgingRelease
- #define HIDRemoteBridgingRetain
- #else /* !__has_feature(objc_arc) */
- #define HIDRemoteRetain(object)
- #define HIDRemoteRetained(object) object
- #define HIDRemoteRelease(object)
- #define HIDRemoteReleaseNil(object) object=nil
- #define HIDRemoteAutoreleased(object) object
- #define HIDRemoteSuperDealloc(object)
- #define HIDRemoteAutoreleasePoolOpen() @autoreleasepool {
- #define HIDRemoteAutoreleasePoolClose() }
- #define __HIDRemoteBridge __bridge
- #define HIDRemoteBridgingRelease CFBridgingRelease
- #define HIDRemoteBridgingRetain CFBridgingRetain
- #endif
- // Callback Prototypes
- static void HIDEventCallback( void * target,
- IOReturn result,
- void * refcon,
- void * sender);
- static void ServiceMatchingCallback( void *refCon,
- io_iterator_t iterator);
- static void ServiceNotificationCallback(void * refCon,
- io_service_t service,
- natural_t messageType,
- void * messageArgument);
- static void SecureInputNotificationCallback( void * refCon,
- io_service_t service,
- natural_t messageType,
- void * messageArgument);
- // Shared HIDRemote instance
- static HIDRemote *sHIDRemote = nil;
- @implementation HIDRemote
- #pragma mark - Init, dealloc & shared instance
- + (HIDRemote *)sharedHIDRemote
- {
- if (sHIDRemote==nil)
- {
- sHIDRemote = [[HIDRemote alloc] init];
- }
- return (sHIDRemote);
- }
- - (id)init
- {
- if ((self = [super init]) != nil)
- {
- #if HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING
- _runOnThread = HIDRemoteRetained([NSThread currentThread]);
- #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:kHIDRemoteDNHIDRemoteRetryGlobalObject];
- [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteRetry object:_pidString];
- // Enabled by default: simulate hold events for plus/minus
- _simulateHoldEvents = YES;
- // 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)
- _secureEventInputWorkAround = YES;
- _secureInputNotification = 0;
- // Initialize instance variables
- _lastSeenRemoteID = -1;
- _lastSeenModel = kHIDRemoteModelUndetermined;
- _unusedButtonCodes = [[NSMutableArray alloc] init];
- _exclusiveLockLending = NO;
- _sendExclusiveResourceReuseNotification = YES;
- _applicationIsTerminating = NO;
- // Send status notifications
- _sendStatusNotifications = YES;
- }
- return (self);
- }
- - (void)dealloc
- {
- [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:[NSApplication sharedApplication]];
- [[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: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];
- if (_unusedButtonCodes != nil)
- {
- HIDRemoteReleaseNil(_unusedButtonCodes);
- }
- #if HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING
- HIDRemoteReleaseNil(_runOnThread);
- #endif
- HIDRemoteReleaseNil(_pidString);
- HIDRemoteSuperDealloc();
- }
- #pragma mark - PUBLIC: System Information
- + (BOOL)isCandelairInstalled
- {
- mach_port_t masterPort = 0;
- kern_return_t kernResult;
- io_service_t matchingService = 0;
- BOOL isInstalled = NO;
- kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
- if ((kernResult!=kIOReturnSuccess) || (masterPort==0)) { return(NO); }
- if ((matchingService = IOServiceGetMatchingService(masterPort, IOServiceMatching("IOSPIRITIRController"))) != 0)
- {
- isInstalled = YES;
- IOObjectRelease((io_object_t) matchingService);
- }
- mach_port_deallocate(mach_task_self(), masterPort);
- return (isInstalled);
- }
- + (BOOL)isCandelairInstallationRequiredForRemoteMode:(HIDRemoteMode)remoteMode
- {
- // Determine OS version
- switch ([self OSXVersion])
- {
- 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;
- 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;
- NSEnumerator *attribDictsEnum;
- NSDictionary *hidAttribsDict;
- attribDictsEnum = [_serviceAttribMap objectEnumerator];
- while ((hidAttribsDict = [attribDictsEnum nextObject]) != nil)
- {
- NSNumber *deviceSupportLevel;
- if ((deviceSupportLevel = [hidAttribsDict objectForKey:kHIDRemoteAluminumRemoteSupportLevel]) != nil)
- {
- if ([deviceSupportLevel intValue] > (int)supportLevel)
- {
- supportLevel = [deviceSupportLevel intValue];
- }
- }
- }
- return (supportLevel);
- }
- #pragma mark - PUBLIC: Interface / API
- - (BOOL)startRemoteControl:(HIDRemoteMode)hidRemoteMode
- {
- if ((_mode == kHIDRemoteModeNone) && (hidRemoteMode != kHIDRemoteModeNone))
- {
- kern_return_t kernReturn;
- CFMutableDictionaryRef matchDict=NULL;
- io_service_t rootService;
- do
- {
- // Get IOKit master port
- kernReturn = IOMasterPort(bootstrap_port, &_masterPort);
- if ((kernReturn!=kIOReturnSuccess) || (_masterPort==0)) { break; }
- // Setup notification port
- _notifyPort = IONotificationPortCreate(_masterPort);
- if ((_notifyRLSource = IONotificationPortGetRunLoopSource(_notifyPort)) != NULL)
- {
- CFRunLoopAddSource( CFRunLoopGetCurrent(),
- _notifyRLSource,
- kCFRunLoopCommonModes);
- }
- else
- {
- break;
- }
- // Setup SecureInput notification
- if ((hidRemoteMode == kHIDRemoteModeExclusive) || (hidRemoteMode == kHIDRemoteModeExclusiveAuto))
- {
- if ((rootService = IORegistryEntryFromPath(_masterPort, kIOServicePlane ":/")) != 0)
- {
- kernReturn = IOServiceAddInterestNotification( _notifyPort,
- rootService,
- kIOBusyInterest,
- SecureInputNotificationCallback,
- (__HIDRemoteBridge void *)self,
- &_secureInputNotification);
- if (kernReturn != kIOReturnSuccess) { break; }
- [self _updateSessionInformation];
- }
- else
- {
- break;
- }
- }
- // Setup notification matching dict
- matchDict = IOServiceMatching(kIOHIDDeviceKey);
- CFRetain(matchDict);
- // Actually add notification
- kernReturn = IOServiceAddMatchingNotification( _notifyPort,
- kIOFirstMatchNotification,
- matchDict, // one reference count consumed by this call
- ServiceMatchingCallback,
- (__HIDRemoteBridge void *) self,
- &_matchingServicesIterator);
- if (kernReturn != kIOReturnSuccess) { break; }
- // Setup serviceAttribMap
- _serviceAttribMap = [[NSMutableDictionary alloc] init];
- if (_serviceAttribMap==nil) { break; }
- // Phew .. everything went well!
- _mode = hidRemoteMode;
- CFRelease(matchDict);
- [self _serviceMatching:_matchingServicesIterator];
- [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!=NULL)
- {
- CFRelease(matchDict);
- matchDict = NULL;
- }
- [self stopRemoteControl];
- }
- return (NO);
- }
- - (void)stopRemoteControl
- {
- UInt32 serviceCount = 0;
- _autoRecover = NO;
- _isStopping = YES;
- if (_autoRecoveryTimer!=nil)
- {
- [_autoRecoveryTimer invalidate];
- HIDRemoteReleaseNil(_autoRecoveryTimer);
- }
- if (_serviceAttribMap!=nil)
- {
- NSDictionary *cloneDict = [[NSDictionary alloc] initWithDictionary:_serviceAttribMap];
- if (cloneDict!=nil)
- {
- NSEnumerator *mapKeyEnum = [cloneDict keyEnumerator];
- NSNumber *serviceValue;
- while ((serviceValue = [mapKeyEnum nextObject]) != nil)
- {
- [self _destructService:(io_object_t)[serviceValue unsignedIntValue]];
- serviceCount++;
- };
- HIDRemoteReleaseNil(cloneDict);
- }
- HIDRemoteReleaseNil(_serviceAttribMap);
- }
- if (_matchingServicesIterator!=0)
- {
- IOObjectRelease((io_object_t) _matchingServicesIterator);
- _matchingServicesIterator = 0;
- }
- if (_secureInputNotification!=0)
- {
- IOObjectRelease((io_object_t) _secureInputNotification);
- _secureInputNotification = 0;
- }
- if (_notifyRLSource!=NULL)
- {
- CFRunLoopSourceInvalidate(_notifyRLSource);
- _notifyRLSource = NULL;
- }
- if (_notifyPort!=NULL)
- {
- IONotificationPortDestroy(_notifyPort);
- _notifyPort = NULL;
- }
- if (_masterPort!=0)
- {
- mach_port_deallocate(mach_task_self(), _masterPort);
- _masterPort = 0;
- }
- if (_returnToPID!=nil)
- {
- HIDRemoteReleaseNil(_returnToPID);
- }
- 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
- {
- return (_mode != kHIDRemoteModeNone);
- }
- - (HIDRemoteMode)startedInMode
- {
- return (_mode);
- }
- - (unsigned)activeRemoteControlCount
- {
- return ((unsigned)[_serviceAttribMap count]);
- }
- - (SInt32)lastSeenRemoteControlID
- {
- return (_lastSeenRemoteID);
- }
- - (HIDRemoteModel)lastSeenModel
- {
- return (_lastSeenModel);
- }
- - (void)setLastSeenModel:(HIDRemoteModel)aModel
- {
- _lastSeenModel = aModel;
- }
- - (void)setSimulateHoldEvents:(BOOL)newSimulateHoldEvents
- {
- _simulateHoldEvents = newSimulateHoldEvents;
- }
- - (BOOL)simulateHoldEvents
- {
- return (_simulateHoldEvents);
- }
- - (NSArray *)unusedButtonCodes
- {
- return (_unusedButtonCodes);
- }
- - (void)setUnusedButtonCodes:(NSArray *)newArrayWithUnusedButtonCodesAsNSNumbers
- {
- HIDRemoteRetain(newArrayWithUnusedButtonCodesAsNSNumbers);
- HIDRemoteRelease(_unusedButtonCodes);
- _unusedButtonCodes = newArrayWithUnusedButtonCodesAsNSNumbers;
- [self _postStatusWithAction:kHIDRemoteDNStatusActionUpdate];
- }
- - (void)setDelegate:(NSObject <HIDRemoteDelegate> *)newDelegate
- {
- _delegate = newDelegate;
- }
- - (NSObject <HIDRemoteDelegate> *)delegate
- {
- return (_delegate);
- }
- #pragma mark - PUBLIC: Expert APIs
- - (void)setEnableSecureEventInputWorkaround:(BOOL)newEnableSecureEventInputWorkaround
- {
- _secureEventInputWorkAround = newEnableSecureEventInputWorkaround;
- }
- - (BOOL)enableSecureEventInputWorkaround
- {
- return (_secureEventInputWorkAround);
- }
- - (void)setExclusiveLockLendingEnabled:(BOOL)newExclusiveLockLendingEnabled
- {
- if (newExclusiveLockLendingEnabled != _exclusiveLockLending)
- {
- _exclusiveLockLending = newExclusiveLockLendingEnabled;
- if (_exclusiveLockLending)
- {
- [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteStatus object:nil];
- }
- else
- {
- [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteStatus object:nil];
- HIDRemoteReleaseNil(_waitForReturnByPID);
- }
- }
- }
- - (BOOL)exclusiveLockLendingEnabled
- {
- return (_exclusiveLockLending);
- }
- - (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 HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING
- if ([self respondsToSelector:@selector(performSelector:onThread:withObject:waitUntilDone:)]) // OS X 10.5+ only
- {
- 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];
- HIDRemoteReleaseNil(_autoRecoveryTimer);
- }
- if ([[notification name] isEqual:NSApplicationDidBecomeActiveNotification])
- {
- if (_autoRecover)
- {
- // Delay autorecover by 0.1 to avoid race conditions
- if ((_autoRecoveryTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:0.1] interval:0.1 target:self selector:@selector(_delayedAutoRecovery:) userInfo:nil repeats:NO]) != nil)
- {
- // Using CFRunLoopAddTimer instead of [[NSRunLoop currentRunLoop] addTimer:.. for consistency with run loop modes.
- // The kCFRunLoopCommonModes counterpart NSRunLoopCommonModes is only available in 10.5 and later, whereas this code
- // is designed to be also compatible with 10.4. CFRunLoopTimerRef is "toll-free-bridged" with NSTimer since 10.0.
- CFRunLoopAddTimer(CFRunLoopGetCurrent(), (CFRunLoopTimerRef)_autoRecoveryTimer, kCFRunLoopCommonModes);
- }
- }
- }
- if ([[notification name] isEqual:NSApplicationWillResignActiveNotification])
- {
- if (_mode == kHIDRemoteModeExclusiveAuto)
- {
- [self stopRemoteControl];
- _autoRecover = YES;
- }
- }
- if ([[notification name] isEqual:NSApplicationWillTerminateNotification])
- {
- _applicationIsTerminating = YES;
- if ([self isStarted])
- {
- [self stopRemoteControl];
- }
- }
- }
- }
- - (void)_delayedAutoRecovery:(NSTimer *)aTimer
- {
- [_autoRecoveryTimer invalidate];
- HIDRemoteReleaseNil(_autoRecoveryTimer);
- if (_autoRecover)
- {
- [self startRemoteControl:kHIDRemoteModeExclusiveAuto];
- _autoRecover = NO;
- }
- }
- #pragma mark - PRIVATE: Distributed notifiations handling
- - (void)_postStatusWithAction:(NSString *)action
- {
- 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;
- #if 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])
- {
- [self _postStatusWithAction:kHIDRemoteDNStatusActionUpdate];
- }
- if ([notificationName isEqual:kHIDRemoteDNHIDRemoteRetry])
- {
- if ([self isStarted])
- {
- BOOL retry = YES;
- // 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)
- {
- if (([self delegate] != nil) &&
- ([[self delegate] respondsToSelector:@selector(hidRemote:shouldRetryExclusiveLockWithInfo:)]))
- {
- retry = [[self delegate] hidRemote:self shouldRetryExclusiveLockWithInfo:[notification userInfo]];
- }
- }
- if (retry)
- {
- HIDRemoteMode restartInMode = _mode;
- if (restartInMode != kHIDRemoteModeNone)
- {
- _isRestarting = YES;
- [self stopRemoteControl];
- HIDRemoteReleaseNil(_returnToPID);
- [self startRemoteControl:restartInMode];
- _isRestarting = NO;
- if (restartInMode != kHIDRemoteModeShared)
- {
- _returnToPID = HIDRemoteRetained([[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey]);
- }
- }
- }
- else
- {
- NSNumber *cacheReturnPID = _returnToPID;
- _returnToPID = HIDRemoteRetained([[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey]);
- [self _postStatusWithAction:kHIDRemoteDNStatusActionNoNeed];
- HIDRemoteRelease(_returnToPID);
- _returnToPID = cacheReturnPID;
- }
- }
- }
- if (_exclusiveLockLending)
- {
- if ([notificationName isEqual:kHIDRemoteDNHIDRemoteStatus])
- {
- NSString *action;
- if ((action = [[notification userInfo] objectForKey:kHIDRemoteDNStatusActionKey]) != nil)
- {
- if ((_mode == kHIDRemoteModeNone) && (_waitForReturnByPID!=nil))
- {
- NSNumber *pidNumber, *returnToPIDNumber;
- if ((pidNumber = [[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey]) != nil)
- {
- returnToPIDNumber = [[notification userInfo] objectForKey:kHIDRemoteDNStatusReturnToPIDKey];
- if ([action isEqual:kHIDRemoteDNStatusActionStart])
- {
- if ([pidNumber isEqual:_waitForReturnByPID])
- {
- NSNumber *startMode;
- if ((startMode = [[notification userInfo] objectForKey:kHIDRemoteDNStatusModeKey]) != nil)
- {
- if ([startMode intValue] == kHIDRemoteModeShared)
- {
- returnToPIDNumber = [NSNumber numberWithInt:getpid()];
- action = kHIDRemoteDNStatusActionNoNeed;
- }
- }
- }
- }
- if (returnToPIDNumber != nil)
- {
- if ([action isEqual:kHIDRemoteDNStatusActionStop] || [action isEqual:kHIDRemoteDNStatusActionNoNeed])
- {
- if ([pidNumber isEqual:_waitForReturnByPID] && ([returnToPIDNumber intValue] == getpid()))
- {
- HIDRemoteReleaseNil(_waitForReturnByPID);
- if (([self delegate] != nil) &&
- ([[self delegate] respondsToSelector:@selector(hidRemote:exclusiveLockReleasedByApplicationWithInfo:)]))
- {
- [[self delegate] hidRemote:self exclusiveLockReleasedByApplicationWithInfo:[notification userInfo]];
- }
- else
- {
- [self startRemoteControl:kHIDRemoteModeExclusive];
- }
- }
- }
- }
- }
- }
- if (_mode==kHIDRemoteModeExclusive)
- {
- if ([action isEqual:kHIDRemoteDNStatusActionStart])
- {
- NSNumber *originPID = [[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey];
- BOOL lendLock = YES;
- if ([originPID intValue] != getpid())
- {
- if (([self delegate] != nil) &&
- ([[self delegate] respondsToSelector:@selector(hidRemote:lendExclusiveLockToApplicationWithInfo:)]))
- {
- lendLock = [[self delegate] hidRemote:self lendExclusiveLockToApplicationWithInfo:[notification userInfo]];
- }
- if (lendLock)
- {
- HIDRemoteRelease(_waitForReturnByPID);
- _waitForReturnByPID = HIDRemoteRetained(originPID);
- if (_waitForReturnByPID != nil)
- {
- [self stopRemoteControl];
- [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kHIDRemoteDNHIDRemoteRetry
- object:[NSString stringWithFormat:@"%d", [_waitForReturnByPID intValue]]
- userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
- [NSNumber numberWithUnsignedInt:(unsigned int)getpid()], kHIDRemoteDNStatusPIDKey,
- [[NSBundle mainBundle] bundleIdentifier], (NSString *)kCFBundleIdentifierKey,
- nil]
- deliverImmediately:YES];
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- - (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;
- NSString *ioClass;
- NSNumber *candelairHIDRemoteCompatibilityMask;
- if (service != 0)
- {
- // IOClass matching
- if ((ioClass = (__HIDRemoteBridge NSString *)IORegistryEntryCreateCFProperty((io_registry_entry_t)service,
- CFSTR(kIOClassKey),
- kCFAllocatorDefault,
- 0)) != nil)
- {
- // Match on Apple's AppleIRController and old versions of the Remote Buddy IR Controller
- if ([ioClass isEqual:@"AppleIRController"] || [ioClass isEqual:@"RBIOKitAIREmu"])
- {
- CFTypeRef candelairHIDRemoteCompatibilityDevice;
- serviceMatches = YES;
- if ((candelairHIDRemoteCompatibilityDevice = IORegistryEntryCreateCFProperty((io_registry_entry_t)service, CFSTR("CandelairHIDRemoteCompatibilityDevice"), kCFAllocatorDefault, 0)) != NULL)
- {
- if (CFEqual(kCFBooleanTrue, candelairHIDRemoteCompatibilityDevice))
- {
- serviceMatches = NO;
- }
- CFRelease (candelairHIDRemoteCompatibilityDevice);
- }
- }
- // Match on the virtual IOSPIRIT IR Controller
- if ([ioClass isEqual:@"IOSPIRITIRController"])
- {
- serviceMatches = YES;
- }
- CFRelease((CFTypeRef)ioClass);
- }
- // Match on services that claim compatibility with the HID Remote class (Candelair or third-party) by having a property of CandelairHIDRemoteCompatibilityMask = 1 <Type: Number>
- if ((candelairHIDRemoteCompatibilityMask = (__HIDRemoteBridge NSNumber *)IORegistryEntryCreateCFProperty((io_registry_entry_t)service, CFSTR("CandelairHIDRemoteCompatibilityMask"), kCFAllocatorDefault, 0)) != nil)
- {
- if ([candelairHIDRemoteCompatibilityMask isKindOfClass:[NSNumber class]])
- {
- if ([candelairHIDRemoteCompatibilityMask unsignedIntValue] & kHIDRemoteCompatibilityFlagsStandardHIDRemoteDevice)
- {
- serviceMatches = YES;
- }
- else
- {
- serviceMatches = NO;
- }
- }
- CFRelease((CFTypeRef)candelairHIDRemoteCompatibilityMask);
- }
- }
- if (([self delegate]!=nil) &&
- ([[self delegate] respondsToSelector:@selector(hidRemote:inspectNewHardwareWithService:prematchResult:)]))
- {
- serviceMatches = [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self inspectNewHardwareWithService:service prematchResult:serviceMatches];
- }
- return (serviceMatches);
- }
- - (HIDRemoteButtonCode)buttonCodeForUsage:(unsigned int)usage usagePage:(unsigned int)usagePage
- {
- HIDRemoteButtonCode buttonCode = kHIDRemoteButtonCodeNone;
- switch (usagePage)
- {
- case kHIDPage_Consumer:
- switch (usage)
- {
- case kHIDUsage_Csmr_MenuPick:
- // Aluminum Remote: Center
- buttonCode = (kHIDRemoteButtonCodeCenter|kHIDRemoteButtonCodeAluminumMask);
- break;
- case kHIDUsage_Csmr_ModeStep:
- // Aluminium Remote: Center Hold
- buttonCode = (kHIDRemoteButtonCodeCenterHold|kHIDRemoteButtonCodeAluminumMask);
- break;
- case kHIDUsage_Csmr_PlayOrPause:
- // Aluminum Remote: Play/Pause
- buttonCode = (kHIDRemoteButtonCodePlay|kHIDRemoteButtonCodeAluminumMask);
- break;
- case kHIDUsage_Csmr_Rewind:
- buttonCode = kHIDRemoteButtonCodeLeftHold;
- break;
- case kHIDUsage_Csmr_FastForward:
- buttonCode = kHIDRemoteButtonCodeRightHold;
- break;
- case kHIDUsage_Csmr_Menu:
- buttonCode = kHIDRemoteButtonCodeMenuHold;
- break;
- case kHIDUsage_Csmr_VolumeIncrement:
- buttonCode = kHIDRemoteButtonCodeUp;
- break;
- case kHIDUsage_Csmr_VolumeDecrement:
- buttonCode = kHIDRemoteButtonCodeDown;
- break;
- }
- break;
- case kHIDPage_GenericDesktop:
- switch (usage)
- {
- case kHIDUsage_GD_SystemAppMenu:
- buttonCode = kHIDRemoteButtonCodeMenu;
- break;
- case kHIDUsage_GD_SystemMenu:
- buttonCode = kHIDRemoteButtonCodeCenter;
- break;
- case kHIDUsage_GD_SystemMenuRight:
- buttonCode = kHIDRemoteButtonCodeRight;
- break;
- case kHIDUsage_GD_SystemMenuLeft:
- buttonCode = kHIDRemoteButtonCodeLeft;
- break;
- case kHIDUsage_GD_SystemMenuUp:
- buttonCode = kHIDRemoteButtonCodeUp;
- break;
- case kHIDUsage_GD_SystemMenuDown:
- buttonCode = kHIDRemoteButtonCodeDown;
- break;
- }
- break;
- case 0x06: /* Reserved */
- switch (usage)
- {
- case 0x22:
- buttonCode = kHIDRemoteButtonCodeIDChanged;
- break;
- }
- break;
- case 0xFF01: /* Vendor specific */
- switch (usage)
- {
- case 0x23:
- buttonCode = kHIDRemoteButtonCodeCenterHold;
- break;
- #ifdef _HIDREMOTE_EXTENSIONS
- #define _HIDREMOTE_EXTENSIONS_SECTION 2
- #include "HIDRemoteAdditions.h"
- #undef _HIDREMOTE_EXTENSIONS_SECTION
- #endif /* _HIDREMOTE_EXTENSIONS */
- }
- break;
- }
- return (buttonCode);
- }
- - (BOOL)_setupService:(io_object_t)service
- {
- kern_return_t kernResult;
- IOReturn returnCode;
- HRESULT hResult;
- SInt32 score;
- BOOL opened = NO, queueStarted = NO;
- IOHIDDeviceInterface122 **hidDeviceInterface = NULL;
- IOCFPlugInInterface **cfPluginInterface = NULL;
- IOHIDQueueInterface **hidQueueInterface = NULL;
- io_object_t serviceNotification = 0;
- CFRunLoopSourceRef queueEventSource = NULL;
- NSMutableDictionary *hidAttribsDict = nil;
- CFArrayRef hidElements = NULL;
- NSError *error = nil;
- UInt32 errorCode = 0;
- if (![self _prematchService:service])
- {
- return (NO);
- }
- do
- {
- // Create a plugin interface ..
- kernResult = IOCreatePlugInInterfaceForService( service,
- kIOHIDDeviceUserClientTypeID,
- kIOCFPlugInInterfaceID,
- &cfPluginInterface,
- &score);
- if (kernResult != kIOReturnSuccess)
- {
- error = [NSError errorWithDomain:NSMachErrorDomain code:kernResult userInfo:nil];
- errorCode = 1;
- break;
- }
- // .. use it to get the HID interface ..
- hResult = (*cfPluginInterface)->QueryInterface( cfPluginInterface,
- CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID122),
- (LPVOID)&hidDeviceInterface);
- if ((hResult!=S_OK) || (hidDeviceInterface==NULL))
- {
- error = [NSError errorWithDomain:NSMachErrorDomain code:hResult userInfo:nil];
- errorCode = 2;
- break;
- }
- // .. then open it ..
- switch (_mode)
- {
- case kHIDRemoteModeShared:
- hResult = (*hidDeviceInterface)->open(hidDeviceInterface, kIOHIDOptionsTypeNone);
- break;
- case kHIDRemoteModeExclusive:
- case kHIDRemoteModeExclusiveAuto:
- hResult = (*hidDeviceInterface)->open(hidDeviceInterface, kIOHIDOptionsTypeSeizeDevice);
- break;
- default:
- goto cleanUp; // Ugh! But there are no "double breaks" available in C AFAIK ..
- break;
- }
- if (hResult!=S_OK)
- {
- error = [NSError errorWithDomain:NSMachErrorDomain code:hResult userInfo:nil];
- errorCode = 3;
- break;
- }
- opened = YES;
- // .. query the HID elements ..
- returnCode = (*hidDeviceInterface)->copyMatchingElements(hidDeviceInterface,
- NULL,
- &hidElements);
- if ((returnCode != kIOReturnSuccess) || (hidElements==NULL))
- {
- error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
- errorCode = 4;
- break;
- }
- // Setup an event queue for HID events!
- hidQueueInterface = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
- if (hidQueueInterface == NULL)
- {
- error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil];
- errorCode = 5;
- break;
- }
- returnCode = (*hidQueueInterface)->create(hidQueueInterface, 0, 32);
- if (returnCode != kIOReturnSuccess)
- {
- error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
- errorCode = 6;
- break;
- }
- // Setup of attributes stored for this HID device
- hidAttribsDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
- [NSValue valueWithPointer:(const void *)cfPluginInterface], kHIDRemoteCFPluginInterface,
- [NSValue valueWithPointer:(const void *)hidDeviceInterface], kHIDRemoteHIDDeviceInterface,
- [NSValue valueWithPointer:(const void *)hidQueueInterface], kHIDRemoteHIDQueueInterface,
- nil];
- {
- UInt32 i, hidElementCnt = (UInt32)CFArrayGetCount(hidElements);
- NSMutableDictionary *cookieButtonCodeLUT = [[NSMutableDictionary alloc] init];
- NSMutableDictionary *cookieCount = [[NSMutableDictionary alloc] init];
- if ((cookieButtonCodeLUT==nil) || (cookieCount==nil))
- {
- HIDRemoteReleaseNil(cookieButtonCodeLUT);
- HIDRemoteReleaseNil(cookieCount);
- error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil];
- errorCode = 7;
- break;
- }
- // Analyze the HID elements and find matching elements
- for (i=0;i<hidElementCnt;i++)
- {
- CFDictionaryRef hidDict;
- NSNumber *usage, *usagePage, *cookie;
- HIDRemoteButtonCode buttonCode = kHIDRemoteButtonCodeNone;
- hidDict = CFArrayGetValueAtIndex(hidElements, i);
- usage = (NSNumber *) CFDictionaryGetValue(hidDict, CFSTR(kIOHIDElementUsageKey));
- usagePage = (NSNumber *) CFDictionaryGetValue(hidDict, CFSTR(kIOHIDElementUsagePageKey));
- cookie = (NSNumber *) CFDictionaryGetValue(hidDict, CFSTR(kIOHIDElementCookieKey));
- if ((usage!=nil) && (usagePage!=nil) && (cookie!=nil))
- {
- // Find the button codes for the ID combos
- buttonCode = [self buttonCodeForUsage:[usage unsignedIntValue] usagePage:[usagePage unsignedIntValue]];
- #ifdef _HIDREMOTE_EXTENSIONS
- // Debug logging code
- #define _HIDREMOTE_EXTENSIONS_SECTION 3
- #include "HIDRemoteAdditions.h"
- #undef _HIDREMOTE_EXTENSIONS_SECTION
- #endif /* _HIDREMOTE_EXTENSIONS */
- // Did record match?
- if (buttonCode != kHIDRemoteButtonCodeNone)
- {
- NSString *pairString = [[NSString alloc] initWithFormat:@"%u_%u", [usagePage unsignedIntValue], [usage unsignedIntValue]];
- NSNumber *buttonCodeNumber = [[NSNumber alloc] initWithUnsignedInt:(unsigned int)buttonCode];
- #ifdef _HIDREMOTE_EXTENSIONS
- // Debug logging code
- #define _HIDREMOTE_EXTENSIONS_SECTION 4
- #include "HIDRemoteAdditions.h"
- #undef _HIDREMOTE_EXTENSIONS_SECTION
- #endif /* _HIDREMOTE_EXTENSIONS */
- [cookieCount setObject:buttonCodeNumber forKey:pairString];
- [cookieButtonCodeLUT setObject:buttonCodeNumber forKey:cookie];
- (*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 */
- HIDRemoteRelease(buttonCodeNumber);
- HIDRemoteRelease(pairString);
- }
- }
- }
- // Compare number of *unique* matches (thus the cookieCount dictionary) with required minimum
- if ([cookieCount count] < 10)
- {
- HIDRemoteReleaseNil(cookieButtonCodeLUT);
- HIDRemoteReleaseNil(cookieCount);
- error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil];
- errorCode = 8;
- break;
- }
- [hidAttribsDict setObject:cookieButtonCodeLUT forKey:kHIDRemoteCookieButtonCodeLUT];
- HIDRemoteReleaseNil(cookieButtonCodeLUT);
- HIDRemoteReleaseNil(cookieCount);
- }
- // Finish setup of IOHIDQueueInterface with CFRunLoop
- returnCode = (*hidQueueInterface)->createAsyncEventSource(hidQueueInterface, &queueEventSource);
- if ((returnCode != kIOReturnSuccess) || (queueEventSource == NULL))
- {
- error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
- errorCode = 9;
- break;
- }
- returnCode = (*hidQueueInterface)->setEventCallout(hidQueueInterface, HIDEventCallback, (void *)((intptr_t)service), (__HIDRemoteBridge void *)self);
- if (returnCode != kIOReturnSuccess)
- {
- error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
- errorCode = 10;
- break;
- }
- CFRunLoopAddSource( CFRunLoopGetCurrent(),
- queueEventSource,
- kCFRunLoopCommonModes);
- [hidAttribsDict setObject:[NSValue valueWithPointer:(const void *)queueEventSource] forKey:kHIDRemoteCFRunLoopSource];
- returnCode = (*hidQueueInterface)->start(hidQueueInterface);
- if (returnCode != kIOReturnSuccess)
- {
- error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
- errorCode = 11;
- break;
- }
- queueStarted = YES;
- // Setup device notifications
- returnCode = IOServiceAddInterestNotification( _notifyPort,
- service,
- kIOGeneralInterest,
- ServiceNotificationCallback,
- (__HIDRemoteBridge void *)(self),
- &serviceNotification);
- if ((returnCode != kIOReturnSuccess) || (serviceNotification==0))
- {
- error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
- errorCode = 12;
- break;
- }
- [hidAttribsDict setObject:[NSNumber numberWithUnsignedInt:(unsigned int)serviceNotification] forKey:kHIDRemoteServiceNotification];
- // Retain service
- if (IOObjectRetain(service) != kIOReturnSuccess)
- {
- error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil];
- errorCode = 13;
- break;
- }
- [hidAttribsDict setObject:[NSNumber numberWithUnsignedInt:(unsigned int)service] forKey:kHIDRemoteService];
- // Get some (somewhat optional) infos on the device
- {
- CFStringRef product, manufacturer, transport;
- if ((product = IORegistryEntryCreateCFProperty( (io_registry_entry_t)service,
- (CFStringRef) @"Product",
- kCFAllocatorDefault,
- 0)) != NULL)
- {
- if (CFGetTypeID(product) == CFStringGetTypeID())
- {
- [hidAttribsDict setObject:(__HIDRemoteBridge NSString *)product forKey:kHIDRemoteProduct];
- }
- CFRelease(product);
- }
- if ((manufacturer = IORegistryEntryCreateCFProperty( (io_registry_entry_t)service,
- (CFStringRef) @"Manufacturer",
- kCFAllocatorDefault,
- 0)) != NULL)
- {
- if (CFGetTypeID(manufacturer) == CFStringGetTypeID())
- {
- [hidAttribsDict setObject:(__HIDRemoteBridge NSString *)manufacturer forKey:kHIDRemoteManufacturer];
- }
- CFRelease(manufacturer);
- }
- if ((transport = IORegistryEntryCreateCFProperty( (io_registry_entry_t)service,
- (CFStringRef) @"Transport",
- kCFAllocatorDefault,
- 0)) != NULL)
- {
- if (CFGetTypeID(transport) == CFStringGetTypeID())
- {
- [hidAttribsDict setObject:(__HIDRemoteBridge NSString *)transport forKey:kHIDRemoteTransport];
- }
- CFRelease(transport);
- }
- }
- // Determine Aluminum Remote support
- {
- CFNumberRef aluSupport;
- HIDRemoteAluminumRemoteSupportLevel supportLevel = kHIDRemoteAluminumRemoteSupportLevelNone;
- if ((_mode == kHIDRemoteModeExclusive) || (_mode == kHIDRemoteModeExclusiveAuto))
- {
- // Determine if this driver offers on-demand support for the Aluminum Remote (only relevant under OS versions < 10.6.2)
- if ((aluSupport = IORegistryEntryCreateCFProperty((io_registry_entry_t)service,
- (CFStringRef) @"AluminumRemoteSupportLevelOnDemand",
- kCFAllocatorDefault,
- 0)) != nil)
- {
- // There is => request the driver to enable it for us
- if (IORegistryEntrySetCFProperty((io_registry_entry_t)service,
- CFSTR("EnableAluminumRemoteSupportForMe"),
- (__HIDRemoteBridge CFTypeRef)([NSDictionary dictionaryWithObjectsAndKeys:
- [NSNumber numberWithLongLong:(long long)getpid()], @"pid",
- [NSNumber numberWithLongLong:(long long)getuid()], @"uid",
- nil])) == kIOReturnSuccess)
- {
- if (CFGetTypeID(aluSupport) == CFNumberGetTypeID())
- {
- supportLevel = (HIDRemoteAluminumRemoteSupportLevel) [(__HIDRemoteBridge NSNumber *)aluSupport intValue];
- }
- [hidAttribsDict setObject:[NSNumber numberWithBool:YES] forKey:kHIDRemoteAluminumRemoteSupportOnDemand];
- }
- CFRelease(aluSupport);
- }
- }
- if (supportLevel == kHIDRemoteAluminumRemoteSupportLevelNone)
- {
- if ((aluSupport = IORegistryEntryCreateCFProperty((io_registry_entry_t)service,
- (CFStringRef) @"AluminumRemoteSupportLevel",
- kCFAllocatorDefault,
- 0)) != nil)
- {
- if (CFGetTypeID(aluSupport) == CFNumberGetTypeID())
- {
- supportLevel = (HIDRemoteAluminumRemoteSupportLevel) [(__HIDRemoteBridge NSNumber *)aluSupport intValue];
- }
- CFRelease(aluSupport);
- }
- else
- {
- CFStringRef ioKitClassName;
- if ((ioKitClassName = IORegistryEntryCreateCFProperty( (io_registry_entry_t)service,
- CFSTR(kIOClassKey),
- kCFAllocatorDefault,
- 0)) != nil)
- {
- if ([(__HIDRemoteBridge NSString *)ioKitClassName isEqual:@"AppleIRController"])
- {
- if ([HIDRemote OSXVersion] >= 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;
- }
- }
- CFRelease(ioKitClassName);
- }
- }
- }
- [hidAttribsDict setObject:(NSNumber *)[NSNumber numberWithInt:(int)supportLevel] forKey:kHIDRemoteAluminumRemoteSupportLevel];
- }
- // Add it to the serviceAttribMap
- [_serviceAttribMap setObject:hidAttribsDict forKey:[NSNumber numberWithUnsignedInt:(unsigned int)service]];
- // And we're done with setup ..
- if (([self delegate]!=nil) &&
- ([[self delegate] respondsToSelector:@selector(hidRemote:foundNewHardwareWithAttributes:)]))
- {
- [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self foundNewHardwareWithAttributes:hidAttribsDict];
- }
- HIDRemoteReleaseNil(hidAttribsDict);
- return(YES);
- }while(0);
- cleanUp:
- if (([self delegate]!=nil) &&
- ([[self delegate] respondsToSelector:@selector(hidRemote:failedNewHardwareWithError:)]))
- {
- if (error!=nil)
- {
- error = [NSError errorWithDomain:[error domain]
- code:[error code]
- userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:errorCode] forKey:@"InternalErrorCode"]
- ];
- }
- [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self failedNewHardwareWithError:error];
- }
- // An error occured or this device is not of interest .. cleanup ..
- if (serviceNotification!=0)
- {
- IOObjectRelease(serviceNotification);
- serviceNotification = 0;
- }
- if (queueEventSource!=NULL)
- {
- CFRunLoopSourceInvalidate(queueEventSource);
- queueEventSource=NULL;
- }
- if (hidQueueInterface!=NULL)
- {
- if (queueStarted)
- {
- (*hidQueueInterface)->stop(hidQueueInterface);
- }
- (*hidQueueInterface)->dispose(hidQueueInterface);
- (*hidQueueInterface)->Release(hidQueueInterface);
- hidQueueInterface = NULL;
- }
- if (hidAttribsDict!=nil)
- {
- HIDRemoteReleaseNil(hidAttribsDict);
- }
- if (hidElements!=NULL)
- {
- CFRelease(hidElements);
- hidElements = NULL;
- }
- if (hidDeviceInterface!=NULL)
- {
- if (opened)
- {
- (*hidDeviceInterface)->close(hidDeviceInterface);
- }
- (*hidDeviceInterface)->Release(hidDeviceInterface);
- // opened = NO;
- hidDeviceInterface = NULL;
- }
- if (cfPluginInterface!=NULL)
- {
- IODestroyPlugInInterface(cfPluginInterface);
- cfPluginInterface = NULL;
- }
- return (NO);
- }
- - (void)_destructService:(io_object_t)service
- {
- NSNumber *serviceValue;
- NSMutableDictionary *serviceDict = NULL;
- if ((serviceValue = [NSNumber numberWithUnsignedInt:(unsigned int)service]) == nil)
- {
- return;
- }
- serviceDict = [_serviceAttribMap objectForKey:serviceValue];
- if (serviceDict!=nil)
- {
- IOHIDDeviceInterface122 **hidDeviceInterface = NULL;
- IOCFPlugInInterface **cfPluginInterface = NULL;
- IOHIDQueueInterface **hidQueueInterface = NULL;
- io_object_t serviceNotification = 0;
- CFRunLoopSourceRef queueEventSource = NULL;
- io_object_t theService = 0;
- NSMutableDictionary *cookieButtonMap = nil;
- NSTimer *simulateHoldTimer = nil;
- serviceNotification = (io_object_t) ([serviceDict objectForKey:kHIDRemoteServiceNotification] ? [[serviceDict objectForKey:kHIDRemoteServiceNotification] unsignedIntValue] : 0);
- theService = (io_object_t) ([serviceDict objectForKey:kHIDRemoteService] ? [[serviceDict objectForKey:kHIDRemoteService] unsignedIntValue] : 0);
- queueEventSource = (CFRunLoopSourceRef) ([serviceDict objectForKey:kHIDRemoteCFRunLoopSource] ? [[serviceDict objectForKey:kHIDRemoteCFRunLoopSource] pointerValue] : NULL);
- hidQueueInterface = (IOHIDQueueInterface **) ([serviceDict objectForKey:kHIDRemoteHIDQueueInterface] ? [[serviceDict objectForKey:kHIDRemoteHIDQueueInterface] pointerValue] : NULL);
- hidDeviceInterface = (IOHIDDeviceInterface122 **) ([serviceDict objectForKey:kHIDRemoteHIDDeviceInterface] ? [[serviceDict objectForKey:kHIDRemoteHIDDeviceInterface] pointerValue] : NULL);
- cfPluginInterface = (IOCFPlugInInterface **) ([serviceDict objectForKey:kHIDRemoteCFPluginInterface] ? [[serviceDict objectForKey:kHIDRemoteCFPluginInterface] pointerValue] : NULL);
- cookieButtonMap = (NSMutableDictionary *) [serviceDict objectForKey:kHIDRemoteCookieButtonCodeLUT];
- simulateHoldTimer = (NSTimer *) [serviceDict objectForKey:kHIDRemoteSimulateHoldEventsTimer];
- HIDRemoteRetain(serviceDict);
- [_serviceAttribMap removeObjectForKey:serviceValue];
- if (([serviceDict objectForKey:kHIDRemoteAluminumRemoteSupportOnDemand]!=nil) && [[serviceDict objectForKey:kHIDRemoteAluminumRemoteSupportOnDemand] boolValue] && (theService != 0))
- {
- // 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
- IORegistryEntrySetCFProperty( (io_registry_entry_t)theService,
- CFSTR("DisableAluminumRemoteSupportForMe"),
- (__HIDRemoteBridge CFTypeRef)([NSDictionary dictionaryWithObjectsAndKeys:
- [NSNumber numberWithLongLong:(long long)getpid()], @"pid",
- [NSNumber numberWithLongLong:(long long)getuid()], @"uid",
- nil]));
- }
- if (([self delegate]!=nil) &&
- ([[self delegate] respondsToSelector:@selector(hidRemote:releasedHardwareWithAttributes:)]))
- {
- [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self releasedHardwareWithAttributes:serviceDict];
- }
- if (simulateHoldTimer!=nil)
- {
- [simulateHoldTimer invalidate];
- }
- if (serviceNotification!=0)
- {
- IOObjectRelease(serviceNotification);
- }
- if (queueEventSource!=NULL)
- {
- CFRunLoopRemoveSource( CFRunLoopGetCurrent(),
- queueEventSource,
- kCFRunLoopCommonModes);
- }
- if ((hidQueueInterface!=NULL) && (cookieButtonMap!=nil))
- {
- NSEnumerator *cookieEnum = [cookieButtonMap keyEnumerator];
- NSNumber *cookie;
- while ((cookie = [cookieEnum nextObject]) != nil)
- {
- if ((*hidQueueInterface)->hasElement(hidQueueInterface, (IOHIDElementCookie) [cookie unsignedIntValue]))
- {
- (*hidQueueInterface)->removeElement(hidQueueInterface,
- (IOHIDElementCookie) [cookie unsignedIntValue]);
- }
- };
- }
- if (hidQueueInterface!=NULL)
- {
- (*hidQueueInterface)->stop(hidQueueInterface);
- (*hidQueueInterface)->dispose(hidQueueInterface);
- (*hidQueueInterface)->Release(hidQueueInterface);
- }
- if (hidDeviceInterface!=NULL)
- {
- (*hidDeviceInterface)->close(hidDeviceInterface);
- (*hidDeviceInterface)->Release(hidDeviceInterface);
- }
- if (cfPluginInterface!=NULL)
- {
- IODestroyPlugInInterface(cfPluginInterface);
- }
- if (theService!=0)
- {
- IOObjectRelease(theService);
- }
- HIDRemoteRelease(serviceDict);
- }
- }
- #pragma mark - PRIVATE: HID Event handling
- - (void)_simulateHoldEvent:(NSTimer *)aTimer
- {
- NSMutableDictionary *hidAttribsDict;
- NSTimer *shTimer;
- NSNumber *shButtonCode;
- if ((hidAttribsDict = (NSMutableDictionary *)[aTimer userInfo]) != nil)
- {
- if (((shTimer = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsTimer]) != nil) &&
- ((shButtonCode = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsOriginButtonCode]) != nil))
- {
- [shTimer invalidate];
- [hidAttribsDict removeObjectForKey:kHIDRemoteSimulateHoldEventsTimer];
- [self _sendButtonCode:(((HIDRemoteButtonCode)[shButtonCode unsignedIntValue])|kHIDRemoteButtonCodeHoldMask) isPressed:YES hidAttribsDict:hidAttribsDict];
- }
- }
- }
- - (void)_handleButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict
- {
- switch (buttonCode)
- {
- case kHIDRemoteButtonCodeIDChanged:
- // Do nothing, this is handled seperately
- break;
- case kHIDRemoteButtonCodeUp:
- case kHIDRemoteButtonCodeDown:
- if (_simulateHoldEvents)
- {
- NSTimer *shTimer = nil;
- NSNumber *shButtonCode = nil;
- [[hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsTimer] invalidate];
- if (isPressed)
- {
- [hidAttribsDict setObject:[NSNumber numberWithUnsignedInt:buttonCode] forKey:kHIDRemoteSimulateHoldEventsOriginButtonCode];
- if ((shTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:0.7] interval:0.1 target:self selector:@selector(_simulateHoldEvent:) userInfo:hidAttribsDict repeats:NO]) != nil)
- {
- [hidAttribsDict setObject:shTimer forKey:kHIDRemoteSimulateHoldEventsTimer];
- // Using CFRunLoopAddTimer instead of [[NSRunLoop currentRunLoop] addTimer:.. for consistency with run loop modes.
- // The kCFRunLoopCommonModes counterpart NSRunLoopCommonModes is only available in 10.5 and later, whereas this code
- // is designed to be also compatible with 10.4. CFRunLoopTimerRef is "toll-free-bridged" with NSTimer since 10.0.
- CFRunLoopAddTimer(CFRunLoopGetCurrent(), (CFRunLoopTimerRef)shTimer, kCFRunLoopCommonModes);
- HIDRemoteRelease(shTimer);
- break;
- }
- }
- else
- {
- shTimer = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsTimer];
- shButtonCode = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsOriginButtonCode];
- 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!=nil)
- {
- [self _sendButtonCode:(((HIDRemoteButtonCode)[shButtonCode unsignedIntValue])|kHIDRemoteButtonCodeHoldMask) isPressed:NO hidAttribsDict:hidAttribsDict];
- }
- }
- }
- [hidAttribsDict removeObjectForKey:kHIDRemoteSimulateHoldEventsTimer];
- [hidAttribsDict removeObjectForKey:kHIDRemoteSimulateHoldEventsOriginButtonCode];
- break;
- }
- default:
- [self _sendButtonCode:buttonCode isPressed:isPressed hidAttribsDict:hidAttribsDict];
- break;
- }
- }
- - (void)_sendButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict
- {
- if (([self delegate]!=nil) &&
- ([[self delegate] respondsToSelector:@selector(hidRemote:eventWithButton:isPressed:fromHardwareWithAttributes:)]))
- {
- switch (buttonCode & (~kHIDRemoteButtonCodeAluminumMask))
- {
- case kHIDRemoteButtonCodePlay:
- case kHIDRemoteButtonCodeCenter:
- if (buttonCode & kHIDRemoteButtonCodeAluminumMask)
- {
- _lastSeenModel = kHIDRemoteModelAluminum;
- _lastSeenModelRemoteID = _lastSeenRemoteID;
- }
- else
- {
- switch ((HIDRemoteAluminumRemoteSupportLevel)[[hidAttribsDict objectForKey:kHIDRemoteAluminumRemoteSupportLevel] intValue])
- {
- case kHIDRemoteAluminumRemoteSupportLevelNone:
- case kHIDRemoteAluminumRemoteSupportLevelEmulation:
- // Remote type can't be determined by just the Center button press
- break;
- case kHIDRemoteAluminumRemoteSupportLevelNative:
- // Remote type can be safely determined by just the Center button press
- if (((_lastSeenModel == kHIDRemoteModelAluminum) && (_lastSeenModelRemoteID != _lastSeenRemoteID)) ||
- (_lastSeenModel == kHIDRemoteModelUndetermined))
- {
- _lastSeenModel = kHIDRemoteModelWhitePlastic;
- }
- break;
- }
- }
- break;
- }
- // As soon as we have received a code that's unique to the Aluminum Remote, we can tell kHIDRemoteButtonCodePlayHold and kHIDRemoteButtonCodeCenterHold apart.
- // Prior to that, a long press of the new "Play" button will be submitted as a "kHIDRemoteButtonCodeCenterHold", not a "kHIDRemoteButtonCodePlayHold" code.
- if ((buttonCode == kHIDRemoteButtonCodeCenterHold) && (_lastSeenModel == kHIDRemoteModelAluminum))
- {
- buttonCode = kHIDRemoteButtonCodePlayHold;
- }
- [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self eventWithButton:(buttonCode & (~kHIDRemoteButtonCodeAluminumMask)) isPressed:isPressed fromHardwareWithAttributes:hidAttribsDict];
- }
- }
- - (void)_hidEventFor:(io_service_t)hidDevice from:(IOHIDQueueInterface **)interface withResult:(IOReturn)result
- {
- NSMutableDictionary *hidAttribsDict = HIDRemoteAutoreleased(HIDRemoteRetained([_serviceAttribMap objectForKey:[NSNumber numberWithUnsignedInt:(unsigned int)hidDevice]]));
- if (hidAttribsDict!=nil)
- {
- IOHIDQueueInterface **queueInterface = NULL;
- queueInterface = [[hidAttribsDict objectForKey:kHIDRemoteHIDQueueInterface] pointerValue];
- if (interface == queueInterface)
- {
- NSNumber *lastButtonPressedNumber = nil;
- HIDRemoteButtonCode lastButtonPressed = kHIDRemoteButtonCodeNone;
- NSMutableDictionary *cookieButtonMap = nil;
- cookieButtonMap = [hidAttribsDict objectForKey:kHIDRemoteCookieButtonCodeLUT];
- if ((lastButtonPressedNumber = [hidAttribsDict objectForKey:kHIDRemoteLastButtonPressed]) != nil)
- {
- lastButtonPressed = [lastButtonPressedNumber unsignedIntValue];
- }
- while (result == kIOReturnSuccess)
- {
- IOHIDEventStruct hidEvent;
- AbsoluteTime supportedTime = { 0,0 };
- result = (*queueInterface)->getNextEvent( queueInterface,
- &hidEvent,
- supportedTime,
- 0);
- if (result == kIOReturnSuccess)
- {
- NSNumber *buttonCodeNumber = [cookieButtonMap objectForKey:[NSNumber numberWithUnsignedInt:(unsigned int) hidEvent.elementCookie]];
- #ifdef _HIDREMOTE_EXTENSIONS
- // Debug logging code
- #define _HIDREMOTE_EXTENSIONS_SECTION 5
- #include "HIDRemoteAdditions.h"
- #undef _HIDREMOTE_EXTENSIONS_SECTION
- #endif /* _HIDREMOTE_EXTENSIONS */
- if (buttonCodeNumber!=nil)
- {
- HIDRemoteButtonCode buttonCode = [buttonCodeNumber unsignedIntValue];
- if (hidEvent.value == 0)
- {
- if (buttonCode == lastButtonPressed)
- {
- [self _handleButtonCode:lastButtonPressed isPressed:NO hidAttribsDict:hidAttribsDict];
- lastButtonPressed = kHIDRemoteButtonCodeNone;
- }
- }
- if (hidEvent.value != 0)
- {
- if (lastButtonPressed != kHIDRemoteButtonCodeNone)
- {
- [self _handleButtonCode:lastButtonPressed isPressed:NO hidAttribsDict:hidAttribsDict];
- // lastButtonPressed = kHIDRemoteButtonCodeNone;
- }
- if (buttonCode == kHIDRemoteButtonCodeIDChanged)
- {
- if (([self delegate]!=nil) &&
- ([[self delegate] respondsToSelector:@selector(hidRemote:remoteIDChangedOldID:newID:forHardwareWithAttributes:)]))
- {
- [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self remoteIDChangedOldID:_lastSeenRemoteID newID:hidEvent.value forHardwareWithAttributes:hidAttribsDict];
- }
- _lastSeenRemoteID = hidEvent.value;
- _lastSeenModel = kHIDRemoteModelUndetermined;
- }
- [self _handleButtonCode:buttonCode isPressed:YES hidAttribsDict:hidAttribsDict];
- lastButtonPressed = buttonCode;
- }
- }
- }
- };
- [hidAttribsDict setObject:[NSNumber numberWithUnsignedInt:lastButtonPressed] forKey:kHIDRemoteLastButtonPressed];
- }
- #ifdef _HIDREMOTE_EXTENSIONS
- // Debug logging code
- #define _HIDREMOTE_EXTENSIONS_SECTION 6
- #include "HIDRemoteAdditions.h"
- #undef _HIDREMOTE_EXTENSIONS_SECTION
- #endif /* _HIDREMOTE_EXTENSIONS */
- }
- }
- #pragma mark - PRIVATE: Notification handling
- - (void)_serviceMatching:(io_iterator_t)iterator
- {
- io_object_t matchingService = 0;
- while ((matchingService = IOIteratorNext(iterator)) != 0)
- {
- [self _setupService:matchingService];
- IOObjectRelease(matchingService);
- };
- }
- - (void)_serviceNotificationFor:(io_service_t)service messageType:(natural_t)messageType messageArgument:(void *)messageArgument
- {
- if (messageType == kIOMessageServiceIsTerminated)
- {
- [self _destructService:service];
- }
- }
- - (void)_updateSessionInformation
- {
- NSArray *consoleUsersArray;
- io_service_t rootService;
- if (_masterPort==0) { return; }
- if ((rootService = IORegistryGetRootEntry(_masterPort)) != 0)
- {
- if ((consoleUsersArray = (__HIDRemoteBridge NSArray *)IORegistryEntryCreateCFProperty((io_registry_entry_t)rootService, CFSTR("IOConsoleUsers"), kCFAllocatorDefault, 0)) != nil)
- {
- if ([consoleUsersArray isKindOfClass:[NSArray class]]) // Be careful - ensure this really is an array
- {
- NSEnumerator *consoleUsersEnum; // I *love* Obj-C2's fast enumerators, but we need to stay compatible with 10.4 :-/
- if ((consoleUsersEnum = [consoleUsersArray objectEnumerator]) != nil)
- {
- UInt64 secureEventInputPIDSum = 0;
- uid_t frontUserSession = 0;
- BOOL screenIsLocked = NO;
- NSDictionary *consoleUserDict;
- while ((consoleUserDict = [consoleUsersEnum nextObject]) != nil)
- {
- if ([consoleUserDict isKindOfClass:[NSDictionary class]]) // Be careful - ensure this really is a dictionary
- {
- NSNumber *secureInputPID;
- NSNumber *onConsole;
- NSNumber *userID;
- NSNumber *screenIsLockedBool;
- if ((secureInputPID = [consoleUserDict objectForKey:@"kCGSSessionSecureInputPID"]) != nil)
- {
- if ([secureInputPID isKindOfClass:[NSNumber class]])
- {
- secureEventInputPIDSum += ((UInt64) [secureInputPID intValue]);
- }
- }
- if (((onConsole = [consoleUserDict objectForKey:@"kCGSSessionOnConsoleKey"]) != nil) &&
- ((userID = [consoleUserDict objectForKey:@"kCGSSessionUserIDKey"]) != nil))
- {
- if ([onConsole isKindOfClass:[NSNumber class]] && [userID isKindOfClass:[NSNumber class]])
- {
- if ([onConsole boolValue])
- {
- frontUserSession = (uid_t) [userID intValue];
- }
- }
- }
- if ((screenIsLockedBool = [consoleUserDict objectForKey:@"CGSSessionScreenIsLocked"]) != nil)
- {
- if ([screenIsLockedBool isKindOfClass:[NSNumber class]])
- {
- screenIsLocked = [screenIsLockedBool boolValue];
- }
- }
- }
- }
- _lastSecureEventInputPIDSum = secureEventInputPIDSum;
- _lastFrontUserSession = frontUserSession;
- _lastScreenIsLocked = screenIsLocked;
- }
- }
- CFRelease((CFTypeRef)consoleUsersArray);
- }
- IOObjectRelease((io_object_t) rootService);
- }
- }
- - (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) ||
- (old_lastScreenIsLocked != _lastScreenIsLocked)) && _secureEventInputWorkAround)
- {
- [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
- {
- #if 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(_computerDidWake:) onThread:_runOnThread withObject:aNotification waitUntilDone:NO];
- return;
- }
- }
- #endif
- [self _silentRestart];
- }
- }
- @end
- #pragma mark - PRIVATE: IOKitLib Callbacks
- static void HIDEventCallback( void * target,
- IOReturn result,
- void * refCon,
- void * sender)
- {
- HIDRemote *hidRemote = (__HIDRemoteBridge HIDRemote *)refCon;
- HIDRemoteAutoreleasePoolOpen();
- [hidRemote _hidEventFor:(io_service_t)((intptr_t)target) from:(IOHIDQueueInterface**)sender withResult:(IOReturn)result];
- HIDRemoteAutoreleasePoolClose();
- }
- static void ServiceMatchingCallback( void *refCon,
- io_iterator_t iterator)
- {
- HIDRemote *hidRemote = (__HIDRemoteBridge HIDRemote *)refCon;
- HIDRemoteAutoreleasePoolOpen();
- [hidRemote _serviceMatching:iterator];
- HIDRemoteAutoreleasePoolClose();
- }
- static void ServiceNotificationCallback(void * refCon,
- io_service_t service,
- natural_t messageType,
- void * messageArgument)
- {
- HIDRemote *hidRemote = (__HIDRemoteBridge HIDRemote *)refCon;
- HIDRemoteAutoreleasePoolOpen();
- [hidRemote _serviceNotificationFor:service
- messageType:messageType
- messageArgument:messageArgument];
- HIDRemoteAutoreleasePoolClose();
- }
- static void SecureInputNotificationCallback( void * refCon,
- io_service_t service,
- natural_t messageType,
- void * messageArgument)
- {
- HIDRemote *hidRemote = (__HIDRemoteBridge HIDRemote *)refCon;
- HIDRemoteAutoreleasePoolOpen();
- [hidRemote _secureInputNotificationFor:service
- messageType:messageType
- messageArgument:messageArgument];
- HIDRemoteAutoreleasePoolClose();
- }
- // Attribute dictionary keys
- NSString *kHIDRemoteCFPluginInterface = @"CFPluginInterface";
- NSString *kHIDRemoteHIDDeviceInterface = @"HIDDeviceInterface";
- NSString *kHIDRemoteCookieButtonCodeLUT = @"CookieButtonCodeLUT";
- NSString *kHIDRemoteHIDQueueInterface = @"HIDQueueInterface";
- NSString *kHIDRemoteServiceNotification = @"ServiceNotification";
- NSString *kHIDRemoteCFRunLoopSource = @"CFRunLoopSource";
- NSString *kHIDRemoteLastButtonPressed = @"LastButtonPressed";
- NSString *kHIDRemoteService = @"Service";
- NSString *kHIDRemoteSimulateHoldEventsTimer = @"SimulateHoldEventsTimer";
- NSString *kHIDRemoteSimulateHoldEventsOriginButtonCode = @"SimulateHoldEventsOriginButtonCode";
- NSString *kHIDRemoteAluminumRemoteSupportLevel = @"AluminumRemoteSupportLevel";
- NSString *kHIDRemoteAluminumRemoteSupportOnDemand = @"AluminumRemoteSupportLevelOnDemand";
- NSString *kHIDRemoteManufacturer = @"Manufacturer";
- NSString *kHIDRemoteProduct = @"Product";
- NSString *kHIDRemoteTransport = @"Transport";
- // Distributed notifications
- 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";
- NSString *kHIDRemoteDNStatusModeKey = @"Mode";
- NSString *kHIDRemoteDNStatusUnusedButtonCodesKey = @"UnusedButtonCodes";
- NSString *kHIDRemoteDNStatusActionKey = @"Action";
- NSString *kHIDRemoteDNStatusRemoteControlCountKey = @"RemoteControlCount";
- NSString *kHIDRemoteDNStatusReturnToPIDKey = @"ReturnToPID";
- NSString *kHIDRemoteDNStatusActionStart = @"start";
- NSString *kHIDRemoteDNStatusActionStop = @"stop";
- NSString *kHIDRemoteDNStatusActionUpdate = @"update";
- NSString *kHIDRemoteDNStatusActionNoNeed = @"noneed";
|