doe 1.1.90

doe is a powerful Rust crate designed to enhance development workflow by providing an extensive collection of useful macros and utility functions. It not only simplifies common tasks but also offers convenient features for clipboard management,robust cryptographic functions,keyboard input, and mouse interaction.
Documentation
//! Linux X11 screen capture via the `x11` crate (Xlib bindings).
//!
//! Replaces the `xcb`-based path in the `screenshots` crate. Enumerates the
//! root window of the default screen and reads pixels with `XGetImage`.
//! Multi-monitor layouts are returned as a single virtual screen spanning the
//! root window; callers that need per-monitor capture can use `capture_area`
//! with the appropriate offsets.

use super::image_utils::bgra_to_rgba;
use super::Screen;
use image::RgbaImage;
use std::ptr;
use x11::xlib::{
    Display as XDisplay, XCloseDisplay, XDefaultRootWindow, XGetImage, XImage, XOpenDisplay,
    XScreenCount, XScreenOfDisplay, ZPixmap,
};

/// Opens the default X display, returning a RAII guard that closes it on drop.
struct DisplayGuard {
    dpy: *mut XDisplay,
}

impl DisplayGuard {
    fn open() -> Result<Self, String> {
        let dpy = unsafe { XOpenDisplay(ptr::null()) };
        if dpy.is_null() {
            return Err("XOpenDisplay failed — is DISPLAY set and an X server running?".to_string());
        }
        Ok(Self { dpy })
    }
}

impl Drop for DisplayGuard {
    fn drop(&mut self) {
        if !self.dpy.is_null() {
            unsafe {
                XCloseDisplay(self.dpy);
            }
        }
    }
}

/// Enumerates screens. Each X screen becomes one `Screen` entry rooted at the
/// top-left of its root window.
pub fn all() -> Result<Vec<Screen>, String> {
    let display = DisplayGuard::open()?;
    let n = unsafe { XScreenCount(display.dpy) };
    let mut out = Vec::with_capacity(n as usize);
    for i in 0..n {
        let screen = unsafe { XScreenOfDisplay(display.dpy, i) };
        if screen.is_null() {
            continue;
        }
        let (w, h) = unsafe { ((*screen).width as u32, (*screen).height as u32) };
        out.push(Screen {
            id: i as u32,
            x: 0,
            y: 0,
            width: w,
            height: h,
            scale_factor: 1.0,
        });
    }
    if out.is_empty() {
        Err("no X screens found".to_string())
    } else {
        Ok(out)
    }
}

/// Reads `width x height` pixels starting at `(x,y)` from the default root
/// window of the default screen, returning an RGBA image.
fn grab(x: i32, y: i32, width: u32, height: u32) -> Result<RgbaImage, String> {
    if width == 0 || height == 0 {
        return Err("capture area is empty".to_string());
    }
    let display = DisplayGuard::open()?;
    let root = unsafe { XDefaultRootWindow(display.dpy) };

    let img_ptr = unsafe { XGetImage(display.dpy, root, x, y, width, height, u64::MAX, ZPixmap) };
    if img_ptr.is_null() {
        return Err("XGetImage returned NULL — coordinates may be outside the root window".to_string());
    }

    // Scope the image so it is freed before the display guard.
    let result = unsafe { read_ximage(img_ptr, width, height) };

    // XDestroyImage must be invoked via the function pointer stored in the
    // XImage struct itself (libX11 requirement: never plain free()).
    unsafe {
        let destroy = (*img_ptr).funcs.destroy_image;
        if let Some(f) = destroy {
            f(img_ptr);
        }
    }
    result
}

/// Converts a 24/32-bit ZPixmap `XImage` into an `RgbaImage`.
unsafe fn read_ximage(img_ptr: *mut XImage, width: u32, height: u32) -> Result<RgbaImage, String> {
    let img = &*img_ptr;
    let depth = img.depth as u32;
    let bytes_per_pixel = (img.bits_per_pixel / 8) as usize;
    let stride = img.bytes_per_line as usize;
    let data = std::slice::from_raw_parts(img.data as *const u8, stride * height as usize);

    // XGetImage with ZPixmap on depth 24/32 is usually already in BGRA/BGRX
    // layout on little-endian hosts, matching the GDI path. We build a packed
    // BGRA buffer and reuse the bgra_to_rgba helper.
    let mut bgra = vec![0u8; (width as usize) * (height as usize) * 4];
    for row in 0..height as usize {
        let src_row = &data[row * stride..];
        for col in 0..width as usize {
            let src = &src_row[col * bytes_per_pixel..];
            let dst = col * 4;
            match bytes_per_pixel {
                4 => {
                    bgra[dst..dst + 4].copy_from_slice(&src[..4]);
                }
                3 => {
                    bgra[dst] = src[0];     // B
                    bgra[dst + 1] = src[1]; // G
                    bgra[dst + 2] = src[2]; // R
                    bgra[dst + 3] = 255;    // A
                }
                _ => {
                    return Err(format!("unsupported bits_per_pixel for depth {depth}"));
                }
            }
        }
    }
    bgra_to_rgba(width, height, bgra)
}

pub fn capture(screen: &Screen) -> Result<RgbaImage, String> {
    let w = ((screen.width as f32) * screen.scale_factor) as u32;
    let h = ((screen.height as f32) * screen.scale_factor) as u32;
    grab(screen.x, screen.y, w, h)
}

pub fn capture_area(screen: &Screen, x: i32, y: i32, width: u32, height: u32) -> Result<RgbaImage, String> {
    let ax = screen.x + ((x as f32) * screen.scale_factor) as i32;
    let ay = screen.y + ((y as f32) * screen.scale_factor) as i32;
    let w = ((width as f32) * screen.scale_factor) as u32;
    let h = ((height as f32) * screen.scale_factor) as u32;
    grab(ax, ay, w, h)
}