Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Capture / Post system-wide Keyboard / Mouse events under Mac OS X?

Tags:

macos

cocoa

For a scripting utility I need to be able to record a series of keyboard and mouse events that occur when an application has focus. The second part is being able to later send those events to the active window.

I do not need to worry about menus or tracking the identifier of which window receives input.

I know how to do this under Windows but have no idea about Mac OS X.

like image 802
Andrew Grant Avatar asked Dec 30 '08 06:12

Andrew Grant


2 Answers

The first thing i will tell you is that you CAN'T do this without the user enabling support for assitive devices in the accessability control panel. It's some kind of security built into OSX.

Here is a code snippit I am using in one of my applications to do this:

//this method calls a carbon method to attach a global event handler
- (void)attachEventHandlers
{
    //create our event type spec for the keyup
    EventTypeSpec eventType;
    eventType.eventClass = kEventClassKeyboard;
    eventType.eventKind = kEventRawKeyUp;

    //create a callback for our event to fire in
    EventHandlerUPP handlerFunction = NewEventHandlerUPP(globalKeyPress);

    //install the event handler
    OSStatus err = InstallEventHandler(GetEventMonitorTarget(), handlerFunction, 1, &eventType, self, NULL);

    //error checking
    if( err )
    {
        //TODO: need an alert sheet here
        NSLog(@"Error registering keyboard handler...%d", err);
    }

    //create our event type spec for the mouse events
    EventTypeSpec eventTypeM;
    eventTypeM.eventClass = kEventClassMouse;
    eventTypeM.eventKind = kEventMouseUp;

    //create a callback for our event to fire in
    EventHandlerUPP handlerFunctionM = NewEventHandlerUPP(globalMousePress);

    //install the event handler
    OSStatus errM = InstallEventHandler(GetEventMonitorTarget(), handlerFunctionM, 1, &eventTypeM, self, NULL);

    //error checking
    if( errM )
    {
        //TODO: need an alert sheet here
        NSLog(@"Error registering mouse handler...%d", err);
    }
}

Here is an example of the callback method i am using:

OSStatus globalKeyPress(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData) 
{
    NSEvent *anEvent = [NSEvent eventWithEventRef:theEvent];
    NSEventType type = [anEvent type];
    WarStrokerApplication *application = (WarStrokerApplication*)userData;

    //is it a key up event?
    if( type == NSKeyUp)
    {
        //which key is it?
        switch( [anEvent keyCode] )
        {
            case NUMERIC_KEYPAD_PLUS: 
                //this is the character we are using for our toggle
                //call the handler function
                [application toggleKeyPressed];
                break;

                //Comment this line back in to figure out the keykode for a particular character                
            default:
                NSLog(@"Keypressed: %d, **%@**", [anEvent keyCode], [anEvent characters]);
                break;
        }
    }

    return CallNextEventHandler(nextHandler, theEvent);
}
like image 58
Lounges Avatar answered Nov 15 '22 02:11

Lounges


For the latter part, posting events, use the CGEvent methods provided in ApplicationServices/ApplicationServices.h

Here's an example function to move the mouse to a specified absolute location:

#include <ApplicationServices/ApplicationServices.h>

int to(int x, int y)
{
    CGPoint newloc;
    CGEventRef eventRef;
    newloc.x = x;
    newloc.y = y;

    eventRef = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, newloc,
                                        kCGMouseButtonCenter);
    //Apparently, a bug in xcode requires this next line
    CGEventSetType(eventRef, kCGEventMouseMoved);
    CGEventPost(kCGSessionEventTap, eventRef);
    CFRelease(eventRef);

    return 0;
}
like image 39
Ben Avatar answered Nov 15 '22 02:11

Ben