redox-wayland-compositor/docs/phase5-input-backend.md
Votre Nom a9bb88d9f3 🎉 Phase 5 — input backend Redox validé runtime
Crates ajoutés :
- redox-wl-input (lib) : InputBackend wrappe Arc<ConsumerHandle>
  + enum InputEvent { Key, TextInput, PointerMotion(Relative)?,
  PointerButton, PointerScroll, Quit, Unhandled, Handoff }
- redox-wl-test-input (bin) : ouvre display + take CRTC + peint bleu
  marine + polle events 30s en logguant chaque event

Modifs redox-wl-display :
- _consumer: ConsumerHandle → consumer: Arc<ConsumerHandle>
- + pub fn consumer() -> Arc<ConsumerHandle> pour partage avec input

Validation runtime sur Redox bootée via QEMU + monitor unix socket :
20 events injectés via `sendkey` et `mouse_button` HMP commands, tous
reçus et traduits correctement :
- a/b/c PRESS+RELEASE — keymap directe
- shift+a → 'A' uppercase — modificateur fonctionnel
- ctrl+c → ctrl PRESS + 'c' PRESS — composition fonctionnelle
- mouse_button 1/0 → PointerButton L=true/false
- Esc, Enter, Shift, Ctrl reçus avec scancode brut

Décision architecturale : un seul ConsumerHandle partagé via Arc entre
RedoxOutput (pour vie du VT) et InputBackend (lecteur unique d'events).
Sinon deux consumers = deux VTs distincts dont un seul reçoit les events.

Capture preuve : docs/phase5-blue-screen-with-input.png — bleu marine
plein écran 1280x800 confirmant que display + input fonctionnent
ensemble dans le même binaire.

docs/phase5-input-backend.md : compte-rendu complet.

Image restaurée à boot Orbital normal après session.

Leyoda 2026 – GPLv3
2026-05-09 11:22:54 +02:00

8 KiB

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 :

Architecture

┌─────────────────────────────────────────┐
│ binaire compositor                      │
│                                         │
│ ┌──────────────┐  Arc<ConsumerHandle>   │
│ │ RedoxOutput  │ ─────┐                 │
│ │ (display)    │      ▼                 │
│ └──────────────┘  ┌─────────────────┐   │
│                   │ InputBackend    │   │
│                   │  poll() ────────┼──▶ Vec<InputEvent>
│                   └─────────────────┘   │
└─────────────────────────────────────────┘
              │
              ▼
       /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 <vt> retourne "non-existent"). Mais c'est InputBackend qui consomme les events. Le partage Arc résout cette tension.

API publique

redox-wl-input::InputEvent

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

impl InputBackend {
    pub fn new(consumer: Arc<ConsumerHandle>) -> Self;
    pub fn event_fd(&self) -> BorrowedFd<'_>;     // pour subscribe à une EventQueue
    pub fn event_raw_fd(&self) -> i32;
    pub fn poll(&self) -> io::Result<Vec<InputEvent>>;
}

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 :

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<ConsumerHandle>
                                    # + pub fn consumer() -> Arc<ConsumerHandle>

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.

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.