mirror of
https://github.com/MonitorControl/MonitorControl.git
synced 2026-05-15 22:01:08 -06:00
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:
parent
5333f2c1cd
commit
19ef0efa11
17 changed files with 86 additions and 84 deletions
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue