Use -displayfd instead of signals for Xwayland readiness
The -displayfd argument is much friendlier to testing and is generally more stable. As a result, omitting the display argument to the program will result in Xwayland searching for the next available display. This also allows integration tests to run simultaneously and when there is already an X server running on the system. This commit also changes how the state of the program is communicated with integration tests (via the RunData trait).
This commit is contained in:
parent
7976e3ad37
commit
b8bd07ce93
5 changed files with 155 additions and 134 deletions
|
|
@ -6,17 +6,11 @@ use crate::server::{PendingSurfaceState, ServerState};
|
|||
use crate::xstate::XState;
|
||||
use log::{error, info};
|
||||
use rustix::event::{poll, PollFd, PollFlags};
|
||||
use signal_hook::consts::*;
|
||||
use std::io::{BufRead, BufReader, Read, Write};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::fd::{AsFd, AsRawFd, BorrowedFd};
|
||||
use std::os::unix::{net::UnixStream, process::CommandExt};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
mpsc::Sender,
|
||||
Arc,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use wayland_server::{Display, ListeningSocket};
|
||||
use xcb::x;
|
||||
|
||||
|
|
@ -36,62 +30,46 @@ pub trait FromServerState<C: XConnection> {
|
|||
|
||||
type RealServerState = ServerState<Arc<xcb::Connection>>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum StateEvent {
|
||||
CreatedServer,
|
||||
ConnectedServer,
|
||||
XwaylandReady,
|
||||
pub trait RunData {
|
||||
fn display(&self) -> Option<&str>;
|
||||
fn server(&self) -> Option<UnixStream> {
|
||||
None
|
||||
}
|
||||
fn created_server(&self) {}
|
||||
fn connected_server(&self) {}
|
||||
fn xwayland_ready(&self, _display: String) {}
|
||||
}
|
||||
|
||||
pub fn main(comp: Option<UnixStream>, state_updater: Option<Sender<StateEvent>>) -> Option<()> {
|
||||
let display_arg = get_display()?;
|
||||
|
||||
pub fn main(data: impl RunData) -> Option<()> {
|
||||
let socket = ListeningSocket::bind_auto("wayland", 1..=128).unwrap();
|
||||
let mut display = Display::<RealServerState>::new().unwrap();
|
||||
let dh = display.handle();
|
||||
data.created_server();
|
||||
|
||||
let mut server_state = RealServerState::new(dh, comp);
|
||||
if let Some(ref s) = state_updater {
|
||||
s.send(StateEvent::CreatedServer).unwrap();
|
||||
}
|
||||
let mut server_state = RealServerState::new(dh, data.server());
|
||||
|
||||
let (xsock_wl, xsock_xwl) = UnixStream::pair().unwrap();
|
||||
// Prevent creation of new Xwayland command from closing fd
|
||||
rustix::io::fcntl_setfd(&xsock_xwl, rustix::io::FdFlags::empty()).unwrap();
|
||||
|
||||
// Flag when Xwayland is ready to accept our connection
|
||||
let ready = Arc::new(AtomicBool::new(false));
|
||||
signal_hook::flag::register(SIGUSR1, ready.clone()).unwrap();
|
||||
|
||||
let (ready_tx, ready_rx) = UnixStream::pair().unwrap();
|
||||
rustix::io::fcntl_setfd(&ready_tx, rustix::io::FdFlags::empty()).unwrap();
|
||||
let mut xwayland = Command::new("Xwayland");
|
||||
let mut xwayland = unsafe {
|
||||
xwayland.pre_exec(|| {
|
||||
// Set SIGUSR1 to SIG_IGN for Xwayland to get SIGUSR1 to our main process,
|
||||
// which signifies that the X server is ready to accept connections
|
||||
let mut sa_mask = MaybeUninit::uninit();
|
||||
libc::sigemptyset(sa_mask.as_mut_ptr());
|
||||
let sa_mask = sa_mask.assume_init();
|
||||
let act = libc::sigaction {
|
||||
sa_sigaction: libc::SIG_IGN,
|
||||
sa_mask,
|
||||
sa_flags: 0,
|
||||
sa_restorer: None,
|
||||
};
|
||||
libc::sigaction(SIGUSR1, &act, std::ptr::null_mut());
|
||||
Ok(())
|
||||
})
|
||||
if let Some(display) = data.display() {
|
||||
xwayland.arg(display);
|
||||
}
|
||||
.env("WAYLAND_DISPLAY", socket.socket_name().unwrap())
|
||||
//.env("WAYLAND_DEBUG", "1")
|
||||
.args([
|
||||
&display_arg,
|
||||
"-rootless",
|
||||
"-wm",
|
||||
&format!("{}", &xsock_xwl.as_raw_fd()),
|
||||
])
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
let mut xwayland = xwayland
|
||||
.args([
|
||||
"-rootless",
|
||||
"-wm",
|
||||
&xsock_xwl.as_raw_fd().to_string(),
|
||||
"-displayfd",
|
||||
&ready_tx.as_raw_fd().to_string(),
|
||||
])
|
||||
.env("WAYLAND_DISPLAY", socket.socket_name().unwrap())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
|
||||
let (mut finish_tx, mut finish_rx) = UnixStream::pair().unwrap();
|
||||
let stderr = xwayland.stderr.take().unwrap();
|
||||
|
|
@ -124,9 +102,7 @@ pub fn main(comp: Option<UnixStream>, state_updater: Option<Sender<StateEvent>>)
|
|||
return None;
|
||||
}
|
||||
|
||||
if let Some(ref s) = state_updater {
|
||||
s.send(StateEvent::ConnectedServer).unwrap();
|
||||
}
|
||||
data.connected_server();
|
||||
socket.accept().unwrap().unwrap()
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
@ -149,26 +125,29 @@ pub fn main(comp: Option<UnixStream>, state_updater: Option<Sender<StateEvent>>)
|
|||
PollFd::from_borrowed_fd(server_fd, PollFlags::IN),
|
||||
PollFd::new(&xsock_wl, PollFlags::IN),
|
||||
PollFd::from_borrowed_fd(display_fd, PollFlags::IN),
|
||||
PollFd::new(&ready_rx, PollFlags::IN),
|
||||
];
|
||||
|
||||
let mut ready = false;
|
||||
loop {
|
||||
match poll(&mut fds, -1) {
|
||||
Ok(_) => {}
|
||||
Err(rustix::io::Errno::INTR) => {
|
||||
// Typically caused by SIGUSR1
|
||||
if !ready.load(Ordering::Relaxed) {
|
||||
continue;
|
||||
Ok(_) => {
|
||||
if !fds[3].revents().is_empty() {
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
Err(other) => panic!("Poll failed: {other:?}"),
|
||||
}
|
||||
|
||||
if xstate.is_none() && ready.load(Ordering::Relaxed) {
|
||||
if xstate.is_none() && ready {
|
||||
let xstate = xstate.insert(XState::new(xsock_wl.as_fd()));
|
||||
info!("Connected to Xwayland on {display_arg}");
|
||||
if let Some(ref s) = state_updater {
|
||||
s.send(StateEvent::XwaylandReady).unwrap();
|
||||
}
|
||||
let mut reader = BufReader::new(&ready_rx);
|
||||
let mut display = String::new();
|
||||
reader.read_line(&mut display).unwrap();
|
||||
display.pop();
|
||||
display.insert(0, ':');
|
||||
info!("Connected to Xwayland on {display}");
|
||||
data.xwayland_ready(display);
|
||||
server_state.set_x_connection(xstate.connection.clone());
|
||||
server_state.atoms = Some(xstate.atoms.clone());
|
||||
}
|
||||
|
|
@ -182,16 +161,3 @@ pub fn main(comp: Option<UnixStream>, state_updater: Option<Sender<StateEvent>>)
|
|||
display.flush_clients().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn get_display() -> Option<String> {
|
||||
let mut args: Vec<_> = std::env::args().collect();
|
||||
if args.len() > 2 {
|
||||
error!("Unexpected arguments: {:?}", &args[2..]);
|
||||
return None;
|
||||
}
|
||||
if args.len() == 1 {
|
||||
Some(":0".into())
|
||||
} else {
|
||||
Some(args.swap_remove(1))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue