From d39a9a85bf939c6b887feae5dfb927a2316f7c92 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Wed, 10 Sep 2025 13:17:35 +0200 Subject: [PATCH] SystemMonitor: add GPU temperature option --- Commons/IconsSets/TablerIcons.qml | 1 + Modules/Bar/Widgets/SidePanelToggle.qml | 2 +- Modules/Bar/Widgets/SystemMonitor.qml | 32 +++++ .../WidgetSettings/SystemMonitorSettings.qml | 11 ++ Services/BarWidgetRegistry.qml | 1 + Services/SystemStatService.qml | 130 +++++++++++++++++- 6 files changed, 175 insertions(+), 2 deletions(-) diff --git a/Commons/IconsSets/TablerIcons.qml b/Commons/IconsSets/TablerIcons.qml index 1966b6f..d3fe29d 100644 --- a/Commons/IconsSets/TablerIcons.qml +++ b/Commons/IconsSets/TablerIcons.qml @@ -33,6 +33,7 @@ Singleton { "upload-speed": "upload", "cpu-usage": "brand-speedtest", "cpu-temperature": "flame-filled", + "gpu-temperature": "device-desktop", "memory": "cpu", "performance": "gauge-filled", "balanced": "scale", diff --git a/Modules/Bar/Widgets/SidePanelToggle.qml b/Modules/Bar/Widgets/SidePanelToggle.qml index 9f901a4..00343be 100644 --- a/Modules/Bar/Widgets/SidePanelToggle.qml +++ b/Modules/Bar/Widgets/SidePanelToggle.qml @@ -33,7 +33,7 @@ NIconButton { readonly property bool useDistroLogo: (widgetSettings.useDistroLogo !== undefined) ? widgetSettings.useDistroLogo : widgetMetadata.useDistroLogo - icon: useDistroLogo ? "" : "panel" + icon: useDistroLogo ? "" : "apps" tooltipText: "Open side panel." sizeRatio: 0.8 diff --git a/Modules/Bar/Widgets/SystemMonitor.qml b/Modules/Bar/Widgets/SystemMonitor.qml index 43ed774..0087a38 100644 --- a/Modules/Bar/Widgets/SystemMonitor.qml +++ b/Modules/Bar/Widgets/SystemMonitor.qml @@ -40,6 +40,8 @@ RowLayout { !== undefined) ? widgetSettings.showNetworkStats : widgetMetadata.showNetworkStats readonly property bool showDiskUsage: (widgetSettings.showDiskUsage !== undefined) ? widgetSettings.showDiskUsage : widgetMetadata.showDiskUsage + readonly property bool showGpuTemp: (widgetSettings.showGpuTemp !== undefined) ? widgetSettings.showGpuTemp : (widgetMetadata.showGpuTemp + || false) Layout.alignment: Qt.AlignVCenter spacing: Style.marginS * scaling @@ -119,6 +121,36 @@ RowLayout { } } + // GPU Temperature Component + Item { + Layout.preferredWidth: gpuTempRow.implicitWidth + Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) + Layout.alignment: Qt.AlignVCenter + visible: showGpuTemp + + RowLayout { + id: gpuTempRow + anchors.centerIn: parent + spacing: Style.marginXS * scaling + + NIcon { + icon: "gpu-temperature" + font.pointSize: Style.fontSizeS * scaling + Layout.alignment: Qt.AlignVCenter + } + + NText { + text: `${SystemStatService.gpuTemp}°C` + font.family: Settings.data.ui.fontFixed + font.pointSize: Style.fontSizeS * scaling + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignVCenter + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + } + } + } + // Memory Usage Component Item { Layout.preferredWidth: memoryUsageRow.implicitWidth diff --git a/Modules/SettingsPanel/Bar/WidgetSettings/SystemMonitorSettings.qml b/Modules/SettingsPanel/Bar/WidgetSettings/SystemMonitorSettings.qml index 39e4614..0c9b9bb 100644 --- a/Modules/SettingsPanel/Bar/WidgetSettings/SystemMonitorSettings.qml +++ b/Modules/SettingsPanel/Bar/WidgetSettings/SystemMonitorSettings.qml @@ -16,6 +16,8 @@ ColumnLayout { // Local, editable state for checkboxes property bool valueShowCpuUsage: widgetData.showCpuUsage !== undefined ? widgetData.showCpuUsage : widgetMetadata.showCpuUsage property bool valueShowCpuTemp: widgetData.showCpuTemp !== undefined ? widgetData.showCpuTemp : widgetMetadata.showCpuTemp + property bool valueShowGpuTemp: widgetData.showGpuTemp !== undefined ? widgetData.showGpuTemp : (widgetMetadata.showGpuTemp + || false) property bool valueShowMemoryUsage: widgetData.showMemoryUsage !== undefined ? widgetData.showMemoryUsage : widgetMetadata.showMemoryUsage property bool valueShowMemoryAsPercent: widgetData.showMemoryAsPercent !== undefined ? widgetData.showMemoryAsPercent : widgetMetadata.showMemoryAsPercent @@ -27,6 +29,7 @@ ColumnLayout { var settings = Object.assign({}, widgetData || {}) settings.showCpuUsage = valueShowCpuUsage settings.showCpuTemp = valueShowCpuTemp + settings.showGpuTemp = valueShowGpuTemp settings.showMemoryUsage = valueShowMemoryUsage settings.showMemoryAsPercent = valueShowMemoryAsPercent settings.showNetworkStats = valueShowNetworkStats @@ -50,6 +53,14 @@ ColumnLayout { onToggled: checked => valueShowCpuTemp = checked } + NToggle { + id: showGpuTemp + Layout.fillWidth: true + label: "GPU temperature" + checked: valueShowGpuTemp + onToggled: checked => valueShowGpuTemp = checked + } + NToggle { id: showMemoryUsage Layout.fillWidth: true diff --git a/Services/BarWidgetRegistry.qml b/Services/BarWidgetRegistry.qml index d92fd18..f104d07 100644 --- a/Services/BarWidgetRegistry.qml +++ b/Services/BarWidgetRegistry.qml @@ -81,6 +81,7 @@ Singleton { "allowUserSettings": true, "showCpuUsage": true, "showCpuTemp": true, + "showGpuTemp": false, "showMemoryUsage": true, "showMemoryAsPercent": false, "showNetworkStats": false, diff --git a/Services/SystemStatService.qml b/Services/SystemStatService.qml index 11a62cf..778461c 100644 --- a/Services/SystemStatService.qml +++ b/Services/SystemStatService.qml @@ -12,6 +12,7 @@ Singleton { // Public values property real cpuUsage: 0 property real cpuTemp: 0 + property real gpuTemp: 0 property real memGb: 0 property real memPercent: 0 property real diskPercent: 0 @@ -35,6 +36,12 @@ Singleton { readonly property var supportedTempCpuSensorNames: ["coretemp", "k10temp", "zenpower"] property string cpuTempSensorName: "" property string cpuTempHwmonPath: "" + // Gpu temperature (simple hwmon read if available) + readonly property var supportedTempGpuSensorNames: ["amdgpu", "nvidia", "radeon"] + property string gpuTempSensorName: "" + property string gpuTempHwmonPath: "" + property bool gpuIsDedicated: false + property string _gpuPendingAmdPath: "" // For Intel coretemp averaging of all cores/sensors property var intelTempValues: [] property int intelTempFilesChecked: 0 @@ -66,6 +73,7 @@ Singleton { dfProcess.running = true updateCpuTemperature() + updateGpuTemperature() } } @@ -115,9 +123,10 @@ Singleton { FileView { id: cpuTempNameReader property int currentIndex: 0 + printErrors: false function checkNext() { - if (currentIndex >= 10) { + if (currentIndex >= 16) { // Check up to hwmon10 Logger.warn("No supported temperature sensor found") return @@ -152,6 +161,105 @@ Singleton { } } + // ---- GPU temperature detection (hwmon) + FileView { + id: gpuTempNameReader + property int currentIndex: 0 + printErrors: false + + function checkNext() { + if (currentIndex >= 16) { + // Check up to hwmon10 + Logger.warn("SystemStat", "No supported GPU temperature sensor found") + return + } + + gpuTempNameReader.path = `/sys/class/hwmon/hwmon${currentIndex}/name` + gpuTempNameReader.reload() + } + + Component.onCompleted: checkNext() + + onLoaded: { + const name = text().trim() + if (root.supportedTempGpuSensorNames.includes(name)) { + const hwPath = `/sys/class/hwmon/hwmon${currentIndex}` + if (name === "nvidia") { + // Treat NVIDIA as dedicated by default + root.gpuTempSensorName = name + root.gpuTempHwmonPath = hwPath + root.gpuIsDedicated = true + Logger.log("SystemStat", `Selected NVIDIA GPU thermal sensor at ${root.gpuTempHwmonPath}`) + } else if (name === "amdgpu") { + // Probe VRAM to distinguish dGPU vs iGPU + root._gpuPendingAmdPath = hwPath + vramReader.requestCheck(hwPath) + } else if (!root.gpuTempHwmonPath) { + // Fallback to first supported sensor (e.g., radeon) + root.gpuTempSensorName = name + root.gpuTempHwmonPath = hwPath + Logger.log("SystemStat", `Selected GPU thermal sensor at ${root.gpuTempHwmonPath}`) + } + } else { + currentIndex++ + Qt.callLater(() => { + checkNext() + }) + } + } + + onLoadFailed: function (error) { + currentIndex++ + Qt.callLater(() => { + checkNext() + }) + } + } + + // Reader to detect AMD dGPU by checking VRAM presence + FileView { + id: vramReader + property string targetHwmonPath: "" + function requestCheck(hwPath) { + targetHwmonPath = hwPath + vramReader.path = `${hwPath}/device/mem_info_vram_total` + vramReader.reload() + } + printErrors: false + onLoaded: { + const val = parseInt(text().trim()) + // If VRAM present (>0), prefer this as dGPU + if (!isNaN(val) && val > 0) { + root.gpuTempSensorName = "amdgpu" + root.gpuTempHwmonPath = targetHwmonPath + root.gpuIsDedicated = true + Logger.log("SystemStat", `Selected AMD dGPU (VRAM=${val}) at ${root.gpuTempHwmonPath}`) + } else if (!root.gpuTempHwmonPath) { + // Use as fallback iGPU if nothing selected yet + root.gpuTempSensorName = "amdgpu" + root.gpuTempHwmonPath = targetHwmonPath + root.gpuIsDedicated = false + Logger.log("SystemStat", `Selected AMD GPU (no VRAM) at ${root.gpuTempHwmonPath}`) + } + // Continue scanning other hwmon entries + gpuTempNameReader.currentIndex++ + Qt.callLater(() => { + gpuTempNameReader.checkNext() + }) + } + onLoadFailed: function (error) { + // If failed to read VRAM, consider as fallback if none selected + if (!root.gpuTempHwmonPath) { + root.gpuTempSensorName = "amdgpu" + root.gpuTempHwmonPath = targetHwmonPath + } + gpuTempNameReader.currentIndex++ + Qt.callLater(() => { + gpuTempNameReader.checkNext() + }) + } + } + // ---- // #2 - Read sensor value FileView { @@ -346,6 +454,26 @@ Singleton { } } + // ------------------------------------------------------- + // Function to start/refresh the GPU temperature + function updateGpuTemperature() { + if (!root.gpuTempHwmonPath) + return + gpuTempReader.path = `${root.gpuTempHwmonPath}/temp1_input` + gpuTempReader.reload() + } + + FileView { + id: gpuTempReader + printErrors: false + onLoaded: { + const data = parseInt(text().trim()) + if (!isNaN(data)) { + root.gpuTemp = Math.round(data / 1000.0) + } + } + } + // ------------------------------------------------------- // Function to check next Intel temperature sensor function checkNextIntelTemp() {