Added a new API endpoint to list uploaded background images, enhancing the branding settings page with a background image library. Implemented UI components for selecting and displaying background images, including improved handling of background size and position. Updated tests to validate the new functionality and ensure proper integration with existing branding features.
BetterDesk RdClient Desktop
Tauri v2 desktop shell for the RdClient operator UI. The app loads your panel’s /remote dashboard in the main window and opens each remote session in a separate window (/remote/:deviceId), similar to RustDesk.
This is production-ready Phase C+ of the RdClient roadmap: server validation, LAN discovery, settings/reset, encrypted peer passwords, full panel i18n, codec fallbacks on Linux, and generator-built installers with embedded panel URL.
Prerequisites
- Node.js 18+ (for
@tauri-apps/cli) - Rust stable (1.77+; tested with Fedora’s toolchain)
- Linux:
webkit2gtk-4.1,libayatana-appindicator3,librsvg2,patchelf(Tauri Linux deps)
Platforms
| OS | Support |
|---|---|
| Windows | Native Tauri / WebView2 |
| Linux X11 | Native GTK + WebKitGTK |
| Linux Wayland | Native Wayland with automatic WebKit workarounds (KDE, GNOME, NVIDIA) |
Session detection and GDK/WebKit env setup run in src-tauri/src/linux_display.rs before GTK starts (also available via scripts/rdclient-launcher.sh for packaged installs).
TLS (HTTP / self-signed / Let's Encrypt)
RdClient targets self-hosted operator panels. By default it accepts:
http://panel URLs (no TLS)https://with self-signed BetterDesk installer certshttps://with Let's Encrypt or commercial CAs (including incomplete intermediate chains on LAN)
| Platform | Mechanism |
|---|---|
| Linux | Patched WebKitGTK TLSErrorsPolicy::Ignore on each WebContext (vendor/wry) |
| Windows | WebView2 --ignore-certificate-errors |
Strict validation (system trust store only):
BETTERDESK_TLS_STRICT=1 npm run dev
Use strict mode only when the panel serves a complete, publicly trusted chain and you do not need LAN/self-signed access.
Linux troubleshooting (Gdk error 71 / Wayland)
If the window fails to open on Wayland with:
Gdk-Message: Error 71 (Protocol error) dispatching to Wayland display.
the app already applies common fixes automatically. You can override:
| Variable | Effect |
|---|---|
BETTERDESK_UI_BACKEND=x11 |
Force XWayland / X11 (GDK_BACKEND=x11) |
BETTERDESK_UI_BACKEND=wayland |
Force native Wayland |
BETTERDESK_WEBKIT_DISABLE_DMABUF=1 |
Set WEBKIT_DISABLE_DMABUF_RENDERER=1 (disables GPU compositing — use if blank window / flicker) |
BETTERDESK_WEBKIT_NO_MEDIA_ACCEL=1 |
Skip GStreamer VA-API hints for WebKit media decode |
BETTERDESK_WEBKIT_NO_WORKAROUND=1 |
Skip all automatic WebKit/GDK tweaks |
BETTERDESK_WEBKIT_NO_COMPOSITING=1 |
Set WEBKIT_DISABLE_COMPOSITING_MODE=1 (last resort) |
NVIDIA + Wayland: the binary sets __NV_DISABLE_EXPLICIT_SYNC=1 when /proc/driver/nvidia/version exists and keeps DMA-BUF enabled for GPU compositing. NVIDIA + X11: DMA-BUF is disabled automatically (blank-window workaround).
GPU / codecs: Wayland keeps WebKit DMA-BUF/GPU compositing enabled by default. Vendor-specific VA-API drivers are auto-selected (Intel iHD, AMD radeonsi, NVIDIA when nvidia-vaapi-driver is installed). GStreamer ranks hardware decoders (vaav1dec, vah264dec, …) above software. WebView2 on Windows enables GPU rasterization, AV1/HEVC HW decode, and WebCodecs.
Linux packages (recommended for AV1 / smooth remote):
| GPU | Packages (Fedora example) |
|---|---|
| Intel | libva-intel-media-driver, gstreamer1-vaapi, gstreamer1-plugins-bad-free |
| AMD | mesa-va-drivers, gstreamer1-vaapi, gstreamer1-plugins-bad-free |
| NVIDIA | nvidia-vaapi-driver (optional VA-API), gstreamer1-plugins-bad-free |
Verify VA-API: vainfo (should list AV1/H264/VP9 profiles). WebKitGTK 2.44+ and GStreamer 1.24+ are required for WebCodecs + DMA-BUF zero-copy.
If the window is blank or flickers, set BETTERDESK_WEBKIT_DISABLE_DMABUF=1.
Examples:
# Force X11 session (works on XWayland)
BETTERDESK_UI_BACKEND=x11 npm run dev
# Wayland with extra-safe WebKit flags
BETTERDESK_WEBKIT_DISABLE_DMABUF=1 npm run dev
Quick start
cd rdclient-desktop
npm install
npm run dev
Clean rebuild (no Cargo cache)
If Connect still does nothing after pulling changes, force a full rebuild:
cd rdclient-desktop
./scripts/rebuild-clean.sh
Production bundle instead of dev:
./scripts/rebuild-clean.sh build
Important: the dashboard HTML/JS is loaded from your panel URL (/remote). Updating only the desktop binary is not enough for UI tweaks — run Settings → Updates on the panel (or deploy web-nodejs) so /js/remote-dashboard.js is current. The desktop shell also injects a Connect bridge, so Connect works even before the panel JS update once you run a freshly built binary.
- On first launch, enter your panel base URL (e.g.
https://desk.example.com) or pick a server from LAN discovery. - Sign in at
/remote/loginwhen prompted (same as the web RdClient). - Use Connect on a device — the desktop opens a new window instead of a browser tab.
- Open Settings (gear icon in the dashboard header) to change URL, TLS mode, language, sign out, or Reset client (clears config, cookies, and saved passwords).
Environment & embedded URL
| Source | Purpose |
|---|---|
BETTERDESK_SERVER_URL |
Auto-configure panel URL before setup UI |
betterdesk-rdclient.json next to the binary |
Installer-embedded { "server_url": "https://…" } from Generator |
| UDP / mDNS LAN discovery | Setup UI lists panels on the local network |
Password storage
- Operator login (“Remember me”):
RdClientSecureStorein IndexedDB (AES-GCM). - Device password (“Remember device password”): same vault, per
deviceId(peer:{id}keys). Passwords never leave the device. - Reset client: clears
config.json, WebView cookies/storage, and the vault.
LAN discovery
The panel publishes itself via UDP (port 21119, always on) and optionally mDNS _betterdesk._tcp when bonjour-service is installed and PANEL_MDNS is not off.
Build
npm run build
Installers/binaries are under src-tauri/target/release/bundle/.
How it works
| Piece | Role |
|---|---|
src/setup.html |
First-run: LAN discovery list + manual panel URL |
src/settings.html |
Local settings: URL, TLS, language, sign out, reset |
src-tauri/src/lib.rs |
Tauri commands: probe, discover, settings, sign out, reset, sessions |
src-tauri/src/config.rs |
Persists extended config + embedded/env URL helpers |
src-tauri/src/server_probe.rs |
Validates panel via /api/bd/server-info |
src-tauri/src/discovery.rs |
UDP LAN browse (BetterDesk announce protocol) |
src-tauri/src/linux_display.rs |
Linux X11/Wayland session + WebKitGTK workarounds |
src-tauri/src/tls_policy.rs |
Windows WebView2 + strict-mode env |
scripts/rdclient-launcher.sh |
Optional wrapper for release binaries |
vendor/wry/ |
Linux WebKit TLS policy patch (see vendor/README.md) |
web-nodejs/public/js/remote-dashboard.js |
Detects window.__TAURI__ and calls open_session |
Config file: config.json in the OS app config directory (com.betterdesk.rdclient).
Dependency note (brotli / alloc-no-stdlib)
Cargo.lock pins a git patch for alloc-no-stdlib so brotli (via Tauri) compiles with a single allocator version. If a fresh cargo update reintroduces alloc-no-stdlib 2.x and the build fails, run once from src-tauri/:
cargo update -p alloc-no-stdlib@2.0.4 --precise 3.0.0
Verify matrix (manual)
| Platform | Check |
|---|---|
| Linux Wayland | Setup discovery, login, VP9/H.264 session (no AV1 loop), settings reset |
| Linux X11 | Same as Wayland; test BETTERDESK_UI_BACKEND=x11 if needed |
| Windows x64 | WebView2 present, Connect, remember passwords, MSI/portable from Generator |
| Fedora deb/rpm | Installed bundle from Generator when build host has toolchain |
Roadmap (not in this release)
- Operator JWT + OS keychain login (
POST /api/bd/operator/login) - WebSocket relay Bearer auth for long-lived desktop sessions
- Native Rust video decoder (if WebCodecs insufficient on Linux)
License
AGPL-3.0 (same as BetterDesk on dev).