redox-wayland-compositor/docs/phase7-1-xdg-shell.md
Votre Nom 4bff319c7f 🎉 Phase 7.1 — xdg-shell minimal validé runtime
Capture preuve : docs/phase7-1-xdg-toplevel.png — fenêtre 320x240 du
client externe positionnée à (60, 60) par le compositor (cascade par
défaut), avec bandes verticales arc-en-ciel + bordure noire 2px.

Différence visuelle vs phase 6.4 : la fenêtre est maintenant
positionnée par le compositor (pas plaquée à 0,0).

Modifications redox-wl-wayland-frontend :
- + dep wayland-protocols (feature server)
- xdg_wm_base global v5
- XdgSurfaceData { wl_surface, last_serial, acked_serial }
- XdgToplevelData { title, app_id }
- SurfaceData.xdg_pending_initial_configure : bloque l'affichage tant
  que ack-configure pas reçu (sémantique xdg-shell standard)
- WaylandFrontend.next_xdg_serial : counter monotone serial
- WaylandFrontend.next_toplevel_index : cascade position +60 par fenêtre
- DEFAULT_TOPLEVEL_SIZE = 640x480 envoyé en initial configure
- Dispatch xdg_wm_base : GetXdgSurface, CreatePositioner (no-op),
  Pong (no-op), Destroy (no-op)
- Dispatch xdg_positioner + xdg_popup : no-op (out of scope)
- Dispatch xdg_surface :
  - GetToplevel → cascade pos + envoie xdg_toplevel.configure(640,480)
    + xdg_surface.configure(serial)
  - AckConfigure(serial) → enregistre + débloque affichage
  - Destroy → cache la surface
- Dispatch xdg_toplevel :
  - SetTitle / SetAppId → stocke
  - tout le reste ignoré (move, resize, maximized, ...)

redox-wl-test-client-shm adapté :
- + dep wayland-protocols (feature client)
- bind 3 globals : wl_compositor, wl_shm, xdg_wm_base
- Dispatch XdgWmBase::Event::Ping → pong (au cas où)
- Dispatch XdgSurface::Event::Configure → store serial
- Dispatch XdgToplevel::Event::Configure / Close → log
- Workflow runtime :
  1. create_surface + get_xdg_surface + get_toplevel
  2. set_title / set_app_id
  3. surface.commit (initial empty)
  4. attente Configure event (timeout 5s)
  5. ack_configure(serial)
  6. shm + buffer + attach + commit POST-ack
  7. boucle 25s
  8. destroy propre

Init submodule wayland-rs/wayland-protocols/protocols (XML files
gitlab.freedesktop.org/wayland/wayland-protocols).

Validation runtime QEMU complète :
[client] globals : compositor=true shm=true xdg_wm_base=true
[client] xdg_toplevel configure suggéré : 640x480
[client] initial configure reçu, serial=1
[client] ack_configure(1)
[client] buffer attach + damage + commit POST-ack
[comp]  tick=30..450 surfaces=1 elapsed=1.2..18.2s
PNG capturée à T+14s : surface visible à (60,60), bandes RGB+jaune+
violet+orange comme attendu.

Critère de fin 7.1 validé : client xdg-shell crée toplevel, reçoit
configure, ack, commit, affiche via shm, sans panic serveur, 18s
stable, destroy propre.

Limitations connues : focus, cursor, move/resize, multi-client,
robustesse — sous-tickets 7.2-7.7.

Image Redox restaurée à boot Orbital normal.

docs/phase7-1-xdg-shell.md : compte-rendu complet, cycle de vie,
limitations, plan 7.2.

Leyoda 2026 – GPLv3
2026-05-09 14:34:45 +02:00

