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
199 lines
8.2 KiB
Markdown
199 lines
8.2 KiB
Markdown
# Phase 7.4 — Focus + raise on click
|
|
|
|
> Document produit le 2026-05-13 dans le cadre du plan directeur
|
|
> `REDOX_COSMIC_XWAYLAND_RS_PLAN.md`.
|
|
>
|
|
> **Scope strict** :
|
|
> - sur clic gauche, faire `registry.hit_test(cursor_x, cursor_y)`
|
|
> - si la surface ciblée est non-cursor : `registry.raise(sid)` +
|
|
> `set_focus(matching wl_surface)` (transfert du keyboard focus)
|
|
> - ajouter un mapping inverse `SurfaceId → wl_surface::WlSurface` pour
|
|
> pouvoir retrouver la WlSurface depuis le hit_test
|
|
> - tester avec 2 fenêtres xdg_toplevel SHM en parallèle (fork)
|
|
>
|
|
> **Hors scope 7.4** : focus-follows-mouse, click-through, move/resize
|
|
> interactifs (7.7), filtrage par client des events pointer (7.6),
|
|
> validation des serials ack_configure (7.5).
|
|
|
|
## Verdict
|
|
|
|
**✅ Click-to-raise + focus transfer validés runtime, 2 clients SHM
|
|
parallèles, 4 captures de preuve.**
|
|
|
|
| Capture | État |
|
|
|---|---|
|
|
| `phase7-4-1-initial-B-top.png` | B violet créée en dernier → au top du Z-order, A vert visible sous B. Curseur sur A (80, 80). |
|
|
| `phase7-4-2-after-click-A-raised.png` | Après clic à (80, 80) sur A : **A passe au top**, B passe en-dessous. Curseur a migré sur B (cycle programmé). |
|
|
| `phase7-4-3-after-click-B-raised.png` | Après clic à (400, 280) sur B : **B repasse au top**, A passe en-dessous. |
|
|
|
|
Trace `/tmp/comp.log` côté Redox (extraite via redoxfs après le run) :
|
|
|
|
```
|
|
[comp] phase 0: cursor → A (80, 80)
|
|
[frontend] focus change: None → Some(SurfaceId(0)) # A créée
|
|
[frontend] focus change: Some(SurfaceId(0)) → Some(SurfaceId(1)) # B créée (commit auto-focus)
|
|
[comp] 1 input events from inputd # mouse_button 1 = press
|
|
[frontend] left-click @ (80, 80) → hit_test = Some(SurfaceId(0)) # A trouvée
|
|
[frontend] focus change: Some(SurfaceId(1)) → Some(SurfaceId(0)) # focus → A
|
|
[comp] 1 input events from inputd # mouse_button 0 = release
|
|
[comp] phase 1: cursor → B (400, 280)
|
|
[comp] 1 input events from inputd # 2e clic press
|
|
[frontend] left-click @ (400, 280) → hit_test = Some(SurfaceId(1)) # B trouvée
|
|
[frontend] focus change: Some(SurfaceId(0)) → Some(SurfaceId(1)) # focus → B
|
|
[comp] 1 input events from inputd # release
|
|
```
|
|
|
|
Le pipeline complet est validé end-to-end :
|
|
`mouse_button QEMU → ps2d → inputd → InputBackend::poll →
|
|
RedoxInputEvent::PointerButton → WaylandFrontend::forward_input →
|
|
SurfaceRegistry::hit_test → registry.raise + set_focus →
|
|
wl_keyboard.leave/enter broadcast`.
|
|
|
|
## Modifications apportées
|
|
|
|
### `redox-wl-wayland-frontend`
|
|
|
|
**`WaylandFrontend`** : nouveau champ
|
|
```rust
|
|
surfaces_by_id: HashMap<SurfaceId, wl_surface::WlSurface>,
|
|
```
|
|
peuplé dans le handler `wl_compositor::Request::CreateSurface` (en
|
|
récupérant le retour de `data_init.init(id, ...)`), nettoyé dans
|
|
`wl_surface::Request::Destroy` avec en plus le clear de
|
|
`focused_surface` et `cursor_surface_id` si la surface détruite était
|
|
ces références (évite les wl_surface fantômes dans les events suivants).
|
|
|
|
**`forward_input(PointerButton { left, .. })`** : avant l'envoi des
|
|
events button aux pointers, si `left == true` :
|
|
1. `self.registry.hit_test(cursor_x, cursor_y) -> Option<SurfaceId>`
|
|
2. Si `Some(sid)` et la surface n'est pas un curseur
|
|
(`is_cursor.load() == false`), récupérer la `wl_surface` dans
|
|
`surfaces_by_id`, faire `registry.raise(sid)` puis
|
|
`set_focus(Some(surf))`.
|
|
|
|
Le `set_focus` envoie déjà les `wl_keyboard.leave` / `wl_keyboard.enter`
|
|
et `wl_pointer.leave` / `wl_pointer.enter` sur les bons wl_surface, et
|
|
ses modifiers reset (cf phase 7.2). Pas de duplication de code.
|
|
|
|
**Instrumentation** : `println!` du frontend à chaque clic
|
|
(`[frontend] left-click @ (...) → hit_test = ...`) et à chaque
|
|
changement de focus (`[frontend] focus change: old_sid → new_sid`).
|
|
Sort sur stdout du processus compositor ; le service init redirige
|
|
vers `/tmp/comp.log` côté Redox pour analyse post-run.
|
|
|
|
### `redox-wl-test-client-shm-two` (nouveau crate)
|
|
|
|
Binaire qui `fork()` une fois. Parent et enfant exécutent chacun la
|
|
même séquence de connexion Wayland + xdg-shell, mais avec :
|
|
- des couleurs ARGB différentes (vert pastel pour A, magenta pour B)
|
|
- un grand pictogramme central distinctif (pyramide tronquée "A" /
|
|
deux disques empilés "B") pour identifier la fenêtre dans les
|
|
screendumps
|
|
- un sleep 800 ms côté enfant pour garantir l'ordre A créée → B créée
|
|
- timeout 160 s aligné sur le compositor
|
|
|
|
Le compositor place automatiquement A à `(60, 60)` et B à `(120, 120)`
|
|
grâce à son cascade offset 7.1, ce qui produit l'overlap voulu pour
|
|
tester le raise.
|
|
|
|
Au début (avant tout clic) : B est au top car commit le plus récent
|
|
→ politique 6.4 "dernière surface commitée = au-dessus" (à raffiner
|
|
en phase 7.6/7.7).
|
|
|
|
## Méthode de validation runtime
|
|
|
|
Limitation runtime héritée 7.3 : Redox n'a pas de driver mouse
|
|
fonctionnel sous QEMU + virtio-vga (PS/2 mouse_button OK, PS/2 et USB
|
|
tablet mouse_move muets). Pour positionner le curseur dans une zone
|
|
précise avant le clic, le compositor a embarqué temporairement un
|
|
cycle de 4 phases qui appelaient `frontend.set_cursor_position(x, y)`
|
|
toutes les 15 s entre `(80, 80)` (uniquement sur A) et `(400, 280)`
|
|
(uniquement sur B).
|
|
|
|
Pendant chaque phase, le script de test envoie via QMP :
|
|
```
|
|
mouse_button 1 # press
|
|
mouse_button 0 # release
|
|
screendump /tmp/screen-N.ppm
|
|
```
|
|
|
|
Le clic, lui, passe par le pipeline réel et déclenche le code
|
|
production `forward_input → hit_test → raise + set_focus`. Le cycle
|
|
de positionnement est retiré du binaire après validation (comme en
|
|
7.3) ; seul le câblage production est commité.
|
|
|
|
Le service init Redox utilisé (purement temporaire) :
|
|
|
|
```
|
|
# /usr/lib/init.d/40_phase74_focus
|
|
requires_weak 30_console
|
|
nowait /usr/bin/launch_phase74.sh
|
|
|
|
# /usr/bin/launch_phase74.sh
|
|
#!/bin/sh
|
|
sleep 2
|
|
/usr/bin/redox-wl-compositor > /tmp/comp.log 2>&1 &
|
|
sleep 4
|
|
/usr/bin/redox-wl-test-client-shm-two > /tmp/client.log 2>&1
|
|
```
|
|
|
|
À noter : l'init Redox ne sait pas parser correctement
|
|
`nowait sh -c "..."` avec guillemets dans la définition de service —
|
|
il faut passer par un script wrapper sur disque pour fork + redirection.
|
|
|
|
## Limitations connues (à traiter en sous-tickets ultérieurs)
|
|
|
|
- **Events pointer/keyboard broadcast** : le clic raise + focus est
|
|
correct, mais les events button/key sont encore broadcast à tous
|
|
les `wl_pointer` / `wl_keyboard` (pas filtrage par client focused).
|
|
Reportable à 7.6 (multi-clients).
|
|
- **Focus follows mouse non implémenté** : on doit cliquer pour
|
|
changer le focus (modèle click-to-focus). Optionnel selon politique
|
|
WM, pas dans le scope.
|
|
- **Pas de transition press/release tracked** : à chaque event
|
|
`PointerButton { left: true, .. }`, on rejoue le hit_test. En
|
|
pratique inputd n'envoie qu'1 event par transition donc OK, mais à
|
|
durcir en 7.5 si on observe des re-raises spurieux.
|
|
- **Surface détruite tout en étant focused** : géré (focused_surface
|
|
cleared, cursor_surface_id cleared), mais pas de re-focus auto sur
|
|
la prochaine surface du Z-order. Le client suivant qui commit
|
|
reprendra le focus via la politique 6.4 — acceptable pour 7.4.
|
|
- **Pas encore de fenêtre "popup"** : focus-stealing par xdg_popup
|
|
reportable.
|
|
|
|
## Critère de fin 7.4
|
|
|
|
> Un clic gauche dans la fenêtre cliquée la fait passer au top du
|
|
> Z-order et lui transfère le keyboard focus, sans déstabiliser le
|
|
> compositor, multi-clients OK.
|
|
|
|
**✅ Validé.** 2 clients SHM en parallèle, 2 transitions Z-order
|
|
distinctes, traces frontend explicites confirmant le hit_test et
|
|
les changements de focus.
|
|
|
|
## Code
|
|
|
|
```
|
|
crates/redox-wl-wayland-frontend/ # +~30 lignes (HashMap mapping, hit_test branch, logs)
|
|
crates/redox-wl-test-client-shm-two/ # nouveau crate (~330 lignes)
|
|
```
|
|
|
|
## Suite phase 7.5
|
|
|
|
Robustesse paquet A : tests négatifs sur les protocoles. Clients qui :
|
|
- ferment sans destroy propre
|
|
- envoient ack_configure avec un mauvais serial
|
|
- détruisent un buffer encore attaché
|
|
- attachent un buffer avec width/height/stride invalides
|
|
- détruisent un xdg_surface avant son toplevel
|
|
- envoient des messages au mauvais moment du cycle de vie
|
|
|
|
But : le compositor doit dans tous les cas soit ignorer proprement,
|
|
soit fermer la connexion du client fautif avec un `post_error`, mais
|
|
JAMAIS paniquer.
|
|
|
|
Estimé : 1-2 sessions.
|
|
|
|
---
|
|
|
|
*Fin du document de phase 7.4.*
|