jay-compositor 1.14.0

The Jay compositor
use {
    crate::{
        dbus::{BUS_DEST, BUS_PATH, DbusError, DictEntry, DynamicType, prelude::Variant},
        state::State,
        utils::{errorfmt::ErrorFmt, opaque::opaque, sleeper::Sleeper},
        wire_dbus::org,
    },
    std::{borrow::Cow, rc::Rc},
    thiserror::Error,
};

const SYSTEMD_DEST: &str = "org.freedesktop.systemd1";
const SYSTEMD_PATH: &str = "/org/freedesktop/systemd1";

#[derive(Debug, Error)]
pub enum UserSessionError {
    #[error("Could not access the user session bus")]
    AcquireSessionBus(#[source] DbusError),
}

pub async fn import_environment(state: &Rc<State>, key: &str, value: &str) {
    if let Err(e) = import_environment_(state, key, value).await {
        log::error!(
            "Could not import `{}={}` into the system environment: {}",
            key,
            value,
            ErrorFmt(e)
        );
    }
}

async fn import_environment_(
    state: &Rc<State>,
    key: &str,
    value: &str,
) -> Result<(), UserSessionError> {
    let session = match state.dbus.session().await {
        Ok(s) => s,
        Err(e) => return Err(UserSessionError::AcquireSessionBus(e)),
    };
    let setting = format!("{}={}", key, value);
    session.call(
        BUS_DEST,
        BUS_PATH,
        org::freedesktop::dbus::UpdateActivationEnvironment {
            environment: Cow::Borrowed(&[DictEntry {
                key: key.into(),
                value: value.into(),
            }]),
        },
        {
            let setting = setting.clone();
            move |rep| {
                if let Err(e) = rep {
                    log::error!(
                        "Could not import `{}` into the dbus environment: {}",
                        setting,
                        ErrorFmt(e)
                    );
                }
            }
        },
    );
    session.call(
        SYSTEMD_DEST,
        SYSTEMD_PATH,
        org::freedesktop::systemd1::manager::SetEnvironment {
            names: Cow::Borrowed(&[Cow::Borrowed(&setting)]),
        },
        {
            let setting = setting.clone();
            move |rep| {
                if let Err(e) = rep {
                    log::error!(
                        "Could not import `{}` into the systemd environment: {}",
                        setting,
                        ErrorFmt(e)
                    );
                }
            }
        },
    );
    Ok(())
}

pub async fn start_graphical_session(state: &Rc<State>) {
    let Some(sleeper) = &state.sleeper else {
        log::warn!("Cannot start graphical session because there is no sleeper");
        return;
    };
    if let Err(e) = start_graphical_session_(state, sleeper).await {
        log::error!("Could not start graphical session unit: {}", ErrorFmt(e));
    }
}

async fn start_graphical_session_(state: &Rc<State>, sleeper: &Sleeper) -> Result<(), DbusError> {
    let session = state.dbus.session().await?;
    let name = format!("jay-{}.scope", opaque());
    let properties = [
        (
            Cow::Borrowed("Upholds"),
            Variant::Array(
                DynamicType::String,
                vec![Variant::String(Cow::Borrowed("graphical-session.target"))],
            ),
        ),
        (
            Cow::Borrowed("PIDFDs"),
            Variant::Array(DynamicType::Fd, vec![Variant::Fd(sleeper.pidfd.clone())]),
        ),
    ];
    session
        .call_async(
            SYSTEMD_DEST,
            SYSTEMD_PATH,
            org::freedesktop::systemd1::manager::StartTransientUnit {
                name: Cow::Borrowed(&name),
                mode: Cow::Borrowed("fail"),
                properties: Cow::Borrowed(&properties),
                aux: Default::default(),
            },
        )
        .await?;
    Ok(())
}