import AppKit import CoreGraphics /// Enumerates on-screen windows using CGWindowListCopyWindowInfo. enum WindowEnumerator { /// Returns all normal, on-screen windows excluding this app and desktop elements. static func enumerateWindows() -> [WindowInfo] { let options: CGWindowListOption = [.optionOnScreenOnly, .excludeDesktopElements] guard let windowList = CGWindowListCopyWindowInfo(options, kCGNullWindowID) as? [[String: Any]] else { return [] } let ownPID = ProcessInfo.processInfo.processIdentifier var windows: [WindowInfo] = [] for (_, entry) in windowList.enumerated() { guard let windowID = entry[kCGWindowNumber as String] as? CGWindowID, let pid = entry[kCGWindowOwnerPID as String] as? pid_t, let layer = entry[kCGWindowLayer as String] as? Int, layer == 0, // normal windows only pid != ownPID else { continue } // Skip windows with no bounds or zero-size bounds guard let boundsDict = entry[kCGWindowBounds as String] as? [String: Any], let bounds = CGRect(dictionaryRepresentation: boundsDict as CFDictionary), bounds.width > 1, bounds.height > 1 else { continue } let ownerName = entry[kCGWindowOwnerName as String] as? String ?? "Unknown" let windowTitle = entry[kCGWindowName as String] as? String ?? ownerName // Look up app info from running applications let app = NSRunningApplication(processIdentifier: pid) let bundleID = app?.bundleIdentifier ?? "unknown.\(pid)" let appIcon = app?.icon let isOnScreen = (entry[kCGWindowIsOnscreen as String] as? Bool) ?? true let info = WindowInfo( windowID: windowID, title: windowTitle, appName: ownerName, appBundleID: bundleID, appIcon: appIcon, frame: bounds, pid: pid, isOnScreen: isOnScreen, thumbnail: nil ) windows.append(info) } return windows } /// Group windows by application, ordered per the layout algorithm spec. static func groupWindows(_ windows: [WindowInfo]) -> [WindowGroup] { var groupMap: [String: WindowGroup] = [:] for (index, window) in windows.enumerated() { if var group = groupMap[window.appBundleID] { group.windows.append(window) group.frontmostIndex = min(group.frontmostIndex, index) groupMap[window.appBundleID] = group } else { groupMap[window.appBundleID] = WindowGroup( appBundleID: window.appBundleID, appName: window.appName, appIcon: window.appIcon, windows: [window], frontmostIndex: index ) } } // Sort windows within each group by title for stable ordering for key in groupMap.keys { groupMap[key]!.windows.sort { $0.title.localizedCaseInsensitiveCompare($1.title) == .orderedAscending } } // Sort groups alphabetically by app name return groupMap.values.sorted { a, b in a.appName.localizedCaseInsensitiveCompare(b.appName) == .orderedAscending } } }