mirror of
https://github.com/samsonjs/Peekaboo.git
synced 2026-03-25 09:25:47 +00:00
Migrate to ScreenCaptureKit
This commit is contained in:
parent
4636aec9d4
commit
f6e9cbc7b9
1 changed files with 97 additions and 12 deletions
|
|
@ -2,6 +2,7 @@ import AppKit
|
|||
import ArgumentParser
|
||||
import CoreGraphics
|
||||
import Foundation
|
||||
import ScreenCaptureKit
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
// Define the wrapper struct
|
||||
|
|
@ -199,7 +200,7 @@ struct ImageCommand: ParsableCommand {
|
|||
|
||||
if captureFocus == .foreground {
|
||||
try PermissionsChecker.requireAccessibilityPermission()
|
||||
targetApp.activate(options: [.activateIgnoringOtherApps])
|
||||
targetApp.activate()
|
||||
Thread.sleep(forTimeInterval: 0.2) // Brief delay for activation
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +246,7 @@ struct ImageCommand: ParsableCommand {
|
|||
|
||||
if captureFocus == .foreground {
|
||||
try PermissionsChecker.requireAccessibilityPermission()
|
||||
targetApp.activate(options: [.activateIgnoringOtherApps])
|
||||
targetApp.activate()
|
||||
Thread.sleep(forTimeInterval: 0.2)
|
||||
}
|
||||
|
||||
|
|
@ -279,24 +280,108 @@ struct ImageCommand: ParsableCommand {
|
|||
}
|
||||
|
||||
private func captureDisplay(_ displayID: CGDirectDisplayID, to path: String) throws(CaptureError) {
|
||||
guard let image = CGDisplayCreateImage(displayID) else {
|
||||
do {
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
var captureError: Error?
|
||||
|
||||
Task {
|
||||
do {
|
||||
try await captureDisplayWithScreenCaptureKit(displayID, to: path)
|
||||
} catch {
|
||||
captureError = error
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
|
||||
semaphore.wait()
|
||||
|
||||
if let error = captureError {
|
||||
throw error
|
||||
}
|
||||
} catch {
|
||||
throw CaptureError.captureCreationFailed
|
||||
}
|
||||
}
|
||||
|
||||
private func captureDisplayWithScreenCaptureKit(_ displayID: CGDirectDisplayID, to path: String) async throws {
|
||||
// Get available content
|
||||
let availableContent = try await SCShareableContent.current
|
||||
|
||||
// Find the display by ID
|
||||
guard let scDisplay = availableContent.displays.first(where: { $0.displayID == displayID }) else {
|
||||
throw CaptureError.captureCreationFailed
|
||||
}
|
||||
|
||||
// Create content filter for the entire display
|
||||
let filter = SCContentFilter(display: scDisplay, excludingWindows: [])
|
||||
|
||||
// Configure capture settings
|
||||
let configuration = SCStreamConfiguration()
|
||||
configuration.width = scDisplay.width
|
||||
configuration.height = scDisplay.height
|
||||
configuration.backgroundColor = .black
|
||||
configuration.shouldBeOpaque = true
|
||||
configuration.showsCursor = true
|
||||
|
||||
// Capture the image
|
||||
let image = try await SCScreenshotManager.captureImage(
|
||||
contentFilter: filter,
|
||||
configuration: configuration
|
||||
)
|
||||
|
||||
try saveImage(image, to: path)
|
||||
}
|
||||
|
||||
private func captureWindow(_ window: WindowData, to path: String) throws(CaptureError) {
|
||||
let options: CGWindowImageOption = [.boundsIgnoreFraming, .shouldBeOpaque]
|
||||
|
||||
guard let image = CGWindowListCreateImage(
|
||||
window.bounds,
|
||||
.optionIncludingWindow,
|
||||
window.windowId,
|
||||
options
|
||||
) else {
|
||||
do {
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
var captureError: Error?
|
||||
|
||||
Task {
|
||||
do {
|
||||
try await captureWindowWithScreenCaptureKit(window, to: path)
|
||||
} catch {
|
||||
captureError = error
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
|
||||
semaphore.wait()
|
||||
|
||||
if let error = captureError {
|
||||
throw error
|
||||
}
|
||||
} catch {
|
||||
throw CaptureError.windowCaptureFailed
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func captureWindowWithScreenCaptureKit(_ window: WindowData, to path: String) async throws {
|
||||
// Get available content
|
||||
let availableContent = try await SCShareableContent.current
|
||||
|
||||
// Find the window by ID
|
||||
guard let scWindow = availableContent.windows.first(where: { $0.windowID == window.windowId }) else {
|
||||
throw CaptureError.windowNotFound
|
||||
}
|
||||
|
||||
// Create content filter for the specific window
|
||||
let filter = SCContentFilter(desktopIndependentWindow: scWindow)
|
||||
|
||||
// Configure capture settings
|
||||
let configuration = SCStreamConfiguration()
|
||||
configuration.width = Int(window.bounds.width)
|
||||
configuration.height = Int(window.bounds.height)
|
||||
configuration.backgroundColor = .clear
|
||||
configuration.shouldBeOpaque = true
|
||||
configuration.showsCursor = false
|
||||
|
||||
// Capture the image
|
||||
let image = try await SCScreenshotManager.captureImage(
|
||||
contentFilter: filter,
|
||||
configuration: configuration
|
||||
)
|
||||
|
||||
try saveImage(image, to: path)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue