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
7.9 KiB
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
commitaprès configure- supporter
set_title/set_app_id(stockés sans usage UI)- destroy propre via
xdg_toplevel.destroy/xdg_surface.destroyHors 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 = 5DEFAULT_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 configureAckConfigure(serial)→ débloque affichageDestroy→ 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 :
compositor.create_surface()wm_base.get_xdg_surface(&surface)xdg_surface.get_toplevel()toplevel.set_title("Phase 7.1 client")toplevel.set_app_id("redox.wl.test.client.shm")surface.commit()— initial commit pour signaler "ready"- Attente du
xdg_surface.configure(serial)event (avec timeout 5s) xdg_surface.ack_configure(serial)shm.create_pool(fd)+pool.create_buffer+ pattern ARGBsurface.attach(buffer)+damage_buffer+commit- Boucle 25s vivante
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
InputBackendvers la surface focalisée - Décision XKB (recommandation :
xkeysymRust pur, layout US uniquement au début ; xkbcommon C reportable) - Premier client qui réagit aux events keyboard
- Tester via QEMU
sendkeyavec un client qui log les events reçus
Estimé 2 sessions.
Fin du document de phase 7.1.