Atoll/DynamicIsland/utils/AppleHardwareInfo.swift
2025-10-19 15:43:44 +05:30

162 lines
5.7 KiB
Swift

//
// AppleHardwareInfo.swift
// DynamicIsland
//
// Minimal system introspection helpers derived from the Stats project.
//
import Foundation
import IOKit
import Darwin
enum ApplePlatform: String {
case intel
case m1
case m1Pro
case m1Max
case m1Ultra
case m2
case m2Pro
case m2Max
case m2Ultra
case m3
case m3Pro
case m3Max
case m3Ultra
case m4
case m4Pro
case m4Max
case m4Ultra
}
struct CPUClusterFrequencies {
let eCoreFrequencies: [Int32]
let pCoreFrequencies: [Int32]
}
struct CPUClusterCounts {
let eCores: Int
let pCores: Int
}
final class AppleHardwareInfo {
static let shared = AppleHardwareInfo()
let cpuName: String?
let platform: ApplePlatform?
let logicalCoreCount: Int
let clusterCounts: CPUClusterCounts
let clusterFrequencies: CPUClusterFrequencies
private init() {
let name = AppleHardwareInfo.fetchCPUName()
cpuName = name
platform = AppleHardwareInfo.detectPlatform(from: name)
logicalCoreCount = AppleHardwareInfo.fetchLogicalCoreCount()
clusterCounts = AppleHardwareInfo.fetchClusterCounts()
clusterFrequencies = AppleHardwareInfo.fetchClusterFrequencies(cpuName: name ?? "")
}
private static func fetchCPUName() -> String? {
var size = 0
sysctlbyname("machdep.cpu.brand_string", nil, &size, nil, 0)
guard size > 0 else { return nil }
var buffer = [CChar](repeating: 0, count: size)
let result = sysctlbyname("machdep.cpu.brand_string", &buffer, &size, nil, 0)
guard result == 0 else { return nil }
return String(cString: buffer)
}
private static func fetchLogicalCoreCount() -> Int {
var cores: Int32 = 0
var size = MemoryLayout<Int32>.size
let result = sysctlbyname("hw.logicalcpu", &cores, &size, nil, 0)
return result == 0 ? Int(cores) : ProcessInfo.processInfo.processorCount
}
private static func detectPlatform(from cpuName: String?) -> ApplePlatform? {
guard let name = cpuName?.lowercased() else { return nil }
if name.contains("intel") { return .intel }
if name.contains("m1") {
if name.contains("ultra") { return .m1Ultra }
if name.contains("max") { return .m1Max }
if name.contains("pro") { return .m1Pro }
return .m1
}
if name.contains("m2") {
if name.contains("ultra") { return .m2Ultra }
if name.contains("max") { return .m2Max }
if name.contains("pro") { return .m2Pro }
return .m2
}
if name.contains("m3") {
if name.contains("ultra") { return .m3Ultra }
if name.contains("max") { return .m3Max }
if name.contains("pro") { return .m3Pro }
return .m3
}
if name.contains("m4") {
if name.contains("ultra") { return .m4Ultra }
if name.contains("max") { return .m4Max }
if name.contains("pro") { return .m4Pro }
return .m4
}
return nil
}
private static func fetchClusterCounts() -> CPUClusterCounts {
var iterator = io_iterator_t()
guard IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("AppleARMPE"), &iterator) == KERN_SUCCESS else {
return CPUClusterCounts(eCores: 0, pCores: 0)
}
defer { IOObjectRelease(iterator) }
var eCoreCount: Int32 = 0
var pCoreCount: Int32 = 0
while case let service = IOIteratorNext(iterator), service != 0 {
defer { IOObjectRelease(service) }
var children = io_iterator_t()
guard IORegistryEntryGetChildIterator(service, kIOServicePlane, &children) == KERN_SUCCESS else { continue }
defer { IOObjectRelease(children) }
while case let child = IOIteratorNext(children), child != 0 {
defer { IOObjectRelease(child) }
guard let name = di_getIOName(child), name == "cpus", let props = di_getIOProperties(child) else { continue }
if let data = props.object(forKey: "e-core-count") as? Data {
eCoreCount = data.withUnsafeBytes { $0.load(as: Int32.self) }
}
if let data = props.object(forKey: "p-core-count") as? Data {
pCoreCount = data.withUnsafeBytes { $0.load(as: Int32.self) }
}
}
}
return CPUClusterCounts(eCores: Int(eCoreCount), pCores: Int(pCoreCount))
}
private static func fetchClusterFrequencies(cpuName: String) -> CPUClusterFrequencies {
var iterator = io_iterator_t()
guard IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("AppleARMIODevice"), &iterator) == KERN_SUCCESS else {
return CPUClusterFrequencies(eCoreFrequencies: [], pCoreFrequencies: [])
}
defer { IOObjectRelease(iterator) }
let lowerName = cpuName.lowercased()
let isM4 = lowerName.contains("m4")
var eFreq: [Int32] = []
var pFreq: [Int32] = []
while case let service = IOIteratorNext(iterator), service != 0 {
defer { IOObjectRelease(service) }
guard let name = di_getIOName(service), name == "pmgr", let props = di_getIOProperties(service) else { continue }
if let data = props["voltage-states1-sram"] as? Data {
eFreq = di_convertCFDataToFrequencies(data, isM4: isM4)
}
if let data = props["voltage-states5-sram"] as? Data {
pFreq = di_convertCFDataToFrequencies(data, isM4: isM4)
}
}
return CPUClusterFrequencies(eCoreFrequencies: eFreq, pCoreFrequencies: pFreq)
}
}