pinenote-service 1.0.1

Management dervice for Pine64's PineNote device
Documentation
use anyhow::Context;
use nix::libc::pid_t;
use pinenote_service::types::{
    Rect,
    rockchip_ebc::{DitherMode, DriverMode, Hint},
};
use tokio::sync::{mpsc, oneshot};

use super::OffScreenError;

pub enum Command {
    Application(Application),
    Dump(String),
    FbDumpToDir(String),
    GlobalRefresh,
    Property(Property),
    SetMode(DriverMode, DitherMode, u16),
    Window(Window),
    OffScreen(String, oneshot::Sender<Result<(), OffScreenError>>),
}

pub enum Application {
    Add(pid_t, oneshot::Sender<String>),
    Remove(String),
}

pub enum Property {
    DefaultHint(oneshot::Sender<Hint>),
    SetDefaultHint(Hint),
    DriverMode(oneshot::Sender<DriverMode>),
    SetDriverMode(DriverMode),
    DitherMode(oneshot::Sender<DitherMode>),
    SetDitherMode(DitherMode),
    RedrawDelay(oneshot::Sender<u16>),
    SetRedrawDelay(u16),
    OffScreenDisable(oneshot::Sender<bool>),
    SetOffScreenDisable(bool),
    OffScreenOverride(oneshot::Sender<String>),
}

#[derive(Default)]
pub struct WindowUpdate {
    pub title: Option<String>,
    pub area: Option<Rect>,
    pub hint: Option<Option<Hint>>,
    pub visible: Option<bool>,
    pub fullscreen: Option<bool>,
    pub z_index: Option<i32>,
}

pub enum Window {
    Add {
        app_key: String,
        title: String,
        area: Rect,
        hint: Option<Hint>,
        visible: bool,
        fullscreen: bool,
        z_index: i32,
        reply: oneshot::Sender<String>,
    },
    Update {
        win_key: String,
        update: WindowUpdate,
    },
    Remove(String),
}

pub trait CommandStr {
    fn get_command_str(&self) -> String;
}

impl CommandStr for Command {
    fn get_command_str(&self) -> String {
        use self::Command::*;

        match self {
            Application(a) => format!("Window::{}", a.get_command_str()),
            Dump(_) => "Dump".into(),
            FbDumpToDir(_) => "FrameBufferDumpToDir".into(),
            GlobalRefresh => "GlobalRefresh".into(),
            Property(p) => format!("Property::{}", p.get_command_str()),
            SetMode(_, _, _) => "SetMode".into(),
            Window(w) => format!("Window::{}", w.get_command_str()),
            OffScreen(_, _) => "OffScreen".into(),
        }
    }
}

impl CommandStr for Application {
    fn get_command_str(&self) -> String {
        match self {
            Self::Add(p, _) => format!("Add({p})"),
            Self::Remove(k) => format!("Remove({k})"),
        }
    }
}

impl CommandStr for Property {
    fn get_command_str(&self) -> String {
        use self::Property::*;
        match self {
            DefaultHint(_) => "DefaultHint::Get".into(),
            SetDefaultHint(_) => "DefaultHint::Set".into(),
            DriverMode(_) => "DriverMode::Get".into(),
            SetDriverMode(_) => "DriverMode::Set".into(),
            DitherMode(_) => "DitherMode::Get".into(),
            SetDitherMode(_) => "DitherMode::Set".into(),
            RedrawDelay(_) => "RedrawDelay::Get".into(),
            SetRedrawDelay(_) => "RedrawDelay::Set".into(),
            OffScreenDisable(_) => "OffScreenDisable::Get".into(),
            SetOffScreenDisable(_) => "OffScreenDisable::Set".into(),
            OffScreenOverride(_) => "OffScreenOverride".into(),
        }
    }
}

impl CommandStr for Window {
    fn get_command_str(&self) -> String {
        match self {
            Self::Add { app_key, .. } => format!("Add({app_key})"),
            Self::Update { win_key, .. } => format!("Update({win_key})"),
            Self::Remove(k) => format!("Remove({k})"),
        }
    }
}

impl From<Application> for Command {
    fn from(value: Application) -> Self {
        Self::Application(value)
    }
}

impl From<Property> for Command {
    fn from(value: Property) -> Self {
        Self::Property(value)
    }
}

impl From<Window> for Command {
    fn from(value: Window) -> Self {
        Self::Window(value)
    }
}

pub struct CommandSender(mpsc::Sender<Command>);

impl CommandSender {
    async fn do_send(&self, cmd: Command, ctx: &String) -> anyhow::Result<()> {
        self.0
            .send(cmd)
            .await
            .with_context(|| format!("Failed to send {ctx}"))
    }

    pub async fn send(&self, cmd: impl Into<Command>) -> anyhow::Result<()> {
        let cmd = cmd.into();
        let ctx_str = cmd.get_command_str();

        self.do_send(cmd, &ctx_str).await
    }

    pub async fn with_reply<T>(
        &self,
        cmd: impl Into<Command>,
        reply: oneshot::Receiver<T>,
    ) -> anyhow::Result<T> {
        let cmd = cmd.into();
        let ctx_str = cmd.get_command_str();

        self.do_send(cmd, &ctx_str).await?;
        reply
            .await
            .with_context(|| format!("Failed to get reply from {ctx_str}"))
    }
}

impl From<mpsc::Sender<Command>> for CommandSender {
    fn from(value: mpsc::Sender<Command>) -> Self {
        Self(value)
    }
}