In my post on event taps, I mentioned the following code to get a global event tap:
CFMachPortRef tapg = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGEventTapOptionDefault,
CGEventMaskBit(kCGEventLeftMouseDown)|CGEventMaskBit(kCGEventLeftMouseUp),
ProcessEvent,NULL);
This taps the event stream at the “annotated session” point (hence the kCGAnnotatedSessionEventTap parameter). Basically, this means that the event has already been analyzed as to destination application, and you can ask the passed event directly for that application’s process ID. This is was I was using in Klicko.
Unfortunately the docs don’t mention another step that takes place while “annotating” a mouse-down event: apparently, if the click was on a window’s title bar (not elsewhere in the window), the owning process is brought to the front before the tap sees the event! Klicko’s processing varied depending on the clicked-on window’s and the owning process’ state, so I was seeing different results depending on the click location – title bar or inside the window. Worse, a workaround I needed to do to bring non-main windows to the front for background processes wouldn’t be applied at all if the user clicked on a title bar!
A more subtle consequence was that I was intercepting (and discarding) mouse clicks, while the system was expecting the click that brought the process to the front to actually arrive at the process… the result was that, while the menu bar changed to the process, its windows would remain in the back.
After beating my head against this wall for several days, I was rereading the CGEvent docs again for ideas and noticed that I hadn’t tried applying a global tap before event annotation:
CFMachPortRef tapg = CGEventTapCreate(kCGSessionEventTap, kCGEventTapOptionDefault,
CGEventMaskBit(kCGEventLeftMouseDown)|CGEventMaskBit(kCGEventLeftMouseUp),
ProcessEvent,NULL);
Note the kCGSessionEventTap parameter. This meant that I had to discover the owning application’s process ID myself (instead of asking the mouse-down event directly). Premature optimization strikes again; I’d selected the annotated tap just to avoid doing these steps.
At any rate, I immediately discovered that I now could properly intercept clicks anywhere, discard them, and do process and window activation as I wanted to. Well, almost; there was still some redundant window activation to do, as Carbon and Cocoa apps seem to still have some residual differences in that regard. I’ll be filing bugs at Apple about some of these things.
Leave a Comment