screencap 0.1.0

Capture screen data
Documentation
use crate::windows::common::Grabber;
use crate::windows::monitor::Monitor;
use crate::Region;
use std::io;
use windows::core::Interface;
use windows::Win32::Foundation::HMODULE;
use windows::Win32::Graphics::Direct3D::{D3D_DRIVER_TYPE_HARDWARE, D3D_FEATURE_LEVEL_11_0};
use windows::Win32::Graphics::Direct3D11::{
    D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D,
    D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_SDK_VERSION,
};
use windows::Win32::Graphics::Dxgi::{
    IDXGIDevice, IDXGIOutput1, IDXGIOutputDuplication, DXGI_OUTDUPL_FRAME_INFO,
};

pub struct ScreenGrabber {
    duplication: IDXGIOutputDuplication,
    device: ID3D11Device,
    context: ID3D11DeviceContext,
}

impl ScreenGrabber {
    pub fn new(monitor: &Monitor) -> io::Result<Self> {
        unsafe {
            let mut device = None;
            let mut context = None;

            D3D11CreateDevice(
                None,
                D3D_DRIVER_TYPE_HARDWARE,
                HMODULE::default(),
                D3D11_CREATE_DEVICE_BGRA_SUPPORT,
                Some(&[D3D_FEATURE_LEVEL_11_0]),
                D3D11_SDK_VERSION,
                Some(&mut device),
                None,
                Some(&mut context),
            )?;

            let device =
                device.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "No D3D11 device"))?;
            let context =
                context.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "No D3D11 context"))?;

            let dxgi_device: IDXGIDevice = device.cast()?;
            let adapter = dxgi_device.GetAdapter()?;
            let output = adapter.EnumOutputs(monitor.index())?;
            let output1: IDXGIOutput1 = output.cast()?;

            let duplication = output1.DuplicateOutput(&device)?;
            Ok(Self {
                duplication,
                device,
                context,
            })
        }
    }
    fn next_texture(&mut self) -> io::Result<ID3D11Texture2D> {
        unsafe {
            let mut frame_info = DXGI_OUTDUPL_FRAME_INFO::default();
            let mut desktop_resource = None;

            self.duplication
                .AcquireNextFrame(500, &mut frame_info, &mut desktop_resource)?;

            let desktop_resource = desktop_resource.ok_or_else(|| {
                io::Error::new(io::ErrorKind::Other, "Failed to acquire desktop resource")
            })?;

            let desktop_texture: ID3D11Texture2D = desktop_resource.cast()?;
            Ok(desktop_texture)
        }
    }
}
impl Grabber for ScreenGrabber {
    fn get_device(&self) -> &ID3D11Device {
        &self.device
    }

    fn get_context(&self) -> &ID3D11DeviceContext {
        &self.context
    }
    fn next_frame_impl(
        &mut self,
        buf: &mut [u8],
        region: Option<Region>,
    ) -> io::Result<(usize, u32, u32)> {
        let texture = self.next_texture();
        let _guard = DxgiFrameGuard(&self.duplication);
        let texture = texture?;
        let (desc, full_width, full_height) = self.get_desc(&texture, region)?;
        let staging = self.create_texture2d(desc)?;
        self.copy_resource(staging, &texture, region, full_width, full_height, buf)
    }
}
struct DxgiFrameGuard<'a>(&'a IDXGIOutputDuplication);
impl Drop for DxgiFrameGuard<'_> {
    fn drop(&mut self) {
        unsafe {
            let _ = self.0.ReleaseFrame();
        }
    }
}