jns_ai_unr
jns_ai_unr

Reputation: 903

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).

Upvotes: 4

Views: 1362

Answers (3)

John Houston
John Houston

Reputation: 33

There's another thing incorrect in your code, although you might be lucky and it isn't normally causing a problem.

As documented for the mode parameter to CFRunLoopAddSource: "Use the constant kCFRunLoopCommonModes to add source to the set of objects monitored by all the common modes."

That third parameter should instead be CFRunLoopMode.commonModes. What you have, CFRunLoopMode.defaultMode aka kCFRunLoopDefaultMode, is instead for use when calling CFRunLoopRun.

Upvotes: 0

TheNextman
TheNextman

Reputation: 12566

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.

Upvotes: 4

Asperi
Asperi

Reputation: 257819

Here is a way to listen for mouseMove global events (tested with Xcode 11.2+, macOS 10.15)

// ... say in AppDelegate

var globalObserver: Any!
var localObserver: Any!

func applicationDidFinishLaunching(_ aNotification: Notification) {

    globalObserver = NSEvent.addGlobalMonitorForEvents(matching: .mouseMoved) { event in
        let location = event.locationInWindow
        print("in background: {\(location.x), \(location.y)}")
    }

    localObserver = NSEvent.addLocalMonitorForEvents(matching: .mouseMoved) { event in
        let location = event.locationInWindow
        print("active: {\(location.x), \(location.y)}")
        return event
    }
    ...

Upvotes: 0

Related Questions