Capture preuve : docs/phase6-4-wayland-client-surface.png — pattern ARGB 320x240 écrit par un binaire client Wayland externe affiché par notre compositor sur le framebuffer Redox dans QEMU. Crates ajoutés : redox-wl-wayland-frontend (lib, ~430 lignes) : - WaylandFrontend struct avec SurfaceRegistry intégré + Display<Self> + ListeningSocket - bind_absolute(path), accept_pending_clients(), dispatch_clients(), flush_clients(), notify_frame_done() - ShmPool : mmap + munmap on drop - BufferData : Arc<Mutex<ShmPool>> + offset/w/h/stride/format - SurfaceData : Arc<...> qui contient SurfaceId + pending_buffer + pending_frame_callbacks - Dispatch impls : wl_compositor v5, wl_shm v1 (advertise ARGB+XRGB), wl_shm_pool, wl_buffer, wl_surface (attach/damage/commit/frame/destroy), wl_callback, wl_region (no-op) Sémantique commit : copy-on-commit (lit pixels via mmap, copie dans SurfaceBuffer owned). Plus simple que de garder le mmap vivant. Au commit, raise auto la surface (politique simple). redox-wl-compositor (bin, ~150 lignes) : - ouvre RedoxOutput + InputBackend partagé - bind WaylandFrontend sur /tmp/redox-wl-comp.sock - export WAYLAND_DISPLAY env var - boucle main 30 fps : accept clients → dispatch → input → render → notify_frame_done → flush - Esc = exit propre redox-wl-test-client-shm (bin, ~170 lignes) : - attente du socket compositor (50 retries × 100ms) - Connection::from_backend après UnixStream::connect - Dispatch handlers minimal pour wl_registry, compositor, shm, pool, buffer, surface - shm_open + ftruncate + mmap + pattern ARGB déterministe (orange + bandes diagonales) - shm.create_pool(fd) + pool.create_buffer + compositor.create_surface - surface.attach + damage_buffer + commit - reste connecté 25s pour qu'on capture l'écran Validation runtime : compositor en init VT=2, client lancé en parallèle via 30_console. Logs serial montrent toute la séquence : [client] globals : compositor=true shm=true [client] shm créé, peint 320x240 ARGB [client] surface attach + damage + commit envoyés [comp] tick=30 surfaces=1 elapsed=1.2s [comp] tick=510 surfaces=1 elapsed=20.7s ← surface persiste 20+s PNG capturée à T+12s montre la surface du client visible sur le framebuffer. Position (0,0) parce que xdg-shell absent (placement absent). Reportable phase 7. Image Redox restaurée à boot Orbital normal. docs/phase6-4-wayland-frontend.md : compte-rendu complet, archi, sémantique commit, limitations, plan phase 7. Phase 6 entièrement close. Le compositor naissant fonctionne avec un vrai client Wayland externe sur Redox. Leyoda 2026 – GPLv3
171 lines
7.9 KiB
Markdown
171 lines
7.9 KiB
Markdown
# Phase 6.4 — Frontend Wayland : un client externe affiche ses pixels
|
||
|
||
> Document produit le 2026-05-09 dans le cadre du plan directeur
|
||
> `REDOX_COSMIC_XWAYLAND_RS_PLAN.md`.
|
||
>
|
||
> **Scope** : un client Wayland externe (process séparé, code Rust pur
|
||
> via wayland-rs) se connecte au compositor binaire, lui envoie un
|
||
> buffer shm peint, et le compositor l'affiche.
|
||
|
||
## Verdict
|
||
|
||
**✅ Pipeline Wayland complet sur Redox, end-to-end, par-dessus
|
||
notre stack `compositor-core` + `redox-wl-display` + `redox-wl-input`.**
|
||
|
||
Capture preuve :  — le pattern
|
||
ARGB 320x240 visible dans le coin haut-gauche est écrit par le client
|
||
externe et affiché par le compositor sur le display Redox.
|
||
|
||
## Architecture finale 6.4
|
||
|
||
```
|
||
┌──────────────────────────────────────┐ ┌─────────────────────────────┐
|
||
│ redox-wl-compositor (bin) │ │ redox-wl-test-client-shm │
|
||
│ │ │ (bin séparé) │
|
||
│ ┌────────────────┐ │ │ │
|
||
│ │ RedoxOutput │ │ │ - shm_open + pattern ARGB │
|
||
│ └────┬───────────┘ │ │ - wl_compositor │
|
||
│ │ Arc<ConsumerHandle> │ │ - wl_shm │
|
||
│ ▼ │ │ - wl_shm_pool.create │
|
||
│ ┌────────────────┐ │ │ - wl_buffer │
|
||
│ │ InputBackend │ │ │ - wl_surface.attach │
|
||
│ └────────────────┘ │ │ - wl_surface.commit │
|
||
│ │ │ │
|
||
│ ┌────────────────┐ │ └─────────────┬───────────────┘
|
||
│ │WaylandFrontend │ │ │ Unix socket
|
||
│ │ - registry │ ◄────────────────┼──────────────────┘ + SCM_RIGHTS
|
||
│ │ (compositor- │ │
|
||
│ │ core) │ │
|
||
│ │ - Display<Self>│ │
|
||
│ │ - Listener │ │
|
||
│ └────────┬───────┘ │
|
||
│ │ compose_into │
|
||
│ ▼ │
|
||
│ framebuffer Redox → écran │
|
||
└──────────────────────────────────────┘
|
||
```
|
||
|
||
## Globaux exposés
|
||
|
||
| Global | Version | Comportement |
|
||
|---|---|---|
|
||
| `wl_compositor` | 5 | `create_surface` → `registry.create()` ; `create_region` no-op |
|
||
| `wl_shm` | 1 | advertise `Argb8888` + `Xrgb8888` ; `create_pool(fd, size)` → mmap immédiat |
|
||
| `wl_shm_pool` | — | `create_buffer` → `BufferData` (offset/w/h/stride/format) ; `resize` no-op (TODO) |
|
||
| `wl_buffer` | — | (pas de request à traiter, juste destroy implicite) |
|
||
| `wl_surface` | 5 | `attach`, `damage`/`damage_buffer` (no-op tracking 6.4), `commit`, `frame`, `destroy` |
|
||
| `wl_callback` | — | utilisé pour `wl_surface.frame` ; `done` envoyé par `notify_frame_done` |
|
||
| `wl_region` | — | no-op (pas d'input region utilisée) |
|
||
|
||
## Sémantique commit Wayland implémentée
|
||
|
||
Quand un client envoie `wl_surface.commit` :
|
||
|
||
1. Récupération du `BufferData` attaché en pending (via `wl_surface.attach`)
|
||
2. Lecture des pixels du shm via `mmap` côté serveur
|
||
3. Création d'un `SurfaceBuffer` (Arc<Vec<u32>>) côté `compositor-core`
|
||
4. `registry.modify_pending(id, |s| s.buffer = Some(...))`
|
||
5. `registry.commit(id)` — pending → current
|
||
6. `registry.raise(id)` — politique simple : dernière surface commitée passe au top
|
||
7. Frame callbacks pending → queue globale, traité au `notify_frame_done` après le prochain present
|
||
|
||
**Approche "copy on commit"** : on copie les pixels du shm vers un Vec<u32>
|
||
owned. Plus simple que de garder une référence vivante au mmap qui peut
|
||
être unmappé par le client à tout moment. Coût ≈ 320×240×4 = 300 KiB par
|
||
commit pour notre client de test, négligeable.
|
||
|
||
## Validation runtime
|
||
|
||
Configuration :
|
||
```toml
|
||
# init.d/20_orbital → nowait VT=2 redox-wl-compositor
|
||
# init.d/30_console → nowait redox-wl-test-client-shm
|
||
```
|
||
|
||
Le compositor démarre, expose le socket `/tmp/redox-wl-comp.sock`, le
|
||
client a une boucle de connexion qui retry 50× × 100 ms et finit par
|
||
se connecter quand le socket apparaît.
|
||
|
||
Logs capturés via /scheme/debug → serial QEMU stdio :
|
||
|
||
```
|
||
[comp] Phase 6.4 — compositor Wayland démarrage
|
||
[comp] display 1280x800, VT=3
|
||
[comp] CRTC pris
|
||
[comp] Wayland socket : /tmp/redox-wl-comp.sock
|
||
[client] connect to compositor
|
||
[client] globals : compositor=true shm=true
|
||
[client] shm créé, peint 320x240 ARGB
|
||
[client] surface attach + damage + commit envoyés
|
||
[comp] tick=30 surfaces=1 elapsed=1.2s
|
||
...
|
||
[comp] tick=510 surfaces=1 elapsed=20.7s ← surface persiste 20s
|
||
```
|
||
|
||
## Limitations / hors scope
|
||
|
||
### Pas de placement (xdg-shell absent)
|
||
La surface du client est affichée à `(0, 0)` parce qu'aucun protocole
|
||
de placement n'est implémenté. Pour des fenêtres positionnables /
|
||
redimensionnables, il faudra `xdg_wm_base` + `xdg_toplevel` (phase 7
|
||
ou plus tard).
|
||
|
||
### Pas de damage tracking effectif
|
||
`wl_surface.damage` et `damage_buffer` sont reçus mais ignorés. Chaque
|
||
frame recompose tout. Pour 1-3 surfaces de petite taille c'est
|
||
imperceptible ; à optimiser quand on aura beaucoup de surfaces.
|
||
|
||
### Pas de wl_buffer.release explicite
|
||
Le `release` Wayland indique au client qu'il peut réutiliser un buffer.
|
||
Notre approche copy-on-commit rend ce protocole inutile (on n'a plus
|
||
besoin du shm après commit). Mais des clients sophistiqués pourraient
|
||
attendre `release` avant d'écrire à nouveau — à vérifier au cas par cas.
|
||
|
||
### Pas de seat / input vers les clients
|
||
`wl_seat`, `wl_keyboard`, `wl_pointer` ne sont pas exposés. Les events
|
||
input sont seulement consommés côté compositor (pour Esc=quit). Phase
|
||
7 ajoutera la propagation des events vers la surface focalisée.
|
||
|
||
### Pas de subcompositor
|
||
`wl_subcompositor` non exposé. Pas critique pour des clients simples.
|
||
|
||
## Code source
|
||
|
||
```
|
||
crates/redox-wl-wayland-frontend/ # lib (~430 lignes)
|
||
├── Cargo.toml
|
||
└── src/lib.rs # WaylandFrontend, Dispatch impls
|
||
|
||
crates/redox-wl-compositor/ # bin (~150 lignes)
|
||
├── Cargo.toml
|
||
└── src/main.rs # boucle main display+input+frontend
|
||
|
||
crates/redox-wl-test-client-shm/ # bin (~170 lignes)
|
||
├── Cargo.toml
|
||
└── src/main.rs # client wayland-rs qui peint + commit
|
||
```
|
||
|
||
## Suite phase 7
|
||
|
||
Si on veut un compositor utilisable au quotidien, il manque (par
|
||
ordre de priorité) :
|
||
|
||
1. **xdg-shell** (`xdg_wm_base` + `xdg_toplevel` + `xdg_surface`) →
|
||
placement, redimensionnement, fermeture propre, titres de fenêtres
|
||
2. **wl_seat + wl_keyboard + wl_pointer** → propager les events input
|
||
vers la surface focalisée. Décision XKB à prendre (porter
|
||
libxkbcommon / impl Rust pur / strings minimales).
|
||
3. **Curseur software** → afficher le pointeur souris à l'écran (le
|
||
compositor en a déjà la position via InputBackend, mais ne le
|
||
dessine pas)
|
||
4. **Gestion focus + raise on click** → utiliser hit_test +
|
||
wl_seat.keyboard_enter/leave events
|
||
5. **Damage tracking effectif** → réduire le coût de composition
|
||
6. **Clipboard** → wl_data_device_manager
|
||
7. **Multiple clients simultanés**, fermeture propre, recover sur crash client
|
||
|
||
Estimé phase 7 complète : 5-8 sessions.
|
||
|
||
---
|
||
|
||
*Fin du document de phase 6.4.*
|