# Phase 5 — Input backend Redox : résultats > Document produit le 2026-05-09 dans le cadre du plan directeur > `REDOX_COSMIC_XWAYLAND_RS_PLAN.md`. > > **Périmètre** : encapsuler `inputd` proprement, exposer un enum > `InputEvent` neutre côté compositor, valider runtime sur Redox bootée > avec injection d'events keyboard/mouse via le monitor QEMU. ## Verdict global **✅ Pipeline input complet validé runtime.** Le binaire `redox-wl-test-input` a : 1. Ouvert le display via `RedoxOutput` (VT alloué = 2) 2. Activé le VT via `inputd -A` 3. Pris le CRTC et peint un fond bleu marine `0xFF103060` (preuve display) 4. Créé un `InputBackend` partageant le même `ConsumerHandle` 5. Pollé en boucle pendant 30 s, traduisant chaque event en `InputEvent` 6. Logué chaque event sur `/scheme/debug` (capté côté host via serial QEMU) 20 events injectés via le monitor QEMU (`sendkey`, `mouse_button`) ont tous été reçus, avec les **modificateurs interprétés correctement par la keymap** Redox (shift+a → `'A'`, ctrl+c → ctrl press + `c` press). Capture preuve display : ![](phase5-blue-screen-with-input.png) ## Architecture ``` ┌─────────────────────────────────────────┐ │ binaire compositor │ │ │ │ ┌──────────────┐ Arc │ │ │ RedoxOutput │ ─────┐ │ │ │ (display) │ ▼ │ │ └──────────────┘ ┌─────────────────┐ │ │ │ InputBackend │ │ │ │ poll() ────────┼──▶ Vec │ └─────────────────┘ │ └─────────────────────────────────────────┘ │ ▼ /scheme/input/consumer (inputd, un seul VT, un seul fd) ``` **Décision architecturale clé** : un seul `ConsumerHandle` partagé via `Arc`, pour 2 raisons : 1. **Un consumer = un VT**. Si on en ouvrait deux, on aurait deux VTs distincts et inputd routerait les events vers le VT actif (un seul à la fois). Le second consumer serait perpétuellement muet. 2. **`RedoxOutput` doit garder le consumer en vie** pour préserver le VT (cf phase 4 — sinon `inputd -A ` retourne "non-existent"). Mais c'est `InputBackend` qui consomme les events. Le partage Arc résout cette tension. ## API publique ### `redox-wl-input::InputEvent` ```rust pub enum InputEvent { Key { character: char, scancode: u8, pressed: bool }, TextInput { character: char }, PointerMotion { x: i32, y: i32 }, // absolu PointerMotionRelative { dx: i32, dy: i32 }, PointerButton { left: bool, middle: bool, right: bool }, PointerScroll { dx: i32, dy: i32 }, Quit, Unhandled { code: i64, a: i64, b: i64 }, // events orbclient pas encore mappés Handoff, // VT switch côté inputd } ``` L'enum est volontairement **agnostique vis-à-vis de Wayland**. Un compositor Wayland le mappera en `wl_keyboard.key`, `wl_pointer.motion`, etc. ; un autre frontend pourra le mapper différemment. ### `redox-wl-input::InputBackend` ```rust impl InputBackend { pub fn new(consumer: Arc) -> Self; pub fn event_fd(&self) -> BorrowedFd<'_>; // pour subscribe à une EventQueue pub fn event_raw_fd(&self) -> i32; pub fn poll(&self) -> io::Result>; } ``` `poll()` est non-bloquant et draine tous les events disponibles. À intégrer plus tard dans une `event::EventQueue` Redox pour event-driven. ## Validation runtime — extrait du log Injection via le monitor QEMU : ```bash for k in a b c ret; do printf "sendkey $k\n" | ncat -U /tmp/qmp.sock; done printf "sendkey shift-a\n" | ncat -U /tmp/qmp.sock printf "sendkey ctrl-c\n" | ncat -U /tmp/qmp.sock printf "sendkey esc\n" | ncat -U /tmp/qmp.sock printf "mouse_button 1\n" | ncat -U /tmp/qmp.sock printf "mouse_button 0\n" | ncat -U /tmp/qmp.sock ``` Sortie côté binaire (lue via /scheme/debug → serial → stdout host) : ``` [input] display 1280x800, VT=2 [input] CRTC pris, fond bleu marine peint [input] InputBackend prêt, fd events=4 [input] #0001 Key 'a' scan=0x1e PRESS [input] #0002 Key 'a' scan=0x1e RELEASE [input] #0003 Key 'b' scan=0x30 PRESS [input] #0004 Key 'b' scan=0x30 RELEASE [input] #0005 Key 'c' scan=0x2e PRESS [input] #0006 Key 'c' scan=0x2e RELEASE [input] #0007 Key '·' scan=0x1c PRESS ← Enter [input] #0008 Key '·' scan=0x1c RELEASE [input] #0009 Key '·' scan=0x2a PRESS ← Shift down [input] #0010 Key 'A' scan=0x1e PRESS ← keymap → 'A' (uppercase) [input] #0011 Key 'A' scan=0x1e RELEASE [input] #0012 Key '·' scan=0x2a RELEASE ← Shift up [input] #0013 Key '·' scan=0x1d PRESS ← Ctrl down [input] #0014 Key 'c' scan=0x2e PRESS [input] #0015 Key 'c' scan=0x2e RELEASE [input] #0016 Key '·' scan=0x1d RELEASE ← Ctrl up [input] #0017 Key '·' scan=0x01 PRESS ← Esc [input] #0018 Key '·' scan=0x01 RELEASE [input] #0019 PointerButton L=true M=false R=false [input] #0020 PointerButton L=false M=false R=false ``` Note : les caractères `'·'` représentent `'\0'` (touche non-imprimable sans char associé : modificateurs, Enter, Esc...). Le scancode reste disponible pour l'interpréter. ## Limitations connues ### Pas de `PointerMotion` testé Le test n'a pas reçu d'event `PointerMotion` parce que la commande QEMU utilisée n'incluait pas `-device usb-tablet` (qui fournit des coords absolues). Avec `make qemu` standard, ce device est inclus. À retester quand on aura un cas d'usage souris dans le compositor. ### Mouse relatif via PS/2 non testé Pareil, dépend de `-device ps2` ou similaire. ### Doublement des lignes dans le log Chaque ligne `[input]` apparaît deux fois dans le log capturé. Cause : le `DebugSink` fait `println!` (→ stdout → fbcond → framebuffer → captured-by-screendump-not-serial) ET `writeln!(/scheme/debug)` (→ serial → captured-by-host-stdio). Les deux finissent par converger dans le serial mux stdio du `make qemu`. Cosmétique, pas critique. ## Ce qui est prêt pour la suite - Phase 6 (composition multi-surfaces shm) : peut consommer `InputEvent` pour focus/clic-pour-amener-au-premier-plan - Phase 7 (compositor utilisable) : peut wrapper `InputBackend` dans une `EventQueue` Redox + traiter raccourcis globaux ## Code source ``` crates/redox-wl-input/ # lib (140 lignes) ├── Cargo.toml └── src/lib.rs # InputEvent + InputBackend crates/redox-wl-test-input/ # bin de test (130 lignes) ├── Cargo.toml └── src/main.rs # display bleu + poll loop + log crates/redox-wl-display/src/lib.rs # MODIFIÉ # _consumer → consumer: Arc # + pub fn consumer() -> Arc ``` ## Méthode de test reproductible Image patchée : `/usr/lib/init.d/20_orbital` lance `redox-wl-test-input` en VT=2, `30_console` vidé pour ne pas créer de getty conflictuel. ```bash qemu-system-x86_64 \ ... \ -chardev stdio,id=debug,signal=off,mux=on \ -serial chardev:debug -mon chardev=debug \ -monitor unix:/tmp/qmp.sock,server,nowait \ -vga std -display none ... & sleep 2 && printf "sendkey ret\n" | ncat -U /tmp/qmp.sock # bootloader sleep 13 # boot + paint printf "sendkey a\n" | ncat -U /tmp/qmp.sock # inject events printf "mouse_button 1\n" | ncat -U /tmp/qmp.sock printf "screendump /tmp/x.ppm\n" | ncat -U /tmp/qmp.sock # capture visuel ``` L'image a été restaurée à boot Orbital normal après la session. --- *Fin du document de phase 5.*