redox-wayland-compositor/docs/existing-redox-gui.md
Votre Nom 53e6626231 Initial commit: phases 1-3 du portage Wayland Rust pour Redox OS
Plan directeur 14 phases / 5 ans (REDOX_COSMIC_XWAYLAND_RS_PLAN.md).

Phase 1 — Audit Redox (docs/existing-redox-gui.md, 486 lignes) :
- Orbital, graphics-ipc (API DRM compatible Linux subset KMS), inputd, vesad
- relibc support : AF_UNIX, SCM_RIGHTS, shm_open, mmap, poll
- 3 manques identifiés : memfd_create, keymap XKB, AT-SPI

Phase 2 — Validation primitives sur Redox via redoxer (5 tests + 1 POC) :
- test-unix-socket : SOCK_STREAM Wayland-shaped roundtrip
- test-fd-passing : SCM_RIGHTS mono-process (artefact kernel)
- test-fd-passing-fork : SCM_RIGHTS multi-process (validation Wayland critique)
- test-shm-open : shm_open + mmap + persistance + unlink
- test-poll-multifd : poll() multiplexing + POLLHUP
- poc-pixels : datapath shm + SCM_RIGHTS bout en bout (10000 pixels ARGB)

Phase 3 — wayland-rs sur Redox (compile + runtime) :
- wayland-{scanner,backend,server,client} compilent pour x86_64-unknown-redox
  sans patch upstream (rustix supporte Redox via libc backend)
- test-handshake : server/client wl_registry handshake roundtrip
- test-shm-pipeline : pipeline complet (ListeningSocket Unix réel + fd passing
  via wl_shm.create_pool + wl_shm_pool + wl_buffer + wl_surface + commit +
  serveur lit pixels via fd reçu, validation pixel-perfect)

Verdict phase 3 : wayland-rs upstream est viable sur Redox out-of-the-box,
le port "Wayland sur Redox" est désormais un problème de compositor à écrire,
pas de stack à porter.

Prérequis build : redoxer (pas cargo direct, car CMSG_NXTHDR/CMSG_DATA
ne sont pas linkés autrement vers librelibc.a).

Leyoda 2026 – GPLv3
2026-05-08 17:41:55 +02:00

486 lines
19 KiB
Markdown

# Audit Phase 1 — Existant Redox GUI
> Document produit le 2026-05-08 dans le cadre du plan directeur
> `REDOX_COSMIC_XWAYLAND_RS_PLAN.md` (phase 1 : audit avant code).
>
> **Périmètre** : comprendre l'architecture graphique actuelle de Redox
> (Orbital + drivers + primitives) pour calibrer le portage Wayland.
## Résumé exécutif
Redox dispose **déjà d'une fondation graphique plus mûre que prévu** :
- **Une API DRM compatible Linux** (subset KMS) exposée via `graphics-ipc` et utilisée par Orbital
- **Plusieurs drivers** : VESA firmware (`vesad`), virtio-gpu (`virtio-gpud`), Intel HD (`ihdgd`)
- **Un input multiplexer** (`inputd`) qui gère VT switching, keymaps, producteurs/consommateurs
- **Orbital** très compact (~3900 lignes Rust) qui valide le pattern serveur-clients via scheme
Côté primitives système, **l'essentiel de ce que Wayland exige est déjà là** :
- AF_UNIX sockets via le scheme `chan:`
- `sendmsg`/`recvmsg` avec **SCM_RIGHTS implémenté** (fd passing) ✅
- `shm_open` via `/scheme/shm/`
- `mmap MAP_SHARED`
- `poll`/`epoll`
- ⚠️ `memfd_create` : numéro syscall défini mais implémentation à vérifier
**Conclusion** : la phase 2 (primitives OS) du plan directeur sera moins lourde que craint.
La majorité des briques existent — il faudra surtout valider le comportement par tests dédiés.
---
## 1. Architecture Orbital
### 1.1 Point d'entrée et boot
`orbital/src/main.rs` (82 lignes) :
```text
main()
└─ orbital()
├─ lit env "VT" (passé par inputd au boot)
├─ Orbital::open_display() ← ouvre display via inputd
├─ exec "inputd -A <vt>" ← s'enregistre comme handler du VT
├─ Config::from_path("/ui/orbital.toml")
├─ OrbitalScheme::new(displays, config)
└─ orbital.run(scheme, login_cmd) ← event loop principal
```
**Note séquence de boot** : c'est `inputd` qui orchestre l'ouverture du display
au moment où le VT s'active. Orbital ne touche pas directement aux drivers
graphiques — il passe par inputd qui lui retourne un fd `/scheme/.../v2/...`.
### 1.2 Event loop (`core/mod.rs`, 662 lignes)
Architecture deux-fd :
```text
EventQueue<Source>
├─ Scheme fd (clients Orbital) → SchemeSync trait
└─ Input fd (depuis inputd consumer) → ConsumerHandleEvent::Events
```
**Patterns clés** :
- Utilise `event::EventQueue` (crate `redox_event`) — équivalent Redox de
`wl_event_loop` Wayland ou `epoll` Linux
- Chaque source a son fd, multiplexé en édge-triggered READ
- Les requests scheme bloquantes sont stockées dans `delayed: VecDeque` puis
réveillées quand un input arrive (équivalent du `wl_display_dispatch_pending`
Wayland)
**Réutilisable pour le compositor Wayland** :
- Le pattern EventQueue avec sources typées (`user_data!` macro) est exactement
ce qu'il faut pour le compositor Wayland
- Le mécanisme `delayed` pour les reads bloquants est réutilisable tel quel
### 1.3 Protocole `orbital:` (le scheme)
Implémenté dans `core/mod.rs::OrbitalHandler::SchemeSync` et `scheme.rs` (1721 lignes).
**API client** (ce qu'un client appelle pour ouvrir une fenêtre) :
```text
open("/scheme/orbital/<flags>/<x>/<y>/<w>/<h>/<title>")
→ returns window_fd
write(window_fd, "T,nouveau titre") → titre
write(window_fd, "S,800,600") → resize
write(window_fd, "P,100,200") → position
write(window_fd, "Y,0,0,800,600,...") → damage rectangles
write(window_fd, "F,t,1") → flag transparent on
read(window_fd, &mut events) → pull events (clavier, souris, ...)
mmap_prep(window_fd, ...) → obtient un buffer mmap zero-copy
fsync(window_fd) → commit (équivalent wl_surface.commit)
```
**Concepts à transposer en Wayland** :
| Orbital | Équivalent Wayland |
|---|---|
| open() avec path verbeux | `xdg_surface` + `xdg_toplevel` configure |
| write("S,...") resize | `xdg_toplevel.configure` + ack |
| write("Y,...") damage | `wl_surface.damage_buffer` |
| mmap_prep | `wl_shm_pool.create_buffer` + `wl_surface.attach` |
| fsync | `wl_surface.commit` |
| read events | event queue Wayland |
**Limites du protocole Orbital** :
- API basée sur strings dans les writes — pas type-safe, pas de versioning
- Une seule sémantique d'extension (préfixes "A","D","F","M","P","S","T","Y")
- Pas de négociation de capabilities côté serveur
- Buffer mmap'é direct — moins flexible que `wl_shm_pool` mais plus rapide
### 1.4 Composition (`compositor.rs`, 281 lignes)
Compositor logiciel CPU pur :
- `Image::roi_mut()` pour écrire dans le framebuffer
- `Color` ARGB8888
- Damage rectangles propagés aux clients via `Y,...`
- Curseur SW si pas de hardware cursor disponible (cf `core/display.rs:140`
`set_client_capability(CursorPlaneHotspot, true)`)
**Réutilisable directement** : la logique damage tracking + composition CPU
est exactement ce qu'il faut pour la phase 6-7 du plan (`wl_shm` + composition).
---
## 2. Stack graphique Redox
### 2.1 API `graphics-ipc` (la lib commune)
`base/drivers/graphics/graphics-ipc/src/lib.rs` (127 lignes).
**Découverte critique** (commentaire ligne 13-15) :
> *The v2 graphics API allows creating framebuffers on the fly, using them for
> page flipping and handles all displays using a single fd. This is basically
> a subset of the Linux DRM interface with a couple of custom ioctls in the
> place of the KMS ioctls that are missing.*
**Conséquence** : Redox a une **API DRM compatible Linux** (subset KMS).
La crate Rust `drm` upstream fonctionne directement sur Redox.
API exposée :
```rust
V2GraphicsHandle::from_file(file)
├─ resource_handles() liste connectors/encoders/CRTCs
├─ get_connector(handle, force_probe)
├─ get_encoder(handle)
├─ create_dumb_buffer(size, fourcc, bpp)
├─ map_dumb_buffer(buffer) mmap CPU-accessible
├─ add_framebuffer(buffer, depth, bpp)
├─ set_crtc(crtc, fb, position, connectors, mode)
├─ dirty_framebuffer(fb, &[ClipRect]) damage flush
├─ move_cursor / set_cursor2
└─ destroy_*
```
`CpuBackedBuffer` (lignes 46-127) :
- Wrapper sur `DumbBuffer` DRM
- Optionnel : shadow buffer host (write-combining mitigation)
- `sync_rect()` pour copier shadow → on-screen par damage
**Implications pour le portage Wayland** :
1. Pas besoin de réinventer le display backend — l'API DRM existe
2. Peut être utilisée telle quelle par un compositor Wayland Rust
3. `CpuBackedBuffer::shadow_buf()` retourne `&mut [u8]` — exactement
le format SHM que Wayland attend pour la composition
### 2.2 Drivers graphiques disponibles
Localisation : `base/drivers/graphics/`.
| Driver | Cible | Statut | Notes |
|---|---|---|---|
| **`vesad`** (133 + 275 lignes) | VESA framebuffer firmware (BIOS/UEFI) | Stable | Lit FRAMEBUFFER_* env, support multi-FB |
| **`virtio-gpud`** (615 + 528 lignes) | virtio-gpu (QEMU/KVM) | Actif | C'est ce qui tourne sur ton image desktop |
| **`ihdgd`** | Intel HD Graphics | Actif | Driver natif HW |
| `fbcond` | Console framebuffer | — | Texte console |
| `fbbootlogd` | Boot log framebuffer | — | Affichage early boot |
| `console-draw` | Helpers dessin console | — | Lib commune |
| `driver-graphics` | Couche d'abstraction commune (`GraphicsScheme`) | — | C'est elle qui exporte `display.<driver>` |
Tous publient leur scheme sous forme `display.<nom>` (ex: `display.vesa`).
### 2.3 Chemin d'ouverture du display
```text
Orbital
└─> ConsumerHandle::new_vt() // /scheme/input/consumer
└─> open_display_v2()
└─> fpath() pour récupérer le path complet
└─> open("/scheme/.../v2/<display>") // fd graphics-ipc v2
└─> V2GraphicsHandle::from_file
```
C'est `inputd` qui détient la connaissance du driver graphique actif et
expose un chemin standard. Orbital ne hard-code pas `vesa` ou `virtio-gpu`
il demande à inputd.
**Implication pour le compositor Wayland** : suivre le même pattern.
Demander à `inputd` quel display est associé au VT, ouvrir le fd retourné,
le wrapper en `V2GraphicsHandle`. Pas de hardcode de driver.
### 2.4 `vesad` — le chemin firmware framebuffer
`base/drivers/graphics/vesad/src/main.rs` (133 lignes).
**Bootstrap** (lignes 19-44) :
```rust
FRAMEBUFFER_WIDTH // hex string, ex "780" → 1920
FRAMEBUFFER_HEIGHT // ex "438" → 1080
FRAMEBUFFER_ADDR // adresse physique du FB fournie par firmware
FRAMEBUFFER_STRIDE // bytes par ligne
```
Le bootloader (UEFI/BIOS) place ces variables avant de chainer vers le kernel.
`vesad` mappe l'adresse physique en mémoire userspace et expose le FB via
`GraphicsScheme`.
**Fallback bootloader env** (lignes 60-84) : `vesad` lit `/scheme/sys/env`
pour `FRAMEBUFFER1`, `FRAMEBUFFER2`, ... — support multi-écran natif au boot
si le firmware en remonte plusieurs.
**Implication** : pour la phase 4 du plan (backend display), partir de `vesad`
comme chemin de validation initial = sûr et simple. virtio-gpud est plus
puissant mais plus complexe (gpu commandes, virgl optionnel).
---
## 3. Stack input
### 3.1 `inputd` — le multiplexer
`base/drivers/inputd/src/{lib.rs,main.rs}` (211 + 663 lignes).
**Schemes exposés** :
| Path | Pour | Rôle |
|---|---|---|
| `/scheme/input/consumer` | Orbital, futur compositor | reçoit les events triés (orbclient::Event) |
| `/scheme/input/consumer_bootlog` | bootlog | events early-boot |
| `/scheme/input/producer` | drivers ps2d, usbhidd | écrit des events bruts |
| `/scheme/input/control` | gestion VT/keymap | active VT, change keymap |
| `/scheme/input/handle/<scheme>` | délégation à un autre scheme | déléguer un VT à un autre serveur |
| `/scheme/input/handle_early/<scheme>` | idem mais avant init VT | bootlog, etc. |
**API ConsumerHandle** :
```rust
let h = ConsumerHandle::new_vt()?;
let display_fd = h.open_display_v2()?; // ← ouvre le display de ce VT
let evt_fd = h.event_handle(); // ← fd à mettre dans EventQueue
match h.read_events(&mut events)? {
ConsumerHandleEvent::Events(slice) => /* traite events */,
ConsumerHandleEvent::Handoff => /* VT change, on perd le contrôle */,
}
```
Le **handoff via `ESTALE`** est crucial : quand le VT switch (Ctrl-Alt-F2),
inputd retourne ESTALE — Orbital sait qu'il doit se mettre en veille
proprement. Le compositor Wayland devra gérer la même sémantique.
### 3.2 Drivers input
`base/drivers/input/` :
- `ps2d` — clavier/souris PS/2 (legacy + KVM)
- `usbhidd` — HID USB (clavier, souris, tablette)
Tous écrivent vers `/scheme/input/producer`. inputd dédoublonne, séquence,
attribue au VT actif, transmet au consumer.
### 3.3 Format d'event
`orbclient::Event` (struct C-compatible) — défini dans la crate `orbclient`.
Types : `KeyEvent`, `MouseEvent`, `MouseRelativeEvent`, `ButtonEvent`,
`ScrollEvent`, `FocusEvent`, `QuitEvent`, etc.
**Pour Wayland** :
- Mapping `orbclient::KeyEvent``wl_keyboard.key` : direct
- Codes de touches : `orbclient` utilise des scancodes — il faudra
traduire vers les **keycodes XKB** que Wayland attend
- ⚠️ **xkb keymap** : à concevoir, voir section 5
---
## 4. Primitives OS pour Wayland (relibc)
Vérifications effectuées dans `~/Projets/Redox/relibc/src/`.
### 4.1 Sockets Unix (AF_UNIX)
`platform/redox/socket.rs` :
- AF_UNIX **bind/connect** : implémenté via syscall direct (commentaire ligne 69 :
*"bind/connect with AF_UNIX were replaced with SYS_CALL"*)
- Path encoding : `chan:<path>` (ligne 177 : `buf.starts_with(b"chan:")`)
- SOCK_STREAM ✅ et SOCK_DGRAM ✅ supportés
- Compatible avec l'usage Wayland (`$XDG_RUNTIME_DIR/wayland-N`)
### 4.2 fd passing — `sendmsg` / `recvmsg` avec SCM_RIGHTS
**🟢 Critique** : `platform/redox/socket.rs` :
- Lignes 272 et 447 : `(SOL_SOCKET, SCM_RIGHTS)` traité dans cmsghdr
- Lignes 835 et 924 : `recvmsg` / `sendmsg` implémentés en interne
C'est **le pré-requis n°1** du plan (risque majeur 1) — il est rempli.
Il faut quand même écrire des tests dédiés en phase 2 pour valider :
- Passage simple de fd entre deux processus
- Multiple fds dans un même cmsg
- Passage d'un fd shm + lecture côté receveur
### 4.3 Mémoire partagée — `shm_open` + mmap
**`shm_open`** : `header/sys_mman/mod.rs:185` :
```rust
pub unsafe extern "C" fn shm_open(name, oflag, mode) -> c_int {
// traduit "/foo" → "/scheme/shm/foo" puis open() classique
}
```
Mécanisme : un scheme dédié `/scheme/shm/` sert de namespace
(équivalent `/dev/shm` Linux mais via le scheme system).
**`mmap MAP_SHARED`** : présent (`header/sys_mman/mod.rs`). Tous les buffers
DRM dumb sont déjà mappés ainsi par `graphics-ipc`.
### 4.4 ⚠️ `memfd_create`
- Numéro syscall défini : `__NR_memfd_create = 319` (x86_64)
- **Implémentation** : pas vue dans la sortie grep — probablement absente
ou stub
- **Impact pour Wayland** : libwayland-client moderne préfère `memfd_create`
pour les shm pools (pas de path needed). Fallback : `shm_open` avec nom
unique → fonctionne, juste un peu plus verbeux
À traiter en phase 2 :
- Soit implémenter `memfd_create` au-dessus de `shm_open + shm_unlink`
- Soit ajouter une couche `cfg(target_os = "redox")` côté `wayland-client`
qui force le path shm_open
### 4.5 Event loop / poll
- `header/poll/mod.rs` : `poll()` POSIX disponible
- `header/sys_select/mod.rs` : `select` POSIX
- `header/sys_epoll/mod.rs` : `epoll_*` (probablement émulé sur Redox via
l'EventQueue native — à vérifier)
- **Native Redox** : `redox_event::EventQueue` — utilisé par Orbital, plus
idiomatique. Pour le compositor Rust pur, autant l'utiliser directement
plutôt que de passer par poll() POSIX.
---
## 5. Manques identifiés (à traiter dans le plan)
### 5.1 Pas de keymap XKB sur Redox
Wayland exige que le serveur envoie un keymap XKB au client (`wl_keyboard.keymap`
event, format `XKB_V1`). Or :
- Redox utilise des scancodes orbclient
- Pas de `xkbcommon` packagé sur Redox (à confirmer en cherchant la recette)
- Pas de fichiers de keymap XKB système
**À traiter en phase 5 (backend input)** :
- Option A : porter `libxkbcommon` (C, ~10k loc)
- Option B : utiliser la crate Rust `xkb` (wrapper xkbcommon, dépend du C)
- Option C : implémenter un sous-ensemble XKB en Rust (plus long mais
cohérent avec l'esprit du projet)
Recommandation : **Option C** mais avec scope minimal (US layout + variantes
basiques) au début. La crate `xkeysym` peut servir de base.
### 5.2 Pas d'AT-SPI / dbus pour l'accessibilité
- Phase 11 du plan vise navigation clavier + métadata d'accessibilité
- Sur Linux, AT-SPI transporte ces métadata via dbus
- Dbus existe sur Redox mais portage limité ; AT-SPI n'est pas porté
- À concevoir : protocole Redox simple d'accessibilité, ou wayland-protocols
custom (text-input-v3 comme point de départ)
### 5.3 GPU userspace driver manquant
- vesad et virtio-gpud existent mais pas de DRM driver "GPU complet" hors virtio
- Mesa3D Redox = LLVMpipe seulement (CPU)
- Phase 12 du plan = GPU expérimental — restera CPU pour le MVP
- À documenter : aucune accélération hardware n'est garantie pour les phases 0-11
### 5.4 Pas de session manager / VT API stable
- Sur Linux : `logind` / `seatd` gèrent les VT et l'accès aux devices
- Sur Redox : `inputd` joue un rôle similaire mais pas équivalent
- Pour Smithay (phase 13), il faudra un backend session Redox basé sur inputd
---
## 6. Ce qui est réutilisable directement
| Composant Redox actuel | Usage dans le compositor Wayland |
|---|---|
| `redox_event::EventQueue` | Boucle principale du compositor |
| `redox-scheme::SchemeSync` | Si on choisit aussi de publier un scheme `wayland:` |
| `graphics-ipc::V2GraphicsHandle` | Backend display (phase 4) — direct |
| `graphics-ipc::CpuBackedBuffer` | wl_shm composition (phase 6) — direct |
| `inputd::ConsumerHandle` | Backend input (phase 5) — direct |
| `orbital/src/compositor.rs` damage tracking | Référence pour notre damage logic (phase 6-7) |
| `orbital/src/scheme.rs` focus + raccourcis | Référence pour focus management (phase 7) |
| `redox-log` | Logging |
| Crate `drm` upstream | Si on veut adresser DRM directement |
---
## 7. Décisions recommandées pour la suite
### Phase 2 (primitives OS) — peut être réduite
Plutôt que d'auditer toutes les primitives, **se limiter à des tests dédiés** :
1. Test fd passing : 2 processus, l'un crée un memfd-like, l'autre le mappe
2. Test Unix socket SOCK_STREAM avec messages Wayland-shaped (header binaire)
3. Test event loop multi-fd avec signaux POSIX
4. Test concurrent shm_open/mmap depuis plusieurs processus
Si les 4 tests passent, on a la garantie que `wayland-rs` peut tourner.
Estimation : **1 mois à 2h/jour** au lieu des 6 mois initialement budgétés.
### Phase 3 (port wayland-rs) — à découper
`wayland-rs` upstream a peu de deps OS-specifiques dans son crate `wayland-backend`.
Ordre suggéré :
1. `wayland-scanner` (génération code XML → Rust) : pure logic, devrait marcher tel quel
2. `wayland-backend` : couche socket — vérifier le `cfg(unix)` vs ajout d'un
`cfg(target_os = "redox")`
3. `wayland-server` : haut niveau — devrait fonctionner si backend OK
4. `wayland-client` : symétrique
### Phase 4 (display backend) — voie rapide possible
`V2GraphicsHandle` est si proche de l'API DRM Linux qu'on peut s'inspirer
directement de `core/display.rs` d'Orbital pour le RedoxOutput.
**Estimation : 2 mois à 2h/jour** (au lieu des 6 mois si on partait de zéro).
---
## 8. Repos clonés localement pour la suite
```text
~/Projets/Redox/
├── orbital/ ← référence compositor (gitlab.redox-os.org/redox-os/orbital)
├── base/ ← drivers + graphics-ipc + inputd (gitlab.redox-os.org/redox-os/base)
├── relibc/ ← libc Redox, primitives POSIX (--depth 1)
├── redox-src/ ← repo umbrella, ~/Projets/Redox/redox-src/build/x86_64/desktop/harddrive.img
└── docs/existing-redox-gui.md ← ce document
```
Sources additionnelles à cloner si on creuse plus :
- `redox-os/wayland-rs-redox-fork` *(à créer le moment venu)*
- `redox-os/drivers` *(déjà inclus dans base/drivers/)*
- `Smithay/smithay` *(plus tard, phase 13)*
---
## 9. Documents à produire ensuite
Selon le plan directeur (section "Documents A Produire Ensuite") :
| Document | Quand | Contenu |
|---|---|---|
| `redox-wayland-primitives.md` | Phase 2, après tests | Résultats des 4 tests dédiés + conclusions |
| `compositor-architecture.md` | Phase 3-4 | Crates, traits, flux de données |
| `display-backend-redox.md` | Phase 4 | Détails RedoxOutput + dirty_framebuffer flow |
| `input-backend-redox.md` | Phase 5 | Mapping orbclient → Wayland + XKB strategy |
| `software-rendering-and-gpu-roadmap.md` | Phase 4 + 12 | CPU paths + GPU horizon |
| `accessibility-roadmap.md` | Phase 11 | Décisions sans AT-SPI |
| `orbital-migration.md` | Phase 8-9 | Comment porter winit/softbuffer/clients |
| `cosmic-smithay-roadmap.md` | Phase 13 | Quand y aller, dépendances bloquantes |
| `x11-compat-rust-roadmap.md` | Phase 14 | Projet séparé |
---
*Fin de l'audit phase 1.*