Prints each NSScreen's frame/backing scale/CG display ID and each
SCDisplay's frame/point size, and copies it to the clipboard, so
secondary-display capture failures can be debugged from the menu bar.
💘 Generated with Crush
Assisted-by: Crush:deepseek-v4-flash-0731
134 lines
4.0 KiB
Swift
134 lines
4.0 KiB
Swift
import SwiftUI
|
|
import AppKit
|
|
import CoreGraphics
|
|
import ServiceManagement
|
|
|
|
@main
|
|
struct KaptureApp: App {
|
|
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
|
|
|
var body: some Scene {
|
|
MenuBarExtra("Kapture", systemImage: "text.viewfinder") {
|
|
MenuView()
|
|
}
|
|
.menuBarExtraStyle(.menu)
|
|
}
|
|
}
|
|
|
|
final class AppDelegate: NSObject, NSApplicationDelegate {
|
|
func applicationDidFinishLaunching(_ notification: Notification) {
|
|
if CommandLine.arguments.contains("--test-ocr") {
|
|
runOCRSelfTest()
|
|
return
|
|
}
|
|
HotkeyManager.shared.onTrigger = { CaptureController.shared.begin() }
|
|
HotkeyManager.shared.register()
|
|
}
|
|
}
|
|
|
|
struct MenuView: View {
|
|
@State private var granted = PermissionManager.isGranted
|
|
|
|
var body: some View {
|
|
Button("Capture & OCR") { CaptureController.shared.begin() }
|
|
.keyboardShortcut("s", modifiers: [.command, .shift])
|
|
|
|
Button("Extract from Image File…") { ResultWindow.shared.showOpenPanel() }
|
|
|
|
Divider()
|
|
|
|
if granted {
|
|
Text("Screen Recording: Granted")
|
|
} else {
|
|
Button("Grant Screen Recording Permission…") {
|
|
PermissionManager.request()
|
|
granted = PermissionManager.isGranted
|
|
}
|
|
}
|
|
|
|
Divider()
|
|
|
|
Button("Copy Diagnostics") {
|
|
let diag = CaptureController.shared.diagnostics()
|
|
let pasteboard = NSPasteboard.general
|
|
pasteboard.clearContents()
|
|
pasteboard.setString(diag, forType: .string)
|
|
}
|
|
|
|
Button(LoginItem.isEnabled ? "Disable Launch at Login" : "Launch at Login") {
|
|
if !LoginItem.toggle() {
|
|
let alert = NSAlert()
|
|
alert.messageText = "Could not update Launch at Login"
|
|
alert.informativeText = "Launch at Login requires a signed app bundle in /Applications. It is not available when running via `swift run`."
|
|
alert.alertStyle = .warning
|
|
alert.addButton(withTitle: "OK")
|
|
alert.runModal()
|
|
}
|
|
}
|
|
|
|
Divider()
|
|
|
|
Button("Quit Kapture") { NSApp.terminate(nil) }
|
|
.keyboardShortcut("q")
|
|
}
|
|
}
|
|
|
|
enum PermissionManager {
|
|
static var isGranted: Bool { CGPreflightScreenCaptureAccess() }
|
|
|
|
static func request() {
|
|
CGRequestScreenCaptureAccess()
|
|
}
|
|
}
|
|
|
|
enum LoginItem {
|
|
static var isEnabled: Bool {
|
|
SMAppService.mainApp.status == .enabled
|
|
}
|
|
|
|
static func toggle() -> Bool {
|
|
let service = SMAppService.mainApp
|
|
do {
|
|
if service.status == .enabled {
|
|
try service.unregister()
|
|
} else {
|
|
try service.register()
|
|
}
|
|
return true
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
func runOCRSelfTest() {
|
|
let text = "Kapture OCR test 1234, hello world"
|
|
let size = NSSize(width: 1600, height: 220)
|
|
guard let rep = NSBitmapImageRep(
|
|
bitmapDataPlanes: nil, pixelsWide: Int(size.width), pixelsHigh: Int(size.height),
|
|
bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false,
|
|
colorSpaceName: .deviceRGB, bytesPerRow: 0, bitsPerPixel: 0
|
|
) else { exit(1) }
|
|
|
|
NSGraphicsContext.saveGraphicsState()
|
|
NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: rep)
|
|
NSColor.white.setFill()
|
|
NSRect(origin: .zero, size: size).fill()
|
|
let attrs: [NSAttributedString.Key: Any] = [
|
|
.font: NSFont.boldSystemFont(ofSize: 56),
|
|
.foregroundColor: NSColor.black,
|
|
]
|
|
text.draw(at: NSPoint(x: 60, y: 75), withAttributes: attrs)
|
|
NSGraphicsContext.restoreGraphicsState()
|
|
|
|
guard let cgImage = rep.cgImage else { exit(1) }
|
|
OCRService.shared.recognize(cgImage) { recognized in
|
|
print("expected: \(text)")
|
|
print("got: \(recognized)")
|
|
let ok = recognized == text
|
|
print(ok ? "PASS" : "FAIL")
|
|
exit(ok ? 0 : 1)
|
|
}
|
|
RunLoop.main.run()
|
|
}
|