import Cocoa let pid = pid_t(CommandLine.arguments[1])! let maxDepth = CommandLine.arguments.count > 2 ? Int(CommandLine.arguments[2])! : 12 let app = AXUIElementCreateApplication(pid) func str(_ e: AXUIElement, _ a: String) -> String { var v: CFTypeRef? guard AXUIElementCopyAttributeValue(e, a as CFString, &v) == .success else { return "" } return "\(v!)" } func pos(_ e: AXUIElement) -> String { var v: CFTypeRef? guard AXUIElementCopyAttributeValue(e, kAXPositionAttribute as CFString, &v) == .success else { return "" } var p = CGPoint.zero AXValueGetValue(v as! AXValue, .cgPoint, &p) var v2: CFTypeRef? guard AXUIElementCopyAttributeValue(e, kAXSizeAttribute as CFString, &v2) == .success else { return "\(p)" } var s = CGSize.zero AXValueGetValue(v2 as! AXValue, .cgSize, &s) return String(format: "(%.0f,%.0f %.0fx%.0f)", p.x, p.y, s.width, s.height) } var count = 0 func walk(_ e: AXUIElement, _ depth: Int) { if depth > maxDepth || count > 3000 { return } count += 1 let role = str(e, kAXRoleAttribute) let title = str(e, kAXTitleAttribute) let desc = str(e, kAXDescriptionAttribute) let val = str(e, kAXValueAttribute) let indent = String(repeating: " ", count: depth) print("\(indent)\(role) \(pos(e)) t=\(title) d=\(desc) v=\(val.prefix(40))") var kids: CFTypeRef? if AXUIElementCopyAttributeValue(e, kAXChildrenAttribute as CFString, &kids) == .success, let arr = kids as? [AXUIElement] { for k in arr { walk(k, depth+1) } } } var winRef: CFTypeRef? if AXUIElementCopyAttributeValue(app, kAXFocusedWindowAttribute as CFString, &winRef) == .success { walk(winRef as! AXUIElement, 0) } else { print("no focused window") }