9.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. A saved grant
is reused only for the same origin, document hash, declared script hashes, and
capability request.
The available permission names are window-management, overlay-rendering,
desktop-applications, xdg, dbus, portals, filesystem,
process-launch, system-control, notifications, audio-capture, and
settings. 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.
API consent is remembered per origin and exact site build. Wo records the
verified HTML hash, the declared JavaScript asset hashes, and the requested
capability set. The grant survives restarts; changing the HTML, a declared
script, or requested capabilities produces a new consent request. Sites with
settings can use window.wo.settings (get, set, remove, keys) for
persistent JSON values. Wo keeps each web origin in its own user settings file
with owner-only permissions, so apps can share their settings across pages on
the same origin without sharing them with other hosts. Store passwords and
tokens with getSecret, setSecret, and removeSecret; Wo encrypts these using
GNOME Keyring/Secret Service or KDE Wallet and declines secret writes when a
secure desktop keyring is unavailable.
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.