tags:

views:

2194

answers:

3

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.

+1  A: 

For tapping mouse events, see http://osxbook.com/book/bonus/chapter2/altermouse/

I haven't checked this under 10.5 Leopard but on 10.4 it works.

diciu
Works for keyboard events, too, but (IIRC) the user must enable access for assistive devices in System Preferences.
Peter Hosey
Addendum: My previous comment refers only to keyboard event taps. Mouse event taps are always available (again, IIRC).
Peter Hosey
+4  A: 

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;
}
Ben
+1  A: 

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);
}
Lounges