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