atagen

monolith

public
Star0
Watch0
47b86d08d05a97fea42c27b6ed194c0ceaa30374
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git

Blame: crates/monolith/src/scene.rs

2af1d8c55876atagen2026-10-16 11:05 UTC1
//! What a frame draws. The space holds final geometry for input, while the
2af1d8c55876atagen2026-10-16 11:05 UTC2
//! scene draws windows where the animator says they are right now.
2af1d8c55876atagen2026-10-16 11:05 UTC3
618afeb78754atagen2026-10-17 13:40 UTC4
use crate::wm::{LastFrame, Wm};
7ce6ae4e5a85atagen2026-10-16 12:16 UTC5
use monolith_config::Decoration;
b44dde08d7acatagen2026-10-19 07:38 UTC6
use monolith_layout::{OutputId, Rect, WindowId};
47259c39793fatagen2026-10-17 10:13 UTC7
use monolith_render_vk::{Clip, Effect, VulkanFrame, VulkanRenderer, VulkanTexture};
0c1685f1d412atagen2026-10-16 12:18 UTC8
use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement;
7ce6ae4e5a85atagen2026-10-16 12:16 UTC9
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
2af1d8c55876atagen2026-10-16 11:05 UTC10
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
cce3ef7e0162atagen2026-10-16 22:31 UTC11
use smithay::backend::renderer::element::surface::render_elements_from_surface_tree;
2af1d8c55876atagen2026-10-16 11:05 UTC12
use smithay::backend::renderer::element::texture::TextureRenderElement;
48af562d968batagen2026-10-16 12:39 UTC13
use smithay::backend::renderer::element::utils::{CropRenderElement, RescaleRenderElement};
96db497859dcatagen2026-10-16 11:27 UTC14
use smithay::backend::renderer::element::{AsRenderElements, Element, Id, Kind, RenderElement};
2af1d8c55876atagen2026-10-16 11:05 UTC15
use smithay::backend::renderer::utils::with_renderer_surface_state;
47259c39793fatagen2026-10-17 10:13 UTC16
use smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions};
96db497859dcatagen2026-10-16 11:27 UTC17
use smithay::backend::renderer::{ContextId, Renderer, Texture};
47259c39793fatagen2026-10-17 10:13 UTC18
use smithay::desktop::{PopupManager, Window};
bc51c38a6fd9atagen2026-10-16 22:17 UTC19
use smithay::output::Output;
96db497859dcatagen2026-10-16 11:27 UTC20
use smithay::utils::user_data::UserDataMap;
96db497859dcatagen2026-10-16 11:27 UTC21
use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};
47259c39793fatagen2026-10-17 10:13 UTC22
use smithay::wayland::compositor::{TraversalAction, with_surface_tree_downward};
bc51c38a6fd9atagen2026-10-16 22:17 UTC23
use smithay::wayland::shell::wlr_layer::Layer;
cce3ef7e0162atagen2026-10-16 22:31 UTC24
use smithay::xwayland::X11Surface;
2af1d8c55876atagen2026-10-16 11:05 UTC25
2af1d8c55876atagen2026-10-16 11:05 UTC26
smithay::backend::renderer::element::render_elements! {
2af1d8c55876atagen2026-10-16 11:05 UTC27
    pub Draw<=VulkanRenderer>;
2af1d8c55876atagen2026-10-16 11:05 UTC28
    Surface=WaylandSurfaceRenderElement<VulkanRenderer>,
2af1d8c55876atagen2026-10-16 11:05 UTC29
    Cropped=CropRenderElement<WaylandSurfaceRenderElement<VulkanRenderer>>,
2af1d8c55876atagen2026-10-16 11:05 UTC30
    Texture=TextureRenderElement<VulkanTexture>,
96db497859dcatagen2026-10-16 11:27 UTC31
    Effect=EffectElement,
7ce6ae4e5a85atagen2026-10-16 12:16 UTC32
    Border=SolidColorRenderElement,
0c1685f1d412atagen2026-10-16 12:18 UTC33
    Memory=MemoryRenderBufferRenderElement<VulkanRenderer>,
48af562d968batagen2026-10-16 12:39 UTC34
    Rescaled=RescaleRenderElement<WaylandSurfaceRenderElement<VulkanRenderer>>,
47259c39793fatagen2026-10-17 10:13 UTC35
    Clipped=ClipElement,
2af1d8c55876atagen2026-10-16 11:05 UTC36
}
2af1d8c55876atagen2026-10-16 11:05 UTC37
0c1685f1d412atagen2026-10-16 12:18 UTC38
/// Elements for output `o` in its own coordinates, front to back.
30aece3a63ceatagen2026-10-16 13:48 UTC39
#[profiling::function]
2af1d8c55876atagen2026-10-16 11:05 UTC40
pub fn build(
60956e3999deatagen2026-10-17 02:50 UTC41
    wm: &mut Wm,
0c1685f1d412atagen2026-10-16 12:18 UTC42
    o: OutputId,
2af1d8c55876atagen2026-10-16 11:05 UTC43
    renderer: &mut VulkanRenderer,
2af1d8c55876atagen2026-10-16 11:05 UTC44
    now: u64,
7ce6ae4e5a85atagen2026-10-16 12:16 UTC45
    deco: &Decoration,
bc51c38a6fd9atagen2026-10-16 22:17 UTC46
    output: Option<&Output>,
cce3ef7e0162atagen2026-10-16 22:31 UTC47
    unmanaged: &[X11Surface],
2af1d8c55876atagen2026-10-16 11:05 UTC48
) -> Vec<Draw> {
82c94edb8c50atagen2026-10-17 04:56 UTC49
    let crate::wm::Parts {
82c94edb8c50atagen2026-10-17 04:56 UTC50
        layout,
82c94edb8c50atagen2026-10-17 04:56 UTC51
        anim,
82c94edb8c50atagen2026-10-17 04:56 UTC52
        windows,
82c94edb8c50atagen2026-10-17 04:56 UTC53
        last_frames,
82c94edb8c50atagen2026-10-17 04:56 UTC54
    } = wm.parts();
0c1685f1d412atagen2026-10-16 12:18 UTC55
    let Some(area) = layout.output(o).map(|out| out.area) else {
0c1685f1d412atagen2026-10-16 12:18 UTC56
        return Vec::new();
0c1685f1d412atagen2026-10-16 12:18 UTC57
    };
0c1685f1d412atagen2026-10-16 12:18 UTC58
    let local = |r: Rect| Rect::new(r.x - area.x, r.y - area.y, r.w, r.h);
7373078d1149atagen2026-10-16 11:24 UTC59
    let mut ghosts = Vec::new();
2af1d8c55876atagen2026-10-16 11:05 UTC60
    let mut tiled = Vec::new();
2af1d8c55876atagen2026-10-16 11:05 UTC61
    let mut above = Vec::new();
618afeb78754atagen2026-10-17 13:40 UTC62
    let mut fading = Vec::new();
0c1685f1d412atagen2026-10-16 12:18 UTC63
    for g in anim.ghosts().iter().filter(|g| g.output == o) {
618afeb78754atagen2026-10-17 13:40 UTC64
        let Some(frame) = last_frames.get(&g.id) else {
618afeb78754atagen2026-10-17 13:40 UTC65
            continue;
618afeb78754atagen2026-10-17 13:40 UTC66
        };
618afeb78754atagen2026-10-17 13:40 UTC67
        match g.fixed {
618afeb78754atagen2026-10-17 13:40 UTC68
            Some(r) => fading.push((g, frame, local(r))),
618afeb78754atagen2026-10-17 13:40 UTC69
            None => {
618afeb78754atagen2026-10-17 13:40 UTC70
                if let Some(r) = anim.tiled(o, g.id, now) {
618afeb78754atagen2026-10-17 13:40 UTC71
                    ghosts.push((g, frame, local(r)));
618afeb78754atagen2026-10-17 13:40 UTC72
                }
618afeb78754atagen2026-10-17 13:40 UTC73
            }
7373078d1149atagen2026-10-16 11:24 UTC74
        }
0c1685f1d412atagen2026-10-16 12:18 UTC75
    }
0c1685f1d412atagen2026-10-16 12:18 UTC76
    let arr = layout.arrange(o);
0c1685f1d412atagen2026-10-16 12:18 UTC77
    let fullscreen_at_rest = arr
0c1685f1d412atagen2026-10-16 12:18 UTC78
        .fullscreen
0c1685f1d412atagen2026-10-16 12:18 UTC79
        .is_some_and(|w| anim.gliding(w, now).is_none());
0c1685f1d412atagen2026-10-16 12:18 UTC80
    for p in &arr.tiled {
0c1685f1d412atagen2026-10-16 12:18 UTC81
        if Some(p.window) == arr.fullscreen || fullscreen_at_rest {
0c1685f1d412atagen2026-10-16 12:18 UTC82
            continue;
2af1d8c55876atagen2026-10-16 11:05 UTC83
        }
0c1685f1d412atagen2026-10-16 12:18 UTC84
        match anim.gliding(p.window, now) {
48af562d968batagen2026-10-16 12:39 UTC85
            Some(r) => above.push((p.window, local(r), true, true)),
0c1685f1d412atagen2026-10-16 12:18 UTC86
            None => {
0c1685f1d412atagen2026-10-16 12:18 UTC87
                let r = anim.tiled(o, p.window, now).unwrap_or(p.rect);
48af562d968batagen2026-10-16 12:39 UTC88
                tiled.push((p.window, local(r), true, false));
0c1685f1d412atagen2026-10-16 12:18 UTC89
            }
2af1d8c55876atagen2026-10-16 11:05 UTC90
        }
2af1d8c55876atagen2026-10-16 11:05 UTC91
    }
b44dde08d7acatagen2026-10-19 07:38 UTC92
    let float = |&(w, r): &(WindowId, Rect)| {
48af562d968batagen2026-10-16 12:39 UTC93
        let glide = anim.gliding(w, now);
b44dde08d7acatagen2026-10-19 07:38 UTC94
        (w, local(glide.unwrap_or(r)), true, glide.is_some())
b44dde08d7acatagen2026-10-19 07:38 UTC95
    };
b44dde08d7acatagen2026-10-19 07:38 UTC96
    let (over, under): (Vec<_>, Vec<_>) =
b44dde08d7acatagen2026-10-19 07:38 UTC97
        arr.floating.iter().partition(|f| arr.over.contains(&f.0));
b44dde08d7acatagen2026-10-19 07:38 UTC98
    above.extend(under.into_iter().map(float));
0c1685f1d412atagen2026-10-16 12:18 UTC99
    if let Some(w) = arr.fullscreen {
48af562d968batagen2026-10-16 12:39 UTC100
        let glide = anim.gliding(w, now);
48af562d968batagen2026-10-16 12:39 UTC101
        above.push((w, local(glide.unwrap_or(area)), false, glide.is_some()));
0c1685f1d412atagen2026-10-16 12:18 UTC102
    }
b44dde08d7acatagen2026-10-19 07:38 UTC103
    above.extend(over.into_iter().map(float));
7ce6ae4e5a85atagen2026-10-16 12:16 UTC104
    let focused = layout.focused();
2af1d8c55876atagen2026-10-16 11:05 UTC105
    let mut out = Vec::new();
cce3ef7e0162atagen2026-10-16 22:31 UTC106
    for x in unmanaged.iter().rev() {
cce3ef7e0162atagen2026-10-16 22:31 UTC107
        let Some(surface) = x.wl_surface() else {
cce3ef7e0162atagen2026-10-16 22:31 UTC108
            continue;
cce3ef7e0162atagen2026-10-16 22:31 UTC109
        };
cce3ef7e0162atagen2026-10-16 22:31 UTC110
        let loc = x.geometry().loc;
cce3ef7e0162atagen2026-10-16 22:31 UTC111
        let at = Point::<i32, Physical>::from((loc.x - area.x as i32, loc.y - area.y as i32));
cce3ef7e0162atagen2026-10-16 22:31 UTC112
        out.extend(
cce3ef7e0162atagen2026-10-16 22:31 UTC113
            render_elements_from_surface_tree(renderer, &surface, at, 1.0, 1.0, Kind::Unspecified)
cce3ef7e0162atagen2026-10-16 22:31 UTC114
                .into_iter()
cce3ef7e0162atagen2026-10-16 22:31 UTC115
                .map(Draw::Surface),
cce3ef7e0162atagen2026-10-16 22:31 UTC116
        );
cce3ef7e0162atagen2026-10-16 22:31 UTC117
    }
bc51c38a6fd9atagen2026-10-16 22:17 UTC118
    if let Some(output) = output {
bc51c38a6fd9atagen2026-10-16 22:17 UTC119
        crate::layers::elements(&mut out, renderer, output, Layer::Overlay);
bc51c38a6fd9atagen2026-10-16 22:17 UTC120
        if !fullscreen_at_rest {
bc51c38a6fd9atagen2026-10-16 22:17 UTC121
            crate::layers::elements(&mut out, renderer, output, Layer::Top);
bc51c38a6fd9atagen2026-10-16 22:17 UTC122
        }
bc51c38a6fd9atagen2026-10-16 22:17 UTC123
    }
618afeb78754atagen2026-10-17 13:40 UTC124
    for (g, frame, r) in fading.into_iter().rev() {
618afeb78754atagen2026-10-17 13:40 UTC125
        out.extend(ghost_element(frame, r, anim.ghost_effect(g, now), deco));
618afeb78754atagen2026-10-17 13:40 UTC126
    }
48af562d968batagen2026-10-16 12:39 UTC127
    for (id, r, framed, gliding) in tiled.into_iter().chain(above).rev() {
60956e3999deatagen2026-10-17 02:50 UTC128
        let Some(state) = windows.get_mut(id) else {
7ce6ae4e5a85atagen2026-10-16 12:16 UTC129
            continue;
7ce6ae4e5a85atagen2026-10-16 12:16 UTC130
        };
7ce6ae4e5a85atagen2026-10-16 12:16 UTC131
        let effect = anim.effect(id, now);
47259c39793fatagen2026-10-17 10:13 UTC132
        let first = out.len();
60956e3999deatagen2026-10-17 02:50 UTC133
        window_elements(&mut out, renderer, &state.window, r, effect, gliding);
47259c39793fatagen2026-10-17 10:13 UTC134
        let clip = framed.then(|| corner_clip(r, deco)).flatten();
47259c39793fatagen2026-10-17 10:13 UTC135
        if let Some(clip) = clip {
47259c39793fatagen2026-10-17 10:13 UTC136
            clip_window(&mut out, first, &state.window, clip);
47259c39793fatagen2026-10-17 10:13 UTC137
        }
7ce6ae4e5a85atagen2026-10-16 12:16 UTC138
        if framed && deco.border > 0 {
7ce6ae4e5a85atagen2026-10-16 12:16 UTC139
            let color = if focused == Some(id) {
7ce6ae4e5a85atagen2026-10-16 12:16 UTC140
                deco.focused.0
7ce6ae4e5a85atagen2026-10-16 12:16 UTC141
            } else {
7ce6ae4e5a85atagen2026-10-16 12:16 UTC142
                deco.unfocused.0
7ce6ae4e5a85atagen2026-10-16 12:16 UTC143
            };
7ce6ae4e5a85atagen2026-10-16 12:16 UTC144
            let alpha = effect.map_or(1.0, |e| e.progress);
60956e3999deatagen2026-10-17 02:50 UTC145
            let borders = &mut state.borders;
47259c39793fatagen2026-10-17 10:13 UTC146
            let first = out.len();
47259c39793fatagen2026-10-17 10:13 UTC147
            border_elements(&mut out, borders, r, deco, color, alpha);
47259c39793fatagen2026-10-17 10:13 UTC148
            if let Some(clip) = clip {
47259c39793fatagen2026-10-17 10:13 UTC149
                ring_borders(&mut out, first, clip, deco.border as i32);
47259c39793fatagen2026-10-17 10:13 UTC150
            }
2af1d8c55876atagen2026-10-16 11:05 UTC151
        }
2af1d8c55876atagen2026-10-16 11:05 UTC152
    }
82c94edb8c50atagen2026-10-17 04:56 UTC153
    for (g, frame, r) in ghosts.into_iter().rev() {
618afeb78754atagen2026-10-17 13:40 UTC154
        out.extend(ghost_element(frame, r, anim.ghost_effect(g, now), deco));
7373078d1149atagen2026-10-16 11:24 UTC155
    }
bc51c38a6fd9atagen2026-10-16 22:17 UTC156
    if let Some(output) = output {
bc51c38a6fd9atagen2026-10-16 22:17 UTC157
        crate::layers::elements(&mut out, renderer, output, Layer::Bottom);
bc51c38a6fd9atagen2026-10-16 22:17 UTC158
        crate::layers::elements(&mut out, renderer, output, Layer::Background);
bc51c38a6fd9atagen2026-10-16 22:17 UTC159
    }
2af1d8c55876atagen2026-10-16 11:05 UTC160
    out
2af1d8c55876atagen2026-10-16 11:05 UTC161
}
2af1d8c55876atagen2026-10-16 11:05 UTC162
48af562d968batagen2026-10-16 12:39 UTC163
/// Draws `window` filling `r`. In a planned move a buffer bigger than `r` is
48af562d968batagen2026-10-16 12:39 UTC164
/// cropped and one smaller has its last row and column stretched over the
48af562d968batagen2026-10-16 12:39 UTC165
/// rest, keeping content aligned with its neighbours. A gliding window is
48af562d968batagen2026-10-16 12:39 UTC166
/// scaled instead, so the client resizing mid-glide does not jump.
96db497859dcatagen2026-10-16 11:27 UTC167
fn window_elements(
96db497859dcatagen2026-10-16 11:27 UTC168
    out: &mut Vec<Draw>,
96db497859dcatagen2026-10-16 11:27 UTC169
    renderer: &mut VulkanRenderer,
96db497859dcatagen2026-10-16 11:27 UTC170
    window: &Window,
96db497859dcatagen2026-10-16 11:27 UTC171
    r: Rect,
96db497859dcatagen2026-10-16 11:27 UTC172
    effect: Option<Effect>,
48af562d968batagen2026-10-16 12:39 UTC173
    scaled: bool,
96db497859dcatagen2026-10-16 11:27 UTC174
) {
7373078d1149atagen2026-10-16 11:24 UTC175
    let Some(((x, y), (w, h))) = pixels(r) else {
2af1d8c55876atagen2026-10-16 11:05 UTC176
        return;
7373078d1149atagen2026-10-16 11:24 UTC177
    };
2af1d8c55876atagen2026-10-16 11:05 UTC178
    let geo = window.geometry();
2af1d8c55876atagen2026-10-16 11:05 UTC179
    let loc = Point::<i32, Physical>::from((x - geo.loc.x, y - geo.loc.y));
96db497859dcatagen2026-10-16 11:27 UTC180
    let alpha = effect.map_or(1.0, |e| e.progress);
2af1d8c55876atagen2026-10-16 11:05 UTC181
    let elems: Vec<WaylandSurfaceRenderElement<VulkanRenderer>> =
96db497859dcatagen2026-10-16 11:27 UTC182
        window.render_elements(renderer, loc, Scale::from(1.0), alpha);
96db497859dcatagen2026-10-16 11:27 UTC183
    if let Some(effect) = effect {
96db497859dcatagen2026-10-16 11:27 UTC184
        effect_elements(out, renderer, window, elems, ((x, y), (w, h)), effect);
96db497859dcatagen2026-10-16 11:27 UTC185
        return;
96db497859dcatagen2026-10-16 11:27 UTC186
    }
48af562d968batagen2026-10-16 12:39 UTC187
    if scaled && geo.size.w > 0 && geo.size.h > 0 {
48af562d968batagen2026-10-16 12:39 UTC188
        let scale = Scale::from((
48af562d968batagen2026-10-16 12:39 UTC189
            f64::from(w) / f64::from(geo.size.w),
48af562d968batagen2026-10-16 12:39 UTC190
            f64::from(h) / f64::from(geo.size.h),
48af562d968batagen2026-10-16 12:39 UTC191
        ));
48af562d968batagen2026-10-16 12:39 UTC192
        let origin = Point::<i32, Physical>::from((x, y));
48af562d968batagen2026-10-16 12:39 UTC193
        out.extend(
48af562d968batagen2026-10-16 12:39 UTC194
            elems
48af562d968batagen2026-10-16 12:39 UTC195
                .into_iter()
48af562d968batagen2026-10-16 12:39 UTC196
                .map(|e| Draw::Rescaled(RescaleRenderElement::from_element(e, origin, scale))),
48af562d968batagen2026-10-16 12:39 UTC197
        );
48af562d968batagen2026-10-16 12:39 UTC198
        return;
48af562d968batagen2026-10-16 12:39 UTC199
    }
2af1d8c55876atagen2026-10-16 11:05 UTC200
    if (w, h) == (geo.size.w, geo.size.h) {
2af1d8c55876atagen2026-10-16 11:05 UTC201
        out.extend(elems.into_iter().map(Draw::Surface));
2af1d8c55876atagen2026-10-16 11:05 UTC202
        return;
2af1d8c55876atagen2026-10-16 11:05 UTC203
    }
2af1d8c55876atagen2026-10-16 11:05 UTC204
    let crop = Rectangle::new((x, y).into(), (w, h).into());
2af1d8c55876atagen2026-10-16 11:05 UTC205
    out.extend(
2af1d8c55876atagen2026-10-16 11:05 UTC206
        elems
2af1d8c55876atagen2026-10-16 11:05 UTC207
            .into_iter()
2af1d8c55876atagen2026-10-16 11:05 UTC208
            .filter_map(|e| CropRenderElement::from_element(e, 1.0, crop))
2af1d8c55876atagen2026-10-16 11:05 UTC209
            .map(Draw::Cropped),
2af1d8c55876atagen2026-10-16 11:05 UTC210
    );
480ead9a41f5atagen2026-10-17 02:54 UTC211
    let Some(root) = crate::wm::root(window) else {
2af1d8c55876atagen2026-10-16 11:05 UTC212
        return;
2af1d8c55876atagen2026-10-16 11:05 UTC213
    };
2af1d8c55876atagen2026-10-16 11:05 UTC214
    let context = renderer.context_id();
7373078d1149atagen2026-10-16 11:24 UTC215
    let Some((tex, scale, transform)) = with_renderer_surface_state(&root, |s| {
2af1d8c55876atagen2026-10-16 11:05 UTC216
        s.texture::<VulkanTexture>(context.clone())
2af1d8c55876atagen2026-10-16 11:05 UTC217
            .cloned()
2af1d8c55876atagen2026-10-16 11:05 UTC218
            .map(|t| (t, s.buffer_scale(), s.buffer_transform()))
2af1d8c55876atagen2026-10-16 11:05 UTC219
    })
2af1d8c55876atagen2026-10-16 11:05 UTC220
    .flatten() else {
2af1d8c55876atagen2026-10-16 11:05 UTC221
        return;
2af1d8c55876atagen2026-10-16 11:05 UTC222
    };
2af1d8c55876atagen2026-10-16 11:05 UTC223
    let (gw, gh) = (geo.size.w, geo.size.h);
2af1d8c55876atagen2026-10-16 11:05 UTC224
    let (cw, ch) = (w.min(gw), h.min(gh));
2af1d8c55876atagen2026-10-16 11:05 UTC225
    let (gx, gy) = (geo.loc.x as f64, geo.loc.y as f64);
2af1d8c55876atagen2026-10-16 11:05 UTC226
    let mut strip = |src: Rectangle<f64, Logical>, at: (i32, i32), size: (i32, i32)| {
7373078d1149atagen2026-10-16 11:24 UTC227
        if size.0 >= 1 && size.1 >= 1 {
7373078d1149atagen2026-10-16 11:24 UTC228
            out.push(texture(&context, &tex, scale, transform, src, at, size));
2af1d8c55876atagen2026-10-16 11:05 UTC229
        }
2af1d8c55876atagen2026-10-16 11:05 UTC230
    };
2af1d8c55876atagen2026-10-16 11:05 UTC231
    let right = gx + f64::from(gw) - 1.0;
2af1d8c55876atagen2026-10-16 11:05 UTC232
    let bottom = gy + f64::from(gh) - 1.0;
2af1d8c55876atagen2026-10-16 11:05 UTC233
    strip(
2af1d8c55876atagen2026-10-16 11:05 UTC234
        Rectangle::new((right, gy).into(), (1.0, f64::from(ch)).into()),
2af1d8c55876atagen2026-10-16 11:05 UTC235
        (x + gw, y),
2af1d8c55876atagen2026-10-16 11:05 UTC236
        (w - gw, ch),
2af1d8c55876atagen2026-10-16 11:05 UTC237
    );
2af1d8c55876atagen2026-10-16 11:05 UTC238
    strip(
2af1d8c55876atagen2026-10-16 11:05 UTC239
        Rectangle::new((gx, bottom).into(), (f64::from(cw), 1.0).into()),
2af1d8c55876atagen2026-10-16 11:05 UTC240
        (x, y + gh),
2af1d8c55876atagen2026-10-16 11:05 UTC241
        (cw, h - gh),
2af1d8c55876atagen2026-10-16 11:05 UTC242
    );
2af1d8c55876atagen2026-10-16 11:05 UTC243
    strip(
2af1d8c55876atagen2026-10-16 11:05 UTC244
        Rectangle::new((right, bottom).into(), (1.0, 1.0).into()),
2af1d8c55876atagen2026-10-16 11:05 UTC245
        (x + gw, y + gh),
2af1d8c55876atagen2026-10-16 11:05 UTC246
        (w - gw, h - gh),
2af1d8c55876atagen2026-10-16 11:05 UTC247
    );
2af1d8c55876atagen2026-10-16 11:05 UTC248
}
7373078d1149atagen2026-10-16 11:24 UTC249
7ce6ae4e5a85atagen2026-10-16 12:16 UTC250
/// Four strips just outside `r`, in the gap, so the client keeps its size.
47259c39793fatagen2026-10-17 10:13 UTC251
/// With rounded corners they reach in by the largest radius, to cover the
47259c39793fatagen2026-10-17 10:13 UTC252
/// band the ring curves through inside the window's corners.
7ce6ae4e5a85atagen2026-10-16 12:16 UTC253
fn border_elements(
7ce6ae4e5a85atagen2026-10-16 12:16 UTC254
    out: &mut Vec<Draw>,
60956e3999deatagen2026-10-17 02:50 UTC255
    borders: &mut Option<[SolidColorBuffer; 4]>,
7ce6ae4e5a85atagen2026-10-16 12:16 UTC256
    r: Rect,
47259c39793fatagen2026-10-17 10:13 UTC257
    deco: &Decoration,
7ce6ae4e5a85atagen2026-10-16 12:16 UTC258
    color: [f32; 4],
7ce6ae4e5a85atagen2026-10-16 12:16 UTC259
    alpha: f32,
7ce6ae4e5a85atagen2026-10-16 12:16 UTC260
) {
7ce6ae4e5a85atagen2026-10-16 12:16 UTC261
    let Some(((x, y), (w, h))) = pixels(r) else {
7ce6ae4e5a85atagen2026-10-16 12:16 UTC262
        return;
7ce6ae4e5a85atagen2026-10-16 12:16 UTC263
    };
47259c39793fatagen2026-10-17 10:13 UTC264
    let b = deco.border as i32;
47259c39793fatagen2026-10-17 10:13 UTC265
    let reach = deco.radius.0.into_iter().max().unwrap_or(0) as i32;
47259c39793fatagen2026-10-17 10:13 UTC266
    let reach = reach.min(w / 2).min(h / 2);
7ce6ae4e5a85atagen2026-10-16 12:16 UTC267
    let strips = [
47259c39793fatagen2026-10-17 10:13 UTC268
        ((x - b, y - b), (w + 2 * b, b + reach)),
47259c39793fatagen2026-10-17 10:13 UTC269
        ((x - b, y + h - reach), (w + 2 * b, b + reach)),
47259c39793fatagen2026-10-17 10:13 UTC270
        ((x - b, y + reach), (b + reach, h - 2 * reach)),
47259c39793fatagen2026-10-17 10:13 UTC271
        ((x + w - reach, y + reach), (b + reach, h - 2 * reach)),
7ce6ae4e5a85atagen2026-10-16 12:16 UTC272
    ];
7ce6ae4e5a85atagen2026-10-16 12:16 UTC273
    let buffers = borders
60956e3999deatagen2026-10-17 02:50 UTC274
        .get_or_insert_with(|| std::array::from_fn(|_| SolidColorBuffer::new((0, 0), color)));
7ce6ae4e5a85atagen2026-10-16 12:16 UTC275
    for (buffer, (at, size)) in buffers.iter_mut().zip(strips) {
7ce6ae4e5a85atagen2026-10-16 12:16 UTC276
        buffer.update(size, color);
7ce6ae4e5a85atagen2026-10-16 12:16 UTC277
        out.push(Draw::Border(SolidColorRenderElement::from_buffer(
7ce6ae4e5a85atagen2026-10-16 12:16 UTC278
            buffer,
7ce6ae4e5a85atagen2026-10-16 12:16 UTC279
            at,
7ce6ae4e5a85atagen2026-10-16 12:16 UTC280
            1.0,
7ce6ae4e5a85atagen2026-10-16 12:16 UTC281
            alpha,
7ce6ae4e5a85atagen2026-10-16 12:16 UTC282
            Kind::Unspecified,
7ce6ae4e5a85atagen2026-10-16 12:16 UTC283
        )));
7ce6ae4e5a85atagen2026-10-16 12:16 UTC284
    }
7ce6ae4e5a85atagen2026-10-16 12:16 UTC285
}
7ce6ae4e5a85atagen2026-10-16 12:16 UTC286
7373078d1149atagen2026-10-16 11:24 UTC287
/// Snaps a logical rect to whole pixels by rounding its edges, so neighbours
7373078d1149atagen2026-10-16 11:24 UTC288
/// that share an edge stay flush.
7373078d1149atagen2026-10-16 11:24 UTC289
fn pixels(r: Rect) -> Option<((i32, i32), (i32, i32))> {
7373078d1149atagen2026-10-16 11:24 UTC290
    let (x, y) = (r.x.round() as i32, r.y.round() as i32);
7373078d1149atagen2026-10-16 11:24 UTC291
    let w = r.right().round() as i32 - x;
7373078d1149atagen2026-10-16 11:24 UTC292
    let h = r.bottom().round() as i32 - y;
7373078d1149atagen2026-10-16 11:24 UTC293
    (w >= 1 && h >= 1).then_some(((x, y), (w, h)))
7373078d1149atagen2026-10-16 11:24 UTC294
}
7373078d1149atagen2026-10-16 11:24 UTC295
7373078d1149atagen2026-10-16 11:24 UTC296
fn texture(
7373078d1149atagen2026-10-16 11:24 UTC297
    context: &ContextId<VulkanTexture>,
7373078d1149atagen2026-10-16 11:24 UTC298
    texture: &VulkanTexture,
7373078d1149atagen2026-10-16 11:24 UTC299
    scale: i32,
7373078d1149atagen2026-10-16 11:24 UTC300
    transform: Transform,
7373078d1149atagen2026-10-16 11:24 UTC301
    src: Rectangle<f64, Logical>,
7373078d1149atagen2026-10-16 11:24 UTC302
    at: (i32, i32),
7373078d1149atagen2026-10-16 11:24 UTC303
    size: (i32, i32),
7373078d1149atagen2026-10-16 11:24 UTC304
) -> Draw {
7373078d1149atagen2026-10-16 11:24 UTC305
    Draw::Texture(TextureRenderElement::from_static_texture(
7373078d1149atagen2026-10-16 11:24 UTC306
        Id::new(),
7373078d1149atagen2026-10-16 11:24 UTC307
        context.clone(),
7373078d1149atagen2026-10-16 11:24 UTC308
        Point::<f64, Physical>::from((f64::from(at.0), f64::from(at.1))),
7373078d1149atagen2026-10-16 11:24 UTC309
        texture.clone(),
7373078d1149atagen2026-10-16 11:24 UTC310
        scale,
7373078d1149atagen2026-10-16 11:24 UTC311
        transform,
7373078d1149atagen2026-10-16 11:24 UTC312
        None,
7373078d1149atagen2026-10-16 11:24 UTC313
        Some(src),
7373078d1149atagen2026-10-16 11:24 UTC314
        Some(Size::from(size)),
7373078d1149atagen2026-10-16 11:24 UTC315
        None,
7373078d1149atagen2026-10-16 11:24 UTC316
        Kind::Unspecified,
7373078d1149atagen2026-10-16 11:24 UTC317
    ))
7373078d1149atagen2026-10-16 11:24 UTC318
}
96db497859dcatagen2026-10-16 11:27 UTC319
96db497859dcatagen2026-10-16 11:27 UTC320
/// A window in a spawn transition. Its root surface goes through the effect
96db497859dcatagen2026-10-16 11:27 UTC321
/// shader, clamped to fill its rect, and its subsurfaces and popups fade with it.
96db497859dcatagen2026-10-16 11:27 UTC322
fn effect_elements(
96db497859dcatagen2026-10-16 11:27 UTC323
    out: &mut Vec<Draw>,
96db497859dcatagen2026-10-16 11:27 UTC324
    renderer: &mut VulkanRenderer,
96db497859dcatagen2026-10-16 11:27 UTC325
    window: &Window,
96db497859dcatagen2026-10-16 11:27 UTC326
    elems: Vec<WaylandSurfaceRenderElement<VulkanRenderer>>,
96db497859dcatagen2026-10-16 11:27 UTC327
    (at, size): ((i32, i32), (i32, i32)),
96db497859dcatagen2026-10-16 11:27 UTC328
    effect: Effect,
96db497859dcatagen2026-10-16 11:27 UTC329
) {
480ead9a41f5atagen2026-10-17 02:54 UTC330
    let Some(root) = crate::wm::root(window) else {
96db497859dcatagen2026-10-16 11:27 UTC331
        return;
96db497859dcatagen2026-10-16 11:27 UTC332
    };
96db497859dcatagen2026-10-16 11:27 UTC333
    let root_id = Id::from_wayland_resource(&root);
96db497859dcatagen2026-10-16 11:27 UTC334
    let crop = Rectangle::new(at.into(), size.into());
96db497859dcatagen2026-10-16 11:27 UTC335
    let context = renderer.context_id();
96db497859dcatagen2026-10-16 11:27 UTC336
    let root_tex = with_renderer_surface_state(&root, |s| {
96db497859dcatagen2026-10-16 11:27 UTC337
        s.texture::<VulkanTexture>(context.clone())
96db497859dcatagen2026-10-16 11:27 UTC338
            .cloned()
96db497859dcatagen2026-10-16 11:27 UTC339
            .map(|t| (t, s.buffer_scale(), s.buffer_transform()))
96db497859dcatagen2026-10-16 11:27 UTC340
    })
96db497859dcatagen2026-10-16 11:27 UTC341
    .flatten();
96db497859dcatagen2026-10-16 11:27 UTC342
    out.extend(
96db497859dcatagen2026-10-16 11:27 UTC343
        elems
96db497859dcatagen2026-10-16 11:27 UTC344
            .into_iter()
96db497859dcatagen2026-10-16 11:27 UTC345
            .filter(|e| root_tex.is_none() || *e.id() != root_id)
96db497859dcatagen2026-10-16 11:27 UTC346
            .filter_map(|e| CropRenderElement::from_element(e, 1.0, crop))
96db497859dcatagen2026-10-16 11:27 UTC347
            .map(Draw::Cropped),
96db497859dcatagen2026-10-16 11:27 UTC348
    );
96db497859dcatagen2026-10-16 11:27 UTC349
    if let Some((tex, scale, transform)) = root_tex {
96db497859dcatagen2026-10-16 11:27 UTC350
        let geo = window.geometry();
96db497859dcatagen2026-10-16 11:27 UTC351
        let src = Rectangle::new(
96db497859dcatagen2026-10-16 11:27 UTC352
            geo.loc.to_f64(),
96db497859dcatagen2026-10-16 11:27 UTC353
            (f64::from(size.0), f64::from(size.1)).into(),
96db497859dcatagen2026-10-16 11:27 UTC354
        );
96db497859dcatagen2026-10-16 11:27 UTC355
        out.push(Draw::Effect(EffectElement::new(
96db497859dcatagen2026-10-16 11:27 UTC356
            &tex, scale, transform, src, at, size, effect,
96db497859dcatagen2026-10-16 11:27 UTC357
        )));
96db497859dcatagen2026-10-16 11:27 UTC358
    }
96db497859dcatagen2026-10-16 11:27 UTC359
}
96db497859dcatagen2026-10-16 11:27 UTC360
96db497859dcatagen2026-10-16 11:27 UTC361
/// A texture drawn through the transition hook in the effect shader.
96db497859dcatagen2026-10-16 11:27 UTC362
pub struct EffectElement {
96db497859dcatagen2026-10-16 11:27 UTC363
    id: Id,
96db497859dcatagen2026-10-16 11:27 UTC364
    texture: VulkanTexture,
96db497859dcatagen2026-10-16 11:27 UTC365
    src: Rectangle<f64, Buffer>,
96db497859dcatagen2026-10-16 11:27 UTC366
    geometry: Rectangle<i32, Physical>,
96db497859dcatagen2026-10-16 11:27 UTC367
    transform: Transform,
96db497859dcatagen2026-10-16 11:27 UTC368
    effect: Effect,
96db497859dcatagen2026-10-16 11:27 UTC369
}
96db497859dcatagen2026-10-16 11:27 UTC370
96db497859dcatagen2026-10-16 11:27 UTC371
impl EffectElement {
96db497859dcatagen2026-10-16 11:27 UTC372
    fn new(
96db497859dcatagen2026-10-16 11:27 UTC373
        texture: &VulkanTexture,
96db497859dcatagen2026-10-16 11:27 UTC374
        scale: i32,
96db497859dcatagen2026-10-16 11:27 UTC375
        transform: Transform,
96db497859dcatagen2026-10-16 11:27 UTC376
        src: Rectangle<f64, Logical>,
96db497859dcatagen2026-10-16 11:27 UTC377
        at: (i32, i32),
96db497859dcatagen2026-10-16 11:27 UTC378
        size: (i32, i32),
96db497859dcatagen2026-10-16 11:27 UTC379
        effect: Effect,
96db497859dcatagen2026-10-16 11:27 UTC380
    ) -> Self {
96db497859dcatagen2026-10-16 11:27 UTC381
        let bounds = texture.size().to_logical(scale, transform).to_f64();
96db497859dcatagen2026-10-16 11:27 UTC382
        Self {
96db497859dcatagen2026-10-16 11:27 UTC383
            id: Id::new(),
96db497859dcatagen2026-10-16 11:27 UTC384
            texture: texture.clone(),
96db497859dcatagen2026-10-16 11:27 UTC385
            src: src.to_buffer(f64::from(scale), transform, &bounds),
96db497859dcatagen2026-10-16 11:27 UTC386
            geometry: Rectangle::new(at.into(), size.into()),
96db497859dcatagen2026-10-16 11:27 UTC387
            transform,
96db497859dcatagen2026-10-16 11:27 UTC388
            effect,
96db497859dcatagen2026-10-16 11:27 UTC389
        }
96db497859dcatagen2026-10-16 11:27 UTC390
    }
96db497859dcatagen2026-10-16 11:27 UTC391
}
96db497859dcatagen2026-10-16 11:27 UTC392
96db497859dcatagen2026-10-16 11:27 UTC393
impl Element for EffectElement {
96db497859dcatagen2026-10-16 11:27 UTC394
    fn id(&self) -> &Id {
96db497859dcatagen2026-10-16 11:27 UTC395
        &self.id
96db497859dcatagen2026-10-16 11:27 UTC396
    }
96db497859dcatagen2026-10-16 11:27 UTC397
96db497859dcatagen2026-10-16 11:27 UTC398
    fn current_commit(&self) -> CommitCounter {
96db497859dcatagen2026-10-16 11:27 UTC399
        CommitCounter::default()
96db497859dcatagen2026-10-16 11:27 UTC400
    }
96db497859dcatagen2026-10-16 11:27 UTC401
96db497859dcatagen2026-10-16 11:27 UTC402
    fn src(&self) -> Rectangle<f64, Buffer> {
96db497859dcatagen2026-10-16 11:27 UTC403
        self.src
96db497859dcatagen2026-10-16 11:27 UTC404
    }
96db497859dcatagen2026-10-16 11:27 UTC405
96db497859dcatagen2026-10-16 11:27 UTC406
    fn geometry(&self, _scale: Scale<f64>) -> Rectangle<i32, Physical> {
96db497859dcatagen2026-10-16 11:27 UTC407
        self.geometry
96db497859dcatagen2026-10-16 11:27 UTC408
    }
96db497859dcatagen2026-10-16 11:27 UTC409
96db497859dcatagen2026-10-16 11:27 UTC410
    fn transform(&self) -> Transform {
96db497859dcatagen2026-10-16 11:27 UTC411
        self.transform
96db497859dcatagen2026-10-16 11:27 UTC412
    }
96db497859dcatagen2026-10-16 11:27 UTC413
}
96db497859dcatagen2026-10-16 11:27 UTC414
96db497859dcatagen2026-10-16 11:27 UTC415
impl RenderElement<VulkanRenderer> for EffectElement {
96db497859dcatagen2026-10-16 11:27 UTC416
    fn draw(
96db497859dcatagen2026-10-16 11:27 UTC417
        &self,
96db497859dcatagen2026-10-16 11:27 UTC418
        frame: &mut VulkanFrame<'_, '_>,
96db497859dcatagen2026-10-16 11:27 UTC419
        src: Rectangle<f64, Buffer>,
96db497859dcatagen2026-10-16 11:27 UTC420
        dst: Rectangle<i32, Physical>,
96db497859dcatagen2026-10-16 11:27 UTC421
        damage: &[Rectangle<i32, Physical>],
96db497859dcatagen2026-10-16 11:27 UTC422
        _opaque_regions: &[Rectangle<i32, Physical>],
96db497859dcatagen2026-10-16 11:27 UTC423
        _cache: Option<&UserDataMap>,
96db497859dcatagen2026-10-16 11:27 UTC424
    ) -> Result<(), monolith_render_vk::Error> {
96db497859dcatagen2026-10-16 11:27 UTC425
        frame.render_effect(
96db497859dcatagen2026-10-16 11:27 UTC426
            &self.texture,
96db497859dcatagen2026-10-16 11:27 UTC427
            src,
96db497859dcatagen2026-10-16 11:27 UTC428
            dst,
96db497859dcatagen2026-10-16 11:27 UTC429
            damage,
96db497859dcatagen2026-10-16 11:27 UTC430
            self.transform,
96db497859dcatagen2026-10-16 11:27 UTC431
            1.0,
96db497859dcatagen2026-10-16 11:27 UTC432
            self.effect,
96db497859dcatagen2026-10-16 11:27 UTC433
        )
96db497859dcatagen2026-10-16 11:27 UTC434
    }
96db497859dcatagen2026-10-16 11:27 UTC435
}
47259c39793fatagen2026-10-17 10:13 UTC436
618afeb78754atagen2026-10-17 13:40 UTC437
fn ghost_element(frame: &LastFrame, r: Rect, effect: Effect, deco: &Decoration) -> Option<Draw> {
618afeb78754atagen2026-10-17 13:40 UTC438
    let (at, size) = pixels(r)?;
618afeb78754atagen2026-10-17 13:40 UTC439
    let src = Rectangle::new(
618afeb78754atagen2026-10-17 13:40 UTC440
        frame.geometry.loc.to_f64(),
618afeb78754atagen2026-10-17 13:40 UTC441
        (f64::from(size.0), f64::from(size.1)).into(),
618afeb78754atagen2026-10-17 13:40 UTC442
    );
618afeb78754atagen2026-10-17 13:40 UTC443
    let ghost = Draw::Effect(EffectElement::new(
618afeb78754atagen2026-10-17 13:40 UTC444
        &frame.texture,
618afeb78754atagen2026-10-17 13:40 UTC445
        frame.scale,
618afeb78754atagen2026-10-17 13:40 UTC446
        frame.transform,
618afeb78754atagen2026-10-17 13:40 UTC447
        src,
618afeb78754atagen2026-10-17 13:40 UTC448
        at,
618afeb78754atagen2026-10-17 13:40 UTC449
        size,
618afeb78754atagen2026-10-17 13:40 UTC450
        effect,
618afeb78754atagen2026-10-17 13:40 UTC451
    ));
618afeb78754atagen2026-10-17 13:40 UTC452
    Some(match corner_clip(r, deco) {
618afeb78754atagen2026-10-17 13:40 UTC453
        Some(clip) => Draw::Clipped(ClipElement::new(ghost, clip)),
618afeb78754atagen2026-10-17 13:40 UTC454
        None => ghost,
618afeb78754atagen2026-10-17 13:40 UTC455
    })
618afeb78754atagen2026-10-17 13:40 UTC456
}
618afeb78754atagen2026-10-17 13:40 UTC457
47259c39793fatagen2026-10-17 10:13 UTC458
/// The rounded outline of a framed window at `r`, if it has one.
47259c39793fatagen2026-10-17 10:13 UTC459
fn corner_clip(r: Rect, deco: &Decoration) -> Option<Clip> {
47259c39793fatagen2026-10-17 10:13 UTC460
    let radii = deco.radius.0;
47259c39793fatagen2026-10-17 10:13 UTC461
    if radii == [0; 4] {
47259c39793fatagen2026-10-17 10:13 UTC462
        return None;
47259c39793fatagen2026-10-17 10:13 UTC463
    }
47259c39793fatagen2026-10-17 10:13 UTC464
    let (at, size) = pixels(r)?;
47259c39793fatagen2026-10-17 10:13 UTC465
    Some(Clip {
47259c39793fatagen2026-10-17 10:13 UTC466
        rect: Rectangle::new(at.into(), size.into()),
47259c39793fatagen2026-10-17 10:13 UTC467
        radii: radii.map(|r| r as f32),
47259c39793fatagen2026-10-17 10:13 UTC468
        ring: 0.0,
47259c39793fatagen2026-10-17 10:13 UTC469
    })
47259c39793fatagen2026-10-17 10:13 UTC470
}
47259c39793fatagen2026-10-17 10:13 UTC471
47259c39793fatagen2026-10-17 10:13 UTC472
/// Clips what a window drew from `first` on, leaving its popups whole.
47259c39793fatagen2026-10-17 10:13 UTC473
fn clip_window(out: &mut [Draw], first: usize, window: &Window, clip: Clip) {
47259c39793fatagen2026-10-17 10:13 UTC474
    let mut popups = std::collections::HashSet::new();
47259c39793fatagen2026-10-17 10:13 UTC475
    if let Some(root) = crate::wm::root(window) {
47259c39793fatagen2026-10-17 10:13 UTC476
        for (popup, _) in PopupManager::popups_for_surface(&root) {
47259c39793fatagen2026-10-17 10:13 UTC477
            with_surface_tree_downward(
47259c39793fatagen2026-10-17 10:13 UTC478
                popup.wl_surface(),
47259c39793fatagen2026-10-17 10:13 UTC479
                (),
47259c39793fatagen2026-10-17 10:13 UTC480
                |_, _, _| TraversalAction::DoChildren(()),
47259c39793fatagen2026-10-17 10:13 UTC481
                |s, _, _| {
47259c39793fatagen2026-10-17 10:13 UTC482
                    popups.insert(Id::from_wayland_resource(s));
47259c39793fatagen2026-10-17 10:13 UTC483
                },
47259c39793fatagen2026-10-17 10:13 UTC484
                |_, _, _| true,
47259c39793fatagen2026-10-17 10:13 UTC485
            );
47259c39793fatagen2026-10-17 10:13 UTC486
        }
47259c39793fatagen2026-10-17 10:13 UTC487
    }
47259c39793fatagen2026-10-17 10:13 UTC488
    for draw in &mut out[first..] {
47259c39793fatagen2026-10-17 10:13 UTC489
        if !popups.contains(draw.id()) {
47259c39793fatagen2026-10-17 10:13 UTC490
            wrap(draw, clip);
47259c39793fatagen2026-10-17 10:13 UTC491
        }
47259c39793fatagen2026-10-17 10:13 UTC492
    }
47259c39793fatagen2026-10-17 10:13 UTC493
}
47259c39793fatagen2026-10-17 10:13 UTC494
47259c39793fatagen2026-10-17 10:13 UTC495
/// Turns the border strips from `first` on into a ring around the window's
47259c39793fatagen2026-10-17 10:13 UTC496
/// rounded outline.
47259c39793fatagen2026-10-17 10:13 UTC497
fn ring_borders(out: &mut [Draw], first: usize, window: Clip, b: i32) {
47259c39793fatagen2026-10-17 10:13 UTC498
    let grow = |r: f32| if r > 0.0 { r + b as f32 } else { 0.0 };
47259c39793fatagen2026-10-17 10:13 UTC499
    let ring = Clip {
47259c39793fatagen2026-10-17 10:13 UTC500
        rect: Rectangle::new(
47259c39793fatagen2026-10-17 10:13 UTC501
            window.rect.loc - Point::from((b, b)),
47259c39793fatagen2026-10-17 10:13 UTC502
            window.rect.size + Size::from((2 * b, 2 * b)),
47259c39793fatagen2026-10-17 10:13 UTC503
        ),
47259c39793fatagen2026-10-17 10:13 UTC504
        radii: window.radii.map(grow),
47259c39793fatagen2026-10-17 10:13 UTC505
        ring: b as f32,
47259c39793fatagen2026-10-17 10:13 UTC506
    };
47259c39793fatagen2026-10-17 10:13 UTC507
    for draw in &mut out[first..] {
47259c39793fatagen2026-10-17 10:13 UTC508
        wrap(draw, ring);
47259c39793fatagen2026-10-17 10:13 UTC509
    }
47259c39793fatagen2026-10-17 10:13 UTC510
}
47259c39793fatagen2026-10-17 10:13 UTC511
47259c39793fatagen2026-10-17 10:13 UTC512
fn wrap(draw: &mut Draw, clip: Clip) {
47259c39793fatagen2026-10-17 10:13 UTC513
    let inner = std::mem::replace(draw, Draw::Clipped(ClipElement::empty()));
47259c39793fatagen2026-10-17 10:13 UTC514
    *draw = Draw::Clipped(ClipElement::new(inner, clip));
47259c39793fatagen2026-10-17 10:13 UTC515
}
47259c39793fatagen2026-10-17 10:13 UTC516
47259c39793fatagen2026-10-17 10:13 UTC517
/// Another element drawn through a rounded clip.
47259c39793fatagen2026-10-17 10:13 UTC518
pub struct ClipElement {
47259c39793fatagen2026-10-17 10:13 UTC519
    inner: Option<Box<Draw>>,
47259c39793fatagen2026-10-17 10:13 UTC520
    clip: Clip,
47259c39793fatagen2026-10-17 10:13 UTC521
    id: Id,
47259c39793fatagen2026-10-17 10:13 UTC522
}
47259c39793fatagen2026-10-17 10:13 UTC523
47259c39793fatagen2026-10-17 10:13 UTC524
impl ClipElement {
47259c39793fatagen2026-10-17 10:13 UTC525
    fn new(inner: Draw, clip: Clip) -> Self {
47259c39793fatagen2026-10-17 10:13 UTC526
        Self {
47259c39793fatagen2026-10-17 10:13 UTC527
            id: inner.id().clone(),
47259c39793fatagen2026-10-17 10:13 UTC528
            inner: Some(Box::new(inner)),
47259c39793fatagen2026-10-17 10:13 UTC529
            clip,
47259c39793fatagen2026-10-17 10:13 UTC530
        }
47259c39793fatagen2026-10-17 10:13 UTC531
    }
47259c39793fatagen2026-10-17 10:13 UTC532
47259c39793fatagen2026-10-17 10:13 UTC533
    /// A placeholder while an element is moved into its wrapper.
47259c39793fatagen2026-10-17 10:13 UTC534
    fn empty() -> Self {
47259c39793fatagen2026-10-17 10:13 UTC535
        Self {
47259c39793fatagen2026-10-17 10:13 UTC536
            inner: None,
47259c39793fatagen2026-10-17 10:13 UTC537
            clip: Clip {
47259c39793fatagen2026-10-17 10:13 UTC538
                rect: Rectangle::default(),
47259c39793fatagen2026-10-17 10:13 UTC539
                radii: [0.0; 4],
47259c39793fatagen2026-10-17 10:13 UTC540
                ring: 0.0,
47259c39793fatagen2026-10-17 10:13 UTC541
            },
47259c39793fatagen2026-10-17 10:13 UTC542
            id: Id::new(),
47259c39793fatagen2026-10-17 10:13 UTC543
        }
47259c39793fatagen2026-10-17 10:13 UTC544
    }
47259c39793fatagen2026-10-17 10:13 UTC545
47259c39793fatagen2026-10-17 10:13 UTC546
    fn inner(&self) -> &Draw {
47259c39793fatagen2026-10-17 10:13 UTC547
        self.inner.as_deref().expect("only placeholders are empty")
47259c39793fatagen2026-10-17 10:13 UTC548
    }
47259c39793fatagen2026-10-17 10:13 UTC549
}
47259c39793fatagen2026-10-17 10:13 UTC550
47259c39793fatagen2026-10-17 10:13 UTC551
impl Element for ClipElement {
47259c39793fatagen2026-10-17 10:13 UTC552
    fn id(&self) -> &Id {
47259c39793fatagen2026-10-17 10:13 UTC553
        &self.id
47259c39793fatagen2026-10-17 10:13 UTC554
    }
47259c39793fatagen2026-10-17 10:13 UTC555
47259c39793fatagen2026-10-17 10:13 UTC556
    fn current_commit(&self) -> CommitCounter {
47259c39793fatagen2026-10-17 10:13 UTC557
        self.inner().current_commit()
47259c39793fatagen2026-10-17 10:13 UTC558
    }
47259c39793fatagen2026-10-17 10:13 UTC559
47259c39793fatagen2026-10-17 10:13 UTC560
    fn location(&self, scale: Scale<f64>) -> Point<i32, Physical> {
47259c39793fatagen2026-10-17 10:13 UTC561
        self.inner().location(scale)
47259c39793fatagen2026-10-17 10:13 UTC562
    }
47259c39793fatagen2026-10-17 10:13 UTC563
47259c39793fatagen2026-10-17 10:13 UTC564
    fn src(&self) -> Rectangle<f64, Buffer> {
47259c39793fatagen2026-10-17 10:13 UTC565
        self.inner().src()
47259c39793fatagen2026-10-17 10:13 UTC566
    }
47259c39793fatagen2026-10-17 10:13 UTC567
47259c39793fatagen2026-10-17 10:13 UTC568
    fn transform(&self) -> Transform {
47259c39793fatagen2026-10-17 10:13 UTC569
        self.inner().transform()
47259c39793fatagen2026-10-17 10:13 UTC570
    }
47259c39793fatagen2026-10-17 10:13 UTC571
47259c39793fatagen2026-10-17 10:13 UTC572
    fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
47259c39793fatagen2026-10-17 10:13 UTC573
        self.inner().geometry(scale)
47259c39793fatagen2026-10-17 10:13 UTC574
    }
47259c39793fatagen2026-10-17 10:13 UTC575
47259c39793fatagen2026-10-17 10:13 UTC576
    fn damage_since(
47259c39793fatagen2026-10-17 10:13 UTC577
        &self,
47259c39793fatagen2026-10-17 10:13 UTC578
        scale: Scale<f64>,
47259c39793fatagen2026-10-17 10:13 UTC579
        commit: Option<CommitCounter>,
47259c39793fatagen2026-10-17 10:13 UTC580
    ) -> DamageSet<i32, Physical> {
47259c39793fatagen2026-10-17 10:13 UTC581
        self.inner().damage_since(scale, commit)
47259c39793fatagen2026-10-17 10:13 UTC582
    }
47259c39793fatagen2026-10-17 10:13 UTC583
47259c39793fatagen2026-10-17 10:13 UTC584
    /// A ring hides nothing behind it, and a rounded window hides nothing
47259c39793fatagen2026-10-17 10:13 UTC585
    /// behind its corners.
47259c39793fatagen2026-10-17 10:13 UTC586
    fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {
47259c39793fatagen2026-10-17 10:13 UTC587
        if self.clip.ring > 0.0 {
47259c39793fatagen2026-10-17 10:13 UTC588
            return OpaqueRegions::default();
47259c39793fatagen2026-10-17 10:13 UTC589
        }
47259c39793fatagen2026-10-17 10:13 UTC590
        let loc = self.geometry(scale).loc;
47259c39793fatagen2026-10-17 10:13 UTC591
        let c = self.clip.rect;
47259c39793fatagen2026-10-17 10:13 UTC592
        let (right, bottom) = (c.loc.x + c.size.w, c.loc.y + c.size.h);
47259c39793fatagen2026-10-17 10:13 UTC593
        let [tl, tr, br, bl] = self.clip.radii.map(|r| r.ceil() as i32);
47259c39793fatagen2026-10-17 10:13 UTC594
        let corners = [
47259c39793fatagen2026-10-17 10:13 UTC595
            Rectangle::new(c.loc, (tl, tl).into()),
47259c39793fatagen2026-10-17 10:13 UTC596
            Rectangle::new((right - tr, c.loc.y).into(), (tr, tr).into()),
47259c39793fatagen2026-10-17 10:13 UTC597
            Rectangle::new((right - br, bottom - br).into(), (br, br).into()),
47259c39793fatagen2026-10-17 10:13 UTC598
            Rectangle::new((c.loc.x, bottom - bl).into(), (bl, bl).into()),
47259c39793fatagen2026-10-17 10:13 UTC599
        ]
47259c39793fatagen2026-10-17 10:13 UTC600
        .map(|mut r| {
47259c39793fatagen2026-10-17 10:13 UTC601
            r.loc -= loc;
47259c39793fatagen2026-10-17 10:13 UTC602
            r
47259c39793fatagen2026-10-17 10:13 UTC603
        });
47259c39793fatagen2026-10-17 10:13 UTC604
        Rectangle::subtract_rects_many(self.inner().opaque_regions(scale), corners)
47259c39793fatagen2026-10-17 10:13 UTC605
            .into_iter()
47259c39793fatagen2026-10-17 10:13 UTC606
            .collect()
47259c39793fatagen2026-10-17 10:13 UTC607
    }
