Vorschau liest macOS' eigene Hintergrundverwaltung
NSWorkspace.desktopImageURL ist die alte Schnittstelle: sie meldet das letzte Bild, das als Datei gesetzt wurde — auch dann noch, wenn es längst gelöscht ist. Genau das war hier der Fall, der Pfad zeigt auf eine Datei, die es nicht mehr gibt. Seit macOS 14 führt das System die Auswahl in com.apple.wallpaper/Store/Index.plist, je Bildschirm und je Schreibtisch, und Anbieter wie Fotos hinterlegen dort ihre Zwischenspeicher. Die Datei ist doppelt verpackt: die Konfiguration je Bildschirm steckt als eigene Property-Liste in einem Datenfeld der äußeren. Ohne den zweiten Durchgang findet man gar nichts. Gelesen wird nur der Eintrag dieses Bildschirms, nachgeschlagen über die Display-Kennung. In der Datei stehen auch alte Bildschirme, der Bildschirmschoner und alles, was einmal gesetzt war — das Neueste von allem zu nehmen führt zuverlässig zu einem Bild, das mit dem Schreibtisch nichts zu tun hat. Ausprobiert: dabei kam ein weißes Platzhalterbild aus einem Kurzbefehl heraus. Fehlt der Eintrag oder die Datei, ist ein Systembild die ehrlichere Antwort als ein Rateergebnis. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
import Testing
|
||||
import Foundation
|
||||
@testable import OnyxNotch
|
||||
|
||||
@Suite("Hintergrundbild aus macOS' Verwaltung")
|
||||
struct WallpaperIndexTests {
|
||||
|
||||
/// Baut einen Eintrag, wie ihn macOS ablegt.
|
||||
private func entry(_ path: String) -> [String: Any] {
|
||||
["type": "imageFile", "url": ["relative": "file://" + path]]
|
||||
}
|
||||
|
||||
private func nested(_ value: Any) throws -> Data {
|
||||
try PropertyListSerialization.data(fromPropertyList: value, format: .binary, options: 0)
|
||||
}
|
||||
|
||||
@Test("Ein Bild in der äußeren Liste wird gefunden")
|
||||
func findsPlainEntry() {
|
||||
var found: [URL] = []
|
||||
WallpaperLoader.collectImageFiles(["Desktop": entry("/tmp/a.jpg")], into: &found)
|
||||
#expect(found.map(\.path) == ["/tmp/a.jpg"])
|
||||
}
|
||||
|
||||
@Test("Ein Bild in einer verschachtelten Liste wird auch gefunden")
|
||||
func findsNestedEntry() throws {
|
||||
// Genau daran hängt es: die Konfiguration je Bildschirm steckt als
|
||||
// eigene Property-Liste in einem Datenfeld der äußeren.
|
||||
var found: [URL] = []
|
||||
let inner = try nested(entry("/tmp/b.jpg"))
|
||||
WallpaperLoader.collectImageFiles(
|
||||
["Displays": ["ABC": ["Desktop": ["Configuration": inner]]]], into: &found)
|
||||
#expect(found.map(\.path) == ["/tmp/b.jpg"])
|
||||
}
|
||||
|
||||
@Test("Mehrere Bildschirme ergeben mehrere Bilder")
|
||||
func findsAllScreens() throws {
|
||||
var found: [URL] = []
|
||||
WallpaperLoader.collectImageFiles(
|
||||
["Displays": ["A": try nested(entry("/tmp/a.jpg")),
|
||||
"B": try nested(entry("/tmp/b.jpg"))]], into: &found)
|
||||
#expect(Set(found.map(\.path)) == ["/tmp/a.jpg", "/tmp/b.jpg"])
|
||||
}
|
||||
|
||||
@Test("Einträge ohne Datei bleiben liegen")
|
||||
func ignoresNonFileEntries() {
|
||||
// Fotos und Aerials stehen als „asset" mit einer Kennung darin — daraus
|
||||
// lässt sich keine Datei machen, und ein Eintrag ohne Datei darf nicht
|
||||
// als Bild durchgehen.
|
||||
var found: [URL] = []
|
||||
WallpaperLoader.collectImageFiles(
|
||||
["Idle": ["type": "asset", "identifier": "3316307E-788F"],
|
||||
"Web": ["type": "imageFile", "url": ["relative": "https://example.com/a.jpg"]]],
|
||||
into: &found)
|
||||
#expect(found.isEmpty)
|
||||
}
|
||||
|
||||
@Test("Nur der eigene Bildschirm zählt")
|
||||
func onlyOwnScreenCounts() throws {
|
||||
// In der Datei stehen auch alte Bildschirme und der Bildschirmschoner.
|
||||
// Das Neueste von allem zu nehmen führt zuverlässig zu einem Bild, das
|
||||
// mit dem Schreibtisch nichts zu tun hat — gemessen war das hier ein
|
||||
// weißes Platzhalterbild aus einem Kurzbefehl.
|
||||
var found: [URL] = []
|
||||
let entry: [String: Any] = ["Desktop": ["Content": ["Choices": [
|
||||
["Configuration": try nested(entry("/tmp/richtig.jpg"))]]]]]
|
||||
WallpaperLoader.collectImageFiles(entry["Desktop"] as Any, into: &found)
|
||||
#expect(found.map(\.path) == ["/tmp/richtig.jpg"])
|
||||
}
|
||||
|
||||
@Test("Unbrauchbare Daten stürzen nicht ab")
|
||||
func survivesGarbage() {
|
||||
var found: [URL] = []
|
||||
WallpaperLoader.collectImageFiles(
|
||||
["A": Data("bplist00 aber kaputt".utf8), "B": 42, "C": Date()], into: &found)
|
||||
#expect(found.isEmpty)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user