🎉 Phase 13.1.c — curseur souris : conversion HID→pixel

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
This commit is contained in:
Votre Nom 2026-05-16 11:32:32 +02:00
parent 18391c85f7
commit 58ef6a85c2
3 changed files with 188 additions and 3 deletions

View file

@ -95,7 +95,13 @@ impl<'a> MakeWriter<'a> for TeeWriter {
}
fn init_tracing() {
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
// Phase 13.1.c : par défaut, info + target=cursor en debug. Le target
// `cursor` est utilisé par les traces ABS/REL du frontend (cf
// redox-wl-wayland-frontend). Pratique sous QEMU graphique où taper
// un RUST_LOG complexe est pénible (pas de copier-coller). Override
// toujours possible via RUST_LOG=… au lancement.
let filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info,cursor=debug"));
tracing_subscriber::fmt()
.with_env_filter(filter)
.with_writer(TeeWriter::new())

View file

@ -844,8 +844,22 @@ impl WaylandFrontend {
}
}
RedoxInputEvent::PointerMotion { x, y } => {
self.cursor_x = *x;
self.cursor_y = *y;
// Phase 13.1.c : ps2d (sous Redox+QEMU) délivre des coords
// absolues en 0..65535 (cf orbital/src/scheme.rs:1575-1587,
// commentaire upstream « ps2d gives us absolute mouse events
// with x and y in the range 0..65535 »). On scale vers les
// pixels écran avec la même formule qu'orbital : x*screen_w/65536.
// Le diviseur 65536 (pas 65535) est volontaire — fixed-point
// 16-bit, donne max=screen-1 quand input=65535.
let scaled_x = ((*x as i64) * (self.screen_w as i64)) / 65536;
let scaled_y = ((*y as i64) * (self.screen_h as i64)) / 65536;
tracing::debug!(
target: "cursor",
"ABS x={x} y={y} → pixel ({scaled_x},{scaled_y}) screen={}x{}",
self.screen_w, self.screen_h
);
self.cursor_x = scaled_x as i32;
self.cursor_y = scaled_y as i32;
self.clamp_cursor();
self.cursor_visible = true;
// Phase 7.7 : si drag actif, déplacer la surface au lieu
@ -868,9 +882,22 @@ impl WaylandFrontend {
}
}
RedoxInputEvent::PointerMotionRelative { dx, dy } => {
let before_x = self.cursor_x;
let before_y = self.cursor_y;
self.cursor_x = self.cursor_x.saturating_add(*dx);
self.cursor_y = self.cursor_y.saturating_add(*dy);
let after_raw_x = self.cursor_x;
let after_raw_y = self.cursor_y;
self.clamp_cursor();
// Phase 13.1.c : trace dx/dy + cursor avant/après pour
// identifier si les deltas dérivent toujours positivement
// (axe inversé ? cumul d'edge ?) ou si le clamp masque le
// problème.
tracing::debug!(
target: "cursor",
"REL dx={dx} dy={dy} before=({before_x},{before_y}) raw=({after_raw_x},{after_raw_y}) clamped=({},{})",
self.cursor_x, self.cursor_y
);
self.cursor_visible = true;
// Phase 7.7 : si drag actif, déplacer la surface.
if self.apply_interactive_drag() {

152
docs/phase13-1-c-cursor.md Normal file
View file

@ -0,0 +1,152 @@
# Phase 13.1.c — Curseur souris : conversion HID→pixel
> Document produit le 2026-05-16, suite de
> [`phase13-1-b-observations.md`](phase13-1-b-observations.md).
>
> **Scope** : isoler et corriger le bug B.1 listé en 13.1.b — « curseur
> software ne suit pas la souris correctement, reste coincé en bas-droite
> de l'écran à `(width-1, height-1)` ». Diagnostic via instrumentation
> ciblée, fix conforme à l'algo d'orbital upstream, validation runtime.
>
> **Verdict** : ✅ **13.1.c validée** — le curseur suit fidèlement la
> souris, les coordonnées de clic sont dans la plage écran.
## Cause racine
Sous Redox + QEMU, `ps2d` délivre les coords absolues du pointer dans
un **espace virtuel 16-bit (0..65535)**, pas en pixels écran. Notre
branche `PointerMotion` faisait une assignation directe :
```rust
self.cursor_x = *x; // x ∈ 0..65535 ⇒ cursor_x ∈ 0..65535
```
Le `clamp_cursor()` ajouté en 13.1.b saturait alors à `screen_w - 1` à
chaque event → cursor coincé en bas-droite. Le bug B.1 13.1.b n'était
pas une dérive de deltas accumulés mais une **mauvaise interprétation de
l'unité d'entrée**.
## Diagnostic
Instrumentation `tracing::debug!(target: "cursor", ...)` ajoutée dans les
deux branches motion de `WaylandFrontend::forward_input` pour logger les
valeurs brutes reçues. Avec `RUST_LOG=info,cursor=debug` (devenu le filtre
par défaut de `init_tracing`), une session de tests motion délibérés a
révélé :
```
DEBUG cursor: ABS x=61642 y=15564 screen=1280x800
DEBUG cursor: ABS x=50174 y=20970 screen=1280x800
DEBUG cursor: ABS x=43006 y=23592 screen=1280x800
DEBUG cursor: ABS x=42084 y=23918 screen=1280x800
```
Valeurs maximales observées ≈ 62000 / 24000 sur écran 1280×800, ratio
~50× pour x et ~30× pour y. Hypothèse 16-bit confirmée par la doc
upstream orbital :
```
// orbital/src/scheme.rs:1576
// ps2d gives us absolute mouse events with x and y in the range 0..65535.
```
## Fix
Conversion fixed-point en pixel via la même formule qu'orbital
(`orbital/src/scheme.rs:1583-1586`) :
```rust
let scaled_x = ((*x as i64) * (self.screen_w as i64)) / 65536;
let scaled_y = ((*y as i64) * (self.screen_h as i64)) / 65536;
self.cursor_x = scaled_x as i32;
self.cursor_y = scaled_y as i32;
self.clamp_cursor(); // garde-fou pour valeurs hors-spec
```
Diviseur `65536` (pas `65535`) volontaire — fixed-point 16-bit, donne
`max output = screen - 1` quand `input = 65535`. Cohérent avec
l'arithmétique d'orbital.
Le `clamp_cursor` ajouté en 13.1.b reste utile comme garde-fou (si un
device sort de la plage 0..65535) mais ne masque plus le bug principal.
## Validation runtime
Test sur écran 1280×800 (fenêtre QEMU graphique, après reboot Redox
frais). Mouvement délibéré de la souris du coin haut-gauche au coin
bas-droit en passant par le centre. Logs filtrés :
```
DEBUG cursor: ABS x=18994 y=19660 → pixel (370,239) screen=1280x800 ← haut-gauche
DEBUG cursor: ABS x=33176 y=33094 → pixel (647,403) screen=1280x800 ← centre
DEBUG cursor: ABS x=64560 y=11714 → pixel (1260,142) screen=1280x800 ← haut-droit
DEBUG cursor: ABS x=37476 y=33176 → pixel (731,404) screen=1280x800 ← retour centre
```
Coordonnées pixel cohérentes avec la position physique de la souris,
toutes dans la plage `0..1279 × 0..799`. Pas de saturation au coin.
Clics au passage :
```
DEBUG redox_wl_wayland_frontend: left-click @ (522, 303) → hit_test = None
DEBUG redox_wl_wayland_frontend: left-click @ (746, 175) → hit_test = None
DEBUG redox_wl_wayland_frontend: left-click @ (370, 239) → hit_test = None
```
`hit_test = None` attendu : aucun client n'était lancé pendant ce test.
Les coords sont en revanche bien dans l'espace écran et pourront matcher
un toplevel quand un client tournera.
## Bonus DX (developer experience)
Type pénible de taper `RUST_LOG=info,redox_wl_wayland_frontend::cursor=debug`
dans la fenêtre QEMU graphique (pas de copier-coller possible).
Modification du filtre par défaut dans `init_tracing` :
```rust
EnvFilter::new("info,cursor=debug")
```
Tu tapes juste `redox-wl-compositor`, tu vois les traces cursor sans
spam des ticks. Override toujours possible via `RUST_LOG=` au launch.
## Sous-bug B.2 (page fault ion) — non traité
Reste documenté pour follow-up upstream. Repro fiable :
1. Lancer un job background Wayland (`redox-wl-real-client-simple-window &`)
2. Tuer le compositor avant que le client n'exit (`Ctrl+Q` sans avoir
pressé ESC d'abord)
3. ion crashe avec page fault `0x70`
À reporter sur `gitlab.redox-os.org/redox-os/ion`. Hors scope du compositor.
## Critère de fin 13.1.c
> Le curseur software du compositor suit fidèlement les déplacements
> souris dans toute l'aire de la fenêtre QEMU graphique, sans saturer
> à un bord, sans dérive monotone. Les coordonnées de clic sont dans
> la plage écran et `hit_test` peut matcher une surface visible.
**✅ Validé 2026-05-16.** Conversion HID→pixel alignée sur l'algo
upstream d'orbital, instrumentation laissée en place pour debug futur.
## Fichiers modifiés
```
crates/redox-wl-wayland-frontend/src/lib.rs # Conversion 0..65535 → pixels écran dans PointerMotion
# Traces tracing target="cursor"
crates/redox-wl-compositor/src/main.rs # Filtre tracing par défaut "info,cursor=debug"
docs/phase13-1-c-cursor.md # ce document
```
## Limites connues
- `PointerMotionRelative` (deltas relatifs) reste instrumenté mais n'a
jamais été observé sous QEMU PS/2 + qemu-xhci. Si un jour Redox passe
à de la souris purement relative, on aura un second `set_screen_size`
+ clamp à valider.
- L'aspect ratio entre l'espace HID 65536² et l'écran 1280×800 n'est
pas carré (1.6 vs 1) — la précision diagonale peut sembler légèrement
décalée. Strictement OK pour 13.1.c, à observer en 13.2+ avec un
vrai client graphique qui dessine sous le curseur (drag, drawing).