Skip to content

Instantly share code, notes, and snippets.

@itzexor
Last active August 8, 2017 19:55
Show Gist options
  • Save itzexor/97128c893e6b1125b3d023980c3ba4b5 to your computer and use it in GitHub Desktop.
Save itzexor/97128c893e6b1125b3d023980c3ba4b5 to your computer and use it in GitHub Desktop.
cinnamonitor
const Lang = imports.lang;
const Signals = imports.signals;
const Applet = imports.ui.applet;
const Mainloop = imports.mainloop;
const Main = imports.ui.main;
const Tooltips = imports.ui.tooltips;
const Clutter = imports.gi.Clutter;
const GTop = imports.gi.GTop;
const Cinnamon = imports.gi.Cinnamon;
const St = imports.gi.St;
const MB = 1048576;
const KB = 1024;
const SEC = 1000;
const MIN = 60000;
const HOUR = 3600000;
// tooltip string templates
const FMT_TIME = "%d:%02d:%02d";
const FMT_MEM = "%.2fm";
const FMT_DIFF = FMT_MEM + " (%s)";
// CinnamonMonitor update interval
const REFRESH_RATE = 1 * SEC;
// minimum change before updating min/max usage
const MIN_MAX_HYSTERESIS = 1 * KB;
// helper funcs
function getDiff(start, end) {
let diff = ((end - start) / MB).toFixed(2);
return diff >= 0 ? "+" + diff : diff.toString();
}
function StatsTooltip(applet, cm, orientation) {
this._init(applet, cm, orientation);
}
StatsTooltip.prototype = {
__proto__: Tooltips.TooltipBase.prototype,
_init: function(applet, cm, orientation) {
Tooltips.TooltipBase.prototype._init.call(this, applet.actor);
this.orientation = orientation;
this._applet = applet;
this._cm = cm;
let tooltip = new St.BoxLayout({name: 'Tooltip', vertical: true});
this._tooltip = tooltip;
tooltip.hide();
let makeCat = (name) => {
let catBox = new St.BoxLayout();
makeLabel("<u>" + name + "</u>: ", {expand:false, x_fill:false}, catBox);
let statLabel = makeLabel("", {expand:true, x_fill:false, x_align:St.Align.END}, catBox);
tooltip.add(catBox, {expand: true});
return statLabel;
};
let makeLabel = (markup, props, parent) => {
let label = new St.Label();
label.clutterText.set_markup(markup);
if (parent)
parent.add(label, props);
else
tooltip.add(label, props);
return label;
};
makeLabel("<b>Uptime</b>");
this._sysUp = makeCat("System");
this._cinUp = makeCat("Cinnamon");
this._statsUp = makeCat("Stats");
makeLabel("<b>Memory</b>");
this._mem = makeCat("Current");
this._startMem = makeCat("Start");
this._minMem = makeCat("Min");
this._maxMem = makeCat("Max");
makeLabel("<i>click to reset</i>");
Main.uiGroup.add_actor(tooltip);
cm.connect("updated", Lang.bind(this, this._update));
},
_update: function(force) {
if (!this.visible && !force) return;
let cm = this._cm;
let msToTime = (ms) => {
let hours = Math.floor(ms / HOUR);
let divisor_for_minutes = ms % HOUR;
let minutes = Math.floor(divisor_for_minutes / MIN);
let divisor_for_seconds = divisor_for_minutes % MIN;
let seconds = Math.floor(divisor_for_seconds / SEC);
return {"h": hours, "m": minutes, "s": seconds};
};
let sysTime = msToTime(cm.sysUptime)
let cinTime = msToTime(cm.cinUptime);
let cmTime = msToTime(cm.elapsed);
this._sysUp.clutterText.set_markup(FMT_TIME.format(sysTime.h, sysTime.m, sysTime.s));
this._cinUp.clutterText.set_markup(FMT_TIME.format(cinTime.h, cinTime.m, cinTime.s));
this._statsUp.clutterText.set_markup(FMT_TIME.format(cmTime.h, cmTime.m, cmTime.s));
this._mem.clutterText.set_markup(FMT_MEM.format(cm.curMem));
this._startMem.clutterText.set_markup(FMT_DIFF.format(cm.startMem, cm.startDiffMem));
this._minMem.clutterText.set_markup(FMT_DIFF.format(cm.minMem, cm.minDiffMem));
this._maxMem.clutterText.set_markup(FMT_DIFF.format(cm.maxMem, cm.maxDiffMem));
},
show: function() {
// from Tooltips.PanelItemTooltip
let op = this._tooltip.get_opacity();
this._tooltip.set_opacity(0);
this._tooltip.show();
this._update(true);
let tooltipHeight = this._tooltip.get_allocation_box().y2 - this._tooltip.get_allocation_box().y1;
let tooltipWidth = this._tooltip.get_allocation_box().x2 - this._tooltip.get_allocation_box().x1;
let monitor = Main.layoutManager.findMonitorForActor(this._applet.actor);
let tooltipTop = 0;
let tooltipLeft = 0;
switch (this.orientation) {
case St.Side.BOTTOM:
tooltipTop = this._applet.actor.get_transformed_position()[1] - tooltipHeight;
tooltipLeft = this.mousePosition[0] - Math.round(tooltipWidth / 2);
tooltipLeft = Math.max(tooltipLeft, monitor.x);
tooltipLeft = Math.min(tooltipLeft, monitor.x + monitor.width - tooltipWidth);
break;
case St.Side.TOP:
tooltipTop = this._applet.actor.get_transformed_position()[1] + this._applet.actor.get_transformed_size()[1];
tooltipLeft = this.mousePosition[0] - Math.round(tooltipWidth / 2);
tooltipLeft = Math.max(tooltipLeft, monitor.x);
tooltipLeft = Math.min(tooltipLeft, monitor.x + monitor.width - tooltipWidth);
break;
case St.Side.LEFT:
[tooltipLeft, tooltipTop] = this._applet.actor.get_transformed_position();
tooltipTop = tooltipTop + Math.round((this._applet.actor.get_allocation_box().y2 -
this._applet.actor.get_allocation_box().y1) / 2) - Math.round(tooltipHeight / 2);
tooltipLeft = tooltipLeft + this._applet.actor.get_allocation_box().x2 -
this._applet.actor.get_allocation_box().x1;
break;
case St.Side.RIGHT:
[tooltipLeft, tooltipTop] = this._applet.actor.get_transformed_position();
tooltipTop = tooltipTop + Math.round((this._applet.actor.get_allocation_box().y2 -
this._applet.actor.get_allocation_box().y1) / 2) - Math.round(tooltipHeight / 2);
tooltipLeft = tooltipLeft - tooltipWidth;
break;
default:
break;
}
this._tooltip.set_position(tooltipLeft, tooltipTop);
this._tooltip.set_opacity(op);
this.visible = true;
},
hide: function() {
// from Tooltips.Tooltip
this._tooltip.hide();
this.visible = false;
},
set_text: function() {
return;
},
_destroy: function() {
this._tooltip.destroy();
delete this._applet;
delete this.cm;
}
}
// all the monitoring and refreshing happens in here
function CinnamonMonitor() {
this._init();
}
CinnamonMonitor.prototype = {
_init: function() {
this._errored = false;
try {
// hack: reads the timestamp off the first log entry to guesstimate the cinnamon start time
this._cinStartTime = Main._errorLogStack[0].timestamp;
// get boot time, returned in seconds.
let gtopTime = new GTop.glibtop_uptime();
GTop.glibtop_get_uptime(gtopTime);
this._sysStartTime = gtopTime.boot_time * SEC;
this._procMem = new GTop.glibtop_proc_mem();
this._pid = global.get_pid();
this._statStartTime = 0;
this._startMem = 0;
this._maxMem = 0;
this._minMem = 0;
this._update_id = 0;
} catch (e) {
this._errored = true;
global.logError("Error in CinnamonMonitor _init", e);
}
},
// type: number (int)
get pid() { return this._pid },
get cinUptime() { return Date.now() - this._cinStartTime; },
get sysUptime() { return Date.now() - this._sysStartTime; },
get elapsed() { return Date.now() - this._statStartTime; },
// type: number (fp)
get curMem() { return this._procMem.rss / MB; },
get startMem() { return this._startMem / MB; },
get minMem() { return this._minMem / MB; },
get maxMem() { return this._maxMem / MB; },
// type: string
get startDiffMem() { return getDiff(this._startMem, this._procMem.rss); },
get minDiffMem() { return getDiff(this._minMem, this._procMem.rss); },
get maxDiffMem() { return getDiff(this._maxMem, this._procMem.rss); },
start: function(refreshRate) {
if (this._errored === true) {
global.logWarning("CinnamonMonitor errored, ignoring start()");
return;
}
if (this._update_id > 0)
return;
this.reset();
this._update();
this._update_id = Mainloop.timeout_add(refreshRate, Lang.bind(this, this._update));
},
stop: function() {
if (this._update_id === 0)
return;
Mainloop.source_remove(this._update_id);
this._update_id = 0;
},
reset: function() {
this._statStartTime = Date.now();
this._startMem = 0;
this._maxMem = 0;
this._minMem = 0;
if (this._update_id > 0) this._update();
},
_update: function() {
try {
this._updateMem();
//this._updateCPU();
} catch (e) {
this._errored = true;
global.logError("Error in CinnamonMonitor _update, stopping", e);
this.stop();
}
if (this._errored)
return false;
this.emit("updated");
return true;
},
_updateMem: function() {
GTop.glibtop_get_proc_mem(this._procMem, this._pid);
if (this._startMem === 0) {
this._startMem = this._procMem.resident;
this._minMem = this._startMem;
this._maxMem = this._startMem;
} else {
if (this._procMem.resident > this._maxMem + MIN_MAX_HYSTERESIS)
this._maxMem = this._procMem.resident;
else if (this._procMem.resident < this._minMem - MIN_MAX_HYSTERESIS)
this._minMem = this._procMem.resident;
}
},
destroy: function() {
this.stop();
this.disconnectAll();
delete this._procMem;
}
}
Signals.addSignalMethods(CinnamonMonitor.prototype);
// actual applet: inits CinnamonMonitor, starts it, and handles updating display when CM emits "updated"
function MyApplet(orientation, panel_height, instance_id) {
this._init(orientation, panel_height, instance_id);
}
MyApplet.prototype = {
__proto__: Applet.TextApplet.prototype,
_init: function(orientation, panel_height, instance_id) {
Applet.TextApplet.prototype._init.call(this, orientation, panel_height, instance_id);
this._orientation = orientation;
this.cm = new CinnamonMonitor();
this._applet_tooltip.destroy();
this._applet_tooltip = new StatsTooltip(this, this.cm, orientation);
this.cm.connect("updated", Lang.bind(this, this._updateLabel));
this.cm.start(REFRESH_RATE);
},
_updateLabel: function() {
this.set_applet_label(FMT_MEM.format(this.cm.curMem));
},
on_applet_removed_from_panel: function() {
this._applet_tooltip.destroy();
this.cm.destroy();
delete this.cm;
delete this._applet_tooltip;
},
on_applet_clicked: function(event) {
this.cm.reset();
},
on_orientation_changed: function (orientation) {
this._orientation = orientation;
this._applet_tooltip.orientation = orientation;
}
};
function main(metadata, orientation, panel_height, instance_id) {
let myApplet = new MyApplet(orientation, panel_height, instance_id);
return myApplet;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment