🎉 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
This commit is contained in:
parent
753a30757b
commit
a9bb88d9f3
7 changed files with 542 additions and 2 deletions
134
crates/redox-wl-input/src/lib.rs
Normal file
134
crates/redox-wl-input/src/lib.rs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
//! Backend input pour le compositor Redox.
|
||||
//!
|
||||
//! Encapsule `inputd::ConsumerHandle` et traduit les `orbclient::Event`
|
||||
//! bruts en un enum `InputEvent` neutre que le compositor peut consommer
|
||||
//! sans être couplé à orbclient ou à inputd directement.
|
||||
//!
|
||||
//! Conception : un seul `ConsumerHandle` est partagé entre `RedoxOutput`
|
||||
//! (qui le tient en vie pour préserver le VT) et `InputBackend` (qui polle
|
||||
//! les events). `RedoxOutput` ne lit pas les events ; `InputBackend` est
|
||||
//! lecteur unique. Ils partagent via `Arc<ConsumerHandle>`.
|
||||
|
||||
use std::io;
|
||||
use std::os::fd::{AsRawFd, BorrowedFd};
|
||||
use std::sync::Arc;
|
||||
|
||||
use inputd::{ConsumerHandle, ConsumerHandleEvent};
|
||||
use orbclient::{Event, EventOption};
|
||||
|
||||
/// Event compositor neutre, traduit depuis `orbclient::Event`.
|
||||
///
|
||||
/// Reste indépendant de Wayland : un compositor peut le mapper vers
|
||||
/// `wl_keyboard.key`, `wl_pointer.motion`, etc. ; un autre frontend
|
||||
/// peut le mapper vers tout autre protocole.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum InputEvent {
|
||||
/// Touche clavier pressée ou relâchée.
|
||||
Key {
|
||||
/// Caractère résolu via la keymap active. `'\0'` si touche non-imprimable.
|
||||
character: char,
|
||||
/// Scancode brut envoyé par le driver clavier.
|
||||
scancode: u8,
|
||||
/// `true` = press, `false` = release.
|
||||
pressed: bool,
|
||||
},
|
||||
/// Caractère issu d'une méthode IME (compose, dead key, etc.).
|
||||
TextInput { character: char },
|
||||
/// Position absolue de la souris dans les coordonnées display.
|
||||
PointerMotion { x: i32, y: i32 },
|
||||
/// Mouvement relatif (utile pour les jeux ou modes "grab").
|
||||
PointerMotionRelative { dx: i32, dy: i32 },
|
||||
/// État des boutons de la souris (toujours envoyé en bloc côté Redox).
|
||||
PointerButton {
|
||||
left: bool,
|
||||
middle: bool,
|
||||
right: bool,
|
||||
},
|
||||
/// Événement de scroll (delta).
|
||||
PointerScroll { dx: i32, dy: i32 },
|
||||
/// L'utilisateur veut sortir (Quit / kill window).
|
||||
Quit,
|
||||
/// Event reçu mais non encore mappé. Conserve le code orbclient pour debug.
|
||||
Unhandled { code: i64, a: i64, b: i64 },
|
||||
/// inputd nous a retiré le contrôle du VT (handoff vers un autre VT).
|
||||
/// Le compositor doit relâcher le display jusqu'au prochain Resume.
|
||||
Handoff,
|
||||
}
|
||||
|
||||
/// Capacité maximale du buffer interne de read_events. Aligné sur Orbital.
|
||||
const READ_BUF: usize = 16;
|
||||
|
||||
pub struct InputBackend {
|
||||
consumer: Arc<ConsumerHandle>,
|
||||
}
|
||||
|
||||
impl InputBackend {
|
||||
/// Construit un backend depuis le `ConsumerHandle` partagé avec le
|
||||
/// `RedoxOutput`. Le caller ne doit pas appeler `read_events` lui-même
|
||||
/// sur le consumer, sinon les events seront répartis entre les lecteurs.
|
||||
pub fn new(consumer: Arc<ConsumerHandle>) -> Self {
|
||||
Self { consumer }
|
||||
}
|
||||
|
||||
/// Borrow le fd des events pour subscription dans une `EventQueue`.
|
||||
pub fn event_fd(&self) -> BorrowedFd<'_> {
|
||||
self.consumer.event_handle()
|
||||
}
|
||||
|
||||
pub fn event_raw_fd(&self) -> i32 {
|
||||
self.consumer.event_handle().as_raw_fd()
|
||||
}
|
||||
|
||||
/// Récupère tous les events disponibles maintenant. Non-bloquant
|
||||
/// (`ConsumerHandle::new_vt` ouvre avec `O_NONBLOCK`).
|
||||
/// Retourne aussi `InputEvent::Handoff` si inputd nous notifie un switch VT.
|
||||
pub fn poll(&self) -> io::Result<Vec<InputEvent>> {
|
||||
let mut out = Vec::new();
|
||||
let mut buf = [Event::new(); READ_BUF];
|
||||
loop {
|
||||
match self.consumer.read_events(&mut buf)? {
|
||||
ConsumerHandleEvent::Events(&[]) => break,
|
||||
ConsumerHandleEvent::Events(events) => {
|
||||
for ev in events {
|
||||
out.push(translate(ev));
|
||||
}
|
||||
}
|
||||
ConsumerHandleEvent::Handoff => {
|
||||
out.push(InputEvent::Handoff);
|
||||
break; // après un handoff on arrête de poller cette fois-ci
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
|
||||
fn translate(ev: &Event) -> InputEvent {
|
||||
match ev.to_option() {
|
||||
EventOption::Key(k) => InputEvent::Key {
|
||||
character: k.character,
|
||||
scancode: k.scancode,
|
||||
pressed: k.pressed,
|
||||
},
|
||||
EventOption::TextInput(t) => InputEvent::TextInput {
|
||||
character: t.character,
|
||||
},
|
||||
EventOption::Mouse(m) => InputEvent::PointerMotion { x: m.x, y: m.y },
|
||||
EventOption::MouseRelative(m) => InputEvent::PointerMotionRelative {
|
||||
dx: m.dx,
|
||||
dy: m.dy,
|
||||
},
|
||||
EventOption::Button(b) => InputEvent::PointerButton {
|
||||
left: b.left,
|
||||
middle: b.middle,
|
||||
right: b.right,
|
||||
},
|
||||
EventOption::Scroll(s) => InputEvent::PointerScroll { dx: s.x, dy: s.y },
|
||||
EventOption::Quit(_) => InputEvent::Quit,
|
||||
_ => InputEvent::Unhandled {
|
||||
code: ev.code,
|
||||
a: ev.a,
|
||||
b: ev.b,
|
||||
},
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue