From 267c4ba092aad26818e48a8eea39ff4dc67e3434 Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Sun, 19 Oct 2025 13:28:42 +0530 Subject: [PATCH] BugFix: Memory usage --- DynamicIsland/ContentView.swift | 58 +++++++------- .../Stats/CPUStatsSharedViews.swift | 5 ++ DynamicIsland/managers/StatsManager.swift | 79 +++++++++++-------- 3 files changed, 80 insertions(+), 62 deletions(-) diff --git a/DynamicIsland/ContentView.swift b/DynamicIsland/ContentView.swift index 97476c9..e5c6517 100644 --- a/DynamicIsland/ContentView.swift +++ b/DynamicIsland/ContentView.swift @@ -36,35 +36,20 @@ struct ContentView: View { // Dynamic sizing based on view type and graph count with smooth transitions var dynamicNotchSize: CGSize { - // Use minimalistic or normal size based on settings let baseSize = Defaults[.enableMinimalisticUI] ? minimalisticOpenNotchSize : openNotchSize - // Always return the target size for the current view to ensure smooth transitions - let targetSize: CGSize - - if coordinator.currentView == .stats { - // Count enabled graphs using the same logic as NotchStatsView - var enabledGraphsCount = 0 - if showCpuGraph { enabledGraphsCount += 1 } - if showMemoryGraph { enabledGraphsCount += 1 } - if showGpuGraph { enabledGraphsCount += 1 } - if showNetworkGraph { enabledGraphsCount += 1 } - if showDiskGraph { enabledGraphsCount += 1 } - - if enabledGraphsCount > 3 { - // Expand height for 4+ graphs - targetSize = CGSize( - width: baseSize.width, - height: baseSize.height + 65 // Add extra height for second row - ) - } else { - targetSize = baseSize - } - } else { - targetSize = baseSize + guard coordinator.currentView == .stats else { + return baseSize } - return targetSize + let rows = statsRowCount() + if rows <= 1 { + return baseSize + } + + let additionalRows = max(rows - 1, 0) + let extraHeight = CGFloat(additionalRows) * statsAdditionalRowHeight + return CGSize(width: baseSize.width, height: baseSize.height + extraHeight) } @@ -96,6 +81,7 @@ struct ContentView: View { private let extendedHoverPadding: CGFloat = 30 private let zeroHeightHoverPadding: CGFloat = 10 + private let statsAdditionalRowHeight: CGFloat = 110 // Use minimalistic corner radius ONLY when opened, keep normal when closed private var activeCornerRadiusInsets: (opened: (top: CGFloat, bottom: CGFloat), closed: (top: CGFloat, bottom: CGFloat)) { @@ -266,10 +252,12 @@ struct ContentView: View { isViewTransitioning = false // Check if this transition will cause a size change - let oldSize = oldValue == .stats && statsTabHasExpandedHeight() ? - CGSize(width: openNotchSize.width, height: openNotchSize.height + 65) : openNotchSize - let newSize = newValue == .stats && statsTabHasExpandedHeight() ? - CGSize(width: openNotchSize.width, height: openNotchSize.height + 65) : openNotchSize + let baseOpenSize = Defaults[.enableMinimalisticUI] ? minimalisticOpenNotchSize : openNotchSize + let expandedHeight = baseOpenSize.height + statsAdditionalRowHeight + let oldSize = oldValue == .stats && statsTabHasExpandedHeight() ? + CGSize(width: baseOpenSize.width, height: expandedHeight) : baseOpenSize + let newSize = newValue == .stats && statsTabHasExpandedHeight() ? + CGSize(width: baseOpenSize.width, height: expandedHeight) : baseOpenSize let sizeWillChange = oldSize != newSize // Set flags for transition tracking @@ -780,13 +768,23 @@ struct ContentView: View { // Helper to check if stats tab has 4+ graphs (needs expanded height) private func statsTabHasExpandedHeight() -> Bool { + return statsRowCount() > 1 + } + + private func enabledStatsGraphCount() -> Int { var enabledCount = 0 if showCpuGraph { enabledCount += 1 } if showMemoryGraph { enabledCount += 1 } if showGpuGraph { enabledCount += 1 } if showNetworkGraph { enabledCount += 1 } if showDiskGraph { enabledCount += 1 } - return enabledCount > 3 + return enabledCount + } + + private func statsRowCount() -> Int { + let count = enabledStatsGraphCount() + if count == 0 { return 0 } + return count <= 3 ? 1 : 2 } // MARK: - Gesture Handling diff --git a/DynamicIsland/components/Stats/CPUStatsSharedViews.swift b/DynamicIsland/components/Stats/CPUStatsSharedViews.swift index bb0abac..f155dbd 100644 --- a/DynamicIsland/components/Stats/CPUStatsSharedViews.swift +++ b/DynamicIsland/components/Stats/CPUStatsSharedViews.swift @@ -227,10 +227,15 @@ struct DetailRow: View { Text(label) .font(.system(size: 12, weight: .medium)) .foregroundColor(.secondary) + .lineLimit(1) + .layoutPriority(0.5) Spacer() Text(value) .font(.system(size: 13, weight: .semibold, design: .monospaced)) .foregroundColor(.primary) + .lineLimit(1) + .minimumScaleFactor(0.85) + .layoutPriority(1) } } } diff --git a/DynamicIsland/managers/StatsManager.swift b/DynamicIsland/managers/StatsManager.swift index 8340757..10f540d 100644 --- a/DynamicIsland/managers/StatsManager.swift +++ b/DynamicIsland/managers/StatsManager.swift @@ -400,6 +400,19 @@ class StatsManager: ObservableObject { private var delayedStopTimer: Timer? private var delayedStartTimer: Timer? private let maxHistoryPoints = 30 + private let totalPhysicalMemory: UInt64 = { + var stats = host_basic_info() + var count = mach_msg_type_number_t(MemoryLayout.size / MemoryLayout.size) + let result = withUnsafeMutablePointer(to: &stats) { + $0.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { + host_info(mach_host_self(), HOST_BASIC_INFO, $0, &count) + } + } + if result == KERN_SUCCESS { + return UInt64(stats.max_mem) + } + return UInt64(ProcessInfo.processInfo.physicalMemory) + }() // Smart monitoring state private var shouldMonitorForStats: Bool = false @@ -759,30 +772,32 @@ class StatsManager: ObservableObject { } let pageSize = UInt64(vm_kernel_page_size) - let freeCount = UInt64(vmStatistics.free_count) + UInt64(vmStatistics.speculative_count) - let activeCount = UInt64(vmStatistics.active_count) - let inactiveCount = UInt64(vmStatistics.inactive_count) - let wiredCount = UInt64(vmStatistics.wire_count) - let compressorCount = UInt64(vmStatistics.compressor_page_count) + let freeBytes = UInt64(vmStatistics.free_count) * pageSize + let speculativeBytes = UInt64(vmStatistics.speculative_count) * pageSize + let activeBytes = UInt64(vmStatistics.active_count) * pageSize + let inactiveBytes = UInt64(vmStatistics.inactive_count) * pageSize + let wiredBytes = UInt64(vmStatistics.wire_count) * pageSize + let compressedBytes = UInt64(vmStatistics.compressor_page_count) * pageSize + let purgeableBytes = UInt64(vmStatistics.purgeable_count) * pageSize + let externalBytes = UInt64(vmStatistics.external_page_count) * pageSize - let totalMemory = (freeCount + activeCount + inactiveCount + wiredCount) * pageSize - guard totalMemory > 0 else { + let totalMemoryBytes: UInt64 + if totalPhysicalMemory > 0 { + totalMemoryBytes = totalPhysicalMemory + } else { + totalMemoryBytes = freeBytes + speculativeBytes + activeBytes + inactiveBytes + wiredBytes + compressedBytes + } + guard totalMemoryBytes > 0 else { return (0.0, .zero) } - let freeMemory = freeCount * pageSize - let speculativeCount = UInt64(vmStatistics.speculative_count) - let externalCount = UInt64(vmStatistics.external_page_count) - let purgeableCount = UInt64(vmStatistics.purgeable_count) - let activeMemory = activeCount * pageSize - let inactiveMemory = inactiveCount * pageSize - let wiredMemory = wiredCount * pageSize - let compressedMemory = compressorCount * pageSize - let purgeableMemory = purgeableCount * pageSize - let externalMemory = externalCount * pageSize - let usedMemory = totalMemory > freeMemory ? totalMemory - freeMemory : 0 - let appMemory = usedMemory > (wiredMemory + compressedMemory) ? usedMemory - wiredMemory - compressedMemory : 0 - let cacheMemory = purgeableMemory + externalMemory + let usedWithoutCache = activeBytes + inactiveBytes + speculativeBytes + wiredBytes + compressedBytes + let cacheBytes = purgeableBytes + externalBytes + let usedBytes = usedWithoutCache > cacheBytes ? usedWithoutCache - cacheBytes : 0 + let clampedUsedBytes = min(usedBytes, totalMemoryBytes) + let freeComputedBytes = totalMemoryBytes > clampedUsedBytes ? totalMemoryBytes - clampedUsedBytes : 0 + let nonAppBytes = wiredBytes + compressedBytes + let appBytes = clampedUsedBytes > nonAppBytes ? clampedUsedBytes - nonAppBytes : 0 var pressureLevelRaw: Int32 = 0 var pressureSize = MemoryLayout.size @@ -807,17 +822,17 @@ class StatsManager: ObservableObject { freeBytes: UInt64(swapUsage.xsu_avail) ) - let usage = Double(usedMemory) / Double(totalMemory) * 100.0 + let usage = Double(clampedUsedBytes) / Double(totalMemoryBytes) * 100.0 let breakdown = MemoryBreakdown( - totalBytes: totalMemory, - usedBytes: usedMemory, - freeBytes: freeMemory, - wiredBytes: wiredMemory, - activeBytes: activeMemory, - inactiveBytes: inactiveMemory, - compressedBytes: compressedMemory, - appBytes: appMemory, - cacheBytes: cacheMemory, + totalBytes: totalMemoryBytes, + usedBytes: clampedUsedBytes, + freeBytes: freeComputedBytes, + wiredBytes: wiredBytes, + activeBytes: activeBytes, + inactiveBytes: inactiveBytes, + compressedBytes: compressedBytes, + appBytes: appBytes, + cacheBytes: cacheBytes, swap: swapInfo, pressure: pressure ) @@ -1237,11 +1252,11 @@ class StatsManager: ObservableObject { } var networkDownloadString: String { - return String(format: "%.1f MB/s", networkDownload) + StatsFormatting.throughput(networkDownload) } var networkUploadString: String { - return String(format: "%.1f MB/s", networkUpload) + StatsFormatting.throughput(networkUpload) } var diskReadString: String {