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
202 lines
7.9 KiB
Markdown
202 lines
7.9 KiB
Markdown
# 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 :  — 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.*
|