HIDRemote.m 68 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168
  1. //
  2. // HIDRemote.m
  3. // HIDRemote V1.4 (18th February 2015)
  4. //
  5. // Created by Felix Schwarz on 06.04.07.
  6. // Copyright 2007-2015 IOSPIRIT GmbH. All rights reserved.
  7. //
  8. // The latest version of this class is available at
  9. // http://www.iospirit.com/developers/hidremote/
  10. //
  11. // ** LICENSE *************************************************************************
  12. //
  13. // Copyright (c) 2007-2014 IOSPIRIT GmbH (http://www.iospirit.com/)
  14. // All rights reserved.
  15. //
  16. // Redistribution and use in source and binary forms, with or without modification,
  17. // are permitted provided that the following conditions are met:
  18. //
  19. // * Redistributions of source code must retain the above copyright notice, this list
  20. // of conditions and the following disclaimer.
  21. //
  22. // * Redistributions in binary form must reproduce the above copyright notice, this
  23. // list of conditions and the following disclaimer in the documentation and/or other
  24. // materials provided with the distribution.
  25. //
  26. // * Neither the name of IOSPIRIT GmbH nor the names of its contributors may be used to
  27. // endorse or promote products derived from this software without specific prior
  28. // written permission.
  29. //
  30. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
  31. // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
  33. // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  34. // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  35. // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  36. // BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  37. // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  38. // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  39. // DAMAGE.
  40. //
  41. // ************************************************************************************
  42. // ************************************************************************************
  43. // ********************************** DOCUMENTATION ***********************************
  44. // ************************************************************************************
  45. //
  46. // - a reference is available at http://www.iospirit.com/developers/hidremote/reference/
  47. // - for a guide, please see http://www.iospirit.com/developers/hidremote/guide/
  48. //
  49. // ************************************************************************************
  50. #import "HIDRemote.h"
  51. // Callback Prototypes
  52. static void HIDEventCallback( void * target,
  53. IOReturn result,
  54. void * refCon,
  55. void * sender);
  56. static void ServiceMatchingCallback( void *refCon,
  57. io_iterator_t iterator);
  58. static void ServiceNotificationCallback(void * refCon,
  59. io_service_t service,
  60. natural_t messageType,
  61. void * messageArgument);
  62. static void SecureInputNotificationCallback( void * refCon,
  63. io_service_t service,
  64. natural_t messageType,
  65. void * messageArgument);
  66. // Shared HIDRemote instance
  67. static HIDRemote *sHIDRemote = nil;
  68. @implementation HIDRemote
  69. #pragma mark - Init, dealloc & shared instance
  70. + (HIDRemote *)sharedHIDRemote
  71. {
  72. if (sHIDRemote==nil)
  73. {
  74. sHIDRemote = [[HIDRemote alloc] init];
  75. }
  76. return (sHIDRemote);
  77. }
  78. - (id)init
  79. {
  80. if ((self = [super init]) != nil)
  81. {
  82. #ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING
  83. _runOnThread = [[NSThread currentThread] retain];
  84. #endif
  85. // Detect application becoming active/inactive
  86. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:) name:NSApplicationDidBecomeActiveNotification object:[NSApplication sharedApplication]];
  87. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:) name:NSApplicationWillResignActiveNotification object:[NSApplication sharedApplication]];
  88. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:) name:NSApplicationWillTerminateNotification object:[NSApplication sharedApplication]];
  89. // Handle distributed notifications
  90. _pidString = [[NSString alloc] initWithFormat:@"%d", getpid()];
  91. [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemotePing object:nil];
  92. [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteRetry object:kHIDRemoteDNHIDRemoteRetryGlobalObject];
  93. [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteRetry object:_pidString];
  94. // Enabled by default: simulate hold events for plus/minus
  95. _simulateHoldEvents = YES;
  96. // 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)
  97. _secureEventInputWorkAround = YES;
  98. _secureInputNotification = 0;
  99. // Initialize instance variables
  100. _lastSeenRemoteID = -1;
  101. _lastSeenModel = kHIDRemoteModelUndetermined;
  102. _unusedButtonCodes = [[NSMutableArray alloc] init];
  103. _exclusiveLockLending = NO;
  104. _sendExclusiveResourceReuseNotification = YES;
  105. _applicationIsTerminating = NO;
  106. // Send status notifications
  107. _sendStatusNotifications = YES;
  108. }
  109. return (self);
  110. }
  111. - (void)dealloc
  112. {
  113. [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:[NSApplication sharedApplication]];
  114. [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillResignActiveNotification object:[NSApplication sharedApplication]];
  115. [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationDidBecomeActiveNotification object:[NSApplication sharedApplication]];
  116. [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemotePing object:nil];
  117. [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteRetry object:kHIDRemoteDNHIDRemoteRetryGlobalObject];
  118. [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteRetry object:_pidString];
  119. [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:nil object:nil]; /* As demanded by the documentation for -[NSDistributedNotificationCenter removeObserver:name:object:] */
  120. [self stopRemoteControl];
  121. [self setExclusiveLockLendingEnabled:NO];
  122. [self setDelegate:nil];
  123. if (_unusedButtonCodes != nil)
  124. {
  125. [_unusedButtonCodes release];
  126. _unusedButtonCodes = nil;
  127. }
  128. #ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING
  129. [_runOnThread release];
  130. _runOnThread = nil;
  131. #endif
  132. [_pidString release];
  133. _pidString = nil;
  134. [super dealloc];
  135. }
  136. #pragma mark - PUBLIC: System Information
  137. + (BOOL)isCandelairInstalled
  138. {
  139. mach_port_t masterPort = 0;
  140. kern_return_t kernResult;
  141. io_service_t matchingService = 0;
  142. BOOL isInstalled = NO;
  143. kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
  144. if ((kernResult!=kIOReturnSuccess) || (masterPort==0)) { return(NO); }
  145. if ((matchingService = IOServiceGetMatchingService(masterPort, IOServiceMatching("IOSPIRITIRController"))) != 0)
  146. {
  147. isInstalled = YES;
  148. IOObjectRelease((io_object_t) matchingService);
  149. }
  150. mach_port_deallocate(mach_task_self(), masterPort);
  151. return (isInstalled);
  152. }
  153. + (BOOL)isCandelairInstallationRequiredForRemoteMode:(HIDRemoteMode)remoteMode
  154. {
  155. // Determine OS version
  156. switch ([self OSXVersion])
  157. {
  158. case 0x1060: // OS 10.6
  159. case 0x1061: // OS 10.6.1
  160. // OS X 10.6(.0) and OS X 10.6.1 require the Candelair driver for to be installed,
  161. // so that third party apps can acquire an exclusive lock on the receiver HID Device
  162. // via IOKit.
  163. switch (remoteMode)
  164. {
  165. case kHIDRemoteModeExclusive:
  166. case kHIDRemoteModeExclusiveAuto:
  167. if (![self isCandelairInstalled])
  168. {
  169. return (YES);
  170. }
  171. break;
  172. default:
  173. return (NO);
  174. break;
  175. }
  176. break;
  177. }
  178. return (NO);
  179. }
  180. // Drop-in replacement for Gestalt(gestaltSystemVersion, &osXVersion) that avoids use of Gestalt for code targeting 10.10 or later
  181. + (SInt32)OSXVersion
  182. {
  183. static SInt32 sHRGestaltOSXVersion = 0;
  184. if (sHRGestaltOSXVersion==0)
  185. {
  186. #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_9
  187. // Code for builds targeting OS X 10.10+
  188. NSOperatingSystemVersion osVersion;
  189. osVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
  190. sHRGestaltOSXVersion = (SInt32)(0x01000 | ((osVersion.majorVersion-10)<<8) | (osVersion.minorVersion<<4) | osVersion.patchVersion);
  191. #else
  192. #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_9
  193. // Code for builds using the OS X 10.10 SDK or later
  194. NSOperatingSystemVersion osVersion;
  195. if ([[NSProcessInfo processInfo] respondsToSelector:@selector(operatingSystemVersion)])
  196. {
  197. osVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
  198. sHRGestaltOSXVersion = (SInt32)(0x01000 | ((osVersion.majorVersion-10)<<8) | (osVersion.minorVersion<<4) | osVersion.patchVersion);
  199. }
  200. else
  201. {
  202. #pragma clang diagnostic push
  203. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  204. Gestalt (gestaltSystemVersion, &sHRGestaltOSXVersion);
  205. #pragma clang diagnostic pop
  206. }
  207. #else /* MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_9 */
  208. // Code for builds using an SDK older than 10.10
  209. #pragma clang diagnostic push
  210. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  211. Gestalt (gestaltSystemVersion, &sHRGestaltOSXVersion);
  212. #pragma clang diagnostic pop
  213. #endif /* MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_9 */
  214. #endif /* MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_9 */
  215. }
  216. return (sHRGestaltOSXVersion);
  217. }
  218. - (HIDRemoteAluminumRemoteSupportLevel)aluminiumRemoteSystemSupportLevel
  219. {
  220. HIDRemoteAluminumRemoteSupportLevel supportLevel = kHIDRemoteAluminumRemoteSupportLevelNone;
  221. NSEnumerator *attribDictsEnum;
  222. NSDictionary *hidAttribsDict;
  223. attribDictsEnum = [_serviceAttribMap objectEnumerator];
  224. while ((hidAttribsDict = [attribDictsEnum nextObject]) != nil)
  225. {
  226. NSNumber *deviceSupportLevel;
  227. if ((deviceSupportLevel = [hidAttribsDict objectForKey:kHIDRemoteAluminumRemoteSupportLevel]) != nil)
  228. {
  229. if ([deviceSupportLevel intValue] > (int)supportLevel)
  230. {
  231. supportLevel = [deviceSupportLevel intValue];
  232. }
  233. }
  234. }
  235. return (supportLevel);
  236. }
  237. #pragma mark - PUBLIC: Interface / API
  238. - (BOOL)startRemoteControl:(HIDRemoteMode)hidRemoteMode
  239. {
  240. if ((_mode == kHIDRemoteModeNone) && (hidRemoteMode != kHIDRemoteModeNone))
  241. {
  242. kern_return_t kernReturn;
  243. CFMutableDictionaryRef matchDict=NULL;
  244. io_service_t rootService;
  245. do
  246. {
  247. // Get IOKit master port
  248. kernReturn = IOMasterPort(bootstrap_port, &_masterPort);
  249. if ((kernReturn!=kIOReturnSuccess) || (_masterPort==0)) { break; }
  250. // Setup notification port
  251. _notifyPort = IONotificationPortCreate(_masterPort);
  252. if ((_notifyRLSource = IONotificationPortGetRunLoopSource(_notifyPort)) != NULL)
  253. {
  254. CFRunLoopAddSource( CFRunLoopGetCurrent(),
  255. _notifyRLSource,
  256. kCFRunLoopCommonModes);
  257. }
  258. else
  259. {
  260. break;
  261. }
  262. // Setup SecureInput notification
  263. if ((hidRemoteMode == kHIDRemoteModeExclusive) || (hidRemoteMode == kHIDRemoteModeExclusiveAuto))
  264. {
  265. if ((rootService = IORegistryEntryFromPath(_masterPort, kIOServicePlane ":/")) != 0)
  266. {
  267. kernReturn = IOServiceAddInterestNotification( _notifyPort,
  268. rootService,
  269. kIOBusyInterest,
  270. SecureInputNotificationCallback,
  271. (void *)self,
  272. &_secureInputNotification);
  273. if (kernReturn != kIOReturnSuccess) { break; }
  274. [self _updateSessionInformation];
  275. }
  276. else
  277. {
  278. break;
  279. }
  280. }
  281. // Setup notification matching dict
  282. matchDict = IOServiceMatching(kIOHIDDeviceKey);
  283. CFRetain(matchDict);
  284. // Actually add notification
  285. kernReturn = IOServiceAddMatchingNotification( _notifyPort,
  286. kIOFirstMatchNotification,
  287. matchDict, // one reference count consumed by this call
  288. ServiceMatchingCallback,
  289. (void *) self,
  290. &_matchingServicesIterator);
  291. if (kernReturn != kIOReturnSuccess) { break; }
  292. // Setup serviceAttribMap
  293. _serviceAttribMap = [[NSMutableDictionary alloc] init];
  294. if (_serviceAttribMap==nil) { break; }
  295. // Phew .. everything went well!
  296. _mode = hidRemoteMode;
  297. CFRelease(matchDict);
  298. [self _serviceMatching:_matchingServicesIterator];
  299. [self _postStatusWithAction:kHIDRemoteDNStatusActionStart];
  300. // Register for system wake notifications
  301. [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(_computerDidWake:) name:NSWorkspaceDidWakeNotification object:nil];
  302. return (YES);
  303. }while(0);
  304. // An error occured. Do necessary clean up.
  305. if (matchDict!=NULL)
  306. {
  307. CFRelease(matchDict);
  308. matchDict = NULL;
  309. }
  310. [self stopRemoteControl];
  311. }
  312. return (NO);
  313. }
  314. - (void)stopRemoteControl
  315. {
  316. UInt32 serviceCount = 0;
  317. _autoRecover = NO;
  318. _isStopping = YES;
  319. if (_autoRecoveryTimer!=nil)
  320. {
  321. [_autoRecoveryTimer invalidate];
  322. [_autoRecoveryTimer release];
  323. _autoRecoveryTimer = nil;
  324. }
  325. if (_serviceAttribMap!=nil)
  326. {
  327. NSDictionary *cloneDict = [[NSDictionary alloc] initWithDictionary:_serviceAttribMap];
  328. if (cloneDict!=nil)
  329. {
  330. NSEnumerator *mapKeyEnum = [cloneDict keyEnumerator];
  331. NSNumber *serviceValue;
  332. while ((serviceValue = [mapKeyEnum nextObject]) != nil)
  333. {
  334. [self _destructService:(io_object_t)[serviceValue unsignedIntValue]];
  335. serviceCount++;
  336. };
  337. [cloneDict release];
  338. cloneDict = nil;
  339. }
  340. [_serviceAttribMap release];
  341. _serviceAttribMap = nil;
  342. }
  343. if (_matchingServicesIterator!=0)
  344. {
  345. IOObjectRelease((io_object_t) _matchingServicesIterator);
  346. _matchingServicesIterator = 0;
  347. }
  348. if (_secureInputNotification!=0)
  349. {
  350. IOObjectRelease((io_object_t) _secureInputNotification);
  351. _secureInputNotification = 0;
  352. }
  353. if (_notifyRLSource!=NULL)
  354. {
  355. CFRunLoopSourceInvalidate(_notifyRLSource);
  356. _notifyRLSource = NULL;
  357. }
  358. if (_notifyPort!=NULL)
  359. {
  360. IONotificationPortDestroy(_notifyPort);
  361. _notifyPort = NULL;
  362. }
  363. if (_masterPort!=0)
  364. {
  365. mach_port_deallocate(mach_task_self(), _masterPort);
  366. _masterPort = 0;
  367. }
  368. if (_returnToPID!=nil)
  369. {
  370. [_returnToPID release];
  371. _returnToPID = nil;
  372. }
  373. if (_mode!=kHIDRemoteModeNone)
  374. {
  375. // Post status
  376. [self _postStatusWithAction:kHIDRemoteDNStatusActionStop];
  377. if (_sendStatusNotifications)
  378. {
  379. // 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
  380. if (((_mode==kHIDRemoteModeExclusive) || (_mode==kHIDRemoteModeExclusiveAuto)) && (_sendExclusiveResourceReuseNotification==YES) && (_exclusiveLockLending==NO) && (serviceCount>0))
  381. {
  382. _mode = kHIDRemoteModeNone;
  383. if (!_isRestarting)
  384. {
  385. [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kHIDRemoteDNHIDRemoteRetry
  386. object:kHIDRemoteDNHIDRemoteRetryGlobalObject
  387. userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
  388. [NSNumber numberWithUnsignedInt:(unsigned int)getpid()], kHIDRemoteDNStatusPIDKey,
  389. [[NSBundle mainBundle] bundleIdentifier], (NSString *)kCFBundleIdentifierKey,
  390. nil]
  391. deliverImmediately:YES];
  392. }
  393. }
  394. }
  395. // Unregister from system wake notifications
  396. [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceDidWakeNotification object:nil];
  397. }
  398. _mode = kHIDRemoteModeNone;
  399. _isStopping = NO;
  400. }
  401. - (BOOL)isStarted
  402. {
  403. return (_mode != kHIDRemoteModeNone);
  404. }
  405. - (HIDRemoteMode)startedInMode
  406. {
  407. return (_mode);
  408. }
  409. - (unsigned)activeRemoteControlCount
  410. {
  411. return ([_serviceAttribMap count]);
  412. }
  413. - (SInt32)lastSeenRemoteControlID
  414. {
  415. return (_lastSeenRemoteID);
  416. }
  417. - (HIDRemoteModel)lastSeenModel
  418. {
  419. return (_lastSeenModel);
  420. }
  421. - (void)setLastSeenModel:(HIDRemoteModel)aModel
  422. {
  423. _lastSeenModel = aModel;
  424. }
  425. - (void)setSimulateHoldEvents:(BOOL)newSimulateHoldEvents
  426. {
  427. _simulateHoldEvents = newSimulateHoldEvents;
  428. }
  429. - (BOOL)simulateHoldEvents
  430. {
  431. return (_simulateHoldEvents);
  432. }
  433. - (NSArray *)unusedButtonCodes
  434. {
  435. return (_unusedButtonCodes);
  436. }
  437. - (void)setUnusedButtonCodes:(NSArray *)newArrayWithUnusedButtonCodesAsNSNumbers
  438. {
  439. [newArrayWithUnusedButtonCodesAsNSNumbers retain];
  440. [_unusedButtonCodes release];
  441. _unusedButtonCodes = newArrayWithUnusedButtonCodesAsNSNumbers;
  442. [self _postStatusWithAction:kHIDRemoteDNStatusActionUpdate];
  443. }
  444. - (void)setDelegate:(NSObject <HIDRemoteDelegate> *)newDelegate
  445. {
  446. _delegate = newDelegate;
  447. }
  448. - (NSObject <HIDRemoteDelegate> *)delegate
  449. {
  450. return (_delegate);
  451. }
  452. #pragma mark - PUBLIC: Expert APIs
  453. - (void)setEnableSecureEventInputWorkaround:(BOOL)newEnableSecureEventInputWorkaround
  454. {
  455. _secureEventInputWorkAround = newEnableSecureEventInputWorkaround;
  456. }
  457. - (BOOL)enableSecureEventInputWorkaround
  458. {
  459. return (_secureEventInputWorkAround);
  460. }
  461. - (void)setExclusiveLockLendingEnabled:(BOOL)newExclusiveLockLendingEnabled
  462. {
  463. if (newExclusiveLockLendingEnabled != _exclusiveLockLending)
  464. {
  465. _exclusiveLockLending = newExclusiveLockLendingEnabled;
  466. if (_exclusiveLockLending)
  467. {
  468. [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteStatus object:nil];
  469. }
  470. else
  471. {
  472. [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteStatus object:nil];
  473. [_waitForReturnByPID release];
  474. _waitForReturnByPID = nil;
  475. }
  476. }
  477. }
  478. - (BOOL)exclusiveLockLendingEnabled
  479. {
  480. return (_exclusiveLockLending);
  481. }
  482. - (void)setSendExclusiveResourceReuseNotification:(BOOL)newSendExclusiveResourceReuseNotification
  483. {
  484. _sendExclusiveResourceReuseNotification = newSendExclusiveResourceReuseNotification;
  485. }
  486. - (BOOL)sendExclusiveResourceReuseNotification
  487. {
  488. return (_sendExclusiveResourceReuseNotification);
  489. }
  490. - (BOOL)isApplicationTerminating
  491. {
  492. return (_applicationIsTerminating);
  493. }
  494. - (BOOL)isStopping
  495. {
  496. return (_isStopping);
  497. }
  498. #pragma mark - PRIVATE: Application becomes active / inactive handling for kHIDRemoteModeExclusiveAuto
  499. - (void)_appStatusChanged:(NSNotification *)notification
  500. {
  501. #ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING
  502. if ([self respondsToSelector:@selector(performSelector:onThread:withObject:waitUntilDone:)]) // OS X 10.5+ only
  503. {
  504. if ([NSThread currentThread] != _runOnThread)
  505. {
  506. if ([[notification name] isEqual:NSApplicationDidBecomeActiveNotification])
  507. {
  508. if (!_autoRecover)
  509. {
  510. return;
  511. }
  512. }
  513. if ([[notification name] isEqual:NSApplicationWillResignActiveNotification])
  514. {
  515. if (_mode != kHIDRemoteModeExclusiveAuto)
  516. {
  517. return;
  518. }
  519. }
  520. [self performSelector:@selector(_appStatusChanged:) onThread:_runOnThread withObject:notification waitUntilDone:[[notification name] isEqual:NSApplicationWillTerminateNotification]];
  521. return;
  522. }
  523. }
  524. #endif
  525. if (notification!=nil)
  526. {
  527. if (_autoRecoveryTimer!=nil)
  528. {
  529. [_autoRecoveryTimer invalidate];
  530. [_autoRecoveryTimer release];
  531. _autoRecoveryTimer = nil;
  532. }
  533. if ([[notification name] isEqual:NSApplicationDidBecomeActiveNotification])
  534. {
  535. if (_autoRecover)
  536. {
  537. // Delay autorecover by 0.1 to avoid race conditions
  538. if ((_autoRecoveryTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:0.1] interval:0.1 target:self selector:@selector(_delayedAutoRecovery:) userInfo:nil repeats:NO]) != nil)
  539. {
  540. // Using CFRunLoopAddTimer instead of [[NSRunLoop currentRunLoop] addTimer:.. for consistency with run loop modes.
  541. // The kCFRunLoopCommonModes counterpart NSRunLoopCommonModes is only available in 10.5 and later, whereas this code
  542. // is designed to be also compatible with 10.4. CFRunLoopTimerRef is "toll-free-bridged" with NSTimer since 10.0.
  543. CFRunLoopAddTimer(CFRunLoopGetCurrent(), (CFRunLoopTimerRef)_autoRecoveryTimer, kCFRunLoopCommonModes);
  544. }
  545. }
  546. }
  547. if ([[notification name] isEqual:NSApplicationWillResignActiveNotification])
  548. {
  549. if (_mode == kHIDRemoteModeExclusiveAuto)
  550. {
  551. [self stopRemoteControl];
  552. _autoRecover = YES;
  553. }
  554. }
  555. if ([[notification name] isEqual:NSApplicationWillTerminateNotification])
  556. {
  557. _applicationIsTerminating = YES;
  558. if ([self isStarted])
  559. {
  560. [self stopRemoteControl];
  561. }
  562. }
  563. }
  564. }
  565. - (void)_delayedAutoRecovery:(NSTimer *)aTimer
  566. {
  567. [_autoRecoveryTimer invalidate];
  568. [_autoRecoveryTimer release];
  569. _autoRecoveryTimer = nil;
  570. if (_autoRecover)
  571. {
  572. [self startRemoteControl:kHIDRemoteModeExclusiveAuto];
  573. _autoRecover = NO;
  574. }
  575. }
  576. #pragma mark - PRIVATE: Distributed notifiations handling
  577. - (void)_postStatusWithAction:(NSString *)action
  578. {
  579. if (_sendStatusNotifications)
  580. {
  581. [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kHIDRemoteDNHIDRemoteStatus
  582. object:((_pidString!=nil) ? _pidString : [NSString stringWithFormat:@"%d",getpid()])
  583. userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
  584. [NSNumber numberWithInt:1], kHIDRemoteDNStatusHIDRemoteVersionKey,
  585. [NSNumber numberWithUnsignedInt:(unsigned int)getpid()], kHIDRemoteDNStatusPIDKey,
  586. [NSNumber numberWithInt:(int)_mode], kHIDRemoteDNStatusModeKey,
  587. [NSNumber numberWithUnsignedInt:(unsigned int)[self activeRemoteControlCount]], kHIDRemoteDNStatusRemoteControlCountKey,
  588. ((_unusedButtonCodes!=nil) ? _unusedButtonCodes : [NSArray array]), kHIDRemoteDNStatusUnusedButtonCodesKey,
  589. action, kHIDRemoteDNStatusActionKey,
  590. [[NSBundle mainBundle] bundleIdentifier], (NSString *)kCFBundleIdentifierKey,
  591. _returnToPID, kHIDRemoteDNStatusReturnToPIDKey,
  592. nil]
  593. deliverImmediately:YES
  594. ];
  595. }
  596. }
  597. - (void)_handleNotifications:(NSNotification *)notification
  598. {
  599. NSString *notificationName;
  600. #ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING
  601. if ([self respondsToSelector:@selector(performSelector:onThread:withObject:waitUntilDone:)]) // OS X 10.5+ only
  602. {
  603. if ([NSThread currentThread] != _runOnThread)
  604. {
  605. [self performSelector:@selector(_handleNotifications:) onThread:_runOnThread withObject:notification waitUntilDone:NO];
  606. return;
  607. }
  608. }
  609. #endif
  610. if ((notification!=nil) && ((notificationName = [notification name]) != nil))
  611. {
  612. if ([notificationName isEqual:kHIDRemoteDNHIDRemotePing])
  613. {
  614. [self _postStatusWithAction:kHIDRemoteDNStatusActionUpdate];
  615. }
  616. if ([notificationName isEqual:kHIDRemoteDNHIDRemoteRetry])
  617. {
  618. if ([self isStarted])
  619. {
  620. BOOL retry = YES;
  621. // Ignore our own global retry broadcasts
  622. if ([[notification object] isEqual:kHIDRemoteDNHIDRemoteRetryGlobalObject])
  623. {
  624. NSNumber *fromPID;
  625. if ((fromPID = [[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey]) != nil)
  626. {
  627. if (getpid() == (int)[fromPID unsignedIntValue])
  628. {
  629. retry = NO;
  630. }
  631. }
  632. }
  633. if (retry)
  634. {
  635. if (([self delegate] != nil) &&
  636. ([[self delegate] respondsToSelector:@selector(hidRemote:shouldRetryExclusiveLockWithInfo:)]))
  637. {
  638. retry = [[self delegate] hidRemote:self shouldRetryExclusiveLockWithInfo:[notification userInfo]];
  639. }
  640. }
  641. if (retry)
  642. {
  643. HIDRemoteMode restartInMode = _mode;
  644. if (restartInMode != kHIDRemoteModeNone)
  645. {
  646. _isRestarting = YES;
  647. [self stopRemoteControl];
  648. [_returnToPID release];
  649. _returnToPID = nil;
  650. [self startRemoteControl:restartInMode];
  651. _isRestarting = NO;
  652. if (restartInMode != kHIDRemoteModeShared)
  653. {
  654. _returnToPID = [[[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey] retain];
  655. }
  656. }
  657. }
  658. else
  659. {
  660. NSNumber *cacheReturnPID = _returnToPID;
  661. _returnToPID = [[[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey] retain];
  662. [self _postStatusWithAction:kHIDRemoteDNStatusActionNoNeed];
  663. [_returnToPID release];
  664. _returnToPID = cacheReturnPID;
  665. }
  666. }
  667. }
  668. if (_exclusiveLockLending)
  669. {
  670. if ([notificationName isEqual:kHIDRemoteDNHIDRemoteStatus])
  671. {
  672. NSString *action;
  673. if ((action = [[notification userInfo] objectForKey:kHIDRemoteDNStatusActionKey]) != nil)
  674. {
  675. if ((_mode == kHIDRemoteModeNone) && (_waitForReturnByPID!=nil))
  676. {
  677. NSNumber *pidNumber, *returnToPIDNumber;
  678. if ((pidNumber = [[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey]) != nil)
  679. {
  680. returnToPIDNumber = [[notification userInfo] objectForKey:kHIDRemoteDNStatusReturnToPIDKey];
  681. if ([action isEqual:kHIDRemoteDNStatusActionStart])
  682. {
  683. if ([pidNumber isEqual:_waitForReturnByPID])
  684. {
  685. NSNumber *startMode;
  686. if ((startMode = [[notification userInfo] objectForKey:kHIDRemoteDNStatusModeKey]) != nil)
  687. {
  688. if ([startMode intValue] == kHIDRemoteModeShared)
  689. {
  690. returnToPIDNumber = [NSNumber numberWithInt:getpid()];
  691. action = kHIDRemoteDNStatusActionNoNeed;
  692. }
  693. }
  694. }
  695. }
  696. if (returnToPIDNumber != nil)
  697. {
  698. if ([action isEqual:kHIDRemoteDNStatusActionStop] || [action isEqual:kHIDRemoteDNStatusActionNoNeed])
  699. {
  700. if ([pidNumber isEqual:_waitForReturnByPID] && ([returnToPIDNumber intValue] == getpid()))
  701. {
  702. [_waitForReturnByPID release];
  703. _waitForReturnByPID = nil;
  704. if (([self delegate] != nil) &&
  705. ([[self delegate] respondsToSelector:@selector(hidRemote:exclusiveLockReleasedByApplicationWithInfo:)]))
  706. {
  707. [[self delegate] hidRemote:self exclusiveLockReleasedByApplicationWithInfo:[notification userInfo]];
  708. }
  709. else
  710. {
  711. [self startRemoteControl:kHIDRemoteModeExclusive];
  712. }
  713. }
  714. }
  715. }
  716. }
  717. }
  718. if (_mode==kHIDRemoteModeExclusive)
  719. {
  720. if ([action isEqual:kHIDRemoteDNStatusActionStart])
  721. {
  722. NSNumber *originPID = [[notification userInfo] objectForKey:kHIDRemoteDNStatusPIDKey];
  723. BOOL lendLock = YES;
  724. if ([originPID intValue] != getpid())
  725. {
  726. if (([self delegate] != nil) &&
  727. ([[self delegate] respondsToSelector:@selector(hidRemote:lendExclusiveLockToApplicationWithInfo:)]))
  728. {
  729. lendLock = [[self delegate] hidRemote:self lendExclusiveLockToApplicationWithInfo:[notification userInfo]];
  730. }
  731. if (lendLock)
  732. {
  733. [_waitForReturnByPID release];
  734. _waitForReturnByPID = [originPID retain];
  735. if (_waitForReturnByPID != nil)
  736. {
  737. [self stopRemoteControl];
  738. [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kHIDRemoteDNHIDRemoteRetry
  739. object:[NSString stringWithFormat:@"%d", [_waitForReturnByPID intValue]]
  740. userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
  741. [NSNumber numberWithUnsignedInt:(unsigned int)getpid()], kHIDRemoteDNStatusPIDKey,
  742. [[NSBundle mainBundle] bundleIdentifier], (NSString *)kCFBundleIdentifierKey,
  743. nil]
  744. deliverImmediately:YES];
  745. }
  746. }
  747. }
  748. }
  749. }
  750. }
  751. }
  752. }
  753. }
  754. }
  755. - (void)_setSendStatusNotifications:(BOOL)doSend
  756. {
  757. _sendStatusNotifications = doSend;
  758. }
  759. - (BOOL)_sendStatusNotifications
  760. {
  761. return (_sendStatusNotifications);
  762. }
  763. #pragma mark - PRIVATE: Service setup and destruction
  764. - (BOOL)_prematchService:(io_object_t)service
  765. {
  766. BOOL serviceMatches = NO;
  767. NSString *ioClass;
  768. NSNumber *candelairHIDRemoteCompatibilityMask;
  769. if (service != 0)
  770. {
  771. // IOClass matching
  772. if ((ioClass = (NSString *)IORegistryEntryCreateCFProperty((io_registry_entry_t)service,
  773. CFSTR(kIOClassKey),
  774. kCFAllocatorDefault,
  775. 0)) != nil)
  776. {
  777. // Match on Apple's AppleIRController and old versions of the Remote Buddy IR Controller
  778. if ([ioClass isEqual:@"AppleIRController"] || [ioClass isEqual:@"RBIOKitAIREmu"])
  779. {
  780. CFTypeRef candelairHIDRemoteCompatibilityDevice;
  781. serviceMatches = YES;
  782. if ((candelairHIDRemoteCompatibilityDevice = IORegistryEntryCreateCFProperty((io_registry_entry_t)service, CFSTR("CandelairHIDRemoteCompatibilityDevice"), kCFAllocatorDefault, 0)) != NULL)
  783. {
  784. if (CFEqual(kCFBooleanTrue, candelairHIDRemoteCompatibilityDevice))
  785. {
  786. serviceMatches = NO;
  787. }
  788. CFRelease (candelairHIDRemoteCompatibilityDevice);
  789. }
  790. }
  791. // Match on the virtual IOSPIRIT IR Controller
  792. if ([ioClass isEqual:@"IOSPIRITIRController"])
  793. {
  794. serviceMatches = YES;
  795. }
  796. CFRelease((CFTypeRef)ioClass);
  797. }
  798. // Match on services that claim compatibility with the HID Remote class (Candelair or third-party) by having a property of CandelairHIDRemoteCompatibilityMask = 1 <Type: Number>
  799. if ((candelairHIDRemoteCompatibilityMask = (NSNumber *)IORegistryEntryCreateCFProperty((io_registry_entry_t)service, CFSTR("CandelairHIDRemoteCompatibilityMask"), kCFAllocatorDefault, 0)) != nil)
  800. {
  801. if ([candelairHIDRemoteCompatibilityMask isKindOfClass:[NSNumber class]])
  802. {
  803. if ([candelairHIDRemoteCompatibilityMask unsignedIntValue] & kHIDRemoteCompatibilityFlagsStandardHIDRemoteDevice)
  804. {
  805. serviceMatches = YES;
  806. }
  807. else
  808. {
  809. serviceMatches = NO;
  810. }
  811. }
  812. CFRelease((CFTypeRef)candelairHIDRemoteCompatibilityMask);
  813. }
  814. }
  815. if (([self delegate]!=nil) &&
  816. ([[self delegate] respondsToSelector:@selector(hidRemote:inspectNewHardwareWithService:prematchResult:)]))
  817. {
  818. serviceMatches = [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self inspectNewHardwareWithService:service prematchResult:serviceMatches];
  819. }
  820. return (serviceMatches);
  821. }
  822. - (HIDRemoteButtonCode)buttonCodeForUsage:(unsigned int)usage usagePage:(unsigned int)usagePage
  823. {
  824. HIDRemoteButtonCode buttonCode = kHIDRemoteButtonCodeNone;
  825. switch (usagePage)
  826. {
  827. case kHIDPage_Consumer:
  828. switch (usage)
  829. {
  830. case kHIDUsage_Csmr_MenuPick:
  831. // Aluminum Remote: Center
  832. buttonCode = (kHIDRemoteButtonCodeCenter|kHIDRemoteButtonCodeAluminumMask);
  833. break;
  834. case kHIDUsage_Csmr_ModeStep:
  835. // Aluminium Remote: Center Hold
  836. buttonCode = (kHIDRemoteButtonCodeCenterHold|kHIDRemoteButtonCodeAluminumMask);
  837. break;
  838. case kHIDUsage_Csmr_PlayOrPause:
  839. // Aluminum Remote: Play/Pause
  840. buttonCode = (kHIDRemoteButtonCodePlay|kHIDRemoteButtonCodeAluminumMask);
  841. break;
  842. case kHIDUsage_Csmr_Rewind:
  843. buttonCode = kHIDRemoteButtonCodeLeftHold;
  844. break;
  845. case kHIDUsage_Csmr_FastForward:
  846. buttonCode = kHIDRemoteButtonCodeRightHold;
  847. break;
  848. case kHIDUsage_Csmr_Menu:
  849. buttonCode = kHIDRemoteButtonCodeMenuHold;
  850. break;
  851. }
  852. break;
  853. case kHIDPage_GenericDesktop:
  854. switch (usage)
  855. {
  856. case kHIDUsage_GD_SystemAppMenu:
  857. buttonCode = kHIDRemoteButtonCodeMenu;
  858. break;
  859. case kHIDUsage_GD_SystemMenu:
  860. buttonCode = kHIDRemoteButtonCodeCenter;
  861. break;
  862. case kHIDUsage_GD_SystemMenuRight:
  863. buttonCode = kHIDRemoteButtonCodeRight;
  864. break;
  865. case kHIDUsage_GD_SystemMenuLeft:
  866. buttonCode = kHIDRemoteButtonCodeLeft;
  867. break;
  868. case kHIDUsage_GD_SystemMenuUp:
  869. buttonCode = kHIDRemoteButtonCodeUp;
  870. break;
  871. case kHIDUsage_GD_SystemMenuDown:
  872. buttonCode = kHIDRemoteButtonCodeDown;
  873. break;
  874. }
  875. break;
  876. case 0x06: /* Reserved */
  877. switch (usage)
  878. {
  879. case 0x22:
  880. buttonCode = kHIDRemoteButtonCodeIDChanged;
  881. break;
  882. }
  883. break;
  884. case 0xFF01: /* Vendor specific */
  885. switch (usage)
  886. {
  887. case 0x23:
  888. buttonCode = kHIDRemoteButtonCodeCenterHold;
  889. break;
  890. #ifdef _HIDREMOTE_EXTENSIONS
  891. #define _HIDREMOTE_EXTENSIONS_SECTION 2
  892. #include "HIDRemoteAdditions.h"
  893. #undef _HIDREMOTE_EXTENSIONS_SECTION
  894. #endif /* _HIDREMOTE_EXTENSIONS */
  895. }
  896. break;
  897. }
  898. return (buttonCode);
  899. }
  900. - (BOOL)_setupService:(io_object_t)service
  901. {
  902. kern_return_t kernResult;
  903. IOReturn returnCode;
  904. HRESULT hResult;
  905. SInt32 score;
  906. BOOL opened = NO, queueStarted = NO;
  907. IOHIDDeviceInterface122 **hidDeviceInterface = NULL;
  908. IOCFPlugInInterface **cfPluginInterface = NULL;
  909. IOHIDQueueInterface **hidQueueInterface = NULL;
  910. io_object_t serviceNotification = 0;
  911. CFRunLoopSourceRef queueEventSource = NULL;
  912. NSMutableDictionary *hidAttribsDict = nil;
  913. CFArrayRef hidElements = NULL;
  914. NSError *error = nil;
  915. UInt32 errorCode = 0;
  916. if (![self _prematchService:service])
  917. {
  918. return (NO);
  919. }
  920. do
  921. {
  922. // Create a plugin interface ..
  923. kernResult = IOCreatePlugInInterfaceForService( service,
  924. kIOHIDDeviceUserClientTypeID,
  925. kIOCFPlugInInterfaceID,
  926. &cfPluginInterface,
  927. &score);
  928. if (kernResult != kIOReturnSuccess)
  929. {
  930. error = [NSError errorWithDomain:NSMachErrorDomain code:kernResult userInfo:nil];
  931. errorCode = 1;
  932. break;
  933. }
  934. // .. use it to get the HID interface ..
  935. hResult = (*cfPluginInterface)->QueryInterface( cfPluginInterface,
  936. CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID122),
  937. (LPVOID)&hidDeviceInterface);
  938. if ((hResult!=S_OK) || (hidDeviceInterface==NULL))
  939. {
  940. error = [NSError errorWithDomain:NSMachErrorDomain code:hResult userInfo:nil];
  941. errorCode = 2;
  942. break;
  943. }
  944. // .. then open it ..
  945. switch (_mode)
  946. {
  947. case kHIDRemoteModeShared:
  948. hResult = (*hidDeviceInterface)->open(hidDeviceInterface, kIOHIDOptionsTypeNone);
  949. break;
  950. case kHIDRemoteModeExclusive:
  951. case kHIDRemoteModeExclusiveAuto:
  952. hResult = (*hidDeviceInterface)->open(hidDeviceInterface, kIOHIDOptionsTypeSeizeDevice);
  953. break;
  954. default:
  955. goto cleanUp; // Ugh! But there are no "double breaks" available in C AFAIK ..
  956. break;
  957. }
  958. if (hResult!=S_OK)
  959. {
  960. error = [NSError errorWithDomain:NSMachErrorDomain code:hResult userInfo:nil];
  961. errorCode = 3;
  962. break;
  963. }
  964. opened = YES;
  965. // .. query the HID elements ..
  966. returnCode = (*hidDeviceInterface)->copyMatchingElements(hidDeviceInterface,
  967. NULL,
  968. &hidElements);
  969. if ((returnCode != kIOReturnSuccess) || (hidElements==NULL))
  970. {
  971. error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
  972. errorCode = 4;
  973. break;
  974. }
  975. // Setup an event queue for HID events!
  976. hidQueueInterface = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
  977. if (hidQueueInterface == NULL)
  978. {
  979. error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil];
  980. errorCode = 5;
  981. break;
  982. }
  983. returnCode = (*hidQueueInterface)->create(hidQueueInterface, 0, 32);
  984. if (returnCode != kIOReturnSuccess)
  985. {
  986. error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
  987. errorCode = 6;
  988. break;
  989. }
  990. // Setup of attributes stored for this HID device
  991. hidAttribsDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
  992. [NSValue valueWithPointer:(const void *)cfPluginInterface], kHIDRemoteCFPluginInterface,
  993. [NSValue valueWithPointer:(const void *)hidDeviceInterface], kHIDRemoteHIDDeviceInterface,
  994. [NSValue valueWithPointer:(const void *)hidQueueInterface], kHIDRemoteHIDQueueInterface,
  995. nil];
  996. {
  997. UInt32 i, hidElementCnt = CFArrayGetCount(hidElements);
  998. NSMutableDictionary *cookieButtonCodeLUT = [[NSMutableDictionary alloc] init];
  999. NSMutableDictionary *cookieCount = [[NSMutableDictionary alloc] init];
  1000. if ((cookieButtonCodeLUT==nil) || (cookieCount==nil))
  1001. {
  1002. [cookieButtonCodeLUT release];
  1003. cookieButtonCodeLUT = nil;
  1004. [cookieCount release];
  1005. cookieCount = nil;
  1006. error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil];
  1007. errorCode = 7;
  1008. break;
  1009. }
  1010. // Analyze the HID elements and find matching elements
  1011. for (i=0;i<hidElementCnt;i++)
  1012. {
  1013. CFDictionaryRef hidDict;
  1014. NSNumber *usage, *usagePage, *cookie;
  1015. HIDRemoteButtonCode buttonCode = kHIDRemoteButtonCodeNone;
  1016. hidDict = CFArrayGetValueAtIndex(hidElements, i);
  1017. usage = (NSNumber *) CFDictionaryGetValue(hidDict, CFSTR(kIOHIDElementUsageKey));
  1018. usagePage = (NSNumber *) CFDictionaryGetValue(hidDict, CFSTR(kIOHIDElementUsagePageKey));
  1019. cookie = (NSNumber *) CFDictionaryGetValue(hidDict, CFSTR(kIOHIDElementCookieKey));
  1020. if ((usage!=nil) && (usagePage!=nil) && (cookie!=nil))
  1021. {
  1022. // Find the button codes for the ID combos
  1023. buttonCode = [self buttonCodeForUsage:[usage unsignedIntValue] usagePage:[usagePage unsignedIntValue]];
  1024. #ifdef _HIDREMOTE_EXTENSIONS
  1025. // Debug logging code
  1026. #define _HIDREMOTE_EXTENSIONS_SECTION 3
  1027. #include "HIDRemoteAdditions.h"
  1028. #undef _HIDREMOTE_EXTENSIONS_SECTION
  1029. #endif /* _HIDREMOTE_EXTENSIONS */
  1030. // Did record match?
  1031. if (buttonCode != kHIDRemoteButtonCodeNone)
  1032. {
  1033. NSString *pairString = [[NSString alloc] initWithFormat:@"%u_%u", [usagePage unsignedIntValue], [usage unsignedIntValue]];
  1034. NSNumber *buttonCodeNumber = [[NSNumber alloc] initWithUnsignedInt:(unsigned int)buttonCode];
  1035. #ifdef _HIDREMOTE_EXTENSIONS
  1036. // Debug logging code
  1037. #define _HIDREMOTE_EXTENSIONS_SECTION 4
  1038. #include "HIDRemoteAdditions.h"
  1039. #undef _HIDREMOTE_EXTENSIONS_SECTION
  1040. #endif /* _HIDREMOTE_EXTENSIONS */
  1041. [cookieCount setObject:buttonCodeNumber forKey:pairString];
  1042. [cookieButtonCodeLUT setObject:buttonCodeNumber forKey:cookie];
  1043. (*hidQueueInterface)->addElement(hidQueueInterface,
  1044. (IOHIDElementCookie) [cookie unsignedIntValue],
  1045. 0);
  1046. #ifdef _HIDREMOTE_EXTENSIONS
  1047. // Get current Apple Remote ID value
  1048. #define _HIDREMOTE_EXTENSIONS_SECTION 7
  1049. #include "HIDRemoteAdditions.h"
  1050. #undef _HIDREMOTE_EXTENSIONS_SECTION
  1051. #endif /* _HIDREMOTE_EXTENSIONS */
  1052. [buttonCodeNumber release];
  1053. [pairString release];
  1054. }
  1055. }
  1056. }
  1057. // Compare number of *unique* matches (thus the cookieCount dictionary) with required minimum
  1058. if ([cookieCount count] < 10)
  1059. {
  1060. [cookieButtonCodeLUT release];
  1061. cookieButtonCodeLUT = nil;
  1062. [cookieCount release];
  1063. cookieCount = nil;
  1064. error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil];
  1065. errorCode = 8;
  1066. break;
  1067. }
  1068. [hidAttribsDict setObject:cookieButtonCodeLUT forKey:kHIDRemoteCookieButtonCodeLUT];
  1069. [cookieButtonCodeLUT release];
  1070. cookieButtonCodeLUT = nil;
  1071. [cookieCount release];
  1072. cookieCount = nil;
  1073. }
  1074. // Finish setup of IOHIDQueueInterface with CFRunLoop
  1075. returnCode = (*hidQueueInterface)->createAsyncEventSource(hidQueueInterface, &queueEventSource);
  1076. if ((returnCode != kIOReturnSuccess) || (queueEventSource == NULL))
  1077. {
  1078. error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
  1079. errorCode = 9;
  1080. break;
  1081. }
  1082. returnCode = (*hidQueueInterface)->setEventCallout(hidQueueInterface, HIDEventCallback, (void *)((intptr_t)service), (void *)self);
  1083. if (returnCode != kIOReturnSuccess)
  1084. {
  1085. error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
  1086. errorCode = 10;
  1087. break;
  1088. }
  1089. CFRunLoopAddSource( CFRunLoopGetCurrent(),
  1090. queueEventSource,
  1091. kCFRunLoopCommonModes);
  1092. [hidAttribsDict setObject:[NSValue valueWithPointer:(const void *)queueEventSource] forKey:kHIDRemoteCFRunLoopSource];
  1093. returnCode = (*hidQueueInterface)->start(hidQueueInterface);
  1094. if (returnCode != kIOReturnSuccess)
  1095. {
  1096. error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
  1097. errorCode = 11;
  1098. break;
  1099. }
  1100. queueStarted = YES;
  1101. // Setup device notifications
  1102. returnCode = IOServiceAddInterestNotification( _notifyPort,
  1103. service,
  1104. kIOGeneralInterest,
  1105. ServiceNotificationCallback,
  1106. self,
  1107. &serviceNotification);
  1108. if ((returnCode != kIOReturnSuccess) || (serviceNotification==0))
  1109. {
  1110. error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil];
  1111. errorCode = 12;
  1112. break;
  1113. }
  1114. [hidAttribsDict setObject:[NSNumber numberWithUnsignedInt:(unsigned int)serviceNotification] forKey:kHIDRemoteServiceNotification];
  1115. // Retain service
  1116. if (IOObjectRetain(service) != kIOReturnSuccess)
  1117. {
  1118. error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil];
  1119. errorCode = 13;
  1120. break;
  1121. }
  1122. [hidAttribsDict setObject:[NSNumber numberWithUnsignedInt:(unsigned int)service] forKey:kHIDRemoteService];
  1123. // Get some (somewhat optional) infos on the device
  1124. {
  1125. CFStringRef product, manufacturer, transport;
  1126. if ((product = IORegistryEntryCreateCFProperty( (io_registry_entry_t)service,
  1127. (CFStringRef) @"Product",
  1128. kCFAllocatorDefault,
  1129. 0)) != NULL)
  1130. {
  1131. if (CFGetTypeID(product) == CFStringGetTypeID())
  1132. {
  1133. [hidAttribsDict setObject:(NSString *)product forKey:kHIDRemoteProduct];
  1134. }
  1135. CFRelease(product);
  1136. }
  1137. if ((manufacturer = IORegistryEntryCreateCFProperty( (io_registry_entry_t)service,
  1138. (CFStringRef) @"Manufacturer",
  1139. kCFAllocatorDefault,
  1140. 0)) != NULL)
  1141. {
  1142. if (CFGetTypeID(manufacturer) == CFStringGetTypeID())
  1143. {
  1144. [hidAttribsDict setObject:(NSString *)manufacturer forKey:kHIDRemoteManufacturer];
  1145. }
  1146. CFRelease(manufacturer);
  1147. }
  1148. if ((transport = IORegistryEntryCreateCFProperty( (io_registry_entry_t)service,
  1149. (CFStringRef) @"Transport",
  1150. kCFAllocatorDefault,
  1151. 0)) != NULL)
  1152. {
  1153. if (CFGetTypeID(transport) == CFStringGetTypeID())
  1154. {
  1155. [hidAttribsDict setObject:(NSString *)transport forKey:kHIDRemoteTransport];
  1156. }
  1157. CFRelease(transport);
  1158. }
  1159. }
  1160. // Determine Aluminum Remote support
  1161. {
  1162. CFNumberRef aluSupport;
  1163. HIDRemoteAluminumRemoteSupportLevel supportLevel = kHIDRemoteAluminumRemoteSupportLevelNone;
  1164. if ((_mode == kHIDRemoteModeExclusive) || (_mode == kHIDRemoteModeExclusiveAuto))
  1165. {
  1166. // Determine if this driver offers on-demand support for the Aluminum Remote (only relevant under OS versions < 10.6.2)
  1167. if ((aluSupport = IORegistryEntryCreateCFProperty((io_registry_entry_t)service,
  1168. (CFStringRef) @"AluminumRemoteSupportLevelOnDemand",
  1169. kCFAllocatorDefault,
  1170. 0)) != nil)
  1171. {
  1172. // There is => request the driver to enable it for us
  1173. if (IORegistryEntrySetCFProperty((io_registry_entry_t)service,
  1174. CFSTR("EnableAluminumRemoteSupportForMe"),
  1175. [NSDictionary dictionaryWithObjectsAndKeys:
  1176. [NSNumber numberWithLongLong:(long long)getpid()], @"pid",
  1177. [NSNumber numberWithLongLong:(long long)getuid()], @"uid",
  1178. nil]) == kIOReturnSuccess)
  1179. {
  1180. if (CFGetTypeID(aluSupport) == CFNumberGetTypeID())
  1181. {
  1182. supportLevel = (HIDRemoteAluminumRemoteSupportLevel) [(NSNumber *)aluSupport intValue];
  1183. }
  1184. [hidAttribsDict setObject:[NSNumber numberWithBool:YES] forKey:kHIDRemoteAluminumRemoteSupportOnDemand];
  1185. }
  1186. CFRelease(aluSupport);
  1187. }
  1188. }
  1189. if (supportLevel == kHIDRemoteAluminumRemoteSupportLevelNone)
  1190. {
  1191. if ((aluSupport = IORegistryEntryCreateCFProperty((io_registry_entry_t)service,
  1192. (CFStringRef) @"AluminumRemoteSupportLevel",
  1193. kCFAllocatorDefault,
  1194. 0)) != nil)
  1195. {
  1196. if (CFGetTypeID(aluSupport) == CFNumberGetTypeID())
  1197. {
  1198. supportLevel = (HIDRemoteAluminumRemoteSupportLevel) [(NSNumber *)aluSupport intValue];
  1199. }
  1200. CFRelease(aluSupport);
  1201. }
  1202. else
  1203. {
  1204. CFStringRef ioKitClassName;
  1205. if ((ioKitClassName = IORegistryEntryCreateCFProperty( (io_registry_entry_t)service,
  1206. CFSTR(kIOClassKey),
  1207. kCFAllocatorDefault,
  1208. 0)) != nil)
  1209. {
  1210. if ([(NSString *)ioKitClassName isEqual:@"AppleIRController"])
  1211. {
  1212. if ([HIDRemote OSXVersion] >= 0x1062)
  1213. {
  1214. // Support for the Aluminum Remote was added only with OS 10.6.2. Previous versions can not distinguish
  1215. // between the Center and the new, seperate Play/Pause button. They'll recognize both as presses of the
  1216. // "Center" button.
  1217. //
  1218. // You CAN, however, receive Aluminum Remote button presses even under OS 10.5 when using Remote Buddy's
  1219. // Virtual Remote. While Remote Buddy does support the Aluminum Remote across all OS releases it runs on,
  1220. // its Virtual Remote can only emulate Aluminum Remote button presses under OS 10.5 and up in order not to
  1221. // break compatibility with applications whose IR Remote code relies on driver internals. [13-Nov-09]
  1222. supportLevel = kHIDRemoteAluminumRemoteSupportLevelNative;
  1223. }
  1224. }
  1225. CFRelease(ioKitClassName);
  1226. }
  1227. }
  1228. }
  1229. [hidAttribsDict setObject:(NSNumber *)[NSNumber numberWithInt:(int)supportLevel] forKey:kHIDRemoteAluminumRemoteSupportLevel];
  1230. }
  1231. // Add it to the serviceAttribMap
  1232. [_serviceAttribMap setObject:hidAttribsDict forKey:[NSNumber numberWithUnsignedInt:(unsigned int)service]];
  1233. // And we're done with setup ..
  1234. if (([self delegate]!=nil) &&
  1235. ([[self delegate] respondsToSelector:@selector(hidRemote:foundNewHardwareWithAttributes:)]))
  1236. {
  1237. [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self foundNewHardwareWithAttributes:hidAttribsDict];
  1238. }
  1239. [hidAttribsDict release];
  1240. hidAttribsDict = nil;
  1241. return(YES);
  1242. }while(0);
  1243. cleanUp:
  1244. if (([self delegate]!=nil) &&
  1245. ([[self delegate] respondsToSelector:@selector(hidRemote:failedNewHardwareWithError:)]))
  1246. {
  1247. if (error!=nil)
  1248. {
  1249. error = [NSError errorWithDomain:[error domain]
  1250. code:[error code]
  1251. userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:errorCode] forKey:@"InternalErrorCode"]
  1252. ];
  1253. }
  1254. [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self failedNewHardwareWithError:error];
  1255. }
  1256. // An error occured or this device is not of interest .. cleanup ..
  1257. if (serviceNotification!=0)
  1258. {
  1259. IOObjectRelease(serviceNotification);
  1260. serviceNotification = 0;
  1261. }
  1262. if (queueEventSource!=NULL)
  1263. {
  1264. CFRunLoopSourceInvalidate(queueEventSource);
  1265. queueEventSource=NULL;
  1266. }
  1267. if (hidQueueInterface!=NULL)
  1268. {
  1269. if (queueStarted)
  1270. {
  1271. (*hidQueueInterface)->stop(hidQueueInterface);
  1272. }
  1273. (*hidQueueInterface)->dispose(hidQueueInterface);
  1274. (*hidQueueInterface)->Release(hidQueueInterface);
  1275. hidQueueInterface = NULL;
  1276. }
  1277. if (hidAttribsDict!=nil)
  1278. {
  1279. [hidAttribsDict release];
  1280. hidAttribsDict = nil;
  1281. }
  1282. if (hidElements!=NULL)
  1283. {
  1284. CFRelease(hidElements);
  1285. hidElements = NULL;
  1286. }
  1287. if (hidDeviceInterface!=NULL)
  1288. {
  1289. if (opened)
  1290. {
  1291. (*hidDeviceInterface)->close(hidDeviceInterface);
  1292. }
  1293. (*hidDeviceInterface)->Release(hidDeviceInterface);
  1294. // opened = NO;
  1295. hidDeviceInterface = NULL;
  1296. }
  1297. if (cfPluginInterface!=NULL)
  1298. {
  1299. IODestroyPlugInInterface(cfPluginInterface);
  1300. cfPluginInterface = NULL;
  1301. }
  1302. return (NO);
  1303. }
  1304. - (void)_destructService:(io_object_t)service
  1305. {
  1306. NSNumber *serviceValue;
  1307. NSMutableDictionary *serviceDict = NULL;
  1308. if ((serviceValue = [NSNumber numberWithUnsignedInt:(unsigned int)service]) == nil)
  1309. {
  1310. return;
  1311. }
  1312. serviceDict = [_serviceAttribMap objectForKey:serviceValue];
  1313. if (serviceDict!=nil)
  1314. {
  1315. IOHIDDeviceInterface122 **hidDeviceInterface = NULL;
  1316. IOCFPlugInInterface **cfPluginInterface = NULL;
  1317. IOHIDQueueInterface **hidQueueInterface = NULL;
  1318. io_object_t serviceNotification = 0;
  1319. CFRunLoopSourceRef queueEventSource = NULL;
  1320. io_object_t theService = 0;
  1321. NSMutableDictionary *cookieButtonMap = nil;
  1322. NSTimer *simulateHoldTimer = nil;
  1323. serviceNotification = (io_object_t) ([serviceDict objectForKey:kHIDRemoteServiceNotification] ? [[serviceDict objectForKey:kHIDRemoteServiceNotification] unsignedIntValue] : 0);
  1324. theService = (io_object_t) ([serviceDict objectForKey:kHIDRemoteService] ? [[serviceDict objectForKey:kHIDRemoteService] unsignedIntValue] : 0);
  1325. queueEventSource = (CFRunLoopSourceRef) ([serviceDict objectForKey:kHIDRemoteCFRunLoopSource] ? [[serviceDict objectForKey:kHIDRemoteCFRunLoopSource] pointerValue] : NULL);
  1326. hidQueueInterface = (IOHIDQueueInterface **) ([serviceDict objectForKey:kHIDRemoteHIDQueueInterface] ? [[serviceDict objectForKey:kHIDRemoteHIDQueueInterface] pointerValue] : NULL);
  1327. hidDeviceInterface = (IOHIDDeviceInterface122 **) ([serviceDict objectForKey:kHIDRemoteHIDDeviceInterface] ? [[serviceDict objectForKey:kHIDRemoteHIDDeviceInterface] pointerValue] : NULL);
  1328. cfPluginInterface = (IOCFPlugInInterface **) ([serviceDict objectForKey:kHIDRemoteCFPluginInterface] ? [[serviceDict objectForKey:kHIDRemoteCFPluginInterface] pointerValue] : NULL);
  1329. cookieButtonMap = (NSMutableDictionary *) [serviceDict objectForKey:kHIDRemoteCookieButtonCodeLUT];
  1330. simulateHoldTimer = (NSTimer *) [serviceDict objectForKey:kHIDRemoteSimulateHoldEventsTimer];
  1331. [serviceDict retain];
  1332. [_serviceAttribMap removeObjectForKey:serviceValue];
  1333. if (([serviceDict objectForKey:kHIDRemoteAluminumRemoteSupportOnDemand]!=nil) && [[serviceDict objectForKey:kHIDRemoteAluminumRemoteSupportOnDemand] boolValue] && (theService != 0))
  1334. {
  1335. // 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
  1336. IORegistryEntrySetCFProperty( (io_registry_entry_t)theService,
  1337. CFSTR("DisableAluminumRemoteSupportForMe"),
  1338. [NSDictionary dictionaryWithObjectsAndKeys:
  1339. [NSNumber numberWithLongLong:(long long)getpid()], @"pid",
  1340. [NSNumber numberWithLongLong:(long long)getuid()], @"uid",
  1341. nil]);
  1342. }
  1343. if (([self delegate]!=nil) &&
  1344. ([[self delegate] respondsToSelector:@selector(hidRemote:releasedHardwareWithAttributes:)]))
  1345. {
  1346. [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self releasedHardwareWithAttributes:serviceDict];
  1347. }
  1348. if (simulateHoldTimer!=nil)
  1349. {
  1350. [simulateHoldTimer invalidate];
  1351. }
  1352. if (serviceNotification!=0)
  1353. {
  1354. IOObjectRelease(serviceNotification);
  1355. }
  1356. if (queueEventSource!=NULL)
  1357. {
  1358. CFRunLoopRemoveSource( CFRunLoopGetCurrent(),
  1359. queueEventSource,
  1360. kCFRunLoopCommonModes);
  1361. }
  1362. if ((hidQueueInterface!=NULL) && (cookieButtonMap!=nil))
  1363. {
  1364. NSEnumerator *cookieEnum = [cookieButtonMap keyEnumerator];
  1365. NSNumber *cookie;
  1366. while ((cookie = [cookieEnum nextObject]) != nil)
  1367. {
  1368. if ((*hidQueueInterface)->hasElement(hidQueueInterface, (IOHIDElementCookie) [cookie unsignedIntValue]))
  1369. {
  1370. (*hidQueueInterface)->removeElement(hidQueueInterface,
  1371. (IOHIDElementCookie) [cookie unsignedIntValue]);
  1372. }
  1373. };
  1374. }
  1375. if (hidQueueInterface!=NULL)
  1376. {
  1377. (*hidQueueInterface)->stop(hidQueueInterface);
  1378. (*hidQueueInterface)->dispose(hidQueueInterface);
  1379. (*hidQueueInterface)->Release(hidQueueInterface);
  1380. }
  1381. if (hidDeviceInterface!=NULL)
  1382. {
  1383. (*hidDeviceInterface)->close(hidDeviceInterface);
  1384. (*hidDeviceInterface)->Release(hidDeviceInterface);
  1385. }
  1386. if (cfPluginInterface!=NULL)
  1387. {
  1388. IODestroyPlugInInterface(cfPluginInterface);
  1389. }
  1390. if (theService!=0)
  1391. {
  1392. IOObjectRelease(theService);
  1393. }
  1394. [serviceDict release];
  1395. }
  1396. }
  1397. #pragma mark - PRIVATE: HID Event handling
  1398. - (void)_simulateHoldEvent:(NSTimer *)aTimer
  1399. {
  1400. NSMutableDictionary *hidAttribsDict;
  1401. NSTimer *shTimer;
  1402. NSNumber *shButtonCode;
  1403. if ((hidAttribsDict = (NSMutableDictionary *)[aTimer userInfo]) != nil)
  1404. {
  1405. if (((shTimer = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsTimer]) != nil) &&
  1406. ((shButtonCode = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsOriginButtonCode]) != nil))
  1407. {
  1408. [shTimer invalidate];
  1409. [hidAttribsDict removeObjectForKey:kHIDRemoteSimulateHoldEventsTimer];
  1410. [self _sendButtonCode:(((HIDRemoteButtonCode)[shButtonCode unsignedIntValue])|kHIDRemoteButtonCodeHoldMask) isPressed:YES hidAttribsDict:hidAttribsDict];
  1411. }
  1412. }
  1413. }
  1414. - (void)_handleButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict
  1415. {
  1416. switch (buttonCode)
  1417. {
  1418. case kHIDRemoteButtonCodeIDChanged:
  1419. // Do nothing, this is handled seperately
  1420. break;
  1421. case kHIDRemoteButtonCodeUp:
  1422. case kHIDRemoteButtonCodeDown:
  1423. if (_simulateHoldEvents)
  1424. {
  1425. NSTimer *shTimer = nil;
  1426. NSNumber *shButtonCode = nil;
  1427. [[hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsTimer] invalidate];
  1428. if (isPressed)
  1429. {
  1430. [hidAttribsDict setObject:[NSNumber numberWithUnsignedInt:buttonCode] forKey:kHIDRemoteSimulateHoldEventsOriginButtonCode];
  1431. if ((shTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:0.7] interval:0.1 target:self selector:@selector(_simulateHoldEvent:) userInfo:hidAttribsDict repeats:NO]) != nil)
  1432. {
  1433. [hidAttribsDict setObject:shTimer forKey:kHIDRemoteSimulateHoldEventsTimer];
  1434. // Using CFRunLoopAddTimer instead of [[NSRunLoop currentRunLoop] addTimer:.. for consistency with run loop modes.
  1435. // The kCFRunLoopCommonModes counterpart NSRunLoopCommonModes is only available in 10.5 and later, whereas this code
  1436. // is designed to be also compatible with 10.4. CFRunLoopTimerRef is "toll-free-bridged" with NSTimer since 10.0.
  1437. CFRunLoopAddTimer(CFRunLoopGetCurrent(), (CFRunLoopTimerRef)shTimer, kCFRunLoopCommonModes);
  1438. [shTimer release];
  1439. break;
  1440. }
  1441. }
  1442. else
  1443. {
  1444. shTimer = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsTimer];
  1445. shButtonCode = [hidAttribsDict objectForKey:kHIDRemoteSimulateHoldEventsOriginButtonCode];
  1446. if ((shTimer!=nil) && (shButtonCode!=nil))
  1447. {
  1448. [self _sendButtonCode:(HIDRemoteButtonCode)[shButtonCode unsignedIntValue] isPressed:YES hidAttribsDict:hidAttribsDict];
  1449. [self _sendButtonCode:(HIDRemoteButtonCode)[shButtonCode unsignedIntValue] isPressed:NO hidAttribsDict:hidAttribsDict];
  1450. }
  1451. else
  1452. {
  1453. if (shButtonCode!=nil)
  1454. {
  1455. [self _sendButtonCode:(((HIDRemoteButtonCode)[shButtonCode unsignedIntValue])|kHIDRemoteButtonCodeHoldMask) isPressed:NO hidAttribsDict:hidAttribsDict];
  1456. }
  1457. }
  1458. }
  1459. [hidAttribsDict removeObjectForKey:kHIDRemoteSimulateHoldEventsTimer];
  1460. [hidAttribsDict removeObjectForKey:kHIDRemoteSimulateHoldEventsOriginButtonCode];
  1461. break;
  1462. }
  1463. default:
  1464. [self _sendButtonCode:buttonCode isPressed:isPressed hidAttribsDict:hidAttribsDict];
  1465. break;
  1466. }
  1467. }
  1468. - (void)_sendButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict
  1469. {
  1470. if (([self delegate]!=nil) &&
  1471. ([[self delegate] respondsToSelector:@selector(hidRemote:eventWithButton:isPressed:fromHardwareWithAttributes:)]))
  1472. {
  1473. switch (buttonCode & (~kHIDRemoteButtonCodeAluminumMask))
  1474. {
  1475. case kHIDRemoteButtonCodePlay:
  1476. case kHIDRemoteButtonCodeCenter:
  1477. if (buttonCode & kHIDRemoteButtonCodeAluminumMask)
  1478. {
  1479. _lastSeenModel = kHIDRemoteModelAluminum;
  1480. _lastSeenModelRemoteID = _lastSeenRemoteID;
  1481. }
  1482. else
  1483. {
  1484. switch ((HIDRemoteAluminumRemoteSupportLevel)[[hidAttribsDict objectForKey:kHIDRemoteAluminumRemoteSupportLevel] intValue])
  1485. {
  1486. case kHIDRemoteAluminumRemoteSupportLevelNone:
  1487. case kHIDRemoteAluminumRemoteSupportLevelEmulation:
  1488. // Remote type can't be determined by just the Center button press
  1489. break;
  1490. case kHIDRemoteAluminumRemoteSupportLevelNative:
  1491. // Remote type can be safely determined by just the Center button press
  1492. if (((_lastSeenModel == kHIDRemoteModelAluminum) && (_lastSeenModelRemoteID != _lastSeenRemoteID)) ||
  1493. (_lastSeenModel == kHIDRemoteModelUndetermined))
  1494. {
  1495. _lastSeenModel = kHIDRemoteModelWhitePlastic;
  1496. }
  1497. break;
  1498. }
  1499. }
  1500. break;
  1501. }
  1502. // As soon as we have received a code that's unique to the Aluminum Remote, we can tell kHIDRemoteButtonCodePlayHold and kHIDRemoteButtonCodeCenterHold apart.
  1503. // Prior to that, a long press of the new "Play" button will be submitted as a "kHIDRemoteButtonCodeCenterHold", not a "kHIDRemoteButtonCodePlayHold" code.
  1504. if ((buttonCode == kHIDRemoteButtonCodeCenterHold) && (_lastSeenModel == kHIDRemoteModelAluminum))
  1505. {
  1506. buttonCode = kHIDRemoteButtonCodePlayHold;
  1507. }
  1508. [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self eventWithButton:(buttonCode & (~kHIDRemoteButtonCodeAluminumMask)) isPressed:isPressed fromHardwareWithAttributes:hidAttribsDict];
  1509. }
  1510. }
  1511. - (void)_hidEventFor:(io_service_t)hidDevice from:(IOHIDQueueInterface **)interface withResult:(IOReturn)result
  1512. {
  1513. NSMutableDictionary *hidAttribsDict = [[[_serviceAttribMap objectForKey:[NSNumber numberWithUnsignedInt:(unsigned int)hidDevice]] retain] autorelease];
  1514. if (hidAttribsDict!=nil)
  1515. {
  1516. IOHIDQueueInterface **queueInterface = NULL;
  1517. queueInterface = [[hidAttribsDict objectForKey:kHIDRemoteHIDQueueInterface] pointerValue];
  1518. if (interface == queueInterface)
  1519. {
  1520. NSNumber *lastButtonPressedNumber = nil;
  1521. HIDRemoteButtonCode lastButtonPressed = kHIDRemoteButtonCodeNone;
  1522. NSMutableDictionary *cookieButtonMap = nil;
  1523. cookieButtonMap = [hidAttribsDict objectForKey:kHIDRemoteCookieButtonCodeLUT];
  1524. if ((lastButtonPressedNumber = [hidAttribsDict objectForKey:kHIDRemoteLastButtonPressed]) != nil)
  1525. {
  1526. lastButtonPressed = [lastButtonPressedNumber unsignedIntValue];
  1527. }
  1528. while (result == kIOReturnSuccess)
  1529. {
  1530. IOHIDEventStruct hidEvent;
  1531. AbsoluteTime supportedTime = { 0,0 };
  1532. result = (*queueInterface)->getNextEvent( queueInterface,
  1533. &hidEvent,
  1534. supportedTime,
  1535. 0);
  1536. if (result == kIOReturnSuccess)
  1537. {
  1538. NSNumber *buttonCodeNumber = [cookieButtonMap objectForKey:[NSNumber numberWithUnsignedInt:(unsigned int) hidEvent.elementCookie]];
  1539. #ifdef _HIDREMOTE_EXTENSIONS
  1540. // Debug logging code
  1541. #define _HIDREMOTE_EXTENSIONS_SECTION 5
  1542. #include "HIDRemoteAdditions.h"
  1543. #undef _HIDREMOTE_EXTENSIONS_SECTION
  1544. #endif /* _HIDREMOTE_EXTENSIONS */
  1545. if (buttonCodeNumber!=nil)
  1546. {
  1547. HIDRemoteButtonCode buttonCode = [buttonCodeNumber unsignedIntValue];
  1548. if (hidEvent.value == 0)
  1549. {
  1550. if (buttonCode == lastButtonPressed)
  1551. {
  1552. [self _handleButtonCode:lastButtonPressed isPressed:NO hidAttribsDict:hidAttribsDict];
  1553. lastButtonPressed = kHIDRemoteButtonCodeNone;
  1554. }
  1555. }
  1556. if (hidEvent.value != 0)
  1557. {
  1558. if (lastButtonPressed != kHIDRemoteButtonCodeNone)
  1559. {
  1560. [self _handleButtonCode:lastButtonPressed isPressed:NO hidAttribsDict:hidAttribsDict];
  1561. // lastButtonPressed = kHIDRemoteButtonCodeNone;
  1562. }
  1563. if (buttonCode == kHIDRemoteButtonCodeIDChanged)
  1564. {
  1565. if (([self delegate]!=nil) &&
  1566. ([[self delegate] respondsToSelector:@selector(hidRemote:remoteIDChangedOldID:newID:forHardwareWithAttributes:)]))
  1567. {
  1568. [((NSObject <HIDRemoteDelegate> *)[self delegate]) hidRemote:self remoteIDChangedOldID:_lastSeenRemoteID newID:hidEvent.value forHardwareWithAttributes:hidAttribsDict];
  1569. }
  1570. _lastSeenRemoteID = hidEvent.value;
  1571. _lastSeenModel = kHIDRemoteModelUndetermined;
  1572. }
  1573. [self _handleButtonCode:buttonCode isPressed:YES hidAttribsDict:hidAttribsDict];
  1574. lastButtonPressed = buttonCode;
  1575. }
  1576. }
  1577. }
  1578. };
  1579. [hidAttribsDict setObject:[NSNumber numberWithUnsignedInt:lastButtonPressed] forKey:kHIDRemoteLastButtonPressed];
  1580. }
  1581. #ifdef _HIDREMOTE_EXTENSIONS
  1582. // Debug logging code
  1583. #define _HIDREMOTE_EXTENSIONS_SECTION 6
  1584. #include "HIDRemoteAdditions.h"
  1585. #undef _HIDREMOTE_EXTENSIONS_SECTION
  1586. #endif /* _HIDREMOTE_EXTENSIONS */
  1587. }
  1588. }
  1589. #pragma mark - PRIVATE: Notification handling
  1590. - (void)_serviceMatching:(io_iterator_t)iterator
  1591. {
  1592. io_object_t matchingService = 0;
  1593. while ((matchingService = IOIteratorNext(iterator)) != 0)
  1594. {
  1595. [self _setupService:matchingService];
  1596. IOObjectRelease(matchingService);
  1597. };
  1598. }
  1599. - (void)_serviceNotificationFor:(io_service_t)service messageType:(natural_t)messageType messageArgument:(void *)messageArgument
  1600. {
  1601. if (messageType == kIOMessageServiceIsTerminated)
  1602. {
  1603. [self _destructService:service];
  1604. }
  1605. }
  1606. - (void)_updateSessionInformation
  1607. {
  1608. NSArray *consoleUsersArray;
  1609. io_service_t rootService;
  1610. if (_masterPort==0) { return; }
  1611. if ((rootService = IORegistryGetRootEntry(_masterPort)) != 0)
  1612. {
  1613. if ((consoleUsersArray = (NSArray *)IORegistryEntryCreateCFProperty((io_registry_entry_t)rootService, CFSTR("IOConsoleUsers"), kCFAllocatorDefault, 0)) != nil)
  1614. {
  1615. if ([consoleUsersArray isKindOfClass:[NSArray class]]) // Be careful - ensure this really is an array
  1616. {
  1617. NSEnumerator *consoleUsersEnum; // I *love* Obj-C2's fast enumerators, but we need to stay compatible with 10.4 :-/
  1618. if ((consoleUsersEnum = [consoleUsersArray objectEnumerator]) != nil)
  1619. {
  1620. UInt64 secureEventInputPIDSum = 0;
  1621. uid_t frontUserSession = 0;
  1622. BOOL screenIsLocked = NO;
  1623. NSDictionary *consoleUserDict;
  1624. while ((consoleUserDict = [consoleUsersEnum nextObject]) != nil)
  1625. {
  1626. if ([consoleUserDict isKindOfClass:[NSDictionary class]]) // Be careful - ensure this really is a dictionary
  1627. {
  1628. NSNumber *secureInputPID;
  1629. NSNumber *onConsole;
  1630. NSNumber *userID;
  1631. NSNumber *screenIsLockedBool;
  1632. if ((secureInputPID = [consoleUserDict objectForKey:@"kCGSSessionSecureInputPID"]) != nil)
  1633. {
  1634. if ([secureInputPID isKindOfClass:[NSNumber class]])
  1635. {
  1636. secureEventInputPIDSum += ((UInt64) [secureInputPID intValue]);
  1637. }
  1638. }
  1639. if (((onConsole = [consoleUserDict objectForKey:@"kCGSSessionOnConsoleKey"]) != nil) &&
  1640. ((userID = [consoleUserDict objectForKey:@"kCGSSessionUserIDKey"]) != nil))
  1641. {
  1642. if ([onConsole isKindOfClass:[NSNumber class]] && [userID isKindOfClass:[NSNumber class]])
  1643. {
  1644. if ([onConsole boolValue])
  1645. {
  1646. frontUserSession = (uid_t) [userID intValue];
  1647. }
  1648. }
  1649. }
  1650. if ((screenIsLockedBool = [consoleUserDict objectForKey:@"CGSSessionScreenIsLocked"]) != nil)
  1651. {
  1652. if ([screenIsLockedBool isKindOfClass:[NSNumber class]])
  1653. {
  1654. screenIsLocked = [screenIsLockedBool boolValue];
  1655. }
  1656. }
  1657. }
  1658. }
  1659. _lastSecureEventInputPIDSum = secureEventInputPIDSum;
  1660. _lastFrontUserSession = frontUserSession;
  1661. _lastScreenIsLocked = screenIsLocked;
  1662. }
  1663. }
  1664. CFRelease((CFTypeRef)consoleUsersArray);
  1665. }
  1666. IOObjectRelease((io_object_t) rootService);
  1667. }
  1668. }
  1669. - (void)_silentRestart
  1670. {
  1671. if ((_mode == kHIDRemoteModeExclusive) || (_mode == kHIDRemoteModeExclusiveAuto))
  1672. {
  1673. HIDRemoteMode restartInMode = _mode;
  1674. unsigned checkActiveRemoteControlCount = [self activeRemoteControlCount];
  1675. // Only restart when we already have active remote controls - to avoid race conditions with other applications using kHIDRemoteModeExclusive mode (new in V1.2.1)
  1676. if (checkActiveRemoteControlCount > 0)
  1677. {
  1678. _isRestarting = YES;
  1679. [self stopRemoteControl];
  1680. [self startRemoteControl:restartInMode];
  1681. _isRestarting = NO;
  1682. // Check whether we lost a remote control due to restarting/secure input change notification handling (new in V1.2.1)
  1683. if (checkActiveRemoteControlCount != [self activeRemoteControlCount])
  1684. {
  1685. // Log message
  1686. 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);
  1687. }
  1688. }
  1689. }
  1690. }
  1691. - (void)_secureInputNotificationFor:(io_service_t)service messageType:(natural_t)messageType messageArgument:(void *)messageArgument
  1692. {
  1693. if (messageType == kIOMessageServiceBusyStateChange)
  1694. {
  1695. UInt64 old_lastSecureEventInputPIDSum = _lastSecureEventInputPIDSum;
  1696. uid_t old_lastFrontUserSession = _lastFrontUserSession;
  1697. BOOL old_lastScreenIsLocked = _lastScreenIsLocked;
  1698. [self _updateSessionInformation];
  1699. if (((old_lastSecureEventInputPIDSum != _lastSecureEventInputPIDSum) ||
  1700. (old_lastFrontUserSession != _lastFrontUserSession) ||
  1701. (old_lastScreenIsLocked != _lastScreenIsLocked)) && _secureEventInputWorkAround)
  1702. {
  1703. [self _silentRestart];
  1704. }
  1705. }
  1706. }
  1707. - (void)_computerDidWake:(NSNotification *)aNotification
  1708. {
  1709. // 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)
  1710. #ifdef NSAppKitVersionNumber10_8
  1711. if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_8)
  1712. #else
  1713. if (NSAppKitVersionNumber >= 1187)
  1714. #endif
  1715. {
  1716. #ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING
  1717. if ([self respondsToSelector:@selector(performSelector:onThread:withObject:waitUntilDone:)]) // OS X 10.5+ only
  1718. {
  1719. if ([NSThread currentThread] != _runOnThread)
  1720. {
  1721. [self performSelector:@selector(_computerDidWake:) onThread:_runOnThread withObject:aNotification waitUntilDone:NO];
  1722. return;
  1723. }
  1724. }
  1725. #endif
  1726. [self _silentRestart];
  1727. }
  1728. }
  1729. @end
  1730. #pragma mark - PRIVATE: IOKitLib Callbacks
  1731. static void HIDEventCallback( void * target,
  1732. IOReturn result,
  1733. void * refCon,
  1734. void * sender)
  1735. {
  1736. HIDRemote *hidRemote = (HIDRemote *)refCon;
  1737. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1738. [hidRemote _hidEventFor:(io_service_t)((intptr_t)target) from:(IOHIDQueueInterface**)sender withResult:(IOReturn)result];
  1739. [pool release];
  1740. }
  1741. static void ServiceMatchingCallback( void *refCon,
  1742. io_iterator_t iterator)
  1743. {
  1744. HIDRemote *hidRemote = (HIDRemote *)refCon;
  1745. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1746. [hidRemote _serviceMatching:iterator];
  1747. [pool release];
  1748. }
  1749. static void ServiceNotificationCallback(void * refCon,
  1750. io_service_t service,
  1751. natural_t messageType,
  1752. void * messageArgument)
  1753. {
  1754. HIDRemote *hidRemote = (HIDRemote *)refCon;
  1755. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1756. [hidRemote _serviceNotificationFor:service
  1757. messageType:messageType
  1758. messageArgument:messageArgument];
  1759. [pool release];
  1760. }
  1761. static void SecureInputNotificationCallback( void * refCon,
  1762. io_service_t service,
  1763. natural_t messageType,
  1764. void * messageArgument)
  1765. {
  1766. HIDRemote *hidRemote = (HIDRemote *)refCon;
  1767. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1768. [hidRemote _secureInputNotificationFor:service
  1769. messageType:messageType
  1770. messageArgument:messageArgument];
  1771. [pool release];
  1772. }
  1773. // Attribute dictionary keys
  1774. NSString *kHIDRemoteCFPluginInterface = @"CFPluginInterface";
  1775. NSString *kHIDRemoteHIDDeviceInterface = @"HIDDeviceInterface";
  1776. NSString *kHIDRemoteCookieButtonCodeLUT = @"CookieButtonCodeLUT";
  1777. NSString *kHIDRemoteHIDQueueInterface = @"HIDQueueInterface";
  1778. NSString *kHIDRemoteServiceNotification = @"ServiceNotification";
  1779. NSString *kHIDRemoteCFRunLoopSource = @"CFRunLoopSource";
  1780. NSString *kHIDRemoteLastButtonPressed = @"LastButtonPressed";
  1781. NSString *kHIDRemoteService = @"Service";
  1782. NSString *kHIDRemoteSimulateHoldEventsTimer = @"SimulateHoldEventsTimer";
  1783. NSString *kHIDRemoteSimulateHoldEventsOriginButtonCode = @"SimulateHoldEventsOriginButtonCode";
  1784. NSString *kHIDRemoteAluminumRemoteSupportLevel = @"AluminumRemoteSupportLevel";
  1785. NSString *kHIDRemoteAluminumRemoteSupportOnDemand = @"AluminumRemoteSupportLevelOnDemand";
  1786. NSString *kHIDRemoteManufacturer = @"Manufacturer";
  1787. NSString *kHIDRemoteProduct = @"Product";
  1788. NSString *kHIDRemoteTransport = @"Transport";
  1789. // Distributed notifications
  1790. NSString *kHIDRemoteDNHIDRemotePing = @"com.candelair.ping";
  1791. NSString *kHIDRemoteDNHIDRemoteRetry = @"com.candelair.retry";
  1792. NSString *kHIDRemoteDNHIDRemoteStatus = @"com.candelair.status";
  1793. NSString *kHIDRemoteDNHIDRemoteRetryGlobalObject = @"global";
  1794. // Distributed notifications userInfo keys and values
  1795. NSString *kHIDRemoteDNStatusHIDRemoteVersionKey = @"HIDRemoteVersion";
  1796. NSString *kHIDRemoteDNStatusPIDKey = @"PID";
  1797. NSString *kHIDRemoteDNStatusModeKey = @"Mode";
  1798. NSString *kHIDRemoteDNStatusUnusedButtonCodesKey = @"UnusedButtonCodes";
  1799. NSString *kHIDRemoteDNStatusActionKey = @"Action";
  1800. NSString *kHIDRemoteDNStatusRemoteControlCountKey = @"RemoteControlCount";
  1801. NSString *kHIDRemoteDNStatusReturnToPIDKey = @"ReturnToPID";
  1802. NSString *kHIDRemoteDNStatusActionStart = @"start";
  1803. NSString *kHIDRemoteDNStatusActionStop = @"stop";
  1804. NSString *kHIDRemoteDNStatusActionUpdate = @"update";
  1805. NSString *kHIDRemoteDNStatusActionNoNeed = @"noneed";