redox-wayland-compositor/crates/redox-wl-input/src/lib.rs
Votre Nom 8795f39f08 Sprint 0 — industrialisation : CI, tracing, tests xdg-shell, GPLv3
5 livrables d'industrialisation posés avant la phase 13 (client réel).

- .gitlab-ci.yml : pipeline minimal (fmt-check + tests core +
  tests frontend nightly). Pas de cross-compile Redox dans le MVP.
- rustfmt.toml + fmt.sh : config formatter racine + boucle sur les 23
  crates ; fmt sweep appliqué (d'où les diffs cosmétiques).
- tracing dans wayland-frontend : 22 println/eprintln remplacés par
  debug/info/warn/error selon sévérité. Préfixe "[frontend]"
  supprimé (le target tracing le fournit).
- tracing-subscriber dans le compositor : TeeWriter écrit sur
  stdout + /scheme/debug, filtre via RUST_LOG (défaut info).
  DebugSink/dlog supprimés.
- 15 tests unitaires xdg-shell après extraction de 2 helpers libres
  (clamp_to_min_max + should_throttle_configure). Couvre
  compute_resize_geom, contraintes min/max et throttling configure.
- LICENSE (GPLv3 texte officiel FSF) + .editorconfig.

Total tests : 27 → 42 automatisés (compositor-core + frontend).

Leyoda 2026 – GPLv3
2026-05-14 20:46:07 +02:00

131 lines
4.8 KiB
Rust

//! 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,
},
}
}