smithay-client-toolkit 0.21.0

Toolkit for making client wayland applications.
use std::{mem, sync::Mutex};
use wayland_client::{
    globals::GlobalList,
    protocol::{wl_output, wl_surface},
    Connection, Dispatch, QueueHandle, WEnum,
};
use wayland_protocols::wp::presentation_time::client::{wp_presentation, wp_presentation_feedback};

use crate::{dispatch2::Dispatch2, error::GlobalError, globals::GlobalData, registry::GlobalProxy};

#[derive(Debug)]
pub struct PresentTime {
    pub clk_id: u32,
    pub tv_sec: u64,
    pub tv_nsec: u32,
}

#[derive(Debug)]
pub struct PresentationTimeState {
    presentation: GlobalProxy<wp_presentation::WpPresentation>,
    clk_id: Option<u32>,
}

impl PresentationTimeState {
    /// Bind `wp_presentation` global, if it exists
    pub fn bind<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Self
    where
        D: Dispatch<wp_presentation::WpPresentation, GlobalData> + 'static,
    {
        let presentation = GlobalProxy::from(globals.bind(qh, 1..=1, GlobalData));
        Self { presentation, clk_id: None }
    }

    /// Request feedback for current submission to surface.
    pub fn feedback<D>(
        &self,
        surface: &wl_surface::WlSurface,
        qh: &QueueHandle<D>,
    ) -> Result<wp_presentation_feedback::WpPresentationFeedback, GlobalError>
    where
        D: Dispatch<wp_presentation_feedback::WpPresentationFeedback, PresentationTimeData>
            + 'static,
    {
        let udata = PresentationTimeData {
            wl_surface: surface.clone(),
            sync_outputs: Mutex::new(Vec::new()),
        };
        Ok(self.presentation.get()?.feedback(surface, qh, udata))
    }
}

pub trait PresentationTimeHandler: Sized {
    fn presentation_time_state(&mut self) -> &mut PresentationTimeState;

    /// Content update displayed to user at indicated time
    #[allow(clippy::too_many_arguments)]
    fn presented(
        &mut self,
        conn: &Connection,
        qh: &QueueHandle<Self>,
        feedback: &wp_presentation_feedback::WpPresentationFeedback,
        surface: &wl_surface::WlSurface,
        outputs: Vec<wl_output::WlOutput>,
        time: PresentTime,
        refresh: u32,
        seq: u64,
        flags: WEnum<wp_presentation_feedback::Kind>,
    );

    /// Content update not displayed
    fn discarded(
        &mut self,
        conn: &Connection,
        qh: &QueueHandle<Self>,
        feedback: &wp_presentation_feedback::WpPresentationFeedback,
        surface: &wl_surface::WlSurface,
    );
}

#[doc(hidden)]
#[derive(Debug)]
pub struct PresentationTimeData {
    wl_surface: wl_surface::WlSurface,
    sync_outputs: Mutex<Vec<wl_output::WlOutput>>,
}

impl<D> Dispatch2<wp_presentation::WpPresentation, D> for GlobalData
where
    D: PresentationTimeHandler,
{
    fn event(
        &self,
        data: &mut D,
        _presentation: &wp_presentation::WpPresentation,
        event: wp_presentation::Event,
        _conn: &Connection,
        _qh: &QueueHandle<D>,
    ) {
        match event {
            wp_presentation::Event::ClockId { clk_id } => {
                data.presentation_time_state().clk_id = Some(clk_id);
            }
            _ => unreachable!(),
        }
    }
}

impl<D> Dispatch2<wp_presentation_feedback::WpPresentationFeedback, D> for PresentationTimeData
where
    D: PresentationTimeHandler,
{
    fn event(
        &self,
        data: &mut D,
        feedback: &wp_presentation_feedback::WpPresentationFeedback,
        event: wp_presentation_feedback::Event,
        conn: &Connection,
        qh: &QueueHandle<D>,
    ) {
        match event {
            wp_presentation_feedback::Event::SyncOutput { output } => {
                self.sync_outputs.lock().unwrap().push(output);
            }
            wp_presentation_feedback::Event::Presented {
                tv_sec_hi,
                tv_sec_lo,
                tv_nsec,
                refresh,
                seq_hi,
                seq_lo,
                flags,
            } => {
                let sync_outputs = mem::take(&mut *self.sync_outputs.lock().unwrap());
                let clk_id = data.presentation_time_state().clk_id.unwrap(); // XXX unwrap
                let time = PresentTime {
                    clk_id,
                    tv_sec: ((tv_sec_hi as u64) << 32) | (tv_sec_lo as u64),
                    tv_nsec,
                };
                let seq = ((seq_hi as u64) << 32) | (seq_lo as u64);
                data.presented(
                    conn,
                    qh,
                    feedback,
                    &self.wl_surface,
                    sync_outputs,
                    time,
                    refresh,
                    seq,
                    flags,
                );
            }
            wp_presentation_feedback::Event::Discarded => {
                data.discarded(conn, qh, feedback, &self.wl_surface)
            }
            _ => {}
        }
    }
}