Initial commit: Greenfield architectural analysis (14 files, ~80 pages, 10 parallel research agents)
This commit is contained in:
commit
c4b45cf218
15 files changed
+5444
No files matched your search
@@ -0,0 +1,443 @@
|
||||
# 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
|
||||
Reference in new issue
Block a user