diff --git a/extension.js b/extension.js index 0306a119..585fa95c 100644 --- a/extension.js +++ b/extension.js @@ -1,929 +1,1081 @@ -import Clutter from 'gi://Clutter'; -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; -import GObject from 'gi://GObject'; -import St from 'gi://St' +import Clutter from "gi://Clutter"; +import Gio from "gi://Gio"; +import GLib from "gi://GLib"; +import GObject from "gi://GObject"; +import St from "gi://St"; -import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; -import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as Util from 'resource:///org/gnome/shell/misc/util.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 Main from "resource:///org/gnome/shell/ui/main.js"; +import * as Util from "resource:///org/gnome/shell/misc/util.js"; -import * as Sensors from './sensors.js'; +import * as Sensors from "./sensors.js"; -import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; +import { + Extension, + gettext as _, +} from "resource:///org/gnome/shell/extensions/extension.js"; -import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js'; -import * as Values from './values.js'; -import * as Config from 'resource:///org/gnome/shell/misc/config.js'; -import * as MenuItem from './menuItem.js'; +import * as MessageTray from "resource:///org/gnome/shell/ui/messageTray.js"; +import * as Values from "./values.js"; +import * as Config from "resource:///org/gnome/shell/misc/config.js"; +import * as MenuItem from "./menuItem.js"; let vitalsMenu; -var VitalsMenuButton = GObject.registerClass({ - GTypeName: 'VitalsMenuButton', -}, class VitalsMenuButton extends PanelMenu.Button { +var VitalsMenuButton = GObject.registerClass( + { + GTypeName: "VitalsMenuButton", + }, + class VitalsMenuButton extends PanelMenu.Button { _init(extensionObject) { - super._init(Clutter.ActorAlign.FILL); - - this._extensionObject = extensionObject; - this._settings = extensionObject.getSettings(); - - this._sensorIcons = { - 'temperature' : { 'icon': 'temperature-symbolic.svg' }, - 'voltage' : { 'icon': 'voltage-symbolic.svg' }, - 'fan' : { 'icon': 'fan-symbolic.svg' }, - 'memory' : { 'icon': 'memory-symbolic.svg' }, - 'processor' : { 'icon': 'cpu-symbolic.svg' }, - 'system' : { 'icon': 'system-symbolic.svg' }, - 'network' : { 'icon': 'network-symbolic.svg', - 'icon-rx': 'network-download-symbolic.svg', - 'icon-tx': 'network-upload-symbolic.svg', - 'icon-ad': '../flags/1x1/ad.svg', - 'icon-ae': '../flags/1x1/ae.svg', - 'icon-af': '../flags/1x1/af.svg', - 'icon-ag': '../flags/1x1/ag.svg', - 'icon-ai': '../flags/1x1/ai.svg', - 'icon-al': '../flags/1x1/al.svg', - 'icon-am': '../flags/1x1/am.svg', - 'icon-ao': '../flags/1x1/ao.svg', - 'icon-ar': '../flags/1x1/ar.svg', - 'icon-at': '../flags/1x1/at.svg', - 'icon-au': '../flags/1x1/au.svg', - 'icon-aw': '../flags/1x1/aw.svg', - 'icon-ax': '../flags/1x1/ax.svg', - 'icon-az': '../flags/1x1/az.svg', - 'icon-ba': '../flags/1x1/ba.svg', - 'icon-bb': '../flags/1x1/bb.svg', - 'icon-bd': '../flags/1x1/bd.svg', - 'icon-be': '../flags/1x1/be.svg', - 'icon-bf': '../flags/1x1/bf.svg', - 'icon-bg': '../flags/1x1/bg.svg', - 'icon-bh': '../flags/1x1/bh.svg', - 'icon-bi': '../flags/1x1/bi.svg', - 'icon-bj': '../flags/1x1/bj.svg', - 'icon-bl': '../flags/1x1/bl.svg', - 'icon-bm': '../flags/1x1/bm.svg', - 'icon-bn': '../flags/1x1/bn.svg', - 'icon-bo': '../flags/1x1/bo.svg', - 'icon-bq': '../flags/1x1/bq.svg', - 'icon-br': '../flags/1x1/br.svg', - 'icon-bs': '../flags/1x1/bs.svg', - 'icon-bt': '../flags/1x1/bt.svg', - 'icon-bv': '../flags/1x1/bv.svg', - 'icon-bw': '../flags/1x1/bw.svg', - 'icon-by': '../flags/1x1/by.svg', - 'icon-bz': '../flags/1x1/bz.svg', - 'icon-ca': '../flags/1x1/ca.svg', - 'icon-cc': '../flags/1x1/cc.svg', - 'icon-cd': '../flags/1x1/cd.svg', - 'icon-cf': '../flags/1x1/cf.svg', - 'icon-cg': '../flags/1x1/cg.svg', - 'icon-ch': '../flags/1x1/ch.svg', - 'icon-ci': '../flags/1x1/ci.svg', - 'icon-ck': '../flags/1x1/ck.svg', - 'icon-cl': '../flags/1x1/cl.svg', - 'icon-cm': '../flags/1x1/cm.svg', - 'icon-cn': '../flags/1x1/cn.svg', - 'icon-co': '../flags/1x1/co.svg', - 'icon-cr': '../flags/1x1/cr.svg', - 'icon-cu': '../flags/1x1/cu.svg', - 'icon-cv': '../flags/1x1/cv.svg', - 'icon-cw': '../flags/1x1/cw.svg', - 'icon-cx': '../flags/1x1/cx.svg', - 'icon-cy': '../flags/1x1/cy.svg', - 'icon-cz': '../flags/1x1/cz.svg', - 'icon-de': '../flags/1x1/de.svg', - 'icon-dj': '../flags/1x1/dj.svg', - 'icon-dk': '../flags/1x1/dk.svg', - 'icon-dm': '../flags/1x1/dm.svg', - 'icon-do': '../flags/1x1/do.svg', - 'icon-dz': '../flags/1x1/dz.svg', - 'icon-ec': '../flags/1x1/ec.svg', - 'icon-ee': '../flags/1x1/ee.svg', - 'icon-eg': '../flags/1x1/eg.svg', - 'icon-eh': '../flags/1x1/eh.svg', - 'icon-er': '../flags/1x1/er.svg', - 'icon-es': '../flags/1x1/es.svg', - 'icon-et': '../flags/1x1/et.svg', - 'icon-eu': '../flags/1x1/eu.svg', - 'icon-fi': '../flags/1x1/fi.svg', - 'icon-fj': '../flags/1x1/fj.svg', - 'icon-fk': '../flags/1x1/fk.svg', - 'icon-fm': '../flags/1x1/fm.svg', - 'icon-fo': '../flags/1x1/fo.svg', - 'icon-fr': '../flags/1x1/fr.svg', - 'icon-ga': '../flags/1x1/ga.svg', - 'icon-gb': '../flags/1x1/gb.svg', - 'icon-gd': '../flags/1x1/gd.svg', - 'icon-ge': '../flags/1x1/ge.svg', - 'icon-gf': '../flags/1x1/gf.svg', - 'icon-gg': '../flags/1x1/gg.svg', - 'icon-gh': '../flags/1x1/gh.svg', - 'icon-gi': '../flags/1x1/gi.svg', - 'icon-gl': '../flags/1x1/gl.svg', - 'icon-gm': '../flags/1x1/gm.svg', - 'icon-gn': '../flags/1x1/gn.svg', - 'icon-gp': '../flags/1x1/gp.svg', - 'icon-gq': '../flags/1x1/gq.svg', - 'icon-gr': '../flags/1x1/gr.svg', - 'icon-gs': '../flags/1x1/gs.svg', - 'icon-gt': '../flags/1x1/gt.svg', - 'icon-gu': '../flags/1x1/gu.svg', - 'icon-gw': '../flags/1x1/gw.svg', - 'icon-gy': '../flags/1x1/gy.svg', - 'icon-hk': '../flags/1x1/hk.svg', - 'icon-hm': '../flags/1x1/hm.svg', - 'icon-hn': '../flags/1x1/hn.svg', - 'icon-hr': '../flags/1x1/hr.svg', - 'icon-ht': '../flags/1x1/ht.svg', - 'icon-hu': '../flags/1x1/hu.svg', - 'icon-id': '../flags/1x1/id.svg', - 'icon-ie': '../flags/1x1/ie.svg', - 'icon-il': '../flags/1x1/il.svg', - 'icon-im': '../flags/1x1/im.svg', - 'icon-in': '../flags/1x1/in.svg', - 'icon-io': '../flags/1x1/io.svg', - 'icon-iq': '../flags/1x1/iq.svg', - 'icon-ir': '../flags/1x1/ir.svg', - 'icon-is': '../flags/1x1/is.svg', - 'icon-it': '../flags/1x1/it.svg', - 'icon-je': '../flags/1x1/je.svg', - 'icon-jm': '../flags/1x1/jm.svg', - 'icon-jo': '../flags/1x1/jo.svg', - 'icon-jp': '../flags/1x1/jp.svg', - 'icon-ke': '../flags/1x1/ke.svg', - 'icon-kg': '../flags/1x1/kg.svg', - 'icon-kh': '../flags/1x1/kh.svg', - 'icon-ki': '../flags/1x1/ki.svg', - 'icon-km': '../flags/1x1/km.svg', - 'icon-kn': '../flags/1x1/kn.svg', - 'icon-kp': '../flags/1x1/kp.svg', - 'icon-kr': '../flags/1x1/kr.svg', - 'icon-kw': '../flags/1x1/kw.svg', - 'icon-ky': '../flags/1x1/ky.svg', - 'icon-kz': '../flags/1x1/kz.svg', - 'icon-la': '../flags/1x1/la.svg', - 'icon-lb': '../flags/1x1/lb.svg', - 'icon-lc': '../flags/1x1/lc.svg', - 'icon-li': '../flags/1x1/li.svg', - 'icon-lk': '../flags/1x1/lk.svg', - 'icon-lr': '../flags/1x1/lr.svg', - 'icon-ls': '../flags/1x1/ls.svg', - 'icon-lt': '../flags/1x1/lt.svg', - 'icon-lu': '../flags/1x1/lu.svg', - 'icon-lv': '../flags/1x1/lv.svg', - 'icon-ly': '../flags/1x1/ly.svg', - 'icon-ma': '../flags/1x1/ma.svg', - 'icon-mc': '../flags/1x1/mc.svg', - 'icon-md': '../flags/1x1/md.svg', - 'icon-me': '../flags/1x1/me.svg', - 'icon-mf': '../flags/1x1/mf.svg', - 'icon-mg': '../flags/1x1/mg.svg', - 'icon-mh': '../flags/1x1/mh.svg', - 'icon-mk': '../flags/1x1/mk.svg', - 'icon-ml': '../flags/1x1/ml.svg', - 'icon-mm': '../flags/1x1/mm.svg', - 'icon-mn': '../flags/1x1/mn.svg', - 'icon-mo': '../flags/1x1/mo.svg', - 'icon-mp': '../flags/1x1/mp.svg', - 'icon-mq': '../flags/1x1/mq.svg', - 'icon-mr': '../flags/1x1/mr.svg', - 'icon-ms': '../flags/1x1/ms.svg', - 'icon-mt': '../flags/1x1/mt.svg', - 'icon-mu': '../flags/1x1/mu.svg', - 'icon-mv': '../flags/1x1/mv.svg', - 'icon-mw': '../flags/1x1/mw.svg', - 'icon-mx': '../flags/1x1/mx.svg', - 'icon-my': '../flags/1x1/my.svg', - 'icon-mz': '../flags/1x1/mz.svg', - 'icon-na': '../flags/1x1/na.svg', - 'icon-nc': '../flags/1x1/nc.svg', - 'icon-ne': '../flags/1x1/ne.svg', - 'icon-nf': '../flags/1x1/nf.svg', - 'icon-ng': '../flags/1x1/ng.svg', - 'icon-ni': '../flags/1x1/ni.svg', - 'icon-nl': '../flags/1x1/nl.svg', - 'icon-no': '../flags/1x1/no.svg', - 'icon-np': '../flags/1x1/np.svg', - 'icon-nr': '../flags/1x1/nr.svg', - 'icon-nu': '../flags/1x1/nu.svg', - 'icon-nz': '../flags/1x1/nz.svg', - 'icon-om': '../flags/1x1/om.svg', - 'icon-pa': '../flags/1x1/pa.svg', - 'icon-pe': '../flags/1x1/pe.svg', - 'icon-pf': '../flags/1x1/pf.svg', - 'icon-pg': '../flags/1x1/pg.svg', - 'icon-ph': '../flags/1x1/ph.svg', - 'icon-pk': '../flags/1x1/pk.svg', - 'icon-pl': '../flags/1x1/pl.svg', - 'icon-pm': '../flags/1x1/pm.svg', - 'icon-pn': '../flags/1x1/pn.svg', - 'icon-pr': '../flags/1x1/pr.svg', - 'icon-ps': '../flags/1x1/ps.svg', - 'icon-pt': '../flags/1x1/pt.svg', - 'icon-pw': '../flags/1x1/pw.svg', - 'icon-py': '../flags/1x1/py.svg', - 'icon-qa': '../flags/1x1/qa.svg', - 'icon-re': '../flags/1x1/re.svg', - 'icon-ro': '../flags/1x1/ro.svg', - 'icon-rs': '../flags/1x1/rs.svg', - 'icon-ru': '../flags/1x1/ru.svg', - 'icon-rw': '../flags/1x1/rw.svg', - 'icon-sa': '../flags/1x1/sa.svg', - 'icon-sb': '../flags/1x1/sb.svg', - 'icon-sc': '../flags/1x1/sc.svg', - 'icon-sd': '../flags/1x1/sd.svg', - 'icon-se': '../flags/1x1/se.svg', - 'icon-sg': '../flags/1x1/sg.svg', - 'icon-sh': '../flags/1x1/sh.svg', - 'icon-si': '../flags/1x1/si.svg', - 'icon-sj': '../flags/1x1/sj.svg', - 'icon-sk': '../flags/1x1/sk.svg', - 'icon-sl': '../flags/1x1/sl.svg', - 'icon-sm': '../flags/1x1/sm.svg', - 'icon-sn': '../flags/1x1/sn.svg', - 'icon-so': '../flags/1x1/so.svg', - 'icon-sr': '../flags/1x1/sr.svg', - 'icon-ss': '../flags/1x1/ss.svg', - 'icon-st': '../flags/1x1/st.svg', - 'icon-sv': '../flags/1x1/sv.svg', - 'icon-sx': '../flags/1x1/sx.svg', - 'icon-sy': '../flags/1x1/sy.svg', - 'icon-sz': '../flags/1x1/sz.svg', - 'icon-tc': '../flags/1x1/tc.svg', - 'icon-td': '../flags/1x1/td.svg', - 'icon-tf': '../flags/1x1/tf.svg', - 'icon-tg': '../flags/1x1/tg.svg', - 'icon-th': '../flags/1x1/th.svg', - 'icon-tj': '../flags/1x1/tj.svg', - 'icon-tk': '../flags/1x1/tk.svg', - 'icon-tl': '../flags/1x1/tl.svg', - 'icon-tm': '../flags/1x1/tm.svg', - 'icon-tn': '../flags/1x1/tn.svg', - 'icon-to': '../flags/1x1/to.svg', - 'icon-tr': '../flags/1x1/tr.svg', - 'icon-tt': '../flags/1x1/tt.svg', - 'icon-tv': '../flags/1x1/tv.svg', - 'icon-tw': '../flags/1x1/tw.svg', - 'icon-tz': '../flags/1x1/tz.svg', - 'icon-ua': '../flags/1x1/ua.svg', - 'icon-ug': '../flags/1x1/ug.svg', - 'icon-um': '../flags/1x1/um.svg', - 'icon-us': '../flags/1x1/us.svg', - 'icon-uy': '../flags/1x1/uy.svg', - 'icon-uz': '../flags/1x1/uz.svg', - 'icon-va': '../flags/1x1/va.svg', - 'icon-vc': '../flags/1x1/vc.svg', - 'icon-ve': '../flags/1x1/ve.svg', - 'icon-vg': '../flags/1x1/vg.svg', - 'icon-vi': '../flags/1x1/vi.svg', - 'icon-vn': '../flags/1x1/vn.svg', - 'icon-vu': '../flags/1x1/vu.svg', - 'icon-wf': '../flags/1x1/wf.svg', - 'icon-ws': '../flags/1x1/ws.svg', - 'icon-xk': '../flags/1x1/xk.svg', - 'icon-ye': '../flags/1x1/ye.svg', - 'icon-yt': '../flags/1x1/yt.svg', - 'icon-za': '../flags/1x1/za.svg', - 'icon-zm': '../flags/1x1/zm.svg', - 'icon-zw': '../flags/1x1/zw.svg' - }, - 'storage' : { 'icon': 'storage-symbolic.svg' }, - 'battery' : { 'icon': 'battery-symbolic.svg' }, - 'gpu' : { 'icon': 'gpu-symbolic.svg' } - } - - // list with the prefixes for the according themes, the index of each - // item must match the index on the combo box - this._sensorsIconPathPrefix = ['/icons/original/', '/icons/gnome/']; - - this._warnings = []; - this._sensorMenuItems = {}; - this._hotLabels = {}; - this._hotItems = {}; - this._groups = {}; - this._widths = {}; - this._numGpus = 1; - this._newGpuDetected = false; - this._newGpuDetectedCount = 0; - this._last_query = new Date().getTime(); - - this._sensors = new Sensors.Sensors(this._settings, this._sensorIcons); - this._values = new Values.Values(this._settings, this._sensorIcons); - this._menuLayout = new St.BoxLayout({ - vertical: false, - clip_to_allocation: true, - x_align: Clutter.ActorAlign.START, - y_align: Clutter.ActorAlign.CENTER, - reactive: true, - x_expand: true, - style_class: 'vitals-panel-menu' - }); - - this._drawMenu(); - this.add_child(this._menuLayout); - this._settingChangedSignals = []; - this._refreshTimeoutId = null; - - this._addSettingChangedSignal('update-time', this._updateTimeSettingChanged.bind(this)); - this._addSettingChangedSignal('position-in-panel', this._positionInPanelChanged.bind(this)); - this._addSettingChangedSignal('menu-centered', this._positionInPanelChanged.bind(this)); - this._addSettingChangedSignal('icon-style', this._iconStyleChanged.bind(this)); - - let settings = [ 'use-higher-precision', 'alphabetize', 'hide-zeros', - 'fixed-widths', 'hide-icons', 'unit', - 'memory-measurement', 'include-public-ip', 'network-public-ip-interval', - 'network-public-ip-show-flag', 'network-speed-format', 'storage-measurement', - 'include-static-info', 'include-static-gpu-info' ]; - - for (let setting of Object.values(settings)) - this._addSettingChangedSignal(setting, this._redrawMenu.bind(this)); - - // add signals for show- preference based categories - for (let sensor in this._sensorIcons) - this._addSettingChangedSignal('show-' + sensor, this._showHideSensorsChanged.bind(this)); + super._init(Clutter.ActorAlign.FILL); + + this._extensionObject = extensionObject; + this._settings = extensionObject.getSettings(); + + this._sensorIcons = { + temperature: { + icon: "temperature-symbolic.svg", + "icon-c": "water-droplet-symbolic.svg", + }, + voltage: { icon: "voltage-symbolic.svg" }, + fan: { icon: "fan-symbolic.svg", "icon-pump": "pump-symbolic.svg" }, + memory: { icon: "memory-symbolic.svg" }, + processor: { icon: "cpu-symbolic.svg" }, + system: { icon: "system-symbolic.svg" }, + network: { + icon: "network-symbolic.svg", + "icon-rx": "network-download-symbolic.svg", + "icon-tx": "network-upload-symbolic.svg", + "icon-ad": "../flags/1x1/ad.svg", + "icon-ae": "../flags/1x1/ae.svg", + "icon-af": "../flags/1x1/af.svg", + "icon-ag": "../flags/1x1/ag.svg", + "icon-ai": "../flags/1x1/ai.svg", + "icon-al": "../flags/1x1/al.svg", + "icon-am": "../flags/1x1/am.svg", + "icon-ao": "../flags/1x1/ao.svg", + "icon-ar": "../flags/1x1/ar.svg", + "icon-at": "../flags/1x1/at.svg", + "icon-au": "../flags/1x1/au.svg", + "icon-aw": "../flags/1x1/aw.svg", + "icon-ax": "../flags/1x1/ax.svg", + "icon-az": "../flags/1x1/az.svg", + "icon-ba": "../flags/1x1/ba.svg", + "icon-bb": "../flags/1x1/bb.svg", + "icon-bd": "../flags/1x1/bd.svg", + "icon-be": "../flags/1x1/be.svg", + "icon-bf": "../flags/1x1/bf.svg", + "icon-bg": "../flags/1x1/bg.svg", + "icon-bh": "../flags/1x1/bh.svg", + "icon-bi": "../flags/1x1/bi.svg", + "icon-bj": "../flags/1x1/bj.svg", + "icon-bl": "../flags/1x1/bl.svg", + "icon-bm": "../flags/1x1/bm.svg", + "icon-bn": "../flags/1x1/bn.svg", + "icon-bo": "../flags/1x1/bo.svg", + "icon-bq": "../flags/1x1/bq.svg", + "icon-br": "../flags/1x1/br.svg", + "icon-bs": "../flags/1x1/bs.svg", + "icon-bt": "../flags/1x1/bt.svg", + "icon-bv": "../flags/1x1/bv.svg", + "icon-bw": "../flags/1x1/bw.svg", + "icon-by": "../flags/1x1/by.svg", + "icon-bz": "../flags/1x1/bz.svg", + "icon-ca": "../flags/1x1/ca.svg", + "icon-cc": "../flags/1x1/cc.svg", + "icon-cd": "../flags/1x1/cd.svg", + "icon-cf": "../flags/1x1/cf.svg", + "icon-cg": "../flags/1x1/cg.svg", + "icon-ch": "../flags/1x1/ch.svg", + "icon-ci": "../flags/1x1/ci.svg", + "icon-ck": "../flags/1x1/ck.svg", + "icon-cl": "../flags/1x1/cl.svg", + "icon-cm": "../flags/1x1/cm.svg", + "icon-cn": "../flags/1x1/cn.svg", + "icon-co": "../flags/1x1/co.svg", + "icon-cr": "../flags/1x1/cr.svg", + "icon-cu": "../flags/1x1/cu.svg", + "icon-cv": "../flags/1x1/cv.svg", + "icon-cw": "../flags/1x1/cw.svg", + "icon-cx": "../flags/1x1/cx.svg", + "icon-cy": "../flags/1x1/cy.svg", + "icon-cz": "../flags/1x1/cz.svg", + "icon-de": "../flags/1x1/de.svg", + "icon-dj": "../flags/1x1/dj.svg", + "icon-dk": "../flags/1x1/dk.svg", + "icon-dm": "../flags/1x1/dm.svg", + "icon-do": "../flags/1x1/do.svg", + "icon-dz": "../flags/1x1/dz.svg", + "icon-ec": "../flags/1x1/ec.svg", + "icon-ee": "../flags/1x1/ee.svg", + "icon-eg": "../flags/1x1/eg.svg", + "icon-eh": "../flags/1x1/eh.svg", + "icon-er": "../flags/1x1/er.svg", + "icon-es": "../flags/1x1/es.svg", + "icon-et": "../flags/1x1/et.svg", + "icon-eu": "../flags/1x1/eu.svg", + "icon-fi": "../flags/1x1/fi.svg", + "icon-fj": "../flags/1x1/fj.svg", + "icon-fk": "../flags/1x1/fk.svg", + "icon-fm": "../flags/1x1/fm.svg", + "icon-fo": "../flags/1x1/fo.svg", + "icon-fr": "../flags/1x1/fr.svg", + "icon-ga": "../flags/1x1/ga.svg", + "icon-gb": "../flags/1x1/gb.svg", + "icon-gd": "../flags/1x1/gd.svg", + "icon-ge": "../flags/1x1/ge.svg", + "icon-gf": "../flags/1x1/gf.svg", + "icon-gg": "../flags/1x1/gg.svg", + "icon-gh": "../flags/1x1/gh.svg", + "icon-gi": "../flags/1x1/gi.svg", + "icon-gl": "../flags/1x1/gl.svg", + "icon-gm": "../flags/1x1/gm.svg", + "icon-gn": "../flags/1x1/gn.svg", + "icon-gp": "../flags/1x1/gp.svg", + "icon-gq": "../flags/1x1/gq.svg", + "icon-gr": "../flags/1x1/gr.svg", + "icon-gs": "../flags/1x1/gs.svg", + "icon-gt": "../flags/1x1/gt.svg", + "icon-gu": "../flags/1x1/gu.svg", + "icon-gw": "../flags/1x1/gw.svg", + "icon-gy": "../flags/1x1/gy.svg", + "icon-hk": "../flags/1x1/hk.svg", + "icon-hm": "../flags/1x1/hm.svg", + "icon-hn": "../flags/1x1/hn.svg", + "icon-hr": "../flags/1x1/hr.svg", + "icon-ht": "../flags/1x1/ht.svg", + "icon-hu": "../flags/1x1/hu.svg", + "icon-id": "../flags/1x1/id.svg", + "icon-ie": "../flags/1x1/ie.svg", + "icon-il": "../flags/1x1/il.svg", + "icon-im": "../flags/1x1/im.svg", + "icon-in": "../flags/1x1/in.svg", + "icon-io": "../flags/1x1/io.svg", + "icon-iq": "../flags/1x1/iq.svg", + "icon-ir": "../flags/1x1/ir.svg", + "icon-is": "../flags/1x1/is.svg", + "icon-it": "../flags/1x1/it.svg", + "icon-je": "../flags/1x1/je.svg", + "icon-jm": "../flags/1x1/jm.svg", + "icon-jo": "../flags/1x1/jo.svg", + "icon-jp": "../flags/1x1/jp.svg", + "icon-ke": "../flags/1x1/ke.svg", + "icon-kg": "../flags/1x1/kg.svg", + "icon-kh": "../flags/1x1/kh.svg", + "icon-ki": "../flags/1x1/ki.svg", + "icon-km": "../flags/1x1/km.svg", + "icon-kn": "../flags/1x1/kn.svg", + "icon-kp": "../flags/1x1/kp.svg", + "icon-kr": "../flags/1x1/kr.svg", + "icon-kw": "../flags/1x1/kw.svg", + "icon-ky": "../flags/1x1/ky.svg", + "icon-kz": "../flags/1x1/kz.svg", + "icon-la": "../flags/1x1/la.svg", + "icon-lb": "../flags/1x1/lb.svg", + "icon-lc": "../flags/1x1/lc.svg", + "icon-li": "../flags/1x1/li.svg", + "icon-lk": "../flags/1x1/lk.svg", + "icon-lr": "../flags/1x1/lr.svg", + "icon-ls": "../flags/1x1/ls.svg", + "icon-lt": "../flags/1x1/lt.svg", + "icon-lu": "../flags/1x1/lu.svg", + "icon-lv": "../flags/1x1/lv.svg", + "icon-ly": "../flags/1x1/ly.svg", + "icon-ma": "../flags/1x1/ma.svg", + "icon-mc": "../flags/1x1/mc.svg", + "icon-md": "../flags/1x1/md.svg", + "icon-me": "../flags/1x1/me.svg", + "icon-mf": "../flags/1x1/mf.svg", + "icon-mg": "../flags/1x1/mg.svg", + "icon-mh": "../flags/1x1/mh.svg", + "icon-mk": "../flags/1x1/mk.svg", + "icon-ml": "../flags/1x1/ml.svg", + "icon-mm": "../flags/1x1/mm.svg", + "icon-mn": "../flags/1x1/mn.svg", + "icon-mo": "../flags/1x1/mo.svg", + "icon-mp": "../flags/1x1/mp.svg", + "icon-mq": "../flags/1x1/mq.svg", + "icon-mr": "../flags/1x1/mr.svg", + "icon-ms": "../flags/1x1/ms.svg", + "icon-mt": "../flags/1x1/mt.svg", + "icon-mu": "../flags/1x1/mu.svg", + "icon-mv": "../flags/1x1/mv.svg", + "icon-mw": "../flags/1x1/mw.svg", + "icon-mx": "../flags/1x1/mx.svg", + "icon-my": "../flags/1x1/my.svg", + "icon-mz": "../flags/1x1/mz.svg", + "icon-na": "../flags/1x1/na.svg", + "icon-nc": "../flags/1x1/nc.svg", + "icon-ne": "../flags/1x1/ne.svg", + "icon-nf": "../flags/1x1/nf.svg", + "icon-ng": "../flags/1x1/ng.svg", + "icon-ni": "../flags/1x1/ni.svg", + "icon-nl": "../flags/1x1/nl.svg", + "icon-no": "../flags/1x1/no.svg", + "icon-np": "../flags/1x1/np.svg", + "icon-nr": "../flags/1x1/nr.svg", + "icon-nu": "../flags/1x1/nu.svg", + "icon-nz": "../flags/1x1/nz.svg", + "icon-om": "../flags/1x1/om.svg", + "icon-pa": "../flags/1x1/pa.svg", + "icon-pe": "../flags/1x1/pe.svg", + "icon-pf": "../flags/1x1/pf.svg", + "icon-pg": "../flags/1x1/pg.svg", + "icon-ph": "../flags/1x1/ph.svg", + "icon-pk": "../flags/1x1/pk.svg", + "icon-pl": "../flags/1x1/pl.svg", + "icon-pm": "../flags/1x1/pm.svg", + "icon-pn": "../flags/1x1/pn.svg", + "icon-pr": "../flags/1x1/pr.svg", + "icon-ps": "../flags/1x1/ps.svg", + "icon-pt": "../flags/1x1/pt.svg", + "icon-pw": "../flags/1x1/pw.svg", + "icon-py": "../flags/1x1/py.svg", + "icon-qa": "../flags/1x1/qa.svg", + "icon-re": "../flags/1x1/re.svg", + "icon-ro": "../flags/1x1/ro.svg", + "icon-rs": "../flags/1x1/rs.svg", + "icon-ru": "../flags/1x1/ru.svg", + "icon-rw": "../flags/1x1/rw.svg", + "icon-sa": "../flags/1x1/sa.svg", + "icon-sb": "../flags/1x1/sb.svg", + "icon-sc": "../flags/1x1/sc.svg", + "icon-sd": "../flags/1x1/sd.svg", + "icon-se": "../flags/1x1/se.svg", + "icon-sg": "../flags/1x1/sg.svg", + "icon-sh": "../flags/1x1/sh.svg", + "icon-si": "../flags/1x1/si.svg", + "icon-sj": "../flags/1x1/sj.svg", + "icon-sk": "../flags/1x1/sk.svg", + "icon-sl": "../flags/1x1/sl.svg", + "icon-sm": "../flags/1x1/sm.svg", + "icon-sn": "../flags/1x1/sn.svg", + "icon-so": "../flags/1x1/so.svg", + "icon-sr": "../flags/1x1/sr.svg", + "icon-ss": "../flags/1x1/ss.svg", + "icon-st": "../flags/1x1/st.svg", + "icon-sv": "../flags/1x1/sv.svg", + "icon-sx": "../flags/1x1/sx.svg", + "icon-sy": "../flags/1x1/sy.svg", + "icon-sz": "../flags/1x1/sz.svg", + "icon-tc": "../flags/1x1/tc.svg", + "icon-td": "../flags/1x1/td.svg", + "icon-tf": "../flags/1x1/tf.svg", + "icon-tg": "../flags/1x1/tg.svg", + "icon-th": "../flags/1x1/th.svg", + "icon-tj": "../flags/1x1/tj.svg", + "icon-tk": "../flags/1x1/tk.svg", + "icon-tl": "../flags/1x1/tl.svg", + "icon-tm": "../flags/1x1/tm.svg", + "icon-tn": "../flags/1x1/tn.svg", + "icon-to": "../flags/1x1/to.svg", + "icon-tr": "../flags/1x1/tr.svg", + "icon-tt": "../flags/1x1/tt.svg", + "icon-tv": "../flags/1x1/tv.svg", + "icon-tw": "../flags/1x1/tw.svg", + "icon-tz": "../flags/1x1/tz.svg", + "icon-ua": "../flags/1x1/ua.svg", + "icon-ug": "../flags/1x1/ug.svg", + "icon-um": "../flags/1x1/um.svg", + "icon-us": "../flags/1x1/us.svg", + "icon-uy": "../flags/1x1/uy.svg", + "icon-uz": "../flags/1x1/uz.svg", + "icon-va": "../flags/1x1/va.svg", + "icon-vc": "../flags/1x1/vc.svg", + "icon-ve": "../flags/1x1/ve.svg", + "icon-vg": "../flags/1x1/vg.svg", + "icon-vi": "../flags/1x1/vi.svg", + "icon-vn": "../flags/1x1/vn.svg", + "icon-vu": "../flags/1x1/vu.svg", + "icon-wf": "../flags/1x1/wf.svg", + "icon-ws": "../flags/1x1/ws.svg", + "icon-xk": "../flags/1x1/xk.svg", + "icon-ye": "../flags/1x1/ye.svg", + "icon-yt": "../flags/1x1/yt.svg", + "icon-za": "../flags/1x1/za.svg", + "icon-zm": "../flags/1x1/zm.svg", + "icon-zw": "../flags/1x1/zw.svg", + }, + storage: { icon: "storage-symbolic.svg" }, + battery: { icon: "battery-symbolic.svg" }, + gpu: { icon: "gpu-symbolic.svg" }, + }; + + // list with the prefixes for the according themes, the index of each + // item must match the index on the combo box + this._sensorsIconPathPrefix = ["/icons/original/", "/icons/gnome/"]; + + this._warnings = []; + this._sensorMenuItems = {}; + this._hotLabels = {}; + this._hotItems = {}; + this._groups = {}; + this._widths = {}; + this._numGpus = 1; + this._newGpuDetected = false; + this._newGpuDetectedCount = 0; + this._last_query = new Date().getTime(); + + this._sensors = new Sensors.Sensors(this._settings, this._sensorIcons); + this._values = new Values.Values(this._settings, this._sensorIcons); + this._menuLayout = new St.BoxLayout({ + vertical: false, + clip_to_allocation: true, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.CENTER, + reactive: true, + x_expand: true, + style_class: "vitals-panel-menu", + }); + + this._drawMenu(); + this.add_child(this._menuLayout); + this._settingChangedSignals = []; + this._refreshTimeoutId = null; + + this._addSettingChangedSignal( + "update-time", + this._updateTimeSettingChanged.bind(this), + ); + this._addSettingChangedSignal( + "position-in-panel", + this._positionInPanelChanged.bind(this), + ); + this._addSettingChangedSignal( + "menu-centered", + this._positionInPanelChanged.bind(this), + ); + this._addSettingChangedSignal( + "icon-style", + this._iconStyleChanged.bind(this), + ); + + let settings = [ + "use-higher-precision", + "alphabetize", + "hide-zeros", + "fixed-widths", + "hide-icons", + "unit", + "memory-measurement", + "include-public-ip", + "network-public-ip-interval", + "network-public-ip-show-flag", + "network-speed-format", + "storage-measurement", + "include-static-info", + "include-static-gpu-info", + ]; + + for (let setting of Object.values(settings)) + this._addSettingChangedSignal(setting, this._redrawMenu.bind(this)); + + // add signals for show- preference based categories + for (let sensor in this._sensorIcons) + this._addSettingChangedSignal( + "show-" + sensor, + this._showHideSensorsChanged.bind(this), + ); - this._initializeMenu(); + this._initializeMenu(); - // start off with fresh sensors - this._querySensors(); + // start off with fresh sensors + this._querySensors(); - // start monitoring sensors - this._initializeTimer(); + // start monitoring sensors + this._initializeTimer(); } _initializeMenu() { - // display sensor categories - for (let sensor in this._sensorIcons) { - // groups associated sensors under accordion menu - if (sensor in this._groups) continue; - - //handle gpus separately. - if (sensor === 'gpu') continue; + // display sensor categories + for (let sensor in this._sensorIcons) { + // groups associated sensors under accordion menu + if (sensor in this._groups) continue; + + //handle gpus separately. + if (sensor === "gpu") continue; + + this._initializeMenuGroup(sensor, sensor); + } + + for (let i = 1; i <= this._numGpus; i++) + this._initializeMenuGroup( + "gpu#" + i, + "gpu", + this._numGpus > 1 ? " " + i : "", + ); - this._initializeMenuGroup(sensor, sensor); - } + // add separator + this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + + let item = new PopupMenu.PopupBaseMenuItem({ + reactive: false, + style_class: "vitals-menu-button-container", + }); + + let customButtonBox = new St.BoxLayout({ + style_class: "vitals-button-box", + vertical: false, + clip_to_allocation: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + reactive: true, + x_expand: true, + }); + + // custom round refresh button + let refreshButton = this._createRoundButton( + "view-refresh-symbolic", + _("Refresh"), + ); + refreshButton.connect("clicked", (self) => { + // force refresh by clearing history + this._sensors.resetHistory(); + this._values.resetHistory(this._numGpus); - for (let i = 1; i <= this._numGpus; i++) - this._initializeMenuGroup('gpu#' + i, 'gpu', (this._numGpus > 1 ? ' ' + i : '')); - - // add separator - this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); - - let item = new PopupMenu.PopupBaseMenuItem({ - reactive: false, - style_class: 'vitals-menu-button-container' - }); - - let customButtonBox = new St.BoxLayout({ - style_class: 'vitals-button-box', - vertical: false, - clip_to_allocation: true, - x_align: Clutter.ActorAlign.CENTER, - y_align: Clutter.ActorAlign.CENTER, - reactive: true, - x_expand: true - }); - - // custom round refresh button - let refreshButton = this._createRoundButton('view-refresh-symbolic', _('Refresh')); - refreshButton.connect('clicked', (self) => { - // force refresh by clearing history - this._sensors.resetHistory(); - this._values.resetHistory(this._numGpus); + // make sure timer fires at next full interval + this._updateTimeChanged(); + // refresh sensors now + this._querySensors(); + }); + customButtonBox.add_child(refreshButton); + + // custom round monitor button + let monitorButton = this._createRoundButton( + "org.gnome.SystemMonitor-symbolic", + _("System Monitor"), + ); + monitorButton.connect("clicked", (self) => { + this.menu._getTopMenu().close(); + Util.spawn(this._settings.get_string("monitor-cmd").split(" ")); + }); + customButtonBox.add_child(monitorButton); + + // custom round preferences button + let prefsButton = this._createRoundButton( + "preferences-system-symbolic", + _("Preferences"), + ); + prefsButton.connect("clicked", (self) => { + this.menu._getTopMenu().close(); + this._extensionObject.openPreferences(); + }); + customButtonBox.add_child(prefsButton); + + // now add the buttons to the top bar + item.actor.add_child(customButtonBox); + + // add buttons + this.menu.addMenuItem(item); + + // query sensors on menu open + this._menuStateChangeId = this.menu.connect( + "open-state-changed", + (self, isMenuOpen) => { + if (isMenuOpen) { // make sure timer fires at next full interval this._updateTimeChanged(); // refresh sensors now this._querySensors(); - }); - customButtonBox.add_child(refreshButton); - - // custom round monitor button - let monitorButton = this._createRoundButton('org.gnome.SystemMonitor-symbolic', _('System Monitor')); - monitorButton.connect('clicked', (self) => { - this.menu._getTopMenu().close(); - Util.spawn(this._settings.get_string('monitor-cmd').split(" ")); - }); - customButtonBox.add_child(monitorButton); - - // custom round preferences button - let prefsButton = this._createRoundButton('preferences-system-symbolic', _('Preferences')); - prefsButton.connect('clicked', (self) => { - this.menu._getTopMenu().close(); - this._extensionObject.openPreferences(); - }); - customButtonBox.add_child(prefsButton); - - // now add the buttons to the top bar - item.actor.add_child(customButtonBox); - - // add buttons - this.menu.addMenuItem(item); - - // query sensors on menu open - this._menuStateChangeId = this.menu.connect('open-state-changed', (self, isMenuOpen) => { - if (isMenuOpen) { - // make sure timer fires at next full interval - this._updateTimeChanged(); - - // refresh sensors now - this._querySensors(); - } - }); + } + }, + ); } - _initializeMenuGroup(groupName, optionName, menuSuffix = '', position = -1) { - this._groups[groupName] = new PopupMenu.PopupSubMenuMenuItem(_(this._ucFirst(groupName) + menuSuffix), true); - this._groups[groupName].icon.gicon = Gio.icon_new_for_string(this._sensorIconPath(groupName)); - - // hide menu items that user has requested to not include - if (!this._settings.get_boolean('show-' + optionName)) - this._groups[groupName].actor.hide(); - - if (!this._groups[groupName].status) { - this._groups[groupName].status = this._defaultLabel(); - this._groups[groupName].actor.insert_child_at_index(this._groups[groupName].status, 4); - this._groups[groupName].status.text = _('No Data'); - } + _initializeMenuGroup( + groupName, + optionName, + menuSuffix = "", + position = -1, + ) { + this._groups[groupName] = new PopupMenu.PopupSubMenuMenuItem( + _(this._ucFirst(groupName) + menuSuffix), + true, + ); + this._groups[groupName].icon.gicon = Gio.icon_new_for_string( + this._sensorIconPath(groupName), + ); + + // hide menu items that user has requested to not include + if (!this._settings.get_boolean("show-" + optionName)) + this._groups[groupName].actor.hide(); + + if (!this._groups[groupName].status) { + this._groups[groupName].status = this._defaultLabel(); + this._groups[groupName].actor.insert_child_at_index( + this._groups[groupName].status, + 4, + ); + this._groups[groupName].status.text = _("No Data"); + } - if(position == -1) this.menu.addMenuItem(this._groups[groupName]); - else this.menu.addMenuItem(this._groups[groupName], position); + if (position == -1) this.menu.addMenuItem(this._groups[groupName]); + else this.menu.addMenuItem(this._groups[groupName], position); } _createRoundButton(iconName) { - let button = new St.Button({ - style_class: 'message-list-clear-button button vitals-button-action' - }); + let button = new St.Button({ + style_class: "message-list-clear-button button vitals-button-action", + }); - button.child = new St.Icon({ - icon_name: iconName - }); + button.child = new St.Icon({ + icon_name: iconName, + }); - return button; + return button; } _removeMissingHotSensors(hotSensors) { - for (let i = hotSensors.length - 1; i >= 0; i--) { - let sensor = hotSensors[i]; + for (let i = hotSensors.length - 1; i >= 0; i--) { + let sensor = hotSensors[i]; - // make sure default icon (if any) stays visible - if (sensor == '_default_icon_') continue; + // make sure default icon (if any) stays visible + if (sensor == "_default_icon_") continue; - // removes sensors that are no longer available - if (!this._sensorMenuItems[sensor]) { - hotSensors.splice(i, 1); - this._removeHotItem(sensor); - } + // removes sensors that are no longer available + if (!this._sensorMenuItems[sensor]) { + hotSensors.splice(i, 1); + this._removeHotItem(sensor); } + } - return hotSensors; + return hotSensors; } _saveHotSensors(hotSensors) { - // removes any sensors that may not currently be available - hotSensors = this._removeMissingHotSensors(hotSensors); - - this._settings.set_strv('hot-sensors', hotSensors.filter( - function(item, pos) { - return hotSensors.indexOf(item) == pos; - } - )); + // removes any sensors that may not currently be available + hotSensors = this._removeMissingHotSensors(hotSensors); + + this._settings.set_strv( + "hot-sensors", + hotSensors.filter(function (item, pos) { + return hotSensors.indexOf(item) == pos; + }), + ); } _initializeTimer() { - // used to query sensors and update display - let update_time = this._settings.get_int('update-time'); - this._refreshTimeoutId = GLib.timeout_add_seconds( - GLib.PRIORITY_DEFAULT, - update_time, - (self) => { - // always query sensors (for panel display when hot) - this._querySensors(); - return GLib.SOURCE_CONTINUE; - } - ); + // used to query sensors and update display + let update_time = this._settings.get_int("update-time"); + this._refreshTimeoutId = GLib.timeout_add_seconds( + GLib.PRIORITY_DEFAULT, + update_time, + (self) => { + // always query sensors (for panel display when hot) + this._querySensors(); + return GLib.SOURCE_CONTINUE; + }, + ); } _createHotItem(key, value, gicon) { - let item = new St.BoxLayout({ - style_class: 'vitals-panel-item', - }); - this._hotItems[key] = item; - this._menuLayout.add_child(item); - - if (!this._settings.get_boolean('hide-icons') || key == '_default_icon_') { - let icon = this._defaultIcon(key); - if (gicon) icon.gicon = gicon; - item.add_child(icon); - } - - // don't add a label when no sensors are in the panel - if (key == '_default_icon_') return; - - let label = new St.Label({ - style_class: 'vitals-panel-label', - text: (value)?value:'\u2026', // ... - y_expand: true, - y_align: Clutter.ActorAlign.CENTER - }); - // attempt to prevent ellipsizes - label.get_clutter_text().ellipsize = 0; - // keep track of label for removal later - this._hotLabels[key] = label; - // prevent "called on the widget" "which is not in the stage" errors by adding before width below - item.add_child(label); - - // support for fixed widths #55, save label (text) width - this._widths[key] = label.get_clutter_text().width; + let item = new St.BoxLayout({ + style_class: "vitals-panel-item", + }); + this._lastHotSensorKey = key; + this._hotItems[key] = item; + this._menuLayout.add_child(item); + + if ( + !this._settings.get_boolean("hide-icons") || + key == "_default_icon_" + ) { + let icon = this._defaultIcon(key); + if (gicon) icon.gicon = gicon; + item.add_child(icon); + } + + // don't add a label when no sensors are in the panel + if (key == "_default_icon_") return; + + let label = new St.Label({ + style_class: "vitals-panel-label", + text: value ? value : "\u2026", // ... + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + }); + // attempt to prevent ellipsizes + label.get_clutter_text().ellipsize = 0; + // keep track of label for removal later + this._hotLabels[key] = label; + // prevent "called on the widget" "which is not in the stage" errors by adding before width below + item.add_child(label); + + // support for fixed widths #55, save label (text) width + this._widths[key] = label.get_clutter_text().width; } _showHideSensorsChanged(self, sensor) { - this._sensors.resetHistory(); + this._sensors.resetHistory(); - const sensorName = sensor.substr(5); - if(sensorName === 'gpu') { - for(let i = 1; i <= this._numGpus; i++) - this._groups[sensorName + '#' + i].visible = this._settings.get_boolean(sensor); - } else - this._groups[sensorName].visible = this._settings.get_boolean(sensor); + const sensorName = sensor.substr(5); + if (sensorName === "gpu") { + for (let i = 1; i <= this._numGpus; i++) + this._groups[sensorName + "#" + i].visible = + this._settings.get_boolean(sensor); + } else + this._groups[sensorName].visible = this._settings.get_boolean(sensor); } _positionInPanelChanged() { - this.container.get_parent().remove_child(this.container); - let position = this._positionInPanel(); - - // allows easily addressable boxes - let boxes = { - left: Main.panel._leftBox, - center: Main.panel._centerBox, - right: Main.panel._rightBox - }; - - // update position when changed from preferences - boxes[position[0]].insert_child_at_index(this.container, position[1]); + this.container.get_parent().remove_child(this.container); + let position = this._positionInPanel(); + + // allows easily addressable boxes + let boxes = { + left: Main.panel._leftBox, + center: Main.panel._centerBox, + right: Main.panel._rightBox, + }; + + // update position when changed from preferences + boxes[position[0]].insert_child_at_index(this.container, position[1]); } _redrawDetailsMenuIcons() { - // updates the icons on the 'details' menu, the one - // you have to click to appear - this._sensors.resetHistory(); - for (const sensor in this._sensorIcons) { - if (sensor == "gpu") continue; - this._groups[sensor].icon.gicon = Gio.icon_new_for_string(this._sensorIconPath(sensor)); - } - - // gpu's are indexed differently, handle them here - const gpuKeys = Object.keys(this._groups).filter(key => key.startsWith("gpu#")); - gpuKeys.forEach((gpuKey) => { - this._groups[gpuKey].icon.gicon = Gio.icon_new_for_string(this._sensorIconPath("gpu")); - }); + // updates the icons on the 'details' menu, the one + // you have to click to appear + this._sensors.resetHistory(); + for (const sensor in this._sensorIcons) { + if (sensor == "gpu") continue; + this._groups[sensor].icon.gicon = Gio.icon_new_for_string( + this._sensorIconPath(sensor), + ); + } + + // gpu's are indexed differently, handle them here + const gpuKeys = Object.keys(this._groups).filter((key) => + key.startsWith("gpu#"), + ); + gpuKeys.forEach((gpuKey) => { + this._groups[gpuKey].icon.gicon = Gio.icon_new_for_string( + this._sensorIconPath("gpu"), + ); + }); } _iconStyleChanged() { - this._redrawDetailsMenuIcons(); - this._redrawMenu(); + this._redrawDetailsMenuIcons(); + this._redrawMenu(); } - _removeHotItems(){ - for (let key in this._hotItems) { - this._removeHotItem(key); - } + _removeHotItems() { + for (let key in this._hotItems) { + this._removeHotItem(key); + } } _removeHotItem(key) { - if (key in this._hotItems) { - this._hotItems[key].destroy(); - delete this._hotItems[key]; - delete this._hotLabels[key]; - delete this._widths[key]; - } + if (key in this._hotItems) { + this._hotItems[key].destroy(); + delete this._hotItems[key]; + delete this._hotLabels[key]; + delete this._widths[key]; + } } _redrawMenu() { - this._removeHotItems(); - - for (let key in this._sensorMenuItems) { - if (key.includes('-group')) continue; - this._sensorMenuItems[key].destroy(); - delete this._sensorMenuItems[key]; - } - - this._drawMenu(); - this._sensors.resetHistory(); - this._values.resetHistory(this._numGpus); - this._querySensors(); + this._removeHotItems(); + + for (let key in this._sensorMenuItems) { + if (key.includes("-group")) continue; + this._sensorMenuItems[key].destroy(); + delete this._sensorMenuItems[key]; + } + + this._drawMenu(); + this._sensors.resetHistory(); + this._values.resetHistory(this._numGpus); + this._querySensors(); } _drawMenu() { - // grab list of selected menubar icons - let hotSensors = this._settings.get_strv('hot-sensors'); - for (let key of Object.values(hotSensors)) { - // fixes issue #225 which started when _max_ was moved to the end - if (key == '__max_network-download__') key = '__network-rx_max__'; - if (key == '__max_network-upload__') key = '__network-tx_max__'; - - this._createHotItem(key); - } + // grab list of selected menubar icons + let hotSensors = this._settings.get_strv("hot-sensors"); + for (let key of Object.values(hotSensors)) { + // fixes issue #225 which started when _max_ was moved to the end + if (key == "__max_network-download__") key = "__network-rx_max__"; + if (key == "__max_network-upload__") key = "__network-tx_max__"; + + this._createHotItem(key); + } } _destroyTimer() { - // invalidate and reinitialize timer - if (this._refreshTimeoutId != null) { - GLib.Source.remove(this._refreshTimeoutId); - this._refreshTimeoutId = null; - } + // invalidate and reinitialize timer + if (this._refreshTimeoutId != null) { + GLib.Source.remove(this._refreshTimeoutId); + this._refreshTimeoutId = null; + } } _updateTimeSettingChanged() { - this._destroyTimer(); - this._initializeTimer(); + this._destroyTimer(); + this._initializeTimer(); } _updateTimeChanged() { - this._destroyTimer(); - this._initializeTimer(); + this._destroyTimer(); + this._initializeTimer(); } _addSettingChangedSignal(key, callback) { - this._settingChangedSignals.push(this._settings.connect('changed::' + key, callback)); + this._settingChangedSignals.push( + this._settings.connect("changed::" + key, callback), + ); } _updateDisplay(label, value, type, key) { - // update sensor value in menubar - if (this._hotLabels[key]) { - this._hotLabels[key].set_text(value); - - // support for fixed widths #55 - if (this._settings.get_boolean('fixed-widths')) { - // grab text box width and see if new text is wider than old text - let width2 = this._hotLabels[key].get_clutter_text().width; - if (width2 > this._widths[key]) { - this._hotLabels[key].set_width(width2); - this._widths[key] = width2; - } - } + // update sensor value in menubar + if (this._hotLabels[key]) { + this._hotLabels[key].set_text(value); + + // support for fixed widths #55 + if (this._settings.get_boolean("fixed-widths")) { + // grab text box width and see if new text is wider than old text + let width2 = this._hotLabels[key].get_clutter_text().width; + if (width2 > this._widths[key]) { + this._hotLabels[key].set_width(width2); + this._widths[key] = width2; + } } - - // have we added this sensor before? - let item = this._sensorMenuItems[key]; - if (item) { - // update sensor value in the group - item.value = value; - } else if (type.includes('-group')) { - // update text next to group header - let group = type.split('-')[0]; - if (this._groups[group]) { - this._groups[group].status.text = value; - this._sensorMenuItems[type] = this._groups[group]; - } - } else { - // add item to group for the first time - let sensor = { 'label': label, 'value': value, 'type': type } - this._appendMenuItem(sensor, key); + } + + // have we added this sensor before? + let item = this._sensorMenuItems[key]; + if (item) { + // update sensor value in the group + item.value = value; + } else if (type.includes("-group")) { + // update text next to group header + let group = type.split("-")[0]; + if (this._groups[group]) { + this._groups[group].status.text = value; + this._sensorMenuItems[type] = this._groups[group]; } + } else { + // add item to group for the first time + let sensor = { label: label, value: value, type: type }; + this._appendMenuItem(sensor, key); + } } _appendMenuItem(sensor, key) { - let split = sensor.type.split('-'); - let type = split[0]; - let icon = (split.length == 2)?'icon-' + split[1]:'icon'; - let gicon = Gio.icon_new_for_string(this._sensorIconPath(type, icon)); - - let item = new MenuItem.MenuItem(gicon, key, sensor.label, sensor.value, this._hotLabels[key]); - item.connect('toggle', (self) => { - let hotSensors = this._settings.get_strv('hot-sensors'); - - if (self.checked) { - // add selected sensor to panel - hotSensors.push(self.key); - this._createHotItem(self.key, self.value, self.gicon); - } else { - // remove selected sensor from panel - hotSensors.splice(hotSensors.indexOf(self.key), 1); - this._removeHotItem(self.key); - } - - if (hotSensors.length <= 0) { - // add generic icon to panel when no sensors are selected - hotSensors.push('_default_icon_'); - this._createHotItem('_default_icon_'); - } else { - let defIconPos = hotSensors.indexOf('_default_icon_'); - if (defIconPos >= 0) { - // remove generic icon from panel when sensors are selected - hotSensors.splice(defIconPos, 1); - this._removeHotItem('_default_icon_'); - } - } - - // this code is called asynchronously - make sure to save it for next round - this._saveHotSensors(hotSensors); - }); - - this._sensorMenuItems[key] = item; - let i = Object.keys(this._sensorMenuItems[key]).length; + let split = sensor.type.split("-"); + let type = split[0]; + let icon = split.length == 2 ? "icon-" + split[1] : "icon"; + // Custom: Use special icon for AIO Pump or Coolant Temp + if (type === "fan" && key.includes("aio")) { + icon = "icon-pump"; + } + if (type === "temperature" && key.includes("coolant")) { + icon = "icon-c"; + } + let gicon = Gio.icon_new_for_string(this._sensorIconPath(type, icon)); + + let item = new MenuItem.MenuItem( + gicon, + key, + sensor.label, + sensor.value, + this._hotLabels[key], + ); + item.connect("toggle", (self) => { + let hotSensors = this._settings.get_strv("hot-sensors"); + + if (self.checked) { + // add selected sensor to panel + hotSensors.push(self.key); + this._createHotItem(self.key, self.value, self.gicon); + } else { + // remove selected sensor from panel + hotSensors.splice(hotSensors.indexOf(self.key), 1); + this._removeHotItem(self.key); + } - // alphabetize the sensors for these categories - if (this._settings.get_boolean('alphabetize')) { - let menuItems = this._groups[type].menu._getMenuItems(); - for (i = 0; i < menuItems.length; i++) - // use natural sort order for system load, etc - if (menuItems[i].label.localeCompare(item.label, undefined, { numeric: true, sensitivity: 'base' }) > 0) - break; + if (hotSensors.length <= 0) { + // add generic icon to panel when no sensors are selected + hotSensors.push("_default_icon_"); + this._createHotItem("_default_icon_"); + } else { + let defIconPos = hotSensors.indexOf("_default_icon_"); + if (defIconPos >= 0) { + // remove generic icon from panel when sensors are selected + hotSensors.splice(defIconPos, 1); + this._removeHotItem("_default_icon_"); + } } - this._groups[type].menu.addMenuItem(item, i); + // this code is called asynchronously - make sure to save it for next round + this._saveHotSensors(hotSensors); + }); + + this._sensorMenuItems[key] = item; + let i = Object.keys(this._sensorMenuItems[key]).length; + + // alphabetize the sensors for these categories + if (this._settings.get_boolean("alphabetize")) { + let menuItems = this._groups[type].menu._getMenuItems(); + for (i = 0; i < menuItems.length; i++) + // use natural sort order for system load, etc + if ( + menuItems[i].label.localeCompare(item.label, undefined, { + numeric: true, + sensitivity: "base", + }) > 0 + ) + break; + } + + this._groups[type].menu.addMenuItem(item, i); } _defaultLabel() { - return new St.Label({ - y_expand: true, - y_align: Clutter.ActorAlign.CENTER - }); + return new St.Label({ + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + }); } _defaultIcon(key) { - let split = key.replaceAll('_', ' ').trim().split(' ')[0].split('-'); - let type = split[0]; - - let icon = new St.Icon({ - style_class: 'system-status-icon vitals-panel-icon-' + type, - reactive: true - }); - - // second condition prevents crash due to issue #225, which started when _max_ was moved to the end - // don't use the default system icon if the type is a gpu; use the universal gpu icon instead - if (type == 'default' || (!(type in this._sensorIcons) && !type.startsWith('gpu'))) { - icon.gicon = Gio.icon_new_for_string(this._sensorIconPath('system')); - } else { // support for hide icons #80 - let iconObj = (split.length == 2)?'icon-' + split[1]:'icon'; - icon.gicon = Gio.icon_new_for_string(this._sensorIconPath(type, iconObj)); - } + let split = key.replaceAll("_", " ").trim().split(" ")[0].split("-"); + let type = split[0]; + + let icon = new St.Icon({ + style_class: "system-status-icon vitals-panel-icon-" + type, + reactive: true, + }); + + // second condition prevents crash due to issue #225, which started when _max_ was moved to the end + // don't use the default system icon if the type is a gpu; use the universal gpu icon instead + if ( + type == "default" || + (!(type in this._sensorIcons) && !type.startsWith("gpu")) + ) { + icon.gicon = Gio.icon_new_for_string(this._sensorIconPath("system")); + } else { + // support for hide icons #80 + let iconObj = split.length == 2 ? "icon-" + split[1] : "icon"; + icon.gicon = Gio.icon_new_for_string( + this._sensorIconPath(type, iconObj), + ); + } - return icon; + return icon; } - _sensorIconPath(sensor, icon = 'icon') { - // If the sensor is a numbered gpu, use the gpu icon. Otherwise use whatever icon associated with the sensor name. - let sensorKey = sensor; - if(sensor.startsWith('gpu')) sensorKey = 'gpu'; - - const iconPathPrefixIndex = this._settings.get_int('icon-style'); - return this._extensionObject.path + this._sensorsIconPathPrefix[iconPathPrefixIndex] + this._sensorIcons[sensorKey][icon]; + _sensorIconPath(sensor, icon = "icon") { + // If the sensor is a numbered gpu, use the gpu icon. Otherwise use whatever icon associated with the sensor name. + let sensorKey = sensor; + if (sensor.startsWith("gpu")) sensorKey = "gpu"; + // Custom: Use water droplet for coolant temp + // The key for your sensor will be like "_temperature_ac_vision_coolant_temp_" + if ( + sensorKey === "temperature" && + icon === "icon" && + this._lastHotSensorKey && + this._lastHotSensorKey.toLowerCase().includes("coolant") + ) { + icon = "icon-c"; + } + if ( + sensorKey === "fan" && + icon === "icon" && + this._lastHotSensorKey && + this._lastHotSensorKey.toLowerCase().includes("aio") + ) { + icon = "icon-pump"; + } + + const iconPathPrefixIndex = this._settings.get_int("icon-style"); + return ( + this._extensionObject.path + + this._sensorsIconPathPrefix[iconPathPrefixIndex] + + this._sensorIcons[sensorKey][icon] + ); } _ucFirst(string) { - if(string.startsWith('gpu')) return 'Graphics'; - return string.charAt(0).toUpperCase() + string.slice(1); + if (string.startsWith("gpu")) return "Graphics"; + return string.charAt(0).toUpperCase() + string.slice(1); } _positionInPanel() { - let alignment = ''; - let gravity = 0; - let arrow_pos = 0; - - switch (this._settings.get_int('position-in-panel')) { - case 0: // left - alignment = 'left'; - gravity = -1; - arrow_pos = 1; - break; - case 1: // center - alignment = 'center'; - gravity = -1; - arrow_pos = 0.5; - break; - case 2: // right - alignment = 'right'; - gravity = 0; - arrow_pos = 0; - break; - case 3: // far left - alignment = 'left'; - gravity = 0; - arrow_pos = 1; - break; - case 4: // far right - alignment = 'right'; - gravity = -1; - arrow_pos = 0; - break; - } - - let centered = this._settings.get_boolean('menu-centered') - if (centered) arrow_pos = 0.5; - - // set arrow position when initializing and moving vitals - this.menu._arrowAlignment = arrow_pos; - - return [alignment, gravity]; + let alignment = ""; + let gravity = 0; + let arrow_pos = 0; + + switch (this._settings.get_int("position-in-panel")) { + case 0: // left + alignment = "left"; + gravity = -1; + arrow_pos = 1; + break; + case 1: // center + alignment = "center"; + gravity = -1; + arrow_pos = 0.5; + break; + case 2: // right + alignment = "right"; + gravity = 0; + arrow_pos = 0; + break; + case 3: // far left + alignment = "left"; + gravity = 0; + arrow_pos = 1; + break; + case 4: // far right + alignment = "right"; + gravity = -1; + arrow_pos = 0; + break; + } + + let centered = this._settings.get_boolean("menu-centered"); + if (centered) arrow_pos = 0.5; + + // set arrow position when initializing and moving vitals + this.menu._arrowAlignment = arrow_pos; + + return [alignment, gravity]; } _querySensors() { - // figure out last run time - let now = new Date().getTime(); - let dwell = (now - this._last_query) / 1000; - this._last_query = now; - - this._sensors.query((label, value, type, format) => { - let typeKey = type.replace('-group', ''); - if (/^network-(?!rx$|tx$)/.test(typeKey)) typeKey = 'network'; - let key = '_' + typeKey + '_' + label.replace(' ', '_').toLowerCase() + '_'; - - // if a sensor is disabled, gray it out - if (key in this._sensorMenuItems) { - this._sensorMenuItems[key].setSensitive((value!='disabled')); - - // don't continue below, last known value is shown - if (value == 'disabled') return; - } + // figure out last run time + let now = new Date().getTime(); + let dwell = (now - this._last_query) / 1000; + this._last_query = now; + + this._sensors.query((label, value, type, format) => { + let typeKey = type.replace("-group", ""); + if (/^network-(?!rx$|tx$)/.test(typeKey)) typeKey = "network"; + let key = + "_" + typeKey + "_" + label.replace(" ", "_").toLowerCase() + "_"; + + // if a sensor is disabled, gray it out + if (key in this._sensorMenuItems) { + this._sensorMenuItems[key].setSensitive(value != "disabled"); + + // don't continue below, last known value is shown + if (value == "disabled") return; + } - // add/initialize any gpu groups that we haven't added yet - if (typeKey.startsWith('gpu') && typeKey !== 'gpu#1') { - const split = typeKey.split('#'); - if(split.length == 2 && this._numGpus < parseInt(split[1])) { - // occasionally two lines from nvidia-smi will be read at once - // so we only actually update the number of gpus if we have recieved multiple lines at least 3 times in a row - // i.e. we make sure that mutiple queries have detected a new gpu back-to-back - if(this._newGpuDetectedCount < 2) { - this._newGpuDetected = true; - return; - } - - this._numGpus = parseInt(split[1]); - this._newGpuDetectedCount = 0; - this._newGpuDetected = false; - // change label for gpu 1 from "Graphics" to "Graphics 1" since we have multiple gpus now - this._groups['gpu#1'].label.text = this._ucFirst('gpu#1') + ' 1'; - for(let i = 2; i <= this._numGpus; i++) - if(!('gpu#' + i in this._groups)) - this._initializeMenuGroup('gpu#' + i, 'gpu', ' ' + i, Object.keys(this._groups).length); - } + // add/initialize any gpu groups that we haven't added yet + if (typeKey.startsWith("gpu") && typeKey !== "gpu#1") { + const split = typeKey.split("#"); + if (split.length == 2 && this._numGpus < parseInt(split[1])) { + // occasionally two lines from nvidia-smi will be read at once + // so we only actually update the number of gpus if we have recieved multiple lines at least 3 times in a row + // i.e. we make sure that mutiple queries have detected a new gpu back-to-back + if (this._newGpuDetectedCount < 2) { + this._newGpuDetected = true; + return; } - let items = this._values.returnIfDifferent(dwell, label, value, type, format, key); - for (let item of items) { - if (item.type.startsWith('network-') && item.type.length == 10 && item.type != 'network-rx' && item.type != 'network-tx') { - // Geo / flags: stable key (no country in key); type stays network- for icon-us etc. - const stem = item.type.slice('network-'.length); - let flagGIcon = Gio.icon_new_for_string(this._sensorIconPath('network', 'icon-' + stem)); - if (this._hotItems[item.key] && !this._settings.get_boolean('hide-icons')) { - // change icon in menu bar - let icon = this._hotItems[item.key].get_first_child(); - if (icon instanceof St.Icon) - icon.gicon = flagGIcon; - } - // change icon in dropdown - let menuRow = this._sensorMenuItems[item.key]; - if (menuRow) menuRow.gicon = flagGIcon; - } - - this._updateDisplay(_(item.label), item.value, item.type, item.key); - } - }, dwell); + this._numGpus = parseInt(split[1]); + this._newGpuDetectedCount = 0; + this._newGpuDetected = false; + // change label for gpu 1 from "Graphics" to "Graphics 1" since we have multiple gpus now + this._groups["gpu#1"].label.text = this._ucFirst("gpu#1") + " 1"; + for (let i = 2; i <= this._numGpus; i++) + if (!("gpu#" + i in this._groups)) + this._initializeMenuGroup( + "gpu#" + i, + "gpu", + " " + i, + Object.keys(this._groups).length, + ); + } + } - //if a new gpu has been detected during the last query, then increment the amount of times we've detected a new gpu - if(this._newGpuDetected) this._newGpuDetectedCount++; - else this._newGpuDetectedCount = 0; - this._newGpuDetected = false; + let items = this._values.returnIfDifferent( + dwell, + label, + value, + type, + format, + key, + ); + for (let item of items) { + if ( + item.type.startsWith("network-") && + item.type.length == 10 && + item.type != "network-rx" && + item.type != "network-tx" + ) { + // Geo / flags: stable key (no country in key); type stays network- for icon-us etc. + const stem = item.type.slice("network-".length); + let flagGIcon = Gio.icon_new_for_string( + this._sensorIconPath("network", "icon-" + stem), + ); + if ( + this._hotItems[item.key] && + !this._settings.get_boolean("hide-icons") + ) { + // change icon in menu bar + let icon = this._hotItems[item.key].get_first_child(); + if (icon instanceof St.Icon) icon.gicon = flagGIcon; + } + // change icon in dropdown + let menuRow = this._sensorMenuItems[item.key]; + if (menuRow) menuRow.gicon = flagGIcon; + } - if (this._warnings.length > 0) { - this._notify('Vitals', this._warnings.join("\n"), 'folder-symbolic'); - this._warnings = []; + this._updateDisplay(_(item.label), item.value, item.type, item.key); } + }, dwell); + + //if a new gpu has been detected during the last query, then increment the amount of times we've detected a new gpu + if (this._newGpuDetected) this._newGpuDetectedCount++; + else this._newGpuDetectedCount = 0; + this._newGpuDetected = false; + + if (this._warnings.length > 0) { + this._notify("Vitals", this._warnings.join("\n"), "folder-symbolic"); + this._warnings = []; + } } _notify(msg, details, icon) { - let source = new MessageTray.Source('MyApp Information', icon); - Main.messageTray.add(source); - let notification = new MessageTray.Notification(source, msg, details); - notification.setTransient(true); - source.notify(notification); + let source = new MessageTray.Source("MyApp Information", icon); + Main.messageTray.add(source); + let notification = new MessageTray.Notification(source, msg, details); + notification.setTransient(true); + source.notify(notification); } destroy() { - this._destroyTimer(); - this._sensors.destroy(); + this._destroyTimer(); + this._sensors.destroy(); - for (let signal of Object.values(this._settingChangedSignals)) - this._settings.disconnect(signal); + for (let signal of Object.values(this._settingChangedSignals)) + this._settings.disconnect(signal); - super.destroy(); + super.destroy(); } -}); + }, +); export default class VitalsExtension extends Extension { - enable() { - vitalsMenu = new VitalsMenuButton(this); - let position = vitalsMenu._positionInPanel(); - Main.panel.addToStatusArea('vitalsMenu', vitalsMenu, position[1], position[0]); - } - - disable() { - vitalsMenu.destroy(); - vitalsMenu = null; - } + enable() { + vitalsMenu = new VitalsMenuButton(this); + let position = vitalsMenu._positionInPanel(); + Main.panel.addToStatusArea( + "vitalsMenu", + vitalsMenu, + position[1], + position[0], + ); + } + + disable() { + vitalsMenu.destroy(); + vitalsMenu = null; + } } diff --git a/icons/gnome/pump-symbolic.svg b/icons/gnome/pump-symbolic.svg new file mode 100644 index 00000000..a64d0d17 --- /dev/null +++ b/icons/gnome/pump-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/gnome/water-droplet-symbolic.svg b/icons/gnome/water-droplet-symbolic.svg new file mode 100644 index 00000000..9d6199a9 --- /dev/null +++ b/icons/gnome/water-droplet-symbolic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/icons/original/pump-symbolic.svg b/icons/original/pump-symbolic.svg new file mode 100644 index 00000000..a64d0d17 --- /dev/null +++ b/icons/original/pump-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/original/water-droplet-symbolic.svg b/icons/original/water-droplet-symbolic.svg new file mode 100644 index 00000000..9d6199a9 --- /dev/null +++ b/icons/original/water-droplet-symbolic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/sensors.js b/sensors.js index d61b0909..a03710d2 100644 --- a/sensors.js +++ b/sensors.js @@ -24,874 +24,1817 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import GLib from 'gi://GLib'; -import GObject from 'gi://GObject'; -import * as SubProcessModule from './helpers/subprocess.js'; -import * as FileModule from './helpers/file.js'; -import { gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js'; -import NM from 'gi://NM'; - -let GTop, hasGTop = true; +import GLib from "gi://GLib"; +import GObject from "gi://GObject"; +import Gio from "gi://Gio"; +import * as SubProcessModule from "./helpers/subprocess.js"; +import * as FileModule from "./helpers/file.js"; +import { gettext as _ } from "resource:///org/gnome/shell/extensions/extension.js"; +import NM from "gi://NM"; + +let GTop, + hasGTop = true; try { - ({default: GTop} = await import('gi://GTop')); + ({ default: GTop } = await import("gi://GTop")); } catch (err) { - log(err); - hasGTop = false; -}; - -export const Sensors = GObject.registerClass({ - GTypeName: 'Sensors', -}, class Sensors extends GObject.Object { + log(err); + hasGTop = false; +} + +export const Sensors = GObject.registerClass( + { + GTypeName: "Sensors", + }, + class Sensors extends GObject.Object { _init(settings, sensorIcons) { - this._settings = settings; - this._sensorIcons = sensorIcons; - - this.resetHistory(); - - this._last_processor = { 'core': {}, 'speed': [] }; - - this._settingChangedSignals = []; - this._addSettingChangedSignal('show-gpu', this._reconfigureNvidiaSmiProcess.bind(this)); - this._addSettingChangedSignal('update-time', this._reconfigureNvidiaSmiProcess.bind(this)); - this._addSettingChangedSignal('network-public-ip-interval', () => {this._lastPublicIPCheck = 0;}); - //this._addSettingChangedSignal('include-static-gpu-info', this._reconfigureNvidiaSmiProcess.bind(this)); - - this._gpu_drm_vendors = null; - this._gpu_drm_indices = null; - this._nvidia_smi_process = null; - this._nvidia_labels = []; - this._bad_split_count = 0; - - this._frameMonitorSignalId = 0; - this._frameMonitorLastTime = 0; - this._frameMonitorFrameCount = 0; - this._frameMonitorAccTime = 0; - this._frameMonitorCurrentHz = 0; - - if (hasGTop) { - this.storage = new GTop.glibtop_fsusage(); - this._storageDevice = ''; - this._findStorageDevice(); - - this._lastRead = 0; - this._lastWrite = 0; - } + this._settings = settings; + this._sensorIcons = sensorIcons; + + this.resetHistory(); + + this._last_processor = { core: {}, speed: [] }; + + this._settingChangedSignals = []; + this._addSettingChangedSignal( + "show-gpu", + this._reconfigureNvidiaSmiProcess.bind(this), + ); + this._addSettingChangedSignal( + "update-time", + this._reconfigureNvidiaSmiProcess.bind(this), + ); + this._addSettingChangedSignal("network-public-ip-interval", () => { + this._lastPublicIPCheck = 0; + }); + //this._addSettingChangedSignal('include-static-gpu-info', this._reconfigureNvidiaSmiProcess.bind(this)); + + this._gpu_drm_vendors = null; + this._gpu_drm_indices = null; + this._nvidia_smi_process = null; + this._nvidia_labels = []; + this._bad_split_count = 0; + + this._frameMonitorSignalId = 0; + this._frameMonitorLastTime = 0; + this._frameMonitorFrameCount = 0; + this._frameMonitorAccTime = 0; + this._frameMonitorCurrentHz = 0; + + if (hasGTop) { + this.storage = new GTop.glibtop_fsusage(); + this._storageDevice = ""; + this._findStorageDevice(); + + this._lastRead = 0; + this._lastWrite = 0; + } } _addSettingChangedSignal(key, callback) { - this._settingChangedSignals.push(this._settings.connect('changed::' + key, callback)); + this._settingChangedSignals.push( + this._settings.connect("changed::" + key, callback), + ); } _refreshIPAddress(callback) { - // check IP address - new FileModule.File('https://ipv4.corecoding.com').read().then(contents => { - let obj = JSON.parse(contents); - let cc = (obj && typeof obj['countryCode'] === 'string') ? obj['countryCode'].trim().toLowerCase() : ''; - let ip = (obj && typeof obj['IPv4'] === 'string') ? obj['IPv4'].trim() : ''; - const showFlag = this._settings.get_boolean('network-public-ip-show-flag'); - let typeOut = (showFlag && /^[a-z]{2}$/.test(cc)) ? ('network-' + cc) : 'network'; - this._returnValue(callback, 'Public IP', ip, typeOut, 'string'); - }).catch(err => { }); + // check IP address + new FileModule.File("https://ipv4.corecoding.com") + .read() + .then((contents) => { + let obj = JSON.parse(contents); + let cc = + obj && typeof obj["countryCode"] === "string" + ? obj["countryCode"].trim().toLowerCase() + : ""; + let ip = + obj && typeof obj["IPv4"] === "string" ? obj["IPv4"].trim() : ""; + const showFlag = this._settings.get_boolean( + "network-public-ip-show-flag", + ); + let typeOut = + showFlag && /^[a-z]{2}$/.test(cc) ? "network-" + cc : "network"; + this._returnValue(callback, "Public IP", ip, typeOut, "string"); + }) + .catch((err) => {}); } _findStorageDevice() { - new FileModule.File('/proc/mounts').read("\n").then(lines => { - for (let line of lines) { - let loadArray = line.trim().split(/\s+/); - if (loadArray[1] == this._settings.get_string('storage-path')) { - this._storageDevice = loadArray[0]; - break; - } + new FileModule.File("/proc/mounts") + .read("\n") + .then((lines) => { + for (let line of lines) { + let loadArray = line.trim().split(/\s+/); + if (loadArray[1] == this._settings.get_string("storage-path")) { + this._storageDevice = loadArray[0]; + break; } - }).catch(err => { }); + } + }) + .catch((err) => {}); } query(callback, dwell) { - if (!this._hardware_detected) { - // we could set _hardware_detected in discoverHardwareMonitors, but by - // doing it here, we guarantee avoidance of race conditions - this._hardware_detected = true; - this._discoverHardwareMonitors(callback); - } - - for (let sensor in this._sensorIcons) { - if (this._settings.get_boolean('show-' + sensor)) { - if (sensor == 'temperature' || sensor == 'voltage' || sensor == 'fan') { - // for temp, volt, fan, we have a shared handler - this._queryTempVoltFan(callback, sensor); - } else { - // directly call queryFunction below - let method = '_query' + sensor[0].toUpperCase() + sensor.slice(1); - this[method](callback, dwell); - } - } + if (!this._hardware_detected) { + // we could set _hardware_detected in discoverHardwareMonitors, but by + // doing it here, we guarantee avoidance of race conditions + this._hardware_detected = true; + this._discoverHardwareMonitors(callback); + } + + for (let sensor in this._sensorIcons) { + if (this._settings.get_boolean("show-" + sensor)) { + if ( + sensor == "temperature" || + sensor == "voltage" || + sensor == "fan" + ) { + // for temp, volt, fan, we have a shared handler + this._queryTempVoltFan(callback, sensor); + } else { + // directly call queryFunction below + let method = "_query" + sensor[0].toUpperCase() + sensor.slice(1); + this[method](callback, dwell); + } } + } } _queryTempVoltFan(callback, type) { - for (let label in this._tempVoltFanSensors[type]) { - let sensor = this._tempVoltFanSensors[type][label]; + for (let label in this._tempVoltFanSensors[type]) { + let sensor = this._tempVoltFanSensors[type][label]; - new FileModule.File(sensor['path']).read().then(value => { - this._returnValue(callback, label, value, type, sensor['format']); - }).catch(err => { - this._returnValue(callback, label, 'disabled', type, sensor['format']); - }); + // Special handling for Aquacomputer Vision + if (sensor.aquacomputer) { + this._readAquacomputerSensor(callback, label, sensor, type); + continue; } + + new FileModule.File(sensor["path"]) + .read() + .then((value) => { + this._returnValue(callback, label, value, type, sensor["format"]); + }) + .catch((err) => { + this._returnValue( + callback, + label, + "disabled", + type, + sensor["format"], + ); + }); + } + } + + _readAquacomputerSensor(callback, label, sensor, type) { + try { + let file = Gio.File.new_for_path(sensor["path"]); + file.read_async(GLib.PRIORITY_DEFAULT, null, (obj, res) => { + try { + let stream = obj.read_finish(res); + let input = new Gio.DataInputStream({ base_stream: stream }); + + input.read_bytes_async( + 64, + GLib.PRIORITY_DEFAULT, + null, + (input, res2) => { + try { + let bytes = input.read_bytes_finish(res2); + let arr = new Uint8Array(bytes.get_data()); + + if (arr.length < 64 || arr[0] !== 0x01) { + this._returnValue( + callback, + label, + "disabled", + type, + sensor["format"], + ); + return; + } + + // Read coolant temperature - correct offset and encoding + const offset = 0x37; // Offset 55 (0x37) contains correct temperature data + let raw = (arr[offset] << 8) | arr[offset + 1]; // Big-endian encoding + let temp = (raw / 100) * 1000; // millidegrees + + this._returnValue( + callback, + label, + temp, + type, + sensor["format"], + ); + } catch (e) { + this._returnValue( + callback, + label, + "disabled", + type, + sensor["format"], + ); + } + }, + ); + } catch (e) { + this._returnValue( + callback, + label, + "disabled", + type, + sensor["format"], + ); + } + }); + } catch (e) { + this._returnValue(callback, label, "disabled", type, sensor["format"]); + } } _queryMemory(callback) { - // check memory info - new FileModule.File('/proc/meminfo').read().then(lines => { - let values = '', total = 0, avail = 0, swapTotal = 0, swapFree = 0, cached = 0, memFree = 0; - - if (values = lines.match(/MemTotal:(\s+)(\d+) kB/)) total = values[2]; - if (values = lines.match(/MemAvailable:(\s+)(\d+) kB/)) avail = values[2]; - if (values = lines.match(/SwapTotal:(\s+)(\d+) kB/)) swapTotal = values[2]; - if (values = lines.match(/SwapFree:(\s+)(\d+) kB/)) swapFree = values[2]; - if (values = lines.match(/Cached:(\s+)(\d+) kB/)) cached = values[2]; - if (values = lines.match(/MemFree:(\s+)(\d+) kB/)) memFree = values[2]; - - let used = total - avail - let utilized = used / total; - let swapUsed = swapTotal - swapFree - let swapUtilized = swapUsed / swapTotal; - - this._returnValue(callback, 'Usage', utilized, 'memory', 'percent'); - this._returnValue(callback, 'memory', utilized, 'memory-group', 'percent'); - this._returnValue(callback, 'Physical', total, 'memory', 'memory'); - this._returnValue(callback, 'Available', avail, 'memory', 'memory'); - this._returnValue(callback, 'Allocated', used, 'memory', 'memory'); - this._returnValue(callback, 'Cached', cached, 'memory', 'memory'); - this._returnValue(callback, 'Free', memFree, 'memory', 'memory'); - this._returnValue(callback, 'Swap Total', swapTotal, 'memory', 'memory'); - this._returnValue(callback, 'Swap Free', swapFree, 'memory', 'memory'); - this._returnValue(callback, 'Swap Used', swapUsed, 'memory', 'memory'); - this._returnValue(callback, 'Swap Usage', swapUtilized, 'memory', 'percent'); - }).catch(err => { }); + // check memory info + new FileModule.File("/proc/meminfo") + .read() + .then((lines) => { + let values = "", + total = 0, + avail = 0, + swapTotal = 0, + swapFree = 0, + cached = 0, + memFree = 0; + + if ((values = lines.match(/MemTotal:(\s+)(\d+) kB/))) + total = values[2]; + if ((values = lines.match(/MemAvailable:(\s+)(\d+) kB/))) + avail = values[2]; + if ((values = lines.match(/SwapTotal:(\s+)(\d+) kB/))) + swapTotal = values[2]; + if ((values = lines.match(/SwapFree:(\s+)(\d+) kB/))) + swapFree = values[2]; + if ((values = lines.match(/Cached:(\s+)(\d+) kB/))) + cached = values[2]; + if ((values = lines.match(/MemFree:(\s+)(\d+) kB/))) + memFree = values[2]; + + let used = total - avail; + let utilized = used / total; + let swapUsed = swapTotal - swapFree; + let swapUtilized = swapUsed / swapTotal; + + this._returnValue(callback, "Usage", utilized, "memory", "percent"); + this._returnValue( + callback, + "memory", + utilized, + "memory-group", + "percent", + ); + this._returnValue(callback, "Physical", total, "memory", "memory"); + this._returnValue(callback, "Available", avail, "memory", "memory"); + this._returnValue(callback, "Allocated", used, "memory", "memory"); + this._returnValue(callback, "Cached", cached, "memory", "memory"); + this._returnValue(callback, "Free", memFree, "memory", "memory"); + this._returnValue( + callback, + "Swap Total", + swapTotal, + "memory", + "memory", + ); + this._returnValue( + callback, + "Swap Free", + swapFree, + "memory", + "memory", + ); + this._returnValue( + callback, + "Swap Used", + swapUsed, + "memory", + "memory", + ); + this._returnValue( + callback, + "Swap Usage", + swapUtilized, + "memory", + "percent", + ); + }) + .catch((err) => {}); } _queryProcessor(callback, dwell) { - let columns = ['user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq', 'steal', 'guest', 'guest_nice']; - - // check processor usage - new FileModule.File('/proc/stat').read("\n").then(lines => { - let statistics = {}; - - for (let line of lines) { - let reverse_data = line.match(/^(cpu\d*\s)(.+)/); - if (reverse_data) { - let cpu = reverse_data[1].trim(); - - if (!(cpu in statistics)) - statistics[cpu] = {}; - - if (!(cpu in this._last_processor['core'])) - this._last_processor['core'][cpu] = 0; - - let stats = reverse_data[2].trim().split(' ').reverse(); - for (let column of columns) - statistics[cpu][column] = parseInt(stats.pop()); - } + let columns = [ + "user", + "nice", + "system", + "idle", + "iowait", + "irq", + "softirq", + "steal", + "guest", + "guest_nice", + ]; + + // check processor usage + new FileModule.File("/proc/stat") + .read("\n") + .then((lines) => { + let statistics = {}; + + for (let line of lines) { + let reverse_data = line.match(/^(cpu\d*\s)(.+)/); + if (reverse_data) { + let cpu = reverse_data[1].trim(); + + if (!(cpu in statistics)) statistics[cpu] = {}; + + if (!(cpu in this._last_processor["core"])) + this._last_processor["core"][cpu] = 0; + + let stats = reverse_data[2].trim().split(" ").reverse(); + for (let column of columns) + statistics[cpu][column] = parseInt(stats.pop()); } - - let cores = Object.keys(statistics).length - 1; - - for (let cpu in statistics) { - let total = statistics[cpu]['user'] + statistics[cpu]['nice'] + statistics[cpu]['system']; - - // make sure we have data to report - if (this._last_processor['core'][cpu] > 0) { - let delta = (total - this._last_processor['core'][cpu]) / dwell; - - // /proc/stat provides overall usage for us under the 'cpu' heading - if (cpu == 'cpu') { - delta = delta / cores; - this._returnValue(callback, 'processor', delta / 100, 'processor-group', 'percent'); - this._returnValue(callback, 'Usage', delta / 100, 'processor', 'percent'); - } else { - this._returnValue(callback, _('Core %d').format(cpu.substr(3)), delta / 100, 'processor', 'percent'); - } - } - - this._last_processor['core'][cpu] = total; + } + + let cores = Object.keys(statistics).length - 1; + + for (let cpu in statistics) { + let total = + statistics[cpu]["user"] + + statistics[cpu]["nice"] + + statistics[cpu]["system"]; + + // make sure we have data to report + if (this._last_processor["core"][cpu] > 0) { + let delta = (total - this._last_processor["core"][cpu]) / dwell; + + // /proc/stat provides overall usage for us under the 'cpu' heading + if (cpu == "cpu") { + delta = delta / cores; + this._returnValue( + callback, + "processor", + delta / 100, + "processor-group", + "percent", + ); + this._returnValue( + callback, + "Usage", + delta / 100, + "processor", + "percent", + ); + } else { + this._returnValue( + callback, + _("Core %d").format(cpu.substr(3)), + delta / 100, + "processor", + "percent", + ); + } } - // if frequency scaling is enabled, gather cpu-freq values - if (!this._processor_uses_cpu_info) { - for (let core = 0; core <= cores; core++) { - new FileModule.File('/sys/devices/system/cpu/cpu' + core + '/cpufreq/scaling_cur_freq').read().then(value => { - this._last_processor['speed'][core] = parseInt(value); - }).catch(err => { }); - } + this._last_processor["core"][cpu] = total; + } + + // if frequency scaling is enabled, gather cpu-freq values + if (!this._processor_uses_cpu_info) { + for (let core = 0; core <= cores; core++) { + new FileModule.File( + "/sys/devices/system/cpu/cpu" + + core + + "/cpufreq/scaling_cur_freq", + ) + .read() + .then((value) => { + this._last_processor["speed"][core] = parseInt(value); + }) + .catch((err) => {}); + } + } + }) + .catch((err) => {}); + + // if frequency scaling is disabled, use cpuinfo for speed + if (this._processor_uses_cpu_info) { + // grab CPU frequency + new FileModule.File("/proc/cpuinfo") + .read("\n") + .then((lines) => { + let freqs = []; + for (let line of lines) { + // grab megahertz + let value = line.match(/^cpu MHz(\s+): ([+-]?\d+(\.\d+)?)/); + if (value) freqs.push(parseFloat(value[2])); } - }).catch(err => { }); - - // if frequency scaling is disabled, use cpuinfo for speed - if (this._processor_uses_cpu_info) { - // grab CPU frequency - new FileModule.File('/proc/cpuinfo').read("\n").then(lines => { - let freqs = []; - for (let line of lines) { - // grab megahertz - let value = line.match(/^cpu MHz(\s+): ([+-]?\d+(\.\d+)?)/); - if (value) freqs.push(parseFloat(value[2])); - } - - let sum = freqs.reduce((a, b) => a + b); - let hertz = (sum / freqs.length) * 1000 * 1000; - this._returnValue(callback, 'Frequency', hertz, 'processor', 'hertz'); - let max_hertz = freqs.reduce((a, b) => Math.max(a, b)) * 1000 * 1000; - this._returnValue(callback, 'Max frequency', max_hertz, 'processor', 'hertz'); - let min_hertz = freqs.reduce((a, b) => Math.min(a, b)) * 1000 * 1000; - this._returnValue(callback, 'Min frequency', min_hertz, 'processor', 'hertz'); - }).catch(err => { }); + let sum = freqs.reduce((a, b) => a + b); + let hertz = (sum / freqs.length) * 1000 * 1000; + this._returnValue( + callback, + "Frequency", + hertz, + "processor", + "hertz", + ); + + let max_hertz = + freqs.reduce((a, b) => Math.max(a, b)) * 1000 * 1000; + this._returnValue( + callback, + "Max frequency", + max_hertz, + "processor", + "hertz", + ); + let min_hertz = + freqs.reduce((a, b) => Math.min(a, b)) * 1000 * 1000; + this._returnValue( + callback, + "Min frequency", + min_hertz, + "processor", + "hertz", + ); + }) + .catch((err) => {}); // if frequency scaling is enabled, cpu-freq reports - } else if (Object.values(this._last_processor['speed']).length > 0) { - let sum = this._last_processor['speed'].reduce((a, b) => a + b); - let hertz = (sum / this._last_processor['speed'].length) * 1000; - this._returnValue(callback, 'Frequency', hertz, 'processor', 'hertz'); - let max_hertz = this._last_processor['speed'].reduce((a, b) => Math.max(a, b)) * 1000; - this._returnValue(callback, 'Max frequency', max_hertz, 'processor', 'hertz'); - let min_hertz = this._last_processor['speed'].reduce((a, b) => Math.min(a, b)) * 1000; - this._returnValue(callback, 'Min frequency', min_hertz, 'processor', 'hertz'); - } + } else if (Object.values(this._last_processor["speed"]).length > 0) { + let sum = this._last_processor["speed"].reduce((a, b) => a + b); + let hertz = (sum / this._last_processor["speed"].length) * 1000; + this._returnValue(callback, "Frequency", hertz, "processor", "hertz"); + let max_hertz = + this._last_processor["speed"].reduce((a, b) => Math.max(a, b)) * 1000; + this._returnValue( + callback, + "Max frequency", + max_hertz, + "processor", + "hertz", + ); + let min_hertz = + this._last_processor["speed"].reduce((a, b) => Math.min(a, b)) * 1000; + this._returnValue( + callback, + "Min frequency", + min_hertz, + "processor", + "hertz", + ); + } } _querySystem(callback) { - // check load average - new FileModule.File('/proc/sys/fs/file-nr').read("\t").then(loadArray => { - this._returnValue(callback, 'Open Files', loadArray[0], 'system', 'string'); - }).catch(err => { }); - - // check load average - new FileModule.File('/proc/loadavg').read(' ').then(loadArray => { - let proc = loadArray[3].split('/'); - - this._returnValue(callback, 'Load 1m', loadArray[0], 'system', 'load'); - this._returnValue(callback, 'system', loadArray[0], 'system-group', 'load'); - this._returnValue(callback, 'Load 5m', loadArray[1], 'system', 'load'); - this._returnValue(callback, 'Load 15m', loadArray[2], 'system', 'load'); - this._returnValue(callback, 'Threads Active', proc[0], 'system', 'string'); - this._returnValue(callback, 'Threads Total', proc[1], 'system', 'string'); - }).catch(err => { }); - - // check uptime - new FileModule.File('/proc/uptime').read(' ').then(upArray => { - this._returnValue(callback, 'Uptime', upArray[0], 'system', 'uptime'); - - let cores = Object.keys(this._last_processor['core']).length - 1; - if (cores > 0) - this._returnValue(callback, 'Process Time', upArray[0] - upArray[1] / cores, 'processor', 'uptime'); - }).catch(err => { }); + // check load average + new FileModule.File("/proc/sys/fs/file-nr") + .read("\t") + .then((loadArray) => { + this._returnValue( + callback, + "Open Files", + loadArray[0], + "system", + "string", + ); + }) + .catch((err) => {}); + + // check load average + new FileModule.File("/proc/loadavg") + .read(" ") + .then((loadArray) => { + let proc = loadArray[3].split("/"); + + this._returnValue( + callback, + "Load 1m", + loadArray[0], + "system", + "load", + ); + this._returnValue( + callback, + "system", + loadArray[0], + "system-group", + "load", + ); + this._returnValue( + callback, + "Load 5m", + loadArray[1], + "system", + "load", + ); + this._returnValue( + callback, + "Load 15m", + loadArray[2], + "system", + "load", + ); + this._returnValue( + callback, + "Threads Active", + proc[0], + "system", + "string", + ); + this._returnValue( + callback, + "Threads Total", + proc[1], + "system", + "string", + ); + }) + .catch((err) => {}); + + // check uptime + new FileModule.File("/proc/uptime") + .read(" ") + .then((upArray) => { + this._returnValue(callback, "Uptime", upArray[0], "system", "uptime"); + + let cores = Object.keys(this._last_processor["core"]).length - 1; + if (cores > 0) + this._returnValue( + callback, + "Process Time", + upArray[0] - upArray[1] / cores, + "processor", + "uptime", + ); + }) + .catch((err) => {}); } _queryNetwork(callback, dwell) { - // check network speed - let directions = ['tx', 'rx']; - let netbase = '/sys/class/net/'; - - new FileModule.File(netbase).list().then(interfaces => { - for (let iface of interfaces) { - for (let direction of directions) { - // lo tx and rx are the same - if (iface == 'lo' && direction == 'rx') continue; - - new FileModule.File(netbase + iface + '/statistics/' + direction + '_bytes').read().then(value => { - // issue #217 - don't include 'lo' traffic in Maximum calculations in values.js - // by not using network-rx or network-tx - let name = iface + ((iface == 'lo')?'':' ' + direction); - - let type = 'network' + ((iface=='lo')?'':'-' + direction); - this._returnValue(callback, name, value, type, 'storage'); - }).catch(err => { }); - } + // check network speed + let directions = ["tx", "rx"]; + let netbase = "/sys/class/net/"; + + new FileModule.File(netbase) + .list() + .then((interfaces) => { + for (let iface of interfaces) { + for (let direction of directions) { + // lo tx and rx are the same + if (iface == "lo" && direction == "rx") continue; + + new FileModule.File( + netbase + iface + "/statistics/" + direction + "_bytes", + ) + .read() + .then((value) => { + // issue #217 - don't include 'lo' traffic in Maximum calculations in values.js + // by not using network-rx or network-tx + let name = iface + (iface == "lo" ? "" : " " + direction); + + let type = "network" + (iface == "lo" ? "" : "-" + direction); + this._returnValue(callback, name, value, type, "storage"); + }) + .catch((err) => {}); } - }).catch(err => { }); - - // some may not want public ip checking - if (this._settings.get_boolean('include-public-ip')) { - // check the public ip every hour or when waking from sleep - if (this._next_public_ip_check <= 0) { - let intervalMinutes = this._settings.get_int('network-public-ip-interval'); - this._next_public_ip_check = intervalMinutes * 60; - - this._refreshIPAddress(callback); - } - - this._next_public_ip_check -= dwell; + } + }) + .catch((err) => {}); + + // some may not want public ip checking + if (this._settings.get_boolean("include-public-ip")) { + // check the public ip every hour or when waking from sleep + if (this._next_public_ip_check <= 0) { + let intervalMinutes = this._settings.get_int( + "network-public-ip-interval", + ); + this._next_public_ip_check = intervalMinutes * 60; + + this._refreshIPAddress(callback); } - // wireless interface statistics - new FileModule.File('/proc/net/wireless').read("\n", true).then(lines => { - // wireless has two headers - first is stripped in helper function - lines.shift(); - - // if multiple wireless device, we use the last one - for (let line of lines) { - let netArray = line.trim().split(/\s+/); - let quality_pct = netArray[2].substr(0, netArray[2].length-1) / 70; - let signal = netArray[3].substr(0, netArray[3].length-1); - - this._returnValue(callback, 'WiFi Link Quality', quality_pct, 'network', 'percent'); - this._returnValue(callback, 'WiFi Signal Level', signal, 'network', 'string'); - } - }).catch(err => { }); + this._next_public_ip_check -= dwell; + } + + // wireless interface statistics + new FileModule.File("/proc/net/wireless") + .read("\n", true) + .then((lines) => { + // wireless has two headers - first is stripped in helper function + lines.shift(); + + // if multiple wireless device, we use the last one + for (let line of lines) { + let netArray = line.trim().split(/\s+/); + let quality_pct = + netArray[2].substr(0, netArray[2].length - 1) / 70; + let signal = netArray[3].substr(0, netArray[3].length - 1); + + this._returnValue( + callback, + "WiFi Link Quality", + quality_pct, + "network", + "percent", + ); + this._returnValue( + callback, + "WiFi Signal Level", + signal, + "network", + "string", + ); + } + }) + .catch((err) => {}); } _queryStorage(callback, dwell) { - // display zfs arc status, if available - new FileModule.File('/proc/spl/kstat/zfs/arcstats').read().then(lines => { - let values = '', target = 0, maximum = 0, current = 0; - - if (values = lines.match(/c(\s+)(\d+)(\s+)(\d+)/)) target = values[4]; - if (values = lines.match(/c_max(\s+)(\d+)(\s+)(\d+)/)) maximum = values[4]; - if (values = lines.match(/size(\s+)(\d+)(\s+)(\d+)/)) current = values[4]; - - // ZFS statistics - this._returnValue(callback, 'ARC Target', target, 'storage', 'storage'); - this._returnValue(callback, 'ARC Maximum', maximum, 'storage', 'storage'); - this._returnValue(callback, 'ARC Current', current, 'storage', 'storage'); - }).catch(err => { }); - - // check disk performance stats - new FileModule.File('/proc/diskstats').read("\n").then(lines => { - for (let line of lines) { - let loadArray = line.trim().split(/\s+/); - if ('/dev/' + loadArray[2] == this._storageDevice) { - var read = (loadArray[5] * 512); - var write = (loadArray[9] * 512); - this._returnValue(callback, 'Read total', read, 'storage', 'storage'); - this._returnValue(callback, 'Write total', write, 'storage', 'storage'); - this._returnValue(callback, 'Read rate', (read - this._lastRead) / dwell, 'storage', 'storage'); - this._returnValue(callback, 'Write rate', (write - this._lastWrite) / dwell, 'storage', 'storage'); - this._lastRead = read; - this._lastWrite = write; - break; - } + // display zfs arc status, if available + new FileModule.File("/proc/spl/kstat/zfs/arcstats") + .read() + .then((lines) => { + let values = "", + target = 0, + maximum = 0, + current = 0; + + if ((values = lines.match(/c(\s+)(\d+)(\s+)(\d+)/))) + target = values[4]; + if ((values = lines.match(/c_max(\s+)(\d+)(\s+)(\d+)/))) + maximum = values[4]; + if ((values = lines.match(/size(\s+)(\d+)(\s+)(\d+)/))) + current = values[4]; + + // ZFS statistics + this._returnValue( + callback, + "ARC Target", + target, + "storage", + "storage", + ); + this._returnValue( + callback, + "ARC Maximum", + maximum, + "storage", + "storage", + ); + this._returnValue( + callback, + "ARC Current", + current, + "storage", + "storage", + ); + }) + .catch((err) => {}); + + // check disk performance stats + new FileModule.File("/proc/diskstats") + .read("\n") + .then((lines) => { + for (let line of lines) { + let loadArray = line.trim().split(/\s+/); + if ("/dev/" + loadArray[2] == this._storageDevice) { + var read = loadArray[5] * 512; + var write = loadArray[9] * 512; + this._returnValue( + callback, + "Read total", + read, + "storage", + "storage", + ); + this._returnValue( + callback, + "Write total", + write, + "storage", + "storage", + ); + this._returnValue( + callback, + "Read rate", + (read - this._lastRead) / dwell, + "storage", + "storage", + ); + this._returnValue( + callback, + "Write rate", + (write - this._lastWrite) / dwell, + "storage", + "storage", + ); + this._lastRead = read; + this._lastWrite = write; + break; } - }).catch(err => { }); - - // skip rest of stats if gtop not available - if (!hasGTop) return; - - GTop.glibtop_get_fsusage(this.storage, this._settings.get_string('storage-path')); - - let total = this.storage.blocks * this.storage.block_size; - let avail = this.storage.bavail * this.storage.block_size; - let free = this.storage.bfree * this.storage.block_size; - let used = total - free; - let reserved = (total - avail) - used; - let freePercent = 0; - let usedPercent = 0; - if (total > 0) { - freePercent = Math.round((free / total) * 100); - usedPercent = Math.round((used / total) * 100); - } - - this._returnValue(callback, 'Total', total, 'storage', 'storage'); - this._returnValue(callback, 'Used', used, 'storage', 'storage'); - this._returnValue(callback, 'Reserved', reserved, 'storage', 'storage'); - this._returnValue(callback, 'Free', avail, 'storage', 'storage'); - this._returnValue(callback, 'Used %', usedPercent + '%', 'storage', 'string'); - this._returnValue(callback, 'Free %', freePercent + '%', 'storage', 'string'); - this._returnValue(callback, 'storage', avail, 'storage-group', 'storage'); + } + }) + .catch((err) => {}); + + // skip rest of stats if gtop not available + if (!hasGTop) return; + + GTop.glibtop_get_fsusage( + this.storage, + this._settings.get_string("storage-path"), + ); + + let total = this.storage.blocks * this.storage.block_size; + let avail = this.storage.bavail * this.storage.block_size; + let free = this.storage.bfree * this.storage.block_size; + let used = total - free; + let reserved = total - avail - used; + let freePercent = 0; + let usedPercent = 0; + if (total > 0) { + freePercent = Math.round((free / total) * 100); + usedPercent = Math.round((used / total) * 100); + } + + this._returnValue(callback, "Total", total, "storage", "storage"); + this._returnValue(callback, "Used", used, "storage", "storage"); + this._returnValue(callback, "Reserved", reserved, "storage", "storage"); + this._returnValue(callback, "Free", avail, "storage", "storage"); + this._returnValue( + callback, + "Used %", + usedPercent + "%", + "storage", + "string", + ); + this._returnValue( + callback, + "Free %", + freePercent + "%", + "storage", + "string", + ); + this._returnValue(callback, "storage", avail, "storage-group", "storage"); } _queryBattery(callback) { - let battery_slot = this._settings.get_int('battery-slot'); - - // create a mapping of indices to battery paths (from prefs.ui) - const BATTERY_PATHS = { - 0: 'BAT0', - 1: 'BAT1', - 2: 'BAT2', - 3: 'BATT', - 4: 'CMB0', - 5: 'CMB1', - 6: 'CMB2', - 7: 'macsmc-battery' - }; - - // uevent has all necessary fields, no need to read individual files - let battery_path = '/sys/class/power_supply/' + BATTERY_PATHS[battery_slot] + '/uevent'; - new FileModule.File(battery_path).read("\n").then(lines => { - let output = {}; - for (let line of lines) { - let split = line.split('='); - output[split[0].replace('POWER_SUPPLY_', '')] = split[1]; - } - - if ('STATUS' in output) { - this._returnValue(callback, 'State', output['STATUS'], 'battery', ''); - } - - if ('CYCLE_COUNT' in output) { - this._returnValue(callback, 'Cycles', output['CYCLE_COUNT'], 'battery', ''); + let battery_slot = this._settings.get_int("battery-slot"); + + // create a mapping of indices to battery paths (from prefs.ui) + const BATTERY_PATHS = { + 0: "BAT0", + 1: "BAT1", + 2: "BAT2", + 3: "BATT", + 4: "CMB0", + 5: "CMB1", + 6: "CMB2", + 7: "macsmc-battery", + }; + + // uevent has all necessary fields, no need to read individual files + let battery_path = + "/sys/class/power_supply/" + BATTERY_PATHS[battery_slot] + "/uevent"; + new FileModule.File(battery_path) + .read("\n") + .then((lines) => { + let output = {}; + for (let line of lines) { + let split = line.split("="); + output[split[0].replace("POWER_SUPPLY_", "")] = split[1]; + } + + if ("STATUS" in output) { + this._returnValue( + callback, + "State", + output["STATUS"], + "battery", + "", + ); + } + + if ("CYCLE_COUNT" in output) { + this._returnValue( + callback, + "Cycles", + output["CYCLE_COUNT"], + "battery", + "", + ); + } + + if ("VOLTAGE_NOW" in output) { + this._returnValue( + callback, + "Voltage", + output["VOLTAGE_NOW"] / 1000, + "battery", + "in", + ); + } + + if ("CAPACITY_LEVEL" in output) { + this._returnValue( + callback, + "Level", + output["CAPACITY_LEVEL"], + "battery", + "", + ); + } + + if ("CAPACITY" in output) { + this._returnValue( + callback, + "Percentage", + output["CAPACITY"] / 100, + "battery", + "percent", + ); + } + + if ( + "VOLTAGE_NOW" in output && + "CURRENT_NOW" in output && + !("POWER_NOW" in output) + ) { + output["POWER_NOW"] = + (output["VOLTAGE_NOW"] * output["CURRENT_NOW"]) / 1000000; + } + + if ("POWER_NOW" in output) { + const powerValue = + parseFloat(output["POWER_NOW"]) * + (output["STATUS"] === "Discharging" ? -1 : 1); + this._returnValue( + callback, + "Power Rate", + powerValue, + "battery", + "watt", + ); + this._returnValue( + callback, + "battery", + powerValue, + "battery-group", + "watt", + ); + } + + if ( + "CHARGE_FULL" in output && + "VOLTAGE_MIN_DESIGN" in output && + !("ENERGY_FULL" in output) + ) { + output["ENERGY_FULL"] = + (output["CHARGE_FULL"] * output["VOLTAGE_MIN_DESIGN"]) / 1000000; + } + + if ("ENERGY_FULL" in output) { + this._returnValue( + callback, + "Energy (full)", + output["ENERGY_FULL"], + "battery", + "watt-hour", + ); + } + + if ( + "CHARGE_FULL_DESIGN" in output && + "VOLTAGE_MIN_DESIGN" in output && + !("ENERGY_FULL_DESIGN" in output) + ) { + output["ENERGY_FULL_DESIGN"] = + (output["CHARGE_FULL_DESIGN"] * output["VOLTAGE_MIN_DESIGN"]) / + 1000000; + } + + if ("ENERGY_FULL_DESIGN" in output) { + this._returnValue( + callback, + "Energy (design)", + output["ENERGY_FULL_DESIGN"], + "battery", + "watt-hour", + ); + + if ("ENERGY_FULL" in output) { + this._returnValue( + callback, + "Capacity", + output["ENERGY_FULL"] / output["ENERGY_FULL_DESIGN"], + "battery", + "percent", + ); } - - if ('VOLTAGE_NOW' in output) { - this._returnValue(callback, 'Voltage', output['VOLTAGE_NOW'] / 1000, 'battery', 'in'); - } - - if ('CAPACITY_LEVEL' in output) { - this._returnValue(callback, 'Level', output['CAPACITY_LEVEL'], 'battery', ''); - } - - if ('CAPACITY' in output) { - this._returnValue(callback, 'Percentage', output['CAPACITY'] / 100, 'battery', 'percent'); - } - - if ('VOLTAGE_NOW' in output && 'CURRENT_NOW' in output && (!('POWER_NOW' in output))) { - output['POWER_NOW'] = (output['VOLTAGE_NOW'] * output['CURRENT_NOW']) / 1000000; - } - - if ('POWER_NOW' in output) { - const powerValue = ( - parseFloat(output['POWER_NOW']) * (output['STATUS'] === 'Discharging' ? -1 : 1) - ); - this._returnValue(callback, 'Power Rate', powerValue, 'battery', 'watt'); - this._returnValue(callback, 'battery', powerValue, 'battery-group', 'watt'); - } - - if ('CHARGE_FULL' in output && 'VOLTAGE_MIN_DESIGN' in output && (!('ENERGY_FULL' in output))) { - output['ENERGY_FULL'] = (output['CHARGE_FULL'] * output['VOLTAGE_MIN_DESIGN']) / 1000000; - } - - if ('ENERGY_FULL' in output) { - this._returnValue(callback, 'Energy (full)', output['ENERGY_FULL'], 'battery', 'watt-hour'); - } - - if ('CHARGE_FULL_DESIGN' in output && 'VOLTAGE_MIN_DESIGN' in output && (!('ENERGY_FULL_DESIGN' in output))) { - output['ENERGY_FULL_DESIGN'] = (output['CHARGE_FULL_DESIGN'] * output['VOLTAGE_MIN_DESIGN']) / 1000000; - } - - if ('ENERGY_FULL_DESIGN' in output) { - this._returnValue(callback, 'Energy (design)', output['ENERGY_FULL_DESIGN'], 'battery', 'watt-hour'); - - if ('ENERGY_FULL' in output) { - this._returnValue(callback, 'Capacity', (output['ENERGY_FULL'] / output['ENERGY_FULL_DESIGN']), 'battery', 'percent'); - } - } - - if ('VOLTAGE_MIN_DESIGN' in output && 'CHARGE_NOW' in output && (!('ENERGY_NOW' in output))) { - output['ENERGY_NOW'] = (output['VOLTAGE_MIN_DESIGN'] * output['CHARGE_NOW']) / 1000000; + } + + if ( + "VOLTAGE_MIN_DESIGN" in output && + "CHARGE_NOW" in output && + !("ENERGY_NOW" in output) + ) { + output["ENERGY_NOW"] = + (output["VOLTAGE_MIN_DESIGN"] * output["CHARGE_NOW"]) / 1000000; + } + + if ("ENERGY_NOW" in output) { + this._returnValue( + callback, + "Energy (now)", + output["ENERGY_NOW"], + "battery", + "watt-hour", + ); + } + + if ( + "ENERGY_FULL" in output && + "ENERGY_NOW" in output && + "POWER_NOW" in output && + output["POWER_NOW"] !== 0 && + "STATUS" in output && + (output["STATUS"] == "Charging" || + output["STATUS"] == "Discharging") + ) { + let timeLeft = 0; + + // two different formulas depending on if we are charging or discharging + if (output["STATUS"] == "Charging") { + timeLeft = + (output["ENERGY_FULL"] - output["ENERGY_NOW"]) / + output["POWER_NOW"]; + } else { + timeLeft = output["ENERGY_NOW"] / Math.abs(output["POWER_NOW"]); } - if ('ENERGY_NOW' in output) { - this._returnValue(callback, 'Energy (now)', output['ENERGY_NOW'], 'battery', 'watt-hour'); + // don't process Infinity values + if (timeLeft !== Infinity) { + if (this._battery_charge_status != output["STATUS"]) { + // clears history due to state change + this._battery_time_left_history = []; + + // clear time left history when laptop goes in and out of charging + this._battery_charge_status = output["STATUS"]; + } + + // add latest time left estimate to our history + this._battery_time_left_history.push(parseInt(timeLeft * 3600)); + + // keep track of last 15 time left estimates by erasing the first + if (this._battery_time_left_history.length > 10) + this._battery_time_left_history.shift(); + + // sum up and create average of our time left history + let sum = this._battery_time_left_history.reduce((a, b) => a + b); + let avg = sum / this._battery_time_left_history.length; + + // use time left history to update screen + this._returnValue( + callback, + "Time left", + parseInt(avg), + "battery", + "runtime", + ); } - - if ('ENERGY_FULL' in output && 'ENERGY_NOW' in output && 'POWER_NOW' in output && output['POWER_NOW'] !== 0 && 'STATUS' in output && (output['STATUS'] == 'Charging' || output['STATUS'] == 'Discharging')) { - - let timeLeft = 0; - - // two different formulas depending on if we are charging or discharging - if (output['STATUS'] == 'Charging') { - timeLeft = ((output['ENERGY_FULL'] - output['ENERGY_NOW']) / output['POWER_NOW']); - } else { - timeLeft = (output['ENERGY_NOW'] / Math.abs(output['POWER_NOW'])); - } - - // don't process Infinity values - if (timeLeft !== Infinity) { - if (this._battery_charge_status != output['STATUS']) { - // clears history due to state change - this._battery_time_left_history = []; - - // clear time left history when laptop goes in and out of charging - this._battery_charge_status = output['STATUS']; - } - - // add latest time left estimate to our history - this._battery_time_left_history.push(parseInt(timeLeft * 3600)); - - // keep track of last 15 time left estimates by erasing the first - if (this._battery_time_left_history.length > 10) - this._battery_time_left_history.shift(); - - // sum up and create average of our time left history - let sum = this._battery_time_left_history.reduce((a, b) => a + b); - let avg = sum / this._battery_time_left_history.length; - - // use time left history to update screen - this._returnValue(callback, 'Time left', parseInt(avg), 'battery', 'runtime'); - } - } else { - this._returnValue(callback, 'Time left', output['STATUS'], 'battery', ''); - } - }).catch(err => { }); + } else { + this._returnValue( + callback, + "Time left", + output["STATUS"], + "battery", + "", + ); + } + }) + .catch((err) => {}); } _initFrameMonitor() { - if (this._frameMonitorSignalId) return; - this._frameMonitorLastTime = 0; - this._frameMonitorFrameCount = 0; - this._frameMonitorAccTime = 0; - this._frameMonitorCurrentHz = 0; - this._frameMonitorSignalId = global.stage.connect('after-paint', () => { - this._onAfterPaint(); - }); + if (this._frameMonitorSignalId) return; + this._frameMonitorLastTime = 0; + this._frameMonitorFrameCount = 0; + this._frameMonitorAccTime = 0; + this._frameMonitorCurrentHz = 0; + this._frameMonitorSignalId = global.stage.connect("after-paint", () => { + this._onAfterPaint(); + }); } _destroyFrameMonitor() { - if (this._frameMonitorSignalId) { - global.stage.disconnect(this._frameMonitorSignalId); - this._frameMonitorSignalId = 0; - } - this._frameMonitorLastTime = 0; - this._frameMonitorCurrentHz = 0; + if (this._frameMonitorSignalId) { + global.stage.disconnect(this._frameMonitorSignalId); + this._frameMonitorSignalId = 0; + } + this._frameMonitorLastTime = 0; + this._frameMonitorCurrentHz = 0; } _onAfterPaint() { - const now = GLib.get_monotonic_time(); + const now = GLib.get_monotonic_time(); - if (this._frameMonitorLastTime === 0) { - this._frameMonitorLastTime = now; - return; - } - - const delta = now - this._frameMonitorLastTime; + if (this._frameMonitorLastTime === 0) { this._frameMonitorLastTime = now; + return; + } - this._frameMonitorFrameCount++; - this._frameMonitorAccTime += delta; + const delta = now - this._frameMonitorLastTime; + this._frameMonitorLastTime = now; - if (this._frameMonitorAccTime >= 500000) { - this._frameMonitorCurrentHz = this._frameMonitorFrameCount / (this._frameMonitorAccTime / 1000000); - this._frameMonitorFrameCount = 0; - this._frameMonitorAccTime = 0; - } + this._frameMonitorFrameCount++; + this._frameMonitorAccTime += delta; + + if (this._frameMonitorAccTime >= 500000) { + this._frameMonitorCurrentHz = + this._frameMonitorFrameCount / (this._frameMonitorAccTime / 1000000); + this._frameMonitorFrameCount = 0; + this._frameMonitorAccTime = 0; + } } _queryGpu(callback) { - if (this._frameMonitorCurrentHz > 0) - this._returnValue(callback, 'Refresh Rate', this._frameMonitorCurrentHz, 'gpu#1', 'hertz'); - - if (!this._nvidia_smi_process) { - // no nvidia-smi, so we use sysfs DRM if any cards was discovered - if (!this._gpu_drm_indices){ - if (this._frameMonitorCurrentHz > 0) - this._returnValue(callback, 'Refresh Rate', this._frameMonitorCurrentHz, 'gpu#1-group', 'hertz'); - this._disableGpuLabels(callback); - return; - } else { - this._readGpuDrm(callback); - return; - } + if (this._frameMonitorCurrentHz > 0) + this._returnValue( + callback, + "Refresh Rate", + this._frameMonitorCurrentHz, + "gpu#1", + "hertz", + ); + + if (!this._nvidia_smi_process) { + // no nvidia-smi, so we use sysfs DRM if any cards was discovered + if (!this._gpu_drm_indices) { + if (this._frameMonitorCurrentHz > 0) + this._returnValue( + callback, + "Refresh Rate", + this._frameMonitorCurrentHz, + "gpu#1-group", + "hertz", + ); + this._disableGpuLabels(callback); + return; + } else { + this._readGpuDrm(callback); + return; } - - this._nvidia_smi_process.read('\n').then(lines => { - /// for debugging multi-gpu on systems with only one gpu - /// duplicates the first gpu's data 3 times, for 4 total gpus - ///if(lines.length == 0) return; - ///for(let _gpuNum = 1; _gpuNum <= 3; _gpuNum++) - /// lines.push(lines[0]); - - for (let i = 0; i < lines.length; i++) { - this._parseNvidiaSmiLine(callback, lines[i], i + 1, lines.length > 1); - } - - // if we've already updated the static info during the last parse, then stop doing so. - // this is so the _parseNvidiaSmiLine function won't return static info anymore - // and the nvidia-smi commmand won't be queried for static info either - if(!this._nvidia_static_returned) { - this._nvidia_static_returned = true; - //reconfigure the process to stop querying static info - this._reconfigureNvidiaSmiProcess(); - } - }).catch(err => { - this._disableGpuLabels(callback); - this._terminateNvidiaSmiProcess(); + } + + this._nvidia_smi_process + .read("\n") + .then((lines) => { + /// for debugging multi-gpu on systems with only one gpu + /// duplicates the first gpu's data 3 times, for 4 total gpus + ///if(lines.length == 0) return; + ///for(let _gpuNum = 1; _gpuNum <= 3; _gpuNum++) + /// lines.push(lines[0]); + + for (let i = 0; i < lines.length; i++) { + this._parseNvidiaSmiLine( + callback, + lines[i], + i + 1, + lines.length > 1, + ); + } + + // if we've already updated the static info during the last parse, then stop doing so. + // this is so the _parseNvidiaSmiLine function won't return static info anymore + // and the nvidia-smi commmand won't be queried for static info either + if (!this._nvidia_static_returned) { + this._nvidia_static_returned = true; + //reconfigure the process to stop querying static info + this._reconfigureNvidiaSmiProcess(); + } + }) + .catch((err) => { + this._disableGpuLabels(callback); + this._terminateNvidiaSmiProcess(); }); } _parseNvidiaSmiLine(callback, csv, gpuNum, multiGpu) { - const expectedSplitLength = 19; - let csv_split = csv.split(','); - - // occasionally the nvidia-smi command can get cut off before it can be fully read, thus the parse function only gets part of a line - // hence we count the number of bad splits and only terminate the process after a few bad splits in a row - // this prevents anomalous readings from terminating the process - if (csv_split.length < expectedSplitLength) { - this._bad_split_count++; - //if we've had 2 bad splits/reads in a row, try to restart the process - if (this._bad_split_count == 2) this._reconfigureNvidiaSmiProcess(); - //if we still get a bad read after that, then it's not an anomaly; terminate the process - else if (this._bad_split_count >= 3) this._terminateNvidiaSmiProcess(); - return; + const expectedSplitLength = 19; + let csv_split = csv.split(","); + + // occasionally the nvidia-smi command can get cut off before it can be fully read, thus the parse function only gets part of a line + // hence we count the number of bad splits and only terminate the process after a few bad splits in a row + // this prevents anomalous readings from terminating the process + if (csv_split.length < expectedSplitLength) { + this._bad_split_count++; + //if we've had 2 bad splits/reads in a row, try to restart the process + if (this._bad_split_count == 2) this._reconfigureNvidiaSmiProcess(); + //if we still get a bad read after that, then it's not an anomaly; terminate the process + else if (this._bad_split_count >= 3) this._terminateNvidiaSmiProcess(); + return; + } + this._bad_split_count = 0; + + let [ + label, + fan_speed_pct, + temp_gpu, + temp_mem, + mem_total, + mem_used, + mem_reserved, + mem_free, + util_gpu, + util_mem, + util_encoder, + util_decoder, + clock_gpu, + clock_mem, + clock_encode_decode, + power, + power_avg, + link_gen_current, + link_width_current, + ] = csv_split; + + const staticNames = [ + "temp_limit", + "power_limit", + "link_gen_max", + "link_width_max", + "addressing_mode", + "driver_version", + "vbios", + "serial", + "domain_num", + "bus_num", + "device_num", + "device_id", + "sub_device_id", + ]; + let staticInfo = {}; + + // if we have queried static info this time around, populate our static info object + if (csv_split.length == expectedSplitLength + staticNames.length) { + for (let i = 0; i < staticNames.length; i++) { + //set the static info to a default (0) if it's undefined + const value = csv_split[expectedSplitLength + i]; + staticInfo[staticNames[i]] = typeof value !== "undefined" ? value : 0; } - this._bad_split_count = 0; - - let [ - label, - fan_speed_pct, - temp_gpu, temp_mem, - mem_total, mem_used, mem_reserved, mem_free, - util_gpu, util_mem, util_encoder, util_decoder, - clock_gpu, clock_mem, clock_encode_decode, - power, power_avg, - link_gen_current, link_width_current - ] = csv_split; - - const staticNames = [ - 'temp_limit', 'power_limit', - 'link_gen_max', 'link_width_max', - 'addressing_mode', - 'driver_version', 'vbios', 'serial', - 'domain_num', 'bus_num', 'device_num', 'device_id', 'sub_device_id' - ]; - let staticInfo = {}; - - // if we have queried static info this time around, populate our static info object - if(csv_split.length == (expectedSplitLength + staticNames.length)){ - for(let i = 0; i < staticNames.length; i++) { - //set the static info to a default (0) if it's undefined - const value = csv_split[expectedSplitLength + i]; - staticInfo[staticNames[i]] = (typeof value !== "undefined") ? value : 0; - } - } - - const typeName = 'gpu#' + gpuNum; - const globalLabel = 'GPU' + (multiGpu ? ' ' + gpuNum : ''); - const memTempValid = !isNaN(parseInt(temp_mem)); - - this._returnGpuValue(callback, 'Graphics', parseInt(util_gpu) * 0.01, typeName + '-group', 'percent'); - - this._returnGpuValue(callback, 'Name', label, typeName, ''); - - this._returnGpuValue(callback, globalLabel, parseInt(fan_speed_pct) * 0.01, 'fan', 'percent'); - this._returnGpuValue(callback, 'Fan', parseInt(fan_speed_pct) * 0.01, typeName, 'percent'); - - this._returnGpuValue(callback, globalLabel, parseInt(temp_gpu) * 1000, 'temperature', 'temp'); - this._returnGpuValue(callback, 'Temperature', parseInt(temp_gpu) * 1000, typeName, 'temp'); - this._returnGpuValue(callback, 'Memory Temperature', parseInt(temp_mem) * 1000, typeName, 'temp', memTempValid); - this._returnStaticGpuValue(callback, 'Temperature Limit', parseInt(staticInfo['temp_limit']) * 1000, typeName, 'temp'); - - this._returnGpuValue(callback, 'Memory Usage', parseInt(mem_used) / parseInt(mem_total), typeName, 'percent'); - this._returnGpuValue(callback, 'Memory Total', parseInt(mem_total) * 1000, typeName, 'memory'); - this._returnGpuValue(callback, 'Memory Used', parseInt(mem_used) * 1000, typeName, 'memory'); - this._returnGpuValue(callback, 'Memory Reserved', parseInt(mem_reserved) * 1000, typeName, 'memory'); - this._returnGpuValue(callback, 'Memory Free', parseInt(mem_free) * 1000, typeName, 'memory'); - - this._returnGpuValue(callback, 'Memory Utilization', parseInt(util_mem) * 0.01, typeName, 'percent'); - this._returnGpuValue(callback, 'Utilization', parseInt(util_gpu) * 0.01, typeName, 'percent'); - this._returnGpuValue(callback, 'Encoder Utilization', parseInt(util_encoder) * 0.01, typeName, 'percent'); - this._returnGpuValue(callback, 'Decoder Utilization', parseInt(util_decoder) * 0.01, typeName, 'percent'); - - this._returnGpuValue(callback, 'Frequency', parseInt(clock_gpu) * 1000 * 1000, typeName, 'hertz'); - this._returnGpuValue(callback, 'Memory Frequency', parseInt(clock_mem) * 1000 * 1000, typeName, 'hertz'); - this._returnGpuValue(callback, 'Encoder/Decoder Frequency', parseInt(clock_encode_decode) * 1000 * 1000, typeName, 'hertz'); - - //this._returnGpuValue(callback, 'Encoder Sessions', parseInt(encoder_sessions), typeName, 'string'); - - this._returnGpuValue(callback, 'Power', power, typeName, 'watt-gpu'); - this._returnGpuValue(callback, 'Average Power', power_avg, typeName, 'watt-gpu'); - this._returnStaticGpuValue(callback, 'Power Limit', parseInt(staticInfo['power_limit']), typeName, 'watt-gpu'); - - this._returnGpuValue(callback, 'Link Speed', link_gen_current + 'x' + link_width_current, typeName, 'pcie'); - this._returnStaticGpuValue(callback, 'Maximum Link Speed', staticInfo['link_gen_max'] + 'x' + staticInfo['link_width_max'], typeName, 'pcie'); - - this._returnStaticGpuValue(callback, 'Addressing Mode', staticInfo['addressing_mode'], typeName, 'string'); - - this._returnStaticGpuValue(callback, 'Driver Version', staticInfo['driver_version'], typeName, 'string'); - this._returnStaticGpuValue(callback, 'vBIOS Version', staticInfo['vbios'], typeName, 'string'); - this._returnStaticGpuValue(callback, 'Serial Number', staticInfo['serial'], typeName, 'string'); - - this._returnStaticGpuValue(callback, 'Domain Number', staticInfo['domain_num'], typeName, 'string'); - this._returnStaticGpuValue(callback, 'Bus Number', staticInfo['bus_num'], typeName, 'string'); - this._returnStaticGpuValue(callback, 'Device Number', staticInfo['device_num'], typeName, 'string'); - this._returnStaticGpuValue(callback, 'Device ID', staticInfo['device_id'], typeName, 'string'); - this._returnStaticGpuValue(callback, 'Sub Device ID', staticInfo['sub_device_id'], typeName, 'string'); + } + + const typeName = "gpu#" + gpuNum; + const globalLabel = "GPU" + (multiGpu ? " " + gpuNum : ""); + const memTempValid = !isNaN(parseInt(temp_mem)); + + this._returnGpuValue( + callback, + "Graphics", + parseInt(util_gpu) * 0.01, + typeName + "-group", + "percent", + ); + + this._returnGpuValue(callback, "Name", label, typeName, ""); + + this._returnGpuValue( + callback, + globalLabel, + parseInt(fan_speed_pct) * 0.01, + "fan", + "percent", + ); + this._returnGpuValue( + callback, + "Fan", + parseInt(fan_speed_pct) * 0.01, + typeName, + "percent", + ); + + this._returnGpuValue( + callback, + globalLabel, + parseInt(temp_gpu) * 1000, + "temperature", + "temp", + ); + this._returnGpuValue( + callback, + "Temperature", + parseInt(temp_gpu) * 1000, + typeName, + "temp", + ); + this._returnGpuValue( + callback, + "Memory Temperature", + parseInt(temp_mem) * 1000, + typeName, + "temp", + memTempValid, + ); + this._returnStaticGpuValue( + callback, + "Temperature Limit", + parseInt(staticInfo["temp_limit"]) * 1000, + typeName, + "temp", + ); + + this._returnGpuValue( + callback, + "Memory Usage", + parseInt(mem_used) / parseInt(mem_total), + typeName, + "percent", + ); + this._returnGpuValue( + callback, + "Memory Total", + parseInt(mem_total) * 1000, + typeName, + "memory", + ); + this._returnGpuValue( + callback, + "Memory Used", + parseInt(mem_used) * 1000, + typeName, + "memory", + ); + this._returnGpuValue( + callback, + "Memory Reserved", + parseInt(mem_reserved) * 1000, + typeName, + "memory", + ); + this._returnGpuValue( + callback, + "Memory Free", + parseInt(mem_free) * 1000, + typeName, + "memory", + ); + + this._returnGpuValue( + callback, + "Memory Utilization", + parseInt(util_mem) * 0.01, + typeName, + "percent", + ); + this._returnGpuValue( + callback, + "Utilization", + parseInt(util_gpu) * 0.01, + typeName, + "percent", + ); + this._returnGpuValue( + callback, + "Encoder Utilization", + parseInt(util_encoder) * 0.01, + typeName, + "percent", + ); + this._returnGpuValue( + callback, + "Decoder Utilization", + parseInt(util_decoder) * 0.01, + typeName, + "percent", + ); + + this._returnGpuValue( + callback, + "Frequency", + parseInt(clock_gpu) * 1000 * 1000, + typeName, + "hertz", + ); + this._returnGpuValue( + callback, + "Memory Frequency", + parseInt(clock_mem) * 1000 * 1000, + typeName, + "hertz", + ); + this._returnGpuValue( + callback, + "Encoder/Decoder Frequency", + parseInt(clock_encode_decode) * 1000 * 1000, + typeName, + "hertz", + ); + + //this._returnGpuValue(callback, 'Encoder Sessions', parseInt(encoder_sessions), typeName, 'string'); + + this._returnGpuValue(callback, "Power", power, typeName, "watt-gpu"); + this._returnGpuValue( + callback, + "Average Power", + power_avg, + typeName, + "watt-gpu", + ); + this._returnStaticGpuValue( + callback, + "Power Limit", + parseInt(staticInfo["power_limit"]), + typeName, + "watt-gpu", + ); + + this._returnGpuValue( + callback, + "Link Speed", + link_gen_current + "x" + link_width_current, + typeName, + "pcie", + ); + this._returnStaticGpuValue( + callback, + "Maximum Link Speed", + staticInfo["link_gen_max"] + "x" + staticInfo["link_width_max"], + typeName, + "pcie", + ); + + this._returnStaticGpuValue( + callback, + "Addressing Mode", + staticInfo["addressing_mode"], + typeName, + "string", + ); + + this._returnStaticGpuValue( + callback, + "Driver Version", + staticInfo["driver_version"], + typeName, + "string", + ); + this._returnStaticGpuValue( + callback, + "vBIOS Version", + staticInfo["vbios"], + typeName, + "string", + ); + this._returnStaticGpuValue( + callback, + "Serial Number", + staticInfo["serial"], + typeName, + "string", + ); + + this._returnStaticGpuValue( + callback, + "Domain Number", + staticInfo["domain_num"], + typeName, + "string", + ); + this._returnStaticGpuValue( + callback, + "Bus Number", + staticInfo["bus_num"], + typeName, + "string", + ); + this._returnStaticGpuValue( + callback, + "Device Number", + staticInfo["device_num"], + typeName, + "string", + ); + this._returnStaticGpuValue( + callback, + "Device ID", + staticInfo["device_id"], + typeName, + "string", + ); + this._returnStaticGpuValue( + callback, + "Sub Device ID", + staticInfo["sub_device_id"], + typeName, + "string", + ); } - _readGpuDrm(callback){ - const multiGpu = this._gpu_drm_indices.length > 1; - const unit = this._settings.get_int('memory-measurement') ? 1000 : 1024; - for (let z = 0; z < this._gpu_drm_indices.length; z++ ) { - let i = this._gpu_drm_indices[z]; - const typeName = 'gpu#' + i; - const vendor = this._gpu_drm_vendors[z]; - - // AMD - if(vendor === "0x1002") { - // read GPU usage and create group lebel for card - new FileModule.File('/sys/class/drm/card'+i+'/device/gpu_busy_percent').read().then(value => { - // create group - this._returnGpuValue(callback, 'Graphics', parseInt(value) * 0.01, typeName + '-group', 'percent'); - this._returnGpuValue(callback, 'Vendor', "AMD", typeName, 'string'); - this._returnGpuValue(callback, 'Usage', parseInt(value) * 0.01, typeName, 'percent'); - }).catch(err => { - // nothing to do, keep old value displayed - }); - new FileModule.File('/sys/class/drm/card'+i+'/device/mem_info_vram_used').read().then(value => { - this._returnGpuValue(callback, 'Memory Used', parseInt(value) / unit, typeName, 'memory'); - }).catch(err => { - // nothing to do, keep old value displayed - }); - new FileModule.File('/sys/class/drm/card'+i+'/device/mem_info_vram_total').read().then(value => { - this._returnGpuValue(callback, 'Memory Total', parseInt(value) / unit, typeName, 'memory'); - }).catch(err => { - // nothing to do, keep old value displayed - }); - } else { - // for other vendors only show basic card info - let vendorName = null; - switch (vendor){ - case '0x10DE': vendorName = 'NVIDIA'; break; // should be never used as nvidia-smi should be preferred - case '0x13B5': vendorName = 'ARM'; break; - case '0x5143': vendorName = 'Qualcomm'; break; - case '0x8086': vendorName = 'Intel'; break; - default: vendorName = "Unknown " + vendor; - } - this._returnGpuValue(callback, 'Graphics', vendorName, typeName + '-group', 'string'); - } + _readGpuDrm(callback) { + const multiGpu = this._gpu_drm_indices.length > 1; + const unit = this._settings.get_int("memory-measurement") ? 1000 : 1024; + for (let z = 0; z < this._gpu_drm_indices.length; z++) { + let i = this._gpu_drm_indices[z]; + const typeName = "gpu#" + i; + const vendor = this._gpu_drm_vendors[z]; + + // AMD + if (vendor === "0x1002") { + // read GPU usage and create group lebel for card + new FileModule.File( + "/sys/class/drm/card" + i + "/device/gpu_busy_percent", + ) + .read() + .then((value) => { + // create group + this._returnGpuValue( + callback, + "Graphics", + parseInt(value) * 0.01, + typeName + "-group", + "percent", + ); + this._returnGpuValue( + callback, + "Vendor", + "AMD", + typeName, + "string", + ); + this._returnGpuValue( + callback, + "Usage", + parseInt(value) * 0.01, + typeName, + "percent", + ); + }) + .catch((err) => { + // nothing to do, keep old value displayed + }); + new FileModule.File( + "/sys/class/drm/card" + i + "/device/mem_info_vram_used", + ) + .read() + .then((value) => { + this._returnGpuValue( + callback, + "Memory Used", + parseInt(value) / unit, + typeName, + "memory", + ); + }) + .catch((err) => { + // nothing to do, keep old value displayed + }); + new FileModule.File( + "/sys/class/drm/card" + i + "/device/mem_info_vram_total", + ) + .read() + .then((value) => { + this._returnGpuValue( + callback, + "Memory Total", + parseInt(value) / unit, + typeName, + "memory", + ); + }) + .catch((err) => { + // nothing to do, keep old value displayed + }); + } else { + // for other vendors only show basic card info + let vendorName = null; + switch (vendor) { + case "0x10DE": + vendorName = "NVIDIA"; + break; // should be never used as nvidia-smi should be preferred + case "0x13B5": + vendorName = "ARM"; + break; + case "0x5143": + vendorName = "Qualcomm"; + break; + case "0x8086": + vendorName = "Intel"; + break; + default: + vendorName = "Unknown " + vendor; + } + this._returnGpuValue( + callback, + "Graphics", + vendorName, + typeName + "-group", + "string", + ); } + } } _disableGpuLabels(callback) { - for (let labelObj of this._nvidia_labels) - this._returnValue(callback, labelObj.label, 'disabled', labelObj.type, labelObj.format); + for (let labelObj of this._nvidia_labels) + this._returnValue( + callback, + labelObj.label, + "disabled", + labelObj.type, + labelObj.format, + ); } _returnStaticGpuValue(callback, label, value, type, format) { - //if we've already tried to return existing static info before or if the option isn't enabled, then do nothing. - if (this._nvidia_static_returned || !this._settings.get_boolean('include-static-gpu-info')) - return; - - //we don't need to disable static info labels, so just use ordinary returnValue function - this._returnValue(callback, label, value, type, format); + //if we've already tried to return existing static info before or if the option isn't enabled, then do nothing. + if ( + this._nvidia_static_returned || + !this._settings.get_boolean("include-static-gpu-info") + ) + return; + + //we don't need to disable static info labels, so just use ordinary returnValue function + this._returnValue(callback, label, value, type, format); } _returnGpuValue(callback, label, value, type, format, display = true) { - if(!display) return; + if (!display) return; - if(format !== "string" && (value === 'N/A' || value === '[N/A]' || isNaN(value))) return; + if ( + format !== "string" && + (value === "N/A" || value === "[N/A]" || isNaN(value)) + ) + return; - let nvidiaLabel = {'label': label, 'type': type, 'format': format}; - if (!this._nvidia_labels.includes(nvidiaLabel)) - this._nvidia_labels.push(nvidiaLabel); + let nvidiaLabel = { label: label, type: type, format: format }; + if (!this._nvidia_labels.includes(nvidiaLabel)) + this._nvidia_labels.push(nvidiaLabel); - this._returnValue(callback, label, value, type, format); + this._returnValue(callback, label, value, type, format); } _returnValue(callback, label, value, type, format) { - // don't return if value is not a number - will revisit later - //if (isNaN(value)) return; - callback(label, value, type, format); + // don't return if value is not a number - will revisit later + //if (isNaN(value)) return; + callback(label, value, type, format); } _discoverHardwareMonitors(callback) { - this._tempVoltFanSensors = { 'temperature': {}, 'voltage': {}, 'fan': {} }; - - let hwbase = '/sys/class/hwmon/'; - - // process sensor_types now so it is not called multiple times below - let sensor_types = {}; - - if (this._settings.get_boolean('show-temperature')) - sensor_types['temp'] = 'temperature'; - - if (this._settings.get_boolean('show-voltage')) - sensor_types['in'] = 'voltage'; - - if (this._settings.get_boolean('show-fan')) - sensor_types['fan'] = 'fan'; - - // a little informal, but this code has zero I/O block - new FileModule.File(hwbase).list().then(files => { - for (let file of files) { - // grab name of sensor - new FileModule.File(hwbase + file + '/name').read().then(name => { - // are we dealing with a CPU? - if (name == 'coretemp') { - // determine which processor (socket) we are dealing with - new FileModule.File(hwbase + file + '/temp1_label').read().then(prefix => { - this._processTempVoltFan(callback, sensor_types, prefix, hwbase + file, file); - }).catch(err => { - // this shouldn't be necessary, but just in case temp1_label doesn't exist - // attempt to fix #266 - this._processTempVoltFan(callback, sensor_types, name, hwbase + file, file); - }); - } else { - // not a CPU, process all other sensors - this._processTempVoltFan(callback, sensor_types, name, hwbase + file, file); - } - }).catch(err => { - new FileModule.File(hwbase + file + '/device/name').read().then(name => { - this._processTempVoltFan(callback, sensor_types, name, hwbase + file + '/device', file); - }).catch(err => { }); - }); - } - }).catch(err => { }); - - // does this system support cpu scaling? if so we will use it to grab Frequency and Boost below - new FileModule.File('/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq').read().then(value => { - this._processor_uses_cpu_info = false; - }).catch(err => { }); - - // is static CPU information enabled? - if (this._settings.get_boolean('include-static-info')) { - // grab static CPU information - new FileModule.File('/proc/cpuinfo').read("\n").then(lines => { - let vendor_id = ''; - let bogomips = ''; - let sockets = {}; - let cache = ''; - - for (let line of lines) { - let value = ''; + this._tempVoltFanSensors = { temperature: {}, voltage: {}, fan: {} }; + + let hwbase = "/sys/class/hwmon/"; + + // process sensor_types now so it is not called multiple times below + let sensor_types = {}; + + if (this._settings.get_boolean("show-temperature")) + sensor_types["temp"] = "temperature"; + + if (this._settings.get_boolean("show-voltage")) + sensor_types["in"] = "voltage"; + + if (this._settings.get_boolean("show-fan")) sensor_types["fan"] = "fan"; + + // a little informal, but this code has zero I/O block + new FileModule.File(hwbase) + .list() + .then((files) => { + for (let file of files) { + // grab name of sensor + new FileModule.File(hwbase + file + "/name") + .read() + .then((name) => { + // are we dealing with a CPU? + if (name == "coretemp") { + // determine which processor (socket) we are dealing with + new FileModule.File(hwbase + file + "/temp1_label") + .read() + .then((prefix) => { + this._processTempVoltFan( + callback, + sensor_types, + prefix, + hwbase + file, + file, + ); + }) + .catch((err) => { + // this shouldn't be necessary, but just in case temp1_label doesn't exist + // attempt to fix #266 + this._processTempVoltFan( + callback, + sensor_types, + name, + hwbase + file, + file, + ); + }); + } else { + // not a CPU, process all other sensors + this._processTempVoltFan( + callback, + sensor_types, + name, + hwbase + file, + file, + ); + } + }) + .catch((err) => { + new FileModule.File(hwbase + file + "/device/name") + .read() + .then((name) => { + this._processTempVoltFan( + callback, + sensor_types, + name, + hwbase + file + "/device", + file, + ); + }) + .catch((err) => {}); + }); + } + }) + .catch((err) => {}); + + // does this system support cpu scaling? if so we will use it to grab Frequency and Boost below + new FileModule.File( + "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq", + ) + .read() + .then((value) => { + this._processor_uses_cpu_info = false; + }) + .catch((err) => {}); + + // is static CPU information enabled? + if (this._settings.get_boolean("include-static-info")) { + // grab static CPU information + new FileModule.File("/proc/cpuinfo") + .read("\n") + .then((lines) => { + let vendor_id = ""; + let bogomips = ""; + let sockets = {}; + let cache = ""; - // grab cpu vendor - if (value = line.match(/^vendor_id(\s+): (\w+.*)/)) vendor_id = value[2]; + for (let line of lines) { + let value = ""; - // grab bogomips - if (value = line.match(/^bogomips(\s+): (\d*\.?\d*)$/)) bogomips = value[2]; + // grab cpu vendor + if ((value = line.match(/^vendor_id(\s+): (\w+.*)/))) + vendor_id = value[2]; - // grab processor count - if (value = line.match(/^physical id(\s+): (\d+)$/)) sockets[value[2]] = 1; + // grab bogomips + if ((value = line.match(/^bogomips(\s+): (\d*\.?\d*)$/))) + bogomips = value[2]; - // grab cache - if (value = line.match(/^cache size(\s+): (\d+) KB$/)) cache = value[2]; - } + // grab processor count + if ((value = line.match(/^physical id(\s+): (\d+)$/))) + sockets[value[2]] = 1; - this._returnValue(callback, 'Vendor', vendor_id, 'processor', 'string'); - this._returnValue(callback, 'Bogomips', bogomips, 'processor', 'string'); - this._returnValue(callback, 'Sockets', Object.keys(sockets).length, 'processor', 'string'); - this._returnValue(callback, 'Cache', cache, 'processor', 'memory'); - }).catch(err => { }); + // grab cache + if ((value = line.match(/^cache size(\s+): (\d+) KB$/))) + cache = value[2]; + } - // grab static CPU information - new FileModule.File('/proc/version').read(' ').then(kernelArray => { - this._returnValue(callback, 'Kernel', kernelArray[2], 'system', 'string'); - }).catch(err => { }); + this._returnValue( + callback, + "Vendor", + vendor_id, + "processor", + "string", + ); + this._returnValue( + callback, + "Bogomips", + bogomips, + "processor", + "string", + ); + this._returnValue( + callback, + "Sockets", + Object.keys(sockets).length, + "processor", + "string", + ); + this._returnValue(callback, "Cache", cache, "processor", "memory"); + }) + .catch((err) => {}); + + // grab static CPU information + new FileModule.File("/proc/version") + .read(" ") + .then((kernelArray) => { + this._returnValue( + callback, + "Kernel", + kernelArray[2], + "system", + "string", + ); + }) + .catch((err) => {}); + } + + // Launch nvidia-smi subprocess if nvidia querying is enabled + this._reconfigureNvidiaSmiProcess(); + this._discoverGpuDrm(); + this._initFrameMonitor(); + + // Aquacomputer Next Vision + const VENDOR_ID = "0c70"; + const PRODUCT_ID = "f00c"; + + let base = "/sys/class/hidraw/"; + let dir = Gio.File.new_for_path(base); + try { + let enumerator = dir.enumerate_children( + "standard::*", + Gio.FileQueryInfoFlags.NONE, + null, + ); + let info; + while ((info = enumerator.next_file(null)) !== null) { + let name = info.get_name(); // e.g. hidraw7 + let deviceSymlink = `${base}${name}/device`; + let realDevicePath = GLib.file_read_link(deviceSymlink); + // realDevicePath will be something like: + // ../../devices/.../0003:0C70:F00C.0008 + let parts = realDevicePath.split("/"); + let devDir = parts[parts.length - 1]; + // devDir is like "0003:0C70:F00C.0008" + let match = devDir.match( + /^[0-9A-Fa-f]+:([0-9A-Fa-f]{4}):([0-9A-Fa-f]{4})\./, + ); + if (match) { + let vid = match[1].toLowerCase(); + let pid = match[2].toLowerCase(); + if (vid === VENDOR_ID && pid === PRODUCT_ID) { + // Check if AC Vision sensor already exists to prevent re-adding + if ( + !("Coolant Temp" in this._tempVoltFanSensors["temperature"]) + ) { + this._addTempVoltFan( + null, // Don't call callback during discovery + { + type: "temperature", + format: "temp", + input: `/dev/${name}`, + aquacomputer: true, + }, + "AC Vision", + "Coolant Temp", + "", + 0, // placeholder value, will be read during query + ); + } + break; + } + } else { + } } - - // Launch nvidia-smi subprocess if nvidia querying is enabled - this._reconfigureNvidiaSmiProcess(); - this._discoverGpuDrm(); - this._initFrameMonitor(); + enumerator.close(null); + } catch (e) { + // If the hidraw device is not available, we just ignore it. + // This can happen if the device is not connected or if the user does not have permission to access it. + } } _discoverGpuDrm() { - // use DRM only if nvidia-smi is not used - if (this._settings.get_boolean('show-gpu') && this._nvidia_smi_process == null) { - // try to discover up to 10 cards starting from index 0 - for(let i = 0; i < 10 ; i++){ - new FileModule.File('/sys/class/drm/card'+i+'/device/vendor').read().then(value => { - if(!this._gpu_drm_indices){ - this._gpu_drm_indices = []; - this._gpu_drm_vendors = []; - } - this._gpu_drm_indices.push(i); - this._gpu_drm_vendors.push(value); - }).catch(err => { }); - } - } else { - this._gpu_drm_vendors = null; - this._gpu_drm_indices = null; + // use DRM only if nvidia-smi is not used + if ( + this._settings.get_boolean("show-gpu") && + this._nvidia_smi_process == null + ) { + // try to discover up to 10 cards starting from index 0 + for (let i = 0; i < 10; i++) { + new FileModule.File("/sys/class/drm/card" + i + "/device/vendor") + .read() + .then((value) => { + if (!this._gpu_drm_indices) { + this._gpu_drm_indices = []; + this._gpu_drm_vendors = []; + } + this._gpu_drm_indices.push(i); + this._gpu_drm_vendors.push(value); + }) + .catch((err) => {}); } + } else { + this._gpu_drm_vendors = null; + this._gpu_drm_indices = null; + } } // The nvidia-smi subprocess will keep running and print new sensor data to stdout every @@ -914,165 +1857,217 @@ export const Sensors = GObject.registerClass({ // wait up to `update_time` seconds before getting any results and reporting them through the // callback. _reconfigureNvidiaSmiProcess() { - if (this._settings.get_boolean('show-gpu')) { - this._terminateNvidiaSmiProcess(); - - try { - let update_time = this._settings.get_int('update-time'); - let query_interval = Math.max(update_time, 1); - let command = [ - 'nvidia-smi', - '--query-gpu=name,' + - 'fan.speed,' + - 'temperature.gpu,temperature.memory,' + - 'memory.total,memory.used,memory.reserved,memory.free,' + - 'utilization.gpu,utilization.memory,utilization.encoder,utilization.decoder,' + - 'clocks.gr,clocks.mem,clocks.video,' + - 'power.draw.instant,power.draw.average,' + - 'pcie.link.gen.gpucurrent,pcie.link.width.current,' + - (!this._nvidia_static_returned && this._settings.get_boolean('include-static-gpu-info') ? - 'temperature.gpu.tlimit,' + - 'power.limit,' + - 'pcie.link.gen.max,pcie.link.width.max,' + - 'addressing_mode,'+ - 'driver_version,vbios_version,serial,' + - 'pci.domain,pci.bus,pci.device,pci.device_id,pci.sub_device_id,' - : ''), - '--format=csv,noheader,nounits', - '-l', query_interval.toString() - ]; - - this._nvidia_smi_process = new SubProcessModule.SubProcess(command); - } catch(e) { - // proprietary nvidia driver not installed - this._terminateNvidiaSmiProcess(); - } - } else { - this._terminateNvidiaSmiProcess(); + if (this._settings.get_boolean("show-gpu")) { + this._terminateNvidiaSmiProcess(); + + try { + let update_time = this._settings.get_int("update-time"); + let query_interval = Math.max(update_time, 1); + let command = [ + "nvidia-smi", + "--query-gpu=name," + + "fan.speed," + + "temperature.gpu,temperature.memory," + + "memory.total,memory.used,memory.reserved,memory.free," + + "utilization.gpu,utilization.memory,utilization.encoder,utilization.decoder," + + "clocks.gr,clocks.mem,clocks.video," + + "power.draw.instant,power.draw.average," + + "pcie.link.gen.gpucurrent,pcie.link.width.current," + + (!this._nvidia_static_returned && + this._settings.get_boolean("include-static-gpu-info") + ? "temperature.gpu.tlimit," + + "power.limit," + + "pcie.link.gen.max,pcie.link.width.max," + + "addressing_mode," + + "driver_version,vbios_version,serial," + + "pci.domain,pci.bus,pci.device,pci.device_id,pci.sub_device_id," + : ""), + "--format=csv,noheader,nounits", + "-l", + query_interval.toString(), + ]; + + this._nvidia_smi_process = new SubProcessModule.SubProcess(command); + } catch (e) { + // proprietary nvidia driver not installed + this._terminateNvidiaSmiProcess(); } + } else { + this._terminateNvidiaSmiProcess(); + } } _terminateNvidiaSmiProcess() { - if (this._nvidia_smi_process) { - this._nvidia_smi_process.terminate(); - this._nvidia_smi_process = null; - } + if (this._nvidia_smi_process) { + this._nvidia_smi_process.terminate(); + this._nvidia_smi_process = null; + } } _processTempVoltFan(callback, sensor_types, name, path, file) { - let sensor_files = [ 'input', 'label' ]; - - // grab files from directory - new FileModule.File(path).list().then(files2 => { - let trisensors = {}; - - // loop over files from directory - for (let file2 of Object.values(files2)) { - // simple way of processing input and label (from above) - for (let key of Object.values(sensor_files)) { - // process toggled on sensors from extension preferences - for (let sensor_type in sensor_types) { - if (file2.substr(0, sensor_type.length) == sensor_type && file2.substr(-(key.length+1)) == '_' + key) { - let key2 = file + file2.substr(0, file2.indexOf('_')); - - if (!(key2 in trisensors)) { - trisensors[key2] = { - 'type': sensor_types[sensor_type], - 'format': sensor_type, - 'label': path + '/name' - }; - } - - trisensors[key2][key] = path + '/' + file2; - } - } + let sensor_files = ["input", "label"]; + + // grab files from directory + new FileModule.File(path) + .list() + .then((files2) => { + let trisensors = {}; + + // loop over files from directory + for (let file2 of Object.values(files2)) { + // simple way of processing input and label (from above) + for (let key of Object.values(sensor_files)) { + // process toggled on sensors from extension preferences + for (let sensor_type in sensor_types) { + if ( + file2.substr(0, sensor_type.length) == sensor_type && + file2.substr(-(key.length + 1)) == "_" + key + ) { + let key2 = file + file2.substr(0, file2.indexOf("_")); + + if (!(key2 in trisensors)) { + trisensors[key2] = { + type: sensor_types[sensor_type], + format: sensor_type, + label: path + "/name", + }; + } + + trisensors[key2][key] = path + "/" + file2; } + } } - - for (let obj of Object.values(trisensors)) { - if (!('input' in obj)) - continue; - - new FileModule.File(obj['input']).read().then(value => { - let extra = (obj['label'].indexOf('_label')==-1) ? ' ' + obj['input'].substr(obj['input'].lastIndexOf('/')+1).split('_')[0] : ''; - - if (value > 0 || !this._settings.get_boolean('hide-zeros') || obj['type'] == 'fan') { - new FileModule.File(obj['label']).read().then(label => { - this._addTempVoltFan(callback, obj, name, label, extra, value); - }).catch(err => { - let tmpFile = obj['label'].substr(0, obj['label'].lastIndexOf('/')) + '/name'; - new FileModule.File(tmpFile).read().then(label => { - this._addTempVoltFan(callback, obj, name, label, extra, value); - }).catch(err => { }); - }); - } - }).catch(err => { }); - } - }).catch(err => { }); + } + + for (let obj of Object.values(trisensors)) { + if (!("input" in obj)) continue; + + new FileModule.File(obj["input"]) + .read() + .then((value) => { + let extra = + obj["label"].indexOf("_label") == -1 + ? " " + + obj["input"] + .substr(obj["input"].lastIndexOf("/") + 1) + .split("_")[0] + : ""; + + if ( + value > 0 || + !this._settings.get_boolean("hide-zeros") || + obj["type"] == "fan" + ) { + new FileModule.File(obj["label"]) + .read() + .then((label) => { + this._addTempVoltFan( + callback, + obj, + name, + label, + extra, + value, + ); + }) + .catch((err) => { + let tmpFile = + obj["label"].substr(0, obj["label"].lastIndexOf("/")) + + "/name"; + new FileModule.File(tmpFile) + .read() + .then((label) => { + this._addTempVoltFan( + callback, + obj, + name, + label, + extra, + value, + ); + }) + .catch((err) => {}); + }); + } + }) + .catch((err) => {}); + } + }) + .catch((err) => {}); } _addTempVoltFan(callback, obj, name, label, extra, value) { - // prepend module that provided sensor data - if (name != label) label = name + ' ' + label; - - //if (label == 'nvme Composite') label = 'NVMe'; - //if (label == 'nouveau') label = 'Nvidia'; - - label = label + extra; - - // in the future we will read /etc/sensors3.conf - if (label == 'acpitz temp1') label = 'ACPI Thermal Zone'; - if (label == 'pch_cannonlake temp1') label = 'Platform Controller Hub'; - if (label == 'iwlwifi_1 temp1') label = 'Wireless Adapter'; - if (label == 'Package id 0') label = 'Processor 0'; - if (label == 'Package id 1') label = 'Processor 1'; - label = label.replace('Package id', 'CPU'); - - let types = [ 'temperature', 'voltage', 'fan' ]; - for (let type of types) { - // check if this label already exists - if (label in this._tempVoltFanSensors[type]) { - for (let i = 2; i <= 9; i++) { - // append an incremented number to end - let new_label = label + ' ' + i; - - // if new label is available, use it - if (!(new_label in this._tempVoltFanSensors[type])) { - label = new_label; - break; - } - } + // prepend module that provided sensor data + if (name != label) label = name + " " + label; + + //if (label == 'nvme Composite') label = 'NVMe'; + //if (label == 'nouveau') label = 'Nvidia'; + + label = label + extra; + + // in the future we will read /etc/sensors3.conf + if (label == "acpitz temp1") label = "ACPI Thermal Zone"; + if (label == "pch_cannonlake temp1") label = "Platform Controller Hub"; + if (label == "iwlwifi_1 temp1") label = "Wireless Adapter"; + if (label == "Package id 0") label = "Processor 0"; + if (label == "Package id 1") label = "Processor 1"; + if (label == "nct6799 fan1") label = "VRM HeatSink Fan"; + if (label == "nct6799 fan2") label = "Radiator Fan(s)"; + if (label == "nct6799 fan6") label = "Chipset/NVMe Fan"; + if (label == "nct6799 fan7") label = "AIO Pump"; + if (label == "nct6799 SYSTIN") label = "Motherboard Temp"; + if (label == "nct6799 CPUTIN") label = "CPU Socket Temp"; + label = label.replace("Package id", "CPU"); + + let types = ["temperature", "voltage", "fan"]; + for (let type of types) { + // check if this label already exists + if (label in this._tempVoltFanSensors[type]) { + for (let i = 2; i <= 9; i++) { + // append an incremented number to end + let new_label = label + " " + i; + + // if new label is available, use it + if (!(new_label in this._tempVoltFanSensors[type])) { + label = new_label; + break; } + } } + } + if (!obj["aquacomputer"] && callback) { // update screen on initial build to prevent delay on update - this._returnValue(callback, label, value, obj['type'], obj['format']); - - this._tempVoltFanSensors[obj['type']][label] = { - 'format': obj['format'], - 'path': obj['input'] - }; + this._returnValue(callback, label, value, obj["type"], obj["format"]); + } + + this._tempVoltFanSensors[obj["type"]][label] = { + format: obj["format"], + path: obj["input"], + aquacomputer: obj["aquacomputer"] || false, + }; } resetHistory() { - this._next_public_ip_check = 0; - this._hardware_detected = false; - this._nvidia_static_returned = false; - this._processor_uses_cpu_info = true; - this._battery_time_left_history = []; - this._battery_charge_status = ''; - this._nvidia_labels = []; - this._bad_split_count = 0; - this._frameMonitorLastTime = 0; - this._frameMonitorFrameCount = 0; - this._frameMonitorAccTime = 0; + this._next_public_ip_check = 0; + this._hardware_detected = false; + this._nvidia_static_returned = false; + this._processor_uses_cpu_info = true; + this._battery_time_left_history = []; + this._battery_charge_status = ""; + this._nvidia_labels = []; + this._bad_split_count = 0; + this._frameMonitorLastTime = 0; + this._frameMonitorFrameCount = 0; + this._frameMonitorAccTime = 0; } destroy() { - this._destroyFrameMonitor(); - this._terminateNvidiaSmiProcess(); + this._destroyFrameMonitor(); + this._terminateNvidiaSmiProcess(); - for (let signal of Object.values(this._settingChangedSignals)) - this._settings.disconnect(signal); + for (let signal of Object.values(this._settingChangedSignals)) + this._settings.disconnect(signal); } -}); + }, +);