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