47259c39793fatagen2026-10-17 10:13 UTC608
47259c39793fatagen2026-10-17 10:13 UTC609
    fn alpha(&self) -> f32 {
47259c39793fatagen2026-10-17 10:13 UTC610
        self.inner().alpha()
47259c39793fatagen2026-10-17 10:13 UTC611
    }
47259c39793fatagen2026-10-17 10:13 UTC612
47259c39793fatagen2026-10-17 10:13 UTC613
    fn kind(&self) -> Kind {
47259c39793fatagen2026-10-17 10:13 UTC614
        self.inner().kind()
47259c39793fatagen2026-10-17 10:13 UTC615
    }
47259c39793fatagen2026-10-17 10:13 UTC616
}
47259c39793fatagen2026-10-17 10:13 UTC617
47259c39793fatagen2026-10-17 10:13 UTC618
impl RenderElement<VulkanRenderer> for ClipElement {
47259c39793fatagen2026-10-17 10:13 UTC619
    fn draw(
47259c39793fatagen2026-10-17 10:13 UTC620
        &self,
47259c39793fatagen2026-10-17 10:13 UTC621
        frame: &mut VulkanFrame<'_, '_>,
47259c39793fatagen2026-10-17 10:13 UTC622
        src: Rectangle<f64, Buffer>,
47259c39793fatagen2026-10-17 10:13 UTC623
        dst: Rectangle<i32, Physical>,
47259c39793fatagen2026-10-17 10:13 UTC624
        damage: &[Rectangle<i32, Physical>],
47259c39793fatagen2026-10-17 10:13 UTC625
        opaque_regions: &[Rectangle<i32, Physical>],
47259c39793fatagen2026-10-17 10:13 UTC626
        cache: Option<&UserDataMap>,
47259c39793fatagen2026-10-17 10:13 UTC627
    ) -> Result<(), monolith_render_vk::Error> {
47259c39793fatagen2026-10-17 10:13 UTC628
        frame.set_clip(Some(self.clip));
47259c39793fatagen2026-10-17 10:13 UTC629
        let drawn = self
47259c39793fatagen2026-10-17 10:13 UTC630
            .inner()
47259c39793fatagen2026-10-17 10:13 UTC631
            .draw(frame, src, dst, damage, opaque_regions, cache);
47259c39793fatagen2026-10-17 10:13 UTC632
        frame.set_clip(None);
47259c39793fatagen2026-10-17 10:13 UTC633
        drawn
47259c39793fatagen2026-10-17 10:13 UTC634
    }
47259c39793fatagen2026-10-17 10:13 UTC635
}