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

12 KiB
Raw Blame History

Phase 13.2 — wl_output v3 + wl_subcompositor/wl_subsurface

Document produit le 2026-05-16, suite de 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)

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)

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)

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)

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)

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)

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 :

#!/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.