Neben ihr wären die Arme zwangsläufig gleich breit — die Silhouette zeichnet den Steg mittig um die Hardware. Ein Symbol links und ein Balken rechts hieße dann 132 Punkte Schwarz für ein Symbol. Darunter hängt die Fläche frei und wird so breit, wie ihr Inhalt es braucht. Lautstärke, Strom, Titelwechsel und Helligkeit. Gemeldet wird nur, was sich **geändert** hat: `PowerWatcher` schweigt beim ersten gelesenen Zustand, sonst begrüßte Onyx jeden Start mit „am Netz". Die Helligkeit kostete einen Spike. Von drei Wegen trägt nur `DisplayServices`, und nur für den eingebauten Bildschirm. `CoreDisplay` — der Weg, den jede Anleitung empfiehlt — gibt für alle Bildschirme 1.0 zurück, auch für den, der auf 62 % stand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
3.1 KiB
Swift
82 lines
3.1 KiB
Swift
import AppKit
|
|
import IOKit
|
|
|
|
// Gibt es einen benutzbaren Weg, die Bildschirmhelligkeit zu *lesen* — und
|
|
// besser noch: auf ihre Änderung zu *hören*?
|
|
//
|
|
// Öffentlich gibt es keinen. Geprüft werden deshalb drei private bzw.
|
|
// halböffentliche Wege, jeder einzeln, damit hinterher klar ist, welcher trägt
|
|
// und welcher nur auf manchen Geräten funktioniert.
|
|
|
|
let displays: [CGDirectDisplayID] = NSScreen.screens.compactMap {
|
|
$0.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID
|
|
}
|
|
print("Bildschirme: \(displays)\n")
|
|
|
|
// MARK: - Weg 1: DisplayServices
|
|
|
|
typealias GetBrightness = @convention(c) (CGDirectDisplayID, UnsafeMutablePointer<Float>) -> Int32
|
|
typealias RegisterNotify = @convention(c) (CGDirectDisplayID, UnsafeRawPointer?, UnsafeRawPointer?) -> Int32
|
|
|
|
let dsPath = "/System/Library/PrivateFrameworks/DisplayServices.framework/DisplayServices"
|
|
if let handle = dlopen(dsPath, RTLD_LAZY) {
|
|
print("DisplayServices: geladen")
|
|
if let sym = dlsym(handle, "DisplayServicesGetBrightness") {
|
|
let fn = unsafeBitCast(sym, to: GetBrightness.self)
|
|
for id in displays {
|
|
var value: Float = -1
|
|
let status = fn(id, &value)
|
|
print(" GetBrightness(\(id)) → status \(status), Wert \(value)")
|
|
}
|
|
} else {
|
|
print(" DisplayServicesGetBrightness: nicht vorhanden")
|
|
}
|
|
print(" RegisterForBrightnessChangeNotifications: " +
|
|
(dlsym(handle, "DisplayServicesRegisterForBrightnessChangeNotifications") != nil
|
|
? "vorhanden" : "FEHLT"))
|
|
} else {
|
|
print("DisplayServices: nicht ladbar — \(String(cString: dlerror()))")
|
|
}
|
|
|
|
// MARK: - Weg 2: CoreDisplay
|
|
|
|
typealias UserBrightness = @convention(c) (CGDirectDisplayID) -> Double
|
|
|
|
let cdPath = "/System/Library/Frameworks/CoreDisplay.framework/CoreDisplay"
|
|
if let handle = dlopen(cdPath, RTLD_LAZY) {
|
|
print("\nCoreDisplay: geladen")
|
|
if let sym = dlsym(handle, "CoreDisplay_Display_GetUserBrightness") {
|
|
let fn = unsafeBitCast(sym, to: UserBrightness.self)
|
|
for id in displays {
|
|
print(" GetUserBrightness(\(id)) → \(fn(id))")
|
|
}
|
|
} else {
|
|
print(" CoreDisplay_Display_GetUserBrightness: nicht vorhanden")
|
|
}
|
|
} else {
|
|
print("\nCoreDisplay: nicht ladbar")
|
|
}
|
|
|
|
// MARK: - Weg 3: IOKit
|
|
|
|
print("\nIOKit IODisplayGetFloatParameter:")
|
|
var iterator = io_iterator_t()
|
|
let matching = IOServiceMatching("IODisplayConnect")
|
|
if IOServiceGetMatchingServices(kIOMainPortDefault, matching, &iterator) == KERN_SUCCESS {
|
|
var found = false
|
|
var service = IOIteratorNext(iterator)
|
|
while service != 0 {
|
|
found = true
|
|
var brightness: Float = -1
|
|
let status = IODisplayGetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString,
|
|
&brightness)
|
|
print(" Dienst \(service) → status \(status), Wert \(brightness)")
|
|
IOObjectRelease(service)
|
|
service = IOIteratorNext(iterator)
|
|
}
|
|
if !found { print(" kein IODisplayConnect gefunden (auf Apple Silicon üblich)") }
|
|
IOObjectRelease(iterator)
|
|
} else {
|
|
print(" IOServiceGetMatchingServices fehlgeschlagen")
|
|
}
|