mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-30 00:12:50 -05:00
meta: many resource usage improvements and consolidations
This commit is contained in:
@@ -1,411 +0,0 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property int refCount
|
||||
|
||||
property var processes: []
|
||||
property bool isUpdating: false
|
||||
property int processUpdateInterval: 3000
|
||||
property int totalMemoryKB: 0
|
||||
property int usedMemoryKB: 0
|
||||
property int totalSwapKB: 0
|
||||
property int usedSwapKB: 0
|
||||
property int cpuCount: 1
|
||||
property real totalCpuUsage: 0
|
||||
property bool systemInfoAvailable: false
|
||||
property var cpuHistory: []
|
||||
property var memoryHistory: []
|
||||
property var networkHistory: ({
|
||||
"rx": [],
|
||||
"tx": []
|
||||
})
|
||||
property var diskHistory: ({
|
||||
"read": [],
|
||||
"write": []
|
||||
})
|
||||
property int historySize: 60
|
||||
property var perCoreCpuUsage: []
|
||||
property real networkRxRate: 0
|
||||
property real networkTxRate: 0
|
||||
property var lastNetworkStats: null
|
||||
property real diskReadRate: 0
|
||||
property real diskWriteRate: 0
|
||||
property var lastDiskStats: null
|
||||
property string sortBy: "cpu"
|
||||
property bool sortDescending: true
|
||||
property int maxProcesses: 20
|
||||
|
||||
function updateProcessList() {
|
||||
if (!root.isUpdating) {
|
||||
root.isUpdating = true;
|
||||
let sortOption = "";
|
||||
switch (root.sortBy) {
|
||||
case "cpu":
|
||||
sortOption = sortDescending ? "--sort=-pcpu" : "--sort=+pcpu";
|
||||
break;
|
||||
case "memory":
|
||||
sortOption = sortDescending ? "--sort=-pmem" : "--sort=+pmem";
|
||||
break;
|
||||
case "name":
|
||||
sortOption = sortDescending ? "--sort=-comm" : "--sort=+comm";
|
||||
break;
|
||||
case "pid":
|
||||
sortOption = sortDescending ? "--sort=-pid" : "--sort=+pid";
|
||||
break;
|
||||
default:
|
||||
sortOption = "--sort=-pcpu";
|
||||
}
|
||||
processListProcess.command = ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd " + sortOption + " | head -" + (root.maxProcesses + 1)];
|
||||
processListProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
function setSortBy(newSortBy) {
|
||||
if (newSortBy !== root.sortBy) {
|
||||
root.sortBy = newSortBy;
|
||||
updateProcessList();
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSortOrder() {
|
||||
root.sortDescending = !root.sortDescending;
|
||||
updateProcessList();
|
||||
}
|
||||
|
||||
property int killPid: 0
|
||||
|
||||
function killProcess(pid) {
|
||||
if (pid > 0) {
|
||||
root.killPid = pid
|
||||
processKiller.command = ["bash", "-c", "kill " + pid]
|
||||
processKiller.running = true
|
||||
}
|
||||
}
|
||||
|
||||
function getProcessIcon(command) {
|
||||
const cmd = command.toLowerCase();
|
||||
if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser"))
|
||||
return "web";
|
||||
|
||||
if (cmd.includes("code") || cmd.includes("editor") || cmd.includes("vim"))
|
||||
return "code";
|
||||
|
||||
if (cmd.includes("terminal") || cmd.includes("bash") || cmd.includes("zsh"))
|
||||
return "terminal";
|
||||
|
||||
if (cmd.includes("music") || cmd.includes("audio") || cmd.includes("spotify"))
|
||||
return "music_note";
|
||||
|
||||
if (cmd.includes("video") || cmd.includes("vlc") || cmd.includes("mpv"))
|
||||
return "play_circle";
|
||||
|
||||
if (cmd.includes("systemd") || cmd.includes("kernel") || cmd.includes("kthread"))
|
||||
return "settings";
|
||||
|
||||
return "memory";
|
||||
}
|
||||
|
||||
function formatCpuUsage(cpu) {
|
||||
return cpu.toFixed(1) + "%";
|
||||
}
|
||||
|
||||
function formatMemoryUsage(memoryKB) {
|
||||
if (memoryKB < 1024)
|
||||
return memoryKB.toFixed(0) + " KB";
|
||||
else if (memoryKB < 1024 * 1024)
|
||||
return (memoryKB / 1024).toFixed(1) + " MB";
|
||||
else
|
||||
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB";
|
||||
}
|
||||
|
||||
function formatSystemMemory(memoryKB) {
|
||||
if (memoryKB < 1024 * 1024)
|
||||
return (memoryKB / 1024).toFixed(0) + " MB";
|
||||
else
|
||||
return (memoryKB / (1024 * 1024)).toFixed(1) + " GB";
|
||||
}
|
||||
|
||||
function parseSystemInfo(text) {
|
||||
const lines = text.split('\n');
|
||||
let section = 'memory';
|
||||
const coreUsages = [];
|
||||
let memFree = 0;
|
||||
let memBuffers = 0;
|
||||
let memCached = 0;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
if (line === '---CPU---') {
|
||||
section = 'cpucount';
|
||||
continue;
|
||||
} else if (line === '---CPUSTAT---') {
|
||||
section = 'cpustat';
|
||||
continue;
|
||||
}
|
||||
if (section === 'memory') {
|
||||
if (line.startsWith('MemTotal:')) {
|
||||
root.totalMemoryKB = parseInt(line.split(/\s+/)[1]);
|
||||
} else if (line.startsWith('MemFree:')) {
|
||||
memFree = parseInt(line.split(/\s+/)[1]);
|
||||
} else if (line.startsWith('Buffers:')) {
|
||||
memBuffers = parseInt(line.split(/\s+/)[1]);
|
||||
} else if (line.startsWith('Cached:')) {
|
||||
memCached = parseInt(line.split(/\s+/)[1]);
|
||||
} else if (line.startsWith('SwapTotal:')) {
|
||||
root.totalSwapKB = parseInt(line.split(/\s+/)[1]);
|
||||
} else if (line.startsWith('SwapFree:')) {
|
||||
const freeSwapKB = parseInt(line.split(/\s+/)[1]);
|
||||
root.usedSwapKB = root.totalSwapKB - freeSwapKB;
|
||||
}
|
||||
} else if (section === 'cpucount') {
|
||||
const count = parseInt(line);
|
||||
if (!isNaN(count))
|
||||
root.cpuCount = count;
|
||||
|
||||
} else if (section === 'cpustat') {
|
||||
if (line.startsWith('cpu ')) {
|
||||
const parts = line.split(/\s+/);
|
||||
if (parts.length >= 8) {
|
||||
const user = parseInt(parts[1]);
|
||||
const nice = parseInt(parts[2]);
|
||||
const system = parseInt(parts[3]);
|
||||
const idle = parseInt(parts[4]);
|
||||
const iowait = parseInt(parts[5]);
|
||||
const irq = parseInt(parts[6]);
|
||||
const softirq = parseInt(parts[7]);
|
||||
const total = user + nice + system + idle + iowait + irq + softirq;
|
||||
const used = total - idle - iowait;
|
||||
root.totalCpuUsage = total > 0 ? (used / total) * 100 : 0;
|
||||
}
|
||||
} else if (line.match(/^cpu\d+/)) {
|
||||
const parts = line.split(/\s+/);
|
||||
if (parts.length >= 8) {
|
||||
const user = parseInt(parts[1]);
|
||||
const nice = parseInt(parts[2]);
|
||||
const system = parseInt(parts[3]);
|
||||
const idle = parseInt(parts[4]);
|
||||
const iowait = parseInt(parts[5]);
|
||||
const irq = parseInt(parts[6]);
|
||||
const softirq = parseInt(parts[7]);
|
||||
const total = user + nice + system + idle + iowait + irq + softirq;
|
||||
const used = total - idle - iowait;
|
||||
const usage = total > 0 ? (used / total) * 100 : 0;
|
||||
coreUsages.push(usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Calculate used memory as total minus free minus buffers minus cached
|
||||
root.usedMemoryKB = root.totalMemoryKB - memFree - memBuffers - memCached;
|
||||
// Update per-core usage
|
||||
root.perCoreCpuUsage = coreUsages;
|
||||
// Update history
|
||||
addToHistory(root.cpuHistory, root.totalCpuUsage);
|
||||
const memoryPercent = root.totalMemoryKB > 0 ? (root.usedMemoryKB / root.totalMemoryKB) * 100 : 0;
|
||||
addToHistory(root.memoryHistory, memoryPercent);
|
||||
root.systemInfoAvailable = true;
|
||||
}
|
||||
|
||||
function parseNetworkStats(text) {
|
||||
const lines = text.split('\n');
|
||||
let totalRx = 0;
|
||||
let totalTx = 0;
|
||||
for (const line of lines) {
|
||||
const parts = line.trim().split(/\s+/);
|
||||
if (parts.length >= 3) {
|
||||
const rx = parseInt(parts[1]);
|
||||
const tx = parseInt(parts[2]);
|
||||
if (!isNaN(rx) && !isNaN(tx)) {
|
||||
totalRx += rx;
|
||||
totalTx += tx;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (root.lastNetworkStats) {
|
||||
const timeDiff = root.processUpdateInterval / 1000;
|
||||
root.networkRxRate = Math.max(0, (totalRx - root.lastNetworkStats.rx) / timeDiff);
|
||||
root.networkTxRate = Math.max(0, (totalTx - root.lastNetworkStats.tx) / timeDiff);
|
||||
addToHistory(root.networkHistory.rx, root.networkRxRate / 1024);
|
||||
addToHistory(root.networkHistory.tx, root.networkTxRate / 1024);
|
||||
}
|
||||
root.lastNetworkStats = {
|
||||
"rx": totalRx,
|
||||
"tx": totalTx
|
||||
};
|
||||
}
|
||||
|
||||
function parseDiskStats(text) {
|
||||
const lines = text.split('\n');
|
||||
let totalRead = 0;
|
||||
let totalWrite = 0;
|
||||
for (const line of lines) {
|
||||
const parts = line.trim().split(/\s+/);
|
||||
if (parts.length >= 3) {
|
||||
const readSectors = parseInt(parts[1]);
|
||||
const writeSectors = parseInt(parts[2]);
|
||||
if (!isNaN(readSectors) && !isNaN(writeSectors)) {
|
||||
totalRead += readSectors * 512;
|
||||
totalWrite += writeSectors * 512;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (root.lastDiskStats) {
|
||||
const timeDiff = root.processUpdateInterval / 1000;
|
||||
root.diskReadRate = Math.max(0, (totalRead - root.lastDiskStats.read) / timeDiff);
|
||||
root.diskWriteRate = Math.max(0, (totalWrite - root.lastDiskStats.write) / timeDiff);
|
||||
addToHistory(root.diskHistory.read, root.diskReadRate / (1024 * 1024));
|
||||
addToHistory(root.diskHistory.write, root.diskWriteRate / (1024 * 1024));
|
||||
}
|
||||
root.lastDiskStats = {
|
||||
"read": totalRead,
|
||||
"write": totalWrite
|
||||
};
|
||||
}
|
||||
|
||||
function addToHistory(array, value) {
|
||||
array.push(value);
|
||||
if (array.length > root.historySize)
|
||||
array.shift();
|
||||
|
||||
}
|
||||
|
||||
Timer {
|
||||
running: root.refCount > 0
|
||||
interval: root.processUpdateInterval
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
systemInfoProcess.running = true;
|
||||
updateProcessList();
|
||||
networkStatsProcess.running = true;
|
||||
diskStatsProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: systemInfoProcess
|
||||
|
||||
command: ["bash", "-c", "cat /proc/meminfo; echo '---CPU---'; nproc; echo '---CPUSTAT---'; grep '^cpu' /proc/stat | head -" + (root.cpuCount + 1)]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("System info check failed with exit code:", exitCode);
|
||||
root.systemInfoAvailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
parseSystemInfo(text.trim());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: networkStatsProcess
|
||||
|
||||
command: ["bash", "-c", "cat /proc/net/dev | grep -E '(wlan|eth|enp|wlp|ens|eno)' | awk '{print $1,$2,$10}' | sed 's/:/ /'"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
parseNetworkStats(text.trim());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: diskStatsProcess
|
||||
|
||||
command: ["bash", "-c", "cat /proc/diskstats | grep -E ' (sd[a-z]+|nvme[0-9]+n[0-9]+|vd[a-z]+) ' | grep -v 'p[0-9]' | awk '{print $3,$6,$10}'"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
parseDiskStats(text.trim());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: processListProcess
|
||||
|
||||
command: ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,rss,comm,cmd --sort=-pcpu | head -" + (root.maxProcesses + 1)]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
root.isUpdating = false;
|
||||
if (exitCode !== 0)
|
||||
console.warn("Process list check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
const lines = text.trim().split('\n');
|
||||
const newProcesses = [];
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
if (!line)
|
||||
continue;
|
||||
|
||||
const parts = line.split(/\s+/);
|
||||
if (parts.length >= 7) {
|
||||
const pid = parseInt(parts[0]);
|
||||
const ppid = parseInt(parts[1]);
|
||||
const cpu = parseFloat(parts[2]);
|
||||
const memoryPercent = parseFloat(parts[3]);
|
||||
const memoryKB = parseInt(parts[4]);
|
||||
const command = parts[5];
|
||||
const fullCmd = parts.slice(6).join(' ');
|
||||
newProcesses.push({
|
||||
"pid": pid,
|
||||
"ppid": ppid,
|
||||
"cpu": cpu,
|
||||
"memoryPercent": memoryPercent,
|
||||
"memoryKB": memoryKB,
|
||||
"command": command,
|
||||
"fullCommand": fullCmd,
|
||||
"displayName": command.length > 15 ? command.substring(0, 15) + "..." : command
|
||||
});
|
||||
}
|
||||
}
|
||||
root.processes = newProcesses;
|
||||
root.isUpdating = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: processKiller
|
||||
command: ["bash", "-c", "kill " + root.killPid]
|
||||
running: false
|
||||
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode === 0) {
|
||||
console.log("Process killed successfully:", root.killPid)
|
||||
} else {
|
||||
console.warn("Failed to kill process:", root.killPid, "exit code:", exitCode)
|
||||
}
|
||||
root.killPid = 0
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
578
Services/SysMonitorService.qml
Normal file
578
Services/SysMonitorService.qml
Normal file
@@ -0,0 +1,578 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property int refCount: 0
|
||||
property int updateInterval: 8000
|
||||
property int maxProcesses: 100
|
||||
property bool isUpdating: false
|
||||
|
||||
// Process data
|
||||
property var processes: []
|
||||
property string sortBy: "cpu"
|
||||
property bool sortDescending: true
|
||||
|
||||
// System stats
|
||||
property real cpuUsage: 0
|
||||
property real totalCpuUsage: 0
|
||||
property int cpuCores: 1
|
||||
property int cpuCount: 1
|
||||
property string cpuModel: ""
|
||||
property real cpuFrequency: 0
|
||||
property real cpuTemperature: 0
|
||||
property var perCoreCpuUsage: []
|
||||
|
||||
// Memory stats
|
||||
property real memoryUsage: 0
|
||||
property real totalMemoryMB: 0
|
||||
property real usedMemoryMB: 0
|
||||
property real freeMemoryMB: 0
|
||||
property real availableMemoryMB: 0
|
||||
property int totalMemoryKB: 0
|
||||
property int usedMemoryKB: 0
|
||||
property int totalSwapKB: 0
|
||||
property int usedSwapKB: 0
|
||||
|
||||
// Network stats
|
||||
property real networkRxRate: 0
|
||||
property real networkTxRate: 0
|
||||
property var lastNetworkStats: null
|
||||
|
||||
// Disk stats
|
||||
property real diskReadRate: 0
|
||||
property real diskWriteRate: 0
|
||||
property var lastDiskStats: null
|
||||
property var diskMounts: []
|
||||
|
||||
// History
|
||||
property int historySize: 60
|
||||
property var cpuHistory: []
|
||||
property var memoryHistory: []
|
||||
property var networkHistory: ({
|
||||
"rx": [],
|
||||
"tx": []
|
||||
})
|
||||
property var diskHistory: ({
|
||||
"read": [],
|
||||
"write": []
|
||||
})
|
||||
|
||||
// System info
|
||||
property string kernelVersion: ""
|
||||
property string distribution: ""
|
||||
property string hostname: ""
|
||||
property string architecture: ""
|
||||
property string loadAverage: ""
|
||||
property int processCount: 0
|
||||
property int threadCount: 0
|
||||
property string bootTime: ""
|
||||
property string motherboard: ""
|
||||
property string biosVersion: ""
|
||||
|
||||
function addRef() {
|
||||
refCount++;
|
||||
if (refCount === 1) {
|
||||
updateAllStats();
|
||||
}
|
||||
}
|
||||
|
||||
function removeRef() {
|
||||
refCount = Math.max(0, refCount - 1);
|
||||
}
|
||||
|
||||
function updateAllStats() {
|
||||
if (refCount > 0) {
|
||||
isUpdating = true;
|
||||
unifiedStatsProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
function setSortBy(newSortBy) {
|
||||
if (newSortBy !== sortBy) {
|
||||
sortBy = newSortBy;
|
||||
sortProcessesInPlace();
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSortOrder() {
|
||||
sortDescending = !sortDescending;
|
||||
sortProcessesInPlace();
|
||||
}
|
||||
|
||||
function sortProcessesInPlace() {
|
||||
if (processes.length === 0) return;
|
||||
|
||||
const sortedProcesses = [...processes];
|
||||
|
||||
sortedProcesses.sort((a, b) => {
|
||||
let aVal, bVal;
|
||||
|
||||
switch (sortBy) {
|
||||
case "cpu":
|
||||
aVal = parseFloat(a.cpu) || 0;
|
||||
bVal = parseFloat(b.cpu) || 0;
|
||||
break;
|
||||
case "memory":
|
||||
aVal = parseFloat(a.memoryPercent) || 0;
|
||||
bVal = parseFloat(b.memoryPercent) || 0;
|
||||
break;
|
||||
case "name":
|
||||
aVal = a.command || "";
|
||||
bVal = b.command || "";
|
||||
break;
|
||||
case "pid":
|
||||
aVal = parseInt(a.pid) || 0;
|
||||
bVal = parseInt(b.pid) || 0;
|
||||
break;
|
||||
default:
|
||||
aVal = parseFloat(a.cpu) || 0;
|
||||
bVal = parseFloat(b.cpu) || 0;
|
||||
}
|
||||
|
||||
if (typeof aVal === "string") {
|
||||
return sortDescending ? bVal.localeCompare(aVal) : aVal.localeCompare(bVal);
|
||||
} else {
|
||||
return sortDescending ? bVal - aVal : aVal - bVal;
|
||||
}
|
||||
});
|
||||
|
||||
processes = sortedProcesses;
|
||||
}
|
||||
|
||||
function killProcess(pid) {
|
||||
if (pid > 0) {
|
||||
Quickshell.execDetached("kill", [pid.toString()]);
|
||||
}
|
||||
}
|
||||
|
||||
function addToHistory(array, value) {
|
||||
array.push(value);
|
||||
if (array.length > historySize)
|
||||
array.shift();
|
||||
}
|
||||
|
||||
function parseUnifiedStats(text) {
|
||||
function num(x) {
|
||||
return (typeof x === "number" && !isNaN(x)) ? x : 0;
|
||||
}
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(text);
|
||||
} catch (error) {
|
||||
console.error("SysMonitorService: Failed to parse JSON:", error, "Raw text:", text.slice(0, 300));
|
||||
isUpdating = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Memory
|
||||
if (data.memory) {
|
||||
const m = data.memory;
|
||||
totalMemoryKB = num(m.total);
|
||||
const free = num(m.free);
|
||||
const buf = num(m.buffers);
|
||||
const cached = num(m.cached);
|
||||
usedMemoryKB = totalMemoryKB - free - buf - cached;
|
||||
totalSwapKB = num(m.swaptotal);
|
||||
usedSwapKB = num(m.swaptotal) - num(m.swapfree);
|
||||
|
||||
totalMemoryMB = totalMemoryKB / 1024;
|
||||
usedMemoryMB = usedMemoryKB / 1024;
|
||||
freeMemoryMB = (totalMemoryKB - usedMemoryKB) / 1024;
|
||||
availableMemoryMB= (free + buf + cached) / 1024;
|
||||
memoryUsage = totalMemoryKB > 0 ? (usedMemoryKB / totalMemoryKB) * 100 : 0;
|
||||
}
|
||||
|
||||
// CPU
|
||||
if (data.cpu) {
|
||||
cpuCores = data.cpu.count || 1;
|
||||
cpuCount = data.cpu.count || 1;
|
||||
cpuModel = data.cpu.model || "";
|
||||
cpuFrequency = data.cpu.frequency || 0;
|
||||
cpuTemperature = data.cpu.temperature || 0;
|
||||
|
||||
if (data.cpu.total && data.cpu.total.length >= 8) {
|
||||
const user = data.cpu.total[0];
|
||||
const nice = data.cpu.total[1];
|
||||
const system = data.cpu.total[2];
|
||||
const idle = data.cpu.total[3];
|
||||
const iowait = data.cpu.total[4];
|
||||
const irq = data.cpu.total[5];
|
||||
const softirq = data.cpu.total[6];
|
||||
const total = user + nice + system + idle + iowait + irq + softirq;
|
||||
const used = total - idle - iowait;
|
||||
const usage = total > 0 ? (used / total) * 100 : 0;
|
||||
cpuUsage = usage;
|
||||
totalCpuUsage = usage;
|
||||
}
|
||||
|
||||
if (data.cpu.cores) {
|
||||
const coreUsages = [];
|
||||
for (const coreStats of data.cpu.cores) {
|
||||
if (coreStats && coreStats.length >= 8) {
|
||||
const user = coreStats[0];
|
||||
const nice = coreStats[1];
|
||||
const system = coreStats[2];
|
||||
const idle = coreStats[3];
|
||||
const iowait = coreStats[4];
|
||||
const irq = coreStats[5];
|
||||
const softirq = coreStats[6];
|
||||
const total = user + nice + system + idle + iowait + irq + softirq;
|
||||
const used = total - idle - iowait;
|
||||
const usage = total > 0 ? (used / total) * 100 : 0;
|
||||
coreUsages.push(usage);
|
||||
}
|
||||
}
|
||||
perCoreCpuUsage = coreUsages;
|
||||
}
|
||||
}
|
||||
|
||||
// Network
|
||||
if (data.network) {
|
||||
let totalRx = 0;
|
||||
let totalTx = 0;
|
||||
for (const iface of data.network) {
|
||||
totalRx += iface.rx;
|
||||
totalTx += iface.tx;
|
||||
}
|
||||
if (lastNetworkStats) {
|
||||
const timeDiff = updateInterval / 1000;
|
||||
const rxDiff = totalRx - lastNetworkStats.rx;
|
||||
const txDiff = totalTx - lastNetworkStats.tx;
|
||||
networkRxRate = Math.max(0, rxDiff / timeDiff);
|
||||
networkTxRate = Math.max(0, txDiff / timeDiff);
|
||||
addToHistory(networkHistory.rx, networkRxRate / 1024);
|
||||
addToHistory(networkHistory.tx, networkTxRate / 1024);
|
||||
}
|
||||
lastNetworkStats = { "rx": totalRx, "tx": totalTx };
|
||||
}
|
||||
|
||||
// Disk
|
||||
if (data.disk) {
|
||||
let totalRead = 0;
|
||||
let totalWrite = 0;
|
||||
for (const disk of data.disk) {
|
||||
totalRead += disk.read * 512;
|
||||
totalWrite += disk.write * 512;
|
||||
}
|
||||
if (lastDiskStats) {
|
||||
const timeDiff = updateInterval / 1000;
|
||||
const readDiff = totalRead - lastDiskStats.read;
|
||||
const writeDiff = totalWrite - lastDiskStats.write;
|
||||
diskReadRate = Math.max(0, readDiff / timeDiff);
|
||||
diskWriteRate = Math.max(0, writeDiff / timeDiff);
|
||||
addToHistory(diskHistory.read, diskReadRate / (1024 * 1024));
|
||||
addToHistory(diskHistory.write, diskWriteRate / (1024 * 1024));
|
||||
}
|
||||
lastDiskStats = { "read": totalRead, "write": totalWrite };
|
||||
}
|
||||
|
||||
// Processes
|
||||
if (data.processes) {
|
||||
const newProcesses = [];
|
||||
for (const proc of data.processes) {
|
||||
newProcesses.push({
|
||||
"pid": proc.pid,
|
||||
"ppid": proc.ppid,
|
||||
"cpu": proc.cpu,
|
||||
"memoryPercent": proc.memoryPercent,
|
||||
"memoryKB": proc.memoryKB,
|
||||
"command": proc.command,
|
||||
"fullCommand": proc.fullCommand,
|
||||
"displayName": proc.command.length > 15 ? proc.command.substring(0, 15) + "..." : proc.command
|
||||
});
|
||||
}
|
||||
processes = newProcesses;
|
||||
sortProcessesInPlace();
|
||||
}
|
||||
|
||||
// System info
|
||||
if (data.system) {
|
||||
kernelVersion = data.system.kernel || "";
|
||||
distribution = data.system.distro || "";
|
||||
hostname = data.system.hostname || "";
|
||||
architecture = data.system.arch || "";
|
||||
loadAverage = data.system.loadavg || "";
|
||||
processCount = data.system.processes || 0;
|
||||
threadCount = data.system.threads || 0;
|
||||
bootTime = data.system.boottime || "";
|
||||
motherboard = data.system.motherboard || "";
|
||||
biosVersion = data.system.bios || "";
|
||||
}
|
||||
|
||||
if (data.diskmounts) {
|
||||
diskMounts = data.diskmounts;
|
||||
}
|
||||
|
||||
// Update history
|
||||
addToHistory(cpuHistory, cpuUsage);
|
||||
addToHistory(memoryHistory, memoryUsage);
|
||||
|
||||
isUpdating = false;
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
function getProcessIcon(command) {
|
||||
const cmd = command.toLowerCase();
|
||||
if (cmd.includes("firefox") || cmd.includes("chrome") || cmd.includes("browser"))
|
||||
return "web";
|
||||
if (cmd.includes("code") || cmd.includes("editor") || cmd.includes("vim"))
|
||||
return "code";
|
||||
if (cmd.includes("terminal") || cmd.includes("bash") || cmd.includes("zsh"))
|
||||
return "terminal";
|
||||
if (cmd.includes("music") || cmd.includes("audio") || cmd.includes("spotify"))
|
||||
return "music_note";
|
||||
if (cmd.includes("video") || cmd.includes("vlc") || cmd.includes("mpv"))
|
||||
return "play_circle";
|
||||
if (cmd.includes("systemd") || cmd.includes("kernel") || cmd.includes("kthread"))
|
||||
return "settings";
|
||||
return "memory";
|
||||
}
|
||||
|
||||
function formatCpuUsage(cpu) {
|
||||
return (cpu || 0).toFixed(1) + "%";
|
||||
}
|
||||
|
||||
function formatMemoryUsage(memoryKB) {
|
||||
const mem = memoryKB || 0;
|
||||
if (mem < 1024)
|
||||
return mem.toFixed(0) + " KB";
|
||||
else if (mem < 1024 * 1024)
|
||||
return (mem / 1024).toFixed(1) + " MB";
|
||||
else
|
||||
return (mem / (1024 * 1024)).toFixed(1) + " GB";
|
||||
}
|
||||
|
||||
function formatSystemMemory(memoryKB) {
|
||||
const mem = memoryKB || 0;
|
||||
if (mem < 1024 * 1024)
|
||||
return (mem / 1024).toFixed(0) + " MB";
|
||||
else
|
||||
return (mem / (1024 * 1024)).toFixed(1) + " GB";
|
||||
}
|
||||
|
||||
function formatMemory(mb) {
|
||||
const mem = mb || 0;
|
||||
if (mem >= 1024)
|
||||
return (mem / 1024).toFixed(1) + " GB";
|
||||
return mem.toFixed(0) + " MB";
|
||||
}
|
||||
|
||||
function getCpuUsageColor() {
|
||||
if (cpuUsage > 80) return "#e74c3c";
|
||||
if (cpuUsage > 60) return "#f39c12";
|
||||
return "#27ae60";
|
||||
}
|
||||
|
||||
function getMemoryUsageColor() {
|
||||
if (memoryUsage > 90) return "#e74c3c";
|
||||
if (memoryUsage > 75) return "#f39c12";
|
||||
return "#3498db";
|
||||
}
|
||||
|
||||
function getTemperatureColor() {
|
||||
if (cpuTemperature > 80) return "#e74c3c";
|
||||
if (cpuTemperature > 65) return "#f39c12";
|
||||
return "#27ae60";
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: updateTimer
|
||||
interval: root.updateInterval
|
||||
running: root.refCount > 0
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: root.updateAllStats()
|
||||
}
|
||||
|
||||
readonly property string scriptBody: `set -Eeuo pipefail
|
||||
trap 'echo "ERR at line $LINENO: $BASH_COMMAND (exit $?)" >&2' ERR
|
||||
sort_key=\${1:-cpu}
|
||||
max_procs=\${2:-20}
|
||||
|
||||
json_escape() { sed -e 's/\\\\/\\\\\\\\/g' -e 's/"/\\\\"/g' -e ':a;N;$!ba;s/\\n/\\\\n/g'; }
|
||||
|
||||
printf "{"
|
||||
|
||||
mem_line="$(awk '/^MemTotal:/{t=$2}
|
||||
/^MemFree:/{f=$2}
|
||||
/^Buffers:/{b=$2}
|
||||
/^Cached:/{c=$2}
|
||||
/^SwapTotal:/{st=$2}
|
||||
/^SwapFree:/{sf=$2}
|
||||
END{printf "%d %d %d %d %d %d",t,f,b,c,st,sf}' /proc/meminfo)"
|
||||
read -r MT MF BU CA ST SF <<< "$mem_line"
|
||||
|
||||
printf '"memory":{"total":%d,"free":%d,"buffers":%d,"cached":%d,"swaptotal":%d,"swapfree":%d},' \\
|
||||
"$MT" "$MF" "$BU" "$CA" "$ST" "$SF"
|
||||
|
||||
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=$(if [ -r /sys/class/thermal/thermal_zone0/temp ]; then
|
||||
awk '{printf "%.1f",$1/1000}' /sys/class/thermal/thermal_zone0/temp 2>/dev/null || echo 0
|
||||
else echo 0; fi)
|
||||
|
||||
printf '"cpu":{"count":%d,"model":"%s","frequency":%s,"temperature":%s,' \\
|
||||
"$cpu_count" "$cpu_model" "$cpu_freq" "$cpu_temp"
|
||||
|
||||
printf '"total":'
|
||||
awk 'NR==1 {printf "[%d,%d,%d,%d,%d,%d,%d,%d]", $2,$3,$4,$5,$6,$7,$8,$9; exit}' /proc/stat
|
||||
|
||||
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 ']},'
|
||||
|
||||
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 '],'
|
||||
|
||||
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 ']',
|
||||
|
||||
printf '"processes":['
|
||||
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
|
||||
|
||||
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 pid ppid cpu memp memk comm rest; do
|
||||
[ -z "$pid" ] && continue
|
||||
cmd=$(printf "%s" "$rest" | json_escape)
|
||||
[ $pfirst -eq 1 ] || printf ","
|
||||
printf '{"pid":%s,"ppid":%s,"cpu":%s,"memoryPercent":%s,"memoryKB":%s,"command":"%s","fullCommand":"%s"}' \\
|
||||
"$pid" "$ppid" "$cpu" "$memp" "$memk" "$comm" "$cmd"
|
||||
pfirst=0
|
||||
done < "$tmp_ps"
|
||||
rm -f "$tmp_ps"
|
||||
printf ']',
|
||||
|
||||
dmip="/sys/class/dmi/id"
|
||||
[ -d "$dmip" ] || dmip="/sys/devices/virtual/dmi/id"
|
||||
mb_vendor=$([ -r "$dmip/board_vendor" ] && cat "$dmip/board_vendor" | json_escape || echo "Unknown")
|
||||
mb_name=$([ -r "$dmip/board_name" ] && cat "$dmip/board_name" | json_escape || echo "")
|
||||
bios_ver=$([ -r "$dmip/bios_version" ] && cat "$dmip/bios_version" | json_escape || echo "Unknown")
|
||||
bios_date=$([ -r "$dmip/bios_date" ] && cat "$dmip/bios_date" | json_escape || echo "")
|
||||
|
||||
kern_ver=$(uname -r | json_escape)
|
||||
distro=$(grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d= -f2- | tr -d '"' | json_escape || echo 'Unknown')
|
||||
host_name=$(hostname | json_escape)
|
||||
arch_name=$(uname -m)
|
||||
load_avg=$(cut -d' ' -f1-3 /proc/loadavg)
|
||||
proc_count=$(( $(ps aux | wc -l) - 1 ))
|
||||
thread_count=$(ps -eL | wc -l)
|
||||
boot_time=$(who -b 2>/dev/null | awk '{print $3, $4}' | json_escape || echo 'Unknown')
|
||||
|
||||
printf '"system":{"kernel":"%s","distro":"%s","hostname":"%s","arch":"%s","loadavg":"%s","processes":%d,"threads":%d,"boottime":"%s","motherboard":"%s %s","bios":"%s %s"},' \\
|
||||
"$kern_ver" "$distro" "$host_name" "$arch_name" "$load_avg" "$proc_count" "$thread_count" "$boot_time" "$mb_vendor" "$mb_name" "$bios_ver" "$bios_date"
|
||||
|
||||
printf '"diskmounts":['
|
||||
tmp_mounts=$(mktemp)
|
||||
df -h --output=source,target,fstype,size,used,avail,pcent | tail -n +2 | grep -vE '^(tmpfs|devtmpfs)' | head -n 10 > "$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 ']'
|
||||
|
||||
printf "}\\n"`
|
||||
|
||||
Process {
|
||||
id: unifiedStatsProcess
|
||||
command: [
|
||||
"bash", "-c",
|
||||
"bash -s \"$1\" \"$2\" <<'QS_EOF'\\n" + root.scriptBody + "\\nQS_EOF\\n",
|
||||
"qsmon", root.sortBy, root.maxProcesses
|
||||
]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Unified stats process failed with exit code:", exitCode);
|
||||
isUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
const fullText = text.trim();
|
||||
const lastBraceIndex = fullText.lastIndexOf('}');
|
||||
|
||||
if (lastBraceIndex === -1) {
|
||||
console.error("SysMonitorService: No JSON object found in output.", fullText);
|
||||
isUpdating = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const jsonText = fullText.substring(0, lastBraceIndex + 1);
|
||||
|
||||
try {
|
||||
const data = JSON.parse(jsonText);
|
||||
parseUnifiedStats(jsonText);
|
||||
} catch (e) {
|
||||
console.error("BROKEN JSON:", e, "Cleaned Text:", jsonText);
|
||||
isUpdating = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,551 +0,0 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property real cpuUsage: 0
|
||||
property int cpuCores: 1
|
||||
property string cpuModel: ""
|
||||
property real cpuFrequency: 0
|
||||
property var prevCpuStats: [0, 0, 0, 0, 0, 0, 0, 0]
|
||||
property real memoryUsage: 0
|
||||
property real totalMemory: 0
|
||||
property real usedMemory: 0
|
||||
property real freeMemory: 0
|
||||
property real availableMemory: 0
|
||||
property real bufferMemory: 0
|
||||
property real cacheMemory: 0
|
||||
property real cpuTemperature: 0
|
||||
property string kernelVersion: ""
|
||||
property string distribution: ""
|
||||
property string hostname: ""
|
||||
property string scheduler: ""
|
||||
property string architecture: ""
|
||||
property string loadAverage: ""
|
||||
property int processCount: 0
|
||||
property int threadCount: 0
|
||||
property string bootTime: ""
|
||||
property string motherboard: ""
|
||||
property string biosVersion: ""
|
||||
property var diskMounts: []
|
||||
property string diskUsage: ""
|
||||
property int cpuUpdateInterval: 3000
|
||||
property int memoryUpdateInterval: 5000
|
||||
property int temperatureUpdateInterval: 10000
|
||||
property int systemInfoUpdateInterval: 30000
|
||||
property bool enabledForTopBar: true
|
||||
property bool enabledForDetailedView: false
|
||||
|
||||
function getCpuInfo() {
|
||||
cpuInfoProcess.running = true;
|
||||
}
|
||||
|
||||
function updateSystemStats() {
|
||||
if (root.enabledForTopBar || root.enabledForDetailedView) {
|
||||
cpuUsageProcess.running = true;
|
||||
memoryUsageProcess.running = true;
|
||||
cpuFrequencyProcess.running = true;
|
||||
if (root.enabledForDetailedView)
|
||||
temperatureProcess.running = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function updateSystemInfo() {
|
||||
kernelInfoProcess.running = true;
|
||||
distributionProcess.running = true;
|
||||
hostnameProcess.running = true;
|
||||
schedulerProcess.running = true;
|
||||
architectureProcess.running = true;
|
||||
loadAverageProcess.running = true;
|
||||
processCountProcess.running = true;
|
||||
threadCountProcess.running = true;
|
||||
bootTimeProcess.running = true;
|
||||
motherboardProcess.running = true;
|
||||
biosProcess.running = true;
|
||||
diskMountsProcess.running = true;
|
||||
}
|
||||
|
||||
function enableTopBarMonitoring(enabled) {
|
||||
root.enabledForTopBar = enabled;
|
||||
}
|
||||
|
||||
function enableDetailedMonitoring(enabled) {
|
||||
root.enabledForDetailedView = enabled;
|
||||
}
|
||||
|
||||
function getCpuUsageColor() {
|
||||
if (cpuUsage > 80)
|
||||
return "#e74c3c";
|
||||
|
||||
if (cpuUsage > 60)
|
||||
return "#f39c12";
|
||||
|
||||
return "#27ae60";
|
||||
}
|
||||
|
||||
function getMemoryUsageColor() {
|
||||
if (memoryUsage > 90)
|
||||
return "#e74c3c";
|
||||
|
||||
if (memoryUsage > 75)
|
||||
return "#f39c12";
|
||||
|
||||
return "#3498db";
|
||||
}
|
||||
|
||||
function formatMemory(mb) {
|
||||
if (mb >= 1024)
|
||||
return (mb / 1024).toFixed(1) + " GB";
|
||||
|
||||
return mb.toFixed(0) + " MB";
|
||||
}
|
||||
|
||||
function getTemperatureColor() {
|
||||
if (cpuTemperature > 80)
|
||||
return "#e74c3c";
|
||||
|
||||
if (cpuTemperature > 65)
|
||||
return "#f39c12";
|
||||
|
||||
return "#27ae60";
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("SystemMonitorService: Starting initialization...");
|
||||
getCpuInfo();
|
||||
updateSystemStats();
|
||||
updateSystemInfo();
|
||||
console.log("SystemMonitorService: Initialization complete");
|
||||
}
|
||||
|
||||
Process {
|
||||
id: cpuInfoProcess
|
||||
|
||||
command: ["bash", "-c", "lscpu | grep -E 'Model name|CPU\\(s\\):' | head -2"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("CPU info check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const lines = text.trim().split('\n');
|
||||
for (const line of lines) {
|
||||
if (line.includes("Model name"))
|
||||
root.cpuModel = line.split(":")[1].trim();
|
||||
else if (line.includes("CPU(s):"))
|
||||
root.cpuCores = parseInt(line.split(":")[1].trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: cpuUsageProcess
|
||||
|
||||
command: ["bash", "-c", "head -1 /proc/stat | awk '{print $2,$3,$4,$5,$6,$7,$8,$9}'"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("CPU usage check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
const stats = text.trim().split(" ").map((x) => {
|
||||
return parseInt(x);
|
||||
});
|
||||
if (root.prevCpuStats[0] > 0) {
|
||||
let diffs = [];
|
||||
for (let i = 0; i < 8; i++) {
|
||||
diffs[i] = stats[i] - root.prevCpuStats[i];
|
||||
}
|
||||
const totalTime = diffs.reduce((a, b) => {
|
||||
return a + b;
|
||||
}, 0);
|
||||
const idleTime = diffs[3] + diffs[4];
|
||||
if (totalTime > 0)
|
||||
root.cpuUsage = Math.max(0, Math.min(100, ((totalTime - idleTime) / totalTime) * 100));
|
||||
|
||||
}
|
||||
root.prevCpuStats = stats;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: memoryUsageProcess
|
||||
|
||||
command: ["bash", "-c", "free -m | awk 'NR==2{printf \"%.1f %.1f %.1f %.1f\", $3*100/$2, $2, $3, $7}'"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Memory usage check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
const parts = text.trim().split(" ");
|
||||
root.memoryUsage = parseFloat(parts[0]);
|
||||
root.totalMemory = parseFloat(parts[1]);
|
||||
root.usedMemory = parseFloat(parts[2]);
|
||||
root.availableMemory = parseFloat(parts[3]);
|
||||
root.freeMemory = root.totalMemory - root.usedMemory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: cpuFrequencyProcess
|
||||
|
||||
command: ["bash", "-c", "cat /proc/cpuinfo | grep 'cpu MHz' | head -1 | awk '{print $4}'"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("CPU frequency check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.cpuFrequency = parseFloat(text.trim());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: temperatureProcess
|
||||
|
||||
command: ["bash", "-c", "if [ -f /sys/class/thermal/thermal_zone0/temp ]; then cat /sys/class/thermal/thermal_zone0/temp | awk '{print $1/1000}'; else sensors 2>/dev/null | grep 'Core 0' | awk '{print $3}' | sed 's/+//g;s/°C//g' | head -1; fi"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("CPU temperature check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.cpuTemperature = parseFloat(text.trim());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: kernelInfoProcess
|
||||
|
||||
command: ["bash", "-c", "uname -r"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Kernel info check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.kernelVersion = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: distributionProcess
|
||||
|
||||
command: ["bash", "-c", "grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '\"' || echo 'Unknown'"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Distribution check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.distribution = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: hostnameProcess
|
||||
|
||||
command: ["bash", "-c", "hostname"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Hostname check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.hostname = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: schedulerProcess
|
||||
|
||||
command: ["bash", "-c", "cat /sys/block/sda/queue/scheduler 2>/dev/null | grep -o '\\[.*\\]' | tr -d '[]' || echo 'Unknown'"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Scheduler check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.scheduler = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: architectureProcess
|
||||
|
||||
command: ["bash", "-c", "uname -m"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Architecture check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.architecture = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: loadAverageProcess
|
||||
|
||||
command: ["bash", "-c", "cat /proc/loadavg | cut -d' ' -f1,2,3"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Load average check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.loadAverage = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: processCountProcess
|
||||
|
||||
command: ["bash", "-c", "ps aux | wc -l"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Process count check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.processCount = parseInt(text.trim()) - 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: threadCountProcess
|
||||
|
||||
command: ["bash", "-c", "cat /proc/stat | grep processes | awk '{print $2}'"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Thread count check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.threadCount = parseInt(text.trim());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: bootTimeProcess
|
||||
|
||||
command: ["bash", "-c", "who -b | awk '{print $3, $4}' || stat -c %w /proc/1 2>/dev/null | cut -d' ' -f1,2 || echo 'Unknown'"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Boot time check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.bootTime = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: motherboardProcess
|
||||
|
||||
command: ["bash", "-c", "if [ -r /sys/devices/virtual/dmi/id/board_vendor ] && [ -r /sys/devices/virtual/dmi/id/board_name ]; then echo \"$(cat /sys/devices/virtual/dmi/id/board_vendor 2>/dev/null) $(cat /sys/devices/virtual/dmi/id/board_name 2>/dev/null)\"; else echo 'Unknown'; fi"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Motherboard check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.motherboard = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: biosProcess
|
||||
|
||||
command: ["bash", "-c", "if [ -r /sys/devices/virtual/dmi/id/bios_version ] && [ -r /sys/devices/virtual/dmi/id/bios_date ]; then echo \"$(cat /sys/devices/virtual/dmi/id/bios_version 2>/dev/null) $(cat /sys/devices/virtual/dmi/id/bios_date 2>/dev/null)\"; else echo 'Unknown'; fi"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("BIOS check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim())
|
||||
root.biosVersion = text.trim();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: diskMountsProcess
|
||||
|
||||
command: ["bash", "-c", "df -h --output=source,target,fstype,size,used,avail,pcent | tail -n +2 | grep -v tmpfs | grep -v devtmpfs | head -10"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Disk mounts check failed with exit code:", exitCode);
|
||||
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
let mounts = [];
|
||||
const lines = text.trim().split('\n');
|
||||
for (const line of lines) {
|
||||
const parts = line.split(/\s+/);
|
||||
if (parts.length >= 7)
|
||||
mounts.push({
|
||||
"device": parts[0],
|
||||
"mount": parts[1],
|
||||
"fstype": parts[2],
|
||||
"size": parts[3],
|
||||
"used": parts[4],
|
||||
"avail": parts[5],
|
||||
"percent": parts[6]
|
||||
});
|
||||
|
||||
}
|
||||
root.diskMounts = mounts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: basicStatsTimer
|
||||
|
||||
interval: 5000
|
||||
running: root.enabledForTopBar || root.enabledForDetailedView
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
cpuUsageProcess.running = true;
|
||||
cpuFrequencyProcess.running = true;
|
||||
memoryUsageProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: detailedStatsTimer
|
||||
|
||||
interval: 15000
|
||||
running: root.enabledForDetailedView
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
temperatureProcess.running = true;
|
||||
updateSystemInfo();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,10 +23,6 @@ Singleton {
|
||||
uptimeProcess.running = true;
|
||||
}
|
||||
|
||||
function getProfilePicture() {
|
||||
profilePictureProcess.running = true;
|
||||
}
|
||||
|
||||
function refreshUserInfo() {
|
||||
getUserInfo();
|
||||
getUptime();
|
||||
@@ -61,8 +57,6 @@ Singleton {
|
||||
root.fullName = parts[1] || parts[0] || "";
|
||||
root.hostname = parts[2] || "";
|
||||
console.log("UserInfoService: User info loaded -", root.username, root.fullName, root.hostname);
|
||||
// Try to find profile picture
|
||||
getProfilePicture();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,53 +82,5 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Look for profile picture in common locations
|
||||
Process {
|
||||
id: profilePictureProcess
|
||||
|
||||
command: ["bash", "-c", `
|
||||
# Try common profile picture locations
|
||||
for path in \
|
||||
"$HOME/.face" \
|
||||
"$HOME/.face.icon" \
|
||||
"/var/lib/AccountsService/icons/$USER" \
|
||||
"/usr/share/pixmaps/faces/$USER" \
|
||||
"/usr/share/pixmaps/faces/$USER.png" \
|
||||
"/usr/share/pixmaps/faces/$USER.jpg"; do
|
||||
if [ -f "$path" ]; then
|
||||
echo "$path"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
# Fallback to generic user icon
|
||||
echo ""
|
||||
`]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("UserInfoService: Failed to find profile picture");
|
||||
root.profilePicture = "";
|
||||
root.profileAvailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const path = text.trim();
|
||||
if (path && path.length > 0) {
|
||||
root.profilePicture = "file://" + path;
|
||||
root.profileAvailable = true;
|
||||
console.log("UserInfoService: Profile picture found at", path);
|
||||
} else {
|
||||
root.profilePicture = "";
|
||||
root.profileAvailable = false;
|
||||
console.log("UserInfoService: No profile picture found, using default avatar");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user