view src/EnsoKeyNotifier.m @ 16:e07f0c7abdc0

Changed the notification messages sent by the key notifier to be more descriptive.
author Atul Varma <varmaa@toolness.com>
date Sun, 24 Feb 2008 20:15:57 -0600
parents af72a81169aa
children 5477703cfb73
line wrap: on
line source

#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: @"EnsoKeyNotifier_msg"
            object: @"EnsoKeyNotifier"
            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: @"EnsoKeyNotifier_msg"
                        object: @"EnsoKeyNotifier"
                        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: @"EnsoKeyNotifier_msg"
                        object: @"EnsoKeyNotifier"
                        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: @"EnsoKeyNotifier_msg"
                    object: @"EnsoKeyNotifier"
                    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 either of the following is true:\n\n" 
            "  (1) This program is running with super-user privileges.\n"
            "  (2) Access for assistive devices is enabled in the \n"
            "      Universal Access System Preferences.\n\n"
            "If one or more of these conditions is not satisfied, then "
            "quasimodal keypresses will not be recognized.\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;
}