Stability and functionality improvements

- Displays with screens that are shadowed by a mirror are now controlled along with the mirror master.
- Fixed OSD when a display is shadowed by an uncontrollable master (first potent shadowed will provide OSD)
- Fixed scenario of constant screen configuration changes (like when user closes and opens lid or plugs/unplugs displays rapidly)
This commit is contained in:
waydabber 2021-08-10 21:46:33 +02:00
parent 5333f2c1cd
commit 19ef0efa11
17 changed files with 86 additions and 84 deletions

View file

@ -17,8 +17,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
var keyRepeatTimers: [MediaKey: Timer] = [:] var keyRepeatTimers: [MediaKey: Timer] = [:]
let coreAudio = SimplyCoreAudio() let coreAudio = SimplyCoreAudio()
var accessibilityObserver: NSObjectProtocol! var accessibilityObserver: NSObjectProtocol!
var willReconfigureDisplay: Bool = false // A reconfigure display command is already dispatched var reconfigureID: Int = 0 // dispatched reconfigure command ID
var displaySleep: Int = 0 // Don't reconfigure display as the system or display is sleeping or wake just recently. var sleepID: Int = 0 // Don't reconfigure display as the system or display is sleeping or wake just recently.
lazy var preferencesWindowController: PreferencesWindowController = { lazy var preferencesWindowController: PreferencesWindowController = {
let storyboard = NSStoryboard(name: "Main", bundle: Bundle.main) let storyboard = NSStoryboard(name: "Main", bundle: Bundle.main)
let mainPrefsVc = storyboard.instantiateController(withIdentifier: "MainPrefsVC") as? MainPrefsViewController let mainPrefsVc = storyboard.instantiateController(withIdentifier: "MainPrefsVC") as? MainPrefsViewController
@ -108,7 +108,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
func getDisplayName(displayID: CGDirectDisplayID) -> String { func getDisplayName(displayID: CGDirectDisplayID) -> String {
let defaultName: String = NSLocalizedString("Unknown", comment: "Unknown display name") // + String(CGDisplaySerialNumber(displayID)) let defaultName: String = NSLocalizedString("Unknown", comment: "Unknown display name") // + String(CGDisplaySerialNumber(displayID))
if let dictionary = ((CoreDisplay_DisplayCreateInfoDictionary(displayID))?.takeRetainedValue() as NSDictionary?), let nameList = dictionary["DisplayProductName"] as? [String: String], let name = nameList[Locale.current.identifier] ?? nameList["en_US"] ?? nameList.first?.value { if let dictionary = ((CoreDisplay_DisplayCreateInfoDictionary(displayID))?.takeRetainedValue() as NSDictionary?), let nameList = dictionary["DisplayProductName"] as? [String: String], var name = nameList[Locale.current.identifier] ?? nameList["en_US"] ?? nameList.first?.value {
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)
}
}
return name return name
} }
if let screen = NSScreen.getByDisplayID(displayID: displayID) { if let screen = NSScreen.getByDisplayID(displayID: displayID) {
@ -118,16 +124,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
return screen.displayName ?? defaultName return screen.displayName ?? defaultName
} }
} }
if CGDisplayIsInHWMirrorSet(displayID) != 0 || CGDisplayIsInMirrorSet(displayID) != 0 {
if let mirroredScreen = NSScreen.getByDisplayID(displayID: CGDisplayMirrorsDisplay(displayID)) {
let name = NSLocalizedString("Mirror of", comment: "Shown in case a display mirrors an other display - like 'Mirror of DisplayName")
if #available(OSX 10.15, *) {
return "" + name + " " + String(mirroredScreen.localizedName)
} else {
return "" + name + " " + String(mirroredScreen.displayName ?? defaultName)
}
}
}
return defaultName return defaultName
} }
@ -155,21 +151,22 @@ class AppDelegate: NSObject, NSApplicationDelegate {
} }
func displayReconfigured() { func displayReconfigured() {
if !self.willReconfigureDisplay, self.displaySleep == 0 { if self.sleepID == 0 {
self.willReconfigureDisplay = true self.reconfigureID += 1
os_log("Display to be reconfigured via updateDisplay in 2 seconds", type: .info) let dispatchedReconfigureID = self.reconfigureID
os_log("Display to be reconfigured with reconfigureID %{public}@", type: .info, String(dispatchedReconfigureID))
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.updateDisplays() self.updateDisplays(dispatchedReconfigureID: dispatchedReconfigureID)
} }
} }
} }
func updateDisplays() { func updateDisplays(dispatchedReconfigureID: Int = 0) {
guard self.displaySleep == 0 else { guard self.sleepID == 0, dispatchedReconfigureID == self.reconfigureID else {
return return
} }
os_log("Request for updateDisplay", type: .info) os_log("Request for updateDisplay with reconfigreID %{public}@", type: .info, String(dispatchedReconfigureID))
self.willReconfigureDisplay = false self.reconfigureID = 0
self.clearDisplays() self.clearDisplays()
var onlineDisplayIDs = [CGDirectDisplayID](repeating: 0, count: 10) var onlineDisplayIDs = [CGDirectDisplayID](repeating: 0, count: 10)
var displayCount: UInt32 = 0 var displayCount: UInt32 = 0
@ -286,24 +283,24 @@ class AppDelegate: NSObject, NSApplicationDelegate {
} }
@objc private func sleepNotification() { @objc private func sleepNotification() {
self.displaySleep += 1 self.sleepID += 1
os_log("Sleeping with sleep %{public}@", type: .info, String(self.displaySleep)) os_log("Sleeping with sleep %{public}@", type: .info, String(self.sleepID))
} }
@objc private func wakeNotofication() { @objc private func wakeNotofication() {
if self.displaySleep != 0 { if self.sleepID != 0 {
os_log("Waking up from sleep %{public}@", type: .info, String(self.displaySleep)) os_log("Waking up from sleep %{public}@", type: .info, String(self.sleepID))
let sleepID = self.displaySleep let dispatchedSleepID = self.sleepID
DispatchQueue.main.asyncAfter(deadline: .now() + 6.0) { // Some displays take time to recover... DispatchQueue.main.asyncAfter(deadline: .now() + 6.0) { // Some displays take time to recover...
self.soberNow(sleepID: sleepID) self.soberNow(dispatchedSleepID: dispatchedSleepID)
} }
} }
} }
private func soberNow(sleepID: Int) { private func soberNow(dispatchedSleepID: Int) {
if self.displaySleep == sleepID { if self.sleepID == dispatchedSleepID {
os_log("Sober from sleep %{public}@", type: .info, String(self.displaySleep)) os_log("Sober from sleep %{public}@", type: .info, String(self.sleepID))
self.displaySleep = 0 self.sleepID = 0
self.updateDisplays() self.updateDisplays()
} }
} }
@ -345,7 +342,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
extension AppDelegate: MediaKeyTapDelegate { extension AppDelegate: MediaKeyTapDelegate {
func handle(mediaKey: MediaKey, event: KeyEvent?, modifiers: NSEvent.ModifierFlags?) { func handle(mediaKey: MediaKey, event: KeyEvent?, modifiers: NSEvent.ModifierFlags?) {
guard self.displaySleep == 0 && !self.willReconfigureDisplay else { guard self.sleepID == 0, self.reconfigureID == 0 else {
return return
} }
if self.handleOpenPrefPane(mediaKey: mediaKey, event: event, modifiers: modifiers) { if self.handleOpenPrefPane(mediaKey: mediaKey, event: event, modifiers: modifiers) {
@ -375,20 +372,37 @@ extension AppDelegate: MediaKeyTapDelegate {
self.sendDisplayCommand(mediaKey: mediaKey, isRepeat: isRepeat, isSmallIncrement: isSmallIncrement) self.sendDisplayCommand(mediaKey: mediaKey, isRepeat: isRepeat, isSmallIncrement: isSmallIncrement)
} }
private func getAffectedDisplays() -> [Display]? {
var affectedDisplays: [Display]
let allDisplays = DisplayManager.shared.getAllDisplays()
guard let currentDisplay = DisplayManager.shared.getCurrentDisplay() else {
return nil
}
// let allDisplays = prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? displays : [currentDisplay]
if prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) {
affectedDisplays = allDisplays
} else {
affectedDisplays = [currentDisplay]
if CGDisplayIsInHWMirrorSet(currentDisplay.identifier) != 0 || CGDisplayIsInMirrorSet(currentDisplay.identifier) != 0, CGDisplayMirrorsDisplay(currentDisplay.identifier) == 0 {
for display in allDisplays where CGDisplayMirrorsDisplay(display.identifier) == currentDisplay.identifier {
affectedDisplays.append(display)
}
}
}
return affectedDisplays
}
private func sendDisplayCommand(mediaKey: MediaKey, isRepeat: Bool, isSmallIncrement: Bool) { private func sendDisplayCommand(mediaKey: MediaKey, isRepeat: Bool, isSmallIncrement: Bool) {
guard self.displaySleep == 0, !self.willReconfigureDisplay else { guard self.sleepID == 0, self.reconfigureID == 0, let affectedDisplays = self.getAffectedDisplays() else {
return return
} }
let displays = DisplayManager.shared.getAllDisplays()
guard let currentDisplay = DisplayManager.shared.getCurrentDisplay() else { return }
let allDisplays = prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? displays : [currentDisplay]
let delay = isRepeat ? 0.05 : 0 // Introduce a small delay to handle the media key being held down let delay = isRepeat ? 0.05 : 0 // Introduce a small delay to handle the media key being held down
var isAnyDisplayInContrastAfterBrightnessMode: Bool = false var isAnyDisplayInContrastAfterBrightnessMode: Bool = false
for display in allDisplays where (display as? ExternalDisplay)?.isContrastAfterBrightnessMode ?? false { for display in affectedDisplays where (display as? ExternalDisplay)?.isContrastAfterBrightnessMode ?? false {
isAnyDisplayInContrastAfterBrightnessMode = true isAnyDisplayInContrastAfterBrightnessMode = true
} }
self.keyRepeatTimers[mediaKey] = Timer.scheduledTimer(withTimeInterval: delay, repeats: false, block: { _ in self.keyRepeatTimers[mediaKey] = Timer.scheduledTimer(withTimeInterval: delay, repeats: false, block: { _ in
for display in allDisplays where display.isEnabled && !display.isVirtual { for display in affectedDisplays where display.isEnabled && !display.isVirtual {
switch mediaKey { switch mediaKey {
case .brightnessUp: case .brightnessUp:
if !(isAnyDisplayInContrastAfterBrightnessMode && !((display as? ExternalDisplay)?.isContrastAfterBrightnessMode ?? false)) { if !(isAnyDisplayInContrastAfterBrightnessMode && !((display as? ExternalDisplay)?.isContrastAfterBrightnessMode ?? false)) {

View file

@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string> <string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1686</string> <string>1722</string>
<key>LSApplicationCategoryType</key> <key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string> <string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>

View file

@ -17,8 +17,6 @@ class DisplayManager {
self.displays = displays self.displays = displays
} }
// cell.button.state = ((display as? ExternalDisplay)?.arm64ddc ?? false) ? .on : .off
func getExternalDisplays() -> [ExternalDisplay] { func getExternalDisplays() -> [ExternalDisplay] {
return self.displays.compactMap { $0 as? ExternalDisplay } return self.displays.compactMap { $0 as? ExternalDisplay }
} }

View file

@ -51,6 +51,23 @@ class Display {
return self.prefs.string(forKey: "friendlyName-\(self.identifier)") ?? self.name return self.prefs.string(forKey: "friendlyName-\(self.identifier)") ?? self.name
} }
func getShowOsdDisplayId() -> CGDirectDisplayID {
if CGDisplayIsInHWMirrorSet(self.identifier) != 0 || CGDisplayIsInMirrorSet(self.identifier) != 0, CGDisplayMirrorsDisplay(self.identifier) != 0 {
for mirrorMaestro in DisplayManager.shared.getAllDisplays() where CGDisplayMirrorsDisplay(self.identifier) == mirrorMaestro.identifier {
if let externalMirrorMaestro = mirrorMaestro as? ExternalDisplay, !externalMirrorMaestro.arm64ddc, externalMirrorMaestro.ddc == nil {
var thereAreOthers = false
for mirrorMember in DisplayManager.shared.getAllDisplays() where CGDisplayMirrorsDisplay(mirrorMember.identifier) == CGDisplayMirrorsDisplay(self.identifier) && mirrorMember.identifier != self.identifier {
thereAreOthers = true
}
if !thereAreOthers {
return externalMirrorMaestro.identifier
}
}
}
}
return self.identifier
}
func showOsd(command: DDC.Command, value: Int, maxValue: Int = 100, roundChiclet: Bool = false) { func showOsd(command: DDC.Command, value: Int, maxValue: Int = 100, roundChiclet: Bool = false) {
guard let manager = OSDManager.sharedManager() as? OSDManager else { guard let manager = OSDManager.sharedManager() as? OSDManager else {
return return
@ -80,7 +97,7 @@ class Display {
} }
manager.showImage(osdImage.rawValue, manager.showImage(osdImage.rawValue,
onDisplayID: self.identifier, onDisplayID: self.getShowOsdDisplayId(),
priority: 0x1F4, priority: 0x1F4,
msecUntilFade: 1000, msecUntilFade: 1000,
filledChiclets: UInt32(filledChiclets), filledChiclets: UInt32(filledChiclets),

View file

@ -187,20 +187,21 @@ class ExternalDisplay: Display {
} }
if !self.isContrastAfterBrightnessMode { if !self.isContrastAfterBrightnessMode {
let ddcValue = UInt16(osdValue) let ddcValue = UInt16(osdValue)
if !isAlreadySet {
guard self.writeDDCValues(command: .brightness, value: ddcValue) == true else { guard self.writeDDCValues(command: .brightness, value: ddcValue) == true else {
return return
} }
if let slider = brightnessSliderHandler?.slider { if let slider = brightnessSliderHandler?.slider {
slider.intValue = Int32(ddcValue) slider.intValue = Int32(ddcValue)
} }
}
self.showOsd(command: .brightness, value: osdValue, roundChiclet: !isSmallIncrement) self.showOsd(command: .brightness, value: osdValue, roundChiclet: !isSmallIncrement)
self.saveValue(osdValue, for: .brightness) self.saveValue(osdValue, for: .brightness)
} }
} }
public func writeDDCValues(command: DDC.Command, value: UInt16, errorRecoveryWaitTime _: UInt32? = nil) -> Bool? { public func writeDDCValues(command: DDC.Command, value: UInt16, errorRecoveryWaitTime _: UInt32? = nil) -> Bool? {
guard app.sleepID == 0, app.reconfigureID == 0 else {
return false
}
if Arm64DDCUtils.isArm64 { if Arm64DDCUtils.isArm64 {
guard self.arm64ddc else { guard self.arm64ddc else {
return false return false
@ -219,6 +220,9 @@ class ExternalDisplay: Display {
func readDDCValues(for command: DDC.Command, tries: UInt, minReplyDelay delay: UInt64?) -> (current: UInt16, max: UInt16)? { func readDDCValues(for command: DDC.Command, tries: UInt, minReplyDelay delay: UInt64?) -> (current: UInt16, max: UInt16)? {
var values: (UInt16, UInt16)? var values: (UInt16, UInt16)?
guard app.sleepID == 0, app.reconfigureID == 0 else {
return values
}
if Arm64DDCUtils.isArm64 { if Arm64DDCUtils.isArm64 {
guard self.arm64ddc else { guard self.arm64ddc else {
return nil return nil

View file

@ -31,9 +31,6 @@
/* Shown in the main prefs window */ /* Shown in the main prefs window */
"General" = "Allgemein"; "General" = "Allgemein";
/* Shown in case a display mirrors an other display - like 'Mirror of DisplayName */
"Mirror of" = "Mirror of";
/* Shown in the alert dialog */ /* Shown in the alert dialog */
"No" = "No"; "No" = "No";

View file

@ -31,9 +31,6 @@
/* Shown in the main prefs window */ /* Shown in the main prefs window */
"General" = "General"; "General" = "General";
/* Shown in case a display mirrors an other display - like 'Mirror of DisplayName */
"Mirror of" = "Mirror of";
/* Shown in the alert dialog */ /* Shown in the alert dialog */
"No" = "No"; "No" = "No";

View file

@ -31,9 +31,6 @@
/* Shown in the main prefs window */ /* Shown in the main prefs window */
"General" = "Général"; "General" = "Général";
/* Shown in case a display mirrors an other display - like 'Mirror of DisplayName */
"Mirror of" = "Mirror of";
/* Shown in the alert dialog */ /* Shown in the alert dialog */
"No" = "No"; "No" = "No";

View file

@ -31,9 +31,6 @@
/* Shown in the main prefs window */ /* Shown in the main prefs window */
"General" = "Általános"; "General" = "Általános";
/* Shown in case a display mirrors an other display - like 'Mirror of DisplayName */
"Mirror of" = "Tükrözött";
/* Shown in the alert dialog */ /* Shown in the alert dialog */
"No" = "Nem"; "No" = "Nem";

View file

@ -31,9 +31,6 @@
/* Shown in the main prefs window */ /* Shown in the main prefs window */
"General" = "Generale"; "General" = "Generale";
/* Shown in case a display mirrors an other display - like 'Mirror of DisplayName */
"Mirror of" = "Mirror of";
/* Shown in the alert dialog */ /* Shown in the alert dialog */
"No" = "No"; "No" = "No";

View file

@ -31,9 +31,6 @@
/* Shown in the main prefs window */ /* Shown in the main prefs window */
"General" = "一般"; "General" = "一般";
/* Shown in case a display mirrors an other display - like 'Mirror of DisplayName */
"Mirror of" = "Mirror of";
/* Shown in the alert dialog */ /* Shown in the alert dialog */
"No" = "No"; "No" = "No";

View file

@ -31,9 +31,6 @@
/* Shown in the main prefs window */ /* Shown in the main prefs window */
"General" = "Ogólne"; "General" = "Ogólne";
/* Shown in case a display mirrors an other display - like 'Mirror of DisplayName */
"Mirror of" = "Mirror of";
/* Shown in the alert dialog */ /* Shown in the alert dialog */
"No" = "Nie"; "No" = "Nie";

View file

@ -31,9 +31,6 @@
/* Shown in the main prefs window */ /* Shown in the main prefs window */
"General" = "Основные"; "General" = "Основные";
/* Shown in case a display mirrors an other display - like 'Mirror of DisplayName */
"Mirror of" = "Mirror of";
/* Shown in the alert dialog */ /* Shown in the alert dialog */
"No" = "No"; "No" = "No";

View file

@ -31,9 +31,6 @@
/* Shown in the main prefs window */ /* Shown in the main prefs window */
"General" = "Загальні"; "General" = "Загальні";
/* Shown in case a display mirrors an other display - like 'Mirror of DisplayName */
"Mirror of" = "Mirror of";
/* Shown in the alert dialog */ /* Shown in the alert dialog */
"No" = "No"; "No" = "No";

View file

@ -31,9 +31,6 @@
/* Shown in the main prefs window */ /* Shown in the main prefs window */
"General" = "通用"; "General" = "通用";
/* Shown in case a display mirrors an other display - like 'Mirror of DisplayName */
"Mirror of" = "Mirror of";
/* Shown in the alert dialog */ /* Shown in the alert dialog */
"No" = "No"; "No" = "No";

View file

@ -12,6 +12,9 @@ class SliderHandler {
} }
@objc func valueChanged(slider: NSSlider) { @objc func valueChanged(slider: NSSlider) {
guard app.sleepID == 0, app.reconfigureID == 0 else {
return
}
let snapInterval = 25 let snapInterval = 25
let snapThreshold = 3 let snapThreshold = 3
@ -37,10 +40,6 @@ class SliderHandler {
} }
} }
guard app.displaySleep == 0, !app.willReconfigureDisplay else {
return
}
_ = self.display.writeDDCValues(command: self.cmd, value: UInt16(value)) _ = self.display.writeDDCValues(command: self.cmd, value: UInt16(value))
self.display.saveValue(value, for: self.cmd) self.display.saveValue(value, for: self.cmd)
} }

View file

@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string> <string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1686</string> <string>1722</string>
<key>LSApplicationCategoryType</key> <key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string> <string>public.app-category.utilities</string>
<key>LSBackgroundOnly</key> <key>LSBackgroundOnly</key>