TesseraGitHub
Português (Brasil)

Esta página ainda não está disponível no seu idioma. A versão em inglês é exibida abaixo.

7 min de leituraEditar no GitHub

Adding a window-manager backend

Home · Development · Platform support

A backend is the adapter between Tessera’s tiling core and one window manager or compositor. This guide lists the files you touch, the rules the adapter must obey, and the evidence needed before anyone may call the result “supported”. Questions and proposals go to the issue tracker.

Writing an adapter does not make a profile supported. Support is announced per complete profile (OS, version, session, integration, architecture) and only after the evidence listed in Announcing support.

Architecture in five rules

Rule Where it lives
Tessera never becomes a window manager. It observes state and requests operations from the session’s own WM. CLAUDE.md, Architecture
BackendId is a closed enum with one variant per adapter. Backends are compiled in; there is no plugin loader and no runtime registration. crates/tessera-backend/src/registry.rs
One active adapter per session. A requested backend that is unknown or unavailable fails; the registry never silently falls back. BackendRegistry::select
Capabilities are atomic and optional: one BackendOperation per thing that can fail on a platform. An operation you do not implement is reported unsupported, never as success. crates/tessera-backend/src/capabilities.rs
A mutation counts as applied only when the resulting state is observed. A delivered request is not an applied change. OperationResult::Applied { observed, at } in operation.rs

Crate roles you will meet:

Crate Role
tessera-core Protocol-neutral tiling policy; no X11, GTK or config dependencies.
tessera-backend BackendFactory, BackendRegistry, BackendId, SessionDriver, capability facets, the shared contract suite.
tessera-backend-x11 The xfwm4 adapter; the only crate that uses x11rb.
tessera-backend-kwin, tessera-backend-gnome Experimental KWin and GNOME Shell Wayland adapters (IPC clients of a KWin script / GNOME extension).
tessera-backend-macos, tessera-backend-windows macOS Accessibility and Windows User32/DWM adapters.
tessera-app Composition root: registers adapters per OS in src/platform/.

The adapter contract

BackendFactory

Implement the trait in tessera_backend::registry:

Method Obligation
id() Return your BackendId variant.
serves(&SessionFacts) Say which sessions you target, using desktop (XDG_CURRENT_DESKTOP), session_type (XDG_SESSION_TYPE) and display (DISPLAY). The default returns true for every session, so always override it.
probe() Inspect the session without taking a window-manager role. Return Probe::available(OperationSet), Probe::available_degraded(..) or Probe::unavailable(UnavailableReason). Declare only operations you really implement here.
open(&Probe) Return a Box<dyn SessionDriver>. Refuse when the probe was unavailable.
open_inspection() Optional read-only session for the debug dumps. The default refuses with NotImplemented, so an adapter that cannot inspect says so instead of reporting an empty session.

SessionDriver and facets

The driver (in session.rs) always provides snapshot() (window and output inventory) and events() (drain pending changes, never block). Everything else is an optional facet accessor that returns None by default, for example geometry_request(), geometry_submit(), window_maximize(), window_focus_request(), workspace_activation(), output_enumeration(), hotkey_register(), inspection(), decorations() and picker(). The capability report is derived from your probe’s OperationSet, never from these accessors alone, so keep the two consistent.

Tiling needs the nine operations in BackendOperation::TILING_REQUIRED, starting with WindowEnumerate, WindowObserve, WindowGeometryRead, WindowMove and WindowResize (see capabilities.rs for the full list). Below that floor a provider may open only in diagnostic mode with tiling off (BackendRegistry::select_diagnostic).

Semantics that tests enforce

  • Unsupported is explicit. A missing operation fails with an UnsupportedReason (for example NotImplemented); it never returns success.
  • Degraded is declared. If an operation works with reduced fidelity, say so through Probe::available_degraded and a DegradedReason.
  • Applied means observed. Report Applied only after reading the state back. Use the other OperationResult outcomes (for example pending confirmation) for everything else.
  • Events are delivered, never dropped. Document any coalescing or suppression you do.

Step-by-step checklist

  1. Create the crate crates/tessera-backend-<name> and add it to the workspace. Implement BackendFactory and SessionDriver. Xfwm4BackendFactory in crates/tessera-backend-x11/src/xfwm4/factory.rs is the reference.
  2. Add identity in crates/tessera-backend/src/registry.rs: a variant on BackendId and one row in BACKENDS (variant, canonical name, accepted aliases, in declaration order). A misplaced row fails a compile-time assertion.
  3. Handle the exhaustive matches. These stop compiling until the new variant is covered:
    • BackendId::is_experimental (new adapters start experimental unless the manifest says otherwise);
    • BackendId::identity_field (what WindowSnapshot::class carries: WmClass, BundleId or AppId);
    • profile_for in crates/tessera-app/tests/support_manifest_and_release_gate_contract.rs.
  4. Register the factory in compiled_backends() of the matching module under crates/tessera-app/src/platform/ (linux.rs, macos.rs, windows.rs, other.rs). On Linux this is a .register(&*ADAPTER) call chained after tessera_backend_x11::xfwm4::registry(), with the adapter held in a lazily built static (see KWIN and GNOME). It must stay inert until probe or open. Add the dependency to crates/tessera-app/Cargo.toml.
  5. Add an integration endpoint only if selection needs one. KWin serves a D-Bus endpoint for its script (serve_kwin); GNOME dials the extension (connect_gnome). Both are listed in INTEGRATIONS in platform/linux.rs and run by serve_integrations_for; each returns at once for a session its adapter does not serve.
  6. Add unavailable reasons. A new refusal is a variant of UnavailableReason in crates/tessera-backend/src/error.rs, mapped in unavailable_reason_text (crates/tessera-app/src/error.rs) to a Fluent message. The message must exist in every catalog under crates/tessera-i18n/i18n/<locale>/tessera.ftl (de, en, es, fr, it, ja, ko, pt-BR, ru, zh-CN) and should tell the user what to do.
  7. Declare the profile in docs/compatibility/support-manifest.yaml (see below).
  8. Write tests (see below) and record what could not run.

Tests

Layer Where What it proves
Registry selection crates/tessera-app/tests/platform_providers.rs Served, unserved, ambiguous and served-but-unavailable sessions select or refuse as intended.
Registry and facets crates/tessera-backend/tests/ (backend_registry.rs, atomic_capabilities_contract.rs, capability_matrix_and_unsupported_contract.rs, session_probe_and_refusal_contracts.rs) Probe and refusal behavior, unsupported never success, an unreachable integration never reports applied.
Semantic contract tessera_backend::contract (ContractFixture, ContractSubject, violations) The shared suite every adapter must pass. It exercises only operations your probe declares, and reads effects back through the inventory or event stream.
Adapter-specific crates/tessera-backend-<name>/tests/ Protocol behavior; see crates/tessera-backend-x11/tests/ for the shape.

Supply a ContractFixture from a synthetic model or a real session, then assert the result is empty:

let subject = ContractSubject::from_driver(session.driver());
let found = violations(&subject, session.capabilities(), &fixture);
assert!(found.is_empty(), "adapter violates the semantic contract: {found:#?}");

Isolated environments

Display-dependent tests never run against your own desktop or session bus. Use scripts/test-env.sh <profile> <cmd...>, which builds a private environment and refuses your :0.

Profile Environment
x11-bare Private Xvfb, no window manager.
x11 Private Xvfb plus a live xfwm4.
x11-dummy Xorg dummy server with two RandR outputs (hotplug, resolution and rotation tests).
wayland Headless Weston (host Weston, else scripts/run-weston-session-container.sh -- <cmd>).
kwin-wayland Headless kwin_wayland --virtual with the shipped script, in a container.
gnome-wayland Headless GNOME Shell with the shipped extension, in a container.
gate Everything plus the workspace gate (fmt, clippy, tests).

Session images are also reachable directly with scripts/run-session-container.sh <xfce|kwin-wayland|gnome> -- <cmd>. Their containers have no network, no host display and no cargo: compile on the host, then pass the built test binary.

Exit codes of test-env.sh: the command’s own code, 2 usage, 3 UNAVAILABLE (a prerequisite is missing), 4 it refused the user’s display, 5 xfwm4 died during a gate run.

A test that could not run is UNRUN, never passed. Skipped tests print an UNRUN: <reason> line, and the gate ends with a gate: UNRUN summary. Do not count them, and say which ones were UNRUN when you report results.

Support manifest

Every registered backend needs one of three things in docs/compatibility/support-manifest.yaml and its contract test: a promoted profile, an experimental profile, or an entry in REGISTERED_UNSUPPORTED (in support_manifest_and_release_gate_contract.rs) with the reason. A stale entry also fails.

Profiles carry a profile_id, an estado_de_evidencia (values in the manifest today: implemented-unverified, experimental, blocked), and the declared operacoes. The list is an inventory of what the profile declares, not a claim of availability.

Announcing support

A new adapter stays out of the supported set until all of these hold for the complete profile:

  1. a verified control/event contract for the compositor or window manager;
  2. the shared semantic contract tests pass;
  3. CI evidence from the same commit;
  4. a smoke run in a real session of that profile (virtual servers and containers support development but are not a real session).

Claims about window-manager behavior are pinned to the latest stable source release; the xfwm4 reference is 4.20.0 (tag xfwm4-4.20.0). If you cannot establish a criterion, the profile remains experimental or blocked.

Some adapters need code inside the compositor (a GNOME Shell extension, a KWin script). Tessera does not install or remove these on its own:

  • The user installs and removes the extension with tessera gnome-extension install|remove. Without --yes the command asks on the terminal and changes nothing unless the answer is yes. tessera gnome-extension status only reports.
  • The adapter checks the integration before use: handshake, health, and version compatibility. A missing, incompatible or unverified peer yields a typed UnavailableReason such as GnomeExtensionAbsent, GnomeExtensionIncompatible or KWinScriptIncompatible, never a silent success.
  • Persistent permissions (for example macOS Accessibility) need explicit consent at the point of risk.

Worked example: GNOME Wayland

GnomeWayland fit the contract without a window-manager special case: one BackendId row, one .register(&*GNOME) in platform/linux.rs, one connect_gnome entry in INTEGRATIONS, and a profile that stays blocked in the manifest until a real GNOME smoke exists. The adapter is experimental and not counted as supported.