mirror of
https://github.com/samsonjs/Peekaboo.git
synced 2026-03-25 09:25:47 +00:00
- Specify Swift 6.0 in .swiftformat to enable all formatting features - Apply Swift 6 formatting improvements: - Use shorthand optional unwrapping syntax - Use implicit returns in computed properties - Use modern Swift 6 syntax throughout 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
119 lines
4.2 KiB
Swift
119 lines
4.2 KiB
Swift
import AppKit
|
|
import CoreGraphics
|
|
import Foundation
|
|
|
|
class WindowManager {
|
|
static func getWindowsForApp(pid: pid_t, includeOffScreen: Bool = false) throws(WindowError) -> [WindowData] {
|
|
Logger.shared.debug("Getting windows for PID: \(pid)")
|
|
|
|
let windowList = try fetchWindowList(includeOffScreen: includeOffScreen)
|
|
let windows = extractWindowsForPID(pid, from: windowList)
|
|
|
|
Logger.shared.debug("Found \(windows.count) windows for PID \(pid)")
|
|
return windows.sorted { $0.windowIndex < $1.windowIndex }
|
|
}
|
|
|
|
private static func fetchWindowList(includeOffScreen: Bool) throws(WindowError) -> [[String: Any]] {
|
|
let options: CGWindowListOption = includeOffScreen
|
|
? [.excludeDesktopElements]
|
|
: [.excludeDesktopElements, .optionOnScreenOnly]
|
|
|
|
guard let windowList = CGWindowListCopyWindowInfo(options, kCGNullWindowID) as? [[String: Any]] else {
|
|
throw WindowError.windowListFailed
|
|
}
|
|
|
|
return windowList
|
|
}
|
|
|
|
private static func extractWindowsForPID(_ pid: pid_t, from windowList: [[String: Any]]) -> [WindowData] {
|
|
var windows: [WindowData] = []
|
|
var windowIndex = 0
|
|
|
|
for windowInfo in windowList {
|
|
if let window = parseWindowInfo(windowInfo, targetPID: pid, index: windowIndex) {
|
|
windows.append(window)
|
|
windowIndex += 1
|
|
}
|
|
}
|
|
|
|
return windows
|
|
}
|
|
|
|
private static func parseWindowInfo(_ info: [String: Any], targetPID: pid_t, index: Int) -> WindowData? {
|
|
guard let windowPID = info[kCGWindowOwnerPID as String] as? Int32,
|
|
windowPID == targetPID,
|
|
let windowID = info[kCGWindowNumber as String] as? CGWindowID else {
|
|
return nil
|
|
}
|
|
|
|
let title = info[kCGWindowName as String] as? String ?? "Untitled"
|
|
let bounds = extractWindowBounds(from: info)
|
|
let isOnScreen = info[kCGWindowIsOnscreen as String] as? Bool ?? true
|
|
|
|
return WindowData(
|
|
windowId: windowID,
|
|
title: title,
|
|
bounds: bounds,
|
|
isOnScreen: isOnScreen,
|
|
windowIndex: index
|
|
)
|
|
}
|
|
|
|
private static func extractWindowBounds(from windowInfo: [String: Any]) -> CGRect {
|
|
guard let boundsDict = windowInfo[kCGWindowBounds as String] as? [String: Any] else {
|
|
return .zero
|
|
}
|
|
|
|
let xCoordinate = boundsDict["X"] as? Double ?? 0
|
|
let yCoordinate = boundsDict["Y"] as? Double ?? 0
|
|
let width = boundsDict["Width"] as? Double ?? 0
|
|
let height = boundsDict["Height"] as? Double ?? 0
|
|
|
|
return CGRect(x: xCoordinate, y: yCoordinate, width: width, height: height)
|
|
}
|
|
|
|
static func getWindowsInfoForApp(
|
|
pid: pid_t,
|
|
includeOffScreen: Bool = false,
|
|
includeBounds: Bool = false,
|
|
includeIDs: Bool = false
|
|
) throws(WindowError) -> [WindowInfo] {
|
|
let windowDataArray = try getWindowsForApp(pid: pid, includeOffScreen: includeOffScreen)
|
|
|
|
return windowDataArray.map { windowData in
|
|
WindowInfo(
|
|
window_title: windowData.title,
|
|
window_id: includeIDs ? windowData.windowId : nil,
|
|
window_index: windowData.windowIndex,
|
|
bounds: includeBounds ? WindowBounds(
|
|
xCoordinate: Int(windowData.bounds.origin.x),
|
|
yCoordinate: Int(windowData.bounds.origin.y),
|
|
width: Int(windowData.bounds.size.width),
|
|
height: Int(windowData.bounds.size.height)
|
|
) : nil,
|
|
is_on_screen: includeOffScreen ? windowData.isOnScreen : nil
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Extension to add the getWindowsForApp function to ImageCommand
|
|
extension ImageCommand {
|
|
func getWindowsForApp(pid: pid_t) throws(WindowError) -> [WindowData] {
|
|
try WindowManager.getWindowsForApp(pid: pid)
|
|
}
|
|
}
|
|
|
|
enum WindowError: Error, LocalizedError {
|
|
case windowListFailed
|
|
case noWindowsFound
|
|
|
|
var errorDescription: String? {
|
|
switch self {
|
|
case .windowListFailed:
|
|
"Failed to get window list from system"
|
|
case .noWindowsFound:
|
|
"No windows found for the specified application"
|
|
}
|
|
}
|
|
}
|