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
200 lines
6.3 KiB
Rust
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
|
|
}
|
|
}
|
|
}
|
|
|