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
This commit is contained in:
Votre Nom 2026-05-08 17:41:55 +02:00
commit 53e6626231
21 changed files with 3676 additions and 0 deletions

View file

@ -0,0 +1,142 @@
//! Test 3 — shm_open + mmap MAP_SHARED.
//!
//! Wayland clients use shm_open + ftruncate + mmap to create wl_shm pools.
//! The compositor then receives the fd via SCM_RIGHTS and mmaps the same region.
//!
//! relibc Redox maps shm_open("/foo") to open("/scheme/shm/foo").
//! This test validates:
//! 1. shm_open with O_RDWR | O_CREAT works
//! 2. ftruncate sets the size
//! 3. mmap MAP_SHARED returns a writable region
//! 4. writes are persistent: close + reopen + remap reads back the data
//! 5. shm_unlink removes the segment
//!
//! Two passes (write then read-back) simulate the producer/consumer pattern
//! used by Wayland: client writes pixels, compositor reads them.
use std::ffi::CString;
use std::io;
use std::os::fd::RawFd;
use std::process::ExitCode;
use std::ptr;
const SHM_NAME: &str = "/redox-wl-test-03";
const SIZE: usize = 4096;
fn errno_str() -> String {
let e = io::Error::last_os_error();
format!("{e} (errno={})", e.raw_os_error().unwrap_or(0))
}
unsafe fn shm_open_rw_create(name: &str, mode: libc::mode_t) -> Result<RawFd, String> {
let cname = CString::new(name).unwrap();
let fd = libc::shm_open(
cname.as_ptr(),
libc::O_RDWR | libc::O_CREAT,
mode as _,
);
if fd < 0 {
return Err(format!("shm_open({name}): {}", errno_str()));
}
Ok(fd)
}
unsafe fn shm_open_rw(name: &str) -> Result<RawFd, String> {
let cname = CString::new(name).unwrap();
let fd = libc::shm_open(cname.as_ptr(), libc::O_RDWR, 0);
if fd < 0 {
return Err(format!("shm_open(reopen {name}): {}", errno_str()));
}
Ok(fd)
}
unsafe fn map_rw(fd: RawFd, size: usize) -> Result<*mut u8, String> {
let p = libc::mmap(
ptr::null_mut(),
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd,
0,
);
if p == libc::MAP_FAILED {
return Err(format!("mmap MAP_SHARED: {}", errno_str()));
}
Ok(p as *mut u8)
}
fn run() -> Result<(), String> {
println!("[test-03] shm_open + mmap MAP_SHARED");
unsafe {
// Phase 1: create + write
let fd1 = shm_open_rw_create(SHM_NAME, 0o600)?;
println!("[test-03] phase1: shm_open created, fd={fd1}");
if libc::ftruncate(fd1, SIZE as _) != 0 {
return Err(format!("ftruncate: {}", errno_str()));
}
let map1 = map_rw(fd1, SIZE)?;
println!("[test-03] phase1: mmap at {:p}", map1);
// Write a recognisable pattern (ARGB8888 simulation: 256 pixels of 0xDEADBEEF)
let pixels = std::slice::from_raw_parts_mut(map1 as *mut u32, SIZE / 4);
for (i, p) in pixels.iter_mut().enumerate() {
*p = 0xDEAD_BEEF_u32.wrapping_add(i as u32);
}
println!("[test-03] phase1: wrote {} u32 pixels", pixels.len());
if libc::munmap(map1 as *mut _, SIZE) != 0 {
return Err(format!("munmap phase1: {}", errno_str()));
}
libc::close(fd1);
// Phase 2: reopen + check pattern
let fd2 = shm_open_rw(SHM_NAME)?;
println!("[test-03] phase2: shm_open reopened, fd={fd2}");
let map2 = map_rw(fd2, SIZE)?;
println!("[test-03] phase2: mmap at {:p}", map2);
let pixels2 = std::slice::from_raw_parts(map2 as *const u32, SIZE / 4);
for (i, &p) in pixels2.iter().enumerate() {
let expected = 0xDEAD_BEEF_u32.wrapping_add(i as u32);
if p != expected {
libc::munmap(map2 as *mut _, SIZE);
libc::close(fd2);
let cname = CString::new(SHM_NAME).unwrap();
libc::shm_unlink(cname.as_ptr());
return Err(format!("pixel {i} mismatch: got {p:#x}, expected {expected:#x}"));
}
}
println!("[test-03] phase2: pattern verified across close/reopen");
if libc::munmap(map2 as *mut _, SIZE) != 0 {
return Err(format!("munmap phase2: {}", errno_str()));
}
libc::close(fd2);
// Cleanup
let cname = CString::new(SHM_NAME).unwrap();
if libc::shm_unlink(cname.as_ptr()) != 0 {
return Err(format!("shm_unlink: {}", errno_str()));
}
println!("[test-03] cleanup: shm_unlink OK");
}
Ok(())
}
fn main() -> ExitCode {
match run() {
Ok(()) => {
println!("[test-03] PASS: shm_open/mmap/persistence/unlink all work");
ExitCode::SUCCESS
}
Err(e) => {
eprintln!("[test-03] FAIL: {e}");
ExitCode::FAILURE
}
}
}