redox-wayland-compositor/docs/phase6-compositor-core.md
Votre Nom 509aae7769 🎉 Phase 6.3 — display + input + compositor-core intégrés runtime
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
2026-05-09 12:20:04 +02:00

8.3 KiB
Raw Permalink Blame History

Phase 6 — Compositor core : résultats

Document produit le 2026-05-09 dans le cadre du plan directeur REDOX_COSMIC_XWAYLAND_RS_PLAN.md.

Périmètre 6.1-6.3 : structures core, pipeline de composition, intégration runtime display + input + composition.

Hors scope (6.4) : frontend Wayland (wl_compositor / wl_shm / xdg-shell), damage tracking, frame callbacks.

Verdict 6.1-6.3

Cœur compositor fonctionnel et intégré.

  • 27 tests unitaires sur compositor-core (12 structures + 11 composition + 4 hit_test)
  • RedoxOutput impl Framebuffer → composition directe dans le framebuffer Redox
  • Bin d'intégration redox-wl-test-compose-static qui combine display + input + compositor

Captures preuves :

Frame Z-order (bottom → top) PNG
Initial (3 surfaces créées) red, green, blue phase6-3-default-z.png
Après sendkey 1 (raise red) green, blue, red phase6-3-red-top.png
Après sendkey 2 (raise green) blue, red, green phase6-3-green-top.png
Après sendkey 3 (raise blue) red, green, blue phase6-3-blue-top.png

Chaque pression de touche déclenche registry.raise(id) puis registry.compose_into(&mut output) puis output.present_with_takeover(), le tout dans le même binaire qui consomme aussi les events via InputBackend.

Architecture finale 6.3

┌──────────────────────────────────────────────────┐
│ redox-wl-test-compose-static (binaire)           │
│                                                  │
│   RedoxOutput      ──────┐                       │
│   (display)              │                       │
│        │                 │ Arc<ConsumerHandle>   │
│        │ impl Framebuffer│                       │
│        │                 ▼                       │
│        │            InputBackend  ──> InputEvent │
│        │                 │                       │
│        ▼                 ▼                       │
│   SurfaceRegistry ◄── (raise, hit_test, modify)  │
│        │                                         │
│        │ compose_into(&mut output)               │
│        ▼                                         │
│   pixels écrits dans le framebuffer Redox        │
│        │                                         │
│        └──> present_with_takeover                │
└──────────────────────────────────────────────────┘

Dépendances (graph propre, pas de cycle) :

redox-wl-compositor-core   ← lib pure Rust, sans dep externe
        ▲
        ├──── redox-wl-display (impl Framebuffer for RedoxOutput)
        │
        └──── redox-wl-test-compose-static (bin)
                ▲
                └──── redox-wl-input (lib)

API publique 6.1-6.3

Types core

pub struct SurfaceId(u64);

pub struct SurfaceBuffer {
    pub pixels: Arc<Vec<u32>>,  // ARGB8888
    pub width: u32,
    pub height: u32,
}

pub struct SurfaceState {
    pub x: i32,
    pub y: i32,
    pub buffer: Option<SurfaceBuffer>,
    pub visible: bool,
}

pub struct Surface { /* id, current, pending */ }
impl Surface {
    pub fn id(&self) -> SurfaceId;
    pub fn current(&self) -> &SurfaceState;
    pub fn pending(&self) -> &SurfaceState;
    pub fn pending_mut(&mut self) -> &mut SurfaceState;
    pub fn commit(&mut self);
}

SurfaceRegistry

pub struct SurfaceRegistry { /* ... */ }
impl SurfaceRegistry {
    pub fn create(&mut self) -> SurfaceId;
    pub fn destroy(&mut self, id: SurfaceId) -> bool;
    pub fn raise(&mut self, id: SurfaceId);
    pub fn get(&self, id: SurfaceId) -> Option<&Surface>;
    pub fn get_mut(&mut self, id: SurfaceId) -> Option<&mut Surface>;
    pub fn commit(&mut self, id: SurfaceId) -> bool;
    pub fn modify_pending<F>(&mut self, id: SurfaceId, f: F) -> bool;

    pub fn iter_z_order_back_to_front(&self) -> impl Iterator<Item = &Surface>;
    pub fn iter_z_order_front_to_back(&self) -> impl Iterator<Item = &Surface>;
    pub fn hit_test(&self, x: i32, y: i32) -> Option<SurfaceId>;
    pub fn compose_into<F: Framebuffer + ?Sized>(&self, target: &mut F);
}

Trait Framebuffer

pub trait Framebuffer {
    fn width(&self) -> u32;
    fn height(&self) -> u32;
    fn pixels_mut(&mut self) -> &mut [u32];
}

Implémenté pour RedoxOutput dans redox-wl-display. Le bin d'intégration peut faire registry.compose_into(&mut output) directement.

Tests unitaires (27 total, 0.00s natif)

Catégorie # Couverture
Surface registry 6 create/destroy/raise (idempotent, unknown, etc.)
Pending/current state 2 modification + commit + isolation
Composition 9 empty, fullscreen, partiel, clipping (4 bords), invisible, sans buffer, z-order, current vs pending
Hit testing 4 topmost, skip invisible, after raise, hors écran
End-to-end 2 iter z-order, workflow compositor typique
Buffer construction 1 new_filled produit bonne taille + couleur

Compile aussi pour x86_64-unknown-redox (pure Rust, aucune dep system).

Méthode validation runtime

# Build
cd crates/redox-wl-test-compose-static && redoxer build --release

# Push dans image
mount image via redoxfs
cp binary into /usr/bin/
modify init.d/20_orbital → nowait VT=2 redox-wl-test-compose-static
clear init.d/30_console
unmount

# Boot QEMU headless avec capture
qemu ... -display none -monitor unix:/tmp/qmp.sock,...
sleep 2 && sendkey ret    # bootloader
sleep 13                   # boot complete
screendump /tmp/frame-1.ppm    # initial state
sendkey 1 && sleep 1 && screendump frame-2.ppm   # red top
sendkey 2 && sleep 1 && screendump frame-3.ppm   # green top
sendkey 3 && sleep 1 && screendump frame-4.ppm   # blue top

Les 4 captures sont visibles dans docs/phase6-3-*.png.

Limitations / hors scope

Pas de damage tracking

Chaque compose_into() rerend les surfaces complètement. Pour 3 surfaces ARGB de ~400x300, c'est ~720 KiB recopiés à chaque event. Acceptable (on est largement sous le ms côté CPU). À reconsidérer en 6.4 quand wl_surface.damage_buffer arrivera côté frontend.

Pas de blending alpha

compose_into overwrite les pixels. Une surface ARGB avec α<255 est traitée comme opaque (l'alpha est ignoré au niveau de la copie). Pour Wayland natif on aura besoin de blending pour les decorations côté client transparentes. Reportable à 6.4 ou 6.5.

Pas de cursor visible

Le bin d'intégration ne dessine pas de curseur. Le pointeur souris QEMU n'apparaît donc pas à l'écran. Hit-test fonctionne (on a vu les events PointerButton) mais sans feedback visuel. Add à phase 7 (curseur stable).

Hit-test au clic non testé visuellement

Le test runtime utilise les touches '1'/'2'/'3' pour raise. Les events PointerMotion n'arrivent pas dans la config QEMU minimale (pas de -device usb-tablet). Le hit-test fonctionne sur les coords qu'on garde côté binaire ; à valider avec un vrai input mouse en 7.

Code source

crates/redox-wl-compositor-core/    # lib pure Rust (450 lignes + 27 tests)
├── Cargo.toml
└── src/lib.rs

crates/redox-wl-display/             # MODIFIÉ
├── Cargo.toml                       # + dep redox-wl-compositor-core
└── src/lib.rs                       # + impl Framebuffer for RedoxOutput

crates/redox-wl-test-compose-static/ # bin d'intégration (190 lignes)
├── Cargo.toml
└── src/main.rs

Suite phase 6.4

  • Ajouter dep wayland-server au futur redox-wl-wayland-frontend
  • Mapper wl_compositor.create_surfaceregistry.create()
  • Mapper wl_shm_pool.create_buffer → wrapper sur mmap'd region
  • Mapper wl_surface.attachstate.buffer = Some(...)
  • Mapper wl_surface.commitregistry.commit(id)
  • Mapper wl_surface.damage_buffer → suivi pour optimiser compose_into
  • Mapper wl_surface.frame → callback après present
  • Tester avec un client Wayland simple (par ex. weston-info ou un client de test custom utilisant wayland-client)

Estimé 2-3 sessions de 2h.


Fin du document de phase 6.1-6.3.