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