From 6248d582ac6691c17a2a67d69b2a50e8c5811da0 Mon Sep 17 00:00:00 2001 From: Istvan T <37590873+waydabber@users.noreply.github.com> Date: Mon, 25 Oct 2021 19:40:52 +0200 Subject: [PATCH] Some additional fixes (#738) - Fix naming of wakeNotification() - Fix for failing to update Advanced Settings checkbox on Preferences Reset. - Better handling of known dummy displays. --- MonitorControl/Info.plist | 2 +- MonitorControl/Model/AppleDisplay.swift | 16 +++++++++-- MonitorControl/Model/Display.swift | 28 +++++++++++++++---- MonitorControl/Model/OtherDisplay.swift | 6 ++-- MonitorControl/Support/AppDelegate.swift | 6 ++-- MonitorControl/Support/Arm64DDC.swift | 4 +++ MonitorControl/Support/DisplayManager.swift | 17 +++++++---- .../DisplaysPrefsViewController.swift | 14 ++++++---- .../MainPrefsViewController.swift | 1 + MonitorControlHelper/Info.plist | 2 +- 10 files changed, 70 insertions(+), 26 deletions(-) diff --git a/MonitorControl/Info.plist b/MonitorControl/Info.plist index 05c4621..4b77b24 100644 --- a/MonitorControl/Info.plist +++ b/MonitorControl/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 6828 + 6850 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/MonitorControl/Model/AppleDisplay.swift b/MonitorControl/Model/AppleDisplay.swift index 6f71d49..68dd948 100644 --- a/MonitorControl/Model/AppleDisplay.swift +++ b/MonitorControl/Model/AppleDisplay.swift @@ -6,18 +6,24 @@ import os.log class AppleDisplay: Display { private var displayQueue: DispatchQueue - override init(_ identifier: CGDirectDisplayID, name: String, vendorNumber: UInt32?, modelNumber: UInt32?, isVirtual: Bool = false) { + override init(_ identifier: CGDirectDisplayID, name: String, vendorNumber: UInt32?, modelNumber: UInt32?, isVirtual: Bool = false, isDummy: Bool = false) { self.displayQueue = DispatchQueue(label: String("displayQueue-\(identifier)")) - super.init(identifier, name: name, vendorNumber: vendorNumber, modelNumber: modelNumber, isVirtual: isVirtual) + super.init(identifier, name: name, vendorNumber: vendorNumber, modelNumber: modelNumber, isVirtual: isVirtual, isDummy: isDummy) } public func getAppleBrightness() -> Float { + guard !self.isDummy else { + return 1 + } var brightness: Float = 0 DisplayServicesGetBrightness(self.identifier, &brightness) return brightness } public func setAppleBrightness(value: Float) { + guard !self.isDummy else { + return + } self.displayQueue.sync { DisplayServicesSetBrightness(self.identifier, value) DisplayServicesBrightnessChanged(self.identifier, Double(value)) @@ -25,6 +31,9 @@ class AppleDisplay: Display { } override func setDirectBrightness(_ to: Float, transient: Bool = false) -> Bool { + guard !self.isDummy else { + return false + } let value = max(min(to, 1), 0) self.setAppleBrightness(value: value) if !transient { @@ -36,6 +45,9 @@ class AppleDisplay: Display { } override func getBrightness() -> Float { + guard !self.isDummy else { + return 1 + } if self.prefExists(for: .brightness) { return self.readPrefAsFloat(for: .brightness) } else { diff --git a/MonitorControl/Model/Display.swift b/MonitorControl/Model/Display.swift index a3b30f7..b68e518 100644 --- a/MonitorControl/Model/Display.swift +++ b/MonitorControl/Model/Display.swift @@ -22,6 +22,7 @@ class Display: Equatable { var sliderHandler: [Command: SliderHandler] = [:] var brightnessSyncSourceValue: Float = 1 var isVirtual: Bool = false + var isDummy: Bool = false var defaultGammaTableRed = [CGGammaValue](repeating: 0, count: 256) var defaultGammaTableGreen = [CGGammaValue](repeating: 0, count: 256) @@ -61,7 +62,7 @@ class Display: Equatable { return (key ?? PrefKey.value).rawValue + (command != nil ? String((command ?? Command.none).rawValue) : "") + self.prefsId } - internal init(_ identifier: CGDirectDisplayID, name: String, vendorNumber: UInt32?, modelNumber: UInt32?, isVirtual: Bool = false) { + internal init(_ identifier: CGDirectDisplayID, name: String, vendorNumber: UInt32?, modelNumber: UInt32?, isVirtual: Bool = false, isDummy: Bool = false) { self.identifier = identifier self.name = name self.vendorNumber = vendorNumber @@ -69,13 +70,14 @@ class Display: Equatable { self.prefsId = "(" + String(name.filter { !$0.isWhitespace }) + String(vendorNumber ?? 0) + String(modelNumber ?? 0) + "@" + String(identifier) + ")" os_log("Display init with prefsIdentifier %{public}@", type: .info, self.prefsId) self.isVirtual = DEBUG_VIRTUAL ? true : isVirtual + self.isDummy = isDummy self.swUpdateDefaultGammaTable() self.smoothBrightnessTransient = self.getBrightness() - if self.isVirtual { - os_log("Creating or updating shade for virtual display %{public}@", type: .info, String(self.identifier)) + if self.isVirtual || self.readPrefAsBool(key: PrefKey.avoidGamma), !self.isDummy { + os_log("Creating or updating shade for display %{public}@", type: .info, String(self.identifier)) _ = DisplayManager.shared.updateShade(displayID: self.identifier) } else { - os_log("Destroying shade (if exists) for real display %{public}@", type: .info, String(self.identifier)) + os_log("Destroying shade (if exists) for display %{public}@", type: .info, String(self.identifier)) _ = DisplayManager.shared.destroyShade(displayID: self.identifier) } self.brightnessSyncSourceValue = self.getBrightness() @@ -187,6 +189,9 @@ class Display: Equatable { } func swUpdateDefaultGammaTable() { + guard !self.isDummy else { + return + } CGGetDisplayTransferByTable(self.identifier, 256, &self.defaultGammaTableRed, &self.defaultGammaTableGreen, &self.defaultGammaTableBlue, &self.defaultGammaTableSampleCount) let redPeak = self.defaultGammaTableRed.max() ?? 0 let greenPeak = self.defaultGammaTableGreen.max() ?? 0 @@ -210,6 +215,10 @@ class Display: Equatable { if !noPrefSave { self.savePref(brightnessValue, key: .SwBrightness) } + guard !self.isDummy else { + self.swBrightnessSemaphore.signal() + return true + } var newValue = brightnessValue currentValue = self.swBrightnessTransform(value: currentValue) newValue = self.swBrightnessTransform(value: newValue) @@ -249,6 +258,13 @@ class Display: Equatable { } func getSwBrightness() -> Float { + guard !self.isDummy else { + if self.prefExists(key: .SwBrightness) { + return self.readPrefAsFloat(key: .SwBrightness) + } else { + return 1 + } + } self.swBrightnessSemaphore.wait() if self.isVirtual || self.readPrefAsBool(key: .avoidGamma) { let rawBrightnessValue = 1 - (DisplayManager.shared.getShadeAlpha(displayID: self.identifier) ?? 1) @@ -274,7 +290,7 @@ class Display: Equatable { func checkGammaInterference() { let currentSwBrightness = self.getSwBrightness() - guard !DisplayManager.shared.gammaInterferenceWarningShown, !(prefs.bool(forKey: PrefKey.disableCombinedBrightness.rawValue)), !self.readPrefAsBool(key: .avoidGamma), !self.isVirtual, !self.smoothBrightnessRunning, self.prefExists(key: .SwBrightness), abs(currentSwBrightness - self.readPrefAsFloat(key: .SwBrightness)) > 0.02 else { + guard !self.isDummy, !DisplayManager.shared.gammaInterferenceWarningShown, !(prefs.bool(forKey: PrefKey.disableCombinedBrightness.rawValue)), !self.readPrefAsBool(key: .avoidGamma), !self.isVirtual, !self.smoothBrightnessRunning, self.prefExists(key: .SwBrightness), abs(currentSwBrightness - self.readPrefAsFloat(key: .SwBrightness)) > 0.02 else { return } DisplayManager.shared.gammaInterferenceCounter += 1 @@ -309,7 +325,7 @@ class Display: Equatable { } func isSwBrightnessNotDefault() -> Bool { - guard !self.isVirtual else { + guard !self.isVirtual, !self.isDummy else { return false } if self.getSwBrightness() < 1 { diff --git a/MonitorControl/Model/OtherDisplay.swift b/MonitorControl/Model/OtherDisplay.swift index 2eebc69..f95afcc 100644 --- a/MonitorControl/Model/OtherDisplay.swift +++ b/MonitorControl/Model/OtherDisplay.swift @@ -23,8 +23,8 @@ class OtherDisplay: Display { set { prefs.set(newValue, forKey: PrefKey.pollingCount.rawValue + self.prefsId) } } - override init(_ identifier: CGDirectDisplayID, name: String, vendorNumber: UInt32?, modelNumber: UInt32?, isVirtual: Bool = false) { - super.init(identifier, name: name, vendorNumber: vendorNumber, modelNumber: modelNumber, isVirtual: isVirtual) + override init(_ identifier: CGDirectDisplayID, name: String, vendorNumber: UInt32?, modelNumber: UInt32?, isVirtual: Bool = false, isDummy: Bool = false) { + super.init(identifier, name: name, vendorNumber: vendorNumber, modelNumber: modelNumber, isVirtual: isVirtual, isDummy: isDummy) if !isVirtual, !Arm64DDC.isArm64 { self.ddc = IntelDDC(for: identifier) } @@ -256,7 +256,7 @@ class OtherDisplay: Display { } func isSwOnly() -> Bool { - return (!self.arm64ddc && self.ddc == nil) || self.isVirtual + return (!self.arm64ddc && self.ddc == nil) || self.isVirtual || self.isDummy } func isSw() -> Bool { diff --git a/MonitorControl/Support/AppDelegate.swift b/MonitorControl/Support/AppDelegate.swift index 24c1383..8b777fd 100644 --- a/MonitorControl/Support/AppDelegate.swift +++ b/MonitorControl/Support/AppDelegate.swift @@ -168,9 +168,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { NotificationCenter.default.addObserver(self, selector: #selector(self.audioDeviceChanged), name: Notification.Name.defaultOutputDeviceChanged, object: nil) // subscribe Audio output detector (SimplyCoreAudio) DistributedNotificationCenter.default.addObserver(self, selector: #selector(self.displayReconfigured), 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.wakeNotification), name: NSWorkspace.screensDidWakeNotification, object: nil) NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(self.sleepNotification), name: NSWorkspace.willSleepNotification, object: nil) - NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(self.wakeNotofication), name: NSWorkspace.didWakeNotification, object: nil) + NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(self.wakeNotification), name: NSWorkspace.didWakeNotification, object: nil) _ = DistributedNotificationCenter.default().addObserver(forName: NSNotification.Name(rawValue: NSNotification.Name.accessibilityApi.rawValue), object: nil, queue: nil) { _ in DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.updateMediaKeyTap() } } // listen for accessibility status changes } @@ -179,7 +179,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { os_log("Sleeping with sleep %{public}@", type: .info, String(self.sleepID)) } - @objc private func wakeNotofication() { + @objc private func wakeNotification() { if self.sleepID != 0 { os_log("Waking up from sleep %{public}@", type: .info, String(self.sleepID)) let dispatchedSleepID = self.sleepID diff --git a/MonitorControl/Support/Arm64DDC.swift b/MonitorControl/Support/Arm64DDC.swift index be78bf1..c08ae7f 100644 --- a/MonitorControl/Support/Arm64DDC.swift +++ b/MonitorControl/Support/Arm64DDC.swift @@ -271,6 +271,10 @@ class Arm64DDC: NSObject { if ioregService.manufacturerID == "AOC", ioregService.productName == "28E850" { return true } + // If the display contains the string "Dummy", then it is highly suspicious + if ioregService.productName.contains("Dummy") || ioregService.productName.contains("dummy") { + return true + } // First service location of Mac Mini HDMI is broken for DDC communication if ioregService.transportDownstream == "HDMI", ioregService.serviceLocation == 1, modelIdentifier == "Macmini9,1" { return true diff --git a/MonitorControl/Support/DisplayManager.swift b/MonitorControl/Support/DisplayManager.swift index 27b35ee..c98c476 100644 --- a/MonitorControl/Support/DisplayManager.swift +++ b/MonitorControl/Support/DisplayManager.swift @@ -151,27 +151,34 @@ class DisplayManager { return } for onlineDisplayID in onlineDisplayIDs where onlineDisplayID != 0 { + let rawName = DisplayManager.getDisplayRawNameByID(displayID: onlineDisplayID) let name = DisplayManager.getDisplayNameByID(displayID: onlineDisplayID) let id = onlineDisplayID let vendorNumber = CGDisplayVendorNumber(onlineDisplayID) let modelNumber = CGDisplayModelNumber(onlineDisplayID) + var isDummy: Bool = false var isVirtual: Bool = false + if rawName == "28E850" || rawName.lowercased().contains("dummy") { + os_log("NOTE: Display is a dummy!", type: .info) + isDummy = true + } if !DEBUG_MACOS10, #available(macOS 11.0, *) { if let dictionary = ((CoreDisplay_DisplayCreateInfoDictionary(onlineDisplayID))?.takeRetainedValue() as NSDictionary?) { let isVirtualDevice = dictionary["kCGDisplayIsVirtualDevice"] as? Bool let displayIsAirplay = dictionary["kCGDisplayIsAirPlay"] as? Bool if isVirtualDevice ?? displayIsAirplay ?? false { + os_log("NOTE: Display is virtual!", type: .info) isVirtual = true } } } if !DEBUG_SW, DisplayManager.isAppleDisplay(displayID: onlineDisplayID) { // MARK: (point of interest for testing) - let appleDisplay = AppleDisplay(id, name: name, vendorNumber: vendorNumber, modelNumber: modelNumber, isVirtual: isVirtual) - os_log("Apple display found - %{public}@", type: .info, "ID: \(appleDisplay.identifier) Name: \(appleDisplay.name) (Vendor: \(appleDisplay.vendorNumber ?? 0), Model: \(appleDisplay.modelNumber ?? 0))") + let appleDisplay = AppleDisplay(id, name: name, vendorNumber: vendorNumber, modelNumber: modelNumber, isVirtual: isVirtual, isDummy: isDummy) + os_log("Apple display found - %{public}@", type: .info, "ID: \(appleDisplay.identifier), Name: \(appleDisplay.name) (Vendor: \(appleDisplay.vendorNumber ?? 0), Model: \(appleDisplay.modelNumber ?? 0))") self.addDisplay(display: appleDisplay) } else { - let otherDisplay = OtherDisplay(id, name: name, vendorNumber: vendorNumber, modelNumber: modelNumber, isVirtual: isVirtual) - os_log("Other display found - %{public}@", type: .info, "ID: \(otherDisplay.identifier) Name: \(otherDisplay.name) (Vendor: \(otherDisplay.vendorNumber ?? 0), Model: \(otherDisplay.modelNumber ?? 0))") + let otherDisplay = OtherDisplay(id, name: name, vendorNumber: vendorNumber, modelNumber: modelNumber, isVirtual: isVirtual, isDummy: isDummy) + os_log("Other display found - %{public}@", type: .info, "ID: \(otherDisplay.identifier), Name: \(otherDisplay.name) (Vendor: \(otherDisplay.vendorNumber ?? 0), Model: \(otherDisplay.modelNumber ?? 0))") self.addDisplay(display: otherDisplay) } } @@ -351,7 +358,7 @@ class DisplayManager { } } - func getAffectedDisplays(isBrightness: Bool = false, isVolume: Bool = false, isContrast _: Bool = false) -> [Display]? { + func getAffectedDisplays(isBrightness: Bool = false, isVolume: Bool = false) -> [Display]? { var affectedDisplays: [Display] let allDisplays = self.getAllDisplays() var currentDisplay: Display? diff --git a/MonitorControl/View Controllers/DisplaysPrefsViewController.swift b/MonitorControl/View Controllers/DisplaysPrefsViewController.swift index 67909de..6bfcf26 100644 --- a/MonitorControl/View Controllers/DisplaysPrefsViewController.swift +++ b/MonitorControl/View Controllers/DisplaysPrefsViewController.swift @@ -25,9 +25,13 @@ class DisplaysPrefsViewController: NSViewController, PreferencePane, NSTableView override func viewDidLoad() { super.viewDidLoad() - self.showAdvancedDisplays.state = prefs.bool(forKey: PrefKey.showAdvancedSettings.rawValue) ? .on : .off - self.loadDisplayList() self.displayScrollView.scrollerStyle = .legacy + self.populateSettings() + self.loadDisplayList() + } + + func populateSettings() { + self.showAdvancedDisplays.state = prefs.bool(forKey: PrefKey.showAdvancedSettings.rawValue) ? .on : .off } override func viewWillAppear() { @@ -88,12 +92,12 @@ class DisplaysPrefsViewController: NSViewController, PreferencePane, NSTableView var displayImage = "display.trianglebadge.exclamationmark" var controlMethod = NSLocalizedString("No Control", comment: "Shown in the Display Preferences") + " ⚠️" var controlStatus = NSLocalizedString("This display has an unspecified control status.", comment: "Shown in the Display Preferences") - if display.isVirtual { + if display.isVirtual, !display.isDummy { displayType = NSLocalizedString("Virtual Display", comment: "Shown in the Display Preferences") displayImage = "tv.and.mediabox" controlMethod = NSLocalizedString("Software (shade)", comment: "Shown in the Display Preferences") + " ⚠️" controlStatus = NSLocalizedString("This is a virtual display (examples: AirPlay, Sidecar, display connected via a DisplayLink Dock or similar) which does not allow hardware or software gammatable control. Shading is used as a substitute but only in non-mirror scenarios. Mouse cursor will be unaffected and artifacts may appear when entering/leaving full screen mode.", comment: "Shown in the Display Preferences") - } else if display is OtherDisplay { + } else if display is OtherDisplay, !display.isDummy { displayType = NSLocalizedString("External Display", comment: "Shown in the Display Preferences") displayImage = "display" if let otherDisplay: OtherDisplay = display as? OtherDisplay { @@ -119,7 +123,7 @@ class DisplaysPrefsViewController: NSViewController, PreferencePane, NSTableView } } } - } else if let appleDisplay: AppleDisplay = display as? AppleDisplay { + } else if !display.isDummy, let appleDisplay: AppleDisplay = display as? AppleDisplay { if appleDisplay.isBuiltIn() { displayType = NSLocalizedString("Built-in Display", comment: "Shown in the Display Preferences") if self.isImac() { diff --git a/MonitorControl/View Controllers/MainPrefsViewController.swift b/MonitorControl/View Controllers/MainPrefsViewController.swift index 52e9373..1f48f69 100644 --- a/MonitorControl/View Controllers/MainPrefsViewController.swift +++ b/MonitorControl/View Controllers/MainPrefsViewController.swift @@ -152,6 +152,7 @@ class MainPrefsViewController: NSViewController, PreferencePane { self.populateSettings() menuslidersPrefsVc?.populateSettings() keyboardPrefsVc?.populateSettings() + displaysPrefsVc?.populateSettings() } } diff --git a/MonitorControlHelper/Info.plist b/MonitorControlHelper/Info.plist index 2b798d7..5bfa29f 100644 --- a/MonitorControlHelper/Info.plist +++ b/MonitorControlHelper/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 6828 + 6850 LSApplicationCategoryType public.app-category.utilities LSBackgroundOnly