Search code examples
pythonobjective-cmouseeventpyobjc

How can I listen for a mouse event in Python on Mac?


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?


Solution

  • The fourth parameter of CGEventTapCreate is CGEventMask eventsOfInterest, and you gave it kCGEventLeftMouseDown which is an enum of type _CGEventType. Instead of the integer constant, you need to flip the appropriate bit in the bitmask. You can do this using CGEventMaskBit

    So instead of this:

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

    We can do this:

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

    or equivalently:

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