mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-07 05:55:37 -05:00
Add system resource dropdown
This commit is contained in:
173
Services/ProcessMonitorService.qml
Normal file
173
Services/ProcessMonitorService.qml
Normal file
@@ -0,0 +1,173 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
// Process list properties
|
||||
property var processes: []
|
||||
property bool isUpdating: false
|
||||
property int processUpdateInterval: 3000
|
||||
|
||||
// Sorting options
|
||||
property string sortBy: "cpu" // "cpu", "memory", "name", "pid"
|
||||
property bool sortDescending: true
|
||||
property int maxProcesses: 20
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("ProcessMonitorService: Starting initialization...")
|
||||
updateProcessList()
|
||||
console.log("ProcessMonitorService: Initialization complete")
|
||||
}
|
||||
|
||||
// Process monitoring with ps command
|
||||
Process {
|
||||
id: processListProcess
|
||||
command: ["bash", "-c", "ps axo pid,ppid,pcpu,pmem,comm,cmd --sort=-pcpu | head -" + (root.maxProcesses + 1)]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
const lines = text.trim().split('\n')
|
||||
const newProcesses = []
|
||||
|
||||
// Skip header line
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const line = lines[i].trim()
|
||||
if (!line) continue
|
||||
|
||||
// Parse ps output: PID PPID %CPU %MEM COMMAND CMD
|
||||
const parts = line.split(/\s+/)
|
||||
if (parts.length >= 6) {
|
||||
const pid = parseInt(parts[0])
|
||||
const ppid = parseInt(parts[1])
|
||||
const cpu = parseFloat(parts[2])
|
||||
const memory = parseFloat(parts[3])
|
||||
const command = parts[4]
|
||||
const fullCmd = parts.slice(5).join(' ')
|
||||
|
||||
newProcesses.push({
|
||||
pid: pid,
|
||||
ppid: ppid,
|
||||
cpu: cpu,
|
||||
memory: memory,
|
||||
command: command,
|
||||
fullCommand: fullCmd,
|
||||
displayName: command.length > 15 ? command.substring(0, 15) + "..." : command
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
root.processes = newProcesses
|
||||
root.isUpdating = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode) => {
|
||||
root.isUpdating = false
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Process list check failed with exit code:", exitCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process monitoring timer
|
||||
Timer {
|
||||
id: processTimer
|
||||
interval: root.processUpdateInterval
|
||||
running: true
|
||||
repeat: true
|
||||
|
||||
onTriggered: {
|
||||
updateProcessList()
|
||||
}
|
||||
}
|
||||
|
||||
// Public functions
|
||||
function updateProcessList() {
|
||||
if (!root.isUpdating) {
|
||||
root.isUpdating = true
|
||||
|
||||
// Update sort command based on current sort option
|
||||
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,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()
|
||||
}
|
||||
|
||||
function killProcess(pid) {
|
||||
if (pid > 0) {
|
||||
const killCmd = ["bash", "-c", "kill " + pid]
|
||||
const killProcess = Qt.createQmlObject(`
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
Process {
|
||||
command: ${JSON.stringify(killCmd)}
|
||||
running: true
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode === 0) {
|
||||
console.log("Process killed successfully:", ${pid})
|
||||
} else {
|
||||
console.warn("Failed to kill process:", ${pid}, "exit code:", exitCode)
|
||||
}
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
`, root)
|
||||
}
|
||||
}
|
||||
|
||||
function getProcessIcon(command) {
|
||||
// Return appropriate Material Design icon for common processes
|
||||
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" // Default process icon
|
||||
}
|
||||
|
||||
function formatCpuUsage(cpu) {
|
||||
return cpu.toFixed(1) + "%"
|
||||
}
|
||||
|
||||
function formatMemoryUsage(memory) {
|
||||
return memory.toFixed(1) + "%"
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,9 @@ Singleton {
|
||||
property string cpuModel: ""
|
||||
property real cpuFrequency: 0.0
|
||||
|
||||
// Previous CPU stats for accurate calculation
|
||||
property var prevCpuStats: [0, 0, 0, 0, 0, 0, 0, 0]
|
||||
|
||||
// Memory properties
|
||||
property real memoryUsage: 0.0
|
||||
property real totalMemory: 0.0
|
||||
@@ -26,8 +29,8 @@ Singleton {
|
||||
property real cpuTemperature: 0.0
|
||||
|
||||
// Update intervals
|
||||
property int cpuUpdateInterval: 2000
|
||||
property int memoryUpdateInterval: 3000
|
||||
property int cpuUpdateInterval: 1000
|
||||
property int memoryUpdateInterval: 2000
|
||||
property int temperatureUpdateInterval: 5000
|
||||
|
||||
Component.onCompleted: {
|
||||
@@ -63,16 +66,33 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// CPU usage monitoring
|
||||
// CPU usage monitoring with accurate calculation
|
||||
Process {
|
||||
id: cpuUsageProcess
|
||||
command: ["bash", "-c", "grep 'cpu ' /proc/stat | awk '{usage=($2+$4)*100/($2+$3+$4+$5)} END {printf \"%.1f\", usage}'"]
|
||||
command: ["bash", "-c", "head -1 /proc/stat | awk '{print $2,$3,$4,$5,$6,$7,$8,$9}'"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text.trim()) {
|
||||
root.cpuUsage = parseFloat(text.trim())
|
||||
const stats = text.trim().split(" ").map(x => parseInt(x))
|
||||
if (root.prevCpuStats[0] > 0) {
|
||||
// Calculate differences
|
||||
let diffs = []
|
||||
for (let i = 0; i < 8; i++) {
|
||||
diffs[i] = stats[i] - root.prevCpuStats[i]
|
||||
}
|
||||
|
||||
// Calculate total and idle time
|
||||
const totalTime = diffs.reduce((a, b) => a + b, 0)
|
||||
const idleTime = diffs[3] + diffs[4] // idle + iowait
|
||||
|
||||
// CPU usage percentage
|
||||
if (totalTime > 0) {
|
||||
root.cpuUsage = Math.max(0, Math.min(100, ((totalTime - idleTime) / totalTime) * 100))
|
||||
}
|
||||
}
|
||||
root.prevCpuStats = stats
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ Singleton {
|
||||
|
||||
property var weather: ({
|
||||
available: false,
|
||||
loading: true,
|
||||
temp: 0,
|
||||
tempF: 0,
|
||||
city: "",
|
||||
@@ -104,6 +105,7 @@ Singleton {
|
||||
|
||||
root.weather = {
|
||||
available: true,
|
||||
loading: false,
|
||||
temp: Number(current.temp_C) || 0,
|
||||
tempF: Number(current.temp_F) || 0,
|
||||
city: location.areaName?.[0]?.value || "Unknown",
|
||||
@@ -122,6 +124,7 @@ Singleton {
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse weather data:", e.message)
|
||||
root.weather.available = false
|
||||
root.weather.loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,6 +133,7 @@ Singleton {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Weather fetch failed with exit code:", exitCode)
|
||||
root.weather.available = false
|
||||
root.weather.loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ singleton BluetoothService 1.0 BluetoothService.qml
|
||||
singleton BrightnessService 1.0 BrightnessService.qml
|
||||
singleton BatteryService 1.0 BatteryService.qml
|
||||
singleton SystemMonitorService 1.0 SystemMonitorService.qml
|
||||
singleton ProcessMonitorService 1.0 ProcessMonitorService.qml
|
||||
singleton AppSearchService 1.0 AppSearchService.qml
|
||||
singleton LauncherService 1.0 LauncherService.qml
|
||||
singleton NiriWorkspaceService 1.0 NiriWorkspaceService.qml
|
||||
|
||||
Reference in New Issue
Block a user