# 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` 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` + `Dispatch` - `Dispatch` (no-op) - `Dispatch` (no-op) - `Dispatch>` : - `GetToplevel` → cascade position + envoie initial configure - `AckConfigure(serial)` → débloque affichage - `Destroy` → cache la surface - `Dispatch>` : - `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.*