Mission Control Turbo: macOS multitasking turbocharged
1import AppKit
2import CoreGraphics
3
4/// Enumerates on-screen windows using CGWindowListCopyWindowInfo.
5enum WindowEnumerator {
6 /// Returns all normal, on-screen windows excluding this app and desktop elements.
7 static func enumerateWindows() -> [WindowInfo] {
8 let options: CGWindowListOption = [.optionOnScreenOnly, .excludeDesktopElements]
9 guard let windowList = CGWindowListCopyWindowInfo(options, kCGNullWindowID) as? [[String: Any]] else {
10 return []
11 }
12
13 let ownPID = ProcessInfo.processInfo.processIdentifier
14
15 var windows: [WindowInfo] = []
16 for (_, entry) in windowList.enumerated() {
17 guard let windowID = entry[kCGWindowNumber as String] as? CGWindowID,
18 let pid = entry[kCGWindowOwnerPID as String] as? pid_t,
19 let layer = entry[kCGWindowLayer as String] as? Int,
20 layer == 0, // normal windows only
21 pid != ownPID
22 else { continue }
23
24 // Skip windows with no bounds or zero-size bounds
25 guard let boundsDict = entry[kCGWindowBounds as String] as? [String: Any],
26 let bounds = CGRect(dictionaryRepresentation: boundsDict as CFDictionary),
27 bounds.width > 1, bounds.height > 1
28 else { continue }
29
30 let ownerName = entry[kCGWindowOwnerName as String] as? String ?? "Unknown"
31 let windowTitle = entry[kCGWindowName as String] as? String ?? ownerName
32
33 // Look up app info from running applications
34 let app = NSRunningApplication(processIdentifier: pid)
35 let bundleID = app?.bundleIdentifier ?? "unknown.\(pid)"
36 let appIcon = app?.icon
37
38 let isOnScreen = (entry[kCGWindowIsOnscreen as String] as? Bool) ?? true
39
40 let info = WindowInfo(
41 windowID: windowID,
42 title: windowTitle,
43 appName: ownerName,
44 appBundleID: bundleID,
45 appIcon: appIcon,
46 frame: bounds,
47 pid: pid,
48 isOnScreen: isOnScreen,
49 thumbnail: nil
50 )
51 windows.append(info)
52 }
53
54 return windows
55 }
56
57 /// Group windows by application, ordered per the layout algorithm spec.
58 static func groupWindows(_ windows: [WindowInfo]) -> [WindowGroup] {
59 var groupMap: [String: WindowGroup] = [:]
60
61 for (index, window) in windows.enumerated() {
62 if var group = groupMap[window.appBundleID] {
63 group.windows.append(window)
64 group.frontmostIndex = min(group.frontmostIndex, index)
65 groupMap[window.appBundleID] = group
66 } else {
67 groupMap[window.appBundleID] = WindowGroup(
68 appBundleID: window.appBundleID,
69 appName: window.appName,
70 appIcon: window.appIcon,
71 windows: [window],
72 frontmostIndex: index
73 )
74 }
75 }
76
77 // Sort windows within each group by title for stable ordering
78 for key in groupMap.keys {
79 groupMap[key]!.windows.sort {
80 $0.title.localizedCaseInsensitiveCompare($1.title) == .orderedAscending
81 }
82 }
83
84 // Sort groups alphabetically by app name
85 return groupMap.values.sorted { a, b in
86 a.appName.localizedCaseInsensitiveCompare(b.appName) == .orderedAscending
87 }
88 }
89}