← Otto Developer GuideProject Structure

Otto is a Cargo workspace. The root crate is the compositor itself, and crates/ holds libraries split out of it (today the render stack, otto-render). components/ holds the applications and the libraries they share; each application is a separate binary that talks to the compositor over Wayland and D-Bus like any other client.

That split is deliberate and worth internalising early: the top bar, the launcher, the lock screen and the greeter are not part of the compositor. They are ordinary Wayland clients built with otto-kit. Only the dock, exposé, the app switcher and window decorations are drawn server-side.

The compositor crate

src/
├── main.rs              Entry point, backend selection
├── lib.rs               Library exports
├── state/               The big compositor state + most protocol handlers
├── shell/               Window management: xdg-shell, layer-shell, XWayland, decorations
├── workspaces/          Workspaces, window views, dock, app switcher, exposé
│   ├── dock/            The compositor-drawn dock
│   ├── app_switcher/    Cmd-Tab switcher
│   └── window_view/     Per-window rendering and effects (genie minimize)
├── input/               Keyboard and pointer event handling
├── input_handler.rs     Seat wiring, hit-testing, focus routing
├── focus.rs             What currently has keyboard/pointer focus
├── a11y/                AT-SPI adapter for Otto's own chrome
├── config/              TOML parsing, layered config, shortcuts
├── theme/               Colour palettes and text styles
├── render.rs            Builds the per-output render element list
├── udev/                Production backend: DRM/GBM/libinput (see below)
├── winit.rs             Development backend: run inside another compositor
├── x11.rs               X11 client backend (basic, not actively maintained)
├── headless.rs          Headless backend used by the integration tests
├── screenshare/         PipeWire screen capture + org.otto.ScreenCast
├── virtual_output/      Virtual (non-physical) outputs
├── settings/            Setting schema and storage
├── settings_service.rs  org.otto.Settings D-Bus service
├── surface_style/       otto-surface-style-v1: client-driven layer styling
├── otto_dock/           otto-dock-v1: lets clients contribute dock items
├── text_cursor/         otto-text-cursor-v1: where the caret is, for clients
├── audio/               Volume and output device handling
├── lock.rs              ext-session-lock-v1
├── login.rs             greetd session handoff
└── utils/               Shared helpers (natural layout, geometry, …)

The render stack is the otto-render crate, which lib.rs re-exports under its old paths (crate::renderer, crate::skia_renderer, …):

crates/otto-render/src/
├── skia_renderer.rs     Skia wrapper over Smithay's GlesRenderer
├── renderer/            EGL surfaces, textures, GPU sync; vulkan/ is Skia on Vulkan
├── render_elements/     Render element types handed to the damage tracker
├── drawing.rs           Pointer element, fps counter
├── textures_storage.rs  Texture cache
├── render_phase_stats.rs Per-phase frame timings
└── debug_hooks.rs       File-driven debug toggles

Being its own crate, it cannot reach into windows, workspaces, input or the configuration: the compositor hands it elements and settings.

src/udev/ is a directory. The interesting parts are render.rs (the frame path, by far the largest), planes.rs and backdrop.rs (hardware plane scanout and cross-plane blur), init.rs and device.rs (DRM setup and hotplug), and input.rs and edge_swipe.rs (the libinput event dispatch).

Components

Each is a crate in components/, either an application or a library the applications share:

ComponentWhat it is
otto-kitThe UI toolkit every other component is built on: window/surface plumbing, theme, typography, icons, widgets
otto-barThe top bar: clock, tray, global menus
otto-islandsThe dynamic island: notifications, HUD, permission dialogs
otto-launcherKeyboard-driven app and window launcher
otto-filesThe file manager
otto-peekPress space on a file and see it: the Peek previewer
otto-emojiEmoji picker that types the pick into the focused window
otto-settingsSettings application
otto-lockLock screen, backed by PAM
otto-greeterLogin screen, backed by greetd
otto-auth-uiThe password panel shared by the greeter and the lock screen
otto-msgDrives the compositor from a script, the way i3-msg and swaymsg do, and writes bug reports (otto-msg doctor)
otto-searchFile search over the desktop’s index: the query language, shared by Files, and the otto-search command
otto-agentsRuns ACP agents and serves them over the Agent Host Protocol
otto-foundationsLibrary: the UI-free base, XDG directories, file:// URIs and name matching; see otto-foundations.md
otto-agents-clientLibrary: talking to otto-agents, its socket, session URIs and file URIs
otto-media-kitLibrary: video playback for Otto apps; a sandboxed GStreamer worker plus the player view that draws its frames
otto-md-kitLibrary: Markdown parsed into the toolkit’s block vocabulary
otto-rdpServes a virtual output over RDP; see rdp-virtual-output.md
xdg-desktop-portal-ottoPortal backend: screencast, screenshot, settings, access dialogs, file chooser

Other top-level directories

protocols/       Wayland protocol XML (otto-surface-style, otto-dock,
                 otto-text-cursor, wlr-*)
docs/            user/ and developer/ guides
specs/           Behavioural specs — the contract each feature must meet
tests/           Integration tests that drive the headless backend
sample-clients/  Minimal Wayland clients for testing protocol behaviour
examples/        Cargo examples, built with `cargo run --example`
scripts/         Session launchers, packaging, and test helpers
assets/          Screen recordings and press material
resources/       Runtime resources (locales, desktop entries, icons, …)
website/         Hugo site generated from docs/

Build & run

Prerequisites

Package names vary by distribution. The Debian/Ubuntu names are what CI installs, so that list is the authoritative one: see the “System dependencies” step in .github/workflows/ci.yml.

On Debian and Ubuntu:

sudo apt-get install -y libdrm-dev libudev-dev libgbm-dev libxkbcommon-dev \
  libegl1-mesa-dev libwayland-dev libinput-dev libdbus-1-dev libsystemd-dev \
  libseat-dev libpipewire-0.3-dev libfreetype-dev libfontconfig-dev \
  libdisplay-info-dev libpixman-1-dev libgstreamer1.0-dev \
  libgstreamer-plugins-base1.0-dev libpam0g-dev

Add xwayland to run X11 applications inside Otto.

Running

With no backend flag, Otto picks --winit when WAYLAND_DISPLAY is set and --tty-udev otherwise. The check does not look for X11, so on an X11 session pass --winit explicitly, or Otto tries to take the display from the running desktop.

cargo run -- --winit          # windowed, inside an existing session — the dev path
cargo run -- --tty-udev       # bare metal DRM/GBM — needs root or libseat
cargo run -- --x11            # as an X11 client
cargo build --release

Build a single component with -p:

cargo build -p otto-kit
cargo run -p otto-settings

To run a component against a development compositor, point it at the compositor’s socket (Otto takes the next free one, usually wayland-1 when you are already inside a session):

cargo run -- --winit &
WAYLAND_DISPLAY=wayland-1 cargo run -p otto-bar

Minimum supported Rust is 1.87.0 for the compositor; building the whole workspace needs 1.96.0, which otto-rdp pins through GStreamer.

Feature flags

The canonical list is [features] in the workspace Cargo.toml. The default build is deliberately lean. Enable developer tooling on demand:

cargo run --features dev -- --winit
FeatureEffect
devConvenience: debug + profile + debugger + debug-hooks
debugDebug-only functionality, including RenderDoc capture
debug-hooksFile-driven live toggles and command files under /tmp (see crates/otto-render/src/debug_hooks.rs)
debuggerlay-rs scene debugger on localhost:8000, needed to inspect the live scene graph
debug-kmsExtra KMS/plane logging; opt in explicitly
profilePuffin profiling (compositor + lay-rs)
profile-with-tracy, profile-with-tracy-memTracy profiling, optionally with memory tracking
perf-countersExtra per-frame statistics logging
tickerOn-screen FPS counter
metricsRender metrics collection

Backends are features too: udev, winit, x11, plus xwayland and egl. default is egl, winit, udev, renderer_sync and xwayland; x11 and headless are opt-in.