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

7.9 KiB
Raw Permalink Blame History

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.