From 3771b2404c855102f8c2a7a4a66f66c37f3918ae Mon Sep 17 00:00:00 2001 From: ewired Date: Wed, 15 Apr 2026 16:56:23 -0400 Subject: [PATCH] ping sensor fixes #72 --- extension.js | 4 +- icons/gnome/network-ping-symbolic.svg | 8 +++ icons/original/network-ping-symbolic.svg | 1 + prefs.js | 18 +++++ prefs.ui | 41 ++++++++++++ schemas/gschemas.compiled | Bin 2084 -> 2140 bytes ....gnome.shell.extensions.vitals.gschema.xml | 5 ++ sensors.js | 63 ++++++++++++++++++ 8 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 icons/gnome/network-ping-symbolic.svg create mode 100644 icons/original/network-ping-symbolic.svg diff --git a/extension.js b/extension.js index c528cad6..8d396d90 100644 --- a/extension.js +++ b/extension.js @@ -39,6 +39,7 @@ var VitalsMenuButton = GObject.registerClass({ 'network' : { 'icon': 'network-symbolic.svg', 'icon-rx': 'network-download-symbolic.svg', 'icon-tx': 'network-upload-symbolic.svg', + 'icon-ping': 'network-ping-symbolic.svg', 'icon-ad': '../flags/1x1/ad.svg', 'icon-ae': '../flags/1x1/ae.svg', 'icon-af': '../flags/1x1/af.svg', @@ -337,7 +338,8 @@ var VitalsMenuButton = GObject.registerClass({ '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' ]; + 'include-static-info', 'include-static-gpu-info', + 'network-ping-host' ]; for (let setting of Object.values(settings)) this._addSettingChangedSignal(setting, this._redrawMenu.bind(this)); diff --git a/icons/gnome/network-ping-symbolic.svg b/icons/gnome/network-ping-symbolic.svg new file mode 100644 index 00000000..b798bef5 --- /dev/null +++ b/icons/gnome/network-ping-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/icons/original/network-ping-symbolic.svg b/icons/original/network-ping-symbolic.svg new file mode 100644 index 00000000..04834170 --- /dev/null +++ b/icons/original/network-ping-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/prefs.js b/prefs.js index bdeb9a80..eee7a2e0 100644 --- a/prefs.js +++ b/prefs.js @@ -78,6 +78,24 @@ const Settings = new GObject.Class({ this._settings.bind('network-public-ip-interval', this.builder.get_object('network-public-ip-interval'), 'value', Gio.SettingsBindFlags.DEFAULT); + let pingHostEntry = this.builder.get_object('network-ping-host'); + let pingHostSave = this.builder.get_object('network-ping-host-save'); + let getSavedPingHost = () => this._settings.get_string('network-ping-host'); + let savePingHost = () => { + if (pingHostEntry.get_text() === getSavedPingHost()) + return; + + this._settings.set_string('network-ping-host', pingHostEntry.get_text()); + pingHostSave.set_sensitive(false); + }; + + pingHostEntry.set_text(getSavedPingHost()); + pingHostEntry.connect('changed', (widget) => { + pingHostSave.set_sensitive(widget.get_text() !== getSavedPingHost()); + }); + pingHostEntry.connect('activate', () => savePingHost()); + pingHostSave.connect('clicked', () => savePingHost()); + // process individual text entry sensor preferences sensors = [ 'storage-path', 'monitor-cmd' ]; for (let key in sensors) { diff --git a/prefs.ui b/prefs.ui index b3c8c4b2..01a10278 100644 --- a/prefs.ui +++ b/prefs.ui @@ -147,6 +147,47 @@ + + + 100 + 1 + 0 + + + 1 + 6 + 6 + 6 + 6 + 12 + + + 1 + 0 + start + Ping host + + + + + e.g. 1.1.1.1 + 1 + 1 + 18 + + + + + 1 + 0 + Save ping host + object-select-symbolic + + + + + + diff --git a/schemas/gschemas.compiled b/schemas/gschemas.compiled index b84716f2279c87ea5ad1ad1bc993508bb54a2564..30155af967ca2247ec4e00ac09a642ad3cd2a07b 100644 GIT binary patch literal 2140 zcmZWqO=ule7`@fT*2FYv+S)&(iA8x)=Dmj6n4$|sidiTGTBV3MnfKn!n_K74xid*# ztRfhpip51Y0S#GN5Vc*1rDD;An2nHycAJWuhiIw`ZL-jM&b{*z(U9}bnKR$qpYQ&B zPe1Ra%8N2l{OtpuvbuCz;cWq5EB`n`=DR^LCeBcA6{2I05V!DmYC8<^j3B=V#A&14 zh~iL|wJ(F9EN3zqX%$DhJf$)(&T3+zH(UbQjnG?g4fJ zeZZ~)yTNT>h=TLl4;(CT54gX;z2N%*j>YYER}X+Wx9ws|On@b@k{|zqK6NMj1e^o! z#&5qspSlbF8{oHq^EZDUr%$~D{ycaAIG%qyO`rNM_!q&S0r}kf&(Wu*zXbjc_$iVf z)2H4IzXkpcXkGdI1NzjA7ajNqR!iPtExz;2b#m(u$cEHQPT6J_lU*wD$_*shPKV@B*+} zIc?@k&2}z;zXl#!`tTCtsp&71u^C=Dw%$jdx)1&UxCGRPt4;dUj2{I*!FSv2I#P2T z$H315-RnI=jHliOe*)|SBiEliO`rN8{IlS5z*n!m-o^b*-2s0dd==@ZUiD{801!2yFN{U~ulyhZ#@Z%YMNNz{NNH zHTu+h;a>)S3H);My%qY@%<~d>8Myh*)w}6abG|L`ufQiy-Kx;1X5QAoe*v$KtRJIK z&2{X>!P^7ecp&MdPtARJ7UvepZWm&aWL-y?Z?wjBgwH6)gfQir86tjn5Q1k z=4B|uIBjm(cuvx|Cbf=J#0atJPsKszHKcJ1e^SlJx-+fnnP1!^Ha?Qsbez8I=tScE zWSoZH#>K|(OUibwvp6l9-0Y|7WtmKyML%YoNvu_dJIhg#lX#H~?ES=cifBg%@n#qB zLmA~xO-5)+)=gXO`Rb<5WN7+pk26tG!|`KnS}19oCso5G$-!2K!Z=bGs#6Q=<}P(? z8&2UP2H(V)qj9yysY!)v)9U6L6Lf`Zzi@-ttGmM?47c5RI^%}kjO&V?fg_^iX$+kU z;)K%Ec`7jx4vTj_=bI;WFOyEDLc0<;mc}?|lNmon(scaUIiY*`&Y%a=YD{7g~ygP30vOO9+0VFd%?E@I7T?{ zw(j7nHZ_TLi1mo|Y7j89z}`q?>SahhESnD%)q)&HM`vD!q~SPWCr-wEkKtE`xK;47 z8Q3-9I{oL9&2YwhLE?K=nWnz|3ZctD7KKz8yYyKmGSXB>PG*fsa$GRL8& ze*`=MJl)t=rccfFdf*tiIX8NZJ~iV{fZqkaeC}?IJ~h`n3qAvke{svFPfhmvMeq{v=gfY4Uetr|S%`hW*iX+M zV?6av_$BaOVC3XePtd2{0Dl~O5LmqU^~dz7?LL4ffaYj@nm#r2e--Qj-))~ePM>-; z{21H;2A78_^r@NuDex@t`hn$P`qabl&x7ZI^KW~1=u>ZnzW}}pEWNnwFoJQCb_O3f-+N3IUiVvuGQ}O(!a%NPb ziyJS>^@{QIloP5@C)2C=?l*Mgm~=W&m<3PS&qG==%D+dAAj2?mRuBA!8_Q zay-B7slXGcn;F?wu5j~A-9XExI~u}pBb83+q;}_ZG!{y`0 z+W0BX0(m;Ij1=;;|0Ourx=fM!v||Fj@}I-}We|I^u2O#{uYun9`AOM#TT3h!Cc(NX z>cXTlWnDxqZGD5P#6Do|?lJ{dfnp3*lsOH>-BZmXV~q3v4YoVi3S_&N2MRk@iA=ME z8TR~QvVX5Xi<>f4PU?q>^}+RJxGF`6eeb3iDrqnx63aicwiut6D=x`@KV#nf`#no< m$62v_?u*U7*lEVwr|ToM``%r*i4~5wrIS$hl0*>lVCX+*E3Ik( diff --git a/schemas/org.gnome.shell.extensions.vitals.gschema.xml b/schemas/org.gnome.shell.extensions.vitals.gschema.xml index fef102db..34cb02b7 100644 --- a/schemas/org.gnome.shell.extensions.vitals.gschema.xml +++ b/schemas/org.gnome.shell.extensions.vitals.gschema.xml @@ -94,6 +94,11 @@ Network speed format Should speed display in bits or bytes? + + "" + Ping target host + Hostname or IP address to ping for latency measurement + "/" Storage path diff --git a/sensors.js b/sensors.js index d61b0909..54a2bc19 100644 --- a/sensors.js +++ b/sensors.js @@ -27,6 +27,7 @@ import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import * as SubProcessModule from './helpers/subprocess.js'; +import Gio from 'gi://Gio'; import * as FileModule from './helpers/file.js'; import { gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js'; import NM from 'gi://NM'; @@ -46,6 +47,8 @@ export const Sensors = GObject.registerClass({ this._settings = settings; this._sensorIcons = sensorIcons; + this._pingCancellable = null; + this.resetHistory(); this._last_processor = { 'core': {}, 'speed': [] }; @@ -54,6 +57,7 @@ export const Sensors = GObject.registerClass({ 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('network-ping-host', () => this._pingCancellable?.cancel()); //this._addSettingChangedSignal('include-static-gpu-info', this._reconfigureNvidiaSmiProcess.bind(this)); this._gpu_drm_vendors = null; @@ -338,6 +342,63 @@ export const Sensors = GObject.registerClass({ this._returnValue(callback, 'WiFi Signal Level', signal, 'network', 'string'); } }).catch(err => { }); + + this._pollPing(value => { + this._returnValue(callback, 'Ping', value, 'network-ping', 'string'); + }); + } + + _pollPing(callback) { + let host = this._settings.get_string('network-ping-host').trim(); + if (!host) return; + + this._pingCancellable?.cancel(); + this._pingCancellable = new Gio.Cancellable(); + let cancellable = this._pingCancellable; + + let subprocess; + try { + subprocess = new Gio.Subprocess({ + argv: ['ping', '-c', '1', '-W', '5', host], + flags: Gio.SubprocessFlags.STDOUT_PIPE | + Gio.SubprocessFlags.STDERR_PIPE, + }); + subprocess.init(null); + } catch (e) { + callback('?? ms'); + return; + } + + cancellable.connect(() => subprocess.force_exit()); + + subprocess.communicate_utf8_async(null, cancellable, (proc, result) => { + let value = this._parsePingResult(proc, result, cancellable); + callback(value); + }); + } + + _parsePingResult(proc, result, cancellable) { + let stdout, exitStatus; + try { + [, stdout] = proc.communicate_utf8_finish(result); + exitStatus = proc.get_exit_status(); + } catch (e) { + let cancelled = cancellable.is_cancelled(); + return cancelled ? this._pingTimeoutLabel() : '?? ms'; + } + + if (exitStatus === 0 && stdout) { + let match = /time[=<]([\d.]+)\s*ms/.exec(stdout); + if (match) + return `${parseFloat(match[1]).toFixed(1)} ms`; + } + + return exitStatus === 1 ? this._pingTimeoutLabel() : '?? ms'; + } + + _pingTimeoutLabel() { + let timeoutMs = Math.min(Math.max(this._settings.get_int('update-time'), 1), 5) * 1000; + return `${timeoutMs}+ ms`; } _queryStorage(callback, dwell) { @@ -1066,11 +1127,13 @@ export const Sensors = GObject.registerClass({ this._frameMonitorLastTime = 0; this._frameMonitorFrameCount = 0; this._frameMonitorAccTime = 0; + this._pingCancellable?.cancel(); } destroy() { this._destroyFrameMonitor(); this._terminateNvidiaSmiProcess(); + this._pingCancellable?.cancel(); for (let signal of Object.values(this._settingChangedSignals)) this._settings.disconnect(signal);