I'm looking for ways that I can test my window manager in a nested X server launched from code, ideally. While I'm working in Rust, I suspect this is going to be a language-independent task.
I'm working on a cross-platform Wayland compositor and X11 window manager in Rust. With Smithay, a Rust library for creating Wayland compositors, I can easily test the compositor in a winit window as a builtin feature of Smithay. There is no such feature in the Rust XCB crate, though.
I have attempted to do this by spawning a Xephyr process and running my window manager on its display output. Unfortunately, there doesn't seem to be a way that I can determine when Xephyr is finished launching, so I have to wait an arbitrary delay before attempting to connect the window manager. This is obviously not ideal, and it could take different amounts of time to launch depending on the operating system.
Here is the code I used if you're interested (for anyone finding this question: feel free to copy this solution if there aren't any better answers):
#[cfg(any(feature = "testing", debug_assertions))]
let _process = testing.then_some({
const TESTING_DISPLAY: &str = ":1";
match Command::new("Xephyr").arg("-resizeable").arg(TESTING_DISPLAY).spawn() {
Ok(process) => {
event!(Level::DEBUG, "Initialised Xephyr");
// Set the `DISPLAY` env variable to `TESTING_DISPLAY`.
env::set_var("DISPLAY", TESTING_DISPLAY);
// Sleep for 3s to wait for Xephyr to launch. Not ideal.
thread::sleep(Duration::from_secs(3));
testing::Xephyr(process)
},
Err(error) => {
event!(Level::ERROR, "Failed to initialise Xephyr: {error}");
return Err(error.into());
},
}
});
// Connect to the X server using the `DISPLAY` env variable.
let (connection, screen_num) = xcb::Connection::connect(None)?;
testing::Xephyr is just a struct I use so I can attempt to kill the Xephyr window in its drop function when the window manager closes.