🎉 Phase 7.3 — curseur software validé runtime

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
This commit is contained in:
Votre Nom 2026-05-13 09:06:56 +02:00
parent baa94701bf
commit 5f7587e79e
9 changed files with 536 additions and 16 deletions

View file

@ -86,6 +86,8 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
// Wayland frontend
let socket_path = PathBuf::from(SOCKET_PATH);
let mut frontend = WaylandFrontend::bind_absolute(&socket_path)?;
// Phase 7.3 : curseur visible dès le démarrage, placé au centre du fb.
frontend.set_cursor_initial_position((fb_w as i32) / 2, (fb_h as i32) / 2);
dlog(&format!("[comp] Wayland socket : {SOCKET_PATH}"));
// Exporter WAYLAND_DISPLAY pour les clients lancés par l'OS qui regarderaient
@ -96,7 +98,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
// Boucle principale
let start = Instant::now();
let total = Duration::from_secs(60);
let total = Duration::from_secs(180);
let frame_period = Duration::from_millis(33); // ~30 fps
let mut last_frame = Instant::now();
let mut tick: u32 = 0;
@ -154,6 +156,8 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
}
}
frontend.registry.compose_into(&mut output);
// Phase 7.3 : curseur software par-dessus la composition.
frontend.draw_cursor(&mut output);
if let Err(e) = output.present_with_takeover() {
dlog(&format!("[comp] present err: {e}"));
}
@ -179,7 +183,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
thread::sleep(frame_period);
}
dlog("[comp] timeout 60s atteint, exit");
dlog("[comp] timeout atteint, exit");
let _ = std::fs::remove_file(SOCKET_PATH);
Ok(())
}