Mercurial > osx-quasimode
diff QuasimodalEventTap.m @ 18:78807eea31b7
Added EnsoKeyNotifier.m from Enso source, renamed to QuasimodalEventTap.m w/ some notification center stuff removed and formatting fixed to match this codebase.
author | Atul Varma <avarma@mozilla.com> |
---|---|
date | Sun, 11 Apr 2010 23:11:14 -0700 |
parents | |
children | 8053681846ad |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/QuasimodalEventTap.m Sun Apr 11 23:11:14 2010 -0700 @@ -0,0 +1,193 @@ +#include <AppKit/NSWorkspace.h> +#include <ApplicationServices/ApplicationServices.h> +#import <Foundation/NSAutoreleasePool.h> +#import <Foundation/NSDistributedNotificationCenter.h> +#import <Foundation/NSArray.h> +#import <Foundation/NSDictionary.h> +#import <Foundation/NSString.h> +#include <stdio.h> + +#define QUASIMODE_KEY kCGEventFlagMaskAlternate +#define MAX_STR_LEN 10 + +#ifdef DEBUG +#define DEBUG_MSG(msg) printf(msg); +#else +#define DEBUG_MSG(msg) +#endif + +CGKeyCode lastQuasimodalKeyCode; +CGEventFlags lastQuasimodalKeyFlags; +int numQuasimodalKeyDowns = 0; + +static BOOL inQuasimode = NO; + +void sendSomeKeyEvent() { + // TODO: Send some-key event +} + +CGEventRef processEvent(CGEventTapProxy proxy, + CGEventType type, + CGEventRef event, + void *refcon) +{ + BOOL passOnEvent = !inQuasimode; + + if (type == kCGEventFlagsChanged) { + CGEventFlags flags = CGEventGetFlags(event); + + if (inQuasimode) { + if (!(flags & QUASIMODE_KEY)) { + // TODO: Send quasimodeend event + 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 & QUASIMODE_KEY) { + // TODO: Send quasimodestart event + inQuasimode = YES; + passOnEvent = NO; + numQuasimodalKeyDowns = 0; + DEBUG_MSG("Enter quasimode\n"); + } else { + sendSomeKeyEvent(); + } + } + } else { + /* Key up/down event */ + + if (inQuasimode) { + UniChar strbuf[MAX_STR_LEN]; + UniCharCount charsCopied; + + CGEventKeyboardGetUnicodeString( + event, + MAX_STR_LEN, + &charsCopied, + strbuf + ); + + NSString *chars = [NSString stringWithCharacters: strbuf + length: charsCopied]; + NSString *eventType; + + int64_t keycode = CGEventGetIntegerValueField( + event, + kCGKeyboardEventKeycode + ); + + if (type == kCGEventKeyDown) { + numQuasimodalKeyDowns += 1; + lastQuasimodalKeyCode = keycode; + lastQuasimodalKeyFlags = CGEventGetFlags(event); + eventType = @"keyDown"; + } else + eventType = @"keyUp"; + + NSNumber *keycodeNum = [NSNumber numberWithUnsignedInt: keycode]; + + // TODO: Send event + } else { + sendSomeKeyEvent(); + } + } + + if (passOnEvent) + return event; + else + return NULL; +} + +CGEventRef myCallback(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 { + retval = processEvent(proxy, type, event, refcon); + } + + [pool release]; + + return retval; +} + +int main(int argc, const char *argv[] ) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + CGEventMask mask = (CGEventMaskBit(kCGEventKeyDown) | + CGEventMaskBit(kCGEventKeyUp) | + CGEventMaskBit(kCGEventFlagsChanged)); + + CFMachPortRef portRef = CGEventTapCreate(kCGHIDEventTap, + kCGHeadInsertEventTap, + 0, + mask, + myCallback, + NULL); + + CFRunLoopSourceRef rlSrcRef; + + if (portRef == NULL) { + printf( "CGEventTapCreate() failed.\n" ); + return -1; + } + + rlSrcRef = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, + portRef, + 0); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), + rlSrcRef, + kCFRunLoopDefaultMode); + + // TODO: Run app. + + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), + rlSrcRef, + kCFRunLoopDefaultMode); + + CFRelease( rlSrcRef ); + CFRelease( portRef ); + + [pool release]; + + return 0; +}