Bug B.1 13.1.b résolu : ps2d sous Redox+QEMU délivre les coords absolues
du pointer dans 0..65535 (16-bit virtuel HID), pas en pixels écran. Notre
PointerMotion faisait une assignation directe → clamp permanent à
(width-1, height-1) → cursor coincé bas-droite, hit_test toujours None.
Fix : conversion fixed-point x*screen_w/65536 (algo orbital upstream,
cf orbital/src/scheme.rs:1583). Le clamp_cursor de 13.1.b reste comme
garde-fou pour valeurs hors-spec.
Validation runtime : curseur suit fidèlement la souris dans toute l'aire
écran 1280×800, clics en (370,239), (522,303), (746,175), (1260,142),
etc. Plus jamais de (1279,799) systématique.
Bonus DX : filtre tracing par défaut « info,cursor=debug » pour éviter
d'avoir à taper RUST_LOG complexe dans la fenêtre QEMU graphique (pas
de copier-coller).
Instrumentation `target="cursor"` laissée en place pour debug futur des
événements pointer.
Bug B.2 (page fault ion) non traité, reste à reporter upstream.
Leyoda 2026 – GPLv3
4 bugs identifiés et corrigés grâce au client tiers :
- ESC mangée par le compositor au lieu d'être forwardée (raccourci compo
déplacé sur Ctrl+Q + tracking ctrl_held)
- Cursor accumulait les deltas PS/2 hors-écran (ajout screen_w/h +
clamp_cursor + set_screen_size au boot)
- Keycode envoyé scancode+8 au lieu d'evdev brut (bug racine — le +8
est entre evdev et X11, pas entre PS/2 et evdev)
- Event loop client ne tolérait pas Interrupted/BrokenPipe transitoires
→ FAIL au lieu de PASS sur sortie normale (5e adaptation Redox côté
client, upstream-compatible)
Plus : tick log compo passé en debug! pour silencer la console série,
run-qemu.sh durci (check /dev/fuse, IMAGE/REDOXFS overridables).
6/6 checkpoints 13.1 verts. Bugs secondaires (curseur stuck en bas-droite,
ion page fault sur broken pipe d'un job background) listés dans le doc
pour follow-up en 13.1.c.
Leyoda 2026 – GPLv3
3 livrables :
1. Cleanup post-disconnect (corrige sub-bug 7.5)
- DumbClientData::disconnected push dans Arc<Mutex<Vec<ClientId>>>
partagé (peuplé à accept_pending_clients)
- SurfaceData.client_id: Mutex<Option<ClientId>> capturé au
wl_compositor.create_surface pendant que _client: &Client est
encore vivant (à la déconnexion surf.client() retourne None,
on ne pourrait plus déduire le mapping)
- WaylandFrontend.garbage_collect_dead_clients drain la queue
et nettoie surfaces_by_id + registry + focused_surface +
cursor_surface_id + pointers/keyboards orphelins
- Appelée à chaque tick depuis le compositor binaire après
dispatch_clients
2. wl_buffer.release après commit-copy
- SurfaceData.pending_buffer passé de Option<BufferData> à
Option<wl_buffer::WlBuffer> pour avoir le Resource sous la main
- Au commit, après la lecture des params via
buf.data::<BufferData>().cloned() et la copie des pixels,
appel buf.release() qui signale au client qu'il peut réutiliser
son buffer
3. Filtrage events par client focused
- forward_input calcule focused_client_id depuis
focused_surface.client().map(|c| c.id())
- wl_pointer.{motion,button,axis,frame} et wl_keyboard.key
n'arrivent qu'aux Resources dont client_id matche le focused
- PointerButton recalcule focused_cid APRÈS le hit_test+set_focus
pour que le clic atterrisse bien sur le nouveau client
Pièges trouvés :
- Resource n'a pas de client_id() direct → utiliser
client().map(|c| c.id())
- À l'instant du disconnected(), surf.client() retourne déjà None
→ capturer le ClientId au CreateSurface, pas après
Validation runtime :
- Test fuzz : surface fantôme du fuzz1 (brutal exit) nettoyée,
surfaces=0 stable post-fuzz, capture phase7-6-cleanup-no-ghost.png
confirme visuellement (vs rectangle noir 7.5)
- Test 2 clients : redox-wl-test-client-shm-two avec parent + fork
affiche A vert + B magenta en parallèle, surfaces=2 stable,
capture phase7-6-two-clients.png
- Log frontend : [frontend] garbage_collect: client X → destroyed
1 surfaces (fuzz1), 0 surfaces (fuzz2-4 qui ont cleanup propre)
Doc complète : docs/phase7-6-multi-clients.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