]> git.sesse.net Git - vlc/commitdiff
* various improvements to the AppleRemote support by Martin Kahr <martin -attt->...
authorFelix Paul Kühne <fkuehne@videolan.org>
Mon, 27 Nov 2006 21:20:23 +0000 (21:20 +0000)
committerFelix Paul Kühne <fkuehne@videolan.org>
Mon, 27 Nov 2006 21:20:23 +0000 (21:20 +0000)
    - holding +/- continuously increases/decreases volume
    - pressing Play twice toggles fullscreen mode
    -  a press on Menu shows the "Position" overlay (like pressing the key
't')

This updates our copy of Martin's unofficial framework to the latest version released.

modules/gui/macosx/AppleRemote.h
modules/gui/macosx/AppleRemote.m
modules/gui/macosx/intf.h
modules/gui/macosx/intf.m

index d53225f84ec62fc7dde1cc0f5e6f9d091f59ff6d..57b7010145fde183b159e9a65f634ddf295a0185 100644 (file)
 
 enum AppleRemoteEventIdentifier
 {
-       kRemoteButtonVolume_Plus=0,
-       kRemoteButtonVolume_Minus,
-       kRemoteButtonMenu,
-       kRemoteButtonPlay,
-       kRemoteButtonRight,     
-       kRemoteButtonLeft,      
-       kRemoteButtonRight_Hold,        
-       kRemoteButtonLeft_Hold,
-       kRemoteButtonMenu_Hold,
-       kRemoteButtonPlay_Sleep,
-       kRemoteControl_Switched
+    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
 };
 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
+/*  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;
-       NSMutableArray*            allCookies;
-       NSMutableDictionary*   cookieToButtonMapping;
-
-       BOOL openInExclusiveMode;
-       
-       int remoteId;
-
-       IBOutlet id delegate;
+    IOHIDDeviceInterface** hidDeviceInterface;
+    IOHIDQueueInterface**  queue;
+    NSMutableArray*        allCookies;
+    NSMutableDictionary*   cookieToButtonMapping;
+    
+    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;
+    
+    IBOutlet id delegate;
 }
 
-- (void) setRemoteId: (int) aValue;
 - (int) remoteId;
 
 - (BOOL) isRemoteAvailable;
@@ -103,6 +116,42 @@ typedef enum AppleRemoteEventIdentifier AppleRemoteEventIdentifier;
 - (BOOL) isOpenInExclusiveMode;
 - (void) setOpenInExclusiveMode: (BOOL) value;
 
+/* 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 */
+- (BOOL) clickCountingEnabled;
+- (void) setClickCountingEnabled: (BOOL) value;
+
+- (unsigned int) clickCountEnabledButtons;
+- (void) setClickCountEnabledButtons: (unsigned int)value;
+
+/* the maximum time difference till which clicks are recognized as multi clicks */
+- (NSTimeInterval) maximumClickCountTimeDifference;
+- (void) setMaximumClickCountTimeDifference: (NSTimeInterval) timeDiff;
+
+/* 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. */
+- (BOOL) processesBacklog;
+- (void) setProcessesBacklog: (BOOL) value;
+
+/* 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. */
+- (BOOL) listeningOnAppActivate;
+- (void) setListeningOnAppActivate: (BOOL) value;
+
+/* 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 */
+- (BOOL) simulatesPlusMinusHold;
+- (void) setSimulatesPlusMinusHold: (BOOL) value;
+
+/* Delegates are not retained */
 - (void) setDelegate: (id) delegate;
 - (id) delegate;
 
@@ -116,15 +165,15 @@ typedef enum AppleRemoteEventIdentifier AppleRemoteEventIdentifier;
 
 @end
 
-/*     Method definitions for the delegate of the AppleRemote class
-*/
+/*  Method definitions for the delegate of the AppleRemote class */
 @interface NSObject(NSAppleRemoteDelegate)
 
-- (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier pressedDown: (BOOL) pressedDown;
+- (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier pressedDown: (BOOL) pressedDown clickCount: (unsigned int) count;
 
 @end
 
 @interface AppleRemote (PrivateMethods) 
+- (void) setRemoteId: (int) aValue;
 - (NSDictionary*) cookieToButtonMapping;
 - (IOHIDQueueInterface**) queue;
 - (IOHIDDeviceInterface**) hidDeviceInterface;
@@ -136,4 +185,15 @@ typedef enum AppleRemoteEventIdentifier AppleRemoteEventIdentifier;
 - (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
index 1b5dafe6e548d9685cc583365ae9400d6d3a3cac..fb81d6797c60110d68c15427d5c8b2ee893aef62 100644 (file)
 
 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
 
-- (id) init
-{      
-    self = [super init];
-
-    if ( self == [super init] ) {
-               openInExclusiveMode = YES;
-               queue = NULL;
-               hidDeviceInterface = NULL;
-               cookieToButtonMapping = [[NSMutableDictionary alloc] init];
-               
-               [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Plus]      forKey:@"14_12_11_6_5_"];
-               [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonVolume_Minus] forKey:@"14_13_11_6_5_"];           
-               [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu]                     forKey:@"14_7_6_5_14_7_6_5_"];          
-               [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay]                     forKey:@"14_8_6_5_14_8_6_5_"];
-               [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight]            forKey:@"14_9_6_5_14_9_6_5_"];
-               [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft]                     forKey:@"14_10_6_5_14_10_6_5_"];
-               [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold]       forKey:@"14_6_5_4_2_"];
-               [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold]        forKey:@"14_6_5_3_2_"];
-               [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold]        forKey:@"14_6_5_14_6_5_"];
-               [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Sleep]       forKey:@"18_14_6_5_18_14_6_5_"];
-               [cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched]       forKey:@"19_"];         
-       }
-       
-       return self;
+#pragma public interface
+
+- (id) init {   
+    if ( self = [super init] ) {
+        openInExclusiveMode = YES;
+        queue = NULL;
+        hidDeviceInterface = NULL;
+        cookieToButtonMapping = [[NSMutableDictionary alloc] init];
+
+        [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_"]; 
+
+        /* defaults */
+        [self setSimulatesPlusMinusHold: YES];
+        maxClickTimeDifference = DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE;
+    }
+
+    return self;
 }
 
 - (void) dealloc {
-       [self stopListening:self];
-       [cookieToButtonMapping release];
-       [super dealloc];
+    [self stopListening:self];
+    [cookieToButtonMapping release];
+    [super dealloc];
 }
 
-- (void) setRemoteId: (int) value {
-       remoteId = value;
-}
 - (int) remoteId {
-       return remoteId;
+    return remoteId;
 }
 
-- (BOOL) isRemoteAvailable {   
-       io_object_t hidDevice = [self findAppleRemoteDevice];
-       if (hidDevice != 0) {
-               IOObjectRelease(hidDevice);
-               return YES;
-       } else {
-               return NO;              
-       }
+- (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);     
+    return (hidDeviceInterface != NULL && allCookies != NULL && queue != NULL); 
 }
 
 - (void) setListeningToRemote: (BOOL) value {
-       if (value == NO) {
-               [self stopListening:self];
-       } else {
-               [self startListening:self];
-       }
+    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 respondsToSelector:@selector(appleRemoteButton:pressedDown:)]==NO) return;
-       
-       [_delegate retain];
-       [delegate release];
-       delegate = _delegate;
+    if (_delegate && [_delegate respondsToSelector:@selector(appleRemoteButton:pressedDown:clickCount:)]==NO) return;
+
+    delegate = _delegate;
 }
 - (id) delegate {
-       return delegate;
+    return delegate;
 }
 
 - (BOOL) isOpenInExclusiveMode {
-       return openInExclusiveMode;
+    return openInExclusiveMode;
 }
 - (void) setOpenInExclusiveMode: (BOOL) value {
-       openInExclusiveMode = 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;
-       
+    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);
+    [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;              
-       }       
+    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
@@ -198,20 +261,20 @@ cleanup:
 
 static AppleRemote* sharedInstance=nil;
 
-+ (AppleRemote*) sharedRemote {        
-       @synchronized(self) {
++ (AppleRemote*) sharedRemote { 
+    @synchronized(self) {
         if (sharedInstance == nil) {
             sharedInstance = [[self alloc] init];
         }
     }
-       return sharedInstance;
+    return sharedInstance;
 }
 + (id)allocWithZone:(NSZone *)zone {
     @synchronized(self) {
         if (sharedInstance == nil) {
             return [super allocWithZone:zone];
         }
-    }  
+    }   
     return sharedInstance;
 }
 - (id)copyWithZone:(NSZone *)zone {
@@ -234,217 +297,408 @@ static AppleRemote* sharedInstance=nil;
 
 @implementation AppleRemote (PrivateMethods) 
 
+- (void) setRemoteId: (int) value {
+    remoteId = value;
+}
+
 - (IOHIDQueueInterface**) queue {
-       return queue;
+    return queue;
 }
 
 - (IOHIDDeviceInterface**) hidDeviceInterface {
-       return hidDeviceInterface;
+    return hidDeviceInterface;
 }
 
 
 - (NSDictionary*) cookieToButtonMapping {
-       return 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 (simulatePlusMinusHold) {
+            if (event == kRemoteButtonVolume_Plus || event == kRemoteButtonVolume_Minus) {
+                if (pressedDown) {
+                    lastPlusMinusEvent = event;
+                    lastPlusMinusEventTime = [NSDate timeIntervalSinceReferenceDate];
+                    [self performSelector:@selector(sendSimulatedPlusMinusEvent:) 
+                               withObject:[NSNumber numberWithDouble: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 = [NSNumber numberWithDouble:lastClickCountEventTime];
+                eventNumber= [NSNumber numberWithUnsignedInt:event];
+            }
+            [self performSelector: @selector(executeClickCountEvent:) 
+                       withObject: [NSArray arrayWithObjects:eventNumber, timeNumber, nil]
+                       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 {
-       NSNumber* buttonId = [[self cookieToButtonMapping] objectForKey: cookieString];
-       if (buttonId != nil) {
-               if (delegate) {         
-                       [delegate appleRemoteButton:[buttonId intValue] pressedDown: (sumOfValues>0)];
-               }               
-       } else {
-               NSLog(@"Unknown button for cookiestring %@", cookieString);
-       }
+    /*
+    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 (processesBacklog) [self handleEventWithCookieString: subCookieString sumOfValues:sumOfValues];
+        }
+        if (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) {
+            NSLog(@"Unknown button for cookiestring %@", cookieString);
+        }       
+    }
 }
 
 @end
 
-/*     Callback method for the device queue
+/*  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 = (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;
-               
-               if (REMOTE_SWITCH_COOKIE == (int)event.elementCookie) {
-                       [remote setRemoteId: event.value];
-                       [remote handleEventWithCookieString: @"19_" sumOfValues: 0];
-               } else {
-                       sumOfValues+=event.value;
-                       [cookieString appendString:[NSString stringWithFormat:@"%d_", event.elementCookie]];                                    
-               }
-               
-               //printf("%d %d %d\n", event.elementCookie, event.value, event.longValue);              
-       }
-       
-       [remote handleEventWithCookieString: cookieString sumOfValues: sumOfValues];    
-       
+static void QueueCallbackFunction(void* target,  IOReturn result, void* refcon, void* sender) { 
+    AppleRemote* remote = (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) {
-               NSLog(@"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) {
-                       NSLog(@"Error: Couldn't create HID class device interface");
-               }
-               // Release
-               if (plugInInterface) (*plugInInterface)->Release(plugInInterface);
-       }
-       return hidDeviceInterface;
+    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) {
+        NSLog(@"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) {
+            NSLog(@"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;
+    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 = nil;
-       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) {
-               
-               [elements autorelease];         
-               /*
-               cookies = calloc(NUMBER_OF_APPLE_REMOTE_ACTIONS, sizeof(IOHIDElementCookie)); 
-               memset(cookies, 0, sizeof(IOHIDElementCookie) * NUMBER_OF_APPLE_REMOTE_ACTIONS);
-               */
-               allCookies = [[NSMutableArray alloc] init];
-               unsigned int i;
-               for (i=0; i< [elements count]; 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];
-
-                       [allCookies addObject: [NSNumber numberWithInt:(int)cookie]];
-               }
-       } else {
-               return NO;
-       }
-       
-       return YES;
+    IOHIDDeviceInterface122** handle = (IOHIDDeviceInterface122**)hidDeviceInterface;
+    IOHIDElementCookie      cookie;
+    long                    usage;
+    long                    usagePage;
+    id                      object;
+    NSArray*                elements = nil;
+    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) {
+
+        [elements autorelease];     
+        /*
+        cookies = calloc(NUMBER_OF_APPLE_REMOTE_ACTIONS, sizeof(IOHIDElementCookie)); 
+        memset(cookies, 0, sizeof(IOHIDElementCookie) * NUMBER_OF_APPLE_REMOTE_ACTIONS);
+        */
+        allCookies = [[NSMutableArray alloc] init];
+        int i;
+        for (i=0; i< [elements count]; 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];
+
+            [allCookies addObject: [NSNumber numberWithInt:(int)cookie]];
+        }
+    } else {
+        return NO;
+    }
+
+    return YES;
 }
 
 - (BOOL) openDevice {
-       HRESULT  result;
-       
-       IOHIDOptionsType openMode = kIOHIDOptionsTypeNone;
-       if ([self isOpenInExclusiveMode]) 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.
-
-                       unsigned int i=0;
-                       for(i=0; i<[allCookies count]; i++) {
-                               IOHIDElementCookie cookie = (IOHIDElementCookie)[[allCookies objectAtIndex:i] intValue];
-                               (*queue)->addElement(queue, cookie, 0);
-                       }
-                                                                         
-                       // add callback for async events
-                       CFRunLoopSourceRef eventSource;
-                       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 {
-                                       NSLog(@"Error when setting event callout");
-                               }
-                       } else {
-                               NSLog(@"Error when creating async event source");
-                       }
-               } else {
-                       NSLog(@"Error when opening device");
-               }
-       }
-       return NO;                              
+    HRESULT  result;
+
+    IOHIDOptionsType openMode = kIOHIDOptionsTypeNone;
+    if ([self isOpenInExclusiveMode]) 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.
+
+            int i=0;
+            for(i=0; i<[allCookies count]; i++) {
+                IOHIDElementCookie cookie = (IOHIDElementCookie)[[allCookies objectAtIndex:i] intValue];
+                (*queue)->addElement(queue, cookie, 0);
+            }
+
+            // add callback for async events
+            CFRunLoopSourceRef eventSource;
+            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 {
+                    NSLog(@"Error when setting event callout");
+                }
+            } else {
+                NSLog(@"Error when creating async event source");
+            }
+        } else {
+            NSLog(@"Error when opening device");
+        }
+    }
+    return NO;              
+}
+
+@end
+
+@implementation AppleRemoteApplicationDelegate
+
+- (id) initWithApplicationDelegate: (id) delegate {
+    if (self = [super init]) {
+        applicationDelegate = [delegate retain];
+    }
+    return self;
+}
+
+- (void) dealloc {
+    NSLog(@"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
index 4ba82f6461decbddc24f29080025c20acdefbeb3..91555d9aa4128b4e3688ca5cae8e1de3e9240d0c 100644 (file)
@@ -286,7 +286,7 @@ struct intf_sys_t
     int     i_lastShownVolume;
 
     AppleRemote * o_remote;
-    BOOL b_left_right_remote_button_hold; /* true as long as the user holds the left or right button on the remote control */
+    BOOL b_remote_button_hold; /* true as long as the user holds the left,right,plus or minus on the remote control */
 }
 
 + (VLCMain *)sharedInstance;
index 58fa96578f72d637fd7edd8314319f609c17a712..fae21c97adc3d5bb729258a340e7d9bf11abeccd 100644 (file)
@@ -350,6 +350,7 @@ static VLCMain *_o_sharedMainInstance = nil;
     i_lastShownVolume = -1;
 
     o_remote = [[AppleRemote alloc] init];
+       [o_remote setClickCountEnabledButtons: kRemoteButtonPlay];
     [o_remote setDelegate: _o_sharedMainInstance];
     
     return _o_sharedMainInstance;
@@ -706,52 +707,56 @@ static VLCMain *_o_sharedMainInstance = nil;
     [o_remote stopListening: self];
 }
 
-/* Helper method for the remote control interface in order to trigger forward/backward as long
-   as the user holds the left/right button */
-- (void) triggerMovieStepForRemoteButton: (NSNumber*) buttonIdentifierNumber 
+/* Helper method for the remote control interface in order to trigger forward/backward and volume
+   increase/decrease as long as the user holds the left/right, plus/minus button */
+- (void) executeHoldActionForRemoteButton: (NSNumber*) buttonIdentifierNumber 
 {
-    if (b_left_right_remote_button_hold) {
-        switch([buttonIdentifierNumber intValue]) {
+    if (b_remote_button_hold) 
+    {
+        switch([buttonIdentifierNumber intValue]) 
+        {
             case kRemoteButtonRight_Hold:       
-                [o_controls forward: self];
+                  [o_controls forward: self];
             break;
             case kRemoteButtonLeft_Hold:
-                [o_controls backward: self];
-            break;          
+                  [o_controls backward: self];
+            break;
+            case kRemoteButtonVolume_Plus_Hold:
+                [o_controls volumeUp: self];
+            break;
+            case kRemoteButtonVolume_Minus_Hold:
+                [o_controls volumeDown: self];
+            break;              
         }
-        if (b_left_right_remote_button_hold) {
+        if (b_remote_button_hold) 
+        {
             /* trigger event */
-            [self performSelector:@selector(triggerMovieStepForRemoteButton:) 
-                       withObject:buttonIdentifierNumber
-                       afterDelay:0.25];         
+            [self performSelector:@selector(executeHoldActionForRemoteButton:) 
+                         withObject:buttonIdentifierNumber
+                         afterDelay:0.25];         
         }
     }
 }
 
 /* Apple Remote callback */
-- (void)appleRemoteButton:(AppleRemoteEventIdentifier)buttonIdentifier
-    pressedDown:(BOOL)pressedDown
+- (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier 
+               pressedDown: (BOOL) pressedDown 
+                clickCount: (unsigned int) count 
 {
     switch( buttonIdentifier )
     {
         case kRemoteButtonPlay:
-            [o_controls play: self];
+            if (count >= 2) {
+                [o_controls toogleFullscreen:self];
+            } else {
+                [o_controls play: self];
+            }            
             break;
         case kRemoteButtonVolume_Plus:
-            /* there are two events when the plus or minus button is pressed
-               one when the button is pressed down and one when the button is released */
-            if( pressedDown )
-            {
-                [o_controls volumeUp: self];
-            }
+            [o_controls volumeUp: self];
             break;
         case kRemoteButtonVolume_Minus:
-            /* there are two events when the plus or minus button is pressed
-               one when the button is pressed down and one when the button is released */
-            if( pressedDown )
-            {
-                [o_controls volumeDown: self];
-            }
+            [o_controls volumeDown: self];
             break;
         case kRemoteButtonRight:
             [o_controls next: self];
@@ -761,17 +766,19 @@ static VLCMain *_o_sharedMainInstance = nil;
             break;
         case kRemoteButtonRight_Hold:
         case kRemoteButtonLeft_Hold:
+        case kRemoteButtonVolume_Plus_Hold:
+        case kRemoteButtonVolume_Minus_Hold:
             /* simulate an event as long as the user holds the button */
-            b_left_right_remote_button_hold = pressedDown;
+            b_remote_button_hold = pressedDown;
             if( pressedDown )
             {                
-                NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt: buttonIdentifier]; 
-                [self performSelector:@selector(triggerMovieStepForRemoteButton:) 
+                NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt: buttonIdentifier];  
+                [self performSelector:@selector(executeHoldActionForRemoteButton:) 
                            withObject:buttonIdentifierNumber];
             }
             break;
         case kRemoteButtonMenu:
-            [o_controls windowAction: self];
+            [o_controls showPosition: self];
             break;
         default:
             /* Add here whatever you want other buttons to do */