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 )