Skip to content

fix(tray): Linux tray icon duplicates and dead menus on minimize-to-tray toggle - #5086

Open
jing2uo wants to merge 1 commit into
RSSNext:devfrom
jing2uo:fix/tray-linux-no-recreate
Open

fix(tray): Linux tray icon duplicates and dead menus on minimize-to-tray toggle#5086
jing2uo wants to merge 1 commit into
RSSNext:devfrom
jing2uo:fix/tray-linux-no-recreate

Conversation

@jing2uo

@jing2uo jing2uo commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Description

On Linux, toggling Settings → General → "Minimize to tray" off and on again
leaves a dead icon in the system tray every time. After a few toggles the tray
shows a stack of Folo icons; each opens a context menu but only the newest one's
menu does anything. The pile only clears when Folo fully quits.

Root cause: setTrayConfig(false) calls tray.destroy(). On a
StatusNotifierItem host (waybar, KDE Plasma, …) that does not remove the icon —
Chromium's StatusIconLinuxDbus registers the item by object path on the
process-wide shared session-bus connection and, on teardown, un-exports its own
objects but never tells org.kde.StatusNotifierWatcher the item is gone. The
connection name outlives the tray, so the host gets no NameOwnerChanged and
keeps a dead icon. Re-enabling then calls new Tray() again and Chromium
registers a fresh /org/chromium/StatusNotifierItem/<n+1>, stacking another.

Fix (commit 1): on Linux, keep the single Tray instance for the app's
lifetime instead of destroying/recreating it on the toggle. registerAppTray()
already guards new Tray() behind if (tray), and the window close handler
reads getTrayConfig() on every close, so a tray icon that outlives a disabled
setting is inert.

Follow-up (commit 2): the icon genuinely can't be removed from a running
process on Linux (Electron's Tray has no hide/visibility API), so
setTrayConfig now returns whether a restart is needed (true only when
disabling on Linux with a tray present). settings.setMinimizeToTray forwards
that and the renderer shows a toast with a "Restart now" action wired to a new
app.relaunch IPC. Enabling the tray, and all behavior on macOS/Windows, is
unchanged and takes effect immediately.

The real fix belongs upstream in Chromium/Electron (StatusIconLinuxDbus should
own a per-item well-known bus name and release it on teardown so hosts get
NameOwnerChanged); this is a workaround until then.

PR Type

  • Bugfix
  • Feature (minor: the "restart to remove the tray icon" prompt)

Screenshots (if UI change)

Demo Video (if new feature)

Linked Issues

Fixes #3940
Related: #4985, #3207

Additional context

Verified on Debian + Hyprland + waybar (Flatpak build, 1.13.0 / Electron 43.1.0).
D-Bus evidence: RegisteredStatusNotifierItems held four
:1.XXX/org/chromium/StatusNotifierItem/1..4 entries from a single process;
busctl --user tree showed only /4 actually exported; dbus-monitor showed
one RegisterStatusNotifierItem per toggle and never an unregister; on quit,
the host dropped all of them at once.

For review — the trade-off in commit 2: after disabling on Linux the icon
lingers until the app restarts (the toast's "Restart now" is optional; ignoring
it just leaves one inert icon, no stacking). Happy to switch to auto-relaunch or
drop the prompt entirely. The two new i18n strings fall back to en in other
locales.

Changelog

  • I have updated the changelog/next.md with my changes.
    (apps/desktop/changelog/next.md — desktop-only change)

Toggling "Minimize to tray" off then on repeatedly left a dead icon in the
system tray every time (waybar, KDE Plasma); each opened a menu but only the
newest one worked, and the pile only cleared when Folo fully quit.

setTrayConfig(false) calls tray.destroy(), which on a StatusNotifierItem host
does not remove the icon: Chromium's StatusIconLinuxDbus registers the item by
object path on the process-wide shared session-bus connection and, on teardown,
un-exports its own objects but never tells org.kde.StatusNotifierWatcher the
item is gone. The connection name outlives the tray, so the host gets no
NameOwnerChanged and keeps a dead icon. Re-enabling then calls new Tray() again
and Chromium registers a fresh /org/chromium/StatusNotifierItem/<n+1>.

- Linux: keep the single Tray instance for the app's lifetime instead of
  destroying/recreating it on the toggle. registerAppTray() already guards
  new Tray() behind `if (tray)`, and the close handler reads getTrayConfig()
  live, so a tray icon that outlives a disabled setting is inert.
- The icon can't be removed from a running process on Linux (Electron's Tray
  has no hide API), so setTrayConfig now returns whether a restart is needed;
  the renderer shows a toast with a "Restart now" action wired to a new
  app.relaunch IPC. Enabling the tray, and all macOS/Windows behavior, is
  unchanged and immediate.

The real fix belongs upstream in Chromium/Electron (StatusIconLinuxDbus should
own a per-item well-known bus name and release it on teardown); this is a
workaround.

Refs: RSSNext#3940, RSSNext#4985, RSSNext#3207

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QmyAEEXr4eHiNFSWGbZk7x
@jing2uo

jing2uo commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

@DIYgod 这个方便看下不~ 问题是: linux 下每次点关闭到托盘, 都会多一个托盘图标, 反复点的话会出一堆但只有最新那个可以点击. 必须关闭应用才能正常.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿


@DIYgod Is this convenient? The problem is: every time you click Close to the tray under Linux, there will be an extra tray icon. If you click it repeatedly, a bunch of them will appear, but only the latest one can be clicked. You must close the application to normal.

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.

Double icons appear on the system tray

1 participant