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 37ad4d677..000000000 Binary files a/Preferences/Preferences/Icons/apple_remote.png and /dev/null differ 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