← Otto Developer Guideotto-rdp

User guide: Remote Desktop
Specs: RDP Bridge

components/otto-rdp/ serves one Otto output over RDP and injects the remote client’s mouse and keyboard back into it. It captures a virtual output’s PipeWire stream, or a physical output through org.otto.ScreenCast, encodes it to H.264 on the GPU (or sends bitmaps) and speaks RDP with ironrdp-server. It is a standalone process that runs next to Otto on its Wayland socket; the compositor does not start it and knows nothing about RDP. It sits in the app layer of architecture.toml, and nothing depends on it.

otto-rdp: PipeWire capture and a GStreamer encoder feed the ironrdp server; input goes back through virtual-pointer and virtual-keyboard

RDP bridge for virtual outputs explains the bridge in depth: the compositor-side support it relies on, the command line, client quirks and the current limitations. This page is the structural overview.

Modules

ModuleWhat it does
main.rsParses the options, resolves the PipeWire node, starts the input thread, chooses the video path and builds the RDP server
discover.rsFinds a virtual output’s PipeWire node by its otto.output.name property
screencast.rsGets a physical output’s node from org.otto.ScreenCast: CreateSession, RecordMonitor, Start, then the stream’s PipeWireNode
pipewire_capture.rsPipeWire consumer for the bitmap path and for NVENC: maps shared-memory and linear dmabuf frames, repacks them as BGRx and fans them out on a broadcast channel
gpu_import.rsReads back tiled dmabufs the CPU cannot map: imports them into EGL on the render node and reads them with glReadPixels
h264.rsThe GStreamer encoder: pipewiresrc into vah264enc with VA-API, or captured frames through appsrc into nvh264enc
egfx.rsThe EGFX graphics-pipeline driver: pushes encoded access units to the client as AVC420 frames
rdp.rsThe ironrdp-server glue: the display handler and the input forwarder
wl_input.rsThe Wayland client that injects input through zwlr_virtual_pointer_v1 and zwp_virtual_keyboard_v1
tls.rsThe self-signed certificate, generated once and kept under the XDG state directory
indicator.rsThe tray indicator shown while a client is served, with its Stop Sharing menu

How it runs

One process, on a multi-threaded tokio runtime, with a few dedicated threads:

Who it talks to

PeerHow
CompositorWayland: zwlr_virtual_pointer_v1 bound to the output, zwp_virtual_keyboard_v1, xdg-output. D-Bus: org.otto.ScreenCast for physical outputs, through the otto-dbus proxies
PipeWirethe output’s video node, consumed directly or by pipewiresrc
GStreamerthe H.264 encode graph; a hardware encoder is required for the H.264 path
EGL and GLthe render node, for reading back tiled dmabufs
RDP clientTCP, 127.0.0.1:3389 by default; TLS with a self-signed certificate unless --no-tls. There is no authentication
Tray hostorg.kde.StatusNotifierItem and com.canonical.dbusmenu, registered with the StatusNotifierWatcher

It links otto-dbus and otto-foundations (for xdg::state_home). GStreamer and PipeWire are banned crates in deny.toml, and otto-rdp is one of the listed wrappers. GStreamer raises its toolchain to Rust 1.96.

Going deeper