Sur clic gauche, le compositor fait hit_test à la position curseur,
raise la surface ciblée au top du Z-order et transfère le keyboard
focus à cette surface (broadcast wl_keyboard.leave/enter via le
set_focus déjà implémenté en 7.2).
Frontend additions :
- HashMap<SurfaceId, wl_surface::WlSurface> dans WaylandFrontend,
peuplée au wl_compositor.create_surface (capture du retour de
data_init.init), nettoyée au wl_surface.destroy
- Au wl_surface.destroy : clear focused_surface et cursor_surface_id
si la surface détruite était l'une de ces références (évite les
wl_surface fantômes dans les events suivants)
- forward_input(PointerButton.left=true) déclenche
registry.hit_test(cursor_x, cursor_y), puis si la cible n'est pas
une surface curseur : registry.raise + set_focus(target)
- println! tracing pour [frontend] left-click et focus change
Nouveau crate : redox-wl-test-client-shm-two
- Binaire qui fork() : parent = fenêtre A (verte, pyramide), enfant
= fenêtre B (magenta, double cercle) après sleep 800ms
- 2 connexions Wayland indépendantes au même socket compositor
- timeout 160s aligné sur le compositor 180s
Validation runtime : 4 captures synchronisées via cycle de
positionnement curseur temporaire (retiré après) prouvent les
2 transitions de Z-order :
- initial : B au top (commit le plus récent)
- click@(80,80) → hit A → A passe au top
- click@(400,280) → hit B → B repasse au top
Traces /tmp/comp.log (extraites via redoxfs) confirment :
[frontend] left-click @ (80, 80) → hit_test = Some(SurfaceId(0))
[frontend] focus change: Some(SurfaceId(1)) → Some(SurfaceId(0))
[frontend] left-click @ (400, 280) → hit_test = Some(SurfaceId(1))
[frontend] focus change: Some(SurfaceId(0)) → Some(SurfaceId(1))
Pipeline validé end-to-end :
mouse_button QEMU → ps2d → inputd → InputBackend::poll →
RedoxInputEvent::PointerButton → forward_input → hit_test →
raise + set_focus → wl_keyboard.leave/enter broadcast.
Doc complète : docs/phase7-4-focus-raise.md.
Leyoda 2026 – GPLv3
Sprite curseur 16x16 ARGB dessiné par-dessus la composition après
`SurfaceRegistry::compose_into()`, avec alpha blending non
prémultiplié (`out = src + dst * (1 - src.a)`) et hot-spot
configurable.
Frontend additions :
- `cursor_surface_id` / `cursor_hot_x` / `cursor_hot_y` /
`cursor_visible` dans `WaylandFrontend`
- `is_cursor: AtomicBool` dans `SurfaceData`
- `default_cursor_sprite()` : flèche hardcoded 16x16
- `blend_argb_over(src, dst)` avec fast paths a=0/a=255
- `draw_cursor<F: Framebuffer>(target)` : clip aux bords du fb,
blit pixel par pixel
- `set_cursor_initial_position` / `set_cursor_position` /
`cursor_position` publiques
- `wl_pointer.set_cursor` handler : store la surface client,
marque `is_cursor = true`, l'exclut du Z-order (visible=false)
- `wl_surface.commit` lit `is_cursor` → si curseur, pas de
raise/focus et reste invisible dans la composition normale
- `cursor_visible = true` au premier PointerMotion(Relative)
Binaire compositor :
- `set_cursor_initial_position(fb_w/2, fb_h/2)` au boot
- `frontend.draw_cursor(&mut output)` après `compose_into`
- timeout porté de 60s à 180s pour validation visuelle confortable
Test client SHM :
- timeout porté de 25s à 170s pour rester aligné avec le compositor
Validation runtime : 5 screendumps à 5 positions distinctes
prouvent que `draw_cursor` est appelé correctement quel que soit
`(cursor_x, cursor_y)`, dont 2 captures par-dessus la fenêtre
client SHM (overlay alpha-blended sur les bandes arc-en-ciel).
Note runtime : Redox n'a pas de driver USB tablet opérationnel
sous QEMU. `mouse_move` PS/2 du monitor QEMU ne produit pas non
plus de PointerMotion côté inputd. Validation faite en mode
programmatique via un cycle temporaire `set_cursor_position`,
retiré du binaire après screendumps. À investiguer ps2d/vesad
en phase 7.5 ou plus tard.
Doc complète : `docs/phase7-3-cursor.md`.
Leyoda 2026 – GPLv3
Capture preuve : docs/phase7-2-input-routing.png — fenêtre client
xdg_toplevel 480x320 (damier turquoise) à (60,60), compositor stable
pendant que les keyboard events transitent en parallèle.
Validation runtime exhaustive : tous les events injectés via QEMU
sendkey/mouse_button arrivent au client via wl_keyboard.key /
wl_pointer.button :
[client-input] wl_keyboard.key key=54 Pressed ← 'c'
[client-input] wl_keyboard.key key=50 Pressed ← shift
[client-input] wl_keyboard.key key=38 Pressed ← 'a' avec shift
[client-input] wl_keyboard.key key=37 Pressed ← ctrl
...
Modifications redox-wl-wayland-frontend :
- + dep redox-wl-input (pour InputEvent type)
- wl_seat global v7 avec capabilities = Pointer | Keyboard
- wl_seat.name = "redox-wl-seat0" (v2+)
- Dispatch wl_seat : GetPointer, GetKeyboard, GetTouch (no-op),
Release ; au get_keyboard envoie keymap NoKeymap + repeat_info
- Dispatch wl_pointer / wl_keyboard / wl_touch : Release retire la
resource de state.{pointers,keyboards}
- forward_input(InputEvent) public method qui broadcast
wl_keyboard.key, wl_pointer.motion/button/axis/frame aux clients
- set_focus(surface) public method qui envoie keyboard/pointer
enter/leave events sur changement de focus
- Tracking : focused_surface, cursor_x/y, next_input_serial,
input_time_ms, pointers/keyboards Vec<Resource>
Modif wl_surface.commit : appelle set_focus(Some(_resource)) pour que
la dernière surface commitée reçoive l'enter automatiquement
(politique simple 7.2, à raffiner en 7.4).
Modif compositor binaire (redox-wl-compositor) :
- Forward chaque InputEvent au frontend.forward_input(&ev)
- Esc reste géré côté compositor pour exit propre
Bin redox-wl-test-client-input ajouté (~280 lignes) :
- Bind wl_compositor + wl_shm + xdg_wm_base + wl_seat
- get_keyboard + get_pointer après reception caps
- Crée xdg_toplevel + buffer ARGB damier turquoise
- Log chaque wl_keyboard.{enter,leave,key,modifiers,repeat_info}
et wl_pointer.{enter,leave,motion,button,axis}
- Boucle event_queue : flush + prepare_read.read + dispatch_pending
(CORRECT pattern pour wayland-rs ; le bug initial était d'utiliser
juste dispatch_pending qui ne lit pas le socket)
Critère de fin 7.2 validé : un client qui bind wl_seat reçoit
keyboard events via wl_keyboard.key sans panic serveur.
Limitations connues (sous-tickets ultérieurs) :
- Keymap NoKeymap (pas de XKB layout) — 7.2 utilise scancodes raw
- Broadcast à tous les keyboards/pointers (pas de filtrage par
client focus) — multi-client viendra en 7.6
- Pas de pointer.motion testé (besoin -device usb-tablet QEMU)
- Pas de validation modifier state (juste enter envoie 0,0,0,0)
Image Redox restaurée à boot Orbital normal.
Phrase reprise 7.3 :
> Reprendre au commit XXX : Phase 7.3 curseur software. Dessiner un
> sprite curseur 16x16 par-dessus la composition, position basée sur
> InputBackend cursor_x/y. Hot-spot configurable via wl_pointer.set_cursor
> (déjà no-op à 7.2). Tester avec usb-tablet QEMU pour avoir motion absolu.
Leyoda 2026 – GPLv3
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
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