use std::any::Any;
use winit::raw_window_handle::{
HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle,
};
use winit::window::Window;
pub(crate) fn request(window: &Window) -> Option<Box<dyn Any>> {
let display = window.display_handle().ok()?.as_raw();
let handle = window.window_handle().ok()?.as_raw();
match (display, handle) {
(RawDisplayHandle::Wayland(d), RawWindowHandle::Wayland(w)) => {
unsafe { wayland::request(d.display.as_ptr(), w.surface.as_ptr()) }
}
(_, RawWindowHandle::Xlib(h)) => x11::request(h.window as u32),
(_, RawWindowHandle::Xcb(h)) => x11::request(h.window.get()),
_ => None,
}
}
pub(crate) fn composited(window: &Window) -> bool {
match window.window_handle().map(|h| h.as_raw()) {
Ok(RawWindowHandle::Xlib(_)) | Ok(RawWindowHandle::Xcb(_)) => x11::composited(),
_ => true,
}
}
mod wayland {
use std::any::Any;
use std::ffi::c_void;
use std::sync::Mutex;
use wayland_backend::client::{Backend, ObjectId};
use wayland_client::globals::{GlobalListContents, registry_queue_init};
use wayland_client::protocol::{
wl_compositor::WlCompositor, wl_region::WlRegion, wl_registry::WlRegistry,
wl_surface::WlSurface,
};
use wayland_client::{
Connection, Dispatch, EventQueue, Proxy, QueueHandle, WEnum, delegate_noop,
};
use wayland_protocols::ext::background_effect::v1::client::{
ext_background_effect_manager_v1::{self, ExtBackgroundEffectManagerV1},
ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1,
};
use wayland_protocols_plasma::blur::client::{
org_kde_kwin_blur::OrgKdeKwinBlur, org_kde_kwin_blur_manager::OrgKdeKwinBlurManager,
};
#[derive(Default)]
pub(super) struct State {
blur: bool,
}
impl Dispatch<WlRegistry, GlobalListContents> for State {
fn event(
_: &mut Self,
_: &WlRegistry,
_: wayland_client::protocol::wl_registry::Event,
_: &GlobalListContents,
_: &Connection,
_: &QueueHandle<Self>,
) {
}
}
impl Dispatch<ExtBackgroundEffectManagerV1, ()> for State {
fn event(
state: &mut Self,
_: &ExtBackgroundEffectManagerV1,
event: ext_background_effect_manager_v1::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
if let ext_background_effect_manager_v1::Event::Capabilities { flags } = event {
state.blur = match flags {
WEnum::Value(c) => {
c.contains(ext_background_effect_manager_v1::Capability::Blur)
}
WEnum::Unknown(bits) => bits & 1 != 0,
};
}
}
}
delegate_noop!(State: WlCompositor);
delegate_noop!(State: WlRegion);
delegate_noop!(State: ExtBackgroundEffectSurfaceV1);
delegate_noop!(State: OrgKdeKwinBlurManager);
delegate_noop!(State: OrgKdeKwinBlur);
enum Effect {
Ext(ExtBackgroundEffectSurfaceV1),
Kde(OrgKdeKwinBlur),
}
struct Kept {
conn: Connection,
effect: Effect,
}
impl Drop for Kept {
fn drop(&mut self) {
match &self.effect {
Effect::Ext(e) => e.destroy(),
Effect::Kde(b) => b.release(),
}
let _ = self.conn.flush();
}
}
struct Shared {
display: usize,
conn: Connection,
queue: EventQueue<State>,
state: State,
compositor: Option<WlCompositor>,
ext: Option<ExtBackgroundEffectManagerV1>,
kde: Option<OrgKdeKwinBlurManager>,
}
static SHARED: Mutex<Option<Shared>> = Mutex::new(None);
impl Shared {
unsafe fn bind(display: *mut c_void) -> Option<Self> {
let conn =
Connection::from_backend(unsafe { Backend::from_foreign_display(display.cast()) });
let (globals, mut queue) = registry_queue_init::<State>(&conn).ok()?;
let qh = queue.handle();
let mut state = State::default();
let ext = globals
.bind::<ExtBackgroundEffectManagerV1, _, _>(&qh, 1..=1, ())
.ok();
if ext.is_some() {
queue.roundtrip(&mut state).ok()?;
}
let compositor = ext
.as_ref()
.and_then(|_| globals.bind::<WlCompositor, _, _>(&qh, 1..=4, ()).ok());
let kde = globals
.bind::<OrgKdeKwinBlurManager, _, _>(&qh, 1..=1, ())
.ok();
Some(Shared {
display: display as usize,
conn,
queue,
state,
compositor,
ext,
kde,
})
}
}
pub(super) unsafe fn request(
display: *mut c_void,
surface: *mut c_void,
) -> Option<Box<dyn Any>> {
let mut shared = SHARED.lock().unwrap_or_else(|e| e.into_inner());
if shared
.as_ref()
.is_none_or(|s| s.display != display as usize)
{
if let Some(old) = shared.take() {
std::mem::forget(old);
}
*shared = unsafe { Shared::bind(display) };
}
let shared = shared.as_mut()?;
let conn = shared.conn.clone();
let qh = shared.queue.handle();
let _ = shared.queue.dispatch_pending(&mut shared.state);
let id = unsafe { ObjectId::from_ptr(WlSurface::interface(), surface.cast()) }.ok()?;
let surface = WlSurface::from_id(&conn, id).ok()?;
if shared.state.blur
&& let (Some(manager), Some(compositor)) = (&shared.ext, &shared.compositor)
{
let effect = manager.get_background_effect(&surface, &qh, ());
let region = compositor.create_region(&qh, ());
region.add(0, 0, 1 << 16, 1 << 16);
effect.set_blur_region(Some(®ion));
region.destroy();
conn.flush().ok()?;
return Some(Box::new(Kept {
conn,
effect: Effect::Ext(effect),
}));
}
if let Some(manager) = &shared.kde {
let blur = manager.create(&surface, &qh, ());
blur.set_region(None);
blur.commit();
conn.flush().ok()?;
return Some(Box::new(Kept {
conn,
effect: Effect::Kde(blur),
}));
}
None
}
}
mod x11 {
use std::any::Any;
use x11rb::connection::Connection as _;
use x11rb::protocol::xproto::{AtomEnum, ConnectionExt as _, PropMode};
use x11rb::wrapper::ConnectionExt as _;
pub(super) fn request(window: u32) -> Option<Box<dyn Any>> {
let (conn, screen) = x11rb::connect(None).ok()?;
let root = conn.setup().roots.get(screen)?.root;
let atom = |name: &[u8]| Some(conn.intern_atom(false, name).ok()?.reply().ok()?.atom);
let supported = atom(b"_NET_SUPPORTED")?;
let blur = atom(b"_KDE_NET_WM_BLUR_BEHIND_REGION")?;
let listed = conn
.get_property(false, root, supported, AtomEnum::ATOM, 0, 4096)
.ok()?
.reply()
.ok()?;
if !listed.value32()?.any(|a| a == blur) {
return None;
}
conn.change_property32(PropMode::REPLACE, window, blur, AtomEnum::CARDINAL, &[])
.ok()?;
conn.flush().ok()?;
Some(Box::new(()))
}
pub(super) fn composited() -> bool {
let Ok((conn, screen)) = x11rb::connect(None) else {
return false;
};
let name = format!("_NET_WM_CM_S{screen}");
let Some(atom) = conn
.intern_atom(false, name.as_bytes())
.ok()
.and_then(|c| c.reply().ok())
.map(|r| r.atom)
else {
return false;
};
conn.get_selection_owner(atom)
.ok()
.and_then(|c| c.reply().ok())
.is_some_and(|r| r.owner != x11rb::NONE)
}
}