📄 appleremote.m
字号:
/***************************************************************************** * AppleRemote.m * 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 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-2007 the VideoLAN team * Authors: Eric Petit <titer@m0k.org> * Felix Kühne <fkuehne at videolan dot org> * * 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"/* this was added by the VideoLAN team to ensure Leopard-compatibility and is VLC-only */#import "intf.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#pragma public interface- (id) init { if ( self = [super init] ) { openInExclusiveMode = YES; queue = NULL; hidDeviceInterface = NULL; cookieToButtonMapping = [[NSMutableDictionary alloc] init]; if( MACOS_VERSION < 10.5f ) { /* use the traditional cookies for Tiger (and Panther, if it is supported by the frame app) */ [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Plus] forKey:@"14_12_11_6_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Minus] forKey:@"14_13_11_6_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu] forKey:@"14_7_6_14_7_6_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay] forKey:@"14_8_6_14_8_6_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight] forKey:@"14_9_6_14_9_6_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft] forKey:@"14_10_6_14_10_6_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold] forKey:@"14_6_4_2_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold] forKey:@"14_6_3_2_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold] forKey:@"14_6_14_6_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Sleep] forKey:@"18_14_6_18_14_6_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched] forKey:@"19_"]; } else { /* we're on Leopard and need to use a new set of cookies */ [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Plus] forKey:@"31_29_28_18_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Minus] forKey:@"31_30_28_18_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu] forKey:@"31_20_18_31_20_18_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay] forKey:@"31_21_18_31_21_18_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight] forKey:@"31_22_18_31_22_18_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft] forKey:@"31_23_18_31_23_18_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold] forKey:@"31_18_4_2_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold] forKey:@"31_18_3_2_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold] forKey:@"31_18_31_18_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Sleep] forKey:@"35_31_18_35_31_18_"]; [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched] forKey:@"19_"]; } /* defaults */ [self setSimulatesPlusMinusHold: YES]; maxClickTimeDifference = DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE; } return self;}- (void) dealloc { [self stopListening:self]; [cookieToButtonMapping release]; [super dealloc];}- (int) remoteId { return remoteId;}- (BOOL) isRemoteAvailable { io_object_t hidDevice = [self findAppleRemoteDevice]; if (hidDevice != 0) { IOObjectRelease(hidDevice); return YES; } else { return NO; }}- (BOOL) isListeningToRemote { 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) isOpenInExclusiveMode { return openInExclusiveMode;}- (void) setOpenInExclusiveMode: (BOOL) value { openInExclusiveMode = value;}- (BOOL) clickCountingEnabled { return clickCountEnabledButtons != 0;}- (void) setClickCountingEnabled: (BOOL) value { if (value) { [self setClickCountEnabledButtons: kRemoteButtonVolume_Plus | kRemoteButtonVolume_Minus | kRemoteButtonPlay | kRemoteButtonLeft | kRemoteButtonRight | kRemoteButtonMenu]; } else { [self setClickCountEnabledButtons: 0]; }}- (unsigned int) clickCountEnabledButtons { return clickCountEnabledButtons;}- (void) setClickCountEnabledButtons: (unsigned int)value { clickCountEnabledButtons = value;}- (NSTimeInterval) maximumClickCountTimeDifference { return maxClickTimeDifference;}- (void) setMaximumClickCountTimeDifference: (NSTimeInterval) timeDiff { maxClickTimeDifference = timeDiff;}- (BOOL) processesBacklog { return processesBacklog;}- (void) setProcessesBacklog: (BOOL) value { processesBacklog = value;}- (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]; [appDelegate release]; }}- (BOOL) simulatesPlusMinusHold { return simulatePlusMinusHold;}- (void) setSimulatesPlusMinusHold: (BOOL) value { simulatePlusMinusHold = value;}- (IBAction) startListening: (id) sender { if ([self isListeningToRemote]) 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 (queue != NULL) { (*queue)->stop(queue); //dispose of queue (*queue)->dispose(queue); //release the queue we allocated (*queue)->Release(queue); queue = NULL; } if (allCookies != nil) { [allCookies autorelease]; 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;}- (id)retain { return self;}- (unsigned)retainCount { return UINT_MAX; //denotes an object that cannot be released}- (void)release { //do nothing}- (id)autorelease { 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -