Commits

rt  committed de81be7

CWS-TOOLING: integrate CWS appleremote01

  • Participants
  • Parent commits 5324115

Comments (0)

Files changed (30)

File apple_remote/AppleRemote.m

+/*****************************************************************************
+ * RemoteControlWrapper.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license. 
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Code modified and adapted to OpenOffice.org 
+ * by Eric Bachard on 11.08.2008 under the same license
+ *
+ * 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.
+ *
+ *****************************************************************************/
+
+#import "AppleRemote.h"
+
+#import <mach/mach.h>
+#import <mach/mach_error.h>
+#import <IOKit/IOKitLib.h>
+#import <IOKit/IOCFPlugIn.h>
+#import <IOKit/hid/IOHIDKeys.h>
+
+const char* AppleRemoteDeviceName = "AppleIRController";
+
+// the WWDC 07 Leopard Build is missing the constant
+#ifndef NSAppKitVersionNumber10_4
+	#define NSAppKitVersionNumber10_4 824
+#endif
+
+@implementation AppleRemote
+
++ (const char*) remoteControlDeviceName {
+	return AppleRemoteDeviceName;
+}
+
+- (void) setCookieMappingInDictionary: (NSMutableDictionary*) _cookieToButtonMapping	{	
+    
+    // TODO : avoid such magics 
+	if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_4) {
+		// 10.4.x Tiger
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlus]		forKey:@"14_12_11_6_"];
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMinus]		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_Hold]	forKey:@"18_14_6_18_14_6_"];
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched]	forKey:@"19_"];			
+	} else {
+		// 10.5.x Leopard
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlus]		forKey:@"31_29_28_19_18_"];
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMinus]		forKey:@"31_30_28_19_18_"];	
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu]		forKey:@"31_20_19_18_31_20_19_18_"];
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay]		forKey:@"31_21_19_18_31_21_19_18_"];
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight]		forKey:@"31_22_19_18_31_22_19_18_"];
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft]		forKey:@"31_23_19_18_31_23_19_18_"];
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold]	forKey:@"31_19_18_4_2_"];
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold]	forKey:@"31_19_18_3_2_"];
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold]	forKey:@"31_19_18_31_19_18_"];
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Hold]	forKey:@"35_31_19_18_35_31_19_18_"];
+		[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched]	forKey:@"19_"];			
+	}
+}
+
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown {
+	if (pressedDown == NO && event == kRemoteButtonMenu_Hold) {
+		// There is no seperate event for pressed down on menu hold. We are simulating that event here
+		[super sendRemoteButtonEvent:event pressedDown:YES];
+	}	
+	
+	[super sendRemoteButtonEvent:event pressedDown:pressedDown];
+	
+	if (pressedDown && (event == kRemoteButtonRight || event == kRemoteButtonLeft || event == kRemoteButtonPlay || event == kRemoteButtonMenu || event == kRemoteButtonPlay_Hold)) {
+		// There is no seperate event when the button is being released. We are simulating that event here
+		[super sendRemoteButtonEvent:event pressedDown:NO];
+	}
+}
+
+@end

File apple_remote/GlobalKeyboardDevice.m

+/*****************************************************************************
+ * GlobalKeyboardDevice.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license. 
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Code modified and adapted to OpenOffice.org 
+ * by Eric Bachard on 11.08.2008 under the same license
+ *
+ * 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.
+ *
+ *****************************************************************************/
+
+
+#import "GlobalKeyboardDevice.h"
+
+#define F1 122
+#define F2 120
+#define F3 99
+#define F4 118
+#define F5 96
+#define F6 97
+#define F7 98
+
+/*
+ the following default keys are read and shall be used to change the keyboard mapping
+ 
+ mac.remotecontrols.GlobalKeyboardDevice.plus_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.plus_keycode
+ mac.remotecontrols.GlobalKeyboardDevice.minus_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.minus_keycode
+ mac.remotecontrols.GlobalKeyboardDevice.play_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.play_keycode
+ mac.remotecontrols.GlobalKeyboardDevice.left_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.left_keycode
+ mac.remotecontrols.GlobalKeyboardDevice.right_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.right_keycode
+ mac.remotecontrols.GlobalKeyboardDevice.menu_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.menu_keycode
+ mac.remotecontrols.GlobalKeyboardDevice.playhold_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.playhold_keycode
+ */
+
+static OSStatus hotKeyEventHandler(EventHandlerCallRef, EventRef, void*);
+
+@implementation GlobalKeyboardDevice
+
+- (id) initWithDelegate: (id) _remoteControlDelegate {	
+	if ( (self = [super initWithDelegate: _remoteControlDelegate]) ) {
+		hotKeyRemoteEventMapping = [[NSMutableDictionary alloc] init];
+		
+		unsigned int modifiers = cmdKey + shiftKey /*+ optionKey*/ + controlKey;
+				
+		[self mapRemoteButton:kRemoteButtonPlus			defaultKeycode:F1 defaultModifiers:modifiers];
+		[self mapRemoteButton:kRemoteButtonMinus		defaultKeycode:F2 defaultModifiers:modifiers];
+		[self mapRemoteButton:kRemoteButtonPlay			defaultKeycode:F3 defaultModifiers:modifiers];
+		[self mapRemoteButton:kRemoteButtonLeft			defaultKeycode:F4 defaultModifiers:modifiers];
+		[self mapRemoteButton:kRemoteButtonRight		defaultKeycode:F5 defaultModifiers:modifiers];
+		[self mapRemoteButton:kRemoteButtonMenu			defaultKeycode:F6 defaultModifiers:modifiers];
+		[self mapRemoteButton:kRemoteButtonPlay_Hold	defaultKeycode:F7 defaultModifiers:modifiers];		
+	}
+	return self;
+}
+
+- (void) dealloc {
+	[hotKeyRemoteEventMapping release];
+	[super dealloc];
+}
+
+- (void) mapRemoteButton: (RemoteControlEventIdentifier) remoteButtonIdentifier defaultKeycode: (unsigned int) defaultKeycode defaultModifiers: (unsigned int) defaultModifiers {
+	NSString* defaultsKey = NULL;	
+	
+	switch(remoteButtonIdentifier) {
+		case kRemoteButtonPlus:
+			defaultsKey = @"plus";
+			break;
+		case kRemoteButtonMinus:
+			defaultsKey = @"minus";
+			break;			
+		case kRemoteButtonMenu:
+			defaultsKey = @"menu";
+			break;			
+		case kRemoteButtonPlay:
+			defaultsKey = @"play";
+			break;
+		case kRemoteButtonRight:
+			defaultsKey = @"right";
+			break;			
+		case kRemoteButtonLeft:			
+			defaultsKey = @"left";
+			break;
+		case kRemoteButtonPlay_Hold:
+			defaultsKey = @"playhold";
+			break;			
+		default: 
+#ifdef DEBUG
+			NSLog(@"Unknown global keyboard defaults key for remote button identifier %d", remoteButtonIdentifier);
+#endif
+            break;
+	}
+	
+	NSNumber* modifiersCfg = [[NSUserDefaults standardUserDefaults] objectForKey: [NSString stringWithFormat: @"mac.remotecontrols.GlobalKeyboardDevice.%@_modifiers", defaultsKey]];
+	NSNumber* keycodeCfg   = [[NSUserDefaults standardUserDefaults] objectForKey: [NSString stringWithFormat: @"mac.remotecontrols.GlobalKeyboardDevice.%@_keycode", defaultsKey]];
+	
+	unsigned int modifiers = defaultModifiers;
+	if (modifiersCfg) modifiers = [modifiersCfg unsignedIntValue];
+	
+	unsigned int keycode = defaultKeycode;
+	if (keycodeCfg) keycode = [keycodeCfg unsignedIntValue];
+				  
+    [self registerHotKeyCode: keycode  modifiers: modifiers remoteEventIdentifier: remoteButtonIdentifier];
+}	
+
+- (void) setListeningToRemote: (BOOL) value {
+	if (value == [self isListeningToRemote]) return;
+	if (value) {
+		[self startListening: self];
+	} else {
+		[self stopListening: self];
+	}
+}
+- (BOOL) isListeningToRemote {	
+	return (eventHandlerRef!=NULL);
+}
+
+- (void) startListening: (id) sender {
+	
+	if (eventHandlerRef) return;
+		
+	EventTypeSpec eventSpec[2] = {
+		{ kEventClassKeyboard, kEventHotKeyPressed },
+		{ kEventClassKeyboard, kEventHotKeyReleased }
+	};    
+	
+	InstallEventHandler( GetEventDispatcherTarget(),
+						 (EventHandlerProcPtr)hotKeyEventHandler, 
+						 2, eventSpec, self, &eventHandlerRef);	
+}
+- (void) stopListening: (id) sender {
+	RemoveEventHandler(eventHandlerRef);
+	eventHandlerRef = NULL;
+}
+
+- (BOOL) sendsEventForButtonIdentifier: (RemoteControlEventIdentifier) identifier {
+	NSEnumerator* values = [hotKeyRemoteEventMapping objectEnumerator];
+	NSNumber* remoteIdentifier;
+	while( (remoteIdentifier = [values nextObject]) ) {
+		if ([remoteIdentifier unsignedIntValue] == identifier) return YES;
+	}
+	return NO;
+}
+
++ (const char*) remoteControlDeviceName {
+	return "Keyboard";
+}
+
+- (BOOL)registerHotKeyCode: (unsigned int) keycode modifiers: (unsigned int) modifiers remoteEventIdentifier: (RemoteControlEventIdentifier) identifier {	
+	OSStatus err;
+	EventHotKeyID hotKeyID;
+	EventHotKeyRef carbonHotKey;
+
+	hotKeyID.signature = 'PTHk';
+	hotKeyID.id = (long)keycode;
+	
+	err = RegisterEventHotKey(keycode, modifiers, hotKeyID, GetEventDispatcherTarget(), nil, &carbonHotKey );
+	
+	if( err )
+		return NO;
+	
+	[hotKeyRemoteEventMapping setObject: [NSNumber numberWithInt:identifier] forKey: [NSNumber numberWithUnsignedInt: hotKeyID.id]];
+	
+	return YES;
+}
+/*
+- (void)unregisterHotKey: (PTHotKey*)hotKey
+{
+	OSStatus err;
+	EventHotKeyRef carbonHotKey;
+	NSValue* key;
+	
+	if( [[self allHotKeys] containsObject: hotKey] == NO )
+		return;
+	
+	carbonHotKey = [self _carbonHotKeyForHotKey: hotKey];
+	NSAssert( carbonHotKey != nil, @"" );
+	
+	err = UnregisterEventHotKey( carbonHotKey );
+	//Watch as we ignore 'err':
+	
+	key = [NSValue valueWithPointer: carbonHotKey];
+	[mHotKeys removeObjectForKey: key];
+	
+	[self _updateEventHandler];
+	
+	//See that? Completely ignored
+}
+*/
+
+- (RemoteControlEventIdentifier) remoteControlEventIdentifierForID: (unsigned int) id {
+	NSNumber* remoteEventIdentifier = [hotKeyRemoteEventMapping objectForKey:[NSNumber numberWithUnsignedInt: id]];
+	return [remoteEventIdentifier unsignedIntValue];
+}
+
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown {
+	[delegate sendRemoteButtonEvent: event pressedDown: pressedDown remoteControl:self];
+}
+
+static RemoteControlEventIdentifier lastEvent;
+
+
+static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* userData )
+{
+	GlobalKeyboardDevice* keyboardDevice = (GlobalKeyboardDevice*) userData;
+	EventHotKeyID hkCom;
+	GetEventParameter(inEvent,kEventParamDirectObject,typeEventHotKeyID,NULL,sizeof(hkCom),NULL,&hkCom);
+
+	RemoteControlEventIdentifier identifier = [keyboardDevice remoteControlEventIdentifierForID:hkCom.id];
+	if (identifier == 0) return noErr;
+	
+	BOOL pressedDown = YES;
+	if (identifier != lastEvent) {
+		lastEvent = identifier;
+	} else {
+		lastEvent = 0;
+	    pressedDown = NO;
+	}
+	[keyboardDevice sendRemoteButtonEvent: identifier pressedDown: pressedDown];
+	
+	return noErr;
+}
+
+@end

File apple_remote/HIDRemoteControlDevice.m

+/*****************************************************************************
+ * HIDRemoteControlDevice.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license. 
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Code modified and adapted to OpenOffice.org 
+ * by Eric Bachard on 11.08.2008 under the same license
+ *
+ * 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.
+ *
+ *****************************************************************************/
+
+#import "HIDRemoteControlDevice.h"
+
+#import <mach/mach.h>
+#import <mach/mach_error.h>
+#import <IOKit/IOKitLib.h>
+#import <IOKit/IOCFPlugIn.h>
+#import <IOKit/hid/IOHIDKeys.h>
+#import <Carbon/Carbon.h>
+
+@interface HIDRemoteControlDevice (PrivateMethods) 
+- (NSDictionary*) cookieToButtonMapping; // Creates the dictionary using the magics, depending on the remote
+- (IOHIDQueueInterface**) queue;
+- (IOHIDDeviceInterface**) hidDeviceInterface;
+- (void) handleEventWithCookieString: (NSString*) cookieString sumOfValues: (SInt32) sumOfValues; 
+- (void) removeNotifcationObserver;
+- (void) remoteControlAvailable:(NSNotification *)notification;
+
+@end
+
+@interface HIDRemoteControlDevice (IOKitMethods) 
++ (io_object_t) findRemoteDevice;
+- (IOHIDDeviceInterface**) createInterfaceForDevice: (io_object_t) hidDevice;
+- (BOOL) initializeCookies;
+- (BOOL) openDevice;
+@end
+
+@implementation HIDRemoteControlDevice
+
++ (const char*) remoteControlDeviceName {
+	return "";
+}
+
++ (BOOL) isRemoteAvailable {	
+	io_object_t hidDevice = [self findRemoteDevice];
+	if (hidDevice != 0) {
+		IOObjectRelease(hidDevice);
+		return YES;
+	} else {
+		return NO;		
+	}
+}
+
+- (id) initWithDelegate: (id) _remoteControlDelegate {	
+	if ([[self class] isRemoteAvailable] == NO) return nil;
+	
+	if ( (self = [super initWithDelegate: _remoteControlDelegate]) ) {
+		openInExclusiveMode = YES;
+		queue = NULL;
+		hidDeviceInterface = NULL;
+		cookieToButtonMapping = [[NSMutableDictionary alloc] init];
+		
+		[self setCookieMappingInDictionary: cookieToButtonMapping];
+
+		NSEnumerator* enumerator = [cookieToButtonMapping objectEnumerator];
+		NSNumber* identifier;
+		supportedButtonEvents = 0;
+		while( (identifier = [enumerator nextObject]) ) {
+			supportedButtonEvents |= [identifier intValue];
+		}
+		
+		fixSecureEventInputBug = [[NSUserDefaults standardUserDefaults] boolForKey: @"remoteControlWrapperFixSecureEventInputBug"];
+	}
+	
+	return self;
+}
+
+- (void) dealloc {
+	[self removeNotifcationObserver];
+	[self stopListening:self];
+	[cookieToButtonMapping release];
+	[super dealloc];
+}
+
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown {
+	[delegate sendRemoteButtonEvent: event pressedDown: pressedDown remoteControl:self];
+}
+
+- (void) setCookieMappingInDictionary: (NSMutableDictionary*) cookieToButtonMapping {
+}
+- (int) remoteIdSwitchCookie {
+	return 0;
+}
+
+- (BOOL) sendsEventForButtonIdentifier: (RemoteControlEventIdentifier) identifier {
+	return (supportedButtonEvents & identifier) == identifier;
+}
+	
+- (BOOL) isListeningToRemote {
+	return (hidDeviceInterface != NULL && allCookies != NULL && queue != NULL);	
+}
+
+- (void) setListeningToRemote: (BOOL) value {
+	if (value == NO) {
+		[self stopListening:self];
+	} else {
+		[self startListening:self];
+	}
+}
+
+- (BOOL) isOpenInExclusiveMode {
+	return openInExclusiveMode;
+}
+- (void) setOpenInExclusiveMode: (BOOL) value {
+	openInExclusiveMode = value;
+}
+
+- (BOOL) processesBacklog {
+	return processesBacklog;
+}
+- (void) setProcessesBacklog: (BOOL) value {
+	processesBacklog = value;
+}
+
+- (void) startListening: (id) sender {	
+	if ([self isListeningToRemote]) return;
+	
+	// 4th July 2007
+	// 
+	// A security update in february of 2007 introduced an odd behavior.
+	// Whenever SecureEventInput is activated or deactivated the exclusive access
+	// to the remote control device is lost. This leads to very strange behavior where
+	// a press on the Menu button activates FrontRow while your app still gets the event.
+	// A great number of people have complained about this.	
+	// 
+	// Enabling the SecureEventInput and keeping it enabled does the trick.
+	//
+	// I'm pretty sure this is a kind of bug at Apple and I'm in contact with the responsible
+	// Apple Engineer. This solution is not a perfect one - I know. 	
+	// One of the side effects is that applications that listen for special global keyboard shortcuts (like Quicksilver)
+	// may get into problems as they no longer get the events.
+	// As there is no official Apple Remote API from Apple I also failed to open a technical incident on this.
+	// 
+	// Note that there is a corresponding DisableSecureEventInput in the stopListening method below.
+	// 
+	if ([self isOpenInExclusiveMode] && fixSecureEventInputBug) EnableSecureEventInput();	
+	
+	[self removeNotifcationObserver];
+	
+	io_object_t hidDevice = [[self class] findRemoteDevice];
+	if (hidDevice == 0) return;
+	
+	if ([self createInterfaceForDevice:hidDevice] == NULL) {
+		goto error;
+	}
+	
+	if ([self initializeCookies]==NO) {
+		goto error;
+	}
+
+	if ([self openDevice]==NO) {
+		goto error;
+	}
+	// be KVO friendly
+	[self willChangeValueForKey:@"listeningToRemote"];
+	[self didChangeValueForKey:@"listeningToRemote"];
+	goto cleanup;
+	
+error:
+	[self stopListening:self];
+	DisableSecureEventInput();
+	
+cleanup:	
+	IOObjectRelease(hidDevice);	
+}
+
+- (void) stopListening: (id) sender {
+	if ([self isListeningToRemote]==NO) return;
+	
+	BOOL sendNotification = NO;
+	
+	if (eventSource != NULL) {
+		CFRunLoopRemoveSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
+		CFRelease(eventSource);
+		eventSource = NULL;
+	}
+	if (queue != NULL) {
+		(*queue)->stop(queue);		
+		
+		//dispose of queue
+		(*queue)->dispose(queue);		
+		
+		//release the queue we allocated
+		(*queue)->Release(queue);	
+		
+		queue = NULL;
+		
+		sendNotification = YES;
+	}
+	
+	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 ([self isOpenInExclusiveMode] && fixSecureEventInputBug) DisableSecureEventInput();
+	
+	if ([self isOpenInExclusiveMode] && sendNotification) {
+		[[self class] sendFinishedNotifcationForAppIdentifier: nil];		
+	}
+	// be KVO friendly
+	[self willChangeValueForKey:@"listeningToRemote"];
+	[self didChangeValueForKey:@"listeningToRemote"];	
+}
+
+@end
+
+@implementation HIDRemoteControlDevice (PrivateMethods) 
+
+- (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) handleEventWithCookieString: (NSString*) cookieString sumOfValues: (SInt32) sumOfValues {
+	/*
+	if (previousRemainingCookieString) {
+		cookieString = [previousRemainingCookieString stringByAppendingString: cookieString];
+		NSLog(@"New cookie string is %@", cookieString);
+		[previousRemainingCookieString release], previousRemainingCookieString=nil;							
+	}*/
+	if (cookieString == nil || [cookieString length] == 0) return;
+		
+	NSNumber* buttonId = [[self cookieToButtonMapping] objectForKey: cookieString];
+	if (buttonId != nil) {
+		[self sendRemoteButtonEvent: [buttonId intValue] pressedDown: (sumOfValues>0)];
+	} else {
+		// let's see if a number of events are stored in the cookie string. this does
+		// happen when the main thread is too busy to handle all incoming events in time.
+		NSString* subCookieString;
+		NSString* lastSubCookieString=nil;
+		while( (subCookieString = [self validCookieSubstring: cookieString]) ) {
+			cookieString = [cookieString substringFromIndex: [subCookieString length]];
+			lastSubCookieString = subCookieString;
+			if (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);
+		}		
+	}
+}
+
+- (void) removeNotifcationObserver {
+	[[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:FINISHED_USING_REMOTE_CONTROL_NOTIFICATION object:nil];
+}
+
+- (void) remoteControlAvailable:(NSNotification *)notification {
+	[self removeNotifcationObserver];
+	[self startListening: self];
+}
+
+@end
+
+/*	Callback method for the device queue
+Will be called for any event of any type (cookie) to which we subscribe
+*/
+static void QueueCallbackFunction(void* target,  IOReturn result, void* refcon, void* sender) {	
+	if (target < 0) {
+		NSLog(@"QueueCallbackFunction called with invalid target!");
+		return;
+	}
+	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+	
+	HIDRemoteControlDevice* remote = (HIDRemoteControlDevice*)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 (((int)event.elementCookie)!=5) {
+			sumOfValues+=event.value;
+			[cookieString appendString:[NSString stringWithFormat:@"%d_", event.elementCookie]];
+		}
+	}
+	[remote handleEventWithCookieString: cookieString sumOfValues: sumOfValues];
+	
+	[pool release];
+}
+
+@implementation HIDRemoteControlDevice (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;
+}
+
+- (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];
+		
+		NSEnumerator *elementsEnumerator = [elements objectEnumerator];
+		
+		while ( (element = [elementsEnumerator nextObject]) ) {						
+			//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.
+
+			IOHIDElementCookie cookie;
+			NSEnumerator *allCookiesEnumerator = [allCookies objectEnumerator];
+			
+			while ( (cookie = (IOHIDElementCookie)[[allCookiesEnumerator nextObject] intValue]) ) {
+				(*queue)->addElement(queue, cookie, 0);
+			}
+									  
+			// add callback for async events			
+			ioReturnValue = (*queue)->createAsyncEventSource(queue, &eventSource);			
+			if (ioReturnValue == KERN_SUCCESS) {
+				ioReturnValue = (*queue)->setEventCallout(queue,QueueCallbackFunction, self, NULL);
+				if (ioReturnValue == KERN_SUCCESS) {
+					CFRunLoopAddSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
+					
+					//start data delivery to queue
+					(*queue)->start(queue);	
+					return YES;
+				} else {
+					NSLog(@"Error when setting event callback");
+				}
+			} else {
+				NSLog(@"Error when creating async event source");
+			}
+		} else {
+			NSLog(@"Error when opening device");
+		}
+	} else if (ioReturnValue == kIOReturnExclusiveAccess) {
+		// the device is used exclusive by another application
+		
+		// 1. we register for the FINISHED_USING_REMOTE_CONTROL_NOTIFICATION notification
+		[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(remoteControlAvailable:) name:FINISHED_USING_REMOTE_CONTROL_NOTIFICATION object:nil];
+		
+		// 2. send a distributed notification that we wanted to use the remote control				
+		[[self class] sendRequestForRemoteControlNotification];
+	}
+	return NO;				
+}
+
++ (io_object_t) findRemoteDevice {
+	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([self remoteControlDeviceName]);
+	
+	// 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;
+}
+
+@end
+

File apple_remote/KeyspanFrontRowControl.m

+/*****************************************************************************
+ * KeyspanFrontRowControl.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license. 
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Code modified and adapted to OpenOffice.org 
+ * by Eric Bachard on 11.08.2008 under the same License
+ *
+ * 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.
+ *
+ *****************************************************************************/
+ 
+#import "KeyspanFrontRowControl.h"
+#import <mach/mach.h>
+#import <mach/mach_error.h>
+#import <IOKit/IOKitLib.h>
+#import <IOKit/IOCFPlugIn.h>
+#import <IOKit/hid/IOHIDKeys.h>
+
+@implementation KeyspanFrontRowControl
+
+- (void) setCookieMappingInDictionary: (NSMutableDictionary*) _cookieToButtonMapping	{	
+	[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlus]		 forKey:@"11_18_99_10_"];
+	[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMinus]		 forKey:@"11_18_98_10_"];		
+	[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu]		 forKey:@"11_18_58_10_"];		
+	[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay]		 forKey:@"11_18_61_10_"];
+	[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight]		 forKey:@"11_18_96_10_"];
+	[_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft]		 forKey:@"11_18_97_10_"];
+	/* hold events are not being send by this device
+	[_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_"];			
+	*/
+}
+
++ (io_object_t) findRemoteDevice {
+	CFMutableDictionaryRef hidMatchDictionary = NULL;
+	IOReturn ioReturnValue = kIOReturnSuccess;	
+	io_iterator_t hidObjectIterator = 0;
+	io_object_t	hidDevice = 0;
+	SInt32                  idVendor = 1741;
+	SInt32                  idProduct = 0x420;	
+	
+	// Set up a matching dictionary to search the I/O Registry by class
+	// name for all HID class devices
+	hidMatchDictionary = IOServiceMatching(kIOHIDDeviceKey);
+	
+	CFNumberRef numberRefVendor = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idVendor);
+    if ( numberRefVendor )
+    {
+        CFDictionaryAddValue(hidMatchDictionary, CFSTR(kIOHIDVendorIDKey), numberRefVendor);
+        CFRelease(numberRefVendor);
+	}
+
+    CFNumberRef numberRefProduct = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idProduct);
+    if ( numberRefProduct )
+    {
+        CFDictionaryAddValue(hidMatchDictionary, CFSTR(kIOHIDProductIDKey), numberRefProduct);
+        CFRelease(numberRefProduct);	
+	}
+
+	// Now search I/O Registry for matching devices.
+	ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault, hidMatchDictionary, &hidObjectIterator);
+	
+	if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0)) {
+		hidDevice = IOIteratorNext(hidObjectIterator);
+	}
+	
+	// release the iterator
+    if ( hidObjectIterator )
+        IOObjectRelease(hidObjectIterator);
+
+	return hidDevice;
+	
+}
+
+@end

File apple_remote/MultiClickRemoteBehavior.m

+/*****************************************************************************
+ * MultiClickRemoteBehavior.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license. 
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Code modified and adapted to OpenOffice.org 
+ * by Eric Bachard on 11.08.2008 under the same License
+ *
+ * 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.
+ *
+ *****************************************************************************/
+
+#import "MultiClickRemoteBehavior.h"
+
+const NSTimeInterval DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE = 0.35;
+const NSTimeInterval HOLD_RECOGNITION_TIME_INTERVAL = 0.4;
+
+@implementation MultiClickRemoteBehavior
+
+- (id) init {
+	if ( (self = [super init]) ) {
+		maxClickTimeDifference = DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE;
+	}
+	return 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(remoteButton:pressedDown:clickCount:)] == NO )) return; // return what ?
+	
+	delegate = _delegate;
+}
+- (id) delegate {
+	return delegate;
+}
+
+- (BOOL) simulateHoldEvent {
+	return simulateHoldEvents;
+}
+- (void) setSimulateHoldEvent: (BOOL) value {
+	simulateHoldEvents = value;
+}
+
+- (BOOL) simulatesHoldForButtonIdentifier: (RemoteControlEventIdentifier) identifier remoteControl: (RemoteControl*) remoteControl {
+	// we do that check only for the normal button identifiers as we would check for hold support for hold events instead
+	if (identifier > (1 << EVENT_TO_HOLD_EVENT_OFFSET)) return NO; 
+	
+	return [self simulateHoldEvent] && [remoteControl sendsEventForButtonIdentifier: (identifier << EVENT_TO_HOLD_EVENT_OFFSET)]==NO;
+}
+
+- (BOOL) clickCountingEnabled {
+	return clickCountEnabledButtons != 0;
+}
+- (void) setClickCountingEnabled: (BOOL) value {
+	if (value) {
+		[self setClickCountEnabledButtons: kRemoteButtonPlus | kRemoteButtonMinus | 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;
+}
+
+- (void) sendSimulatedHoldEvent: (id) time {
+	BOOL startSimulateHold = NO;
+	RemoteControlEventIdentifier event = lastHoldEvent;
+	@synchronized(self) {
+		startSimulateHold = (lastHoldEvent>0 && lastHoldEventTime == [time doubleValue]);
+	}
+	if (startSimulateHold) {
+		lastEventSimulatedHold = YES;
+		event = (event << EVENT_TO_HOLD_EVENT_OFFSET);
+		[delegate remoteButton:event pressedDown: YES clickCount: 1];
+	}
+}
+
+- (void) executeClickCountEvent: (NSArray*) values {
+	RemoteControlEventIdentifier 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;		
+			lastClickCountEvent = 0;
+			lastClickCountEventTime = 0;
+		}
+	}
+	
+	if (finishedClicking) {	
+		[delegate remoteButton:event pressedDown: YES clickCount:finalClickCount];		
+		// trigger a button release event, too
+		[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow:0.1]];
+		[delegate remoteButton:event pressedDown: NO clickCount:finalClickCount];
+	}
+}
+
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown remoteControl: (RemoteControl*) remoteControl {	
+	if (!delegate)  return;
+	
+	BOOL clickCountingForEvent = ([self clickCountEnabledButtons] & event) == event;
+
+	if ([self simulatesHoldForButtonIdentifier: event remoteControl: remoteControl] && lastClickCountEvent==0) {
+		if (pressedDown) {
+			// wait to see if it is a hold
+			lastHoldEvent = event;
+			lastHoldEventTime = [NSDate timeIntervalSinceReferenceDate];
+			[self performSelector:@selector(sendSimulatedHoldEvent:) 
+					   withObject:[NSNumber numberWithDouble:lastHoldEventTime]
+					   afterDelay:HOLD_RECOGNITION_TIME_INTERVAL];
+			return;
+		} else {
+			if (lastEventSimulatedHold) {
+				// it was a hold
+				// send an event for "hold release"
+				event = (event << EVENT_TO_HOLD_EVENT_OFFSET);
+				lastHoldEvent = 0;
+				lastEventSimulatedHold = NO;
+
+				[delegate remoteButton:event pressedDown: pressedDown clickCount:1];
+				return;
+			} else {
+				RemoteControlEventIdentifier previousEvent = lastHoldEvent;
+				@synchronized(self) {
+					lastHoldEvent = 0;
+				}						
+				
+				// in case click counting is enabled we have to setup the state for that, too
+				if (clickCountingForEvent) {
+					lastClickCountEvent = previousEvent;
+					lastClickCountEventTime = lastHoldEventTime;
+					NSNumber* eventNumber;
+					NSNumber* timeNumber;		
+					eventClickCount = 1;
+					timeNumber = [NSNumber numberWithDouble:lastClickCountEventTime];
+					eventNumber= [NSNumber numberWithUnsignedInt:previousEvent];
+					NSTimeInterval diffTime = maxClickTimeDifference-([NSDate timeIntervalSinceReferenceDate]-lastHoldEventTime);
+					[self performSelector: @selector(executeClickCountEvent:) 
+							   withObject: [NSArray arrayWithObjects:eventNumber, timeNumber, nil]
+							   afterDelay: diffTime];							
+					// we do not return here because we are still in the press-release event
+					// that will be consumed below
+				} else {
+					// trigger the pressed down event that we consumed first
+					[delegate remoteButton:event pressedDown: YES clickCount:1];							
+				}
+			}										
+		}
+	}
+	
+	if (clickCountingForEvent) {
+		if (pressedDown == NO) return;
+
+		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 remoteButton:event pressedDown: pressedDown clickCount:1];
+	}		
+
+}
+
+@end

File apple_remote/RemoteControl.m

+/*****************************************************************************
+ * RemoteControl.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license. 
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Code modified and adapted to OpenOffice.org 
+ * by Eric Bachard on 11.08.2008 under the same License
+ *
+ * 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.
+ *
+ *****************************************************************************/
+ 
+#import "RemoteControl.h"
+
+// notifaction names that are being used to signal that an application wants to 
+// have access to the remote control device or if the application has finished
+// using the remote control device
+NSString* REQUEST_FOR_REMOTE_CONTROL_NOTIFCATION     = @"mac.remotecontrols.RequestForRemoteControl";
+NSString* FINISHED_USING_REMOTE_CONTROL_NOTIFICATION = @"mac.remotecontrols.FinishedUsingRemoteControl";
+
+// keys used in user objects for distributed notifications
+NSString* kRemoteControlDeviceName = @"RemoteControlDeviceName";
+NSString* kApplicationIdentifier   = @"CFBundleIdentifier";
+// bundle identifier of the application that should get access to the remote control
+// this key is being used in the FINISHED notification only
+NSString* kTargetApplicationIdentifier = @"TargetBundleIdentifier";
+
+
+@implementation RemoteControl
+
+// returns nil if the remote control device is not available
+- (id) initWithDelegate: (id) _remoteControlDelegate {	
+	if ( (self = [super init]) ) {
+		delegate = [_remoteControlDelegate retain];
+#ifdef DEBUG
+        NSLog(@"RemoteControl initWithDelegate ok");
+#endif
+    }
+	return self;
+}
+
+- (void) dealloc {
+	[delegate release];
+	[super dealloc];
+}
+
+- (void) setListeningToRemote: (BOOL) value {
+#ifdef DEBUG
+        NSLog(@"setListeningToRemote ok");
+#endif
+}
+- (BOOL) isListeningToRemote {
+	return NO;
+}
+
+- (void) startListening: (id) sender {
+#ifdef DEBUG
+            NSLog(@"startListening ok");
+#endif
+}
+- (void) stopListening: (id) sender {
+#ifdef DEBUG
+            NSLog(@"stopListening ok");
+#endif
+}
+
+- (BOOL) isOpenInExclusiveMode {
+	return YES;
+}
+- (void) setOpenInExclusiveMode: (BOOL) value {
+}
+
+- (BOOL) sendsEventForButtonIdentifier: (RemoteControlEventIdentifier) identifier {
+#ifdef DEBUG
+   NSLog(@"sending event for button identifier \n");
+#endif
+	return YES;
+}
+
++ (void) sendDistributedNotification: (NSString*) notificationName targetBundleIdentifier: (NSString*) targetIdentifier
+{
+    if ( (self = [super init]) ) {
+    NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys: [NSString stringWithCString:[self remoteControlDeviceName] encoding:NSASCIIStringEncoding],
+                            kRemoteControlDeviceName /* key = RemoteControlDeviceName  -> OK */,
+                            [[NSBundle mainBundle] bundleIdentifier] /* value = org.openoffice.script -> OK */,
+                            kApplicationIdentifier/* key = CFBundleIdentifier -> OK */,
+                            targetIdentifier /*value = AppleIRController -> OK */,
+                            kTargetApplicationIdentifier /*targetBundleIdentifier -> does not appear, since the peer is nil*/,
+                            nil];
+#ifdef DEBUG
+    // Debug purpose: returns all the existing dictionary keys. 
+    NSString *s;
+    NSEnumerator *e = [userInfo keyEnumerator];
+    while ( (s = [e nextObject]) ) {
+        NSLog(@"key = %@ ",s);
+    }
+    NSEnumerator *f = [userInfo objectEnumerator ];
+    while ( (s = [f nextObject]) ) {
+        NSLog(@"value = %@ ",s);
+    }
+    NSLog(@"sendDistributedNotification ...");
+#endif
+
+	[[NSDistributedNotificationCenter defaultCenter] postNotificationName:notificationName
+																   object:nil
+																 userInfo:userInfo
+													   deliverImmediately:YES];	
+    }
+}
+
++ (void) sendFinishedNotifcationForAppIdentifier: (NSString*) identifier {
+    [self sendDistributedNotification:FINISHED_USING_REMOTE_CONTROL_NOTIFICATION targetBundleIdentifier:identifier];
+#ifdef DEBUG
+    NSLog(@"sendFinishedNotifcationForAppIdentifier ...");
+#endif
+}
++ (void) sendRequestForRemoteControlNotification {
+    [self sendDistributedNotification:REQUEST_FOR_REMOTE_CONTROL_NOTIFCATION targetBundleIdentifier:nil];
+#ifdef DEBUG
+    NSLog(@"sendRequestForRemoteControlNotification ...");
+#endif
+}
+
++ (const char*) remoteControlDeviceName {
+	return NULL;
+}
+
+@end

File apple_remote/RemoteControlContainer.m

+/*****************************************************************************
+ * RemoteControlContainer.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license. 
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Code modified and adapted to OpenOffice.org 
+ * by Eric Bachard on 11.08.2008 under the same License
+ *
+ * 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.
+ *
+ *****************************************************************************/
+
+#import "RemoteControlContainer.h"
+
+@implementation RemoteControlContainer
+
+- (id) initWithDelegate: (id) _remoteControlDelegate {
+	if ( (self = [super initWithDelegate:_remoteControlDelegate]) ) {
+		remoteControls = [[NSMutableArray alloc] init];
+#ifdef DEBUG
+        NSLog(@"RemoteControlContainer initWithDelegate ok");
+	}
+    else {
+        NSLog(@"RemoteControlContainer initWithDelegate failed");
+#endif
+    }
+
+	return self;
+}
+
+- (void) dealloc {
+	[self stopListening: self];
+	[remoteControls release];
+	[super dealloc];
+}
+
+- (BOOL) instantiateAndAddRemoteControlDeviceWithClass: (Class) clazz {
+    BOOL toReturn = NO;
+    RemoteControl* remoteControl = [[clazz alloc] initWithDelegate: delegate];
+    if (remoteControl) {
+        [remoteControls addObject: remoteControl];
+        [remoteControl addObserver: self forKeyPath:@"listeningToRemote" options:NSKeyValueObservingOptionNew context:nil];
+        toReturn = YES;		
+    }
+#ifdef DEBUG
+    else {
+        NSLog(@"RemoteControlContainer instantiateAndAddRemoteControlDeviceWithClass failed");
+        toReturn = NO;	
+    }
+#endif
+    return toReturn;
+}
+
+- (unsigned int) count {
+	return [remoteControls count];
+}
+
+- (void) reset {
+	[self willChangeValueForKey:@"listeningToRemote"];
+	[self didChangeValueForKey:@"listeningToRemote"];
+#ifdef DEBUG
+	// debug purpose
+    NSLog(@"reset... (after listening to remote)");
+#endif
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+	[self reset];
+}
+
+- (void) setListeningToRemote: (BOOL) value {
+	int i;
+	for(i=0; i < [remoteControls count]; i++) {
+		[[remoteControls objectAtIndex: i] setListeningToRemote: value];
+	}
+	if (value && value != [self isListeningToRemote]) [self performSelector:@selector(reset) withObject:nil afterDelay:0.01];
+}
+- (BOOL) isListeningToRemote {
+	int i;
+	for(i=0; i < [remoteControls count]; i++) {
+		if ([[remoteControls objectAtIndex: i] isListeningToRemote]) {
+			return YES;
+		}
+	}
+	return NO;
+}
+
+- (void) startListening: (id) sender {
+#ifdef DEBUG
+	NSLog(@"startListening to events... ");
+#endif
+	int i;
+	for(i=0; i < [remoteControls count]; i++) {
+		[[remoteControls objectAtIndex: i] startListening: sender];
+	}	
+}
+- (void) stopListening: (id) sender {
+#ifdef DEBUG
+	NSLog(@"stopListening to events... ");
+#endif
+	int i;
+	for(i=0; i < [remoteControls count]; i++) {
+		[[remoteControls objectAtIndex: i] stopListening: sender];
+	}	
+}
+
+- (BOOL) isOpenInExclusiveMode {
+	BOOL mode = YES;
+	int i;
+	for(i=0; i < [remoteControls count]; i++) {
+		mode = mode && ([[remoteControls objectAtIndex: i] isOpenInExclusiveMode]);
+	}
+	return mode;	
+}
+- (void) setOpenInExclusiveMode: (BOOL) value {
+	int i;
+	for(i=0; i < [remoteControls count]; i++) {
+		[[remoteControls objectAtIndex: i] setOpenInExclusiveMode:value];
+	}	
+}
+
+@end

File apple_remote/RemoteMainController.m

+/*****************************************************************************
+ * RemoteMainController.m
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license. 
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Code modified and adapted to OpenOffice.org 
+ * by Eric Bachard on 11.08.2008 under the same License
+ *
+ * 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.
+ *
+ *****************************************************************************/
+
+#import "RemoteMainController.h"
+#import "AppleRemote.h"
+#import "KeyspanFrontRowControl.h"
+#import "GlobalKeyboardDevice.h"
+#import "RemoteControlContainer.h"
+#import "MultiClickRemoteBehavior.h"
+
+
+
+// -------------------------------------------------------------------------------------------
+// Sample Code 3: Multi Click Behavior and Hold Event Simulation
+// -------------------------------------------------------------------------------------------
+
+@implementation MainController
+
+- (id) init {
+    self = [super init];  // because we redefined our own init instead of use the fu..nny awakeFromNib 
+    if (self != nil) {
+
+        // 1. instantiate the desired behavior for the remote control device
+        remoteControlBehavior = [[MultiClickRemoteBehavior alloc] init];	
+
+        // 2. configure the behavior
+        [remoteControlBehavior setDelegate: self];
+		
+        // 3. a Remote Control Container manages a number of devices and conforms to the RemoteControl interface
+        //    Therefore you can enable or disable all the devices of the container with a single "startListening:" call.
+        RemoteControlContainer* container = [[RemoteControlContainer alloc] initWithDelegate: remoteControlBehavior];
+
+        if ( [container instantiateAndAddRemoteControlDeviceWithClass: [AppleRemote class]] != nil ) {
+#ifdef DEBUG
+            NSLog(@"[container instantiateAndAddRemoteControlDeviceWithClass: [AppleRemote class]] successfull");
+        }
+        else {
+            NSLog(@"[container instantiateAndAddRemoteControlDeviceWithClass: [AppleRemote class]] failed");
+#endif
+        }
+
+        if ( [container instantiateAndAddRemoteControlDeviceWithClass: [KeyspanFrontRowControl class]] != nil ) {
+#ifdef DEBUG
+            NSLog(@"[container instantiateAndAddRemoteControlDeviceWithClass: [KeyspanFrontRowControl class]] successfull");
+        }
+        else {
+            NSLog(@"[container instantiateAndAddRemoteControlDeviceWithClass: [KeyspanFrontRowControl class]] failed");
+#endif
+        }
+
+        if ( [container instantiateAndAddRemoteControlDeviceWithClass: [GlobalKeyboardDevice class]] != nil ) {
+#ifdef DEBUG
+            NSLog(@"[container instantiateAndAddRemoteControlDeviceWithClass: [GlobalKeyboardDevice class]] successfull");
+        }
+        else {
+            NSLog(@"[container instantiateAndAddRemoteControlDeviceWithClass: [GlobalKeyboardDevice class]] failed");
+#endif
+        }	
+        // to give the binding mechanism a chance to see the change of the attribute
+        [self setValue: container forKey: @"remoteControl"];	
+#ifdef DEBUG
+            NSLog(@"MainController init done");
+#endif
+    }
+    else 
+        NSLog(@"MainController init failed");
+    return self;
+}
+
+// delegate method for the MultiClickRemoteBehavior
+
+// Hack: we define here our events, and post them directly to the NSApp, who is there to receive the notifications.
+// NSEvent class gives several possibilities, and the one above works out of the box :
+//+ keyEventWithType:location:modifierFlags:timestamp:windowNumber:context:characters:charactersIgnoringModifiers:isARepeat:keyCode:  
+- (void) postTheEvent: (unichar)theUnicharCode theKeyCode:(long unsigned int)theKeyCode modifierFlags:(int)modifierFlags isARepeat:(BOOL)toBeRepeated
+{
+
+    unichar theKey=theUnicharCode;
+    NSString *characters=[NSString stringWithCharacters: &theKey length: 1];
+
+    [NSApp postEvent: 
+
+    [NSEvent keyEventWithType:NSKeyDown
+    location: NSZeroPoint
+    modifierFlags : modifierFlags
+    timestamp: 0
+    windowNumber: [[NSApp keyWindow] windowNumber]
+    context: nil
+    characters: characters
+    charactersIgnoringModifiers: characters
+    isARepeat: toBeRepeated
+    keyCode: theKeyCode]
+    atStart: NO];
+}
+
+- (void) remoteButton: (RemoteControlEventIdentifier)buttonIdentifier pressedDown: (BOOL) pressedDown clickCount: (unsigned int)clickCount
+{
+    NSString* buttonName = nil;
+    NSString* pressed = @"";
+
+    SystemUIMode currentMode; // Fullscreen is kUIModeAllHidden, other are "normal mode"
+    SystemUIOptions currentModeOptions;
+    GetSystemUIMode (&currentMode,&currentModeOptions);
+
+    if (pressedDown)
+    {
+        pressed = @"(pressed)";
+        // kUIModeAllHidden means fullscreen 
+        // and pressed only allows F5 in normal mode (and fixes the bounce) 
+        if ( kUIModeAllHidden == currentMode )
+        {
+            switch(buttonIdentifier) 
+            {
+                case kRemoteButtonPlus:
+                {
+                    // MEDIA_COMMAND_VOLUME_UP  ( see vcl/inc/vcl/cmdevt.hxx )
+                    buttonName = @"Volume up";			
+                    unichar volumeUpKey=NSUpArrowFunctionKey;//NSPageUpFunctionKey;//1025; //
+                    [ self postTheEvent: volumeUpKey theKeyCode: 0xF72C modifierFlags: 0 isARepeat:NO ]; 
+                }
+                    break;
+
+                case kRemoteButtonMinus:
+                {
+                    // MEDIA_COMMAND_VOLUME_DOWN
+                    buttonName = @"Volume down";			
+                    unichar volumeDownKey=NSDownArrowFunctionKey;
+                    [ self postTheEvent: volumeDownKey theKeyCode: 1024 modifierFlags: 0 isARepeat:NO ];
+                }
+                    break;
+
+                case kRemoteButtonMenu:
+                {
+                    // MEDIA_COMMAND_MENU
+                    buttonName = @"Menu";
+                    unichar menuKey=NSMenuFunctionKey; //
+                    [ self postTheEvent: menuKey theKeyCode: 0xF735 modifierFlags: 0 isARepeat:NO ]; 
+
+                }
+                    break;
+
+                case kRemoteButtonPlay:
+                {
+                    // MEDIA_COMMAND_PLAY
+                    buttonName = @"Play";
+                    unichar playFunction=NSF5FunctionKey; //
+                    [ self postTheEvent: playFunction theKeyCode: 96 modifierFlags: 0 isARepeat:NO ]; 
+                }
+                    break;			
+
+                case kRemoteButtonRight:
+                {
+                    // MEDIA_COMMAND_NEXTTRACK
+                    buttonName = @"Next slide";
+                    unichar rightArrow=NSRightArrowFunctionKey; //NSRightArrowKey == 124
+                    [ self postTheEvent: rightArrow theKeyCode: 124 modifierFlags: 0 isARepeat:NO ]; 
+                }
+                    break;
+
+                case kRemoteButtonLeft:
+                {
+                    // MEDIA_COMMAND_PREVIOUSTRACK
+                    buttonName = @"Left";
+                    unichar leftArrow=NSLeftArrowFunctionKey;
+                    [ self postTheEvent: leftArrow theKeyCode: 123 modifierFlags: 0 isARepeat:NO ];
+                }
+                    break;
+
+                case kRemoteButtonRight_Hold:
+                {
+                    // MEDIA_COMMAND_NEXTTRACK_HOLD
+                    buttonName = @"Last slide";	
+                    unichar lastSlide=NSEndFunctionKey; // Goes to last slide(value from in offuh/com/sun/star/awt/Key.hdl)
+                    [ self postTheEvent: lastSlide theKeyCode: 1029 modifierFlags: 0 isARepeat:NO ]; 
+                }
+                    break;
+
+                case kRemoteButtonLeft_Hold:
+                {
+                    // MEDIA_COMMAND_PREVIOUSTRACK_HOLD
+                    buttonName = @"First slide";	
+                    unichar firstSlide=NSHomeFunctionKey; // Goes to first slide (value from in offuh/com/sun/star/awt/Key.hdl)
+                    [ self postTheEvent: firstSlide theKeyCode: 1028 modifierFlags: 0 isARepeat:NO ]; 
+                }
+            	    break;			
+
+                case kRemoteButtonPlus_Hold:
+                    buttonName = @"Volume up holding";	
+                    break;				
+
+                case kRemoteButtonMinus_Hold:			
+                    buttonName = @"Volume down holding";	
+                    break;				
+
+                case kRemoteButtonPlay_Hold:
+                    // MEDIA_COMMAND_PLAY_HOLD
+                    buttonName = @"Play (sleep mode)";
+                    break;			
+
+                case kRemoteButtonMenu_Hold:
+                {
+                    // MEDIA_COMMAND_MENU_HOLD
+                    buttonName = @"Menu (long)";
+                    unichar escapeKey=27;
+                    [ self postTheEvent: escapeKey theKeyCode: 27 modifierFlags: 0 isARepeat:NO ]; 
+                }
+                    break;
+
+                case kRemoteControl_Switched:
+                    buttonName = @"Remote Control Switched";
+                    break;
+
+                default:
+#ifdef DEBUG
+                    NSLog(@"Unmapped event for button %d", buttonIdentifier); 
+#endif
+                    break;
+            }
+        }
+        else // normal mode
+        {
+        
+            switch(buttonIdentifier) 
+            {
+                case kRemoteButtonPlay:
+                {
+                    // MEDIA_COMMAND_PLAY
+                    buttonName = @"Play";
+                    unichar playFunction=NSF5FunctionKey; //
+                    [ self postTheEvent: playFunction theKeyCode: 772 modifierFlags: 0 isARepeat:NO ]; 
+                }
+                    break;
+		
+                case kRemoteButtonMenu_Hold:
+                {
+                    // MEDIA_COMMAND_MENU_HOLD
+                    buttonName = @"Menu (long)";
+                    unichar escapeKey=27;
+                    [ self postTheEvent: escapeKey theKeyCode: 27 modifierFlags: 0 isARepeat:NO ]; 
+                }
+                    break;		
+
+                case kRemoteControl_Switched:
+                    buttonName = @"Remote Control Switched";
+                    break;
+
+                default:
+#ifdef DEBUG
+                    NSLog(@"Unmapped event for button %d", buttonIdentifier); 
+#endif
+                    break;       
+
+#ifdef DEBUG
+                NSLog(@"Normal mode ");
+#endif
+            }
+        }
+    }
+    else // not pressed
+    {
+        pressed = @"(released)";
+    }
+
+#ifdef DEBUG
+	//NSLog(@"Button %@ pressed %@", buttonName, pressed);
+	NSString* clickCountString = @"";
+	if (clickCount > 1) clickCountString = [NSString stringWithFormat: @"%d clicks", clickCount];
+	NSString* feedbackString = [NSString stringWithFormat:@"(Value:%4d) %@  %@ %@",buttonIdentifier, buttonName, pressed, clickCountString];
+
+	// print out events
+	NSLog(@"%@", feedbackString);
+
+    if (pressedDown == NO) printf("\n");
+	// simulate slow processing of events
+	// [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.5]];
+#endif
+}
+
+- (void) dealloc {
+    [remoteControl autorelease];
+	[remoteControlBehavior autorelease];
+	[super dealloc];
+}
+
+// for bindings access
+- (RemoteControl*) remoteControl {
+	return remoteControl;
+}
+
+- (MultiClickRemoteBehavior*) remoteBehavior {
+	return remoteControlBehavior;
+}
+
+@end

File apple_remote/inc/AppleRemote.h

+/*****************************************************************************
+ * RemoteControlWrapper.h
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license. 
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Code modified and adapted to OpenOffice.org 
+ * by Eric Bachard on 11.08.2008 under the same license
+ *
+ * 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.
+ *
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+#import "HIDRemoteControlDevice.h"
+
+/*	Interacts with the Apple Remote Control HID device
+	The class is not thread safe
+*/
+@interface AppleRemote : HIDRemoteControlDevice {		
+}
+
+@end

File apple_remote/inc/GlobalKeyboardDevice.h

+/*****************************************************************************
+ * GlobalKeyboardDevice.h
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license. 
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Code modified and adapted to OpenOffice.org 
+ * by Eric Bachard on 11.08.2008 under the same license
+ *
+ * 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.
+ *
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+#import <Carbon/Carbon.h>
+
+#import "RemoteControl.h"
+
+
+/*
+ This class registers for a number of global keyboard shortcuts to simulate a remote control
+ */
+
+@interface GlobalKeyboardDevice : RemoteControl {
+	
+	NSMutableDictionary* hotKeyRemoteEventMapping;
+	EventHandlerRef eventHandlerRef;
+		
+}
+
+- (void) mapRemoteButton: (RemoteControlEventIdentifier) remoteButtonIdentifier defaultKeycode: (unsigned int) defaultKeycode defaultModifiers: (unsigned int) defaultModifiers;
+
+- (BOOL)registerHotKeyCode: (unsigned int) keycode modifiers: (unsigned int) modifiers remoteEventIdentifier: (RemoteControlEventIdentifier) identifier;
+
+
+
+@end

File apple_remote/inc/HIDRemoteControlDevice.h

+/*****************************************************************************
+ * HIDRemoteControlDevice.h
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license. 
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Code modified and adapted to OpenOffice.org 
+ * by Eric Bachard on 11.08.2008 under the same license
+ *
+ * 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.
+ *
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+#import <IOKit/hid/IOHIDLib.h>
+
+#import "RemoteControl.h"
+
+/*
+	Base class for HID based remote control devices
+ */
+@interface HIDRemoteControlDevice : RemoteControl {
+	IOHIDDeviceInterface** hidDeviceInterface; // see IOKit/hid/IOHIDLib.h
+	IOHIDQueueInterface**  queue;  // IOKit/hid/IOHIDLib.h
+	NSMutableArray*		   allCookies;
+	NSMutableDictionary*   cookieToButtonMapping;
+	CFRunLoopSourceRef	   eventSource;
+	
+	BOOL fixSecureEventInputBug;
+	BOOL openInExclusiveMode;
+	BOOL processesBacklog;	
+	
+	int supportedButtonEvents;
+}
+
+// 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;
+
+// methods that should be overwritten by subclasses
+- (void) setCookieMappingInDictionary: (NSMutableDictionary*) cookieToButtonMapping;
+
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown;
+
++ (BOOL) isRemoteAvailable;
+
+@end

File apple_remote/inc/KeyspanFrontRowControl.h