mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
system-stats: many optimizations, removed GUI settings to control polling as it's too risky, disable all when on the lockscreen.
This commit is contained in:
@@ -311,13 +311,7 @@
|
||||
"diskAvailCriticalThreshold": 10,
|
||||
"batteryWarningThreshold": 20,
|
||||
"batteryCriticalThreshold": 5,
|
||||
"cpuPollingInterval": 1000,
|
||||
"gpuPollingInterval": 3000,
|
||||
"enableDgpuMonitoring": false,
|
||||
"memPollingInterval": 1000,
|
||||
"diskPollingInterval": 30000,
|
||||
"networkPollingInterval": 1000,
|
||||
"loadAvgPollingInterval": 3000,
|
||||
"useCustomColors": false,
|
||||
"warningColor": "",
|
||||
"criticalColor": "",
|
||||
|
||||
@@ -5,7 +5,7 @@ QtObject {
|
||||
id: root
|
||||
|
||||
function migrate(adapter, logger, rawJson) {
|
||||
logger.i("Migration47", "Removing network_stats.json cache and updating polling intervals");
|
||||
logger.i("Migration47", "Removing network_stats.json cache");
|
||||
|
||||
// Remove the network_stats.json cache file (no longer used - autoscaling from history now)
|
||||
const shellName = "noctalia";
|
||||
@@ -13,15 +13,7 @@ QtObject {
|
||||
const networkStatsFile = cacheDir + "network_stats.json";
|
||||
Quickshell.execDetached(["rm", "-f", networkStatsFile]);
|
||||
|
||||
// Update polling intervals to 1000ms for smoother graphs (only if currently slower)
|
||||
if (adapter.systemMonitor.cpuPollingInterval > 1000)
|
||||
adapter.systemMonitor.cpuPollingInterval = 1000;
|
||||
if (adapter.systemMonitor.memPollingInterval > 1000)
|
||||
adapter.systemMonitor.memPollingInterval = 1000;
|
||||
if (adapter.systemMonitor.networkPollingInterval > 1000)
|
||||
adapter.systemMonitor.networkPollingInterval = 1000;
|
||||
|
||||
logger.d("Migration47", "Removed network_stats.json and adjusted polling intervals");
|
||||
logger.d("Migration47", "Removed network_stats.json");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -510,13 +510,7 @@ Singleton {
|
||||
property int diskAvailCriticalThreshold: 10
|
||||
property int batteryWarningThreshold: 20
|
||||
property int batteryCriticalThreshold: 5
|
||||
property int cpuPollingInterval: 1000
|
||||
property int gpuPollingInterval: 3000
|
||||
property bool enableDgpuMonitoring: false // Opt-in: reading dGPU sysfs/nvidia-smi wakes it from D3cold, draining battery
|
||||
property int memPollingInterval: 1000
|
||||
property int diskPollingInterval: 30000
|
||||
property int networkPollingInterval: 1000
|
||||
property int loadAvgPollingInterval: 3000
|
||||
property bool useCustomColors: false
|
||||
property string warningColor: ""
|
||||
property string criticalColor: ""
|
||||
|
||||
@@ -81,6 +81,9 @@ Item {
|
||||
implicitWidth: contentWidth
|
||||
implicitHeight: contentHeight
|
||||
|
||||
Component.onCompleted: SystemStatService.registerComponent("bar-sysmon:" + (screen?.name || "unknown"))
|
||||
Component.onDestruction: SystemStatService.unregisterComponent("bar-sysmon:" + (screen?.name || "unknown"))
|
||||
|
||||
function openExternalMonitor() {
|
||||
Quickshell.execDetached(["sh", "-c", Settings.data.systemMonitor.externalMonitor]);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ import qs.Widgets
|
||||
NBox {
|
||||
id: root
|
||||
|
||||
Component.onCompleted: SystemStatService.registerComponent("card-sysmonitor")
|
||||
Component.onDestruction: SystemStatService.unregisterComponent("card-sysmonitor")
|
||||
|
||||
readonly property string diskPath: Settings.data.controlCenter.diskPath || "/"
|
||||
readonly property real contentScale: 0.95 * Style.uiScaleRatio
|
||||
|
||||
|
||||
@@ -158,6 +158,9 @@ DraggableDesktopWidget {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: SystemStatService.registerComponent("desktop-sysstat:" + root.statType)
|
||||
Component.onDestruction: SystemStatService.unregisterComponent("desktop-sysstat:" + root.statType)
|
||||
|
||||
implicitWidth: Math.round(240 * widgetScale)
|
||||
implicitHeight: Math.round(120 * widgetScale)
|
||||
width: implicitWidth
|
||||
@@ -167,15 +170,15 @@ DraggableDesktopWidget {
|
||||
readonly property int graphUpdateInterval: {
|
||||
switch (root.statType) {
|
||||
case "CPU":
|
||||
return Settings.data.systemMonitor.cpuPollingInterval;
|
||||
return SystemStatService.cpuIntervalMs;
|
||||
case "GPU":
|
||||
return Settings.data.systemMonitor.gpuPollingInterval;
|
||||
return SystemStatService.gpuIntervalMs;
|
||||
case "Memory":
|
||||
return Settings.data.systemMonitor.memPollingInterval;
|
||||
return SystemStatService.memIntervalMs;
|
||||
case "Disk":
|
||||
return Settings.data.systemMonitor.diskPollingInterval;
|
||||
return SystemStatService.diskIntervalMs;
|
||||
case "Network":
|
||||
return Settings.data.systemMonitor.networkPollingInterval;
|
||||
return SystemStatService.networkIntervalMs;
|
||||
default:
|
||||
return 1000;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import qs.Widgets
|
||||
Item {
|
||||
id: root
|
||||
|
||||
Component.onDestruction: SystemStatService.unregisterComponent("settings")
|
||||
|
||||
// Screen reference for child components
|
||||
property var screen
|
||||
|
||||
@@ -389,6 +391,7 @@ Item {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
SystemStatService.registerComponent("settings");
|
||||
// Restore sidebar state
|
||||
sidebarExpanded = ShellState.getSettingsSidebarExpanded();
|
||||
}
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Services.System
|
||||
import qs.Widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: Style.marginL
|
||||
Layout.fillWidth: true
|
||||
|
||||
NLabel {
|
||||
Layout.fillWidth: true
|
||||
description: I18n.tr("panels.system-monitor.polling-section-description")
|
||||
}
|
||||
// CPU Polling
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
NText {
|
||||
Layout.fillWidth: true
|
||||
text: I18n.tr("bar.system-monitor.cpu-usage-label")
|
||||
pointSize: Style.fontSizeM
|
||||
}
|
||||
|
||||
NSpinBox {
|
||||
from: 250
|
||||
to: 10000
|
||||
stepSize: 250
|
||||
value: Settings.data.systemMonitor.cpuPollingInterval
|
||||
defaultValue: Settings.getDefaultValue("systemMonitor.cpuPollingInterval")
|
||||
onValueChanged: Settings.data.systemMonitor.cpuPollingInterval = value
|
||||
suffix: " ms"
|
||||
}
|
||||
}
|
||||
|
||||
// GPU Polling
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
visible: SystemStatService.gpuAvailable
|
||||
|
||||
NText {
|
||||
Layout.fillWidth: true
|
||||
text: I18n.tr("panels.system-monitor.gpu-section-label")
|
||||
pointSize: Style.fontSizeM
|
||||
}
|
||||
|
||||
NSpinBox {
|
||||
from: 250
|
||||
to: 10000
|
||||
stepSize: 250
|
||||
value: Settings.data.systemMonitor.gpuPollingInterval
|
||||
defaultValue: Settings.getDefaultValue("systemMonitor.gpuPollingInterval")
|
||||
onValueChanged: Settings.data.systemMonitor.gpuPollingInterval = value
|
||||
suffix: " ms"
|
||||
}
|
||||
}
|
||||
|
||||
// Load Average Polling
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
NText {
|
||||
Layout.fillWidth: true
|
||||
text: I18n.tr("bar.system-monitor.load-average-label")
|
||||
pointSize: Style.fontSizeM
|
||||
}
|
||||
|
||||
NSpinBox {
|
||||
from: 250
|
||||
to: 10000
|
||||
stepSize: 250
|
||||
value: Settings.data.systemMonitor.loadAvgPollingInterval
|
||||
defaultValue: Settings.getDefaultValue("systemMonitor.loadAvgPollingInterval")
|
||||
onValueChanged: Settings.data.systemMonitor.loadAvgPollingInterval = value
|
||||
suffix: " ms"
|
||||
}
|
||||
}
|
||||
|
||||
// Memory Polling
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
NText {
|
||||
Layout.fillWidth: true
|
||||
text: I18n.tr("bar.system-monitor.memory-usage-label")
|
||||
pointSize: Style.fontSizeM
|
||||
}
|
||||
|
||||
NSpinBox {
|
||||
from: 250
|
||||
to: 10000
|
||||
stepSize: 250
|
||||
value: Settings.data.systemMonitor.memPollingInterval
|
||||
defaultValue: Settings.getDefaultValue("systemMonitor.memPollingInterval")
|
||||
onValueChanged: Settings.data.systemMonitor.memPollingInterval = value
|
||||
suffix: " ms"
|
||||
}
|
||||
}
|
||||
|
||||
// Disk Polling
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
NText {
|
||||
Layout.fillWidth: true
|
||||
text: I18n.tr("panels.system-monitor.disk-section-label")
|
||||
pointSize: Style.fontSizeM
|
||||
}
|
||||
|
||||
NSpinBox {
|
||||
from: 1000
|
||||
to: 60000
|
||||
stepSize: 250
|
||||
value: Settings.data.systemMonitor.diskPollingInterval
|
||||
defaultValue: Settings.getDefaultValue("systemMonitor.diskPollingInterval")
|
||||
onValueChanged: Settings.data.systemMonitor.diskPollingInterval = value
|
||||
suffix: " ms"
|
||||
}
|
||||
}
|
||||
|
||||
// Network Polling
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
NText {
|
||||
Layout.fillWidth: true
|
||||
text: I18n.tr("common.network")
|
||||
pointSize: Style.fontSizeM
|
||||
}
|
||||
|
||||
NSpinBox {
|
||||
from: 250
|
||||
to: 10000
|
||||
stepSize: 250
|
||||
value: Settings.data.systemMonitor.networkPollingInterval
|
||||
defaultValue: Settings.getDefaultValue("systemMonitor.networkPollingInterval")
|
||||
onValueChanged: Settings.data.systemMonitor.networkPollingInterval = value
|
||||
suffix: " ms"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,11 +27,6 @@ ColumnLayout {
|
||||
tabIndex: 1
|
||||
checked: subTabBar.currentIndex === 1
|
||||
}
|
||||
NTabButton {
|
||||
text: I18n.tr("common.polling")
|
||||
tabIndex: 2
|
||||
checked: subTabBar.currentIndex === 2
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
@@ -47,6 +42,5 @@ ColumnLayout {
|
||||
screen: root.screen
|
||||
}
|
||||
ThresholdsSubTab {}
|
||||
PollingSubTab {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ import qs.Widgets
|
||||
SmartPanel {
|
||||
id: root
|
||||
|
||||
Component.onCompleted: SystemStatService.registerComponent("panel-systemstats")
|
||||
Component.onDestruction: SystemStatService.unregisterComponent("panel-systemstats")
|
||||
|
||||
preferredWidth: Math.round(440 * Style.uiScaleRatio)
|
||||
|
||||
panelContent: Item {
|
||||
@@ -137,7 +140,7 @@ SmartPanel {
|
||||
color2: Color.mSecondary
|
||||
fill: true
|
||||
fillOpacity: 0.15
|
||||
updateInterval: Settings.data.systemMonitor.cpuPollingInterval
|
||||
updateInterval: SystemStatService.cpuIntervalMs
|
||||
edgeToEdge: true
|
||||
}
|
||||
}
|
||||
@@ -193,7 +196,7 @@ SmartPanel {
|
||||
color: Color.mPrimary
|
||||
fill: true
|
||||
fillOpacity: 0.15
|
||||
updateInterval: Settings.data.systemMonitor.memPollingInterval
|
||||
updateInterval: SystemStatService.memIntervalMs
|
||||
edgeToEdge: true
|
||||
}
|
||||
}
|
||||
@@ -267,7 +270,7 @@ SmartPanel {
|
||||
color2: Color.mSecondary
|
||||
fill: true
|
||||
fillOpacity: 0.15
|
||||
updateInterval: Settings.data.systemMonitor.networkPollingInterval
|
||||
updateInterval: SystemStatService.networkIntervalMs
|
||||
animateScale: true
|
||||
edgeToEdge: true
|
||||
}
|
||||
|
||||
@@ -5,18 +5,37 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Services.UI
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
// Configuration
|
||||
readonly property int minimumIntervalMs: 250
|
||||
readonly property int defaultIntervalMs: 3000
|
||||
|
||||
function normalizeInterval(value) {
|
||||
return Math.max(minimumIntervalMs, value || defaultIntervalMs);
|
||||
// Component registration - only poll when something needs system stat data
|
||||
function registerComponent(componentId) {
|
||||
root._registered[componentId] = true;
|
||||
root._registered = Object.assign({}, root._registered);
|
||||
Logger.d("SystemStat", "Component registered:", componentId, "- total:", root._registeredCount);
|
||||
}
|
||||
|
||||
function unregisterComponent(componentId) {
|
||||
delete root._registered[componentId];
|
||||
root._registered = Object.assign({}, root._registered);
|
||||
Logger.d("SystemStat", "Component unregistered:", componentId, "- total:", root._registeredCount);
|
||||
}
|
||||
|
||||
property var _registered: ({})
|
||||
readonly property int _registeredCount: Object.keys(_registered).length
|
||||
readonly property bool _lockScreenActive: PanelService.lockScreen?.active ?? false
|
||||
readonly property bool shouldRun: _registeredCount > 0 && !_lockScreenActive
|
||||
|
||||
// Polling intervals (hardcoded to sensible values per stat type)
|
||||
readonly property int cpuIntervalMs: 3000
|
||||
readonly property int memIntervalMs: 5000
|
||||
readonly property int networkIntervalMs: 3000
|
||||
readonly property int loadAvgIntervalMs: 10000
|
||||
readonly property int diskIntervalMs: 30000
|
||||
readonly property int gpuIntervalMs: 5000
|
||||
|
||||
// Public values
|
||||
property real cpuUsage: 0
|
||||
property real cpuTemp: 0
|
||||
@@ -52,11 +71,11 @@ Singleton {
|
||||
readonly property int historyDurationMs: (1 * 60 * 1000) // 1 minute
|
||||
|
||||
// Computed history lengths based on polling intervals
|
||||
readonly property int cpuHistoryLength: Math.ceil(historyDurationMs / normalizeInterval(Settings.data.systemMonitor.cpuPollingInterval))
|
||||
readonly property int gpuHistoryLength: Math.ceil(historyDurationMs / normalizeInterval(Settings.data.systemMonitor.gpuPollingInterval))
|
||||
readonly property int memHistoryLength: Math.ceil(historyDurationMs / normalizeInterval(Settings.data.systemMonitor.memPollingInterval))
|
||||
readonly property int diskHistoryLength: Math.ceil(historyDurationMs / normalizeInterval(Settings.data.systemMonitor.diskPollingInterval))
|
||||
readonly property int networkHistoryLength: Math.ceil(historyDurationMs / normalizeInterval(Settings.data.systemMonitor.networkPollingInterval))
|
||||
readonly property int cpuHistoryLength: Math.ceil(historyDurationMs / cpuIntervalMs)
|
||||
readonly property int gpuHistoryLength: Math.ceil(historyDurationMs / gpuIntervalMs)
|
||||
readonly property int memHistoryLength: Math.ceil(historyDurationMs / memIntervalMs)
|
||||
readonly property int diskHistoryLength: Math.ceil(historyDurationMs / diskIntervalMs)
|
||||
readonly property int networkHistoryLength: Math.ceil(historyDurationMs / networkIntervalMs)
|
||||
|
||||
property var cpuHistory: new Array(cpuHistoryLength).fill(0)
|
||||
property var cpuTempHistory: new Array(cpuHistoryLength).fill(40) // Reasonable default temp
|
||||
@@ -263,22 +282,38 @@ Singleton {
|
||||
|
||||
// --------------------------------------------
|
||||
Component.onCompleted: {
|
||||
Logger.i("SystemStat", "Service started with custom polling intervals");
|
||||
Logger.i("SystemStat", "Service started (polling deferred until a consumer registers).");
|
||||
|
||||
// Kickoff the cpu name detection for temperature
|
||||
// Kickoff the cpu name detection for temperature (one-time probes, not polling)
|
||||
cpuTempNameReader.checkNext();
|
||||
|
||||
// Kickoff the gpu sensor detection for temperature
|
||||
// Kickoff the gpu sensor detection for temperature (one-time probes, not polling)
|
||||
gpuTempNameReader.checkNext();
|
||||
|
||||
// Check for ZFS ARC stats on startup
|
||||
zfsArcStatsFile.reload();
|
||||
|
||||
// Get nproc on startup
|
||||
// Get nproc on startup (one-time)
|
||||
nprocProcess.running = true;
|
||||
}
|
||||
|
||||
// Get initial load average
|
||||
loadAvgFile.reload();
|
||||
onShouldRunChanged: {
|
||||
if (shouldRun) {
|
||||
// Reset differential state so first readings after resume are clean
|
||||
root.prevCpuStats = null;
|
||||
root.prevTime = 0;
|
||||
|
||||
// Trigger initial reads
|
||||
zfsArcStatsFile.reload();
|
||||
loadAvgFile.reload();
|
||||
|
||||
// Start persistent disk shell
|
||||
if (!dfShell.running) {
|
||||
dfShell.running = true;
|
||||
}
|
||||
} else {
|
||||
// Stop persistent disk shell
|
||||
if (dfShell.running) {
|
||||
dfShell.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Re-run GPU detection when dGPU opt-in setting changes
|
||||
@@ -318,18 +353,13 @@ Singleton {
|
||||
// Timer for CPU usage, frequency, and temperature
|
||||
Timer {
|
||||
id: cpuTimer
|
||||
interval: root.normalizeInterval(Settings.data.systemMonitor.cpuPollingInterval)
|
||||
interval: root.cpuIntervalMs
|
||||
repeat: true
|
||||
running: true
|
||||
running: root.shouldRun
|
||||
triggeredOnStart: true
|
||||
onIntervalChanged: {
|
||||
if (running) {
|
||||
restart();
|
||||
}
|
||||
}
|
||||
onTriggered: {
|
||||
cpuStatFile.reload();
|
||||
cpuFreqProcess.running = true;
|
||||
cpuInfoFile.reload();
|
||||
updateCpuTemperature();
|
||||
}
|
||||
}
|
||||
@@ -337,30 +367,20 @@ Singleton {
|
||||
// Timer for load average
|
||||
Timer {
|
||||
id: loadAvgTimer
|
||||
interval: root.normalizeInterval(Settings.data.systemMonitor.loadAvgPollingInterval)
|
||||
interval: root.loadAvgIntervalMs
|
||||
repeat: true
|
||||
running: true
|
||||
running: root.shouldRun
|
||||
triggeredOnStart: true
|
||||
onIntervalChanged: {
|
||||
if (running) {
|
||||
restart();
|
||||
}
|
||||
}
|
||||
onTriggered: loadAvgFile.reload()
|
||||
}
|
||||
|
||||
// Timer for memory stats
|
||||
Timer {
|
||||
id: memoryTimer
|
||||
interval: root.normalizeInterval(Settings.data.systemMonitor.memPollingInterval)
|
||||
interval: root.memIntervalMs
|
||||
repeat: true
|
||||
running: true
|
||||
running: root.shouldRun
|
||||
triggeredOnStart: true
|
||||
onIntervalChanged: {
|
||||
if (running) {
|
||||
restart();
|
||||
}
|
||||
}
|
||||
onTriggered: {
|
||||
memInfoFile.reload();
|
||||
zfsArcStatsFile.reload();
|
||||
@@ -370,45 +390,34 @@ Singleton {
|
||||
// Timer for disk usage
|
||||
Timer {
|
||||
id: diskTimer
|
||||
interval: root.normalizeInterval(Settings.data.systemMonitor.diskPollingInterval)
|
||||
interval: root.diskIntervalMs
|
||||
repeat: true
|
||||
running: true
|
||||
running: root.shouldRun
|
||||
triggeredOnStart: true
|
||||
onIntervalChanged: {
|
||||
if (running) {
|
||||
restart();
|
||||
onTriggered: {
|
||||
if (dfShell.running) {
|
||||
dfShell.write("df --output=target,pcent,used,size,avail --block-size=1 -x efivarfs 2>/dev/null; echo '@@DF_END@@'\n");
|
||||
}
|
||||
}
|
||||
onTriggered: dfProcess.running = true
|
||||
}
|
||||
|
||||
// Timer for network speeds
|
||||
Timer {
|
||||
id: networkTimer
|
||||
interval: root.normalizeInterval(Settings.data.systemMonitor.networkPollingInterval)
|
||||
interval: root.networkIntervalMs
|
||||
repeat: true
|
||||
running: true
|
||||
running: root.shouldRun
|
||||
triggeredOnStart: true
|
||||
onIntervalChanged: {
|
||||
if (running) {
|
||||
restart();
|
||||
}
|
||||
}
|
||||
onTriggered: netDevFile.reload()
|
||||
}
|
||||
|
||||
// Timer for GPU temperature
|
||||
Timer {
|
||||
id: gpuTempTimer
|
||||
interval: root.normalizeInterval(Settings.data.systemMonitor.gpuPollingInterval)
|
||||
interval: root.gpuIntervalMs
|
||||
repeat: true
|
||||
running: root.gpuAvailable
|
||||
running: root.shouldRun && root.gpuAvailable
|
||||
triggeredOnStart: true
|
||||
onIntervalChanged: {
|
||||
if (running) {
|
||||
restart();
|
||||
}
|
||||
}
|
||||
onTriggered: updateGpuTemperature()
|
||||
}
|
||||
|
||||
@@ -452,17 +461,31 @@ Singleton {
|
||||
}
|
||||
|
||||
// --------------------------------------------
|
||||
// Process to fetch disk usage (percent, used, size, avail)
|
||||
// Persistent shell for disk usage queries (avoids fork+exec of large Quickshell process every poll)
|
||||
// Uses 'df' aka 'disk free'
|
||||
// "-x efivarfs' skips efivarfs mountpoints, for which the `statfs` syscall may cause system-wide stuttering
|
||||
// "-x efivarfs" skips efivarfs mountpoints, for which the `statfs` syscall may cause system-wide stuttering
|
||||
// --block-size=1 gives us bytes for precise GB calculation
|
||||
// Timer writes commands to stdin; SplitParser reads output delimited by @@DF_END@@
|
||||
Process {
|
||||
id: dfProcess
|
||||
command: ["df", "--output=target,pcent,used,size,avail", "--block-size=1", "-x", "efivarfs"]
|
||||
id: dfShell
|
||||
command: ["sh"]
|
||||
stdinEnabled: true
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const lines = text.trim().split('\n');
|
||||
|
||||
onRunningChanged: {
|
||||
if (!running && root.shouldRun) {
|
||||
// Restart if it died unexpectedly while we still need it
|
||||
Logger.w("SystemStat", "Disk shell exited unexpectedly, restarting");
|
||||
Qt.callLater(() => {
|
||||
dfShell.running = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
stdout: SplitParser {
|
||||
splitMarker: "@@DF_END@@"
|
||||
onRead: data => {
|
||||
const lines = data.trim().split('\n');
|
||||
const newPercents = {};
|
||||
const newAvailPercents = {};
|
||||
const newUsedGb = {};
|
||||
@@ -508,49 +531,42 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// Process to get avg cpu frquency
|
||||
Process {
|
||||
id: cpuFreqProcess
|
||||
command: ["cat", "/proc/cpuinfo"]
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
let txt = text;
|
||||
let matches = txt.match(/cpu MHz\s+:\s+([0-9.]+)/g);
|
||||
if (matches && matches.length > 0) {
|
||||
let totalFreq = 0.0;
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
totalFreq += parseFloat(matches[i].split(":")[1]);
|
||||
}
|
||||
let avgFreq = (totalFreq / matches.length) / 1000.0;
|
||||
root.cpuFreq = avgFreq.toFixed(1) + "GHz";
|
||||
cpuMaxFreqProcess.running = true;
|
||||
if (avgFreq > root.cpuGlobalMaxFreq)
|
||||
root.cpuGlobalMaxFreq = avgFreq;
|
||||
if (root.cpuGlobalMaxFreq > 0) {
|
||||
root.cpuFreqRatio = Math.min(1.0, avgFreq / root.cpuGlobalMaxFreq);
|
||||
}
|
||||
// FileView to get avg cpu frequency (replaces subprocess spawn of `cat /proc/cpuinfo`)
|
||||
FileView {
|
||||
id: cpuInfoFile
|
||||
path: "/proc/cpuinfo"
|
||||
onLoaded: {
|
||||
let txt = text();
|
||||
let matches = txt.match(/cpu MHz\s+:\s+([0-9.]+)/g);
|
||||
if (matches && matches.length > 0) {
|
||||
let totalFreq = 0.0;
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
totalFreq += parseFloat(matches[i].split(":")[1]);
|
||||
}
|
||||
let avgFreq = (totalFreq / matches.length) / 1000.0;
|
||||
root.cpuFreq = avgFreq.toFixed(1) + "GHz";
|
||||
cpuMaxFreqFile.reload();
|
||||
if (avgFreq > root.cpuGlobalMaxFreq)
|
||||
root.cpuGlobalMaxFreq = avgFreq;
|
||||
if (root.cpuGlobalMaxFreq > 0) {
|
||||
root.cpuFreqRatio = Math.min(1.0, avgFreq / root.cpuGlobalMaxFreq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process to get maximum CPU frequency limit
|
||||
// Uses sysfs 'scaling_max_freq' to respect power profiles (e.g. power-profiles-daemon)
|
||||
// 'sort -nr | head -n1' ensures we get the highest limit across all cores
|
||||
// '2>/dev/null' ignores errors if cpufreq driver is missing or cores are offline
|
||||
Process {
|
||||
id: cpuMaxFreqProcess
|
||||
command: ["sh", "-c", "cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq 2>/dev/null | sort -nr | head -n1"]
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
let maxKHz = parseInt(text.trim());
|
||||
if (!isNaN(maxKHz) && maxKHz > 0) {
|
||||
let newMaxFreq = maxKHz / 1000000.0;
|
||||
if (Math.abs(root.cpuGlobalMaxFreq - newMaxFreq) > 0.01) {
|
||||
root.cpuGlobalMaxFreq = newMaxFreq;
|
||||
}
|
||||
// FileView to get maximum CPU frequency limit (replaces subprocess spawn)
|
||||
// Reads cpu0's scaling_max_freq as representative value
|
||||
FileView {
|
||||
id: cpuMaxFreqFile
|
||||
path: "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
|
||||
printErrors: false
|
||||
onLoaded: {
|
||||
let maxKHz = parseInt(text().trim());
|
||||
if (!isNaN(maxKHz) && maxKHz > 0) {
|
||||
let newMaxFreq = maxKHz / 1000000.0;
|
||||
if (Math.abs(root.cpuGlobalMaxFreq - newMaxFreq) > 0.01) {
|
||||
root.cpuGlobalMaxFreq = newMaxFreq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user