Skip to content

Commit c5f8a70

Browse files
committed
Implement ext-background-effect for popups
1 parent 219e29c commit c5f8a70

File tree

8 files changed

+115
-6
lines changed

8 files changed

+115
-6
lines changed

docs/wiki/Window-Effects.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,19 @@ Non-xray effects are currently experimental because they have some known limitat
6262

6363
- They disappear during window open/close animations and while dragging a tiled window.
6464
Fixing this requries a refactor to the niri rendering code to defer offscreen rendering, and possibly other refactors.
65+
66+
### Implementation notes
67+
68+
The `ext-background-effect` protocol supports any wl_surface.
69+
We currently implement it only for toplevels, layer surfaces, and pop-ups, which should cover the vast majority of what's actually used by applications.
70+
71+
For pop-ups, effects default to *non-xray* because pop-ups generally appear on top of windows.
72+
73+
In particular, the following surface types don't support `ext-background-effect`.
74+
They can be implemented as the need arises.
75+
76+
- Subsurfaces. Would require implementing `clip-to-geometry` support for background effects.
77+
- Lock surfaces. Not useful as it would just show our red locked session background.
78+
- Cursor and drag-and-drop icon.
79+
The main challenge here will be screencasts where the cursor is rendered separately.
80+
This is problematic because non-xray effects require rendering the whole scene in one go rather than separately.

src/layer/mapped.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,8 @@ impl MappedLayer {
255255

256256
pub fn render_popups<R: NiriRenderer>(
257257
&self,
258-
ctx: RenderCtx<R>,
258+
mut ctx: RenderCtx<R>,
259+
ns: Option<usize>,
259260
location: Point<f64, Logical>,
260261
push: &mut dyn FnMut(LayerSurfaceRenderElement<R>),
261262
) {
@@ -269,17 +270,28 @@ impl MappedLayer {
269270

270271
let surface = self.surface.wl_surface();
271272
for (popup, offset) in PopupManager::popups_for_surface(surface) {
273+
let surface = popup.wl_surface();
272274
let surface_loc = location + (offset - popup.geometry().loc).to_f64();
273275

274276
push_elements_from_surface_tree(
275277
ctx.renderer,
276-
popup.wl_surface(),
278+
surface,
277279
surface_loc.to_physical_precise_round(scale),
278280
scale,
279281
alpha,
280282
Kind::ScanoutCandidate,
281283
&mut |elem| push(elem.into()),
282284
);
285+
286+
background_effect::render_for_surface(
287+
surface,
288+
ctx.as_gles(),
289+
ns,
290+
self.blur_config,
291+
surface_loc,
292+
scale,
293+
&mut |elem| push(elem.into()),
294+
);
283295
}
284296
}
285297
}

src/layout/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ niri_render_elements! {
114114
LayoutElementRenderElement<R> => {
115115
Wayland = WaylandSurfaceRenderElement<R>,
116116
SolidColor = SolidColorRenderElement,
117+
BackgroundEffect = BackgroundEffectElement,
117118
}
118119
}
119120

src/layout/tile.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,12 @@ impl<W: LayoutElement> Tile<W> {
12051205
// Otherwise, render the solid color as is.
12061206
LayoutElementRenderElement::SolidColor(elem).into()
12071207
}
1208+
elem @ LayoutElementRenderElement::BackgroundEffect(_) => {
1209+
// This is only used on popups for now. If subsurface blur is implemented, this
1210+
// will need to be handled somehow.
1211+
error!("background effect clipping is unimplemented");
1212+
elem.into()
1213+
}
12081214
};
12091215

