The story of snixembed

It's February 2020 and my riot-desktop tray icon vanished. Electron (Chrome, rather) dropped support for the System Tray Protocol Specification: an old but widely used system tray protocol for the Linux desktop. Chrome moved to StatusNotifierItem (SNI), a D-Bus based protocol that — while still too complicated for my tastes — is simpler than the X based tray protocol, and ready for Wayland.

The problem: I was not ready for Wayland. I was running dwm with a tray bar patch that doesn't support SNI. Convincing Electron to reconsider seemed impossible, so I looked for another way.

The solution: implement the bare minimum of SNI and then "translate" it to the old protocol. Thus snixembed was born.

I do not understand Xorg, and I did not feel like learning legacy technology (for which there is surprisingly little tutorial-style documentation online). So I decided to use a toolkit to handle the translation: I would set up the proper D-Bus services, parse the data, and leave the rest to Qt.

It didn't work: QSystemTrayIcon does not allow for choosing the backend. As soon as I started impersonating an SNI host properly, Qt started talking SNI back to me instead of XEmbed to my old tray.

Then I tried GTK. It worked: GTK didn't (and probably still doesn't) support the new tray icon specifications, because they had stopped believing in system trays since GNOME 3. The old implementation was marked deprecated as well. That didn't bother me much: I had little free time and wanted my icons back, fast.

I decided to use Vala, a language I used previously for a never published project. Vala has a simple (too simple, it turned out) abstraction over D-Bus and is made specifically for using GTK. That meant writing less code. Less code is my first ingredient for finished projects.

Two days later, snixembed worked. The code was awful, slapped together in a few hours with little design considerations. But I had my icons back, so I was happy. I put it on sourcehut (warning for crappy code and incomplete functionality), and announced it on the Arch Linux forums.

I didn't expect anyone to use it, but someone did. I saw people recommending snixembed on GitHub. I got sourcehut tickets, and made a Matrix room that saw use. Small numbers, about ten people in total. Far more interaction than I had expected.

I no longer use snixembed. Months after its creation, Electron decided to reintroduce its legacy tray code. Some time later it had hit production. I spread the word, and expected that would be the happy end of snixembed.

But once in a while, someone comes around with a bug or feature request. These people do not use snixembed in any way I would have imagined. One person uses (used?) it because they were running a KDE session but without its tray, and just the KDE session being there made applications ignore their legacy tray. Someone is using obscure apps that only use AppIndicator. (AI was Canonicals "competitor" to SNI that uses the same service names as SNI but then breaks from it in silly ways. I could write a whole blog post about the pains of this and other ways in which the ecosystem manages to just diverge from quite simple specifications and how I worked around it.) It's fun to see my project live longer than I have a need for it to.

These are the things I learned from snixembed: