tmandry / Swindler

macOS window management library for Swift
https://tmandry.github.io/Swindler/docs/main/
MIT License
690 stars 66 forks source link

How do I find the topmost window at a given point? #76

Open stijlist opened 3 years ago

stijlist commented 3 years ago

How would I find the topmost window at a point? Here's the code I have, but I'm not sure how to figure out which window is on top.

I don't see any references to z-index in the existing or planned API docs.

import Cocoa
import Swindler
import PromiseKit

let currentCursor = NSEvent.mouseLocation
print(currentCursor)
let _ = Swindler.initialize()
    .done { state -> Void in
        print(
            state.knownWindows.filter({
                window in
                guard window.isMinimized.value else { return false }
                return window.frame.value.contains(currentCursor)
            }))
        exit(0)
    }.catch { error in
        print(error)
    }
CFRunLoopRun()
tmandry commented 3 years ago

Hey! As I understand it it's actually kind of hard to do this with public APIs, but possible.

It is possible to get the window number of the topmost window at a given point (link). Then you have to tie that to the same window in the AX system. The best way using public APIs I know is to enumerate the windows in both systems and match their frames, pids, and titles: see this answer.

There is a private (unstable) API that ties them together directly: _AXUIElementGetWindow. We should expose a way to get the underlying AXUIElement for people who want to do this. It would have to be an Optional to work with the fake testing API.

Sorry it's such a pain. Converting from/to window IDs is something I'd like to see in Swindler itself, and it would enable APIs that do what you want directly. It would have to deal with the edge case of having two windows with the same frame, pid, and title somehow. We could also support the private API, but it would need to be with a compile time switch.