12101216
if clip_to_geometry && clip_shader.is_some() {

src/niri.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4522,14 +4522,14 @@ impl Niri {
45224522
fn render_layer_popups<R: NiriRenderer>(
45234523
&self,
45244524
mut ctx: RenderCtx<R>,
4525-
_ns: Option<usize>,
4525+
ns: Option<usize>,
45264526
layer_map: &LayerMap,
45274527
layer: Layer,
45284528
for_backdrop: bool,
45294529
push: &mut dyn FnMut(LayerSurfaceRenderElement<R>),
45304530
) {
45314531
for (mapped, geo) in self.layers_in_render_order(layer_map, layer, for_backdrop) {
4532-
mapped.render_popups(ctx.r(), geo.loc.to_f64(), push);
4532+
mapped.render_popups(ctx.r(), ns, geo.loc.to_f64(), push);
45334533
}
45344534
}
45354535

src/render_helpers/background_effect.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,60 @@ pub fn render_for_tile(
330330
background_effect.render(ctx, ns, params, xray_pos, push);
331331
});
332332
}
333+
334+
pub fn render_for_surface(
335+
surface: &WlSurface,
336+
ctx: RenderCtx<GlesRenderer>,
337+
ns: Option<usize>,
338+
blur_config: niri_config::Blur,
339+
location: Point<f64, Logical>,
340+
scale: Scale<f64>,
341+
push: &mut dyn FnMut(BackgroundEffectElement),
342+
) {
343+
let blur_region = with_states(surface, get_cached_blur_region);
344+
let Some(rects) = blur_region else {
345+
return;
346+
};
347+
if rects.is_empty() {
348+
return;
349+
}
350+
351+
with_states(surface, |states| {
352+
let mut main_surface_geo = surface_geo(states).unwrap_or_default().to_f64();
353+
main_surface_geo.loc += location;
354+
355+
let subregion = TransformedRegion {
356+
rects,
357+
scale: Scale::from(1.),
358+
offset: main_surface_geo.loc,
359+
};
360+
361+
let geometry = main_surface_geo
362+
.to_physical_precise_round(scale)
363+
.to_logical(scale);
364+
365+
let params = RenderParams {
366+
geometry,
367+
subregion: Some(subregion),
368+
clip: None,
369+
scale: scale.x,
370+
};
371+
372+
let background_effect = SurfaceBackgroundEffect::get(states);
373+
let mut background_effect = background_effect.0.lock().unwrap();
374+
375+
background_effect.update_config(blur_config);
376+
background_effect.update_render_elements(
377+
CornerRadius::default(),
378+
niri_config::BackgroundEffect {
379+
// We don't do xray on popups.
380+
xray: Some(false),
381+
..Default::default()
382+
},
383+
// We always have a blur region.
384+
true,
385+
);
386+
387+
background_effect.render(ctx, ns, params, XrayPos::default(), push);
388+
});
389+
}

src/ui/mru.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,12 @@ impl Thumbnail {
421421
// Otherwise, render the solid color as is.
422422
LayoutElementRenderElement::SolidColor(elem).into()
423423
}
424+
elem @ LayoutElementRenderElement::BackgroundEffect(_) => {
425+
// This is only used on popups for now. If subsurface blur is implemented, this
426+
// will need to be handled somehow.
427+
error!("background effect clipping is unimplemented");
428+
elem.into()
429+
}
424430
};
425431

426432
let downscale = move |elem| {

src/window/mapped.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ impl LayoutElement for Mapped {
658658

659659
fn render_popups<R: NiriRenderer>(
660660
&self,
661-
ctx: RenderCtx<R>,
661+
mut ctx: RenderCtx<R>,
662662
location: Point<f64, Logical>,
663663
scale: Scale<f64>,
664664
alpha: f32,
@@ -670,17 +670,28 @@ impl LayoutElement for Mapped {
670670

671671
let surface = self.toplevel().wl_surface();
672672
for (popup, offset) in PopupManager::popups_for_surface(surface) {
673+
let surface = popup.wl_surface();
673674
let surface_loc = location + (offset - popup.geometry().loc).to_f64();
674675

675676
push_elements_from_surface_tree(
676677
ctx.renderer,
677-
popup.wl_surface(),
678+
surface,
678679
surface_loc.to_physical_precise_round(scale),
679680
scale,
680681
alpha,
681682
Kind::ScanoutCandidate,
682683
&mut |elem| push(elem.into()),
683684
);
685+
686+
background_effect::render_for_surface(
687+
surface,
688+
ctx.as_gles(),
689+
None,
690+
self.blur_config,
691+
surface_loc,
692+
scale,
693+
&mut |elem| push(elem.into()),
694+
);
684695
}
685696
}
686697

0 commit comments

Comments
 (0)