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