Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MacOS Quartz Event Tap listening to wrong events

I am trying to intercept mouse move events using the CGEvent.tapCreate(tap:place:options:eventsOfInterest:callback:userInfo:) method as shown below:

let cfMachPort = CGEvent.tapCreate(tap: CGEventTapLocation.cghidEventTap, 
                                   place: CGEventTapPlacement.headInsertEventTap, 
                                   options: CGEventTapOptions.defaultTap, 
                                   eventsOfInterest:CGEventMask(CGEventType.mouseMoved.rawValue), 
                                   callback: {(eventTapProxy, eventType, event, mutablePointer) -> Unmanaged<CGEvent>? in event
    print(event.type.rawValue)   //Breakpoint
    return nil
}, userInfo: nil)

let runloopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, cfMachPort!, 0)

let runLoop = RunLoop.current
let cfRunLoop = runLoop.getCFRunLoop()
CFRunLoopAddSource(cfRunLoop, runloopSource, CFRunLoopMode.defaultMode)

I pass as event type eventsOfInterest mouseMoved events with a raw value of 5 as seen in the documentation. But for some reason my print() is not executed unless I click with the mouse. Inspecting the send mouse event in the debugger gives me a raw value of 2, which according to the documentation is a leftMouseUp event.

In the documentation for CGEvent.tapCreate(tap:place:options:eventsOfInterest:callback:userInfo:) it says:

Event taps receive key up and key down events [...]

So it seems like the method ignores mouseMoved events in general?! But how am I supposed to listen to mouseMoved events? I am trying to prevent my cursor (custom cursor) from being replaced (for example when I hover over the application dock at the bottom of the screen).

like image 518
j.unruh Avatar asked Oct 30 '25 00:10

j.unruh


1 Answers

You need to bitshift the CGEventType value used to create the CGEventMask parameter. In Objective-C, there is a macro to do this: CGEventMaskBit.

From the CGEventMask documentation:

to form the bit mask, use the CGEventMaskBit macro to convert each constant into an event mask and then OR the individual masks together

I don't know the equivalent mechanism in swift; but the macro itself looks like this:

*/ #define CGEventMaskBit(eventType) ((CGEventMask)1 << (eventType))

In your example, it's sufficient to just manually shift the argument; e.g.

eventsOfInterest:CGEventMask(1 << CGEventType.mouseMoved.rawValue),

I would point out that the code example given in the question is a little dangerous; as it creates a default event tap and then drops the events rather than allowing them to be processed. This messes up mouse click handling and it was tricky to actually terminate the application using the mouse. Anyone running the example could set the event tap type to CGEventTapOptions.listenOnly to prevent that.

like image 127
TheNextman Avatar answered Oct 31 '25 16:10

TheNextman