Last active
April 8, 2024 05:22
-
-
Save mikechau/15c7d6e77ff5b7077747 to your computer and use it in GitHub Desktop.
cinnamon-system-monitor-applet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
system-monitor@ebbes applet | |
Cinnamon applet displaying system informations in gnome shell status bar, such as memory usage, cpu usage, network rates… | |
forked from gnome-shell extension (for gnome-shell 3.2) to Cinnamon applet by [email protected] | |
Changes that were done: | |
* Removed battery functionality | |
* Removed tooltips (as they were crashing Cinnamon) | |
* Implemented simpler tooltips | |
* Some backports from gnome-shell 3.4 extension | |
* Some small changes I liked | |
Copyright (C) 2011 Florian Mounier aka paradoxxxzero | |
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. | |
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 <http://www.gnu.org/licenses/>. | |
Original author: Florian Mounier aka paradoxxxzero | |
*/ | |
let smaDepsGtop = true; | |
let smaDepsNM = true; | |
const Applet = imports.ui.applet; | |
const Clutter = imports.gi.Clutter; | |
const GLib = imports.gi.GLib; | |
const Gio = imports.gi.Gio; | |
const Lang = imports.lang; | |
const Cinnamon = imports.gi.Cinnamon; | |
const St = imports.gi.St; | |
const ModalDialog = imports.ui.modalDialog; | |
const Tooltips = imports.ui.tooltips; | |
try { | |
const NMClient = imports.gi.NMClient; | |
const NetworkManager = imports.gi.NetworkManager; | |
} catch (e) { | |
global.logError(e); | |
smaDepsNM = false; | |
} | |
try { | |
const GTop = imports.gi.GTop; | |
} catch (e) { | |
global.logError(e); | |
smaDepsGtop = false; | |
} | |
const Main = imports.ui.main; | |
const Panel = imports.ui.panel; | |
const PopupMenu = imports.ui.popupMenu; | |
const Gettext = imports.gettext; | |
const Mainloop = imports.mainloop; | |
const Util = imports.misc.util; | |
const MESSAGE = _("Dependencies missing. Please install \n\ | |
libgtop, Network Manager and gir bindings \n\ | |
\t on Ubuntu: gir1.2-gtop-2.0, gir1.2-networkmanager-1.0 \n\ | |
\t on Fedora: libgtop2-devel, NetworkManager-glib-devel \n\ | |
\t on Arch: libgtop, networkmanager\n\ | |
and restart Cinnamon.\n"); | |
let ElementBase, Cpu, Mem, Swap, Net, Disk, Thermal, Freq, Graph, Bar, Pie, Chart, Icon; | |
let Schema, Background, IconSize; | |
let UsePython2 = false; | |
function l_limit(t) { | |
return (t > 0) ? t : 1000; | |
} | |
function change_text() { | |
this.label.visible = Schema.get_boolean(this.elt + '-show-text'); | |
} | |
function change_style() { | |
let style = Schema.get_string(this.elt + '-style'); | |
this.text_box.visible = style == 'digit' || style == 'both'; | |
this.chart.actor.visible = style == 'graph' || style == 'both'; | |
} | |
function init(metadata) { | |
// let schemaSource; | |
// try { | |
// schemaSource = Gio.SettingsSchemaSource.new_from_directory(metadata.path, | |
// Gio.SettingsSchemaSource.get_default(), false); | |
// } catch (e) { | |
// //fall back to default schema source | |
// schemaSource = Gio.SettingsSchemaSource.get_default(); | |
// } | |
// let schema = schemaSource.lookup('org.cinnamon.applets.system-monitor', true); | |
// Schema = new Gio.Settings({ settings_schema: schema }); | |
Schema = new Gio.Settings({ schema: 'org.cinnamon.applets.system-monitor' }); | |
// let [res, clutterColor] = new Clutter.Color.from_string(Schema.get_string('background')); | |
// Background = clutterColor; | |
let [res, clutterColor] = Clutter.Color.from_string(Schema.get_string('background')); | |
Background = clutterColor; | |
//IconSize = Math.round(Panel.PANEL_ICON_SIZE * 4 / 5); | |
//Constant doesn't exist. Took me ages to figure out WHAT caused Net() to break... | |
IconSize = 16; | |
//Determine python binary to use | |
[status, stdout, stderr] = GLib.spawn_command_line_sync("python --version"); | |
//Somehow python seems to print version on stderr? | |
//Output: e.g. "Python 2.7.3" | |
if (stderr && stderr.length > 7) | |
{ | |
UsePython2 = (stderr[7] != 50); //50 == ASCII for '2' | |
} | |
} | |
let ErrorDialog = function() { | |
this._init.apply(this, arguments); | |
}; | |
ErrorDialog.prototype = { | |
__proto__: ModalDialog.ModalDialog.prototype, | |
_init: function() { | |
ModalDialog.ModalDialog.prototype._init.call(this); | |
let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout', vertical: false }); | |
this.contentLayout.add(mainContentBox, { x_fill: true, y_fill: true }); | |
let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout', vertical: true }); | |
mainContentBox.add(messageBox, { y_align: St.Align.START }); | |
this._subjectLabel = new St.Label({ style_class: 'sma-dialog-headline', text: _("System Monitor Applet: Error") }); | |
messageBox.add(this._subjectLabel, {y_fill: false, y_align: St.Align.START }); | |
this._descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description', text: MESSAGE }); | |
messageBox.add(this._descriptionLabel, { y_fill: true, y_align: St.Align.START }); | |
this.setButtons([ | |
{ | |
label: _("OK"), | |
action: Lang.bind(this, function() { | |
this.close(); | |
}), | |
key: Clutter.Escape | |
} | |
]); | |
}, | |
}; | |
let Chart = function () { | |
this._init.apply(this, arguments); | |
}; | |
Chart.prototype = { | |
_init: function(width, height, parent) { | |
this.actor = new St.DrawingArea({ style_class: "sma-chart", reactive: false}); | |
this.parent = parent; | |
this.actor.set_width(this.width=width); | |
this.actor.set_height(this.height=height); | |
this.actor.connect('repaint', Lang.bind(this, this._draw)); | |
this.data = []; | |
for (let i = 0;i < this.parent.colors.length;i++) | |
this.data[i] = []; | |
}, | |
update: function() { | |
let data_a = this.parent.vals; | |
if (data_a.length != this.parent.colors.length) return; | |
let accdata = []; | |
for (let l = 0 ; l < data_a.length ; l++) { | |
accdata[l] = (l == 0) ? data_a[0] : accdata[l - 1] + ((data_a[l] > 0) ? data_a[l] : 0); | |
this.data[l].push(accdata[l]); | |
if (this.data[l].length > this.width) | |
this.data[l].shift(); | |
} | |
if (!this.actor.visible) return; | |
this.actor.queue_repaint(); | |
}, | |
_draw: function() { | |
if (!this.actor.visible) return; | |
let [width, height] = this.actor.get_surface_size(); | |
let cr = this.actor.get_context(); | |
let max; | |
if (this.parent.max) { | |
max = this.parent.max; | |
} else { | |
max = Math.max.apply(this, this.data[this.data.length - 1]); | |
max = Math.max(1, Math.pow(2, Math.ceil(Math.log(max) / Math.log(2)))); | |
} | |
Clutter.cairo_set_source_color(cr, Background); | |
cr.rectangle(0, 0, width, height); | |
cr.fill(); | |
for (let i = this.parent.colors.length - 1;i >= 0;i--) { | |
cr.moveTo(width, height); | |
for (let j = this.data[i].length - 1;j >= 0;j--) | |
cr.lineTo(width - (this.data[i].length - 1 - j), (1 - this.data[i][j] / max) * height); | |
cr.lineTo(width - (this.data[i].length - 1), height); | |
cr.closePath(); | |
Clutter.cairo_set_source_color(cr, this.parent.colors[i]); | |
cr.fill(); | |
} | |
}, | |
resize: function(schema, key) { | |
let old_width = this.width; | |
this.width = Schema.get_int(key); | |
if (old_width == this.width) return; | |
this.actor.set_width(this.width); | |
if (this.width < this.data[0].length) | |
for (let i = 0;i < this.parent.colors.length;i++) | |
this.data[i] = this.data[i].slice(-this.width); | |
} | |
}; | |
ElementBase = function () { | |
throw new TypeError('Trying to instantiate abstract class ElementBase'); | |
}; | |
ElementBase.prototype = { | |
elt: '', | |
color_name: [], | |
text_items: [], | |
menu_items: [], | |
_init: function(orientation) { | |
this.actor = new St.BoxLayout({ reactive: true }); | |
this.actor._delegate = this; | |
this.vals = []; | |
this.tip_vals = []; | |
this.tip_unit_labels = []; | |
this.colors = []; | |
for(let color in this.color_name) { | |
let name = this.elt + '-' + this.color_name[color] + '-color'; | |
// let [res, clutterColor] = new Clutter.Color.from_string(Schema.get_string(name)); | |
let [res, clutterColor] = Clutter.Color.from_string(Schema.get_string(name)); | |
Schema.connect('changed::' + name, Lang.bind( | |
clutterColor, function (schema, key) { | |
this.from_string(Schema.get_string(key)); | |
})); | |
Schema.connect('changed::' + name, | |
Lang.bind(this, | |
function() { | |
this.chart.actor.queue_repaint(); | |
})); | |
this.colors.push(clutterColor); | |
} | |
this.chart = new Chart(Schema.get_int(this.elt + '-graph-width'), IconSize, this); | |
Schema.connect('changed::background', | |
Lang.bind(this, | |
function() { | |
this.chart.actor.queue_repaint(); | |
})); | |
this.actor.visible = Schema.get_boolean(this.elt + "-display"); | |
Schema.connect( | |
'changed::' + this.elt + '-display', | |
Lang.bind(this, | |
function(schema, key) { | |
this.actor.visible = Schema.get_boolean(key); | |
})); | |
this.interval = l_limit(Schema.get_int(this.elt + "-refresh-time")); | |
this.timeout = Mainloop.timeout_add(this.interval, | |
Lang.bind(this, this.update)); | |
Schema.connect( | |
'changed::' + this.elt + '-refresh-time', | |
Lang.bind(this, | |
function(schema, key) { | |
Mainloop.source_remove(this.timeout); | |
this.interval = l_limit(Schema.get_int(key)); | |
this.timeout = Mainloop.timeout_add(this.interval, | |
Lang.bind(this, this.update)); | |
})); | |
Schema.connect('changed::' + this.elt + '-graph-width', | |
Lang.bind(this.chart, this.chart.resize)); | |
this.label = new St.Label({ text: this.elt == "memory" ? "mem" : _(this.elt), | |
style_class: "sma-status-label"}); | |
change_text.call(this); | |
Schema.connect('changed::' + this.elt + '-show-text', Lang.bind(this, change_text)); | |
this.actor.add_actor(this.label); | |
this.text_box = new St.BoxLayout(); | |
this.tooltip = new Tooltips.PanelItemTooltip(this, "", orientation); | |
this.actor.add_actor(this.text_box); | |
this.text_items = this.create_text_items(); | |
for (let item in this.text_items) | |
this.text_box.add_actor(this.text_items[item]); | |
this.actor.add_actor(this.chart.actor); | |
change_style.call(this); | |
Schema.connect('changed::' + this.elt + '-style', Lang.bind(this, change_style)); | |
this.menu_items = this.create_menu_items(); | |
for (let item in this.menu_items) | |
this.menu_item.addActor(this.menu_items[item]); | |
}, | |
tip_format: function(unit) { | |
typeof(unit) == 'undefined' && (unit = '%'); | |
if(typeof(unit) == 'string') { | |
let all_unit = unit; | |
unit = []; | |
for (let i = 0;i < this.tip_names.length;i++) { | |
unit.push(all_unit); | |
} | |
} | |
for (let i = 0;i < this.color_name.length;i++) { | |
this.tip_unit_labels[i] = unit[i]; | |
} | |
}, | |
set_tip_unit: function(unit) { | |
for (let i = 0;i < this.tip_unit_labels.length;i++) { | |
this.tip_unit_labels[i] = unit[i]; | |
} | |
}, | |
update: function() { | |
this.refresh(); | |
this._apply(); | |
this.chart.update(); | |
let text = ""; | |
for (let i = 0;i < this.tip_vals.length;i++) { | |
text += this.tip_names[i] + " " + this.tip_vals[i].toString() + " " + this.tip_unit_labels[i]; | |
if (i != this.tip_vals.length - 1) | |
text += "\n"; | |
} | |
this.tooltip._tooltip.set_text(text); | |
return true; | |
}, | |
destroy: function() { | |
Mainloop.source_remove(this.timeout); | |
} | |
}; | |
Cpu = function () { | |
this._init.apply(this, arguments); | |
}; | |
Cpu.prototype = { | |
__proto__: ElementBase.prototype, | |
elt: 'cpu', | |
color_name: ['user', 'system', 'nice', 'iowait', 'other'], | |
tip_names: [_('User'), _('System'), _('Nice'), _('Wait'), _('Other')], | |
max: 100, | |
_init: function(orientation) { | |
this.gtop = new GTop.glibtop_cpu(); | |
this.last = [0,0,0,0,0]; | |
this.current = [0,0,0,0,0]; | |
try { | |
this.total_cores = GTop.glibtop_get_sysinfo().ncpu; | |
this.max *= this.total_cores; | |
} catch(e) { | |
this.total_cores = 1; | |
global.logError(e); | |
global.logError("Assuming 1 core"); | |
} | |
this.last_total = 0; | |
this.usage = [0,0,0,1,0]; | |
this.menu_item = new PopupMenu.PopupMenuItem(_("Cpu"), {reactive: false}); | |
ElementBase.prototype._init.call(this, orientation); | |
this.tip_format(); | |
this.update(); | |
}, | |
refresh: function() { | |
GTop.glibtop_get_cpu(this.gtop); | |
this.current[0] = this.gtop.user; | |
this.current[1] = this.gtop.sys; | |
this.current[2] = this.gtop.nice; | |
this.current[3] = this.gtop.idle; | |
this.current[4] = this.gtop.iowait; | |
let delta = (this.gtop.total - this.last_total)/(100*this.total_cores) ; | |
if (delta > 0){ | |
for (let i = 0;i < 5;i++){ | |
this.usage[i] = Math.round((this.current[i] - this.last[i])/delta); | |
this.last[i] = this.current[i]; | |
} | |
this.last_total = this.gtop.total; | |
} | |
}, | |
_apply: function() { | |
let percent = Math.round(((100*this.total_cores)-this.usage[3])/this.total_cores); | |
this.text_items[0].text = this.menu_items[3].text = percent.toString(); | |
let other = 100 * this.total_cores; | |
for (let i = 0;i < this.usage.length;i++) | |
other -= this.usage[i]; | |
//Not to be confusing | |
other = Math.max(0, other); | |
this.vals = [this.usage[0], this.usage[1], this.usage[2], this.usage[4], other]; | |
for (let i = 0;i < 5;i++) | |
this.tip_vals[i] = Math.round(this.vals[i] / this.total_cores); | |
}, | |
create_text_items: function() { | |
return [new St.Label({ style_class: "sma-status-value"}), | |
new St.Label({ text: '%', style_class: "sma-perc-label"})]; | |
}, | |
create_menu_items: function() { | |
return [new St.Label({ style_class: "sma-void"}), | |
new St.Label({ style_class: "sma-void"}), | |
new St.Label({ style_class: "sma-void"}), | |
new St.Label({ style_class: "sma-value"}), | |
new St.Label({ style_class: "sma-void"}), | |
new St.Label({ text: '%', style_class: "sma-label"})]; | |
} | |
}; | |
Mem = function () { | |
this._init.apply(this, arguments); | |
}; | |
Mem.prototype = { | |
__proto__: ElementBase.prototype, | |
elt: 'memory', | |
color_name: ['program', 'buffer', 'cache'], | |
tip_names: [_('Program'), _('Buffer'), _('Cache')], | |
max: 1, | |
_init: function(orientation) { | |
this.menu_item = new PopupMenu.PopupMenuItem(_("Memory"), {reactive: false}); | |
this.gtop = new GTop.glibtop_mem(); | |
this.mem = [0, 0, 0]; | |
ElementBase.prototype._init.call(this, orientation); | |
this.tip_format(); | |
this.update(); | |
}, | |
refresh: function() { | |
GTop.glibtop_get_mem(this.gtop); | |
this.mem[0] = Math.round(this.gtop.user / 1024 / 1024); | |
this.mem[1] = Math.round(this.gtop.buffer / 1024 / 1024); | |
this.mem[2] = Math.round(this.gtop.cached / 1024 / 1024); | |
this.total = Math.round(this.gtop.total / 1024 / 1024); | |
}, | |
_apply: function() { | |
if (this.total == 0) { | |
this.vals = this.tip_vals = [0,0,0]; | |
} else { | |
for (let i = 0;i < 3;i++) { | |
this.vals[i] = this.mem[i] / this.total; | |
this.tip_vals[i] = Math.round(this.vals[i] * 100); | |
} | |
} | |
this.text_items[0].text = this.tip_vals[0].toString(); | |
this.menu_items[0].text = this.mem[0].toString(); | |
this.menu_items[3].text = this.total.toString(); | |
}, | |
create_text_items: function() { | |
return [new St.Label({ style_class: "sma-status-value"}), | |
new St.Label({ text: '%', style_class: "sma-perc-label"})]; | |
}, | |
create_menu_items: function() { | |
return [new St.Label({ style_class: "sma-value"}), | |
new St.Label({ style_class: "sma-void"}), | |
new St.Label({ text: "/", style_class: "sma-label"}), | |
new St.Label({ style_class: "sma-value"}), | |
new St.Label({ style_class: "sma-void"}), | |
new St.Label({ text: "MiB", style_class: "sma-label"})]; | |
} | |
}; | |
Swap = function () { | |
this._init.apply(this, arguments); | |
}; | |
Swap.prototype = { | |
__proto__: ElementBase.prototype, | |
elt: 'swap', | |
color_name: ['used'], | |
tip_names: [_('Used')], | |
max: 1, | |
_init: function(orientation) { | |
this.menu_item = new PopupMenu.PopupMenuItem(_("Swap"), {reactive: false}); | |
this.gtop = new GTop.glibtop_swap(); | |
ElementBase.prototype._init.call(this, orientation); | |
this.tip_format(); | |
this.update(); | |
}, | |
refresh: function() { | |
GTop.glibtop_get_swap(this.gtop); | |
this.swap = Math.round(this.gtop.used / 1024 / 1024); | |
this.total = Math.round(this.gtop.total / 1024 / 1024); | |
}, | |
_apply: function() { | |
if (this.total == 0) { | |
this.vals = this.tip_vals = [0]; | |
} else { | |
this.vals[0] = this.swap / this.total; | |
this.tip_vals[0] = Math.round(this.vals[0] * 100); | |
} | |
this.text_items[0].text = this.tip_vals[0].toString(); | |
this.menu_items[0].text = this.swap.toString(); | |
this.menu_items[3].text = this.total.toString(); | |
}, | |
create_text_items: function() { | |
return [new St.Label({ style_class: "sma-status-value"}), | |
new St.Label({ text: '%', style_class: "sma-perc-label"})]; | |
}, | |
create_menu_items: function() { | |
return [new St.Label({ style_class: "sma-value"}), | |
new St.Label({ style_class: "sma-void"}), | |
new St.Label({ text: "/", style_class: "sma-label"}), | |
new St.Label({ style_class: "sma-value"}), | |
new St.Label({ style_class: "sma-void"}), | |
new St.Label({ text: "MiB", style_class: "sma-label"})]; | |
} | |
}; | |
Net = function () { | |
this._init.apply(this, arguments); | |
}; | |
Net.prototype = { | |
__proto__: ElementBase.prototype, | |
elt: 'net', | |
color_name: ['down', 'downerrors', 'up', 'uperrors', 'collisions'], | |
tip_names: [_('Down'), _('Down errors'), _('Up'), _('Up errors'), _('Collisions')], | |
speed_in_bits: false, | |
_init: function(orientation) { | |
this.ifs = []; | |
this.client = NMClient.Client.new(); | |
this.update_iface_list(); | |
if(!this.ifs.length){ | |
let net_lines = Cinnamon.get_file_contents_utf8_sync('/proc/net/dev').split("\n"); | |
// for(let i = 3; i < net_lines.length - 1 ; i++) { | |
for(let i = 2; i < net_lines.length - 1 ; i++) { | |
let ifc = net_lines[i].replace(/^\s+/g, '').split(":")[0]; | |
if(Cinnamon.get_file_contents_utf8_sync('/sys/class/net/' + ifc + '/operstate') | |
.replace(/\s/g, "") == "up" && | |
ifc.indexOf("br") < 0 && | |
ifc.indexOf("lo") < 0) { | |
this.ifs.push(ifc); | |
} | |
} | |
} | |
this.gtop = new GTop.glibtop_netload(); | |
this.last = [0, 0, 0, 0, 0]; | |
this.usage = [0, 0, 0, 0, 0]; | |
this.last_time = 0; | |
this.menu_item = new PopupMenu.PopupMenuItem(_("Network"), {reactive: false}); | |
ElementBase.prototype._init.call(this, orientation); | |
this.tip_format(['kiB/s', '/s', 'kiB/s', '/s', '/s']); | |
this.update_units(); | |
Schema.connect('changed::' + this.elt + '-speed-in-bits', Lang.bind(this, this.update_units)); | |
try { | |
let iface_list = this.client.get_devices(); | |
this.NMsigID = [] | |
for(let j = 0; j < iface_list.length; j++){ | |
this.NMsigID[j] = iface_list[j].connect('state-changed' , Lang.bind(this, this.update_iface_list)); | |
} | |
} | |
catch(e) { | |
global.logError("Please install Network Manager GObject Introspection Bindings"); | |
} | |
this.update(); | |
}, | |
update_units: function() { | |
let previous_setting = this.speed_in_bits; | |
this.speed_in_bits = Schema.get_boolean(this.elt + '-speed-in-bits'); | |
if (this.speed_in_bits) { | |
this.set_tip_unit(['kbps', '/s', 'kbps', '/s', '/s']); | |
this.text_items[2].text = 'kbps'; | |
this.text_items[5].text = 'kbps'; | |
if (!previous_setting) { | |
this.last[0] *= 8; | |
this.last[1] *= 8; | |
this.usage[0] *= 8; | |
this.usage[1] *= 8; | |
} | |
} else { | |
this.set_tip_unit(['kiB/s', '/s', 'kiB/s', '/s', '/s']); | |
this.text_items[2].text = 'kB/s'; | |
this.text_items[5].text = 'kB/s'; | |
if (previous_setting) { | |
this.last[0] /= 8; | |
this.last[1] /= 8; | |
this.usage[0] /= 8; | |
this.usage[1] /= 8; | |
} | |
} | |
}, | |
update_iface_list: function(){ | |
try { | |
this.ifs = [] | |
let iface_list = this.client.get_devices(); | |
for(let j = 0; j < iface_list.length; j++){ | |
if (iface_list[j].state == NetworkManager.DeviceState.ACTIVATED){ | |
//this.ifs.push(iface_list[j].get_iface()); | |
this.ifs.push(iface_list[j].get_ip_iface()); | |
} | |
} | |
} | |
catch(e) { | |
global.logError("Please install Network Manager GObject Introspection Bindings"); | |
} | |
}, | |
refresh: function() { | |
let accum = [0, 0, 0, 0, 0]; | |
for (let ifn in this.ifs) { | |
GTop.glibtop_get_netload(this.gtop, this.ifs[ifn]); | |
accum[0] += this.gtop.bytes_in * (this.speed_in_bits ? 8 : 1); | |
accum[1] += this.gtop.errors_in; | |
accum[2] += this.gtop.bytes_out * (this.speed_in_bits ? 8 : 1); | |
accum[3] += this.gtop.errors_out; | |
accum[4] += this.gtop.collisions; | |
} | |
let time = GLib.get_monotonic_time() / 1000; | |
let delta = time - this.last_time; | |
if (delta > 0) | |
for (let i = 0;i < 5;i++) { | |
this.usage[i] = Math.round((accum[i] - this.last[i]) / delta); | |
this.last[i] = accum[i]; | |
} | |
this.last_time = time; | |
}, | |
_apply: function() { | |
this.tip_vals = this.vals = this.usage; | |
this.menu_items[0].text = this.text_items[1].text = this.tip_vals[0].toString(); | |
this.menu_items[3].text = this.text_items[4].text = this.tip_vals[2].toString(); | |
}, | |
create_text_items: function() { | |
return [new St.Label({ text: 'D', style_class: 'sma-status-label'}), | |
new St.Label({ style_class: "sma-status-value"}), | |
new St.Label({ text: 'kiB/s', style_class: "sma-perc-label"}), | |
new St.Label({ text: 'U', style_class: 'sma-status-label' }), | |
new St.Label({ style_class: "sma-status-value"}), | |
new St.Label({ text: 'kiB/s', style_class: "sma-perc-label"})]; | |
}, | |
create_menu_items: function() { | |
return [new St.Label({ style_class: "sma-value"}), | |
new St.Label({ text:'k', style_class: "sma-label"}), | |
new St.Label({ text: 'D', style_class: 'sma-label'}), | |
new St.Label({ style_class: "sma-value"}), | |
new St.Label({ text:'k', style_class: "sma-label"}), | |
new St.Label({ text: 'U', style_class: 'sma-label'})]; | |
} | |
}; | |
Disk = function () { | |
this._init.apply(this, arguments); | |
}; | |
Disk.prototype = { | |
__proto__: ElementBase.prototype, | |
elt: 'disk', | |
color_name: ['read', 'write'], | |
tip_names: [_('Read'), _('Write')], | |
_init: function(orientation) { | |
// Can't get mountlist: | |
// GTop.glibtop_get_mountlist | |
// Error: No symbol 'glibtop_get_mountlist' in namespace 'GTop' | |
// Getting it with mtab | |
let mount_lines = Cinnamon.get_file_contents_utf8_sync('/etc/mtab').split("\n"); | |
this.mounts = []; | |
for(let mount_line in mount_lines) { | |
let mount = mount_lines[mount_line].split(" "); | |
if(mount[0].indexOf("/dev/") == 0 && this.mounts.indexOf(mount[1]) < 0) { | |
this.mounts.push(mount[1]); | |
} | |
} | |
this.gtop = new GTop.glibtop_fsusage(); | |
this.last = [0,0]; | |
this.usage = [0,0]; | |
this.last_time = 0; | |
GTop.glibtop_get_fsusage(this.gtop, this.mounts[0]); | |
this.block_size = this.gtop.block_size/1024/1024/8; | |
this.menu_item = new PopupMenu.PopupMenuItem(_("Disk"), {reactive: false}); | |
ElementBase.prototype._init.call(this, orientation); | |
this.tip_format('kB/s'); | |
this.update(); | |
}, | |
refresh: function() { | |
let accum = [0, 0]; | |
for(let mount in this.mounts) { | |
GTop.glibtop_get_fsusage(this.gtop, this.mounts[mount]); | |
accum[0] += this.gtop.read; | |
accum[1] += this.gtop.write; | |
} | |
let time = GLib.get_monotonic_time() / 1000; | |
let delta = (time - this.last_time) / 1000; | |
if (delta > 0) | |
for (let i = 0;i < 2;i++) { | |
this.usage[i] =(this.block_size* (accum[i] - this.last[i]) / delta) ; | |
this.last[i] = accum[i]; | |
} | |
this.last_time = time; | |
}, | |
_apply: function() { | |
this.vals = this.usage.slice(); | |
for (let i = 0;i < 2;i++) { | |
if (this.usage[i] < 10) | |
this.usage[i] = this.usage[i].toFixed(1); | |
else | |
this.usage[i] = Math.round(this.usage[i]); | |
} | |
this.tip_vals = [this.usage[0] , this.usage[1] ]; | |
this.menu_items[0].text = this.text_items[1].text = this.tip_vals[0].toString(); | |
this.menu_items[3].text = this.text_items[4].text = this.tip_vals[1].toString(); | |
}, | |
create_text_items: function() { | |
return [new St.Label({ text: 'R', style_class: "sma-status-label"}), | |
new St.Label({ style_class: "sma-status-value"}), | |
new St.Label({ text: 'MiB/s', style_class: "sma-perc-label"}), | |
new St.Label({ text: 'W', style_class: "sma-status-label"}), | |
new St.Label({ style_class: "sma-status-value"}), | |
new St.Label({ text: 'MiB/s', style_class: "sma-perc-label"})]; | |
}, | |
create_menu_items: function() { | |
return [new St.Label({ style_class: "sma-value"}), | |
new St.Label({ text:'MiB/s', style_class: "sma-label"}), | |
new St.Label({ text:'R', style_class: "sma-label"}), | |
new St.Label({ style_class: "sma-value"}), | |
new St.Label({ text:'MiB/s', style_class: "sma-label"}), | |
new St.Label({ text:'W', style_class: "sma-label"})]; | |
} | |
}; | |
Thermal = function() { | |
this._init.apply(this, arguments); | |
}; | |
Thermal.prototype = { | |
__proto__: ElementBase.prototype, | |
elt: 'thermal', | |
color_name: ['tz0'], | |
tip_names: [_('Temperature')], | |
_init: function(orientation) { | |
this.temperature = -273.15; | |
this.menu_item = new PopupMenu.PopupMenuItem(_("Thermal"), {reactive: false}); | |
ElementBase.prototype._init.call(this, orientation); | |
this.tip_format('\u00b0C'); | |
Schema.connect('changed::' + this.elt + '-sensor-file', Lang.bind(this, this.refresh)); | |
this.update(); | |
}, | |
refresh: function() { | |
let sfile = Schema.get_string(this.elt + '-sensor-file'); | |
if(GLib.file_test(sfile,1<<4)){ | |
//global.logError("reading sensor"); | |
let t_str = Cinnamon.get_file_contents_utf8_sync(sfile).split("\n")[0]; | |
this.temperature = parseInt(t_str)/1000.0; | |
} | |
else | |
global.logError("error reading: " + sfile); | |
}, | |
_apply: function() { | |
this.text_items[0].text = this.menu_items[3].text = this.temperature.toString(); | |
this.vals = [this.temperature]; | |
this.tip_vals[0] = Math.round(this.vals[0]); | |
}, | |
create_text_items: function() { | |
return [new St.Label({ style_class: "sma-status-value"}), | |
new St.Label({ text: '\u00b0C', style_class: "sma-unit-label"})]; | |
}, | |
create_menu_items: function() { | |
return [new St.Label({ style_class: "sma-void"}), | |
new St.Label({ style_class: "sma-void"}), | |
new St.Label({ style_class: "sma-void"}), | |
new St.Label({ style_class: "sma-value"}), | |
new St.Label({ style_class: "sma-void"}), | |
new St.Label({ text: '\u00b0C', style_class: "sma-label"})]; | |
} | |
}; | |
Freq = function () { | |
this._init.apply(this, arguments); | |
}; | |
Freq.prototype = { | |
__proto__: ElementBase.prototype, | |
elt: 'freq', | |
color_name: ['freq'], | |
tip_names: [_('Frequency')], | |
_init: function(orientation) { | |
this.freq = 0; | |
this.menu_item = new PopupMenu.PopupMenuItem(_("Frequency"), {reactive: false}); | |
ElementBase.prototype._init.call(this, orientation); | |
this.tip_format('MHz'); | |
this.update(); | |
}, | |
refresh: function() { | |
let lines = Cinnamon.get_file_contents_utf8_sync('/proc/cpuinfo').split("\n"); | |
for(let i = 0; i < lines.length; i++) { | |
let line = lines[i]; | |
if(line.search(/cpu mhz/i) < 0) | |
continue; | |
this.freq = parseInt(line.substring(line.indexOf(':') + 2)); | |
break; | |
} | |
}, | |
_apply: function() { | |
let value = this.freq.toString(); | |
this.text_items[0].text = value + ' '; | |
this.tip_vals[0] = value; | |
this.menu_items[3].text = value; | |
}, | |
create_text_items: function() { | |
return [new St.Label({ style_class: "sma-big-status-value"}), | |
new St.Label({ text: 'MHz', style_class: "sma-perc-label"})]; | |
}, | |
create_menu_items: function() { | |
return [new St.Label({ style_class: "sma-void"}), | |
new St.Label({ style_class: "sma-void"}), | |
new St.Label({ style_class: "sma-void"}), | |
new St.Label({ style_class: "sma-value"}), | |
new St.Label({ style_class: "sma-void"}), | |
new St.Label({ text: 'MHz', style_class: "sma-label"})]; | |
} | |
}; | |
/* Battery was removed because | |
* a) There is an app(let) for that | |
* b) I would have had to change much of its implementation | |
* c) I was too lazy to change much of its implementation. | |
*/ | |
Graph = function() { | |
this._init.apply(this, arguments); | |
}; | |
Graph.prototype = { | |
menu_item: '', | |
_init: function() { | |
// Can't get mountlist: | |
// GTop.glibtop_get_mountlist | |
// Error: No symbol 'glibtop_get_mountlist' in namespace 'GTop' | |
// Getting it with mtab | |
let mount_lines = Cinnamon.get_file_contents_utf8_sync('/etc/mtab').split("\n"); | |
this.mounts = []; | |
for(let mount_line in mount_lines) { | |
let mount = mount_lines[mount_line].split(" "); | |
if(mount[0].indexOf("/dev/") == 0 && this.mounts.indexOf(mount[1]) < 0) { | |
this.mounts.push(mount[1]); | |
} | |
} | |
this.actor = new St.DrawingArea({ style_class: "sma-chart", reactive: false}); | |
this.width = arguments[0][1]; | |
this.height = arguments[0][1]; | |
this.actor.set_width(this.width); | |
this.actor.set_height(this.height); | |
this.actor.connect('repaint', Lang.bind(this, this._draw)); | |
this.gtop = new GTop.glibtop_fsusage(); | |
// FIXME Handle colors correctly | |
this.colors = ["#444", "#666", "#888", "#aaa", "#ccc", "#eee"]; | |
for(let color in this.colors) { | |
// let [res, clutterColor] = new Clutter.Color.from_string(this.colors[color]); | |
let [res, clutterColor] = Clutter.Color.from_string(this.colors[color]); | |
this.colors[color] = clutterColor; | |
} | |
}, | |
create_menu_item: function() { | |
this.menu_item = new PopupMenu.PopupBaseMenuItem({reactive: false}); | |
this.menu_item.addActor(this.actor, {span: -1, expand: true}); | |
}, | |
show: function(visible) { | |
this.menu_item.actor.visible = visible; | |
} | |
}; | |
Bar = function () { | |
this._init.apply(this, arguments); | |
}; | |
Bar.prototype = { | |
__proto__: Graph.prototype, | |
_init: function() { | |
this.thickness = 15; | |
this.fontsize = 14; | |
Graph.prototype._init.call(this, arguments); | |
this.actor.set_height(this.mounts.length * (3 * this.thickness) / 2); | |
}, | |
_draw: function() { | |
if (!this.actor.visible) return; | |
let [width, height] = this.actor.get_surface_size(); | |
let cr = this.actor.get_context(); | |
let x0 = width/8; | |
let y0 = this.thickness/2; | |
cr.setLineWidth(this.thickness); | |
cr.setFontSize(this.fontsize); | |
for (let mount in this.mounts) { | |
GTop.glibtop_get_fsusage(this.gtop, this.mounts[mount]); | |
let perc_full = (this.gtop.blocks - this.gtop.bfree)/this.gtop.blocks; | |
Clutter.cairo_set_source_color(cr, this.colors[mount % this.colors.length]); | |
cr.moveTo(2*x0,y0) | |
cr.relLineTo(perc_full*0.6*width, 0); | |
cr.moveTo(0, y0+this.thickness/3); | |
cr.showText(this.mounts[mount]); | |
//cr.stroke(); | |
cr.moveTo(width - x0, y0+this.thickness/3); | |
cr.showText(Math.round(perc_full*100).toString()+'%'); | |
cr.stroke(); | |
y0 += (3 * this.thickness) / 2; | |
} | |
} | |
}; | |
Pie = function () { | |
this._init.apply(this, arguments); | |
}; | |
Pie.prototype = { | |
__proto__: Graph.prototype, | |
_init: function() { | |
Graph.prototype._init.call(this, arguments); | |
}, | |
_draw: function() { | |
if (!this.actor.visible) return; | |
let [width, height] = this.actor.get_surface_size(); | |
let cr = this.actor.get_context(); | |
let xc = width / 2; | |
let yc = height / 2; | |
let rc = Math.min(xc, yc); | |
let pi = Math.PI; | |
function arc(r, value, max, angle) { | |
if(max == 0) return angle; | |
let new_angle = angle + (value * 2 * pi / max); | |
cr.arc(xc, yc, r, angle, new_angle); | |
return new_angle; | |
} | |
let rings = (this.mounts.length < 7 ? this.mounts.length : 7); | |
let thickness = (2 * rc) / (3 * rings); | |
let fontsize = 14; | |
let r = rc - (thickness / 2); | |
cr.setLineWidth(thickness); | |
cr.setFontSize(fontsize); | |
for (let mount in this.mounts) { | |
GTop.glibtop_get_fsusage(this.gtop, this.mounts[mount]); | |
Clutter.cairo_set_source_color(cr, this.colors[mount % this.colors.length]); | |
arc(r, this.gtop.blocks - this.gtop.bfree, this.gtop.blocks, -pi/2); | |
cr.moveTo(0, yc - r + thickness / 2); | |
cr.showText(this.mounts[mount]); | |
cr.stroke(); | |
r -= (3 * thickness) / 2; | |
} | |
} | |
}; | |
Icon = function () { | |
this._init.apply(this, arguments); | |
}; | |
Icon.prototype = { | |
_init: function() { | |
this.actor = new St.Icon({ icon_name: 'utilities-system-monitor', | |
icon_type: St.IconType.SYMBOLIC, | |
style_class: 'system-status-icon'}); | |
this.actor.visible = Schema.get_boolean("icon-display"); | |
Schema.connect( | |
'changed::icon-display', | |
Lang.bind(this, | |
function () { | |
this.actor.visible = Schema.get_boolean("icon-display"); | |
})); | |
} | |
}; | |
function MyApplet(metadata, orientation) { | |
this._init(metadata, orientation); | |
} | |
MyApplet.prototype = { | |
__proto__: Applet.Applet.prototype, | |
_init: function(metadata, orientation) { | |
Applet.Applet.prototype._init.call(this, orientation); | |
try { | |
this.menuManager = new PopupMenu.PopupMenuManager(this); | |
this.menu = new Applet.AppletPopupMenu(this, orientation); | |
this.menuManager.addMenu(this.menu); | |
let elts = { | |
cpu: new Cpu(orientation), | |
freq: new Freq(orientation), | |
memory: new Mem(orientation), | |
swap: new Swap(orientation), | |
net: new Net(orientation), | |
disk: new Disk(orientation), | |
thermal: new Thermal(orientation), | |
} | |
let icon = new Icon(); | |
let box = new St.BoxLayout(); | |
this.actor.add_actor(box); | |
box.add_actor(icon.actor); | |
for (let elt in elts) { | |
box.add_actor(elts[elt].actor); | |
this.menu.addMenuItem(elts[elt].menu_item); | |
} | |
this.pie = new Pie(300, 300); | |
this.pie.create_menu_item(); | |
this.menu.addMenuItem(this.pie.menu_item); | |
this.bar = new Bar(300, 150); | |
this.bar.create_menu_item(); | |
this.menu.addMenuItem(this.bar.menu_item); | |
this.change_usage(); | |
Schema.connect('changed::' + 'disk-usage-style', Lang.bind(this, this.change_usage)); | |
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | |
let menu_timeout; | |
let pie=this.pie; | |
this.menu.connect( | |
'open-state-changed', | |
function (menu, isOpen) { | |
if(isOpen) { | |
pie.actor.queue_repaint(); | |
menu_timeout = Mainloop.timeout_add_seconds( | |
1, | |
function () { | |
pie.actor.queue_repaint(); | |
return true; | |
}); | |
} else { | |
Mainloop.source_remove(menu_timeout); | |
} | |
} | |
); | |
let _appSys = Cinnamon.AppSystem.get_default(); | |
let _gsmApp = _appSys.lookup_app('gnome-system-monitor.desktop'); | |
let item = new PopupMenu.PopupMenuItem(_("System Monitor")); | |
item.connect('activate', function () { | |
_gsmApp.activate(); | |
}); | |
this.menu.addMenuItem(item); | |
item = new PopupMenu.PopupMenuItem(_("Preferences")); | |
item.connect('activate', function () { | |
if (UsePython2) | |
{ | |
GLib.spawn_command_line_async('python2 ' + metadata.path + '/config.py'); | |
} | |
else | |
{ | |
GLib.spawn_command_line_async('python ' + metadata.path + '/config.py'); | |
} | |
}); | |
this.menu.addMenuItem(item); | |
} catch (e) { | |
global.logError(e); | |
} | |
}, | |
on_applet_clicked: function(event) { | |
this.menu.toggle(); | |
}, | |
change_usage: function() { | |
let usage = Schema.get_string('disk-usage-style'); | |
this.pie.show(usage == 'pie'); | |
this.bar.show(usage == 'bar'); | |
}, | |
}; | |
function ErrorApplet(metadata, orientation) { | |
this._init(metadata, orientation); | |
} | |
ErrorApplet.prototype = { | |
__proto__: Applet.IconApplet.prototype, | |
_init: function(metadata, orientation) { | |
Applet.IconApplet.prototype._init.call(this, orientation); | |
this.set_applet_icon_symbolic_name("dialog-error-symbolic"); | |
this.set_applet_tooltip(_("Error")); | |
}, | |
on_applet_clicked: function(event) { | |
let errorDialog = new ErrorDialog(); | |
errorDialog.open(); | |
}, | |
}; | |
function main(metadata, orientation) { | |
if (!smaDepsGtop || !smaDepsNM) { | |
let errorDialog = new ErrorDialog(); | |
let dialog_timeout = Mainloop.timeout_add_seconds( | |
1, | |
function () { | |
errorDialog.open(); | |
Mainloop.source_remove(dialog_timeout); | |
return true; | |
}); | |
return new ErrorApplet(metadata, orientation); | |
} | |
init(metadata); | |
let myApplet = new MyApplet(metadata, orientation); | |
return myApplet; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.sma-label { | |
color: #bbb; | |
font-size: 12px; | |
} | |
/* cpu, mem */ | |
.sma-status-label { | |
color: #bbb; | |
font-size: 11px; | |
padding: 5px 4px 0px 10px; | |
} | |
/* c (temp) */ | |
.sma-unit-label { | |
color: #aaa; | |
font-size: 12px; | |
padding: 4px 3px 0 2px; | |
} | |
/* percent */ | |
.sma-perc-label { | |
color: #aaa; | |
font-size: 12px; | |
padding: 4px 3px 0 2px; | |
} | |
/* value */ | |
.sma-status-value { | |
min-width: 10px; | |
font-size: 12px; | |
padding-top: 4px; | |
text-align: right; | |
} | |
.sma-big-status-value { | |
min-width: 20px; | |
font-size: 12px; | |
padding-left: 10px; | |
padding-top: 4px; | |
text-align: right; | |
} | |
.sma-value { | |
text-align: right; | |
width: 45px; | |
} | |
.sma-void { | |
width: 0px; | |
} | |
.sma-chart { | |
padding: 1px 2px; | |
} | |
.sma-tooltip-box { | |
font-size: 0.85em; | |
font-weight: bold; | |
color: rgba(255, 255, 255, 0.9); | |
background-color: rgba(10, 10, 10, 0.7); | |
border-radius: 5px; | |
padding: 3px; | |
} | |
.sma-tooltip-item { | |
padding: 0px; | |
spacing: 10px; | |
} | |
.sma-dialog-headline { | |
font-size: 12pt; | |
font-weight: bold; | |
color: #fff; | |
} | |
.sma-dialog-main-layout { | |
spacing: 24px; | |
padding: 10px; | |
} | |
.sma-dialog-message-layout { | |
spacing: 16px; | |
} | |
.sma-dialog-description { | |
font-size: 10pt; | |
color: white; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment