wo/README.md
2026-10-25 23:44:14 -04:00

9 KiB

wo compositor

Wo is an Electron-centered Smithay Wayland compositor. The portal backend is available as xdg-desktop-portal-wo; run wo portals to start it directly. The packaged xdg-desktop-portal configuration also starts it on demand.

User-local install

With Rust, Bun, and Python 3 installed, run just install from this checkout. It builds Wo, Electron, and comraw, then installs them under ~/.local and $XDG_DATA_HOME/wo. On first install it copies config.example.toml to $XDG_CONFIG_HOME/wo/config.toml and configures the copied comraw build to be served locally. Existing user configuration is preserved. Add ~/.local/bin to PATH and select the Wo session from your display manager.

Portal requests in the Wo API

Renderer code can subscribe to compositor-mediated requests and resolve them through the context-isolated API. Requests are denied by default if the application does not respond.

const unsubscribe = window.woClient.portals.onRequest(async ({ requestId, payload }) => {
  const allowed = await showConsentDialog(payload);
  await window.woClient.portals.respond(requestId, {
    allowed,
    type: 'monitor', // or 'window'
    windowName: null,
  });
});

The API is also available as window.compositor.portals in compositor UI contexts. Portal approval transport uses a per-user runtime socket with owner only permissions.

Desktop and system APIs

Wo exposes a typed convenience layer as window.compositor and window.woClient:

const directories = await window.compositor.xdg.directories();
const apps = await window.compositor.desktop.applications.list();
await window.compositor.desktop.applications.launch('org.gnome.Terminal.desktop');
const icon = await window.compositor.icons.resolve('utilities-terminal', { size: 48 });
const names = await window.compositor.dbus.listNames('user');

Desktop entries are read from the XDG data directories, localized names and user directories are honored, icons are resolved from XDG icon themes, and desktop Exec fields are launched as argument vectors without a shell. The D-Bus helpers use busctl; call arguments should include a D-Bus signature. window.compositor.audio lists PipeWire audio sources and can forward PCM from a desktop monitor source to the renderer. PipeWire and pw-cat must be available. Audio forwarding is exposed separately from ScreenCast because the standard XDG ScreenCast stream contains video, not a companion audio stream.

Website-first Wo API and component overlays

Wo treats loaded pages as websites first: ordinary pages receive no compositor bridge. A site can publish /_wo.capabilities.json and /_wo.components.css. The manifest requests named APIs and binds the request to the exact index.html SHA-256, a stylesheet SHA-256, and SHA-256 values for its script assets. Wo prefetches and verifies those files before navigation, caches verified assets by URL and hash, then asks the user before exposing the requested API groups. Granting access applies only to that page/window; a top-level navigation revokes it. Scripts for a manifest-enabled page are served from Wo's verified cache, and unlisted script loads are blocked.

The available permission names are window-management, overlay-rendering, desktop-applications, xdg, dbus, portals, filesystem, process-launch, system-control, notifications, and audio-capture. A page can inspect window.wo.permissions.requested, .granted, and .has(name). The compositor bridge groups (window.compositor and window.woClient) are present only after at least one API group is approved; unapproved groups are omitted and their IPC operations remain denied by the main process.

Pages granted portals can open a desktop context menu with await window.wo.portals.contextMenu({ x, y, items }). It resolves with the selected item ID after dismissal. Electron delegates the menu to the native desktop popup implementation, so Wayland presents it as a popup surface and it uses the desktop's native menu theme. Root portal UIs such as comraw's screen share prompt can be themed with _wo.components.css.

Comraw publishes _wo.capabilities.json, its schema, and _wo.components.css from comraw/public. The schema is _wo.capabilities.schema.json. Its Vite production build writes the current index.html hash, stylesheet hash, and script hashes into the public manifest, so the request stays tied to the exact build. The development server generates the matching index and stylesheet hashes on request. Wo sends the verified document and stylesheet bytes to the compositor, which computes SHA-256 itself and stores both hashes against the owning window. _wo.components.css provides component theme tokens and keyboard focus styling that Wo preloads and applies to the page. Websites can use the same manifest format to request a window surface or use the overlay-rendering bridge for HTML/CSS/JS surfaces.

