Zwei Anzeigen für einen Handgriff sind eine zu viel
Apples eigene Einblendung lässt sich aushängen, ohne Root und ohne SIP anzufassen: `com.apple.OSDUIHelper` liegt in der GUI-Domäne des angemeldeten Benutzers, nicht im System. `launchctl bootout` dort hinein genügt. Standardmäßig aus, denn es verschwindet **jede** Einblendung — auch Helligkeit und Feststelltaste, und für die Feststelltaste hat Onyx keinen Ersatz. Beim Beenden wird wieder eingehängt: eine Einstellung darf nicht überleben, was sie eingestellt hat. Dazu zwei Fehler im Anlernen behoben. Der Posenname stand als roher Schlüssel da — die Zeichenketten liegen im Bündel von `FaceProvider`, gesucht wurden sie im Hauptbündel. Und „Gesicht vergleichen" blieb nach dem Anlernen grau, weil `isEnrolled` bei jedem Aufruf den Schlüsselbund las: richtig, aber für SwiftUI unsichtbar, weil es nichts gab, woran eine Ansicht die Änderung hätte bemerken können. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
fd29df51cf
commit
a3399d3a96
@@ -0,0 +1,78 @@
|
||||
import Foundation
|
||||
import os
|
||||
|
||||
private let log = Logger(subsystem: "com.scarriffleservices.onyx", category: "OSD")
|
||||
|
||||
/// Apples eigene Einblendung für Lautstärke, Helligkeit und Feststelltaste.
|
||||
///
|
||||
/// Solange sie erscheint, zeigt der Mac bei jedem Lautstärkeschritt **zwei**
|
||||
/// Anzeigen: Apples Kasten in der Bildschirmmitte und die Live Activity unter
|
||||
/// der Notch. Eine davon ist eine zu viel.
|
||||
///
|
||||
/// **Wie es geht — und warum es ohne Root geht.** `com.apple.OSDUIHelper` ist
|
||||
/// ein LaunchAgent in der GUI-Domäne des angemeldeten Benutzers, nicht im
|
||||
/// System. `launchctl bootout gui/<uid>/…` hängt ihn dort aus. Die Datei selbst
|
||||
/// liegt unter SIP-Schutz und wird **nicht** angefasst; geändert wird nur, was
|
||||
/// in der eigenen Sitzung geladen ist. Siehe `docs/spikes/J-osd.md`.
|
||||
///
|
||||
/// **Und was das kostet.** Es verschwindet nicht nur die Lautstärkeanzeige,
|
||||
/// sondern jede: auch Helligkeit und Feststelltaste. Deshalb steht der Schalter
|
||||
/// standardmäßig aus, und deshalb wird beim Beenden wieder eingehängt — sonst
|
||||
/// säße jemand bis zum nächsten Abmelden ganz ohne Rückmeldung da, ohne zu
|
||||
/// wissen, warum.
|
||||
public enum SystemOSD {
|
||||
|
||||
private static let service = "com.apple.OSDUIHelper"
|
||||
|
||||
private static var domain: String { "gui/\(getuid())/\(service)" }
|
||||
|
||||
/// Ob der Dienst gerade ausgehängt ist.
|
||||
public static var isSuppressed: Bool {
|
||||
// `launchctl print` gibt für einen ausgehängten Dienst einen Fehler
|
||||
// zurück. Kein Fehler heißt: er ist da.
|
||||
run(["print", domain]) != 0
|
||||
}
|
||||
|
||||
/// Hängt die Einblendung aus. Wirkt bis zum nächsten Abmelden.
|
||||
@discardableResult
|
||||
public static func suppress() -> Bool {
|
||||
guard !isSuppressed else { return true }
|
||||
let status = run(["bootout", domain])
|
||||
if status != 0 {
|
||||
log.notice("OSD-Dienst nicht aushängbar: \(status, privacy: .public)")
|
||||
}
|
||||
return status == 0
|
||||
}
|
||||
|
||||
/// Hängt sie wieder ein.
|
||||
///
|
||||
/// Beim Beenden zu rufen. Ohne das bliebe der Mac bis zum nächsten Abmelden
|
||||
/// ohne jede Einblendung — auch für Helligkeit, mit der Onyx nichts zu tun
|
||||
/// hat.
|
||||
@discardableResult
|
||||
public static func restore() -> Bool {
|
||||
guard isSuppressed else { return true }
|
||||
let status = run(["bootstrap", "gui/\(getuid())",
|
||||
"/System/Library/LaunchAgents/\(service).plist"])
|
||||
if status != 0 {
|
||||
log.notice("OSD-Dienst nicht einhängbar: \(status, privacy: .public)")
|
||||
}
|
||||
return status == 0
|
||||
}
|
||||
|
||||
private static func run(_ arguments: [String]) -> Int32 {
|
||||
let task = Process()
|
||||
task.executableURL = URL(fileURLWithPath: "/bin/launchctl")
|
||||
task.arguments = arguments
|
||||
task.standardOutput = FileHandle.nullDevice
|
||||
task.standardError = FileHandle.nullDevice
|
||||
do {
|
||||
try task.run()
|
||||
task.waitUntilExit()
|
||||
return task.terminationStatus
|
||||
} catch {
|
||||
log.error("launchctl nicht startbar: \(error.localizedDescription, privacy: .public)")
|
||||
return -1
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user