redox-wayland-compositor/docs/phase13-2-output-subcompositor.md
Votre Nom c8b2cb5b61 Phase 13.2 — doc récapitulative
Document de synthèse pour la phase 13.2 (wl_output v3 +
wl_subcompositor/wl_subsurface). Couvre :

- Verdict global et les 5 sprints livrés (a, b.1, b.2, b.3 + fix, b.4)
- Détails de chaque sprint avec commits référencés
- Stratégie de tests hybride natif+runtime (innovation 13.2)
- 6 limitations connues documentées pour traitement futur
- Liste complète des fichiers livrés
- Critère de fin et étapes suivantes possibles (13.3 backend extraction,
  13.2.c retry sctk, durcissement)

Format aligné sur phase13-1-b-observations.md et phase13-1-c-cursor.md
pour cohérence audit trail.

Leyoda 2026 – GPLv3
2026-05-16 14:39:52 +02:00

262 lines
12 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 13.2 — `wl_output` v3 + `wl_subcompositor`/`wl_subsurface`
> Document produit le 2026-05-16, suite de
> [`phase13-1-c-cursor.md`](phase13-1-c-cursor.md).
>
> **Scope** : combler 2 dettes protocolaires bloquantes pour les
> toolkits clients tiers modernes :
>
> - **`wl_output` complet** — annonce le single display avec geometry/
> mode/scale/done, en respectant le gating par version (v1/v2/v3).
> Plusieurs toolkits clients (sctk, GTK, Qt) considèrent ce global
> implicitement requis — son absence en 13.1.b était une bombe à
> retardement.
> - **`wl_subcompositor` + `wl_subsurface`** — supporte les surfaces
> parent-enfant et les rend visuellement à `parent_pos + offset`.
> Pré-requis pour tout client multi-surface (popups, badges, splash
> screens, decorations CSD, etc.).
>
> **Stratégie de validation hybride** : pour chaque sprint, un test
> natif (cargo test, sans QEMU) valide la couche protocolaire ; un
> test runtime visuel valide le rendu effectif. Le coût de QEMU
> est ainsi réduit aux validations vraiment visuelles.
>
> **Verdict** : ✅ **13.2 entièrement validée** — 6 commits, 5
> sprints, 2 nouveaux clients de test, 3 scripts ion shortcuts.
## Sous-phases livrées
### 13.2.a — `wl_output` v3 complet ([commit `7413745`](../crates/redox-wl-wayland-frontend/src/lib.rs))
Avant : `wl_output` n'était PAS déclaré comme global. Les clients qui
le bind-eraient implicitement (sctk, GTK) se retrouvaient sans output.
Après :
- Global `wl_output` à `OUTPUT_VERSION = 3`
- `GlobalDispatch::bind` envoie au bind, dans l'ordre :
1. `geometry(0, 0, 0_mm, 0_mm, Subpixel::Unknown, "Redox", "redox-wl-output:0", Transform::Normal)`
2. `mode(CURRENT | PREFERRED, screen_w, screen_h, 60_000_mHz)`
3. `scale(1)`**uniquement si version bound ≥ 3** (gating conservateur)
4. `done()`**uniquement si version bound ≥ 2**
- `Dispatch::request` accepte `Release` (v3+) en no-op
Taille pixel issue de `screen_w / screen_h` qui sont settés par
`set_screen_size()` (introduit en 13.1.b pour clamp curseur). Refresh
hardcodé à 60 Hz : notre boucle main tourne à ~30 fps mais les apps
modernes ignorent largement ce champ.
**Test natif** (`crates/redox-wl-test-wl-output/tests/gating_native.rs`) :
serveur + client en-process via `UnixStream::pair()`, 11 assertions
vérifient v1 (geometry+mode), v2 (+done), v3 (+scale). PASS le 2026-05-16.
**Test runtime** (`redox-wl-test-wl-output`, runnable via `test-out`) :
bind aux 3 versions et reporte PASS/FAIL côté serial. Confirme le
gating sur la vraie cible Redox.
### 13.2.b.1 — `wl_subcompositor` protocole ([commit `f9c3de1`](../crates/redox-wl-wayland-frontend/src/lib.rs))
Implémentation protocole uniquement (sans rendering, repoussé à .b.2) :
- Global `wl_subcompositor` à `SUBCOMPOSITOR_VERSION = 1`
- `GlobalDispatch::bind` no-op (resource init)
- `Dispatch<WlSubcompositor>::request` :
- `Destroy` : no-op
- `GetSubsurface { id, surface, parent }` : crée un `Arc<SubsurfaceData>`
avec parent ref, child ref, position pending `(0,0)`, sync mode `true`
- `Dispatch<WlSubsurface>::request` :
- `SetPosition { x, y }` : update SubsurfaceData.position
- `PlaceAbove`, `PlaceBelow` : no-op (single-subsurface use-case)
- `SetSync`, `SetDesync` : update SubsurfaceData.sync
- `Destroy` : no-op (resource cleanup par wayland-server)
**SubsurfaceData** est une nouvelle struct portée par la wl_subsurface
resource via `data_init.init(id, Arc<SubsurfaceData>)`.
**Test natif** (`subcompositor_native.rs`) : cycle complet bind →
get_subsurface → set_position → set_sync/desync → destroy. Aucune
erreur protocole détectée.
### 13.2.b.2 — Rendering subsurface ([commit `bba2d7b`](../crates/redox-wl-wayland-frontend/src/lib.rs))
Wire la SubsurfaceData de .b.1 au pipeline de composition :
1. Nouveau champ `subsurface_link: Mutex<Option<Arc<SubsurfaceData>>>`
sur `SurfaceData`
2. À `GetSubsurface`, on link la SubsurfaceData à la child SurfaceData
3. Au commit du child wl_surface :
- Si `subsurface_link` est Some : récupère le parent SurfaceId via
`sub_data.parent.data::<Arc<SurfaceData>>()`
- Lookup la position actuelle du parent dans le registry
- `modify_pending(child_id, |s| s.x = px + ox; s.y = py + oy)` avant
le `commit(id)` qui applique pending → current
- Skip `raise()` et `set_focus()` pour les subsurfaces (elles ne
grabbent pas le focus indépendamment, et leur z-order est géré
comme cascade au-dessus du parent)
`compose_into` (côté `redox-wl-compositor-core`) n'est PAS modifié — il
itère le z_order et dessine chaque surface à son `(x, y)` absolu. Notre
prep au commit assure que le child est à la bonne position.
### 13.2.b.3 — Client de test visuel ([commit `1dab6ff`](../crates/redox-wl-test-client-subcompositor/))
Nouveau crate `redox-wl-test-client-subcompositor` qui :
1. Connecte au socket `/tmp/redox-wl-comp.sock`
2. Bind 5 globals : `wl_compositor`, `wl_shm`, `xdg_wm_base`, `wl_seat`,
`wl_subcompositor`
3. Crée un parent xdg_toplevel 300×200 bleu nuit avec bordure noire 2px,
ack_configure puis attach + commit buffer ARGB
4. Crée une child wl_surface, `get_subsurface(child, parent)`,
`set_position(50, 50)`, `set_desync`, attach + commit buffer 60×60
rouge avec bordure noire
5. ESC fermes le client proprement (le path keyboard étant routé au
parent toplevel qui a le focus auto-grant)
Reprend l'event-loop tolérant Interrupted/BrokenPipe de la phase 13.1.b.
### Fix bug visuel ([commit `dfb5c66`](../crates/redox-wl-wayland-frontend/src/lib.rs))
Détecté lors du runtime QEMU 13.2.b.3. Symptôme : le carré rouge est
calculé à la bonne position au commit du child, mais reste invisible.
Cause : `parent_surface.commit()` final dans le client (refresh post-
subsurface) appelle `raise(parent_id)` dans le compositor, plaçant le
parent **au-dessus** du child dans le z_order. Le parent overdraw le
child à compose_into.
Z-order observé : `[child, parent]` au lieu de `[parent, child]`.
**Fix** : après `raise(parent_id)`, scanner `surfaces_by_id` pour
trouver toutes les subsurfaces dont `subsurface_link.parent` pointe
vers cette SurfaceId, et les `raise()` juste après. Le z-order devient
`[parent, child]` (avec child raised en dernier = au-dessus).
Coût O(N) par raise — N est typiquement petit (1-10 toplevels). À
durcir avec un index parent_id → Vec<child_id> si N explose.
### 13.2.b.4 — Scripts ion shortcuts ([commit `6fe3214`](../tools/redox-scripts/))
UX : pour éviter de retaper 3 lignes à chaque test runtime dans la
fenêtre QEMU graphique (où le copier-coller depuis CachyOS n'est pas
disponible), 3 scripts ion prêts à l'emploi déployés dans `/usr/bin` :
- **`test-sw`** : compositor + simple_window upstream (phase 13.1.b)
- **`test-subc`** : compositor + subcompositor visuel (phase 13.2.b.3)
- **`test-out`** : compositor + wl_output gating test (phase 13.2.a)
Tous suivent le même pattern :
```ion
#!/usr/bin/ion
rm -f /tmp/redox-wl-comp.sock
redox-wl-compositor &
sleep 1
<client>
```
run-qemu.sh copie maintenant `tools/redox-scripts/*` dans `/usr/bin` de
l'image (avec `cp -p` pour préserver le bit exécutable).
## Stratégie de tests : hybride natif + runtime
Innovation méthodologique de cette phase. Pour chaque sprint :
- **Test natif** (cargo test, sur l'hôte CachyOS, sans QEMU) :
- Spawn un thread serveur avec wayland-server + la logique de gating
identique au prod code
- Connecte un client wayland-client via `UnixStream::pair()`
- Exercise toute la séquence Wayland
- Asserts précis (versions, events reçus, ordre, etc.)
- Tourne en < 6 secondes
- **Test runtime** (QEMU, visuel) :
- Confirme le rendu effectif sur le framebuffer Redox
- Confirme l'interaction utilisateur (clic, ESC, focus, etc.)
- Validation finale "tout le pipeline marche"
Gain : on n'a QEMU dans la boucle que pour ce qui est vraiment visuel.
La couche protocolaire est testée en CI-friendly time.
## Limitations connues
À traiter en phase ultérieure si besoin :
- **Parent move propagation aux enfants** : si on drag un parent
toplevel (phase 7.7), ses subsurfaces ne suivent pas tant que le
client ne re-commit pas l'enfant. Pour des use-cases statiques
c'est OK ; pour Wayland conforme strict il faudra walker les enfants
au move du parent.
- **wl_subsurface.Destroy ne unmap pas le child** : spec violation
mineure. Si le client veut vraiment retirer la subsurface, il doit
aussi destroy son wl_surface enfant.
- **Pas de cascade sync** : un commit du parent en mode sync devrait
flusher les pending states des subsurfaces sync. Pas implémenté.
Notre client de test utilise `set_desync` donc le path n'est pas
exercé.
- **Pas de role-tracking** : spec exige `bad_surface` error si une
wl_surface a déjà un rôle (xdg_toplevel) et qu'on appelle
GetSubsurface dessus. Pour l'instant on log debug seulement.
- **PlaceAbove/PlaceBelow no-op** : z-order relatif entre subsurfaces
siblings non géré. Pas observé en pratique avec nos clients de test
single-subsurface.
- **Raise children scan O(N)** : naïf. Avec un index parent_id
Vec<child_id> on passe O(1). À faire si N de surfaces visibles
devient grand (> 50 ?).
## Fichiers livrés en 13.2
```
# Code compositor (frontend Wayland)
crates/redox-wl-wayland-frontend/src/lib.rs # globals + Dispatch + commit hook
# Clients de test
crates/redox-wl-test-wl-output/ # validation gating wl_output (13.2.a)
├── src/main.rs # runtime client
└── tests/
├── gating_native.rs # test natif v1/v2/v3
└── subcompositor_native.rs # test natif protocole
crates/redox-wl-test-client-subcompositor/ # validation visuelle (13.2.b.3)
└── src/main.rs
# Tooling runtime
tools/redox-scripts/test-sw # shortcut simple_window
tools/redox-scripts/test-subc # shortcut subcompositor
tools/redox-scripts/test-out # shortcut output gating
# Build/deploy
run-qemu.sh # copie binaires + scripts
# Doc
docs/phase13-2-output-subcompositor.md # ce document
```
## Critère de fin 13.2
> 1. Le compositor expose `wl_output` v3 avec gating correct des events
> par version bound (testé v1/v2/v3 en natif et runtime)
> 2. Le compositor expose `wl_subcompositor` v1 et accepte tous les
> requests `wl_subsurface` sans erreur protocole
> 3. Un client multi-surface (parent + 1 subsurface) rend visuellement
> les deux surfaces aux bonnes positions (parent à `cascade_offset`,
> child à `parent + offset`)
> 4. Le z-order reste cohérent même après recommit du parent (le child
> reste au-dessus)
> 5. Le focus clavier reste sur le parent (les subsurfaces ne grabbent
> pas)
**✅ Tous critères validés 2026-05-16.**
## Étapes suivantes possibles
- **13.3** : extraction backend pour Smithay/COSMIC (Phase 13 du master
plan). Auditer notre code pour identifier les primitives réutilisables
comme backend Smithay (display, input, session, renderer logiciel).
- **13.2.c** : retenter sctk simple_window — avec wl_output et
wl_subcompositor maintenant présents, il manque encore xdg-decoration
et un wayland-server qui charge libxkbcommon. À évaluer si le ROI
vaut le coût d'implémentation.
- **Durcissement 13.2** : reprendre les limitations listées ci-dessus
(cascade sync, parent move propagation, role-tracking).
- **Phase 8** : remplacement expérimental d'Orbital avec ce compositor.
Préparé maintenant qu'on couvre les protocoles de base.