mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 02:17:31 +00:00
* fix(computer-use): observe minimized, hidden and other-Space windows on macOS without a restore macOS only enumerated on-screen windows and dropped minimized AX windows, so an app whose window was minimized, hidden or on another Space (any full-screen app) could not be observed, and agents fell back to --restore-window, which activated every window of the app and switched the user's Space. Enumerate every window, keep the off-screen ones accessibility can still read (other-Space windows by remote token, closed windows dropped by their Space membership), and report their real minimized and off-screen state. Background pointer and key input on such a window refuses before sending anything. --restore-window now raises only the target window, once per request, and delivery is measured from whether the target became frontmost. * fix(computer-use): pin the restored window and prefer visible windows by default * fix(computer-use): keep window-index cache keys and stale-element checks across a restore * fix(computer-use): restore only this app's resolved window and refuse a closed target before raising * docs(computer-use): say macOS reads minimized, hidden and other-Space windows
41 lines
1.8 KiB
Swift
41 lines
1.8 KiB
Swift
import CoreGraphics
|
|
import Darwin
|
|
import Foundation
|
|
|
|
/// Which windows belong to a Space. Minimized, hidden and other-Space windows do; a closed window an app keeps around does not.
|
|
public enum WindowSpaces {
|
|
/// Nil when SkyLight's lookup is missing, so closed windows can't be told apart from other-Space ones.
|
|
public static func windowsInAnySpace(_ windowIDs: [CGWindowID]) -> Set<CGWindowID>? {
|
|
guard let symbols else { return nil }
|
|
let connection = symbols.mainConnection()
|
|
return Set(windowIDs.filter { windowID in
|
|
let spaces = symbols.copySpacesForWindows(connection, allSpacesMask, [NSNumber(value: windowID)] as CFArray)?
|
|
.takeRetainedValue() as? [NSNumber]
|
|
return spaces?.isEmpty == false
|
|
})
|
|
}
|
|
|
|
private static let allSpacesMask: Int32 = 0x7
|
|
|
|
private typealias MainConnectionID = @convention(c) () -> Int32
|
|
private typealias CopySpacesForWindows = @convention(c) (Int32, Int32, CFArray) -> Unmanaged<CFArray>?
|
|
|
|
private struct Symbols {
|
|
let mainConnection: MainConnectionID
|
|
let copySpacesForWindows: CopySpacesForWindows
|
|
}
|
|
|
|
private static let symbols: Symbols? = {
|
|
let skyLight = dlopen("/System/Library/PrivateFrameworks/SkyLight.framework/SkyLight", RTLD_LAZY)
|
|
guard let main = dlsym(skyLight, "SLSMainConnectionID"),
|
|
let copy = dlsym(skyLight, "SLSCopySpacesForWindows")
|
|
else { return nil }
|
|
// SAFETY: SkyLight exports CGSConnectionID SLSMainConnectionID(void) and
|
|
// CFArrayRef SLSCopySpacesForWindows(CGSConnectionID, CGSSpaceMask, CFArrayRef), returning a +1 array (as yabai uses them).
|
|
return Symbols(
|
|
mainConnection: unsafeBitCast(main, to: MainConnectionID.self),
|
|
copySpacesForWindows: unsafeBitCast(copy, to: CopySpacesForWindows.self)
|
|
)
|
|
}()
|
|
}
|