jay-compositor 1.15.0

The Jay compositor
use crate::backend::BackendConnectorState;
use crate::backend::Connector;
use crate::backend::Mode;
use crate::backend::transaction::ConnectorTransaction;
use crate::client::ClientCaps;
use crate::fixed::Fixed;
use crate::format::XRGB8888;
use crate::ifs::wl_seat::WlSeatGlobal;
use crate::it::test_backend::TestBackend;
use crate::it::test_backend::TestBackendKb;
use crate::it::test_backend::TestBackendMouse;
use crate::it::test_backend::TestConnector;
use crate::it::test_client::TestClient;
use crate::it::test_config::TestConfig;
use crate::it::test_error::TestError;
use crate::it::test_error::TestErrorExt;
use crate::security_context_acceptor::AcceptorMetadata;
use crate::state::State;
use crate::tree::OutputNode;
use crate::tree::VrrMode;
use crate::utils::stack::Stack;
use crate::virtual_output::VirtualOutput;
use arrayvec::ArrayVec;
use std::rc::Rc;
use uapi::c;

pub struct TestRun {
    pub state: Rc<State>,
    pub backend: Rc<TestBackend>,
    pub errors: Stack<String>,
    pub out_dir: String,
    pub in_dir: String,
    pub cfg: Rc<TestConfig>,
}

impl TestRun {
    pub fn create_client(self: &Rc<Self>) -> Result<TestClient, TestError> {
        self.create_client2()
            .with_context(|| "Could not create a client")
    }

    fn create_client2(self: &Rc<Self>) -> Result<TestClient, TestError> {
        let (client1, client2) = uapi::socketpair(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0)
            .with_context(|| "Could not create a unix socket")?;
        let client = self.state.clients.spawn2(
            self.state.clients.id(),
            &self.state,
            Rc::new(client2),
            uapi::getuid(),
            uapi::getpid(),
            ClientCaps::all(),
            true,
            false,
            &Rc::new(AcceptorMetadata::secure()),
        );
        client.send_jay_compositor_enable_symmetric_delete();
        Ok(TestClient {
            run: self.clone(),
            _socket: client1,
            client,
        })
    }

    pub fn get_seat(&self, name: &str) -> Result<Rc<WlSeatGlobal>, TestError> {
        let id = self.cfg.get_seat(name)?;
        for seat in self.state.globals.seats.lock().values() {
            if seat.id() == id {
                return Ok(seat.clone());
            }
        }
        bail!("Seat {} does not exist", id)
    }

    pub async fn create_default_setup(&self) -> Result<DefaultSetup, TestError> {
        self.create_default_setup2(false).await
    }

    pub async fn create_default_setup2(&self, need_drm: bool) -> Result<DefaultSetup, TestError> {
        self.backend.install_default2(need_drm).await?;
        let seat = self.get_seat("default")?;
        self.state.eng.yield_now().await;
        let output = match self.state.root.outputs.lock().values().next() {
            None => bail!("No output"),
            Some(d) => d.clone(),
        };
        self.cfg
            .set_input_device_seat(self.backend.default_kb.common.id, seat.id())?;
        self.cfg
            .set_input_device_seat(self.backend.default_mouse.common.id, seat.id())?;
        self.backend.default_mouse.click(1);
        self.state.eng.yield_now().await;
        self.cfg.show_workspace(seat.id(), "")?;
        Ok(DefaultSetup {
            connector: self.backend.default_connector.clone(),
            output,
            kb: self.backend.default_kb.clone(),
            mouse: self.backend.default_mouse.clone(),
            seat,
        })
    }

    async fn create_virtual_output(
        &self,
        name: &str,
    ) -> Result<(Rc<VirtualOutput>, Rc<OutputNode>), TestError> {
        let vo = self.state.virtual_outputs.get_or_create(&self.state, name);
        let mut ct = ConnectorTransaction::new(&self.state);
        ct.add(
            &(vo.clone() as _),
            BackendConnectorState {
                serial: self.state.backend_connector_state_serials.next(),
                enabled: true,
                active: true,
                mode: Mode {
                    width: 800,
                    height: 600,
                    refresh_rate_millihz: 0,
                },
                non_desktop_override: None,
                vrr: true,
                tearing: false,
                format: XRGB8888,
                color_space: Default::default(),
                eotf: Default::default(),
                gamma_lut: Default::default(),
            },
        )?;
        ct.prepare()?.apply()?.commit();
        self.sync().await;
        let Some(node) = self.state.root.outputs.get(&vo.id()) else {
            bail!("no output node");
        };
        Ok((vo, node))
    }

    pub async fn create_session_management_setup(&self) -> Result<SmSetup, TestError> {
        self.backend.install_render_context(false)?;
        let mut outputs = ArrayVec::new();
        for i in 0..outputs.capacity() {
            let (vo, node) = self.create_virtual_output(&format!("o{i}")).await?;
            node.set_position(i as i32 * 800, 0);
            node.set_vrr_mode(&VrrMode::Always);
            outputs.push(SmOutput { vo, node });
        }
        self.state.fallback_output.set(Some(outputs[0].vo.id()));
        Ok(SmSetup { outputs })
    }

    pub async fn sync(&self) {
        self.state.eng.yield_now().await;
    }
}

pub struct DefaultSetup {
    pub connector: Rc<TestConnector>,
    pub output: Rc<OutputNode>,
    pub kb: Rc<TestBackendKb>,
    pub mouse: Rc<TestBackendMouse>,
    pub seat: Rc<WlSeatGlobal>,
}

impl DefaultSetup {
    pub fn move_to(&self, x: i32, y: i32) {
        let (ox, oy) = self.seat.pointer_cursor().position();
        let (nx, ny) = (Fixed::from_int(x), Fixed::from_int(y));
        let (dx, dy) = (nx - ox, ny - oy);
        self.mouse.rel(dx.to_f64(), dy.to_f64());
    }
}

pub struct SmSetup {
    pub outputs: ArrayVec<SmOutput, 2>,
}

pub struct SmOutput {
    vo: Rc<VirtualOutput>,
    pub node: Rc<OutputNode>,
}