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
7.9 KiB
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 :
- Récupération du
BufferDataattaché en pending (viawl_surface.attach) - Lecture des pixels du shm via
mmapcôté serveur - Création d'un
SurfaceBuffer(Arc<Vec>) côtécompositor-core registry.modify_pending(id, |s| s.buffer = Some(...))registry.commit(id)— pending → currentregistry.raise(id)— politique simple : dernière surface commitée passe au top- Frame callbacks pending → queue globale, traité au
notify_frame_doneaprès le prochain present
Approche "copy on commit" : on copie les pixels du shm vers un Vec 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 :
# 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é) :
- xdg-shell (
xdg_wm_base+xdg_toplevel+xdg_surface) → placement, redimensionnement, fermeture propre, titres de fenêtres - 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).
- Curseur software → afficher le pointeur souris à l'écran (le compositor en a déjà la position via InputBackend, mais ne le dessine pas)
- Gestion focus + raise on click → utiliser hit_test + wl_seat.keyboard_enter/leave events
- Damage tracking effectif → réduire le coût de composition
- Clipboard → wl_data_device_manager
- 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.