Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ disabled_rules:
- identifier_name
- trailing_comma
type_body_length: 500
file_length: 500
file_length: 750
cyclomatic_complexity:
ignores_case_statements: true
opening_brace:
Expand Down
12 changes: 6 additions & 6 deletions MonitorControl.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -751,8 +751,8 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 4.0.0;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 4.0.1;
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControl;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -786,8 +786,8 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 4.0.0;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 4.0.1;
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControl;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -821,7 +821,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 4.0.1;
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControlHelper;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down Expand Up @@ -850,7 +850,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 4.0.1;
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControlHelper;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down
2 changes: 1 addition & 1 deletion MonitorControl/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>6851</string>
<string>6941</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
Expand Down
6 changes: 3 additions & 3 deletions MonitorControl/Model/Display.swift
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ class Display: Equatable {
return
}
if self.isVirtual || self.readPrefAsBool(key: .avoidGamma) {
_ = DisplayManager.shared.setShadeAlpha(value: 1 - transientValue, displayID: self.identifier)
_ = DisplayManager.shared.setShadeAlpha(value: 1 - transientValue, displayID: DisplayManager.resolveEffectiveDisplayID(self.identifier))
} else {
let gammaTableRed = self.defaultGammaTableRed.map { $0 * transientValue }
let gammaTableGreen = self.defaultGammaTableGreen.map { $0 * transientValue }
Expand All @@ -243,7 +243,7 @@ class Display: Equatable {
} else {
if self.isVirtual || self.readPrefAsBool(key: .avoidGamma) {
self.swBrightnessSemaphore.signal()
return DisplayManager.shared.setShadeAlpha(value: 1 - newValue, displayID: self.identifier)
return DisplayManager.shared.setShadeAlpha(value: 1 - newValue, displayID: DisplayManager.resolveEffectiveDisplayID(self.identifier))
} else {
let gammaTableRed = self.defaultGammaTableRed.map { $0 * newValue }
let gammaTableGreen = self.defaultGammaTableGreen.map { $0 * newValue }
Expand All @@ -267,7 +267,7 @@ class Display: Equatable {
}
self.swBrightnessSemaphore.wait()
if self.isVirtual || self.readPrefAsBool(key: .avoidGamma) {
let rawBrightnessValue = 1 - (DisplayManager.shared.getShadeAlpha(displayID: self.identifier) ?? 1)
let rawBrightnessValue = 1 - (DisplayManager.shared.getShadeAlpha(displayID: DisplayManager.resolveEffectiveDisplayID(self.identifier)) ?? 1)
self.swBrightnessSemaphore.signal()
return self.swBrightnessTransform(value: rawBrightnessValue, reverse: true)
}
Expand Down
70 changes: 43 additions & 27 deletions MonitorControl/Model/OtherDisplay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ class OtherDisplay: Display {
var arm64ddc: Bool = false
var arm64avService: IOAVService?
var isDiscouraged: Bool = false
let writeDDCQueue = DispatchQueue(label: "Local write DDC queue")
var writeDDCNextValue: [Command: UInt16] = [:]
var writeDDCLastSavedValue: [Command: UInt16] = [:]
var pollingCount: Int {
get {
switch self.readPrefAsInt(key: .pollingMode) {
Expand Down Expand Up @@ -76,11 +79,11 @@ class OtherDisplay: Display {
if !self.smoothBrightnessRunning, !self.isSw(), !self.readPrefAsBool(key: .unavailableDDC, for: command), self.readPrefAsBool(key: .isTouched, for: command), prefs.integer(forKey: PrefKey.startupAction.rawValue) == StartupAction.write.rawValue, !app.safeMode {
let restoreValue = self.getDDCValueFromPrefs(command)
os_log("Restoring %{public}@ DDC value %{public}@ for %{public}@", type: .info, String(reflecting: command), String(restoreValue), self.name)
_ = self.writeDDCValues(command: command, value: restoreValue)
self.writeDDCValues(command: command, value: restoreValue)
if command == .audioSpeakerVolume, self.readPrefAsBool(key: .enableMuteUnmute) {
let currentMuteValue = self.readPrefAsInt(for: .audioMuteScreenBlank) == 0 ? 2 : self.readPrefAsInt(for: .audioMuteScreenBlank)
os_log("- Writing last saved DDC value for Mute: %{public}@", type: .info, String(currentMuteValue))
_ = self.writeDDCValues(command: .audioMuteScreenBlank, value: UInt16(currentMuteValue))
self.writeDDCValues(command: .audioMuteScreenBlank, value: UInt16(currentMuteValue))
}
}
}
Expand Down Expand Up @@ -178,13 +181,11 @@ class OtherDisplay: Display {
let isAlreadySet = volumeOSDValue == self.readPrefAsFloat(for: .audioSpeakerVolume)
if !isAlreadySet {
if let muteValue = muteValue, self.readPrefAsBool(key: .enableMuteUnmute) {
guard self.writeDDCValues(command: .audioMuteScreenBlank, value: UInt16(muteValue)) == true else {
return
}
self.writeDDCValues(command: .audioMuteScreenBlank, value: UInt16(muteValue))
self.savePref(muteValue, for: .audioMuteScreenBlank)
}
if !self.readPrefAsBool(key: .enableMuteUnmute) || volumeOSDValue != 0 {
_ = self.writeDDCValues(command: .audioSpeakerVolume, value: self.convValueToDDC(for: .audioSpeakerVolume, from: volumeOSDValue))
self.writeDDCValues(command: .audioSpeakerVolume, value: self.convValueToDDC(for: .audioSpeakerVolume, from: volumeOSDValue))
}
}
if !self.readPrefAsBool(key: .hideOsd) {
Expand All @@ -206,7 +207,7 @@ class OtherDisplay: Display {
let contrastOSDValue = self.calcNewValue(currentValue: currentValue, isUp: isUp, isSmallIncrement: isSmallIncrement)
let isAlreadySet = contrastOSDValue == self.readPrefAsFloat(for: .contrast)
if !isAlreadySet {
_ = self.writeDDCValues(command: .contrast, value: self.convValueToDDC(for: .contrast, from: contrastOSDValue))
self.writeDDCValues(command: .contrast, value: self.convValueToDDC(for: .contrast, from: contrastOSDValue))
}
OSDUtils.showOsd(displayID: self.identifier, command: .contrast, value: contrastOSDValue, roundChiclet: !isSmallIncrement)
if !isAlreadySet {
Expand Down Expand Up @@ -237,13 +238,11 @@ class OtherDisplay: Display {
}
}
if self.readPrefAsBool(key: .enableMuteUnmute) {
guard self.writeDDCValues(command: .audioMuteScreenBlank, value: UInt16(muteValue)) == true else {
return
}
self.writeDDCValues(command: .audioMuteScreenBlank, value: UInt16(muteValue))
}
self.savePref(muteValue, for: .audioMuteScreenBlank)
if !self.readPrefAsBool(key: .enableMuteUnmute) || volumeOSDValue > 0 {
_ = self.writeDDCValues(command: .audioSpeakerVolume, value: self.convValueToDDC(for: .audioSpeakerVolume, from: volumeOSDValue))
self.writeDDCValues(command: .audioSpeakerVolume, value: self.convValueToDDC(for: .audioSpeakerVolume, from: volumeOSDValue))
}
if !fromVolumeSlider {
if !self.readPrefAsBool(key: .hideOsd) {
Expand Down Expand Up @@ -345,12 +344,12 @@ class OtherDisplay: Display {
brightnessValue = 0
brightnessSwValue = (value / self.combinedBrightnessSwitchingValue())
}
_ = self.writeDDCValues(command: .brightness, value: self.convValueToDDC(for: .brightness, from: brightnessValue))
self.writeDDCValues(command: .brightness, value: self.convValueToDDC(for: .brightness, from: brightnessValue))
if self.readPrefAsFloat(key: .SwBrightness) != brightnessSwValue {
_ = self.setSwBrightness(brightnessSwValue)
}
} else {
_ = self.writeDDCValues(command: .brightness, value: self.convValueToDDC(for: .brightness, from: value))
self.writeDDCValues(command: .brightness, value: self.convValueToDDC(for: .brightness, from: value))
}
if !transient {
self.savePref(value, for: .brightness)
Expand Down Expand Up @@ -378,28 +377,45 @@ class OtherDisplay: Display {
return intCodes
}

public func writeDDCValues(command: Command, value: UInt16, errorRecoveryWaitTime _: UInt32? = nil) -> Bool? {
public func writeDDCValues(command: Command, value: UInt16) {
guard app.sleepID == 0, app.reconfigureID == 0, !self.readPrefAsBool(key: .forceSw), !self.readPrefAsBool(key: .unavailableDDC, for: command) else {
return false
return
}
self.writeDDCQueue.async(flags: .barrier) {
self.writeDDCNextValue[command] = value
}
DisplayManager.shared.globalDDCQueue.async(flags: .barrier) {
self.asyncPerformWriteDDCValues(command: command)
}
}

func asyncPerformWriteDDCValues(command: Command) {
var value = UInt16.max
var lastValue = UInt16.max
self.writeDDCQueue.sync {
value = self.writeDDCNextValue[command] ?? UInt16.max
lastValue = self.writeDDCLastSavedValue[command] ?? UInt16.max
}
guard value != UInt16.max, value != lastValue else {
return
}
self.writeDDCQueue.async(flags: .barrier) {
self.writeDDCLastSavedValue[command] = value
self.savePref(true, key: PrefKey.isTouched, for: command)
}
var success: Bool = false
var controlCodes = self.getRemapControlCodes(command: command)
if controlCodes.count == 0 {
controlCodes.append(command.rawValue)
}
for controlCode in controlCodes {
DisplayManager.shared.ddcQueue.sync {
if Arm64DDC.isArm64 {
if self.arm64ddc {
success = Arm64DDC.write(service: self.arm64avService, command: controlCode, value: value)
}
} else {
success = self.ddc?.write(command: command.rawValue, value: value, errorRecoveryWaitTime: 2000) ?? false
if Arm64DDC.isArm64 {
if self.arm64ddc {
_ = Arm64DDC.write(service: self.arm64avService, command: controlCode, value: value)
}
self.savePref(true, key: PrefKey.isTouched, for: command) // We deliberatly consider the value tuched no matter if the call succeeded
} else {
_ = self.ddc?.write(command: controlCode, value: value, errorRecoveryWaitTime: 2000) ?? false
}
}
return success
}

func readDDCValues(for command: Command, tries: UInt, minReplyDelay delay: UInt64?) -> (current: UInt16, max: UInt16)? {
Expand All @@ -413,15 +429,15 @@ class OtherDisplay: Display {
guard self.arm64ddc else {
return nil
}
DisplayManager.shared.ddcQueue.sync {
DisplayManager.shared.globalDDCQueue.sync {
if let unwrappedDelay = delay {
values = Arm64DDC.read(service: self.arm64avService, command: controlCode, tries: UInt8(min(tries, 255)), minReplyDelay: UInt32(unwrappedDelay / 1000))
} else {
values = Arm64DDC.read(service: self.arm64avService, command: controlCode, tries: UInt8(min(tries, 255)))
}
}
} else {
DisplayManager.shared.ddcQueue.sync {
DisplayManager.shared.globalDDCQueue.sync {
values = self.ddc?.read(command: controlCode, tries: tries, minReplyDelay: delay)
}
}
Expand Down
82 changes: 56 additions & 26 deletions MonitorControl/Support/DisplayManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class DisplayManager {

var displays: [Display] = []
var audioControlTargetDisplays: [OtherDisplay] = []
let ddcQueue = DispatchQueue(label: "DDC queue")
let globalDDCQueue = DispatchQueue(label: "Global DDC queue")
let gammaActivityEnforcer = NSWindow(contentRect: .init(origin: NSPoint(x: 0, y: 0), size: .init(width: DEBUG_GAMMA_ENFORCER ? 15 : 1, height: DEBUG_GAMMA_ENFORCER ? 15 : 1)), styleMask: [], backing: .buffered, defer: false)
var gammaInterferenceCounter = 0
var gammaInterferenceWarningShown = false
Expand Down Expand Up @@ -43,22 +43,39 @@ class DisplayManager {
internal var shades: [CGDirectDisplayID: NSWindow] = [:]
internal var shadeGrave: [NSWindow] = []

func isDisqualifiedFromShade(_ displayID: CGDirectDisplayID) -> Bool { // We ban mirror members from shade control as it might lead to double control
return (CGDisplayIsInHWMirrorSet(displayID) != 0 || CGDisplayIsInMirrorSet(displayID) != 0) ? true : false
func isDisqualifiedFromShade(_ displayID: CGDirectDisplayID) -> Bool {
if CGDisplayIsInHWMirrorSet(displayID) != 0 || CGDisplayIsInMirrorSet(displayID) != 0 {
if displayID == DisplayManager.resolveEffectiveDisplayID(displayID), DisplayManager.isVirtual(displayID: displayID) || DisplayManager.isDummy(displayID: displayID) {
var displayIDs = [CGDirectDisplayID](repeating: 0, count: 16)
var displayCount: UInt32 = 0
guard CGGetOnlineDisplayList(16, &displayIDs, &displayCount) == .success else {
return true
}
for displayId in displayIDs where CGDisplayMirrorsDisplay(displayId) == displayID && !DisplayManager.isVirtual(displayID: displayID) {
return true
}
return false
}
return true
}
return false
}

internal func createShadeOnDisplay(displayID: CGDirectDisplayID) -> NSWindow? {
if let screen = DisplayManager.getByDisplayID(displayID: displayID) {
let shade = NSWindow(contentRect: .init(origin: NSPoint(x: 0, y: 0), size: .init(width: 10, height: 1)), styleMask: [], backing: .buffered, defer: false)
shade.title = "Monitor Control Window Shade for Display " + String(displayID)
shade.isMovableByWindowBackground = false
shade.backgroundColor = .black
shade.backgroundColor = .clear
shade.ignoresMouseEvents = true
shade.level = NSWindow.Level(rawValue: Int(CGShieldingWindowLevel()))
shade.alphaValue = 0
shade.orderFrontRegardless()
shade.collectionBehavior = [.stationary, .canJoinAllSpaces, .ignoresCycle]
shade.setFrame(screen.frame, display: true)
shade.contentView?.wantsLayer = true
shade.contentView?.alphaValue = 0.0
shade.contentView?.layer?.backgroundColor = .black
shade.contentView?.setNeedsDisplay(shade.frame)
os_log("Window shade created for display %{public}@", type: .info, String(displayID))
return shade
}
Expand Down Expand Up @@ -125,7 +142,7 @@ class DisplayManager {
return 1
}
if let shade = getShade(displayID: displayID) {
return Float(shade.alphaValue)
return Float(shade.contentView?.alphaValue ?? 1)
} else {
return 1
}
Expand All @@ -136,7 +153,7 @@ class DisplayManager {
return false
}
if let shade = getShade(displayID: displayID) {
shade.alphaValue = CGFloat(value)
shade.contentView?.alphaValue = CGFloat(value)
return true
}
return false
Expand All @@ -151,27 +168,12 @@ 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
}
}
}
let isDummy: Bool = DisplayManager.isDummy(displayID: onlineDisplayID)
let isVirtual: Bool = DisplayManager.isVirtual(displayID: onlineDisplayID)
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, isDummy: isDummy)
os_log("Apple display found - %{public}@", type: .info, "ID: \(appleDisplay.identifier), Name: \(appleDisplay.name) (Vendor: \(appleDisplay.vendorNumber ?? 0), Model: \(appleDisplay.modelNumber ?? 0))")
Expand Down Expand Up @@ -391,6 +393,30 @@ class DisplayManager {
return affectedDisplays
}

static func isDummy(displayID: CGDirectDisplayID) -> Bool {
let rawName = DisplayManager.getDisplayRawNameByID(displayID: displayID)
var isDummy: Bool = false
if rawName == "28E850" || rawName.lowercased().contains("dummy") {
os_log("NOTE: Display is a dummy!", type: .info)
isDummy = true
}
return isDummy
}

static func isVirtual(displayID: CGDirectDisplayID) -> Bool {
var isVirtual: Bool = false
if !DEBUG_MACOS10, #available(macOS 11.0, *) {
if let dictionary = ((CoreDisplay_DisplayCreateInfoDictionary(displayID))?.takeRetainedValue() as NSDictionary?) {
let isVirtualDevice = dictionary["kCGDisplayIsVirtualDevice"] as? Bool
let displayIsAirplay = dictionary["kCGDisplayIsAirPlay"] as? Bool
if isVirtualDevice ?? displayIsAirplay ?? false {
isVirtual = true
}
}
}
return isVirtual
}

static func engageMirror() -> Bool {
var onlineDisplayIDs = [CGDirectDisplayID](repeating: 0, count: 16)
var displayCount: UInt32 = 0
Expand Down Expand Up @@ -478,14 +504,18 @@ class DisplayManager {
if CGDisplayIsInHWMirrorSet(displayID) != 0 || CGDisplayIsInMirrorSet(displayID) != 0 {
let mirroredDisplayID = CGDisplayMirrorsDisplay(displayID)
if mirroredDisplayID != 0, let dictionary = ((CoreDisplay_DisplayCreateInfoDictionary(mirroredDisplayID))?.takeRetainedValue() as NSDictionary?), let nameList = dictionary["DisplayProductName"] as? [String: String], let mirroredName = nameList[Locale.current.identifier] ?? nameList["en_US"] ?? nameList.first?.value {
name.append("~" + mirroredName)
name.append(" | " + mirroredName)
}
}
return name
}
}
if let screen = getByDisplayID(displayID: displayID) { // MARK: This, and NSScreen+Extension.swift will not be needed when we drop MacOS 10 support.
return screen.localizedName
if #available(macOS 10.15, *) {
return screen.localizedName
} else {
return screen.displayName ?? defaultName
}
}
return defaultName
}
Expand Down
Loading