Captures preuves dans docs/phase6-3-*.png : 4 frames qui prouvent
visuellement que raise change l'ordre Z et que compose_into propage le
résultat à l'écran QEMU :
- default-z.png : 3 surfaces overlap, blue top (créé en dernier)
- red-top.png : sendkey 1 → raise(red) → red couvre vert et bleu
- green-top.png : sendkey 2 → raise(green) → green couvre tout dans sa zone
- blue-top.png : sendkey 3 → raise(blue) → retour visuel à initial
Modifications :
compositor-core (commit dbf3bff → maintenant) :
- + iter_z_order_front_to_back() : utile pour hit testing
- + hit_test(x, y) -> Option<SurfaceId> : trouve la surface visible la
plus haute qui contient le point
- + 4 tests unitaires : 27 total / 27 pass natif (0.00s)
redox-wl-display :
- + dep redox-wl-compositor-core
- + impl Framebuffer for RedoxOutput (délègue à pixels_mut + width/height)
bin redox-wl-test-compose-static (190 lignes) :
- ouvre RedoxOutput + take_crtc
- crée InputBackend partagé
- 3 surfaces ARGB unies (rouge/vert/bleu) avec overlap centré
- boucle event : '1'/'2'/'3' raise resp. red/green/blue
- clic souris → hit_test puis raise (motion non testé sans usb-tablet)
- ré-render seulement si raise → économie CPU
- present_with_takeover() à chaque iter pour tenir le CRTC
Validation QEMU automatisée : sendkey 1/2/3 + screendump entre chaque.
Les 4 PNG montrent l'ordre Z évoluer correctement.
Image Redox restaurée à boot Orbital normal.
docs/phase6-compositor-core.md : compte-rendu 6.1-6.3 complet,
architecture, dépendances, API, limitations, plan 6.4.
Phase 6.3 close. Reste 6.4 (frontend Wayland : wl_compositor + wl_shm
+ xdg-shell mappés vers compositor-core, damage tracking, frame
callbacks). Estimé 2-3 sessions.
Leyoda 2026 – GPLv3
Trait Framebuffer dans compositor-core (lib pure Rust):
pub trait Framebuffer {
fn width(&self) -> u32;
fn height(&self) -> u32;
fn pixels_mut(&mut self) -> &mut [u32];
}
Backends impl ce trait pour leur framebuffer (RedoxOutput le fera en 6.3).
Tests utilisent un mock MockFb sur Vec<u32>.
SurfaceRegistry::compose_into<F: Framebuffer + ?Sized>(&self, target: &mut F) :
- itère iter_z_order_back_to_front()
- skip surfaces invisible / sans buffer / entièrement offscreen
- clip aux bords du framebuffer (x/y négatifs, débordement droit/bas)
- copie row-major ARGB8888 (overwrite, pas de blending alpha)
Tests ajoutés (11) :
- compose_empty_registry_keeps_fb_unchanged
- compose_one_fullscreen_surface_fills_fb
- compose_partial_surface_only_modifies_its_rect
- compose_clips_at_right_edge
- compose_clips_at_left_top_with_negative_position
- compose_skips_offscreen_surface
- compose_skips_invisible_surface
- compose_skips_surface_without_buffer
- compose_respects_z_order_top_overwrites_bottom
- compose_after_raise_changes_visible_overlap
- compose_uses_current_state_not_pending
Total : 23/23 tests pass cargo test --release (0.00s, c'est dire la
légèreté de la lib). Compile aussi pour x86_64-unknown-redox.
Pas de damage tracking, pas de blending alpha — reportés à 6.4 quand
le frontend Wayland aura besoin de damage_buffer et de surfaces
transparentes.
Phase 6.2 close. Suite : 6.3 — bin redox-wl-test-compose-static qui
impl Framebuffer for RedoxOutput + 3 rectangles synthétiques + raise
au clic via InputBackend + screenshot validation.
Leyoda 2026 – GPLv3
Crate redox-wl-compositor-core (lib pure Rust, sans deps externes) :
- SurfaceId : newtype u64 opaque
- SurfaceBuffer : Arc<Vec<u32>> ARGB8888 + width/height
- SurfaceState : x, y, buffer, visible
- Surface : id + current + pending + commit()
- SurfaceRegistry : HashMap<SurfaceId, Surface> + z_order Vec
- create() / destroy() / raise()
- get() / get_mut() / commit() / modify_pending()
- iter_z_order_back_to_front() pour la composition
Sémantique Wayland (pending → current via commit) prévue dans l'API
mais implémentation triviale (clone). Pas de damage tracking, pas de
double-buffer atomique : reportés à 6.4 quand wl_shm/xdg-shell arriveront.
12 tests unitaires :
- création/destruction/idempotence
- z-order par défaut + raise sur top/non-top/unknown
- pending vs current state séparés
- commit propage pending → current
- destroyed surface skipped during iteration
- workflow compositor typique end-to-end (3 fenêtres + raise)
Tous passent en cargo test natif (0.77s release).
La crate compile aussi pour x86_64-unknown-redox via redoxer
(pure Rust, aucune dep system).
Phase 6.1 close. Suite : 6.2 (compose_into RedoxOutput).
Leyoda 2026 – GPLv3
Capture : docs/phase4-victory-1280x800.png — dégradé ARGB animé 1280x800
écrit par redox-wl-fullscreen-paint, occupant tout l'écran QEMU sans
trace de bootlog, fbcond ou Orbital.
Cause racine du verrou (3 bugs en cascade) :
1. ConsumerHandle local à RedoxOutput::open() → droppé en fin de fn →
inputd::on_close retirait le VT de self.vts → tous les `inputd -A <vt>`
ultérieurs retournaient warning "switch to non-existent VT"
2. L'env var VT=N posée par init n'a aucun lien avec le VT alloué par
inputd. inputd auto-incrémente next_vt_id à partir de 2 (VT 1 réservé
bootlog). Avec fbbootlogd VT 1 + fbcond VT 2, notre paint = VT 3.
3. Sans le bon VT activé, set_crtc est silencieusement no-op côté
driver-graphics (lib.rs:575 : `if *vt == self.active_vt { ... }`).
Fixes :
- RedoxOutput stocke `_consumer: ConsumerHandle` pour préserver le VT
- RedoxOutput.vt() lu via fpath sur consumer fd (inputd retourne
`<scheme>/<vt>`)
- Binary lit output.vt() puis fait inputd -A <vt> avec le bon numéro
- 300ms de sleep pour propagation active_vt avant take_crtc
Validation automatisée : qemu -display none + monitor unix socket +
ncat -U pour sendkey ret + screendump à T+14s + ImageMagick.
Image Redox restaurée à boot Orbital normal après la session.
Phase 4 close. La piste 1 (consume events VT) reste utile pour le
hot-switch propre Ctrl+Alt+Fn mais n'est plus bloquante.
Leyoda 2026 – GPLv3
Crates ajoutés :
- redox-wl-display (lib) : RedoxOutput { open, take_crtc, pixels_mut,
present, present_with_takeover, Drop }
- redox-wl-fullscreen-paint (bin) : take CRTC + 30 frames ARGB animées
Validation logique du pipeline display sur Redox bootée :
- ConsumerHandle::new_vt() OK
- open_display_v2() retourne le fd graphics-ipc
- V2GraphicsHandle énumère 1 connecteur 1280x800
- CpuBackedBuffer ARGB8888 plein écran allocable
- add_framebuffer + set_crtc + dirty_framebuffer répondent tous OK
- 30 itérations sync_rect sans erreur ni leak
Validation visuelle automatisée via QEMU monitor screendump :
- QEMU en -display none + -monitor unix:socket
- ncat -U envoie sendkey ret au bootloader puis screendump à T+15s
- ImageMagick convertit PPM → PNG, visualisable
Verrou identifié : fbbootlogd (lancé par init.initfs.d/20_fbbootlogd.service,
embarqué dans le blob initfs) écrit directement dans le framebuffer mémoire
mappé par vesad, hors du pipeline DRM. Il ne release pas le display quand
notre paint fait set_crtc.
Pour vrai visuel, il faut soit :
1. Consommer les events VT côté RedoxOutput (le pattern Orbital, propre)
2. Désactiver fbbootlogd dans l'image (rapide, debug)
3. Implémenter le handoff complet (long, prod)
Le pipeline étant validé, on peut passer phase 5 (input backend) et revenir
sur le visuel quand on aura un compositor qui consomme les events VT.
docs/phase4-display-backend.md enrichi avec l'analyse complète.
Leyoda 2026 – GPLv3
Test exécuté avec succès sur Redox bootée via make qemu :
[disp] 1 connector(s) reported by KMS subset
[disp] #0 connector ::Handle(19): state=Connected, 1 mode(s)
[disp] first mode: 1280x800
[disp] CpuBackedBuffer allocated 64x64 ARGB8888 (shadow=false)
[disp] painted test pattern + sync_rect
[disp] PASS: display backend pipeline reachable
Implications majeures :
- la crate drm 0.15 upstream fonctionne sur Redox runtime, pas seulement
compile-time
- graphics-ipc::V2GraphicsHandle est un subset DRM/KMS suffisant pour
un compositor minimal
- inputd accepte plusieurs consumers sur des VTs différents → on peut
développer un compositor sur VT 2 tout en gardant Orbital sur VT 3
(validation de la stratégie de coexistence du plan directeur)
Le test n'inclut pas encore modeset/scanout (set_crtc) — c'est l'étape
suivante : crate redox-wl-display + binaire qui prend le CRTC pour
afficher de vrais pixels.
docs/phase4-display-backend.md : compte-rendu complet + roadmap.
Leyoda 2026 – GPLv3
Modifications du test display-backend pour le runtime VT-handler réel :
- DebugSink wrapper qui mirroir stdout vers /scheme/debug (serial host stdio)
→ la sortie est visible côté host même si on tourne sur un VT non actif
- Lecture env var VT (mise par le init Redox)
- Appel `inputd -A <vt>` après open_display, comme Orbital le fait
(cf orbital/src/main.rs ligne ~43)
- Sleep 500 ms en fin de main pour laisser le serial flush
README enrichi avec les étapes Voie B précises (commande redoxfs locale,
clavier FR via QEMU_USER_FLAGS, switch VT Ctrl+Alt+F2, login root/password).
Note locale (non versionnée) : ajout d'un hook QEMU_USER_FLAGS dans
~/Projets/Redox/redox-src/mk/qemu.mk pour passer des args qemu user-supplied.
Leyoda 2026 – GPLv3
Crate redox-wl-test-display-backend qui réutilise le pattern Orbital :
inputd::ConsumerHandle::new_vt() → open_display_v2() → V2GraphicsHandle.
Comportement vérifié :
- compile pour x86_64-unknown-redox sans patch (graphics-ipc, inputd, drm
via git deps gitlab.redox-os.org/redox-os/base.git, comme Orbital)
- sous redoxer run headless : ConsumerHandle::new_vt() OK, open_display_v2
retourne EINVAL (cohérent avec absence framebuffer)
- runtime sur framebuffer (redoxer --gui ou make qemu) : à valider
manuellement par le user (la fenêtre QEMU étant interactive)
README enrichi avec la marche à suivre pour les tests display
(voie A: redoxer exec --gui ; voie B: redoxfs + make qemu).
Leyoda 2026 – GPLv3