mutsumi 0.3.0

wl-proxy based GTK MPV embedder
use std::{cell::RefCell, rc::Rc};

use wl_proxy::{
    object::ObjectCoreApi,
    protocols::{
        fractional_scale_v1::{
            wp_fractional_scale_manager_v1::{
                WpFractionalScaleManagerV1, WpFractionalScaleManagerV1Handler,
            },
            wp_fractional_scale_v1::{WpFractionalScaleV1, WpFractionalScaleV1Handler},
        },
        viewporter::{
            wp_viewport::{WpViewport, WpViewportHandler},
            wp_viewporter::{WpViewporter, WpViewporterHandler},
        },
        wayland::{
            wl_buffer::WlBuffer,
            wl_callback::WlCallback,
            wl_compositor::{WlCompositor, WlCompositorHandler},
            wl_subcompositor::{WlSubcompositor, WlSubcompositorHandler},
            wl_subsurface::{WlSubsurface, WlSubsurfaceHandler},
            wl_surface::{WlSurface, WlSurfaceHandler},
        },
    },
};

use super::{
    CURRENT_SCALE, FRAME_CHANNEL, FrameCallbacks, SharedState, SurfaceContentUpdate, SurfaceUpdate,
};

pub struct CompositorHandler {
    pub state: Rc<RefCell<SharedState>>,
}

impl WlCompositorHandler for CompositorHandler {
    fn handle_create_surface(&mut self, _slf: &Rc<WlCompositor>, id: &Rc<WlSurface>) {
        id.set_forward_to_server(false);
        id.set_handler(SurfaceHandler {
            shared: Rc::clone(&self.state),
            pending_buffer: None,
            pending_callbacks: Vec::new(),
        });

        let mut state = self.state.borrow_mut();
        if id.version() >= 6 {
            let scale = *CURRENT_SCALE.lock().unwrap();
            id.send_preferred_buffer_scale(scale.ceil() as i32);
        }
        state.surfaces.push(Rc::clone(id));
    }
}

pub struct SubcompositorHandler;

impl WlSubcompositorHandler for SubcompositorHandler {
    fn handle_get_subsurface(
        &mut self,
        _slf: &Rc<WlSubcompositor>,
        id: &Rc<WlSubsurface>,
        _surface: &Rc<WlSurface>,
        _parent: &Rc<WlSurface>,
    ) {
        id.set_forward_to_server(false);
        id.set_handler(SubsurfaceHandler);
    }
}

struct SubsurfaceHandler;

impl WlSubsurfaceHandler for SubsurfaceHandler {
    fn handle_destroy(&mut self, slf: &Rc<WlSubsurface>) {
        slf.delete_id();
    }
}

pub struct ViewporterHandler;

impl WpViewporterHandler for ViewporterHandler {
    fn handle_destroy(&mut self, slf: &Rc<WpViewporter>) {
        slf.delete_id();
    }

    fn handle_get_viewport(
        &mut self,
        _slf: &Rc<WpViewporter>,
        id: &Rc<WpViewport>,
        _surface: &Rc<WlSurface>,
    ) {
        id.set_forward_to_server(false);
        id.set_handler(ViewportHandler);
    }
}

struct ViewportHandler;

impl WpViewportHandler for ViewportHandler {
    fn handle_destroy(&mut self, slf: &Rc<WpViewport>) {
        slf.delete_id();
    }
}

pub struct FractionalScaleManagerHandler {
    pub state: Rc<RefCell<SharedState>>,
}

impl WpFractionalScaleManagerV1Handler for FractionalScaleManagerHandler {
    fn handle_destroy(&mut self, slf: &Rc<WpFractionalScaleManagerV1>) {
        slf.delete_id();
    }

    fn handle_get_fractional_scale(
        &mut self,
        _slf: &Rc<WpFractionalScaleManagerV1>,
        id: &Rc<WpFractionalScaleV1>,
        _surface: &Rc<WlSurface>,
    ) {
        id.set_forward_to_server(false);
        let scale_120 = (*CURRENT_SCALE.lock().unwrap() * 120.0).round() as u32;
        id.send_preferred_scale(scale_120);
        id.set_handler(FractionalScaleHandler {
            state: Rc::clone(&self.state),
        });
        self.state
            .borrow_mut()
            .fractional_scales
            .push(Rc::clone(id));
    }
}

struct FractionalScaleHandler {
    state: Rc<RefCell<SharedState>>,
}

impl WpFractionalScaleV1Handler for FractionalScaleHandler {
    fn handle_destroy(&mut self, slf: &Rc<WpFractionalScaleV1>) {
        self.state
            .borrow_mut()
            .fractional_scales
            .retain(|s| !Rc::ptr_eq(s, slf));
        slf.delete_id();
    }
}

struct SurfaceHandler {
    shared: Rc<RefCell<SharedState>>,
    pending_buffer: Option<Option<Rc<WlBuffer>>>,
    pending_callbacks: Vec<Rc<WlCallback>>,
}

impl WlSurfaceHandler for SurfaceHandler {
    fn handle_destroy(&mut self, slf: &Rc<WlSurface>) {
        let surface_id = slf.unique_id();
        self.shared
            .borrow_mut()
            .surfaces
            .retain(|surface| surface.unique_id() != surface_id);
        slf.delete_id();
    }

    fn handle_attach(
        &mut self,
        _slf: &Rc<WlSurface>,
        buffer: Option<&Rc<WlBuffer>>,
        _x: i32,
        _y: i32,
    ) {
        self.pending_buffer = Some(buffer.map(Rc::clone));
    }

    fn handle_frame(&mut self, _slf: &Rc<WlSurface>, callback: &Rc<WlCallback>) {
        self.pending_callbacks.push(Rc::clone(callback));
    }

    fn handle_commit(&mut self, _slf: &Rc<WlSurface>) {
        let mut state = self.shared.borrow_mut();
        let callbacks = std::mem::take(&mut self.pending_callbacks);
        let frame_callbacks = if callbacks.is_empty() {
            None
        } else {
            let callback_batch_id = state.next_callback_batch_id;
            state.next_callback_batch_id = callback_batch_id.wrapping_add(1);
            state.frame_callbacks.insert(callback_batch_id, callbacks);

            Some(FrameCallbacks {
                callback_batch_id,
                event_tx: state.event_tx.clone(),
            })
        };

        let content = match self.pending_buffer.take() {
            None => SurfaceContentUpdate::Unchanged,
            Some(None) => SurfaceContentUpdate::Clear,
            Some(Some(buffer)) => {
                if let Some(info) = state.buffer_info.get(&buffer.unique_id()) {
                    match info.to_frame(buffer.unique_id(), state.event_tx.clone()) {
                        Some(frame) => SurfaceContentUpdate::Frame(frame),
                        None => {
                            tracing::warn!(
                                buffer_id = buffer.unique_id(),
                                "wl-proxy-mpv: attached buffer was not created through a supported dmabuf protocol"
                            );
                            buffer.send_release();
                            SurfaceContentUpdate::Clear
                        }
                    }
                } else {
                    buffer.send_release();
                    SurfaceContentUpdate::Clear
                }
            }
        };
        drop(state);

        if !matches!(content, SurfaceContentUpdate::Unchanged) || frame_callbacks.is_some() {
            let _ = FRAME_CHANNEL.tx.send(SurfaceUpdate {
                content,
                frame_callbacks,
            });
        }
    }
}