Oliver Cooper
Oliver Cooper

Reputation: 845

Cocoa/Objective-C - Send/Receive clicks from other window layers?

I am making an application similar to GeekTool. I'm not very familiar with how GeekTool works on the inside but it looks and behaves similar. Basically I have a border less window that covers the entire screen, this works fine. I currently have the window on the 'kCGDesktopIconWindowLevel' layer, however I can not interact with anything on the desktop (Moving/Opening files, etc.). When I have the window one layer below this level (kCGDesktopIconWindowLevel-1) I can interact with the desktop but not my window, and I need to be able to interact. Is there anyway I can receive click from an above layer or send them to a lower layer?

By the way, if you have a better idea on how to achive this but avoiding this problem I will greatly appreciate it.

Upvotes: 1

Views: 439

Answers (1)

Smilin Brian
Smilin Brian

Reputation: 990

I suggest you create an event tap like this (lifted from this answer) in applicationDidFinishLaunching::

CGEventMask emask;
CFMachPortRef myEventTap;
CFRunLoopSourceRef eventTapRLSrc;

// We only want one kind of event at the moment: Left mouse down
emask = CGEventMaskBit(kCGEventLeftMouseDown);

// Create the Tap
myEventTap = CGEventTapCreate (
    kCGSessionEventTap, // Catch all events for current user session
    kCGTailAppendEventTap, // Append to end of EventTap list
    kCGEventTapOptionListenOnly, // We only listen, we don't modify
    emask,
    &myEventTapCallback,
    NULL // We need no extra data in the callback
);

// Create a RunLoop Source for it
eventTapRLSrc = CFMachPortCreateRunLoopSource(
    kCFAllocatorDefault,
    myEventTap,
    0
);

// Add the source to the current RunLoop
CFRunLoopAddSource(
    CFRunLoopGetCurrent(),
    eventTapRLSrc,
    kCFRunLoopDefaultMode
);

Set your window to normally ignore mouse events -- [myWindow setIgnoresMouseEvents: YES];

Then your event tap will look for mouse clicks it wants to "catch" -- something like this:

static CGEventRef myEventTapCallback (
    CGEventTapProxy proxy,
    CGEventType type,
    CGEventRef event,
    void * refcon
) {
    CGPoint mouseLocation;

    // If we would get different kind of events, we can distinguish them
    // by the variable "type", but we know we only get mouse moved events

    mouseLocation = CGEventGetLocation(event);

    // Figure out if the mouse is clicking on something we want to "catch"
    if (/* want this click */)
       [myWindow setIgnoresMouseEvents: NO];

    // Pass on the event, we must not modify it anyway, we are a listener
    return event;
}

After the mouse event is completed, return your window to ignoring mouse events.

Upvotes: 1

Related Questions