Skip to content

fix(desktop): only update the tray icon when the service state changes - #4017

Open
willherr72 wants to merge 2 commits into
Automattic:masterfrom
willherr72:will/solo-tray-icon-state
Open

fix(desktop): only update the tray icon when the service state changes#4017
willherr72 wants to merge 2 commits into
Automattic:masterfrom
willherr72:will/solo-tray-icon-state

Conversation

@willherr72

Copy link
Copy Markdown
Contributor

Issues

Description

The tray icon is rebuilt and reapplied every 250 ms whether or not anything
changed. Each tick decodes the PNG, builds an Image, and calls set_icon
four times a second, forever, to draw the icon it is already showing.

On macOS and Linux this is merely wasted work. On Windows it becomes a real
fault: after several days of uptime the shell started rejecting every update
with ERROR_TIMEOUT, which filled the log with one error per tick and left the
tray icon unresponsive. Shell_NotifyIcon is rate-sensitive, and a permanent
4 Hz stream of redundant updates is exactly the shape of traffic it gives up on.

This tracks the state the icon is currently showing and only touches the tray
when it actually changes.

One detail worth noting for review: when set_icon fails, the remembered state
is deliberately left unchanged, so the next tick retries rather than assuming
an update landed that did not.

Found while implementing Windows support, but the bug and the fix are
cross-platform — nothing here is cfg-gated.

How Has This Been Tested?

  • Reproduced the original failure on Windows: ERROR_TIMEOUT from set_icon
    repeating once per tick after several days of uptime.
  • With the change, set_icon is called only on an actual transition. Toggling
    the highlighter service from the tray menu still updates the icon
    immediately, in both directions.
  • Full CI green, including Linux and macOS.

AI Disclosure

  • I am a human and didn't use any AI.
  • I used LLM features of my editor, but not an agent.
  • I consulted one or more coding AIs, but didn't use an agent.
  • I used an AI agent interactively.
  • I am an agent or I got an agent to do the work autonomously.

Checklist

  • I have performed a self-review of my own code
  • I have added tests to cover my changes — the behavior is a timing loop
    against the OS tray API; I could not find a way to assert it that would
    be more convincing than the manual reproduction above. Happy to add one
    if you have a pattern in mind.
  • I have considered splitting this into smaller pull requests.

willherr72 and others added 2 commits August 10, 2026 15:25
The tray refresh loop rebuilt the icon and called set_icon every 250 ms
regardless of state: decoding the bundled PNG, redrawing the status
stripe, and making a cross-process shell call four times a second for
the entire life of the app.

On Windows this eventually fails outright. After several days of uptime
the shell began rejecting every update with ERROR_TIMEOUT ("This
operation returned because the timeout period expired"), which filled
the log at four lines a second and left the tray icon unresponsive.

The loop now compares against the state currently shown and only
touches the tray on a transition. A failed update deliberately does not
record the new state, so the next tick retries rather than leaving the
icon permanently stale.
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