Unifying fragmented Linux desktop environments and Windows behind a single, type-safe API.
English · 简体中文
Building a cross-platform desktop application today means writing the same feature five times: SystemParametersInfoW on Windows, then gsettings, qdbus, swww and hyprpaper on Linux — each with its own failure modes, session assumptions, and no shared type system. UDA replaces that fragmentation with one capability-driven Rust API that probes the environment, picks the working backend, and degrades gracefully instead of panicking.
| Principle | What it means |
|---|---|
| Cascading fallback | XDG Desktop Portal → native DE IPC ($XDG_CURRENT_DESKTOP) → CLI tools (swww, hyprpaper, …) → a typed UdaError::NotSupported. Never a panic. Not every module uses Tier 1: wallpaper and wake locks start at Tier 2, and appearance detection is the only module that engages the portal, through org.freedesktop.portal.Settings. |
| Capability-driven | Features advertise SupportLevel::Full / Partial(reason) / None, so your app branches before it breaks. Partial carries the reason, so a degradation can be explained rather than guessed at. |
| Zero heavy runtime dependencies | Pure Rust zbus on Linux, windows-rs on Windows. No Qt, no GTK, no bundled toolkit. |
| Feature | Windows 10/11 | GNOME 42+ | KDE Plasma 5/6 | XFCE | Hyprland | Sway | Generic X11 |
|---|---|---|---|---|---|---|---|
| Theme detection | ✅ | ✅ | ✅ | ✅ | |||
| Accent colour | ✅ | ✅ | ❌ | ❌ | ❌ | ||
| Wallpaper | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Notification | ✅ 1 | ✅ | ✅ | ✅ | |||
| Notification icon | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Notification actions | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | |
| Wake lock | ✅ | ✅ | ✅ | ✅ | |||
| System tray | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | |
| Media control | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Session — lock | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Session — logout | ✅ | ✅ | ✅ | ||||
| Session — suspend / hibernate | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Session — reboot / shutdown | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
✅ verified against the real backend · ⚠️ best-effort fallback or an extra component · ❌ the platform has no such concept. The exact capability is always reported at runtime through capabilities() / support_level() — never assume from this table.
The per-cell backend, the
Three entry points: the native Rust crates, and a stable C-ABI (include/uda.h) reachable from Python and Node.js.
// Rust — native traits, no FFI layer
let manager = uda_platform_linux::LinuxWallpaperManager::new();
manager.set_wallpaper("~/Pictures/a.png", FillMode::Fill)?;# Python — examples/python/uda.py (ctypes, zero dependencies beyond the stdlib)
from uda import Uda
with Uda() as uda:
print(uda.theme) # 'dark' | 'light' | 'unknown'
uda.notify("标题", "正文内容") # native notification// Node.js — examples/nodejs/uda.js (koffi, zero-compile)
const { Uda } = require('./uda');
const uda = new Uda();
uda.notify('标题', '正文内容');
uda.dispose();Full install steps, capability guides and the complete API reference: unidesktop.github.io.
uda/
├── crates/
│ ├── uda-core/ # Public traits, enums, Capability flags, unified errors
│ ├── uda-platform-linux/ # Linux backends: D-Bus (zbus), Wayland IPC, X11
│ ├── uda-platform-windows/ # Windows backends: Win32, COM, WinRT
│ ├── uda-ffi/ # C-ABI export layer (Python ctypes / Node.js koffi)
│ └── uda-cli/ # Diagnostic CLI for manual probing
├── docs/internals/ # Protocol specification dictionary (the cheat book)
├── examples/ # 01_appearance … 07_session, aligned in 3 languages
└── plans/ # Per-phase implementation plans
| Module | Linux backend | Windows backend |
|---|---|---|
| Appearance | FreeDesktop portal Settings → GSettings → kreadconfig → xfconf-query; Theme::Unknown when nothing answers |
Registry AppsUseLightTheme / SystemUsesLightTheme |
| Wallpaper | GSettings → KDE plasmashell D-Bus → hyprpaper / swww → feh / nitrogen. No portal: org.freedesktop.portal.Wallpaper exists but is never called |
SystemParametersInfoW |
| Notification | org.freedesktop.Notifications |
WinRT ToastNotificationManager |
| WakeLock | org.freedesktop.ScreenSaver Inhibit |
SetThreadExecutionState |
| System tray | org.kde.StatusNotifierItem + com.canonical.dbusmenu |
Shell_NotifyIconW + worker-thread message pump |
| Media | MPRIS v2 over the session bus | WinRT SMTC |
| Session & power | systemd-logind + org.freedesktop.ScreenSaver |
Win32 power & shutdown APIs |
The C-ABI returns an int32_t status per call (0 success, -1 invalid argument, -2 unsupported, -3 detection failed, -4 I/O error, -5 internal, -6 a contained panic) and never unwinds across the boundary — every export body runs inside catch_unwind.
| Phase | Scope | Status |
|---|---|---|
| 1 | Core types, appearance, wallpaper, notifications, wake locks, C-ABI + SDKs | v0.1.0 released |
| 2 | System tray, media playback control, session & power lifecycle | v0.2.0 released |
| 3 | Global shortcuts, advanced clipboard, audio endpoint routing, display brightness | in progress |
| 4 | Virtual desktops, external window control, window aesthetics (Mica / Acrylic / KWin blur) | planned |
| 5 | Input simulation, eyedropper, screen capture, idle detection | planned |
See plans/ for the per-phase breakdown and AGENTS.md for the engineering rules.
Issues and pull requests are welcome on GitHub. The project is dual-licensed under MIT and Apache-2.0.
Footnotes
-
On a Microsoft Store–installed runtime, the toast source is shown as the host's package family name because Package Identity overrides the AppUserModelID. See
docs/internals/notification_specs.md§3.1. ↩ -
In an unpackaged script host, interactive action buttons degrade to a read-only text card, because toast buttons require a COM activator registered by an MSIX package. See the same document §3.2. ↩