Sites with window-management can subscribe to the compositor's monitor layout with window.wo.windows?.monitors(callback). Sites with overlay-rendering can pin an element to a monitor's global desktop rectangle:

const unmount = window.wo.overlay?.mount(element, monitor.id);
// Call unmount() to restore the element's original inline positioning.

Monitor coordinates are global desktop coordinates, so pointer positions and window placement remain consistent when an element is mounted on another display.

Comraw provides four persistent virtual desktops. Windows and their transient dialogs are assigned together, hidden from other desktops, and can be moved from the window titlebar; each monitor has an independent desktop selection, and Ctrl+Alt+Left/Right switches desktops on the monitor under the pointer. The DRM backend discovers connected outputs, assigns compatible CRTCs, places them side by side, and composites the shared desktop scene into a per-output swapchain. Native Wayland windows use the same global Smithay Space and can span monitors. Newly connected DRM outputs are detected by a two-second topology poll and attached when a compatible free CRTC is available. Unplug cleanup and live mode replacement are not implemented yet; restart Wo after removing a monitor or changing its mode. Workspace hiding remains a comraw shell feature; it does not isolate native Wayland clients. Portal monitor capture currently publishes the primary output rather than a stitched multi-monitor image.

Native offscreen frames

On Linux the Electron renderer requests webPreferences.offscreen.useSharedTexture and forwards the native-pixmap DMA-BUF planes to Wo over SCM_RIGHTS. This avoids the JS bitmap readback and Wo's RGBA-to-DMA-BUF import for normal offscreen frames. Wo still imports the DMA-BUF into Smithay for composition. The Electron and CEF source trees used to verify upstream support are separate checkouts; their Linux shared-texture/accelerated-paint APIs already expose native pixmap planes, so Wo's integration uses those APIs rather than carrying a fork of the frame capture implementation. The CEF tree is not a current runtime backend.

packaging/engines contains GN argument presets for an optional source build. For Electron, copy electron-wo.gn into the Electron checkout's build/args/wo.gn and import it from the Chromium out/<config>/args.gn as import("//electron/build/args/wo.gn"). For CEF, copy cef-wo.gn into the CEF checkout's tools/wo.gn and import it as import("//cef/tools/wo.gn"). Both source builds need the matching Chromium checkout and platform toolchain. The Arch package currently links to the system Electron package, so those presets do not change its Electron binary. Ubuntu builds bundle Electron with packaging/ubuntu/build-deb.sh and install a matching launcher/runtime pair. See packaging/ubuntu/README.md for the build requirements and install command.

GPU rendering and native input

Wo's compositor renderer uses Smithay GLES through EGL. Electron/Chromium content leaves GPU backend selection to Chromium by default. Vulkan is disabled for native Wayland Electron clients because Chromium's Wayland surface factory does not support it; this leaves Chromium free to use its GL/EGL path. Set WO_ELECTRON_GPU_BACKEND=angle or gl to select ANGLE OpenGL, vulkan for ANGLE/Vulkan, or desktop for native X11 GL. To test XWayland/GLX for Electron while Wo itself remains a Wayland compositor, launch with WO_ELECTRON_OZONE_PLATFORM=x11 WO_ELECTRON_GPU_BACKEND=desktop. Wo's nested backend prefers an inherited WAYLAND_DISPLAY, or discovers a wayland-* socket under XDG_RUNTIME_DIR when launchers omit that variable. GPU support still depends on the system driver and Chromium build.

Wayland clients receive pointer motion, buttons, scroll axes, relative pointer motion, and locked-pointer cursor hints through Smithay's native protocols. Forwarded input accounts for client-surface geometry offsets so window borders and client-side decorations do not skew pointer coordinates.

Headless tests

Run the Electron renderer and buffer tests with cd electron && bun run test. Run configuration tests without starting Wayland with cargo test --lib config::tests.