jay-compositor 1.15.0

The Jay compositor
use crate::client::CAP_IDLE_NOTIFIER;
use crate::client::Client;
use crate::client::ClientCaps;
use crate::client::LookupError;
use crate::globals::Global;
use crate::globals::GlobalName;
use crate::ifs::ext_idle_notification_v1::ExtIdleNotificationV1;
use crate::leaks::Tracker;
use crate::object::Version;
use crate::utils::errorfmt::ErrorFmt;
use crate::wire::ExtIdleNotificationV1Id;
use crate::wire::ExtIdleNotifierV1Id;
use crate::wire::WlSeatId;
use crate::wire::ext_idle_notifier_v1::*;
use jay_proc::Global;
use jay_proc::Object;
use std::cell::Cell;
use std::convert::Infallible;
use std::rc::Rc;

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

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

    fn bind_(
        self: Rc<Self>,
        id: ExtIdleNotifierV1Id,
        client: &Rc<Client>,
        version: Version,
    ) -> Result<(), Infallible> {
        let obj = Rc::new(ExtIdleNotifierV1 {
            id,
            client: client.clone(),
            tracker: Default::default(),
            version,
        });
        track!(client, obj);
        client.add_client_obj(&obj);
        Ok(())
    }
}

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

impl ExtIdleNotifierV1 {
    fn create_notification(
        &self,
        id: ExtIdleNotificationV1Id,
        timeout: u32,
        seat: WlSeatId,
        skip_if_inhibited: bool,
    ) -> Result<(), LookupError> {
        let seat = self.client.lookup(seat)?;
        let notification = Rc::new(ExtIdleNotificationV1 {
            id,
            client: self.client.clone(),
            tracker: Default::default(),
            resume: Default::default(),
            task: Cell::new(None),
            seat: seat.global.clone(),
            duration_usec: (timeout as u64).max(1000).saturating_mul(1000),
            version: self.version,
        });
        track!(self.client, notification);
        self.client.add_client_obj(&notification);
        let future = self.client.state.eng.spawn(
            "idle notifier",
            run(notification.clone(), skip_if_inhibited),
        );
        notification.task.set(Some(future));
        Ok(())
    }
}

impl ExtIdleNotifierV1RequestHandler for ExtIdleNotifierV1 {
    type Error = LookupError;

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

    fn get_idle_notification(
        &self,
        req: GetIdleNotification,
        _slf: &Rc<Self>,
    ) -> Result<(), Self::Error> {
        self.create_notification(req.id, req.timeout, req.seat, true)
    }

    fn get_input_idle_notification(
        &self,
        req: GetInputIdleNotification,
        _slf: &Rc<Self>,
    ) -> Result<(), Self::Error> {
        self.create_notification(req.id, req.timeout, req.seat, false)
    }
}

async fn run(n: Rc<ExtIdleNotificationV1>, skip_if_inhibited: bool) {
    loop {
        let now = n.client.state.now_usec();
        let elapsed = now.saturating_sub(n.seat.last_input());
        if elapsed < n.duration_usec {
            let res = n
                .client
                .state
                .wheel
                .timeout((n.duration_usec - elapsed).div_ceil(1000))
                .await;
            if let Err(e) = res {
                log::error!("Could not wait for idle timeout to elapse: {}", ErrorFmt(e));
                return;
            }
        } else {
            let idle = &n.client.state.idle;
            let send_idle = !skip_if_inhibited || idle.inhibitors.is_empty();
            if send_idle {
                n.send_idled();
                n.seat.add_idle_notification(&n);
            } else {
                idle.add_inhibited_notification(&n);
            }
            n.resume.triggered().await;
            if send_idle {
                n.send_resumed();
            }
        }
    }
}

impl Global for ExtIdleNotifierV1Global {
    fn version(&self) -> u32 {
        2
    }

    fn required_caps(&self) -> ClientCaps {
        CAP_IDLE_NOTIFIER
    }
}