summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DDHotKey.xcodeproj/project.pbxproj10
-rw-r--r--DDHotKeyAppDelegate.m26
-rw-r--r--DDHotKeyCenter.h32
-rw-r--r--DDHotKeyCenter.m246
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