Skip to content

appIndicator: Guarantee the last icon update paints - #667

Open
everyx wants to merge 1 commit into
ubuntu:masterfrom
everyx:guarantee-last-icon-paint
Open

everyx wants to merge 1 commit into
ubuntu:masterfrom
everyx:guarantee-last-icon-paint

Conversation

@everyx

@everyx everyx commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Pixmap-based StatusNotifierItems that update in bursts, or that re-register (sleep/resume, app or shell restarts), can leave the tray with a blank slot that never heals and logs nothing: if the settled fetch is undisplayable -- an empty pixmap because the signal arrived before the data, or a failed Get during re-registration flicker -- no later signal ever repaints it. Fresh registrations hit the same hole at first paint, when the name and object exist before the app populates its pixels.

Observed instance: WeChat, China's national-level Qt chat app, is pixmap-only (empty IconName). It fires NewIcon bursts while its IconPixmap flaps between the real icon and fully transparent 16x16 frames; after sleep/resume it re-registers under a new bus name and first-paints before its pixels land.

Fix with delivery guarantee, under these principles:

Refs #468 (pixmap/content duality), #663/#664 (async icon loading races).

@everyx
everyx force-pushed the guarantee-last-icon-paint branch 2 times, most recently from da81c03 to a34580a Compare September 17, 2026 01:29
Pixmap-based StatusNotifierItems that update in bursts, or that
re-register (sleep/resume, app or shell restarts), can leave the tray
with a blank slot that never heals and logs nothing: if the settled
fetch is undisplayable -- an empty pixmap because the signal arrived
before the data, or a failed Get during re-registration flicker -- no
later signal ever repaints it. Fresh registrations hit the same hole
at first paint, when the name and object exist before the app
populates its pixels.

Observed instance: WeChat, China's national-level Qt chat app,
is pixmap-only (empty IconName). It fires NewIcon bursts while its
IconPixmap flaps between the real icon and fully transparent 16x16
frames; after sleep/resume it re-registers under a new bus name and
first-paints before its pixels land.

Fix with delivery guarantee, under these principles:

- Faithful renderer: any pixels the app exports are painted as-is,
  however suspicious (even fully transparent). Guessing intent at
  this layer breaks real apps (cf. fcitx5's deliberate transparent
  overlay, upstream ubuntu#468). Transparent overlays are never touched.
- Exception fallback only: keep the old icon solely when there is
  literally nothing to display (empty/failed/malformed fetch).
- Last-wins delivery: after a refresh leaves a base icon family
  (Icon/AttentionIcon) undisplayable, re-fetch with bounded backoff
  once the burst settles (350ms, 2s, 8s; max 3 attempts). Also covered
  is the first paint of fresh registrations, via the ready-transition
  hook. Generation-guarded and bounded, so a stale retry can never
  overwrite newer state
  (the ubuntu#663/ubuntu#664 race class). AttentionIcon participates only while
  NeedsAttention; an empty attention icon is otherwise healthy.

Refs ubuntu#468 (pixmap/content duality), ubuntu#663/ubuntu#664 (async icon
loading races).
@everyx
everyx force-pushed the guarantee-last-icon-paint branch from a34580a to 564e444 Compare September 24, 2026 06:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant