Phase 13.1 — port simple_window comme premier client tiers
Crate redox-wl-real-client-simple-window : port Redox de l'exemple upstream wayland-rs/wayland-client/examples/simple_window.rs. Premier client que nous n'avons PAS écrit pour valider notre compositor, donc révélateur des paths protocolaires non anticipés. Code upstream préservé verbatim sauf 4 adaptations Redox justifiées dans le doc : - Connection::connect_to_env() → UnixStream::connect(SOCKET_PATH) car WAYLAND_DISPLAY pas garanti dans l'init Redox. - tempfile::tempfile() → libc::shm_open + ftruncate + mmap car O_TMPFILE/mkostemp non garantis par relibc. - Attente initiale 50×100ms sur l'existence du socket (lancement parallèle compositor + client dans init script). - Logs tee stdout + /scheme/debug pour observer côté serial host. L'algorithme draw() gradient ARGB, les Dispatch impls, le delegate_noop!, init_xdg_surface, Close/ESC sont conservés ligne pour ligne. Tout bug observé sera donc attribuable au compositor, pas au port. Cross-compile via `redoxer build --release` : 1.1 Mo, ELF statique, 0 warning, 0 erreur (testé 2026-05-15). Doc phase13-1-real-client-simple-window.md : justifications des 4 adaptations, procédure runtime make qemu, 6 critères d'observation pour la 13.1.b runtime à venir. Runtime non validé (exige make qemu interactif) — sera consigné en phase 13.1.b après test utilisateur. Leyoda 2026 – GPLv3
This commit is contained in:
parent
8795f39f08
commit
530d022840
3 changed files with 577 additions and 0 deletions
11
crates/redox-wl-real-client-simple-window/Cargo.toml
Normal file
11
crates/redox-wl-real-client-simple-window/Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "redox-wl-real-client-simple-window"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Port Redox du client wayland-rs/examples/simple_window.rs — premier client tiers (Phase 13.1)"
|
||||
|
||||
[dependencies]
|
||||
wayland-client = { path = "../../../wayland-rs/wayland-client", default-features = false }
|
||||
wayland-backend = { path = "../../../wayland-rs/wayland-backend", default-features = false }
|
||||
wayland-protocols = { path = "../../../wayland-rs/wayland-protocols", default-features = false, features = ["client"] }
|
||||
libc = "0.2"
|
||||
370
crates/redox-wl-real-client-simple-window/src/main.rs
Normal file
370
crates/redox-wl-real-client-simple-window/src/main.rs
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
//! Phase 13.1 — Port Redox de l'exemple upstream
|
||||
//! `wayland-rs/wayland-client/examples/simple_window.rs`.
|
||||
//!
|
||||
//! Premier client **tiers** (= que nous n'avons pas écrit pour
|
||||
//! satisfaire notre compositor). Objectif : faire émerger les manques
|
||||
//! réels du compositor en exerçant les chemins protocolaires que nos
|
||||
//! propres clients de test ne couvrent pas exhaustivement.
|
||||
//!
|
||||
//! Différences vs upstream (et seulement ces différences) :
|
||||
//!
|
||||
//! - `Connection::connect_to_env()` → `UnixStream::connect(SOCKET_PATH)`
|
||||
//! + `Backend::connect(stream)` parce que `WAYLAND_DISPLAY` n'est pas
|
||||
//! toujours posé dans l'environnement init Redox.
|
||||
//! - `tempfile::tempfile()` → `libc::shm_open` + `ftruncate` + `mmap`
|
||||
//! parce que `tempfile` dépend de `O_TMPFILE`/`mkostemp` non garantis
|
||||
//! par relibc.
|
||||
//! - Petite attente initiale sur l'existence du socket pour permettre
|
||||
//! au compositor de démarrer en parallèle dans le même init script.
|
||||
//! - Sortie sur `/scheme/debug` en plus de stdout (serial console host).
|
||||
//!
|
||||
//! Tout le reste — pattern Dispatch, `delegate_noop!`, `init_xdg_surface`,
|
||||
//! le dessin (gradient ARGB), la gestion de Close/ESC — est conservé
|
||||
//! verbatim depuis l'upstream pour bien isoler les manques du compositor
|
||||
//! du code du client.
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::os::fd::{AsFd, FromRawFd, OwnedFd};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::process::ExitCode;
|
||||
use std::ptr;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use wayland_client::{
|
||||
Dispatch, QueueHandle, WEnum, delegate_noop,
|
||||
backend::Backend,
|
||||
protocol::{
|
||||
wl_buffer, wl_compositor, wl_keyboard, wl_registry, wl_seat, wl_shm, wl_shm_pool,
|
||||
wl_surface,
|
||||
},
|
||||
Connection,
|
||||
};
|
||||
|
||||
use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
|
||||
|
||||
const SOCKET_PATH: &str = "/tmp/redox-wl-comp.sock";
|
||||
|
||||
// ---------- Logging tee stdout + /scheme/debug ----------
|
||||
struct DebugSink(Mutex<Option<std::fs::File>>);
|
||||
impl DebugSink {
|
||||
fn new() -> Self {
|
||||
Self(Mutex::new(
|
||||
OpenOptions::new().write(true).open("/scheme/debug").ok(),
|
||||
))
|
||||
}
|
||||
fn writeln(&self, s: &str) {
|
||||
println!("{s}");
|
||||
if let Ok(mut g) = self.0.lock() {
|
||||
if let Some(f) = g.as_mut() {
|
||||
let _ = writeln!(f, "{s}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn dlog(s: &str) {
|
||||
static SINK: OnceLock<DebugSink> = OnceLock::new();
|
||||
SINK.get_or_init(DebugSink::new).writeln(s);
|
||||
}
|
||||
|
||||
// ---------- Buffer SHM (équivalent de tempfile + draw upstream) ----------
|
||||
const INIT_W: u32 = 320;
|
||||
const INIT_H: u32 = 240;
|
||||
const PIXEL_BYTES: usize = 4;
|
||||
const BUFFER_SIZE: usize = (INIT_W as usize) * (INIT_H as usize) * PIXEL_BYTES;
|
||||
|
||||
/// Équivalent du `draw()` upstream + création shm.
|
||||
/// Garde le même algorithme de gradient pour que le rendu visuel
|
||||
/// matche le screenshot upstream.
|
||||
unsafe fn create_shm_buffer() -> Result<OwnedFd, String> {
|
||||
let cname = CString::new("/redox-wl-real-client-simple-window").unwrap();
|
||||
let _ = libc::shm_unlink(cname.as_ptr());
|
||||
let fd = libc::shm_open(cname.as_ptr(), libc::O_RDWR | libc::O_CREAT, 0o600);
|
||||
if fd < 0 {
|
||||
return Err(format!(
|
||||
"shm_open errno={}",
|
||||
std::io::Error::last_os_error().raw_os_error().unwrap_or(0)
|
||||
));
|
||||
}
|
||||
if libc::ftruncate(fd, BUFFER_SIZE as _) != 0 {
|
||||
libc::close(fd);
|
||||
return Err("ftruncate failed".into());
|
||||
}
|
||||
let p = libc::mmap(
|
||||
ptr::null_mut(),
|
||||
BUFFER_SIZE,
|
||||
libc::PROT_READ | libc::PROT_WRITE,
|
||||
libc::MAP_SHARED,
|
||||
fd,
|
||||
0,
|
||||
);
|
||||
if p == libc::MAP_FAILED {
|
||||
libc::close(fd);
|
||||
return Err("mmap failed".into());
|
||||
}
|
||||
|
||||
let pixels = std::slice::from_raw_parts_mut(p as *mut u8, BUFFER_SIZE);
|
||||
// Reproduction verbatim du draw() upstream : gradient ARGB.
|
||||
// Layout = [B, G, R, A] little-endian (Argb8888 Wayland).
|
||||
use std::cmp::min;
|
||||
let buf_x = INIT_W;
|
||||
let buf_y = INIT_H;
|
||||
let mut idx = 0usize;
|
||||
for y in 0..buf_y {
|
||||
for x in 0..buf_x {
|
||||
let a: u32 = 0xFF;
|
||||
let r = min(((buf_x - x) * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
|
||||
let g = min((x * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
|
||||
let b = min(((buf_x - x) * 0xFF) / buf_x, (y * 0xFF) / buf_y);
|
||||
pixels[idx] = b as u8;
|
||||
pixels[idx + 1] = g as u8;
|
||||
pixels[idx + 2] = r as u8;
|
||||
pixels[idx + 3] = a as u8;
|
||||
idx += 4;
|
||||
}
|
||||
}
|
||||
libc::munmap(p, BUFFER_SIZE);
|
||||
Ok(OwnedFd::from_raw_fd(fd))
|
||||
}
|
||||
|
||||
// ---------- État client + Dispatch impls (verbatim upstream) ----------
|
||||
struct State {
|
||||
running: bool,
|
||||
base_surface: Option<wl_surface::WlSurface>,
|
||||
buffer: Option<wl_buffer::WlBuffer>,
|
||||
wm_base: Option<xdg_wm_base::XdgWmBase>,
|
||||
xdg_surface: Option<(xdg_surface::XdgSurface, xdg_toplevel::XdgToplevel)>,
|
||||
configured: bool,
|
||||
}
|
||||
|
||||
impl Dispatch<wl_registry::WlRegistry, ()> for State {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
registry: &wl_registry::WlRegistry,
|
||||
event: wl_registry::Event,
|
||||
_: &(),
|
||||
_: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
) {
|
||||
if let wl_registry::Event::Global { name, interface, .. } = event {
|
||||
match &interface[..] {
|
||||
"wl_compositor" => {
|
||||
let compositor =
|
||||
registry.bind::<wl_compositor::WlCompositor, _, _>(name, 1, qh, ());
|
||||
let surface = compositor.create_surface(qh, ());
|
||||
state.base_surface = Some(surface);
|
||||
|
||||
if state.wm_base.is_some() && state.xdg_surface.is_none() {
|
||||
state.init_xdg_surface(qh);
|
||||
}
|
||||
}
|
||||
"wl_shm" => {
|
||||
let shm = registry.bind::<wl_shm::WlShm, _, _>(name, 1, qh, ());
|
||||
let fd = unsafe { create_shm_buffer() }.unwrap_or_else(|e| {
|
||||
dlog(&format!("[real-client] create_shm_buffer FAIL: {e}"));
|
||||
std::process::exit(2);
|
||||
});
|
||||
let pool = shm.create_pool(fd.as_fd(), BUFFER_SIZE as i32, qh, ());
|
||||
let buffer = pool.create_buffer(
|
||||
0,
|
||||
INIT_W as i32,
|
||||
INIT_H as i32,
|
||||
(INIT_W as i32) * 4,
|
||||
wl_shm::Format::Argb8888,
|
||||
qh,
|
||||
(),
|
||||
);
|
||||
state.buffer = Some(buffer.clone());
|
||||
|
||||
if state.configured {
|
||||
let surface = state.base_surface.as_ref().unwrap();
|
||||
surface.attach(Some(&buffer), 0, 0);
|
||||
surface.commit();
|
||||
}
|
||||
}
|
||||
"wl_seat" => {
|
||||
registry.bind::<wl_seat::WlSeat, _, _>(name, 1, qh, ());
|
||||
}
|
||||
"xdg_wm_base" => {
|
||||
let wm_base = registry.bind::<xdg_wm_base::XdgWmBase, _, _>(name, 1, qh, ());
|
||||
state.wm_base = Some(wm_base);
|
||||
|
||||
if state.base_surface.is_some() && state.xdg_surface.is_none() {
|
||||
state.init_xdg_surface(qh);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate_noop!(State: ignore wl_compositor::WlCompositor);
|
||||
delegate_noop!(State: ignore wl_surface::WlSurface);
|
||||
delegate_noop!(State: ignore wl_shm::WlShm);
|
||||
delegate_noop!(State: ignore wl_shm_pool::WlShmPool);
|
||||
delegate_noop!(State: ignore wl_buffer::WlBuffer);
|
||||
|
||||
impl State {
|
||||
fn init_xdg_surface(&mut self, qh: &QueueHandle<State>) {
|
||||
let wm_base = self.wm_base.as_ref().unwrap();
|
||||
let base_surface = self.base_surface.as_ref().unwrap();
|
||||
|
||||
let xdg_surface = wm_base.get_xdg_surface(base_surface, qh, ());
|
||||
let toplevel = xdg_surface.get_toplevel(qh, ());
|
||||
toplevel.set_title("A fantastic window!".into());
|
||||
|
||||
base_surface.commit();
|
||||
|
||||
self.xdg_surface = Some((xdg_surface, toplevel));
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<xdg_wm_base::XdgWmBase, ()> for State {
|
||||
fn event(
|
||||
_: &mut Self,
|
||||
wm_base: &xdg_wm_base::XdgWmBase,
|
||||
event: xdg_wm_base::Event,
|
||||
_: &(),
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
) {
|
||||
if let xdg_wm_base::Event::Ping { serial } = event {
|
||||
wm_base.pong(serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<xdg_surface::XdgSurface, ()> for State {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
xdg_surface: &xdg_surface::XdgSurface,
|
||||
event: xdg_surface::Event,
|
||||
_: &(),
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
) {
|
||||
if let xdg_surface::Event::Configure { serial, .. } = event {
|
||||
xdg_surface.ack_configure(serial);
|
||||
state.configured = true;
|
||||
let surface = state.base_surface.as_ref().unwrap();
|
||||
if let Some(ref buffer) = state.buffer {
|
||||
surface.attach(Some(buffer), 0, 0);
|
||||
surface.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<xdg_toplevel::XdgToplevel, ()> for State {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
_: &xdg_toplevel::XdgToplevel,
|
||||
event: xdg_toplevel::Event,
|
||||
_: &(),
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
) {
|
||||
if let xdg_toplevel::Event::Close = event {
|
||||
dlog("[real-client] xdg_toplevel.close → exit");
|
||||
state.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<wl_seat::WlSeat, ()> for State {
|
||||
fn event(
|
||||
_: &mut Self,
|
||||
seat: &wl_seat::WlSeat,
|
||||
event: wl_seat::Event,
|
||||
_: &(),
|
||||
_: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
) {
|
||||
if let wl_seat::Event::Capabilities {
|
||||
capabilities: WEnum::Value(capabilities),
|
||||
} = event
|
||||
{
|
||||
if capabilities.contains(wl_seat::Capability::Keyboard) {
|
||||
seat.get_keyboard(qh, ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<wl_keyboard::WlKeyboard, ()> for State {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
_: &wl_keyboard::WlKeyboard,
|
||||
event: wl_keyboard::Event,
|
||||
_: &(),
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
) {
|
||||
if let wl_keyboard::Event::Key { key, .. } = event {
|
||||
if key == 1 {
|
||||
// ESC scancode (evdev)
|
||||
dlog("[real-client] ESC → exit");
|
||||
state.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
dlog("[real-client] simple_window port Redox — start");
|
||||
|
||||
// Attente du socket compositor (init scripts lancent compositor + client
|
||||
// en parallèle ; le client peut démarrer avant que le socket soit bound).
|
||||
for i in 0..50 {
|
||||
if std::path::Path::new(SOCKET_PATH).exists() {
|
||||
break;
|
||||
}
|
||||
if i == 49 {
|
||||
return Err("compositor socket missing after 5s".into());
|
||||
}
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
|
||||
let stream = UnixStream::connect(SOCKET_PATH)?;
|
||||
let backend = Backend::connect(stream)?;
|
||||
let conn = Connection::from_backend(backend);
|
||||
|
||||
let mut event_queue = conn.new_event_queue();
|
||||
let qhandle = event_queue.handle();
|
||||
|
||||
let display = conn.display();
|
||||
display.get_registry(&qhandle, ());
|
||||
|
||||
let mut state = State {
|
||||
running: true,
|
||||
base_surface: None,
|
||||
buffer: None,
|
||||
wm_base: None,
|
||||
xdg_surface: None,
|
||||
configured: false,
|
||||
};
|
||||
|
||||
dlog("[real-client] entering event loop");
|
||||
while state.running {
|
||||
event_queue.blocking_dispatch(&mut state)?;
|
||||
}
|
||||
dlog("[real-client] loop exited cleanly");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
match run() {
|
||||
Ok(()) => {
|
||||
dlog("[real-client] PASS");
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
Err(e) => {
|
||||
dlog(&format!("[real-client] FAIL: {e}"));
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
196
docs/phase13-1-real-client-simple-window.md
Normal file
196
docs/phase13-1-real-client-simple-window.md
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
# Phase 13.1 — Premier client tiers : `simple_window` de wayland-rs
|
||||
|
||||
> Document produit le 2026-05-15 dans le cadre du plan directeur
|
||||
> `REDOX_COSMIC_XWAYLAND_RS_PLAN.md`.
|
||||
>
|
||||
> **Scope strict** :
|
||||
> - Porter l'exemple `wayland-rs/wayland-client/examples/simple_window.rs`
|
||||
> en crate Redox autonome (`redox-wl-real-client-simple-window`).
|
||||
> - Cross-compiler via `redoxer build` pour `x86_64-unknown-redox`.
|
||||
> - Documenter la procédure runtime (image bootable + `make qemu`).
|
||||
> - **Cette phase ne valide pas encore le rendu** : elle prépare le test.
|
||||
> Le runtime sera observé et l'observabilité des bugs sera consignée
|
||||
> dans une phase 13.1.b suivante.
|
||||
>
|
||||
> **Hors scope 13.1** : modifier le code de l'exemple upstream pour
|
||||
> contourner un manque du compositor. Si l'exemple échoue, le verdict
|
||||
> est "le compositor doit s'adapter au client standard", pas l'inverse.
|
||||
> Seules les 4 adaptations listées ci-dessous (justifiées par
|
||||
> l'environnement Redox) sont autorisées.
|
||||
|
||||
## Pourquoi cet exemple, et pas un autre
|
||||
|
||||
`simple_window.rs` est :
|
||||
|
||||
- **Officiel upstream wayland-rs** — donc représentatif du standard.
|
||||
- **Pur-Rust** — cross-compilable via redoxer sans toucher au sysroot.
|
||||
- **Minimal mais réaliste** : `wl_compositor` + `wl_shm` + `xdg_wm_base`
|
||||
+ `xdg_toplevel` + `wl_seat` + `wl_keyboard`. Tous nos chemins critiques
|
||||
sont exercés.
|
||||
- **Pas écrit par nous** : c'est sa principale qualité. Nos propres clients
|
||||
de test (phases 6.x et 7.x) ont été conçus pour valider des paths
|
||||
particuliers du compositor — ils ne révèlent pas les paths qu'on n'a
|
||||
pas anticipés. Un client tiers le fait par construction.
|
||||
|
||||
Alternatives écartées :
|
||||
- `list_globals.rs` (74 lignes) : trop simple, ne stresse pas xdg-shell.
|
||||
- `weston-simple-shm` (C) : exige libwayland-client.so, non porté sur Redox.
|
||||
- `smithay-client-toolkit/examples/simple_window` : ajoute une dépendance
|
||||
importante (sctk) qui dépasse le périmètre 13.1. À reprendre en 13.2.
|
||||
- GTK4 client : ratera immédiatement sur dmabuf, decoration, subsurface.
|
||||
À garder pour une phase 13.4+ après durcissement.
|
||||
|
||||
## Les 4 adaptations Redox
|
||||
|
||||
Toute différence vs l'upstream doit être justifiée par l'environnement,
|
||||
pas par un manque du compositor. Liste exhaustive :
|
||||
|
||||
1. **`Connection::connect_to_env()` → `UnixStream::connect(SOCKET_PATH)`**.
|
||||
Justification : sous Redox, `WAYLAND_DISPLAY` n'est pas garanti dans
|
||||
l'environnement des processus lancés par un script init. Connexion
|
||||
explicite au socket connu.
|
||||
2. **`tempfile::tempfile()` → `libc::shm_open` + `ftruncate` + `mmap`**.
|
||||
Justification : `tempfile` repose sur `O_TMPFILE` ou `mkostemp` qui
|
||||
ne sont pas garantis par relibc. On utilise le pattern que nos clients
|
||||
de test 6.x/7.x utilisent déjà — c'est la voie Redox-stable.
|
||||
3. **Petite attente sur l'existence du socket** (50×100 ms). Justification :
|
||||
compositor et client sont lancés en parallèle par l'init Redox ; sans
|
||||
poll, le client lose une race au démarrage.
|
||||
4. **Logs tee sur stdout + `/scheme/debug`**. Justification : permet
|
||||
d'observer le client depuis le terminal host (serial) même si on
|
||||
lance le compositor sur un VT et le client sur un autre.
|
||||
|
||||
Tout le reste — `Dispatch`, `delegate_noop!`, `init_xdg_surface`,
|
||||
l'algorithme `draw()` gradient ARGB, la gestion `Close`/ESC — est
|
||||
**verbatim upstream**.
|
||||
|
||||
## Compilation
|
||||
|
||||
```bash
|
||||
cd ~/Projets/Redox/redox-wayland-compositor/crates/redox-wl-real-client-simple-window
|
||||
redoxer build --release
|
||||
```
|
||||
|
||||
Sortie attendue (testée 2026-05-15) :
|
||||
- `target/x86_64-unknown-redox/release/redox-wl-real-client-simple-window`
|
||||
- ~1.1 Mo, ELF 64-bit statically linked
|
||||
- 0 warning, 0 erreur
|
||||
|
||||
`cargo +nightly check` natif fonctionne aussi pour smoke-check syntaxique,
|
||||
mais ne produit pas un binaire utilisable.
|
||||
|
||||
## Procédure de test runtime
|
||||
|
||||
Le compositor a besoin d'un vrai framebuffer Redox + d'inputd actif.
|
||||
La voie validée est `make qemu` sur une image qui contient le compositor
|
||||
ET le client copiés dans `/usr/bin`.
|
||||
|
||||
### Étape 1 — copier les deux binaires dans l'image Redox
|
||||
|
||||
```bash
|
||||
mkdir -p /tmp/redox-mnt
|
||||
~/Projets/Redox/redox-src/build/fstools/bin/redoxfs \
|
||||
~/Projets/Redox/redox-src/build/x86_64/desktop/harddrive.img \
|
||||
/tmp/redox-mnt &
|
||||
sleep 2
|
||||
|
||||
# compositor (déjà cross-compilé en phase 6.4)
|
||||
cp ~/Projets/Redox/redox-wayland-compositor/crates/redox-wl-compositor/target/x86_64-unknown-redox/release/redox-wl-compositor \
|
||||
/tmp/redox-mnt/usr/bin/
|
||||
|
||||
# le client tiers
|
||||
cp ~/Projets/Redox/redox-wayland-compositor/crates/redox-wl-real-client-simple-window/target/x86_64-unknown-redox/release/redox-wl-real-client-simple-window \
|
||||
/tmp/redox-mnt/usr/bin/
|
||||
|
||||
fusermount -u /tmp/redox-mnt
|
||||
rmdir /tmp/redox-mnt
|
||||
```
|
||||
|
||||
### Étape 2 — boot Redox sous QEMU
|
||||
|
||||
```bash
|
||||
cd ~/Projets/Redox/redox-src
|
||||
make qemu audio=no QEMU_USER_FLAGS="-k fr"
|
||||
```
|
||||
|
||||
Une fois Redox bootée :
|
||||
- **Ctrl+Alt+F2** → bascule sur VT 2 (console texte, sans Orbital)
|
||||
- login `root` / `password`
|
||||
|
||||
### Étape 3 — lancer compositor + client
|
||||
|
||||
Dans le shell Redox sur VT 2 :
|
||||
|
||||
```bash
|
||||
# compositor en background
|
||||
RUST_LOG=info redox-wl-compositor &
|
||||
|
||||
# attendre ~1 s pour que le socket soit bound
|
||||
# (le client poll de toute façon — c'est juste pour rendre la séquence
|
||||
# lisible dans les logs)
|
||||
sleep 1
|
||||
|
||||
# le client
|
||||
redox-wl-real-client-simple-window
|
||||
```
|
||||
|
||||
### Étape 4 — observations à consigner
|
||||
|
||||
À chaque session de test, noter dans une section "Observation" du
|
||||
prochain doc (13.1.b) :
|
||||
|
||||
1. **Connexion** : le client connecte-t-il (`[real-client] entering event loop` apparaît) ?
|
||||
2. **Globals reçus** : le client log-t-il que `wl_compositor`, `wl_shm`,
|
||||
`wl_seat`, `xdg_wm_base` sont bind ? (Côté compositor : `xdg_wm_base.create_xdg_surface` doit logger en `debug`.)
|
||||
3. **Configure initial** : le client ack-t-il un `xdg_surface.configure` ?
|
||||
Si non, on a un bug dans notre `GetToplevel` handler.
|
||||
4. **Premier commit pixel** : la fenêtre apparaît-elle à l'écran ? Gradient
|
||||
ARGB visible ? Si la fenêtre est noire ou corrompue, comparer le pattern
|
||||
au screenshot upstream (gradient diagonal rouge/vert/bleu).
|
||||
5. **Input** : `ESC` ferme-t-il le client proprement ?
|
||||
6. **Sortie** : le client termine-t-il sur `[real-client] PASS` côté serial ?
|
||||
|
||||
Lister explicitement les comportements qui dévient de l'attendu.
|
||||
|
||||
## Risques techniques anticipés
|
||||
|
||||
À voir avec le runtime — ne pas tirer de conclusion avant.
|
||||
|
||||
- **`get_xdg_surface` sans `set_role`** : on n'envoie pas d'erreur pour
|
||||
les rôles incompatibles, donc OK probablement.
|
||||
- **Initial configure suggestion** : on envoie (640, 480) en suggestion,
|
||||
l'exemple upstream commit son buffer 320×240 — c'est légal spec mais
|
||||
notre logique de raise/positionnement utilise la taille de buffer, à
|
||||
voir si ça pose problème.
|
||||
- **`wl_keyboard.enter` requis pour que la key ESC remonte** : notre
|
||||
routing input via `focused_client_id` (phase 7.6) devrait envoyer le
|
||||
`keyboard.enter` à la fenêtre quand elle reçoit le focus initial. À
|
||||
observer.
|
||||
- **`xdg_toplevel.close` jamais émis par notre compositor** : l'exemple
|
||||
upstream s'attend à recevoir un `Close` quand l'utilisateur clique
|
||||
sur un bouton de fermeture. Comme nous n'avons pas de décoration,
|
||||
ce path est mort-né — donc la seule sortie possible est ESC.
|
||||
- **Buffer fd via shm_open** : la mémoire reste vivante après `munmap`
|
||||
côté client tant que le compositor garde le fd. Notre `ShmPool::new`
|
||||
fait un mmap côté serveur, donc OK.
|
||||
|
||||
## Critère de fin 13.1
|
||||
|
||||
> Le binaire `redox-wl-real-client-simple-window` :
|
||||
> 1. Compile pour `x86_64-unknown-redox` via `redoxer build --release`,
|
||||
> sans warning ni erreur.
|
||||
> 2. Vit dans `crates/redox-wl-real-client-simple-window/` avec son
|
||||
> propre `Cargo.toml`.
|
||||
> 3. Préserve le code upstream verbatim sauf pour les 4 adaptations
|
||||
> Redox justifiées ci-dessus.
|
||||
> 4. La procédure runtime est documentée et reproductible.
|
||||
|
||||
**✅ Validé au niveau compilation et préparation** (2026-05-15).
|
||||
Runtime à observer dans la phase 13.1.b après test interactif.
|
||||
|
||||
## Code
|
||||
|
||||
```
|
||||
crates/redox-wl-real-client-simple-window/ # nouvelle crate
|
||||
docs/phase13-1-real-client-simple-window.md # ce document
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue