/core/externals/google-toolbox-for-mac/AppKit/GTMCarbonEvent.m
Objective C | 764 lines | 394 code | 98 blank | 272 comment | 50 complexity | be9a9dacce6952e86ad3d7ea234f6bb8 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, GPL-2.0
- //
- // GTMCarbonEvent.m
- //
- // Copyright 2006-2008 Google Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License"); you may not
- // use this file except in compliance with the License. You may obtain a copy
- // of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- // License for the specific language governing permissions and limitations under
- // the License.
- //
- #import "GTMCarbonEvent.h"
- #import <AppKit/AppKit.h>
- #import "GTMDebugSelectorValidation.h"
- #import "GTMTypeCasting.h"
- // Wrapper for all the info we need about a hotkey that we can store in a
- // Foundation storage class.
- @interface GTMCarbonHotKey (GTMCarbonHotKeyPrivate)
- // Create a HotKey record
- // Arguments:
- // keyID - id of the hotkey
- // target - object we are going to call when the hotkey is hit
- // action - selector we are going to call on target
- // userInfo - user storage
- // whenPressed - do we do it on key down or key up?
- // Returns:
- // a hotkey record, or nil on failure
- - (id)initWithHotKey:(EventHotKeyID)keyID
- target:(id)target
- action:(SEL)selector
- userInfo:(id)userInfo
- whenPressed:(BOOL)onKeyDown;
- // Does this record match key |keyID|
- // Arguments:
- // keyID - the id to match against
- // Returns:
- // Yes if we match this key id
- - (BOOL)matchesHotKeyID:(EventHotKeyID)keyID;
- // Make target perform selector
- // Returns:
- // Yes if handled
- - (BOOL)sendAction;
- - (void)setHotKeyRef:(EventHotKeyRef)ref;
- @end
- @implementation GTMCarbonEvent
- // Create an event of class |inClass| and kind |inKind|
- //
- // Returns:
- // Autoreleased GTMCarbonEvent
- //
- + (id)eventWithClass:(UInt32)inClass kind:(UInt32)kind {
- return [[[self alloc] initWithClass:inClass kind:kind] autorelease];
- }
- // Create an event based on |event|. Retains |event|.
- //
- // Returns:
- // Autoreleased GTMCarbonEvent
- //
- + (id)eventWithEvent:(EventRef)event {
- return [[[self alloc] initWithEvent:event] autorelease];
- }
- // Create an event based on the event currently being handled.
- //
- // Returns:
- // Autoreleased GTMCarbonEvent
- //
- + (id)currentEvent {
- return [self eventWithEvent:GetCurrentEvent()];
- }
- // Create an event of class |inClass| and kind |inKind|
- //
- // Returns:
- // GTMCarbonEvent
- //
- - (id)initWithClass:(UInt32)inClass kind:(UInt32)kind {
- if ((self = [super init])) {
- verify_noerr(CreateEvent(kCFAllocatorDefault, inClass, kind,
- 0, kEventAttributeNone, &event_));
- }
- return self;
- }
- // Create an event based on |event|. Retains |event|.
- //
- // Returns:
- // GTMCarbonEvent
- //
- - (id)initWithEvent:(EventRef)event {
- if ((self = [super init])) {
- if (event) {
- event_ = RetainEvent(event);
- }
- }
- return self;
- }
- // This does a proper event copy, but ignores the |zone|. No way to do a copy
- // of an event into a specific zone.
- //
- // Arguments:
- // zone - the zone to copy to
- // Returns:
- // the copied event. nil on failure
- - (id)copyWithZone:(NSZone *)zone {
- GTMCarbonEvent *carbonEvent = nil;
- EventRef newEvent = CopyEvent([self event]);
- if (newEvent) {
- carbonEvent = [[[self class] allocWithZone:zone] initWithEvent:newEvent];
- ReleaseEvent(newEvent);
- }
- return carbonEvent;
- }
- // releases our retained event
- //
- - (void)dealloc {
- if (event_) {
- ReleaseEvent(event_);
- event_ = NULL;
- }
- [super dealloc];
- }
- // description utliity for debugging
- //
- - (NSString *)description {
- // Use 8 bytes because stack protection gives us a warning if we use a
- // smaller buffer.
- char cls[8];
- UInt32 kind;
- // Need everything bigendian if we are printing out the class as a "string"
- *((UInt32 *)cls) = CFSwapInt32HostToBig([self eventClass]);
- kind = [self eventKind];
- cls[4] = 0;
- return [NSString stringWithFormat:@"GTMCarbonEvent '%s' %lu",
- cls, (unsigned long)kind];
- }
- // Get the event's class.
- //
- // Returns:
- // event class
- //
- - (UInt32)eventClass {
- return GetEventClass(event_);
- }
- // Get the event's kind.
- //
- // Returns:
- // event kind
- //
- - (UInt32)eventKind {
- return GetEventKind(event_);
- }
- // Set the event's time.
- //
- // Arguments:
- // time - the time you want associated with the event
- //
- - (void)setTime:(EventTime)eventTime {
- verify_noerr(SetEventTime(event_, eventTime));
- }
- // Get the event's time.
- //
- // Returns:
- // the time associated with the event
- //
- - (EventTime)time {
- return GetEventTime(event_);
- }
- // Get the event's eventref for passing to other carbon functions.
- //
- // Returns:
- // the event ref associated with the event
- //
- - (EventRef)event {
- return event_;
- }
- // Sends event to an event target with options
- //
- // Arguments:
- // target - target to send event to.
- // options - options to send event. See SendEventToEventTargetWithOptions
- // for details.
- //
- // Returns:
- // OSStatus value.
- //
- - (OSStatus)sendToTarget:(GTMCarbonEventHandler *)target
- options:(OptionBits)options {
- return SendEventToEventTargetWithOptions(event_,
- [target eventTarget], options);
- }
- // Post event to an event queue.
- //
- // Arguments:
- // queue - queue to post it to.
- // priority - priority to post it with
- //
- // Returns:
- // OSStatus value.
- //
- - (OSStatus)postToQueue:(EventQueueRef)queue priority:(EventPriority)priority {
- return PostEventToQueue(queue, event_, priority);
- }
- // Post event to current queue with standard priority.
- //
- - (void)postToCurrentQueue {
- verify_noerr([self postToQueue:GetCurrentEventQueue()
- priority:kEventPriorityStandard]);
- }
- // Post event to main queue with standard priority.
- //
- - (void)postToMainQueue {
- verify_noerr([self postToQueue:GetMainEventQueue()
- priority:kEventPriorityStandard]);
- }
- // Sets (or adds) a parameter to an event. Try not to use this function
- // directly. Look at the PARAM_TEMPLATE_DECL/DEFN macros below.
- //
- // Arguments:
- // name - the parameter name.
- // type - the parameter type.
- // size - the size of the data that |data| points to.
- // data - pointer to the data you want to set the parameter to.
- //
- - (void)setParameterNamed:(EventParamName)name
- type:(EventParamType)type
- size:(ByteCount)size
- data:(const void *)data {
- verify_noerr(SetEventParameter(event_, name, type, size, data));
- }
- // Gets a parameter from an event. Try not to use this function
- // directly. Look at the PARAM_TEMPLATE_DECL/DEFN macros below.
- //
- // Arguments:
- // name - the parameter name.
- // type - the parameter type.
- // size - the size of the data that |data| points to.
- // data - pointer to the buffer that you want to fill with your data.
- //
- // Returns:
- // YES is parameter is retrieved successfully. NO if parameter doesn't exist.
- //
- - (BOOL)getParameterNamed:(EventParamName)name
- type:(EventParamType)type
- size:(ByteCount)size
- data:(void *)data {
- OSStatus status = GetEventParameter(event_, name, type,
- NULL, size, NULL, data);
- return status == noErr;
- }
- // Gets a the size of a parameter from an event.
- //
- // Arguments:
- // name - the parameter name.
- // type - the parameter type.
- //
- // Returns:
- // The size of the buffer required to hold the parameter. 0 if parameter
- // doesn't exist.
- //
- - (ByteCount)sizeOfParameterNamed:(EventParamName)name
- type:(EventParamType)type {
- ByteCount size = 0;
- verify_noerr(GetEventParameter(event_, name, type, NULL, 0, &size, NULL));
- return size;
- }
- @end
- @implementation GTMCarbonEvent (GTMCarbonEventGettersAndSetters)
- GTM_PARAM_TEMPLATE_DEFN(UInt32)
- GTM_PARAM_TEMPLATE_DEFN(EventHotKeyID)
- @end
- UInt32 GTMCocoaToCarbonKeyModifiers(NSUInteger inCocoaModifiers) {
- UInt32 carbModifiers = 0;
- if (inCocoaModifiers & NSAlphaShiftKeyMask) carbModifiers |= alphaLock;
- if (inCocoaModifiers & NSShiftKeyMask) carbModifiers |= shiftKey;
- if (inCocoaModifiers & NSControlKeyMask) carbModifiers |= controlKey;
- if (inCocoaModifiers & NSAlternateKeyMask) carbModifiers |= optionKey;
- if (inCocoaModifiers & NSCommandKeyMask) carbModifiers |= cmdKey;
- return carbModifiers;
- }
- NSUInteger GTMCarbonToCocoaKeyModifiers(UInt32 inCarbonModifiers) {
- NSUInteger nsModifiers = 0;
- if (inCarbonModifiers & alphaLock) nsModifiers |= NSAlphaShiftKeyMask;
- if (inCarbonModifiers & shiftKey) nsModifiers |= NSShiftKeyMask;
- if (inCarbonModifiers & controlKey) nsModifiers |= NSControlKeyMask;
- if (inCarbonModifiers & optionKey) nsModifiers |= NSAlternateKeyMask;
- if (inCarbonModifiers & cmdKey) nsModifiers |= NSCommandKeyMask;
- return nsModifiers;
- }
- const OSType kGTMCarbonFrameworkSignature = 'GTM ';
- @implementation GTMCarbonEventHandler
- // Does our delegate respond to eventHandler:receivedEvent:handler:
- //
- // Returns:
- // YES if delegate responds to eventHandler:receivedEvent:handler:
- - (BOOL) delegateRespondsToHandleEvent {
- return delegateRespondsToHandleEvent_;
- }
- // Registers the event handler to listen for |events|.
- //
- // Arguments:
- // events - an array of EventTypeSpec. The events to register for.
- // count - the number of EventTypeSpecs in events.
- //
- - (void)registerForEvents:(const EventTypeSpec *)events count:(size_t)count {
- verify_noerr(AddEventTypesToHandler([self eventHandler], count, events));
- }
- // Causes the event handler to stop listening for |events|.
- //
- // Arguments:
- // events - an array of EventTypeSpec. The events to register for.
- // count - the number of EventTypeSpecs in events.
- //
- - (void)unregisterForEvents:(const EventTypeSpec *)events count:(size_t)count {
- verify_noerr(RemoveEventTypesFromHandler([self eventHandler], count, events));
- }
- // To be overridden by subclasses to respond to events. All subclasses should
- // call [super handleEvent:handler:] if they don't handle the event themselves.
- //
- // Arguments:
- // event - the event to be handled
- // handler - the call ref in case you want to call CallNextEventHandler
- // in your method
- // Returns:
- // OSStatus - usually either noErr or eventNotHandledErr
- //
- - (OSStatus)handleEvent:(GTMCarbonEvent *)event
- handler:(EventHandlerCallRef)handler {
- OSStatus status = eventNotHandledErr;
- require(event, CantUseParams);
- require(handler, CantUseParams);
- require([event event], CantUseParams);
- status = CallNextEventHandler(handler, [event event]);
- CantUseParams:
- return status;
- }
- // To be overridden by subclasses to return the event target for the class.
- // GTMCarbonEventHandler's implementation returns NULL.
- //
- // Returns:
- // The event target ref.
- //
- - (EventTargetRef)eventTarget {
- // Defaults implementation needs to be overridden
- return NULL;
- }
- // C callback for our registered EventHandlerUPP.
- //
- // Arguments:
- // inHandler - handler given to us from Carbon Event system
- // inEvent - the event we are handling
- // inUserData - refcon that we gave to the carbon event system. Is a
- // GTMCarbonEventHandler in disguise.
- // Returns:
- // status of event handler
- //
- static OSStatus EventHandler(EventHandlerCallRef inHandler,
- EventRef inEvent,
- void *inUserData) {
- GTMCarbonEvent *event = [GTMCarbonEvent eventWithEvent:inEvent];
- GTMCarbonEventHandler *handler
- = GTM_STATIC_CAST(GTMCarbonEventHandler, inUserData);
- // First check to see if our delegate cares about this event. If the delegate
- // handles it (i.e responds to it and does not return eventNotHandledErr) we
- // do not pass it on to default handling.
- OSStatus status = eventNotHandledErr;
- if ([handler delegateRespondsToHandleEvent]) {
- status = [[handler delegate] gtm_eventHandler:handler
- receivedEvent:event
- handler:inHandler];
- }
- if (status == eventNotHandledErr) {
- status = [handler handleEvent:event handler:inHandler];
- }
- return status;
- }
- // Gets the underlying EventHandlerRef for that this class wraps.
- //
- // Returns:
- // The EventHandlerRef this class wraps.
- //
- - (EventHandlerRef)eventHandler {
- if (!eventHandler_) {
- static EventHandlerUPP sHandlerProc = NULL;
- if ( sHandlerProc == NULL ) {
- sHandlerProc = NewEventHandlerUPP(EventHandler);
- }
- verify_noerr(InstallEventHandler([self eventTarget],
- sHandlerProc, 0,
- NULL, self, &eventHandler_));
- }
- return eventHandler_;
- }
- // Gets the delegate for the handler
- //
- // Returns:
- // the delegate
- - (id)delegate {
- return delegate_;
- }
- // Sets the delegate for the handler and caches whether it responds to
- // the eventHandler:receivedEvent:handler: selector for performance purposes.
- //
- // Arguments:
- // delegate - the delegate for the handler
- - (void)setDelegate:(id)delegate {
- delegate_ = delegate;
- SEL selector = @selector(gtm_eventHandler:receivedEvent:handler:);
- delegateRespondsToHandleEvent_ = [delegate respondsToSelector:selector];
- }
- @end
- @implementation GTMCarbonEventMonitorHandler
- + (GTMCarbonEventMonitorHandler *)sharedEventMonitorHandler {
- static GTMCarbonEventMonitorHandler *obj = nil;
- if (!obj) {
- obj = [[self alloc] init];
- }
- return obj;
- }
- - (EventTargetRef)eventTarget {
- return GetEventMonitorTarget();
- }
- @end
- #if (MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5)
- // Accidentally marked as !LP64 in the 10.5sdk, it's back in the 10.6 sdk.
- // If you remove this decl, please remove it from GTMCarbonEventTest.m as well.
- extern EventTargetRef GetApplicationEventTarget(void);
- #endif // (MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5)
- @implementation GTMCarbonEventApplicationEventHandler
- + (GTMCarbonEventApplicationEventHandler *)sharedApplicationEventHandler {
- static GTMCarbonEventApplicationEventHandler *obj = nil;
- if (!obj) {
- obj = [[self alloc] init];
- }
- return obj;
- }
- - (EventTargetRef)eventTarget {
- return GetApplicationEventTarget();
- }
- @end
- @implementation GTMCarbonEventDispatcherHandler
- + (GTMCarbonEventDispatcherHandler *)sharedEventDispatcherHandler {
- static GTMCarbonEventDispatcherHandler *obj = nil;
- if (!obj) {
- obj = [[self alloc] init];
- }
- return obj;
- }
- // Register for the events we handle, and set up the dictionaries we need
- // to keep track of the hotkeys and commands that we handle.
- // Returns:
- // GTMCarbonApplication or nil on failure
- - (id)init {
- if ((self = [super init])) {
- static EventTypeSpec events[] = {
- { kEventClassKeyboard, kEventHotKeyPressed },
- { kEventClassKeyboard, kEventHotKeyReleased },
- };
- [self registerForEvents:events count:GetEventTypeCount(events)];
- hotkeys_ = [[NSMutableArray alloc] initWithCapacity:0];
- }
- return self;
- }
- // COV_NF_START
- // Singleton, we never get released. Just here for completeness.
- - (void)dealloc {
- [hotkeys_ release];
- [super dealloc];
- }
- // COV_NF_END
- - (EventTargetRef)eventTarget {
- return GetEventDispatcherTarget();
- }
- // Registers a hotkey. When the hotkey is executed by the user, target will be
- // called with selector.
- // Arguments:
- // keyCode - the virtual keycode of the hotkey
- // cocoaModifiers - the modifiers that need to be used with |keyCode|. NB
- // that these are cocoa modifiers, so NSCommandKeyMask etc.
- // target - instance that will get |action| called when the hotkey fires
- // action - the method to call on |target| when the hotkey fires
- // action should have the signature - (void)handler:(GTMCarbonEventDispatcherHandler *)handler
- // userInfo - user storage
- // onKeyDown - is YES, the hotkey fires on the keydown (usual) otherwise
- // it fires on the key up.
- // Returns:
- // a EventHotKeyRef that you can use with other Carbon functions, or for
- // unregistering the hotkey. Note that all hotkeys are unregistered
- // automatically when an app quits. Will be NULL on failure.
- - (GTMCarbonHotKey *)registerHotKey:(NSUInteger)keyCode
- modifiers:(NSUInteger)cocoaModifiers
- target:(id)target
- action:(SEL)selector
- userInfo:(id)userInfo
- whenPressed:(BOOL)onKeyDown {
- static UInt32 sCurrentID = 0;
- GTMCarbonHotKey *newKey = nil;
- EventHotKeyRef theRef = NULL;
- EventHotKeyID keyID;
- keyID.signature = kGTMCarbonFrameworkSignature;
- keyID.id = ++sCurrentID;
- newKey = [[[GTMCarbonHotKey alloc] initWithHotKey:keyID
- target:target
- action:selector
- userInfo:userInfo
- whenPressed:onKeyDown] autorelease];
- require(newKey, CantCreateKey);
- require_noerr_action(RegisterEventHotKey((UInt32)keyCode,
- GTMCocoaToCarbonKeyModifiers(cocoaModifiers),
- keyID,
- [self eventTarget],
- 0,
- &theRef),
- CantRegisterHotKey, newKey = nil);
- [newKey setHotKeyRef:theRef];
- [hotkeys_ addObject:newKey];
- CantRegisterHotKey:
- CantCreateKey:
- return newKey;
- }
- // Unregisters a hotkey previously registered with registerHotKey.
- // Arguments:
- // keyRef - the EventHotKeyRef to unregister
- - (void)unregisterHotKey:(GTMCarbonHotKey *)keyRef {
- check([hotkeys_ containsObject:keyRef]);
- [[keyRef retain] autorelease];
- [hotkeys_ removeObject:keyRef];
- verify_noerr(UnregisterEventHotKey([keyRef hotKeyRef]));
- }
- // A hotkey has been hit. See if it is one of ours, and if so fire it.
- // Arguments:
- // event - the hotkey even that was received
- // Returns:
- // Yes if handled.
- - (BOOL)handleHotKeyEvent:(GTMCarbonEvent *)event {
- EventHotKeyID keyID;
- BOOL handled = [event getEventHotKeyIDParameterNamed:kEventParamDirectObject
- data:&keyID];
- if (handled) {
- GTMCarbonHotKey *hotkey;
- GTM_FOREACH_OBJECT(hotkey, hotkeys_) {
- if ([hotkey matchesHotKeyID:keyID]) {
- EventKind kind = [event eventKind];
- BOOL onKeyDown = [hotkey onKeyDown];
- if ((kind == kEventHotKeyPressed && onKeyDown) ||
- (kind == kEventHotKeyReleased && !onKeyDown)) {
- handled = [hotkey sendAction];
- }
- break;
- }
- }
- }
- return handled;
- }
- // Currently we handle hotkey and command events here. If we get one of them
- // we dispatch them off to the handlers above. Otherwise we just call up to
- // super.
- // Arguments:
- // event - the event to check
- // handler - the handler call ref
- // Returns:
- // OSStatus
- - (OSStatus)handleEvent:(GTMCarbonEvent *)event
- handler:(EventHandlerCallRef)handler {
- OSStatus theStatus = eventNotHandledErr;
- if ([event eventClass] == kEventClassKeyboard) {
- EventKind kind = [event eventKind];
- if (kind == kEventHotKeyPressed || kind == kEventHotKeyReleased) {
- theStatus = [self handleHotKeyEvent:event] ? noErr : eventNotHandledErr;
- }
- }
- // We didn't handle it, maybe somebody upstairs will.
- if (theStatus == eventNotHandledErr) {
- theStatus = [super handleEvent:event handler:handler];
- }
- return theStatus;
- }
- @end
- @implementation GTMCarbonHotKey
- // Init a HotKey record. In debug version make sure that the selector we are
- // passed matches what we expect. (
- // Arguments:
- // keyID - id of the hotkey
- // reference - hotkey reference
- // target - object we are going to call when the hotkey is hit
- // action - selector we are going to call on target
- // userinfo - info for user
- // whenPressed - do we do it on key down or key up?
- // Returns:
- // a hotkey record, or nil on failure
- - (id)initWithHotKey:(EventHotKeyID)keyID
- target:(id)target
- action:(SEL)selector
- userInfo:(id)userInfo
- whenPressed:(BOOL)onKeyDown {
- if ((self = [super init])) {
- if(!target || !selector) {
- [self release];
- return nil;
- }
- id_ = keyID;
- target_ = [target retain];
- userInfo_ = [userInfo retain];
- selector_ = selector;
- onKeyDown_ = onKeyDown;
- GTMAssertSelectorNilOrImplementedWithReturnTypeAndArguments(target,
- selector,
- @encode(void),
- @encode(id),
- NULL);
- }
- return self;
- }
- - (void)dealloc {
- [target_ release];
- [userInfo_ release];
- [super dealloc];
- }
- - (NSUInteger)hash {
- return (NSUInteger)hotKeyRef_;
- }
- - (BOOL)isEqual:(id)object {
- return [object isMemberOfClass:[self class]]
- && (hotKeyRef_ == [object hotKeyRef]);
- }
- // Does this record match key |keyID|
- // Arguments:
- // keyID - the id to match against
- // Returns:
- // Yes if we match this key id
- - (BOOL)matchesHotKeyID:(EventHotKeyID)keyID {
- return (id_.signature == keyID.signature) && (id_.id == keyID.id);
- }
- - (BOOL)sendAction {
- BOOL handled = NO;
- @try {
- [target_ performSelector:selector_ withObject:self];
- handled = YES;
- }
- @catch (NSException * e) {
- handled = NO;
- _GTMDevLog(@"Exception fired in hotkey: %@ (%@)", [e name], [e reason]);
- } // COV_NF_LINE
- return handled;
- }
- - (BOOL)onKeyDown {
- return onKeyDown_;
- }
- - (id)userInfo {
- return userInfo_;
- }
- - (EventHotKeyRef)hotKeyRef {
- return hotKeyRef_;
- }
- - (void)setHotKeyRef:(EventHotKeyRef)ref {
- hotKeyRef_ = ref;
- }
- - (NSString *)description {
- return [NSString stringWithFormat:@"<%@ %p> - ref %p signature %lu id %lu",
- [self class], self, hotKeyRef_,
- (unsigned long)id_.signature, (unsigned long)id_.id];
- }
- @end