use {
    crate::{
        cmm::cmm_render_intent::RenderIntent,
        gfx_api::{AcquireSync, AlphaMode, GfxApiOpt, ReleaseSync, SampleRect},
        ifs::wl_surface::{
            SurfaceBuffer, WlSurface,
            x_surface::xwindow::Xwindow,
            xdg_surface::{XdgSurface, xdg_toplevel::XdgToplevel},
            zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
        },
        rect::Rect,
        renderer::renderer_base::RendererBase,
        scale::Scale,
        state::State,
        theme::{Color, CornerRadius},
        tree::{
            ContainerNode, DisplayNode, FloatNode, OutputNode, PlaceholderNode, ToplevelData,
            ToplevelNodeBase, WorkspaceNode, tab_bar::TabBar,
        },
    },
    std::{ops::Deref, rc::Rc, slice},
};

pub mod renderer_base;

pub struct Renderer<'a> {
    pub base: RendererBase<'a>,
    pub state: &'a State,
    pub logical_extents: Rect,
    pub pixel_extents: Rect,
    pub stretch: Option<(i32, i32)>,
    pub corner_radius: Option<CornerRadius>,
}

impl Renderer<'_> {
    pub fn scale(&self) -> Scale {
        self.base.scale
    }

    pub fn pixel_extents(&self) -> Rect {
        self.pixel_extents
    }

    pub fn logical_extents(&self) -> Rect {
        self.logical_extents
    }

    pub fn render_display(&mut self, display: &DisplayNode, x: i32, y: i32) {
        let ext = display.extents.get();
        let outputs = display.outputs.lock();
        for output in outputs.values() {
            let opos = output.global.pos.get();
            let (ox, oy) = ext.translate(opos.x1(), opos.y1());
            self.render_output(output, x + ox, y + oy);
        }
    }

    pub fn render_output(&mut self, output: &OutputNode, x: i32, y: i32) {
        if self.state.lock.locked.get() {
            if let Some(surface) = output.lock_surface.get()
                && surface.surface.buffer.is_some()
            {
                self.render_surface(&surface.surface, x, y, None);
            }
            return;
        }
        let opos = output.global.pos.get();
        macro_rules! render_layer {
            ($layer:expr) => {
                for ls in $layer.iter() {
                    let pos = ls.output_extents();
                    self.render_layer_surface(ls.deref(), x + pos.x1(), y + pos.y1());
                    self.base.ops.push(GfxApiOpt::Sync);
                }
            };
        }
        let mut fullscreen = None;
        if let Some(ws) = output.workspace.get() {
            fullscreen = ws.fullscreen.get();
        }
        let theme = &self.state.theme;
        let srgb_srgb = self.state.color_manager.srgb_gamma22();
        let srgb = &srgb_srgb.linear;
        let perceptual = RenderIntent::Perceptual;
        if let Some(fs) = &fullscreen {
            fs.node_render(self, x, y, None);
        } else {
            render_layer!(output.layers[0]);
            render_layer!(output.layers[1]);
            let ws = output.workspace.get();
            if self.state.show_bar.get() {
                let non_exclusive_rect_rel = output.non_exclusive_rect_rel.get();
                let (mut x, mut y) = non_exclusive_rect_rel.translate_inv(x, y);
                let bar_rect = output.bar_rect_rel.get();
                let bar_bg = bar_rect.move_(
                    x - non_exclusive_rect_rel.x1(),
                    y - non_exclusive_rect_rel.y1(),
                );
                let bar_bg = self.base.scale_rect(bar_bg);
                let c = theme.colors.bar_background.get();
                self.base
                    .fill_scaled_boxes(slice::from_ref(&bar_bg), &c, None, srgb, perceptual);
                self.base.sync();
                let rd = output.render_data.borrow_mut();
                if let Some(aw) = &rd.active_workspace {
                    let c = match aw.captured {
                        true => theme.colors.captured_focused_title_background.get(),
                        false => theme.colors.focused_title_background.get(),
                    };
                    self.base
                        .fill_boxes2(slice::from_ref(&aw.rect), &c, srgb, perceptual, x, y);
                }
                let mut c = theme.colors.separator.get();
                if let Some(ws) = &ws
                    && ws.seat_state.is_active()
                {
                    c = theme.colors.focused_title_background.get();
                }
                self.base.fill_boxes2(
                    slice::from_ref(&rd.bar_separator),
                    &c,
                    srgb,
                    perceptual,
                    x,
                    y,
                );
                let c = theme.colors.unfocused_title_background.get();
                self.base
                    .fill_boxes2(&rd.inactive_workspaces, &c, srgb, perceptual, x, y);
                let c = theme.colors.captured_unfocused_title_background.get();
                self.base
                    .fill_boxes2(&rd.captured_inactive_workspaces, &c, srgb, perceptual, x, y);
                self.base.sync();
                let c = theme.colors.attention_requested_background.get();
                self.base.fill_boxes2(
                    &rd.attention_requested_workspaces,
                    &c,
                    srgb,
                    perceptual,
                    x,
                    y,
                );
                let scale = output.global.persistent.scale.get();
                for title in &rd.titles {
                    let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y);
                    self.base.render_texture(
                        &title.tex,
                        None,
                        x,
                        y,
                        None,
                        None,
                        scale,
                        Some(&bar_bg),
                        None,
                        AcquireSync::None,
                        ReleaseSync::None,
                        false,
                        self.state.color_manager.srgb_gamma22(),
                        perceptual,
                        AlphaMode::PremultipliedElectrical,
                    );
                }
                x += bar_rect.x1() - non_exclusive_rect_rel.x1();
                y += bar_rect.y1() - non_exclusive_rect_rel.y1();
                if let Some(status) = &rd.status
                    && let Some(texture) = status.tex.texture()
                {
                    let (x, y) = self.base.scale_point(x + status.tex_x, y);
                    self.base.render_texture(
                        &texture,
                        None,
                        x,
                        y,
                        None,
                        None,
                        scale,
                        Some(&bar_bg),
                        None,
                        AcquireSync::None,
                        ReleaseSync::None,
                        false,
                        srgb_srgb,
                        perceptual,
                        AlphaMode::PremultipliedElectrical,
                    );
                }
                for item in output.tray_items.iter() {
                    let data = item.data();
                    if data.surface.buffer.is_some() {
                        let rect = data.rel_pos.get().move_(x, y);
                        let bounds = self.base.scale_rect(rect);
                        self.render_surface(&data.surface, rect.x1(), rect.y1(), Some(&bounds));
                    }
                }
            }
            if let Some(ws) = &ws {
                let ws_rect = output.workspace_rect_rel.get();
                let (x, y) = ws_rect.translate_inv(x, y);
                self.render_workspace(&ws, x, y);
            }
        }
        macro_rules! render_stacked {
            ($stack:expr) => {
                for stacked in $stack.iter() {
                    if stacked.node_visible() {
                        self.base.sync();
                        let pos = stacked.node_absolute_position();
                        if pos.intersects(&opos) {
                            let (x, y) = opos.translate(pos.x1(), pos.y1());
                            stacked.node_render(self, x, y, None);
                        }
                    }
                }
            };
        }
        render_stacked!(self.state.root.stacked);
        // Flush RoundedFillRect ops from container/float borders so they don't
        // sort after (and render on top of) layer-shell CopyTexture ops.
        self.base.sync();
        if fullscreen.is_none() {
            render_layer!(output.layers[2]);
        }
        render_layer!(output.layers[3]);
        render_stacked!(self.state.root.stacked_above_layers);
        if let Some(ws) = output.workspace.get()
            && ws.render_highlight.get() > 0
        {
            let color = self.state.theme.colors.highlight.get();
            let bounds = output.workspace_rect_rel.get().move_(x, y);
            self.base.sync();
            self.base.fill_boxes(&[bounds], &color, srgb, perceptual);
        }
    }

    pub fn render_workspace(&mut self, workspace: &WorkspaceNode, x: i32, y: i32) {
        if let Some(node) = workspace.container.get() {
            self.render_container(&node, x, y)
        }
    }

    pub fn render_placeholder(
        &mut self,
        placeholder: &PlaceholderNode,
        x: i32,
        y: i32,
        bounds: Option<&Rect>,
    ) {
        let pos = placeholder.tl_data().pos.get();
        self.base.fill_boxes(
            std::slice::from_ref(&pos.at_point(x, y)),
            &Color::from_srgba_straight(20, 20, 20, 255),
            &self.state.color_manager.srgb_gamma22().linear,
            RenderIntent::Perceptual,
        );
        if let Some(tex) = placeholder.textures.borrow().get(&self.base.scale)
            && let Some(texture) = tex.texture()
        {
            let (tex_width, tex_height) = texture.size();
            let x = x + (pos.width() - tex_width) / 2;
            let y = y + (pos.height() - tex_height) / 2;
            self.base.render_texture(
                &texture,
                None,
                x,
                y,
                None,
                None,
                self.base.scale,
                bounds,
                None,
                AcquireSync::None,
                ReleaseSync::None,
                false,
                self.state.color_manager.srgb_gamma22(),
                RenderIntent::Perceptual,
                AlphaMode::PremultipliedElectrical,
            );
        }
        self.render_tl_aux(placeholder.tl_data(), bounds, true);
    }

    fn render_tab_bar(&mut self, tab_bar: &TabBar, x: i32, y: i32, _container_width: i32) {
        let srgb_srgb = self.state.color_manager.srgb_gamma22();
        let srgb = &srgb_srgb.linear;
        let perceptual = RenderIntent::Perceptual;
        let radius = self.state.theme.sizes.tab_bar_radius.get();
        let border_width = self.state.theme.sizes.tab_bar_border_width.get();
        let text_padding = self.state.theme.sizes.tab_bar_text_padding.get();
        let bar_height = tab_bar.height;
        let render_scale = tab_bar.render_scale;

        // Vulkan sorts ops: Fill < RoundedFill (by z_order, color) < Tex/RoundedTex (by index).
        // We use:
        //   FillRect           - tiny strip for Vulkan paint regions (hidden)
        //   RoundedFillRect z0 - solid rounded bg
        //   RoundedFillRect z1 - rounded border ring (on top of bg)
        //   RoundedCopyTexture - title text (on top of everything)
        for entry in &tab_bar.entries {
            let (bg_color, border_color, _text_color) = TabBar::entry_colors(self.state, entry);
            let ex = entry.x.get();
            let ew = entry.width.get();
            let tab_rect = Rect::new_sized_saturating(ex, 0, ew, bar_height);
            let tab_cr = CornerRadius::from(radius as f32);

            // Tiny FillRect strip to establish Vulkan paint regions (visually hidden
            // behind the RoundedFillRect bg that renders later).
            let strip = Rect::new_sized_saturating(
                ex + radius,
                bar_height / 2,
                (ew - 2 * radius).max(1),
                1,
            );
            self.base
                .fill_boxes2(slice::from_ref(&strip), &bg_color, srgb, perceptual, x, y);

            // Rounded solid bg fill (z_order=0, renders first among RoundedFill).
            self.base.fill_rounded_rect_z(
                tab_rect.move_(x, y),
                &bg_color,
                None,
                srgb,
                perceptual,
                self.scale_corner_radius(tab_cr),
                0.0,
                0,
            );

            // Rounded border ring on top (z_order=1, renders after bg).
            if border_width > 0 {
                self.base.fill_rounded_rect_z(
                    tab_rect.move_(x, y),
                    &border_color,
                    None,
                    srgb,
                    perceptual,
                    self.scale_corner_radius(tab_cr),
                    self.scale_len(border_width),
                    1,
                );
            }

            // Title text as RoundedCopyTexture (sorts after all RoundedFill).
            let tex_ref = entry.title_texture.borrow();
            if let Some(tex) = tex_ref.as_ref()
                && let Some(texture) = tex.texture()
            {
                use crate::theme::TabTitleAlign;
                let (tw, _th) = texture.size();
                let tex_width = (tw as f64 / render_scale.to_f64()).round() as i32;
                let tab_inner = ew - 2 * (text_padding + border_width);
                let text_x = match self.state.theme.tab_title_align.get() {
                    TabTitleAlign::Start => x + ex + text_padding + border_width,
                    TabTitleAlign::Center => {
                        x + ex
                            + border_width
                            + (tab_inner.max(0) - tex_width).max(0) / 2
                            + text_padding.min(tab_inner.max(0) / 2)
                    }
                    TabTitleAlign::End => {
                        let end_x = x + ex + ew - tex_width - text_padding - border_width;
                        end_x.max(x + ex + border_width)
                    }
                };
                let (tx, ty) = self.base.scale_point(text_x, y);
                self.base.render_rounded_texture(
                    &texture,
                    None,
                    tx,
                    ty,
                    None,
                    None,
                    render_scale,
                    None,
                    None,
                    AcquireSync::None,
                    ReleaseSync::None,
                    self.state.color_manager.srgb_gamma22(),
                    perceptual,
                    AlphaMode::PremultipliedElectrical,
                    CornerRadius::from(0.0_f32),
                );
            }
        }
    }

    fn scale_len(&self, len: i32) -> f32 {
        if self.base.scaled {
            (len as f64 * self.base.scalef).round() as f32
        } else {
            len as f32
        }
    }

    fn scale_corner_radius(&self, cr: CornerRadius) -> CornerRadius {
        if !self.base.scaled {
            return cr;
        }
        let scale = self.base.scalef as f32;
        CornerRadius {
            top_left: (cr.top_left * scale).round(),
            top_right: (cr.top_right * scale).round(),
            bottom_right: (cr.bottom_right * scale).round(),
            bottom_left: (cr.bottom_left * scale).round(),
        }
    }

    fn render_rounded_frame(
        &mut self,
        rect: Rect,
        color: &Color,
        corner_radius: CornerRadius,
        border_width: i32,
        x: i32,
        y: i32,
    ) {
        if border_width <= 0 {
            return;
        }
        let srgb_srgb = self.state.color_manager.srgb_gamma22();
        let srgb = &srgb_srgb.linear;
        let perceptual = RenderIntent::Perceptual;
        if corner_radius.is_zero() {
            let bw = border_width;
            let frame_rects = [
                Rect::new_sized_saturating(rect.x1(), rect.y1(), bw, rect.height()),
                Rect::new_sized_saturating(rect.x2() - bw, rect.y1(), bw, rect.height()),
                Rect::new_sized_saturating(rect.x1(), rect.y1(), rect.width(), bw),
                Rect::new_sized_saturating(rect.x1(), rect.y2() - bw, rect.width(), bw),
            ];
            self.base
                .fill_boxes2(&frame_rects, color, srgb, perceptual, x, y);
        } else {
            self.base.fill_rounded_rect(
                rect.move_(x, y),
                color,
                None,
                srgb,
                perceptual,
                self.scale_corner_radius(corner_radius),
                self.scale_len(border_width),
            );
        }
    }

    fn render_container_decorations(&mut self, container: &ContainerNode, x: i32, y: i32) {
        let srgb_srgb = self.state.color_manager.srgb_gamma22();
        let srgb = &srgb_srgb.linear;
        let perceptual = RenderIntent::Perceptual;
        let rd = container.render_data.borrow_mut();
        let c = self.state.theme.colors.border.get();
        self.base
            .fill_boxes2(&rd.border_rects, &c, srgb, perceptual, x, y);
    }

    pub fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32) {
        self.render_container_decorations(container, x, y);

        if let Some(child) = container.mono_child.get() {
            // Render tab bar if present.
            {
                let tab_bar = container.tab_bar.borrow();
                if let Some(tb) = tab_bar.as_ref() {
                    self.render_tab_bar(tb, x, y, container.width.get());
                }
            }
            let mb = container.mono_body.get();
            if self.state.theme.sizes.gap.get() != 0 {
                let bw = self.state.theme.sizes.border_width.get();
                let border_color = self.state.theme.colors.border.get();
                let focused_border_color = self.state.theme.colors.active_border.get();
                let c = if child.active.get() {
                    &focused_border_color
                } else {
                    &border_color
                };
                if !child.node.node_is_container() {
                    let frame = Rect::new_sized_saturating(
                        mb.x1() - bw,
                        mb.y1() - bw,
                        mb.width() + 2 * bw,
                        mb.height() + 2 * bw,
                    );
                    self.render_rounded_frame(
                        frame,
                        c,
                        self.state.theme.corner_radius.get(),
                        bw,
                        x,
                        y,
                    );
                }
            }
            let body = mb.move_(x, y);
            let body = self.base.scale_rect(body);
            let content = container.mono_content.get();
            self.stretch = if content.width() != mb.width() || content.height() != mb.height() {
                Some(self.base.scale_point(mb.width(), mb.height()))
            } else {
                None
            };
            if self.state.theme.sizes.gap.get() != 0 && !child.node.node_is_container() {
                let cr = self.state.theme.corner_radius.get();
                if !cr.is_zero() {
                    let bw = self.state.theme.sizes.border_width.get();
                    let inner_cr = self.scale_corner_radius(cr.expanded_by(-(bw as f32)));
                    self.corner_radius = Some(inner_cr);
                }
            }
            child
                .node
                .node_render(self, x + content.x1(), y + content.y1(), Some(&body));
            self.stretch = None;
            self.corner_radius = None;
        } else {
            let gap = self.state.theme.sizes.gap.get();
            let (bw, border_color, focused_border_color) = if gap != 0 {
                let bw = self.state.theme.sizes.border_width.get();
                let border_color = self.state.theme.colors.border.get();
                let focused_border_color = self.state.theme.colors.active_border.get();
                (bw, border_color, focused_border_color)
            } else {
                (0, Color::SOLID_BLACK, Color::SOLID_BLACK)
            };
            let cr = self.state.theme.corner_radius.get();
            for child in container.children.iter() {
                let body = child.body.get();
                if body.x1() >= container.width.get() || body.y1() >= container.height.get() {
                    break;
                }
                if gap != 0 {
                    let c = if child.border_color_is_focused.get() {
                        &focused_border_color
                    } else {
                        &border_color
                    };
                    if !child.node.node_is_container() && gap != 0 {
                        let frame = Rect::new_sized_saturating(
                            body.x1() - bw,
                            body.y1() - bw,
                            body.width() + 2 * bw,
                            body.height() + 2 * bw,
                        );
                        self.render_rounded_frame(frame, c, cr, bw, x, y);
                    }
                }
                let content = child.content.get();
                self.stretch =
                    if content.width() != body.width() || content.height() != body.height() {
                        Some(self.base.scale_point(body.width(), body.height()))
                    } else {
                        None
                    };
                if !cr.is_zero() && !child.node.node_is_container() && gap != 0 {
                    let inner_cr = self.scale_corner_radius(cr.expanded_by(-(bw as f32)));
                    self.corner_radius = Some(inner_cr);
                }
                let body = body.move_(x, y);
                let body = self.base.scale_rect(body);
                child
                    .node
                    .node_render(self, x + content.x1(), y + content.y1(), Some(&body));
                self.stretch = None;
                self.corner_radius = None;
            }
        }

        self.render_tl_aux(container.tl_data(), None, false);
    }

    pub fn render_xwindow(&mut self, tl: &Xwindow, x: i32, y: i32, bounds: Option<&Rect>) {
        self.render_surface(&tl.x.surface, x, y, bounds);
        self.render_tl_aux(tl.tl_data(), bounds, true);
    }

    pub fn render_xdg_toplevel(&mut self, tl: &XdgToplevel, x: i32, y: i32, bounds: Option<&Rect>) {
        self.render_xdg_surface(&tl.xdg, x, y, bounds);
        self.render_tl_aux(tl.tl_data(), bounds, true);
    }

    pub fn render_xdg_surface(
        &mut self,
        xdg: &XdgSurface,
        mut x: i32,
        mut y: i32,
        bounds: Option<&Rect>,
    ) {
        let surface = &xdg.surface;
        let geo = xdg.geometry();
        (x, y) = geo.translate(x, y);
        self.render_surface(surface, x, y, bounds);
    }

    fn render_tl_aux(
        &mut self,
        tl_data: &ToplevelData,
        bounds: Option<&Rect>,
        render_highlight: bool,
    ) {
        if render_highlight {
            self.render_tl_highlight(tl_data, bounds);
        }
    }

    fn render_tl_highlight(&mut self, tl_data: &ToplevelData, bounds: Option<&Rect>) {
        if tl_data.render_highlight.get() == 0 {
            return;
        }
        let Some(bounds) = bounds else {
            return;
        };
        let color = self.state.theme.colors.highlight.get();
        self.base.sync();
        self.base.fill_scaled_boxes(
            slice::from_ref(bounds),
            &color,
            None,
            &self.state.color_manager.srgb_gamma22().linear,
            RenderIntent::Perceptual,
        );
    }

    pub fn render_highlight(&mut self, rect: &Rect) {
        let color = self.state.theme.colors.highlight.get();
        self.base.sync();
        self.base.fill_boxes(
            slice::from_ref(rect),
            &color,
            &self.state.color_manager.srgb_gamma22().linear,
            RenderIntent::Perceptual,
        );
    }

    pub fn render_surface(&mut self, surface: &WlSurface, x: i32, y: i32, bounds: Option<&Rect>) {
        let (x, y) = self.base.scale_point(x, y);
        self.render_surface_scaled(surface, x, y, None, bounds, false);
    }

    pub fn render_surface_scaled(
        &mut self,
        surface: &WlSurface,
        x: i32,
        y: i32,
        pos_rel: Option<(i32, i32)>,
        bounds: Option<&Rect>,
        is_subsurface: bool,
    ) {
        let stretch = self.stretch.take();
        // Take corner_radius early so it is never leaked on early return
        // and so that below-subsurfaces cannot steal it from the main surface.
        let corner_radius = self.corner_radius.take();
        let children = surface.children.borrow();
        let buffer = match surface.buffer.get() {
            Some(b) => b,
            _ => {
                if !surface.is_cursor() && !is_subsurface {
                    log::warn!("surface has no buffer attached");
                }
                return;
            }
        };
        let tpoints = surface.buffer_points_norm.borrow_mut();
        let mut size = surface.buffer_abs_pos.get().size();
        if let Some((x_rel, y_rel)) = pos_rel {
            let (x, y) = self.base.scale_point(x_rel, y_rel);
            let (w, h) = self.base.scale_point(x_rel + size.0, y_rel + size.1);
            size = (w - x, h - y);
        } else {
            size = self.base.scale_point(size.0, size.1);
        }
        let mut tpoints = *tpoints;
        if let Some(s) = stretch {
            if size.0 > 0 && size.1 > 0 {
                let sx = s.0 as f32 / size.0 as f32;
                let sy = s.1 as f32 / size.1 as f32;
                tpoints.x2 *= sx;
                tpoints.y2 *= sy;
            }
            size = s;
        }
        if let Some(children) = children.deref() {
            macro_rules! render {
                ($children:expr) => {
                    for child in $children.iter() {
                        if child.pending.get() {
                            continue;
                        }
                        let pos = child.sub_surface.position.get();
                        let (x1, y1) = self.base.scale_point(pos.0, pos.1);
                        self.render_surface_scaled(
                            &child.sub_surface.surface,
                            x + x1,
                            y + y1,
                            Some(pos),
                            bounds,
                            true,
                        );
                    }
                };
            }
            render!(&children.below);
            // Restore corner_radius only for the main surface's render_buffer call.
            self.corner_radius = corner_radius;
            self.render_buffer(surface, &buffer, x, y, tpoints, size, bounds);
            render!(&children.above);
        } else {
            self.corner_radius = corner_radius;
            self.render_buffer(surface, &buffer, x, y, tpoints, size, bounds);
        }
    }

    pub fn render_buffer(
        &mut self,
        surface: &WlSurface,
        buffer: &Rc<SurfaceBuffer>,
        x: i32,
        y: i32,
        tpoints: SampleRect,
        tsize: (i32, i32),
        bounds: Option<&Rect>,
    ) {
        let buf = &buffer.buffer.buf;
        let alpha = surface.alpha();
        let cd = surface.color_description();
        let intent = surface.render_intent();
        let alpha_mode = surface.alpha_mode();
        let corner_radius = self.corner_radius.take();
        if let Some(tex) = buf.get_texture(surface) {
            if let Some(cr) = corner_radius {
                self.base.render_rounded_texture(
                    &tex,
                    alpha,
                    x,
                    y,
                    Some(tpoints),
                    Some(tsize),
                    self.base.scale,
                    bounds,
                    Some(buffer.clone()),
                    AcquireSync::Unnecessary,
                    buffer.release_sync,
                    &cd,
                    intent,
                    alpha_mode,
                    cr,
                );
            } else {
                let mut opaque = surface.opaque();
                if !opaque && tex.format().has_alpha {
                    opaque = self.bounds_are_opaque(x, y, bounds, surface);
                }
                self.base.render_texture(
                    &tex,
                    alpha,
                    x,
                    y,
                    Some(tpoints),
                    Some(tsize),
                    self.base.scale,
                    bounds,
                    Some(buffer.clone()),
                    AcquireSync::Unnecessary,
                    buffer.release_sync,
                    opaque,
                    &cd,
                    intent,
                    alpha_mode,
                );
            }
        } else if let Some(color) = &buf.color {
            if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
                let rect = match bounds {
                    None => rect,
                    Some(bounds) => rect.intersect(*bounds),
                };
                if !rect.is_empty() {
                    let color = Color::from_u32(
                        cd.eotf, alpha_mode, color[0], color[1], color[2], color[3],
                    );
                    self.base.sync();
                    self.base
                        .fill_scaled_boxes(&[rect], &color, alpha, &cd.linear, intent);
                }
            }
        } else {
            log::info!("live buffer has neither a texture nor is a single-pixel buffer");
        }
    }

    pub fn render_floating(&mut self, floating: &FloatNode, x: i32, y: i32) {
        let child = match floating.child.get() {
            Some(c) => c,
            _ => return,
        };
        let pos = floating.position.get();
        let theme = &self.state.theme;
        let bw = theme.sizes.border_width.get();
        let bc = if floating.active.get() {
            theme.colors.active_border.get()
        } else {
            theme.colors.border.get()
        };
        let cr = theme.corner_radius.get();
        let outer = Rect::new_sized_saturating(0, 0, pos.width(), pos.height());
        self.render_rounded_frame(outer, &bc, cr, bw, x, y);
        let body =
            Rect::new_sized_saturating(x + bw, y + bw, pos.width() - 2 * bw, pos.height() - 2 * bw);
        let scissor_body = self.base.scale_rect(body);
        if !cr.is_zero() {
            let inner_cr = self.scale_corner_radius(cr.expanded_by(-(bw as f32)));
            self.corner_radius = Some(inner_cr);
        }
        child.node_render(self, body.x1(), body.y1(), Some(&scissor_body));
        self.corner_radius = None;
    }

    pub fn render_layer_surface(&mut self, surface: &ZwlrLayerSurfaceV1, x: i32, y: i32) {
        let (dx, dy) = surface.surface.extents.get().position();
        self.render_surface(&surface.surface, x - dx, y - dy, None);
    }

    fn bounds_are_opaque(
        &self,
        x: i32,
        y: i32,
        bounds: Option<&Rect>,
        surface: &WlSurface,
    ) -> bool {
        let Some(bounds) = bounds else {
            return false;
        };
        let Some(region) = surface.opaque_region() else {
            return false;
        };
        let surface_size = surface.buffer_abs_pos.get().at_point(0, 0);
        let surface_size = self.base.scale_rect(surface_size);
        let bounds = bounds.move_(-x, -y).intersect(surface_size);
        region.contains_rect2(&bounds, |r| self.base.scale_rect(*r))
    }
}
