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 | 