202 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Phase 7.1 — xdg-shell minimal
> Document produit le 2026-05-09 dans le cadre du plan directeur
> `REDOX_COSMIC_XWAYLAND_RS_PLAN.md`.
>
> **Scope strict** validé par user :
> - implémenter `xdg_wm_base`
> - gérer `get_xdg_surface(wl_surface)`
> - gérer `xdg_surface.get_toplevel`
> - envoyer `configure` (xdg_toplevel + xdg_surface)
> - recevoir `ack_configure`
> - gérer `commit` après configure
> - supporter `set_title` / `set_app_id` (stockés sans usage UI)
> - destroy propre via `xdg_toplevel.destroy` / `xdg_surface.destroy`
>
> **Hors scope 7.1** : focus, cursor, move/resize, decorations,
> multi-client avancé.
## Verdict
**✅ Cycle xdg-shell complet validé runtime.**
Capture preuve : ![](phase7-1-xdg-toplevel.png) — fenêtre 320×240
positionnée à `(60, 60)` par le compositor (cascade par défaut), avec
bandes verticales arc-en-ciel + bordure noire 2px peintes par le client.
## Cycle de vie xdg-shell implémenté
```
client compositor
────── ──────────
Connection::connect_to_env
get_registry
◄──── wl_compositor + wl_shm + xdg_wm_base
advertised
bind globals
compositor.create_surface ────►
SurfaceData { id: registry.create(),
xdg_pending: false }
wm_base.get_xdg_surface ────►
XdgSurfaceData { wl_surface, serial: 0 }
sd.xdg_pending_initial_configure = true
xdg_surface.get_toplevel ────►
XdgToplevelData::default()
next_toplevel_index += 1
surface.x = +60, surface.y = +60 (cascade)
next_xdg_serial = 1
◄──── xdg_toplevel.configure(640, 480, [])
◄──── xdg_surface.configure(serial=1)
────────────────────────────────────────
toplevel.set_title(...) ────► data.title = Some(...)
toplevel.set_app_id(...) ────► data.app_id = Some(...)
xdg_surface.ack_configure(1)────► data.acked_serial = 1
sd.xdg_pending_initial_configure = false
shm.create_pool(fd, size) ────► ShmPool::new(fd) → mmap
pool.create_buffer(...) ────► BufferData { pool, offset, w, h, ... }
surface.attach(buffer) ────► sd.pending_buffer = Some(buf)
surface.damage_buffer(...) ────► (no-op tracking 7.1)
surface.commit ────►
!xdg_pending → continue
read shm pixels via mmap
SurfaceBuffer::from_pixels(...)
registry.modify_pending(id, |s|
s.buffer = Some(sb); s.visible = true)
registry.commit(id)
registry.raise(id)
(toplevel.destroy) ────► no-op (wl_surface destroy fera le reste)
(xdg_surface.destroy) ────► surface.visible = false; commit
(surface.destroy) ────► registry.destroy(id)
```
## Modifications apportées
### `redox-wl-wayland-frontend`
Ajout dep `wayland-protocols` (feature `server`).
Constants :
- `XDG_WM_BASE_VERSION: u32 = 5`
- `DEFAULT_TOPLEVEL_SIZE: (i32, i32) = (640, 480)`
- `CASCADE_OFFSET: i32 = 60`
Nouveaux UserData :
- `XdgSurfaceData { wl_surface, last_serial, acked_serial }`
- `XdgToplevelData { title, app_id }`
Modif `SurfaceData` :
- ajout `xdg_pending_initial_configure: Mutex<bool>` qui bloque l'affichage
tant que pas ack-configure (sémantique xdg-shell standard)
Modif `WaylandFrontend` :
- `next_xdg_serial: u32` (counter monotone pour les configure serials)
- `next_toplevel_index: u32` (counter cascading)
Dispatch ajoutés :
- `GlobalDispatch<XdgWmBase>` + `Dispatch<XdgWmBase>`
- `Dispatch<XdgPositioner>` (no-op)
- `Dispatch<XdgPopup>` (no-op)
- `Dispatch<XdgSurface, Arc<XdgSurfaceData>>` :
- `GetToplevel` → cascade position + envoie initial configure
- `AckConfigure(serial)` → débloque affichage
- `Destroy` → cache la surface
- `Dispatch<XdgToplevel, Arc<XdgToplevelData>>` :
- `SetTitle`/`SetAppId` → stocke
- reste : ignoré (move, resize, set_*, etc.)
### `redox-wl-test-client-shm`
Ajout dep `wayland-protocols` (feature `client`).
Bind 3 globals : `wl_compositor`, `wl_shm`, `xdg_wm_base`.
Séquence runtime :
1. `compositor.create_surface()`
2. `wm_base.get_xdg_surface(&surface)`
3. `xdg_surface.get_toplevel()`
4. `toplevel.set_title("Phase 7.1 client")`
5. `toplevel.set_app_id("redox.wl.test.client.shm")`
6. `surface.commit()` — initial commit pour signaler "ready"
7. Attente du `xdg_surface.configure(serial)` event (avec timeout 5s)
8. `xdg_surface.ack_configure(serial)`
9. `shm.create_pool(fd)` + `pool.create_buffer` + pattern ARGB
10. `surface.attach(buffer)` + `damage_buffer` + `commit`
11. Boucle 25s vivante
12. `toplevel.destroy()` + `xdg_surface.destroy()` + `surface.destroy()`
Bonus : Dispatch sur `XdgWmBase::Event::Ping` répond `pong(serial)` (le
compositor ne l'envoie pas en 7.1 mais c'est gratuit côté client).
## Preuve runtime
Logs serial QEMU complets :
```
[client] connect to compositor
[comp] CRTC pris
[comp] Wayland socket : /tmp/redox-wl-comp.sock
[client] globals : compositor=true shm=true xdg_wm_base=true
[client] xdg_toplevel créé, attente initial configure
[client] xdg_toplevel configure suggéré : 640x480
[client] initial configure reçu, serial=1
[client] ack_configure(1)
[client] buffer attach + damage + commit POST-ack
[comp] tick=30 surfaces=1 elapsed=1.2s
... (compositeur stable 18s avec 1 surface) ...
[comp] tick=450 surfaces=1 elapsed=18.2s
```
Capture frame T+14s : fenêtre client à `(60, 60)`, bandes RGB+orange+jaune+
violet visibles avec bordure noire.
## Limitations connues (à traiter en sous-tickets ultérieurs)
- **Pas de focus** → toutes les surfaces sont "actives", pas de keyboard
enter/leave events. Phase 7.4.
- **Pas de cursor visible** côté écran. Phase 7.3.
- **Pas de move/resize interactifs** (xdg_toplevel.Move/Resize ignorés).
Phase 7.7.
- **Window geometry ignoré** (utilisé pour les decorations CSD que nous
ne supportons pas).
- **Multi-client non testé** (1 seul client en 7.1). Phase 7.6.
- **Pas de validation des serials** : si un client envoie un mauvais
ack_configure, on accepte sans broncher. À durcir en phase 7.5
(robustesse).
## Critère de fin 7.1
> Un client Wayland externe qui utilise xdg-shell peut créer une fenêtre
> toplevel visible, recevoir son configure, ack, commit, puis afficher
> via shm sans panic serveur.
**✅ Validé.** Test runtime 18+ secondes, surface visible, aucun panic
serveur ni client, destroy propre à la fin.
## Code
```
crates/redox-wl-wayland-frontend/ # +220 lignes pour xdg-shell (5 Dispatch + helpers)
└── Cargo.toml # + wayland-protocols/server
crates/redox-wl-test-client-shm/ # adapté entièrement à xdg-shell
└── Cargo.toml # + wayland-protocols/client
```
Submodule `wayland-rs/wayland-protocols/protocols/` initialisé via
`git submodule update --init --depth 1`.
## Suite phase 7.2
`wl_seat` + `wl_pointer` + `wl_keyboard` :
- Routage des events `InputBackend` vers la surface focalisée
- Décision XKB (recommandation : `xkeysym` Rust pur, layout US uniquement
au début ; xkbcommon C reportable)
- Premier client qui réagit aux events keyboard
- Tester via QEMU `sendkey` avec un client qui log les events reçus
Estimé 2 sessions.
---
*Fin du document de phase 7.1.*