FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

X11: WindowGrabber still spikes flatpak-session-helper CPU — xprop @ 5 Hz via flatpak-spawn (Hyprland got fixed in #580, X11 did not) · Issue #644 · StreamController/StreamController · GitHub

X11: WindowGrabber still spikes flatpak-session-helper CPU — xprop @ 5 Hz via flatpak-spawn (Hyprland got fixed in #580, X11 did not) #644

Description

Summary

StreamController (flathub, com.core447.StreamController 1.5.0-beta.16, flatpak 1.14.6) makes
flatpak-session-helper permanently burn ~19–22 % of one CPU core (mostly kernel/system time)
on an X11 desktop (Cinnamon). The helper also suffers a voluntary context-switch storm
(~26 000–45 000/s)
. The cause is the X11 WindowGrabber: it polls the active window every 0.2 s
by spawning xprop, and because the app is sandboxed each call goes through
flatpak-spawn --host xprop … → org.freedesktop.Flatpak.Development.HostCommand (implemented by
flatpak-session-helper, which fork()+exec()s and streams output back over D-Bus). It runs
forever, even when the active window never changes.

This is a follow-up to #457, which was closed without the X11 path actually being fixed. There is
no open issue tracking this — see the issue landscape below before closing this as a duplicate.

Environment

OS             : Linux bro (X11 session), XDG_CURRENT_DESKTOP=X-Cinnamon
Flatpak        : 1.14.6 (system installation)
StreamController: com.core447.StreamController 1.5.0-beta.16 (flathub)
Runtime        : org.gnome.Platform/x86_64/50
CPU            : 20 cores
Helper process : /usr/libexec/flatpak-session-helper (PID varies; 4292 in this report)

Steps to reproduce

  1. Run StreamController under any X11 desktop (Cinnamon here; flatpak-session-helper high CPU usage and excessive window monitoring whenStreamController running #457 was GNOME Shell/X11).
  2. Observe flatpak-session-helper CPU in top/htop — sustained double-digit % of one core,
    mostly in sys/kernel time.

Reproduction without touching anything (works cross-user, read-only):

# helper PID (per-user): find via: ps -eo pid,comm | grep flatpak-session-helper
u1=$(awk '{print $14+$15}' /proc/4292/stat); sleep 8
u2=$(awk '{print $14+$15}' /proc/4292/stat)
echo "$(( (u2-u1) * 100 / 800 ))% of one core"          # observed: 19
v1=$(awk '{print $23}' /proc/4292/stat); sleep 3
v2=$(awk '{print $23}' /proc/4292/stat)
echo "voluntary ctx switches/s: $(( (v2-v1) / 3 ))"     # observed: ~45000

With access to the session bus, the D-Bus flood is directly visible:

timeout 12 dbus-monitor --session | grep -c 'member=HostCommand$'
# observed: 69 in 12 s (~5.75 Hz); message path/interface/member:
#   path=/org/freedesktop/Flatpak/Development
#   interface=org.freedesktop.Flatpak.Development  member=HostCommand
#   argv = ["xprop", "-id", "<active window id>", "WM_CLASS"] (plus fd passing)

Root cause (code level)

Installed app image: /var/lib/flatpak/app/com.core447.StreamController/x86_64/stable/<commit>/files/bin/StreamController/src/backend/WindowGrabber/Integrations/X11.py
(identical on main — diff-verified byte-for-byte).

  1. WatchForActiveWindowChange.run() loops time.sleep(0.2) → get_active_window() forever:
    while gl.threads_running:
        time.sleep(0.2)
        new_active_window = self.x11.get_active_window()
  2. One poll runs 3 separate xprop subprocesses — parse_window_ids() (xprop -root _NET_ACTIVE_WINDOW),
    get_title() (xprop -id <win> WM_NAME), get_class() (xprop -id <win> WM_CLASS).
    All 3 run on every cycle — the code only compares after fetching everything, so it keeps
    spawning even when the active window is unchanged (the if new_active_window == self.last_active_window: continue
    check happens after the 3 subprocesses).
  3. _run_command() wraps every command for the sandbox:
    if self.flatpak:
        command.insert(0, "flatpak-spawn"); command.insert(1, "--host")
    The manifest grants --talk-name=org.freedesktop.Flatpak, so each call lands on
    flatpak-session-helper. Each flatpak-spawn invocation is a fresh D-Bus connection
    (full AUTH/BEGIN handshake + Hello + per-connection proxy match rules) plus a fork()+exec()
    and fd-streaming on the helper — the helper-side cost is a small fork/exec per call at 5 Hz+
    (up to 3 xprop per poll = up to 15 host spawns/s in the worst case).
  4. The thread is started unconditionally when host xprop exists
    (self.is_xprop_installed = self.get_is_xprop_installed(); then
    self.start_active_window_change_thread()). There is no setting to disable it, and it runs
    even if the user never uses any feature that consumes the data.
  5. Consumers of the data (i.e. why it is "needed"): on_active_window_changed() in
    src/backend/WindowGrabber/WindowGrabber.py → notify_foreground_window_changed() → the D-Bus
    API ForegroundWindow property + the "auto change page by active window" feature.

Attribution is unambiguous: a grep of the whole SC src/ shows xprop is used only by
X11.py, and the other flatpak-spawn users are one-shot (permission manager) or event-driven
(Hyprland). All 4 installed plugins were also checked during the original investigation — no
plugin does any of this (the only periodic plugin activity is a Prometheus fetch and a
screensaver D-Bus GetActive check, neither of which goes through HostCommand).

Evidence

Measured live on the affected machine (2026-08-30, helper PID 4292, SC running):

Metric Value
Helper CPU in an 8 s window (/proc/4292/stat utime+stime) +152 ticks ≈ 19 % of one core
Voluntary context switches 45 056/s (3 s sample); earlier capture: ~26 000/s in 5 s
D-Bus HostCommand calls (12 s capture) 69 ≈ 5.75 Hz; 47 in the 8 s CPU sample (≈22 %)
xprop callers in SC core only X11.py (verified by grep)
Active window id being polled unchanged (same gnome-terminal window re-fetched every 0.2 s; SC log shows matching ForegroundWindow notifications)

Additional captures from the original investigation (same machine; strace of the app's
xdg-dbus-proxy): 17 148 lines = 96 identical connection lifecycles, each = accept →
connect to the session bus → full handshake → Hello (:1.224080…) → ~50 per-connection
AddMatch/GetNameOwner (normal xdg-dbus-proxy filter setup) → exactly one HostCommand
with SCM_RIGHTS fds → EOF → next connection. Cumulative flatpak-session-helper I/O counters:
rchar ≈ 14.4 GB, wchar ≈ 114 MB, syscr ≈ 9.83 M, syscw ≈ 7.82 M.

Corroboration from the #457 thread: the reporter found CPU drops to normal after
flatpak override --user --no-talk-name=org.freedesktop.Flatpak …, i.e. blocking HostCommand —
the exact mechanism identified here.

Issue landscape — no open issue tracks this

Checked 2026-08-30 (search: xprop, flatpak-session-helper, CPU):

And the decisive fact: src/backend/WindowGrabber/Integrations/X11.py on main is
byte-for-byte identical to the file in the installed 1.5.0-beta.16 image (diff-verified). Git
history shows no rework ever — last commits are "Fix: xprop spamming when xprop not installed"
(2025-04) and "Chore: Allow start on macos" (2025-11).

Expected behavior

Near-zero flatpak-session-helper CPU when the active window does not change — same outcome PR
#580 achieved for Hyprland. (flatpak-session-helper itself is innocent; it just serves the
churn.)

Suggested fix (summary — see companion PR proposal)

Make the X11 integration event-driven, mirroring the Hyprland rewrite:

  • Open a direct X11 connection from inside the sandbox (already permitted:
    sockets=fallback-x11;wayland; / --socket=x11 in the manifest — no manifest change needed)
    and wait for PropertyNotify of _NET_ACTIVE_WINDOW on the root window
    (select() on the X socket, no polling).
  • Read WM_CLASS/WM_NAME with XGetWindowProperty on the same connection — zero
    subprocesses, zero flatpak-spawn, zero D-Bus, zero helper involvement
    .
  • Implementation options, in order of preference:
    1. python-xlib (pure Python; SC already vendors all Python deps into the app image, so
      bundling it is a one-liner) — the approach Rework WindowGrabber under x11 to not call xprop #233 proposed;
    2. ctypes over libX11.so.6, which is already shipped in org.gnome.Platform/50
      (no new dependency at all);
    3. If the X connection is unavailable, disable window tracking with a log message —
      no polling fallback (xprop without a working X connection can never succeed, so a
      fallback would only reproduce the churn; the Hyprland fallback exists because
      hyprctl works even when the IPC socket is temporarily down — that does not apply
      here).
  • Not viable (checked): org.freedesktop.portal.WindowManager — portals deliberately expose no
    active-window query (that is why the GNOME integration uses a Shell extension instead); the
    portal interface only supports CreateTransientFor/DestroyTransientFor.

System information

StreamController: 1.5.0-beta.16 (flathub)
Flatpak: 1.14.6
Desktop: Cinnamon, X11 (also reproduced/config-identical to #457 on GNOME 46/X11)
Runtime: org.gnome.Platform/x86_64/50

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    • Status
      No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


    Back | FazBrowse Home | New Git URL