diff options
| -rw-r--r-- | DDHotKey.xcodeproj/project.pbxproj | 10 | ||||
| -rw-r--r-- | DDHotKeyAppDelegate.m | 26 | ||||
| -rw-r--r-- | DDHotKeyCenter.h | 32 | ||||
| -rw-r--r-- | DDHotKeyCenter.m | 246 |
4 files changed, 314 insertions, 0 deletions
diff --git a/DDHotKey.xcodeproj/project.pbxproj b/DDHotKey.xcodeproj/project.pbxproj index 6afb0e4..0bdda50 100644 --- a/DDHotKey.xcodeproj/project.pbxproj +++ b/DDHotKey.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; }; 256AC3DA0F4B6AC300CF3369 /* DDHotKeyAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 256AC3D90F4B6AC300CF3369 /* DDHotKeyAppDelegate.m */; }; + 55378E8A1135C08E0038E405 /* DDHotKeyCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 55378E891135C08E0038E405 /* DDHotKeyCenter.m */; }; + 55378E8C1135C0A70038E405 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55378E8B1135C0A70038E405 /* Carbon.framework */; }; 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; @@ -25,6 +27,9 @@ 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; }; + 55378E881135C08E0038E405 /* DDHotKeyCenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDHotKeyCenter.h; sourceTree = "<group>"; }; + 55378E891135C08E0038E405 /* DDHotKeyCenter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DDHotKeyCenter.m; sourceTree = "<group>"; }; + 55378E8B1135C0A70038E405 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; 8D1107310486CEB800E47090 /* DDHotKey-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "DDHotKey-Info.plist"; sourceTree = "<group>"; }; 8D1107320486CEB800E47090 /* DDHotKey.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DDHotKey.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -35,6 +40,7 @@ buildActionMask = 2147483647; files = ( 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, + 55378E8C1135C0A70038E405 /* Carbon.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -44,6 +50,8 @@ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( + 55378E881135C08E0038E405 /* DDHotKeyCenter.h */, + 55378E891135C08E0038E405 /* DDHotKeyCenter.m */, 256AC3D80F4B6AC300CF3369 /* DDHotKeyAppDelegate.h */, 256AC3D90F4B6AC300CF3369 /* DDHotKeyAppDelegate.m */, ); @@ -84,6 +92,7 @@ 29B97317FDCFA39411CA2CEA /* Resources */, 29B97323FDCFA39411CA2CEA /* Frameworks */, 19C28FACFE9D520D11CA2CBB /* Products */, + 55378E8B1135C0A70038E405 /* Carbon.framework */, ); name = DDHotKey; sourceTree = "<group>"; @@ -173,6 +182,7 @@ files = ( 8D11072D0486CEB800E47090 /* main.m in Sources */, 256AC3DA0F4B6AC300CF3369 /* DDHotKeyAppDelegate.m in Sources */, + 55378E8A1135C08E0038E405 /* DDHotKeyCenter.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/DDHotKeyAppDelegate.m b/DDHotKeyAppDelegate.m index aeffd7a..a091fc6 100644 --- a/DDHotKeyAppDelegate.m +++ b/DDHotKeyAppDelegate.m @@ -7,6 +7,7 @@ // #import "DDHotKeyAppDelegate.h" +#import "DDHotKeyCenter.h" @implementation DDHotKeyAppDelegate @@ -14,6 +15,31 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // Insert code here to initialize your application + + DDHotKeyCenter * center = [[DDHotKeyCenter alloc] init]; + + [center registerHotKeyWithTarget:self action:@selector(hotkeyWithEvent:) object:nil keyCode:9 modifierFlags:NSControlKeyMask]; + [center registerHotKeyWithTarget:self action:@selector(hotkeyWithEvent:object:) object:@"foo!" keyCode:9 modifierFlags:(NSControlKeyMask | NSAlternateKeyMask)]; + + int theAnswer = 42; + [center registerHotKeyWithBlock:^(NSEvent *hkEvent) { + NSLog(@"Firing block hotkey"); + NSLog(@"Hotkey event: %@", hkEvent); + NSLog(@"the answer is: %d", theAnswer); + } keyCode:9 modifierFlags:(NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask)]; + + [center release]; +} + +- (void) hotkeyWithEvent:(NSEvent *)hkEvent { + NSLog(@"Firing -[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); + NSLog(@"Hotkey event: %@", hkEvent); +} + +- (void) hotkeyWithEvent:(NSEvent *)hkEvent object:(id)anObject { + NSLog(@"Firing -[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); + NSLog(@"Hotkey event: %@", hkEvent); + NSLog(@"Object: %@", anObject); } @end diff --git a/DDHotKeyCenter.h b/DDHotKeyCenter.h new file mode 100644 index 0000000..e1cd603 --- /dev/null +++ b/DDHotKeyCenter.h @@ -0,0 +1,32 @@ +// +// DDHotKeyManager.h +// EmptyAppKit +// +// Created by Dave DeLong on 2/20/10. +// Copyright 2010 Home. All rights reserved. +// + +#import <Cocoa/Cocoa.h> + +#define BUILD_FOR_SNOWLEOPARD (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) + +#if BUILD_FOR_SNOWLEOPARD +typedef void (^DDHotKeyTask)(NSEvent*); +#endif + +@interface DDHotKeyCenter : NSObject { + +} + +- (BOOL) registerHotKeyWithTarget:(id)target action:(SEL)action object:(id)object keyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags; + +#if BUILD_FOR_SNOWLEOPARD +- (BOOL) registerHotKeyWithBlock:(DDHotKeyTask)task keyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags; +#endif + +- (BOOL) hasRegisteredHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags; +- (void) unregisterHotKeysWithTarget:(id)target; +- (void) unregisterHotKeyWithTarget:(id)target action:(SEL)action; +- (void) unregisterHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags; + +@end diff --git a/DDHotKeyCenter.m b/DDHotKeyCenter.m new file mode 100644 index 0000000..8cd465e --- /dev/null +++ b/DDHotKeyCenter.m @@ -0,0 +1,246 @@ +// +// DDHotKeyManager.m +// EmptyAppKit +// +// Created by Dave DeLong on 2/20/10. +// Copyright 2010 Home. All rights reserved. +// + +#import "DDHotKeyCenter.h" +#import <Carbon/Carbon.h> + +#pragma mark Private Global Declarations + +static NSMutableSet * _registeredHotKeys = nil; +static UInt32 _nextHotKeyID = 1; +OSStatus dd_hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void * userData); +NSUInteger dd_translateModifierFlags(NSUInteger flags); + +#pragma mark DDHotKey + +@interface DDHotKey : NSObject { + id target; + SEL action; + id object; + +#if BUILD_FOR_SNOWLEOPARD + DDHotKeyTask task; +#endif + + unsigned short keyCode; + NSUInteger modifierFlags; + UInt32 hotKeyID; + NSValue * hotKeyRef; +} + +@property (retain) id target; +@property SEL action; +@property (retain) id object; +#if BUILD_FOR_SNOWLEOPARD +@property (copy) DDHotKeyTask task; +#endif +@property unsigned short keyCode; +@property NSUInteger modifierFlags; +@property UInt32 hotKeyID; +@property (retain) NSValue * hotKeyRef; + +- (void) invokeWithEvent:(NSEvent *)event; +- (BOOL) registerHotKey; +- (void) unregisterHotKey; + +@end + +@implementation DDHotKey + +@synthesize target, action, object, task, keyCode, modifierFlags, hotKeyID, hotKeyRef; + +- (void) invokeWithEvent:(NSEvent *)event { + if (target != nil && action != nil && [target respondsToSelector:action]) { + [target performSelector:action withObject:event withObject:object]; + } +#if BUILD_FOR_SNOWLEOPARD + else if (task != nil) { + task(event); + } +#endif +} + +- (NSString *) actionString { + return NSStringFromSelector(action); +} + +- (BOOL) registerHotKey { + EventHotKeyID keyID; + keyID.signature = 'htk1'; + keyID.id = _nextHotKeyID; + + EventHotKeyRef carbonHotKey; + OSStatus err = RegisterEventHotKey(keyCode, modifierFlags, 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; +} + +- (void) unregisterHotKey { + EventHotKeyRef carbonHotKey = (EventHotKeyRef)[hotKeyRef pointerValue]; + UnregisterEventHotKey(carbonHotKey); + [self setHotKeyRef:nil]; +} + +- (void) dealloc { + [target release], target = nil; + [object release], object = nil; + if (hotKeyRef != nil) { + [self unregisterHotKey]; + [hotKeyRef release], hotKeyRef = nil; + } + [super dealloc]; +} + +@end + +#pragma mark DDHotKeyCenter + +@implementation DDHotKeyCenter + ++ (void) initialize { + if (_registeredHotKeys == nil) { + _registeredHotKeys = [[NSMutableSet alloc] init]; + _nextHotKeyID = 1; + EventTypeSpec eventSpec; + eventSpec.eventClass = kEventClassKeyboard; + eventSpec.eventKind = kEventHotKeyReleased; + InstallApplicationEventHandler(&dd_hotKeyHandler, 1, &eventSpec, NULL, NULL); + } +} + +- (NSSet *) hotKeysMatchingPredicate:(NSPredicate *)predicate { + return [_registeredHotKeys filteredSetUsingPredicate:predicate]; +} + +- (BOOL) hasRegisteredHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags { + NSPredicate * predicate = [NSPredicate predicateWithFormat:@"keyCode = %hu AND modifierFlags = %lu", keyCode, dd_translateModifierFlags(flags)]; + return ([[self hotKeysMatchingPredicate:predicate] count] > 0); +} + +#if BUILD_FOR_SNOWLEOPARD +- (BOOL) registerHotKeyWithBlock:(DDHotKeyTask)task keyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags { + //we can't add a new hotkey if something already has this combo + if ([self hasRegisteredHotKeyWithKeyCode:keyCode modifierFlags:flags]) { return NO; } + + //translate the flags + NSUInteger modifierFlags = dd_translateModifierFlags(flags); + + DDHotKey * newHotKey = [[DDHotKey alloc] init]; + [newHotKey setTask:task]; + [newHotKey setKeyCode:keyCode]; + [newHotKey setModifierFlags:modifierFlags]; + + BOOL success = [newHotKey registerHotKey]; + if (success) { + [_registeredHotKeys addObject:newHotKey]; + } + + [newHotKey release]; + return success; +} +#endif + +- (BOOL) registerHotKeyWithTarget:(id)target action:(SEL)action object:(id)object keyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags { + //we can't add a new hotkey if something already has this combo + if ([self hasRegisteredHotKeyWithKeyCode:keyCode modifierFlags:flags]) { return NO; } + + //translate the flags + NSUInteger modifierFlags = dd_translateModifierFlags(flags); + + //build the hotkey object: + DDHotKey * newHotKey = [[DDHotKey alloc] init]; + [newHotKey setTarget:target]; + [newHotKey setAction:action]; + [newHotKey setObject:object]; + [newHotKey setKeyCode:keyCode]; + [newHotKey setModifierFlags:modifierFlags]; + + BOOL success = [newHotKey registerHotKey]; + if (success) { + [_registeredHotKeys addObject:newHotKey]; + } + + [newHotKey release]; + return success; +} + +- (void) unregisterHotKeysMatchingPredicate:(NSPredicate *)predicate { + //when a DDHotKey is deallocated, it unregisters itself + //-hotKeysMatchingPredicate returns an autoreleased set + //so we force sooner deallocation (and unregistering) with a explicit pool + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + NSSet * matches = [self hotKeysMatchingPredicate:predicate]; + [_registeredHotKeys minusSet:matches]; + [pool release]; +} + +- (void) unregisterHotKeysWithTarget:(id)target { + NSPredicate * predicate = [NSPredicate predicateWithFormat:@"target = %@", target]; + [self unregisterHotKeysMatchingPredicate:predicate]; +} + +- (void) unregisterHotKeyWithTarget:(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, dd_translateModifierFlags(flags)]; + [self unregisterHotKeysMatchingPredicate:predicate]; +} + +@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; +} + +NSUInteger dd_translateModifierFlags(NSUInteger flags) { + NSUInteger 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; +}
\ No newline at end of file |
