mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
189 lines
No EOL
6.3 KiB
Swift
189 lines
No EOL
6.3 KiB
Swift
//
|
||
// ScreenshotSnippingTool.swift
|
||
// DynamicIsland
|
||
//
|
||
// Created by Assistant based on ScreenshotApp-main research
|
||
|
||
import AppKit
|
||
import SwiftUI
|
||
import Foundation
|
||
|
||
// MARK: - Simplified Screenshot Tool (Based on ScreenshotApp Research)
|
||
class ScreenshotSnippingTool: NSObject, ObservableObject {
|
||
static let shared = ScreenshotSnippingTool()
|
||
|
||
@Published var isSnipping = false
|
||
private var completion: ((URL) -> Void)?
|
||
|
||
// MARK: - Screenshot Types (Based on ScreenshotApp)
|
||
enum ScreenshotType {
|
||
case full
|
||
case window
|
||
case area
|
||
|
||
var processArguments: [String] {
|
||
switch self {
|
||
case .full:
|
||
return ["-c"] // -c = clipboard
|
||
case .window:
|
||
return ["-cw"] // -c = clipboard, -w = window selection
|
||
case .area:
|
||
return ["-cs"] // -c = clipboard, -s = area selection
|
||
}
|
||
}
|
||
|
||
var displayName: String {
|
||
switch self {
|
||
case .full: return "Full Screen"
|
||
case .window: return "Window"
|
||
case .area: return "Area"
|
||
}
|
||
}
|
||
|
||
var iconName: String {
|
||
switch self {
|
||
case .full: return "rectangle.dashed"
|
||
case .window: return "macwindow"
|
||
case .area: return "viewfinder.rectangular"
|
||
}
|
||
}
|
||
}
|
||
|
||
enum ScreenshotError: Error {
|
||
case captureFailed
|
||
case noImageInPasteboard
|
||
case saveFailed
|
||
}
|
||
|
||
override init() {
|
||
super.init()
|
||
}
|
||
|
||
// MARK: - Enhanced API (Based on ScreenshotApp Implementation)
|
||
func startSnipping(type: ScreenshotType = .area, completion: @escaping (URL) -> Void) {
|
||
guard !isSnipping else { return }
|
||
|
||
print("🖼️ ScreenshotTool: Starting \(type.displayName.lowercased()) screenshot using screencapture tool")
|
||
self.completion = completion
|
||
isSnipping = true
|
||
|
||
// Use the same approach as ScreenshotApp - direct screencapture command
|
||
takeScreenshot(type: type)
|
||
}
|
||
|
||
// MARK: - Convenience Methods for Different Types
|
||
func startAreaScreenshot(completion: @escaping (URL) -> Void) {
|
||
startSnipping(type: .area, completion: completion)
|
||
}
|
||
|
||
func startFullScreenshot(completion: @escaping (URL) -> Void) {
|
||
startSnipping(type: .full, completion: completion)
|
||
}
|
||
|
||
func startWindowScreenshot(completion: @escaping (URL) -> Void) {
|
||
startSnipping(type: .window, completion: completion)
|
||
}
|
||
|
||
// MARK: - ScreenshotApp-Style Implementation
|
||
private func takeScreenshot(type: ScreenshotType) {
|
||
let task = Process()
|
||
task.executableURL = URL(fileURLWithPath: "/usr/sbin/screencapture")
|
||
task.arguments = type.processArguments
|
||
|
||
do {
|
||
print("📸 ScreenshotTool: Running screencapture \(type.processArguments.joined(separator: " ")) command")
|
||
try task.run()
|
||
task.waitUntilExit()
|
||
|
||
// Process completed - check if successful
|
||
if task.terminationStatus == 0 {
|
||
print("✅ ScreenshotTool: screencapture completed successfully")
|
||
getImageFromPasteboard()
|
||
} else {
|
||
print("❌ ScreenshotTool: screencapture failed with status: \(task.terminationStatus)")
|
||
finishSnipping()
|
||
}
|
||
|
||
} catch {
|
||
print("❌ ScreenshotTool: Failed to run screencapture: \(error)")
|
||
finishSnipping()
|
||
}
|
||
}
|
||
|
||
// MARK: - Pasteboard Integration (ScreenshotApp Pattern)
|
||
private func getImageFromPasteboard() {
|
||
print("<EFBFBD> ScreenshotTool: Checking pasteboard for screenshot")
|
||
|
||
guard NSPasteboard.general.canReadItem(withDataConformingToTypes: NSImage.imageTypes) else {
|
||
print("❌ ScreenshotTool: No image data in pasteboard")
|
||
finishSnipping()
|
||
return
|
||
}
|
||
|
||
guard let image = NSImage(pasteboard: NSPasteboard.general) else {
|
||
print("❌ ScreenshotTool: Failed to create NSImage from pasteboard")
|
||
finishSnipping()
|
||
return
|
||
}
|
||
|
||
print("✅ ScreenshotTool: Got image from pasteboard: \(image.size)")
|
||
saveImageAndComplete(image: image)
|
||
}
|
||
|
||
// MARK: - Image Saving
|
||
private func saveImageAndComplete(image: NSImage) {
|
||
let filename = "screenshot_\(Int(Date().timeIntervalSince1970)).png"
|
||
let screenshotDir = ScreenAssistantManager.screenshotDataDirectory
|
||
|
||
// Ensure directory exists
|
||
if !FileManager.default.fileExists(atPath: screenshotDir.path) {
|
||
try? FileManager.default.createDirectory(at: screenshotDir, withIntermediateDirectories: true)
|
||
}
|
||
|
||
let screenshotURL = screenshotDir.appendingPathComponent(filename)
|
||
|
||
// Convert NSImage to PNG data
|
||
guard let imageData = image.tiffRepresentation,
|
||
let bitmapRep = NSBitmapImageRep(data: imageData),
|
||
let pngData = bitmapRep.representation(using: NSBitmapImageRep.FileType.png, properties: [:]) else {
|
||
print("❌ ScreenshotTool: Failed to convert image to PNG")
|
||
finishSnipping()
|
||
return
|
||
}
|
||
|
||
do {
|
||
try pngData.write(to: screenshotURL)
|
||
print("✅ ScreenshotTool: Screenshot saved to: \(screenshotURL.path)")
|
||
|
||
// Execute completion callback
|
||
let callback = self.completion
|
||
self.completion = nil
|
||
finishSnipping()
|
||
|
||
// Call completion on main thread
|
||
DispatchQueue.main.async {
|
||
callback?(screenshotURL)
|
||
}
|
||
|
||
} catch {
|
||
print("❌ ScreenshotTool: Failed to save image: \(error)")
|
||
finishSnipping()
|
||
}
|
||
}
|
||
|
||
// MARK: - State Management
|
||
private func finishSnipping() {
|
||
print("🔄 ScreenshotTool: Finishing snipping process")
|
||
|
||
DispatchQueue.main.async {
|
||
self.isSnipping = false
|
||
self.completion = nil
|
||
print("✅ ScreenshotTool: Snipping process completed")
|
||
}
|
||
}
|
||
|
||
func cancelSnipping() {
|
||
print("❌ ScreenshotTool: Snipping cancelled")
|
||
finishSnipping()
|
||
}
|
||
} |