From b65917113a2058c0aaec867245b88976acaa9703 Mon Sep 17 00:00:00 2001 From: bbedward Date: Sat, 9 Aug 2025 00:07:43 -0400 Subject: [PATCH] lighter sysmon script --- Modules/TopBar/TopBar.qml | 60 +++++++++ Services/NotificationService.qml | 1 - Services/SysMonitorService.qml | 62 +-------- Services/WeatherService.qml | 1 - sysmon_dynamic_lite.sh | 223 +++++++++++++++++++++++++++++++ 5 files changed, 288 insertions(+), 59 deletions(-) create mode 100755 sysmon_dynamic_lite.sh diff --git a/Modules/TopBar/TopBar.qml b/Modules/TopBar/TopBar.qml index 5449a823..c552be53 100644 --- a/Modules/TopBar/TopBar.qml +++ b/Modules/TopBar/TopBar.qml @@ -42,12 +42,72 @@ PanelWindow { }) }) }) + + // Configure GPU temperature monitoring based on widget configuration + updateGpuTempConfig() + } + + function updateGpuTempConfig() { + const allWidgets = [...(SettingsData.topBarLeftWidgets || []), + ...(SettingsData.topBarCenterWidgets || []), + ...(SettingsData.topBarRightWidgets || [])] + + const hasGpuTempWidget = allWidgets.some(widget => { + const widgetId = typeof widget === "string" ? widget : widget.id + const widgetEnabled = typeof widget === "string" ? true : (widget.enabled !== false) + return widgetId === "gpuTemp" && widgetEnabled + }) + + let hasNvidiaGpuWidget = false + let hasNonNvidiaGpuWidget = false + + if (hasGpuTempWidget) { + hasNvidiaGpuWidget = allWidgets.some(widget => { + const widgetId = typeof widget === "string" ? widget : widget.id + const widgetEnabled = typeof widget === "string" ? true : (widget.enabled !== false) + if (widgetId !== "gpuTemp" || !widgetEnabled) return false + + const selectedGpuIndex = typeof widget === "string" ? 0 : (widget.selectedGpuIndex || 0) + if (SysMonitorService.availableGpus && SysMonitorService.availableGpus[selectedGpuIndex]) { + return SysMonitorService.availableGpus[selectedGpuIndex].driver === "nvidia" + } + return false + }) + + hasNonNvidiaGpuWidget = allWidgets.some(widget => { + const widgetId = typeof widget === "string" ? widget : widget.id + const widgetEnabled = typeof widget === "string" ? true : (widget.enabled !== false) + if (widgetId !== "gpuTemp" || !widgetEnabled) return false + + const selectedGpuIndex = typeof widget === "string" ? 0 : (widget.selectedGpuIndex || 0) + if (SysMonitorService.availableGpus && SysMonitorService.availableGpus[selectedGpuIndex]) { + return SysMonitorService.availableGpus[selectedGpuIndex].driver !== "nvidia" + } + return true + }) + } + + SysMonitorService.gpuTempEnabled = hasGpuTempWidget + SysMonitorService.nvidiaGpuTempEnabled = hasNvidiaGpuWidget + SysMonitorService.nonNvidiaGpuTempEnabled = hasNonNvidiaGpuWidget } Connections { function onTopBarTransparencyChanged() { root.backgroundTransparency = SettingsData.topBarTransparency } + + function onTopBarLeftWidgetsChanged() { + root.updateGpuTempConfig() + } + + function onTopBarCenterWidgetsChanged() { + root.updateGpuTempConfig() + } + + function onTopBarRightWidgetsChanged() { + root.updateGpuTempConfig() + } target: SettingsData } diff --git a/Services/NotificationService.qml b/Services/NotificationService.qml index 4b4e1acf..9083e1c7 100644 --- a/Services/NotificationService.qml +++ b/Services/NotificationService.qml @@ -6,7 +6,6 @@ import QtQuick import Quickshell import Quickshell.Services.Notifications import Quickshell.Widgets -import qs.Services import qs.Common import "../Common/markdown2html.js" as Markdown2Html diff --git a/Services/SysMonitorService.qml b/Services/SysMonitorService.qml index cfb0a696..36ced164 100644 --- a/Services/SysMonitorService.qml +++ b/Services/SysMonitorService.qml @@ -5,8 +5,6 @@ pragma ComponentBehavior import QtQuick import Quickshell import Quickshell.Io -import qs.Services -import qs.Common Singleton { id: root @@ -80,61 +78,11 @@ Singleton { property string motherboard: "" property string biosVersion: "" property var availableGpus: [] - - // Check if any GPU temperature widgets are configured - function hasGpuTempWidgets() { - const allWidgets = [...(SettingsData.topBarLeftWidgets || []), - ...(SettingsData.topBarCenterWidgets || []), - ...(SettingsData.topBarRightWidgets || [])] - - return allWidgets.some(widget => { - const widgetId = typeof widget === "string" ? widget : widget.id - const widgetEnabled = typeof widget === "string" ? true : (widget.enabled !== false) - return widgetId === "gpuTemp" && widgetEnabled - }) - } - // Check if any NVIDIA GPU temperature widgets are configured - function hasNvidiaGpuTempWidgets() { - if (!hasGpuTempWidgets()) return false - - const allWidgets = [...(SettingsData.topBarLeftWidgets || []), - ...(SettingsData.topBarCenterWidgets || []), - ...(SettingsData.topBarRightWidgets || [])] - - return allWidgets.some(widget => { - const widgetId = typeof widget === "string" ? widget : widget.id - const widgetEnabled = typeof widget === "string" ? true : (widget.enabled !== false) - if (widgetId !== "gpuTemp" || !widgetEnabled) return false - - const selectedGpuIndex = typeof widget === "string" ? 0 : (widget.selectedGpuIndex || 0) - if (availableGpus && availableGpus[selectedGpuIndex]) { - return availableGpus[selectedGpuIndex].driver === "nvidia" - } - return false - }) - } - - // Check if any non-NVIDIA GPU temperature widgets are configured - function hasNonNvidiaGpuTempWidgets() { - if (!hasGpuTempWidgets()) return false - - const allWidgets = [...(SettingsData.topBarLeftWidgets || []), - ...(SettingsData.topBarCenterWidgets || []), - ...(SettingsData.topBarRightWidgets || [])] - - return allWidgets.some(widget => { - const widgetId = typeof widget === "string" ? widget : widget.id - const widgetEnabled = typeof widget === "string" ? true : (widget.enabled !== false) - if (widgetId !== "gpuTemp" || !widgetEnabled) return false - - const selectedGpuIndex = typeof widget === "string" ? 0 : (widget.selectedGpuIndex || 0) - if (availableGpus && availableGpus[selectedGpuIndex]) { - return availableGpus[selectedGpuIndex].driver !== "nvidia" - } - return true // Default to true if GPU not found yet (static data might not be loaded) - }) - } + // Properties to control GPU temperature collection - set externally by shell.qml + property bool gpuTempEnabled: false + property bool nvidiaGpuTempEnabled: false + property bool nonNvidiaGpuTempEnabled: false function addRef() { refCount++ @@ -636,7 +584,7 @@ Singleton { Process { id: dynamicStatsProcess - command: [root.shellDir + "/sysmon_dynamic.sh", root.sortBy, String(root.maxProcesses), root.hasGpuTempWidgets() ? "1" : "0", root.hasNvidiaGpuTempWidgets() ? "1" : "0", root.hasNonNvidiaGpuTempWidgets() ? "1" : "0"] + command: [root.shellDir + "/sysmon_dynamic_lite.sh", root.sortBy, String(root.maxProcesses), root.gpuTempEnabled ? "1" : "0", root.nvidiaGpuTempEnabled ? "1" : "0", root.nonNvidiaGpuTempEnabled ? "1" : "0"] running: false onExited: exitCode => { if (exitCode !== 0) { diff --git a/Services/WeatherService.qml b/Services/WeatherService.qml index dbdc64e0..297c2d16 100644 --- a/Services/WeatherService.qml +++ b/Services/WeatherService.qml @@ -6,7 +6,6 @@ import QtQuick import Quickshell import Quickshell.Io import qs.Common -import qs.Services Singleton { id: root diff --git a/sysmon_dynamic_lite.sh b/sysmon_dynamic_lite.sh new file mode 100755 index 00000000..13769ded --- /dev/null +++ b/sysmon_dynamic_lite.sh @@ -0,0 +1,223 @@ +#!/usr/bin/env bash +# Lightweight version of sysmon_dynamic.sh that skips expensive PSS calculations +# Args: +# $1 sort_key (cpu|memory|name|pid) +# $2 max_procs +# $3 collect_gpu_temps (0/1) +# $4 collect_nvidia_only (0/1) +# $5 collect_non_nvidia (0/1) + +set -o pipefail + +sort_key=${1:-cpu} +max_procs=${2:-20} +collect_gpu_temps=${3:-0} +collect_nvidia_only=${4:-0} +collect_non_nvidia=${5:-1} + +json_escape() { sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e ':a;N;$!ba;s/\n/\\n/g'; } + +printf "{" + +# ---- Memory block (exact fields/keys) ---- +mem_line="$(awk ' + /^MemTotal:/ {t=$2} + /^MemFree:/ {f=$2} + /^MemAvailable:/ {a=$2} + /^Buffers:/ {b=$2} + /^Cached:/ {c=$2} + /^Shmem:/ {s=$2} + /^SwapTotal:/ {st=$2} + /^SwapFree:/ {sf=$2} + END{printf "%d %d %d %d %d %d %d %d", t,f,a,b,c,s,st,sf} +' /proc/meminfo)" +read -r MT MF MA BU CA SH ST SF <<< "$mem_line" + +printf '"memory":{"total":%d,"free":%d,"available":%d,"buffers":%d,"cached":%d,"shared":%d,"swaptotal":%d,"swapfree":%d},' \ + "$MT" "$MF" "$MA" "$BU" "$CA" "$SH" "$ST" "$SF" + +# ---- CPU (meta + temp) ---- +cpu_count=$(nproc) +cpu_model=$(grep -m1 'model name' /proc/cpuinfo | cut -d: -f2- | sed 's/^ *//' | json_escape || echo 'Unknown') +cpu_freq=$(awk -F: '/cpu MHz/{gsub(/ /,"",$2);print $2;exit}' /proc/cpuinfo || echo 0) + +cpu_temp=0 +for hwmon_dir in /sys/class/hwmon/hwmon*/; do + [ -d "$hwmon_dir" ] || continue + name_file="${hwmon_dir}name" + [ -r "$name_file" ] || continue + if grep -qE 'coretemp|k10temp|k8temp|cpu_thermal|soc_thermal' "$name_file" 2>/dev/null; then + for temp_file in "${hwmon_dir}"temp*_input; do + [ -r "$temp_file" ] || continue + cpu_temp=$(awk '{printf "%.1f", $1/1000}' "$temp_file" 2>/dev/null || echo 0) + break 2 + done + fi +done + +printf '"cpu":{"count":%d,"model":"%s","frequency":%s,"temperature":%s,' \ + "$cpu_count" "$cpu_model" "$cpu_freq" "$cpu_temp" + +# cpu.total (first line of /proc/stat, fields 2..) +printf '"total":' +awk 'NR==1 { + printf "["; + for(i=2; i<=NF; i++) { if(i>2) printf ","; printf "%d", $i; } + printf "]"; + exit +}' /proc/stat + +# cpu.cores (each cpuN line, 8 columns) +printf ',"cores":[' +cpu_cores=$(nproc) +awk -v n="$cpu_cores" 'BEGIN{c=0} + /^cpu[0-9]+/ { + if(c>0) printf ","; + printf "[%d,%d,%d,%d,%d,%d,%d,%d]", $2,$3,$4,$5,$6,$7,$8,$9; + c++; + if(c==n) exit + }' /proc/stat +printf ']},' + +# ---- Network (rx/tx bytes per iface) ---- +printf '"network":[' +tmp_net=$(mktemp) +grep -E '(wlan|eth|enp|wlp|ens|eno)' /proc/net/dev > "$tmp_net" || true +nfirst=1 +while IFS= read -r line; do + [ -z "$line" ] && continue + iface=$(echo "$line" | awk '{print $1}' | sed 's/://') + rx_bytes=$(echo "$line" | awk '{print $2}') + tx_bytes=$(echo "$line" | awk '{print $10}') + [ $nfirst -eq 1 ] || printf "," + printf '{"name":"%s","rx":%d,"tx":%d}' "$iface" "$rx_bytes" "$tx_bytes" + nfirst=0 +done < "$tmp_net" +rm -f "$tmp_net" +printf '],' + +# ---- Disk (/proc/diskstats sectors; read/write) ---- +printf '"disk":[' +tmp_disk=$(mktemp) +grep -E ' (sd[a-z]+|nvme[0-9]+n[0-9]+|vd[a-z]+|dm-[0-9]+|mmcblk[0-9]+) ' /proc/diskstats > "$tmp_disk" || true +dfirst=1 +while IFS= read -r line; do + [ -z "$line" ] && continue + name=$(echo "$line" | awk '{print $3}') + read_sectors=$(echo "$line" | awk '{print $6}') + write_sectors=$(echo "$line" | awk '{print $10}') + [ $dfirst -eq 1 ] || printf "," + printf '{"name":"%s","read":%d,"write":%d}' "$name" "$read_sectors" "$write_sectors" + dfirst=0 +done < "$tmp_disk" +rm -f "$tmp_disk" +printf '],' + +# ---- Processes (SIMPLIFIED - no PSS, use RSS from ps directly) ---- +case "$sort_key" in + cpu) SORT_OPT="--sort=-pcpu" ;; + memory) SORT_OPT="--sort=-pmem" ;; + name) SORT_OPT="--sort=+comm" ;; + pid) SORT_OPT="--sort=+pid" ;; + *) SORT_OPT="--sort=-pcpu" ;; +esac + +printf '"processes":[' +tmp_ps=$(mktemp) +ps -eo pid,ppid,pcpu,pmem,rss,comm,cmd --no-headers $SORT_OPT | head -n "$max_procs" > "$tmp_ps" || true +pfirst=1 +while IFS= read -r line; do + [ -z "$line" ] && continue + pid=$( awk '{print $1}' <<<"$line") + ppid=$( awk '{print $2}' <<<"$line") + cpu=$( awk '{print $3}' <<<"$line") + pmem=$( awk '{print $4}' <<<"$line") + rss_kib=$(awk '{print $5}' <<<"$line") + comm=$( awk '{print $6}' <<<"$line") + rest=$( printf '%s\n' "$line" | cut -d' ' -f7- ) + + # CPU ticks (utime+stime) + pticks=$(awk '{print $14+$15}' "/proc/$pid/stat" 2>/dev/null || echo 0) + + # Use RSS-based memory percentage (skip expensive PSS calculation) + cmd=$(printf "%s %s" "$comm" "${rest:-}" | json_escape) + comm_esc=$(printf "%s" "$comm" | json_escape) + + [ $pfirst -eq 1 ] || printf "," + printf '{"pid":%s,"ppid":%s,"cpu":%s,"pticks":%s,"memoryPercent":%s,"memoryKB":%s,"pssKB":%s,"pssPercent":%s,"command":"%s","fullCommand":"%s"}' \ + "$pid" "$ppid" "$cpu" "$pticks" "$pmem" "$rss_kib" "$rss_kib" "$pmem" "$comm_esc" "$cmd" + pfirst=0 +done < "$tmp_ps" +rm -f "$tmp_ps" +printf '],' + +# ---- System (dynamic bits) ---- +load_avg=$(cut -d' ' -f1-3 /proc/loadavg) +proc_count=$(ls -Ud /proc/[0-9]* 2>/dev/null | wc -l) +thread_count=$(ls -Ud /proc/[0-9]*/task/[0-9]* 2>/dev/null | wc -l) +boot_time=$(who -b 2>/dev/null | awk '{print $3, $4}' | json_escape || echo 'Unknown') + +printf '"system":{"loadavg":"%s","processes":%d,"threads":%d,"boottime":"%s"},' \ + "$load_avg" "$proc_count" "$thread_count" "$boot_time" + +# ---- Mounts (same df -h shape/strings) ---- +printf '"diskmounts":[' +tmp_mounts=$(mktemp) +df -h --output=source,target,fstype,size,used,avail,pcent | tail -n +2 | grep -vE '^(tmpfs|devtmpfs)' > "$tmp_mounts" || true +mfirst=1 +while IFS= read -r line; do + [ -z "$line" ] && continue + device=$(echo "$line" | awk '{print $1}' | json_escape) + mount=$( echo "$line" | awk '{print $2}' | json_escape) + fstype=$(echo "$line" | awk '{print $3}') + size=$( echo "$line" | awk '{print $4}') + used=$( echo "$line" | awk '{print $5}') + avail=$( echo "$line" | awk '{print $6}') + percent=$(echo "$line" | awk '{print $7}') + [ $mfirst -eq 1 ] || printf "," + printf '{"device":"%s","mount":"%s","fstype":"%s","size":"%s","used":"%s","avail":"%s","percent":"%s"}' \ + "$device" "$mount" "$fstype" "$size" "$used" "$avail" "$percent" + mfirst=0 +done < "$tmp_mounts" +rm -f "$tmp_mounts" +printf '],' + +# ---- GPU temps (optional) ---- +printf '"gputemps":[' +if [ "$collect_gpu_temps" = "1" ]; then + gfirst=1 + for card in /sys/class/drm/card*; do + [ -e "$card/device/driver" ] || continue + drv=$(basename "$(readlink -f "$card/device/driver")"); drv=${drv##*/} + hw=""; temp="0" + + if [ "$collect_non_nvidia" = "1" ]; then + for h in "$card/device"/hwmon/hwmon*; do + [ -e "$h/temp1_input" ] || continue + hw=$(basename "$h") + temp=$(awk '{printf "%.1f",$1/1000}' "$h/temp1_input" 2>/dev/null || echo "0") + break + done + fi + + if [ "$drv" = "nvidia" ] && [ "$temp" = "0" ] && [ "$collect_nvidia_only" = "1" ] && command -v nvidia-smi >/dev/null 2>&1; then + t=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits 2>/dev/null | head -1) + [ -n "$t" ] && { temp="$t"; hw="${hw:-nvidia}"; } + fi + + if [ "$temp" != "0" ]; then + [ $gfirst -eq 1 ] || printf "," + printf '{"driver":"%s","hwmon":"%s","temperature":%s}' "$drv" "${hw:-unknown}" "${temp:-0}" + gfirst=0 + fi + done + + # Fallback: nvidia-smi only + if [ ${gfirst:-1} -eq 1 ] && [ "$collect_nvidia_only" = "1" ] && command -v nvidia-smi >/dev/null 2>&1; then + temp=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits 2>/dev/null | head -1) + [ -n "$temp" ] && printf '{"driver":"nvidia","hwmon":"nvidia","temperature":%s}' "$temp" + fi +fi +printf ']' + +printf "}\n" \ No newline at end of file