Last active
August 8, 2017 19:55
-
-
Save itzexor/97128c893e6b1125b3d023980c3ba4b5 to your computer and use it in GitHub Desktop.
cinnamonitor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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