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