1
0
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:
bbedward
2025-07-13 15:36:41 -04:00
parent a9b5e0b09d
commit a1b6c9e791
13 changed files with 965 additions and 15 deletions

View 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) + "%"
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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