From a9affda6632098b77f3df7c9314d126f92b38753 Mon Sep 17 00:00:00 2001 From: Betafer Date: Fri, 21 Aug 2026 22:25:11 +0200 Subject: [PATCH 1/2] feat(installer): add safe Mac role selection policy --- .../TargetBridgeInstallerCore.swift | 394 ++++++++++++++++++ .../tests/InstallerCoreTests.swift | 131 ++++++ .../tests/test_installer_core.zsh | 15 + 3 files changed, 540 insertions(+) create mode 100644 packaging/monitor-mode/installer-app/TargetBridgeInstallerCore.swift create mode 100644 packaging/monitor-mode/tests/InstallerCoreTests.swift create mode 100755 packaging/monitor-mode/tests/test_installer_core.zsh diff --git a/packaging/monitor-mode/installer-app/TargetBridgeInstallerCore.swift b/packaging/monitor-mode/installer-app/TargetBridgeInstallerCore.swift new file mode 100644 index 0000000..4ede1d1 --- /dev/null +++ b/packaging/monitor-mode/installer-app/TargetBridgeInstallerCore.swift @@ -0,0 +1,394 @@ +import CoreGraphics +import Foundation + +enum InstallRole: String, CaseIterable { + case sender + case receiver + + var deviceTitle: String { + switch self { + case .receiver: return "Questo Mac diventa lo schermo" + case .sender: return "Questo è il Mac che userai" + } + } + + var detail: String { + switch self { + case .receiver: + return "Mostrerà l’immagine dell’altro Mac." + case .sender: + return "Qui aprirai app, documenti e finestre." + } + } +} +enum RoleConfidence: Equatable { + case exact + case inferred + case compatibleFamily + case unsupported +} + +struct DisplayProfile: Equatable { + var widthPixels: Int + var heightPixels: Int + var physicalWidthMillimetres: Double? + var physicalHeightMillimetres: Double? + var isBuiltIn: Bool + var isActive: Bool + var isVirtual: Bool + + init( + widthPixels: Int, + heightPixels: Int, + physicalWidthMillimetres: Double? = nil, + physicalHeightMillimetres: Double? = nil, + isBuiltIn: Bool, + isActive: Bool = true, + isVirtual: Bool = false + ) { + self.widthPixels = widthPixels + self.heightPixels = heightPixels + self.physicalWidthMillimetres = physicalWidthMillimetres + self.physicalHeightMillimetres = physicalHeightMillimetres + self.isBuiltIn = isBuiltIn + self.isActive = isActive + self.isVirtual = isVirtual + } + + var pixelArea: Int64 { + Int64(max(widthPixels, 0)) * Int64(max(heightPixels, 0)) + } + + var diagonalInches: Double? { + guard let width = physicalWidthMillimetres, + let height = physicalHeightMillimetres, + width >= 100, + height >= 60 else { return nil } + return hypot(width, height) / 25.4 + } + + var isUsablePhysicalDisplay: Bool { + isActive && !isVirtual && widthPixels > 0 && heightPixels > 0 + } +} + +struct HardwareProfile: Equatable { + var modelIdentifier: String + var modelName: String + var architecture: String + var operatingSystemVersion: String + var displays: [DisplayProfile] + + init( + modelIdentifier: String, + modelName: String = "", + architecture: String, + operatingSystemVersion: String, + displays: [DisplayProfile] = [] + ) { + self.modelIdentifier = modelIdentifier + self.modelName = modelName + self.architecture = architecture + self.operatingSystemVersion = operatingSystemVersion + self.displays = displays + } + + var usableDisplays: [DisplayProfile] { + displays.filter(\.isUsablePhysicalDisplay) + } + + var hasUsableDisplay: Bool { !usableDisplays.isEmpty } + + var largestDisplay: DisplayProfile? { + usableDisplays.max(by: TargetBridgeHardwarePolicy.displayIsSmaller) + } + + var largestBuiltInDisplay: DisplayProfile? { + usableDisplays.filter(\.isBuiltIn).max(by: TargetBridgeHardwarePolicy.displayIsSmaller) + } +} + +struct RoleDecision: Equatable { + var role: InstallRole? + var confidence: RoleConfidence + var explanation: String +} + +struct PairRoleDecision: Equatable { + var firstRole: InstallRole? + var secondRole: InstallRole? + var confidence: RoleConfidence + var explanation: String +} + +enum TargetBridgeHardwarePolicy { + static let referenceReceiver = "iMac18,2" + static let referenceSender = "Mac16,10" + + static func decide(for hardware: HardwareProfile) -> RoleDecision { + switch hardware.modelIdentifier { + case referenceReceiver: + return RoleDecision( + role: .receiver, + confidence: .exact, + explanation: "iMac Retina 4K 21,5-inch 2017 riconosciuto" + ) + case referenceSender: + return RoleDecision( + role: .sender, + confidence: .exact, + explanation: "Mac mini M4 riconosciuto" + ) + default: + if isIMac(hardware) { + return RoleDecision( + role: .receiver, + confidence: .compatibleFamily, + explanation: "Il pannello integrato dell’iMac è adatto a diventare lo schermo." + ) + } + if isMacBook(hardware) { + return RoleDecision( + role: .sender, + confidence: .compatibleFamily, + explanation: "Un portatile è normalmente il Mac più comodo da usare direttamente." + ) + } + if isMacMini(hardware) { + return RoleDecision( + role: .sender, + confidence: .compatibleFamily, + explanation: hardware.hasUsableDisplay + ? "Questo Mac è normalmente quello su cui lavorerai." + : "Non risulta collegato uno schermo: questo è il Mac su cui lavorerai." + ) + } + if let diagonal = hardware.largestBuiltInDisplay?.diagonalInches { + if diagonal >= 18 { + return RoleDecision( + role: .receiver, + confidence: .inferred, + explanation: "Il grande pannello integrato è adatto a diventare lo schermo." + ) + } + if diagonal <= 17.5 { + return RoleDecision( + role: .sender, + confidence: .inferred, + explanation: "Il pannello compatto indica che probabilmente userai direttamente questo Mac." + ) + } + } + if !hardware.hasUsableDisplay { + return RoleDecision( + role: .sender, + confidence: .inferred, + explanation: "Non risulta collegato uno schermo: questo è il Mac su cui lavorerai." + ) + } + return RoleDecision( + role: nil, + confidence: .unsupported, + explanation: "Non c’è una scelta evidente. Seleziona come vuoi usare questo Mac." + ) + } + } + + static func decide(first: HardwareProfile, second: HardwareProfile) -> PairRoleDecision { + if isIMac(first) != isIMac(second) { + return complementaryDecision( + firstIsDisplay: isIMac(first), + confidence: .exact, + explanation: "L’iMac ha il pannello più adatto a diventare lo schermo." + ) + } + + if first.hasUsableDisplay != second.hasUsableDisplay { + return complementaryDecision( + firstIsDisplay: first.hasUsableDisplay, + confidence: .exact, + explanation: "Il Mac senza schermo rimane quello su cui lavorerai." + ) + } + + if isMacBook(first) != isMacBook(second) { + return complementaryDecision( + firstIsDisplay: !isMacBook(first), + confidence: .exact, + explanation: "Il portatile rimane il Mac che userai direttamente." + ) + } + + // Due portatili sono entrambi plausibili come Mac principale: la sola + // dimensione non basta per imporre all'utente una scelta sorprendente. + if isMacBook(first) && isMacBook(second) { + return PairRoleDecision( + firstRole: nil, + secondRole: nil, + confidence: .unsupported, + explanation: "I due Mac sono simili. Scegli quale deve diventare lo schermo." + ) + } + + if let firstDisplay = first.largestDisplay, + let secondDisplay = second.largestDisplay { + if let firstDiagonal = firstDisplay.diagonalInches, + let secondDiagonal = secondDisplay.diagonalInches, + abs(firstDiagonal - secondDiagonal) >= 1.5 { + return complementaryDecision( + firstIsDisplay: firstDiagonal > secondDiagonal, + confidence: .inferred, + explanation: "Il Mac con lo schermo fisicamente più grande è la scelta consigliata." + ) + } + + let smallerArea = max(1, min(firstDisplay.pixelArea, secondDisplay.pixelArea)) + let largerArea = max(firstDisplay.pixelArea, secondDisplay.pixelArea) + if Double(largerArea) / Double(smallerArea) >= 1.35 { + return complementaryDecision( + firstIsDisplay: firstDisplay.pixelArea > secondDisplay.pixelArea, + confidence: .inferred, + explanation: "Il Mac con lo schermo più definito è la scelta consigliata." + ) + } + } + + return PairRoleDecision( + firstRole: nil, + secondRole: nil, + confidence: .unsupported, + explanation: "Non c’è una scelta evidente. Seleziona quale Mac deve diventare lo schermo." + ) + } + + static func displayIsSmaller(_ lhs: DisplayProfile, _ rhs: DisplayProfile) -> Bool { + switch (lhs.diagonalInches, rhs.diagonalInches) { + case let (left?, right?) where abs(left - right) >= 0.1: + return left < right + default: + return lhs.pixelArea < rhs.pixelArea + } + } + + private static func isIMac(_ hardware: HardwareProfile) -> Bool { + let value = "\(hardware.modelName) \(hardware.modelIdentifier)".lowercased() + return value.contains("imac") + } + + private static func isMacBook(_ hardware: HardwareProfile) -> Bool { + let value = "\(hardware.modelName) \(hardware.modelIdentifier)".lowercased() + return value.contains("macbook") + } + + private static func isMacMini(_ hardware: HardwareProfile) -> Bool { + let value = "\(hardware.modelName) \(hardware.modelIdentifier)".lowercased() + return value.contains("mac mini") || value.contains("macmini") + } + + private static func complementaryDecision( + firstIsDisplay: Bool, + confidence: RoleConfidence, + explanation: String + ) -> PairRoleDecision { + PairRoleDecision( + firstRole: firstIsDisplay ? .receiver : .sender, + secondRole: firstIsDisplay ? .sender : .receiver, + confidence: confidence, + explanation: explanation + ) + } + + static func minimumMajorVersion(for role: InstallRole) -> Int { + role == .receiver ? 13 : 14 + } + + static func supportsOS(_ version: String, role: InstallRole) -> Bool { + guard let first = version.split(separator: ".").first, + let major = Int(first) else { return false } + return major >= minimumMajorVersion(for: role) + } +} + +enum TargetBridgeHardwareProbe { + static func currentProfile(environment: [String: String] = ProcessInfo.processInfo.environment) -> HardwareProfile { + let model = environment["TB_TEST_MODEL"] ?? sysctlValue("hw.model") ?? "unknown" + let modelName = environment["TB_TEST_MODEL_NAME"] + ?? (environment["TB_TEST_MODEL"] == nil ? systemProfilerModelName() : nil) + ?? "" + let architecture = environment["TB_TEST_ARCH"] ?? sysctlValue("hw.optional.arm64").flatMap { + $0 == "1" ? "arm64" : nil + } ?? machineArchitecture() + let version = environment["TB_TEST_OS_VERSION"] ?? ProcessInfo.processInfo.operatingSystemVersionString + return HardwareProfile( + modelIdentifier: model, + modelName: modelName, + architecture: architecture, + operatingSystemVersion: normalizedVersion(version), + displays: environment["TB_TEST_MODEL"] == nil ? activeDisplays() : [] + ) + } + + private static func activeDisplays() -> [DisplayProfile] { + var count: UInt32 = 0 + guard CGGetActiveDisplayList(0, nil, &count) == .success, count > 0 else { return [] } + var ids = Array(repeating: CGDirectDisplayID(), count: Int(count)) + guard CGGetActiveDisplayList(count, &ids, &count) == .success else { return [] } + return ids.prefix(Int(count)).map { id in + let size = CGDisplayScreenSize(id) + return DisplayProfile( + widthPixels: CGDisplayPixelsWide(id), + heightPixels: CGDisplayPixelsHigh(id), + physicalWidthMillimetres: size.width > 0 ? size.width : nil, + physicalHeightMillimetres: size.height > 0 ? size.height : nil, + isBuiltIn: CGDisplayIsBuiltin(id) != 0, + isActive: CGDisplayIsActive(id) != 0, + isVirtual: CGDisplayVendorNumber(id) == 0xEEEE + ) + } + } + + private static func systemProfilerModelName() -> String? { + let process = Process() + let output = Pipe() + process.executableURL = URL(fileURLWithPath: "/usr/sbin/system_profiler") + process.arguments = ["SPHardwareDataType", "-json"] + process.standardOutput = output + process.standardError = Pipe() + do { + try process.run() + process.waitUntilExit() + guard process.terminationStatus == 0 else { return nil } + let data = output.fileHandleForReading.readDataToEndOfFile() + guard let root = try JSONSerialization.jsonObject(with: data) as? [String: Any], + let items = root["SPHardwareDataType"] as? [[String: Any]], + let modelName = items.first?["machine_name"] as? String, + !modelName.isEmpty else { return nil } + return modelName + } catch { + return nil + } + } + + private static func machineArchitecture() -> String { + var info = utsname() + uname(&info) + return withUnsafePointer(to: &info.machine) { + $0.withMemoryRebound(to: CChar.self, capacity: 1) { String(cString: $0) } + } + } + + private static func sysctlValue(_ name: String) -> String? { + var size = 0 + guard sysctlbyname(name, nil, &size, nil, 0) == 0, size > 0 else { return nil } + var buffer = [CChar](repeating: 0, count: size) + guard sysctlbyname(name, &buffer, &size, nil, 0) == 0 else { return nil } + return String(cString: buffer) + } + + private static func normalizedVersion(_ value: String) -> String { + if let match = value.range(of: #"\d+(?:\.\d+){0,2}"#, options: .regularExpression) { + return String(value[match]) + } + return value + } +} diff --git a/packaging/monitor-mode/tests/InstallerCoreTests.swift b/packaging/monitor-mode/tests/InstallerCoreTests.swift new file mode 100644 index 0000000..ad4d2d1 --- /dev/null +++ b/packaging/monitor-mode/tests/InstallerCoreTests.swift @@ -0,0 +1,131 @@ +import Foundation + +@main +struct InstallerCoreTests { + private static var checks = 0 + private static var failures = 0 + + private static func check(_ label: String, _ condition: @autoclosure () -> Bool) { + checks += 1 + if condition() { + print("OK: \(label)") + } else { + failures += 1 + fputs("FAIL: \(label)\n", stderr) + } + } + + static func main() { + let imac = HardwareProfile(modelIdentifier: "iMac18,2", architecture: "x86_64", operatingSystemVersion: "13.7.8") + let mini = HardwareProfile(modelIdentifier: "Mac16,10", architecture: "arm64", operatingSystemVersion: "26.6") + let rosettaMini = HardwareProfile(modelIdentifier: "Mac16,10", architecture: "x86_64", operatingSystemVersion: "26.6") + let unknownArm = HardwareProfile( + modelIdentifier: "Mac15,6", + architecture: "arm64", + operatingSystemVersion: "15.0", + displays: [display(inches: 24, builtIn: false, width: 3840, height: 2160)] + ) + let otherIMac = HardwareProfile(modelIdentifier: "iMac18,3", architecture: "x86_64", operatingSystemVersion: "13.7") + let legacyMini = HardwareProfile(modelIdentifier: "Macmini8,1", architecture: "x86_64", operatingSystemVersion: "14.7") + let headlessMini = HardwareProfile(modelIdentifier: "Macmini9,1", architecture: "arm64", operatingSystemVersion: "14.7") + let macBook13 = HardwareProfile( + modelIdentifier: "MacBookPro17,1", + modelName: "MacBook Pro", + architecture: "arm64", + operatingSystemVersion: "15.0", + displays: [display(inches: 13.3, builtIn: true, width: 2560, height: 1600)] + ) + let largeUnknown = HardwareProfile( + modelIdentifier: "Mac99,1", + modelName: "Mac", + architecture: "arm64", + operatingSystemVersion: "15.0", + displays: [display(inches: 23.6, builtIn: true, width: 4480, height: 2520)] + ) + + check("reference iMac selects Receiver", TargetBridgeHardwarePolicy.decide(for: imac).role == .receiver) + check("reference iMac is exact", TargetBridgeHardwarePolicy.decide(for: imac).confidence == .exact) + check("reference mini selects Sender", TargetBridgeHardwarePolicy.decide(for: mini).role == .sender) + check("Rosetta cannot change mini role", TargetBridgeHardwarePolicy.decide(for: rosettaMini).role == .sender) + check("unknown desktop Mac is not guessed", TargetBridgeHardwarePolicy.decide(for: unknownArm).role == nil) + check("other Intel iMac is a warned Receiver", TargetBridgeHardwarePolicy.decide(for: otherIMac).confidence == .compatibleFamily) + check("legacy Mac mini is a warned Sender", TargetBridgeHardwarePolicy.decide(for: legacyMini).role == .sender) + check("headless Mac mini is the Mac to use", TargetBridgeHardwarePolicy.decide(for: headlessMini).role == .sender) + check("13-inch MacBook is the Mac to use", TargetBridgeHardwarePolicy.decide(for: macBook13).role == .sender) + check("large built-in panel becomes the display", TargetBridgeHardwarePolicy.decide(for: largeUnknown).role == .receiver) + check("large built-in panel is inferred", TargetBridgeHardwarePolicy.decide(for: largeUnknown).confidence == .inferred) + + let miniAndIMac = TargetBridgeHardwarePolicy.decide(first: headlessMini, second: otherIMac) + check("headless mini paired with iMac stays the Mac to use", miniAndIMac.firstRole == .sender) + check("iMac paired with headless mini becomes the display", miniAndIMac.secondRole == .receiver) + + let macBookAndIMac = TargetBridgeHardwarePolicy.decide(first: macBook13, second: otherIMac) + check("MacBook paired with iMac stays the Mac to use", macBookAndIMac.firstRole == .sender) + check("iMac paired with MacBook becomes the display", macBookAndIMac.secondRole == .receiver) + + let secondNotebook = HardwareProfile( + modelIdentifier: "MacBookAir10,1", + modelName: "MacBook Air", + architecture: "arm64", + operatingSystemVersion: "15.0", + displays: [display(inches: 13.6, builtIn: true, width: 2560, height: 1664)] + ) + let twoNotebooks = TargetBridgeHardwarePolicy.decide(first: macBook13, second: secondNotebook) + check("two notebooks require a user choice", twoNotebooks.firstRole == nil && twoNotebooks.secondRole == nil) + + let virtualOnlyMini = HardwareProfile( + modelIdentifier: "Macmini9,1", + architecture: "arm64", + operatingSystemVersion: "15.0", + displays: [DisplayProfile( + widthPixels: 5120, + heightPixels: 2880, + physicalWidthMillimetres: 600, + physicalHeightMillimetres: 340, + isBuiltIn: false, + isVirtual: true + )] + ) + check("TargetBridge virtual display is ignored", !virtualOnlyMini.hasUsableDisplay) + + let visibleRoleCopy = InstallRole.allCases + .flatMap { [$0.deviceTitle, $0.detail] } + .joined(separator: " ") + .lowercased() + check("visible role copy avoids sender", !visibleRoleCopy.contains("sender")) + check("visible role copy avoids receiver", !visibleRoleCopy.contains("receiver")) + check("visible role copy avoids trasmettitore", !visibleRoleCopy.contains("trasmettitore")) + check("visible role copy avoids ricevitore", !visibleRoleCopy.contains("ricevitore")) + check("Receiver accepts macOS 13", TargetBridgeHardwarePolicy.supportsOS("13.0", role: .receiver)) + check("Receiver rejects macOS 12", !TargetBridgeHardwarePolicy.supportsOS("12.6.9", role: .receiver)) + check("Sender accepts macOS 14", TargetBridgeHardwarePolicy.supportsOS("14.0", role: .sender)) + check("Sender rejects malformed version", !TargetBridgeHardwarePolicy.supportsOS("unknown", role: .sender)) + let normalized = TargetBridgeHardwareProbe.currentProfile(environment: [ + "TB_TEST_MODEL": "Mac16,10", + "TB_TEST_ARCH": "arm64", + "TB_TEST_OS_VERSION": "Version 26.6 (Build 25G72)" + ]) + check("runtime OS description is normalized", normalized.operatingSystemVersion == "26.6") + + print("installer core tests: \(checks) checks, \(failures) failures") + exit(failures == 0 ? 0 : 1) + } + + private static func display( + inches: Double, + builtIn: Bool, + width: Int, + height: Int + ) -> DisplayProfile { + let aspectWidth = 16.0 + let aspectHeight = 9.0 + let scale = inches * 25.4 / hypot(aspectWidth, aspectHeight) + return DisplayProfile( + widthPixels: width, + heightPixels: height, + physicalWidthMillimetres: aspectWidth * scale, + physicalHeightMillimetres: aspectHeight * scale, + isBuiltIn: builtIn + ) + } +} diff --git a/packaging/monitor-mode/tests/test_installer_core.zsh b/packaging/monitor-mode/tests/test_installer_core.zsh new file mode 100755 index 0000000..1d24dea --- /dev/null +++ b/packaging/monitor-mode/tests/test_installer_core.zsh @@ -0,0 +1,15 @@ +#!/bin/zsh +set -euo pipefail + +TEST_DIR="${0:A:h}" +SOURCE_DIR="${TEST_DIR:h}/installer-app" +BUILD_DIR="$(/usr/bin/mktemp -d /private/tmp/targetbridge-installer-core-tests.XXXXXX)" +trap '/bin/rm -R "${BUILD_DIR}"' EXIT + +/usr/bin/xcrun swiftc \ + -module-cache-path "${BUILD_DIR}/ModuleCache" \ + "${SOURCE_DIR}/TargetBridgeInstallerCore.swift" \ + "${TEST_DIR}/InstallerCoreTests.swift" \ + -o "${BUILD_DIR}/InstallerCoreTests" + +"${BUILD_DIR}/InstallerCoreTests" From b0acf6680b0e1677dc3b08a54bf65cbc14ab49f7 Mon Sep 17 00:00:00 2001 From: Betafer Date: Fri, 21 Aug 2026 22:26:28 +0200 Subject: [PATCH 2/2] style(installer): separate role policy declarations --- .../monitor-mode/installer-app/TargetBridgeInstallerCore.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/monitor-mode/installer-app/TargetBridgeInstallerCore.swift b/packaging/monitor-mode/installer-app/TargetBridgeInstallerCore.swift index 4ede1d1..8a08292 100644 --- a/packaging/monitor-mode/installer-app/TargetBridgeInstallerCore.swift +++ b/packaging/monitor-mode/installer-app/TargetBridgeInstallerCore.swift @@ -21,6 +21,7 @@ enum InstallRole: String, CaseIterable { } } } + enum RoleConfidence: Equatable { case exact case inferred