From 97cc1e9845a75743f12ec57383151ce97cf4edfc Mon Sep 17 00:00:00 2001 From: Dzmitry Neviadomski Date: Fri, 19 Feb 2021 07:32:33 +0300 Subject: [PATCH] Remove Apple Remote leftovers. --- Application/AppController.h | 6 +- .../Preferences/Icons/apple_remote.png | Bin 5003 -> 0 bytes .../Preferences.xcodeproj/project.pbxproj | 6 +- ThirdParty/AppleRemote/AppleRemote.h | 192 ----- ThirdParty/AppleRemote/AppleRemote.m | 680 ------------------ 5 files changed, 2 insertions(+), 882 deletions(-) delete mode 100644 Preferences/Preferences/Icons/apple_remote.png delete mode 100644 ThirdParty/AppleRemote/AppleRemote.h delete mode 100644 ThirdParty/AppleRemote/AppleRemote.m diff --git a/Application/AppController.h b/Application/AppController.h index c3767ac68..bfc0f3bb2 100644 --- a/Application/AppController.h +++ b/Application/AppController.h @@ -6,7 +6,6 @@ @class PlaybackController; @class PlaylistController; @class PlaylistView; -@class AppleRemote; @class PlaylistLoader; @interface AppController : NSObject @@ -44,10 +43,7 @@ IBOutlet NSWindowController *spotlightWindowController; IBOutlet FileTreeViewController *fileTreeViewController; - - AppleRemote *remote; - BOOL remoteButtonHeld; /* true as long as the user holds the left,right,plus or minus on the remote control */ - + NSOperationQueue *queue; // Since we are the app delegate, we take care of the op queue NSMutableSet* expandedNodes; diff --git a/Preferences/Preferences/Icons/apple_remote.png b/Preferences/Preferences/Icons/apple_remote.png deleted file mode 100644 index 37ad4d6774fc526ad68843879e516e062147e918..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5003 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 V1CODLS%v@r002ovPDHLkV1jBWd?Ek< diff --git a/Preferences/Preferences/Preferences.xcodeproj/project.pbxproj b/Preferences/Preferences/Preferences.xcodeproj/project.pbxproj index 9f6aa4226..82e8ee4bf 100644 --- a/Preferences/Preferences/Preferences.xcodeproj/project.pbxproj +++ b/Preferences/Preferences/Preferences.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 170744AD0BFF3938002475C9 /* AppcastArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 170744AC0BFF3938002475C9 /* AppcastArrayController.m */; }; - 172D72AD0B8926CA00D095BB /* apple_remote.png in Resources */ = {isa = PBXBuildFile; fileRef = 172D72AC0B8926CA00D095BB /* apple_remote.png */; }; 1766C7A80B912A71004A7AE4 /* lastfm.png in Resources */ = {isa = PBXBuildFile; fileRef = 1766C7A70B912A71004A7AE4 /* lastfm.png */; }; 178E386E0C3DA64500EE6711 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 178E386D0C3DA64500EE6711 /* InfoPlist.strings */; }; 17C643380B8A77CC00C53518 /* OutputsArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 17C643360B8A77CC00C53518 /* OutputsArrayController.m */; }; @@ -96,7 +95,6 @@ 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 170744AB0BFF3938002475C9 /* AppcastArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppcastArrayController.h; sourceTree = ""; }; 170744AC0BFF3938002475C9 /* AppcastArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppcastArrayController.m; sourceTree = ""; }; - 172D72AC0B8926CA00D095BB /* apple_remote.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = apple_remote.png; path = Icons/apple_remote.png; sourceTree = ""; }; 1766C7A70B912A71004A7AE4 /* lastfm.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = lastfm.png; path = Icons/lastfm.png; sourceTree = ""; }; 17C643360B8A77CC00C53518 /* OutputsArrayController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = OutputsArrayController.m; sourceTree = ""; }; 17C643370B8A77CC00C53518 /* OutputsArrayController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = OutputsArrayController.h; sourceTree = ""; }; @@ -331,7 +329,6 @@ 1766C7A70B912A71004A7AE4 /* lastfm.png */, 17C643680B8A788000C53518 /* output.png */, 8E15A86B0B894768006DC802 /* updates.png */, - 172D72AC0B8926CA00D095BB /* apple_remote.png */, 8E07ABDB0AAC95BC00A4B32F /* hot_keys.png */, ); name = Icons; @@ -379,7 +376,7 @@ LastUpgradeCheck = 1230; TargetAttributes = { 8D5B49AC048680CD000E48DA = { - DevelopmentTeam = ""; + DevelopmentTeam = RXH4D9SUXM; ProvisioningStyle = Automatic; }; }; @@ -448,7 +445,6 @@ 83F27E6D1810DD3A00CEF538 /* lastfm@2x.png in Resources */, 178E386E0C3DA64500EE6711 /* InfoPlist.strings in Resources */, 8E07ABDD0AAC95BC00A4B32F /* hot_keys.png in Resources */, - 172D72AD0B8926CA00D095BB /* apple_remote.png in Resources */, 8384917818084D9F00E7332D /* growl.png in Resources */, 8E15A86C0B894768006DC802 /* updates.png in Resources */, 8384917718084D9F00E7332D /* appearance.png in Resources */, diff --git a/ThirdParty/AppleRemote/AppleRemote.h b/ThirdParty/AppleRemote/AppleRemote.h deleted file mode 100644 index 045b9a0c0..000000000 --- a/ThirdParty/AppleRemote/AppleRemote.h +++ /dev/null @@ -1,192 +0,0 @@ -/***************************************************************************** - * AppleRemote.h - * AppleRemote - * $Id$ - * - * Created by Martin Kahr on 11.03.06 under a MIT-style license. - * Copyright (c) 2006 martinkahr.com. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - ***************************************************************************** - * - * Note that changes made by any members or contributors of the VideoLAN team - * (i.e. changes that were checked in exclusively into one of VideoLAN's source code - * repositories) are licensed under the GNU General Public License version 2, - * or (at your option) any later version. - * Thus, the following statements apply to our changes: - * - * Copyright (C) 2006-2007 VLC authors and VideoLAN - * Authors: Eric Petit - * Felix Kühne - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -#import -#import -#import -#import -#import -#import -#import - -enum AppleRemoteEventIdentifier -{ - kRemoteButtonVolume_Plus =1<<1, - kRemoteButtonVolume_Minus =1<<2, - kRemoteButtonMenu =1<<3, - kRemoteButtonPlay =1<<4, - kRemoteButtonRight =1<<5, - kRemoteButtonLeft =1<<6, - kRemoteButtonRight_Hold =1<<7, - kRemoteButtonLeft_Hold =1<<8, - kRemoteButtonMenu_Hold =1<<9, - kRemoteButtonPlay_Sleep =1<<10, - kRemoteControl_Switched =1<<11, - kRemoteButtonVolume_Plus_Hold =1<<12, - kRemoteButtonVolume_Minus_Hold =1<<13, - k2009RemoteButtonPlay =1<<14, - k2009RemoteButtonFullscreen =1<<15 -}; -typedef enum AppleRemoteEventIdentifier AppleRemoteEventIdentifier; - -/* Encapsulates usage of the apple remote control -This class is implemented as a singleton as there is exactly one remote per machine (until now) -The class is not thread safe -*/ -@interface AppleRemote : NSObject { - IOHIDDeviceInterface** hidDeviceInterface; - IOHIDQueueInterface** queue; - NSArray* _allCookies; - NSDictionary* _cookieToButtonMapping; - CFRunLoopSourceRef eventSource; - - BOOL _openInExclusiveMode; - BOOL _simulatePlusMinusHold; - BOOL _processesBacklog; - - /* state for simulating plus/minus hold */ - BOOL lastEventSimulatedHold; - AppleRemoteEventIdentifier lastPlusMinusEvent; - NSTimeInterval lastPlusMinusEventTime; - - int remoteId; - unsigned int _clickCountEnabledButtons; - NSTimeInterval _maxClickTimeDifference; - NSTimeInterval lastClickCountEventTime; - AppleRemoteEventIdentifier lastClickCountEvent; - unsigned int eventClickCount; - - id delegate; -} -+ (AppleRemote *)sharedInstance; - -@property (readonly) int remoteId; -@property (readonly) BOOL remoteAvailable; -@property (readwrite) BOOL listeningToRemote; -@property (readwrite) BOOL openInExclusiveMode; - -/* click counting makes it possible to recognize if the user has pressed a button repeatedly - * click counting does delay each event as it has to wait if there is another event (second click) - * therefore there is a slight time difference (maximumClickCountTimeDifference) between a single click - * of the user and the call of your delegate method - * click counting can be enabled individually for specific buttons. Use the property clickCountEnableButtons - * to set the buttons for which click counting shall be enabled */ -@property (readwrite) BOOL clickCountingEnabled; - -@property (readwrite) unsigned int clickCountEnabledButtons; - -/* the maximum time difference till which clicks are recognized as multi clicks */ -@property (readwrite) NSTimeInterval maximumClickCountTimeDifference; - -/* When your application needs to much time on the main thread when processing an event other events - * may already be received which are put on a backlog. As soon as your main thread - * has some spare time this backlog is processed and may flood your delegate with calls. - * Backlog processing is turned off by default. */ -@property (readwrite) BOOL processesBacklog; - -/* Sets an NSApplication delegate which starts listening when application is becoming active - * and stops listening when application resigns being active. - * If an NSApplication delegate has been already set all method calls will be forwarded to this delegate, too. */ -@property (readwrite) BOOL listeningOnAppActivate; - -/* Simulating plus/minus hold does deactivate sending of individual requests for plus/minus pressed down/released. - * Instead special hold events are being triggered when the user is pressing and holding plus/minus for a small period. - * With simulating enabled the plus/minus buttons do behave as the left/right buttons */ -@property (readwrite) BOOL simulatesPlusMinusHold; - -/* Delegates are not retained */ -@property (readwrite, assign) id delegate; - -- (IBAction) startListening: (id) sender; -- (IBAction) stopListening: (id) sender; -@end - -@interface AppleRemote (Singleton) - -+ (AppleRemote*) sharedRemote; - -@end - -/* Method definitions for the delegate of the AppleRemote class */ -@interface NSObject(NSAppleRemoteDelegate) - -- (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier pressedDown: (BOOL) pressedDown clickCount: (unsigned int) count; - -@end - -@interface AppleRemote (PrivateMethods) -@property (readonly) NSDictionary * cookieToButtonMapping; - -- (void) setRemoteId: (int) aValue; -- (IOHIDQueueInterface**) queue; -- (IOHIDDeviceInterface**) hidDeviceInterface; -- (void) handleEventWithCookieString: (NSString*) cookieString sumOfValues: (SInt32) sumOfValues; -@end - -@interface AppleRemote (IOKitMethods) -- (io_object_t) findAppleRemoteDevice; -- (IOHIDDeviceInterface**) createInterfaceForDevice: (io_object_t) hidDevice; -- (BOOL) initializeCookies; -- (BOOL) openDevice; -@end - -/* A NSApplication delegate which is used to activate and deactivate listening to the remote control - * dependent on the activation state of your application. - * All events are delegated to the original NSApplication delegate if necessary */ -@interface AppleRemoteApplicationDelegate : NSObject { - id applicationDelegate; -} - -- (id) initWithApplicationDelegate: (id) delegate; -- (id) applicationDelegate; -@end \ No newline at end of file diff --git a/ThirdParty/AppleRemote/AppleRemote.m b/ThirdParty/AppleRemote/AppleRemote.m deleted file mode 100644 index 4dcdd9e8e..000000000 --- a/ThirdParty/AppleRemote/AppleRemote.m +++ /dev/null @@ -1,680 +0,0 @@ -/***************************************************************************** - * AppleRemote.m - * AppleRemote - * $Id: AppleRemote.m 18683 2007-02-02 09:12:37Z fkuehne $ - * - * Created by Martin Kahr on 11.03.06 under a MIT-style license. - * Copyright (c) 2006 martinkahr.com. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - ***************************************************************************** - * - * Note that changes made by any members or contributors of the VideoLAN team - * (i.e. changes that were exclusively checked in to one of VideoLAN's source code - * repositories) are licensed under the GNU General Public License version 2, - * or (at your option) any later version. - * Thus, the following statements apply to our changes: - * - * Copyright (C) 2006-2009 VLC authors and VideoLAN - * Authors: Eric Petit - * Felix Kühne - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -#import "AppleRemote.h" - -#import "Logging.h" - -const char* AppleRemoteDeviceName = "AppleIRController"; -const int REMOTE_SWITCH_COOKIE=19; -const NSTimeInterval DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE=0.35; -const NSTimeInterval HOLD_RECOGNITION_TIME_INTERVAL=0.4; - -@implementation AppleRemote - -@synthesize openInExclusiveMode = _openInExclusiveMode, clickCountEnabledButtons = _clickCountEnabledButtons, maximumClickCountTimeDifference = _maxClickTimeDifference, processesBacklog=_processesBacklog, simulatesPlusMinusHold = _simulatePlusMinusHold; - -#pragma public interface - -static AppleRemote *_o_sharedInstance = nil; - -+ (AppleRemote *)sharedInstance -{ - return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init]; -} - -- (id)init -{ - if (_o_sharedInstance) { - self = nil; - } else { - _o_sharedInstance = [super init]; - _openInExclusiveMode = YES; - queue = NULL; - hidDeviceInterface = NULL; - NSMutableDictionary * mutableCookieToButtonMapping = [[NSMutableDictionary alloc] init]; - - [mutableCookieToButtonMapping setObject:@(kRemoteButtonVolume_Plus) forKey:@"33_31_30_21_20_2_"]; - [mutableCookieToButtonMapping setObject:@(kRemoteButtonVolume_Minus) forKey:@"33_32_30_21_20_2_"]; - [mutableCookieToButtonMapping setObject:@(kRemoteButtonMenu) forKey:@"33_22_21_20_2_33_22_21_20_2_"]; - [mutableCookieToButtonMapping setObject:@(kRemoteButtonPlay) forKey:@"33_23_21_20_2_33_23_21_20_2_"]; - [mutableCookieToButtonMapping setObject:@(kRemoteButtonRight) forKey:@"33_24_21_20_2_33_24_21_20_2_"]; - [mutableCookieToButtonMapping setObject:@(kRemoteButtonLeft) forKey:@"33_25_21_20_2_33_25_21_20_2_"]; - [mutableCookieToButtonMapping setObject:@(kRemoteButtonRight_Hold) forKey:@"33_21_20_14_12_2_"]; - [mutableCookieToButtonMapping setObject:@(kRemoteButtonLeft_Hold) forKey:@"33_21_20_13_12_2_"]; - [mutableCookieToButtonMapping setObject:@(kRemoteButtonMenu_Hold) forKey:@"33_21_20_2_33_21_20_2_"]; - [mutableCookieToButtonMapping setObject:@(kRemoteButtonPlay_Sleep) forKey:@"37_33_21_20_2_37_33_21_20_2_"]; - [mutableCookieToButtonMapping setObject:@(k2009RemoteButtonPlay) forKey:@"33_21_20_8_2_33_21_20_8_2_"]; - [mutableCookieToButtonMapping setObject:@(k2009RemoteButtonFullscreen) forKey:@"33_21_20_3_2_33_21_20_3_2_"]; - - if( floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_6) - /* 10.6.2+ Snow Leopard cookies */ - [mutableCookieToButtonMapping setObject:@(kRemoteControl_Switched) forKey:@"19_"]; - else - /* Lion cookies */ - [mutableCookieToButtonMapping setObject:@(kRemoteControl_Switched) forKey:@"42_33_23_21_20_2_33_23_21_20_2_"]; - - _cookieToButtonMapping = [[NSDictionary alloc] initWithDictionary: mutableCookieToButtonMapping]; - - /* defaults */ - _simulatePlusMinusHold = YES; - _maxClickTimeDifference = DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE; - } - - return _o_sharedInstance; -} - -- (void) dealloc { - [self stopListening:self]; -} - -- (int) remoteId { - return remoteId; -} - -- (BOOL) remoteAvailable { - io_object_t hidDevice = [self findAppleRemoteDevice]; - if (hidDevice != 0) { - IOObjectRelease(hidDevice); - return YES; - } else { - return NO; - } -} - -- (BOOL) listeningToRemote { - return (hidDeviceInterface != NULL && _allCookies != NULL && queue != NULL); -} - -- (void) setListeningToRemote: (BOOL) value { - if (value == NO) { - [self stopListening:self]; - } else { - [self startListening:self]; - } -} - -/* Delegates are not retained! - * http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/chapter_6_section_4.html - * Delegating objects do not (and should not) retain their delegates. - * However, clients of delegating objects (applications, usually) are responsible for ensuring that their delegates are around - * to receive delegation messages. To do this, they may have to retain the delegate. */ -- (void) setDelegate: (id) _delegate { - if (_delegate && [_delegate respondsToSelector:@selector(appleRemoteButton:pressedDown:clickCount:)]==NO) return; - - delegate = _delegate; -} -- (id) delegate { - return delegate; -} - -- (BOOL) clickCountingEnabled { - return self.clickCountEnabledButtons != 0; -} -- (void) setClickCountingEnabled: (BOOL) value { - if (value) { - [self setClickCountEnabledButtons: kRemoteButtonVolume_Plus | kRemoteButtonVolume_Minus | kRemoteButtonPlay | kRemoteButtonLeft | kRemoteButtonRight | kRemoteButtonMenu | k2009RemoteButtonPlay | k2009RemoteButtonFullscreen]; - } else { - [self setClickCountEnabledButtons: 0]; - } -} - -- (BOOL) listeningOnAppActivate { - id appDelegate = [NSApp delegate]; - return (appDelegate!=nil && [appDelegate isKindOfClass: [AppleRemoteApplicationDelegate class]]); -} -- (void) setListeningOnAppActivate: (BOOL) value { - if (value) { - if ([self listeningOnAppActivate]) return; - AppleRemoteApplicationDelegate* appDelegate = [[AppleRemoteApplicationDelegate alloc] initWithApplicationDelegate: [NSApp delegate]]; - /* NSApp does not retain its delegate therefore we keep retain count on 1 */ - [NSApp setDelegate: appDelegate]; - } else { - if ([self listeningOnAppActivate]==NO) return; - AppleRemoteApplicationDelegate* appDelegate = (AppleRemoteApplicationDelegate*)[NSApp delegate]; - id previousAppDelegate = [appDelegate applicationDelegate]; - [NSApp setDelegate: previousAppDelegate]; - } -} - -- (IBAction) startListening: (id) sender { - if ([self listeningToRemote]) return; - - io_object_t hidDevice = [self findAppleRemoteDevice]; - if (hidDevice == 0) return; - - if ([self createInterfaceForDevice:hidDevice] == NULL) { - goto error; - } - - if ([self initializeCookies]==NO) { - goto error; - } - - if ([self openDevice]==NO) { - goto error; - } - goto cleanup; - - error: - [self stopListening:self]; - - cleanup: - IOObjectRelease(hidDevice); -} - -- (IBAction) stopListening: (id) sender { - if (eventSource != NULL) { - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode); - CFRelease(eventSource); - eventSource = NULL; - } - if (queue != NULL) { - (*queue)->stop(queue); - - //dispose of queue - (*queue)->dispose(queue); - - //release the queue we allocated - (*queue)->Release(queue); - - queue = NULL; - } - - _allCookies = nil; - - if (hidDeviceInterface != NULL) { - //close the device - (*hidDeviceInterface)->close(hidDeviceInterface); - - //release the interface - (*hidDeviceInterface)->Release(hidDeviceInterface); - - hidDeviceInterface = NULL; - } -} - -@end - -@implementation AppleRemote (Singleton) - -static AppleRemote* sharedInstance=nil; - -+ (AppleRemote*) sharedRemote { - @synchronized(self) { - if (sharedInstance == nil) { - sharedInstance = [[self alloc] init]; - } - } - return sharedInstance; -} -+ (id)allocWithZone:(NSZone *)zone { - @synchronized(self) { - if (sharedInstance == nil) { - return [super allocWithZone:zone]; - } - } - return sharedInstance; -} -- (id)copyWithZone:(NSZone *)zone { - return self; -} - -@end - -@implementation AppleRemote (PrivateMethods) - -- (void) setRemoteId: (int) value { - remoteId = value; -} - -- (IOHIDQueueInterface**) queue { - return queue; -} - -- (IOHIDDeviceInterface**) hidDeviceInterface { - return hidDeviceInterface; -} - -- (NSDictionary*) cookieToButtonMapping { - return _cookieToButtonMapping; -} - -- (NSString*) validCookieSubstring: (NSString*) cookieString { - if (cookieString == nil || [cookieString length] == 0) return nil; - NSEnumerator* keyEnum = [[self cookieToButtonMapping] keyEnumerator]; - NSString* key; - while((key = [keyEnum nextObject])) { - NSRange range = [cookieString rangeOfString:key]; - if (range.location == 0) return key; - } - return nil; -} - -- (void) sendSimulatedPlusMinusEvent: (id) time { - BOOL startSimulateHold = NO; - AppleRemoteEventIdentifier event = lastPlusMinusEvent; - @synchronized(self) { - startSimulateHold = (lastPlusMinusEvent>0 && lastPlusMinusEventTime == [time doubleValue]); - } - if (startSimulateHold) { - lastEventSimulatedHold = YES; - event = (event==kRemoteButtonVolume_Plus) ? kRemoteButtonVolume_Plus_Hold : kRemoteButtonVolume_Minus_Hold; - [delegate appleRemoteButton:event pressedDown: YES clickCount: 1]; - } -} - -- (void) sendRemoteButtonEvent: (AppleRemoteEventIdentifier) event pressedDown: (BOOL) pressedDown { - if (delegate) { - if (self.simulatesPlusMinusHold) { - if (event == kRemoteButtonVolume_Plus || event == kRemoteButtonVolume_Minus) { - if (pressedDown) { - lastPlusMinusEvent = event; - lastPlusMinusEventTime = [NSDate timeIntervalSinceReferenceDate]; - [self performSelector:@selector(sendSimulatedPlusMinusEvent:) - withObject:@(lastPlusMinusEventTime) - afterDelay:HOLD_RECOGNITION_TIME_INTERVAL]; - return; - } else { - if (lastEventSimulatedHold) { - event = (event==kRemoteButtonVolume_Plus) ? kRemoteButtonVolume_Plus_Hold : kRemoteButtonVolume_Minus_Hold; - lastPlusMinusEvent = 0; - lastEventSimulatedHold = NO; - } else { - @synchronized(self) { - lastPlusMinusEvent = 0; - } - pressedDown = YES; - } - } - } - } - - if ((self.clickCountEnabledButtons & event) == event) { - if (pressedDown==NO && (event == kRemoteButtonVolume_Minus || event == kRemoteButtonVolume_Plus)) { - return; // this one is triggered automatically by the handler - } - NSNumber* eventNumber; - NSNumber* timeNumber; - @synchronized(self) { - lastClickCountEventTime = [NSDate timeIntervalSinceReferenceDate]; - if (lastClickCountEvent == event) { - eventClickCount = eventClickCount + 1; - } else { - eventClickCount = 1; - } - lastClickCountEvent = event; - timeNumber = @(lastClickCountEventTime); - eventNumber= @(event); - } - [self performSelector: @selector(executeClickCountEvent:) - withObject: @[eventNumber, timeNumber] - afterDelay: _maxClickTimeDifference]; - } else { - [delegate appleRemoteButton:event pressedDown: pressedDown clickCount:1]; - } - } -} - -- (void) executeClickCountEvent: (NSArray*) values { - AppleRemoteEventIdentifier event = [[values objectAtIndex:0] unsignedIntValue]; - NSTimeInterval eventTimePoint = [[values objectAtIndex:1] doubleValue]; - - BOOL finishedClicking = NO; - int finalClickCount = eventClickCount; - - @synchronized(self) { - finishedClicking = (event != lastClickCountEvent || eventTimePoint == lastClickCountEventTime); - if (finishedClicking) eventClickCount = 0; - } - - if (finishedClicking) { - [delegate appleRemoteButton:event pressedDown: YES clickCount:finalClickCount]; - if ([self simulatesPlusMinusHold]==NO && (event == kRemoteButtonVolume_Minus || event == kRemoteButtonVolume_Plus)) { - // trigger a button release event, too - [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow:0.1]]; - [delegate appleRemoteButton:event pressedDown: NO clickCount:finalClickCount]; - } - } - -} - -- (void) handleEventWithCookieString: (NSString*) cookieString sumOfValues: (SInt32) sumOfValues { - /* - if (previousRemainingCookieString) { - cookieString = [previousRemainingCookieString stringByAppendingString: cookieString]; - NSLog(@"New cookie string is %@", cookieString); - [previousRemainingCookieString release], previousRemainingCookieString=nil; - }*/ - if (cookieString == nil || [cookieString length] == 0) return; - NSNumber* buttonId = [[self cookieToButtonMapping] objectForKey: cookieString]; - if (buttonId != nil) { - [self sendRemoteButtonEvent: [buttonId intValue] pressedDown: (sumOfValues>0)]; - } else { - // let's see if a number of events are stored in the cookie string. this does - // happen when the main thread is too busy to handle all incoming events in time. - NSString* subCookieString; - NSString* lastSubCookieString=nil; - while((subCookieString = [self validCookieSubstring: cookieString])) { - cookieString = [cookieString substringFromIndex: [subCookieString length]]; - lastSubCookieString = subCookieString; - if (self.processesBacklog) [self handleEventWithCookieString: subCookieString sumOfValues:sumOfValues]; - } - if (self.processesBacklog == NO && lastSubCookieString != nil) { - // process the last event of the backlog and assume that the button is not pressed down any longer. - // The events in the backlog do not seem to be in order and therefore (in rare cases) the last event might be - // a button pressed down event while in reality the user has released it. - // NSLog(@"processing last event of backlog"); - [self handleEventWithCookieString: lastSubCookieString sumOfValues:0]; - } - if ([cookieString length] > 0) { - ALog(@"Unknown button for cookiestring %s", [cookieString UTF8String]); - } - } -} - -@end - -/* Callback method for the device queue -Will be called for any event of any type (cookie) to which we subscribe -*/ -static void QueueCallbackFunction(void* target, IOReturn result, void* refcon, void* sender) { - AppleRemote* remote = (__bridge AppleRemote*)target; - - IOHIDEventStruct event; - AbsoluteTime zeroTime = {0,0}; - NSMutableString* cookieString = [NSMutableString string]; - SInt32 sumOfValues = 0; - while (result == kIOReturnSuccess) - { - result = (*[remote queue])->getNextEvent([remote queue], &event, zeroTime, 0); - if ( result != kIOReturnSuccess ) - continue; - - //printf("%d %d %d\n", event.elementCookie, event.value, event.longValue); - - if (REMOTE_SWITCH_COOKIE == (int)event.elementCookie) { - [remote setRemoteId: event.value]; - [remote handleEventWithCookieString: @"19_" sumOfValues: 0]; - } else { - if (((int)event.elementCookie)!=5) { - sumOfValues+=event.value; - [cookieString appendString:[NSString stringWithFormat:@"%d_", event.elementCookie]]; - } - } - } - - [remote handleEventWithCookieString: cookieString sumOfValues: sumOfValues]; -} - -@implementation AppleRemote (IOKitMethods) - -- (IOHIDDeviceInterface**) createInterfaceForDevice: (io_object_t) hidDevice { - io_name_t className; - IOCFPlugInInterface** plugInInterface = NULL; - HRESULT plugInResult = S_OK; - SInt32 score = 0; - IOReturn ioReturnValue = kIOReturnSuccess; - - hidDeviceInterface = NULL; - - ioReturnValue = IOObjectGetClass(hidDevice, className); - - if (ioReturnValue != kIOReturnSuccess) { - ALog(@"Error: Failed to get class name."); - return NULL; - } - - ioReturnValue = IOCreatePlugInInterfaceForService(hidDevice, - kIOHIDDeviceUserClientTypeID, - kIOCFPlugInInterfaceID, - &plugInInterface, - &score); - if (ioReturnValue == kIOReturnSuccess) - { - //Call a method of the intermediate plug-in to create the device interface - plugInResult = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (LPVOID) &hidDeviceInterface); - - if (plugInResult != S_OK) { - ALog(@"Error: Couldn't create HID class device interface"); - } - // Release - if (plugInInterface) (*plugInInterface)->Release(plugInInterface); - } - return hidDeviceInterface; -} - -- (io_object_t) findAppleRemoteDevice { - CFMutableDictionaryRef hidMatchDictionary = NULL; - IOReturn ioReturnValue = kIOReturnSuccess; - io_iterator_t hidObjectIterator = 0; - io_object_t hidDevice = 0; - - // Set up a matching dictionary to search the I/O Registry by class - // name for all HID class devices - hidMatchDictionary = IOServiceMatching(AppleRemoteDeviceName); - - // Now search I/O Registry for matching devices. - ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault, hidMatchDictionary, &hidObjectIterator); - - if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0)) { - hidDevice = IOIteratorNext(hidObjectIterator); - } - - // release the iterator - IOObjectRelease(hidObjectIterator); - - return hidDevice; -} - -- (BOOL) initializeCookies { - IOHIDDeviceInterface122** handle = (IOHIDDeviceInterface122**)hidDeviceInterface; - IOHIDElementCookie cookie; - long usage; - long usagePage; - id object; - NSArray* elements; - NSDictionary* element; - IOReturn success; - - if (!handle || !(*handle)) return NO; - - /* Copy all elements, since we're grabbing most of the elements - * for this device anyway, and thus, it's faster to iterate them - * ourselves. When grabbing only one or two elements, a matching - * dictionary should be passed in here instead of NULL. */ - success = (*handle)->copyMatchingElements(handle, NULL, (CFArrayRef*)&elements); - - if (success == kIOReturnSuccess) { - /* - cookies = calloc(NUMBER_OF_APPLE_REMOTE_ACTIONS, sizeof(IOHIDElementCookie)); - memset(cookies, 0, sizeof(IOHIDElementCookie) * NUMBER_OF_APPLE_REMOTE_ACTIONS); - */ - NSMutableArray *mutableAllCookies = [[NSMutableArray alloc] init]; - NSUInteger elementCount = [elements count]; - for (NSUInteger i=0; i< elementCount; i++) { - element = [elements objectAtIndex:i]; - - //Get cookie - object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementCookieKey) ]; - if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue; - if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue; - cookie = (IOHIDElementCookie) [object longValue]; - - //Get usage - object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementUsageKey) ]; - if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue; - usage = [object longValue]; - - //Get usage page - object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementUsagePageKey) ]; - if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue; - usagePage = [object longValue]; - - [mutableAllCookies addObject: @((int)cookie)]; - } - _allCookies = [[NSArray alloc] initWithArray: mutableAllCookies]; - [mutableAllCookies release]; - [elements release]; - } else { - if (elements) - [elements release]; - return NO; - } - - return YES; -} - -- (BOOL) openDevice { - HRESULT result; - - IOHIDOptionsType openMode = kIOHIDOptionsTypeNone; - if ([self openInExclusiveMode]) openMode = kIOHIDOptionsTypeSeizeDevice; - IOReturn ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, openMode); - - if (ioReturnValue == KERN_SUCCESS) { - queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface); - if (queue) { - result = (*queue)->create(queue, 0, 12); //depth: maximum number of elements in queue before oldest elements in queue begin to be lost. - - NSUInteger cookieCount = [_allCookies count]; - for(NSUInteger i=0; iaddElement(queue, cookie, 0); - } - - // add callback for async events - ioReturnValue = (*queue)->createAsyncEventSource(queue, &eventSource); - if (ioReturnValue == KERN_SUCCESS) { - ioReturnValue = (*queue)->setEventCallout(queue,QueueCallbackFunction, self, NULL); - if (ioReturnValue == KERN_SUCCESS) { - CFRunLoopAddSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode); - //start data delivery to queue - (*queue)->start(queue); - return YES; - } else { - ALog(@"Error when setting event callout"); - } - } else { - ALog(@"Error when creating async event source"); - } - } else { - ALog(@"Error when opening device"); - } - } - return NO; -} - -@end - -@implementation AppleRemoteApplicationDelegate - -- (id) initWithApplicationDelegate: (id) delegate { - if((self = [super init])) - applicationDelegate = [delegate retain]; - return self; -} - -- (void) dealloc { - [applicationDelegate release]; - [super dealloc]; -} - -- (id) applicationDelegate { - return applicationDelegate; -} - -- (void)applicationWillBecomeActive:(NSNotification *)aNotification { - if ([applicationDelegate respondsToSelector: @selector(applicationWillBecomeActive:)]) { - [applicationDelegate applicationWillBecomeActive: aNotification]; - } -} -- (void)applicationDidBecomeActive:(NSNotification *)aNotification { - [[AppleRemote sharedRemote] setListeningToRemote: YES]; - - if ([applicationDelegate respondsToSelector: @selector(applicationDidBecomeActive:)]) { - [applicationDelegate applicationDidBecomeActive: aNotification]; - } -} -- (void)applicationWillResignActive:(NSNotification *)aNotification { - [[AppleRemote sharedRemote] setListeningToRemote: NO]; - - if ([applicationDelegate respondsToSelector: @selector(applicationWillResignActive:)]) { - [applicationDelegate applicationWillResignActive: aNotification]; - } -} -- (void)applicationDidResignActive:(NSNotification *)aNotification { - if ([applicationDelegate respondsToSelector: @selector(applicationDidResignActive:)]) { - [applicationDelegate applicationDidResignActive: aNotification]; - } -} - -- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { - NSMethodSignature* signature = [super methodSignatureForSelector: aSelector]; - if (signature == nil && applicationDelegate != nil) { - signature = [applicationDelegate methodSignatureForSelector: aSelector]; - } - return signature; -} - -- (void)forwardInvocation:(NSInvocation *)invocation { - SEL aSelector = [invocation selector]; - - if (applicationDelegate==nil || [applicationDelegate respondsToSelector:aSelector]==NO) { - [super forwardInvocation: invocation]; - return; - } - - [invocation invokeWithTarget:applicationDelegate]; -} -@end \ No newline at end of file