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] = [:]
let coreAudio = SimplyCoreAudio()
var accessibilityObserver: NSObjectProtocol!
var willReconfigureDisplay: Bool = false // A reconfigure display command is already dispatched
var displaySleep: Int = 0 // Don't reconfigure display as the system or display is sleeping or wake just recently.
var reconfigureID: Int = 0 // dispatched reconfigure command ID
var sleepID: Int = 0 // Don't reconfigure display as the system or display is sleeping or wake just recently.
lazy var preferencesWindowController: PreferencesWindowController = {
let storyboard = NSStoryboard(name: "Main", bundle: Bundle.main)
let mainPrefsVc = storyboard.instantiateController(withIdentifier: "MainPrefsVC") as? MainPrefsViewController
@ -108,7 +108,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
func getDisplayName(displayID: CGDirectDisplayID) -> String {
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
}
if let screen = NSScreen.getByDisplayID(displayID: displayID) {
@ -118,16 +124,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
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
}
@ -155,21 +151,22 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
func displayReconfigured() {
if !self.willReconfigureDisplay, self.displaySleep == 0 {
self.willReconfigureDisplay = true
os_log("Display to be reconfigured via updateDisplay in 2 seconds", type: .info)
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) {
self.updateDisplays()
self.updateDisplays(dispatchedReconfigureID: dispatchedReconfigureID)
}
}
}
func updateDisplays() {
guard self.displaySleep == 0 else {
func updateDisplays(dispatchedReconfigureID: Int = 0) {
guard self.sleepID == 0, dispatchedReconfigureID == self.reconfigureID else {
return
}
os_log("Request for updateDisplay", type: .info)
self.willReconfigureDisplay = false
os_log("Request for updateDisplay with reconfigreID %{public}@", type: .info, String(dispatchedReconfigureID))
self.reconfigureID = 0
self.clearDisplays()
var onlineDisplayIDs = [CGDirectDisplayID](repeating: 0, count: 10)
var displayCount: UInt32 = 0
@ -286,24 +283,24 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
@objc private func sleepNotification() {
self.displaySleep += 1
os_log("Sleeping with sleep %{public}@", type: .info, String(self.displaySleep))
self.sleepID += 1
os_log("Sleeping with sleep %{public}@", type: .info, String(self.sleepID))
}
@objc private func wakeNotofication() {
if self.displaySleep != 0 {
os_log("Waking up from sleep %{public}@", type: .info, String(self.displaySleep))
let sleepID = self.displaySleep
if self.sleepID != 0 {
os_log("Waking up from sleep %{public}@", type: .info, String(self.sleepID))
let dispatchedSleepID = self.sleepID
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) {
if self.displaySleep == sleepID {
os_log("Sober from sleep %{public}@", type: .info, String(self.displaySleep))
self.displaySleep = 0
private func soberNow(dispatchedSleepID: Int) {
if self.sleepID == dispatchedSleepID {
os_log("Sober from sleep %{public}@", type: .info, String(self.sleepID))
self.sleepID = 0
self.updateDisplays()
}
}
@ -345,7 +342,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
extension AppDelegate: MediaKeyTapDelegate {
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
}
if self.handleOpenPrefPane(mediaKey: mediaKey, event: event, modifiers: modifiers) {
@ -375,20 +372,37 @@ extension AppDelegate: MediaKeyTapDelegate {
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) {
guard self.displaySleep == 0, !self.willReconfigureDisplay else {
guard self.sleepID == 0, self.reconfigureID == 0, let affectedDisplays = self.getAffectedDisplays() else {
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
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
}
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 {
case .brightnessUp:
if !(isAnyDisplayInContrastAfterBrightnessMode && !((display as? ExternalDisplay)?.isContrastAfterBrightnessMode ?? false)) {

View file

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

View file

@ -17,8 +17,6 @@ class DisplayManager {
self.displays = displays
}
// cell.button.state = ((display as? ExternalDisplay)?.arm64ddc ?? false) ? .on : .off
func getExternalDisplays() -> [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
}
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) {
guard let manager = OSDManager.sharedManager() as? OSDManager else {
return
@ -80,7 +97,7 @@ class Display {
}
manager.showImage(osdImage.rawValue,
onDisplayID: self.identifier,
onDisplayID: self.getShowOsdDisplayId(),
priority: 0x1F4,
msecUntilFade: 1000,
filledChiclets: UInt32(filledChiclets),

View file

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

View file

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

View file

@ -31,9 +31,6 @@
/* 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 */
"No" = "No";

View file

@ -31,9 +31,6 @@
/* Shown in the main prefs window */
"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 */
"No" = "No";

View file

@ -31,9 +31,6 @@
/* Shown in the main prefs window */
"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 */
"No" = "Nem";

View file

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

View file

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

View file

@ -31,9 +31,6 @@
/* Shown in the main prefs window */
"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 */
"No" = "Nie";

View file

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

View file

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

View file

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

View file

@ -12,6 +12,9 @@ class SliderHandler {
}
@objc func valueChanged(slider: NSSlider) {
guard app.sleepID == 0, app.reconfigureID == 0 else {
return
}
let snapInterval = 25
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.saveValue(value, for: self.cmd)
}

View file

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