AppleRemote.m
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:28k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * AppleRemote.m
  3.  * AppleRemote
  4.  * $Id: f758f6adb1a16c3fb50dcbd973a0e09aac3d9190 $
  5.  *
  6.  * Created by Martin Kahr on 11.03.06 under a MIT-style license.
  7.  * Copyright (c) 2006 martinkahr.com. All rights reserved.
  8.  *
  9.  * Permission is hereby granted, free of charge, to any person obtaining a
  10.  * copy of this software and associated documentation files (the "Software"),
  11.  * to deal in the Software without restriction, including without limitation
  12.  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  13.  * and/or sell copies of the Software, and to permit persons to whom the
  14.  * Software is furnished to do so, subject to the following conditions:
  15.  *
  16.  * The above copyright notice and this permission notice shall be included
  17.  * in all copies or substantial portions of the Software.
  18.  *
  19.  * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  22.  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25.  * THE SOFTWARE.
  26.  *
  27.  *****************************************************************************
  28.  *
  29.  * Note that changes made by any members or contributors of the VideoLAN team
  30.  * (i.e. changes that were exclusively checked in to one of VideoLAN's source code
  31.  * repositories) are licensed under the GNU General Public License version 2,
  32.  * or (at your option) any later version.
  33.  * Thus, the following statements apply to our changes:
  34.  *
  35.  * Copyright (C) 2006-2009 the VideoLAN team
  36.  * Authors: Eric Petit <titer@m0k.org>
  37.  *          Felix Kühne <fkuehne at videolan dot org>
  38.  *
  39.  * This program is free software; you can redistribute it and/or modify
  40.  * it under the terms of the GNU General Public License as published by
  41.  * the Free Software Foundation; either version 2 of the License, or
  42.  * (at your option) any later version.
  43.  *
  44.  * This program is distributed in the hope that it will be useful,
  45.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  46.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  47.  * GNU General Public License for more details.
  48.  *
  49.  * You should have received a copy of the GNU General Public License
  50.  * along with this program; if not, write to the Free Software
  51.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  52.  *****************************************************************************/
  53. #import "AppleRemote.h"
  54. /* this was added by the VideoLAN team to ensure Leopard-compatibility and is VLC-only */
  55. #import "intf.h"
  56. const char* AppleRemoteDeviceName = "AppleIRController";
  57. const int REMOTE_SWITCH_COOKIE=19;
  58. const NSTimeInterval DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE=0.35;
  59. const NSTimeInterval HOLD_RECOGNITION_TIME_INTERVAL=0.4;
  60. @implementation AppleRemote
  61. #pragma public interface
  62. - (id) init {
  63.     if ( self = [super init] ) {
  64.         openInExclusiveMode = YES;
  65.         queue = NULL;
  66.         hidDeviceInterface = NULL;
  67.         cookieToButtonMapping = [[NSMutableDictionary alloc] init];
  68.         if( NSAppKitVersionNumber < 1038.13 )
  69.         {
  70.             /* Leopard and early Snow Leopard Cookies */
  71.             msg_Dbg( VLCIntf, "using Leopard AR cookies" );
  72.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Plus]  forKey:@"31_29_28_18_"];
  73.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Minus] forKey:@"31_30_28_18_"];
  74.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu]         forKey:@"31_20_18_31_20_18_"];
  75.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay]         forKey:@"31_21_18_31_21_18_"];
  76.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight]        forKey:@"31_22_18_31_22_18_"];
  77.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft]         forKey:@"31_23_18_31_23_18_"];
  78.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold]   forKey:@"31_18_4_2_"];
  79.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold]    forKey:@"31_18_3_2_"];
  80.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold]    forKey:@"31_18_31_18_"];
  81.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Sleep]   forKey:@"35_31_18_35_31_18_"];
  82.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched]   forKey:@"19_"];
  83.         }
  84.         else
  85.         {
  86.             /* current Snow Leopard cookies */
  87.             msg_Dbg( VLCIntf, "using Snow Leopard AR cookies" );
  88.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Plus]    forKey:@"33_31_30_21_20_2_"];
  89.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Minus]   forKey:@"33_32_30_21_20_2_"];
  90.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu]           forKey:@"33_22_21_20_2_33_22_21_20_2_"];
  91.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay]           forKey:@"33_23_21_20_2_33_23_21_20_2_"];
  92.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight]          forKey:@"33_24_21_20_2_33_24_21_20_2_"];
  93.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft]           forKey:@"33_25_21_20_2_33_25_21_20_2_"];
  94.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold]     forKey:@"33_21_20_14_12_2_"];
  95.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold]      forKey:@"33_21_20_13_12_2_"];
  96.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold]      forKey:@"33_21_20_2_33_21_20_2_"];
  97.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Sleep]     forKey:@"37_33_21_20_2_37_33_21_20_2_"];
  98.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched]     forKey:@"19_"];
  99.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:k2009RemoteButtonPlay]       forKey:@"33_21_20_8_2_33_21_20_8_2_"];
  100.             [cookieToButtonMapping setObject:[NSNumber numberWithInt:k2009RemoteButtonMiddlePlay] forKey:@"33_21_20_3_2_33_21_20_3_2_"];
  101.         }
  102.         /* defaults */
  103.         [self setSimulatesPlusMinusHold: YES];
  104.         maxClickTimeDifference = DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE;
  105.     }
  106.     return self;
  107. }
  108. - (void) dealloc {
  109.     [self stopListening:self];
  110.     [cookieToButtonMapping release];
  111.     [super dealloc];
  112. }
  113. - (int) remoteId {
  114.     return remoteId;
  115. }
  116. - (BOOL) isRemoteAvailable {
  117.     io_object_t hidDevice = [self findAppleRemoteDevice];
  118.     if (hidDevice != 0) {
  119.         IOObjectRelease(hidDevice);
  120.         return YES;
  121.     } else {
  122.         return NO;
  123.     }
  124. }
  125. - (BOOL) isListeningToRemote {
  126.     return (hidDeviceInterface != NULL && allCookies != NULL && queue != NULL);
  127. }
  128. - (void) setListeningToRemote: (BOOL) value {
  129.     if (value == NO) {
  130.         [self stopListening:self];
  131.     } else {
  132.         [self startListening:self];
  133.     }
  134. }
  135. /* Delegates are not retained!
  136.  * http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/chapter_6_section_4.html
  137.  * Delegating objects do not (and should not) retain their delegates.
  138.  * However, clients of delegating objects (applications, usually) are responsible for ensuring that their delegates are around
  139.  * to receive delegation messages. To do this, they may have to retain the delegate. */
  140. - (void) setDelegate: (id) _delegate {
  141.     if (_delegate && [_delegate respondsToSelector:@selector(appleRemoteButton:pressedDown:clickCount:)]==NO) return;
  142.     delegate = _delegate;
  143. }
  144. - (id) delegate {
  145.     return delegate;
  146. }
  147. - (BOOL) isOpenInExclusiveMode {
  148.     return openInExclusiveMode;
  149. }
  150. - (void) setOpenInExclusiveMode: (BOOL) value {
  151.     openInExclusiveMode = value;
  152. }
  153. - (BOOL) clickCountingEnabled {
  154.     return clickCountEnabledButtons != 0;
  155. }
  156. - (void) setClickCountingEnabled: (BOOL) value {
  157.     if (value) {
  158.         [self setClickCountEnabledButtons: kRemoteButtonVolume_Plus | kRemoteButtonVolume_Minus | kRemoteButtonPlay | kRemoteButtonLeft | kRemoteButtonRight | kRemoteButtonMenu | k2009RemoteButtonPlay | k2009RemoteButtonMiddlePlay];
  159.     } else {
  160.         [self setClickCountEnabledButtons: 0];
  161.     }
  162. }
  163. - (unsigned int) clickCountEnabledButtons {
  164.     return clickCountEnabledButtons;
  165. }
  166. - (void) setClickCountEnabledButtons: (unsigned int)value {
  167.     clickCountEnabledButtons = value;
  168. }
  169. - (NSTimeInterval) maximumClickCountTimeDifference {
  170.     return maxClickTimeDifference;
  171. }
  172. - (void) setMaximumClickCountTimeDifference: (NSTimeInterval) timeDiff {
  173.     maxClickTimeDifference = timeDiff;
  174. }
  175. - (BOOL) processesBacklog {
  176.     return processesBacklog;
  177. }
  178. - (void) setProcessesBacklog: (BOOL) value {
  179.     processesBacklog = value;
  180. }
  181. - (BOOL) listeningOnAppActivate {
  182.     id appDelegate = [NSApp delegate];
  183.     return (appDelegate!=nil && [appDelegate isKindOfClass: [AppleRemoteApplicationDelegate class]]);
  184. }
  185. - (void) setListeningOnAppActivate: (BOOL) value {
  186.     if (value) {
  187.         if ([self listeningOnAppActivate]) return;
  188.         AppleRemoteApplicationDelegate* appDelegate = [[AppleRemoteApplicationDelegate alloc] initWithApplicationDelegate: [NSApp delegate]];
  189.         /* NSApp does not retain its delegate therefore we keep retain count on 1 */
  190.         [NSApp setDelegate: appDelegate];
  191.     } else {
  192.         if ([self listeningOnAppActivate]==NO) return;
  193.         AppleRemoteApplicationDelegate* appDelegate = (AppleRemoteApplicationDelegate*)[NSApp delegate];
  194.         id previousAppDelegate = [appDelegate applicationDelegate];
  195.         [NSApp setDelegate: previousAppDelegate];
  196.         [appDelegate release];
  197.     }
  198. }
  199. - (BOOL) simulatesPlusMinusHold {
  200.     return simulatePlusMinusHold;
  201. }
  202. - (void) setSimulatesPlusMinusHold: (BOOL) value {
  203.     simulatePlusMinusHold = value;
  204. }
  205. - (IBAction) startListening: (id) sender {
  206.     if ([self isListeningToRemote]) return;
  207.     io_object_t hidDevice = [self findAppleRemoteDevice];
  208.     if (hidDevice == 0) return;
  209.     if ([self createInterfaceForDevice:hidDevice] == NULL) {
  210.         goto error;
  211.     }
  212.     if ([self initializeCookies]==NO) {
  213.         goto error;
  214.     }
  215.     if ([self openDevice]==NO) {
  216.         goto error;
  217.     }
  218.     goto cleanup;
  219. error:
  220.     [self stopListening:self];
  221. cleanup:
  222.     IOObjectRelease(hidDevice);
  223. }
  224. - (IBAction) stopListening: (id) sender {
  225.     if (eventSource != NULL) {
  226.         CFRunLoopRemoveSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
  227.         CFRelease(eventSource);
  228.         eventSource = NULL;
  229.     }
  230.     if (queue != NULL) {
  231.         (*queue)->stop(queue);
  232.         //dispose of queue
  233.         (*queue)->dispose(queue);
  234.         //release the queue we allocated
  235.         (*queue)->Release(queue);
  236.         queue = NULL;
  237.     }
  238.     if (allCookies != nil) {
  239.         [allCookies autorelease];
  240.         allCookies = nil;
  241.     }
  242.     if (hidDeviceInterface != NULL) {
  243.         //close the device
  244.         (*hidDeviceInterface)->close(hidDeviceInterface);
  245.         //release the interface
  246.         (*hidDeviceInterface)->Release(hidDeviceInterface);
  247.         hidDeviceInterface = NULL;
  248.     }
  249. }
  250. @end
  251. @implementation AppleRemote (Singleton)
  252. static AppleRemote* sharedInstance=nil;
  253. + (AppleRemote*) sharedRemote {
  254.     @synchronized(self) {
  255.         if (sharedInstance == nil) {
  256.             sharedInstance = [[self alloc] init];
  257.         }
  258.     }
  259.     return sharedInstance;
  260. }
  261. + (id)allocWithZone:(NSZone *)zone {
  262.     @synchronized(self) {
  263.         if (sharedInstance == nil) {
  264.             return [super allocWithZone:zone];
  265.         }
  266.     }
  267.     return sharedInstance;
  268. }
  269. - (id)copyWithZone:(NSZone *)zone {
  270.     return self;
  271. }
  272. - (id)retain {
  273.     return self;
  274. }
  275. - (NSUInteger)retainCount {
  276.     return UINT_MAX;  //denotes an object that cannot be released
  277. }
  278. - (void)release {
  279.     //do nothing
  280. }
  281. - (id)autorelease {
  282.     return self;
  283. }
  284. @end
  285. @implementation AppleRemote (PrivateMethods)
  286. - (void) setRemoteId: (int) value {
  287.     remoteId = value;
  288. }
  289. - (IOHIDQueueInterface**) queue {
  290.     return queue;
  291. }
  292. - (IOHIDDeviceInterface**) hidDeviceInterface {
  293.     return hidDeviceInterface;
  294. }
  295. - (NSDictionary*) cookieToButtonMapping {
  296.     return cookieToButtonMapping;
  297. }
  298. - (NSString*) validCookieSubstring: (NSString*) cookieString {
  299.     if (cookieString == nil || [cookieString length] == 0) return nil;
  300.     NSEnumerator* keyEnum = [[self cookieToButtonMapping] keyEnumerator];
  301.     NSString* key;
  302.     while(key = [keyEnum nextObject]) {
  303.         NSRange range = [cookieString rangeOfString:key];
  304.         if (range.location == 0) return key;
  305.     }
  306.     return nil;
  307. }
  308. - (void) sendSimulatedPlusMinusEvent: (id) time {
  309.     BOOL startSimulateHold = NO;
  310.     AppleRemoteEventIdentifier event = lastPlusMinusEvent;
  311.     @synchronized(self) {
  312.         startSimulateHold = (lastPlusMinusEvent>0 && lastPlusMinusEventTime == [time doubleValue]);
  313.     }
  314.     if (startSimulateHold) {
  315.         lastEventSimulatedHold = YES;
  316.         event = (event==kRemoteButtonVolume_Plus) ? kRemoteButtonVolume_Plus_Hold : kRemoteButtonVolume_Minus_Hold;
  317.         [delegate appleRemoteButton:event pressedDown: YES clickCount: 1];
  318.     }
  319. }
  320. - (void) sendRemoteButtonEvent: (AppleRemoteEventIdentifier) event pressedDown: (BOOL) pressedDown {
  321.     if (delegate) {
  322.         if (simulatePlusMinusHold) {
  323.             if (event == kRemoteButtonVolume_Plus || event == kRemoteButtonVolume_Minus) {
  324.                 if (pressedDown) {
  325.                     lastPlusMinusEvent = event;
  326.                     lastPlusMinusEventTime = [NSDate timeIntervalSinceReferenceDate];
  327.                     [self performSelector:@selector(sendSimulatedPlusMinusEvent:)
  328.                                withObject:[NSNumber numberWithDouble:lastPlusMinusEventTime]
  329.                                afterDelay:HOLD_RECOGNITION_TIME_INTERVAL];
  330.                     return;
  331.                 } else {
  332.                     if (lastEventSimulatedHold) {
  333.                         event = (event==kRemoteButtonVolume_Plus) ? kRemoteButtonVolume_Plus_Hold : kRemoteButtonVolume_Minus_Hold;
  334.                         lastPlusMinusEvent = 0;
  335.                         lastEventSimulatedHold = NO;
  336.                     } else {
  337.                         @synchronized(self) {
  338.                             lastPlusMinusEvent = 0;
  339.                         }
  340.                         pressedDown = YES;
  341.                     }
  342.                 }
  343.             }
  344.         }
  345.         if (([self clickCountEnabledButtons] & event) == event) {
  346.             if (pressedDown==NO && (event == kRemoteButtonVolume_Minus || event == kRemoteButtonVolume_Plus)) {
  347.                 return; // this one is triggered automatically by the handler
  348.             }
  349.             NSNumber* eventNumber;
  350.             NSNumber* timeNumber;
  351.             @synchronized(self) {
  352.                 lastClickCountEventTime = [NSDate timeIntervalSinceReferenceDate];
  353.                 if (lastClickCountEvent == event) {
  354.                     eventClickCount = eventClickCount + 1;
  355.                 } else {
  356.                     eventClickCount = 1;
  357.                 }
  358.                 lastClickCountEvent = event;
  359.                 timeNumber = [NSNumber numberWithDouble:lastClickCountEventTime];
  360.                 eventNumber= [NSNumber numberWithUnsignedInt:event];
  361.             }
  362.             [self performSelector: @selector(executeClickCountEvent:)
  363.                        withObject: [NSArray arrayWithObjects:eventNumber, timeNumber, nil]
  364.                        afterDelay: maxClickTimeDifference];
  365.         } else {
  366.             [delegate appleRemoteButton:event pressedDown: pressedDown clickCount:1];
  367.         }
  368.     }
  369. }
  370. - (void) executeClickCountEvent: (NSArray*) values {
  371.     AppleRemoteEventIdentifier event = [[values objectAtIndex: 0] unsignedIntValue];
  372.     NSTimeInterval eventTimePoint = [[values objectAtIndex: 1] doubleValue];
  373.     BOOL finishedClicking = NO;
  374.     int finalClickCount = eventClickCount;
  375.     @synchronized(self) {
  376.         finishedClicking = (event != lastClickCountEvent || eventTimePoint == lastClickCountEventTime);
  377.         if (finishedClicking) eventClickCount = 0;
  378.     }
  379.     if (finishedClicking) {
  380.         [delegate appleRemoteButton:event pressedDown: YES clickCount:finalClickCount];
  381.         if ([self simulatesPlusMinusHold]==NO && (event == kRemoteButtonVolume_Minus || event == kRemoteButtonVolume_Plus)) {
  382.             // trigger a button release event, too
  383.             [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow:0.1]];
  384.             [delegate appleRemoteButton:event pressedDown: NO clickCount:finalClickCount];
  385.         }
  386.     }
  387. }
  388. - (void) handleEventWithCookieString: (NSString*) cookieString sumOfValues: (SInt32) sumOfValues {
  389.     /*
  390.     if (previousRemainingCookieString) {
  391.         cookieString = [previousRemainingCookieString stringByAppendingString: cookieString];
  392.         NSLog(@"New cookie string is %@", cookieString);
  393.         [previousRemainingCookieString release], previousRemainingCookieString=nil;
  394. }*/
  395.     if (cookieString == nil || [cookieString length] == 0) return;
  396.     NSNumber* buttonId = [[self cookieToButtonMapping] objectForKey: cookieString];
  397.     if (buttonId != nil) {
  398.         switch ([buttonId intValue]) {
  399.             case k2009RemoteButtonPlay:
  400.             case k2009RemoteButtonMiddlePlay:
  401.                 buttonId = [NSNumber numberWithInt:kRemoteButtonPlay];
  402.                 break;
  403.             default:
  404.                 break;
  405. }
  406.         [self sendRemoteButtonEvent: [buttonId intValue] pressedDown: (sumOfValues>0)];
  407.     } else {
  408.         // let's see if a number of events are stored in the cookie string. this does
  409.         // happen when the main thread is too busy to handle all incoming events in time.
  410.         NSString* subCookieString;
  411.         NSString* lastSubCookieString=nil;
  412.         while(subCookieString = [self validCookieSubstring: cookieString]) {
  413.             cookieString = [cookieString substringFromIndex: [subCookieString length]];
  414.             lastSubCookieString = subCookieString;
  415.             if (processesBacklog) [self handleEventWithCookieString: subCookieString sumOfValues:sumOfValues];
  416.         }
  417.         if (processesBacklog == NO && lastSubCookieString != nil) {
  418.             // process the last event of the backlog and assume that the button is not pressed down any longer.
  419.             // The events in the backlog do not seem to be in order and therefore (in rare cases) the last event might be
  420.             // a button pressed down event while in reality the user has released it.
  421.             // NSLog(@"processing last event of backlog");
  422.             [self handleEventWithCookieString: lastSubCookieString sumOfValues:0];
  423.         }
  424.         if ([cookieString length] > 0) {
  425.             msg_Warn( VLCIntf, "Unknown AR button for cookiestring %s", [cookieString UTF8String]);
  426.         }
  427.     }
  428. }
  429. @end
  430. /*  Callback method for the device queue
  431. Will be called for any event of any type (cookie) to which we subscribe
  432. */
  433. static void QueueCallbackFunction(void* target,  IOReturn result, void* refcon, void* sender) {
  434.     AppleRemote* remote = (AppleRemote*)target;
  435.     IOHIDEventStruct event;
  436.     AbsoluteTime     zeroTime = {0,0};
  437.     NSMutableString* cookieString = [NSMutableString string];
  438.     SInt32           sumOfValues = 0;
  439.     while (result == kIOReturnSuccess)
  440.     {
  441.         result = (*[remote queue])->getNextEvent([remote queue], &event, zeroTime, 0);
  442.         if ( result != kIOReturnSuccess )
  443.             continue;
  444.         //printf("%d %d %dn", event.elementCookie, event.value, event.longValue);
  445.         if (REMOTE_SWITCH_COOKIE == (int)event.elementCookie) {
  446.             [remote setRemoteId: event.value];
  447.             [remote handleEventWithCookieString: @"19_" sumOfValues: 0];
  448.         } else {
  449.             if (((int)event.elementCookie)!=5) {
  450.                 sumOfValues+=event.value;
  451.                 [cookieString appendString:[NSString stringWithFormat:@"%d_", event.elementCookie]];
  452.             }
  453.         }
  454.     }
  455.     [remote handleEventWithCookieString: cookieString sumOfValues: sumOfValues];
  456. }
  457. @implementation AppleRemote (IOKitMethods)
  458. - (IOHIDDeviceInterface**) createInterfaceForDevice: (io_object_t) hidDevice {
  459.     io_name_t               className;
  460.     IOCFPlugInInterface**   plugInInterface = NULL;
  461.     HRESULT                 plugInResult = S_OK;
  462.     SInt32                  score = 0;
  463.     IOReturn                ioReturnValue = kIOReturnSuccess;
  464.     hidDeviceInterface = NULL;
  465.     ioReturnValue = IOObjectGetClass(hidDevice, className);
  466.     if (ioReturnValue != kIOReturnSuccess) {
  467.         msg_Err( VLCIntf, "Failed to get IOKit class name.");
  468.         return NULL;
  469.     }
  470.     ioReturnValue = IOCreatePlugInInterfaceForService(hidDevice,
  471.                                                       kIOHIDDeviceUserClientTypeID,
  472.                                                       kIOCFPlugInInterfaceID,
  473.                                                       &plugInInterface,
  474.                                                       &score);
  475.     if (ioReturnValue == kIOReturnSuccess)
  476.     {
  477.         //Call a method of the intermediate plug-in to create the device interface
  478.         plugInResult = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (LPVOID) &hidDeviceInterface);
  479.         if (plugInResult != S_OK) {
  480.             msg_Err( VLCIntf, "Couldn't create HID class device interface");
  481.         }
  482.         // Release
  483.         if (plugInInterface) (*plugInInterface)->Release(plugInInterface);
  484.     }
  485.     return hidDeviceInterface;
  486. }
  487. - (io_object_t) findAppleRemoteDevice {
  488.     CFMutableDictionaryRef hidMatchDictionary = NULL;
  489.     IOReturn ioReturnValue = kIOReturnSuccess;
  490.     io_iterator_t hidObjectIterator = 0;
  491.     io_object_t hidDevice = 0;
  492.     // Set up a matching dictionary to search the I/O Registry by class
  493.     // name for all HID class devices
  494.     hidMatchDictionary = IOServiceMatching(AppleRemoteDeviceName);
  495.     // Now search I/O Registry for matching devices.
  496.     ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault, hidMatchDictionary, &hidObjectIterator);
  497.     if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0)) {
  498.         hidDevice = IOIteratorNext(hidObjectIterator);
  499.     }
  500.     // release the iterator
  501.     IOObjectRelease(hidObjectIterator);
  502.     return hidDevice;
  503. }
  504. - (BOOL) initializeCookies {
  505.     IOHIDDeviceInterface122** handle = (IOHIDDeviceInterface122**)hidDeviceInterface;
  506.     IOHIDElementCookie      cookie;
  507.     long                    usage;
  508.     long                    usagePage;
  509.     id                      object;
  510.     NSArray*                elements = nil;
  511.     NSDictionary*           element;
  512.     IOReturn success;
  513.     if (!handle || !(*handle)) return NO;
  514.     /* Copy all elements, since we're grabbing most of the elements
  515.      * for this device anyway, and thus, it's faster to iterate them
  516.      * ourselves. When grabbing only one or two elements, a matching
  517.      * dictionary should be passed in here instead of NULL. */
  518.     success = (*handle)->copyMatchingElements(handle, NULL, (CFArrayRef*)&elements);
  519.     if (success == kIOReturnSuccess) {
  520.         [elements autorelease];
  521.         /*
  522.         cookies = calloc(NUMBER_OF_APPLE_REMOTE_ACTIONS, sizeof(IOHIDElementCookie));
  523.         memset(cookies, 0, sizeof(IOHIDElementCookie) * NUMBER_OF_APPLE_REMOTE_ACTIONS);
  524.         */
  525.         allCookies = [[NSMutableArray alloc] init];
  526.         int i;
  527.         for (i=0; i< [elements count]; i++) {
  528.             element = [elements objectAtIndex:i];
  529.             //Get cookie
  530.             object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementCookieKey) ];
  531.             if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue;
  532.             if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue;
  533.             cookie = (IOHIDElementCookie) [object longValue];
  534.             //Get usage
  535.             object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementUsageKey) ];
  536.             if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue;
  537.             usage = [object longValue];
  538.             //Get usage page
  539.             object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementUsagePageKey) ];
  540.             if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue;
  541.             usagePage = [object longValue];
  542.             [allCookies addObject: [NSNumber numberWithInt:(int)cookie]];
  543.         }
  544.     } else {
  545.         return NO;
  546.     }
  547.     return YES;
  548. }
  549. - (BOOL) openDevice {
  550.     HRESULT  result;
  551.     IOHIDOptionsType openMode = kIOHIDOptionsTypeNone;
  552.     if ([self isOpenInExclusiveMode]) openMode = kIOHIDOptionsTypeSeizeDevice;
  553.     IOReturn ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, openMode);
  554.     if (ioReturnValue == KERN_SUCCESS) {
  555.         queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
  556.         if (queue) {
  557.             result = (*queue)->create(queue, 0, 12);    //depth: maximum number of elements in queue before oldest elements in queue begin to be lost.
  558.             int i=0;
  559.             for(i=0; i<[allCookies count]; i++) {
  560.                 IOHIDElementCookie cookie = (IOHIDElementCookie)[[allCookies objectAtIndex:i] intValue];
  561.                 (*queue)->addElement(queue, cookie, 0);
  562.             }
  563.             // add callback for async events
  564.             ioReturnValue = (*queue)->createAsyncEventSource(queue, &eventSource);
  565.             if (ioReturnValue == KERN_SUCCESS) {
  566.                 ioReturnValue = (*queue)->setEventCallout(queue,QueueCallbackFunction, self, NULL);
  567.                 if (ioReturnValue == KERN_SUCCESS) {
  568.                     CFRunLoopAddSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
  569.                     //start data delivery to queue
  570.                     (*queue)->start(queue);
  571.                     return YES;
  572.                 } else {
  573.                     msg_Err( VLCIntf, "Error when setting event callout");
  574.                 }
  575.             } else {
  576.                 msg_Err( VLCIntf, "Error when creating async event source");
  577.             }
  578.         } else {
  579.             msg_Err( VLCIntf, "Error when opening HUD device");
  580.         }
  581.     }
  582.     return NO;
  583. }
  584. @end
  585. @implementation AppleRemoteApplicationDelegate
  586. - (id) initWithApplicationDelegate: (id) delegate {
  587.     if (self = [super init]) {
  588.         applicationDelegate = [delegate retain];
  589.     }
  590.     return self;
  591. }
  592. - (void) dealloc {
  593.     [applicationDelegate release];
  594.     [super dealloc];
  595. }
  596. - (id) applicationDelegate {
  597.     return applicationDelegate;
  598. }
  599. - (void)applicationWillBecomeActive:(NSNotification *)aNotification {
  600.     if ([applicationDelegate respondsToSelector: @selector(applicationWillBecomeActive:)]) {
  601.         [applicationDelegate applicationWillBecomeActive: aNotification];
  602.     }
  603. }
  604. - (void)applicationDidBecomeActive:(NSNotification *)aNotification {
  605.     [[AppleRemote sharedRemote] setListeningToRemote: YES];
  606.     if ([applicationDelegate respondsToSelector: @selector(applicationDidBecomeActive:)]) {
  607.         [applicationDelegate applicationDidBecomeActive: aNotification];
  608.     }
  609. }
  610. - (void)applicationWillResignActive:(NSNotification *)aNotification {
  611.     [[AppleRemote sharedRemote] setListeningToRemote: NO];
  612.     if ([applicationDelegate respondsToSelector: @selector(applicationWillResignActive:)]) {
  613.         [applicationDelegate applicationWillResignActive: aNotification];
  614.     }
  615. }
  616. - (void)applicationDidResignActive:(NSNotification *)aNotification {
  617.     if ([applicationDelegate respondsToSelector: @selector(applicationDidResignActive:)]) {
  618.         [applicationDelegate applicationDidResignActive: aNotification];
  619.     }
  620. }
  621. - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
  622.     NSMethodSignature* signature = [super methodSignatureForSelector: aSelector];
  623.     if (signature == nil && applicationDelegate != nil) {
  624.         signature = [applicationDelegate methodSignatureForSelector: aSelector];
  625.     }
  626.     return signature;
  627. }
  628. - (void)forwardInvocation:(NSInvocation *)invocation {
  629.     SEL aSelector = [invocation selector];
  630.     if (applicationDelegate==nil || [applicationDelegate respondsToSelector:aSelector]==NO) {
  631.         [super forwardInvocation: invocation];
  632.         return;
  633.     }
  634.     [invocation invokeWithTarget:applicationDelegate];
  635. }
  636. @end