// Created by Justin Williams on 7/26/09. // Copyright 2009 Second Gear. All rights reserved. #import <Carbon/Carbon.h> #import "SGHotKeyCenter.h" OSType const kHotKeySignature = 'SGHk'; @interface SGHotKeyCenter () - (SGHotKey *)_hotKeyForCarbonHotKey:(EventHotKeyRef)carbonHotKey; - (EventHotKeyRef)_carbonHotKeyForHotKey:(SGHotKey *)hotKey; - (void)_updateEventHandler; - (void)_hotKeyDown: (SGHotKey *)hotKey; - (void)_hotKeyUp: (SGHotKey *)hotKey; static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* refCon ); static SGHotKeyCenter *sharedCenter = nil; @implementation SGHotKeyCenter sharedCenter = [[self alloc] init]; + (SGHotKeyCenter *)sharedCenter { + (id) allocWithZone:(NSZone *)zone { //Usually already set by +initialize. //The caller expects to receive a new object, so implicitly retain it to balance out the caller's eventual release message. return [sharedCenter retain]; //When not already set, +initialize is our caller–it's creating the shared instance. Let this go through. return [super allocWithZone:zone]; if ((self = [super init])) { //Initialize the instance here. hotKeys = [[NSMutableDictionary alloc] init]; - (BOOL)registerHotKey:(SGHotKey *)theHotKey { EventHotKeyRef carbonHotKey; if ([[self allHotKeys] containsObject:theHotKey]) [self unregisterHotKey:theHotKey]; if (![[theHotKey keyCombo] isValidHotKeyCombo]) static UInt32 currentId = 0; hotKeyID.signature = kHotKeySignature; hotKeyID.id = ++currentId; theHotKey.hotKeyID = hotKeyID; error = RegisterEventHotKey(theHotKey.keyCombo.keyCode, (UInt32)theHotKey.keyCombo.modifiers, GetEventDispatcherTarget(), key = [NSValue valueWithPointer:carbonHotKey]; [hotKeys setObject:theHotKey forKey:key]; [self _updateEventHandler]; - (void)unregisterHotKey:(SGHotKey *)theHotKey { EventHotKeyRef carbonHotKey; if (![[self allHotKeys] containsObject:theHotKey]) carbonHotKey = [self _carbonHotKeyForHotKey:theHotKey]; NSAssert(carbonHotKey != nil, @""); UnregisterEventHotKey(carbonHotKey); key = [NSValue valueWithPointer:carbonHotKey]; [hotKeys removeObjectForKey:key]; [self _updateEventHandler]; - (NSArray *)allHotKeys { return [hotKeys allValues]; - (SGHotKey *)hotKeyWithIdentifier:(id)theIdentifier { for (SGHotKey *hotKey in [self allHotKeys]) { if([[hotKey identifier] isEqual:theIdentifier] ) - (OSStatus)sendCarbonEvent:(EventRef)event { NSAssert(GetEventClass(event) == kEventClassKeyboard, @"Unknown event class"); error = GetEventParameter(event, NSAssert(hotKeyID.signature == kHotKeySignature, @"Invalid hot key id"); NSAssert(hotKeyID.id != 0, @"Invalid hot key id"); for (SGHotKey *thisHotKey in [self allHotKeys]) { if ([thisHotKey matchesHotKeyID:hotKeyID]) { switch (GetEventKind(event)) { case kEventHotKeyPressed: [self _hotKeyDown:hotKey]; case kEventHotKeyReleased: NSAssert(0, @"Unknown event kind"); - (SGHotKey *)_hotKeyForCarbonHotKey:(EventHotKeyRef)carbonHotKey { NSValue *key = [NSValue valueWithPointer:carbonHotKey]; return [hotKeys objectForKey:key]; - (EventHotKeyRef)_carbonHotKeyForHotKey:(SGHotKey *)hotKey { values = [hotKeys allKeysForObject:hotKey]; NSAssert([values count] == 1, @"Failed to find Carbon Hotkey for SGHotKey"); value = [values lastObject]; return (EventHotKeyRef)[value pointerValue]; - (void)_updateEventHandler { if ([hotKeys count] && eventHandlerInstalled == NO) { EventTypeSpec eventSpec[2] = { { kEventClassKeyboard, kEventHotKeyPressed }, { kEventClassKeyboard, kEventHotKeyReleased } InstallEventHandler(GetEventDispatcherTarget(), (EventHandlerProcPtr)hotKeyEventHandler, eventHandlerInstalled = YES; - (void)_hotKeyDown:(SGHotKey *)hotKey { - (void)_hotKeyUp:(SGHotKey *)hotKey { static OSStatus hotKeyEventHandler(EventHandlerCallRef theHandlerRef, EventRef theEvent, void *userData ) { return [[SGHotKeyCenter sharedCenter] sendCarbonEvent:theEvent];