Mailing Lists: Apple Mailing Lists
Image of Mac OS face in stamp
Re: Trying out CGEventTapCreate, but it appears to be broken!
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Trying out CGEventTapCreate, but it appears to be broken!




On Oct 9, 2005, at 2:49 AM, Brian Kendall wrote:

I just recently discovered the new CoreGraphics events API that are in Tiger, and in particular I'm interested in event taps. So far they're undocumented, but there was enough information in /System/ Library/Frameworks/ApplicationServices.framework/Frameworks/ CoreGraphics.framework/Headers/CGEvent.h for me to get the basic jist of it.

So I tried throwing together a quick Carbon app that would make an event tap that outputs a message when it's triggered (for say, pressing the middle mouse button) but it doesn't work. If I set the event tap to be a listener only, everything on my computer behaves normally but no event is ever received by the tap. More alarming though is when I set it not to be a listening-only tap. As soon as I clicked a mouse button > 2, the entire system would lock up. No keypresses or mouse events would be registered by anything at all, though the cursor would still move around on the screen. This problem persists until a few seconds after the application terminates or the event tap is released. I've tried making the function the event tap calls do various different minor things like beeping or just doing nothing, but the UI lock still occurs. Also, changing what events are being monitored seems to have no effect - tapping any kind of event causes this to happen.

A listen-only event tap listens to events, but does not filter them. Without the kCGEventTapOptionListenOnly option, the event tap acts as a filter, which can effectively stall the flow of events through the system unless it acts on and processes events promptly. At the very low level that event taps operate at, the system guarantees that events will be delivered sequentially. (We wouldn't want that modifier key for your mouse click to arrive after the click, right?)


The symptoms you are describing are all indicative of the tapping application not yet actually listening for events on the event tap's CFMachPortRef. In your source code, nothing was done to actually place the tap's CFMachPortRef in the Carbon MainEventLoop.

The CGEventTap code doesn't do this automatically, because there may be many different kinds of run loops in applications. In general, for fitering event tap applications, I recommend running the event tap filter in it's own thread, with it's own CFRunLoop, rather than in the main application thread. Trying to filter all the system's events in the main application thread effectively throttles the flow of events through the system to the rate at which your application can process, draw, and fetch the next event.



So... what can I do? There's no documentation about these functions so I can't check if I'm doing something wrong, and no one else seems to have used them yet. I would have a hard time believing that they're just broken.


Here's the relevant portion of the code I wrote:


CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
{
printf("eventTap triggered\n");
return event;
}


int main(int argc, char* argv[])
{
    // window and menus set up here

    CFMachPortRef machPortRef = NULL;
    machPortRef =  CGEventTapCreate(kCGSessionEventTap,
                            kCGTailAppendEventTap,
                            0,
                            CGEventMaskBit(kCGEventOtherMouseDown),
                            (CGEventTapCallBack)eventTapFunction,
                            NULL);
    if (machPortRef == NULL)
        printf("CGEventTapCreate failed!\n");


So far, so good. You have an event tap port. Now you need to turn it into a runloop source, and in this case, add it to the runloop behind the Carbon main event loop. Note that I normally don't recommend doing this, as it will throttle the flow of events to the rate at which the main loop can process and draw. In this case, the main event loop will just be servicing the CGEventTap, so we might be able to get away with this.



CFRunLoopSourceRef eventSrc; CFRunLoopRef runLoop;

    eventSrc = CFMachPortCreateRunLoopSource(NULL, machPortRef, 0);
    if ( eventSrc == NULL )
        printf( "No event run loop src?\n" );

// Get the CFRunLoop primitive for the Carbon Main Event Loop, and add the new event souce
CFRunLoopAddSource(GetCFRunLoopFromEventLoop(GetMainEventLoop ()), eventSrc, kCFRunLoopDefaultMode);


    RunApplicationEventLoop();

    if (machPortRef)
        CFRelease(machPortRef);

if ( eventSrc ) CFRelease(eventSrc);
}



In a command line tool, faceless app, or daemon, you can do something similar purely at the CoreFoundation level:

#include <ApplicationServices/ApplicationServices.h>

CGEventRef printEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
{
printf( "Got event of type %d\n", type )
return event;
}


int main(int argc, char ** argv)
{
    CFMachPortRef eventPort;
    CFRunLoopSourceRef  eventSrc;
    CFRunLoopRef    runLoop;

    eventPort = CGEventTapCreate(kCGSessionEventTap,
                                kCGHeadInsertEventTap,
                                kCGEventTapOptionListenOnly,
                                CGEventMaskBit(kCGEventOtherMouseDown),
                                printEventCallback,
                                NULL );
    if ( eventPort == NULL )
    {
        printf( "NULL event port\n" );
        exit( 1 );
    }

    eventSrc = CFMachPortCreateRunLoopSource(NULL, eventPort, 0);
    if ( eventSrc == NULL )
        printf( "No event run loop src?\n" );

    runLoop = CFRunLoopGetCurrent();
    if ( runLoop == NULL )
        printf( "No run loop?\n" );

    CFRunLoopAddSource(runLoop,  eventSrc, kCFRunLoopDefaultMode);
    CFRunLoopRun();
}
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Quartz-dev mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


References: 
 >Trying out CGEventTapCreate, but it appears to be broken! (From: "Brian Kendall" <email@hidden>)



Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Contact Apple | Terms of Use | Privacy Policy

Copyright © 2011 Apple Inc. All rights reserved.