diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2025-07-12 18:15:32 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2025-07-12 18:15:32 +0800 |
| commit | 299bf0bae8be6ab505f97d6aba4f82e4e6f96f62 (patch) | |
| tree | e1a72a68b5c2fae7fab097a80462233878a3231c | |
| parent | ce5f49a9324bceccab821c1d2ebf3f72bdfc09ea (diff) | |
lot of minor improvement added utils and working networkusage
| -rw-r--r-- | bar/ActiveWindow.qml | 30 | ||||
| -rw-r--r-- | bar/Bar.qml | 24 | ||||
| -rw-r--r-- | bar/Cpu.qml | 27 | ||||
| -rw-r--r-- | bar/Gpu.qml | 27 | ||||
| -rw-r--r-- | bar/Memory.qml | 27 | ||||
| -rw-r--r-- | bar/Mpris.qml | 4 | ||||
| -rw-r--r-- | bar/Network.qml | 34 | ||||
| -rw-r--r-- | bar/Temp.qml | 27 | ||||
| -rw-r--r-- | config/Bar.qml | 5 | ||||
| -rw-r--r-- | config/General.qml | 6 | ||||
| -rw-r--r-- | config/Icons.qml | 34 | ||||
| -rw-r--r-- | services/Mpris.qml (renamed from services/SMpris.qml) | 2 | ||||
| -rw-r--r-- | services/Network.qml | 86 | ||||
| -rw-r--r-- | services/Resource.qml | 105 | ||||
| -rw-r--r-- | shell.qml | 5 | ||||
| -rw-r--r-- | utils/CircularProgress.qml | 91 | ||||
| -rw-r--r-- | utils/Icons.qml | 34 | ||||
| -rw-r--r-- | utils/NetworkUsage.qml | 66 | ||||
| -rw-r--r-- | utils/Resource.qml | 59 | ||||
| -rw-r--r-- | utils/networkUsage.js | 41 |
20 files changed, 716 insertions, 18 deletions
diff --git a/bar/ActiveWindow.qml b/bar/ActiveWindow.qml index efdbfd9..4cb1a08 100644 --- a/bar/ActiveWindow.qml +++ b/bar/ActiveWindow.qml @@ -1,23 +1,35 @@ import QtQuick +import QtQuick.Layouts import Quickshell import Quickshell.Wayland import Quickshell.Widgets import qs.utils +import qs.config Item { id: root property var toplevel: ToplevelManager.activeToplevel property var icon: Quickshell.iconPath(AppSearch.guessIcon(toplevel?.appId), "image-missing") + anchors.verticalCenter: parent.verticalCenter - IconImage { - source: root.icon - implicitWidth: 32 - implicitHeight: 32 - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: 8 + Rectangle { + anchors.fill: parent + color: "blue" } - Text { - text: root.toplevel?.title + RowLayout { + anchors.fill: parent + + anchors.verticalCenter: parent.verticalCenter + IconImage { + source: root.icon + implicitWidth: Bar.appIconSize + implicitHeight: Bar.appIconSize + Layout.fillHeight: true + Layout.fillWidth: false + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + } + Text { + text: root.toplevel?.title + } } } diff --git a/bar/Bar.qml b/bar/Bar.qml index f8d3a2d..24fc0ce 100644 --- a/bar/Bar.qml +++ b/bar/Bar.qml @@ -65,7 +65,7 @@ Scope { Layout.preferredWidth: 1 } } - RowLayout { + Item { id: centerRow Layout.fillWidth: true Layout.fillHeight: true @@ -76,11 +76,25 @@ Scope { id: rightRow Layout.fillWidth: true Layout.preferredWidth: 1 - Rectangle { + Cpu { + Layout.fillHeight: true + Layout.fillWidth: true + } + Memory { + Layout.fillHeight: true + Layout.fillWidth: true + } + Gpu { + Layout.fillHeight: true + Layout.fillWidth: true + } + Temp { + Layout.fillHeight: true + Layout.fillWidth: true + } + Network { + Layout.fillHeight: true Layout.fillWidth: true - Text { - text: "a" - } } } } diff --git a/bar/Cpu.qml b/bar/Cpu.qml new file mode 100644 index 0000000..5d65415 --- /dev/null +++ b/bar/Cpu.qml @@ -0,0 +1,27 @@ +import QtQuick +import QtQuick.Layouts + +import qs.services as Services +import qs.utils +import qs.config as Config + +Item { + id: root + RowLayout { + Item { + Layout.fillWidth: true + } + anchors.fill: parent + spacing: Config.Bar.resourceIconTextSpacing + + Icons { + text: Config.Icons.resource.cpu + } + Text { + text: Math.round(Services.Resource.cpuUsage * 100) + } + Item { + Layout.fillWidth: true + } + } +} diff --git a/bar/Gpu.qml b/bar/Gpu.qml new file mode 100644 index 0000000..e47d8f8 --- /dev/null +++ b/bar/Gpu.qml @@ -0,0 +1,27 @@ +import QtQuick +import QtQuick.Layouts + +import qs.services as Services +import qs.utils +import qs.config as Config + +Item { + id: root + RowLayout { + Item { + Layout.fillWidth: true + } + anchors.fill: parent + spacing: Config.Bar.resourceIconTextSpacing + + Icons { + text: Config.Icons.resource.gpu + } + Text { + text: Services.Resource.gpuUsage + } + Item { + Layout.fillWidth: true + } + } +} diff --git a/bar/Memory.qml b/bar/Memory.qml new file mode 100644 index 0000000..130c9ea --- /dev/null +++ b/bar/Memory.qml @@ -0,0 +1,27 @@ +import QtQuick +import QtQuick.Layouts + +import qs.services as Services +import qs.utils +import qs.config as Config + +Item { + id: root + RowLayout { + Item { + Layout.fillWidth: true + } + anchors.fill: parent + spacing: Config.Bar.resourceIconTextSpacing + + Icons { + text: Config.Icons.resource.memory + } + Text { + text: Math.round(Services.Resource.memoryUsedPercentage * 100) + "%" + } + Item { + Layout.fillWidth: true + } + } +} diff --git a/bar/Mpris.qml b/bar/Mpris.qml index 87bef93..8d56335 100644 --- a/bar/Mpris.qml +++ b/bar/Mpris.qml @@ -2,7 +2,7 @@ import QtQuick.Layouts import QtQuick import Quickshell import Quickshell.Services.Mpris -import qs.services +import qs.services as Services import qs.config // import Quickshell.Utils @@ -10,7 +10,7 @@ import qs.config Item { id: root height: Bar.height - readonly property MprisPlayer activePlayer: SMpris.activePlayer + readonly property MprisPlayer activePlayer: Services.Mpris.activePlayer readonly property string cleanedTitle: cleanMusicTitle(activePlayer?.trackTitle) || qsTr("No media") // length broken (https://github.com/quickshell-mirror/quickshell/issues/109) readonly property string position: friendlyTimeForSeconds(activePlayer?.position) || "0:00" diff --git a/bar/Network.qml b/bar/Network.qml new file mode 100644 index 0000000..df70ecd --- /dev/null +++ b/bar/Network.qml @@ -0,0 +1,34 @@ +import QtQuick +import QtQuick.Layouts + +import qs.services as Services +import qs.utils +import qs.config as Config + +Item { + id: root + + RowLayout { + Item { + Layout.fillWidth: true + } + anchors.fill: parent + spacing: Config.Bar.resourceIconTextSpacing + + Icons { + text: Services.Network.state + } + Text { + text: Math.round(Services.Resource.cpuUsage * 100) + } + Text { + text: NetworkUsage.up + " " + NetworkUsage.upUnit + } + Text { + text: NetworkUsage.down + " " + NetworkUsage.downUnit + } + Item { + Layout.fillWidth: true + } + } +} diff --git a/bar/Temp.qml b/bar/Temp.qml new file mode 100644 index 0000000..c2c5f1d --- /dev/null +++ b/bar/Temp.qml @@ -0,0 +1,27 @@ +import QtQuick +import QtQuick.Layouts + +import qs.services as Services +import qs.utils +import qs.config as Config + +Item { + id: root + RowLayout { + Item { + Layout.fillWidth: true + } + anchors.fill: parent + spacing: Config.Bar.resourceIconTextSpacing + + Icons { + text: Config.Icons.resource.temp + } + Text { + text: Math.round(Services.Resource.cpuTemp * 100) + } + Item { + Layout.fillWidth: true + } + } +} diff --git a/config/Bar.qml b/config/Bar.qml index 5a5be3a..8e14851 100644 --- a/config/Bar.qml +++ b/config/Bar.qml @@ -11,5 +11,10 @@ Singleton { readonly property int leftMargin: Style.spacing.larger readonly property int rightMargin: Style.spacing.larger + readonly property int workspaceIconSize: 32 readonly property int workspaces: 10 + + readonly property int appIconSize: 32 + + readonly property int resourceIconTextSpacing: 8 } diff --git a/config/General.qml b/config/General.qml index 80df622..2b71635 100644 --- a/config/General.qml +++ b/config/General.qml @@ -6,4 +6,10 @@ Singleton { id: root readonly property bool sloopySearch: false + + readonly property int resourceUpdateInterval: 1 + + readonly property real iconSize: Style.font.size.small ?? 16 + + readonly property string cpuThermalPath: "/sys/class/thermal/thermal_zone0/temp" } diff --git a/config/Icons.qml b/config/Icons.qml index ec52e80..13deee9 100644 --- a/config/Icons.qml +++ b/config/Icons.qml @@ -29,4 +29,38 @@ Singleton { mute: "", unmute: "" }) + + readonly property var wifi: ({ + min: "", + low: "", + mid: "", + high: "", + max: "" + }) + + readonly property var network: ({ + disconnected: "", + download: "", + upload: "", + wifi: wifi + }) + + readonly property var resource: ({ + cpu: "", + memory: "", + disk: "", + gpu: "", + temp: "", + network: network, + clock: "", + calendar: "" + }) + + readonly property var power: ({ + shutdown: "", + dpms: "", + lock: "", + suspend: "", + restart: "" + }) } diff --git a/services/SMpris.qml b/services/Mpris.qml index 012a354..9618459 100644 --- a/services/SMpris.qml +++ b/services/Mpris.qml @@ -1,5 +1,5 @@ pragma Singleton -pragma ComponentBehavior: Bound +// pragma ComponentBehavior: Bound // From https://git.outfoxxed.me/outfoxxed/nixnew // It does not have a license, but the author is okay with redistribution. diff --git a/services/Network.qml b/services/Network.qml new file mode 100644 index 0000000..52f15fa --- /dev/null +++ b/services/Network.qml @@ -0,0 +1,86 @@ +pragma Singleton +// pragma ComponentBehavior: Bound + +import Quickshell +import Quickshell.Io +import QtQuick + +import qs.config + +Singleton { + id: root + + property bool wifi: true + property bool ethernet: false + property string networkName: "" + property int networkStrength + // property string materialSymbol: ethernet ? "lan" : (Network.networkName.length > 0 && Network.networkName != "lo") ? (Network.networkStrength > 80 ? "signal_wifi_4_bar" : Network.networkStrength > 60 ? "network_wifi_3_bar" : Network.networkStrength > 40 ? "network_wifi_2_bar" : Network.networkStrength > 20 ? "network_wifi_1_bar" : "signal_wifi_0_bar") : "signal_wifi_off" + property string state: ethernet ? Icons.resource.network.wifi : (networkName.length >= 0 && networkName !== "lo") ? (networkStrength >= 90 ? Icons.resource.network.wifi.max : networkStrength >= 80 ? Icons.resource.network.wifi.high : networkStrength >= 60 ? Icons.resource.network.wifi.mid : networkStrength >= 40 ? Icons.resource.network.wifi.low : networkStrength >= 20 ? Icons.resource.network.wifi.min : Icons.resource.network.disconnected) : Icons.resource.network.disconnected + + function update() { + updateConnectionType.startCheck(); + updateNetworkName.running = true; + updateNetworkStrength.running = true; + } + + Timer { + interval: General.resourceUpdateInterval + running: true + repeat: true + onTriggered: { + root.update(); + interval = General.resourceUpdateInterval * 1000; + } + } + + Process { + id: updateConnectionType + property string buffer + command: ["nmcli", "-t", "-f", "NAME,TYPE,DEVICE", "c", "show", "--active"] + running: true + function startCheck() { + buffer = ""; + updateConnectionType.running = true; + } + stdout: SplitParser { + onRead: data => { + updateConnectionType.buffer += data + "\n"; + } + } + onExited: (exitCode, exitStatus) => { + const lines = updateConnectionType.buffer.trim().split('\n'); + let hasEthernet = false; + let hasWifi = false; + lines.forEach(line => { + if (line.includes("ethernet")) + hasEthernet = true; + else if (line.includes("wireless")) + hasWifi = true; + }); + root.ethernet = hasEthernet; + root.wifi = hasWifi; + } + } + + Process { + id: updateNetworkName + command: ["sh", "-c", "nmcli -t -f NAME c show --active | head -1"] + running: true + stdout: SplitParser { + onRead: data => { + root.networkName = data; + } + } + } + + Process { + id: updateNetworkStrength + running: true + command: ["sh", "-c", "nmcli -f IN-USE,SIGNAL,SSID device wifi | awk '/^\*/{if (NR!=1) {print $2}}'"] + stdout: SplitParser { + onRead: data => { + root.networkStrength = parseInt(data); + } + } + } +} diff --git a/services/Resource.qml b/services/Resource.qml new file mode 100644 index 0000000..b45538b --- /dev/null +++ b/services/Resource.qml @@ -0,0 +1,105 @@ +pragma Singleton +// pragma ComponentBehavior: Bound + +import QtQuick +import Quickshell +import Quickshell.Io +import qs.config + +Singleton { + id: root + + property double memoryTotal: 1 + property double memoryFree: 1 + property double memoryUsed: memoryTotal - memoryFree + property double memoryUsedPercentage: memoryUsed / memoryTotal + property double swapTotal: 1 + property double swapFree: 1 + property double swapUsed: swapTotal - swapFree + property double swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0 + + property double cpuUsage: 0 + property double cpuTemp: 0 + property var previousCpuStats + + property int gpuUsage: 0 + property int gpuTemp: 0 + + Timer { + interval: General.resourceUpdateInterval + running: true + repeat: true + onTriggered: { + fileMeminfo.reload(); + fileStat.reload(); + fileCpuThermal.reload(); + + const textMeminfo = fileMeminfo.text(); + memoryTotal = Number(textMeminfo.match(/MemTotal: *(\d+)/)[1] ?? 1); + memoryFree = Number(textMeminfo.match(/MemAvailable: *(\d+)/)[1] ?? 0); + memoryUsed = memoryTotal - memoryFree; + memoryUsedPercentage = memoryUsed / memoryTotal; + swapTotal = Number(textMeminfo.match(/SwapTotal: *(\d+)/)[1] ?? 1); + swapFree = Number(textMeminfo.match(/SwapFree: *(\d+)/)[1] ?? 0); + + const textStat = fileStat.text(); + const cpuLine = textStat.match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/); + if (cpuLine) { + const stats = cpuLine.slice(1).map(Number); + const total = stats.reduce((a, b) => a + b, 0); + const idle = stats[3]; + + if (root.previousCpuStats) { + const totalDiff = total - root.previousCpuStats.total; + const idleDiff = idle - root.previousCpuStats.idle; + cpuUsage = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0; + } + + root.previousCpuStats = { + total, + idle + }; + } + const cpuTempText = fileCpuThermal.text(); + cpuTemp = cpuTempText ? parseInt(cpuTempText.trim()) / 100000 : 0; + + gpuUsageProcess.running = true; + gpuTempProcess.running = true; + interval = General.resourceUpdateInterval * 1000; + } + } + + FileView { + id: fileMeminfo + path: "/proc/meminfo" + } + FileView { + id: fileStat + path: "/proc/stat" + } + FileView { + id: fileCpuThermal + path: General.cpuThermalPath + } + // nvidia-smi may be dead if firmware update so ABI break etc. + Process { + id: gpuUsageProcess + running: true + command: ["nvidia-smi", "--query-gpu=utilization.gpu", "--format=csv,noheader,nounits"] + stdout: StdioCollector { + onStreamFinished: { + root.gpuUsage = parseInt(this.text.trim()); + } + } + } + Process { + id: gpuTempProcess + running: true + command: ["nvidia-smi", "--query-gpu=temperature.gpu", "--format=csv,noheader,nounits"] + stdout: StdioCollector { + onStreamFinished: { + root.gpuTemp = parseInt(this.text.trim()); + } + } + } +} @@ -1,6 +1,9 @@ import Quickshell import "bar" +import QtQuick +import QtQuick.Layouts + Scope { - Bar {} + Bar {} } diff --git a/utils/CircularProgress.qml b/utils/CircularProgress.qml new file mode 100644 index 0000000..240e520 --- /dev/null +++ b/utils/CircularProgress.qml @@ -0,0 +1,91 @@ +// From https://github.com/rafzby/circular-progressbar with modifications +// License: LGPL-3.0 - A copy can be found in `licenses` folder of repo + +import QtQuick + +/** + * Material 3 circular progress. See https://m3.material.io/components/progress-indicators/specs + */ +Item { + id: root + + property int size: 30 + property int lineWidth: 2 + property real value: 0 + // property color primaryColor: Appearance.m3colors.m3onSecondaryContainer + // property color secondaryColor: Appearance.colors.colSecondaryContainer + property real gapAngle: Math.PI / 9 + property bool fill: false + property int fillOverflow: 2 + property int animationDuration: 1000 + property var easingType: Easing.OutCubic + + width: size + height: size + + signal animationFinished + + onValueChanged: { + canvas.degree = value * 360; + } + // onPrimaryColorChanged: { + // canvas.requestPaint(); + // } + // onSecondaryColorChanged: { + // canvas.requestPaint(); + // } + + Canvas { + id: canvas + + property real degree: 0 + + anchors.fill: parent + antialiasing: true + + onDegreeChanged: { + requestPaint(); + } + + onPaint: { + var ctx = getContext("2d"); + var x = root.width / 2; + var y = root.height / 2; + var radius = root.size / 2 - root.lineWidth; + var startAngle = (Math.PI / 180) * 270; + var fullAngle = (Math.PI / 180) * (270 + 360); + var progressAngle = (Math.PI / 180) * (270 + degree); + var epsilon = 0.01; // Small angle in radians + + ctx.reset(); + if (root.fill) { + // ctx.fillStyle = root.secondaryColor; + ctx.beginPath(); + ctx.arc(x, y, radius + fillOverflow, startAngle, fullAngle); + ctx.fill(); + } + ctx.lineCap = 'round'; + ctx.lineWidth = root.lineWidth; + + // Secondary + ctx.beginPath(); + ctx.arc(x, y, radius, progressAngle + gapAngle, fullAngle - gapAngle); + // ctx.strokeStyle = root.secondaryColor; + ctx.stroke(); + + // Primary (value indication) + var endAngle = progressAngle + (value > 0 ? 0 : epsilon); + ctx.beginPath(); + ctx.arc(x, y, radius, startAngle, endAngle); + // ctx.strokeStyle = root.primaryColor; + ctx.stroke(); + } + + Behavior on degree { + NumberAnimation { + duration: root.animationDuration + easing.type: root.easingType + } + } + } +} diff --git a/utils/Icons.qml b/utils/Icons.qml new file mode 100644 index 0000000..4b3915c --- /dev/null +++ b/utils/Icons.qml @@ -0,0 +1,34 @@ +import QtQuick +import QtQuick.Layouts + +import qs.config + +Text { + id: root + property real iconSize: General.iconSize + property real fill: 0 + property real truncatedFill: Math.round(fill * 100) / 100 // Reduce memory consumption spikes from constant font remapping + renderType: Text.NativeRendering + font { + hintingPreference: Font.PreferFullHinting + family: Style.font.family.symbols ?? "Symbols Nerd Font" + pixelSize: iconSize + weight: Font.Normal + (Font.DemiBold - Font.Normal) * fill + variableAxes: { + "FILL": truncatedFill, + // "wght": font.weight, + // "GRAD": 0, + "opsz": iconSize + } + } + verticalAlignment: Text.AlignVCenter + // color: Style.m3colors.m3onBackground + + // Behavior on fill { + // NumberAnimation { + // duration: Appearance?.animation.elementMoveFast.duration ?? 200 + // easing.type: Appearance?.animation.elementMoveFast.type ?? Easing.BezierSpline + // easing.bezierCurve: Appearance?.animation.elementMoveFast.bezierCurve ?? [0.34, 0.80, 0.34, 1.00, 1, 1] + // } + // } +} diff --git a/utils/NetworkUsage.qml b/utils/NetworkUsage.qml new file mode 100644 index 0000000..f71e3b7 --- /dev/null +++ b/utils/NetworkUsage.qml @@ -0,0 +1,66 @@ +pragma Singleton +// pragma ComponentBehavior: Bound + +import Quickshell +import Quickshell.Io +import QtQuick +import "networkUsage.js" as Network +import qs.config + +Singleton { + property int up: 0 + property string upUnit: "" + property int down: 0 + property string downUnit: "" + + property string __iface: "" + property var __prev: ({ + rx: 0, + tx: 0 + }) + + Timer { + interval: General.resourceUpdateInterval * 1000 + running: true + repeat: true + onTriggered: update() + } + + function update() { + __iface = Network.defaultInterface(getRouteProcess.stdout.text); + const cur = Network.getRxTxBytes(getNetDevProcess.stdout.text, __iface); + const sec = General.resourceUpdateInterval; + const rawUp = (cur.tx - __prev.tx) / sec; + const rawDown = (cur.rx - __prev.rx) / sec; + + up = Network.formatSize(rawUp, false); + upUnit = Network.formatSize(rawUp, true); + down = Network.formatSize(rawDown, false); + downUnit = Network.formatSize(rawDown, true); + + __prev = cur; + getRouteProcess.running = true; + getNetDevProcess.running = true; + } + + Process { + id: getRouteProcess + running: true + command: ["ip", "route"] + stdout: StdioCollector {} + } + + Process { + id: getNetDevProcess + running: true + command: ["cat", "/proc/net/dev"] + stdout: StdioCollector {} + } + + Component.onCompleted: { + getRouteProcess.running = true; + getNetDevProcess.running = true; + __iface = Network.defaultInterface(getRouteProcess.stdout.text); + __prev = Network.getRxTxBytes(getNetDevProcess.stdout.text, __iface); + } +} diff --git a/utils/Resource.qml b/utils/Resource.qml new file mode 100644 index 0000000..2048e98 --- /dev/null +++ b/utils/Resource.qml @@ -0,0 +1,59 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Io + +import qs.config + +Item { + id: root + + required property string iconName + required property double percentage + property bool shown: true + clip: true + visible: width > 0 && height > 0 + implicitWidth: resourceRowLayout.x < 0 ? 0 : childrenRect.width + implicitHeight: childrenRect.height + + RowLayout { + id: resourceRowLayout + spacing: 4 + x: root.shown ? 0 : -resourceRowLayout.width + + CircularProgress { + Layout.alignment: Qt.AlignVCenter + lineWidth: 2 + value: root.percentage + size: 26 + // secondaryColor: Appearance.colors.colSecondaryContainer + // primaryColor: Appearance.m3colors.m3onSecondaryContainer + + MaterialSymbol { + anchors.centerIn: parent + fill: 1 + text: iconName + // iconSize: Appearance.font.pixelSize.normal + // color: Appearance.m3colors.m3onSecondaryContainer + } + } + + Text { + Layout.alignment: Qt.AlignVCenter + // color: Appearance.colors.colOnLayer1 + text: `${Math.round(percentage * 100)}` + } + + Behavior on x { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + } + + Behavior on implicitWidth { + NumberAnimation { + duration: Appearance.animation.elementMove.duration + easing.type: Appearance.animation.elementMove.type + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + } + } +} diff --git a/utils/networkUsage.js b/utils/networkUsage.js new file mode 100644 index 0000000..a7131f2 --- /dev/null +++ b/utils/networkUsage.js @@ -0,0 +1,41 @@ +function defaultInterface(route) { + var lines = route.split(/\r?\n/) + for (var i = 0; i < lines.length; i++) { + var l = lines[i] + if (l.indexOf("default via") === 0) return l.split(/\s+/)[4] + } + throw "No default interface" +} +function getRxTxBytes(dev, iface) { + var lines = dev.split(/\r?\n/) + for (var i = 0; i < lines.length; i++) { + var l = lines[i].trim() + if (!l.startsWith(iface + ":")) continue + var p = l.split(/\s+/) + return { rx: parseInt(p[1]), tx: parseInt(p[9]) } + } + throw "Interface not found" +} +function formatSize(size, unit) { + const labels = ["B", "K", "M", "G", "T"] + let power = 0, kilo = 2 ** 10 + while (size > kilo && power < labels.length - 1) { + size /= kilo; power++ + } + if (unit) { + return labels[power] + } else { + return size.toFixed(0) + } +} + +// const iface = defaultInterface() +// let prev = getRxTxBytes(iface) + +// setInterval(() => { +// const cur = getRxTxBytes(iface) +// const dr = (cur.rx - prev.rx) / (interval / 1000) +// const dt = (cur.tx - prev.tx) / (interval / 1000) +// prev = cur +// console.log(formatSize(dr), formatSize(dt)) +// }, interval) |
