4 // Created by Quentin Carnicelli on Sat Aug 02 2003.
5 // Copyright (c) 2003 iThink Software. All rights reserved.
8 #import "ITHotKeyCenter.h"
10 #import "ITKeyCombo.h"
11 #import <Carbon/Carbon.h>
14 #import "NSObjectAdditions.h"
17 @interface ITHotKeyCenter (Private)
18 - (BOOL)_hasCarbonEventSupport;
20 - (ITHotKey*)_hotKeyForCarbonHotKey: (EventHotKeyRef)carbonHotKey;
21 - (EventHotKeyRef)_carbonHotKeyForHotKey: (ITHotKey*)hotKey;
23 - (void)_updateEventHandler;
24 - (void)_hotKeyDown: (ITHotKey*)hotKey;
25 - (void)_hotKeyUp: (ITHotKey*)hotKey;
26 static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* refCon );
29 @implementation ITHotKeyCenter
31 static id _sharedHotKeyCenter = nil;
35 if( _sharedHotKeyCenter == nil )
37 _sharedHotKeyCenter = [[self alloc] init];
39 [_sharedHotKeyCenter releaseOnTerminate];
43 return _sharedHotKeyCenter;
52 mHotKeys = [[NSMutableDictionary alloc] init];
66 - (BOOL)registerHotKey: (ITHotKey*)hotKey
69 EventHotKeyID hotKeyID;
70 EventHotKeyRef carbonHotKey;
73 if( [[self allHotKeys] containsObject: hotKey] == YES )
74 [self unregisterHotKey: hotKey];
76 if( [[hotKey keyCombo] isValidHotKeyCombo] == NO )
79 hotKeyID.signature = 'PTHk';
80 hotKeyID.id = (long)hotKey;
82 err = RegisterEventHotKey( [[hotKey keyCombo] keyCode],
83 [[hotKey keyCombo] modifiers],
85 GetEventDispatcherTarget(),
92 key = [NSValue valueWithPointer: carbonHotKey];
93 [mHotKeys setObject: hotKey forKey: key];
95 [self _updateEventHandler];
100 - (void)unregisterHotKey: (ITHotKey*)hotKey
103 EventHotKeyRef carbonHotKey;
106 if( [[self allHotKeys] containsObject: hotKey] == NO )
109 carbonHotKey = [self _carbonHotKeyForHotKey: hotKey];
110 NSAssert( carbonHotKey != nil, @"" );
112 err = UnregisterEventHotKey( carbonHotKey );
113 //Watch as we ignore 'err':
115 key = [NSValue valueWithPointer: carbonHotKey];
116 [mHotKeys removeObjectForKey: key];
118 [self _updateEventHandler];
120 //See that? Completely ignored
123 - (NSArray*)allHotKeys
125 return [mHotKeys allValues];
130 - (BOOL)_hasCarbonEventSupport
132 return floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_1;
135 - (ITHotKey*)_hotKeyForCarbonHotKey: (EventHotKeyRef)carbonHotKey
137 NSValue* key = [NSValue valueWithPointer: carbonHotKey];
138 return [mHotKeys objectForKey: key];
141 - (EventHotKeyRef)_carbonHotKeyForHotKey: (ITHotKey*)hotKey
146 values = [mHotKeys allKeysForObject: hotKey];
147 NSAssert( [values count] == 1, @"Failed to find Carbon Hotkey for ITHotKey" );
149 value = [values lastObject];
151 return (EventHotKeyRef)[value pointerValue];
154 - (void)_updateEventHandler
156 if( [self _hasCarbonEventSupport] == NO ) //Don't use event handler on these systems
159 if( [mHotKeys count] && mEventHandlerInstalled == NO )
161 EventTypeSpec eventSpec[2] = {
162 { kEventClassKeyboard, kEventHotKeyPressed },
163 { kEventClassKeyboard, kEventHotKeyReleased }
166 InstallEventHandler( GetEventDispatcherTarget(),
167 (EventHandlerProcPtr)hotKeyEventHandler,
168 2, eventSpec, nil, nil);
170 mEventHandlerInstalled = YES;
174 - (void)_hotKeyDown: (ITHotKey*)hotKey
179 - (void)_hotKeyUp: (ITHotKey*)hotKey
183 - (void)sendEvent: (NSEvent*)event
186 EventHotKeyRef carbonHotKey;
188 //We only have to intercept sendEvent to do hot keys on old system versions
189 if( [self _hasCarbonEventSupport] )
192 if( [event type] == NSSystemDefined )
194 subType = [event subtype];
196 if( subType == 6 ) //6 is hot key down
198 carbonHotKey= (EventHotKeyRef)[event data1]; //data1 is our hot key ref
199 if( carbonHotKey != nil )
201 ITHotKey* hotKey = [self _hotKeyForCarbonHotKey: carbonHotKey];
202 [self _hotKeyDown: hotKey];
205 else if( subType == 9 ) //9 is hot key up
207 carbonHotKey= (EventHotKeyRef)[event data1];
208 if( carbonHotKey != nil )
210 ITHotKey* hotKey = [self _hotKeyForCarbonHotKey: carbonHotKey];
211 [self _hotKeyUp: hotKey];
217 - (OSStatus)sendCarbonEvent: (EventRef)event
220 EventHotKeyID hotKeyID;
223 NSAssert( [self _hasCarbonEventSupport], @"" );
224 NSAssert( GetEventClass( event ) == kEventClassKeyboard, @"Unknown event class" );
226 err = GetEventParameter( event,
227 kEventParamDirectObject,
230 sizeof(EventHotKeyID),
237 NSAssert( hotKeyID.signature == 'PTHk', @"Invalid hot key id" );
238 NSAssert( hotKeyID.id != nil, @"Invalid hot key id" );
240 hotKey = (ITHotKey*)hotKeyID.id;
242 switch( GetEventKind( event ) )
244 case kEventHotKeyPressed:
245 [self _hotKeyDown: hotKey];
248 case kEventHotKeyReleased:
249 [self _hotKeyUp: hotKey];
253 NSAssert( 0, @"Unknown event kind" );
260 static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* refCon )
262 return [[ITHotKeyCenter sharedCenter] sendCarbonEvent: inEvent];