444 lines
27 KiB
Markdown
444 lines
27 KiB
Markdown
# 01 — Layered Architecture Map
|
||
|
||
This chapter walks Greenfield's architecture layer by layer, from the lowest
|
||
abstraction (Wayland XML) up to the highest (the user-facing shell). Each layer
|
||
is described in terms of: **responsibility**, **key abstractions**,
|
||
**dependencies**, and **files of interest**. The chapter ends with an ASCII
|
||
diagram showing how the layers stack and where data flows between them.
|
||
|
||
## The eight layers
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────────────────────┐
|
||
│ Layer 8: USER SHELL (Odoo 20 OWL addon, supplied by user) │
|
||
│ Hooks: UserShellApi (events + actions) │
|
||
│ ─ NOT covered in this chapter (see 08-app-lifecycle.md) │
|
||
├──────────────────────────────────────────────────────────────────────────┤
|
||
│ Layer 7: DESKTOP / LAYOUT POLICY │
|
||
│ Floating vs AlwaysFullScreen, view stacking, focus activation refcount │
|
||
│ packages/compositor/src/desktop/Desktop.ts │
|
||
├──────────────────────────────────────────────────────────────────────────┤
|
||
│ Layer 6: SURFACE LIFECYCLE / ROLES │
|
||
│ XdgWmBase, XdgSurface, XdgToplevel, XdgPopup, XdgPositioner, Shell, │
|
||
│ ShellSurface, Subcompositor, Subsurface, SurfaceRole │
|
||
│ packages/compositor/src/{Xdg*, Shell*, Sub*, SurfaceRole, SurfaceChild} │
|
||
├──────────────────────────────────────────────────────────────────────────┤
|
||
│ Layer 5: RENDERING PIPELINE │
|
||
│ Surface (state stack), View, Output (WebGL canvas), Renderer, Scene, │
|
||
│ Shaders (YUV→RGBA, YUVA→RGBA, RGB+A→RGBA), Loop (RAF), Callback │
|
||
│ packages/compositor/src/{Surface, View, Output, Loop, Callback, Region} │
|
||
│ packages/compositor/src/render/ │
|
||
├──────────────────────────────────────────────────────────────────────────┤
|
||
│ Layer 4: TRANSPORT (browser side) │
|
||
│ Channel (ARQ + Simple), ReconnectingWebSocket, RemoteAppLauncher, │
|
||
│ StreamingBuffer, BufferStream, EncodedFrame, EncoderFeedback, decoders │
|
||
│ (WebCodecs + WASM fallback) │
|
||
│ packages/compositor/src/remote/ │
|
||
├──────────────────────────────────────────────────────────────────────────┤
|
||
│ Layer 4: TRANSPORT (native side) │
|
||
│ Channel (ARQ + Simple), SessionController, kcp.ts, NativeAppContext, │
|
||
│ FrameFeedback, SurfaceBufferEncoding, wayland-server.ts │
|
||
│ packages/compositor-proxy/src/{Channel, kcp, Session*, Native*, ...} │
|
||
├──────────────────────────────────────────────────────────────────────────┤
|
||
│ Layer 3: WAYLAND RUNTIME (browser side) │
|
||
│ Connection (wire framing), Client, WlObject registry, marshallers │
|
||
│ libs/common/src/Connection.ts │
|
||
│ libs/compositor-protocol/src/{westfield-runtime-server.ts, generated/*} │
|
||
├──────────────────────────────────────────────────────────────────────────┤
|
||
│ Layer 3: WAYLAND RUNTIME (native side) │
|
||
│ wayland-server.ts + wayland-server-addon.node (N-API → libwayland-server)│
|
||
│ MessageInterceptor (per-resource request/event routing) │
|
||
│ packages/compositor-proxy/src/{wayland-server.ts, protocol/*_interceptor}│
|
||
├──────────────────────────────────────────────────────────────────────────┤
|
||
│ Layer 2: PROTOCOL GENERATORS │
|
||
│ client-generator, compositor-generator, compositor-proxy-generator │
|
||
│ libs/{client,compositor,compositor-proxy}-generator/ │
|
||
├──────────────────────────────────────────────────────────────────────────┤
|
||
│ Layer 1: WAYLAND PROTOCOL XML │
|
||
│ protocol/{wayland, xdg-shell, wayland-drm, web-bitmapbuf-unstable-v1}.xml│
|
||
└──────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## Layer 1 — Wayland Protocol XML
|
||
|
||
**Responsibility**: declare the protocol surface — what interfaces, requests,
|
||
events, enums, and bitfield types exist on the wire.
|
||
|
||
**Files of interest**:
|
||
|
||
- `protocol/wayland.xml` — standard upstream Wayland core. 22 interfaces,
|
||
`wl_compositor` v4, `wl_seat` v6, `wl_surface` v4 (roughly Wayland
|
||
1.20-ish). Kristian Høgsberg copyright. Used by all 3 generators.
|
||
- `protocol/xdg-shell.xml` — stable `xdg_shell` v1, 5 interfaces (xdg_wm_base,
|
||
xdg_surface, xdg_toplevel, xdg_popup, xdg_positioner). Used by all 3
|
||
generators.
|
||
- `protocol/wayland-drm.xml` — legacy `wl_drm` v2, used by native apps (Mesa,
|
||
XWayland) to advertise dmabuf buffers. **Only the proxy generator
|
||
processes this file** — so the proxy can intercept `create_buffer` /
|
||
`create_planar_buffer` / `create_prime_buffer` and turn dmabuf fds into
|
||
ProxyFDs for the browser's GStreamer encoding pipeline. Browser-side libs
|
||
do not generate `wl_drm` stubs.
|
||
- `protocol/web-bitmapbuf-unstable-v1.xml` — **Greenfield's custom
|
||
extension**. Single interface `web_bitmapbuf_factory` v1, single request
|
||
`create_buffer(id: new_id<wl_buffer>, bitmap: fd)`. Lets browser JS apps
|
||
hand a GPU-backed `ImageBitmap` directly to the compositor as a `wl_buffer`
|
||
— bypassing SHM copy, encoding, and dmabuf import. The `fd` arg is
|
||
marshalled via the same `fileDescriptor(...)` path as
|
||
`wl_shm.create_pool`'s fd, but holds an `ImageBitmap` (becomes a
|
||
transferable on the flush path). **Not processed by the proxy generator**
|
||
— native apps never use it.
|
||
|
||
**Dependencies**: none. This is the source of truth.
|
||
|
||
## Layer 2 — Protocol Generators
|
||
|
||
**Responsibility**: parse the XML with `xml2js`, walk the interface tree,
|
||
emit typed TypeScript bindings for each side.
|
||
|
||
**Key abstractions**: `ProtocolParser.mjs` (shared XML walker) +
|
||
`ProtocolArguments.mjs` (per-side argument-type dispatch table).
|
||
|
||
**Files of interest**:
|
||
|
||
- `libs/client-generator/{ProtocolParser, ProtocolArguments, index}.mjs` —
|
||
emits TS `*Proxy` classes for browser apps that want to be Wayland clients
|
||
of the browser compositor (via `MessagePort`).
|
||
- `libs/compositor-generator/{ProtocolParser, ProtocolArguments, index}.mjs`
|
||
— emits TS `*Resource` + `*Requests` classes for the browser-resident
|
||
compositor itself (receives requests, dispatches to `*Requests` impls,
|
||
emits events). Skips `wl_display`, `wl_registry` (hand-written in runtime).
|
||
- `libs/compositor-proxy-generator/{EndpointProtocolParser,
|
||
EndpointProtocolArguments, index}.mjs` — emits CommonJS
|
||
`*_interface.js` (wl_interface struct definitions for the N-API addon)
|
||
+ `*_interceptor.js` (per-opcode interceptors that decide routing) for
|
||
the Node.js native proxy.
|
||
|
||
**Why three generators?** Because each side has a fundamentally different job:
|
||
|
||
1. **client-generator** → browser apps that want to be Wayland clients of the
|
||
browser compositor (via `MessagePort`).
|
||
2. **compositor-generator** → the browser-resident compositor itself (receives
|
||
requests, dispatches to `*Requests` impls, emits events).
|
||
3. **compositor-proxy-generator** → the Node.js native proxy bridging real
|
||
libwayland-client apps to the browser. Emits `wl_interface` struct
|
||
definitions for the N-API addon + per-opcode interceptors that decide
|
||
routing.
|
||
|
||
**Dependencies**: `xml2js`, `camelcase`, `uppercamelcase`, `meow`.
|
||
|
||
## Layer 3 — Wayland Runtime
|
||
|
||
**Responsibility**: marshal and unmarshal the actual wire bytes. Implement
|
||
object lifecycle (creation, dispatch, destruction), new-id tracking, listener
|
||
dispatch, file descriptor handling.
|
||
|
||
**Browser side** (`libs/common/src/Connection.ts`) — the shared runtime for
|
||
both `@gfld/client-protocol` and `@gfld/compositor-protocol`:
|
||
|
||
- Wire format is **standard upstream Wayland**, byte-for-byte. 8-byte
|
||
header: `u32 object_id | u16 opcode | u16 size-incl-header`, then per-arg
|
||
bytes. `u/i/f/n/o` are 4 bytes; `s` and `a` are length-prefixed u32 +
|
||
payload padded to 4-byte alignment; `h` (FD) takes zero bytes — FDs travel
|
||
out-of-band in `wireMsg.fds[]`.
|
||
- Reading: `wlObject[opcode](message)` — the generated classes use
|
||
computed-property-key methods `[0](message) { ... }`, `[1](message) { ... }`
|
||
so opcode is the array index.
|
||
- Client side: `Proxy.marshallConstructor` calls `display.generateNextId()`
|
||
(1-indexed, recycles ids from `wl_display.delete_id`), constructs the new
|
||
Proxy (self-registers in constructor), patches the `new_id` arg's `value`,
|
||
then flushes.
|
||
- Server side: `Client.marshallConstructor` allocates from
|
||
`SERVER_OBJECT_ID_BASE = 0xff000000` (or recycles locally). When a
|
||
client-allocated id is destroyed, the server sends `wl_display.delete_id`
|
||
so the client can recycle.
|
||
- Connection registry: `wlObjects: { [id]: WlObject }`; `registerWlObject`
|
||
throws on collision.
|
||
- `Connection.message` dispatches by literal `wlObject[opcode](message)`.
|
||
Each handler returns void or a Promise; `Connection` awaits the promise
|
||
before reading the next message — this preserves wire-order while still
|
||
allowing async implementations. Per-client Connections mean a slow client
|
||
doesn't block others.
|
||
|
||
**Native side** (`packages/compositor-proxy/src/wayland-server.ts`) — a TS
|
||
wrapper around `wayland-server-addon.node` (the C N-API addon that wraps a
|
||
**patched** `libwayland-server`):
|
||
|
||
- `MessageInterceptor` — per-resource request/event dispatch. Each
|
||
`wl_*_interceptor.js` file declares `R<opcode>(...args)` and
|
||
`E<opcode>(...args)` functions that decide whether a message is routed
|
||
`{native: true}` (handle in the proxy), `{browser: true}` (forward to
|
||
browser compositor), or both.
|
||
- Special cases: `wl_display.sync` → both native and browser (must complete
|
||
before `done` event), `wl_display.get_registry` → both, `wl_registry.bind`
|
||
→ native if id is in `nativeGlobalNames[]` (e.g. `wl_drm`, `wl_shm`),
|
||
else browser. Fast-sync optimization skips browser round trip when no
|
||
prior browser-bound request occurred.
|
||
|
||
**File descriptor handling — the Greenfield innovation**:
|
||
|
||
`type FD = unknown` — it's intentionally opaque. Browser-side FDs are
|
||
**transferable JS objects**:
|
||
|
||
- `ArrayBufferView` for `wl_shm.create_pool`'s `fd` (SHM backing memory lives
|
||
in browser, zero-copy transferred via `MessagePort.postMessage(...,
|
||
transferList)`).
|
||
- `ImageBitmap` for `web_bitmapbuf_factory.create_buffer`'s `bitmap` arg
|
||
(GPU-backed texture, zero-copy).
|
||
- `MessagePort` for `wl_data_offer.receive`'s `fd` (in-browser pipe).
|
||
|
||
Native-side (proxy) FDs are real POSIX ints.
|
||
`NativeWaylandClientSession.onWireMessageEnd` converts each to a JSON
|
||
`ProxyFD = {handle, type, host}` (host = proxy's HTTP base URL),
|
||
length-prefixed + 4-byte aligned, prepended to the wire message before
|
||
sending over WebSocket/KCP. Browser's `webFS` resolves them by fetching
|
||
data over HTTP.
|
||
|
||
**Dependencies**: Layer 2's generated bindings + `@gfld/common`.
|
||
|
||
## Layer 4 — Transport
|
||
|
||
**Responsibility**: carry wire bytes between the browser compositor and the
|
||
native proxy. Multiplex logical channels. Handle reconnection, backpressure,
|
||
and latency.
|
||
|
||
**Browser side** (`packages/compositor/src/remote/`):
|
||
|
||
- `Channel.ts` — defines `ARQChannel` (KCP-wrapped, reliable, low-latency)
|
||
and `SimpleChannel` (raw WebSocket).
|
||
- `ReconnectingWebSocket` — vendored from `pladaria/reconnecting-websocket`
|
||
with `minDelay=1-5s`, `factor=1.3`, `maxDelay=10s`, `minUptime=5s`,
|
||
`connTimeout=4s`, `retries=∞`.
|
||
- `RemoteAppLauncher.ts` — sets up the 4 logical channels per app client
|
||
(PROTOCOL, FRAME, XWM, FEEDBACK) and routes incoming messages.
|
||
- `RemoteInputOutput.ts` — implements the `InputOutput` interface for native
|
||
apps, with `mkfifo`, `mkstempMmap`, `readFd`, `writeFdAsStream`, `closeFd`
|
||
over separate WebSocket endpoints.
|
||
- `StreamingBuffer.ts`, `BufferStream.ts`, `EncodedFrame.ts`,
|
||
`EncoderFeedback.ts` — decode pipeline for incoming H.264 / PNG frames.
|
||
- `webcodec-buffer-decoder.ts`, `wasm-buffer-decoder.ts`,
|
||
`H264NALDecoder.worker.ts` — the actual decoders.
|
||
|
||
**Native side** (`packages/compositor-proxy/src/`):
|
||
|
||
- `Channel.ts` — mirror image of the browser side. `ARQChannel` and
|
||
`SimpleChannel`.
|
||
- `kcp.ts` — ~1100-line TypeScript port of the canonical C `ikcp.c`.
|
||
- `SessionController.ts` — WS server with 9 endpoint paths:
|
||
`/signal`, `/channel`, `/mkfifo`, `/mkstemp-mmap`, `/read-fd`,
|
||
`/read-fd-as-stream`, `/write-fd-as-stream`, `/close-fd`, `/request-keyframe`.
|
||
- `Session.ts`, `SessionProcess.ts`, `SessionController.ts` — process model:
|
||
one main process, one forked child per browser session.
|
||
- `NativeAppContext.ts` — per-app state, signaling WS, lifecycle (10-min
|
||
SIGHUP grace on disconnect, SIGTERM→SIGKILL escalation).
|
||
- `FrameFeedback.ts` — receives 4-byte feedback messages from the browser,
|
||
uses them to pace `wl_frame.done` callbacks.
|
||
- `SurfaceBufferEncoding.ts`, `ProxyBuffer.ts` — encoding pipeline.
|
||
- `io/` subdirectory — WebSocket server glue.
|
||
- `encoding/` subdirectory — high-level encoding orchestration.
|
||
|
||
**Wire format**: see `04-transport.md` for the full byte-level breakdown of
|
||
PROTOCOL, FRAME, XWM, and FEEDBACK channels.
|
||
|
||
**Dependencies**: Layer 3 + `ws` (Node) + browser-native WebSocket + `ReconnectingWebSocket`.
|
||
|
||
## Layer 5 — Rendering Pipeline
|
||
|
||
**Responsibility**: take incoming buffer frames (SHM or encoded), turn them
|
||
into pixels on a browser canvas/WebGL context. Manage surface state, damage
|
||
tracking, frame callbacks, and the compositing loop.
|
||
|
||
**Key abstractions**:
|
||
|
||
- `Surface.ts` — the `wl_surface` implementation. Double-buffered state
|
||
(`state` vs `pendingState`), atomic merge in `commitPending()`. Subsurface
|
||
sync mode cascades through the ancestor chain.
|
||
- `SurfaceChild.ts`, `SurfaceRole.ts`, `Subsurface.ts`, `Subcompositor.ts` —
|
||
surface tree / role system.
|
||
- `View.ts` — represents a surface's position on an output, used for
|
||
compositing.
|
||
- `Output.ts` — the `wl_output` implementation; how output is rendered to a
|
||
WebGL canvas.
|
||
- `Shm.ts` — `wl_shm` / shm pool handling. How shared memory buffers arrive
|
||
in the browser.
|
||
- `BufferContents.ts`, `BufferImplementation.ts`, `ImageBitmapBuffer.ts` —
|
||
buffer abstraction hierarchy.
|
||
- `Region.ts` — `wl_region`, damage regions (pixman-WASM backed).
|
||
- `Callback.ts` — frame callback mechanism.
|
||
- `Loop.ts` — the compositor main loop / `requestAnimationFrame` integration.
|
||
- `Compositor.ts` — the `wl_compositor` global; how it creates surfaces and
|
||
regions.
|
||
- `Session.ts` — top-level browser-side session.
|
||
- `Globals.ts` — how Wayland globals are registered.
|
||
- `render/` subdirectory — the WebGL renderer: `Renderer.ts`, `Scene.ts`,
|
||
`SceneShader.ts`, `Texture.ts`, `RenderState.ts`, `Program.ts`,
|
||
`ShaderCompiler.ts`, `ShaderSources.ts`, `YUVA2RGBA.ts`, `YUVA2RGBAShader.ts`,
|
||
`YUV2RGBShader.ts`, `RGBXandAToRGBA.ts`, `RGBandA2RGBAShader.ts`.
|
||
- `browser/` subdirectory — browser integration glue (canvas, RAF,
|
||
ResizeObserver, dnd, selection, input).
|
||
- `math/` subdirectory — matrix/region math (`Mat4.ts`, `Rect.ts`).
|
||
|
||
**Pipeline**: see `03-html5-rendering.md` for the full
|
||
commit → damage → render → present pipeline.
|
||
|
||
**Dependencies**: Layer 4 + WebGL + WebCodecs + `@gfld/compositor-wasm` (pixman + xkbcommon).
|
||
|
||
## Layer 6 — Surface Lifecycle / Roles
|
||
|
||
**Responsibility**: implement the role-specific surface state machines
|
||
(xdg-toplevel, xdg-popup, xdg-positioner, wl_shell_surface, subsurface).
|
||
Manage configure/ack-configure round-trips, popup grabs, window state.
|
||
|
||
**Files of interest**:
|
||
|
||
- `XdgWmBase.ts` — the `xdg_wm_base` global. How clients advertise they're
|
||
XDG clients. Includes ping-based unresponsiveness detection.
|
||
- `XdgSurface.ts` — `xdg_surface`. The role wrapper around a `wl_surface`.
|
||
- `XdgToplevel.ts` — `xdg_toplevel`. Top-level windows. State (maximized,
|
||
fullscreen, minimized, activated, resizing). Configure/ack-configure
|
||
round-trip.
|
||
- `XdgPopup.ts` — `xdg_popup`. Transient windows, menus, tooltips. Grab
|
||
semantics.
|
||
- `XdgPositioner.ts` — `xdg_positioner`. How popups are positioned relative
|
||
to parent.
|
||
- `Shell.ts`, `ShellSurface.ts` — the legacy `wl_shell` (deprecated but still
|
||
implemented for old clients).
|
||
- `Subcompositor.ts`, `Subsurface.ts`, `SurfaceChild.ts`, `SurfaceRole.ts`
|
||
— subsurface compositing, the surface tree.
|
||
|
||
**State machines**: see `08-app-lifecycle.md` for the full
|
||
XdgToplevel lifecycle, configure/ack round-trip semantics, popup
|
||
positioner math, and subsurface sync/desync mode.
|
||
|
||
**Dependencies**: Layer 5 + Layer 3 (Wayland runtime).
|
||
|
||
## Layer 7 — Desktop / Layout Policy
|
||
|
||
**Responsibility**: arrange surfaces on the output. Decide stacking order,
|
||
focus policy, and the high-level window management mode (Floating vs
|
||
AlwaysFullScreen).
|
||
|
||
**Files of interest**:
|
||
|
||
- `desktop/Desktop.ts` — implements both `FloatingDesktopSurface` and
|
||
`AlwaysFullScreenDesktopSurface`, chosen by `session.config.mode`.
|
||
Floating lets the user move/resize windows freely; AlwaysFullScreen
|
||
enforces one window per output (kiosk mode).
|
||
|
||
**Dependencies**: Layer 6 + Layer 5 (for View rendering).
|
||
|
||
## Layer 8 — User Shell
|
||
|
||
**Responsibility**: the actual user-facing UI. The taskbar, window
|
||
decorations, app launcher, keyboard layout picker, etc.
|
||
|
||
**Out of scope for the user** — they will build their own shell as an Odoo 20
|
||
OWL addon. The integration point is `UserShellApi.ts` (see
|
||
`08-app-lifecycle.md`).
|
||
|
||
**Greenfield's reference shell** (`packages/compositor-shell`) is a Preact +
|
||
Tailwind SPA, 562 lines of TypeScript total. Mentioned here only for
|
||
orientation; not analyzed in this bundle.
|
||
|
||
## How data flows across the layers
|
||
|
||
```
|
||
NATIVE APP (e.g. gtk4-demo, X11 via XWayland)
|
||
│
|
||
│ libwayland-client → UNIX socket + SCM_RIGHTS FDs
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────────┐
|
||
│ NATIVE PROXY (Node.js + C N-API addons) │
|
||
│ │
|
||
│ Layer 3 (native): wayland-server.ts + wayland-server-addon.node │
|
||
│ ↳ patched libwayland-server parses wire bytes │
|
||
│ ↳ MessageInterceptor routes each request to {native, browser} │
|
||
│ ↳ FDs wrapped as ProxyFD JSON │
|
||
│ │
|
||
│ Layer 4 (native): Channel.ts + kcp.ts + SessionController │
|
||
│ ↳ 4 logical channels: PROTOCOL, FRAME, XWM, FEEDBACK │
|
||
│ ↳ KCP-over-WebSocket for ARQ channels │
|
||
│ ↳ SimpleChannel for FEEDBACK │
|
||
│ │
|
||
│ Layer 5 (encoding): SurfaceBufferEncoding + proxy-encoding-addon │
|
||
│ ↳ GStreamer H.264 (x264 / nvh264 / vaapih264) on dedicated thread │
|
||
│ ↳ PNG fallback for ≤256×256 surfaces │
|
||
│ ↳ Zero-copy dmabuf via EGLImage → GstGLMemoryEGL │
|
||
│ │
|
||
│ Layer 1 (XML) → Layer 2 (proxy-generator) feeds Layer 3 interceptors │
|
||
└─────────────────────────────────────────────────────────────────────────┘
|
||
│
|
||
│ WebSocket (TCP) + KCP-over-WebSocket
|
||
│ 9 endpoint paths per session
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────────┐
|
||
│ BROWSER COMPOSITOR (TypeScript + WebGL + WebCodecs + WASM) │
|
||
│ │
|
||
│ Layer 4 (browser): Channel.ts + RemoteAppLauncher + decoders │
|
||
│ ↳ ReconnectingWebSocket with exponential backoff │
|
||
│ ↳ ARQChannel + SimpleChannel mirror the native side │
|
||
│ ↳ WebCodecs VideoDecoder preferred, FFmpeg WASM fallback │
|
||
│ ↳ H264NALDecoder.worker.ts (2 workers: opaque + alpha) │
|
||
│ │
|
||
│ Layer 3 (browser): @gfld/common Connection + compositor-protocol │
|
||
│ ↳ Standard Wayland wire format, byte-for-byte │
|
||
│ ↳ WlObject registry, opcode dispatch via wlObject[opcode](msg) │
|
||
│ ↳ Per-client Connections (slow client never blocks another) │
|
||
│ │
|
||
│ Layer 5: Surface.ts + View.ts + Output.ts + render/ │
|
||
│ ↳ Double-buffered state (state vs pendingState) │
|
||
│ ↳ Damage tracking via pixman-WASM regions │
|
||
│ ↳ WebGL1 with FBOs for YUV→RGBA, RGB+Alpha→RGBA conversion │
|
||
│ ↳ desynchronized:true canvas for low-latency presentation │
|
||
│ ↳ Frame callbacks fired on requestAnimationFrame vsync │
|
||
│ │
|
||
│ Layer 6: XdgWmBase + XdgSurface + XdgToplevel + XdgPopup + ... │
|
||
│ ↳ Configure/ack-configure round-trips │
|
||
│ ↳ Popup positioner math (anchor + gravity + constraints) │
|
||
│ ↳ Subsurface sync/desync mode cascading │
|
||
│ │
|
||
│ Layer 7: desktop/Desktop.ts │
|
||
│ ↳ Floating vs AlwaysFullScreen mode │
|
||
│ ↳ View stacking, focus refcount │
|
||
│ │
|
||
│ Layer 8: UserShellApi (events + actions) │
|
||
│ ↳ Events: clientCreated, surfaceCreated, surfaceTitleUpdated, │
|
||
│ surfaceActivationUpdated, ... │
|
||
│ ↳ Actions: initScene, refreshScene, destroyScene, │
|
||
│ closeClient, activateSurface, setUserConfiguration │
|
||
│ ↳ THE INTEGRATION SEAM — Odoo 20 OWL shell hooks here │
|
||
└─────────────────────────────────────────────────────────────────────────┘
|
||
│
|
||
│ UserShellApi.events.* fired by compositor
|
||
│ UserShellApi.actions.* called by shell
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────────────┐
|
||
│ USER SHELL (Odoo 20 OWL addon — supplied by user) │
|
||
│ │
|
||
│ - Application launcher (lists apps from --applications JSON) │
|
||
│ - Window management UI (taskbar, decorations, minimize/maximize) │
|
||
│ - Keyboard layout picker (RMLVO) │
|
||
│ - Per-output canvas (HTMLCanvasElement, one per Output) │
|
||
│ - Trigger app launches via fetch('http://proxy:8081/<path>') │
|
||
│ - Open signalURL WebSocket to receive CONNECT_CHANNEL messages │
|
||
│ │
|
||
│ Not analyzed in this bundle (out of scope). │
|
||
└─────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## Reading guide for the rest of the bundle
|
||
|
||
Now that you've seen the layer map, the remaining chapters drill into each
|
||
layer in detail:
|
||
|
||
- `02-wayland-protocol.md` — Layers 1, 2, 3 (XML → generators → runtime)
|
||
- `03-html5-rendering.md` — Layer 5 (rendering pipeline)
|
||
- `04-transport.md` — Layer 4 (transport, both sides)
|
||
- `05-input-handling.md` — Layer 5 input subsystem (cross-cuts Layers 3, 4, 5)
|
||
- `06-data-clipboard.md` — Layer 5 data device (cross-cuts Layers 3, 4, 5)
|
||
- `07-native-app-bridge.md` — Layer 4 native + Layer 3 native + Layer 1 XML native (proxy-only `wl_drm`)
|
||
- `08-app-lifecycle.md` — Layer 6 + Layer 8 (UserShellApi seam)
|
||
- `09-hardware-accel.md` — Layer 5 encoding + Layer 4 frame channel + Layer 3 WASM
|
||
- `10-porting-to-odoo-js-python.md` — how to rebuild all 8 layers in your stack
|
||
- `11-pitfalls.md` — what Greenfield does wrong, layer by layer
|
||
- `12-appendix.md` — glossary, file index, external references
|