aboutsummaryrefslogtreecommitdiffstats
path: root/MASShortcut+UserDefaults.m
diff options
context:
space:
mode:
Diffstat (limited to 'MASShortcut+UserDefaults.m')
-rw-r--r--MASShortcut+UserDefaults.m151
1 files changed, 151 insertions, 0 deletions
diff --git a/MASShortcut+UserDefaults.m b/MASShortcut+UserDefaults.m
new file mode 100644
index 0000000..af3b5af
--- /dev/null
+++ b/MASShortcut+UserDefaults.m
@@ -0,0 +1,151 @@
+#import "MASShortcut+UserDefaults.h"
+
+@interface MASShortcutHotKey : NSObject
+
+@property (nonatomic, readonly) NSString *userDefaultsKey;
+@property (nonatomic, readonly) void (^handler)();
+@property (nonatomic, readonly) EventHotKeyRef carbonHotKey;
+@property (nonatomic, readonly) UInt32 carbonHotKeyID;
+
+- (id)initWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler;
+
++ (void)uninstallEventHandler;
+
+@end
+
+#pragma mark -
+
+@implementation MASShortcut (UserDefaults)
+
++ (NSMutableDictionary *)registeredHotKeys
+{
+ static NSMutableDictionary *shared = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ shared = [NSMutableDictionary dictionary];
+ });
+ return shared;
+}
+
++ (void)registerGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler;
+{
+ MASShortcutHotKey *hotKey = [[MASShortcutHotKey alloc] initWithUserDefaultsKey:userDefaultsKey handler:handler];
+ [[self registeredHotKeys] setObject:hotKey forKey:userDefaultsKey];
+}
+
++ (void)unregisterGlobalShortcutWithUserDefaultsKey:(NSString *)userDefaultsKey
+{
+ NSMutableDictionary *registeredHotKeys = [self registeredHotKeys];
+ [registeredHotKeys removeObjectForKey:userDefaultsKey];
+ if (registeredHotKeys.count == 0) {
+ [MASShortcutHotKey uninstallEventHandler];
+ }
+}
+
+@end
+
+#pragma mark -
+
+@implementation MASShortcutHotKey
+
+- (id)initWithUserDefaultsKey:(NSString *)userDefaultsKey handler:(void (^)())handler
+{
+ self = [super init];
+ if (self) {
+ _userDefaultsKey = userDefaultsKey.copy;
+ _handler = [handler copy];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:)
+ name:NSUserDefaultsDidChangeNotification object:[NSUserDefaults standardUserDefaults]];
+ [self installHotKeyFromUserDefaults];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:NSUserDefaultsDidChangeNotification object:[NSUserDefaults standardUserDefaults]];
+ [self uninstallExisitingHotKey];
+}
+
+#pragma mark -
+
+- (void)userDefaultsDidChange:(NSNotification *)note
+{
+ [self uninstallExisitingHotKey];
+ [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;
+}
+
+@end \ No newline at end of file