Skip to content

Fix tray icon missing when KeePassXC starts before the StatusNotifierWatcher - #13708

Open
tassiovirginio wants to merge 1 commit into
keepassxreboot:release/2.7.xfrom
tassiovirginio:fix/trayicon-watcher-retry
Open

tassiovirginio wants to merge 1 commit into
keepassxreboot:release/2.7.xfrom
tassiovirginio:fix/trayicon-watcher-retry

Conversation

@tassiovirginio

Copy link
Copy Markdown

Summary

When KeePassXC starts before the StatusNotifierWatcher (system tray host) is available — e.g. right after login, before the shell is up, or after a tray-host restart — the tray icon never appears, and it never recovers even after the watcher shows up seconds later. The only way out was restarting the app.

This was reproducible on Wayland (niri + quickshell dms.service) and affects any SNI host that starts after KeePassXC.

Root cause (Qt 5.15)

Qt's generic Unix theme decides once per process whether a D-Bus tray is available:

// qgenericunixthemes.cpp (qtbase 5.15)
static bool isDBusTrayAvailable() {
    static bool dbusTrayAvailable = false;      // process-wide cache
    static bool dbusTrayAvailableKnown = false;
    if (!dbusTrayAvailableKnown) {
        QDBusMenuConnection conn;
        if (conn.isStatusNotifierHostRegistered())
            dbusTrayAvailable = true;
        dbusTrayAvailableKnown = true;
    }
    return dbusTrayAvailable;
}

If the first QSystemTrayIcon of the process is created while the watcher is absent, createPlatformSystemTrayIcon() returns nullptr: the icon never gets a real QDBusTrayIcon, /StatusNotifierItem is never exported, and recreating the icon later still hits the poisoned static cache — so no amount of show()/recreate retries can ever register.

Fix

  • Defer icon creation until org.kde.StatusNotifierWatcher is present on the session bus, so Qt evaluates its one-shot cache with the watcher up and a real QDBusTrayIcon (with /StatusNotifierItem + /MenuBar) is born.
  • Retry timer (5s) + QDBusServiceWatcher::serviceRegistered on org.kde.StatusNotifierWatcher to re-trigger registration as soon as a host appears (first-boot race and mid-session host restart both covered).
  • Verify actual registration by querying RegisteredStatusNotifierItems and resolving the owning PID of each item — accepts org.kde.StatusNotifierItem-<pid>-<n>, org.freedesktop.StatusNotifierItem-<pid>-<n> and the dedicated QDBusMenuConnection unique name (e.g. :1.853/StatusNotifierItem).
  • Guarded with Q_OS_UNIX && !Q_OS_MACOS && !QT_NO_DBUS; no change on Windows/macOS. Tray menu is now owned via QPointer so icon re-creation stays leak-free.

Testing

Validated live on the session bus with busctl while the real instance keeps running:

Scenario Result
Watcher down at launch, comes up later icon created on a dedicated connection ~6s after watcher appears, Id="KeePassXC", registered, stable (no churn)
Watcher up at launch (regression) icon created immediately and registered — happy path unchanged
Watcher flap (stop+start mid-session) re-registered in ~2s, stable afterwards

Before the fix, the watcher-down case never exported /StatusNotifierItem even after minutes; after the fix it registers without restarting the app.

Qt 5.15 decides once per process whether a D-Bus system tray is
available (static cache in QGenericUnixTheme::isDBusTrayAvailable).
If KeePassXC starts before the StatusNotifierWatcher (e.g. right
after login, before the shell is up), the first QSystemTrayIcon is
created with no platform tray icon, never exports /StatusNotifierItem
and a plain show()/retry can never recover: recreating the icon still
hits the poisoned per-process cache.

Fix by deferring creation of the tray icon until a watcher is present
on the session bus, and keep retrying (5s timer) plus a
QDBusServiceWatcher on org.kde.StatusNotifierWatcher that re-triggers
registration as soon as a host appears (first boot race or tray host
restart mid-session). Registration is verified against the watcher's
RegisteredStatusNotifierItems by resolving the owning process of each
item, accepting org.kde/org.freedesktop.StatusNotifierItem-<pid>-<n>
and the dedicated QDBusMenuConnection unique name.
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