203 lines
7.9 KiB
Markdown
203 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.*
|