Mercurial > enso_osx
changeset 7:d8bb1adf3688
Added key notifier.
author | Atul Varma <varmaa@toolness.com> |
---|---|
date | Sun, 24 Feb 2008 09:49:10 -0600 |
parents | 853d8676221f |
children | 334f67ad9f22 |
files | src/KeyNotifier.m src/SConscript |
diffstat | 2 files changed, 249 insertions(+), 11 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/KeyNotifier.m Sun Feb 24 09:49:10 2008 -0600 @@ -0,0 +1,221 @@ +#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 + +NSDistributedNotificationCenter *center; + +CGKeyCode lastQuasimodalKeyCode; +CGEventFlags lastQuasimodalKeyFlags; +int numQuasimodalKeyDowns = 0; + +static BOOL inQuasimode = NO; + +static void sendSomeKeyEvent( void ) +{ + NSArray *keys = [NSArray arrayWithObjects: @"event", nil]; + NSArray *values = [NSArray arrayWithObjects: @"someKey", nil]; + NSDictionary *dict = [NSDictionary dictionaryWithObjects: values forKeys: keys]; + + [center postNotificationName: @"KeyNotifier_msg" + object: @"KeyNotifier" + userInfo: dict]; +} + +CGEventRef myCallback( CGEventTapProxy proxy, + CGEventType type, + CGEventRef event, + void *refcon ) +{ + //int64_t keycode = CGEventGetIntegerValueField( + // event, + // kCGKeyboardEventKeycode + // ); + + BOOL passOnEvent = !inQuasimode; + + if ( type == kCGEventFlagsChanged ) + { + CGEventFlags flags = CGEventGetFlags( event ); + + if ( inQuasimode ) + { + if ( !(flags & QUASIMODE_KEY) ) + { + NSArray *keys = [NSArray arrayWithObjects: @"event", nil]; + NSArray *values = [NSArray arrayWithObjects: @"quasimodeEnd", nil]; + NSDictionary *dict = [NSDictionary dictionaryWithObjects: values forKeys: keys]; + + [center postNotificationName: @"KeyNotifier_msg" + object: @"KeyNotifier" + userInfo: dict]; + inQuasimode = NO; + if ( numQuasimodalKeyDowns == 1 ) + { + CGEventRef event[2]; + + printf( "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] ); + } + printf( "Exit quasimode\n" ); + } + } else { + if ( flags & QUASIMODE_KEY ) + { + NSArray *keys = [NSArray arrayWithObjects: @"event", nil]; + NSArray *values = [NSArray arrayWithObjects: @"quasimodeStart", nil]; + NSDictionary *dict = [NSDictionary dictionaryWithObjects: values forKeys: keys]; + + [center postNotificationName: @"KeyNotifier_msg" + object: @"KeyNotifier" + userInfo: dict]; + inQuasimode = YES; + passOnEvent = NO; + numQuasimodalKeyDowns = 0; + printf( "Enter quasimode\n" ); + } else { + sendSomeKeyEvent(); + } + } + } else { + /* Key up/down event */ + + if ( inQuasimode ) + { +#define MAX_STR_LEN 10 + + 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]; + + NSArray *keys = [NSArray arrayWithObjects: @"event", @"chars", @"keycode", nil]; + NSArray *values = [NSArray arrayWithObjects: eventType, chars, keycodeNum, nil]; + NSDictionary *dict = [NSDictionary dictionaryWithObjects: values forKeys: keys]; + + [center postNotificationName: @"KeyNotifier_msg" + object: @"KeyNotifier" + userInfo: dict]; + } else { + sendSomeKeyEvent(); + } + } + + if ( passOnEvent ) + return event; + else + return NULL; +} + +int main( int argc, const char *argv[] ) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + center = [NSDistributedNotificationCenter defaultCenter]; + + CGEventMask mask = ( CGEventMaskBit( kCGEventKeyDown ) | + CGEventMaskBit( kCGEventKeyUp ) | + CGEventMaskBit( kCGEventFlagsChanged ) ); + + CFMachPortRef portRef = CGEventTapCreate( + //kCGSessionEventTap, + 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 + ); + + printf( "Please make sure this program is running with " ); + printf( "super-user privileges, or else quasimodal keypresses " ); + printf( "will not be recognized.\n\n" ); + printf( "Running event loop...\n" ); + + //CFRunLoopRunInMode( kCFRunLoopDefaultMode, 10.0, false ); + CFRunLoopRun(); + + printf( "Done running event loop.\n" ); + + CFRunLoopRemoveSource( + CFRunLoopGetCurrent(), + rlSrcRef, + kCFRunLoopDefaultMode + ); + + CFRelease( rlSrcRef ); + CFRelease( portRef ); + + [pool release]; + + return 0; +}
--- a/src/SConscript Sun Feb 24 09:34:36 2008 -0600 +++ b/src/SConscript Sun Feb 24 09:49:10 2008 -0600 @@ -16,17 +16,16 @@ # ---------------------------------------------------------------------------- -# Library Definitions -# ---------------------------------------------------------------------------- - -env = env.Copy() - - -# ---------------------------------------------------------------------------- -# Build Actions +# Helper Functions # ---------------------------------------------------------------------------- def getOutput( params ): + """ + Runs the given program, as specified by the parameter list. Raises an + exception with stderr output if the process has a nonzero return + code. Otherwise, it returns the stdout of the process as a string. + """ + popen = subprocess.Popen( params, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) @@ -37,10 +36,19 @@ raise SystemError() return output + +# ---------------------------------------------------------------------------- +# Build Actions +# ---------------------------------------------------------------------------- + +# quartz cairo bridge + cairoLibFlags = getOutput( ["pkg-config", "cairo", "--libs"] ) cairoIncludeFlags = getOutput( ["pkg-config", "cairo", "--cflags"] ) -env.Append( +qcbEnv = env.Copy() + +qcbEnv.Append( CPPPATH=[os.path.join( sys.prefix, "include/pycairo" )], CCFLAGS=cairoIncludeFlags.split(), LINKFLAGS=cairoLibFlags, @@ -48,9 +56,18 @@ FRAMEWORKS=["AppKit"], ) -osXQuartzCairoBridge = env.LoadableModule( +quartzCairoBridge = qcbEnv.LoadableModule( source = ["quartz_cairo_bridge.m"], target = ["quartz_cairo_bridge.so"], ) -env.Install( "#enso_osx/graphics", osXQuartzCairoBridge ) +qcbEnv.Install( "#enso_osx/graphics", quartzCairoBridge ) + +# key notifier + +keyNotifier = env.Program( + source = ["KeyNotifier.m"], + FRAMEWORKS = ["ApplicationServices", "Foundation"] + ) + +env.Install( "#bin", keyNotifier )