From f5fcef758e82bba70af4b54b02e6ba23948cea5f Mon Sep 17 00:00:00 2001 From: waydabber <37590873+waydabber@users.noreply.github.com> Date: Sat, 31 Jul 2021 20:00:00 +0200 Subject: [PATCH] - More consistent display naming (proper name is shown even if display is shadowed in a mirror). - Display naming now should match System Report. - Some cleanup (removed unneccesary OSD.framework folder, stuff moved to Bridging-Header.h, removed search location in Build settings, framework proper linked instead). --- Apple Silicon.md | 3 +- MonitorControl.xcodeproj/project.pbxproj | 20 ++++++++-- MonitorControl/AppDelegate.swift | 43 +++++++++++++-------- MonitorControl/Info.plist | 2 +- MonitorControl/Support/Bridging-Header.h | 35 ++++++++++++++++- MonitorControlHelper/Info.plist | 2 +- OSD.framework/Headers/OSDManager.h | 21 ---------- OSD.framework/Headers/OSDUIHelperProtocol.h | 11 ------ 8 files changed, 80 insertions(+), 57 deletions(-) delete mode 100644 OSD.framework/Headers/OSDManager.h delete mode 100644 OSD.framework/Headers/OSDUIHelperProtocol.h diff --git a/Apple Silicon.md b/Apple Silicon.md index 4242640..8e2e20c 100644 --- a/Apple Silicon.md +++ b/Apple Silicon.md @@ -14,10 +14,11 @@ You need to have a *single*, *compatible* external display (aside from the inter - [x] Make DDC read work on Apple Silicon (to set up initial brightness and volume during app start). - [x] Add proper checks to safeguard things. - [x] Fix issue with internal display brightness display control on Apple Slicon (relevant services moved to a different private framework) +- [x] Better handling of mirrored displays to prepare for better mulit monitor support - [ ] Proper external multi monitor support ### About multiple external displays -Unfortunatelly proper external multi monitor support is rather difficult to achieve for several reasons (needs a complicated display matching logic based on various properties). It is doable but needs lots of work and testing to work really well. Also other changes are needed (like fixing how MonitorControl handles mirrored displays). For the M1 class devices this only affects the Mac mini when both HDMI and DP is connected. Even then the HDMI port will not work (which is a hard limitation as of now) so such users probably won't want use MonitorControl anyway. This issue will have to be resolved though for future Apple Silicon devices +Unfortunatelly proper external multi monitor support is rather difficult to achieve for several reasons (needs a complicated display matching logic based on various properties). It is doable but needs lots of work and testing to work really well. ~~Also other changes are needed (like fixing how MonitorControl handles mirrored displays - UPDATE: DONE).~~ For the M1 class devices this only affects the Mac mini when both HDMI and DP is connected. Even then the HDMI port will not work (which is a hard limitation as of now) so such users probably won't want use MonitorControl anyway. This issue will have to be resolved though for future Apple Silicon devices This does not affect MonitorControl's ability to handle the internal display alongside a single external display connected via USB-C. diff --git a/MonitorControl.xcodeproj/project.pbxproj b/MonitorControl.xcodeproj/project.pbxproj index a32b36a..366467c 100644 --- a/MonitorControl.xcodeproj/project.pbxproj +++ b/MonitorControl.xcodeproj/project.pbxproj @@ -21,7 +21,6 @@ 6C85EFDD22CBAA8F00227EA1 /* PollingModeCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C85EFDC22CBAA8F00227EA1 /* PollingModeCellView.swift */; }; 6C85EFDF22CBB54100227EA1 /* PollingCountCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C85EFDE22CBB54100227EA1 /* PollingCountCellView.swift */; }; 6C85EFE122CC00AD00227EA1 /* NSNotification+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C85EFE022CC00AD00227EA1 /* NSNotification+Extension.swift */; }; - 6CB2B64B263EDA6600F0E0EF /* OSD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0A987D61F77B290009B603D /* OSD.framework */; }; 6CBFE27A23DB266000D1BC41 /* Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CBFE27923DB266000D1BC41 /* Display.swift */; }; 6CBFE27C23DB27A200D1BC41 /* InternalDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CBFE27B23DB27A200D1BC41 /* InternalDisplay.swift */; }; 6CC260F6256AD8F900613714 /* Preferences+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC260F5256AD8F900613714 /* Preferences+Extension.swift */; }; @@ -34,6 +33,8 @@ 6CDA0FCF26485A8300F52125 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6CDA0FCD26485A8300F52125 /* Main.storyboard */; }; AA3B4A2826AE103C00B74CD2 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = AA3B4A2726AE103C00B74CD2 /* README.md */; }; AA3B4A2A26AE108E00B74CD2 /* Apple Silicon.md in Resources */ = {isa = PBXBuildFile; fileRef = AA3B4A2926AE108E00B74CD2 /* Apple Silicon.md */; }; + AA9AE86F26B5BF3D00B6CA65 /* OSD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA9AE86E26B5BF3D00B6CA65 /* OSD.framework */; }; + AA9AE87126B5BFB700B6CA65 /* CoreDisplay.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA9AE87026B5BFB700B6CA65 /* CoreDisplay.framework */; }; F01B0699228221B7008E64DB /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F01B0680228221B6008E64DB /* Localizable.strings */; }; F01B069A228221B7008E64DB /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F01B0683228221B6008E64DB /* Utils.swift */; }; F01B069E228221B7008E64DB /* ButtonCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F01B068E228221B6008E64DB /* ButtonCellView.swift */; }; @@ -115,6 +116,8 @@ 6CDA0FD826485AAE00F52125 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Main.strings; sourceTree = ""; }; AA3B4A2726AE103C00B74CD2 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; AA3B4A2926AE108E00B74CD2 /* Apple Silicon.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "Apple Silicon.md"; sourceTree = ""; }; + AA9AE86E26B5BF3D00B6CA65 /* OSD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OSD.framework; path = ../../../../../System/Library/PrivateFrameworks/OSD.framework; sourceTree = ""; }; + AA9AE87026B5BFB700B6CA65 /* CoreDisplay.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreDisplay.framework; path = ../../../../../System/Library/Frameworks/CoreDisplay.framework; sourceTree = ""; }; B0C4810623357CE500053F91 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; B0C4810823357CE500053F91 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/MainMenu.strings; sourceTree = ""; }; F01B0681228221B6008E64DB /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; @@ -138,7 +141,6 @@ F06792E9200A73460066C438 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; F06792F0200A73470066C438 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F06792F1200A73470066C438 /* MonitorControlHelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MonitorControlHelper.entitlements; sourceTree = ""; }; - F0A987D61F77B290009B603D /* OSD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OSD.framework; sourceTree = ""; }; FE4E0895249D584C003A50BB /* OSDUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSDUtils.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -147,10 +149,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + AA9AE87126B5BFB700B6CA65 /* CoreDisplay.framework in Frameworks */, 6CD35F53264FFFC6001F1344 /* SimplyCoreAudio in Frameworks */, 6CD35F5626500008001F1344 /* MediaKeyTap in Frameworks */, - 6CB2B64B263EDA6600F0E0EF /* OSD.framework in Frameworks */, 6CD35F592650002E001F1344 /* DDC in Frameworks */, + AA9AE86F26B5BF3D00B6CA65 /* OSD.framework in Frameworks */, 6CD35F5C2650003F001F1344 /* Preferences in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -168,6 +171,8 @@ 28D1DDD1227FB759004CB494 /* Frameworks */ = { isa = PBXGroup; children = ( + AA9AE86E26B5BF3D00B6CA65 /* OSD.framework */, + AA9AE87026B5BFB700B6CA65 /* CoreDisplay.framework */, 6CC260F9256ADA7400613714 /* Preferences.framework */, 28D1DDE3227FB7D0004CB494 /* AMCoreAudio.framework */, 28D1DE10227FD006004CB494 /* AMCoreAudio.framework.dSYM */, @@ -177,7 +182,6 @@ 28D1DE0E227FD005004CB494 /* MASPreferences.framework.dSYM */, 28D1DDE2227FB7D0004CB494 /* MediaKeyTap.framework */, 28D1DE0F227FD006004CB494 /* MediaKeyTap.framework.dSYM */, - F0A987D61F77B290009B603D /* OSD.framework */, ); name = Frameworks; sourceTree = ""; @@ -773,6 +777,10 @@ PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "MonitorControl/Support/Bridging-Header.h"; SWIFT_VERSION = 5.0; + SYSTEM_FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); }; name = Debug; }; @@ -801,6 +809,10 @@ PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "MonitorControl/Support/Bridging-Header.h"; SWIFT_VERSION = 5.0; + SYSTEM_FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); }; name = Release; }; diff --git a/MonitorControl/AppDelegate.swift b/MonitorControl/AppDelegate.swift index 2772b82..cd4c7ac 100644 --- a/MonitorControl/AppDelegate.swift +++ b/MonitorControl/AppDelegate.swift @@ -86,6 +86,31 @@ class AppDelegate: NSObject, NSApplicationDelegate { DisplayManager.shared.clearDisplays() } + 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 { + return name + } + if let screen = NSScreen.getByDisplayID(displayID: displayID) { + if #available(OSX 10.15, *) { + return screen.localizedName + } else { + 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 + } + func updateDisplays() { self.clearDisplays() @@ -98,23 +123,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } for onlineDisplayID in onlineDisplayIDs where onlineDisplayID != 0 { - var name: String = NSLocalizedString("Unknown", comment: "Unknown display name") + String(CGDisplaySerialNumber(onlineDisplayID)) - if let screen = NSScreen.getByDisplayID(displayID: onlineDisplayID) { - if #available(OSX 10.15, *) { - name = screen.localizedName - } else { - name = screen.displayName ?? name - } - } else if CGDisplayIsInHWMirrorSet(onlineDisplayID) != 0 || CGDisplayIsInMirrorSet(onlineDisplayID) != 0 { - if let mirroredScreen = NSScreen.getByDisplayID(displayID: CGDisplayMirrorsDisplay(onlineDisplayID)) { - name = NSLocalizedString("Mirror of", comment: "Shown in case a display mirrors an other display - like 'Mirror of DisplayName") - if #available(OSX 10.15, *) { - name = "" + name + " " + String(mirroredScreen.localizedName) - } else { - name = "" + name + " " + String(mirroredScreen.displayName ?? NSLocalizedString("Unknown", comment: "Unknown display name")) - } - } - } + let name = getDisplayName(displayID: onlineDisplayID) let id = onlineDisplayID let vendorNumber = CGDisplayVendorNumber(onlineDisplayID) let modelNumber = CGDisplayVendorNumber(onlineDisplayID) diff --git a/MonitorControl/Info.plist b/MonitorControl/Info.plist index fdd77de..23dbfda 100644 --- a/MonitorControl/Info.plist +++ b/MonitorControl/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 1097 + 1114 LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/MonitorControl/Support/Bridging-Header.h b/MonitorControl/Support/Bridging-Header.h index af30d0a..cff1bb6 100644 --- a/MonitorControl/Support/Bridging-Header.h +++ b/MonitorControl/Support/Bridging-Header.h @@ -1,10 +1,43 @@ #pragma once #import -#import #import +#import typedef CFTypeRef IOAVService; extern IOAVService IOAVServiceCreate(CFAllocatorRef allocator); extern IOReturn IOAVServiceReadI2C(IOAVService service, uint32_t chipAddress, uint32_t offset, void* outputBuffer, uint32_t outputBufferSize); extern IOReturn IOAVServiceWriteI2C(IOAVService service, uint32_t chipAddress, uint32_t dataAddress, void* inputBuffer, uint32_t inputBufferSize); +extern CFDictionaryRef CoreDisplay_DisplayCreateInfoDictionary(CGDirectDisplayID); + +@class NSString; + +@protocol OSDUIHelperProtocol +- (void)showFullScreenImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecToAnimate:(unsigned int)arg4; +- (void)fadeClassicImageOnDisplay:(unsigned int)arg1; +- (void)showImageAtPath:(NSString *)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(NSString *)arg5; +- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 filledChiclets:(unsigned int)arg5 totalChiclets:(unsigned int)arg6 locked:(BOOL)arg7; +- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(NSString *)arg5; +- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4; +@end + +@class NSXPCConnection; + +@interface OSDManager : NSObject +{ + id _proxyObject; + NSXPCConnection *connection; +} + ++ (id)sharedManager; +@property(retain) NSXPCConnection *connection; // @synthesize connection; +- (void)showFullScreenImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecToAnimate:(unsigned int)arg4; +- (void)fadeClassicImageOnDisplay:(unsigned int)arg1; +- (void)showImageAtPath:(id)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(id)arg5; +- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 filledChiclets:(unsigned int)arg5 totalChiclets:(unsigned int)arg6 locked:(BOOL)arg7; +- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(id)arg5; +- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4; +@property(readonly) id remoteObjectProxy; // @dynamic remoteObjectProxy; + +@end + diff --git a/MonitorControlHelper/Info.plist b/MonitorControlHelper/Info.plist index 109c2cc..efb2221 100644 --- a/MonitorControlHelper/Info.plist +++ b/MonitorControlHelper/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion - 1097 + 1114 LSApplicationCategoryType public.app-category.utilities LSBackgroundOnly diff --git a/OSD.framework/Headers/OSDManager.h b/OSD.framework/Headers/OSDManager.h deleted file mode 100644 index a067a8d..0000000 --- a/OSD.framework/Headers/OSDManager.h +++ /dev/null @@ -1,21 +0,0 @@ -#import "OSDUIHelperProtocol.h" - -@class NSXPCConnection; - -@interface OSDManager : NSObject -{ - id _proxyObject; - NSXPCConnection *connection; -} - -+ (id)sharedManager; -@property(retain) NSXPCConnection *connection; // @synthesize connection; -- (void)showFullScreenImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecToAnimate:(unsigned int)arg4; -- (void)fadeClassicImageOnDisplay:(unsigned int)arg1; -- (void)showImageAtPath:(id)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(id)arg5; -- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 filledChiclets:(unsigned int)arg5 totalChiclets:(unsigned int)arg6 locked:(BOOL)arg7; -- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(id)arg5; -- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4; -@property(readonly) id remoteObjectProxy; // @dynamic remoteObjectProxy; - -@end diff --git a/OSD.framework/Headers/OSDUIHelperProtocol.h b/OSD.framework/Headers/OSDUIHelperProtocol.h deleted file mode 100644 index a1246c0..0000000 --- a/OSD.framework/Headers/OSDUIHelperProtocol.h +++ /dev/null @@ -1,11 +0,0 @@ -@class NSString; - -@protocol OSDUIHelperProtocol -- (void)showFullScreenImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecToAnimate:(unsigned int)arg4; -- (void)fadeClassicImageOnDisplay:(unsigned int)arg1; -- (void)showImageAtPath:(NSString *)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(NSString *)arg5; -- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 filledChiclets:(unsigned int)arg5 totalChiclets:(unsigned int)arg6 locked:(BOOL)arg7; -- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4 withText:(NSString *)arg5; -- (void)showImage:(long long)arg1 onDisplayID:(unsigned int)arg2 priority:(unsigned int)arg3 msecUntilFade:(unsigned int)arg4; -@end -