use super::{BackdropBackend, BackdropError, Request};
use argui_core::Rect;
use wayland_client::{
Connection, Dispatch, Proxy, QueueHandle, WEnum, delegate_noop,
protocol::{wl_compositor, wl_region, wl_registry, wl_surface},
};
use wayland_protocols::ext::background_effect::v1::client::{
ext_background_effect_manager_v1 as ext_manager,
ext_background_effect_surface_v1 as ext_surface,
};
use wayland_protocols_plasma::blur::client::{
org_kde_kwin_blur as kde_blur, org_kde_kwin_blur_manager as kde_manager,
};
use winit::raw_window_handle::{RawDisplayHandle, RawWindowHandle};
#[derive(Default)]
struct Globals {
compositor: Option<wl_compositor::WlCompositor>,
ext: Option<ext_manager::ExtBackgroundEffectManagerV1>,
kde: Option<kde_manager::OrgKdeKwinBlurManager>,
blur: bool,
ext_name: u32,
kde_name: u32,
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl Dispatch<wl_registry::WlRegistry, ()> for Globals {
fn event(
state: &mut Self,
registry: &wl_registry::WlRegistry,
event: wl_registry::Event,
_: &(),
_: &Connection,
queue: &QueueHandle<Self>,
) {
match event {
wl_registry::Event::Global {
name,
interface,
version,
} => match interface.as_str() {
"wl_compositor" => {
state.compositor = Some(registry.bind(name, version.min(4), queue, ()))
}
"ext_background_effect_manager_v1" => {
state.ext_name = name;
state.ext = Some(registry.bind(name, 1, queue, ()));
}
"org_kde_kwin_blur_manager" => {
state.kde_name = name;
state.kde = Some(registry.bind(name, 1, queue, ()));
}
_ => (),
},
wl_registry::Event::GlobalRemove { name } => {
if state.ext_name == name {
state.blur = false;
}
if state.kde_name == name {
state.kde = None;
}
}
_ => (),
}
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl Dispatch<ext_manager::ExtBackgroundEffectManagerV1, ()> for Globals {
fn event(
state: &mut Self,
_: &ext_manager::ExtBackgroundEffectManagerV1,
event: ext_manager::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
if let ext_manager::Event::Capabilities { flags } = event {
state.blur = matches!(flags, WEnum::Value(flags) if flags.contains(ext_manager::Capability::Blur));
}
}
}
delegate_noop!(Globals: ignore wl_compositor::WlCompositor);
delegate_noop!(Globals: ignore wl_region::WlRegion);
delegate_noop!(Globals: ignore ext_surface::ExtBackgroundEffectSurfaceV1);
delegate_noop!(Globals: ignore kde_manager::OrgKdeKwinBlurManager);
delegate_noop!(Globals: ignore kde_blur::OrgKdeKwinBlur);
pub(in crate::desktop_backdrop) struct Backend {
connection: Connection,
queue: wayland_client::EventQueue<Globals>,
globals: Globals,
surface: wl_surface::WlSurface,
ext: Option<ext_surface::ExtBackgroundEffectSurfaceV1>,
kde: Option<kde_blur::OrgKdeKwinBlur>,
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl BackdropBackend for Backend {
#[allow(unsafe_code)]
fn new(display: RawDisplayHandle, window: RawWindowHandle) -> Result<Self, BackdropError> {
let (RawDisplayHandle::Wayland(display), RawWindowHandle::Wayland(window)) =
(display, window)
else {
return Err(BackdropError::Unsupported);
};
let backend = unsafe {
wayland_backend::client::Backend::from_foreign_display(display.display.as_ptr().cast())
};
let connection = Connection::from_backend(backend);
let id = unsafe {
wayland_backend::client::ObjectId::from_ptr(
wl_surface::WlSurface::interface(),
window.surface.as_ptr().cast(),
)
}
.map_err(error)?;
let surface = wl_surface::WlSurface::from_id(&connection, id).map_err(error)?;
let mut queue = connection.new_event_queue();
let handle = queue.handle();
connection.display().get_registry(&handle, ());
let mut globals = Globals::default();
queue.roundtrip(&mut globals).map_err(error)?;
queue.roundtrip(&mut globals).map_err(error)?;
if globals.compositor.is_none() || (globals.ext.is_none() && globals.kde.is_none()) {
return Err(BackdropError::Unsupported);
}
let ext = globals
.ext
.as_ref()
.map(|manager| manager.get_background_effect(&surface, &handle, ()));
Ok(Self {
connection,
queue,
globals,
surface,
ext,
kde: None,
})
}
fn available(&mut self) -> Result<bool, BackdropError> {
self.queue
.dispatch_pending(&mut self.globals)
.map_err(error)?;
Ok(if self.ext.is_some() {
self.globals.blur
} else {
self.globals.kde.is_some()
})
}
fn apply(&mut self, regions: &[Rect], _: &Request) -> Result<(), BackdropError> {
let handle = self.queue.handle();
let region = self
.globals
.compositor
.as_ref()
.ok_or(BackdropError::Unsupported)?
.create_region(&handle, ());
for rect in regions {
region.add(
rect.origin.x as i32,
rect.origin.y as i32,
rect.size.width as i32,
rect.size.height as i32,
);
}
if let Some(effect) = &self.ext {
effect.set_blur_region(Some(®ion));
} else if let Some(manager) = &self.globals.kde {
if regions.is_empty() {
manager.unset(&self.surface);
if let Some(effect) = self.kde.take() {
effect.release();
}
} else {
let effect = self
.kde
.get_or_insert_with(|| manager.create(&self.surface, &handle, ()));
effect.set_region(Some(®ion));
effect.commit();
}
}
region.destroy();
self.connection.flush().map_err(error)
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl Drop for Backend {
fn drop(&mut self) {
if let Some(effect) = self.ext.take() {
effect.destroy();
}
if let Some(effect) = self.kde.take() {
if let Some(manager) = &self.globals.kde {
manager.unset(&self.surface);
}
effect.release();
}
let _ = self.connection.flush();
}
}
fn error(error: impl std::fmt::Display) -> BackdropError {
BackdropError::Platform(error.to_string())
}