1/*****************************************************************************
2 * GlobalKeyboardDevice.m
3 * RemoteControlWrapper
4 *
5 * Created by Martin Kahr on 11.03.06 under a MIT-style license.
6 * Copyright (c) 2006 martinkahr.com. All rights reserved.
7 *
8 * Code modified and adapted to OpenOffice.org
9 * by Eric Bachard on 11.08.2008 under the same license
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 * THE SOFTWARE.
28 *
29 *****************************************************************************/
30
31
32#import "GlobalKeyboardDevice.h"
33
34#define F1 122
35#define F2 120
36#define F3 99
37#define F4 118
38#define F5 96
39#define F6 97
40#define F7 98
41
42/*
43 the following default keys are read and shall be used to change the keyboard mapping
44
45 mac.remotecontrols.GlobalKeyboardDevice.plus_modifiers
46 mac.remotecontrols.GlobalKeyboardDevice.plus_keycode
47 mac.remotecontrols.GlobalKeyboardDevice.minus_modifiers
48 mac.remotecontrols.GlobalKeyboardDevice.minus_keycode
49 mac.remotecontrols.GlobalKeyboardDevice.play_modifiers
50 mac.remotecontrols.GlobalKeyboardDevice.play_keycode
51 mac.remotecontrols.GlobalKeyboardDevice.left_modifiers
52 mac.remotecontrols.GlobalKeyboardDevice.left_keycode
53 mac.remotecontrols.GlobalKeyboardDevice.right_modifiers
54 mac.remotecontrols.GlobalKeyboardDevice.right_keycode
55 mac.remotecontrols.GlobalKeyboardDevice.menu_modifiers
56 mac.remotecontrols.GlobalKeyboardDevice.menu_keycode
57 mac.remotecontrols.GlobalKeyboardDevice.playhold_modifiers
58 mac.remotecontrols.GlobalKeyboardDevice.playhold_keycode
59 */
60
61static OSStatus hotKeyEventHandler(EventHandlerCallRef, EventRef, void*);
62
63@implementation GlobalKeyboardDevice
64
65- (id) initWithDelegate: (id) _remoteControlDelegate {
66	if ( (self = [super initWithDelegate: _remoteControlDelegate]) ) {
67		hotKeyRemoteEventMapping = [[NSMutableDictionary alloc] init];
68
69		unsigned int modifiers = cmdKey + shiftKey /*+ optionKey*/ + controlKey;
70
71		[self mapRemoteButton:kRemoteButtonPlus			defaultKeycode:F1 defaultModifiers:modifiers];
72		[self mapRemoteButton:kRemoteButtonMinus		defaultKeycode:F2 defaultModifiers:modifiers];
73		[self mapRemoteButton:kRemoteButtonPlay			defaultKeycode:F3 defaultModifiers:modifiers];
74		[self mapRemoteButton:kRemoteButtonLeft			defaultKeycode:F4 defaultModifiers:modifiers];
75		[self mapRemoteButton:kRemoteButtonRight		defaultKeycode:F5 defaultModifiers:modifiers];
76		[self mapRemoteButton:kRemoteButtonMenu			defaultKeycode:F6 defaultModifiers:modifiers];
77		[self mapRemoteButton:kRemoteButtonPlay_Hold	defaultKeycode:F7 defaultModifiers:modifiers];
78	}
79	return self;
80}
81
82- (void) dealloc {
83	[hotKeyRemoteEventMapping release];
84	[super dealloc];
85}
86
87- (void) mapRemoteButton: (RemoteControlEventIdentifier) remoteButtonIdentifier defaultKeycode: (unsigned int) defaultKeycode defaultModifiers: (unsigned int) defaultModifiers {
88	NSString* defaultsKey = NULL;
89
90	switch(remoteButtonIdentifier) {
91		case kRemoteButtonPlus:
92			defaultsKey = @"plus";
93			break;
94		case kRemoteButtonMinus:
95			defaultsKey = @"minus";
96			break;
97		case kRemoteButtonMenu:
98			defaultsKey = @"menu";
99			break;
100		case kRemoteButtonPlay:
101			defaultsKey = @"play";
102			break;
103		case kRemoteButtonRight:
104			defaultsKey = @"right";
105			break;
106		case kRemoteButtonLeft:
107			defaultsKey = @"left";
108			break;
109		case kRemoteButtonPlay_Hold:
110			defaultsKey = @"playhold";
111			break;
112		default:
113#ifdef DEBUG
114			NSLog( @"Apple Remote: Unknown global keyboard defaults key for button identifier %d", remoteButtonIdentifier);
115#endif
116            break;
117	}
118
119	NSNumber* modifiersCfg = [[NSUserDefaults standardUserDefaults] objectForKey: [NSString stringWithFormat: @"mac.remotecontrols.GlobalKeyboardDevice.%@_modifiers", defaultsKey]];
120	NSNumber* keycodeCfg   = [[NSUserDefaults standardUserDefaults] objectForKey: [NSString stringWithFormat: @"mac.remotecontrols.GlobalKeyboardDevice.%@_keycode", defaultsKey]];
121
122	unsigned int modifiers = defaultModifiers;
123	if (modifiersCfg) modifiers = [modifiersCfg unsignedIntValue];
124
125	unsigned int keycode = defaultKeycode;
126	if (keycodeCfg) keycode = [keycodeCfg unsignedIntValue];
127
128    [self registerHotKeyCode: keycode  modifiers: modifiers remoteEventIdentifier: remoteButtonIdentifier];
129}
130
131- (void) setListeningToRemote: (BOOL) value {
132	if (value == [self isListeningToRemote]) return;
133	if (value) {
134		[self startListening: self];
135	} else {
136		[self stopListening: self];
137	}
138}
139- (BOOL) isListeningToRemote {
140	return (eventHandlerRef!=NULL);
141}
142
143- (void) startListening: (id) sender {
144
145	if (eventHandlerRef) return;
146
147	EventTypeSpec eventSpec[2] = {
148		{ kEventClassKeyboard, kEventHotKeyPressed },
149		{ kEventClassKeyboard, kEventHotKeyReleased }
150	};
151
152	InstallEventHandler( GetEventDispatcherTarget(),
153						 (EventHandlerProcPtr)hotKeyEventHandler,
154						 2, eventSpec, self, &eventHandlerRef);
155}
156- (void) stopListening: (id) sender {
157	RemoveEventHandler(eventHandlerRef);
158	eventHandlerRef = NULL;
159}
160
161- (BOOL) sendsEventForButtonIdentifier: (RemoteControlEventIdentifier) identifier {
162	NSEnumerator* values = [hotKeyRemoteEventMapping objectEnumerator];
163	NSNumber* remoteIdentifier;
164	while( (remoteIdentifier = [values nextObject]) ) {
165		if ([remoteIdentifier unsignedIntValue] == identifier) return YES;
166	}
167	return NO;
168}
169
170+ (const char*) remoteControlDeviceName {
171	return "Keyboard";
172}
173
174- (BOOL)registerHotKeyCode: (unsigned int) keycode modifiers: (unsigned int) modifiers remoteEventIdentifier: (RemoteControlEventIdentifier) identifier {
175	OSStatus err;
176	EventHotKeyID hotKeyID;
177	EventHotKeyRef carbonHotKey;
178
179	hotKeyID.signature = 'PTHk';
180	hotKeyID.id = (long)keycode;
181
182	err = RegisterEventHotKey(keycode, modifiers, hotKeyID, GetEventDispatcherTarget(), 0, &carbonHotKey );
183
184	if( err )
185		return NO;
186
187	[hotKeyRemoteEventMapping setObject: [NSNumber numberWithInt:identifier] forKey: [NSNumber numberWithUnsignedInt: hotKeyID.id]];
188
189	return YES;
190}
191/*
192- (void)unregisterHotKey: (PTHotKey*)hotKey
193{
194	OSStatus err;
195	EventHotKeyRef carbonHotKey;
196	NSValue* key;
197
198	if( [[self allHotKeys] containsObject: hotKey] == NO )
199		return;
200
201	carbonHotKey = [self _carbonHotKeyForHotKey: hotKey];
202	NSAssert( carbonHotKey != nil, @"" );
203
204	err = UnregisterEventHotKey( carbonHotKey );
205	//Watch as we ignore 'err':
206
207	key = [NSValue valueWithPointer: carbonHotKey];
208	[mHotKeys removeObjectForKey: key];
209
210	[self _updateEventHandler];
211
212	//See that? Completely ignored
213}
214*/
215
216- (RemoteControlEventIdentifier) remoteControlEventIdentifierForID: (unsigned int) id {
217	NSNumber* remoteEventIdentifier = [hotKeyRemoteEventMapping objectForKey:[NSNumber numberWithUnsignedInt: id]];
218	return [remoteEventIdentifier unsignedIntValue];
219}
220
221- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown {
222	[delegate sendRemoteButtonEvent: event pressedDown: pressedDown remoteControl:self];
223}
224
225static RemoteControlEventIdentifier lastEvent;
226
227
228static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* userData )
229{
230	GlobalKeyboardDevice* keyboardDevice = (GlobalKeyboardDevice*) userData;
231	EventHotKeyID hkCom;
232	GetEventParameter(inEvent,kEventParamDirectObject,typeEventHotKeyID,NULL,sizeof(hkCom),NULL,&hkCom);
233
234	RemoteControlEventIdentifier identifier = [keyboardDevice remoteControlEventIdentifierForID:hkCom.id];
235	if (identifier == 0) return noErr;
236
237	BOOL pressedDown = YES;
238	if (identifier != lastEvent) {
239		lastEvent = identifier;
240	} else {
241		lastEvent = 0;
242	    pressedDown = NO;
243	}
244	[keyboardDevice sendRemoteButtonEvent: identifier pressedDown: pressedDown];
245
246	return noErr;
247}
248
249@end
250