Update per display windows for betterdisplay

This commit is contained in:
Hariharan Mudaliar 2026-03-03 00:41:49 +05:30
commit 12f8bc28ba
No known key found for this signature in database
GPG key ID: 025583EB0BE5640E
4 changed files with 99 additions and 59 deletions

View file

@ -190,6 +190,8 @@ final class BetterDisplayManager: ObservableObject {
return
}
NSLog("📺 BetterDisplay OSD raw payload: \(notificationString)")
do {
let osd = try JSONDecoder().decode(
BetterDisplayOSDNotification.self,
@ -208,21 +210,20 @@ final class BetterDisplayManager: ObservableObject {
let targetScreen = resolveScreen(for: osd.displayID)
let isExternalDisplay = isExternal(displayID: osd.displayID)
NSLog("📺 BetterDisplay OSD: target=\(osd.controlTarget ?? "nil") displayID=\(osd.displayID.map(String.init) ?? "nil") resolvedScreen=\(targetScreen?.localizedName ?? "nil") isExternal=\(isExternalDisplay) value=\(osd.value ?? -1)")
switch category {
case .brightness:
guard Defaults[.enableBrightnessHUD] else { return }
let icon = isExternalDisplay ? "display" : nil
dispatchBrightnessHUD(value: normalizedValue, customSymbol: icon, onScreen: targetScreen)
case .volume:
guard Defaults[.enableVolumeHUD] else { return }
let isMuted = osd.controlTarget == "mute" || osd.systemIconID == 4
dispatchVolumeHUD(value: normalizedValue, isMuted: isMuted, onScreen: targetScreen)
case .other:
// For unsupported control targets (contrast, gamma, temperature, etc.),
// show a generic brightness-style HUD if the user has brightness HUD enabled
guard Defaults[.enableBrightnessHUD] else { return }
let icon = isExternalDisplay ? "display" : osd.customSymbol
dispatchBrightnessHUD(value: normalizedValue, customSymbol: icon, onScreen: targetScreen)
}
@ -281,14 +282,26 @@ final class BetterDisplayManager: ObservableObject {
/// Resolve a BetterDisplay `displayID` (CGDirectDisplayID) to the matching NSScreen, if any.
private func resolveScreen(for displayID: Int?) -> NSScreen? {
guard let displayID else { return nil }
guard let displayID else {
NSLog("📺 BetterDisplay resolveScreen: displayID is nil, falling back to all screens")
return nil
}
let target = UInt32(displayID)
return NSScreen.screens.first { screen in
let availableScreens = NSScreen.screens.map { screen -> (String, UInt32) in
let num = (screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber)?.uint32Value ?? 0
return (screen.localizedName, num)
}
NSLog("📺 BetterDisplay resolveScreen: looking for displayID=\(displayID) (UInt32=\(target)) among screens: \(availableScreens)")
let matched = NSScreen.screens.first { screen in
guard let screenNumber = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber else {
return false
}
return screenNumber.uint32Value == target
}
if matched == nil {
NSLog("📺 BetterDisplay resolveScreen: no match found for displayID=\(displayID), HUD will show on all screens")
}
return matched
}
/// Whether the given displayID refers to an external (non-built-in) display.

View file

@ -108,70 +108,76 @@ class SystemHUDManager {
}.store(in: &cancellables)
// Observe individual HUD toggles
Defaults.publisher(.enableVolumeHUD, options: []).sink { [weak self] change in
Defaults.publisher(.enableVolumeHUD, options: []).sink { [weak self] _ in
guard let self = self, self.isSetupComplete, self.requiresSystemToggleHandling else {
return
}
let flags = self.resolvedControlFlags()
self.changesObserver?.update(
volumeEnabled: change.newValue,
brightnessEnabled: Defaults[.enableBrightnessHUD],
keyboardBacklightEnabled: Defaults[.enableKeyboardBacklightHUD]
volumeEnabled: flags.volume,
brightnessEnabled: flags.brightness,
keyboardBacklightEnabled: flags.backlight
)
}.store(in: &cancellables)
Defaults.publisher(.enableBrightnessHUD, options: []).sink { [weak self] change in
Defaults.publisher(.enableBrightnessHUD, options: []).sink { [weak self] _ in
guard let self = self, self.isSetupComplete, self.requiresSystemToggleHandling else {
return
}
let flags = self.resolvedControlFlags()
self.changesObserver?.update(
volumeEnabled: Defaults[.enableVolumeHUD],
brightnessEnabled: change.newValue,
keyboardBacklightEnabled: Defaults[.enableKeyboardBacklightHUD]
volumeEnabled: flags.volume,
brightnessEnabled: flags.brightness,
keyboardBacklightEnabled: flags.backlight
)
}.store(in: &cancellables)
Defaults.publisher(.enableKeyboardBacklightHUD, options: []).sink { [weak self] change in
Defaults.publisher(.enableKeyboardBacklightHUD, options: []).sink { [weak self] _ in
guard let self = self, self.isSetupComplete, self.requiresSystemToggleHandling else {
return
}
let flags = self.resolvedControlFlags()
self.changesObserver?.update(
volumeEnabled: Defaults[.enableVolumeHUD],
brightnessEnabled: Defaults[.enableBrightnessHUD],
keyboardBacklightEnabled: change.newValue
volumeEnabled: flags.volume,
brightnessEnabled: flags.brightness,
keyboardBacklightEnabled: flags.backlight
)
}.store(in: &cancellables)
// Observe individual OSD toggles
Defaults.publisher(.enableOSDVolume, options: []).sink { [weak self] change in
Defaults.publisher(.enableOSDVolume, options: []).sink { [weak self] _ in
guard let self = self, self.isSetupComplete, Defaults[.enableCustomOSD] else {
return
}
let flags = self.resolvedControlFlags()
self.changesObserver?.update(
volumeEnabled: change.newValue,
brightnessEnabled: Defaults[.enableOSDBrightness],
keyboardBacklightEnabled: Defaults[.enableOSDKeyboardBacklight]
volumeEnabled: flags.volume,
brightnessEnabled: flags.brightness,
keyboardBacklightEnabled: flags.backlight
)
}.store(in: &cancellables)
Defaults.publisher(.enableOSDBrightness, options: []).sink { [weak self] change in
Defaults.publisher(.enableOSDBrightness, options: []).sink { [weak self] _ in
guard let self = self, self.isSetupComplete, Defaults[.enableCustomOSD] else {
return
}
let flags = self.resolvedControlFlags()
self.changesObserver?.update(
volumeEnabled: Defaults[.enableOSDVolume],
brightnessEnabled: change.newValue,
keyboardBacklightEnabled: Defaults[.enableOSDKeyboardBacklight]
volumeEnabled: flags.volume,
brightnessEnabled: flags.brightness,
keyboardBacklightEnabled: flags.backlight
)
}.store(in: &cancellables)
Defaults.publisher(.enableOSDKeyboardBacklight, options: []).sink { [weak self] change in
Defaults.publisher(.enableOSDKeyboardBacklight, options: []).sink { [weak self] _ in
guard let self = self, self.isSetupComplete, Defaults[.enableCustomOSD] else {
return
}
let flags = self.resolvedControlFlags()
self.changesObserver?.update(
volumeEnabled: Defaults[.enableOSDVolume],
brightnessEnabled: Defaults[.enableOSDBrightness],
keyboardBacklightEnabled: change.newValue
volumeEnabled: flags.volume,
brightnessEnabled: flags.brightness,
keyboardBacklightEnabled: flags.backlight
)
}.store(in: &cancellables)
@ -214,22 +220,13 @@ class SystemHUDManager {
}
}
@MainActor
private func startSystemObserver() async {
guard let coordinator = coordinator, !isSystemOperationInProgress else { return }
isSystemOperationInProgress = true
await stopSystemObserver() // Stop any existing observer
changesObserver = SystemChangesObserver(coordinator: coordinator)
// Determine which controls to enable based on HUD or OSD mode
let volumeEnabled: Bool
let brightnessEnabled: Bool
/// Resolves the effective control flags, applying BetterDisplay overrides.
private func resolvedControlFlags() -> (volume: Bool, brightness: Bool, backlight: Bool) {
var volumeEnabled: Bool
var brightnessEnabled: Bool
var keyboardBacklightEnabled: Bool
if Defaults[.enableCircularHUD] || Defaults[.enableVerticalHUD] {
// Respect per-control toggles so the system HUD can handle disabled events
volumeEnabled = Defaults[.enableVolumeHUD]
brightnessEnabled = Defaults[.enableBrightnessHUD]
keyboardBacklightEnabled = Defaults[.enableKeyboardBacklightHUD]
@ -243,22 +240,40 @@ class SystemHUDManager {
keyboardBacklightEnabled = Defaults[.enableKeyboardBacklightHUD]
}
// When BetterDisplay integration is on, disable Cmd+Brightness key interception
// because BetterDisplay uses Cmd+F1/F2 for its own display controls.
// When BetterDisplay integration is on, stop intercepting brightness and
// Cmd+Brightness keys so BetterDisplay receives them and sends OSD notifications.
if Defaults[.enableBetterDisplayIntegration] {
brightnessEnabled = false
keyboardBacklightEnabled = false
}
return (volumeEnabled, brightnessEnabled, keyboardBacklightEnabled)
}
@MainActor
private func startSystemObserver() async {
guard let coordinator = coordinator, !isSystemOperationInProgress else { return }
isSystemOperationInProgress = true
// Stop any existing observer directly (cannot call stopSystemObserver()
// because isSystemOperationInProgress is already true).
changesObserver?.stopObserving()
changesObserver = nil
changesObserver = SystemChangesObserver(coordinator: coordinator)
let flags = resolvedControlFlags()
changesObserver?.startObserving(
volumeEnabled: volumeEnabled,
brightnessEnabled: brightnessEnabled,
keyboardBacklightEnabled: keyboardBacklightEnabled
volumeEnabled: flags.volume,
brightnessEnabled: flags.brightness,
keyboardBacklightEnabled: flags.backlight
)
// Force disable system HUD to ensure no duplicates
SystemOSDManager.disableSystemHUD()
print("System observer started (HUD: \(Defaults[.enableSystemHUD]), OSD: \(Defaults[.enableCustomOSD]), Vertical: \(Defaults[.enableVerticalHUD]))")
print("System observer started (HUD: \(Defaults[.enableSystemHUD]), OSD: \(Defaults[.enableCustomOSD]), Vertical: \(Defaults[.enableVerticalHUD]), BD: \(Defaults[.enableBetterDisplayIntegration]))")
isSystemOperationInProgress = false
}