use smithay_client_toolkit::compositor::Region;
use smithay_client_toolkit::reexports::client::{
Connection, Dispatch, QueueHandle, WEnum, delegate_noop,
};
use wayland_protocols::ext::background_effect::v1::client::{
ext_background_effect_manager_v1::{
self, Capability as BgCapability, ExtBackgroundEffectManagerV1,
},
ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1,
};
use super::wayland::WaylandState;
use crate::region::RegionRect;
use crate::surface::SurfaceId;
pub struct Backdrop {
pub(super) bg_effect_manager: Option<ExtBackgroundEffectManagerV1>,
pub(super) bg_effect_supports_blur: bool,
}
impl Backdrop {
pub(super) fn new(bg_effect_manager: Option<ExtBackgroundEffectManagerV1>) -> Self {
Self {
bg_effect_manager,
bg_effect_supports_blur: false,
}
}
}
impl WaylandState {
pub(crate) fn supports_blur_region(&self) -> bool {
self.backdrop.bg_effect_supports_blur && self.backdrop.bg_effect_manager.is_some()
}
pub(crate) fn has_published_blur(&self, id: SurfaceId) -> bool {
self.surfaces
.get(&id)
.and_then(|s| s.blur_region.as_ref())
.is_some_and(|rects| !rects.is_empty())
}
pub(crate) fn take_blur_resync(&mut self, id: SurfaceId) -> bool {
if !self.supports_blur_region() {
return false;
}
self.surfaces
.get_mut(&id)
.is_some_and(|s| std::mem::take(&mut s.blur_resync_owed))
}
pub(crate) fn sync_blur_region(&mut self, id: SurfaceId, rects: Vec<RegionRect>, commit: bool) {
if !self.backdrop.bg_effect_supports_blur {
return;
}
let Some(manager) = self.backdrop.bg_effect_manager.clone() else {
return;
};
let Some(surface_state) = self.surfaces.get_mut(&id) else {
return;
};
surface_state.blur_resync_owed = false;
if rects.is_empty()
&& surface_state.blur_region.is_none()
&& surface_state.bg_effect_surface.is_none()
{
return;
}
if surface_state.blur_region.as_deref() == Some(rects.as_slice()) {
return;
}
let effect = surface_state.bg_effect_surface.get_or_insert_with(|| {
manager.get_background_effect(&surface_state.wl_surface, &self.qh, ())
});
let Ok(region) = Region::new(&self.compositor_state) else {
log::warn!("Failed to create wl_region for blur");
return;
};
for r in &rects {
region.add(r.x, r.y, r.width, r.height);
}
effect.set_blur_region(Some(region.wl_region()));
log::debug!(
"Surface {:?} blur region set to {} rect(s)",
id,
rects.len()
);
surface_state.blur_region = Some(rects);
if commit {
surface_state.wl_surface.commit();
}
}
}
impl Dispatch<ExtBackgroundEffectManagerV1, ()> for WaylandState {
fn event(
state: &mut Self,
_proxy: &ExtBackgroundEffectManagerV1,
event: ext_background_effect_manager_v1::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Self>,
) {
if let ext_background_effect_manager_v1::Event::Capabilities { flags } = event {
let blur = match flags {
WEnum::Value(c) => c.contains(BgCapability::Blur),
WEnum::Unknown(_) => false,
};
if blur == state.backdrop.bg_effect_supports_blur {
return;
}
log::info!(
"Compositor blur capability {}",
if blur { "available" } else { "lost" }
);
state.backdrop.bg_effect_supports_blur = blur;
crate::compositor::set_blur_capability(blur);
for surface_state in state.surfaces.values_mut() {
surface_state.blur_region = None;
surface_state.blur_resync_owed = true;
}
crate::jobs::wake_loop();
}
}
}
delegate_noop!(WaylandState: ignore ExtBackgroundEffectSurfaceV1);