- 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).
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.