From 05e9561fd687c15baba56fc9eeca9a29b504abd2 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Sun, 15 Jun 2025 15:43:50 +1000 Subject: internal: use stdiocollector Removes dependency on jq Also fix beatdetector --- modules/dashboard/dash/User.qml | 4 +- services/BeatDetector.qml | 2 +- services/Bluetooth.qml | 52 +++++++++++++++-------- services/Brightness.qml | 25 ++++------- services/Hyprland.qml | 15 ++++--- services/Network.qml | 48 ++++++++++++---------- services/SystemUsage.qml | 91 +++++++++++++++++++++-------------------- services/Wallpapers.qml | 12 +++--- services/Weather.qml | 28 +++++++++---- widgets/CachingImage.qml | 4 +- 10 files changed, 154 insertions(+), 127 deletions(-) diff --git a/modules/dashboard/dash/User.qml b/modules/dashboard/dash/User.qml index f1b9b18..d2dc4e7 100644 --- a/modules/dashboard/dash/User.qml +++ b/modules/dashboard/dash/User.qml @@ -69,8 +69,8 @@ Row { running: true command: ["uptime", "-p"] - stdout: SplitParser { - onRead: data => uptimeProc.uptime = data + stdout: StdioCollector { + onStreamFinished: uptimeProc.uptime = text } } } diff --git a/services/BeatDetector.qml b/services/BeatDetector.qml index 66bf2d4..196d40c 100644 --- a/services/BeatDetector.qml +++ b/services/BeatDetector.qml @@ -10,7 +10,7 @@ Singleton { Process { running: true - command: [`${Quickshell.shellRoot}/assets/realtime-beat-detector.py`] + command: [`${Quickshell.configDir}/assets/realtime-beat-detector.py`] stdout: SplitParser { onRead: data => root.bpm = parseFloat(data) } diff --git a/services/Bluetooth.qml b/services/Bluetooth.qml index 43f3636..f5e9de1 100644 --- a/services/Bluetooth.qml +++ b/services/Bluetooth.qml @@ -24,31 +24,47 @@ Singleton { Process { id: getInfo + running: true - command: ["sh", "-c", "bluetoothctl show | paste -s"] - stdout: SplitParser { - onRead: data => { - root.powered = data.includes("Powered: yes"); - root.discovering = data.includes("Discovering: yes"); + command: ["bluetoothctl", "show"] + stdout: StdioCollector { + onStreamFinished: { + root.powered = text.includes("Powered: yes"); + root.discovering = text.includes("Discovering: yes"); } } } Process { id: getDevices + running: true - command: ["fish", "-c", `for a in (bluetoothctl devices | cut -d ' ' -f 2); bluetoothctl info $a | jq -R 'reduce (inputs / ":") as [$key, $value] ({}; .[$key | ltrimstr("\t")] = ($value | ltrimstr(" ")))' | jq -c --arg addr $a '.Address = $addr'; end | jq -sc`] - stdout: SplitParser { - onRead: data => { - const devices = JSON.parse(data).filter(d => d.Name); + command: ["fish", "-c", ` + for a in (bluetoothctl devices) + if string match -q 'Device *' $a + bluetoothctl info $addr (string split ' ' $a)[2] + echo + end + end`] + stdout: StdioCollector { + onStreamFinished: { + const devices = text.trim().split("\n\n").map(d => ({ + name: d.match(/Name: (.*)/)[1], + alias: d.match(/Alias: (.*)/)[1], + address: d.match(/Device ([0-9A-Z:]{17})/)[1], + icon: d.match(/Icon: (.*)/)[1], + connected: d.includes("Connected: yes"), + paired: d.includes("Paired: yes"), + trusted: d.includes("Trusted: yes") + })); const rDevices = root.devices; - const destroyed = rDevices.filter(rd => !devices.find(d => d.Address === rd.address)); + const destroyed = rDevices.filter(rd => !devices.find(d => d.address === rd.address)); for (const device of destroyed) rDevices.splice(rDevices.indexOf(device), 1).forEach(d => d.destroy()); for (const device of devices) { - const match = rDevices.find(d => d.address === device.Address); + const match = rDevices.find(d => d.address === device.address); if (match) { match.lastIpcObject = device; } else { @@ -63,13 +79,13 @@ Singleton { component Device: QtObject { required property var lastIpcObject - readonly property string name: lastIpcObject.Name - readonly property string alias: lastIpcObject.Alias - readonly property string address: lastIpcObject.Address - readonly property string icon: lastIpcObject.Icon - readonly property bool connected: lastIpcObject.Connected === "yes" - readonly property bool paired: lastIpcObject.Paired === "yes" - readonly property bool trusted: lastIpcObject.Trusted === "yes" + readonly property string name: lastIpcObject.name + readonly property string alias: lastIpcObject.alias + readonly property string address: lastIpcObject.address + readonly property string icon: lastIpcObject.icon + readonly property bool connected: lastIpcObject.connected + readonly property bool paired: lastIpcObject.paired + readonly property bool trusted: lastIpcObject.trusted } Component { diff --git a/services/Brightness.qml b/services/Brightness.qml index 1687878..3715853 100644 --- a/services/Brightness.qml +++ b/services/Brightness.qml @@ -9,7 +9,7 @@ import QtQuick Singleton { id: root - property var ddcMonitors: [] + property list ddcMonitors: [] readonly property list monitors: variants.instances function getMonitorForScreen(screen: ShellScreen): var { @@ -49,19 +49,12 @@ Singleton { id: ddcProc command: ["ddcutil", "detect", "--brief"] - stdout: SplitParser { - splitMarker: "\n\n" - onRead: data => { - if (data.startsWith("Display ")) { - const lines = data.split("\n").map(l => l.trim()); - root.ddcMonitors.push({ - model: lines.find(l => l.startsWith("Monitor:")).split(":")[2], - busNum: lines.find(l => l.startsWith("I2C bus:")).split("/dev/i2c-")[1] - }); - } - } + stdout: StdioCollector { + onStreamFinished: root.ddcMonitors = text.trim().split("\n\n").filter(d => d.startsWith("Display ")).map(d => ({ + model: d.match(/Monitor:.*:(.*):.*/)[1], + busNum: d.match(/I2C bus:[ ]*\/dev\/i2c-([0-9]+)/)[1] + })) } - onExited: root.ddcMonitorsChanged() } Process { @@ -87,9 +80,9 @@ Singleton { property real brightness readonly property Process initProc: Process { - stdout: SplitParser { - onRead: data => { - const [, , , current, max] = data.split(" "); + stdout: StdioCollector { + onStreamFinished: { + const [, , , current, max] = text.split(" "); monitor.brightness = parseInt(current) / parseInt(max); } } diff --git a/services/Hyprland.qml b/services/Hyprland.qml index 610fba2..b8fa7c5 100644 --- a/services/Hyprland.qml +++ b/services/Hyprland.qml @@ -41,10 +41,10 @@ Singleton { Process { id: getClients - command: ["sh", "-c", "hyprctl -j clients | jq -c"] - stdout: SplitParser { - onRead: data => { - const clients = JSON.parse(data); + command: ["hyprctl", "-j", "clients"] + stdout: StdioCollector { + onStreamFinished: { + const clients = JSON.parse(text); const rClients = root.clients; const destroyed = rClients.filter(rc => !clients.find(c => c.address === rc.address)); @@ -68,10 +68,9 @@ Singleton { Process { id: getActiveClient command: ["hyprctl", "-j", "activewindow"] - stdout: SplitParser { - splitMarker: "" - onRead: data => { - const client = JSON.parse(data); + stdout: StdioCollector { + onStreamFinished: { + const client = JSON.parse(text); const rClient = root.activeClient; if (client.address) { if (rClient) diff --git a/services/Network.qml b/services/Network.qml index b4827c8..6a7f50e 100644 --- a/services/Network.qml +++ b/services/Network.qml @@ -23,31 +23,36 @@ Singleton { Process { id: getNetworks running: true - command: ["sh", "-c", `nmcli -g ACTIVE,SIGNAL,FREQ,SSID,BSSID d w | jq -ncR '[(inputs | split("(?= 4)]'`] - stdout: SplitParser { - onRead: data => { - const networks = JSON.parse(data).map(n => [n[0] === "yes", parseInt(n[1]), parseInt(n[2]), n[3], n[4].replace(/\\/g, "")]); + command: ["nmcli", "-g", "ACTIVE,SIGNAL,FREQ,SSID,BSSID", "d", "w"] + stdout: StdioCollector { + onStreamFinished: { + const PLACEHOLDER = "STRINGWHICHHOPEFULLYWONTBEUSED"; + const rep = new RegExp("\\\\:", "g"); + const rep2 = new RegExp(PLACEHOLDER, "g"); + + const networks = text.trim().split("\n").map(n => { + const net = n.replace(rep, PLACEHOLDER).split(":"); + return { + active: net[0] === "yes", + strength: parseInt(net[1]), + frequency: parseInt(net[2]), + ssid: net[3], + bssid: net[4].replace(rep2, ":") + }; + }); const rNetworks = root.networks; - const destroyed = rNetworks.filter(rn => !networks.find(n => n[2] === rn.frequency && n[3] === rn.ssid && n[4] === rn.bssid)); + const destroyed = rNetworks.filter(rn => !networks.find(n => n.frequency === rn.frequency && n.ssid === rn.ssid && n.bssid === rn.bssid)); for (const network of destroyed) rNetworks.splice(rNetworks.indexOf(network), 1).forEach(n => n.destroy()); for (const network of networks) { - const match = rNetworks.find(n => n.frequency === network[2] && n.ssid === network[3] && n.bssid === network[4]); + const match = rNetworks.find(n => n.frequency === network.frequency && n.ssid === network.ssid && n.bssid === network.bssid); if (match) { - match.active = network[0]; - match.strength = network[1]; - match.frequency = network[2]; - match.ssid = network[3]; - match.bssid = network[4]; + match.lastIpcObject = network; } else { rNetworks.push(apComp.createObject(root, { - active: network[0], - strength: network[1], - frequency: network[2], - ssid: network[3], - bssid: network[4] + lastIpcObject: network })); } } @@ -56,11 +61,12 @@ Singleton { } component AccessPoint: QtObject { - required property string ssid - required property string bssid - required property int strength - required property int frequency - required property bool active + required property var lastIpcObject + readonly property string ssid: lastIpcObject.ssid + readonly property string bssid: lastIpcObject.bssid + readonly property int strength: lastIpcObject.strength + readonly property int frequency: lastIpcObject.frequency + readonly property bool active: lastIpcObject.active } Component { diff --git a/services/SystemUsage.qml b/services/SystemUsage.qml index ef953f1..9bf665a 100644 --- a/services/SystemUsage.qml +++ b/services/SystemUsage.qml @@ -98,38 +98,40 @@ Singleton { running: true command: ["sh", "-c", "df | grep '^/dev/' | awk '{print $1, $3, $4}'"] - stdout: SplitParser { - splitMarker: "" - onRead: data => { - const deviceMap = new Map(); - - for (const line of data.trim().split("\n")) { - if (line.trim() === "") continue; - - const parts = line.trim().split(/\s+/); - if (parts.length >= 3) { - const device = parts[0]; - const used = parseInt(parts[1], 10) || 0; - const avail = parseInt(parts[2], 10) || 0; - - // only keep the entry with the largest total space for each device - if (!deviceMap.has(device) || - (used + avail) > (deviceMap.get(device).used + deviceMap.get(device).avail)) { - deviceMap.set(device, { used: used, avail: avail }); - } - } - } - - let totalUsed = 0; - let totalAvail = 0; - - for (const [device, stats] of deviceMap) { - totalUsed += stats.used; - totalAvail += stats.avail; - } - - root.storageUsed = totalUsed; - root.storageTotal = totalUsed + totalAvail; + stdout: StdioCollector { + onStreamFinished: { + const deviceMap = new Map(); + + for (const line of text.trim().split("\n")) { + if (line.trim() === "") + continue; + + const parts = line.trim().split(/\s+/); + if (parts.length >= 3) { + const device = parts[0]; + const used = parseInt(parts[1], 10) || 0; + const avail = parseInt(parts[2], 10) || 0; + + // Only keep the entry with the largest total space for each device + if (!deviceMap.has(device) || (used + avail) > (deviceMap.get(device).used + deviceMap.get(device).avail)) { + deviceMap.set(device, { + used: used, + avail: avail + }); + } + } + } + + let totalUsed = 0; + let totalAvail = 0; + + for (const [device, stats] of deviceMap) { + totalUsed += stats.used; + totalAvail += stats.avail; + } + + root.storageUsed = totalUsed; + root.storageTotal = totalUsed + totalAvail; } } } @@ -138,10 +140,10 @@ Singleton { id: cpuTemp running: true - command: ["fish", "-c", "cat /sys/class/thermal/thermal_zone*/temp | string join ' '"] - stdout: SplitParser { - onRead: data => { - const temps = data.trim().split(" "); + command: ["sh", "-c", "cat /sys/class/thermal/thermal_zone*/temp"] + stdout: StdioCollector { + onStreamFinished: { + const temps = text.trim().split(" "); const sum = temps.reduce((acc, d) => acc + parseInt(d, 10), 0); root.cpuTemp = sum / temps.length / 1000; } @@ -153,10 +155,9 @@ Singleton { running: true command: ["sh", "-c", "cat /sys/class/drm/card*/device/gpu_busy_percent"] - stdout: SplitParser { - splitMarker: "" - onRead: data => { - const percs = data.trim().split("\n"); + stdout: StdioCollector { + onStreamFinished: { + const percs = text.trim().split("\n"); const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0); root.gpuPerc = sum / percs.length / 100; } @@ -167,13 +168,14 @@ Singleton { id: gpuTemp running: true - command: ["sh", "-c", "sensors | jq -nRc '[inputs]'"] - stdout: SplitParser { - onRead: data => { + command: ["sensors"] + stdout: StdioCollector { + onStreamFinished: { let eligible = false; let sum = 0; let count = 0; - for (const line of JSON.parse(data)) { + + for (const line of text.trim().split("\n")) { if (line === "Adapter: PCI adapter") eligible = true; else if (line === "") @@ -186,6 +188,7 @@ Singleton { } } } + root.gpuTemp = count > 0 ? sum / count : 0; } } diff --git a/services/Wallpapers.qml b/services/Wallpapers.qml index dd6a9f7..ef0eb3d 100644 --- a/services/Wallpapers.qml +++ b/services/Wallpapers.qml @@ -78,10 +78,9 @@ Singleton { id: getPreviewColoursProc command: ["caelestia", "wallpaper", "-p", root.previewPath] - stdout: SplitParser { - splitMarker: "" - onRead: data => { - Colours.load(data, true); + stdout: StdioCollector { + onStreamFinished: { + Colours.load(text, true); Colours.showPreview = true; } } @@ -98,9 +97,8 @@ Singleton { Process { running: true command: ["fd", ".", root.path, "-t", "f", "-e", "jpg", "-e", "jpeg", "-e", "png", "-e", "webp", "-e", "tif", "-e", "tiff"] - stdout: SplitParser { - splitMarker: "" - onRead: data => wallpapers.model = data.trim().split("\n") + stdout: StdioCollector { + onStreamFinished: wallpapers.model = text.trim().split("\n") } } diff --git a/services/Weather.qml b/services/Weather.qml index 13503f9..4f53d0b 100644 --- a/services/Weather.qml +++ b/services/Weather.qml @@ -7,6 +7,7 @@ import Quickshell.Io Singleton { id: root + property string loc property string icon property string description property real temperature @@ -15,17 +16,28 @@ Singleton { wttrProc.running = true; } + onLocChanged: wttrProc.running = true + Process { - id: wttrProc + id: ipProc running: true - command: ["fish", "-c", `curl "https://wttr.in/$(curl ipinfo.io | jq -r '.city' | string replace -a ' ' '%20')?format=j1" | jq -c '.current_condition[0] | {code: .weatherCode, desc: .weatherDesc[0].value, temp: .temp_C}'`] - stdout: SplitParser { - onRead: data => { - const json = JSON.parse(data); - root.icon = Icons.getWeatherIcon(json.code); - root.description = json.desc; - root.temperature = parseFloat(json.temp); + command: ["curl", "ipinfo.io"] + stdout: StdioCollector { + onStreamFinished: root.loc = JSON.parse(text).loc + } + } + + Process { + id: wttrProc + + command: ["curl", `https://wttr.in/${root.loc}?format=j1`] + stdout: StdioCollector { + onStreamFinished: { + const json = JSON.parse(text).current_condition[0]; + root.icon = Icons.getWeatherIcon(json.weatherCode); + root.description = json.weatherDesc[0].value; + root.temperature = parseFloat(json.temp_C); } } } diff --git a/widgets/CachingImage.qml b/widgets/CachingImage.qml index 3382615..8f7f711 100644 --- a/widgets/CachingImage.qml +++ b/widgets/CachingImage.qml @@ -43,8 +43,8 @@ Image { property string path command: ["sha256sum", path] - stdout: SplitParser { - onRead: data => root.hash = data.split(" ")[0] + stdout: StdioCollector { + onStreamFinished: root.hash = text.split(" ")[0] } } } -- cgit v1.2.3-freya