codeburn/mac/Sources/CodeBurnMenubar/RefreshCadence.swift
Aditya Vikram Singh 71a8e8e250
menubar: skip unchanged background refreshes, 120s AC idle cadence, utility QoS for tick spawns (#703) (#704)
Co-authored-by: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com>
2026-07-16 15:11:25 -07:00

51 lines
2.1 KiB
Swift

import Foundation
import IOKit.ps
/// Decides how often the background refresh loop may spawn CLI fetches. The
/// 30s timer keeps firing (cheap); this throttles the expensive part - each
/// fetch is a full Node process at 100%+ CPU for seconds (#647). With the
/// popover closed nobody is looking at anything but the status figure, so AC
/// uses a 120s minimum and battery or Low Power Mode backs off further. Opening the
/// popover always refreshes immediately via refreshPayloadForPopoverOpen, so
/// the backoff never shows a user stale data they are actually looking at.
enum RefreshCadence {
static let activeSeconds: TimeInterval = 30
static let acIdleSeconds: TimeInterval = 120
static let batteryIdleSeconds: TimeInterval = 150
static let lowPowerIdleSeconds: TimeInterval = 300
/// nil means "never auto-spawn" (manual mode): usage refreshes only on
/// popover open, Refresh Now, and first launch.
static func interval(
mode: UsageRefreshCadence,
popoverOpen: Bool,
onBattery: Bool,
lowPowerMode: Bool
) -> TimeInterval? {
switch mode {
case .manual:
return nil
case .auto:
if popoverOpen { return activeSeconds }
if lowPowerMode { return lowPowerIdleSeconds }
if onBattery { return batteryIdleSeconds }
return acIdleSeconds
case .oneMinute, .fiveMinutes, .fifteenMinutes:
// A fixed user-chosen cadence, except an open popover always gets
// the active cadence: the user is looking at the numbers.
return popoverOpen
? min(activeSeconds, TimeInterval(mode.rawValue))
: TimeInterval(mode.rawValue)
}
}
}
enum PowerSource {
static func isOnBattery() -> Bool {
// Copy function -> retained; Get function -> borrowed (unretained).
guard let snapshot = IOPSCopyPowerSourcesInfo()?.takeRetainedValue(),
let type = IOPSGetProvidingPowerSourceType(snapshot)?.takeUnretainedValue() as String?
else { return false }
return type == kIOPMBatteryPowerKey
}
}