Skip to content

Capability and fallback

Linux desktops are heavily fragmented, and Wayland strictly restricts some capabilities outright. Code that assumes a feature exists fails on the next machine.

UDA’s contract: every feature reports its support level, so the application branches before calling.

SupportLevel is defined in uda_core::capability with three variants:

Variant Meaning What your app should do
SupportLevel::Full fully supported use it normally
SupportLevel::Partial available in a degraded form (a limited set of monitors, modes or formats) use it, but do not promise the missing part to the user
SupportLevel::None not available hide the entry, or explain why

Partial carries no reason string. When a backend needs to say why, it returns a typed UdaError at call time instead — the diagnostic text travels with the error, not with the capability level.

Underneath, capabilities are a set of bitflags, one bit per feature:

use uda_core::capability::Capability;
let caps = manager.capabilities()?;
if caps.contains(Capability::SET_WALLPAPER) {
// show the "change wallpaper" button
}

A Rust trait’s capabilities() returns a Capability; the C-ABI exposes the same information as plain integer bits (see session capability bits for that module’s table).

Every OS desktop action on Linux strictly follows:

Tier 1 XDG Desktop Portal is org.freedesktop.portal.* available?
↓ no
Tier 2 Native DE IPC read $XDG_CURRENT_DESKTOP, call GNOME/KDE
D-Bus methods, or Hyprland/Sway sockets
↓ no
Tier 3 CLI tools probe PATH for swww / hyprpaper / feh /
nitrogen / xfconf-query
↓ no
Tier 4 Typed error UdaError::Unsupported("...")

When all three service tiers fail there is no panic and no bare io::Error — the caller gets a UdaError::Unsupported carrying a diagnosis.

The actual chain for wallpaper, which does not use the portal:

Desktop Mechanism
GNOME 42+ the gsettings CLI, writing picture-uri and picture-uri-dark as a pair
KDE Plasma D-Bus org.kde.plasmashell → /PlasmaShell → evaluateScript
Hyprland the hyprpaper CLI, falling back to swww
Sway the swww CLI
Generic X11 the feh CLI, falling back to nitrogen
use uda_core::capability::SupportLevel;
use uda_core::tray::{TrayFeature, TrayManager};
let manager = uda_platform_linux::LinuxTrayManager::new();
// Cheap, always answers.
let level = manager.support_level(TrayFeature::Tooltip);
match level {
SupportLevel::Full => { /* draw the tooltip field */ }
SupportLevel::Partial => { warn!("tooltip is degraded on this backend") }
SupportLevel::None => { /* hide it */ }
}

support_level answers from the capability set the backend published at registration, so the value always describes this icon’s environment. Before a backend publishes, the answer stays SupportLevel::None — the honest default when no tray mechanism could be reached.

For features that are not tray-related, the same information is exposed per module: the notification backend has capabilities(), the session backend reports one bit per action, and the Python / Node.js SDKs surface the session bits directly.

A backend publishes Partial only when it can genuinely deliver a degraded behaviour — Linux double-click synthesis, or a tooltip longer than the soft cap. An absent flag is a plain None, never a guess. This keeps “advertised” equivalent to “deliverable”.