Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Loop/Extensions/NSScreen+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ extension NSScreen {
let mouseLocation = NSEvent.mouseLocation
let screens = NSScreen.screens

// Ask SkyLight first so we match WindowServer's tie-breaking at display boundaries.
if let primary = screens.first,
let displayID = SkyLightToolBelt.bestManagedDisplayID(forCGPoint: mouseLocation.flipY(screen: primary)),
let match = screens.first(where: { $0.displayID == displayID }) {
return match
}

// CGRect.contains uses half-open intervals [minX, maxX) × [minY, maxY),
// excluding points on the maxX/maxY edges. So we cannot use frame.contains(_:)
return screens.first {
Expand Down
20 changes: 20 additions & 0 deletions Loop/Private APIs/SkyLightSymbolLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,26 @@ extension SkyLightSymbolLoader {
@available(macOS 26.0, *)
static let SLSWindowIteratorGetResolvedCornerRadii: SLSWindowIteratorGetResolvedCornerRadiiFunc? = loadSymbol("SLSWindowIteratorGetResolvedCornerRadii")

typealias SLSFindWindowByGeometryFunc = @convention(c) (
_ cid: SLSConnectionID,
_ filterWindowID: CGWindowID,
_ flags: Int32,
_ reserved: Int32,
_ screenPoint: UnsafePointer<CGPoint>,
_ outWindowPoint: UnsafeMutablePointer<CGPoint>,
_ outWindowID: UnsafeMutablePointer<CGWindowID>,
_ outWindowCID: UnsafeMutablePointer<Int32>
) -> CGError
static let SLSFindWindowByGeometry: SLSFindWindowByGeometryFunc? = loadSymbol("SLSFindWindowByGeometry")

/// Returns the UUID (`CFString`) of the display containing `point`, using the same
/// tie-breaking WindowServer uses internally. `nil` if no managed display contains it.
typealias SLSCopyBestManagedDisplayForPointFunc = @convention(c) (
_ cid: SLSConnectionID,
_ point: CGPoint
) -> Unmanaged<CFString>?
static let SLSCopyBestManagedDisplayForPoint: SLSCopyBestManagedDisplayForPointFunc? = loadSymbol("SLSCopyBestManagedDisplayForPoint")

typealias SLSSetWindowBackgroundBlurRadiusFunc = @convention(c) (_ connection: SLSConnectionID, _ wid: CGWindowID, _ radius: Int) -> OSStatus
static let SLSSetWindowBackgroundBlurRadius: SLSSetWindowBackgroundBlurRadiusFunc? = loadSymbol("SLSSetWindowBackgroundBlurRadius")

Expand Down
42 changes: 42 additions & 0 deletions Loop/Private APIs/SkyLightToolBelt.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,48 @@ enum SkyLightToolBelt {
}
}

/// Returns the display ID containing the given point, using the same tie-breaking
/// WindowServer uses at display boundaries.
/// - Parameter cgPoint: The point in the CoreGraphics coordinate system.
/// - Returns: The matching `CGDirectDisplayID`, or `nil` if the point isn't on any managed display.
static func bestManagedDisplayID(forCGPoint cgPoint: CGPoint) -> CGDirectDisplayID? {
guard let SLSMainConnectionID = SkyLightSymbolLoader.SLSMainConnectionID,
let SLSCopyBestManagedDisplayForPoint = SkyLightSymbolLoader.SLSCopyBestManagedDisplayForPoint
else {
return nil
}

guard let uuidString = SLSCopyBestManagedDisplayForPoint(SLSMainConnectionID(), cgPoint)?.takeRetainedValue(),
let uuid = CFUUIDCreateFromString(nil, uuidString)
else {
return nil
}

let displayID = CGDisplayGetDisplayIDFromUUID(uuid)
return displayID != 0 ? displayID : nil
}

/// Finds the topmost window at a given screen position.
/// - Parameter position: The screen position to check.
/// - Returns: The `CGWindowID` of the window at the position, or `nil` if none found.
static func windowIDAtPosition(_ position: CGPoint) -> CGWindowID? {
guard let SLSMainConnectionID = SkyLightSymbolLoader.SLSMainConnectionID,
let SLSFindWindowByGeometry = SkyLightSymbolLoader.SLSFindWindowByGeometry
else {
return nil
}

let cid = SLSMainConnectionID()
var screenPoint = position
var windowPoint = CGPoint.zero
var hitWindowID: CGWindowID = 0
var windowCID: Int32 = 0

_ = SLSFindWindowByGeometry(cid, 0, 1, 0, &screenPoint, &windowPoint, &hitWindowID, &windowCID)

return hitWindowID != 0 ? hitWindowID : nil
}

/// Captures images for each of the windows that are passed in.
/// - Parameter windowIDs: The `CGWindowID`s for each of the windows to capture.
/// - Returns: An array of `CGImage`s for each window, in the same order as the windows that were passed in.
Expand Down
12 changes: 12 additions & 0 deletions Loop/Window Management/Window/Window.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ final class Window {
)
}

/// Retrieve a window from a `CGWindowID`.
/// - Parameter windowID: The window ID to look up.
static func fromWindowID(_ windowID: CGWindowID) throws -> Window {
guard let windowInfoList = CGWindowListCopyWindowInfo([.optionIncludingWindow], windowID) as? [[String: AnyObject]],
let windowInfo = windowInfoList.first
else {
throw WindowError.cannotGetWindow
}

return try fromWindowInfo(windowInfo)
}

/// Retrieve a window from an entry in a dictionary returned by `CGWindowListCopyWindowInfo`.
/// - Parameter windowInfo: The dictionary containing information about the window.
static func fromWindowInfo(_ windowInfo: [String: AnyObject]) throws -> Window {
Expand Down
6 changes: 6 additions & 0 deletions Loop/Window Management/Window/WindowUtility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ enum WindowUtility {
/// - Parameter position: The position to check for
/// - Returns: The window at the given position, if any
static func windowAtPosition(_ position: CGPoint) -> Window? {
// Try SkyLight first, as it is faster and doesn't deadlock on own process
if let windowID = SkyLightToolBelt.windowIDAtPosition(position),
let window = try? Window.fromWindowID(windowID) {
return window
}

do {
// If we can find the window at a point using the Accessibility API, return it
if let element = try AXUIElement.systemWide.getElementAtPosition(position),
Expand Down