vikingowl dcb8facf11 feat: rescue orphan icons, inject menu clicks, reposition popups
- Adopt pre-existing XEmbed tray clients on startup so apps that
  don't respond to the MANAGER broadcast (notably Wine apps such as
  Battle.net) re-appear without relaunching.
- DBusMenu now exposes a non-empty layout and injects an X11
  right-click into the embedded client on AboutToShow / Event so the
  app renders its own native context menu. Quickshell otherwise
  refuses to display an empty menu.
- Subscribe to SubstructureNotify on root and reposition the
  freshly-mapped menu next to the tray-icon anchor via plain
  ConfigureWindow. Hyprland's XWM does not implement
  _NET_MOVERESIZE_WINDOW; the raw configure path is what wmctrl -e
  falls back to and is the only thing that actually moves the window.
- A DOCK request from a client we already adopted via rescue triggers
  a one-shot embed-refresh cycle (reparent out, reparent in, resend
  XEMBED_EMBEDDED_NOTIFY) so Wine's tray state machine transitions
  from "pending dock" to "embedded" and starts dispatching menus.
- ReparentNotify is no longer treated as an undock signal; Wine never
  voluntarily reparents its icon, and our own refresh path generates
  legitimate reparents we must not interpret as a client disappearing.
- WIP: container hidden via _NET_WM_WINDOW_OPACITY=0 and a WM_CLASS
  marker for compositor windowrules instead of off-screen positioning
  (Hyprland's XWayland remaps negative coords to monitor-local
  origin, making the container partially visible). Stacking changed
  to ABOVE so injected clicks land on the embedded child instead of
  whatever happens to overlap (0,0). Click path switched to
  XSendEvent for diagnosis (XTest doesn't appear to route through
  XWayland surface tracking to the embedded child).
2026-05-16 18:44:12 +02:00

xembed-sni-proxy

A lightweight proxy that bridges legacy XEmbed system tray icons to the StatusNotifierItem (SNI) D-Bus protocol. This allows old X11 tray applications to appear natively in Wayland compositors that support SNI (sway, Waybar, etc.).

Why?

Many older applications (e.g. some Java apps, Wine programs, legacy GTK2 apps) still use the X11 system tray (XEmbed) protocol. Wayland compositors don't support XEmbed — they expect SNI over D-Bus. This proxy sits in the middle: it claims the X11 system tray selection, receives embedded icons, captures their pixmaps, and re-exposes them as SNI items.

Requirements

  • Rust 1.70+
  • X11 libraries: libx11, libxcb, libxcb-composite, libxcb-damage
  • D-Bus
  • An active X11 server (e.g. Xwayland)

Arch Linux

sudo pacman -S libx11 libxcb

Debian / Ubuntu

sudo apt install libx11-dev libxcb1-dev libxcb-composite0-dev libxcb-damage0-dev

Fedora

sudo dnf install libX11-devel libxcb-devel

Building

cargo build --release

The binary will be at target/release/xembed-sni-proxy.

Installation

cargo install --path .

Or copy the binary manually:

sudo install -Dm755 target/release/xembed-sni-proxy /usr/local/bin/

Usage

Simply run the binary — it will connect to the current X11 display and start proxying tray icons:

xembed-sni-proxy

Environment Variables

Variable Default Description
TRAY_ICON_SIZE 64 Icon capture size in pixels
RUST_LOG xembed_sni_proxy=info Log level filter

systemd user service

A service unit is included for autostart with your graphical session:

install -Dm644 xembed-sni-proxy.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now xembed-sni-proxy

License

GPL-3.0-or-later. See LICENSE for details.

S
Description
No description provided
Readme GPL-3.0 77 KiB
Languages
Rust 100%