import AppKit import ApplicationServices /// Bridge to macOS Accessibility APIs for window activation and title retrieval. enum AccessibilityBridge { /// Check if we have Accessibility permission. static var hasPermission: Bool { AXIsProcessTrusted() } /// Prompt the user to grant Accessibility permission if not already granted. static func requestPermissionIfNeeded() { let options = [kAXTrustedCheckOptionPrompt.takeUnretainedValue(): true] as CFDictionary AXIsProcessTrustedWithOptions(options) } /// Activate the window with the given window ID and PID. static func activateWindow(pid: pid_t, windowID: CGWindowID) -> Bool { guard let app = NSRunningApplication(processIdentifier: pid) else { return false } // Activate the application app.activate() // Use Accessibility to raise the specific window let appElement = AXUIElementCreateApplication(pid) var windowsRef: CFTypeRef? let result = AXUIElementCopyAttributeValue(appElement, kAXWindowsAttribute as CFString, &windowsRef) guard result == .success, let axWindows = windowsRef as? [AXUIElement] else { return true // App activated but couldn't raise specific window } for axWindow in axWindows { if let cgWindowID = axWindowID(for: axWindow), cgWindowID == windowID { AXUIElementPerformAction(axWindow, kAXRaiseAction as CFString) return true } } return true } /// Get the title of a window via Accessibility (fallback when CGWindowList doesn't have it). static func windowTitle(pid: pid_t, windowID: CGWindowID) -> String? { let appElement = AXUIElementCreateApplication(pid) var windowsRef: CFTypeRef? let result = AXUIElementCopyAttributeValue(appElement, kAXWindowsAttribute as CFString, &windowsRef) guard result == .success, let axWindows = windowsRef as? [AXUIElement] else { return nil } for axWindow in axWindows { if let cgWinID = axWindowID(for: axWindow), cgWinID == windowID { var titleRef: CFTypeRef? let titleResult = AXUIElementCopyAttributeValue(axWindow, kAXTitleAttribute as CFString, &titleRef) if titleResult == .success, let title = titleRef as? String { return title } } } return nil } /// Move a window to a new position using AXUIElement. /// The point should be in Quartz coordinates (top-left origin). static func moveWindow(pid: pid_t, windowID: CGWindowID, to point: CGPoint) -> Bool { let appElement = AXUIElementCreateApplication(pid) var windowsRef: CFTypeRef? let result = AXUIElementCopyAttributeValue(appElement, kAXWindowsAttribute as CFString, &windowsRef) guard result == .success, let axWindows = windowsRef as? [AXUIElement] else { return false } for axWindow in axWindows { if let cgWinID = axWindowID(for: axWindow), cgWinID == windowID { var position = point guard let axValue = AXValueCreate(.cgPoint, &position) else { return false } let setResult = AXUIElementSetAttributeValue(axWindow, kAXPositionAttribute as CFString, axValue) return setResult == .success } } return false } // MARK: - Private /// Map an AXUIElement to a CGWindowID using the private _AXUIElementGetWindow API. private static func axWindowID(for element: AXUIElement) -> CGWindowID? { var windowID: CGWindowID = 0 let result = _AXUIElementGetWindow(element, &windowID) return result == .success ? windowID : nil } } // Private API declaration — widely used by AltTab, Rectangle, yabai, etc. @_silgen_name("_AXUIElementGetWindow") func _AXUIElementGetWindow(_ element: AXUIElement, _ windowID: UnsafeMutablePointer) -> AXError