jay-compositor 1.15.0

The Jay compositor
use crate::client::Client;
use crate::globals::Global;
use crate::globals::GlobalName;
use crate::globals::GlobalsError;
use crate::globals::Singleton;
use crate::leaks::Tracker;
use crate::object::Interface;
use crate::object::Version;
use crate::wire::WlRegistryId;
use crate::wire::wl_registry::*;
use jay_proc::Object;
use linearize::StaticMap;
use std::cell::Cell;
use std::rc::Rc;
use thiserror::Error;

#[derive(Object)]
pub struct WlRegistry {
    id: WlRegistryId,
    version: Version,
    pub client: Rc<Client>,
    pub tracker: Tracker<Self>,
    advertised: StaticMap<Singleton, Cell<bool>>,
}

impl WlRegistry {
    pub fn new(id: WlRegistryId, client: &Rc<Client>) -> Self {
        Self {
            id,
            version: Version(1),
            client: client.clone(),
            tracker: Default::default(),
            advertised: Default::default(),
        }
    }

    pub fn handle_global(&self, global: &Rc<dyn Global>) {
        if let Some(singleton) = global.singleton()
            && self.advertised[singleton].replace(true)
        {
            return;
        }
        self.client.event(crate::wire::wl_registry::Global {
            self_id: self.id,
            name: global.name().raw(),
            interface: global.interface().name(),
            version: global.version(),
        });
    }

    pub fn handle_global_removed(&self, global: &dyn Global) {
        if let Some(singleton) = global.singleton()
            && !self.advertised[singleton].replace(false)
        {
            return;
        }
        self.client.event(GlobalRemove {
            self_id: self.id,
            name: global.name().raw(),
        });
    }
}

impl WlRegistryRequestHandler for WlRegistry {
    type Error = WlRegistryError;

    fn bind(&self, bind: Bind<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
        let name = GlobalName::from_raw(bind.name);
        let globals = &self.client.state.globals;
        let global = globals.get(
            name,
            self.client.effective_caps.get(),
            self.client.is_xwayland,
        )?;
        if global.interface().name() != bind.interface {
            return Err(WlRegistryError::InvalidInterface(InterfaceError {
                name: global.name(),
                interface: global.interface(),
                actual: bind.interface.to_string(),
            }));
        }
        if bind.version > global.version() {
            return Err(WlRegistryError::InvalidVersion(VersionError {
                name: global.name(),
                interface: global.interface(),
                version: global.version(),
                actual: bind.version,
            }));
        }
        global.bind(&self.client, bind.id, Version(bind.version))?;
        Ok(())
    }
}

#[derive(Debug, Error)]
pub enum WlRegistryError {
    #[error(transparent)]
    GlobalsError(Box<GlobalsError>),
    #[error("Tried to bind to global {} of type {} using interface {}", .0.name, .0.interface.name(), .0.actual)]
    InvalidInterface(InterfaceError),
    #[error("Tried to bind to global {} of type {} and version {} using version {}", .0.name, .0.interface.name(), .0.version, .0.actual)]
    InvalidVersion(VersionError),
}
efrom!(WlRegistryError, GlobalsError);

#[derive(Debug)]
pub struct InterfaceError {
    name: GlobalName,
    interface: Interface,
    actual: String,
}

#[derive(Debug)]
pub struct VersionError {
    name: GlobalName,
    interface: Interface,
    version: u32,
    actual: u32,
}