🎉 Phase 13.1.b — simple_window upstream validé runtime sur Redox

4 bugs identifiés et corrigés grâce au client tiers :
- ESC mangée par le compositor au lieu d'être forwardée (raccourci compo
  déplacé sur Ctrl+Q + tracking ctrl_held)
- Cursor accumulait les deltas PS/2 hors-écran (ajout screen_w/h +
  clamp_cursor + set_screen_size au boot)
- Keycode envoyé scancode+8 au lieu d'evdev brut (bug racine — le +8
  est entre evdev et X11, pas entre PS/2 et evdev)
- Event loop client ne tolérait pas Interrupted/BrokenPipe transitoires
  → FAIL au lieu de PASS sur sortie normale (5e adaptation Redox côté
  client, upstream-compatible)

Plus : tick log compo passé en debug! pour silencer la console série,
run-qemu.sh durci (check /dev/fuse, IMAGE/REDOXFS overridables).

6/6 checkpoints 13.1 verts. Bugs secondaires (curseur stuck en bas-droite,
ion page fault sur broken pipe d'un job background) listés dans le doc
pour follow-up en 13.1.c.

Leyoda 2026 – GPLv3
This commit is contained in:
Votre Nom 2026-05-16 10:59:35 +02:00
parent 0d8032b5ff
commit 18391c85f7
5 changed files with 401 additions and 15 deletions

View file

@ -374,6 +374,11 @@ pub struct WaylandFrontend {
/// Mise à jour par `forward_input(PointerMotion / PointerMotionRelative)`.
cursor_x: i32,
cursor_y: i32,
/// Phase 13.1.b : bornes écran pour clamper le cursor. Si non
/// défini par `set_screen_size`, valeurs i32::MAX → le clamp est un
/// no-op (compat tests qui ne configurent pas le frontend).
screen_w: i32,
screen_h: i32,
/// Counter monotone pour les serials seat events (différent de next_xdg_serial).
next_input_serial: u32,
/// Timestamp incrémental pour les events seat (ms-like).
@ -445,6 +450,8 @@ impl WaylandFrontend {
focused_surface: None,
cursor_x: 0,
cursor_y: 0,
screen_w: i32::MAX,
screen_h: i32::MAX,
next_input_serial: 1,
input_time_ms: 0,
cursor_surface_id: None,
@ -816,12 +823,15 @@ impl WaylandFrontend {
};
let time = self.alloc_input_time();
let serial = self.alloc_input_serial();
// Wayland keycodes = scancode evdev = scancode +8 on linux
// (cf xkb_keycodes minimum=8). On garde le scancode brut
// ici en attendant une keymap correcte (US Linux evdev offset
// ne s'applique pas forcément à orbclient ; à durcir
// quand on aura une vraie keymap XKB).
let key = (*scancode as u32).saturating_add(8);
// Phase 13.1.b : Wayland `wl_keyboard.key` attend le keycode
// **evdev** brut, pas X11. L'offset +8 est entre evdev et
// X11 (historique X11 où KeyCode minimum = 8), pas entre
// PS/2 et evdev — pour les touches alphanumériques de base,
// PS/2 Set 1 scancode == evdev keycode (ESC=1, Q=16, etc.).
// Bug originel : on envoyait scancode+8 → simple_window
// upstream qui teste `key == 1` pour ESC recevait 9 et
// n'exitait jamais.
let key = *scancode as u32;
let state = if *pressed {
wl_keyboard::KeyState::Pressed
} else {
@ -836,6 +846,7 @@ impl WaylandFrontend {
RedoxInputEvent::PointerMotion { x, y } => {
self.cursor_x = *x;
self.cursor_y = *y;
self.clamp_cursor();
self.cursor_visible = true;
// Phase 7.7 : si drag actif, déplacer la surface au lieu
// d'envoyer motion au client.
@ -859,6 +870,7 @@ impl WaylandFrontend {
RedoxInputEvent::PointerMotionRelative { dx, dy } => {
self.cursor_x = self.cursor_x.saturating_add(*dx);
self.cursor_y = self.cursor_y.saturating_add(*dy);
self.clamp_cursor();
self.cursor_visible = true;
// Phase 7.7 : si drag actif, déplacer la surface.
if self.apply_interactive_drag() {
@ -1089,6 +1101,26 @@ impl WaylandFrontend {
self.cursor_x = x;
self.cursor_y = y;
self.cursor_visible = true;
self.clamp_cursor();
}
/// Phase 13.1.b : déclare la taille du framebuffer pour borner le
/// curseur. Sans ça, les deltas `PointerMotionRelative` (PS/2 sous QEMU)
/// s'accumulent et finissent par sortir l'écran → tous les hit_test
/// retournent None → aucune fenêtre ne reçoit le focus → ESC tombe
/// dans le vide. Doit être appelé après `bind_absolute` et idéalement
/// re-appelé si la résolution change.
pub fn set_screen_size(&mut self, w: i32, h: i32) {
self.screen_w = w.max(1);
self.screen_h = h.max(1);
self.clamp_cursor();
}
fn clamp_cursor(&mut self) {
let max_x = (self.screen_w - 1).max(0);
let max_y = (self.screen_h - 1).max(0);
self.cursor_x = self.cursor_x.clamp(0, max_x);
self.cursor_y = self.cursor_y.clamp(0, max_y);
}
/// Force la position du curseur à tout moment. Utile pour tests
@ -1101,6 +1133,7 @@ impl WaylandFrontend {
pub fn set_cursor_position(&mut self, x: i32, y: i32) {
self.cursor_x = x;
self.cursor_y = y;
self.clamp_cursor();
self.cursor_visible = true;
let _ = self.apply_interactive_drag();
}