1036 lines
32 KiB
JavaScript
1036 lines
32 KiB
JavaScript
import Clutter from 'gi://Clutter';
|
|
import GLib from 'gi://GLib';
|
|
import GObject from 'gi://GObject';
|
|
import Gio from 'gi://Gio';
|
|
import St from 'gi://St';
|
|
import Shell from 'gi://Shell';
|
|
import GTop from 'gi://GTop';
|
|
|
|
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
|
import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
|
|
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
|
import * as ModalDialog from 'resource:///org/gnome/shell/ui/modalDialog.js';
|
|
import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
|
|
|
|
let debugOutput = false;
|
|
let pingDepsGtop = true;
|
|
|
|
function print_info(str) {
|
|
log('[Ping monitor INFO] ' + str);
|
|
}
|
|
|
|
function print_debug(str) {
|
|
if (debugOutput) {
|
|
log('[Ping monitor DEBUG] ' + str);
|
|
}
|
|
}
|
|
|
|
function color_from_string(color) {
|
|
let clutterColor = new Clutter.Color();
|
|
clutterColor = Clutter.Color.from_string(color)[1];
|
|
return clutterColor;
|
|
}
|
|
|
|
const PingStyleManager = class PingMonitor_PingStyleManager {
|
|
constructor() {
|
|
print_debug('pingStyleManager constructor()');
|
|
|
|
this._extension = '';
|
|
this._iconsize = 1;
|
|
this._text_scaling = 1;
|
|
|
|
let interfaceSettings = new Gio.Settings({
|
|
schema: 'org.gnome.desktop.interface'
|
|
});
|
|
this._text_scaling = interfaceSettings.get_double('text-scaling-factor');
|
|
if (!this._text_scaling) {
|
|
this._text_scaling = 1;
|
|
}
|
|
}
|
|
|
|
get(style) {
|
|
return style + this._extension;
|
|
}
|
|
|
|
text_scaling() {
|
|
return this._text_scaling;
|
|
}
|
|
};
|
|
|
|
const PingDialog = GObject.registerClass(
|
|
class PingDialog extends ModalDialog.ModalDialog {
|
|
_init() {
|
|
super._init({styleClass: 'prompt-dialog'});
|
|
|
|
print_debug('PingDialog construct()');
|
|
|
|
let mainContentBox = new St.BoxLayout({
|
|
style_class: 'prompt-dialog-main-layout',
|
|
vertical: false
|
|
});
|
|
this.contentLayout.add_child(mainContentBox);
|
|
|
|
let messageBox = new St.BoxLayout({
|
|
style_class: 'prompt-dialog-message-layout',
|
|
vertical: true
|
|
});
|
|
mainContentBox.add_child(messageBox);
|
|
|
|
this._subjectLabel = new St.Label({
|
|
style_class: 'prompt-dialog-headline',
|
|
text: _('Ping Monitor Extension')
|
|
});
|
|
|
|
messageBox.add_child(this._subjectLabel);
|
|
|
|
const MESSAGE = _('Dependencies Missing\n\
|
|
Please install: \n\
|
|
libgtop and gir bindings\n\
|
|
\t on Ubuntu: gir1.2-gtop-2.0\n\
|
|
\t on Fedora: libgtop2-devel\n\
|
|
\t on Arch: libgtop\n\
|
|
\t on openSUSE: typelib-1_0-GTop-2_0\n');
|
|
|
|
this._descriptionLabel = new St.Label({
|
|
style_class: 'prompt-dialog-description',
|
|
text: MESSAGE
|
|
});
|
|
|
|
messageBox.add_child(this._descriptionLabel);
|
|
|
|
this.setButtons([{
|
|
label: _('Cancel'),
|
|
action: () => {
|
|
this.close();
|
|
},
|
|
key: Clutter.KEY_Escape
|
|
}]);
|
|
}
|
|
});
|
|
|
|
const StatusSquare = class PingMonitor_StatusSquare {
|
|
constructor(height, parent) {
|
|
print_debug('StatusSquare constructor()');
|
|
|
|
this._width = 12;
|
|
this._height = 12;
|
|
this._color = '#ff0000';
|
|
this._activityState = 0;
|
|
this._isPingUpdate = false;
|
|
|
|
this.actor = new St.Widget({
|
|
style_class: 'ping-chart',
|
|
reactive: false,
|
|
width: this._width,
|
|
height: this._height,
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
y_expand: false
|
|
});
|
|
|
|
this.parentC = parent;
|
|
this.data = [];
|
|
this._updateStyle();
|
|
}
|
|
|
|
update(color, isPingUpdate) {
|
|
print_debug('StatusSquare update()');
|
|
|
|
this._color = color;
|
|
this._isPingUpdate = isPingUpdate;
|
|
|
|
if (!this.actor.visible) {
|
|
return;
|
|
}
|
|
|
|
if (this._isPingUpdate) {
|
|
this._activityState = (this._activityState + 1) % 4;
|
|
}
|
|
|
|
this._updateStyle();
|
|
}
|
|
|
|
_updateStyle() {
|
|
print_debug('StatusSquare _updateStyle()');
|
|
|
|
if (!this.actor.visible) {
|
|
return;
|
|
}
|
|
|
|
this.actor.set_style(`
|
|
background-color: ${this._color};
|
|
border-radius: 2px;
|
|
width: ${this._width}px;
|
|
height: ${this._height}px;
|
|
min-width: ${this._width}px;
|
|
min-height: ${this._height}px;
|
|
`);
|
|
}
|
|
|
|
resize(schema, key) {
|
|
print_debug('StatusSquare resize()');
|
|
}
|
|
};
|
|
|
|
const PingTipItem = GObject.registerClass(
|
|
class PingTipItem extends PopupMenu.PopupBaseMenuItem {
|
|
_init() {
|
|
super._init();
|
|
this.remove_style_class_name('popup-menu-item');
|
|
this.add_style_class_name('ping-tooltip-item');
|
|
}
|
|
});
|
|
|
|
const TipMenu = class PingMonitor_TipMenu extends PopupMenu.PopupMenuBase {
|
|
constructor(sourceActor) {
|
|
print_debug('TipMenu constructor()');
|
|
super(sourceActor, 'ping-tooltip-box');
|
|
this.actor = new Clutter.Actor();
|
|
this.actor.add_child(this.box);
|
|
}
|
|
|
|
_shift() {
|
|
print_debug('TipMenu _shift()');
|
|
|
|
let node = this.sourceActor.get_theme_node();
|
|
let contentbox = node.get_content_box(this.sourceActor.get_allocation_box());
|
|
let allocation = Shell.util_get_transformed_allocation(this.sourceActor);
|
|
let monitor = Main.layoutManager.findMonitorForActor(this.sourceActor);
|
|
let [x, y] = [allocation.x1 + contentbox.x1,
|
|
allocation.y1 + contentbox.y1];
|
|
let [cx, cy] = [allocation.x1 + (contentbox.x1 + contentbox.x2) / 2,
|
|
allocation.y1 + (contentbox.y1 + contentbox.y2) / 2];
|
|
let [xm, ym] = [allocation.x1 + contentbox.x2,
|
|
allocation.y1 + 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);
|
|
|
|
if (allocation.y1 / monitor.height > 0.3) {
|
|
tipy = allocation.y1 - height;
|
|
}
|
|
this.actor.set_position(tipx, tipy);
|
|
}
|
|
|
|
open(animate) {
|
|
print_debug('TipMenu open()');
|
|
|
|
if (this.isOpen) {
|
|
return;
|
|
}
|
|
|
|
this.isOpen = true;
|
|
this.actor.show();
|
|
this._shift();
|
|
this.actor.get_parent()?.set_child_above_sibling(this.actor, null);
|
|
this.emit('open-state-changed', true);
|
|
}
|
|
|
|
close(animate) {
|
|
print_debug('TipMenu close()');
|
|
|
|
this.isOpen = false;
|
|
this.actor.hide();
|
|
this.emit('open-state-changed', false);
|
|
}
|
|
};
|
|
|
|
const TipBox = class PingMonitor_TipBox {
|
|
constructor() {
|
|
print_debug('TipBox constructor()');
|
|
|
|
this.show_tooltip = true;
|
|
this.actor = new St.BoxLayout({
|
|
reactive: true,
|
|
y_align: Clutter.ActorAlign.CENTER
|
|
});
|
|
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) {
|
|
print_debug('TipBox set_tip()');
|
|
|
|
if (this.tipmenu) {
|
|
this.tipmenu.destroy();
|
|
}
|
|
this.tipmenu = tipmenu;
|
|
if (this.tipmenu) {
|
|
Main.uiGroup.add_child(this.tipmenu.actor);
|
|
this.hide_tip();
|
|
}
|
|
}
|
|
|
|
show_tip() {
|
|
print_debug('TipBox show_tip()');
|
|
|
|
if (!this.tipmenu) {
|
|
return;
|
|
}
|
|
this.tipmenu.open();
|
|
if (this.in_to) {
|
|
GLib.Source.remove(this.in_to);
|
|
this.in_to = 0;
|
|
}
|
|
}
|
|
|
|
hide_tip() {
|
|
print_debug('TipBox hide_tip()');
|
|
|
|
if (!this.tipmenu) {
|
|
return;
|
|
}
|
|
this.tipmenu.close();
|
|
if (this.out_to) {
|
|
GLib.Source.remove(this.out_to);
|
|
this.out_to = 0;
|
|
}
|
|
if (this.in_to) {
|
|
GLib.Source.remove(this.in_to);
|
|
this.in_to = 0;
|
|
}
|
|
}
|
|
|
|
on_enter() {
|
|
print_debug('TipBox on_enter()');
|
|
|
|
let show_tooltip = this.show_tooltip;
|
|
|
|
if (!show_tooltip) {
|
|
return;
|
|
}
|
|
|
|
if (this.out_to) {
|
|
GLib.Source.remove(this.out_to);
|
|
this.out_to = 0;
|
|
}
|
|
if (!this.in_to) {
|
|
this.in_to = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
|
|
this.show_tip();
|
|
this.in_to = 0;
|
|
return GLib.SOURCE_REMOVE;
|
|
});
|
|
}
|
|
}
|
|
|
|
on_leave() {
|
|
print_debug('TipBox on_leave()');
|
|
|
|
if (this.in_to) {
|
|
GLib.Source.remove(this.in_to);
|
|
this.in_to = 0;
|
|
}
|
|
if (!this.out_to) {
|
|
this.out_to = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
|
|
this.hide_tip();
|
|
this.out_to = 0;
|
|
return GLib.SOURCE_REMOVE;
|
|
});
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
print_debug('TipBox destroy()');
|
|
|
|
if (this.in_to) {
|
|
GLib.Source.remove(this.in_to);
|
|
this.in_to = 0;
|
|
}
|
|
|
|
if (this.out_to) {
|
|
GLib.Source.remove(this.out_to);
|
|
this.out_to = 0;
|
|
}
|
|
|
|
this.actor.destroy();
|
|
}
|
|
};
|
|
|
|
const ElementBase = class PingMonitor_ElementBase extends TipBox {
|
|
constructor(properties, settings, extensionPath) {
|
|
super();
|
|
print_debug('ElementBase constructor()');
|
|
|
|
this._settings = settings;
|
|
this._extensionPath = extensionPath;
|
|
this.elt = '';
|
|
this.tag = '';
|
|
this.name = '';
|
|
this.show_name = false;
|
|
this.color_name = [];
|
|
this.text_items = [];
|
|
this.menu_items = [];
|
|
this.menu_visible = true;
|
|
this.color = '#ff0000';
|
|
this.refresh_interval = 5000;
|
|
this.visible = true;
|
|
this.timeout = undefined;
|
|
|
|
this._pingStdout = null;
|
|
this._pingDataStdout = null;
|
|
this._pingStderr = null;
|
|
this._pingDataStderr = null;
|
|
|
|
this._prepareToDestroy = false;
|
|
|
|
Object.assign(this, properties);
|
|
|
|
this.vals = [];
|
|
this.tip_labels = [];
|
|
this.tip_vals = [];
|
|
this.tip_unit_labels = [];
|
|
|
|
this.chart = new StatusSquare(Math.round(20 * 4 / 5), this);
|
|
this._settings.connect('changed::background', () => {
|
|
this.chart._updateStyle();
|
|
});
|
|
|
|
this.actor.visible = this.visible;
|
|
|
|
this.interval = this.refresh_interval;
|
|
this.add_timeout();
|
|
|
|
this.label = new St.Label({
|
|
text: this.name,
|
|
style_class: 'ping-status-label',
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
x_expand: false,
|
|
y_expand: false,
|
|
natural_height_set: false
|
|
});
|
|
this.label.visible = this.show_name;
|
|
|
|
this.menu_visible = true;
|
|
|
|
this.actor.add_child(this.label);
|
|
this.text_box = new St.BoxLayout();
|
|
|
|
this.text_items = this.create_text_items();
|
|
this.actor.add_child(this.chart.actor);
|
|
this.text_box.visible = true;
|
|
this.chart.actor.visible = this.visible;
|
|
this.menu_items = this.create_menu_items();
|
|
}
|
|
|
|
add_timeout() {
|
|
this.remove_timeout();
|
|
print_debug('Add timeout: ' + this.tag);
|
|
if (!this._prepareToDestroy) {
|
|
this.timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, this.interval, () => {
|
|
return this.update();
|
|
});
|
|
}
|
|
}
|
|
|
|
remove_timeout() {
|
|
print_debug('Remove (try) timeout: ' + this.tag);
|
|
if (this.timeout !== undefined) {
|
|
print_debug('Remove timeout: ' + this.tag);
|
|
GLib.Source.remove(this.timeout);
|
|
this.timeout = undefined;
|
|
}
|
|
}
|
|
|
|
tip_format() {
|
|
print_debug('ElementBase tip_format()');
|
|
|
|
for (let i = 0; i < this.color_name.length; i++) {
|
|
let tipline = new PingTipItem();
|
|
this.tipmenu.addMenuItem(tipline);
|
|
this.tip_labels[i] = new St.Label({text: ''});
|
|
tipline.add_child(this.tip_labels[i]);
|
|
this.tip_vals[i] = 0;
|
|
}
|
|
}
|
|
|
|
update() {
|
|
print_debug('ElementBase update()');
|
|
|
|
this.remove_timeout();
|
|
|
|
if (!this.menu_visible && !this.actor.visible) {
|
|
return false;
|
|
}
|
|
this.refresh();
|
|
|
|
return GLib.SOURCE_CONTINUE;
|
|
}
|
|
|
|
updateDrawing() {
|
|
this._apply();
|
|
this.chart.update(this.color, true);
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
print_debug('ElementBase destroy()');
|
|
|
|
this.remove_timeout();
|
|
TipBox.prototype.destroy.call(this);
|
|
}
|
|
|
|
stop() {
|
|
this._prepareToDestroy = true;
|
|
this.remove_timeout();
|
|
}
|
|
|
|
isRunning() {
|
|
return (
|
|
this._pingStdout != null ||
|
|
this._pingDataStdout != null ||
|
|
this._pingStderr != null ||
|
|
this._pingDataStderr != null
|
|
);
|
|
}
|
|
};
|
|
|
|
const Ping = class PingMonitor_Ping extends ElementBase {
|
|
constructor(id, tag, name, address, ping_count, ping_interval,
|
|
ping_deadline, refresh_interval, active, visible,
|
|
show_name, show_address, show_tooltip, warning_threshold,
|
|
settings, extensionPath) {
|
|
if (show_address) {
|
|
name = name + '\n' + address;
|
|
}
|
|
super({
|
|
elt: 'ping',
|
|
tag: tag,
|
|
name: name,
|
|
show_name: show_name,
|
|
visible: visible,
|
|
refresh_interval: refresh_interval,
|
|
color_name: ['used'],
|
|
}, settings, extensionPath);
|
|
|
|
print_debug('Ping constructor()');
|
|
|
|
this.ping_message = '';
|
|
this.id = id;
|
|
this.address = address;
|
|
this.ping_count = ping_count;
|
|
this.ping_interval = ping_interval;
|
|
this.ping_deadline = ping_deadline;
|
|
this.active = active;
|
|
this.show_address = show_address;
|
|
this.show_tooltip = show_tooltip;
|
|
this.warning_threshold = warning_threshold;
|
|
this.tip_format();
|
|
this.update();
|
|
}
|
|
|
|
_pingReadStdout() {
|
|
this._pingDataStdout.fill_async(-1, GLib.PRIORITY_DEFAULT, null, (stream, result) => {
|
|
if (stream.fill_finish(result) == 0) {
|
|
try {
|
|
let buffer = stream.peek_buffer();
|
|
if (buffer instanceof Uint8Array) {
|
|
this._pingOutput = new TextDecoder().decode(buffer);
|
|
} else {
|
|
this._pingOutput = buffer.toString();
|
|
}
|
|
if (this._pingOutput) {
|
|
print_debug('Ping info: ' + this._pingOutput);
|
|
|
|
let firstLine = this._pingOutput.match(/[\w .:()]+\n/m);
|
|
print_debug('First line: ' + firstLine[0]);
|
|
|
|
let lastLines = this._pingOutput.match(/---[\w\W]+/m);
|
|
lastLines[0] = lastLines[0].replace(/^\s+|\s+$/g, '');
|
|
print_debug('Last lines: ' + lastLines[0]);
|
|
|
|
this.ping_message = firstLine[0] + lastLines[0];
|
|
print_debug('Ping info: ' + this.ping_message);
|
|
|
|
let loss = this._pingOutput.match(/received, (\d*)/m);
|
|
let times = this._pingOutput.match(/mdev = (\d*.\d*)\/(\d*.\d*)\/(\d*.\d*)\/(\d*.\d*)/m);
|
|
|
|
if (times != null && times.length == 5 &&
|
|
loss != null && loss.length == 2) {
|
|
print_debug('loss: ' + loss[1]);
|
|
print_debug('min: ' + times[1]);
|
|
print_debug('avg: ' + times[2]);
|
|
print_debug('max: ' + times[3]);
|
|
print_debug('mdev: ' + times[4]);
|
|
|
|
if (loss[1] != 0 && loss[1] != 100) {
|
|
if (!this._prepareToDestroy) {
|
|
this.color = this._settings.get_string('ping-loss-color');
|
|
}
|
|
} else if (loss[1] == 100) {
|
|
if (!this._prepareToDestroy) {
|
|
this.color = this._settings.get_string('ping-bad-color');
|
|
}
|
|
} else if (times[3] > this.warning_threshold) {
|
|
if (!this._prepareToDestroy) {
|
|
this.color = this._settings.get_string('ping-warning-color');
|
|
}
|
|
} else {
|
|
if (!this._prepareToDestroy) {
|
|
this.color = this._settings.get_string('ping-good-color');
|
|
}
|
|
}
|
|
} else {
|
|
if (!this._prepareToDestroy) {
|
|
this.color = this._settings.get_string('ping-bad-color');
|
|
}
|
|
}
|
|
if (!this._prepareToDestroy) {
|
|
this.updateDrawing();
|
|
}
|
|
}
|
|
} catch (e) {
|
|
print_info(e.toString());
|
|
if (!this._prepareToDestroy) {
|
|
this.color = this._settings.get_string('ping-bad-color');
|
|
this.updateDrawing();
|
|
}
|
|
}
|
|
this._pingStdout.close(null);
|
|
this._pingStdout = null;
|
|
this._pingDataStdout.close(null);
|
|
this._pingDataStdout = null;
|
|
|
|
this.add_timeout();
|
|
return;
|
|
}
|
|
|
|
stream.set_buffer_size(2 * stream.get_buffer_size());
|
|
this._pingReadStdout();
|
|
});
|
|
}
|
|
|
|
_pingReadStderr() {
|
|
this._pingDataStderr.fill_async(-1, GLib.PRIORITY_DEFAULT, null, (stream, result) => {
|
|
if (stream.fill_finish(result) == 0) {
|
|
try {
|
|
let buffer = stream.peek_buffer();
|
|
if (buffer instanceof Uint8Array) {
|
|
this._pingOutputErr = new TextDecoder().decode(buffer);
|
|
} else {
|
|
this._pingOutputErr = buffer.toString();
|
|
}
|
|
if (this._pingOutputErr) {
|
|
this.ping_message = this._pingOutputErr;
|
|
print_debug('Ping error: ' + this.ping_message);
|
|
|
|
if (!this._prepareToDestroy) {
|
|
this.color = this._settings.get_string('ping-bad-color');
|
|
this.updateDrawing();
|
|
}
|
|
}
|
|
} catch (e) {
|
|
print_info(e.toString());
|
|
if (!this._prepareToDestroy) {
|
|
this.color = this._settings.get_string('ping-bad-color');
|
|
this.updateDrawing();
|
|
}
|
|
}
|
|
this._pingStderr.close(null);
|
|
this._pingStderr = null;
|
|
this._pingDataStderr.close(null);
|
|
this._pingDataStderr = null;
|
|
|
|
return;
|
|
}
|
|
|
|
stream.set_buffer_size(2 * stream.get_buffer_size());
|
|
this._pingReadStderr();
|
|
});
|
|
}
|
|
|
|
refresh() {
|
|
print_debug('Ping refresh()');
|
|
|
|
try {
|
|
let script = [
|
|
'/bin/bash',
|
|
this._extensionPath + '/ping.sh',
|
|
this.address,
|
|
'' + this.ping_count,
|
|
'' + this.ping_deadline,
|
|
'' + this.ping_interval
|
|
];
|
|
|
|
let success;
|
|
[success, this.child_pid, this.in_fd, this.out_fd, this.err_fd] = GLib.spawn_async_with_pipes(
|
|
null,
|
|
script,
|
|
null,
|
|
GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
|
null
|
|
);
|
|
|
|
this._pingStdout = new Gio.UnixInputStream({fd: this.out_fd, close_fd: true});
|
|
this._pingDataStdout = new Gio.DataInputStream({base_stream: this._pingStdout});
|
|
this._pingStderr = new Gio.UnixInputStream({fd: this.err_fd, close_fd: true});
|
|
this._pingDataStderr = new Gio.DataInputStream({base_stream: this._pingStderr});
|
|
new Gio.UnixOutputStream({fd: this.in_fd, close_fd: true}).close(null);
|
|
|
|
this._tagWatchChild = GLib.child_watch_add(GLib.PRIORITY_DEFAULT, this.child_pid,
|
|
(pid, status, data) => {
|
|
GLib.Source.remove(this._tagWatchChild);
|
|
GLib.spawn_close_pid(pid);
|
|
this.child_pid = undefined;
|
|
}
|
|
);
|
|
|
|
this._pingReadStdout();
|
|
this._pingReadStderr();
|
|
} catch (e) {
|
|
print_info(e.toString());
|
|
}
|
|
}
|
|
|
|
_apply() {
|
|
print_debug('Ping _apply()');
|
|
|
|
this.menu_items[0].text = this.ping_message;
|
|
this.tip_vals[0] = this.ping_message;
|
|
}
|
|
|
|
create_text_items() {
|
|
print_debug('Ping create_text_items()');
|
|
|
|
return [
|
|
new St.Label({
|
|
text: '',
|
|
style_class: 'ping-status-value',
|
|
y_align: Clutter.ActorAlign.CENTER
|
|
}),
|
|
new St.Label({
|
|
text: '%',
|
|
style_class: 'ping-perc-label',
|
|
y_align: Clutter.ActorAlign.CENTER
|
|
})
|
|
];
|
|
}
|
|
|
|
create_menu_items() {
|
|
print_debug('Ping create_menu_items()');
|
|
|
|
return [
|
|
new St.Label({
|
|
text: '',
|
|
style_class: 'ping-value-left'
|
|
})
|
|
];
|
|
}
|
|
};
|
|
|
|
const Icon = class PingMonitor_Icon {
|
|
constructor(settings) {
|
|
print_debug('Icon constructor()');
|
|
this._settings = settings;
|
|
this.actor = new St.Icon({
|
|
icon_name: 'system-run-symbolic',
|
|
style_class: 'system-status-icon'
|
|
});
|
|
this.actor.visible = this._settings.get_boolean('icon-display');
|
|
this._settings.connect('changed::icon-display', () => {
|
|
print_debug('changed icon-display');
|
|
this.actor.visible = this._settings.get_boolean('icon-display');
|
|
});
|
|
}
|
|
};
|
|
|
|
export default class PingMonitorExtension extends Extension {
|
|
enable() {
|
|
print_info('applet enabling');
|
|
|
|
this._settings = this.getSettings();
|
|
this._style = new PingStyleManager();
|
|
this._pingBox = null;
|
|
this._elts = [];
|
|
this._tray = null;
|
|
|
|
if (!pingDepsGtop) {
|
|
this._pingDialog = new PingDialog();
|
|
this._dialogTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
|
|
this._pingDialog.open();
|
|
this._dialogTimeout = null;
|
|
return GLib.SOURCE_REMOVE;
|
|
});
|
|
} else {
|
|
this._tray = new PanelMenu.Button(0.5, 'PingMonitor', false);
|
|
this._icon = new Icon(this._settings);
|
|
this._elts = [];
|
|
|
|
let isFileOk = false;
|
|
let path = this._settings.get_string('ping-config-path');
|
|
if (path == '') {
|
|
path = GLib.getenv('HOME') + '/.config/ping-monitor.conf';
|
|
this._settings.set_string('ping-config-path', path);
|
|
}
|
|
isFileOk = this._read_from_file(path);
|
|
this._settings.set_boolean('icon-display', !isFileOk);
|
|
|
|
let spacing = '4';
|
|
this._pingBox = new St.BoxLayout({style: 'spacing: ' + spacing + 'px;'});
|
|
this._tray.add_child(this._pingBox);
|
|
this._pingBox.add_child(this._icon.actor);
|
|
|
|
for (let elt of this._elts) {
|
|
this._pingBox.add_child(elt.actor);
|
|
}
|
|
|
|
let menu_info = new PopupMenu.PopupBaseMenuItem({reactive: false});
|
|
let menu_info_box = new St.BoxLayout();
|
|
menu_info.add_child(menu_info_box);
|
|
this._tray.menu.addMenuItem(menu_info, 0);
|
|
|
|
this._build_menu_info();
|
|
|
|
this._tray.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
|
|
|
this._tray.menu.connect('open-state-changed', (menu, isOpen) => {
|
|
if (isOpen) {
|
|
this._ping_menu_timeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 5, () => {
|
|
return GLib.SOURCE_CONTINUE;
|
|
});
|
|
} else {
|
|
if (this._ping_menu_timeout) {
|
|
GLib.Source.remove(this._ping_menu_timeout);
|
|
this._ping_menu_timeout = null;
|
|
}
|
|
}
|
|
});
|
|
|
|
let item = new PopupMenu.PopupMenuItem(_('Reload config'));
|
|
item.connect('activate', () => {
|
|
this._reload_async();
|
|
});
|
|
this._tray.menu.addMenuItem(item);
|
|
|
|
item = new PopupMenu.PopupMenuItem(_('Preferences...'));
|
|
item.connect('activate', () => {
|
|
this.openPreferences();
|
|
});
|
|
this._tray.menu.addMenuItem(item);
|
|
|
|
Main.panel.addToStatusArea('ping-monitor', this._tray);
|
|
}
|
|
|
|
print_info('applet enabling done');
|
|
}
|
|
|
|
disable() {
|
|
print_info('disable applet');
|
|
|
|
if (this._dialogTimeout) {
|
|
GLib.Source.remove(this._dialogTimeout);
|
|
this._dialogTimeout = null;
|
|
}
|
|
|
|
if (this._pingDialog) {
|
|
this._pingDialog.destroy();
|
|
this._pingDialog = null;
|
|
}
|
|
|
|
if (this._ping_menu_timeout) {
|
|
GLib.Source.remove(this._ping_menu_timeout);
|
|
this._ping_menu_timeout = null;
|
|
}
|
|
|
|
for (let elt of this._elts) {
|
|
elt.stop();
|
|
}
|
|
|
|
if (this._pingBox) {
|
|
for (let elt of this._elts) {
|
|
this._pingBox.remove_child(elt.actor);
|
|
}
|
|
}
|
|
|
|
for (let elt of this._elts) {
|
|
elt.destroy();
|
|
}
|
|
|
|
if (this._tray) {
|
|
this._tray.destroy();
|
|
this._tray = null;
|
|
}
|
|
|
|
this._elts = [];
|
|
this._pingBox = null;
|
|
this._icon = null;
|
|
this._settings = null;
|
|
this._style = null;
|
|
|
|
print_info('applet disabled');
|
|
}
|
|
|
|
_read_from_file(path) {
|
|
print_info('read_from_file()');
|
|
|
|
try {
|
|
let [ok, contents] = GLib.file_get_contents(path);
|
|
if (ok) {
|
|
if (contents instanceof Uint8Array) {
|
|
contents = new TextDecoder().decode(contents);
|
|
}
|
|
let map = JSON.parse(contents);
|
|
|
|
try {
|
|
debugOutput = map['debug_output'];
|
|
} catch (e) {
|
|
debugOutput = false;
|
|
}
|
|
|
|
try {
|
|
for (let i = 0; i < map['ping_config'].length; i++) {
|
|
let tag = map['ping_config'][i]['tag'];
|
|
let name = map['ping_config'][i]['name'];
|
|
let address = map['ping_config'][i]['address'];
|
|
let ping_count = map['ping_config'][i]['ping_count'];
|
|
let ping_interval = map['ping_config'][i]['ping_interval'];
|
|
let ping_deadline = map['ping_config'][i]['ping_deadline'];
|
|
let refresh_interval = map['ping_config'][i]['refresh_interval'];
|
|
let active = map['ping_config'][i]['active'];
|
|
let visible = map['ping_config'][i]['visible'];
|
|
let show_name = map['ping_config'][i]['show_name'];
|
|
let show_address = map['ping_config'][i]['show_address'];
|
|
let show_tooltip = map['ping_config'][i]['show_tooltip'];
|
|
let warning_threshold = map['ping_config'][i]['warning_threshold'];
|
|
|
|
print_debug('tag: ' + tag);
|
|
print_debug('name: ' + name);
|
|
print_debug('address: ' + address);
|
|
|
|
this._elts.push(new Ping(
|
|
i,
|
|
tag,
|
|
name,
|
|
address,
|
|
ping_count,
|
|
ping_interval,
|
|
ping_deadline,
|
|
refresh_interval,
|
|
active,
|
|
visible,
|
|
show_name,
|
|
show_address,
|
|
show_tooltip,
|
|
warning_threshold,
|
|
this._settings,
|
|
this.path));
|
|
}
|
|
} catch (e) {
|
|
print_info('could not load config');
|
|
print_info('error: ' + e);
|
|
return false;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
print_info('Error: ' + e);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
_build_menu_info() {
|
|
print_debug('build_menu_info()');
|
|
|
|
if (this._tray.menu._getMenuItems().length &&
|
|
this._tray.menu._getMenuItems()[0].get_last_child) {
|
|
let lastChild = this._tray.menu._getMenuItems()[0].get_last_child();
|
|
if (lastChild) {
|
|
lastChild.destroy_all_children();
|
|
for (let elt of this._elts) {
|
|
elt.menu_items = elt.create_menu_items();
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
} 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;
|
|
|
|
let row_index = 0;
|
|
for (let elt of this._elts) {
|
|
if (!elt.menu_visible) {
|
|
continue;
|
|
}
|
|
|
|
menu_info_box_table_layout.attach(
|
|
new St.Label({
|
|
text: elt.name,
|
|
style_class: 'ping-title',
|
|
x_align: Clutter.ActorAlign.START,
|
|
y_align: Clutter.ActorAlign.CENTER
|
|
}), 0, row_index, 1, 1);
|
|
|
|
let col_index = 1;
|
|
for (let item of elt.menu_items) {
|
|
menu_info_box_table_layout.attach(item, col_index, row_index, 1, 1);
|
|
col_index++;
|
|
}
|
|
|
|
row_index++;
|
|
}
|
|
|
|
let lastChild = this._tray.menu._getMenuItems()[0].get_last_child();
|
|
if (lastChild) {
|
|
lastChild.add_child(menu_info_box_table);
|
|
}
|
|
}
|
|
|
|
async _reload_async() {
|
|
print_info('Reload ping applet async...');
|
|
|
|
for (let elt of this._elts) {
|
|
elt.stop();
|
|
}
|
|
|
|
for (let elt of this._elts) {
|
|
while (elt.isRunning()) {
|
|
print_info('still running');
|
|
await new Promise(resolve => GLib.timeout_add(GLib.PRIORITY_DEFAULT, 10, () => {
|
|
resolve();
|
|
return GLib.SOURCE_REMOVE;
|
|
}));
|
|
}
|
|
}
|
|
|
|
for (let elt of this._elts) {
|
|
this._pingBox.remove_child(elt.actor);
|
|
}
|
|
|
|
for (let elt of this._elts) {
|
|
elt.destroy();
|
|
}
|
|
this._elts = [];
|
|
|
|
let isFileOk = false;
|
|
let path = this._settings.get_string('ping-config-path');
|
|
if (path == '') {
|
|
path = GLib.getenv('HOME') + '/.config/ping-monitor.conf';
|
|
this._settings.set_string('ping-config-path', path);
|
|
}
|
|
isFileOk = this._read_from_file(path);
|
|
this._settings.set_boolean('icon-display', !isFileOk);
|
|
|
|
for (let elt of this._elts) {
|
|
this._pingBox.add_child(elt.actor);
|
|
}
|
|
|
|
this._build_menu_info();
|
|
|
|
print_info('Reloaded.');
|
|
}
|
|
}
|