argui-platform 0.2.1

Native platform integrations for Argui applications
Documentation
use super::{BackdropBackend, BackdropError, Request};
use argui_core::Rect;
use winit::raw_window_handle::{RawDisplayHandle, RawWindowHandle};
use x11rb::{
    connection::Connection,
    protocol::{
        Event,
        xproto::{AtomEnum, ChangeWindowAttributesAux, ConnectionExt, EventMask, PropMode},
    },
    rust_connection::RustConnection,
    wrapper::ConnectionExt as _,
};

pub(in crate::desktop_backdrop) struct Backend {
    connection: RustConnection,
    window: u32,
    root: u32,
    atom: u32,
    available: bool,
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl BackdropBackend for Backend {
    fn new(_: RawDisplayHandle, window: RawWindowHandle) -> Result<Self, BackdropError> {
        let window = match window {
            RawWindowHandle::Xlib(handle) => handle.window as u32,
            RawWindowHandle::Xcb(handle) => handle.window.get(),
            _ => return Err(BackdropError::Unsupported),
        };
        let (connection, screen) = x11rb::connect(None).map_err(error)?;
        let root = connection.setup().roots[screen].root;
        let atom = connection
            .intern_atom(false, b"_KDE_NET_WM_BLUR_BEHIND_REGION")
            .map_err(error)?
            .reply()
            .map_err(error)?
            .atom;
        connection
            .change_window_attributes(
                root,
                &ChangeWindowAttributesAux::new().event_mask(EventMask::PROPERTY_CHANGE),
            )
            .map_err(error)?
            .check()
            .map_err(error)?;
        let available = connection
            .list_properties(root)
            .map_err(error)?
            .reply()
            .map_err(error)?
            .atoms
            .contains(&atom);
        Ok(Self {
            connection,
            window,
            root,
            atom,
            available,
        })
    }
    fn available(&mut self) -> Result<bool, BackdropError> {
        while let Some(event) = self.connection.poll_for_event().map_err(error)? {
            if matches!(event, Event::PropertyNotify(event) if event.window == self.root && event.atom == self.atom)
            {
                self.available = self
                    .connection
                    .list_properties(self.root)
                    .map_err(error)?
                    .reply()
                    .map_err(error)?
                    .atoms
                    .contains(&self.atom);
            }
        }
        Ok(self.available)
    }
    fn apply(&mut self, regions: &[Rect], request: &Request) -> Result<(), BackdropError> {
        if regions.is_empty() {
            self.connection
                .delete_property(self.window, self.atom)
                .map_err(error)?
                .check()
                .map_err(error)?;
        } else {
            let data: Vec<_> = regions
                .iter()
                .flat_map(|rect| {
                    [
                        (rect.origin.x * request.scale).floor() as i32 as u32,
                        (rect.origin.y * request.scale).floor() as i32 as u32,
                        (rect.size.width * request.scale).ceil() as u32,
                        (rect.size.height * request.scale).ceil() as u32,
                    ]
                })
                .collect();
            self.connection
                .change_property32(
                    PropMode::REPLACE,
                    self.window,
                    self.atom,
                    AtomEnum::CARDINAL,
                    &data,
                )
                .map_err(error)?
                .check()
                .map_err(error)?;
        }
        self.connection.flush().map_err(error)
    }
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl Drop for Backend {
    fn drop(&mut self) {
        let _ = self.connection.delete_property(self.window, self.atom);
        let _ = self.connection.flush();
    }
}
fn error(error: impl std::fmt::Display) -> BackdropError {
    BackdropError::Platform(error.to_string())
}