redox-wayland-compositor/docs/phase6-4-wayland-frontend.md
Votre Nom 8a897d975d 🎉🎉🎉 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
2026-05-09 13:30:05 +02:00

171 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.*