195 lines
17 KiB
Markdown
195 lines
17 KiB
Markdown
# 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 + sandboxing
|
|
- `08-app-lifecycle.md` — Layer 6 + Layer 8 (UserShellApi clean-room spec)
|
|
- `09-hardware-accel.md` — Layer 5 encoding + Layer 4 frame channel + WebCodecs
|
|
- `10-odoo-integration.md` — Layer 8 + Odoo 20 backend addon + JWT + Nginx + Docker
|
|
- `11-pitfalls.md` — 3 GPL contaminants found + clean-room legal requirements
|
|
- `12-appendix.md` — glossary, file index, references
|