{"id":51587715,"url":"https://github.com/jmylchreest/xembsni","last_synced_at":"2026-07-11T12:02:05.337Z","repository":{"id":368689317,"uuid":"1286354786","full_name":"jmylchreest/xembsni","owner":"jmylchreest","description":"XEmbed → StatusNotifierItem tray bridge for Wayland: shows legacy X11 \u0026 Wine/Proton system-tray icons (Battle.net, etc.) in waybar and other SNI hosts on niri, Hyprland and sway.","archived":false,"fork":false,"pushed_at":"2026-07-01T18:08:49.000Z","size":71,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-01T20:08:47.448Z","etag":null,"topics":["freedesktop","hyprland","niri","proton","rust","sni","statusnotifieritem","sway","system-tray","tray-icon","waybar","wayland","wine","wlroots","xembed","xwayland"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jmylchreest.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-01T17:36:35.000Z","updated_at":"2026-07-01T18:08:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jmylchreest/xembsni","commit_stats":null,"previous_names":["jmylchreest/xembsni"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/jmylchreest/xembsni","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmylchreest%2Fxembsni","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmylchreest%2Fxembsni/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmylchreest%2Fxembsni/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmylchreest%2Fxembsni/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jmylchreest","download_url":"https://codeload.github.com/jmylchreest/xembsni/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmylchreest%2Fxembsni/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35361644,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-11T02:00:05.354Z","response_time":104,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["freedesktop","hyprland","niri","proton","rust","sni","statusnotifieritem","sway","system-tray","tray-icon","waybar","wayland","wine","wlroots","xembed","xwayland"],"created_at":"2026-07-11T12:02:04.578Z","updated_at":"2026-07-11T12:02:05.331Z","avatar_url":"https://github.com/jmylchreest.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# xembsni\n\n**XEmbed → StatusNotifierItem bridge for Wayland.**\n\nMany X11 applications — notably Wine/Proton games and launchers — create a\nsystem-tray icon using the legacy freedesktop **System Tray Protocol** (the\n\"XEMBED tray\"). Modern Wayland bars (waybar, and anything speaking\n**StatusNotifierItem**) don't understand that protocol, so those icons either\nvanish or leave a stray Wine window lying around.\n\n`xembsni` is a small daemon that:\n\n1. Pretends to be an X11 system tray — it owns the `_NET_SYSTEM_TRAY_S\u003cn\u003e`\n   selection and accepts icons that apps try to dock.\n2. Republishes each docked icon as a modern **StatusNotifierItem** on D-Bus, so\n   it shows up cleanly in `waybar`'s `tray` module (or any SNI host).\n3. Proxies interaction — clicks, scrolls, and context menus — back to the\n   original icon, and hides the stray Wine window.\n\nIt works with anything that speaks the System Tray Protocol, not just Wine.\n\n## Status\n\nThe core bridge works end-to-end: docked icons are embedded, captured, and\nrepublished as StatusNotifierItems, and host interactions are forwarded back to\nthe X11 icon. Verified headlessly (Xvfb + a private D-Bus session) by the test\nsuite, and by a manual run with the bundled `mktray` helper.\n\nRoadmap:\n\n- [x] **M1 — Tray host spine:** own `_NET_SYSTEM_TRAY_S\u003cn\u003e`, announce `MANAGER`,\n  handle `SYSTEM_TRAY_REQUEST_DOCK`.\n- [x] **M2 — Embed \u0026 publish:** XEMBED docked icons into offscreen containers,\n  capture their pixmaps via the Composite + Damage extensions, and export a\n  `StatusNotifierItem` per icon (each on its own bus name, so removal is clean).\n- [x] **M3 — Interaction:** `Activate` / `SecondaryActivate` / `ContextMenu` /\n  `Scroll` are forwarded to the X11 icon as synthetic pointer events. Most\n  legacy tray icons pop their own native menu on right-click.\n- [~] **M4 — Polish:** done — `_XEMBED_INFO` map/unmap tracking driving SNI\n  `Status` (Active/Passive), clean un-dock handling, `--help`/`--version`,\n  a `Makefile` installer, and a Wine-like `winetray` test app. Remaining:\n  icon-theme/`IconName` lookup, richer tooltips, `com.canonical.dbusmenu`\n  passthrough, distro packaging.\n\n### Known limitations\n\n- Interactions are delivered via `XSendEvent`. This works for Wine and most\n  toolkits; a few apps that ignore synthetic events may not respond. A future\n  option is XTest-based delivery.\n- Only 24/32-bpp icon visuals are captured (what tray icons use in practice);\n  other depths fall back to a blank image.\n\n## Architecture\n\nCargo workspace under `crates/`:\n\n| Crate                 | Role |\n|-----------------------|------|\n| `xembsni`             | Binary/daemon: lifecycle, signals, wiring. |\n| `xembsni-tray-host`   | X11 side: owns the tray selection, XEMBED host. |\n| `xembsni-sni`         | D-Bus side: publishes `StatusNotifierItem`s (placeholder). |\n| `xembsni-bridge`      | Glue: maps X11 icons ↔ SNI items (placeholder). |\n\nAsync (Tokio) is used only at I/O boundaries (signals, and later D-Bus); the\nX11 event loop is synchronous and runs on its own thread.\n\n## Running\n\nRequires an X server. On Wayland, that's **Xwayland** (started automatically by\nmost compositors); for headless development, an **Xvfb** display works too.\n\n```sh\ncargo run -p xembsni\n# increase logging\nRUST_LOG=debug cargo run -p xembsni\n```\n\nIf another system tray already owns the selection on your display, `xembsni`\nexits immediately rather than fighting over it.\n\n### Trying it without a Wine app\n\nTwo example clients dock into a running `xembsni` so you can test the daemon\nand your bar without Wine:\n\n- **`mktray`** — the minimal case: a solid-colour icon that prints forwarded\n  clicks.\n- **`winetray`** — a faithful stand-in for a Wine/Proton tray app: it paints a\n  real icon, sets `_XEMBED_INFO`, re-docks if the tray restarts, and pops its\n  own native menu on right-click (just like Wine does).\n\n```sh\n# Terminal 1\ncargo run -p xembsni\n# Terminal 2 — pick one:\ncargo run -p xembsni-tray-host --example mktray -- 00ff8800 \"My Fake Icon\"\ncargo run -p xembsni-tray-host --example winetray -- \"Fake Wine App\" --blink\n```\n\nThe icon should appear in your bar's tray; left-clicking prints in the example's\nterminal, and right-clicking `winetray` opens its menu.\n\n### Options\n\n```\nxembsni --help       # usage\nxembsni --version    # version\n```\n\nLogging verbosity is controlled by `RUST_LOG` (e.g. `RUST_LOG=debug`).\n\n## Testing\n\nThe suite runs headlessly against a throwaway X server and D-Bus session:\n\n```sh\n# X-only tests (embedding, capture, pixel conversion)\nxvfb-run -a cargo test -p xembsni-tray-host\n\n# Everything, including the full publish + interaction end-to-end test\nxvfb-run -a dbus-run-session -- cargo test --workspace\n```\n\nTests that need an X server or bus **skip cleanly** when one isn't present, so\n`cargo test` stays green in a bare environment too. The end-to-end test stands\nup a fake `StatusNotifierWatcher`, docks a fake icon, and asserts it's published\nwith the right pixmap and that `Activate`/`ContextMenu` reach the X11 window.\n\n### As a user systemd service\n\nIntended to run as a user service under compositors like niri and Hyprland.\nThe `Makefile` installs the binary and unit for you:\n\n```sh\nmake install                 # -\u003e ~/.local/bin + ~/.config/systemd/user\nsystemctl --user daemon-reload\nsystemctl --user enable --now xembsni.service\njournalctl --user -u xembsni -f\n```\n\nOr by hand:\n\n```sh\ncargo build --release\ninstall -Dm755 target/release/xembsni ~/.local/bin/xembsni\ninstall -Dm644 contrib/systemd/xembsni.service \\\n  ~/.config/systemd/user/xembsni.service\n```\n\nThe unit is bound to `graphical-session.target` so it starts and stops with your\ncompositor.\n\n## Prior art\n\nKDE Plasma ships [`xembed-sni-proxy`][xesp], which solves the same problem\ninside a Plasma session. `xembsni` is an independent, compositor-agnostic\nimplementation aimed at bare wlroots/niri/Hyprland setups. It was written from\nthe freedesktop [System Tray][systray-spec] and [XEMBED][xembed-spec]\nspecifications; `xembed-sni-proxy` was consulted as design inspiration only —\nno code was copied.\n\n[xesp]: https://invent.kde.org/plasma/plasma-workspace/tree/master/xembed-sni-proxy\n[systray-spec]: https://specifications.freedesktop.org/systemtray-spec/\n[xembed-spec]: https://specifications.freedesktop.org/xembed-spec/\n\n## License\n\nDual-licensed under either MIT or Apache-2.0, at your option.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjmylchreest%2Fxembsni","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjmylchreest%2Fxembsni","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjmylchreest%2Fxembsni/lists"}