Removing the use of private CoreGraphics APIs to draw shadows, and replacing with...
[ITKit.git] / ITHotKeyCenter.m
1 #import "ITHotKeyCenter.h"
2 #import "ITHotKey.h"
3 #import "ITKeyCombo.h"
4 #import <Carbon/Carbon.h>
5
6 @interface ITHotKeyCenter (Private)
7 - (BOOL)_hasCarbonEventSupport;
8
9 - (ITHotKey *)_hotKeyForCarbonHotKey:(EventHotKeyRef)carbonHotKey;
10 - (EventHotKeyRef)_carbonHotKeyForHotKey:(ITHotKey *)hotKey;
11
12 - (void)_updateEventHandler;
13 - (void)_hotKeyDown:(ITHotKey *)hotKey;
14 - (void)_hotKeyUp:(ITHotKey *)hotKey;
15
16 static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *refCon);
17 @end
18
19 @implementation ITHotKeyCenter
20
21 static ITHotKeyCenter *_sharedHotKeyCenter = nil;
22
23 + (id)sharedCenter {
24         if (!_sharedHotKeyCenter) {
25                 _sharedHotKeyCenter = [[self alloc] init];
26         }
27         return _sharedHotKeyCenter;
28 }
29
30 - (id)init {
31         if ((self = [super init])) {
32                 mHotKeys = [[NSMutableDictionary alloc] init];
33                 _enabled = YES;
34         }
35         return self;
36 }
37
38 - (void)dealloc {
39         [mHotKeys release];
40         [super dealloc];
41 }
42
43 - (BOOL)isEnabled {
44         return _enabled;
45 }
46
47 - (void)setEnabled:(BOOL)flag {
48         _enabled = flag;
49 }
50
51 - (BOOL)registerHotKey:(ITHotKey *)hotKey {
52         EventHotKeyID hotKeyID;
53         EventHotKeyRef carbonHotKey;
54         NSValue *key;
55         
56         if ([[self allHotKeys] containsObject:hotKey]) {
57                 [self unregisterHotKey:hotKey];
58         }
59         
60         if (![[hotKey keyCombo] isValidHotKeyCombo]) {
61                 return YES;
62         }
63         
64         hotKeyID.signature = 'PTHk';
65         hotKeyID.id = (long)hotKey;
66         
67         if (RegisterEventHotKey([[hotKey keyCombo] keyCode], [[hotKey keyCombo] modifiers], hotKeyID, GetEventDispatcherTarget(), nil, &carbonHotKey)) {
68                 return NO;
69         }
70         
71         key = [NSValue valueWithPointer:carbonHotKey];
72         [mHotKeys setObject:hotKey forKey:key];
73         
74         [self _updateEventHandler];
75         
76         return YES;
77 }
78
79 - (void)unregisterHotKey:(ITHotKey *)hotKey {
80         OSStatus err;
81         EventHotKeyRef carbonHotKey;
82         NSValue *key;
83         
84         if (![[self allHotKeys] containsObject:hotKey]) {
85                 return;
86         }
87         
88         carbonHotKey = [self _carbonHotKeyForHotKey:hotKey];
89         NSAssert(carbonHotKey, @"");
90         
91         err = UnregisterEventHotKey(carbonHotKey);
92         
93         key = [NSValue valueWithPointer:carbonHotKey];
94         [mHotKeys removeObjectForKey:key];
95         
96         [self _updateEventHandler];
97 }
98
99 - (NSArray *)allHotKeys {
100         return [mHotKeys allValues];
101 }
102
103 - (BOOL)_hasCarbonEventSupport {
104         return (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_1);
105 }
106
107 - (ITHotKey *)_hotKeyForCarbonHotKey:(EventHotKeyRef)carbonHotKey {
108         NSValue *key = [NSValue valueWithPointer:carbonHotKey];
109         return [mHotKeys objectForKey:key];
110 }
111
112 - (EventHotKeyRef)_carbonHotKeyForHotKey:(ITHotKey *)hotKey {
113         NSArray *values;
114         NSValue *value;
115         
116         values = [mHotKeys allKeysForObject:hotKey];
117         NSAssert(([values count] == 1), @"Failed to find Carbon Hotkey for ITHotKey");
118         
119         value = [values lastObject];
120         
121         return (EventHotKeyRef)[value pointerValue];
122 }
123
124 - (void)_updateEventHandler {
125         if (![self _hasCarbonEventSupport]) {
126                 return;
127         }
128         
129         if ([mHotKeys count] && !mEventHandlerInstalled) {
130                 EventTypeSpec eventSpec[2] = {
131                         { kEventClassKeyboard, kEventHotKeyPressed },
132                         { kEventClassKeyboard, kEventHotKeyReleased }
133                 };
134                 
135                 InstallEventHandler(GetEventDispatcherTarget(), (EventHandlerProcPtr)hotKeyEventHandler, 2, eventSpec, nil, nil);
136                 
137                 mEventHandlerInstalled = YES;
138         }
139 }
140
141 - (void)_hotKeyDown:(ITHotKey *)hotKey {
142         [hotKey invoke];
143 }
144
145 - (void)_hotKeyUp:(ITHotKey *)hotKey {
146 }
147
148 - (void)sendEvent:(NSEvent *)event {
149         long subType;
150         EventHotKeyRef carbonHotKey;
151         
152         if (!_enabled) {
153                 return;
154         }
155         
156         //We only have to intercept sendEvent to do hot keys on old system versions
157         if ([self _hasCarbonEventSupport]) {
158                 return;
159         }
160         
161         if ([event type] == NSSystemDefined) {
162                 subType = [event subtype];
163                 
164                 if (subType == 6) { //6 is hot key down
165                         carbonHotKey = (EventHotKeyRef)[event data1]; //data1 is our hot key ref
166                         if (carbonHotKey) {
167                                 ITHotKey *hotKey = [self _hotKeyForCarbonHotKey:carbonHotKey];
168                                 [self _hotKeyDown:hotKey];
169                         }
170                 } else if (subType == 9) { //9 is hot key up
171                         carbonHotKey = (EventHotKeyRef)[event data1];
172                         if (carbonHotKey) {
173                                 ITHotKey *hotKey = [self _hotKeyForCarbonHotKey:carbonHotKey];
174                                 [self _hotKeyUp:hotKey];
175                         }
176                 }
177         }
178 }
179
180 - (OSStatus)sendCarbonEvent:(EventRef)event {
181         OSStatus err;
182         EventHotKeyID hotKeyID;
183         ITHotKey *hotKey;
184         
185         if (!_enabled) {
186                 return -1;
187         }
188         
189         NSAssert([self _hasCarbonEventSupport], @"");
190         NSAssert((GetEventClass(event) == kEventClassKeyboard), @"Unknown event class");
191
192         if ((err = GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, nil, sizeof(EventHotKeyID), nil, &hotKeyID))) {
193                 return err;
194         }
195         
196         NSAssert((hotKeyID.signature == 'PTHk'), @"Invalid hot key id");
197         NSAssert((hotKeyID.id != nil), @"Invalid hot key id");
198
199         hotKey = (ITHotKey *)hotKeyID.id;
200         
201         switch (GetEventKind(event)) {
202                 case kEventHotKeyPressed:
203                         [self _hotKeyDown:hotKey];
204                         break;
205                 case kEventHotKeyReleased:
206                         [self _hotKeyUp:hotKey];
207                         break;
208                 default:
209                         NSAssert(NO, @"Unknown event kind");
210                         break;
211         }
212         
213         return noErr;
214 }
215
216 static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *refCon) {
217         return [[ITHotKeyCenter sharedCenter] sendCarbonEvent:inEvent];
218 }
219
220 @end