summaryrefslogtreecommitdiffstats
path: root/DDHotKeyCenter.m
diff options
context:
space:
mode:
Diffstat (limited to 'DDHotKeyCenter.m')
-rw-r--r--DDHotKeyCenter.m480
1 files changed, 244 insertions, 236 deletions
diff --git a/DDHotKeyCenter.m b/DDHotKeyCenter.m
index 465baac..a5ae678 100644
--- a/DDHotKeyCenter.m
+++ b/DDHotKeyCenter.m
@@ -1,7 +1,7 @@
/*
DDHotKey -- DDHotKeyCenter.m
- Copyright (c) 2010, Dave DeLong <http://www.davedelong.com>
+ Copyright (c) 2012, Dave DeLong <http://www.davedelong.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
@@ -12,302 +12,310 @@
#import <Carbon/Carbon.h>
#import <objc/runtime.h>
+#if __has_feature(objc_arc)
+
+#define DDHK_HAS_ARC 1
+#define DDHK_RETAIN(_o) (_o)
+#define DDHK_RELEASE(_o)
+#define DDHK_AUTORELEASE(_o) (_o)
+
+#else
+
+#define DDHK_HAS_ARC 0
+#define DDHK_RETAIN(_o) [(_o) retain]
+#define DDHK_RELEASE(_o) [(_o) release]
+#define DDHK_AUTORELEASE(_o) [(_o) autorelease]
+
+#endif
+
#pragma mark Private Global Declarations
-static NSMutableSet * _registeredHotKeys = nil;
-static UInt32 _nextHotKeyID = 1;
-OSStatus dd_hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void * userData);
+OSStatus dd_hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData);
UInt32 dd_translateModifierFlags(NSUInteger flags);
-NSString* dd_stringifyModifierFlags(NSUInteger flags);
#pragma mark DDHotKey
-@implementation DDHotKey
+@interface DDHotKey ()
-- (id) target { return nil; }
-- (SEL) action { return nil; }
-- (id) object { return nil; }
-- (unsigned short) keyCode { return 0; }
-- (NSUInteger) modifierFlags { return 0; }
+@property (nonatomic, retain) NSValue *hotKeyRef;
+@property (nonatomic) UInt32 hotKeyID;
-#if NS_BLOCKS_AVAILABLE
-- (DDHotKeyTask) task { return nil; }
-#endif
+@end
-- (NSUInteger) hash {
- return [self keyCode] + [self modifierFlags];
+@implementation DDHotKey {
+ id _target;
+ SEL _action;
+ id _object;
+
+ unsigned short _keyCode;
+ NSUInteger _modifierFlags;
+ DDHotKeyTask _task;
}
-- (BOOL) isEqual:(id)object {
- BOOL equal = NO;
- if ([object isKindOfClass:[DDHotKey class]]) {
- equal = ([object keyCode] == [self keyCode]);
- equal &= ([object modifierFlags] == [self modifierFlags]);
- }
- return equal;
+- (void) dealloc {
+ [[DDHotKeyCenter sharedHotKeyCenter] unregisterHotKey:self];
+#if !DDHK_HAS_ARC
+ DDHK_RELEASE(_target); _target = nil;
+ DDHK_RELEASE(_object); _object = nil;
+ DDHK_RELEASE(_hotKeyRef); _hotKeyRef = nil;
+ DDHK_RELEASE(_task); _task = nil;
+ [super dealloc];
+#endif
}
-- (NSString *) description {
- NSString * flags = dd_stringifyModifierFlags([self modifierFlags]);
- NSString * invokes = @"(block)";
- if ([self target] != nil && [self action] != nil) {
- invokes = [NSString stringWithFormat:@"[%@ %@]", [self target], NSStringFromSelector([self action])];
- }
- return [NSString stringWithFormat:@"%@\n\t(key: %hu\n\tflags: %@\n\tinvokes: %@)", [super description], [self keyCode], flags, invokes];
+- (void)_setTarget:(id)target {
+ if (target != _target) {
+ DDHK_RELEASE(_target);
+ _target = DDHK_RETAIN(target);
+ }
}
-@end
-
-@interface _DDHotKey : DDHotKey {
- @private
- id target;
- SEL action;
- id object;
-
-#if NS_BLOCKS_AVAILABLE
- DDHotKeyTask task;
-#endif
-
- unsigned short keyCode;
- NSUInteger modifierFlags;
- UInt32 hotKeyID;
- NSValue * hotKeyRef;
+- (void)_setAction:(SEL)action {
+ _action = action;
}
-@property (nonatomic, retain) id target;
-@property (nonatomic) SEL action;
-@property (nonatomic, retain) id object;
-@property (nonatomic) unsigned short keyCode;
-@property (nonatomic) NSUInteger modifierFlags;
-@property (nonatomic) UInt32 hotKeyID;
-@property (nonatomic, retain) NSValue * hotKeyRef;
+- (void)_setObject:(id)object {
+ if (object != _object) {
+ DDHK_RELEASE(_object);
+ _object = DDHK_RETAIN(object);
+ }
+}
-#if NS_BLOCKS_AVAILABLE
-@property (nonatomic, copy) DDHotKeyTask task;
-#endif
+- (void)_setKeyCode:(unsigned short)keyCode {
+ _keyCode = keyCode;
+}
-- (void) invokeWithEvent:(NSEvent *)event;
-- (BOOL) registerHotKey;
-- (void) unregisterHotKey;
+- (void)_setModifierFlags:(NSUInteger)modifierFlags {
+ _modifierFlags = modifierFlags;
+}
-@end
+- (void)_setTask:(DDHotKeyTask)task {
+ DDHK_RELEASE(_task);
+ _task = [task copy];
+}
-@implementation _DDHotKey
+- (NSUInteger)hash {
+ return [self keyCode] ^ [self modifierFlags];
+}
-@synthesize target, action, object, keyCode, modifierFlags, hotKeyID, hotKeyRef;
-#if NS_BLOCKS_AVAILABLE
-@synthesize task;
-#endif
+- (BOOL)isEqual:(id)object {
+ BOOL equal = NO;
+ if ([object isKindOfClass:[DDHotKey class]]) {
+ equal = ([object keyCode] == [self keyCode]);
+ equal &= ([object modifierFlags] == [self modifierFlags]);
+ }
+ return equal;
+}
-- (Class) class { return [DDHotKey class]; }
+- (NSString *)description {
+ NSMutableArray *bits = [NSMutableArray array];
+ if ((_modifierFlags & NSControlKeyMask) > 0) { [bits addObject:@"NSControlKeyMask"]; }
+ if ((_modifierFlags & NSCommandKeyMask) > 0) { [bits addObject:@"NSCommandKeyMask"]; }
+ if ((_modifierFlags & NSShiftKeyMask) > 0) { [bits addObject:@"NSShiftKeyMask"]; }
+ if ((_modifierFlags & NSAlternateKeyMask) > 0) { [bits addObject:@"NSAlternateKeyMask"]; }
+
+ NSString *flags = [NSString stringWithFormat:@"(%@)", [bits componentsJoinedByString:@" | "]];
+ NSString *invokes = @"(block)";
+ if ([self target] != nil && [self action] != nil) {
+ invokes = [NSString stringWithFormat:@"[%@ %@]", [self target], NSStringFromSelector([self action])];
+ }
+ return [NSString stringWithFormat:@"%@\n\t(key: %hu\n\tflags: %@\n\tinvokes: %@)", [super description], [self keyCode], flags, invokes];
+}
-- (void) invokeWithEvent:(NSEvent *)event {
- if (target != nil && action != nil && [target respondsToSelector:action]) {
- [target performSelector:action withObject:event withObject:object];
- }
-#if NS_BLOCKS_AVAILABLE
- else if (task != nil) {
- task(event);
- }
-#endif
+- (void)invokeWithEvent:(NSEvent *)event {
+ if (_target != nil && _action != nil && [_target respondsToSelector:_action]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ [_target performSelector:_action withObject:event withObject:_object];
+#pragma clang diagnostic pop
+ } else if (_task != nil) {
+ _task(event);
+ }
}
-- (NSString *) actionString {
- return NSStringFromSelector(action);
+- (NSString *)actionString {
+ return NSStringFromSelector(_action);
}
-- (BOOL) registerHotKey {
- EventHotKeyID keyID;
- keyID.signature = 'htk1';
- keyID.id = _nextHotKeyID;
-
- EventHotKeyRef carbonHotKey;
- UInt32 flags = dd_translateModifierFlags(modifierFlags);
- OSStatus err = RegisterEventHotKey(keyCode, flags, keyID, GetEventDispatcherTarget(), 0, &carbonHotKey);
-
- //error registering hot key
- if (err != 0) { return NO; }
-
- NSValue * refValue = [NSValue valueWithPointer:carbonHotKey];
- [self setHotKeyRef:refValue];
- [self setHotKeyID:_nextHotKeyID];
-
- _nextHotKeyID++;
-
- return YES;
+@end
+
+#pragma mark DDHotKeyCenter
+
+static DDHotKeyCenter *center = nil;
+
+@implementation DDHotKeyCenter {
+ NSMutableSet *_registeredHotKeys;
+ UInt32 _nextHotKeyID;
}
-- (void) unregisterHotKey {
- EventHotKeyRef carbonHotKey = (EventHotKeyRef)[hotKeyRef pointerValue];
- UnregisterEventHotKey(carbonHotKey);
- [self setHotKeyRef:nil];
++ (id)sharedHotKeyCenter {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ center = [[DDHotKeyCenter alloc] _init];
+ });
+ return center;
}
-- (void) dealloc {
- [target release], target = nil;
- [object release], object = nil;
- if (hotKeyRef != nil) {
- [self unregisterHotKey];
- [hotKeyRef release], hotKeyRef = nil;
- }
- [super dealloc];
++ (id)allocWithZone:(NSZone *)zone {
+ if (center == nil) {
+ return [super allocWithZone:zone];
+ }
+ return DDHK_RETAIN(center);
}
-@end
+- (id)_init {
+ self = [super init];
+ if (self) {
+ _registeredHotKeys = [[NSMutableSet alloc] init];
+ _nextHotKeyID = 1;
+ }
+ return self;
+}
-#pragma mark DDHotKeyCenter
+- (NSSet *)hotKeysMatchingPredicate:(NSPredicate *)predicate {
+ return [_registeredHotKeys filteredSetUsingPredicate:predicate];
+}
-@implementation DDHotKeyCenter
-
-+ (void) initialize {
- if (self == [DDHotKeyCenter class] && _registeredHotKeys == nil) {
- _registeredHotKeys = [[NSMutableSet alloc] init];
- _nextHotKeyID = 1;
- EventTypeSpec eventSpec;
- eventSpec.eventClass = kEventClassKeyboard;
- eventSpec.eventKind = kEventHotKeyReleased;
- InstallApplicationEventHandler(&dd_hotKeyHandler, 1, &eventSpec, NULL, NULL);
- }
+- (BOOL)hasRegisteredHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"keyCode = %hu AND modifierFlags = %lu", keyCode, flags];
+ return ([[self hotKeysMatchingPredicate:predicate] count] > 0);
}
-- (NSSet *) hotKeysMatchingPredicate:(NSPredicate *)predicate {
- return [_registeredHotKeys filteredSetUsingPredicate:predicate];
+- (BOOL)_registerHotKey:(DDHotKey *)hotKey {
+ BOOL success = NO;
+
+ EventHotKeyID keyID;
+ keyID.signature = 'htk1';
+ keyID.id = _nextHotKeyID;
+
+ EventHotKeyRef carbonHotKey;
+ UInt32 flags = dd_translateModifierFlags([hotKey modifierFlags]);
+ OSStatus err = RegisterEventHotKey([hotKey keyCode], flags, keyID, GetEventDispatcherTarget(), 0, &carbonHotKey);
+
+ //error registering hot key
+ if (err != 0) { return NO; }
+
+ NSValue *refValue = [NSValue valueWithPointer:carbonHotKey];
+ [hotKey setHotKeyRef:refValue];
+ [hotKey setHotKeyID:_nextHotKeyID];
+
+ _nextHotKeyID++;
+ [_registeredHotKeys addObject:hotKey];
+
+ return success;
}
-- (BOOL) hasRegisteredHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags {
- NSPredicate * predicate = [NSPredicate predicateWithFormat:@"keyCode = %hu AND modifierFlags = %lu", keyCode, flags];
- return ([[self hotKeysMatchingPredicate:predicate] count] > 0);
+- (void)unregisterHotKey:(DDHotKey *)hotKey {
+ NSValue *hotKeyRef = [hotKey hotKeyRef];
+ if (hotKeyRef) {
+ EventHotKeyRef carbonHotKey = (EventHotKeyRef)[hotKeyRef pointerValue];
+ UnregisterEventHotKey(carbonHotKey);
+ [hotKey setHotKeyRef:nil];
+
+ [_registeredHotKeys removeObject:hotKey];
+ }
}
-#if NS_BLOCKS_AVAILABLE
-- (BOOL) registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags task:(DDHotKeyTask)task {
- //we can't add a new hotkey if something already has this combo
- if ([self hasRegisteredHotKeyWithKeyCode:keyCode modifierFlags:flags]) { return NO; }
-
- _DDHotKey * newHotKey = [[_DDHotKey alloc] init];
- [newHotKey setTask:task];
- [newHotKey setKeyCode:keyCode];
- [newHotKey setModifierFlags:flags];
-
- BOOL success = [newHotKey registerHotKey];
- if (success) {
- [_registeredHotKeys addObject:newHotKey];
- }
-
- [newHotKey release];
- return success;
+- (BOOL)registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags task:(DDHotKeyTask)task {
+ //we can't add a new hotkey if something already has this combo
+ if ([self hasRegisteredHotKeyWithKeyCode:keyCode modifierFlags:flags]) { return NO; }
+
+ DDHotKey *newHotKey = DDHK_AUTORELEASE([[DDHotKey alloc] init]);
+ [newHotKey _setTask:task];
+ [newHotKey _setKeyCode:keyCode];
+ [newHotKey _setModifierFlags:flags];
+
+ return [self _registerHotKey:newHotKey];
}
-#endif
-- (BOOL) registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags target:(id)target action:(SEL)action object:(id)object {
- //we can't add a new hotkey if something already has this combo
- if ([self hasRegisteredHotKeyWithKeyCode:keyCode modifierFlags:flags]) { return NO; }
-
- //build the hotkey object:
- _DDHotKey * newHotKey = [[_DDHotKey alloc] init];
- [newHotKey setTarget:target];
- [newHotKey setAction:action];
- [newHotKey setObject:object];
- [newHotKey setKeyCode:keyCode];
- [newHotKey setModifierFlags:flags];
-
- BOOL success = [newHotKey registerHotKey];
- if (success) {
- [_registeredHotKeys addObject:newHotKey];
- }
-
- [newHotKey release];
- return success;
+- (BOOL)registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags target:(id)target action:(SEL)action object:(id)object {
+ //we can't add a new hotkey if something already has this combo
+ if ([self hasRegisteredHotKeyWithKeyCode:keyCode modifierFlags:flags]) { return NO; }
+
+ //build the hotkey object:
+ DDHotKey *newHotKey = DDHK_AUTORELEASE([[DDHotKey alloc] init]);
+ [newHotKey _setTarget:target];
+ [newHotKey _setAction:action];
+ [newHotKey _setObject:object];
+ [newHotKey _setKeyCode:keyCode];
+ [newHotKey _setModifierFlags:flags];
+ return [self _registerHotKey:newHotKey];
}
-- (void) unregisterHotKeysMatchingPredicate:(NSPredicate *)predicate {
- //explicitly unregister the hotkey, since relying on the unregistration in -dealloc can be problematic
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- NSSet * matches = [self hotKeysMatchingPredicate:predicate];
- [_registeredHotKeys minusSet:matches];
- [matches makeObjectsPerformSelector:@selector(unregisterHotKey)];
- [pool release];
+- (void)unregisterHotKeysMatchingPredicate:(NSPredicate *)predicate {
+ //explicitly unregister the hotkey, since relying on the unregistration in -dealloc can be problematic
+ @autoreleasepool {
+ NSSet *matches = [self hotKeysMatchingPredicate:predicate];
+ for (DDHotKey *hotKey in matches) {
+ [self unregisterHotKey:hotKey];
+ }
+ }
}
-- (void) unregisterHotKey:(DDHotKey *)hotKey {
- if (object_getClass(hotKey) == [_DDHotKey class]) {
- _DDHotKey * key = (_DDHotKey *)hotKey;
- [_registeredHotKeys removeObject:key];
- [key unregisterHotKey];
- } else {
- [NSException raise:NSInvalidArgumentException format:@"Invalid hotkey"];
- }
+- (void)unregisterHotKeysWithTarget:(id)target {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"target = %@", target];
+ [self unregisterHotKeysMatchingPredicate:predicate];
}
-- (void) unregisterHotKeysWithTarget:(id)target {
- NSPredicate * predicate = [NSPredicate predicateWithFormat:@"target = %@", target];
- [self unregisterHotKeysMatchingPredicate:predicate];
+- (void)unregisterHotKeysWithTarget:(id)target action:(SEL)action {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"target = %@ AND actionString = %@", target, NSStringFromSelector(action)];
+ [self unregisterHotKeysMatchingPredicate:predicate];
}
-- (void) unregisterHotKeysWithTarget:(id)target action:(SEL)action {
- NSPredicate * predicate = [NSPredicate predicateWithFormat:@"target = %@ AND actionString = %@", target, NSStringFromSelector(action)];
- [self unregisterHotKeysMatchingPredicate:predicate];
+- (void)unregisterHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"keyCode = %hu AND modifierFlags = %lu", keyCode, flags];
+ [self unregisterHotKeysMatchingPredicate:predicate];
}
-- (void) unregisterHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags {
- NSPredicate * predicate = [NSPredicate predicateWithFormat:@"keyCode = %hu AND modifierFlags = %lu", keyCode, flags];
- [self unregisterHotKeysMatchingPredicate:predicate];
+- (void)unregisterAllHotKeys {
+ NSSet *keys = [_registeredHotKeys copy];
+ for (DDHotKey *key in keys) {
+ [self unregisterHotKey:key];
+ }
+ DDHK_RELEASE(keys);
}
-- (NSSet *) registeredHotKeys {
- return [self hotKeysMatchingPredicate:[NSPredicate predicateWithFormat:@"hotKeyRef != NULL"]];
+- (NSSet *)registeredHotKeys {
+ return [self hotKeysMatchingPredicate:[NSPredicate predicateWithFormat:@"hotKeyRef != NULL"]];
}
@end
-OSStatus dd_hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void * userData) {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
-
- EventHotKeyID hotKeyID;
- GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(hotKeyID),NULL,&hotKeyID);
-
- UInt32 keyID = hotKeyID.id;
-
- NSSet * matchingHotKeys = [_registeredHotKeys filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"hotKeyID = %u", keyID]];
- if ([matchingHotKeys count] > 1) { NSLog(@"ERROR!"); }
- _DDHotKey * matchingHotKey = [matchingHotKeys anyObject];
-
- NSEvent * event = [NSEvent eventWithEventRef:theEvent];
- NSEvent * keyEvent = [NSEvent keyEventWithType:NSKeyUp
- location:[event locationInWindow]
- modifierFlags:[event modifierFlags]
- timestamp:[event timestamp]
- windowNumber:-1
- context:nil
- characters:@""
- charactersIgnoringModifiers:@""
- isARepeat:NO
- keyCode:[matchingHotKey keyCode]];
-
- [matchingHotKey invokeWithEvent:keyEvent];
-
- [pool release];
-
- return noErr;
+OSStatus dd_hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData) {
+ @autoreleasepool {
+ EventHotKeyID hotKeyID;
+ GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(hotKeyID),NULL,&hotKeyID);
+
+ UInt32 keyID = hotKeyID.id;
+
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"hotKeyID = %u", keyID];
+ NSSet *matchingHotKeys = [[DDHotKeyCenter sharedHotKeyCenter] hotKeysMatchingPredicate:predicate];
+ if ([matchingHotKeys count] > 1) { NSLog(@"ERROR!"); }
+ DDHotKey *matchingHotKey = [matchingHotKeys anyObject];
+
+ NSEvent *event = [NSEvent eventWithEventRef:theEvent];
+ NSEvent *keyEvent = [NSEvent keyEventWithType:NSKeyUp
+ location:[event locationInWindow]
+ modifierFlags:[event modifierFlags]
+ timestamp:[event timestamp]
+ windowNumber:-1
+ context:nil
+ characters:@""
+ charactersIgnoringModifiers:@""
+ isARepeat:NO
+ keyCode:[matchingHotKey keyCode]];
+
+ [matchingHotKey invokeWithEvent:keyEvent];
+ }
+
+ return noErr;
}
UInt32 dd_translateModifierFlags(NSUInteger flags) {
- UInt32 newFlags = 0;
- if ((flags & NSControlKeyMask) > 0) { newFlags |= controlKey; }
- if ((flags & NSCommandKeyMask) > 0) { newFlags |= cmdKey; }
- if ((flags & NSShiftKeyMask) > 0) { newFlags |= shiftKey; }
- if ((flags & NSAlternateKeyMask) > 0) { newFlags |= optionKey; }
- return newFlags;
-}
-
-NSString* dd_stringifyModifierFlags(NSUInteger flags) {
- NSMutableArray * bits = [NSMutableArray array];
- if ((flags & NSControlKeyMask) > 0) { [bits addObject:@"NSControlKeyMask"]; }
- if ((flags & NSCommandKeyMask) > 0) { [bits addObject:@"NSCommandKeyMask"]; }
- if ((flags & NSShiftKeyMask) > 0) { [bits addObject:@"NSShiftKeyMask"]; }
- if ((flags & NSAlternateKeyMask) > 0) { [bits addObject:@"NSAlternateKeyMask"]; }
- if ([bits count] > 0) {
- return [NSString stringWithFormat:@"(%@)", [bits componentsJoinedByString:@" | "]];
- }
- return @"ERROR: No valid flags";
+ UInt32 newFlags = 0;
+ if ((flags & NSControlKeyMask) > 0) { newFlags |= controlKey; }
+ if ((flags & NSCommandKeyMask) > 0) { newFlags |= cmdKey; }
+ if ((flags & NSShiftKeyMask) > 0) { newFlags |= shiftKey; }
+ if ((flags & NSAlternateKeyMask) > 0) { newFlags |= optionKey; }
+ return newFlags;
}