dotfiles/.local/share/gnome-shell/extensions/system-monitor@paradoxxx.ze.../extension.js

2593 lines
90 KiB
JavaScript

/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// system-monitor: Gnome shell extension displaying system informations in gnome shell status bar, such as memory usage, cpu usage, network rates…
// 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/>.
// Author: Florian Mounier aka paradoxxxzero
/* Ugly. This is here so that we don't crash old libnm-glib based shells unnecessarily
* by loading the new libnm.so. Should go away eventually */
var libnm_glib = imports.gi.GIRepository.Repository.get_default().is_registered('NMClient', '1.0');
var smDepsGtop = true;
var smDepsNM = true;
var Config = imports.misc.config;
var Clutter = imports.gi.Clutter;
var GLib = imports.gi.GLib;
var GObject = imports.gi.GObject;
var Lang = imports.lang;
var Gio = imports.gi.Gio;
var Shell = imports.gi.Shell;
var St = imports.gi.St;
const UPower = imports.gi.UPowerGlib;
// const System = imports.system;
var ModalDialog = imports.ui.modalDialog;
var ByteArray = imports.byteArray;
var ExtensionSystem = imports.ui.extensionSystem;
var ExtensionUtils = imports.misc.extensionUtils;
var Me = ExtensionUtils.getCurrentExtension();
var Convenience = Me.imports.convenience;
var Compat = Me.imports.compat;
var Background, GTop, IconSize, Locale, MountsMonitor, NM, NetworkManager, Schema, StatusArea, Style, gc_timeout, menu_timeout;
try {
GTop = imports.gi.GTop;
} catch (e) {
log('[System monitor] catched error: ' + e);
smDepsGtop = false;
}
try {
NM = libnm_glib ? imports.gi.NMClient : imports.gi.NM;
NetworkManager = libnm_glib ? imports.gi.NetworkManager : NM;
} catch (e) {
log('[System monitor] catched error: ' + e);
smDepsNM = false;
}
const Main = imports.ui.main;
const Panel = imports.ui.panel;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Gettext = imports.gettext.domain('system-monitor');
const Mainloop = imports.mainloop;
const Util = imports.misc.util;
const _ = Gettext.gettext;
const MESSAGE = _('Dependencies Missing\n\
Please install: \n\
gnome-system-monitor and libgtop, clutter and Network Manager gir bindings \n\
\t on Debian and Ubuntu: gir1.2-gtop-2.0, gir1.2-nm-1.0, gir1.2-clutter-1.0, gnome-system-monitor \n\
\t on Fedora: libgtop2-devel, NetworkManager-libnm-devel, gnome-system-monitor \n\
\t on Arch: libgtop, networkmanager, gnome-system-monitor\n\
\t on openSUSE: typelib-1_0-GTop-2_0, typelib-1_0-NetworkManager-1_0, gnome-system-monitor \n\
\t on Mageia 64-bit: lib64gtop-gir2.0, lib64nm-gir1.0, lib64clutter-gir1.0, gnome-system-monitor\n');
// stale network shares will cause the shell to freeze, enable this with caution
const ENABLE_NETWORK_DISK_USAGE = false;
let extension = imports.misc.extensionUtils.getCurrentExtension();
let metadata = extension.metadata;
let shell_Version = Config.PACKAGE_VERSION;
Clutter.Actor.prototype.raise_top = function raise_top() {
const parent = this.get_parent();
if (!parent) {
return;
}
parent.set_child_above_sibling(this, null);
}
Clutter.Actor.prototype.reparent = function reparent(newParent) {
const parent = this.get_parent();
if (parent) {
parent.remove_child(this);
}
newParent.add_child(this);
}
function parse_bytearray(bytearray) {
if (!ByteArray.toString(bytearray).match(/GjsModule byteArray/)) {
return ByteArray.toString(bytearray);
}
return bytearray
}
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 build_menu_info() {
let elts = Main.__sm.elts;
let tray_menu = Main.__sm.tray.menu;
if (tray_menu._getMenuItems().length &&
typeof tray_menu._getMenuItems()[0].actor.get_last_child() !== 'undefined') {
tray_menu._getMenuItems()[0].actor.get_last_child().destroy_all_children();
for (let elt in elts) {
elts[elt].menu_items = elts[elt].create_menu_items();
}
} else {
return;
}
let menu_info_box_table = new St.Widget({
style: 'padding: 10px 0px 10px 0px; spacing-rows: 10px; spacing-columns: 15px;',
layout_manager: new Clutter.GridLayout({orientation: Clutter.Orientation.VERTICAL})
});
let menu_info_box_table_layout = menu_info_box_table.layout_manager;
// Populate Table
let row_index = 0;
for (let elt in elts) {
if (!elts[elt].menu_visible) {
continue;
}
// Add item name to table
menu_info_box_table_layout.attach(
new St.Label({
text: elts[elt].item_name,
style_class: Style.get('sm-title'),
x_align: Clutter.ActorAlign.START,
y_align: Clutter.ActorAlign.CENTER
}), 0, row_index, 1, 1);
// Add item data to table
let col_index = 1;
for (let item in elts[elt].menu_items) {
menu_info_box_table_layout.attach(
elts[elt].menu_items[item], col_index, row_index, 1, 1);
col_index++;
}
row_index++;
}
if (shell_Version < '3.36') {
tray_menu._getMenuItems()[0].actor.get_last_child().add(menu_info_box_table, {expand: true});
} else {
tray_menu._getMenuItems()[0].actor.get_last_child().add_child(menu_info_box_table);
}
}
function change_menu() {
this.menu_visible = Schema.get_boolean(this.elt + '-show-menu');
build_menu_info();
}
function change_usage() {
let usage = Schema.get_string('disk-usage-style');
Main.__sm.pie.show(usage === 'pie');
Main.__sm.bar.show(usage === 'bar');
}
let color_from_string = Compat.color_from_string;
function interesting_mountpoint(mount) {
if (mount.length < 3) {
return false;
}
return ((mount[0].indexOf('/dev/') === 0 || mount[2].toLowerCase() === 'nfs') && mount[2].toLowerCase() !== 'udf');
}
const smStyleManager = class SystemMonitor_smStyleManager {
constructor() {
this._extension = '';
this._iconsize = 1;
this._diskunits = _('MiB/s');
this._netunits_kbytes = _('KiB/s');
this._netunits_mbytes = _('MiB/s');
this._netunits_gbytes = _('GiB/s');
this._netunits_kbits = _('kbit/s');
this._netunits_mbits = _('Mbit/s');
this._netunits_gbits = _('Gbit/s');
this._pie_size = 300;
this._pie_fontsize = 14;
this._bar_width = 300;
this._bar_thickness = 15;
this._bar_fontsize = 14;
this._compact = Schema.get_boolean('compact-display');
if (this._compact) {
this._extension = '-compact';
this._iconsize = 3 / 5;
this._diskunits = _('MB');
this._netunits_kbytes = _('kB');
this._netunits_mbytes = _('MB');
this._netunits_gbytes = _('GB');
this._netunits_kbits = 'kb';
this._netunits_mbits = 'Mb';
this._netunits_gbits = 'Gb';
this._pie_size *= 4 / 5;
this._pie_fontsize = 12;
this._bar_width *= 3 / 5;
this._bar_thickness = 12;
this._bar_fontsize = 12;
}
}
get(style) {
return style + this._extension;
}
iconsize() {
return this._iconsize;
}
diskunits() {
return this._diskunits;
}
netunits_kbytes() {
return this._netunits_kbytes;
}
netunits_mbytes() {
return this._netunits_mbytes;
}
netunits_gbytes() {
return this._netunits_gbytes;
}
netunits_kbits() {
return this._netunits_kbits;
}
netunits_mbits() {
return this._netunits_mbits;
}
netunits_gbits() {
return this._netunits_gbits;
}
pie_size() {
return this._pie_size;
}
pie_fontsize() {
return this._pie_fontsize;
}
bar_width() {
return this._bar_width;
}
bar_thickness() {
return this._bar_thickness;
}
bar_fontsize() {
return this._bar_fontsize;
}
}
const smDialog = class SystemMonitor_smDialog extends ModalDialog.ModalDialog {
constructor() {
super({styleClass: 'prompt-dialog'});
let mainContentBox = new St.BoxLayout({style_class: 'prompt-dialog-main-layout',
vertical: false});
this.contentLayout.add(mainContentBox,
{x_fill: true,
y_fill: true});
let messageBox = new St.BoxLayout({style_class: 'prompt-dialog-message-layout',
vertical: true});
mainContentBox.add(messageBox,
{y_align: St.Align.START});
this._subjectLabel = new St.Label({style_class: 'prompt-dialog-headline',
text: _('System Monitor Extension')});
messageBox.add(this._subjectLabel,
{y_fill: false,
y_align: St.Align.START});
this._descriptionLabel = new St.Label({style_class: 'prompt-dialog-description',
text: MESSAGE});
messageBox.add(this._descriptionLabel,
{y_fill: true,
y_align: St.Align.START});
this.setButtons([
{
label: _('Cancel'),
action: () => {
this.close();
},
key: Clutter.Escape
}
]);
}
}
const Chart = class SystemMonitor_Chart {
constructor(width, height, parent) {
this.actor = new St.DrawingArea({style_class: Style.get('sm-chart'), reactive: false});
this.parentC = parent;
this.width = width;
let themeContext = St.ThemeContext.get_for_stage(global.stage);
this.scale_factor = themeContext.scale_factor;
this.actor.set_width(this.width * this.scale_factor);
this.actor.set_height(height);
this.data = [];
for (let i = 0; i < this.parentC.colors.length; i++) {
this.data[i] = [];
}
themeContext.connect('notify::scale-factor', this.rescale.bind(this));
this.actor.connect('repaint', this._draw.bind(this));
}
update() {
let data_a = this.parentC.vals;
if (data_a.length !== this.parentC.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() {
if (!this.actor.visible) {
return;
}
let [width, height] = this.actor.get_surface_size();
let cr = this.actor.get_context();
let max;
if (this.parentC.max) {
max = this.parentC.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.parentC.colors.length - 1; i >= 0; i--) {
let samples = this.data[i].length - 1;
if (samples > 0) {
cr.moveTo(width, height); // bottom right
let x = width - 0.25 * this.scale_factor;
cr.lineTo(x, (1 - this.data[i][samples] / max) * height);
x -= 0.5 * this.scale_factor;
for (let j = samples; j >= 0; j--) {
let y = (1 - this.data[i][j] / max) * height;
cr.lineTo(x, y);
x -= 0.5 * this.scale_factor;
cr.lineTo(x, y);
x -= 0.5 * this.scale_factor;
}
x += 0.25 * this.scale_factor;
cr.lineTo(x, (1 - this.data[i][0] / max) * height);
cr.lineTo(x, height);
cr.closePath();
Clutter.cairo_set_source_color(cr, this.parentC.colors[i]);
cr.fill();
}
}
cr.$dispose();
}
resize(width) {
if (this.width === width) {
return;
}
this.width = width;
if (this.width < this.data[0].length) {
for (let i = 0; i < this.parentC.colors.length; i++) {
this.data[i] = this.data[i].slice(-this.width);
}
}
this.actor.set_width(this.width * this.scale_factor); // repaints
}
rescale(themeContext) {
this.scale_factor = themeContext.scale_factor;
this.actor.set_width(this.width * this.scale_factor); // repaints
}
}
// Class to deal with volumes insertion / ejection
const smMountsMonitor = class SystemMonitor_smMountsMonitor {
constructor() {
this.files = [];
this.num_mounts = -1;
this.listeners = [];
this.connected = false;
this._volumeMonitor = Gio.VolumeMonitor.get();
let sys_mounts = ['/home', '/tmp', '/boot', '/usr', '/usr/local'];
this.base_mounts = ['/'];
sys_mounts.forEach((sMount) => {
if (this.is_sys_mount(sMount + '/')) {
this.base_mounts.push(sMount);
}
});
this.connect();
}
refresh() {
// try check that number of volumes has changed
// try {
// let num_mounts = this.manager.getMounts().length;
// if (num_mounts == this.num_mounts)
// return;
// this.num_mounts = num_mounts;
// } catch (e) {};
// Can't get mountlist:
// GTop.glibtop_get_mountlist
// Error: No symbol 'glibtop_get_mountlist' in namespace 'GTop'
// Getting it with mtab
// let mount_lines = Shell.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(interesting_mountpoint(mount) && this.mounts.indexOf(mount[1]) < 0) {
// this.mounts.push(mount[1]);
// }
// }
// log("[System monitor] old mounts: " + this.mounts);
this.mounts = [];
for (let base in this.base_mounts) {
// log("[System monitor] " + this.base_mounts[base]);
this.mounts.push(this.base_mounts[base]);
}
let mount_lines = this._volumeMonitor.get_mounts();
mount_lines.forEach((mount) => {
if ((!this.is_net_mount(mount) || ENABLE_NETWORK_DISK_USAGE) &&
!this.is_ro_mount(mount)) {
let mpath = mount.get_root().get_path() || mount.get_default_location().get_path();
if (mpath) {
this.mounts.push(mpath);
}
}
});
// log("[System monitor] base: " + this.base_mounts);
// log("[System monitor] mounts: " + this.mounts);
for (let i in this.listeners) {
this.listeners[i](this.mounts);
}
}
add_listener(cb) {
this.listeners.push(cb);
}
remove_listener(cb) {
this.listeners.pop(cb);
}
get_mounts() {
return this.mounts;
}
is_sys_mount(mpath) {
let file = Gio.file_new_for_path(mpath);
let info = file.query_info(Gio.FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT,
Gio.FileQueryInfoFlags.NONE, null);
return info.get_attribute_boolean(Gio.FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT);
}
is_ro_mount(mount) {
// FIXME: running this function after "login after waking from suspend"
// can make login hang. Actual issue seems to occur when a former net
// mount got broken (e.g. due to a VPN connection terminated or
// otherwise broken connection)
try {
let file = mount.get_default_location();
let info = file.query_filesystem_info(Gio.FILE_ATTRIBUTE_FILESYSTEM_READONLY, null);
return info.get_attribute_boolean(Gio.FILE_ATTRIBUTE_FILESYSTEM_READONLY);
} catch (e) {
return false;
}
}
is_net_mount(mount) {
try {
let file = mount.get_default_location();
let info = file.query_filesystem_info(Gio.FILE_ATTRIBUTE_FILESYSTEM_TYPE, null);
let result = info.get_attribute_string(Gio.FILE_ATTRIBUTE_FILESYSTEM_TYPE);
let net_fs = ['nfs', 'smbfs', 'cifs', 'ftp', 'sshfs', 'sftp', 'mtp', 'mtpfs'];
return !file.is_native() || net_fs.indexOf(result) > -1;
} catch (e) {
return false;
}
}
connect() {
if (this.connected) {
return;
}
try {
this.manager = this._volumeMonitor;
this.mount_added_id = this.manager.connect('mount-added', this.refresh.bind(this));
this.mount_removed_id = this.manager.connect('mount-removed', this.refresh.bind(this));
// need to add the other signals here
this.connected = true;
} catch (e) {
log('[System monitor] Failed to register on placesManager notifications');
log('[System monitor] Got exception : ' + e);
}
this.refresh();
}
disconnect() {
if (!this.connected) {
return;
}
this.manager.disconnect(this.mount_added_id);
this.manager.disconnect(this.mount_removed_id);
this.connected = false;
}
destroy() {
this.disconnect();
}
}
const Graph = class SystemMonitor_Graph {
constructor(width, height) {
this.menu_item = '';
this.actor = new St.DrawingArea({style_class: Style.get('sm-chart'), reactive: false});
this.width = width;
this.height = height;
this.gtop = new GTop.glibtop_fsusage();
this.colors = ['#888', '#aaa', '#ccc'];
for (let color in this.colors) {
this.colors[color] = color_from_string(this.colors[color]);
}
let themeContext = St.ThemeContext.get_for_stage(global.stage);
themeContext.connect('notify::scale-factor', this.set_scale.bind(this));
this.scale_factor = themeContext.scale_factor;
let interfaceSettings = new Gio.Settings({
schema: 'org.gnome.desktop.interface'
});
interfaceSettings.connect('changed', this.set_text_scaling.bind(this));
this.text_scaling = interfaceSettings.get_double('text-scaling-factor');
if (!this.text_scaling) {
this.text_scaling = 1;
}
this.actor.set_width(this.width * this.scale_factor * this.text_scaling);
this.actor.set_height(this.height * this.scale_factor * this.text_scaling);
this.actor.connect('repaint', this._draw.bind(this));
}
create_menu_item() {
this.menu_item = new PopupMenu.PopupBaseMenuItem({reactive: false});
if (shell_Version < '3.36') {
this.menu_item.actor.add(this.actor, {span: -1, expand: true});
} else {
this.menu_item.actor.add_child(this.actor);
}
// tray.menu.addMenuItem(this.menu_item);
}
show(visible) {
this.menu_item.actor.visible = visible;
}
set_scale(themeContext) {
this.scale_factor = themeContext.scale_factor;
this.actor.set_width(this.width * this.scale_factor * this.text_scaling);
this.actor.set_height(this.height * this.scale_factor * this.text_scaling);
}
set_text_scaling(interfaceSettings, key) {
// FIXME: for some reason we only get this signal once, not on later
// changes to the setting
//log('[System monitor] got text scaling signal');
this.text_scaling = interfaceSettings.get_double(key);
this.actor.set_width(this.width * this.scale_factor * this.text_scaling);
this.actor.set_height(this.height * this.scale_factor * this.text_scaling);
}
}
const Bar = class SystemMonitor_Bar extends Graph {
constructor() {
// Height doesn't matter, it gets set on every draw.
super(Style.bar_width(), 100);
this.mounts = MountsMonitor.get_mounts();
MountsMonitor.add_listener(this.update_mounts.bind(this));
}
_draw() {
if (!this.actor.visible) {
return;
}
let thickness = Style.bar_thickness() * this.scale_factor * this.text_scaling;
let fontsize = Style.bar_fontsize() * this.scale_factor * this.text_scaling;
this.actor.set_height(this.mounts.length * (3 * thickness));
let [width, height] = this.actor.get_surface_size();
let cr = this.actor.get_context();
let x0 = width / 8;
let y0 = thickness / 2;
cr.setLineWidth(thickness);
cr.setFontSize(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]);
var text = this.mounts[mount];
if (text.length > 10) {
text = text.split('/').pop();
}
cr.moveTo(0, y0 + thickness / 3);
cr.showText(text);
cr.moveTo(width - x0, y0 + thickness / 3);
cr.showText(Math.round(perc_full * 100).toString() + '%');
y0 += (5 * thickness) / 4;
cr.moveTo(0, y0);
cr.relLineTo(perc_full * width, 0);
cr.stroke();
y0 += (7 * thickness) / 4;
}
cr.$dispose();
}
update_mounts(mounts) {
this.mounts = mounts;
this.actor.queue_repaint();
}
}
const Pie = class SystemMonitor_Pie extends Graph {
constructor() {
super(Style.pie_size(), Style.pie_size());
this.mounts = MountsMonitor.get_mounts();
MountsMonitor.add_listener(this.update_mounts.bind(this));
}
_draw() {
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 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;
}
// Set the ring thickness so that at least 7 rings can be displayed. If
// there are more mounts, make the rings thinner. If the rings are too
// thin to have a line height of 1.2 for the labels, shrink the labels.
let rings = Math.max(this.mounts.length, 7);
let ring_width = width / (2 * rings);
let fontsize = Style.pie_fontsize() * this.scale_factor * this.text_scaling;
if (ring_width < 1.2 * fontsize) {
fontsize = ring_width / 1.2;
}
let thickness = ring_width / 1.5;
cr.setLineWidth(thickness);
cr.setFontSize(fontsize);
let r = (height - ring_width) / 2;
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.stroke();
r -= ring_width;
}
let y = (ring_width + fontsize) / 2;
for (let mount in this.mounts) {
var text = this.mounts[mount];
if (text.length > 10) {
text = text.split('/').pop();
}
cr.moveTo(0, y);
cr.showText(text);
y += ring_width;
}
cr.$dispose();
}
update_mounts(mounts) {
this.mounts = mounts;
this.actor.queue_repaint();
}
}
var TipItem = null;
if (shell_Version < '3.36') {
TipItem = class SystemMonitor_TipItem extends PopupMenu.PopupBaseMenuItem {
constructor() {
super();
// PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
this.actor.remove_style_class_name('popup-menu-item');
this.actor.add_style_class_name('sm-tooltip-item');
}
}
} else {
TipItem = GObject.registerClass(
{
GTypeName: 'TipItem'
},
class SystemMonitor_TipItem extends PopupMenu.PopupBaseMenuItem {
_init() {
super._init();
// PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
this.actor.remove_style_class_name('popup-menu-item');
this.actor.add_style_class_name('sm-tooltip-item');
}
}
);
}
const TipMenu = class SystemMonitor_TipMenu extends PopupMenu.PopupMenuBase {
constructor(sourceActor) {
// PopupMenu.PopupMenuBase.prototype._init.call(this, sourceActor, 'sm-tooltip-box');
super(sourceActor, 'sm-tooltip-box');
this.actor = new Clutter.Actor();
// this.actor.connect('get-preferred-width',
// this._boxGetPreferredWidth).bind(this);
// this.actor.connect('get-preferred-height',
// this._boxGetPreferredHeight.bind(this));
this.actor.add_actor(this.box);
}
// _boxGetPreferredWidth (actor, forHeight, alloc) {
// // let columnWidths = this.getColumnWidths();
// // this.setColumnWidths(columnWidths);
//
// [alloc.min_size, alloc.natural_size] = this.box.get_preferred_width(forHeight);
// }
// _boxGetPreferredHeight (actor, forWidth, alloc) {
// [alloc.min_size, alloc.natural_size] = this.box.get_preferred_height(forWidth);
// }
// _boxAllocate (actor, box, flags) {
// this.box.allocate(box, flags);
// }
_shift() {
// Probably old but works
let node = this.sourceActor.get_theme_node();
let contentbox = node.get_content_box(this.sourceActor.get_allocation_box());
let sourceTopLeftX = 0;
let sourceTopLeftY = 0;
if (typeof this.sourceActor.get_transformed_extents === 'function') {
let extents = this.sourceActor.get_transformed_extents();
let sourceTopLeft = extents.get_top_left();
sourceTopLeftY = sourceTopLeft.y;
sourceTopLeftX = sourceTopLeft.x;
} else {
let allocation = Shell.util_get_transformed_allocation(this.sourceActor);
sourceTopLeftY = allocation.y1;
sourceTopLeftX = allocation.x1;
}
let monitor = Main.layoutManager.findMonitorForActor(this.sourceActor);
let [x, y] = [sourceTopLeftX + contentbox.x1,
sourceTopLeftY + contentbox.y1];
let [cx, cy] = [sourceTopLeftX + (contentbox.x1 + contentbox.x2) / 2,
sourceTopLeftY + (contentbox.y1 + contentbox.y2) / 2];
let [xm, ym] = [sourceTopLeftX + contentbox.x2,
sourceTopLeftY + contentbox.y2];
let [width, height] = this.actor.get_size();
let tipx = cx - width / 2;
tipx = Math.max(tipx, monitor.x);
tipx = Math.min(tipx, monitor.x + monitor.width - width);
let tipy = Math.floor(ym);
// Hacky condition to determine if the status bar is at the top or at the bottom of the screen
if (sourceTopLeftY / monitor.height > 0.3) {
tipy = sourceTopLeftY - height; // If it is at the bottom, place the tooltip above instead of below
}
this.actor.set_position(tipx, tipy);
}
open(animate) {
if (this.isOpen) {
return;
}
this.isOpen = true;
this.actor.show();
this._shift();
this.actor.raise_top();
this.emit('open-state-changed', true);
}
close(animate) {
this.isOpen = false;
this.actor.hide();
this.emit('open-state-changed', false);
}
}
const TipBox = class SystemMonitor_TipBox {
constructor() {
this.actor = new St.BoxLayout({reactive: true});
this.actor._delegate = this;
this.set_tip(new TipMenu(this.actor));
this.in_to = this.out_to = 0;
this.actor.connect('enter-event', this.on_enter.bind(this));
this.actor.connect('leave-event', this.on_leave.bind(this));
}
set_tip(tipmenu) {
if (this.tipmenu) {
this.tipmenu.destroy();
}
this.tipmenu = tipmenu;
if (this.tipmenu) {
Main.uiGroup.add_actor(this.tipmenu.actor);
this.hide_tip();
}
}
show_tip() {
if (!this.tipmenu) {
return;
}
this.tipmenu.open();
if (this.in_to) {
Mainloop.source_remove(this.in_to);
this.in_to = 0;
}
}
hide_tip() {
if (!this.tipmenu) {
return;
}
this.tipmenu.close();
if (this.out_to) {
Mainloop.source_remove(this.out_to);
this.out_to = 0;
}
if (this.in_to) {
Mainloop.source_remove(this.in_to);
this.in_to = 0;
}
}
on_enter() {
let show_tooltip = Schema.get_boolean('show-tooltip');
if (!show_tooltip) {
return;
}
if (this.out_to) {
Mainloop.source_remove(this.out_to);
this.out_to = 0;
}
if (!this.in_to) {
this.in_to = Mainloop.timeout_add(500,
this.show_tip.bind(this));
}
}
on_leave() {
if (this.in_to) {
Mainloop.source_remove(this.in_to);
this.in_to = 0;
}
if (!this.out_to) {
this.out_to = Mainloop.timeout_add(500,
this.hide_tip.bind(this));
}
}
destroy() {
if (this.in_to) {
Mainloop.source_remove(this.in_to);
this.in_to = 0;
}
if (this.out_to) {
Mainloop.source_remove(this.out_to);
this.out_to = 0;
}
this.actor.destroy();
}
}
const ElementBase = class SystemMonitor_ElementBase extends TipBox {
constructor(properties) {
super();
this.elt = '';
this.item_name = _('');
this.color_name = [];
this.text_items = [];
this.menu_items = [];
this.menu_visible = true;
Object.assign(this, properties);
// TipBox.prototype._init.apply(this, arguments);
this.vals = [];
this.tip_labels = [];
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 clutterColor = color_from_string(Schema.get_string(name));
Schema.connect('changed::' + name, (schema, key) => {
this.clutterColor = color_from_string(Schema.get_string(key));
});
Schema.connect('changed::' + name, () => {
this.chart.actor.queue_repaint();
});
this.colors.push(clutterColor);
}
let element_width = Schema.get_int(this.elt + '-graph-width');
if (Style.get('') === '-compact') {
element_width = Math.round(element_width / 1.5);
}
this.chart = new Chart(element_width, IconSize, this);
Schema.connect('changed::background', () => {
this.chart.actor.queue_repaint();
});
this.actor.visible = Schema.get_boolean(this.elt + '-display');
Schema.connect(
'changed::' + this.elt + '-display', (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,
this.update.bind(this),
GLib.PRIORITY_DEFAULT_IDLE
);
Schema.connect(
'changed::' + this.elt + '-refresh-time',
(schema, key) => {
Mainloop.source_remove(this.timeout);
this.timeout = null;
this.interval = l_limit(Schema.get_int(key));
this.timeout = Mainloop.timeout_add(
this.interval, this.update.bind(this), GLib.PRIORITY_DEFAULT_IDLE);
});
Schema.connect('changed::' + this.elt + '-graph-width', this.resize.bind(this));
if (this.elt === 'thermal') {
Schema.connect('changed::thermal-threshold',
() => {
Mainloop.source_remove(this.timeout);
this.timeout = null;
this.reset_style();
this.timeout = Mainloop.timeout_add(
this.interval, this.update.bind(this), GLib.PRIORITY_DEFAULT_IDLE);
});
}
this.label = new St.Label({text: this.elt === 'memory' ? _('mem') : _(this.elt),
style_class: Style.get('sm-status-label')});
change_text.call(this);
Schema.connect('changed::' + this.elt + '-show-text', change_text.bind(this));
this.menu_visible = Schema.get_boolean(this.elt + '-show-menu');
Schema.connect('changed::' + this.elt + '-show-menu', change_menu.bind(this));
this.actor.add_actor(this.label);
this.text_box = new St.BoxLayout();
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', change_style.bind(this));
this.menu_items = this.create_menu_items();
}
tip_format(unit) {
if (typeof (unit) === 'undefined') {
unit = '%';
}
if (typeof (unit) === 'string') {
let all_unit = unit;
unit = [];
for (let i = 0; i < this.color_name.length; i++) {
unit.push(all_unit);
}
}
for (let i = 0; i < this.color_name.length; i++) {
let tipline = new TipItem();
this.tipmenu.addMenuItem(tipline);
tipline.actor.add(new St.Label({text: _(this.color_name[i])}));
this.tip_labels[i] = new St.Label({text: ''});
tipline.actor.add(this.tip_labels[i]);
this.tip_unit_labels[i] = new St.Label({text: unit[i]});
tipline.actor.add(this.tip_unit_labels[i]);
this.tip_vals[i] = 0;
}
}
// set_tip_unit: function(unit) {
// for (let i = 0;i < this.tip_unit_labels.length;i++) {
// this.tip_unit_labels[i].text = unit[i];
// }
// }
update() {
if (!this.menu_visible && !this.actor.visible) {
return false;
}
this.refresh();
this._apply();
if (this.elt === 'thermal') {
this.threshold();
}
this.chart.update();
for (let i = 0; i < this.tip_vals.length; i++) {
if (this.tip_labels[i]) {
this.tip_labels[i].text = this.tip_vals[i].toString();
}
}
return true;
}
reset_style() {
this.text_items[0].set_style('color: rgba(255, 255, 255, 1)');
}
threshold() {
if (Schema.get_int('thermal-threshold')) {
if (this.temp_over_threshold) {
this.text_items[0].set_style('color: rgba(255, 0, 0, 1)');
} else {
this.text_items[0].set_style('color: rgba(255, 255, 255, 1)');
}
}
}
resize(schema, key) {
let width = Schema.get_int(key);
if (Style.get('') === '-compact') {
width = Math.round(width / 1.5);
}
this.chart.resize(width);
}
destroy() {
TipBox.prototype.destroy.call(this);
if (this.timeout) {
Mainloop.source_remove(this.timeout);
this.timeout = null;
}
}
}
const Battery = class SystemMonitor_Battery extends ElementBase {
constructor() {
super({
elt: 'battery',
item_name: _('Battery'),
color_name: ['batt0'],
icon: '. GThemedIcon battery-good-symbolic battery-good'
});
this.max = 100;
this.icon_hidden = false;
this.percentage = 0;
this.timeString = '-- ';
this._proxy = StatusArea.aggregateMenu._power._proxy;
if (typeof (this._proxy) === 'undefined') {
this._proxy = StatusArea.battery._proxy;
}
this.powerSigID = this._proxy.connect('g-properties-changed', this.update_battery.bind(this));
// need to specify a default icon, since the contructor completes before UPower callback
this.gicon = Gio.icon_new_for_string(this.icon);
this.tip_format('%');
this.update_battery();
this.update_tips();
// this.hide_system_icon();
this.update();
// Schema.connect('changed::' + this.elt + '-hidesystem', this.hide_system_icon.bind(this));
Schema.connect('changed::' + this.elt + '-time', this.update_tips.bind(this));
}
refresh() {
// do nothing here?
}
update_battery() {
// callback function for when battery stats updated.
let battery_found = false;
let isBattery = false;
if (typeof (this._proxy.GetDevicesRemote) === 'undefined') {
let device_type = this._proxy.Type;
isBattery = (device_type === UPower.DeviceKind.BATTERY);
if (isBattery) {
battery_found = true;
let icon = this._proxy.IconName;
let percentage = this._proxy.Percentage;
let seconds = this._proxy.TimeToEmpty;
this.update_battery_value(seconds, percentage, icon);
} else {
// log("[System monitor] No battery found");
this.actor.hide();
this.menu_visible = false;
build_menu_info();
}
} else {
this._proxy.GetDevicesRemote((devices, error) => {
if (error) {
log('[System monitor] Power proxy error: ' + error);
this.actor.hide();
this.menu_visible = false;
build_menu_info();
return;
}
let [result] = devices;
for (let i = 0; i < result.length; i++) {
let [device_id, device_type, icon, percentage, state, seconds] = result[i];
isBattery = (device_type === UPower.DeviceKind.BATTERY);
if (isBattery) {
battery_found = true;
this.update_battery_value(seconds, percentage, icon);
break;
}
}
if (!battery_found) {
// log("[System monitor] No battery found");
this.actor.hide();
this.menu_visible = false;
build_menu_info();
}
});
}
}
update_battery_value(seconds, percentage, icon) {
if (seconds > 60) {
let time = Math.round(seconds / 60);
let minutes = time % 60;
let hours = Math.floor(time / 60);
this.timeString = C_('battery time remaining', '%d:%02d').format(hours, minutes);
} else {
this.timeString = '-- ';
}
this.percentage = Math.ceil(percentage);
this.gicon = Gio.icon_new_for_string(icon);
if (Schema.get_boolean(this.elt + '-display')) {
this.actor.show()
}
if (Schema.get_boolean(this.elt + '-show-menu') && !this.menu_visible) {
this.menu_visible = true;
build_menu_info();
}
}
hide_system_icon(override) {
let value = Schema.get_boolean(this.elt + '-hidesystem');
if (!override) {
value = false;
}
if (value && Schema.get_boolean(this.elt + '-display')) {
if (shell_Version > '3.5') {
if (StatusArea.battery.actor.visible) {
StatusArea.battery.destroy();
this.icon_hidden = true;
}
} else {
for (let Index = 0; Index < Main.panel._rightBox.get_children().length; Index++) {
if (StatusArea.battery === Main.panel._rightBox.get_children()[Index]._delegate) {
Main.panel._rightBox.get_children()[Index].destroy();
StatusArea.battery = null;
this.icon_hidden = true;
break;
}
}
}
} else if (this.icon_hidden) {
if (shell_Version < '3.5') {
let Indicator = new Panel.STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION.battery();
Main.panel.addToStatusArea('battery', Indicator, Panel.STANDARD_STATUS_AREA_ORDER.indexOf('battery'));
} else {
let Indicator = new Panel.PANEL_ITEM_IMPLEMENTATIONS.battery();
Main.panel.addToStatusArea('battery', Indicator, Main.sessionMode.panel.right.indexOf('battery'), 'right');
}
this.icon_hidden = false;
// Main.panel._updatePanel('right');
}
}
get_battery_unit() {
let unitString;
let value = Schema.get_boolean(this.elt + '-time');
if (value) {
unitString = 'h';
} else {
unitString = '%';
}
return unitString;
}
update_tips() {
let unitString = this.get_battery_unit();
if (Schema.get_boolean(this.elt + '-display')) {
this.text_items[2].text = unitString;
}
if (Schema.get_boolean(this.elt + '-show-menu')) {
this.menu_items[1].text = unitString;
}
this.update();
}
_apply() {
let displayString;
let value = Schema.get_boolean(this.elt + '-time');
if (value) {
displayString = this.timeString;
} else {
displayString = this.percentage.toString()
}
if (Schema.get_boolean(this.elt + '-display')) {
this.text_items[0].gicon = this.gicon;
this.text_items[1].text = displayString;
}
if (Schema.get_boolean(this.elt + '-show-menu')) {
this.menu_items[0].text = displayString;
}
this.vals = [this.percentage];
this.tip_vals[0] = Math.round(this.percentage);
}
create_text_items() {
return [
new St.Icon({
gicon: Gio.icon_new_for_string(this.icon),
style_class: Style.get('sm-status-icon')}),
new St.Label({
text: '',
style_class: Style.get('sm-status-value'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Label({
text: this.get_battery_unit(),
style_class: Style.get('sm-perc-label'),
y_align: Clutter.ActorAlign.CENTER})
];
}
create_menu_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: this.get_battery_unit(),
style_class: Style.get('sm-label')})
];
}
destroy() {
ElementBase.prototype.destroy.call(this);
this._proxy.disconnect(this.powerSigID);
}
}
const Cpu = class SystemMonitor_Cpu extends ElementBase {
constructor(cpuid) {
super({
elt: 'cpu',
item_name: _('CPU'),
color_name: ['user', 'system', 'nice', 'iowait', 'other'],
cpuid: -1 // cpuid is -1 when all cores are displayed in the same graph
});
this.max = 100;
this.cpuid = cpuid;
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;
if (cpuid === -1) {
this.max *= this.total_cores;
}
} catch (e) {
this.total_cores = this.get_cores();
global.logError(e)
}
this.last_total = 0;
this.usage = [0, 0, 0, 1, 0];
this.item_name = _('Cpu');
if (cpuid !== -1) {
this.item_name += ' ' + (cpuid + 1);
} // append cpu number to cpu name in popup
// ElementBase.prototype._init.call(this);
this.tip_format();
this.update();
}
refresh() {
GTop.glibtop_get_cpu(this.gtop);
// display global cpu usage on 1 graph
if (this.cpuid === -1) {
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;
} else if (delta < 0) {
this.last = [0, 0, 0, 0, 0];
this.current = [0, 0, 0, 0, 0];
this.last_total = 0;
this.usage = [0, 0, 0, 1, 0];
}
} else {
// display per cpu data
this.current[0] = this.gtop.xcpu_user[this.cpuid];
this.current[1] = this.gtop.xcpu_sys[this.cpuid];
this.current[2] = this.gtop.xcpu_nice[this.cpuid];
this.current[3] = this.gtop.xcpu_idle[this.cpuid];
this.current[4] = this.gtop.xcpu_iowait[this.cpuid];
let delta = (this.gtop.xcpu_total[this.cpuid] - this.last_total) / 100;
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.xcpu_total[this.cpuid];
} else if (delta < 0) {
this.last = [0, 0, 0, 0, 0];
this.current = [0, 0, 0, 0, 0];
this.last_total = 0;
this.usage = [0, 0, 0, 1, 0];
}
}
// GTop.glibtop_get_cpu(this.gtop);
// // display global cpu usage on 1 graph
// if (this.cpuid == -1) {
// 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;
// } else {
// // display cpu usage for given core
// this.current[0] = this.gtop.xcpu_user[this.cpuid];
// this.current[1] = this.gtop.xcpu_sys[this.cpuid];
// this.current[2] = this.gtop.xcpu_nice[this.cpuid];
// this.current[3] = this.gtop.xcpu_idle[this.cpuid];
// this.current[4] = this.gtop.xcpu_iowait[this.cpuid];
// }
//
// let delta = 0;
// if (this.cpuid == -1)
// delta = (this.gtop.total - this.last_total)/(100*this.total_cores);
// else
// delta = (this.gtop.xcpu_total[this.cpuid] - this.last_total)/100;
//
// 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];
// }
// if (this.cpuid == -1)
// this.last_total = this.gtop.total;
// else
// this.last_total = this.gtop.xcpu_total[this.cpuid];
// }
}
_apply() {
let percent = 0;
if (this.cpuid === -1) {
percent = Math.round(((100 * this.total_cores) - this.usage[3]) /
this.total_cores);
} else {
percent = Math.round((100 - this.usage[3]));
}
this.text_items[0].text = this.menu_items[0].text = percent.toString();
let other = 100;
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]);
}
}
get_cores() {
// Getting xcpu_total makes gjs 1.29.18 segfault
// let cores = 0;
// GTop.glibtop_get_cpu(this.gtop);
// let gtop_total = this.gtop.xcpu_total
// for (let i = 0; i < gtop_total.length;i++) {
// if (gtop_total[i] > 0)
// cores++;
// }
// return cores;
return 1;
}
create_text_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-status-value'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Label({
text: '%', style_class: Style.get('sm-perc-label'),
y_align: Clutter.ActorAlign.CENTER})
];
}
create_menu_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: '%',
style_class: Style.get('sm-label')})
];
}
}
// Check if one graph per core must be displayed and create the
// appropriate number of cpu items
function createCpus() {
let array = [];
let numcores = 1;
if (Schema.get_boolean('cpu-individual-cores')) {
// get number of cores
let gtop = new GTop.glibtop_cpu();
try {
numcores = GTop.glibtop_get_sysinfo().ncpu;
} catch (e) {
global.logError(e);
numcores = 1;
}
}
// there are several cores to display,
// instantiate each cpu
if (numcores > 1) {
for (let i = 0; i < numcores; i++) {
array.push(new Cpu(i));
}
} else {
// individual cores option is not set or we failed to
// get the number of cores, create a global cpu item
array.push(new Cpu(-1));
}
return array;
}
const Disk = class SystemMonitor_Disk extends ElementBase {
constructor() {
super({
elt: 'disk',
item_name: _('Disk'),
color_name: ['read', 'write']
});
this.mounts = MountsMonitor.get_mounts();
MountsMonitor.add_listener(this.update_mounts.bind(this));
this.last = [0, 0];
this.usage = [0, 0];
this.last_time = 0;
this.tip_format(_('MiB/s'));
this.update();
}
update_mounts(mounts) {
this.mounts = mounts;
}
refresh() {
let accum = [0, 0];
let file = Gio.file_new_for_path('/proc/diskstats');
file.load_contents_async(null, (source, result) => {
let as_r = source.load_contents_finish(result);
let lines = parse_bytearray(as_r[1]).toString().split('\n');
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
let entry = line.trim().split(/[\s]+/);
if (typeof (entry[1]) === 'undefined') {
break;
}
accum[0] += parseInt(entry[5]);
accum[1] += parseInt(entry[9]);
}
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] = ((accum[i] - this.last[i]) / delta / 1024 / 8);
this.last[i] = accum[i];
}
}
this.last_time = time;
});
}
_apply() {
this.vals = this.usage.slice();
for (let i = 0; i < 2; i++) {
if (this.usage[i] < 10) {
this.usage[i] = Math.round(10 * this.usage[i]) / 10;
} 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].toLocaleString(Locale);
this.menu_items[3].text = this.text_items[4].text = this.tip_vals[1].toLocaleString(Locale);
}
create_text_items() {
return [
new St.Label({
text: _('R'),
style_class: Style.get('sm-status-label')}),
new St.Label({
text: '',
style_class: Style.get('sm-disk-value'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Label({
text: Style.diskunits(),
style_class: Style.get('sm-disk-unit-label'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Label({
text: _('W'),
style_class: Style.get('sm-status-label')}),
new St.Label({
text: '',
style_class: Style.get('sm-disk-value'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Label({
text: Style.diskunits(),
style_class: Style.get('sm-disk-unit-label'),
y_align: Clutter.ActorAlign.CENTER})
];
}
create_menu_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: Style.diskunits(),
style_class: Style.get('sm-label')}),
new St.Label({
text: _('R'),
style_class: Style.get('sm-label')}),
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: Style.diskunits(),
style_class: Style.get('sm-label')}),
new St.Label({
text: ' ' + _('W'),
style_class: Style.get('sm-label')})
];
}
}
const Freq = class SystemMonitor_Freq extends ElementBase {
constructor() {
super({
elt: 'freq',
item_name: _('Freq'),
color_name: ['freq']
});
this.freq = 0;
this.tip_format('MHz');
this.update();
}
refresh() {
let total_frequency = 0;
let num_cpus = GTop.glibtop_get_sysinfo().ncpu;
let i = 0;
let file = Gio.file_new_for_path(`/sys/devices/system/cpu/cpu${i}/cpufreq/scaling_cur_freq`);
var that = this;
file.load_contents_async(null, function cb(source, result) {
let as_r = source.load_contents_finish(result);
total_frequency += parseInt(parse_bytearray(as_r[1]));
if (++i >= num_cpus) {
that.freq = Math.round(total_frequency / num_cpus / 1000);
} else {
file = Gio.file_new_for_path(`/sys/devices/system/cpu/cpu${i}/cpufreq/scaling_cur_freq`);
file.load_contents_async(null, cb.bind(that));
}
});
}
_apply() {
let value = this.freq.toString();
this.text_items[0].text = value + ' ';
this.vals[0] = value;
this.tip_vals[0] = value;
if (Style.get('') !== '-compact') {
this.menu_items[0].text = value;
} else {
this.menu_items[0].text = this._pad(value, 4);
}
}
// pad a string with leading spaces
_pad(number, length) {
var str = '' + number;
while (str.length < length) {
str = ' ' + str;
}
return str;
}
create_text_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-big-status-value'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Label({
text: 'MHz', style_class: Style.get('sm-perc-label'),
y_align: Clutter.ActorAlign.CENTER})
];
}
create_menu_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: 'MHz',
style_class: Style.get('sm-label')})
];
}
}
const Mem = class SystemMonitor_Mem extends ElementBase {
constructor() {
super({
elt: 'memory',
item_name: _('Memory'),
color_name: ['program', 'buffer', 'cache']
});
this.max = 1;
this.gtop = new GTop.glibtop_mem();
this.mem = [0, 0, 0];
GTop.glibtop_get_mem(this.gtop);
this.total = Math.round(this.gtop.total / 1024 / 1024);
let threshold = 4 * 1024; // In MiB
this.useGiB = false;
this._unitConversion = 1024 * 1024;
this._decimals = 100;
if (this.total > threshold) {
this.useGiB = true;
this._unitConversion *= 1024 / this._decimals;
}
this.tip_format();
this.update();
}
refresh() {
GTop.glibtop_get_mem(this.gtop);
if (this.useGiB) {
this.mem[0] = Math.round(this.gtop.user / this._unitConversion);
this.mem[0] /= this._decimals;
this.mem[1] = Math.round(this.gtop.buffer / this._unitConversion);
this.mem[1] /= this._decimals;
this.mem[2] = Math.round(this.gtop.cached / this._unitConversion);
this.mem[2] /= this._decimals;
this.total = Math.round(this.gtop.total / this._unitConversion);
this.total /= this._decimals;
} else {
this.mem[0] = Math.round(this.gtop.user / this._unitConversion);
this.mem[1] = Math.round(this.gtop.buffer / this._unitConversion);
this.mem[2] = Math.round(this.gtop.cached / this._unitConversion);
this.total = Math.round(this.gtop.total / this._unitConversion);
}
}
_pad(number) {
if (this.useGiB) {
if (number < 1) {
// examples: 0.01, 0.10, 0.88
return number.toLocaleString(Locale, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
// examples: 5.85, 16.0, 128
return number.toLocaleString(Locale, {minimumSignificantDigits: 3, maximumSignificantDigits: 3});
}
return number.toLocaleString(Locale);
}
_apply() {
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.tip_vals[0].toLocaleString(Locale);
if (Style.get('') !== '-compact') {
this.menu_items[3].text = this._pad(this.mem[0]) +
' / ' + this._pad(this.total);
} else {
this.menu_items[3].text = this._pad(this.mem[0]) +
'/' + this._pad(this.total);
}
}
create_text_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-status-value'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Label({
text: '%', style_class: Style.get('sm-perc-label'),
y_align: Clutter.ActorAlign.CENTER})
];
}
create_menu_items() {
let unit = _('MiB');
if (this.useGiB) {
unit = _('GiB');
}
return [
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: '%',
style_class: Style.get('sm-label')}),
new St.Label({
text: '',
style_class: Style.get('sm-label')}),
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({text: unit,
style_class: Style.get('sm-label')})
];
}
}
const Net = class SystemMonitor_Net extends ElementBase {
constructor() {
super({
elt: 'net',
item_name: _('Net'),
color_name: ['down', 'downerrors', 'up', 'uperrors', 'collisions']
});
this.speed_in_bits = false;
this.ifs = [];
this.client = libnm_glib ? NM.Client.new() : NM.Client.new(null);
this.update_iface_list();
if (!this.ifs.length) {
let net_lines = Shell.get_file_contents_utf8_sync('/proc/net/dev').split('\n');
for (let i = 2; i < net_lines.length - 1; i++) {
let ifc = net_lines[i].replace(/^\s+/g, '').split(':')[0];
if (Shell.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.tip_format([_('KiB/s'), '/s', _('KiB/s'), '/s', '/s']);
this.update_units();
Schema.connect('changed::' + this.elt + '-speed-in-bits', this.update_units.bind(this));
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', this.update_iface_list.bind(this));
}
} catch (e) {
global.logError('Please install Network Manager Gobject Introspection Bindings: ' + e);
}
this.update();
}
update_units() {
this.speed_in_bits = Schema.get_boolean(this.elt + '-speed-in-bits');
}
update_iface_list() {
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_ip_iface() || iface_list[j].get_iface());
}
}
} catch (e) {
global.logError('Please install Network Manager Gobject Introspection Bindings');
}
}
refresh() {
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;
accum[1] += this.gtop.errors_in;
accum[2] += this.gtop.bytes_out;
accum[3] += this.gtop.errors_out;
accum[4] += this.gtop.collisions;
}
let time = GLib.get_monotonic_time() * 0.001024;
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.vals[i] = this.usage[i];
}
}
this.last_time = time;
}
// pad a string with leading spaces
_pad(number, length) {
var str = '' + number;
while (str.length < length) {
str = ' ' + str;
}
return str;
}
_apply() {
this.tip_vals = this.usage;
if (this.speed_in_bits) {
this.tip_vals[0] = Math.round(this.tip_vals[0] * 8.192);
this.tip_vals[2] = Math.round(this.tip_vals[2] * 8.192);
if (this.tip_vals[0] < 1000) {
this.text_items[2].text = Style.netunits_kbits();
this.menu_items[1].text = this.tip_unit_labels[0].text = _('kbit/s');
} else if (this.tip_vals[0] < 1000000) {
this.text_items[2].text = Style.netunits_mbits();
this.menu_items[1].text = this.tip_unit_labels[0].text = _('Mbit/s');
this.tip_vals[0] = (this.tip_vals[0] / 1000).toPrecision(3);
} else {
this.text_items[2].text = Style.netunits_gbits();
this.menu_items[1].text = this.tip_unit_labels[0].text = _('Gbit/s');
this.tip_vals[0] = (this.tip_vals[0] / 1000000).toPrecision(3);
}
if (this.tip_vals[2] < 1000) {
this.text_items[5].text = Style.netunits_kbits();
this.menu_items[4].text = this.tip_unit_labels[2].text = _('kbit/s');
} else if (this.tip_vals[2] < 1000000) {
this.text_items[5].text = Style.netunits_mbits();
this.menu_items[4].text = this.tip_unit_labels[2].text = _('Mbit/s');
this.tip_vals[2] = (this.tip_vals[2] / 1000).toPrecision(3);
} else {
this.text_items[5].text = Style.netunits_gbits();
this.menu_items[4].text = this.tip_unit_labels[2].text = _('Gbit/s');
this.tip_vals[2] = (this.tip_vals[2] / 1000000).toPrecision(3);
}
} else {
if (this.tip_vals[0] < 1024) {
this.text_items[2].text = Style.netunits_kbytes();
this.menu_items[1].text = this.tip_unit_labels[0].text = _('KiB/s');
} else if (this.tip_vals[0] < 1048576) {
this.text_items[2].text = Style.netunits_mbytes();
this.menu_items[1].text = this.tip_unit_labels[0].text = _('MiB/s');
this.tip_vals[0] = (this.tip_vals[0] / 1024).toPrecision(3);
} else {
this.text_items[2].text = Style.netunits_gbytes();
this.menu_items[1].text = this.tip_unit_labels[0].text = _('GiB/s');
this.tip_vals[0] = (this.tip_vals[0] / 1048576).toPrecision(3);
}
if (this.tip_vals[2] < 1024) {
this.text_items[5].text = Style.netunits_kbytes();
this.menu_items[4].text = this.tip_unit_labels[2].text = _('KiB/s');
} else if (this.tip_vals[2] < 1048576) {
this.text_items[5].text = Style.netunits_mbytes();
this.menu_items[4].text = this.tip_unit_labels[2].text = _('MiB/s');
this.tip_vals[2] = (this.tip_vals[2] / 1024).toPrecision(3);
} else {
this.text_items[5].text = Style.netunits_gbytes();
this.menu_items[4].text = this.tip_unit_labels[2].text = _('GiB/s');
this.tip_vals[2] = (this.tip_vals[2] / 1048576).toPrecision(3);
}
}
if (Style.get('') !== '-compact') {
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();
} else {
this.menu_items[0].text = this.text_items[1].text = this._pad(this.tip_vals[0].toString(), 4);
this.menu_items[3].text = this.text_items[4].text = this._pad(this.tip_vals[2].toString(), 4);
}
}
create_text_items() {
return [
new St.Icon({
icon_size: 2 * IconSize / 3 * Style.iconsize(),
icon_name: 'go-down-symbolic'}),
new St.Label({
text: '',
style_class: Style.get('sm-net-value'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Label({
text: _('KiB/s'),
style_class: Style.get('sm-net-unit-label'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Icon({
icon_size: 2 * IconSize / 3 * Style.iconsize(),
icon_name: 'go-up-symbolic'}),
new St.Label({
text: '',
style_class: Style.get('sm-net-value'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Label({
text: _('KiB/s'),
style_class: Style.get('sm-net-unit-label'),
y_align: Clutter.ActorAlign.CENTER})
];
}
create_menu_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: _('KiB/s'),
style_class: Style.get('sm-label')}),
new St.Label({
text: _(' ↓'),
style_class: Style.get('sm-label')}),
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: _(' KiB/s'),
style_class: Style.get('sm-label')}),
new St.Label({
text: _(' ↑'),
style_class: Style.get('sm-label')})
];
}
}
const Swap = class SystemMonitor_Swap extends ElementBase {
constructor() {
super({
elt: 'swap',
item_name: _('Swap'),
color_name: ['used']
});
this.max = 1;
this.gtop = new GTop.glibtop_swap();
GTop.glibtop_get_swap(this.gtop);
this.total = Math.round(this.gtop.total / 1024 / 1024);
let threshold = 4 * 1024; // In MiB
this.useGiB = false;
this._unitConversion = 1024 * 1024;
this._decimals = 100;
if (this.total > threshold) {
this.useGiB = true;
this._unitConversion *= 1024 / this._decimals;
}
this.tip_format();
this.update();
}
refresh() {
GTop.glibtop_get_swap(this.gtop);
if (this.useGiB) {
this.swap = Math.round(this.gtop.used / this._unitConversion);
this.swap /= this._decimals;
this.total = Math.round(this.gtop.total / this._unitConversion);
this.total /= this._decimals;
} else {
this.swap = Math.round(this.gtop.used / this._unitConversion);
this.total = Math.round(this.gtop.total / this._unitConversion);
}
}
_pad(number) {
if (this.useGiB) {
if (number < 1) {
// examples: 0.01, 0.10, 0.88
return number.toLocaleString(Locale, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
// examples: 5.85, 16.0, 128
return number.toLocaleString(Locale, {minimumSignificantDigits: 3, maximumSignificantDigits: 3});
}
return number.toLocaleString(Locale);
}
_apply() {
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.tip_vals[0].toString();
if (Style.get('') !== '-compact') {
this.menu_items[3].text = this._pad(this.swap) +
' / ' + this._pad(this.total);
} else {
this.menu_items[3].text = this._pad(this.swap) +
'/' + this._pad(this.total);
}
}
create_text_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-status-value'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Label({
text: '%',
style_class: Style.get('sm-perc-label'),
y_align: Clutter.ActorAlign.CENTER})
];
}
create_menu_items() {
let unit = 'MiB';
if (this.useGiB) {
unit = 'GiB';
}
return [
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: '%',
style_class: Style.get('sm-label')}),
new St.Label({
text: '',
style_class: Style.get('sm-label')}),
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: _(unit),
style_class: Style.get('sm-label')})
];
}
}
const Thermal = class SystemMonitor_Thermal extends ElementBase {
constructor() {
super({
elt: 'thermal',
item_name: _('Thermal'),
color_name: ['tz0']
});
this.max = 100;
this.item_name = _('Thermal');
this.temperature = '-- ';
this.fahrenheit_unit = Schema.get_boolean(this.elt + '-fahrenheit-unit');
this.display_error = true;
this.tip_format(this.temperature_symbol());
Schema.connect('changed::' + this.elt + '-sensor-file', this.refresh.bind(this));
this.update();
}
refresh() {
let sfile = Schema.get_string(this.elt + '-sensor-file');
if (GLib.file_test(sfile, GLib.FileTest.EXISTS)) {
let file = Gio.file_new_for_path(sfile);
file.load_contents_async(null, (source, result) => {
let as_r = source.load_contents_finish(result)
this.temperature = Math.round(parseInt(parse_bytearray(as_r[1])) / 1000);
});
} else if (this.display_error) {
global.logError('error reading: ' + sfile);
this.display_error = false;
}
this.fahrenheit_unit = Schema.get_boolean(this.elt + '-fahrenheit-unit');
}
_apply() {
this.text_items[0].text = this.menu_items[0].text = this.temperature_text();
this.temp_over_threshold = this.temperature > Schema.get_int('thermal-threshold');
this.vals = [this.temperature];
this.tip_vals[0] = this.temperature_text();
this.text_items[1].text = this.menu_items[1].text = this.temperature_symbol();
this.tip_unit_labels[0].text = _(this.temperature_symbol());
}
create_text_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-status-value'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Label({
text: this.temperature_symbol(),
style_class: Style.get('sm-temp-label'),
y_align: Clutter.ActorAlign.CENTER})
];
}
create_menu_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: this.temperature_symbol(),
style_class: Style.get('sm-label')})
];
}
temperature_text() {
let temperature = this.temperature;
if (this.fahrenheit_unit) {
temperature = Math.round(temperature * 1.8 + 32);
}
return temperature.toString();
}
temperature_symbol() {
return this.fahrenheit_unit ? '°F' : '°C';
}
}
const Fan = class SystemMonitor_Fan extends ElementBase {
constructor() {
super({
elt: 'fan',
item_name: _('Fan'),
color_name: ['fan0']
});
this.rpm = 0;
this.display_error = true;
this.tip_format(_('rpm'));
Schema.connect('changed::' + this.elt + '-sensor-file', this.refresh.bind(this));
this.update();
}
refresh() {
let sfile = Schema.get_string(this.elt + '-sensor-file');
if (GLib.file_test(sfile, GLib.FileTest.EXISTS)) {
let file = Gio.file_new_for_path(sfile);
file.load_contents_async(null, (source, result) => {
let as_r = source.load_contents_finish(result)
this.rpm = parseInt(parse_bytearray(as_r[1]));
});
} else if (this.display_error) {
global.logError('error reading: ' + sfile);
this.display_error = false;
}
}
_apply() {
this.text_items[0].text = this.rpm.toString();
this.menu_items[0].text = this.rpm.toString();
this.vals = [this.rpm / 10];
this.tip_vals[0] = this.rpm;
}
create_text_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-status-value'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Label({
text: _('rpm'), style_class: Style.get('sm-unit-label'),
y_align: Clutter.ActorAlign.CENTER})
];
}
create_menu_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: _('rpm'),
style_class: Style.get('sm-label')})
];
}
}
const Gpu = class SystemMonitor_Gpu extends ElementBase {
constructor() {
super({
elt: 'gpu',
item_name: _('GPU'),
color_name: ['used', 'memory']
});
this.max = 100;
this.item_name = _('GPU');
this.mem = 0;
this.total = 0;
this.tip_format();
this.update();
}
_unit(total) {
this.total = total;
let threshold = 4 * 1024; // In MiB
this.useGiB = false;
this._unitConversion = 1;
this._decimals = 100;
if (this.total > threshold) {
this.useGiB = true;
this._unitConversion *= 1024 / this._decimals;
}
}
refresh() {
// Run asynchronously, to avoid shell freeze
try {
let path = Me.dir.get_path();
let script = ['/bin/bash', path + '/gpu_usage.sh'];
// Create subprocess and capture STDOUT
let proc = new Gio.Subprocess({argv: script, flags: Gio.SubprocessFlags.STDOUT_PIPE});
proc.init(null);
// Asynchronously call the output handler when script output is ready
proc.communicate_utf8_async(null, null, Lang.bind(this, this._handleOutput));
} catch (err) {
global.logError(err.message);
}
}
_handleOutput(proc, result) {
let [ok, output, ] = proc.communicate_utf8_finish(result);
if (ok) {
this._readTemperature(output);
} else {
global.logError('gpu_usage.sh invocation failed');
}
}
_sanitizeUsageValue(val) {
val = parseInt(val);
if (isNaN(val)) {
val = 0
}
return val;
}
_readTemperature(procOutput) {
let usage = procOutput.split('\n');
let memTotal = this._sanitizeUsageValue(usage[0]);
let memUsed = this._sanitizeUsageValue(usage[1]);
this.percentage = this._sanitizeUsageValue(usage[2]);
if (typeof this.useGiB === 'undefined') {
this._unit(memTotal);
this._update_unit();
}
if (this.useGiB) {
this.mem = Math.round(memUsed / this._unitConversion);
this.mem /= this._decimals;
this.total = Math.round(memTotal / this._unitConversion);
this.total /= this._decimals;
} else {
this.mem = Math.round(memUsed / this._unitConversion);
this.total = Math.round(memTotal / this._unitConversion);
}
}
_pad(number) {
if (this.useGiB) {
if (number < 1) {
// examples: 0.01, 0.10, 0.88
return number.toFixed(2);
}
// examples: 5.85, 16.0, 128
return number.toPrecision(3);
}
return number;
}
_update_unit() {
let unit = _('MiB');
if (this.useGiB) {
unit = _('GiB');
}
this.menu_items[4].text = unit;
}
_apply() {
this.tip_unit_labels[1].text = "/ " + this.total + " " + this.menu_items[4].text;
if (this.total === 0) {
this.vals = [0, 0];
this.tip_vals = [0, 0];
} else {
// we subtract percentage from memory because we do not want memory to be
// "accumulated" in the chart with utilization; these two measures should be
// independent
this.vals = [this.percentage, this.mem / this.total * 100 - this.percentage];
this.tip_vals = [Math.round(this.vals[0]), this.mem];
}
this.text_items[0].text = this.tip_vals[0].toString();
this.menu_items[0].text = this.tip_vals[0].toLocaleString(Locale);
if (Style.get('') !== '-compact') {
this.menu_items[3].text = this._pad(this.mem).toLocaleString(Locale) +
' / ' + this._pad(this.total).toLocaleString(Locale);
} else {
this.menu_items[3].text = this._pad(this.mem).toLocaleString(Locale) +
'/' + this._pad(this.total).toLocaleString(Locale);
}
}
create_text_items() {
return [
new St.Label({
text: '',
style_class: Style.get('sm-status-value'),
y_align: Clutter.ActorAlign.CENTER}),
new St.Label({
text: '%',
style_class: Style.get('sm-perc-label'),
y_align: Clutter.ActorAlign.CENTER})
];
}
create_menu_items() {
let unit = _('MiB');
if (this.useGiB) {
unit = _('GiB');
}
return [
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: '%',
style_class: Style.get('sm-label')}),
new St.Label({
text: '',
style_class: Style.get('sm-label')}),
new St.Label({
text: '',
style_class: Style.get('sm-value')}),
new St.Label({
text: unit,
style_class: Style.get('sm-label')})
];
}
}
const Icon = class SystemMonitor_Icon {
constructor() {
this.actor = new St.Icon({
icon_name: 'org.gnome.SystemMonitor-symbolic',
style_class: 'system-status-icon'
});
this.actor.visible = Schema.get_boolean('icon-display');
Schema.connect(
'changed::icon-display',
() => {
this.actor.visible = Schema.get_boolean('icon-display');
}
);
}
}
function init() {
log('[System monitor] applet init from ' + extension.path);
Convenience.initTranslations();
// Get locale, needed as an argument for toLocaleString() since GNOME Shell 3.24
// See: mozjs library bug https://bugzilla.mozilla.org/show_bug.cgi?id=999003
Locale = GLib.get_language_names()[0];
if (Locale.indexOf('_') !== -1) {
Locale = Locale.split('_')[0];
}
IconSize = Math.round(Panel.PANEL_ICON_SIZE * 4 / 5);
}
function enable() {
log('[System monitor] applet enabling');
Schema = Convenience.getSettings();
Style = new smStyleManager();
MountsMonitor = new smMountsMonitor();
Background = color_from_string(Schema.get_string('background'));
if (!(smDepsGtop && smDepsNM)) {
Main.__sm = {
smdialog: new smDialog()
};
let dialog_timeout = Mainloop.timeout_add_seconds(
1,
() => {
Main.__sm.smdialog.open();
Mainloop.source_remove(dialog_timeout);
return true;
});
} else {
let panel = Main.panel._rightBox;
StatusArea = Main.panel._statusArea;
if (typeof (StatusArea) === 'undefined') {
StatusArea = Main.panel.statusArea;
}
if (Schema.get_boolean('center-display')) {
panel = Main.panel._centerBox;
}
MountsMonitor.connect();
// Debug
Main.__sm = {
tray: new PanelMenu.Button(0.5),
icon: new Icon(),
pie: new Pie(),
bar: new Bar(),
elts: [],
};
// Items to Monitor
Main.__sm.elts = createCpus();
Main.__sm.elts.push(new Freq());
Main.__sm.elts.push(new Mem());
Main.__sm.elts.push(new Swap());
Main.__sm.elts.push(new Net());
Main.__sm.elts.push(new Disk());
Main.__sm.elts.push(new Gpu());
Main.__sm.elts.push(new Thermal());
Main.__sm.elts.push(new Fan());
Main.__sm.elts.push(new Battery());
let tray = Main.__sm.tray;
let elts = Main.__sm.elts;
if (Schema.get_boolean('move-clock')) {
let dateMenu = Main.panel.statusArea.dateMenu;
Main.panel._centerBox.remove_actor(dateMenu.container);
Main.panel._addToPanelBox('dateMenu', dateMenu, -1, Main.panel._rightBox);
tray.clockMoved = true;
}
Schema.connect('changed::background', (schema, key) => {
Background = color_from_string(Schema.get_string(key));
});
Main.panel._addToPanelBox('system-monitor', tray, 1, panel);
// The spacing adds a distance between the graphs/text on the top bar
let spacing = Schema.get_boolean('compact-display') ? '1' : '4';
let box = new St.BoxLayout({style: 'spacing: ' + spacing + 'px;'});
if (shell_Version < '3.36') {
tray.actor.add_actor(box);
} else {
tray.add_actor(box);
}
box.add_actor(Main.__sm.icon.actor);
// Add items to panel box
for (let elt in elts) {
box.add_actor(elts[elt].actor);
}
// Build Menu Info Box Table
let menu_info = new PopupMenu.PopupBaseMenuItem({reactive: false});
let menu_info_box = new St.BoxLayout();
menu_info.actor.add(menu_info_box);
Main.__sm.tray.menu.addMenuItem(menu_info, 0);
build_menu_info();
tray.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
let pie_item = Main.__sm.pie;
pie_item.create_menu_item();
tray.menu.addMenuItem(pie_item.menu_item);
let bar_item = Main.__sm.bar;
bar_item.create_menu_item();
tray.menu.addMenuItem(bar_item.menu_item);
change_usage();
Schema.connect('changed::disk-usage-style', change_usage);
tray.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
tray.menu.connect(
'open-state-changed',
function (menu, isOpen) {
if (isOpen) {
Main.__sm.pie.actor.queue_repaint();
menu_timeout = Mainloop.timeout_add_seconds(
5,
() => {
Main.__sm.pie.actor.queue_repaint();
return true;
});
} else {
Mainloop.source_remove(menu_timeout);
}
}
);
let _appSys = Shell.AppSystem.get_default();
let _gsmApp = _appSys.lookup_app('gnome-system-monitor.desktop');
let _gsmPrefs = _appSys.lookup_app('gnome-shell-extension-prefs.desktop');
if (_gsmPrefs === null) {
_gsmPrefs = _appSys.lookup_app('org.gnome.Extensions.desktop');
}
let item;
item = new PopupMenu.PopupMenuItem(_('System Monitor...'));
item.connect('activate', () => {
_gsmApp.activate();
});
tray.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_('Preferences...'));
item.connect('activate', () => {
if (typeof ExtensionUtils.openPrefs === 'function') {
ExtensionUtils.openPrefs();
} else if (_gsmPrefs.get_state() === _gsmPrefs.SHELL_APP_STATE_RUNNING) {
_gsmPrefs.activate();
} else {
let info = _gsmPrefs.get_app_info();
let timestamp = global.display.get_current_time_roundtrip();
info.launch_uris([metadata.uuid], global.create_app_launch_context(timestamp, -1));
}
});
tray.menu.addMenuItem(item);
Main.panel.menuManager.addMenu(tray.menu);
}
log('[System monitor] applet enabling done');
}
function disable() {
// restore clock
if (Main.__sm.tray.clockMoved) {
let dateMenu = Main.panel.statusArea.dateMenu;
Main.panel._rightBox.remove_actor(dateMenu.container);
Main.panel._addToPanelBox('dateMenu', dateMenu, Main.sessionMode.panel.center.indexOf('dateMenu'), Main.panel._centerBox);
}
// restore system power icon if necessary
// workaround bug introduced by multiple cpus init :
// if (Schema.get_boolean('battery-hidesystem') && Main.__sm.elts.battery.icon_hidden) {
// Main.__sm.elts.battery.hide_system_icon(false);
// }
// for (let i in Main.__sm.elts) {
// if (Main.__sm.elts[i].elt == 'battery')
// Main.__sm.elts[i].hide_system_icon(false);
// }
if (MountsMonitor) {
MountsMonitor.disconnect();
MountsMonitor = null;
}
if (Style) {
Style = null;
}
Schema.run_dispose();
for (let eltName in Main.__sm.elts) {
Main.__sm.elts[eltName].destroy();
}
if (shell_Version < '3.36') {
Main.__sm.tray.actor.destroy();
} else {
Main.__sm.tray.destroy();
}
Main.__sm = null;
log('[System monitor] applet disable');
}