//! Screen capture module //! //! Provides DXGI Desktop Duplication for high-performance screen capture on Windows 8+, //! with GDI fallback for legacy systems or edge cases. #[cfg(windows)] mod dxgi; #[cfg(windows)] mod gdi; mod display; pub use display::{Display, DisplayInfo}; use anyhow::Result; use std::time::Instant; /// Captured frame data #[derive(Debug)] pub struct CapturedFrame { /// Frame width in pixels pub width: u32, /// Frame height in pixels pub height: u32, /// Raw BGRA pixel data (4 bytes per pixel) pub data: Vec, /// Timestamp when frame was captured pub timestamp: Instant, /// Display ID this frame is from pub display_id: u32, /// Regions that changed since last frame (if available) pub dirty_rects: Option>, } /// Rectangular region that changed #[derive(Debug, Clone, Copy)] pub struct DirtyRect { pub x: u32, pub y: u32, pub width: u32, pub height: u32, } /// Screen capturer trait pub trait Capturer: Send { /// Capture the next frame /// /// Returns None if no new frame is available (screen unchanged) fn capture(&mut self) -> Result>; /// Get the current display info fn display(&self) -> &Display; /// Check if capturer is still valid (display may have changed) fn is_valid(&self) -> bool; } /// Create a capturer for the specified display #[cfg(windows)] pub fn create_capturer(display: Display, use_dxgi: bool, gdi_fallback: bool) -> Result> { if use_dxgi { match dxgi::DxgiCapturer::new(display.clone()) { Ok(capturer) => { tracing::info!("Using DXGI Desktop Duplication for capture"); return Ok(Box::new(capturer)); } Err(e) => { tracing::warn!("DXGI capture failed: {}, trying fallback", e); if !gdi_fallback { return Err(e); } } } } // GDI fallback tracing::info!("Using GDI for capture"); Ok(Box::new(gdi::GdiCapturer::new(display)?)) } #[cfg(not(windows))] pub fn create_capturer(_display: Display, _use_dxgi: bool, _gdi_fallback: bool) -> Result> { anyhow::bail!("Screen capture only supported on Windows") } /// Get all available displays pub fn enumerate_displays() -> Result> { display::enumerate_displays() } /// Get the primary display pub fn primary_display() -> Result { let displays = enumerate_displays()?; displays .into_iter() .find(|d| d.is_primary) .ok_or_else(|| anyhow::anyhow!("No primary display found")) }