mirror of
https://github.com/MonitorControl/MonitorControl.git
synced 2026-05-15 06:05:52 -06:00
DDC support for high-end M2 (Pro/Max/Ultra) systems (#1404)
This commit is contained in:
parent
be1f2e378d
commit
c5c8bf1b3c
5 changed files with 119 additions and 141 deletions
|
|
@ -3,6 +3,7 @@ disabled_rules:
|
|||
- function_body_length
|
||||
- identifier_name
|
||||
- trailing_comma
|
||||
- large_tuple
|
||||
type_body_length: 500
|
||||
file_length: 750
|
||||
cyclomatic_complexity:
|
||||
|
|
@ -10,4 +11,4 @@ cyclomatic_complexity:
|
|||
opening_brace:
|
||||
allow_multiline_func: true
|
||||
excluded:
|
||||
- .build
|
||||
- .build
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>7036</string>
|
||||
<string>7045</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.utilities</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
|
|
|||
|
|
@ -431,9 +431,9 @@ class OtherDisplay: Display {
|
|||
}
|
||||
DisplayManager.shared.globalDDCQueue.sync {
|
||||
if let unwrappedDelay = delay {
|
||||
values = Arm64DDC.read(service: self.arm64avService, command: controlCode, tries: UInt8(min(tries, 255)), minReplyDelay: UInt32(unwrappedDelay / 1000))
|
||||
values = Arm64DDC.read(service: self.arm64avService, command: controlCode, readSleepTime: UInt32(unwrappedDelay / 1000), numOfRetryAttemps: UInt8(min(tries, 255)))
|
||||
} else {
|
||||
values = Arm64DDC.read(service: self.arm64avService, command: controlCode, tries: UInt8(min(tries, 255)))
|
||||
values = Arm64DDC.read(service: self.arm64avService, command: controlCode, numOfRetryAttemps: UInt8(min(tries, 255)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -3,32 +3,52 @@
|
|||
import Foundation
|
||||
import IOKit
|
||||
|
||||
class Arm64DDC: NSObject {
|
||||
public struct DisplayService {
|
||||
var displayID: CGDirectDisplayID = 0
|
||||
var service: IOAVService?
|
||||
var serviceLocation: Int = 0
|
||||
var isDiscouraged: Bool = false
|
||||
var isDummy: Bool = false
|
||||
}
|
||||
let ARM64_DDC_7BIT_ADDRESS: UInt8 = 0x37 // This works with DisplayPort devices
|
||||
let ARM64_DDC_DATA_ADDRESS: UInt8 = 0x51
|
||||
|
||||
class Arm64DDC: NSObject {
|
||||
#if arch(arm64)
|
||||
public static let isArm64: Bool = true
|
||||
#else
|
||||
public static let isArm64: Bool = false
|
||||
#endif
|
||||
static let MAX_MATCH_SCORE: Int = 20
|
||||
|
||||
// This matches Displays to the right IOAVService
|
||||
public static func getServiceMatches(displayIDs: [CGDirectDisplayID]) -> [DisplayService] {
|
||||
struct IOregService {
|
||||
var edidUUID: String = ""
|
||||
var manufacturerID: String = ""
|
||||
var productName: String = ""
|
||||
var serialNumber: Int64 = 0
|
||||
var alphanumericSerialNumber: String = ""
|
||||
var location: String = ""
|
||||
var ioDisplayLocation: String = ""
|
||||
var transportUpstream: String = ""
|
||||
var transportDownstream: String = ""
|
||||
var service: IOAVService?
|
||||
var serviceLocation: Int = 0
|
||||
var displayAttributes: NSDictionary?
|
||||
}
|
||||
|
||||
struct Arm64Service {
|
||||
var displayID: CGDirectDisplayID = 0
|
||||
var service: IOAVService?
|
||||
var serviceLocation: Int = 0
|
||||
var discouraged: Bool = false
|
||||
var dummy: Bool = false
|
||||
var serviceDetails: IOregService
|
||||
var matchScore: Int = 0
|
||||
}
|
||||
|
||||
static func getServiceMatches(displayIDs: [CGDirectDisplayID]) -> [Arm64Service] {
|
||||
let ioregServicesForMatching = self.getIoregServicesForMatching()
|
||||
var matchedDisplayServices: [DisplayService] = []
|
||||
var scoredCandidateDisplayServices: [Int: [DisplayService]] = [:]
|
||||
var matchedDisplayServices: [Arm64Service] = []
|
||||
var scoredCandidateDisplayServices: [Int: [Arm64Service]] = [:]
|
||||
for displayID in displayIDs {
|
||||
for ioregServiceForMatching in ioregServicesForMatching {
|
||||
let score = self.ioregMatchScore(displayID: displayID, ioregEdidUUID: ioregServiceForMatching.edidUUID, ioregProductName: ioregServiceForMatching.productName, ioregSerialNumber: ioregServiceForMatching.serialNumber, serviceLocation: ioregServiceForMatching.serviceLocation)
|
||||
let isDiscouraged = self.checkIfDiscouraged(ioregService: ioregServiceForMatching)
|
||||
let isDummy = self.checkIfDummy(ioregService: ioregServiceForMatching)
|
||||
let displayService = DisplayService(displayID: displayID, service: ioregServiceForMatching.service, serviceLocation: ioregServiceForMatching.serviceLocation, isDiscouraged: isDiscouraged, isDummy: isDummy)
|
||||
let score = self.ioregMatchScore(displayID: displayID, ioregEdidUUID: ioregServiceForMatching.edidUUID, ioDisplayLocation: ioregServiceForMatching.ioDisplayLocation, ioregProductName: ioregServiceForMatching.productName, ioregSerialNumber: ioregServiceForMatching.serialNumber, serviceLocation: ioregServiceForMatching.serviceLocation)
|
||||
let discouraged = self.checkIfDiscouraged(ioregService: ioregServiceForMatching)
|
||||
let dummy = self.checkIfDummy(ioregService: ioregServiceForMatching)
|
||||
let displayService = Arm64Service(displayID: displayID, service: ioregServiceForMatching.service, serviceLocation: ioregServiceForMatching.serviceLocation, discouraged: discouraged, dummy: dummy, serviceDetails: ioregServiceForMatching, matchScore: score)
|
||||
if scoredCandidateDisplayServices[score] == nil {
|
||||
scoredCandidateDisplayServices[score] = []
|
||||
}
|
||||
|
|
@ -39,24 +59,21 @@ class Arm64DDC: NSObject {
|
|||
var takenDisplayIDs: [CGDirectDisplayID] = []
|
||||
for score in stride(from: self.MAX_MATCH_SCORE, to: 0, by: -1) {
|
||||
if let scoredCandidateDisplayService = scoredCandidateDisplayServices[score] {
|
||||
for candidateDisplayService in scoredCandidateDisplayService {
|
||||
if !(takenDisplayIDs.contains(candidateDisplayService.displayID) || takenServiceLocations.contains(candidateDisplayService.serviceLocation)) {
|
||||
takenDisplayIDs.append(candidateDisplayService.displayID)
|
||||
takenServiceLocations.append(candidateDisplayService.serviceLocation)
|
||||
matchedDisplayServices.append(candidateDisplayService)
|
||||
}
|
||||
for candidateDisplayService in scoredCandidateDisplayService where !(takenDisplayIDs.contains(candidateDisplayService.displayID) || takenServiceLocations.contains(candidateDisplayService.serviceLocation)) {
|
||||
takenDisplayIDs.append(candidateDisplayService.displayID)
|
||||
takenServiceLocations.append(candidateDisplayService.serviceLocation)
|
||||
matchedDisplayServices.append(candidateDisplayService)
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchedDisplayServices
|
||||
}
|
||||
|
||||
// Perform DDC read
|
||||
public static func read(service: IOAVService?, command: UInt8, tries: UInt8 = 3, minReplyDelay: UInt32 = 10000) -> (current: UInt16, max: UInt16)? {
|
||||
static func read(service: IOAVService?, command: UInt8, writeSleepTime: UInt32? = nil, numOfWriteCycles: UInt8? = nil, readSleepTime: UInt32? = nil, numOfRetryAttemps: UInt8? = nil, retrySleepTime: UInt32? = nil) -> (current: UInt16, max: UInt16)? {
|
||||
var values: (UInt16, UInt16)?
|
||||
var send: [UInt8] = [command]
|
||||
var reply = [UInt8](repeating: 0, count: 11)
|
||||
if Arm64DDC.performDDCCommunication(service: service, send: &send, reply: &reply, readSleepTime: minReplyDelay, numOfRetryAttemps: tries) {
|
||||
if Self.performDDCCommunication(service: service, send: &send, reply: &reply, writeSleepTime: writeSleepTime, numOfWriteCycles: numOfWriteCycles, readSleepTime: readSleepTime, numOfRetryAttemps: numOfRetryAttemps, retrySleepTime: retrySleepTime) {
|
||||
let max = UInt16(reply[6]) * 256 + UInt16(reply[7])
|
||||
let current = UInt16(reply[8]) * 256 + UInt16(reply[9])
|
||||
values = (current, max)
|
||||
|
|
@ -66,64 +83,41 @@ class Arm64DDC: NSObject {
|
|||
return values
|
||||
}
|
||||
|
||||
// Perform DDC write
|
||||
public static func write(service: IOAVService?, command: UInt8, value: UInt16) -> Bool {
|
||||
static func write(service: IOAVService?, command: UInt8, value: UInt16, writeSleepTime: UInt32? = nil, numOfWriteCycles: UInt8? = nil, numOfRetryAttemps: UInt8? = nil, retrySleepTime: UInt32? = nil) -> Bool {
|
||||
var send: [UInt8] = [command, UInt8(value >> 8), UInt8(value & 255)]
|
||||
var reply: [UInt8] = []
|
||||
return Arm64DDC.performDDCCommunication(service: service, send: &send, reply: &reply)
|
||||
return Self.performDDCCommunication(service: service, send: &send, reply: &reply, writeSleepTime: writeSleepTime, numOfWriteCycles: numOfWriteCycles, numOfRetryAttemps: numOfRetryAttemps, retrySleepTime: retrySleepTime)
|
||||
}
|
||||
|
||||
// Performs DDC read or write
|
||||
public static func performDDCCommunication(service: IOAVService?, send: inout [UInt8], reply: inout [UInt8], writeSleepTime: UInt32 = 10000, numofWriteCycles: UInt8 = 2, readSleepTime: UInt32 = 10000, numOfRetryAttemps: UInt8 = 3, retrySleepTime: UInt32 = 20000) -> Bool {
|
||||
static func performDDCCommunication(service: IOAVService?, send: inout [UInt8], reply: inout [UInt8], writeSleepTime: UInt32? = nil, numOfWriteCycles: UInt8? = nil, readSleepTime: UInt32? = nil, numOfRetryAttemps: UInt8? = nil, retrySleepTime: UInt32? = nil) -> Bool {
|
||||
let dataAddress = ARM64_DDC_DATA_ADDRESS
|
||||
var success = false
|
||||
guard service != nil else {
|
||||
return success
|
||||
}
|
||||
var checkedsend: [UInt8] = [UInt8(0x80 | (send.count + 1)), UInt8(send.count)] + send + [0]
|
||||
checkedsend[checkedsend.count - 1] = self.checksum(chk: send.count == 1 ? 0x6E : 0x6E ^ 0x51, data: &checkedsend, start: 0, end: checkedsend.count - 2)
|
||||
for _ in 1 ... numOfRetryAttemps {
|
||||
for _ in 1 ... numofWriteCycles {
|
||||
usleep(writeSleepTime)
|
||||
if IOAVServiceWriteI2C(service, 0x37, 0x51, &checkedsend, UInt32(checkedsend.count)) == 0 {
|
||||
success = true
|
||||
}
|
||||
var packet: [UInt8] = [UInt8(0x80 | (send.count + 1)), UInt8(send.count)] + send + [0] // Note: the last byte is the place of the checksum, see next line!
|
||||
packet[packet.count - 1] = self.checksum(chk: send.count == 1 ? ARM64_DDC_7BIT_ADDRESS << 1 : ARM64_DDC_7BIT_ADDRESS << 1 ^ dataAddress, data: &packet, start: 0, end: packet.count - 2)
|
||||
for _ in 1 ... (numOfRetryAttemps ?? 4) + 1 {
|
||||
for _ in 1 ... max((numOfWriteCycles ?? 2) + 0, 1) {
|
||||
usleep(writeSleepTime ?? 10000)
|
||||
success = IOAVServiceWriteI2C(service, UInt32(ARM64_DDC_7BIT_ADDRESS), UInt32(dataAddress), &packet, UInt32(packet.count)) == 0
|
||||
}
|
||||
if reply.count > 0 {
|
||||
usleep(readSleepTime)
|
||||
if IOAVServiceReadI2C(service, 0x37, 0x51, &reply, UInt32(reply.count)) == 0 {
|
||||
if self.checksum(chk: 0x50, data: &reply, start: 0, end: reply.count - 2) == reply[reply.count - 1] {
|
||||
success = true
|
||||
} else {
|
||||
success = false
|
||||
}
|
||||
if !reply.isEmpty {
|
||||
usleep(readSleepTime ?? 50000)
|
||||
if IOAVServiceReadI2C(service, UInt32(ARM64_DDC_7BIT_ADDRESS), UInt32(dataAddress), &reply, UInt32(reply.count)) == 0 {
|
||||
success = self.checksum(chk: 0x50, data: &reply, start: 0, end: reply.count - 2) == reply[reply.count - 1]
|
||||
}
|
||||
}
|
||||
if success {
|
||||
return success
|
||||
}
|
||||
usleep(retrySleepTime)
|
||||
usleep(retrySleepTime ?? 20000)
|
||||
}
|
||||
return success
|
||||
}
|
||||
|
||||
// -------
|
||||
|
||||
private struct IOregService {
|
||||
var edidUUID: String = ""
|
||||
var manufacturerID: String = ""
|
||||
var productName: String = ""
|
||||
var serialNumber: Int64 = 0
|
||||
var location: String = ""
|
||||
var transportUpstream: String = ""
|
||||
var transportDownstream: String = ""
|
||||
var service: IOAVService?
|
||||
var serviceLocation: Int = 0
|
||||
}
|
||||
|
||||
private static let MAX_MATCH_SCORE: Int = 13
|
||||
|
||||
// DDC checksum calculator
|
||||
private static func checksum(chk: UInt8, data: inout [UInt8], start: Int, end: Int) -> UInt8 {
|
||||
static func checksum(chk: UInt8, data: inout [UInt8], start: Int, end: Int) -> UInt8 {
|
||||
var chkd: UInt8 = chk
|
||||
for i in start ... end {
|
||||
chkd ^= data[i]
|
||||
|
|
@ -131,8 +125,7 @@ class Arm64DDC: NSObject {
|
|||
return chkd
|
||||
}
|
||||
|
||||
// Scores the likelihood of a display match based on EDID UUID, ProductName and SerialNumber from in ioreg, compared to DisplayCreateInfoDictionary.
|
||||
private static func ioregMatchScore(displayID: CGDirectDisplayID, ioregEdidUUID: String, ioregProductName: String = "", ioregSerialNumber: Int64 = 0, serviceLocation: Int = 0) -> Int {
|
||||
static func ioregMatchScore(displayID: CGDirectDisplayID, ioregEdidUUID: String, ioDisplayLocation: String = "", ioregProductName: String = "", ioregSerialNumber: Int64 = 0, serviceLocation _: Int = 0) -> Int {
|
||||
var matchScore = 0
|
||||
if let dictionary = CoreDisplay_DisplayCreateInfoDictionary(displayID)?.takeRetainedValue() as NSDictionary? {
|
||||
if let kDisplayYearOfManufacture = dictionary[kDisplayYearOfManufacture] as? Int64, let kDisplayWeekOfManufacture = dictionary[kDisplayWeekOfManufacture] as? Int64, let kDisplayVendorID = dictionary[kDisplayVendorID] as? Int64, let kDisplayProductID = dictionary[kDisplayProductID] as? Int64, let kDisplayVerticalImageSize = dictionary[kDisplayVerticalImageSize] as? Int64, let kDisplayHorizontalImageSize = dictionary[kDisplayHorizontalImageSize] as? Int64 {
|
||||
|
|
@ -154,66 +147,69 @@ class Arm64DDC: NSObject {
|
|||
+ String(format: "%02x", UInt8(max(0, min(kDisplayVerticalImageSize / 10, 256 - 1)))).uppercased(), loc: 30),
|
||||
]
|
||||
for searchKey in edidUUIDSearchKeys where searchKey.key != "0000" && searchKey.key == ioregEdidUUID.prefix(searchKey.loc + 4).suffix(4) {
|
||||
matchScore += 2
|
||||
matchScore += 1
|
||||
}
|
||||
}
|
||||
if ioDisplayLocation != "", let kIODisplayLocation = dictionary[kIODisplayLocationKey] as? String, ioDisplayLocation == kIODisplayLocation {
|
||||
matchScore += 10
|
||||
}
|
||||
if ioregProductName != "", let nameList = dictionary["DisplayProductName"] as? [String: String], let name = nameList["en_US"] ?? nameList.first?.value, name.lowercased() == ioregProductName.lowercased() {
|
||||
matchScore += 2
|
||||
matchScore += 1
|
||||
}
|
||||
if ioregSerialNumber != 0, let serial = dictionary[kDisplaySerialNumber] as? Int64, serial == ioregSerialNumber {
|
||||
matchScore += 2
|
||||
}
|
||||
if serviceLocation == displayID {
|
||||
matchScore += 1
|
||||
}
|
||||
}
|
||||
return matchScore
|
||||
}
|
||||
|
||||
// Iterate to the next AppleCLCD2 or DCPAVServiceProxy item in the ioreg tree and return the name and corresponding service
|
||||
private static func ioregIterateToNextObjectOfInterest(interests _: [String], iterator: inout io_iterator_t) -> (name: String, service: io_service_t)? {
|
||||
var objectName = ""
|
||||
var service: io_service_t = IO_OBJECT_NULL
|
||||
static func ioregIterateToNextObjectOfInterest(interests: [String], iterator: inout io_iterator_t) -> (name: String, entry: io_service_t, preceedingEntry: io_service_t)? {
|
||||
var entry: io_service_t = IO_OBJECT_NULL
|
||||
var preceedingEntry: io_service_t = IO_OBJECT_NULL
|
||||
let name = UnsafeMutablePointer<CChar>.allocate(capacity: MemoryLayout<io_name_t>.size)
|
||||
defer {
|
||||
name.deallocate()
|
||||
}
|
||||
while true {
|
||||
service = IOIteratorNext(iterator)
|
||||
guard service != MACH_PORT_NULL else {
|
||||
service = IO_OBJECT_NULL
|
||||
preceedingEntry = entry
|
||||
entry = IOIteratorNext(iterator)
|
||||
guard IORegistryEntryGetName(entry, name) == KERN_SUCCESS, entry != MACH_PORT_NULL else {
|
||||
break
|
||||
}
|
||||
guard IORegistryEntryGetName(service, name) == KERN_SUCCESS else {
|
||||
service = IO_OBJECT_NULL
|
||||
break
|
||||
}
|
||||
if String(cString: name) == "AppleCLCD2" || String(cString: name) == "DCPAVServiceProxy" {
|
||||
objectName = String(cString: name)
|
||||
return (objectName, service)
|
||||
let nameString = String(cString: name)
|
||||
for interest in interests where entry != IO_OBJECT_NULL && nameString.contains(interest) {
|
||||
return (nameString, entry, preceedingEntry)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns EDID UUDI, Product Name and Serial Number in an IOregService if it is found using the provided io_service_t pointing to a AppleCDC2 item in the ioreg tree
|
||||
private static func getIORegServiceAppleCDC2Properties(service: io_service_t) -> IOregService {
|
||||
static func getIORegServiceAppleCDC2Properties(entry: io_service_t) -> IOregService {
|
||||
var ioregService = IOregService()
|
||||
if let unmanagedEdidUUID = IORegistryEntryCreateCFProperty(service, "EDID UUID" as CFString, kCFAllocatorDefault, IOOptionBits(kIORegistryIterateRecursively)), let edidUUID = unmanagedEdidUUID.takeRetainedValue() as? String {
|
||||
if let unmanagedEdidUUID = IORegistryEntryCreateCFProperty(entry, "EDID UUID" as CFString, kCFAllocatorDefault, IOOptionBits(kIORegistryIterateRecursively)), let edidUUID = unmanagedEdidUUID.takeRetainedValue() as? String {
|
||||
ioregService.edidUUID = edidUUID
|
||||
}
|
||||
if let unmanagedDisplayAttrs = IORegistryEntryCreateCFProperty(service, "DisplayAttributes" as CFString, kCFAllocatorDefault, IOOptionBits(kIORegistryIterateRecursively)), let displayAttrs = unmanagedDisplayAttrs.takeRetainedValue() as? NSDictionary, let productAttrs = displayAttrs.value(forKey: "ProductAttributes") as? NSDictionary {
|
||||
if let manufacturerID = productAttrs.value(forKey: "ManufacturerID") as? String {
|
||||
ioregService.manufacturerID = manufacturerID
|
||||
}
|
||||
if let productName = productAttrs.value(forKey: "ProductName") as? String {
|
||||
ioregService.productName = productName
|
||||
}
|
||||
if let serialNumber = productAttrs.value(forKey: "SerialNumber") as? Int64 {
|
||||
ioregService.serialNumber = serialNumber
|
||||
let cpath = UnsafeMutablePointer<CChar>.allocate(capacity: MemoryLayout<io_string_t>.size)
|
||||
IORegistryEntryGetPath(entry, kIOServicePlane, cpath)
|
||||
ioregService.ioDisplayLocation = String(cString: cpath)
|
||||
if let unmanagedDisplayAttrs = IORegistryEntryCreateCFProperty(entry, "DisplayAttributes" as CFString, kCFAllocatorDefault, IOOptionBits(kIORegistryIterateRecursively)), let displayAttrs = unmanagedDisplayAttrs.takeRetainedValue() as? NSDictionary {
|
||||
ioregService.displayAttributes = displayAttrs
|
||||
if let productAttrs = displayAttrs.value(forKey: "ProductAttributes") as? NSDictionary {
|
||||
if let manufacturerID = productAttrs.value(forKey: "ManufacturerID") as? String {
|
||||
ioregService.manufacturerID = manufacturerID
|
||||
}
|
||||
if let productName = productAttrs.value(forKey: "ProductName") as? String {
|
||||
ioregService.productName = productName
|
||||
}
|
||||
if let serialNumber = productAttrs.value(forKey: "SerialNumber") as? Int64 {
|
||||
ioregService.serialNumber = serialNumber
|
||||
}
|
||||
if let alphanumericSerialNumber = productAttrs.value(forKey: "AlphanumericSerialNumber") as? String {
|
||||
ioregService.alphanumericSerialNumber = alphanumericSerialNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
if let unmanagedTransport = IORegistryEntryCreateCFProperty(service, "Transport" as CFString, kCFAllocatorDefault, IOOptionBits(kIORegistryIterateRecursively)), let transport = unmanagedTransport.takeRetainedValue() as? NSDictionary {
|
||||
if let unmanagedTransport = IORegistryEntryCreateCFProperty(entry, "Transport" as CFString, kCFAllocatorDefault, IOOptionBits(kIORegistryIterateRecursively)), let transport = unmanagedTransport.takeRetainedValue() as? NSDictionary {
|
||||
if let upstream = transport.value(forKey: "Upstream") as? String {
|
||||
ioregService.transportUpstream = upstream
|
||||
}
|
||||
|
|
@ -224,18 +220,16 @@ class Arm64DDC: NSObject {
|
|||
return ioregService
|
||||
}
|
||||
|
||||
// Sets up the service in an IOregService if it is found using the provided io_service_t pointing to a DCPAVServiceProxy item in the ioreg tree
|
||||
private static func setIORegServiceDCPAVServiceProxy(service: io_service_t, ioregService: inout IOregService) {
|
||||
if let unmanagedLocation = IORegistryEntryCreateCFProperty(service, "Location" as CFString, kCFAllocatorDefault, IOOptionBits(kIORegistryIterateRecursively)), let location = unmanagedLocation.takeRetainedValue() as? String {
|
||||
static func setIORegServiceDCPAVServiceProxy(entry: io_service_t, ioregService: inout IOregService) {
|
||||
if let unmanagedLocation = IORegistryEntryCreateCFProperty(entry, "Location" as CFString, kCFAllocatorDefault, IOOptionBits(kIORegistryIterateRecursively)), let location = unmanagedLocation.takeRetainedValue() as? String {
|
||||
ioregService.location = location
|
||||
if location == "External" {
|
||||
ioregService.service = IOAVServiceCreateWithService(kCFAllocatorDefault, service)?.takeRetainedValue() as IOAVService
|
||||
ioregService.service = IOAVServiceCreateWithService(kCFAllocatorDefault, entry)?.takeRetainedValue() as IOAVService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns IOAVSerivces with associated display properties for matching logic
|
||||
private static func getIoregServicesForMatching() -> [IOregService] {
|
||||
static func getIoregServicesForMatching() -> [IOregService] {
|
||||
var serviceLocation = 0
|
||||
var ioregServicesForMatching: [IOregService] = []
|
||||
let ioregRoot: io_registry_entry_t = IORegistryGetRootEntry(kIOMasterPortDefault)
|
||||
|
|
@ -250,49 +244,32 @@ class Arm64DDC: NSObject {
|
|||
guard IORegistryEntryCreateIterator(ioregRoot, "IOService", IOOptionBits(kIORegistryIterateRecursively), &iterator) == KERN_SUCCESS else {
|
||||
return ioregServicesForMatching
|
||||
}
|
||||
let keyDCPAVServiceProxy = "DCPAVServiceProxy"
|
||||
let keysFramebuffer = ["AppleCLCD2", "IOMobileFramebufferShim"]
|
||||
while true {
|
||||
if let objectOfInterest = ioregIterateToNextObjectOfInterest(interests: ["AppleCLCD2", "DCPAVServiceProxy"], iterator: &iterator) {
|
||||
if objectOfInterest.name == "AppleCLCD2", objectOfInterest.service != IO_OBJECT_NULL {
|
||||
ioregService = self.getIORegServiceAppleCDC2Properties(service: objectOfInterest.service)
|
||||
serviceLocation += 1
|
||||
ioregService.serviceLocation = serviceLocation
|
||||
}
|
||||
if objectOfInterest.name == "DCPAVServiceProxy", objectOfInterest.service != IO_OBJECT_NULL {
|
||||
self.setIORegServiceDCPAVServiceProxy(service: objectOfInterest.service, ioregService: &ioregService)
|
||||
ioregServicesForMatching.append(ioregService)
|
||||
}
|
||||
} else {
|
||||
guard let objectOfInterest = ioregIterateToNextObjectOfInterest(interests: [keyDCPAVServiceProxy] + keysFramebuffer, iterator: &iterator) else {
|
||||
break
|
||||
}
|
||||
if keysFramebuffer.contains(objectOfInterest.name) {
|
||||
ioregService = self.getIORegServiceAppleCDC2Properties(entry: objectOfInterest.entry)
|
||||
serviceLocation += 1
|
||||
ioregService.serviceLocation = serviceLocation
|
||||
} else if objectOfInterest.name == keyDCPAVServiceProxy {
|
||||
self.setIORegServiceDCPAVServiceProxy(entry: objectOfInterest.entry, ioregService: &ioregService)
|
||||
ioregServicesForMatching.append(ioregService)
|
||||
}
|
||||
}
|
||||
return ioregServicesForMatching
|
||||
}
|
||||
|
||||
// Check if display is a dummy
|
||||
private static func checkIfDummy(ioregService: IOregService) -> Bool {
|
||||
// This is a well known dummy plug
|
||||
static func checkIfDummy(ioregService: IOregService) -> Bool {
|
||||
if ioregService.manufacturerID == "AOC", ioregService.productName == "28E850" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if it is problematic to enable DDC on the display
|
||||
private static func checkIfDiscouraged(ioregService: IOregService) -> Bool {
|
||||
var modelIdentifier = ""
|
||||
let platformExpertDevice = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"))
|
||||
if let modelData = IORegistryEntryCreateCFProperty(platformExpertDevice, "model" as CFString, kCFAllocatorDefault, 0).takeRetainedValue() as? Data, let modelIdentifierCString = String(data: modelData, encoding: .utf8)?.cString(using: .utf8) {
|
||||
modelIdentifier = String(cString: modelIdentifierCString)
|
||||
}
|
||||
// First service location of M1 Mac Mini & Macbook Pro HDMI is broken for DDC communication
|
||||
let ddcBlockedModels = [
|
||||
"Macmini9", // Mac mini M1 2020
|
||||
"MacBookPro18", // Macbook Pro M1 Pro/Max 2021 (14", 16")
|
||||
]
|
||||
let isBlockedModel = ddcBlockedModels.contains { $0.starts(with: modelIdentifier) }
|
||||
if ioregService.transportDownstream == "HDMI", ioregService.serviceLocation == 1, isBlockedModel {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
static func checkIfDiscouraged(ioregService _: IOregService) -> Bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -312,10 +312,10 @@ class DisplayManager {
|
|||
for otherDisplay in self.getOtherDisplays() where otherDisplay.identifier == serviceMatch.displayID && serviceMatch.service != nil {
|
||||
otherDisplay.arm64avService = serviceMatch.service
|
||||
os_log("Display service match successful for display %{public}@", type: .info, String(serviceMatch.displayID))
|
||||
if serviceMatch.isDiscouraged {
|
||||
if serviceMatch.discouraged {
|
||||
os_log("Display %{public}@ is flagged as discouraged by Arm64DDC.", type: .info, String(serviceMatch.displayID))
|
||||
otherDisplay.isDiscouraged = true
|
||||
} else if serviceMatch.isDummy {
|
||||
} else if serviceMatch.dummy {
|
||||
os_log("Display %{public}@ is flagged as dummy by Arm64DDC.", type: .info, String(serviceMatch.displayID))
|
||||
otherDisplay.isDiscouraged = true
|
||||
otherDisplay.isDummy = true
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue