main.swift•3.26 kB
import AppKit
import CoreGraphics
import Foundation
// LSUIElement helper that enumerates menu bar windows via private CGS APIs and prints JSON.
// Running inside AppKit provides the GUI WindowServer connection needed to see third-party extras.
private struct CGSWindowListOption: OptionSet {
let rawValue: UInt32
static let onScreen = CGSWindowListOption(rawValue: 1 << 0)
static let menuBarItems = CGSWindowListOption(rawValue: 1 << 1)
static let activeSpace = CGSWindowListOption(rawValue: 1 << 2)
}
private func loadSymbol<T>(_ name: String, handle: UnsafeMutableRawPointer?) -> T? {
guard let sym = dlsym(handle, name) else { return nil }
return unsafeBitCast(sym, to: T.self)
}
private func loadCGSHandle() -> UnsafeMutableRawPointer? {
let handles = [
"/System/Library/PrivateFrameworks/SkyLight.framework/SkyLight",
"/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics",
]
for path in handles {
if let h = dlopen(path, RTLD_NOW) { return h }
}
return nil
}
private func listMenuBarWindowIDs() -> [UInt32] {
guard let handle = loadCGSHandle(),
let mainConnSym: @convention(c) () -> UInt32 = loadSymbol("CGSMainConnectionID", handle: handle),
let copySym: @convention(c) (UInt32, Int32, UInt32) -> CFArray? =
loadSymbol("CGSCopyWindowsWithOptions", handle: handle),
let getCountSym: @convention(c) (UInt32, UInt32, UnsafeMutablePointer<Int32>) -> Int32 =
loadSymbol("CGSGetWindowCount", handle: handle),
let getMenuBarSym: @convention(c) (
UInt32, UInt32, Int32, UnsafeMutablePointer<CGWindowID>, UnsafeMutablePointer<Int32>) -> Int32 =
loadSymbol("CGSGetProcessMenuBarWindowList", handle: handle)
else {
return []
}
let cid = mainConnSym()
// Process-level list (Ice primary path).
var total: Int32 = 0
_ = getCountSym(cid, 0, &total)
var buf = [CGWindowID](repeating: 0, count: Int(max(total, 32)))
var out: Int32 = 0
_ = getMenuBarSym(cid, 0, total, &buf, &out)
let procIDs = Array(buf.prefix(Int(out)))
// Copy-with-options (sometimes returns extras).
let opts: CGSWindowListOption = [.menuBarItems, .onScreen, .activeSpace]
let copyIDs = (copySym(cid, 0, opts.rawValue) as? [UInt32]) ?? []
return Array(Set(procIDs + copyIDs))
}
// Initialize AppKit to get a GUI connection (LSUIElement).
NSApplication.shared
// Screen Recording prompt: CGS window metadata may require it.
let preflight = CGPreflightScreenCaptureAccess()
if !preflight {
let granted = CGRequestScreenCaptureAccess()
if !granted {
let payload: [String: Any] = ["error": "screen_recording_denied"]
if let data = try? JSONSerialization.data(withJSONObject: payload, options: [.prettyPrinted]) {
FileHandle.standardOutput.write(data)
}
fflush(stdout)
exit(1)
}
}
let ids = listMenuBarWindowIDs()
let payload: [String: Any] = ["window_ids": ids]
if let data = try? JSONSerialization.data(withJSONObject: payload, options: [.prettyPrinted]) {
FileHandle.standardOutput.write(data)
} else {
fputs("{\"error\":\"serialization_failed\"}", stdout)
}
fflush(stdout)
exit(0)