phonto 0.1.0

GPU-accelerated video wallpaper program for wayland compositors
use std::{path::Path, sync::mpsc};

use anyhow::Context;
use wayland_client::{Connection, EventQueue, QueueHandle};

use crate::{decoder, gl_renderer, wayland};

pub struct Phonto {
    state: wayland::State,
    eq: EventQueue<wayland::State>,
    qh: QueueHandle<wayland::State>,
    renderer: gl_renderer::GlRenderer,
}

impl Phonto {
    pub fn new() -> anyhow::Result<Self> {
        let conn = Connection::connect_to_env().context("connect to Wayland display")?;
        let mut eq = conn.new_event_queue();
        let qh = eq.handle();

        let mut state = wayland::State::new(conn);
        state.conn.display().get_registry(&qh, ());
        eq.roundtrip(&mut state)
            .context("initial Wayland roundtrip")?;

        state.create_background_surface(&qh)?;
        eq.roundtrip(&mut state).context("create layer surface")?;
        state.wait_until_configured(&mut eq)?;

        let (width, height) = state.size();
        let renderer = gl_renderer::GlRenderer::new(&state.conn, state.surface()?, width, height)?;

        Ok(Self {
            state,
            eq,
            qh,
            renderer,
        })
    }

    pub fn play(&mut self, wallpaper_path: String) -> anyhow::Result<()> {
        let (tx, rx) = mpsc::sync_channel(1);

        let (gl_display, gl_context) =
            decoder::wrap_gl(self.renderer.egl_display(), self.renderer.egl_context())?;

        self.renderer.make_current()?;

        let decoder_gl_context = gl_context.clone();
        std::thread::Builder::new()
            .name("decoder".into())
            .spawn(move || {
                if let Err(e) = decoder::run(
                    Path::new(&wallpaper_path),
                    gl_display,
                    decoder_gl_context,
                    tx,
                ) {
                    log::error!("decoder error: {e:#}");
                }
            })?;

        loop {
            self.state.wait_for_frame_callback(&mut self.eq)?;

            let sample = rx.recv().context("receive decoded sample")?;
            let frame = decoder::sample_to_frame(sample, &gl_context)?;

            self.state.request_frame_callback(&self.qh);
            self.renderer.render(&frame)?;

            self.eq
                .dispatch_pending(&mut self.state)
                .context("dispatch pending Wayland events")?;

            self.state
                .conn
                .flush()
                .context("flush Wayland connection")?;
        }
    }
}