From 2a816b8da8c285a8cf6ddd22988384fdcccc8012 Mon Sep 17 00:00:00 2001 From: waydabber <37590873+waydabber@users.noreply.github.com> Date: Sat, 14 Aug 2021 00:43:53 +0200 Subject: [PATCH] Full ColorSync expert profile compatibility for software adjustments. Needs more testing. --- MonitorControl/AppDelegate.swift | 16 +++- MonitorControl/Info.plist | 2 +- MonitorControl/Manager/DisplayManager.swift | 3 +- MonitorControl/Model/Display.swift | 91 +++++++++++--------- MonitorControl/UI/Base.lproj/Main.storyboard | 44 ++++------ MonitorControl/UI/de.lproj/Main.strings | 3 - MonitorControl/UI/en.lproj/Main.strings | 3 - MonitorControl/UI/fr.lproj/Main.strings | 3 - MonitorControl/UI/hu.lproj/Main.strings | 3 - MonitorControl/UI/it.lproj/Main.strings | 3 - MonitorControl/UI/ja.lproj/Main.strings | 3 - MonitorControl/UI/pl.lproj/Main.strings | 3 - MonitorControl/UI/ru.lproj/Main.strings | 3 - MonitorControl/UI/uk.lproj/Main.strings | 3 - MonitorControl/UI/zh-Hans.lproj/Main.strings | 3 - MonitorControlHelper/Info.plist | 2 +- 16 files changed, 83 insertions(+), 105 deletions(-) diff --git a/MonitorControl/AppDelegate.swift b/MonitorControl/AppDelegate.swift index 96548e9..e61ae91 100644 --- a/MonitorControl/AppDelegate.swift +++ b/MonitorControl/AppDelegate.swift @@ -124,8 +124,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { } func displayReconfigured() { + self.reconfigureID += 1 + os_log("Bumping reconfigureID to %{public}@", type: .info, String(self.reconfigureID)) if self.sleepID == 0 { - self.reconfigureID += 1 let dispatchedReconfigureID = self.reconfigureID os_log("Display to be reconfigured with reconfigureID %{public}@", type: .info, String(dispatchedReconfigureID)) DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { @@ -255,6 +256,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { NotificationCenter.default.addObserver(self, selector: #selector(handleFriendlyNameChanged), name: .friendlyName, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(handlePreferenceReset), name: .preferenceReset, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(audioDeviceChanged), name: Notification.Name.defaultOutputDeviceChanged, object: nil) // subscribe Audio output detector (SimplyCoreAudio) + NotificationCenter.default.addObserver(self, selector: #selector(colorSyncSettingsChanged), name: NSNotification.Name(rawValue: kColorSyncDisplayDeviceProfilesNotification.takeRetainedValue() as String), object: nil) // ColorSync change + NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(self.sleepNotification), name: NSWorkspace.screensDidSleepNotification, object: nil) // sleep and wake listeners NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(self.wakeNotofication), name: NSWorkspace.screensDidWakeNotification, object: nil) NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(self.sleepNotification), name: NSWorkspace.willSleepNotification, object: nil) @@ -284,7 +287,11 @@ class AppDelegate: NSObject, NSApplicationDelegate { if self.sleepID == dispatchedSleepID { os_log("Sober from sleep %{public}@", type: .info, String(self.sleepID)) self.sleepID = 0 - self.updateDisplays() + if self.reconfigureID != 0 { + let dispatchedReconfigureID = self.reconfigureID + os_log("Display needs reconfig after sober with reconfigureID %{public}@", type: .info, String(dispatchedReconfigureID)) + self.updateDisplays(dispatchedReconfigureID: dispatchedReconfigureID) + } } } @@ -480,4 +487,9 @@ extension AppDelegate: MediaKeyTapDelegate { #endif self.updateMediaKeyTap() } + + @objc private func colorSyncSettingsChanged() { + // We should perform a protected colorsync reset here using CGDisplayRestoreColorSyncSettings(), black bug check and recovery if needed + // Afterwards we should perform a swUpdateDefaultGammaTable() for every display + } } diff --git a/MonitorControl/Info.plist b/MonitorControl/Info.plist index d159dbb..bed639f 100644 --- a/MonitorControl/Info.plist +++ b/MonitorControl/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 2156 + 2240 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/MonitorControl/Manager/DisplayManager.swift b/MonitorControl/Manager/DisplayManager.swift index 2a819cd..f8c4f5b 100644 --- a/MonitorControl/Manager/DisplayManager.swift +++ b/MonitorControl/Manager/DisplayManager.swift @@ -87,8 +87,7 @@ class DisplayManager { func restoreSwBrightnessForAllDisplays() { for externalDisplay in self.getExternalDisplays() { if externalDisplay.getValue(for: .brightness) == 0 || externalDisplay.isSw() { - // Out of caution we won't let it restore to complete darkness, not to interfere with login, etc. This is how Apple devices work as well. - _ = externalDisplay.setSwBrightness(value: UInt8(max(externalDisplay.getSwBrightnessPrefValue(), 20))) + _ = externalDisplay.setSwBrightness(value: UInt8(externalDisplay.getSwBrightnessPrefValue())) } else { _ = externalDisplay.setSwBrightness(value: externalDisplay.getSwMaxBrightness()) } diff --git a/MonitorControl/Model/Display.swift b/MonitorControl/Model/Display.swift index fbcc836..7e0a4da 100644 --- a/MonitorControl/Model/Display.swift +++ b/MonitorControl/Model/Display.swift @@ -32,6 +32,12 @@ class Display { var isVirtual: Bool = false + var defaultGammaTableRed = [CGGammaValue](repeating: 0, count: 256) + var defaultGammaTableGreen = [CGGammaValue](repeating: 0, count: 256) + var defaultGammaTableBlue = [CGGammaValue](repeating: 0, count: 256) + var defaultGammaTableSampleCount: UInt32 = 0 + var defaultGammaTablePeak: Float = 1 + private let prefs = UserDefaults.standard internal init(_ identifier: CGDirectDisplayID, name: String, vendorNumber: UInt32?, modelNumber: UInt32?, isVirtual: Bool = false) { @@ -40,6 +46,7 @@ class Display { self.vendorNumber = vendorNumber self.modelNumber = modelNumber self.isVirtual = isVirtual + self.swUpdateDefaultGammaTable() } func stepBrightness(isUp _: Bool, isSmallIncrement _: Bool) {} @@ -69,8 +76,16 @@ class Display { return self.identifier } + func swUpdateDefaultGammaTable() { + CGGetDisplayTransferByTable(self.identifier, 256, &self.defaultGammaTableRed, &self.defaultGammaTableGreen, &self.defaultGammaTableBlue, &self.defaultGammaTableSampleCount) + let redPeak = self.defaultGammaTableRed.max() ?? 0 + let greenPeak = self.defaultGammaTableGreen.max() ?? 0 + let bluePeak = self.defaultGammaTableBlue.max() ?? 0 + self.defaultGammaTablePeak = max(redPeak, greenPeak, bluePeak) + } + func swBrightnessTransform(value: Float, reverse: Bool = false) -> Float { - let lowTreshold: Float = 0.05 // We don't allow decrease lower than 5% for safety reasons and because some displays blank off after a while on full screen black + let lowTreshold: Float = 0.05 // We don't allow decrease lower than 5% for safety reasons and because some displays blank off after a while on full black screen due to energy saving settings if !reverse { return value * (1 - lowTreshold) + lowTreshold } else { @@ -78,50 +93,44 @@ class Display { } } - func setSwBrightness(value: UInt8, fast: Bool = false) -> Bool { - var redMin: CGGammaValue = 0 - var redMax: CGGammaValue = 0 - var redGamma: CGGammaValue = 0 - var greenMin: CGGammaValue = 0 - var greenMax: CGGammaValue = 0 - var greenGamma: CGGammaValue = 0 - var blueMin: CGGammaValue = 0 - var blueMax: CGGammaValue = 0 - var blueGamma: CGGammaValue = 0 + func setSwBrightness(value: UInt8, fast _: Bool = false) -> Bool { let brightnessValue: UInt8 = min(getSwMaxBrightness(), value) - var floatValue = Float(Float(brightnessValue) / Float(self.getSwMaxBrightness())) - floatValue = self.swBrightnessTransform(value: floatValue) - os_log("setting software brightness to: %{public}@", type: .debug, String(floatValue)) - if CGGetDisplayTransferByFormula(self.identifier, &redMin, &redMax, &redGamma, &greenMin, &greenMax, &greenGamma, &blueMin, &blueMax, &blueGamma) == CGError.success { - if !fast { - DispatchQueue.global(qos: .userInitiated).async { - for value in stride(from: redMax, to: floatValue, by: 0.0025 * (redMax > floatValue ? -1 : 1)) { - CGSetDisplayTransferByFormula(self.identifier, 0, value, redGamma, 0, value, greenGamma, 0, value, blueGamma) - Thread.sleep(forTimeInterval: 0.001) - } - } - } else { - CGSetDisplayTransferByFormula(self.identifier, 0, floatValue, redGamma, 0, floatValue, greenGamma, 0, floatValue, blueGamma) - } - self.saveSwBirghtnessPrefValue(Int(brightnessValue)) - return true - } - return false + var currentValue = Float(self.getSwBrightness()) / Float(self.getSwMaxBrightness()) +// var currentValue = Float(self.getSwBrightnessPrefValue()) / Float(self.getSwMaxBrightness()) // Better for async version but is not right after display reconfiguration + self.saveSwBirghtnessPrefValue(Int(brightnessValue)) + var newValue = Float(Float(brightnessValue)) / Float(self.getSwMaxBrightness()) + currentValue = self.swBrightnessTransform(value: currentValue) + newValue = self.swBrightnessTransform(value: newValue) + os_log("setting software brightness to: %{public}@", type: .debug, String(newValue)) + let gammaTableRed = self.defaultGammaTableRed.map { $0 * /* transientValue */ newValue } + let gammaTableGreen = self.defaultGammaTableGreen.map { $0 * /* transientValue */ newValue } + let gammaTableBlue = self.defaultGammaTableBlue.map { $0 * /* transientValue */ newValue } + CGSetDisplayTransferByTable(self.identifier, self.defaultGammaTableSampleCount, gammaTableRed, gammaTableGreen, gammaTableBlue) +// DispatchQueue.global(qos: .userInitiated).async { +// for transientValue in stride(from: currentValue, to: newValue, by: 0.0025 * (currentValue > newValue ? -1 : 1)) { +// let gammaTableRed = self.defaultGammaTableRed.map { $0 * transientValue } +// let gammaTableGreen = self.defaultGammaTableGreen.map { $0 * transientValue } +// let gammaTableBlue = self.defaultGammaTableBlue.map { $0 * transientValue } +// CGSetDisplayTransferByTable(self.identifier, self.defaultGammaTableSampleCount, gammaTableRed, gammaTableGreen, gammaTableBlue) +// Thread.sleep(forTimeInterval: 0.0005) // This will make things a bit nicer and mimic delay of DDC communication for consistency +// } +// } + return true } func getSwBrightness() -> UInt8 { - var redMin: CGGammaValue = 0 - var redMax: CGGammaValue = 0 - var redGamma: CGGammaValue = 0 - var greenMin: CGGammaValue = 0 - var greenMax: CGGammaValue = 0 - var greenGamma: CGGammaValue = 0 - var blueMin: CGGammaValue = 0 - var blueMax: CGGammaValue = 0 - var blueGamma: CGGammaValue = 0 - if CGGetDisplayTransferByFormula(self.identifier, &redMin, &redMax, &redGamma, &greenMin, &greenMax, &greenGamma, &blueMin, &blueMax, &blueGamma) == CGError.success { - let brightnessValue = UInt8(round(swBrightnessTransform(value: max(redMax, greenMax, blueMax), reverse: true) * Float(self.getSwMaxBrightness()))) - os_log("Current read software brightness is: %{public}@", type: .debug, String(brightnessValue)) + var gammaTableRed = [CGGammaValue](repeating: 0, count: 256) + var gammaTableGreen = [CGGammaValue](repeating: 0, count: 256) + var gammaTableBlue = [CGGammaValue](repeating: 0, count: 256) + var gammaTableSampleCount: UInt32 = 0 + if CGGetDisplayTransferByTable(self.identifier, 256, &gammaTableRed, &gammaTableGreen, &gammaTableBlue, &gammaTableSampleCount) == CGError.success { + let redPeak = gammaTableRed.max() ?? 0 + let greenPeak = gammaTableGreen.max() ?? 0 + let bluePeak = gammaTableBlue.max() ?? 0 + let gammaTablePeak = max(redPeak, greenPeak, bluePeak) + let peakRatio = gammaTablePeak / self.defaultGammaTablePeak + let brightnessValue = UInt8(round(self.swBrightnessTransform(value: peakRatio, reverse: true) * Float(self.getSwMaxBrightness()))) + os_log("Current software gammatable brightness is: %{public}@", type: .debug, String(brightnessValue)) return brightnessValue } return self.getSwMaxBrightness() diff --git a/MonitorControl/UI/Base.lproj/Main.storyboard b/MonitorControl/UI/Base.lproj/Main.storyboard index 54db928..511d088 100644 --- a/MonitorControl/UI/Base.lproj/Main.storyboard +++ b/MonitorControl/UI/Base.lproj/Main.storyboard @@ -309,10 +309,10 @@ - + - + @@ -325,7 +325,6 @@ - @@ -338,7 +337,7 @@ - + @@ -348,7 +347,7 @@