Commit f7b1b7c5 by PLN (Algolia)

perf-tray: publish tray state on change, not on the 2s timer

The xpswatch tray lane measured ~1.9 tray signals/s at idle: refresh()
re-pushed icon (sparkline sample made every pixmap 'new'), tooltip and
menu rows every 2s, each push a NewIcon/PropertiesChanged the shell turns
into StTextureCache growth (the gnome-shell heap). Icon now publishes
only on (temp, gear, trim, badge) change, tooltip on text change with a
10s floor, rows/headers only on change.
parent 954b5d65
......@@ -60,6 +60,9 @@ import rig_health # noqa: E402 (the one health.json gig-log publishes; this on
SERVICE = "perf-tray" # systemd --user unit controlling autostart
END_SESSION = Path(__file__).resolve().parent / "tools" / "end_session.py"
REFRESH_MS = 2000
TIP_MIN_S = 10 # floor between tooltip publishes: the freq readout flaps
# every tick, and each flap is a D-Bus PropertiesChanged
# the appindicator extension makes the shell chew
TRAY_WAIT_S = 120 # how long to wait for the tray host (a GNOME extension) at login
BRIDGE_URL = "http://127.0.0.1:8773/"
......@@ -290,6 +293,21 @@ class PerfTray:
self.timer.start(REFRESH_MS)
self.refresh()
# Publish-gating state (2026-11-01 tray-churn fix). The xpswatch tray lane
# measured this process at ~1.9 tray signals/s while idling, all of it from
# refresh() re-pushing the icon, tooltip and menu rows every 2 s: the
# sparkline gets a new sample per tick, so the pixmap is always "new", and
# each push is a NewIcon/PropertiesChanged the shell turns into
# StTextureCache growth (the gnome-shell heap, runbooks/
# tray-churn-and-gnome-shell-heap.md). A tray icon must change on a state
# change, not on a timer; the sparkline catches up on the next real one.
self._icon_sig = None # (temp, gear, trim, badge) last published
self._last_tip = None
self._last_tip_t = 0.0
self._stat_txt = [None] * 4
self._health_txt = None
self._headers_txt = (None, None)
# ------------------------------------------------------------- ending a session
def _rig_is_up(self):
active, _ = sc_state()
......@@ -939,7 +957,10 @@ class PerfTray:
gb = GB.state()
hs = rig_health.tray_summary(rig_health.read_health())
self.tray.setIcon(make_icon(pkg, self.hist, gb["gear"], gb["trim"], hs["badge"]))
sig = (pkg, gb["gear"], gb["trim"], hs["badge"])
if sig != self._icon_sig:
self._icon_sig = sig
self.tray.setIcon(make_icon(pkg, self.hist, sig[1], sig[2], sig[3]))
# Check the gear the FILE says, never one inferred from sysfs. The old
# menu inferred it (perf.detect_mode reads governor/EPP/max_perf_pct), and
......@@ -955,12 +976,16 @@ class PerfTray:
act.setChecked(key == (gb["trim"] or GB.DEFAULT_TRIM))
act.blockSignals(False)
if GB.installed():
self.gear_header.setText(
hdr = (
"⚙ gear: " + (GB.label("gear", gb["gear"]) if gb["gear"]
else "none engaged (ppd owns the knobs)"))
self.trim_header.setText(
else "none engaged (ppd owns the knobs)"),
"🌡 trim: " + GB.label("trim", gb["trim"] or GB.DEFAULT_TRIM)
+ ("" if gb["trim"] else " (default)"))
+ ("" if gb["trim"] else " (default)"),
)
if hdr != self._headers_txt:
self._headers_txt = hdr
self.gear_header.setText(hdr[0])
self.trim_header.setText(hdr[1])
# Reflect autostart state without re-triggering the toggle handler.
self.autostart_action.blockSignals(True)
......@@ -999,6 +1024,12 @@ class PerfTray:
)
if hs["tip"]:
tip += "\n" + "\n".join(hs["tip"])
# The freq readout alone flaps every tick; push the tooltip only when the
# text changed AND the floor elapsed, so a live tick never publishes more
# than one PropertiesChanged per TIP_MIN_S.
if tip != self._last_tip and time.monotonic() - self._last_tip_t >= TIP_MIN_S:
self._last_tip = tip
self._last_tip_t = time.monotonic()
self.tray.setToolTip(tip)
rows = [
......@@ -1007,8 +1038,12 @@ class PerfTray:
f"freq {favg}/{fmax} MHz · fans {'/'.join(str(f) for f in fans) or 'n/a'} RPM",
f"gpu {gpu} · thermald {td} · throttle Δ {t.throttle_delta()}",
]
for a, txt in zip(self.stat_actions, rows):
for a, txt, old in zip(self.stat_actions, rows, self._stat_txt):
if txt != old:
a.setText(txt)
self._stat_txt = rows
if hs["row"] != self._health_txt:
self._health_txt = hs["row"]
self.health_action.setText(hs["row"])
self.fix_driver_action.setVisible("start_driver" in hs["fixes"])
self.fix_recorder_action.setVisible("start_recorder" in hs["fixes"])
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment