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
316 lines
13 KiB
Markdown
316 lines
13 KiB
Markdown
# Phase 4 — Display backend Redox : résultats
|
|
|
|
> Document produit le 2026-05-08 dans le cadre du plan directeur
|
|
> `REDOX_COSMIC_XWAYLAND_RS_PLAN.md`.
|
|
>
|
|
> **Périmètre** : valider qu'un binaire Rust userspace peut ouvrir le display
|
|
> Redox, énumérer les modes via le subset DRM/KMS, et allouer un buffer
|
|
> hardware-backed pour le rendu.
|
|
|
|
## Verdict global
|
|
|
|
**✅ Pipeline display backend Redox accessible et fonctionnel.**
|
|
|
|
Le crate `redox-wl-test-display-backend` a tourné dans une session Redox
|
|
réelle (image bootée via `make qemu audio=no`), depuis la console VT 2,
|
|
avec succès complet de la séquence :
|
|
|
|
```
|
|
ConsumerHandle::new_vt()
|
|
└─> open_display_v2()
|
|
└─> V2GraphicsHandle::from_file()
|
|
└─> resource_handles().connectors() → 1 connecteur
|
|
└─> mode 1280x800
|
|
└─> get_driver_capability(DumbBuffer) → Ok(1)
|
|
└─> CpuBackedBuffer::new(64x64, Argb8888) → OK
|
|
└─> shadow_buf().fill(pattern)
|
|
└─> sync_rect()
|
|
└─> destroy()
|
|
```
|
|
|
|
**Aucun blocage.** Le display backend Redox fonctionne directement avec
|
|
les APIs Orbital sans modification.
|
|
|
|
## Sortie complète du test
|
|
|
|
Capturée le 2026-05-08 dans la console VT 2 de Redox bootée via `make qemu` :
|
|
|
|
```
|
|
[disp] Phase 4 display backend test on Redox
|
|
[disp] VT env = None
|
|
[disp] inputd consumer handle opened
|
|
[disp] display file opened from inputd path
|
|
[disp] V2GraphicsHandle created
|
|
[disp] 1 connector(s) reported by KMS subset
|
|
[disp] #0 connector connector::Handle(19): state=Connected, 1 mode(s)
|
|
[disp] first mode: 1280x800
|
|
[disp] 1 connected display(s)
|
|
[disp] driver caps: dumb_buffer=Ok(1) cursor=?x?
|
|
[disp] CpuBackedBuffer allocated 64x64 ARGB8888 (shadow=false)
|
|
[disp] painted test pattern + sync_rect
|
|
[disp] CpuBackedBuffer destroyed
|
|
[disp] PASS: display backend pipeline reachable
|
|
```
|
|
|
|
Notes :
|
|
- `VT env = None` : le binaire a été lancé manuellement, pas via init.
|
|
Pour un compositor en production, il sera lancé par init avec `VT=N` et
|
|
appellera `inputd -A N` (déjà branché dans le code, désactivé sans VT).
|
|
- `cursor=?x?` : `get_driver_capability(CursorWidth/Height)` retourne Err
|
|
pour ce display QEMU. Pas critique, on a `DumbBuffer=Ok(1)` qui suffit.
|
|
- `shadow=false` : `DumbPreferShadow` capability vaut 0 sur ce backend,
|
|
donc les writes vont direct au framebuffer (pas de shadow buffer en RAM).
|
|
|
|
## Fait notable : coexistence avec Orbital
|
|
|
|
Au moment du test, Orbital tournait sur **VT 3** (init `20_orbital`).
|
|
Notre binaire a été lancé depuis **VT 2** (console getty depuis `30_console`).
|
|
|
|
inputd a accepté **deux handlers consumer** simultanés sur deux VTs
|
|
distincts, et le scheme `display.*` a été accessible depuis le second
|
|
handler sans conflit avec celui d'Orbital.
|
|
|
|
C'est une **bonne nouvelle pour la stratégie de coexistence du plan directeur** :
|
|
on peut développer un compositor Wayland sur un VT séparé tout en gardant
|
|
Orbital opérationnel sur le sien — sans modifier l'init Redox.
|
|
|
|
## Implications pour la suite
|
|
|
|
### Ce qui est désormais validé
|
|
|
|
1. Le subset DRM/KMS Redox (`graphics-ipc::V2GraphicsHandle`) est
|
|
complet pour les besoins d'un compositor minimal :
|
|
- énumération connecteurs/encoders/CRTCs
|
|
- création/destruction de DumbBuffers
|
|
- mapping CPU writable + sync rect (= dirty_framebuffer)
|
|
- support driver capabilities (DumbBuffer, DumbPreferShadow)
|
|
|
|
2. La crate Rust `drm 0.15` upstream fonctionne directement sur Redox
|
|
sans patch (déjà confirmé en compile time, maintenant en runtime).
|
|
|
|
3. La fondation pour un `RedoxOutput` (cf phase 4 du plan directeur)
|
|
est en place : il suffit de wrapper ce qu'on a fait dans le test
|
|
en une struct propre + ajouter modeset (`set_crtc`) et page-flipping.
|
|
|
|
### Ce qui reste pour un display backend complet
|
|
|
|
Le test ne fait **pas** :
|
|
|
|
- **modeset/scanout** : on alloue un buffer mais on ne fait pas
|
|
`set_crtc(crtc, fb, ...)` pour qu'il soit affiché. Orbital tient
|
|
déjà le CRTC sur VT 3. Pour vraiment "afficher", il faudra
|
|
prendre la place d'Orbital sur un VT (ou lui prendre VT 3).
|
|
- **page flipping** : pas de double buffering, pas de
|
|
`wait_for_vblank`, pas de `page_flip`.
|
|
- **hotplug** : pas d'écoute des events DRM (connector connect/disconnect).
|
|
- **resize** : `V2DisplayMap::resize_if_necessary` (cf Orbital
|
|
core/display.rs) à porter.
|
|
- **cursor plane** : alloc + `set_cursor` non testés (driver caps
|
|
cursor=Err sur QEMU desktop, mais c'est dispo selon Orbital).
|
|
|
|
Toutes ces étapes sont des extensions évidentes du test actuel,
|
|
documentées dans `orbital/src/core/display.rs` qu'on peut copier presque
|
|
verbatim.
|
|
|
|
## Phase 4 vraie : tentative et limite identifiée (2026-05-08 soir)
|
|
|
|
Crates créés :
|
|
- `redox-wl-display` (lib) : `RedoxOutput` avec `open()` + `take_crtc()`
|
|
+ `pixels_mut()` + `present()` + `present_with_takeover()` + Drop
|
|
- `redox-wl-fullscreen-paint` (bin) : prend le CRTC sur VT 2, peint un
|
|
dégradé ARGB animé sur 30 frames
|
|
|
|
**Pipeline logique** : toutes les API DRM répondent OK (open, énumère
|
|
connecteurs, alloc CpuBackedBuffer, add_framebuffer, set_crtc, sync_rect,
|
|
dirty_framebuffer). Aucune erreur retour.
|
|
|
|
**Validation visuelle automatisée** : QEMU lancé sans display via
|
|
`-display none`, monitor unix socket pour `screendump`. Captures PPM 1280x800
|
|
prises pendant et après l'exécution du paint. Conversion en PNG via ImageMagick
|
|
pour visualisation.
|
|
|
|
### Verrou rencontré
|
|
|
|
L'écran capturé montre **les logs kernel + init en mode texte**, pas notre
|
|
dégradé. Notre paint a bien tourné (lignes `[paint]` visibles dans la
|
|
capture, écrites par `fbcond` sur le framebuffer texte), mais le rendu
|
|
graphique de notre `present_with_takeover()` n'apparaît pas.
|
|
|
|
**Cause** : `fbbootlogd` (lancé par `init.initfs.d/20_fbbootlogd.service`,
|
|
embarqué dans le blob initfs) écrit **directement dans la mémoire
|
|
framebuffer** mappée par vesad, hors du pipeline DRM. Il est ouvert sur le
|
|
scheme `consumer_bootlog` qui force VT 1 actif (cf inputd `main.rs:189`).
|
|
|
|
Les essais infructueux :
|
|
1. `inputd -A <vt>` après `take_crtc` : warning "switch to non-existent VT"
|
|
parce que l'env var `VT=N` mise par init n'est pas le VT alloué par inputd
|
|
(inputd auto-incrémente depuis 2)
|
|
2. `VT=2` (vrai VT alloué) + `inputd -A 2` : retour OK mais l'écran reste
|
|
sur la sortie fbbootlogd
|
|
3. Désactivation de `30_console` : aucun changement (fbbootlogd est dans
|
|
l'initfs, pas dans `/usr/lib/init.d/`)
|
|
4. `set_crtc` à chaque frame (`present_with_takeover`) : pas d'effet visuel
|
|
(fbbootlogd ne lit pas le CRTC, il écrit directement en mémoire)
|
|
|
|
### Cas où Orbital arrive à afficher
|
|
|
|
Dans le boot standard, Orbital remplace bien fbbootlogd à l'écran. Mais
|
|
Orbital reçoit un signal **VtEvent::Activate** via inputd et fait un *handoff*
|
|
explicite avec fbbootlogd (cf `inputd::ConsumerHandleEvent::Handoff` qui
|
|
arrête fbbootlogd quand un autre VT prend la main).
|
|
|
|
Notre `RedoxOutput` ne consomme pas les events VT, donc le handoff ne se
|
|
déclenche pas et fbbootlogd reste actif.
|
|
|
|
### Étapes restantes pour le visuel
|
|
|
|
Trois pistes, par coût croissant :
|
|
|
|
1. **Consommer les events VT côté `RedoxOutput`** — ajouter une boucle
|
|
`consumer.read_events()` qui détecte `Handoff` et release/re-take le CRTC
|
|
en conséquence. C'est ce que fait Orbital. **~1 jour de travail.**
|
|
2. **Désactiver fbbootlogd dans l'image** — modifier
|
|
`~/Projets/Redox/base/init.initfs.d/20_fbbootlogd.service` (commentaire
|
|
`cmd =`), puis `make all` dans `redox-src/`. Pratique pour test mais pas
|
|
pour production. **~15 min build + 5 min config.**
|
|
3. **Implémenter le protocole inputd handler complet** — release_display,
|
|
handoff réciproque, gestion full du switch VT. **~3-5 jours de travail.**
|
|
|
|
La piste 1 est celle qu'utilise Orbital, donc la cible légitime. La piste 2
|
|
permet de valider visuellement plus tôt.
|
|
|
|
### Validation indirecte
|
|
|
|
Même sans visuel, le pipeline est validé par :
|
|
- les codes de retour OK de toutes les API DRM (set_crtc, dirty_framebuffer)
|
|
- les lignes `[paint] frame X/30 présentée` qui sortent à chaque iteration
|
|
- la persistance du buffer `CpuBackedBuffer` (alloc + écriture + sync sans
|
|
panic, plusieurs centaines de fois sans fuite mémoire visible)
|
|
|
|
## VICTOIRE 2026-05-09 — phase 4 vraie validée visuellement
|
|
|
|
Capture : 
|
|
|
|
L'écran QEMU montre intégralement le dégradé ARGB animé écrit par notre
|
|
binaire `redox-wl-fullscreen-paint`, plein écran 1280x800. Aucune trace
|
|
de bootlog, fbcond ou fbbootlogd. C'est notre code Rust qui pilote le
|
|
framebuffer Redox.
|
|
|
|
### La cause racine du verrou (3 bugs en cascade)
|
|
|
|
Bug 1 — **`ConsumerHandle` était local à `RedoxOutput::open()`** et droppé
|
|
à la sortie de la fonction. inputd-daemon réagissait avec `on_close` qui
|
|
retire le VT de `self.vts`. Tous les `inputd -A <vt>` ultérieurs
|
|
retournaient warning "switch to non-existent VT".
|
|
|
|
Bug 2 — **L'env var `VT=N` posée par init n'a aucun lien avec le VT
|
|
réellement alloué par inputd**. inputd auto-incrémente `next_vt_id` à
|
|
partir de 2 (VT 1 réservé bootlog). On dénombrait :
|
|
- VT 1 = `consumer_bootlog` de fbbootlogd
|
|
- VT 2 = consumer de fbcond (lancé par `init.initfs.d/20_fbcond.service`)
|
|
- VT 3 = notre consumer
|
|
|
|
Bug 3 — **Sans le bon VT activé, `set_crtc` est silencieusement no-op**
|
|
côté `driver-graphics` (cf `lib.rs:575` : `if *vt == self.active_vt {
|
|
self.adapter.set_crtc(...) }`).
|
|
|
|
### Le fix
|
|
|
|
```rust
|
|
pub struct RedoxOutput {
|
|
/// CONSUMER GARDÉ EN VIE : si on le drop, inputd retire notre VT et
|
|
/// `inputd -A <vt>` dira "non-existent".
|
|
_consumer: ConsumerHandle,
|
|
// ...
|
|
/// VT alloué par inputd, lu via fpath() sur le consumer fd.
|
|
/// (cf `inputd::main.rs:271-281` qui retourne `display.scheme/<vt>`)
|
|
vt: usize,
|
|
}
|
|
```
|
|
|
|
Et le binaire qui appelle :
|
|
```rust
|
|
let our_vt = output.vt(); // VT exact alloué par inputd
|
|
Command::new("inputd").arg("-A").arg(our_vt.to_string()).status()?;
|
|
thread::sleep(Duration::from_millis(300)); // propagation active_vt
|
|
output.take_crtc()?; // set_crtc passe la condition
|
|
output.present_with_takeover()?; // pixels visibles à l'écran
|
|
```
|
|
|
|
### Validation visuelle automatisée
|
|
|
|
Méthode reproductible dans le repo :
|
|
```bash
|
|
qemu-system-x86_64 \
|
|
... -vga std -display none \
|
|
-monitor unix:/tmp/qmp.sock,server,nowait \
|
|
-serial file:/tmp/qemu-serial.log ... &
|
|
sleep 2; printf "sendkey ret\n" | ncat -U /tmp/qmp.sock # passe bootloader
|
|
sleep 14; printf "screendump /tmp/frame.ppm\n" | ncat -U /tmp/qmp.sock
|
|
magick /tmp/frame.ppm /tmp/frame.png # convert
|
|
# Lire le PNG pour voir le rendu
|
|
```
|
|
|
|
### Implications pour la suite
|
|
|
|
Phase 4 entièrement validée. La fondation `RedoxOutput` est utilisable telle
|
|
quelle pour :
|
|
- Phase 5 (input backend) : ajouter `consumer.read_events()` pour récupérer
|
|
les events keyboard/mouse de inputd
|
|
- Phase 6 (surfaces shm composées) : composer plusieurs `wl_shm` buffers
|
|
dans le `pixels_mut()` de `RedoxOutput`
|
|
- Phase 7 (compositor utilisable) : ajouter focus, stacking, damage
|
|
tracking au-dessus
|
|
|
|
La piste 1 du plan (consommer events VT) reste utile à terme pour gérer le
|
|
hot-switch propre entre VTs (Ctrl+Alt+Fn), mais n'est plus bloquante pour
|
|
afficher.
|
|
|
|
## Prochaine étape : phase 4 vraie
|
|
|
|
Le test actuel prouve **la possibilité technique**. La phase 4 vraie
|
|
consiste à :
|
|
|
|
1. Créer une crate `redox-wl-display` propre (pas un test, une lib)
|
|
- struct `RedoxOutput` qui wrappe `V2GraphicsHandle` + `Displays`
|
|
- méthodes `enumerate()`, `take_crtc()`, `present(buffer)`,
|
|
`release_crtc()`
|
|
2. Faire un binaire test qui **prend effectivement** le CRTC, écrit
|
|
un buffer plein écran, puis le rend (l'écran devient visible
|
|
du pattern écrit).
|
|
3. Tester en remplaçant temporairement `20_orbital` dans l'init
|
|
par notre binaire — Orbital ne tourne pas, notre binaire tient
|
|
le display, et l'écran montre notre rendu.
|
|
|
|
C'est le premier moment où **on aura de vrais pixels Redox sortis
|
|
de notre code** sur l'écran.
|
|
|
|
## Code source
|
|
|
|
```
|
|
crates/redox-wl-test-display-backend/
|
|
├── Cargo.toml # graphics-ipc, inputd, drm via git deps base.git
|
|
└── src/main.rs # 200 lignes, pattern Orbital simplifié
|
|
```
|
|
|
|
Build :
|
|
|
|
```bash
|
|
cd crates/redox-wl-test-display-backend
|
|
redoxer build --release
|
|
```
|
|
|
|
Test :
|
|
|
|
1. Monter `~/Projets/Redox/redox-src/build/x86_64/desktop/harddrive.img`
|
|
via `redoxfs` (cf README section "Voie B")
|
|
2. Copier le binaire dans `/usr/bin/` de l'image
|
|
3. Démonter
|
|
4. `cd ~/Projets/Redox/redox-src && make qemu audio=no QEMU_USER_FLAGS="-k fr"`
|
|
5. Ctrl+Alt+F2 → console VT 2
|
|
6. login `root` / mot de passe `password`
|
|
7. `redox-wl-test-display-backend`
|
|
|
|
---
|
|
|
|
*Fin du document de phase 4.*
|