views:

378

answers:

2

I need to listen for global mouse events(not bound to an app) on my Mac in an app written in Python.

I'm using PyObjC, but I can't figure out how to do it. Plain ObjC examples or other Python techniques also appreciated.

My code so far:

from Quartz import *
def MyFunction(proxy, type, event):
    print event

CGEventTapCreate(kCGHIDEventTap, kCGTailAppendEventTap, kCGEventTapOptionListenOnly, kCGEventLeftMouseDown, MyFunction)

== Segmentation fault

I know I need to add it to an event source later on, but I need to get this working first.

[update]

Using PyObjC form Macports solved the segfault, so now I wrote this:

from Quartz import *

def MyFunction(p, t, e, c):
    print e

tap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, kCGEventLeftMouseDown, MyFunction, None)

runLoopSource = CFMachPortCreateRunLoopSource(None, tap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
CGEventTapEnable(tap, True);

CFRunLoopRun();

But this just runs forever and does not respond to mouse events, what is wrong?

A: 

First, CGEventTapCreate and CGEventTapCreateForPSN leak some memory when they are called. This is needed to avoid memory management problems. It is therefore advisable not to call these functions, are at least call them a small number of times.

Now, a mouse event works something like this:

evt = CGEventCreateMouseEvent(None, kCGEventLeftMouseDown, (80, 90), kCGMouseButtonLeft)
self.failUnlessIsInstance(evt, CGEventRef)
Todd Moses
I don't want to send events, I want to listen for them. CGEventCreateMouseEvent is for creating events, not event taps.
Pepijn
Sorry. I messed up with giving you the wrong code example.
Todd Moses
There is another method for the Tap Event that I thought I was giving you.
Todd Moses
So could you paste the correct example?
Pepijn
+1  A: 

The documentation for CGEventTapCreate (http://developer.apple.com/mac/library/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html#//apple_ref/c/func/CGEventTapCreate) says that you need to be root to use kCGHIDEventTap. Are you running your script as root? (sudo is one way to do this)

If you are, you should also check whether tap is None; that will help narrow down the problem. There are several error conditions listed in the documentation that can cause CGEventTapCreate to return NULL, which should be reflected as None in Python.

Scott Wolchok
Sudo or not does not make a difference, both tap and runLoopSource contain something. Is there an alternative to kCGHIDEventTap? I noticed that CFRunLoopRunInMode(kCFRunLoopDefaultMode, 20, False) runs for 20 seconds, but supplying True ends immediately, but still not output from MyFunction.
Pepijn
Could it be something to do with stringifying the CGEventRef you're trying to print? Perhaps you could try printing a constant string in MyFunction just to make sure. Other than that, I don't really know.
Scott Wolchok
I tried to just print 'hi', but that didn't help either. Could it be that the function is called somewhere where stdout is set differently?
Pepijn