17 KiB
17 KiB
01 — Layered Architecture Map
This chapter walks the proposed MIT-licensed architecture layer by layer,
mirroring the Greenfield analysis structure (see ../greenfield-analysis/01-architecture.md)
so you can compare them side-by-side.
The eight layers (MIT approach)
┌──────────────────────────────────────────────────────────────────────────┐
│ Layer 8: USER SHELL (Odoo 20 OWL addon, PROPRIETARY) │
│ Hooks: UserShellApi (events + actions) — clean-room spec │
│ See 10-odoo-integration.md │
├──────────────────────────────────────────────────────────────────────────┤
│ Layer 7: DESKTOP / LAYOUT POLICY (PROPRIETARY TS, clean-room) │
│ Floating vs AlwaysFullScreen, view stacking, focus activation refcount │
│ Custom: desktop/Desktop.ts (~800 LOC) │
├──────────────────────────────────────────────────────────────────────────┤
│ Layer 6: SURFACE LIFECYCLE / ROLES (PROPRIETARY TS, clean-room) │
│ XdgWmBase, XdgSurface, XdgToplevel, XdgPopup, XdgPositioner, Shell, │
│ ShellSurface, Subcompositor, Subsurface, SurfaceRole │
│ Custom: roles/*.ts (~1,800 LOC) │
├──────────────────────────────────────────────────────────────────────────┤
│ Layer 5: RENDERING PIPELINE (PROPRIETARY TS, clean-room, modernized) │
│ Surface (state stack), View, Output (WebGL2 canvas), Renderer, Scene, │
│ Shaders (YUV→RGBA, YUVA→RGBA, RGB+A→RGBA), Loop (RAF), Callback │
│ NEW: OffscreenCanvas + Web Workers + WebGL2 (Greenfield uses WebGL1) │
│ Custom: render/*.ts (~1,800 LOC), surface/*.ts (~1,200 LOC) │
├──────────────────────────────────────────────────────────────────────────┤
│ Layer 4: TRANSPORT (browser side, PROPRIETARY TS, clean-room) │
│ Channel (ARQ + Simple), ReconnectingWebSocket, RemoteAppLauncher, │
│ StreamingBuffer, BufferStream, EncodedFrame, EncoderFeedback, decoders │
│ NEW: WebCodecs VideoDecoder replaces FFmpeg WASM (was GPL) │
│ Custom: transport/*.ts (~3,500 LOC), decoders/*.ts (~500 LOC) │
├──────────────────────────────────────────────────────────────────────────┤
│ Layer 4: TRANSPORT (Python side, PROPRIETARY, clean-room) │
│ aiohttp WebSocket server (9 endpoints), asyncio + cffi shim, │
│ kcp PyPI package, multiprocessing.Process per session, JWT auth │
│ Custom: proxy/*.py (~2,880 LOC) │
├──────────────────────────────────────────────────────────────────────────┤
│ Layer 3: WAYLAND RUNTIME (browser side) │
│ REUSED: @gfld/common (Connection, WlObject, Fixed, marshallers) — MIT │
│ REUSED: @gfld/compositor-protocol (Display, Client, Resource, Global) │
│ — MIT, verified NO @gfld/compositor import │
├──────────────────────────────────────────────────────────────────────────┤
│ Layer 3: WAYLAND RUNTIME (Python side) │
│ REUSED: pywayland (Apache-2.0) — Display, Client, Resource, Global │
│ + 5-line cffi shim for wl_event_loop_get_fd (asyncio integration) │
│ + Greenfield's changes.diff (MIT patches) applied to libwayland-server │
├──────────────────────────────────────────────────────────────────────────┤
│ Layer 2: PROTOCOL GENERATORS (REUSED) │
│ REUSED: @gfld/client-generator, @gfld/compositor-generator (MIT) │
│ REUSED: pywayland-protocols (auto-generated from wayland-protocols repo) │
├──────────────────────────────────────────────────────────────────────────┤
│ Layer 1: WAYLAND PROTOCOL XML (REUSED) │
│ REUSED: wayland.xml, xdg-shell.xml, wayland-drm.xml (upstream MIT) │
│ REUSED: web-bitmapbuf-unstable-v1.xml (Greenfield's custom extension) │
└──────────────────────────────────────────────────────────────────────────┘
Side-by-side comparison with Greenfield
| Layer | Greenfield | MIT approach |
|---|---|---|
| Layer 8: User Shell | @gfld/compositor-shell (AGPL, Preact) |
Custom Odoo 20 OWL addon (proprietary) |
| Layer 7: Desktop policy | desktop/Desktop.ts (AGPL) |
Clean-room rewrite (~800 LOC TS) |
| Layer 6: Surface roles | Xdg*.ts, Shell*.ts, Sub*.ts (AGPL) |
Clean-room rewrite (~1,800 LOC TS) |
| Layer 5: Rendering | render/*.ts, Surface.ts, etc. (AGPL, WebGL1, main-thread) |
Clean-room rewrite (~3,000 LOC TS, WebGL2 + OffscreenCanvas + Workers) |
| Layer 4: Transport (browser) | remote/*.ts (AGPL) + FFmpeg WASM (GPL) |
Clean-room rewrite (~3,500 LOC TS) + WebCodecs (W3C standard) |
| Layer 4: Transport (host) | compositor-proxy/src/*.ts (AGPL) + 3 N-API addons |
Clean-room Python rewrite (~2,880 LOC Python, NO native addons) |
| Layer 3: Wayland runtime (browser) | @gfld/common + @gfld/compositor-protocol (MIT) |
REUSED AS-IS |
| Layer 3: Wayland runtime (host) | wayland-server-addon.node (AGPL, 1231 lines C, patched libwayland) |
REPLACED with pywayland (Apache-2.0) + 5-line cffi shim + Greenfield's changes.diff (MIT patches) |
| Layer 2: Generators | @gfld/*-generator (MIT) |
REUSED AS-IS + pywayland-protocols (auto-generated) |
| Layer 1: Protocol XML | protocol/*.xml (upstream MIT + Greenfield's web-bitmapbuf) |
REUSED AS-IS |
How data flows across the layers
NATIVE APP (e.g. gtk4-demo, X11 via XWayland)
│
│ libwayland-client → UNIX socket + SCM_RIGHTS FDs
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ PYTHON PROXY (aiohttp + pywayland + GStreamer + bubblewrap) │
│ │
│ Layer 3 (Python): pywayland + 5-line cffi shim │
│ ↳ VANILLA libwayland-server (NO patches needed for basic use) │
│ ↳ Apply Greenfield's changes.diff (MIT) IF we want wire-message │
│ interception hooks (recommended for the fast-sync optimization) │
│ ↳ Per-resource dispatchers (auto-generated by pywayland scanner) │
│ ↳ FDs wrapped as ProxyFD JSON │
│ │
│ Layer 4 (Python): aiohttp WebSocket + kcp PyPI package │
│ ↳ 9 endpoints: /signal, /channel, /mkfifo, /mkstemp-mmap, │
│ /read-fd, /read-fd-as-stream, /write-fd-as-stream, │
│ /close-fd, /request-keyframe │
│ ↳ asyncio integrates libwayland FD via loop.add_reader() │
│ ↳ multiprocessing.Process per Odoo session (fork) │
│ ↳ JWT validation (pyjwt) │
│ │
│ Layer 5 (encoding): PyGObject + GStreamer │
│ ↳ gst_parse_launch: same pipeline strings as Greenfield │
│ ↳ x264enc REPLACED with vaapih264enc / nvh264enc / openh264enc │
│ (x264enc is GPL — see 11-pitfalls.md) │
│ ↳ Zero-copy dmabuf via EGLImage → GstGLMemoryEGL (same as Greenfield)│
│ ↳ GLib MainLoop in separate Python thread │
│ ↳ asyncio.run_coroutine_threadsafe for cross-thread marshalling │
│ │
│ Layer 7 (sandboxing): bubblewrap + seccomp + systemd-run --scope │
│ ↳ Per-app: MemoryMax=1G, CPUQuota=200%, TasksMax=256 │
│ ↳ --unshare-all, --clearenv, --ro-bind /usr /usr │
│ ↳ --dev-bind /dev/dri/renderD128 /dev/dri/renderD128 (single GPU) │
│ ↳ --ro-bind $HOST_XDG/wayland-N /run/user/<UID>/wayland-N │
│ ↳ --uid <100000-165535 per-app> --gid <same> │
│ ↳ --unshare-net by default │
│ │
│ Layer 1 (XML) → Layer 2 (pywayland scanner) feeds Layer 3 dispatchers │
└─────────────────────────────────────────────────────────────────────────┘
│
│ WebSocket (TCP) + KCP-over-WS, JWT auth
│ Same-origin via Nginx reverse proxy
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ BROWSER COMPOSITOR (Custom TypeScript + WebGL2 + WebCodecs + WASM) │
│ │
│ Layer 4 (browser): Custom Channel + KCP + ReconnectingWebSocket │
│ ↳ ARQChannel + SimpleChannel (clean-room, ~250 LOC) │
│ ↳ KCP ported fresh from MIT C reference (skywind3000/kcp) │
│ ↳ ReconnectingWebSocket (vendored from pladaria, MIT) │
│ ↳ WebCodecs VideoDecoder (primary, hardware-accelerated) │
│ ↳ OpenH264 BSD Wasm (fallback, NOT FFmpeg — FFmpeg is GPL) │
│ │
│ Layer 3 (browser): REUSED @gfld/common + @gfld/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: Custom Surface.ts + View.ts + Output.ts + render/ │
│ ↳ Double-buffered state (state vs pendingState) │
│ ↳ Damage tracking via @gfld/compositor-wasm pixman regions │
│ ↳ WebGL2 with FBOs for YUV→RGBA, RGB+Alpha→RGBA conversion │
│ ↳ OffscreenCanvas in Web Worker (NEW vs Greenfield) │
│ ↳ Frame callbacks fired on requestAnimationFrame vsync │
│ │
│ Layer 6: Custom XdgWmBase + XdgSurface + XdgToplevel + XdgPopup + ... │
│ ↳ Configure/ack-configure round-trips │
│ ↳ Popup positioner math (anchor + gravity + constraints) │
│ ↳ Subsurface sync/desync mode cascading │
│ ↳ Primary selection (NEW — Greenfield lacks this) │
│ │
│ Layer 7: Custom desktop/Desktop.ts │
│ ↳ Floating vs AlwaysFullScreen mode │
│ ↳ View stacking, focus refcount │
│ ↳ Adaptive bitrate (NEW — consumes EncoderFeedback data) │
│ │
│ Layer 8: UserShellApi (clean-room spec) + Odoo 20 OWL shell │
│ ↳ Events: clientCreated, surfaceCreated, surfaceTitleUpdated, │
│ surfaceActivationUpdated, ... │
│ ↳ Actions: initScene, refreshScene, destroyScene, │
│ closeClient, activateSurface, setUserConfiguration │
│ ↳ OWL shell wires events to reactive state │
└─────────────────────────────────────────────────────────────────────────┘
│
│ userShell.events.* fired by compositor
│ userShell.actions.* called by OWL shell
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ USER SHELL (Odoo 20 OWL addon — PROPRIETARY) │
│ │
│ - ir.actions.client with target=fullscreen │
│ - Application launcher (lists apps from Odoo /web/compositor/apps) │
│ - Window management UI (taskbar, decorations, minimize/maximize) │
│ - Keyboard layout picker (RMLVO) │
│ - Per-output <canvas> element (OWL component owns the lifecycle) │
│ - Triggers app launches via /web/compositor/launch │
│ - Receives JWT from /web/compositor/token │
│ │
│ Not a Greenfield component — entirely your code. │
└─────────────────────────────────────────────────────────────────────────┘
Reading guide for the rest of the bundle
02-wayland-protocol.md— Layer 1, 2, 3 (XML → generators → runtime, both sides)03-html5-rendering.md— Layer 5 (custom WebGL2 + Workers + OffscreenCanvas)04-transport.md— Layer 4 (Python aiohttp + kcp, browser side clean-room)05-input-handling.md— Layer 5 input subsystem (Python xkbcommon, browser side)06-data-clipboard.md— Layer 5 data device (with primary selection, Greenfield lacks)07-native-app-bridge.md— Layer 4 Python + Layer 3 Python + sandboxing08-app-lifecycle.md— Layer 6 + Layer 8 (UserShellApi clean-room spec)09-hardware-accel.md— Layer 5 encoding + Layer 4 frame channel + WebCodecs10-odoo-integration.md— Layer 8 + Odoo 20 backend addon + JWT + Nginx + Docker11-pitfalls.md— 3 GPL contaminants found + clean-room legal requirements12-appendix.md— glossary, file index, references