Files
guru-connect/agent/src/capture/mod.rs
Mike Swanson d0de888dd1
All checks were successful
Build and Test / Build Agent (Windows) (push) Successful in 6m53s
Build and Test / Build Server (Linux) (push) Successful in 10m59s
Build and Test / Security Audit (push) Successful in 4m31s
Build and Test / Build Summary (push) Successful in 10s
style(agent): clear 77 pre-existing clippy -D warnings
CI never ran clippy on the agent crate (the build-server clippy job is
Linux-only and can't compile the Windows agent; build-agent only runs cargo
build), so 77 clippy -D-warnings errors had accumulated. Behavior-preserving
cleanup, code-reviewed APPROVED, locally verified (cargo clippy --workspace
--all-targets --all-features -- -D warnings exits 0; cargo test --workspace =
57 passed).

- let _ = on Win32 resource-teardown BOOL returns (gdi.rs); fallible
  BitBlt/GetDIBits stay error-handled
- removed unused imports/vars; idiom fixes (div_ceil, is_null, transmute
  annotations, match collapsing, useless_conversion)
- #[allow(dead_code)] + comment on genuine Task-6/7 scaffolding (vk consts,
  SpecialKey emission, SAS mgmt API, modifier tracking, GDI frame-diff fields)
- Cargo.lock: cargo pruned ~147 stale transitive entries (no version changes)

Follow-up: add cargo clippy -D warnings to the build-agent CI job so the agent
crate stays clippy-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 08:51:45 -07:00

115 lines
2.8 KiB
Rust

//! Screen capture module
//!
//! Provides DXGI Desktop Duplication for high-performance screen capture on Windows 8+,
//! with GDI fallback for legacy systems or edge cases.
mod display;
#[cfg(windows)]
mod dxgi;
#[cfg(windows)]
mod gdi;
pub use display::Display;
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<u8>,
/// 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)
// Populated by capturers; not yet consumed by the encoder pipeline.
#[allow(dead_code)]
pub dirty_rects: Option<Vec<DirtyRect>>,
}
/// 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<Option<CapturedFrame>>;
/// Get the current display info
#[allow(dead_code)]
fn display(&self) -> &Display;
/// Check if capturer is still valid (display may have changed)
#[allow(dead_code)]
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<Box<dyn Capturer>> {
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<Box<dyn Capturer>> {
anyhow::bail!("Screen capture only supported on Windows")
}
/// Get all available displays
pub fn enumerate_displays() -> Result<Vec<Display>> {
display::enumerate_displays()
}
/// Get the primary display
pub fn primary_display() -> Result<Display> {
let displays = enumerate_displays()?;
displays
.into_iter()
.find(|d| d.is_primary)
.ok_or_else(|| anyhow::anyhow!("No primary display found"))
}