Files
asepharyana 1cdb82a76f Sync config from arch
- hypr/apps.lua
- hypr/autostart.lua
- hypr/envs.lua
- hypr/hyprland.lua
- hypr/hyprsunset.conf
- hypr/input.lua
- hypr/looknfeel.lua
- hypr/omasettings.lua
- hypr/xdph.conf
- omarchy/branding/about.txt
- omarchy/branding/screensaver.txt
- omarchy/extensions/omarchy-menu.jsonc
- omarchy/hooks/battery-low.d/play-warning-sound.sample
- omarchy/hooks/font-set.d/show-font-notification.sample
- omarchy/hooks/post-boot.d/weather.sample
- omarchy/hooks/post-update.d/install-voxtype.hook
- omarchy/hooks/post-update.d/setup-agent.hook
- omarchy/hooks/post-update.d/setup-fingerprint.hook
- omarchy/hooks/post-update.d/show-update-notification.sample
- omarchy/hooks/pre-refresh-pacman.d/add-custom-repo.sample
- omarchy/hooks/theme-set.d/show-theme-notification.sample
- omarchy/shell.json
- omarchy/shell.toml
- omarchy/theme.name
- omarchy/themes/azure-glow/README.md
- omarchy/themes/azure-glow/alacritty.toml
- omarchy/themes/azure-glow/btop.theme
- omarchy/themes/azure-glow/hyprland.conf
- omarchy/themes/azure-glow/hyprlock.conf
- omarchy/themes/azure-glow/icons.theme
- … 269 more
2026-09-23 15:19:12 +07:00

630 lines
20 KiB
QML

import QtQuick
import Quickshell.Io
import "Model.js" as Model
// Owns the Activity panel's sampling lifecycle and derived data. Keeping process I/O,
// framing, watchdogs, and power-reader escalation here leaves Panel.qml focused
// on presentation and interaction.
Item {
id: root
width: 0
height: 0
visible: false
property var settings: ({})
property bool active: false
property bool expanded: false
property bool processPowerEnabled: true
readonly property string statsHelperPath: decodeURIComponent(
String(Qt.resolvedUrl("activity-sampler")).replace("file://", ""))
readonly property string powerHelperPath: decodeURIComponent(
String(Qt.resolvedUrl("activity-power-reader")).replace("file://", ""))
readonly property var snapshot: _snapshot
readonly property var thermalSnapshot: _thermalSnapshot
readonly property var storageSnapshot: _storageSnapshot
readonly property var gpuSnapshot: _gpuSnapshot
readonly property var gpus: _gpus
readonly property var processPower: _processPower
readonly property var processes: _processes
readonly property var metrics: _metrics
readonly property bool processMetricsReady: _processMetricsReady
readonly property bool processPowerUnavailable: _processPowerState === powerStateUnavailable
readonly property string processPowerState: _processPowerState
readonly property string powerStateStopped: "stopped"
readonly property string powerStateStartingUser: "starting-user"
readonly property string powerStateReadyUser: "ready-user"
readonly property string powerStateSwitchingRoot: "switching-root"
readonly property string powerStateStartingRoot: "starting-root"
readonly property string powerStateReadyRoot: "ready-root"
readonly property string powerStateUnavailable: "unavailable"
readonly property bool samplingProfileConfigured: String(setting("samplingSpeed", "")).trim() !== ""
readonly property var samplingProfile: Model.samplingProfile(setting("samplingSpeed", "Balanced"))
readonly property int refreshInterval: intervalSetting("refreshIntervalMs", 1500, 500, 10000, samplingProfile.resources)
readonly property int processRefreshInterval: intervalSetting("processRefreshIntervalMs", 3000, 1000, 15000, samplingProfile.processes)
readonly property int thermalRefreshInterval: intervalSetting("thermalRefreshIntervalMs", 10000, 3000, 60000, samplingProfile.thermals)
readonly property int gpuRefreshInterval: intervalSetting("gpuRefreshIntervalMs", 2000, 1000, 15000, samplingProfile.gpus)
readonly property int storageRefreshInterval: intervalSetting("storageRefreshIntervalMs", 30000, 10000, 120000, samplingProfile.storage)
readonly property int historySamples: boundedSetting("historySamples", 60, 20, 240)
property var _snapshot: Model.emptySnapshot()
property var _thermalSnapshot: Model.emptyThermalSnapshot()
property var _storageSnapshot: Model.emptyStorageSnapshot()
property var _gpuSnapshot: Model.emptyGpuSnapshot()
property var _gpus: []
property var _processPower: Model.emptyPower()
property var _processes: []
property var _metrics: Model.emptyMetrics()
property bool _processMetricsReady: false
property var _previousSnapshot: Model.emptySnapshot()
property var _previousProcessSnapshot: Model.emptyProcessSnapshot()
property var _previousProcessPowerSnapshot: Model.emptyPackagePowerSnapshot()
property var _previousGpuSnapshot: Model.emptyGpuSnapshot()
property var _statsReaderBuffer: []
property bool _statsReaderReady: false
property bool _resourceSamplePending: false
property bool _processSamplePending: false
property bool _thermalSamplePending: false
property bool _gpuSamplePending: false
property bool _storageSamplePending: false
property double _resourceDue: 0
property double _processDue: 0
property double _thermalDue: 0
property double _gpuDue: 0
property double _storageDue: 0
property var _processPowerBuffer: []
property bool _processPowerSamplePending: false
property string _processPowerState: powerStateStopped
function setting(name, fallback) {
var value = settings ? settings[name] : undefined
return value === undefined || value === null ? fallback : value
}
function boundedSetting(name, fallback, minimum, maximum) {
var value = Number(setting(name, fallback))
if (!isFinite(value)) value = fallback
return Math.max(minimum, Math.min(maximum, value))
}
function intervalSetting(name, fallback, minimum, maximum, profileValue) {
if (samplingProfileConfigured)
return Math.max(minimum, Math.min(maximum, Number(profileValue) || fallback))
return boundedSetting(name, fallback, minimum, maximum)
}
function nextDeadline(previous, interval, now) {
if (previous <= 0) return now + interval
if (previous > now) return previous
return previous + (Math.floor((now - previous) / interval) + 1) * interval
}
function scheduleNextDeadline() {
deadlineScheduler.stop()
if (!active) return
var next = Math.min(_resourceDue, _processDue, _thermalDue, _storageDue)
if (expanded && _gpuDue > 0) next = Math.min(next, _gpuDue)
deadlineScheduler.interval = Math.max(1, Math.round(next - Date.now()))
deadlineScheduler.start()
}
function resetPeriodicDeadlines() {
var now = Date.now()
_resourceDue = now + refreshInterval
_processDue = now + processRefreshInterval
_thermalDue = now + thermalRefreshInterval
_storageDue = now + storageRefreshInterval
_gpuDue = expanded ? now + gpuRefreshInterval : 0
scheduleNextDeadline()
}
function collectDueSnapshots() {
if (!active) return
var now = Date.now()
if (_resourceDue <= now + 1) {
refreshResources()
_resourceDue = nextDeadline(_resourceDue, refreshInterval, now)
}
if (_processDue <= now + 1) {
refreshProcessCycle()
_processDue = nextDeadline(_processDue, processRefreshInterval, now)
}
if (_thermalDue <= now + 1) {
refreshThermals()
_thermalDue = nextDeadline(_thermalDue, thermalRefreshInterval, now)
}
if (_storageDue <= now + 1) {
refreshStorage()
_storageDue = nextDeadline(_storageDue, storageRefreshInterval, now)
}
if (expanded && _gpuDue > 0 && _gpuDue <= now + 1) {
refreshGpus()
_gpuDue = nextDeadline(_gpuDue, gpuRefreshInterval, now)
}
scheduleNextDeadline()
}
function statsPending(kind) {
if (kind === "resources") return _resourceSamplePending
if (kind === "processes") return _processSamplePending
if (kind === "thermals") return _thermalSamplePending
if (kind === "gpus") return _gpuSamplePending
if (kind === "storage") return _storageSamplePending
return false
}
function anyStatsPending() {
return _resourceSamplePending
|| _processSamplePending
|| _thermalSamplePending
|| _gpuSamplePending
|| _storageSamplePending
}
function setStatsPending(kind, value) {
if (kind === "resources") _resourceSamplePending = value
else if (kind === "processes") _processSamplePending = value
else if (kind === "thermals") _thermalSamplePending = value
else if (kind === "gpus") _gpuSamplePending = value
else if (kind === "storage") _storageSamplePending = value
}
function resetStatsReaderState() {
_statsReaderReady = false
_statsReaderBuffer = []
_resourceSamplePending = false
_processSamplePending = false
_thermalSamplePending = false
_gpuSamplePending = false
_storageSamplePending = false
statsReaderWatchdog.stop()
}
function startStatsReader() {
if (!active || statsReaderProc.running) return
_statsReaderReady = false
_statsReaderBuffer = []
statsReaderProc.running = true
}
function sendStatsRequest(kind) {
if (!active || statsPending(kind)) return
setStatsPending(kind, true)
statsReaderWatchdog.restart()
if (!statsReaderProc.running) startStatsReader()
if (_statsReaderReady) statsReaderProc.write(kind + "\n")
}
function sendPendingStatsRequests() {
if (!_statsReaderReady) return
if (_resourceSamplePending) statsReaderProc.write("resources\n")
if (_processSamplePending) statsReaderProc.write("processes\n")
if (_thermalSamplePending) statsReaderProc.write("thermals\n")
if (_gpuSamplePending) statsReaderProc.write("gpus\n")
if (_storageSamplePending) statsReaderProc.write("storage\n")
}
function refreshResources() {
sendStatsRequest("resources")
}
function refreshProcesses() {
sendStatsRequest("processes")
}
function refreshThermals() {
sendStatsRequest("thermals")
}
function refreshGpus() {
if (expanded) sendStatsRequest("gpus")
}
function refreshStorage() {
sendStatsRequest("storage")
}
function consumeStatsReaderLine(line) {
var value = String(line || "")
if (value.indexOf("snapshot-end\t") !== 0) {
_statsReaderBuffer.push(value)
return
}
var kind = value.slice("snapshot-end\t".length)
var raw = _statsReaderBuffer.join("\n")
_statsReaderBuffer = []
setStatsPending(kind, false)
if (anyStatsPending()) statsReaderWatchdog.restart()
else statsReaderWatchdog.stop()
if (kind === "resources") consumeSnapshot(raw)
else if (kind === "processes") consumeProcesses(raw)
else if (kind === "thermals") consumeThermals(raw)
else if (kind === "gpus") consumeGpus(raw)
else if (kind === "storage") consumeStorage(raw)
}
function processPowerUsesUserReader() {
return _processPowerState === powerStateStartingUser
|| _processPowerState === powerStateReadyUser
}
function processPowerReaderReady() {
return _processPowerState === powerStateReadyUser
|| _processPowerState === powerStateReadyRoot
}
function startProcessPowerReader() {
if (!active || !expanded || !processPowerEnabled
|| processPowerUnavailable || processPowerProc.running) return
var useRoot = _processPowerState === powerStateSwitchingRoot
_processPowerBuffer = []
_processPowerState = useRoot ? powerStateStartingRoot : powerStateStartingUser
processPowerProc.command = useRoot
? [powerHelperPath]
: [statsHelperPath, "--activity-process-power-reader"]
processPowerProc.running = true
}
function requestProcessPowerSample() {
if (!active || !expanded || !processPowerEnabled
|| processPowerUnavailable || _processPowerSamplePending) return
_processPowerSamplePending = true
processPowerWatchdog.restart()
if (!processPowerProc.running) startProcessPowerReader()
if (processPowerReaderReady()) processPowerProc.write("sample\n")
}
function stopProcessPowerReader() {
_processPowerState = powerStateStopped
_processPowerSamplePending = false
_processPowerBuffer = []
processPowerWatchdog.stop()
if (processPowerProc.running) processPowerProc.running = false
}
function consumeProcessPowerLine(line) {
var value = String(line || "")
if (value !== "snapshot-end\tpower") {
_processPowerBuffer.push(value)
return
}
var raw = _processPowerBuffer.join("\n")
_processPowerBuffer = []
_processPowerSamplePending = false
processPowerWatchdog.stop()
consumeProcessPower(raw)
}
function refreshProcessCycle() {
if (expanded && processPowerEnabled && !processPowerUnavailable) requestProcessPowerSample()
else refreshProcesses()
}
function refresh() {
refreshResources()
refreshProcessCycle()
refreshThermals()
refreshGpus()
refreshStorage()
}
function consumeSnapshot(raw) {
var next = Model.parseSnapshot(raw)
if (next.schema !== 1 || next.sample <= 0) return
if (Model.sameTopology(_snapshot.topology, next.topology))
next.topology = _snapshot.topology
if (_previousSnapshot.sample <= 0) {
_metrics = Model.baselineMetrics(next, metrics)
_previousSnapshot = next
_snapshot = next
if (active) resourceWarmup.restart()
return
}
_metrics = Model.nextMetrics(_previousSnapshot, next, metrics, historySamples)
_previousSnapshot = next
_snapshot = next
}
function consumeThermals(raw) {
var next = Model.parseThermalSnapshot(raw)
if (next.schema !== 1 || next.sample <= 0) return
_thermalSnapshot = next
}
function consumeStorage(raw) {
var next = Model.parseStorageSnapshot(raw)
if (next.schema !== 1 || next.sample <= 0) return
_storageSnapshot = next
}
function consumeGpus(raw) {
if (!expanded) return
var next = Model.parseGpuSnapshot(raw)
if (next.schema !== 1 || next.sample <= 0) return
if (_previousGpuSnapshot.sample <= 0) {
if (_gpus.length === 0) _gpus = Model.nextGpus(_previousGpuSnapshot, next)
_previousGpuSnapshot = next
_gpuSnapshot = next
if (active) gpuWarmup.restart()
return
}
_gpus = Model.nextGpus(_previousGpuSnapshot, next)
_previousGpuSnapshot = next
_gpuSnapshot = next
}
function consumeProcessPower(raw) {
if (!expanded || !processPowerEnabled) return
var next = Model.parsePackagePowerSnapshot(raw)
if (next.schema !== 1 || next.sample <= 0 || !Model.packageEnergyReadable(next)) {
rejectProcessPowerSample()
return
}
var needsWarmup = _previousProcessPowerSnapshot.sample <= 0
_processPower = Model.nextPackagePower(_previousProcessPowerSnapshot, next)
_previousProcessPowerSnapshot = next
if (needsWarmup && active) powerWarmup.restart()
refreshProcesses()
}
function rejectProcessPowerSample() {
var retryAsRoot = processPowerUsesUserReader()
if (retryAsRoot) {
resetProcessPowerMeasurement()
_processPowerState = powerStateSwitchingRoot
} else {
markProcessPowerUnavailable()
refreshProcesses()
}
if (processPowerProc.running) processPowerProc.running = false
}
function consumeProcesses(raw) {
var next = Model.parseProcessSnapshot(raw)
if (next.schema !== 1 || next.sample <= 0) return
if (_previousProcessSnapshot.sample <= 0) {
_previousProcessSnapshot = next
if (active) processWarmup.restart()
return
}
var busyDelta = Model.processSystemBusyDelta(_previousProcessSnapshot, next)
var nextProcesses = Model.nextProcesses(
_previousProcessSnapshot,
next,
snapshot.memory.total
)
_processes = Model.estimateProcessPower(
nextProcesses,
processPower.available ? processPower.watts : -1,
busyDelta
)
_previousProcessSnapshot = next
_processMetricsReady = true
}
function resetProcessPowerMeasurement() {
_previousProcessPowerSnapshot = Model.emptyPackagePowerSnapshot()
_processPower = Model.emptyPower()
_processes = Model.estimateProcessPower(processes, -1, -1)
}
function resetGpuMeasurement() {
_previousGpuSnapshot = Model.emptyGpuSnapshot()
}
function markProcessPowerUnavailable() {
_processPowerState = powerStateUnavailable
resetProcessPowerMeasurement()
}
function resetSessionSampling() {
_previousSnapshot = Model.emptySnapshot()
_previousProcessSnapshot = Model.emptyProcessSnapshot()
_previousProcessPowerSnapshot = Model.emptyPackagePowerSnapshot()
_previousGpuSnapshot = Model.emptyGpuSnapshot()
resetProcessPowerMeasurement()
resetStatsReaderState()
_processPowerBuffer = []
_processPowerSamplePending = false
_processPowerState = powerStateStopped
}
function startSession() {
resetSessionSampling()
refresh()
resetPeriodicDeadlines()
}
function stopSession() {
deadlineScheduler.stop()
resetStatsReaderState()
if (statsReaderProc.running) statsReaderProc.running = false
stopProcessPowerReader()
resourceWarmup.stop()
processWarmup.stop()
gpuWarmup.stop()
powerWarmup.stop()
statsReaderRetry.stop()
}
onActiveChanged: {
if (active) startSession()
else stopSession()
}
onExpandedChanged: {
if (!expanded) {
stopProcessPowerReader()
resetProcessPowerMeasurement()
resetGpuMeasurement()
_gpuDue = 0
scheduleNextDeadline()
} else if (active) {
if (processPowerEnabled)
_previousProcessPowerSnapshot = Model.emptyPackagePowerSnapshot()
_previousGpuSnapshot = Model.emptyGpuSnapshot()
refreshProcessCycle()
refreshGpus()
_gpuDue = Date.now() + gpuRefreshInterval
scheduleNextDeadline()
}
}
onProcessPowerEnabledChanged: {
if (!processPowerEnabled) {
stopProcessPowerReader()
resetProcessPowerMeasurement()
if (active) refreshProcesses()
} else if (active && expanded) {
_previousProcessPowerSnapshot = Model.emptyPackagePowerSnapshot()
refreshProcessCycle()
}
}
onRefreshIntervalChanged: if (active) resetPeriodicDeadlines()
onProcessRefreshIntervalChanged: if (active) resetPeriodicDeadlines()
onThermalRefreshIntervalChanged: if (active) resetPeriodicDeadlines()
onGpuRefreshIntervalChanged: if (active) resetPeriodicDeadlines()
onStorageRefreshIntervalChanged: if (active) resetPeriodicDeadlines()
Process {
id: statsReaderProc
command: [root.statsHelperPath, "--activity-reader"]
stdinEnabled: true
stdout: SplitParser {
onRead: function(line) { root.consumeStatsReaderLine(line) }
}
onStarted: {
root._statsReaderReady = true
root.sendPendingStatsRequests()
}
onExited: {
root.resetStatsReaderState()
if (root.active) statsReaderRetry.restart()
}
}
Process {
id: processPowerProc
stdinEnabled: true
stdout: SplitParser {
onRead: function(line) { root.consumeProcessPowerLine(line) }
}
onStarted: {
if (!root.active || !root.expanded || !root.processPowerEnabled) {
root.stopProcessPowerReader()
return
}
if (root._processPowerState === root.powerStateStartingUser)
root._processPowerState = root.powerStateReadyUser
else if (root._processPowerState === root.powerStateStartingRoot)
root._processPowerState = root.powerStateReadyRoot
else {
root.stopProcessPowerReader()
return
}
if (root._processPowerSamplePending) processPowerProc.write("sample\n")
}
onExited: {
var exitedState = root._processPowerState
root._processPowerSamplePending = false
root._processPowerBuffer = []
processPowerWatchdog.stop()
if (!root.active || !root.expanded || !root.processPowerEnabled) {
root._processPowerState = root.powerStateStopped
return
}
if (exitedState === root.powerStateStopped) {
root._processPowerState = root.powerStateStopped
Qt.callLater(function() { root.requestProcessPowerSample() })
return
}
if (exitedState === root.powerStateUnavailable) return
if (exitedState === root.powerStateSwitchingRoot
|| exitedState === root.powerStateStartingUser
|| exitedState === root.powerStateReadyUser) {
root._processPowerState = root.powerStateSwitchingRoot
Qt.callLater(function() { root.requestProcessPowerSample() })
return
}
root.markProcessPowerUnavailable()
Qt.callLater(function() { root.refreshProcesses() })
}
}
Timer {
id: statsReaderRetry
interval: 250
repeat: false
onTriggered: if (root.active) root.refresh()
}
Timer {
id: resourceWarmup
interval: 300
repeat: false
onTriggered: if (root.active) root.refreshResources()
}
Timer {
id: processWarmup
interval: 300
repeat: false
onTriggered: if (root.active) root.refreshProcesses()
}
Timer {
id: gpuWarmup
interval: 300
repeat: false
onTriggered: if (root.active && root.expanded) root.refreshGpus()
}
Timer {
id: powerWarmup
interval: 300
repeat: false
onTriggered: if (root.active && root.expanded && root.processPowerEnabled)
root.requestProcessPowerSample()
}
Timer {
id: statsReaderWatchdog
interval: 5000
repeat: false
onTriggered: {
root._statsReaderReady = false
if (statsReaderProc.running) statsReaderProc.running = false
}
}
Timer {
id: processPowerWatchdog
interval: 5000
repeat: false
onTriggered: if (processPowerProc.running) processPowerProc.running = false
}
Timer {
id: deadlineScheduler
repeat: false
onTriggered: root.collectDueSnapshots()
}
}