https://bugs.kde.org/show_bug.cgi?id=525808

            Bug ID: 525808
           Summary: Session restore never relaunches tray-icon-only
                    (StatusNotifierItem) applications
    Classification: Plasma
           Product: plasmashell
      Version First 6.6.6
       Reported In:
          Platform: Other
                OS: Linux
            Status: REPORTED
          Severity: normal
          Priority: NOR
         Component: Session Management
          Assignee: [email protected]
          Reporter: [email protected]
                CC: [email protected]
  Target Milestone: 1.0

# Session restore never relaunches tray-icon-only (StatusNotifierItem)
applications

## Product / Component

plasmashell — Session Restore (the `session-restore` save/restore tool in
`plasma-workspace`, authored in MR !3523 "Reopen running apps on startup").

## Summary

"Restore previous session" ("fake session restore", landed in Plasma 6.1)
never relaunches applications whose *only* UI is a system tray icon
(`org.kde.StatusNotifierItem` / `org.freedesktop.StatusNotifierItem`) with
no top-level window. This is not a timing race (unlike the superficially
similar bug 425315/385828, where apps *do* relaunch but their tray icon
fails to render due to a `StatusNotifierWatcher` startup race) — the
application process itself is never launched at all, because it was never
recorded as "running" in the first place.

## Root cause (verified against the actual source)

`session-restore/save.cpp` (from MR !3523) enumerates "running
applications" by iterating `TaskManager::WindowTasksModel`'s rows and
recording each row's `AppId`:

```cpp
// save.cpp, paraphrased from the actual MR:
// performs a Wayland roundtrip, then iterates tasksModel.rowCount(),
// reading AbstractTasksModel::AppId for each row, and persists that
// list to the session-restore state config.
```

`WindowTasksModel` is fundamentally a *window* enumeration model — it is
populated from Wayland toplevel surfaces (via KWin's window-management
protocols). An application that never creates a toplevel window during
normal operation — because its entire UI is a `StatusNotifierItem` tray
icon, shown/hidden windows on demand only (e.g. a settings dialog opened
by clicking the tray icon) — produces **zero rows** in this model. There
is no code path in `save.cpp` by which such an application could ever be
recorded, regardless of anything the application itself does.

`session-restore/restore.cpp` then reads back the saved `AppId` list,
resolves each one via `KService::serviceByMenuId(appId)`, and relaunches
it via `KIO::ApplicationLauncherJob` — but since tray-only apps were never
in the saved list to begin with, they are never reached by this code
either.

## Concrete real-world examples

Two independent, unrelated real-world applications reproduce this
consistently:

1. **[Safe Eyes](https://github.com/slgobinath/safeeyes)** (`safeeyes`,
   v3.5.1) — a break-reminder utility. Its tray icon
   (`safeeyes/plugins/trayicon/plugin.py`) is implemented as a
   `StatusNotifierItemService` — a plain `DBusService` subclass that
   registers itself with the `StatusNotifierWatcher` via
   `watcher.RegisterStatusNotifierItem(...)`. There is no `Gtk.Window`
   instantiated anywhere for the idle/tray state; a window only appears
   transiently for the fullscreen break screen (triggered periodically)
   or if the user manually opens Settings/About from the tray menu. At
   session-save time (logout), if no break screen or settings dialog
   happens to be open, SafeEyes has zero windows and is invisible to
   `WindowTasksModel`.

2. **[Seafile client](https://github.com/haiwen/seafile-client)**
   (`seafile-applet`) — a file-sync tray application. Its own config
   (`~/.config/Seafile/Seafile Client.conf`) explicitly sets
   `hideMainWindowWhenStarted=true`, meaning even its one actual window is
   deliberately kept unmapped/hidden during normal operation, for exactly
   the same "just a tray icon" use case.

## Steps to reproduce

1. In System Settings → Startup and Shutdown → Desktop Session, set "On
   Login" → "Restore previous session".
2. Launch a tray-icon-only application (e.g. `safeeyes`, or
   `seafile-applet` with "hide main window" enabled) alongside some
   ordinary windowed applications (e.g. a browser, a terminal).
3. Log out, then log back in.

## Expected result

All applications that were running at logout, including the tray-only
ones, are relaunched.

## Actual result

Ordinary windowed applications (browser, terminal, etc.) are correctly
relaunched. The tray-icon-only applications are silently never relaunched
— no error, no log message, nothing for the user to act on. The only
symptom is "it's just not there after I log back in," which is very hard
for an end user (or the tray application's own developer, who has no
reason to suspect Plasma's window-based session enumeration) to diagnose
without reading `session-restore`'s source directly.

## Why this is worth fixing at the Plasma level rather than in each app

Tray-only background utilities are a common and legitimate application
category (sync clients, break reminders, indicator applets, etc.). There
is no session-management protocol such an application could implement to
opt in to today's window-based enumeration — the gap is structural to
`save.cpp` only looking at `WindowTasksModel`, not at registered
`StatusNotifierItem`s. A fix at the Plasma level (e.g. `save.cpp` also
querying `org.kde.StatusNotifierWatcher`'s
`RegisteredStatusNotifierItems` property, recording the owning process's
command/desktop file the same way autostart-phase code already resolves
PIDs to `.desktop` files) would transparently benefit every existing
tray-only application without requiring any of them to change a single
line of code.

## Suggested fix sketch

In `save.cpp`, in addition to enumerating `WindowTasksModel`, also query
`org.kde.StatusNotifierWatcher.RegisteredStatusNotifierItems` (a standard
property already used by every tray implementation), map each item's
D-Bus service name back to its owning PID
(`org.freedesktop.DBus.GetConnectionUnixProcessID`),
resolve that PID to a `.desktop` file the same way the rest of the
save/restore code already resolves `AppId`s, and add it to the saved list
if it is not already present via a window. `restore.cpp` needs no changes
at all — it already just relaunches whatever `AppId`s were saved.

## Environment

- KDE Plasma: 6.6.6 (Wayland session)
- Distro: Gentoo, OpenRC
- Confirmed via live testing: after enabling "Restore previous session"
  and doing a real logout/login cycle, ordinary windowed apps (Chrome,
  System Settings, Konsole) were correctly restored; `safeeyes` and
  `seafile-applet` (both tray-only at the time of logout) were not,
  consistent with the source-level analysis above.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to