redox-wayland-compositor/crates/redox-wl-test-handshake/src/main.rs
Votre Nom 53e6626231 Initial commit: phases 1-3 du portage Wayland Rust pour Redox OS
Plan directeur 14 phases / 5 ans (REDOX_COSMIC_XWAYLAND_RS_PLAN.md).

Phase 1 — Audit Redox (docs/existing-redox-gui.md, 486 lignes) :
- Orbital, graphics-ipc (API DRM compatible Linux subset KMS), inputd, vesad
- relibc support : AF_UNIX, SCM_RIGHTS, shm_open, mmap, poll
- 3 manques identifiés : memfd_create, keymap XKB, AT-SPI

Phase 2 — Validation primitives sur Redox via redoxer (5 tests + 1 POC) :
- test-unix-socket : SOCK_STREAM Wayland-shaped roundtrip
- test-fd-passing : SCM_RIGHTS mono-process (artefact kernel)
- test-fd-passing-fork : SCM_RIGHTS multi-process (validation Wayland critique)
- test-shm-open : shm_open + mmap + persistance + unlink
- test-poll-multifd : poll() multiplexing + POLLHUP
- poc-pixels : datapath shm + SCM_RIGHTS bout en bout (10000 pixels ARGB)

Phase 3 — wayland-rs sur Redox (compile + runtime) :
- wayland-{scanner,backend,server,client} compilent pour x86_64-unknown-redox
  sans patch upstream (rustix supporte Redox via libc backend)
- test-handshake : server/client wl_registry handshake roundtrip
- test-shm-pipeline : pipeline complet (ListeningSocket Unix réel + fd passing
  via wl_shm.create_pool + wl_shm_pool + wl_buffer + wl_surface + commit +
  serveur lit pixels via fd reçu, validation pixel-perfect)

Verdict phase 3 : wayland-rs upstream est viable sur Redox out-of-the-box,
le port "Wayland sur Redox" est désormais un problème de compositor à écrire,
pas de stack à porter.

Prérequis build : redoxer (pas cargo direct, car CMSG_NXTHDR/CMSG_DATA
ne sont pas linkés autrement vers librelibc.a).

Leyoda 2026 – GPLv3
2026-05-08 17:41:55 +02:00

200 lines
6.3 KiB
Rust

//! Test 5 — Wayland handshake server/client sur Redox.
//!
//! Valide que wayland-rs (server + client + backend) ne se contente pas de
//! compiler pour Redox mais fonctionne réellement à l'exécution :
//!
//! 1. Serveur Wayland local + globals (wl_compositor, wl_shm)
//! 2. socketpair AF_UNIX, on insère le côté serveur via insert_client
//! 3. Client Wayland connecté sur l'autre côté via WAYLAND_SOCKET fd env
//! 4. Client fait wl_display.get_registry, dispatch
//! 5. On vérifie que les globals attendus arrivent côté client
//!
//! Si ça passe, le port wayland-rs sur Redox est validé runtime, pas juste
//! compile-time.
use std::os::unix::net::UnixStream;
use std::process::ExitCode;
use std::sync::Arc;
use wayland_client::{
Connection as ClientConnection, Dispatch as ClientDispatch, EventQueue,
backend::Backend as ClientBackend,
protocol::wl_registry,
};
use wayland_server::{
Display as ServerDisplay, GlobalDispatch, New,
backend::ClientData,
protocol::{wl_compositor, wl_shm},
};
// ---- Server side ----
struct ServerState;
#[derive(Debug)]
struct DumbClientData;
impl ClientData for DumbClientData {}
impl GlobalDispatch<wl_compositor::WlCompositor, ()> for ServerState {
fn bind(
_state: &mut Self,
_handle: &wayland_server::DisplayHandle,
_client: &wayland_server::Client,
_resource: New<wl_compositor::WlCompositor>,
_data: &(),
_data_init: &mut wayland_server::DataInit<'_, Self>,
) {
}
}
impl wayland_server::Dispatch<wl_compositor::WlCompositor, ()> for ServerState {
fn request(
_state: &mut Self,
_client: &wayland_server::Client,
_resource: &wl_compositor::WlCompositor,
_request: wl_compositor::Request,
_data: &(),
_dh: &wayland_server::DisplayHandle,
_data_init: &mut wayland_server::DataInit<'_, Self>,
) {
}
}
impl GlobalDispatch<wl_shm::WlShm, ()> for ServerState {
fn bind(
_state: &mut Self,
_handle: &wayland_server::DisplayHandle,
_client: &wayland_server::Client,
_resource: New<wl_shm::WlShm>,
_data: &(),
_data_init: &mut wayland_server::DataInit<'_, Self>,
) {
}
}
impl wayland_server::Dispatch<wl_shm::WlShm, ()> for ServerState {
fn request(
_state: &mut Self,
_client: &wayland_server::Client,
_resource: &wl_shm::WlShm,
_request: wl_shm::Request,
_data: &(),
_dh: &wayland_server::DisplayHandle,
_data_init: &mut wayland_server::DataInit<'_, Self>,
) {
}
}
// ---- Client side ----
#[derive(Default, Debug)]
struct ClientState {
globals: Vec<(u32, String, u32)>,
done: bool,
}
impl ClientDispatch<wl_registry::WlRegistry, ()> for ClientState {
fn event(
state: &mut Self,
_registry: &wl_registry::WlRegistry,
event: wl_registry::Event,
_data: &(),
_conn: &ClientConnection,
_qhandle: &wayland_client::QueueHandle<Self>,
) {
if let wl_registry::Event::Global { name, interface, version } = event {
println!("[test-05 CLIENT] global #{name}: {interface} v{version}");
state.globals.push((name, interface, version));
}
}
}
fn run() -> Result<(), Box<dyn std::error::Error>> {
println!("[test-05] wayland-rs handshake server/client on Redox");
// -- Server setup --
let mut server_display: ServerDisplay<ServerState> = ServerDisplay::new()?;
let mut dh = server_display.handle();
dh.create_global::<ServerState, wl_compositor::WlCompositor, _>(5, ());
dh.create_global::<ServerState, wl_shm::WlShm, _>(1, ());
println!("[test-05 SERVER] created wl_compositor v5 + wl_shm v1 globals");
// -- Socket linking --
let (s_server, s_client) = UnixStream::pair()?;
s_server.set_nonblocking(true)?;
let _client_handle = dh.insert_client(s_server, Arc::new(DumbClientData))?;
println!("[test-05] linked server side via insert_client");
// -- Client setup using the other end of the socketpair --
let client_backend = ClientBackend::connect(s_client)?;
let client_conn = ClientConnection::from_backend(client_backend);
let mut event_queue: EventQueue<ClientState> = client_conn.new_event_queue();
let qhandle = event_queue.handle();
let _registry = client_conn.display().get_registry(&qhandle, ());
println!("[test-05 CLIENT] called get_registry, dispatching...");
let mut server_state = ServerState;
let mut client_state = ClientState::default();
// -- Roundtrip: dispatch alternately until client has events --
for round in 0..10 {
// server side
server_display.dispatch_clients(&mut server_state)?;
server_display.flush_clients()?;
// client side
let _ = event_queue.flush();
let processed = event_queue.dispatch_pending(&mut client_state)?;
if processed > 0 {
println!("[test-05] round {round}: client processed {processed} event(s)");
}
if !client_state.globals.is_empty() {
client_state.done = true;
break;
}
// Block briefly waiting for incoming events on client side
match event_queue.prepare_read() {
Some(read_guard) => {
let _ = read_guard.read();
}
None => {}
}
}
if !client_state.done || client_state.globals.is_empty() {
return Err(format!(
"client did not receive globals (got {})",
client_state.globals.len()
)
.into());
}
let names: Vec<&str> = client_state.globals.iter().map(|(_, n, _)| n.as_str()).collect();
let has_compositor = names.contains(&"wl_compositor");
let has_shm = names.contains(&"wl_shm");
if !has_compositor || !has_shm {
return Err(format!("missing expected globals: got {:?}", names).into());
}
println!(
"[test-05] received {} global(s): {:?}",
client_state.globals.len(),
names
);
Ok(())
}
fn main() -> ExitCode {
match run() {
Ok(()) => {
println!("[test-05] PASS: wayland-rs server/client handshake works on Redox");
ExitCode::SUCCESS
}
Err(e) => {
eprintln!("[test-05] FAIL: {e}");
ExitCode::FAILURE
}
}
}