Update Volume listener for ddc controls

This commit is contained in:
Hariharan Mudaliar 2026-03-15 12:45:10 +05:30
commit 68cb3fd2de
No known key found for this signature in database
GPG key ID: 025583EB0BE5640E
6 changed files with 74 additions and 18 deletions

View file

@ -119,6 +119,10 @@ final class BetterDisplayManager: ObservableObject {
Defaults[.enableThirdPartyDDCIntegration] && Defaults[.thirdPartyDDCProvider] == .betterDisplay
}
private var isExternalVolumeListenerEnabled: Bool {
isBetterDisplayIntegrationEnabled && Defaults[.enableExternalVolumeControlListener]
}
private init() {
isDetected = Self.checkInstallation()
isRunning = Self.checkRunning()
@ -239,6 +243,7 @@ final class BetterDisplayManager: ObservableObject {
let isExternalDisplay = isExternal(displayID: osd.displayID, resolvedScreen: targetScreen)
let inferredMute = osd.controlTarget == "mute" || osd.systemIconID == 4
let hasVolumeData = category == .volume && osd.value != nil
let externalVolumeListenerEnabled = isExternalVolumeListenerEnabled
let targetText = osd.controlTarget ?? "nil"
let displayIDText = osd.displayID.map { String($0) } ?? "nil"
let resolvedScreenText = targetScreen?.localizedName ?? "nil"
@ -247,7 +252,7 @@ final class BetterDisplayManager: ObservableObject {
let normalizedText = String(format: "%.3f", normalizedValue)
NSLog(
"📺 BetterDisplay routed payload: category=\(categoryName(category)) target=\(targetText) displayID=\(displayIDText) resolvedScreen=\(resolvedScreenText) isExternal=\(isExternalDisplay) rawValue=\(valueText) maxValue=\(maxValueText) normalized=\(normalizedText) hasVolumeData=\(hasVolumeData) inferredMute=\(inferredMute)"
"📺 BetterDisplay routed payload: category=\(categoryName(category)) target=\(targetText) displayID=\(displayIDText) resolvedScreen=\(resolvedScreenText) isExternal=\(isExternalDisplay) rawValue=\(valueText) maxValue=\(maxValueText) normalized=\(normalizedText) hasVolumeData=\(hasVolumeData) inferredMute=\(inferredMute) externalVolumeListener=\(externalVolumeListenerEnabled)"
)
switch category {
@ -256,6 +261,10 @@ final class BetterDisplayManager: ObservableObject {
dispatchBrightnessHUD(value: normalizedValue, customSymbol: icon, onScreen: targetScreen)
case .volume:
guard externalVolumeListenerEnabled else {
NSLog("📺 BetterDisplay volume payload ignored because external volume listener is disabled")
return
}
let isMuted = osd.controlTarget == "mute" || osd.systemIconID == 4
dispatchVolumeHUD(value: normalizedValue, isMuted: isMuted, onScreen: targetScreen)

View file

@ -91,6 +91,10 @@ final class LunarManager: ObservableObject {
Defaults[.enableThirdPartyDDCIntegration] && Defaults[.thirdPartyDDCProvider] == .lunar
}
private var isExternalVolumeListenerEnabled: Bool {
isLunarIntegrationEnabled && Defaults[.enableExternalVolumeControlListener]
}
private init() {
isDetected = Self.checkInstallation()
isRunning = Self.checkRunning()
@ -293,6 +297,7 @@ final class LunarManager: ObservableObject {
let targetScreen = resolveScreen(for: data.display)
let isExternal = isExternalDisplay(data.display, resolvedScreen: targetScreen)
let hasVolumeData = data.volume != nil || data.mute != nil
let externalVolumeListenerEnabled = isExternalVolumeListenerEnabled
let resolvedScreenText = targetScreen?.localizedName ?? "nil"
let brightnessText = data.brightness.map { String($0) } ?? "nil"
let contrastText = data.contrast.map { String($0) } ?? "nil"
@ -300,7 +305,7 @@ final class LunarManager: ObservableObject {
let muteText = data.mute.map { String($0) } ?? "nil"
NSLog(
"🌙 Lunar routed payload: display=\(data.display) resolvedScreen=\(resolvedScreenText) isExternal=\(isExternal) brightness=\(brightnessText) contrast=\(contrastText) volume=\(volumeText) mute=\(muteText) hasVolumeData=\(hasVolumeData)"
"🌙 Lunar routed payload: display=\(data.display) resolvedScreen=\(resolvedScreenText) isExternal=\(isExternal) brightness=\(brightnessText) contrast=\(contrastText) volume=\(volumeText) mute=\(muteText) hasVolumeData=\(hasVolumeData) externalVolumeListener=\(externalVolumeListenerEnabled)"
)
// Nits-only events are informational (no user-facing HUD needed).
@ -321,13 +326,17 @@ final class LunarManager: ObservableObject {
}
// Volume
if let volume = data.volume {
let value = CGFloat(volume)
let isMuted = data.mute ?? false
dispatchVolumeHUD(value: value, isMuted: isMuted, onScreen: targetScreen)
} else if let mute = data.mute {
// Mute toggle without a volume value
dispatchVolumeHUD(value: mute ? 0 : 1, isMuted: mute, onScreen: targetScreen)
if externalVolumeListenerEnabled {
if let volume = data.volume {
let value = CGFloat(volume)
let isMuted = data.mute ?? false
dispatchVolumeHUD(value: value, isMuted: isMuted, onScreen: targetScreen)
} else if let mute = data.mute {
// Mute toggle without a volume value
dispatchVolumeHUD(value: mute ? 0 : 1, isMuted: mute, onScreen: targetScreen)
}
} else if hasVolumeData {
NSLog("🌙 Lunar volume payload ignored because external volume listener is disabled")
}
}

View file

@ -195,6 +195,13 @@ class SystemHUDManager {
await self.startSystemObserver()
}
}.store(in: &cancellables)
Defaults.publisher(.enableExternalVolumeControlListener, options: []).sink { [weak self] _ in
guard let self = self, self.isSetupComplete else { return }
Task { @MainActor in
await self.startSystemObserver()
}
}.store(in: &cancellables)
}
private var cancellables = Set<AnyCancellable>()
@ -227,7 +234,7 @@ class SystemHUDManager {
}
}
/// Resolves the effective control flags, applying BetterDisplay overrides.
/// Resolves the effective control flags, applying third-party DDC overrides.
private func resolvedControlFlags() -> (volume: Bool, brightness: Bool, backlight: Bool) {
var volumeEnabled: Bool
var brightnessEnabled: Bool
@ -252,6 +259,11 @@ class SystemHUDManager {
if Defaults[.enableThirdPartyDDCIntegration] {
brightnessEnabled = false
keyboardBacklightEnabled = false
// Optional: route volume exclusively from the selected external provider.
if Defaults[.enableExternalVolumeControlListener] {
volumeEnabled = false
}
}
return (volumeEnabled, brightnessEnabled, keyboardBacklightEnabled)
@ -280,7 +292,7 @@ class SystemHUDManager {
// Force disable system HUD to ensure no duplicates
SystemOSDManager.disableSystemHUD()
print("System observer started (HUD: \(Defaults[.enableSystemHUD]), OSD: \(Defaults[.enableCustomOSD]), Vertical: \(Defaults[.enableVerticalHUD]), ThirdPartyDDC: \(Defaults[.enableThirdPartyDDCIntegration]), Provider: \(Defaults[.thirdPartyDDCProvider].displayName))")
print("System observer started (HUD: \(Defaults[.enableSystemHUD]), OSD: \(Defaults[.enableCustomOSD]), Vertical: \(Defaults[.enableVerticalHUD]), ThirdPartyDDC: \(Defaults[.enableThirdPartyDDCIntegration]), Provider: \(Defaults[.thirdPartyDDCProvider].displayName), ExternalVolumeListener: \(Defaults[.enableExternalVolumeControlListener]))")
isSystemOperationInProgress = false
}