tmandry / Swindler

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

NSScreen.Screens Coordinates don't align with AXUIElement coordinates for multi-monitor displays #62

Closed camhart closed 3 years ago

camhart commented 5 years ago

https://github.com/tmandry/Swindler/blob/5c1d49d407cbd7066e09f4cfab3e26f7cf3a8ba8/Sources/Screen.swift#L131

I'm doing my own research here, but I think you'll find it relevant based on the code. Right now I have a laptop, with two 1080p monitors. So 3 screens in total. The laptop display is "arranged" roughly as shown below (with the laptop display below the monitor displays, but right aligned with the monitor above):

[1080P monitor][1080p monitor]
  [   laptop  ]

However, with all that being said, different monitor displays don't seem to fix the issue I'm finding (read below).

Using this code:

    var counter = 0
    for screen in NSScreen.screens {
        print("\(counter): \(screen.frame)\t\(screen.visibleFrame)")
        counter += 1
    }

I get the following output:

0: (0.0, 0.0, 1440.0, 900.0)    (0.0, 80.0, 1440.0, 797.0) //laptop display
1: (-480.0, 900.0, 1920.0, 1080.0)  (-480.0, 900.0, 1920.0, 1080.0) // monitor up and to the left from laptop
2: (1440.0, 900.0, 1920.0, 1080.0)  (1440.0, 900.0, 1920.0, 1080.0) // monitor up and to the right from laptop

However, when I put a window on screen 1 or 2, the Accessibility Inspector reports y coordinates that are negative. For example, a full screen window on screen #1 has a Frame of x=-480, y=-1080, w=1920, h=1080.

Do you know why, and how to account for, the fact that the screens through NSScreen.screens reports a different Y value (y = 900 when I think it should be -1080).

This is all on macOS Mojave 10.14.5, on a Macbook Pro 13" 2018 if that's worth anything.

Edit: May have something to do with "quartz" coordinates (see https://stackoverflow.com/questions/22671916/cocoa-getting-the-top-left-logical-point-of-multiple-screens). Edit 2: Might have a winner https://stackoverflow.com/questions/19884363/in-objective-c-os-x-is-the-global-display-coordinate-space-used-by-quartz-d#answer-19887161

camhart commented 5 years ago

This converts the NSScreen.frame to Quartz coordinates, which is what AXFrame's position is in.

    var counter = 0
    for screen in NSScreen.screens {

        var frame = screen.frame
        frame.origin.y = NSMaxY(NSScreen.screens[0].frame) - NSMaxY(screen.frame)

        print("\(counter): \(screen.frame)\t\(screen.visibleFrame)\t\(frame)")
        counter += 1
    }
camhart commented 5 years ago

I'll let you close if you want--I think you're going to need to account for this (unless you do and I missed it somewhere)

tmandry commented 3 years ago

Yeah, the AX-based APIs now all convert their coordinates to Quartz coordinates. Some better documentation on this is probably warranted.