jay-compositor 1.15.0

The Jay compositor
use crate::client::Client;
use crate::globals::Global;
use crate::globals::GlobalName;
use crate::ifs::xdg_session_v1::XdgSessionV1;
use crate::leaks::Tracker;
use crate::object::Version;
use crate::sm::SessionGetStatus;
use crate::sm::SessionManager;
use crate::sm::SessionName;
use crate::sm::SessionReason;
use crate::sm::session_name;
use crate::state::State;
use crate::wire::XdgSessionManagerV1Id;
use crate::wire::xdg_session_manager_v1::*;
use jay_proc::Global;
use jay_proc::Object;
use std::rc::Rc;
use std::str::FromStr;
use thiserror::Error;

pub const REASON_LAUNCH: u32 = 1;
pub const REASON_RECOVER: u32 = 2;
const REASON_SESSION_RESTORE: u32 = 3;

#[derive(Global)]
pub struct XdgSessionManagerV1Global {
    name: GlobalName,
}

impl XdgSessionManagerV1Global {
    pub fn new(name: GlobalName) -> Self {
        Self { name }
    }

    fn bind_(
        self: Rc<Self>,
        id: XdgSessionManagerV1Id,
        client: &Rc<Client>,
        version: Version,
    ) -> Result<(), XdgSessionManagerV1Error> {
        let Some(sm) = &client.state.sm else {
            return Err(XdgSessionManagerV1Error::SessionManagerNotAvailable);
        };
        let obj = Rc::new(XdgSessionManagerV1 {
            id,
            client: client.clone(),
            tracker: Default::default(),
            version,
            sm: sm.clone(),
        });
        track!(client, obj);
        client.add_client_obj(&obj);
        Ok(())
    }
}

impl Global for XdgSessionManagerV1Global {
    fn version(&self) -> u32 {
        1
    }

    fn exposed(&self, state: &State) -> bool {
        state.sm.is_some() && state.session_management_enabled.get()
    }
}

#[derive(Object)]
pub struct XdgSessionManagerV1 {
    id: XdgSessionManagerV1Id,
    client: Rc<Client>,
    tracker: Tracker<Self>,
    version: Version,
    sm: Rc<SessionManager>,
}

const MAX_LIVE_SESSIONS: usize = 32;

impl XdgSessionManagerV1RequestHandler for XdgSessionManagerV1 {
    type Error = XdgSessionManagerV1Error;

    fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
        self.client.remove_obj(self);
        Ok(())
    }

    fn get_session(&self, req: GetSession<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
        let reason = match req.reason {
            REASON_LAUNCH => SessionReason::Launch,
            REASON_RECOVER => SessionReason::Recover,
            REASON_SESSION_RESTORE => SessionReason::SessionRestore,
            n => return Err(XdgSessionManagerV1Error::UnknownReason(n)),
        };
        let name = req
            .session_id
            .and_then(|s| SessionName::from_str(s).ok())
            .unwrap_or_else(session_name);
        let obj = Rc::new(XdgSessionV1 {
            id: req.id,
            client: self.client.clone(),
            tracker: Default::default(),
            version: self.version,
            session: Default::default(),
            name,
            link: Default::default(),
        });
        track!(self.client, obj);
        self.client.add_client_obj(&obj);
        let (session, status) = self
            .sm
            .get(name, req.session_id.is_some(), reason, obj.clone());
        obj.session.set(Some(session));
        if self.client.num_live_sessions.fetch_add(1) >= MAX_LIVE_SESSIONS
            && let Some(old) = self.client.live_sessions.first()
        {
            old.disown_to_peer();
            old.send_replaced();
        }
        *obj.link.borrow_mut() = Some(self.client.live_sessions.add_last(obj.clone()));
        if let Some(status) = status {
            match status {
                SessionGetStatus::Created => obj.send_created(name),
                SessionGetStatus::Restored => obj.send_restored(),
            }
        }
        Ok(())
    }
}

#[derive(Debug, Error)]
pub enum XdgSessionManagerV1Error {
    #[error("The session manager is not available")]
    SessionManagerNotAvailable,
    #[error("Unknown reason {0}")]
    UnknownReason(u32),
}