pragma Singleton pragma ComponentBehavior: Bound import qs.modules.common import QtQuick import Quickshell import Quickshell.Io /** * Simple polled resource usage service with RAM, Swap, and CPU usage. */ Singleton { id: root property real memoryTotal: 1 property real memoryFree: 0 property real memoryUsed: memoryTotal - memoryFree property real memoryUsedPercentage: memoryUsed / memoryTotal property real swapTotal: 1 property real swapFree: 0 property real swapUsed: swapTotal - swapFree property real swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0 property real cpuUsage: 0 property var previousCpuStats property string maxAvailableMemoryString: kbToGbString(ResourceUsage.memoryTotal) property string maxAvailableSwapString: kbToGbString(ResourceUsage.swapTotal) property string maxAvailableCpuString: "--" readonly property int historyLength: Config?.options.resources.historyLength ?? 60 property list cpuUsageHistory: [] property list memoryUsageHistory: [] property list swapUsageHistory: [] function kbToGbString(kb) { return (kb / (1024 * 1024)).toFixed(1) + " GB"; } function updateMemoryUsageHistory() { memoryUsageHistory = [...memoryUsageHistory, memoryUsedPercentage] if (memoryUsageHistory.length > historyLength) { memoryUsageHistory.shift() } } function updateSwapUsageHistory() { swapUsageHistory = [...swapUsageHistory, swapUsedPercentage] if (swapUsageHistory.length > historyLength) { swapUsageHistory.shift() } } function updateCpuUsageHistory() { cpuUsageHistory = [...cpuUsageHistory, cpuUsage] if (cpuUsageHistory.length > historyLength) { cpuUsageHistory.shift() } } function updateHistories() { updateMemoryUsageHistory() updateSwapUsageHistory() updateCpuUsageHistory() } Timer { interval: 1 running: true repeat: true onTriggered: { // Reload files fileMeminfo.reload() fileStat.reload() fileCpuinfo.reload() // Parse memory and swap usage const textMeminfo = fileMeminfo.text() memoryTotal = Number(textMeminfo.match(/MemTotal: *(\d+)/)?.[1] ?? 1) memoryFree = Number(textMeminfo.match(/MemAvailable: *(\d+)/)?.[1] ?? 0) swapTotal = Number(textMeminfo.match(/SwapTotal: *(\d+)/)?.[1] ?? 1) swapFree = Number(textMeminfo.match(/SwapFree: *(\d+)/)?.[1] ?? 0) // Parse CPU usage const textStat = fileStat.text() const cpuLine = textStat.match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/) if (cpuLine) { const stats = cpuLine.slice(1).map(Number) const total = stats.reduce((a, b) => a + b, 0) const idle = stats[3] if (previousCpuStats) { const totalDiff = total - previousCpuStats.total const idleDiff = idle - previousCpuStats.idle cpuUsage = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0 } previousCpuStats = { total, idle } } // Parse max CPU frequency const textCpuinfo = fileCpuinfo.text() // Try to find 'cpu max MHz', fallback to highest 'cpu MHz' let maxMHz = 0 let match // Try cpu max MHz (modern kernels) match = textCpuinfo.match(/cpu max MHz\s*:\s*([\d.]+)/) if (match) { maxMHz = Number(match[1]) } else { // Fallback: find all cpu MHz lines and take the max let mhzRegex = /cpu MHz\s*:\s*([\d.]+)/g let mhzMatch let mhzList = [] while ((mhzMatch = mhzRegex.exec(textCpuinfo)) !== null) { mhzList.push(Number(mhzMatch[1])) } if (mhzList.length > 0) { maxMHz = Math.max.apply(null, mhzList) } } root.maxAvailableCpuString = maxMHz > 0 ? (maxMHz / 1000).toFixed(1) + "GHz" : "--" root.updateHistories() interval = Config.options?.resources?.updateInterval ?? 3000 } } FileView { id: fileMeminfo; path: "/proc/meminfo" } FileView { id: fileStat; path: "/proc/stat" } FileView { id: fileCpuinfo; path: "/proc/cpuinfo" } }