Files
Flaro/Tools/WindowShot/main.swift
T
ScarriffleandClaude Opus 5 e7de8a9055 Bilder ohne Warten — und der Beweis, dass der Mac die Leiste selbst zeichnet
Der Schriftzug hing weiter nicht an der Animation, und der Zwischenspeicher war
nicht schuld: er lag im Actor, und ein `await` kostet immer mindestens einen
Durchlauf. Das neue Bild erschien deshalb im ersten Bild ohne Schriftzug und im
zweiten mit — er sprang also getrennt herein, obwohl er längst da war.

Der Speicher liegt jetzt außerhalb des Actors und lässt sich beim Zeichnen
lesen. Das gilt für alle Bilder: auch eine zurückscrollende Kachel blitzt nicht
mehr kurz grau auf.

Zur Seitenleiste auf dem Mac, gemessen statt vermutet: `automatic` löst sich
laut Apples eigener Beschreibung auf iOS zu `overlap` auf (die Leiste schwebt
über dem Inhalt) und auf macOS zu `tile` (sie schiebt ihn zur Seite und klebt
am Fensterrand). Der Griff dorthin sitzt richtig — derselbe Weg hat die Leiste
auf Kommando ganz ausgeblendet —, aber `overlap` wird auf dem Mac
stillschweigend verworfen. Die Bitte bleibt trotzdem stehen; sie kostet nichts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 17:40:46 +02:00

50 lines
2.1 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import CoreGraphics
import Foundation
// Sucht das größte sichtbare Fenster eines Programms und gibt seine Kennung
// aus. Damit lässt sich `screencapture -l<kennung>` aufrufen — das fotografiert
// genau dieses eine Fenster und sonst nichts vom Bildschirm.
//
// Gebaut, weil es sonst keinen Weg dorthin gibt: das System-Python bringt
// PyObjC nicht mehr mit, und `screencapture` kennt nur Kennungen, keine Namen.
let name = CommandLine.arguments.dropFirst().first ?? "Flixy"
/// Zweites Argument: die Prozesskennung. Nötig, sobald zwei Bauarten
/// desselben Programms gleichzeitig laufen — sonst trifft es die falsche.
let wantedPID = CommandLine.arguments.dropFirst(2).first.flatMap(Int.init)
guard let windows = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID) as? [[String: Any]] else {
FileHandle.standardError.write(Data("Fensterliste nicht lesbar\n".utf8))
exit(2)
}
let candidates = windows.filter { window in
guard let owner = window[kCGWindowOwnerName as String] as? String, owner == name else { return false }
// Ebene 0 ist das normale Fenster; alles darüber sind Menüs und Schatten.
guard (window[kCGWindowLayer as String] as? Int) == 0 else { return false }
if let wantedPID, (window[kCGWindowOwnerPID as String] as? Int) != wantedPID { return false }
return true
}
let largest = candidates.max { a, b in
func area(_ w: [String: Any]) -> Double {
guard let bounds = w[kCGWindowBounds as String] as? [String: Any],
let width = bounds["Width"] as? Double,
let height = bounds["Height"] as? Double
else { return 0 }
return width * height
}
return area(a) < area(b)
}
guard let window = largest, let id = window[kCGWindowNumber as String] as? Int else {
FileHandle.standardError.write(Data("Kein sichtbares Fenster von „\(name)“\n".utf8))
exit(1)
}
if let bounds = window[kCGWindowBounds as String] as? [String: Any],
let width = bounds["Width"] as? Double, let height = bounds["Height"] as? Double {
FileHandle.standardError.write(Data("\(name): \(Int(width))×\(Int(height))\n".utf8))
}
print(id)