jay-compositor 1.15.0

The Jay compositor
use crate::client::Client;
use crate::client::LookupError;
use crate::leaks::Tracker;
use crate::object::BreakLoops;
use crate::object::Version;
use crate::sm::Session;
use crate::sm::SessionGetStatus;
use crate::sm::SessionManagementError;
use crate::sm::SessionName;
use crate::sm::SessionOwner;
use crate::utils::clonecell::CloneCell;
use crate::utils::linkedlist::LinkedNode;
use crate::wire::XdgSessionV1Id;
use crate::wire::xdg_session_v1::AddToplevel;
use crate::wire::xdg_session_v1::Created;
use crate::wire::xdg_session_v1::Destroy;
use crate::wire::xdg_session_v1::Remove;
use crate::wire::xdg_session_v1::RemoveToplevel;
use crate::wire::xdg_session_v1::Replaced;
use crate::wire::xdg_session_v1::RestoreToplevel;
use crate::wire::xdg_session_v1::Restored;
use crate::wire::xdg_session_v1::XdgSessionV1RequestHandler;
use jay_proc::Object;
use std::cell::RefCell;
use std::error::Error;
use std::rc::Rc;
use thiserror::Error;

#[derive(Object)]
#[break_loops]
pub struct XdgSessionV1 {
    pub id: XdgSessionV1Id,
    pub client: Rc<Client>,
    pub tracker: Tracker<Self>,
    pub version: Version,
    pub session: CloneCell<Option<Rc<Session>>>,
    pub name: SessionName,
    pub link: RefCell<Option<LinkedNode<Rc<Self>>>>,
}

impl XdgSessionV1 {
    fn disown_(&self, to_peer: bool) {
        if let Some(session) = self.session.take() {
            if to_peer {
                session.disown_from_peer();
            }
        }
        if self.link.take().is_some() {
            self.client.num_live_sessions.fetch_sub(1);
        }
    }

    pub fn disown_to_peer(&self) {
        self.disown_(true);
    }

    pub fn bump(&self) {
        if let Some(link) = &*self.link.borrow() {
            self.client.live_sessions.add_last_existing(link);
        }
    }
}

impl SessionOwner for XdgSessionV1 {
    fn client(&self) -> Option<&Rc<Client>> {
        Some(&self.client)
    }

    fn loaded(&self, status: SessionGetStatus) {
        match status {
            SessionGetStatus::Created => self.send_created(self.name),
            SessionGetStatus::Restored => self.send_restored(),
        }
    }

    fn error(&self, e: &dyn Error) {
        self.client.error(e);
    }

    fn disown_from_peer(&self, replaced: bool) {
        if replaced {
            self.send_replaced();
        }
        self.disown_(false);
    }
}

impl XdgSessionV1 {
    pub fn send_created(&self, name: SessionName) {
        self.client.event(Created {
            self_id: self.id,
            session_id: &name.to_string(),
        });
    }

    pub fn send_restored(&self) {
        self.client.event(Restored { self_id: self.id });
    }

    pub fn send_replaced(&self) {
        self.client.event(Replaced { self_id: self.id });
    }
}

impl XdgSessionV1RequestHandler for XdgSessionV1 {
    type Error = XdgSessionV1Error;

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

    fn remove(&self, _req: Remove, _slf: &Rc<Self>) -> Result<(), Self::Error> {
        if let Some(session) = self.session.get() {
            session.remove();
        }
        self.disown_from_peer(false);
        self.client.remove_obj(self);
        Ok(())
    }

    fn add_toplevel(&self, req: AddToplevel<'_>, slf: &Rc<Self>) -> Result<(), Self::Error> {
        slf.create_session(req.id, req.toplevel, false, req.name)
    }

    fn restore_toplevel(
        &self,
        req: RestoreToplevel<'_>,
        slf: &Rc<Self>,
    ) -> Result<(), Self::Error> {
        slf.create_session(req.id, req.toplevel, true, req.name)
    }

    fn remove_toplevel(&self, req: RemoveToplevel<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
        self.bump();
        if let Some(session) = self.session.get() {
            session.remove_toplevel(req.name);
        }
        Ok(())
    }
}

impl BreakLoops for XdgSessionV1 {
    fn break_loops(self: Rc<Self>) {
        self.disown_to_peer();
    }
}

#[derive(Debug, Error)]
pub enum XdgSessionV1Error {
    #[error(transparent)]
    Lookup(#[from] LookupError),
    #[error("The toplevel is already part of a session")]
    HasSession,
    #[error("The toplevel has already been committed")]
    Committed,
    #[error(transparent)]
    SessionManagementError(#[from] SessionManagementError),
}