├── NetMonitor.png
├── NetMonitor@zdyb.tk
├── extension.js
├── indicator.js
├── metadata.json
├── netinterface.js
└── stylesheet.css
├── NetMonitorMenu.png
├── README.rst
└── org.gnome.shell.extensions.net-monitor.gschema.xml
/NetMonitor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azdyb/gnome-shell-extension-netmonitor/2367b6c68ca3b195d4e87143292c11b5a483095f/NetMonitor.png
--------------------------------------------------------------------------------
/NetMonitor@zdyb.tk/extension.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Aleksander Zdyb
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | const St = imports.gi.St;
19 | const Mainloop = imports.mainloop;
20 | const Main = imports.ui.main;
21 | const Lang = imports.lang;
22 | const Gio = imports.gi.Gio;
23 | const PopupMenu = imports.ui.popupMenu;
24 | const PanelMenu = imports.ui.panelMenu;
25 | const Gettext = imports.gettext;
26 | const Clutter = imports.gi.Clutter;
27 | const NMClient = imports.gi.NMClient;
28 |
29 |
30 | const NetMonitor = imports.ui.extensionSystem.extensions["NetMonitor@zdyb.tk"];
31 | const SpeedIndicator = NetMonitor.indicator.SpeedIndicator;
32 | const NetInterface = NetMonitor.netinterface.NetInterface;
33 |
34 |
35 | const _ = Gettext.gettext;
36 |
37 | const UPDATE_INTERVAL = 2;
38 | const GSETTINGS_SCHEMA = "org.gnome.shell.extensions.net-monitor";
39 | const HIDDEN_INTERFACES_SETTING = "hidden-interfaces";
40 |
41 |
42 | function NetSpeed(extensionMeta) {
43 | this._init.apply(this, [extensionMeta]);
44 | }
45 |
46 | NetSpeed.prototype = {
47 | __proto__: PanelMenu.Button.prototype,
48 |
49 | run: false,
50 | indicators: [],
51 | nmclient: null,
52 |
53 | _init: function(extensionMeta) {
54 | PanelMenu.Button.prototype._init.call(this, 0.0);
55 |
56 | this.extensionMeta = extensionMeta;
57 | this.settings = new Gio.Settings({ schema: GSETTINGS_SCHEMA });
58 |
59 | this.build_ui();
60 |
61 | this.nmclient = NMClient.Client.new();
62 | for each (let dev in this.nmclient.get_devices()) {
63 | this.device_added(this.nmclient, dev);
64 | }
65 | this.nmclient.connect("device-added", Lang.bind(this, this.device_added));
66 | this.nmclient.connect("device-removed", Lang.bind(this, this.device_removed));
67 |
68 | this.indicator_visibility_changed();
69 | },
70 |
71 | build_ui: function() {
72 | this.icon = new St.Icon({
73 | icon_type: St.IconType.SYMBOLIC,
74 | style_class: "popup-menu-icon",
75 | icon_name: "network-offline"
76 | });
77 |
78 | this.main_box = new St.BoxLayout();
79 | this.main_box.add_actor(this.icon);
80 |
81 | this.actor.add_actor(this.main_box);
82 |
83 | this.menu_section_interfaces = new PopupMenu.PopupMenuSection(_("Show interfaces"));
84 |
85 | let title = new PopupMenu.PopupMenuItem(_("Show interfaces when connected"), { reactive: false, style_class: "section-title" });
86 | this.menu_section_interfaces.addMenuItem(title);
87 | this.menu.addMenuItem(this.menu_section_interfaces);
88 |
89 | this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
90 |
91 | this.menu_section_settings = new PopupMenu.PopupMenuSection("Settings");
92 |
93 | this.menu_section_settings.addSettingsAction(_("Network Settings"), "gnome-network-panel.desktop");
94 | this.menu.addMenuItem(this.menu_section_settings);
95 | },
96 |
97 | /// Fired every UPDATE_INTERVAL seconds
98 | on_timeout: function() {
99 | for each (let ind in this.indicators) {
100 | ind.update();
101 | }
102 | return this.run;
103 | },
104 |
105 | enable: function() {
106 | this.run = true;
107 | this.on_timeout();
108 | this._update_handler = Mainloop.timeout_add_seconds(UPDATE_INTERVAL, Lang.bind(this, this.on_timeout));
109 | Main.panel._rightBox.insert_actor(this.actor, 0);
110 | Main.panel._menus.addMenu(this.menu)
111 | },
112 |
113 | disable: function() {
114 | this.run = false;
115 | Mainloop.source_remove(this._update_handler);
116 | Main.panel._rightBox.remove_actor(this.actor);
117 | Main.panel._menus.removeMenu(this.menu)
118 | },
119 |
120 | device_added: function(sender, dev) {
121 | let ni = new NetInterface(this.extensionMeta, dev);
122 | let ind = new SpeedIndicator(this.extensionMeta, ni);
123 |
124 | ind.actor.connect("show", Lang.bind(this, this.indicator_visibility_changed, ind));
125 | ind.actor.connect("hide", Lang.bind(this, this.indicator_visibility_changed, ind));
126 | ind.menu.connect("toggled", Lang.bind(this, this.indicator_menu_toggled, ind));
127 |
128 | ind.set_hidden(this.settings.get_strv(HIDDEN_INTERFACES_SETTING).indexOf(ind.net_interface.get_ifname()) >= 0);
129 | this.main_box.add(ind.actor);
130 | this.menu_section_interfaces.addMenuItem(ind.menu);
131 | this.indicators.push(ind);
132 | },
133 |
134 | device_removed: function(sender, dev) {
135 | for (let i in this.indicators) {
136 | let ind = this.indicators[i];
137 | if (dev == ind.net_interface.nmdevice) {
138 | this.indicators.splice(i, 1);
139 | this.main_box.remove_actor(ind.actor);
140 | ind.destroy();
141 | break;
142 | }
143 | }
144 | },
145 |
146 | indicator_visibility_changed: function(indicator_actor, indicator) {
147 | for each(let ind in this.indicators) {
148 | if (ind.actor.visible) {
149 | this.icon.hide();
150 | return;
151 | }
152 | }
153 | this.icon.show();
154 | },
155 |
156 | indicator_menu_toggled: function(menu, state, indicator) {
157 | indicator.set_hidden(!state);
158 |
159 | let ifname = indicator.net_interface.get_ifname();
160 | let list = this.settings.get_strv(HIDDEN_INTERFACES_SETTING);
161 | let i = list.indexOf(ifname);
162 |
163 | if (!state && (i < 0)) {
164 | list.push(ifname);
165 | } else if (state && (i >= 0)) {
166 | list.splice(i, 1);
167 | }
168 |
169 | this.settings.set_strv(HIDDEN_INTERFACES_SETTING, list);
170 | }
171 | };
172 |
173 | function init(extensionMeta) {
174 | let userExtensionLocalePath = extensionMeta.path + '/locale';
175 | Gettext.bindtextdomain("NetMonitor", userExtensionLocalePath);
176 | Gettext.textdomain("NetMonitor");
177 |
178 | return new NetSpeed(extensionMeta);
179 | }
180 |
--------------------------------------------------------------------------------
/NetMonitor@zdyb.tk/indicator.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Aleksander Zdyb
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | const St = imports.gi.St;
19 | const Lang = imports.lang;
20 | const NetworkManager = imports.gi.NetworkManager;
21 | const Mainloop = imports.mainloop;
22 | const PopupMenu = imports.ui.popupMenu;
23 |
24 | function SpeedIndicator(extensionMeta, net_interface) {
25 | this._init.apply(this, [extensionMeta, net_interface]);
26 | };
27 |
28 | SpeedIndicator.prototype = {
29 | net_interface: null,
30 | is_hidden: false,
31 | actor: null,
32 | menu: null,
33 |
34 | _init: function(extensionMeta, net_interface) {
35 | this.net_interface = net_interface;
36 | this.build_ui();
37 | this.net_interface.nmdevice.connect("state-changed", Lang.bind(this, this.state_changed));
38 | },
39 |
40 | build_ui: function() {
41 | this.icon = new St.Icon({
42 | icon_type: St.IconType.SYMBOLIC,
43 | style_class: "popup-menu-icon",
44 | icon_name: this.get_icon_name()
45 | });
46 |
47 | this.actor = new St.BoxLayout( { style_class: "speed-indicator", visible: (!this.is_hidden) } );
48 | this.actor.add_actor(this.icon);
49 |
50 | this.label_in = new St.Label({ style_class: "bandwidth-label", text: "---" });
51 | this.label_out = new St.Label({ style_class: "bandwidth-label", text: "---" });
52 |
53 | this.actor.add_actor(this.label_in);
54 | this.actor.add_actor(this.label_out);
55 | this.actor.connect("parent-set", Lang.bind(this, this.parent_set));
56 |
57 | this.menu = new PopupMenu.PopupSwitchMenuItem(this.net_interface.get_ifname(), !this.is_hidden);
58 | },
59 |
60 | parent_set: function(sender, old_parent) {
61 | this.state_changed(this.net_interface.nmdevice, this.net_interface.get_state());
62 | },
63 |
64 | state_changed: function(sender, new_state, old_state, reason) {
65 | this.show();
66 | },
67 |
68 | update: function() {
69 | this.net_interface.update();
70 | let speeds = this.net_interface.get_formated_speeds();
71 | this.label_in.set_text(speeds[0]);
72 | this.label_out.set_text(speeds[1]);
73 | this.icon.icon_name = this.get_icon_name();
74 | },
75 |
76 | show: function(force) {
77 | if (force) this.is_hidden = false;
78 | if ( (this.is_hidden == false) && this.is_active()) this.actor.show();
79 | else this.actor.hide();
80 | this.update_menu();
81 | },
82 |
83 | hide: function() {
84 | this.actor.hide();
85 | this.update_menu();
86 | },
87 |
88 | set_hidden: function(hidden) {
89 | this.is_hidden = hidden;
90 | this.show();
91 | },
92 |
93 | is_active_for_state: function(state) {
94 | return (state == NetworkManager.DeviceState.ACTIVATED);
95 | },
96 |
97 | is_active: function() {
98 | return this.is_active_for_state(this.net_interface.get_state())
99 | },
100 |
101 | get_icon_name: function() {
102 | // TODO: Add 3G, Bluetooth and so on...
103 | let icon_name = "network-wired";
104 | let type = this.net_interface.get_type();
105 |
106 | if (type == NetworkManager.DeviceType.ETHERNET) {
107 | icon_name = "network-wired";
108 | } else if (type == NetworkManager.DeviceType.WIFI) {
109 | let ap = this.net_interface.get_active_access_point();
110 | if (ap) {
111 | let strength = ap.get_strength();
112 |
113 | if (strength > 80) strength = "excellent";
114 | else if (strength > 55) strength = "good";
115 | else if (strength > 30) strength = "ok";
116 | else if (strength > 5) strength = "weak";
117 | else strength = "none";
118 |
119 | icon_name = "network-wireless-signal-" + strength;
120 | } else {
121 | icon_name = "network-offline";
122 | }
123 | }
124 |
125 | return icon_name;
126 | },
127 |
128 | update_menu: function() {
129 | let active = this.is_active();
130 | let label = this.net_interface.get_ifname();
131 | if (!active) label += _(" (disconnected)");
132 |
133 | this.menu.actor.reactive = active;
134 | this.menu.label.set_text(label);
135 | this.menu.setToggleState(!this.is_hidden);
136 | },
137 |
138 | destroy: function() {
139 | this.actor.destroy();
140 | this.menu.destroy();
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/NetMonitor@zdyb.tk/metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "shell-version": ["3.2"],
3 | "uuid": "NetMonitor@zdyb.tk",
4 | "name": "NetMonitor",
5 | "description": "Shows current bandwidth on network interfaces.",
6 | "url": "http://github.com/ojo/gnome-shell-extension-netmonitor",
7 | "author": "Aleksander Zdyb"
8 | }
9 |
--------------------------------------------------------------------------------
/NetMonitor@zdyb.tk/netinterface.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2011 Aleksander Zdyb
3 | *
4 | * This program is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | const GLib = imports.gi.GLib;
19 | const NetworkManager = imports.gi.NetworkManager;
20 | const NMClient = imports.gi.NMClient;
21 | const GTop = imports.gi.GTop;
22 |
23 | function NetInterface(extensionMeta, nmdevice) {
24 | this._init.apply(this, [extensionMeta, nmdevice]);
25 | };
26 |
27 | NetInterface.prototype = {
28 | nmdevice: null,
29 | bytes_in: 0,
30 | bytes_out: 0,
31 | speed_in: 0,
32 | speed_out: 0,
33 | last_probe_time: 0,
34 |
35 | _init: function(extensionMeta, nmdevice) {
36 | this.nmdevice = nmdevice;
37 | },
38 |
39 | update: function() {
40 | let netload = new GTop.glibtop_netload();
41 | let probe_time = GLib.get_monotonic_time();
42 |
43 | GTop.glibtop_get_netload(netload, this.get_ifname());
44 |
45 | let bytes_in_delta = netload.bytes_in - this.bytes_in;
46 | let bytes_out_delta = netload.bytes_out - this.bytes_out;
47 |
48 | this.bytes_in = netload.bytes_in;
49 | this.bytes_out = netload.bytes_out;
50 |
51 | let time_interval = (probe_time - this.last_probe_time) / 1000000;
52 | this.last_probe_time = probe_time;
53 |
54 | this.speed_in = bytes_in_delta / time_interval;
55 | this.speed_out = bytes_out_delta / time_interval;
56 | },
57 |
58 | get_ifname: function() {
59 | return this.nmdevice.interface;
60 | },
61 |
62 | get_type: function() {
63 | return this.nmdevice.device_type;
64 | },
65 |
66 | get_state: function() {
67 | return this.nmdevice.get_state();
68 | },
69 |
70 | is_connected: function() {
71 | return (this.nmdevice.get_state() == NetworkManager.DeviceState.ACTIVATED);
72 | },
73 |
74 | get_formated_speeds: function() {
75 | return [this.format_string(this.speed_in), this.format_string(this.speed_out)];
76 | },
77 |
78 | get_active_access_point: function() {
79 | if (this.nmdevice.device_type == NetworkManager.DeviceType.WIFI)
80 | return this.nmdevice.get_active_access_point();
81 | return null;
82 | },
83 |
84 | get_ip4: function() {
85 | let ip4_config = this.nmdevice.get_ip4_config();
86 |
87 | if (ip4_config != null) {
88 | let addresses = [];
89 |
90 | for each (let addr in ip4_config.get_addresses()) {
91 | let ip_uint32 = addr.get_address();
92 | let ip = [];
93 |
94 | for (let i=0; i<4; ++i)
95 | ip.push(ip_uint32 >> i*8 & 0xFF);
96 |
97 | addresses.push(ip.join("."));
98 | }
99 |
100 | return addresses;
101 | } else
102 | return [];
103 | },
104 |
105 | /// Formats bytes per second as IEC 60027-2 units
106 | /// For example: 483 B/s, 67.3 KiB/s, 1.28 MiB/s
107 | format_string: function(Bps) {
108 | let unit = 0;
109 |
110 | while(Bps >= 1024) {
111 | Bps /= 1024;
112 | ++unit;
113 | }
114 |
115 | // Can't use toPrecision as it may return exponential notation
116 | // let label = Bps.toPrecision(3);
117 |
118 | let precision = 0;
119 | if (Bps < 10) precision = 2;
120 | else if (Bps < 100) precision = 1;
121 | else precision = 0;
122 |
123 | let label = Bps.toFixed(precision);
124 | if (unit == 0) label += " B/s";
125 | if (unit == 1) label += " KiB/s";
126 | if (unit == 2) label += " MiB/s";
127 | if (unit == 3) label += " GiB/s"; // envy
128 |
129 | return label;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/NetMonitor@zdyb.tk/stylesheet.css:
--------------------------------------------------------------------------------
1 | .bandwidth-label {
2 | font-size: x-small;
3 | padding-left: 0.4em;
4 | min-width: 5.5em;
5 | }
6 |
7 | .speed-indicator {
8 | padding-right: 0.5em;
9 | }
10 |
11 | .speed-indicator:last-child {
12 | padding-right: 0px;
13 | }
14 |
--------------------------------------------------------------------------------
/NetMonitorMenu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azdyb/gnome-shell-extension-netmonitor/2367b6c68ca3b195d4e87143292c11b5a483095f/NetMonitorMenu.png
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | What is NetMonitor?
2 | ===================
3 |
4 | NetMonitor is a humble Netspeed_ replacement for gnome-shell_.
5 |
6 | .. _Netspeed: http://projects.gnome.org/netspeed/
7 | .. _gnome-shell: https://live.gnome.org/GnomeShell
8 |
9 | What it looks like?
10 | ===================
11 |
12 | Everybody loves screenshots, right?
13 |
14 | .. image:: http://img848.imageshack.us/img848/883/netmonitor.png
15 | :alt: Normal view
16 |
17 | .. image:: http://img11.imageshack.us/img11/4089/netmonitormenu.png
18 | :alt: Menu
19 |
20 |
21 | Disclaimer
22 | ==========
23 |
24 | As I couldn't find any real documentation for writing gnome-shell extensions, I based my code on better or worse snippets and tutorials found on internet. Some of the sources are mentioned below:
25 |
26 | * `gnome-shell-extensions `_
27 | * `Musings of an OS plumber `_
28 | * `gnome-shell-system-monitor-applet `_
29 |
30 |
31 | How it works?
32 | =============
33 |
34 | The extension lists available network devices using NMClient and uses GTop to get devices' statistics.
35 |
36 |
37 | Instalation
38 | ===========
39 |
40 | In order to install gsettings schema, copy org.gnome.shell.extensions.net-monitor.gschema.xml file to /usr/share/glib-2.0/schemas [1]_ and invoke glib-compile-schemas on that direcory. Note that you need to be root or use sudo to do that::
41 |
42 | # cp org.gnome.shell.extensions.net-monitor.gschema.xml /usr/share/glib-2.0/schemas
43 | # glib-compile-schemas /usr/share/glib-2.0/schemas
44 |
45 | The NetMonitor@zdyb.tk directory should be copied to /usr/share/gnome-shell/extensions or ~/.local/share/gnome-shell/extensions/::
46 |
47 | # cp -r NetMonitor\@zdyb.tk /usr/share/gnome-shell/extensions
48 |
49 | or::
50 |
51 | $ cp -r NetMonitor\@zdyb.tk ~/.local/share/gnome-shell/extensions/
52 |
53 | .. [1] Note, that paths may differ depending on distribution (eg /usr/local/share/)
54 |
55 | License
56 | =======
57 |
58 | Copyright 2011 Aleksander Zdyb
59 |
60 | 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 3 of the License, or (at your option) any later version.
61 |
62 | 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.
63 |
64 | You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.
65 |
--------------------------------------------------------------------------------
/org.gnome.shell.extensions.net-monitor.gschema.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | []
5 | Hidden iterfaces
6 | A list of interfaces which won't be shown on panel even if they're connected.
7 |
8 |
9 |
--------------------------------------------------------------------------------