Mercurial > osx-quasimode
view QuasimodalEventTap.m @ 30:9aa3c2fd0baf
Quasimode app now terminates on window.close().
author | Atul Varma <avarma@mozilla.com> |
---|---|
date | Mon, 12 Apr 2010 11:42:22 -0700 |
parents | 284fe09c6e64 |
children |
line wrap: on
line source
#include <AppKit/NSWorkspace.h> #import <Foundation/NSAutoreleasePool.h> #import <Foundation/NSString.h> #import <Foundation/NSArray.h> #import <Foundation/NSDictionary.h> #import "QuasimodalEventTap.h" #define MAX_STR_LEN 10 #ifdef DEBUG #define DEBUG_MSG(msg) printf(msg); #else #define DEBUG_MSG(msg) #endif static NSNumber *isPressed(CGEventFlags flags, CGEventFlags filter) { return [NSNumber numberWithBool: ((flags & filter) != 0)]; } static CGEventRef eventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon); @implementation QuasimodalEventTap - (void)sendSomeKeyEvent { [center postNotificationName: @"NonQuasimodalKeypress" object: name userInfo: nil]; } - (void)sendQuasimodeEvent:(CGEventRef)event withType:(NSString *)eventType { UniChar strbuf[MAX_STR_LEN]; UniCharCount charsCopied; CGEventKeyboardGetUnicodeString(event, MAX_STR_LEN, &charsCopied, strbuf); NSString *chars = [NSString stringWithCharacters: strbuf length: charsCopied]; int64_t keycode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode); NSNumber *keycodeNum = [NSNumber numberWithUnsignedInt: keycode]; NSArray *keys = [NSArray arrayWithObjects: @"type", @"keyIdentifier", @"keyLocation", @"ctrlKey", @"altKey", @"shiftKey", @"metaKey", nil]; CGEventFlags flags = CGEventGetFlags(event); NSArray *values = [NSArray arrayWithObjects: eventType, chars, keycodeNum, isPressed(flags, kCGEventFlagMaskControl), isPressed(flags, kCGEventFlagMaskAlternate), isPressed(flags, kCGEventFlagMaskShift), isPressed(flags, kCGEventFlagMaskCommand), nil]; NSDictionary *dict = [NSDictionary dictionaryWithObjects: values forKeys: keys]; [center postNotificationName: @"QuasimodeEvent" object: name userInfo: dict]; } - (CGEventRef)processEventWithProxy: (CGEventTapProxy)proxy type: (CGEventType)type event: (CGEventRef)event { BOOL passOnEvent = !inQuasimode; CGEventFlags flags = CGEventGetFlags(event); if (type == kCGEventFlagsChanged) { if (inQuasimode) { if (!(flags & quasimodeKey)) { [self sendQuasimodeEvent: event withType: @"quasimodeend"]; inQuasimode = NO; if (numQuasimodalKeyDowns == 1) { CGEventRef event[2]; DEBUG_MSG("Re-posting single keypress\n"); event[0] = CGEventCreateKeyboardEvent( NULL, (CGKeyCode) lastQuasimodalKeyCode, true ); event[1] = CGEventCreateKeyboardEvent( NULL, (CGKeyCode) lastQuasimodalKeyCode, false ); CGEventSetFlags(event[0], lastQuasimodalKeyFlags); CGEventSetFlags(event[1], lastQuasimodalKeyFlags); CGEventTapPostEvent(proxy, event[0]); CGEventTapPostEvent(proxy, event[1]); CFRelease(event[0]); CFRelease(event[1]); } DEBUG_MSG("Exit quasimode\n"); } } else { if (flags & quasimodeKey) { [self sendQuasimodeEvent: event withType: @"quasimodestart"]; inQuasimode = YES; passOnEvent = NO; numQuasimodalKeyDowns = 0; DEBUG_MSG("Enter quasimode\n"); } else { [self sendSomeKeyEvent]; } } } else { /* Key up/down event */ if (inQuasimode) { NSString *eventType; int64_t keycode = CGEventGetIntegerValueField( event, kCGKeyboardEventKeycode ); if (type == kCGEventKeyDown) { numQuasimodalKeyDowns += 1; lastQuasimodalKeyCode = keycode; lastQuasimodalKeyFlags = CGEventGetFlags(event); eventType = @"quasimodekeydown"; } else eventType = @"quasimodekeyup"; [self sendQuasimodeEvent: event withType: eventType]; } else { [self sendSomeKeyEvent]; } } if (passOnEvent) return event; else return NULL; } - (id)initWithName:(NSString *)objectName quasimodeKey:(CGEventFlags)key { if (self = [super init]) { quasimodeKey = key; numQuasimodalKeyDowns = 0; inQuasimode = NO; name = [objectName copy]; center = [NSNotificationCenter defaultCenter]; CGEventMask mask = (CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp) | CGEventMaskBit(kCGEventFlagsChanged)); portRef = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, 0, mask, eventTapCallback, self); if (portRef == NULL) printf( "CGEventTapCreate() failed.\n" ); rlSrcRef = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, portRef, 0); CFRunLoopAddSource(CFRunLoopGetCurrent(), rlSrcRef, kCFRunLoopDefaultMode); } return self; } - (void)finalize { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), rlSrcRef, kCFRunLoopDefaultMode); CFRelease(rlSrcRef); CFRelease(portRef); } @end static CGEventRef eventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; CGEventRef retval; NSString *bundleId = [ [[NSWorkspace sharedWorkspace] activeApplication] objectForKey: @"NSApplicationBundleIdentifier" ]; if (bundleId && [bundleId isEqualToString: @"com.blizzard.worldofwarcraft"]) { retval = event; } else { QuasimodalEventTap *tap = (QuasimodalEventTap *) refcon; retval = [tap processEventWithProxy: proxy type: type event: event]; } [pool release]; return retval; }