2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 1 | //! What a frame draws. The space holds final geometry for input, while the |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 2 | //! scene draws windows where the animator says they are right now. |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 3 | |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 4 | use crate::wm::{LastFrame, Wm}; |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 5 | use monolith_config::Decoration; |
b44dde08d7ac | atagen | 2026-10-19 07:38 UTC | 6 | use monolith_layout::{OutputId, Rect, WindowId}; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 7 | use monolith_render_vk::{Clip, Effect, VulkanFrame, VulkanRenderer, VulkanTexture}; |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 8 | use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement; |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 9 | use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement}; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 10 | use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 11 | use smithay::backend::renderer::element::surface::render_elements_from_surface_tree; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 12 | use smithay::backend::renderer::element::texture::TextureRenderElement; |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 13 | use smithay::backend::renderer::element::utils::{CropRenderElement, RescaleRenderElement}; |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 14 | use smithay::backend::renderer::element::{AsRenderElements, Element, Id, Kind, RenderElement}; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 15 | use smithay::backend::renderer::utils::with_renderer_surface_state; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 16 | use smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions}; |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 17 | use smithay::backend::renderer::{ContextId, Renderer, Texture}; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 18 | use smithay::desktop::{PopupManager, Window}; |
bc51c38a6fd9 | atagen | 2026-10-16 22:17 UTC | 19 | use smithay::output::Output; |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 20 | use smithay::utils::user_data::UserDataMap; |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 21 | use smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform}; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 22 | use smithay::wayland::compositor::{TraversalAction, with_surface_tree_downward}; |
bc51c38a6fd9 | atagen | 2026-10-16 22:17 UTC | 23 | use smithay::wayland::shell::wlr_layer::Layer; |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 24 | use smithay::xwayland::X11Surface; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 25 | |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 26 | smithay::backend::renderer::element::render_elements! { |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 27 | pub Draw<=VulkanRenderer>; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 28 | Surface=WaylandSurfaceRenderElement<VulkanRenderer>, |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 29 | Cropped=CropRenderElement<WaylandSurfaceRenderElement<VulkanRenderer>>, |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 30 | Texture=TextureRenderElement<VulkanTexture>, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 31 | Effect=EffectElement, |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 32 | Border=SolidColorRenderElement, |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 33 | Memory=MemoryRenderBufferRenderElement<VulkanRenderer>, |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 34 | Rescaled=RescaleRenderElement<WaylandSurfaceRenderElement<VulkanRenderer>>, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 35 | Clipped=ClipElement, |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 36 | } |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 37 | |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 38 | /// Elements for output `o` in its own coordinates, front to back. |
30aece3a63ce | atagen | 2026-10-16 13:48 UTC | 39 | #[profiling::function] |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 40 | pub fn build( |
60956e3999de | atagen | 2026-10-17 02:50 UTC | 41 | wm: &mut Wm, |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 42 | o: OutputId, |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 43 | renderer: &mut VulkanRenderer, |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 44 | now: u64, |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 45 | deco: &Decoration, |
bc51c38a6fd9 | atagen | 2026-10-16 22:17 UTC | 46 | output: Option<&Output>, |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 47 | unmanaged: &[X11Surface], |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 48 | ) -> Vec<Draw> { |
82c94edb8c50 | atagen | 2026-10-17 04:56 UTC | 49 | let crate::wm::Parts { |
82c94edb8c50 | atagen | 2026-10-17 04:56 UTC | 50 | layout, |
82c94edb8c50 | atagen | 2026-10-17 04:56 UTC | 51 | anim, |
82c94edb8c50 | atagen | 2026-10-17 04:56 UTC | 52 | windows, |
82c94edb8c50 | atagen | 2026-10-17 04:56 UTC | 53 | last_frames, |
82c94edb8c50 | atagen | 2026-10-17 04:56 UTC | 54 | } = wm.parts(); |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 55 | let Some(area) = layout.output(o).map(|out| out.area) else { |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 56 | return Vec::new(); |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 57 | }; |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 58 | let local = |r: Rect| Rect::new(r.x - area.x, r.y - area.y, r.w, r.h); |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 59 | let mut ghosts = Vec::new(); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 60 | let mut tiled = Vec::new(); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 61 | let mut above = Vec::new(); |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 62 | let mut fading = Vec::new(); |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 63 | for g in anim.ghosts().iter().filter(|g| g.output == o) { |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 64 | let Some(frame) = last_frames.get(&g.id) else { |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 65 | continue; |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 66 | }; |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 67 | match g.fixed { |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 68 | Some(r) => fading.push((g, frame, local(r))), |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 69 | None => { |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 70 | if let Some(r) = anim.tiled(o, g.id, now) { |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 71 | ghosts.push((g, frame, local(r))); |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 72 | } |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 73 | } |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 74 | } |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 75 | } |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 76 | let arr = layout.arrange(o); |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 77 | let fullscreen_at_rest = arr |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 78 | .fullscreen |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 79 | .is_some_and(|w| anim.gliding(w, now).is_none()); |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 80 | for p in &arr.tiled { |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 81 | if Some(p.window) == arr.fullscreen || fullscreen_at_rest { |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 82 | continue; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 83 | } |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 84 | match anim.gliding(p.window, now) { |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 85 | Some(r) => above.push((p.window, local(r), true, true)), |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 86 | None => { |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 87 | let r = anim.tiled(o, p.window, now).unwrap_or(p.rect); |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 88 | tiled.push((p.window, local(r), true, false)); |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 89 | } |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 90 | } |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 91 | } |
b44dde08d7ac | atagen | 2026-10-19 07:38 UTC | 92 | let float = |&(w, r): &(WindowId, Rect)| { |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 93 | let glide = anim.gliding(w, now); |
b44dde08d7ac | atagen | 2026-10-19 07:38 UTC | 94 | (w, local(glide.unwrap_or(r)), true, glide.is_some()) |
b44dde08d7ac | atagen | 2026-10-19 07:38 UTC | 95 | }; |
b44dde08d7ac | atagen | 2026-10-19 07:38 UTC | 96 | let (over, under): (Vec<_>, Vec<_>) = |
b44dde08d7ac | atagen | 2026-10-19 07:38 UTC | 97 | arr.floating.iter().partition(|f| arr.over.contains(&f.0)); |
b44dde08d7ac | atagen | 2026-10-19 07:38 UTC | 98 | above.extend(under.into_iter().map(float)); |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 99 | if let Some(w) = arr.fullscreen { |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 100 | let glide = anim.gliding(w, now); |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 101 | above.push((w, local(glide.unwrap_or(area)), false, glide.is_some())); |
0c1685f1d412 | atagen | 2026-10-16 12:18 UTC | 102 | } |
b44dde08d7ac | atagen | 2026-10-19 07:38 UTC | 103 | above.extend(over.into_iter().map(float)); |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 104 | let focused = layout.focused(); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 105 | let mut out = Vec::new(); |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 106 | for x in unmanaged.iter().rev() { |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 107 | let Some(surface) = x.wl_surface() else { |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 108 | continue; |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 109 | }; |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 110 | let loc = x.geometry().loc; |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 111 | let at = Point::<i32, Physical>::from((loc.x - area.x as i32, loc.y - area.y as i32)); |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 112 | out.extend( |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 113 | render_elements_from_surface_tree(renderer, &surface, at, 1.0, 1.0, Kind::Unspecified) |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 114 | .into_iter() |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 115 | .map(Draw::Surface), |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 116 | ); |
cce3ef7e0162 | atagen | 2026-10-16 22:31 UTC | 117 | } |
bc51c38a6fd9 | atagen | 2026-10-16 22:17 UTC | 118 | if let Some(output) = output { |
bc51c38a6fd9 | atagen | 2026-10-16 22:17 UTC | 119 | crate::layers::elements(&mut out, renderer, output, Layer::Overlay); |
bc51c38a6fd9 | atagen | 2026-10-16 22:17 UTC | 120 | if !fullscreen_at_rest { |
bc51c38a6fd9 | atagen | 2026-10-16 22:17 UTC | 121 | crate::layers::elements(&mut out, renderer, output, Layer::Top); |
bc51c38a6fd9 | atagen | 2026-10-16 22:17 UTC | 122 | } |
bc51c38a6fd9 | atagen | 2026-10-16 22:17 UTC | 123 | } |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 124 | for (g, frame, r) in fading.into_iter().rev() { |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 125 | out.extend(ghost_element(frame, r, anim.ghost_effect(g, now), deco)); |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 126 | } |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 127 | for (id, r, framed, gliding) in tiled.into_iter().chain(above).rev() { |
60956e3999de | atagen | 2026-10-17 02:50 UTC | 128 | let Some(state) = windows.get_mut(id) else { |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 129 | continue; |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 130 | }; |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 131 | let effect = anim.effect(id, now); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 132 | let first = out.len(); |
60956e3999de | atagen | 2026-10-17 02:50 UTC | 133 | window_elements(&mut out, renderer, &state.window, r, effect, gliding); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 134 | let clip = framed.then(|| corner_clip(r, deco)).flatten(); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 135 | if let Some(clip) = clip { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 136 | clip_window(&mut out, first, &state.window, clip); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 137 | } |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 138 | if framed && deco.border > 0 { |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 139 | let color = if focused == Some(id) { |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 140 | deco.focused.0 |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 141 | } else { |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 142 | deco.unfocused.0 |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 143 | }; |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 144 | let alpha = effect.map_or(1.0, |e| e.progress); |
60956e3999de | atagen | 2026-10-17 02:50 UTC | 145 | let borders = &mut state.borders; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 146 | let first = out.len(); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 147 | border_elements(&mut out, borders, r, deco, color, alpha); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 148 | if let Some(clip) = clip { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 149 | ring_borders(&mut out, first, clip, deco.border as i32); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 150 | } |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 151 | } |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 152 | } |
82c94edb8c50 | atagen | 2026-10-17 04:56 UTC | 153 | for (g, frame, r) in ghosts.into_iter().rev() { |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 154 | out.extend(ghost_element(frame, r, anim.ghost_effect(g, now), deco)); |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 155 | } |
bc51c38a6fd9 | atagen | 2026-10-16 22:17 UTC | 156 | if let Some(output) = output { |
bc51c38a6fd9 | atagen | 2026-10-16 22:17 UTC | 157 | crate::layers::elements(&mut out, renderer, output, Layer::Bottom); |
bc51c38a6fd9 | atagen | 2026-10-16 22:17 UTC | 158 | crate::layers::elements(&mut out, renderer, output, Layer::Background); |
bc51c38a6fd9 | atagen | 2026-10-16 22:17 UTC | 159 | } |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 160 | out |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 161 | } |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 162 | |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 163 | /// Draws `window` filling `r`. In a planned move a buffer bigger than `r` is |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 164 | /// cropped and one smaller has its last row and column stretched over the |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 165 | /// rest, keeping content aligned with its neighbours. A gliding window is |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 166 | /// scaled instead, so the client resizing mid-glide does not jump. |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 167 | fn window_elements( |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 168 | out: &mut Vec<Draw>, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 169 | renderer: &mut VulkanRenderer, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 170 | window: &Window, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 171 | r: Rect, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 172 | effect: Option<Effect>, |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 173 | scaled: bool, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 174 | ) { |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 175 | let Some(((x, y), (w, h))) = pixels(r) else { |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 176 | return; |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 177 | }; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 178 | let geo = window.geometry(); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 179 | let loc = Point::<i32, Physical>::from((x - geo.loc.x, y - geo.loc.y)); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 180 | let alpha = effect.map_or(1.0, |e| e.progress); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 181 | let elems: Vec<WaylandSurfaceRenderElement<VulkanRenderer>> = |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 182 | window.render_elements(renderer, loc, Scale::from(1.0), alpha); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 183 | if let Some(effect) = effect { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 184 | effect_elements(out, renderer, window, elems, ((x, y), (w, h)), effect); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 185 | return; |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 186 | } |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 187 | if scaled && geo.size.w > 0 && geo.size.h > 0 { |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 188 | let scale = Scale::from(( |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 189 | f64::from(w) / f64::from(geo.size.w), |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 190 | f64::from(h) / f64::from(geo.size.h), |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 191 | )); |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 192 | let origin = Point::<i32, Physical>::from((x, y)); |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 193 | out.extend( |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 194 | elems |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 195 | .into_iter() |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 196 | .map(|e| Draw::Rescaled(RescaleRenderElement::from_element(e, origin, scale))), |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 197 | ); |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 198 | return; |
48af562d968b | atagen | 2026-10-16 12:39 UTC | 199 | } |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 200 | if (w, h) == (geo.size.w, geo.size.h) { |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 201 | out.extend(elems.into_iter().map(Draw::Surface)); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 202 | return; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 203 | } |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 204 | let crop = Rectangle::new((x, y).into(), (w, h).into()); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 205 | out.extend( |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 206 | elems |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 207 | .into_iter() |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 208 | .filter_map(|e| CropRenderElement::from_element(e, 1.0, crop)) |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 209 | .map(Draw::Cropped), |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 210 | ); |
480ead9a41f5 | atagen | 2026-10-17 02:54 UTC | 211 | let Some(root) = crate::wm::root(window) else { |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 212 | return; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 213 | }; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 214 | let context = renderer.context_id(); |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 215 | let Some((tex, scale, transform)) = with_renderer_surface_state(&root, |s| { |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 216 | s.texture::<VulkanTexture>(context.clone()) |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 217 | .cloned() |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 218 | .map(|t| (t, s.buffer_scale(), s.buffer_transform())) |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 219 | }) |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 220 | .flatten() else { |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 221 | return; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 222 | }; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 223 | let (gw, gh) = (geo.size.w, geo.size.h); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 224 | let (cw, ch) = (w.min(gw), h.min(gh)); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 225 | let (gx, gy) = (geo.loc.x as f64, geo.loc.y as f64); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 226 | let mut strip = |src: Rectangle<f64, Logical>, at: (i32, i32), size: (i32, i32)| { |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 227 | if size.0 >= 1 && size.1 >= 1 { |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 228 | out.push(texture(&context, &tex, scale, transform, src, at, size)); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 229 | } |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 230 | }; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 231 | let right = gx + f64::from(gw) - 1.0; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 232 | let bottom = gy + f64::from(gh) - 1.0; |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 233 | strip( |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 234 | Rectangle::new((right, gy).into(), (1.0, f64::from(ch)).into()), |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 235 | (x + gw, y), |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 236 | (w - gw, ch), |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 237 | ); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 238 | strip( |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 239 | Rectangle::new((gx, bottom).into(), (f64::from(cw), 1.0).into()), |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 240 | (x, y + gh), |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 241 | (cw, h - gh), |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 242 | ); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 243 | strip( |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 244 | Rectangle::new((right, bottom).into(), (1.0, 1.0).into()), |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 245 | (x + gw, y + gh), |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 246 | (w - gw, h - gh), |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 247 | ); |
2af1d8c55876 | atagen | 2026-10-16 11:05 UTC | 248 | } |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 249 | |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 250 | /// Four strips just outside `r`, in the gap, so the client keeps its size. |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 251 | /// With rounded corners they reach in by the largest radius, to cover the |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 252 | /// band the ring curves through inside the window's corners. |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 253 | fn border_elements( |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 254 | out: &mut Vec<Draw>, |
60956e3999de | atagen | 2026-10-17 02:50 UTC | 255 | borders: &mut Option<[SolidColorBuffer; 4]>, |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 256 | r: Rect, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 257 | deco: &Decoration, |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 258 | color: [f32; 4], |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 259 | alpha: f32, |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 260 | ) { |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 261 | let Some(((x, y), (w, h))) = pixels(r) else { |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 262 | return; |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 263 | }; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 264 | let b = deco.border as i32; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 265 | let reach = deco.radius.0.into_iter().max().unwrap_or(0) as i32; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 266 | let reach = reach.min(w / 2).min(h / 2); |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 267 | let strips = [ |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 268 | ((x - b, y - b), (w + 2 * b, b + reach)), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 269 | ((x - b, y + h - reach), (w + 2 * b, b + reach)), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 270 | ((x - b, y + reach), (b + reach, h - 2 * reach)), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 271 | ((x + w - reach, y + reach), (b + reach, h - 2 * reach)), |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 272 | ]; |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 273 | let buffers = borders |
60956e3999de | atagen | 2026-10-17 02:50 UTC | 274 | .get_or_insert_with(|| std::array::from_fn(|_| SolidColorBuffer::new((0, 0), color))); |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 275 | for (buffer, (at, size)) in buffers.iter_mut().zip(strips) { |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 276 | buffer.update(size, color); |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 277 | out.push(Draw::Border(SolidColorRenderElement::from_buffer( |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 278 | buffer, |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 279 | at, |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 280 | 1.0, |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 281 | alpha, |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 282 | Kind::Unspecified, |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 283 | ))); |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 284 | } |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 285 | } |
7ce6ae4e5a85 | atagen | 2026-10-16 12:16 UTC | 286 | |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 287 | /// Snaps a logical rect to whole pixels by rounding its edges, so neighbours |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 288 | /// that share an edge stay flush. |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 289 | fn pixels(r: Rect) -> Option<((i32, i32), (i32, i32))> { |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 290 | let (x, y) = (r.x.round() as i32, r.y.round() as i32); |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 291 | let w = r.right().round() as i32 - x; |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 292 | let h = r.bottom().round() as i32 - y; |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 293 | (w >= 1 && h >= 1).then_some(((x, y), (w, h))) |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 294 | } |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 295 | |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 296 | fn texture( |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 297 | context: &ContextId<VulkanTexture>, |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 298 | texture: &VulkanTexture, |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 299 | scale: i32, |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 300 | transform: Transform, |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 301 | src: Rectangle<f64, Logical>, |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 302 | at: (i32, i32), |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 303 | size: (i32, i32), |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 304 | ) -> Draw { |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 305 | Draw::Texture(TextureRenderElement::from_static_texture( |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 306 | Id::new(), |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 307 | context.clone(), |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 308 | Point::<f64, Physical>::from((f64::from(at.0), f64::from(at.1))), |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 309 | texture.clone(), |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 310 | scale, |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 311 | transform, |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 312 | None, |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 313 | Some(src), |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 314 | Some(Size::from(size)), |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 315 | None, |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 316 | Kind::Unspecified, |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 317 | )) |
7373078d1149 | atagen | 2026-10-16 11:24 UTC | 318 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 319 | |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 320 | /// A window in a spawn transition. Its root surface goes through the effect |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 321 | /// shader, clamped to fill its rect, and its subsurfaces and popups fade with it. |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 322 | fn effect_elements( |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 323 | out: &mut Vec<Draw>, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 324 | renderer: &mut VulkanRenderer, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 325 | window: &Window, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 326 | elems: Vec<WaylandSurfaceRenderElement<VulkanRenderer>>, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 327 | (at, size): ((i32, i32), (i32, i32)), |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 328 | effect: Effect, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 329 | ) { |
480ead9a41f5 | atagen | 2026-10-17 02:54 UTC | 330 | let Some(root) = crate::wm::root(window) else { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 331 | return; |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 332 | }; |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 333 | let root_id = Id::from_wayland_resource(&root); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 334 | let crop = Rectangle::new(at.into(), size.into()); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 335 | let context = renderer.context_id(); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 336 | let root_tex = with_renderer_surface_state(&root, |s| { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 337 | s.texture::<VulkanTexture>(context.clone()) |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 338 | .cloned() |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 339 | .map(|t| (t, s.buffer_scale(), s.buffer_transform())) |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 340 | }) |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 341 | .flatten(); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 342 | out.extend( |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 343 | elems |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 344 | .into_iter() |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 345 | .filter(|e| root_tex.is_none() || *e.id() != root_id) |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 346 | .filter_map(|e| CropRenderElement::from_element(e, 1.0, crop)) |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 347 | .map(Draw::Cropped), |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 348 | ); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 349 | if let Some((tex, scale, transform)) = root_tex { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 350 | let geo = window.geometry(); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 351 | let src = Rectangle::new( |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 352 | geo.loc.to_f64(), |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 353 | (f64::from(size.0), f64::from(size.1)).into(), |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 354 | ); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 355 | out.push(Draw::Effect(EffectElement::new( |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 356 | &tex, scale, transform, src, at, size, effect, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 357 | ))); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 358 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 359 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 360 | |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 361 | /// A texture drawn through the transition hook in the effect shader. |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 362 | pub struct EffectElement { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 363 | id: Id, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 364 | texture: VulkanTexture, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 365 | src: Rectangle<f64, Buffer>, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 366 | geometry: Rectangle<i32, Physical>, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 367 | transform: Transform, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 368 | effect: Effect, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 369 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 370 | |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 371 | impl EffectElement { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 372 | fn new( |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 373 | texture: &VulkanTexture, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 374 | scale: i32, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 375 | transform: Transform, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 376 | src: Rectangle<f64, Logical>, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 377 | at: (i32, i32), |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 378 | size: (i32, i32), |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 379 | effect: Effect, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 380 | ) -> Self { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 381 | let bounds = texture.size().to_logical(scale, transform).to_f64(); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 382 | Self { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 383 | id: Id::new(), |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 384 | texture: texture.clone(), |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 385 | src: src.to_buffer(f64::from(scale), transform, &bounds), |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 386 | geometry: Rectangle::new(at.into(), size.into()), |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 387 | transform, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 388 | effect, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 389 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 390 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 391 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 392 | |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 393 | impl Element for EffectElement { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 394 | fn id(&self) -> &Id { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 395 | &self.id |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 396 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 397 | |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 398 | fn current_commit(&self) -> CommitCounter { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 399 | CommitCounter::default() |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 400 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 401 | |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 402 | fn src(&self) -> Rectangle<f64, Buffer> { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 403 | self.src |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 404 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 405 | |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 406 | fn geometry(&self, _scale: Scale<f64>) -> Rectangle<i32, Physical> { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 407 | self.geometry |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 408 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 409 | |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 410 | fn transform(&self) -> Transform { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 411 | self.transform |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 412 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 413 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 414 | |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 415 | impl RenderElement<VulkanRenderer> for EffectElement { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 416 | fn draw( |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 417 | &self, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 418 | frame: &mut VulkanFrame<'_, '_>, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 419 | src: Rectangle<f64, Buffer>, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 420 | dst: Rectangle<i32, Physical>, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 421 | damage: &[Rectangle<i32, Physical>], |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 422 | _opaque_regions: &[Rectangle<i32, Physical>], |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 423 | _cache: Option<&UserDataMap>, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 424 | ) -> Result<(), monolith_render_vk::Error> { |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 425 | frame.render_effect( |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 426 | &self.texture, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 427 | src, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 428 | dst, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 429 | damage, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 430 | self.transform, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 431 | 1.0, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 432 | self.effect, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 433 | ) |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 434 | } |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 435 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 436 | |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 437 | fn ghost_element(frame: &LastFrame, r: Rect, effect: Effect, deco: &Decoration) -> Option<Draw> { |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 438 | let (at, size) = pixels(r)?; |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 439 | let src = Rectangle::new( |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 440 | frame.geometry.loc.to_f64(), |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 441 | (f64::from(size.0), f64::from(size.1)).into(), |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 442 | ); |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 443 | let ghost = Draw::Effect(EffectElement::new( |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 444 | &frame.texture, |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 445 | frame.scale, |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 446 | frame.transform, |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 447 | src, |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 448 | at, |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 449 | size, |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 450 | effect, |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 451 | )); |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 452 | Some(match corner_clip(r, deco) { |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 453 | Some(clip) => Draw::Clipped(ClipElement::new(ghost, clip)), |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 454 | None => ghost, |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 455 | }) |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 456 | } |
618afeb78754 | atagen | 2026-10-17 13:40 UTC | 457 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 458 | /// The rounded outline of a framed window at `r`, if it has one. |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 459 | fn corner_clip(r: Rect, deco: &Decoration) -> Option<Clip> { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 460 | let radii = deco.radius.0; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 461 | if radii == [0; 4] { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 462 | return None; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 463 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 464 | let (at, size) = pixels(r)?; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 465 | Some(Clip { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 466 | rect: Rectangle::new(at.into(), size.into()), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 467 | radii: radii.map(|r| r as f32), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 468 | ring: 0.0, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 469 | }) |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 470 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 471 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 472 | /// Clips what a window drew from `first` on, leaving its popups whole. |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 473 | fn clip_window(out: &mut [Draw], first: usize, window: &Window, clip: Clip) { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 474 | let mut popups = std::collections::HashSet::new(); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 475 | if let Some(root) = crate::wm::root(window) { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 476 | for (popup, _) in PopupManager::popups_for_surface(&root) { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 477 | with_surface_tree_downward( |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 478 | popup.wl_surface(), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 479 | (), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 480 | |_, _, _| TraversalAction::DoChildren(()), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 481 | |s, _, _| { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 482 | popups.insert(Id::from_wayland_resource(s)); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 483 | }, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 484 | |_, _, _| true, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 485 | ); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 486 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 487 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 488 | for draw in &mut out[first..] { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 489 | if !popups.contains(draw.id()) { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 490 | wrap(draw, clip); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 491 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 492 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 493 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 494 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 495 | /// Turns the border strips from `first` on into a ring around the window's |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 496 | /// rounded outline. |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 497 | fn ring_borders(out: &mut [Draw], first: usize, window: Clip, b: i32) { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 498 | let grow = |r: f32| if r > 0.0 { r + b as f32 } else { 0.0 }; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 499 | let ring = Clip { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 500 | rect: Rectangle::new( |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 501 | window.rect.loc - Point::from((b, b)), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 502 | window.rect.size + Size::from((2 * b, 2 * b)), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 503 | ), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 504 | radii: window.radii.map(grow), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 505 | ring: b as f32, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 506 | }; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 507 | for draw in &mut out[first..] { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 508 | wrap(draw, ring); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 509 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 510 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 511 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 512 | fn wrap(draw: &mut Draw, clip: Clip) { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 513 | let inner = std::mem::replace(draw, Draw::Clipped(ClipElement::empty())); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 514 | *draw = Draw::Clipped(ClipElement::new(inner, clip)); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 515 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 516 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 517 | /// Another element drawn through a rounded clip. |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 518 | pub struct ClipElement { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 519 | inner: Option<Box<Draw>>, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 520 | clip: Clip, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 521 | id: Id, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 522 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 523 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 524 | impl ClipElement { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 525 | fn new(inner: Draw, clip: Clip) -> Self { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 526 | Self { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 527 | id: inner.id().clone(), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 528 | inner: Some(Box::new(inner)), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 529 | clip, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 530 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 531 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 532 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 533 | /// A placeholder while an element is moved into its wrapper. |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 534 | fn empty() -> Self { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 535 | Self { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 536 | inner: None, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 537 | clip: Clip { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 538 | rect: Rectangle::default(), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 539 | radii: [0.0; 4], |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 540 | ring: 0.0, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 541 | }, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 542 | id: Id::new(), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 543 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 544 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 545 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 546 | fn inner(&self) -> &Draw { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 547 | self.inner.as_deref().expect("only placeholders are empty") |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 548 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 549 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 550 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 551 | impl Element for ClipElement { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 552 | fn id(&self) -> &Id { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 553 | &self.id |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 554 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 555 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 556 | fn current_commit(&self) -> CommitCounter { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 557 | self.inner().current_commit() |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 558 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 559 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 560 | fn location(&self, scale: Scale<f64>) -> Point<i32, Physical> { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 561 | self.inner().location(scale) |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 562 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 563 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 564 | fn src(&self) -> Rectangle<f64, Buffer> { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 565 | self.inner().src() |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 566 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 567 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 568 | fn transform(&self) -> Transform { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 569 | self.inner().transform() |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 570 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 571 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 572 | fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 573 | self.inner().geometry(scale) |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 574 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 575 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 576 | fn damage_since( |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 577 | &self, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 578 | scale: Scale<f64>, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 579 | commit: Option<CommitCounter>, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 580 | ) -> DamageSet<i32, Physical> { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 581 | self.inner().damage_since(scale, commit) |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 582 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 583 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 584 | /// A ring hides nothing behind it, and a rounded window hides nothing |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 585 | /// behind its corners. |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 586 | fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 587 | if self.clip.ring > 0.0 { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 588 | return OpaqueRegions::default(); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 589 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 590 | let loc = self.geometry(scale).loc; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 591 | let c = self.clip.rect; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 592 | let (right, bottom) = (c.loc.x + c.size.w, c.loc.y + c.size.h); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 593 | let [tl, tr, br, bl] = self.clip.radii.map(|r| r.ceil() as i32); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 594 | let corners = [ |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 595 | Rectangle::new(c.loc, (tl, tl).into()), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 596 | Rectangle::new((right - tr, c.loc.y).into(), (tr, tr).into()), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 597 | Rectangle::new((right - br, bottom - br).into(), (br, br).into()), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 598 | Rectangle::new((c.loc.x, bottom - bl).into(), (bl, bl).into()), |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 599 | ] |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 600 | .map(|mut r| { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 601 | r.loc -= loc; |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 602 | r |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 603 | }); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 604 | Rectangle::subtract_rects_many(self.inner().opaque_regions(scale), corners) |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 605 | .into_iter() |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 606 | .collect() |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 607 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 608 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 609 | fn alpha(&self) -> f32 { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 610 | self.inner().alpha() |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 611 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 612 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 613 | fn kind(&self) -> Kind { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 614 | self.inner().kind() |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 615 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 616 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 617 | |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 618 | impl RenderElement<VulkanRenderer> for ClipElement { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 619 | fn draw( |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 620 | &self, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 621 | frame: &mut VulkanFrame<'_, '_>, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 622 | src: Rectangle<f64, Buffer>, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 623 | dst: Rectangle<i32, Physical>, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 624 | damage: &[Rectangle<i32, Physical>], |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 625 | opaque_regions: &[Rectangle<i32, Physical>], |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 626 | cache: Option<&UserDataMap>, |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 627 | ) -> Result<(), monolith_render_vk::Error> { |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 628 | frame.set_clip(Some(self.clip)); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 629 | let drawn = self |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 630 | .inner() |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 631 | .draw(frame, src, dst, damage, opaque_regions, cache); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 632 | frame.set_clip(None); |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 633 | drawn |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 634 | } |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 635 | } |