jay-compositor 1.15.0

The Jay compositor
use crate::format::ARGB8888;
use crate::gfx_api::AcquireSync;
use crate::gfx_api::GfxFramebuffer;
use crate::gfx_api::GfxTexture;
use crate::gfx_api::ReleaseSync;
use crate::gfx_api::ScalingFilter;
use crate::ifs::wl_surface::WlSurface;
use crate::rect::Rect;
use crate::renderer::renderer_base::RenderTexture;
use crate::renderer::renderer_base::RendererBase;
use crate::scale::Scale;
use crate::state::GfxCtxChangedListener;
use crate::state::ScalesChangedListener;
use crate::theme::Color;
use crate::tree::Transform;
use crate::utils::errorfmt::ErrorFmt;
use crate::utils::event_listener::EventListener;
use crate::utils::lazy_event_source::LazyEventSourceListener;
use crate::utils::smallmap::SmallMapMut;
use crate::utils::type_view::TypeView;
use crate::utils::type_view::tv_wrap_weak;
use std::cell::Cell;
use std::cell::RefCell;
use std::mem;
use std::rc::Rc;

pub struct SurfaceRenderCache {
    surface: Rc<WlSurface>,
    _gfx_ctx_listener: EventListener<dyn GfxCtxChangedListener>,
    _commit_listener: EventListener<dyn LazyEventSourceListener>,
    _scale_listener: EventListener<dyn ScalesChangedListener>,
    size: Cell<[i32; 2]>,
    scaled: RefCell<SmallMapMut<Scale, Option<Scaled>, 2>>,
}

struct Scaled {
    need_render: bool,
    tex: Rc<dyn GfxTexture>,
    fb: Rc<dyn GfxFramebuffer>,
}

impl SurfaceRenderCache {
    pub fn new(surface: &Rc<WlSurface>) -> Rc<Self> {
        let state = &surface.state;
        Rc::<Self>::new_cyclic(|slf| Self {
            surface: surface.clone(),
            _gfx_ctx_listener: EventListener::attached(slf.clone(), &state.gfx_ctx_changed),
            _scale_listener: EventListener::attached(slf.clone(), &state.scales_changed),
            _commit_listener: EventListener::attached(
                tv_wrap_weak::<_, CommitView>(slf.clone()),
                &surface
                    .tree_committed_listeners
                    .get_or_init(|| state.lazy_event_sources.create_source()),
            ),
            size: Default::default(),
            scaled: Default::default(),
        })
    }

    fn clear_tex(&self) {
        self.scaled.borrow_mut().clear();
    }

    pub fn set_size(&self, width: i32, height: i32) {
        let size = [width, height];
        if self.size.replace(size) == size {
            return;
        }
        self.clear_tex();
    }

    pub fn render(
        &self,
        renderer: &mut RendererBase<'_>,
        x: i32,
        y: i32,
        grayscale: bool,
        bounds: Option<&Rect>,
    ) {
        let state = &self.surface.state;
        let scale = renderer.scale;
        let scales = &mut *self.scaled.borrow_mut();
        let scaled = scales.get_or_insert_with(scale, || {
            let [w, h] = scale.pixel_size(self.size.get());
            if w <= 0 || h <= 0 {
                return None;
            }
            let ctx = state.render_ctx.get()?;
            let img = ctx.create_read_write_img(&state.dma_buf_ids, w, h, ARGB8888);
            let (fb, tex) = match img {
                Ok(v) => v,
                Err(e) => {
                    log::error!("Could not allocate read/write image: {}", ErrorFmt(e));
                    return None;
                }
            };
            let scaled = Scaled {
                need_render: true,
                tex,
                fb,
            };
            Some(scaled)
        });
        let Some(scaled) = scaled else {
            return;
        };
        if mem::take(&mut scaled.need_render) {
            let mut rp = scaled.fb.create_render_pass(
                &*self.surface,
                state,
                None,
                scale,
                ScalingFilter::Linear,
                false,
                false,
                false,
                false,
                Transform::None,
                None,
                false,
            );
            rp.clear = Some(Color::TRANSPARENT);
            let cd = &self.surface.color_description();
            let res = scaled.fb.perform_render_pass(
                AcquireSync::Unnecessary,
                ReleaseSync::None,
                cd,
                &rp,
                &scaled.fb.full_region(),
                None,
                cd,
            );
            if let Err(e) = res {
                log::error!("Could not render surface icon: {}", ErrorFmt(e));
            }
        }
        let (x, y) = renderer.scale_point(x, y);
        renderer.render_texture(
            &scaled.tex,
            x,
            y,
            RenderTexture {
                acquire_sync: AcquireSync::None,
                release_sync: ReleaseSync::None,
                cd: Some(&self.surface.color_description()),
                grayscale,
                bounds,
                ..Default::default()
            },
        );
    }
}

impl GfxCtxChangedListener for SurfaceRenderCache {
    fn handle_gfx_context_change(self: Rc<Self>) {
        self.clear_tex();
    }
}

struct CommitView;
impl LazyEventSourceListener for TypeView<SurfaceRenderCache, CommitView> {
    fn triggered(self: Rc<Self>) {
        for (_, v) in self.scaled.borrow_mut().iter_mut() {
            if let Some(v) = v {
                v.need_render = true;
            }
        }
    }
}

impl ScalesChangedListener for SurfaceRenderCache {
    fn changed(self: Rc<Self>) {
        self.clear_tex();
    }
}