aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MASShortcut+UserDefaults.h1
-rw-r--r--MASShortcut+UserDefaults.m100
-rw-r--r--MASShortcut.h3
-rw-r--r--MASShortcut.m155
4 files changed, 172 insertions, 87 deletions
diff --git a/MASShortcut+UserDefaults.h b/MASShortcut+UserDefaults.h
index 1b44885..0c7f14e 100644
--- a/MASShortcut+UserDefaults.h
+++ b/MASShortcut+UserDefaults.h
@@ -5,5 +5,4 @@
+ (void)registerGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler;
+ (void)unregisterGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey;
-
@end
diff --git a/MASShortcut+UserDefaults.m b/MASShortcut+UserDefaults.m
index 28fd633..38d3707 100644
--- a/MASShortcut+UserDefaults.m
+++ b/MASShortcut+UserDefaults.m
@@ -1,23 +1,20 @@
#import "MASShortcut+UserDefaults.h"
-@interface MASShortcutHotKey : NSObject
+@interface MASShortcutUserDefaultsHotKey : NSObject
@property (nonatomic, readonly) NSString *userDefaultsKey;
-@property (nonatomic, readonly, copy) void (^handler)();
-@property (nonatomic, readonly) EventHotKeyRef carbonHotKey;
-@property (nonatomic, readonly) UInt32 carbonHotKeyID;
+@property (nonatomic, copy) void (^handler)();
+@property (nonatomic, weak) id monitor;
- (id)initWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler;
-+ (void)uninstallEventHandler;
-
@end
#pragma mark -
@implementation MASShortcut (UserDefaults)
-+ (NSMutableDictionary *)registeredHotKeys
++ (NSMutableDictionary *)registeredUserDefaultsHotKeys
{
static NSMutableDictionary *shared = nil;
static dispatch_once_t onceToken;
@@ -29,29 +26,25 @@
+ (void)registerGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler;
{
- MASShortcutHotKey *hotKey = [[MASShortcutHotKey alloc] initWithUserDefaultsKey:userDefaultsKey handler:handler];
- [[self registeredHotKeys] setObject:hotKey forKey:userDefaultsKey];
+ MASShortcutUserDefaultsHotKey *hotKey = [[MASShortcutUserDefaultsHotKey alloc] initWithUserDefaultsKey:userDefaultsKey handler:handler];
+ [[self registeredUserDefaultsHotKeys] setObject:hotKey forKey:userDefaultsKey];
}
+ (void)unregisterGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey
{
- NSMutableDictionary *registeredHotKeys = [self registeredHotKeys];
+ NSMutableDictionary *registeredHotKeys = [self registeredUserDefaultsHotKeys];
[registeredHotKeys removeObjectForKey:userDefaultsKey];
- if (registeredHotKeys.count == 0) {
- [MASShortcutHotKey uninstallEventHandler];
- }
}
@end
#pragma mark -
-@implementation MASShortcutHotKey
+@implementation MASShortcutUserDefaultsHotKey
-@synthesize carbonHotKeyID = _carbonHotKeyID;
+@synthesize monitor = _monitor;
@synthesize handler = _handler;
@synthesize userDefaultsKey = _userDefaultsKey;
-@synthesize carbonHotKey = _carbonHotKey;
#pragma mark -
@@ -71,88 +64,23 @@
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSUserDefaultsDidChangeNotification object:[NSUserDefaults standardUserDefaults]];
- [self uninstallExisitingHotKey];
+ [MASShortcut removeGlobalHotkeyMonitor:self.monitor];
}
#pragma mark -
- (void)userDefaultsDidChange:(NSNotification *)note
{
- [self uninstallExisitingHotKey];
+ [MASShortcut removeGlobalHotkeyMonitor:self.monitor];
[self installHotKeyFromUserDefaults];
}
-#pragma mark - Carbon events
-
-static EventHandlerRef sEventHandler = NULL;
-
-+ (BOOL)installCommonEventHandler
-{
- if (sEventHandler == NULL) {
- EventTypeSpec hotKeyPressedSpec = { .eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed };
- OSStatus status = InstallEventHandler(GetEventDispatcherTarget(), CarbonCallback, 1, &hotKeyPressedSpec, NULL, &sEventHandler);
- if (status != noErr) {
- sEventHandler = NULL;
- return NO;
- }
- }
- return YES;
-}
-
-+ (void)uninstallEventHandler
-{
- if (sEventHandler) {
- RemoveEventHandler(sEventHandler);
- sEventHandler = NULL;
- }
-}
-
-#pragma mark -
-
-- (void)uninstallExisitingHotKey
-{
- if (_carbonHotKey) {
- UnregisterEventHotKey(_carbonHotKey);
- _carbonHotKey = NULL;
- }
-}
-
-FourCharCode const kMASShortcutSignature = 'MASS';
-
- (void)installHotKeyFromUserDefaults
{
NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:_userDefaultsKey];
MASShortcut *shortcut = [MASShortcut shortcutWithData:data];
- if ((shortcut == nil) || ![[self class] installCommonEventHandler]) return;
-
- static UInt32 sCarbonHotKeyID = 0;
- _carbonHotKeyID = ++ sCarbonHotKeyID;
- EventHotKeyID hotKeyID = { .signature = kMASShortcutSignature, .id = _carbonHotKeyID };
- if (RegisterEventHotKey(shortcut.carbonKeyCode, shortcut.carbonFlags, hotKeyID, GetEventDispatcherTarget(), kEventHotKeyExclusive, &_carbonHotKey) != noErr) {
- _carbonHotKey = NULL;
- }
-}
-
-static OSStatus CarbonCallback(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
-{
- if (GetEventClass(inEvent) != kEventClassKeyboard) return noErr;
-
- EventHotKeyID hotKeyID;
- OSStatus status = GetEventParameter(inEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(hotKeyID), NULL, &hotKeyID);
- if (status != noErr) return status;
-
- if (hotKeyID.signature != kMASShortcutSignature) return noErr;
-
- [[MASShortcut registeredHotKeys] enumerateKeysAndObjectsUsingBlock:^(id key, MASShortcutHotKey *hotKey, BOOL *stop) {
- if (hotKeyID.id == hotKey.carbonHotKeyID) {
- if (hotKey.handler) {
- hotKey.handler();
- }
- *stop = YES;
- }
- }];
-
- return noErr;
+ if (shortcut == nil) return;
+ self.monitor = [MASShortcut addGlobalHotkeyMonitorWithShortcut:shortcut handler:self.handler];
}
-@end \ No newline at end of file
+@end
diff --git a/MASShortcut.h b/MASShortcut.h
index 3c6c109..a5c8fa9 100644
--- a/MASShortcut.h
+++ b/MASShortcut.h
@@ -51,4 +51,7 @@ enum {
- (BOOL)isTakenError:(NSError **)error;
++ (id)addGlobalHotkeyMonitorWithShortcut:(MASShortcut *)shortcut handler:(void (^)())handler;
++ (void)removeGlobalHotkeyMonitor:(id)monitor;
+
@end
diff --git a/MASShortcut.m b/MASShortcut.m
index ca5a1ae..fd9250b 100644
--- a/MASShortcut.m
+++ b/MASShortcut.m
@@ -3,6 +3,26 @@
NSString *const kMASShortcutKeyCode = @"KeyCode";
NSString *const kMASShortcutModifierFlags = @"ModifierFlags";
+NSMutableDictionary *MASRegisteredHotKeys();
+BOOL InstallCommonEventHandler();
+void UninstallEventHandler();
+void InstallHotkeyWithShortcut(MASShortcut *shortcut, UInt32 *outCarbonHotKeyID, EventHotKeyRef *outCarbonHotKey);
+
+#pragma mark -
+
+@interface MASShortcutHotKey : NSObject
+
+@property (nonatomic, readonly) MASShortcut *shortcut;
+@property (nonatomic, readonly, copy) void (^handler)();
+@property (nonatomic, readonly) EventHotKeyRef carbonHotKey;
+@property (nonatomic, readonly) UInt32 carbonHotKeyID;
+
+- (id)initWithShortcut:(MASShortcut *)shortcut handler:(void (^)())handler;
+
+@end
+
+#pragma mark -
+
@implementation MASShortcut {
NSUInteger _keyCode; // NSNotFound if empty
NSUInteger _modifierFlags; // 0 if empty
@@ -305,4 +325,139 @@ NSString *const kMASShortcutModifierFlags = @"ModifierFlags";
return [self isKeyEquivalent:self.keyCodeStringForKeyEquivalent flags:self.modifierFlags takenInMenu:[NSApp mainMenu] error:outError];
}
+#pragma mark - Global monitoring
+
++ (id)addGlobalHotkeyMonitorWithShortcut:(MASShortcut *)shortcut handler:(void (^)())handler
+{
+ NSString *monitor = [NSString stringWithFormat:@"%p: %@", shortcut, shortcut.description];
+ MASShortcutHotKey *hotKey = [[MASShortcutHotKey alloc] initWithShortcut:shortcut handler:handler];
+ [MASRegisteredHotKeys() setObject:hotKey forKey:monitor];
+ return monitor;
+}
+
++ (void)removeGlobalHotkeyMonitor:(id)monitor
+{
+ if (monitor == nil) return;
+ NSMutableDictionary *registeredHotKeys = MASRegisteredHotKeys();
+ [registeredHotKeys removeObjectForKey:monitor];
+ if (registeredHotKeys.count == 0) {
+ UninstallEventHandler();
+ }
+}
+
+@end
+
+#pragma mark -
+
+@implementation MASShortcutHotKey
+
+@synthesize carbonHotKeyID = _carbonHotKeyID;
+@synthesize handler = _handler;
+@synthesize shortcut = _shortcut;
+@synthesize carbonHotKey = _carbonHotKey;
+
+#pragma mark -
+
+- (id)initWithShortcut:(MASShortcut *)shortcut handler:(void (^)())handler
+{
+ self = [super init];
+ if (self) {
+ _shortcut = shortcut;
+ _handler = [handler copy];
+ InstallHotkeyWithShortcut(shortcut, &_carbonHotKeyID, &_carbonHotKey);
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [self uninstallExisitingHotKey];
+}
+
+- (void)uninstallExisitingHotKey
+{
+ if (_carbonHotKey) {
+ UnregisterEventHotKey(_carbonHotKey);
+ _carbonHotKey = NULL;
+ }
+}
+
@end
+
+#pragma mark - Carbon magic
+
+NSMutableDictionary *MASRegisteredHotKeys()
+{
+ static NSMutableDictionary *shared = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ shared = [NSMutableDictionary dictionary];
+ });
+ return shared;
+}
+
+#pragma mark -
+
+FourCharCode const kMASShortcutSignature = 'MASS';
+
+void InstallHotkeyWithShortcut(MASShortcut *shortcut, UInt32 *outCarbonHotKeyID, EventHotKeyRef *outCarbonHotKey)
+{
+ if ((shortcut == nil) || !InstallCommonEventHandler()) return;
+
+ static UInt32 sCarbonHotKeyID = 0;
+ EventHotKeyID hotKeyID = { .signature = kMASShortcutSignature, .id = ++ sCarbonHotKeyID };
+ EventHotKeyRef carbonHotKey = NULL;
+ if (RegisterEventHotKey(shortcut.carbonKeyCode, shortcut.carbonFlags, hotKeyID, GetEventDispatcherTarget(), kEventHotKeyExclusive, &carbonHotKey) != noErr) {
+ carbonHotKey = NULL;
+ }
+
+ if (outCarbonHotKeyID) *outCarbonHotKeyID = hotKeyID.id;
+ if (outCarbonHotKey) *outCarbonHotKey = carbonHotKey;
+}
+
+static OSStatus CarbonCallback(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
+{
+ if (GetEventClass(inEvent) != kEventClassKeyboard) return noErr;
+
+ EventHotKeyID hotKeyID;
+ OSStatus status = GetEventParameter(inEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(hotKeyID), NULL, &hotKeyID);
+ if (status != noErr) return status;
+
+ if (hotKeyID.signature != kMASShortcutSignature) return noErr;
+
+ [MASRegisteredHotKeys() enumerateKeysAndObjectsUsingBlock:^(id key, MASShortcutHotKey *hotKey, BOOL *stop) {
+ if (hotKeyID.id == hotKey.carbonHotKeyID) {
+ if (hotKey.handler) {
+ hotKey.handler();
+ }
+ *stop = YES;
+ }
+ }];
+
+ return noErr;
+}
+
+#pragma mark -
+
+static EventHandlerRef sEventHandler = NULL;
+
+BOOL InstallCommonEventHandler()
+{
+ if (sEventHandler == NULL) {
+ EventTypeSpec hotKeyPressedSpec = { .eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed };
+ OSStatus status = InstallEventHandler(GetEventDispatcherTarget(), CarbonCallback, 1, &hotKeyPressedSpec, NULL, &sEventHandler);
+ if (status != noErr) {
+ sEventHandler = NULL;
+ return NO;
+ }
+ }
+ return YES;
+}
+
+void UninstallEventHandler()
+{
+ if (sEventHandler) {
+ RemoveEventHandler(sEventHandler);
+ sEventHandler = NULL;
+ }
+}