4 // Created by Quentin D. Carnicelli on Thu Jun 06 2002.
5 // Copyright (c) 2002 Subband inc.. All rights reserved.
7 // Feedback welcome at qdc@subband.com
8 // This code is provided AS IS, so don't hurt yourself with it...
11 #import "HotKeyCenter.h"
14 #import <Carbon/Carbon.h>
16 #define kHotKeyCenterSignature 'HKyC'
19 @interface _HotKeyData : NSObject
30 @implementation _HotKeyData
34 @interface HotKeyCenter (Private)
35 - (OSStatus)handleHotKeyEvent: (EventRef)inEvent;
37 - (BOOL)_registerHotKeyIfNeeded:(_HotKeyData *)hk;
38 - (void)_unregisterHotKeyIfNeeded:(_HotKeyData *)hk;
40 + (BOOL)_systemSupportsHotKeys;
41 - (void)_hotKeyUp:(_HotKeyData *)hotKey;
42 - (void)_hotKeyDown:(_HotKeyData *)hotKey;
43 - (void)_hotKeyDownWithRef:(EventHotKeyRef)ref;
44 - (void)_hotKeyUpWithRef:(EventHotKeyRef)ref;
45 - (_HotKeyData*)_findHotKeyWithRef:(EventHotKeyRef)ref;
47 pascal OSErr keyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *refCon);
50 @implementation HotKeyCenter
52 static id _sharedHKCenter = nil;
56 if (_sharedHKCenter != nil)
58 return _sharedHKCenter;
61 _sharedHKCenter = [[HotKeyCenter alloc] init];
63 if ([self _systemSupportsHotKeys])
65 EventTypeSpec eventSpec[2] =
67 { kEventClassKeyboard, kEventHotKeyPressed },
68 { kEventClassKeyboard, kEventHotKeyReleased }
71 InstallEventHandler( GetEventDispatcherTarget(), NewEventHandlerUPP((EventHandlerProcPtr) keyEventHandler), 2, eventSpec, nil, nil);
74 return _sharedHKCenter;
79 if ( (self = [super init]) )
82 mHotKeys = [[NSMutableDictionary alloc] init];
93 - (BOOL)addHotKey:(NSString *)name combo:(KeyCombo *)combo target:(id)target action:(SEL)action
95 _HotKeyData *oldHotKey;
96 _HotKeyData *newHotKey;
98 NSParameterAssert(name != nil);
99 NSParameterAssert(combo != nil);
100 NSParameterAssert(target != nil);
101 NSParameterAssert(action != nil);
103 //** Check if we have one of these yet
104 oldHotKey = [mHotKeys objectForKey:name];
106 if (oldHotKey) //Registered already?
108 [self removeHotKey:name];
111 //** Save the hot key to our own list
112 newHotKey = [[[_HotKeyData alloc] init] autorelease];
113 newHotKey->mRegistered = NO;
114 newHotKey->mRef = nil;
115 newHotKey->mCombo = [combo retain];
116 newHotKey->mTarget = target; //Retain this?
117 newHotKey->mAction = action;
119 [mHotKeys setObject:newHotKey forKey:name];
121 return [self _registerHotKeyIfNeeded:newHotKey];
124 - (void)removeHotKey:(NSString *)name;
128 hotKey = [mHotKeys objectForKey:name];
129 if (hotKey == nil) //Not registered
132 [self _unregisterHotKeyIfNeeded:hotKey];
133 [hotKey->mCombo release];
135 //Drop it from our hot key list
136 [mHotKeys removeObjectForKey: name];
139 - (NSArray *)allNames
141 return [mHotKeys allKeys];
144 - (KeyCombo *)keyComboForName:(NSString *)name
146 _HotKeyData * hotKey;
148 hotKey = [mHotKeys objectForKey:name];
149 if( hotKey == nil ) //Not registered
152 return hotKey->mCombo;
155 - (void)setEnabled:(BOOL)enabled
157 NSEnumerator *enumerator;
160 enumerator = [mHotKeys objectEnumerator];
162 while ((hotKey = [enumerator nextObject]) != nil)
165 [self _registerHotKeyIfNeeded:hotKey];
167 [self _unregisterHotKeyIfNeeded:hotKey];
178 - (void)sendEvent:(NSEvent *)event;
181 EventHotKeyRef hotKeyRef;
183 //We only have to intercept sendEvent to do hot keys on old system versions
184 if ([HotKeyCenter _systemSupportsHotKeys] == YES)
187 if ([event type] == NSSystemDefined)
189 subType = [event subtype];
191 if (subType == 6) //6 is hot key down
193 hotKeyRef = (EventHotKeyRef)[event data1]; //data1 is our hot key ref
194 if (hotKeyRef != nil)
196 [self _hotKeyDownWithRef:hotKeyRef];
199 else if (subType == 9) //9 is hot key up
201 hotKeyRef = (EventHotKeyRef)[event data1];
202 if (hotKeyRef != nil)
204 [self _hotKeyUpWithRef:hotKeyRef];
210 - (OSStatus)handleHotKeyEvent:(EventRef)inEvent
213 EventHotKeyID hotKeyID;
216 //Shouldnt get here on non-hotkey supporting system versions
217 NSAssert([HotKeyCenter _systemSupportsHotKeys] == YES, @"");
218 NSAssert(GetEventClass(inEvent) == kEventClassKeyboard, @"Got unhandled event class");
220 err = GetEventParameter(inEvent, kEventParamDirectObject, typeEventHotKeyID, nil, sizeof(EventHotKeyID), nil, &hotKeyID);
227 NSAssert(hotKeyID.signature == kHotKeyCenterSignature, @"Got unknown hot key");
229 hk = (_HotKeyData *)hotKeyID.id;
230 NSAssert(hk != nil, @"Got bad hot key");
232 switch (GetEventKind(inEvent))
234 case kEventHotKeyPressed:
235 [self _hotKeyDown:hk];
238 case kEventHotKeyReleased:
249 + (BOOL)_systemSupportsHotKeys
252 Gestalt(gestaltSystemVersion,&vers);
253 return (vers >= 0x00001020);
256 - (BOOL)_registerHotKeyIfNeeded:(_HotKeyData *)hk
260 NSParameterAssert(hk != nil);
264 if( mEnabled == YES && hk->mRegistered == NO && [combo isValid] == YES )
269 keyID.signature = kHotKeyCenterSignature;
270 keyID.id = (unsigned long)hk;
271 err = RegisterEventHotKey([combo keyCode], [combo modifiers],
272 keyID, GetEventDispatcherTarget(), 0, &hk->mRef);
278 hk->mRegistered = YES;
284 - (void)_unregisterHotKeyIfNeeded:(_HotKeyData *)hk
286 NSParameterAssert(hk != nil);
288 if (hk->mRegistered && hk->mRef != nil)
290 UnregisterEventHotKey(hk->mRef);
294 - (void)_hotKeyDown:(_HotKeyData *)hotKey
296 id target = hotKey->mTarget;
297 SEL action = hotKey->mAction;
298 [target performSelector:action withObject:self];
301 - (void)_hotKeyUp: (_HotKeyData *)hotKey
305 - (void)_hotKeyDownWithRef:(EventHotKeyRef)ref
309 hotKey = [self _findHotKeyWithRef:ref];
312 [self _hotKeyDown:hotKey];
316 - (void)_hotKeyUpWithRef:(EventHotKeyRef)ref
320 - (_HotKeyData *)_findHotKeyWithRef:(EventHotKeyRef)ref
322 NSEnumerator *enumerator;
325 enumerator = [mHotKeys objectEnumerator];
327 while ((hotKey = [enumerator nextObject]) != nil)
329 if (hotKey->mRef == ref)
338 pascal OSErr keyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *refCon)
340 return [[HotKeyCenter sharedCenter] handleHotKeyEvent:inEvent];