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;
+}