You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过代码(Rust)启动嵌套X服务器以测试X11窗口管理器?

解决方案:替代固定延时检测Xephyr就绪状态

针对Xephyr启动后无法准确判断就绪时间的问题,以下是几种更可靠的替代方案:

方案1:循环尝试X服务器连接(最可靠)

核心思路是反复尝试建立xcb连接,直到成功或达到最大重试次数。这是判断X服务器是否就绪的最直接标准,完全不依赖外部输出或环境差异。

修改后的代码片段:

#[cfg(any(feature = "testing", debug_assertions))]
let (connection, screen_num) = {
    const TESTING_DISPLAY: &str = ":1";
    const MAX_CONN_ATTEMPTS: u32 = 30;
    const RETRY_DELAY: Duration = Duration::from_millis(100);

    let process = match Command::new("Xephyr")
        .arg("-resizeable")
        .arg(TESTING_DISPLAY)
        .spawn()
    {
        Ok(p) => {
            event!(Level::DEBUG, "Started Xephyr process");
            p
        }
        Err(e) => {
            event!(Level::ERROR, "Failed to start Xephyr: {e}");
            return Err(e.into());
        }
    };

    // 循环尝试建立X连接
    let mut conn_attempts = 0;
    let connection = loop {
        conn_attempts += 1;
        match xcb::Connection::connect(Some(TESTING_DISPLAY)) {
            Ok(conn) => break conn,
            Err(e) => {
                if conn_attempts >= MAX_CONN_ATTEMPTS {
                    event!(Level::ERROR, "Failed to connect to Xephyr after {MAX_CONN_ATTEMPTS} attempts: {e}");
                    process.kill().ok();
                    return Err(e.into());
                }
                thread::sleep(RETRY_DELAY);
            }
        }
    };

    // 保存Xephyr进程,通过drop自动清理
    let _process = testing::Xephyr(process);
    connection
};

// 后续直接使用connection和screen_num进行业务逻辑处理

方案2:监听Xephyr的启动日志

Xephyr启动完成并准备接受客户端连接时,会在stderr输出特定日志(不同版本可能略有差异,比如Waiting for clients to connect)。你可以通过捕获并监听这个输出来判断就绪状态。

代码示例:

#[cfg(any(feature = "testing", debug_assertions))]
let (connection, screen_num) = {
    const TESTING_DISPLAY: &str = ":1";
    const READY_PATTERN: &str = "Waiting for clients to connect";
    const WAIT_TIMEOUT: Duration = Duration::from_secs(5);

    let mut process = Command::new("Xephyr")
        .arg("-resizeable")
        .arg(TESTING_DISPLAY)
        .stderr(std::process::Stdio::piped())
        .spawn()
        .map_err(|e| {
            event!(Level::ERROR, "Failed to start Xephyr: {e}");
            e.into()
        })?;

    event!(Level::DEBUG, "Started Xephyr process");

    // 启动线程监听stderr输出
    let (tx, rx) = std::sync::mpsc::channel();
    let mut stderr = process.stderr.take().unwrap();
    std::thread::spawn(move || {
        let mut reader = std::io::BufReader::new(stderr);
        let mut line = String::new();
        while reader.read_line(&mut line).is_ok() {
            if line.contains(READY_PATTERN) {
                let _ = tx.send(());
                break;
            }
            line.clear();
        }
    });

    // 等待就绪信号或超时
    match rx.recv_timeout(WAIT_TIMEOUT) {
        Ok(_) => event!(Level::DEBUG, "Xephyr is ready to accept connections"),
        Err(_) => {
            event!(Level::WARN, "Timeout waiting for Xephyr ready signal, falling back to short sleep");
            thread::sleep(Duration::from_secs(1));
        }
    }

    // 建立连接
    let connection = xcb::Connection::connect(Some(TESTING_DISPLAY))
        .map_err(|e| {
            event!(Level::ERROR, "Failed to connect to Xephyr: {e}");
            process.kill().ok();
            e.into()
        })?;

    let _process = testing::Xephyr(process);
    connection
};

注意:如果你的Xephyr版本输出的就绪日志不同,需要先手动运行Xephyr :1查看对应内容,替换READY_PATTERN的值。

方案3:使用Xvfb进行无头测试(可选)

如果不需要可视化测试界面,可以使用Xvfb(虚拟帧缓冲X服务器)替代Xephyr。它启动更快、资源占用更低,同样可以用方案1的循环连接法检测就绪状态:

// 启动Xvfb的代码示例
let process = Command::new("Xvfb")
    .arg(TESTING_DISPLAY)
    .arg("-screen")
    .arg("0")
    .arg("1024x768x24")
    .spawn()?;

后续连接逻辑与方案1完全一致。


内容的提问来源于stack exchange,提问作者Antikyth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 08:04:54