🎉🎉🎉 Phase 6.4 — Wayland complet : un client externe affiche ses pixels

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
This commit is contained in:
Votre Nom 2026-05-09 13:30:05 +02:00
parent 509aae7769
commit 8a897d975d
8 changed files with 1098 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

View file

@ -0,0 +1,171 @@
# 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 : ![](phase6-4-wayland-client-surface.png) — 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.*