Skip to content

Protocol overview

UDA lands on different low-level protocols on Linux and on Windows. This section documents each protocol’s interface details, its known traps, and the trade-offs UDA makes. This page explains what each one covers and how they fit together.

Protocol Platform Capability it carries
XDG Desktop Portal Linux Tier 1 of the fallback chain; UDA consumes only its Settings interface, for the colour scheme and accent colour
StatusNotifierItem (SNI) Linux the tray icon and its activation events
com.canonical.dbusmenu Linux the tray context menu, pulled by the shell
Shell_NotifyIconW Windows the tray icon and menu, via a local HMENU and a dedicated worker thread

Linux: the portal goes first, but is often not enough

Section titled “Linux: the portal goes first, but is often not enough”

The portal is freedesktop’s standard sandboxed interface layer. Every desktop environment implements it itself, and applications reach it over D-Bus without caring whether GNOME or KDE sits behind it. UDA treats it as Tier 1 of the fallback chain: if the interface exists and is exported, use it; otherwise drop to native IPC and CLI tools.

Real coverage varies a lot. GNOME 42+ is fairly complete, KDE and XFCE implement it partially depending on version, and Hyprland / Sway usually have none at all.

It is worth noting that although the portal defines Wallpaper and Inhibit interfaces, UDA’s backends do not use them — wallpaper goes straight to the desktop-native path and CLI toolchain, and keep-awake calls org.freedesktop.ScreenSaver.Inhibit on the session bus. The only capability that actually travels through the portal today is appearance.

Linux tray: SNI and DBusMenu come as a pair

Section titled “Linux tray: SNI and DBusMenu come as a pair”

On Wayland the tray can only go over D-Bus: the older XEmbed tray depends on a globally owned X window, and Wayland has no such thing.

The two protocols have clearly separated duties, and neither is optional:

  • SNI carries the icon itself and the activation events — clicks, scroll, context-menu requests.
  • DBusMenu carries the menu contents, and the direction is the shell pulling rather than the item pushing.

That puts the responsibility for menu state on the item side: after a user interaction the item must broadcast ItemsPropertiesUpdated before the shell redraws a checkmark. The classic symptom of skipping that step is “the first click works, the second snaps back to the old state”.

Shell_NotifyIconW’s NOTIFYICONDATAW embeds an hWnd, and the shell posts callback messages through it — but a window belongs to the thread that created it. UDA therefore spawns a dedicated worker thread, creates a message-only window there and runs its own message loop; every host-side state change is posted across as a message. That keeps the host’s event loop free while still satisfying “the owning thread makes the calls”.

The menu takes no protocol at all: a local HMENU plus TrackPopupMenuEx pops it directly. TrayManager absorbs the difference between the two tray mechanisms, so callers see one TrayMenu model.

For the high-level API see the system tray guide; for capability probing and the tier chain see capability and fallback.