diff --git a/README.md b/README.md new file mode 100644 index 0000000..d80f912 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +Net Speed +========= + +Show current net speed on panel. +-------------------------------- + +This extension is inspired by which has some annoying flickers existing for a long time so I decide to create my own alternative. I removed all features that I won't use. + +It only shows text like `↓ 777 K/s ↑ 2.33 K/s` on the right part of panel. No font size changing, no total downloaded bytes, no bits. diff --git a/extension.js b/extension.js new file mode 100644 index 0000000..2c95df7 --- /dev/null +++ b/extension.js @@ -0,0 +1,211 @@ +/* extension.js + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* exported init */ + +// const GETTEXT_DOMAIN = "net-speed"; + +const {GObject, GLib, Gio, Clutter, St} = imports.gi; + +// const Gettext = imports.gettext.domain(GETTEXT_DOMAIN); +// const _ = Gettext.gettext; + +const ByteArray = imports.byteArray; +const ExtensionUtils = imports.misc.extensionUtils; +const Main = imports.ui.main; +const PanelMenu = imports.ui.panelMenu; +// const PopupMenu = imports.ui.popupMenu; + +const refreshInterval = 3; +const speedUnits = [ + "B/s", "K/s", "M/s", "G/s", "T/s", "P/s", "E/s", "Z/s", "Y/s" +]; +let lastTotalDownBytes = 0; +let lastTotalUpBytes = 0; + +const getCurrentNetSpeed = (refreshInterval) => { + const speed = {"down": 0, "up": 0}; + + try { + const inputFile = Gio.File.new_for_path("/proc/net/dev"); + const fileInputStream = inputFile.read(null); + // See . + // If we want new operator, we need to pass params in object. + // Short param is only used for static constructor. + const dataInputStream = new Gio.DataInputStream({ + "base_stream": fileInputStream + }); + + // Caculate the sum of all interfaces' traffic line by line. + let totalDownBytes = 0; + let totalUpBytes = 0; + let line = null; + // See . + while ((line = dataInputStream.read_line(null)) != null) { + // See . + // It seems Uint8Array is only returned at the first time. + if (line instanceof Uint8Array) { + line = ByteArray.toString(line).trim(); + } else { + line = line.toString().trim(); + } + const fields = line.split(/\W+/); + if (fields.length <= 2) { + break; + } + + // Skip virtual interfaces. + const interface = fields[0]; + const currentInterfaceDownBytes = Number.parseInt(fields[1]); + const currentInterfaceUpBytes = Number.parseInt(fields[9]); + if (interface == "lo" || + // Created by python-based bandwidth manager "traffictoll". + interface.match(/^ifb[0-9]+/) || + // Created by lxd container manager. + interface.match(/^lxdbr[0-9]+/) || + interface.match(/^virbr[0-9]+/) || + interface.match(/^br[0-9]+/) || + interface.match(/^vnet[0-9]+/) || + interface.match(/^tun[0-9]+/) || + interface.match(/^tap[0-9]+/) || + isNaN(currentInterfaceDownBytes) || + isNaN(currentInterfaceUpBytes)) { + continue; + } + + totalDownBytes += currentInterfaceDownBytes; + totalUpBytes += currentInterfaceUpBytes; + } + + fileInputStream.close(null); + + if (lastTotalDownBytes === 0) { + lastTotalDownBytes = totalDownBytes; + } + if (lastTotalUpBytes === 0) { + lastTotalUpBytes = totalUpBytes; + } + + speed["down"] = (totalDownBytes - lastTotalDownBytes) / refreshInterval; + speed["up"] = (totalUpBytes - lastTotalUpBytes) / refreshInterval; + + lastTotalDownBytes = totalDownBytes; + lastTotalUpBytes = totalUpBytes; + } catch (e) { + logError(e); + } + + return speed; +}; + +const formatSpeedWithUnit = (amount) => { + let unitIndex = 0; + while (amount >= 1000 && unitIndex < speedUnits.length - 1) { + amount /= 1000; + ++unitIndex; + } + + let digits = 0; + // Instead of showing 0.00123456 as 0.00, show it as 0. + if (amount >= 100 || amount - 0 < 0.01) { + // 100 M/s, 200 K/s, 300 B/s. + digits = 0; + } else if (amount >= 10) { + // 10.1 M/s, 20.2 K/s, 30.3 B/s. + digits = 1; + } else { + // 1.01 M/s, 2.02 K/s, 3.03 B/s. + digits = 2; + } + + // See . + return `${amount.toFixed(digits)} ${speedUnits[unitIndex]}`; +}; + +const toSpeedString = (speed) => { + return `↓ ${formatSpeedWithUnit(speed["down"])} ↑ ${formatSpeedWithUnit(speed["up"])}`; +}; + +const Indicator = GObject.registerClass( +class Indicator extends PanelMenu.Button { + _init() { + // menuAlignment, nameText, dontCreateMenu. + super._init(0.0, "Net Speed", true); + + this._label = new St.Label({ + "y_align": Clutter.ActorAlign.CENTER, + "text": "---" + }); + + this.add_child(this._label); + + // let item = new PopupMenu.PopupMenuItem(_("Show Notification")); + // item.connect("activate", () => { + // Main.notify(_("Whatʼs up, folks?")); + // }); + // this.menu.addMenuItem(item); + } + + setText(text) { + return this._label.set_text(text); + } +}); + +class Extension { + constructor(uuid) { + this._uuid = uuid; + + // ExtensionUtils.initTranslations(GETTEXT_DOMAIN); + } + + enable() { + lastTotalDownBytes = 0; + lastTotalUpBytes = 0; + + this._indicator = new Indicator(); + // role, indicator, position, box. + // -1 is not OK, because it will show in the right side of system menu. + Main.panel.addToStatusArea(this._uuid, this._indicator, 0, "right"); + + this._timeout = GLib.timeout_add_seconds( + GLib.PRIORITY_DEFAULT, refreshInterval, () => { + const speed = getCurrentNetSpeed(refreshInterval); + const text = toSpeedString(speed); + // log(text); + this._indicator.setText(text); + // Run as loop, not once. + return GLib.SOURCE_CONTINUE; + } + ) + } + + disable() { + if (this._indicator != null) { + this._indicator.destroy(); + this._indicator = null; + } + if (this._timeout != null) { + GLib.source_remove(this._timeout); + this._timeout = null; + } + } +} + +function init(meta) { + return new Extension(meta.uuid); +} diff --git a/metadata.json b/metadata.json new file mode 100644 index 0000000..052b081 --- /dev/null +++ b/metadata.json @@ -0,0 +1,9 @@ +{ + "name": "Net Speed", + "description": "Show current net speed on panel.", + "uuid": "netspeed@alynx.one", + "version": 1, + "shell-version": [ + "40" + ] +} diff --git a/stylesheet.css b/stylesheet.css new file mode 100644 index 0000000..37b93f2 --- /dev/null +++ b/stylesheet.css @@ -0,0 +1 @@ +/* Add your custom extension styling here */