diff --git a/MonitorControl/AppDelegate.swift b/MonitorControl/AppDelegate.swift index a3a4e7e..9f63fda 100644 --- a/MonitorControl/AppDelegate.swift +++ b/MonitorControl/AppDelegate.swift @@ -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)) { diff --git a/MonitorControl/Info.plist b/MonitorControl/Info.plist index 1981894..871f940 100644 --- a/MonitorControl/Info.plist +++ b/MonitorControl/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 1686 + 1722 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/MonitorControl/Manager/DisplayManager.swift b/MonitorControl/Manager/DisplayManager.swift index a5d99a7..f0d4d35 100644 --- a/MonitorControl/Manager/DisplayManager.swift +++ b/MonitorControl/Manager/DisplayManager.swift @@ -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 } } diff --git a/MonitorControl/Model/Display.swift b/MonitorControl/Model/Display.swift index 120232a..03c3284 100644 --- a/MonitorControl/Model/Display.swift +++ b/MonitorControl/Model/Display.swift @@ -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), diff --git a/MonitorControl/Model/ExternalDisplay.swift b/MonitorControl/Model/ExternalDisplay.swift index 4e4fece..fbcbcf6 100644 --- a/MonitorControl/Model/ExternalDisplay.swift +++ b/MonitorControl/Model/ExternalDisplay.swift @@ -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 diff --git a/MonitorControl/Support/de.lproj/Localizable.strings b/MonitorControl/Support/de.lproj/Localizable.strings index 9308840..0b4814d 100644 --- a/MonitorControl/Support/de.lproj/Localizable.strings +++ b/MonitorControl/Support/de.lproj/Localizable.strings @@ -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"; diff --git a/MonitorControl/Support/en.lproj/Localizable.strings b/MonitorControl/Support/en.lproj/Localizable.strings index 0b32fa2..ccb5aa4 100644 --- a/MonitorControl/Support/en.lproj/Localizable.strings +++ b/MonitorControl/Support/en.lproj/Localizable.strings @@ -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"; diff --git a/MonitorControl/Support/fr.lproj/Localizable.strings b/MonitorControl/Support/fr.lproj/Localizable.strings index f1f790a..ddc62ef 100644 --- a/MonitorControl/Support/fr.lproj/Localizable.strings +++ b/MonitorControl/Support/fr.lproj/Localizable.strings @@ -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"; diff --git a/MonitorControl/Support/hu.lproj/Localizable.strings b/MonitorControl/Support/hu.lproj/Localizable.strings index 7e4a2aa..6f5471e 100644 --- a/MonitorControl/Support/hu.lproj/Localizable.strings +++ b/MonitorControl/Support/hu.lproj/Localizable.strings @@ -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"; diff --git a/MonitorControl/Support/it.lproj/Localizable.strings b/MonitorControl/Support/it.lproj/Localizable.strings index 8bcb8f1..8316e32 100644 --- a/MonitorControl/Support/it.lproj/Localizable.strings +++ b/MonitorControl/Support/it.lproj/Localizable.strings @@ -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"; diff --git a/MonitorControl/Support/ja.lproj/Localizable.strings b/MonitorControl/Support/ja.lproj/Localizable.strings index 310f752..e45b6ca 100644 --- a/MonitorControl/Support/ja.lproj/Localizable.strings +++ b/MonitorControl/Support/ja.lproj/Localizable.strings @@ -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"; diff --git a/MonitorControl/Support/pl.lproj/Localizable.strings b/MonitorControl/Support/pl.lproj/Localizable.strings index 015025c..85d2595 100644 --- a/MonitorControl/Support/pl.lproj/Localizable.strings +++ b/MonitorControl/Support/pl.lproj/Localizable.strings @@ -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"; diff --git a/MonitorControl/Support/ru.lproj/Localizable.strings b/MonitorControl/Support/ru.lproj/Localizable.strings index e92a860..e348d7b 100644 --- a/MonitorControl/Support/ru.lproj/Localizable.strings +++ b/MonitorControl/Support/ru.lproj/Localizable.strings @@ -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"; diff --git a/MonitorControl/Support/uk.lproj/Localizable.strings b/MonitorControl/Support/uk.lproj/Localizable.strings index 3fdcd1b..106627b 100644 --- a/MonitorControl/Support/uk.lproj/Localizable.strings +++ b/MonitorControl/Support/uk.lproj/Localizable.strings @@ -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"; diff --git a/MonitorControl/Support/zh-Hans.lproj/Localizable.strings b/MonitorControl/Support/zh-Hans.lproj/Localizable.strings index bce5233..8d3e70a 100644 --- a/MonitorControl/Support/zh-Hans.lproj/Localizable.strings +++ b/MonitorControl/Support/zh-Hans.lproj/Localizable.strings @@ -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"; diff --git a/MonitorControl/UI/SliderHandler.swift b/MonitorControl/UI/SliderHandler.swift index 328ac93..b0b77c8 100644 --- a/MonitorControl/UI/SliderHandler.swift +++ b/MonitorControl/UI/SliderHandler.swift @@ -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) } diff --git a/MonitorControlHelper/Info.plist b/MonitorControlHelper/Info.plist index a0964d2..00cb818 100644 --- a/MonitorControlHelper/Info.plist +++ b/MonitorControlHelper/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 1686 + 1722 LSApplicationCategoryType public.app-category.utilities LSBackgroundOnly