1
0
Fork 0
forked from wry/wry
wry/src/renderer.rs

867 lines
34 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 scalef = self.base.scalef as f32;
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,
tab_cr.scaled_by(scalef),
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,
tab_cr.scaled_by(scalef),
border_width as f32 * scalef,
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 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 srgb_srgb = self.state.color_manager.srgb_gamma22();
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.focused_title_background.get();
let c = if child.active.get() {
&focused_border_color
} else {
&border_color
};
let full_w = mb.width();
let srgb = &srgb_srgb.linear;
let perceptual = RenderIntent::Perceptual;
if !child.node.node_is_container() {
let cr = self.state.theme.corner_radius.get();
let frame_y = mb.y1();
let frame_h = mb.height();
if cr.is_zero() {
let frame_rects = [
Rect::new_sized_saturating(mb.x1() - bw, frame_y, bw, frame_h),
Rect::new_sized_saturating(mb.x2(), frame_y, bw, frame_h),
Rect::new_sized_saturating(mb.x1() - bw, frame_y - bw, full_w + 2 * bw, bw),
Rect::new_sized_saturating(mb.x1() - bw, frame_y + frame_h, full_w + 2 * bw, bw),
];
self.base.fill_boxes2(&frame_rects, c, srgb, perceptual, x, y);
} else {
let outer = Rect::new_sized_saturating(
mb.x1() - bw,
frame_y - bw,
full_w + 2 * bw,
frame_h + 2 * bw,
);
let scalef = self.base.scalef as f32;
let scaled_cr = cr.scaled_by(scalef);
self.base.fill_rounded_rect(
outer.move_(x, y),
c,
None,
srgb,
perceptual,
scaled_cr,
bw as f32 * scalef,
);
}
}
}
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 scalef = self.base.scalef as f32;
let bw = self.state.theme.sizes.border_width.get();
let inner_cr = cr.expanded_by(-(bw as f32)).scaled_by(scalef);
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 (srgb_srgb, bw, border_color, focused_border_color) = if gap != 0 {
let srgb_srgb = self.state.color_manager.srgb_gamma22();
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.focused_title_background.get();
(
Some(srgb_srgb),
bw,
border_color,
focused_border_color,
)
} else {
(None, 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 let Some(srgb_srgb) = srgb_srgb {
let srgb = &srgb_srgb.linear;
let c = if child.border_color_is_focused.get() {
&focused_border_color
} else {
&border_color
};
if !child.node.node_is_container() && gap != 0 {
let full_w = body.width();
let perceptual = RenderIntent::Perceptual;
let full_h = body.height();
if cr.is_zero() {
let frame_rects = [
Rect::new_sized_saturating(body.x1() - bw, body.y1(), bw, full_h),
Rect::new_sized_saturating(body.x2(), body.y1(), bw, full_h),
Rect::new_sized_saturating(body.x1() - bw, body.y1() - bw, full_w + 2 * bw, bw),
Rect::new_sized_saturating(body.x1() - bw, body.y2(), full_w + 2 * bw, bw),
];
self.base.fill_boxes2(&frame_rects, c, srgb, perceptual, x, y);
} else {
let outer = Rect::new_sized_saturating(
body.x1() - bw,
body.y1() - bw,
full_w + 2 * bw,
full_h + 2 * bw,
);
let scalef = self.base.scalef as f32;
let scaled_cr = cr.scaled_by(scalef);
self.base.fill_rounded_rect(
outer.move_(x, y),
c,
None,
srgb,
perceptual,
scaled_cr,
bw as f32 * scalef,
);
}
}
}
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 scalef = self.base.scalef as f32;
let inner_cr = cr.expanded_by(-(bw as f32)).scaled_by(scalef);
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.focused_title_background.get()
} else {
theme.colors.border.get()
};
let srgb_srgb = self.state.color_manager.srgb_gamma22();
let srgb = &srgb_srgb.linear;
let perceptual = RenderIntent::Perceptual;
let cr = theme.corner_radius.get();
if cr.is_zero() {
let borders = [
Rect::new_sized_saturating(x, y, pos.width(), bw),
Rect::new_sized_saturating(x, y + bw, bw, pos.height() - bw),
Rect::new_sized_saturating(
x + pos.width() - bw,
y + bw,
bw,
pos.height() - bw,
),
Rect::new_sized_saturating(
x + bw,
y + pos.height() - bw,
pos.width() - 2 * bw,
bw,
),
];
self.base.fill_boxes(&borders, &bc, srgb, perceptual);
} else {
let outer = Rect::new_sized_saturating(x, y, pos.width(), pos.height());
let scalef = self.base.scalef as f32;
let scaled_cr = cr.scaled_by(scalef);
self.base.fill_rounded_rect(
outer,
&bc,
None,
srgb,
perceptual,
scaled_cr,
bw as f32 * scalef,
);
}
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 scalef = self.base.scalef as f32;
let inner_cr = cr.expanded_by(-(bw as f32)).scaled_by(scalef);
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))
}
}