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