#![allow(dead_code, unused_imports, unused_variables)]
use anyhow::{bail, Result};
use serde::{Deserialize, Serialize};
use tracing::{debug, info};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapturedScreen {
pub width: u32,
pub height: u32,
pub base64_png: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub analysis: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct ScreenRegion {
pub x: i32,
pub y: i32,
pub width: u32,
pub height: u32,
}
impl ScreenRegion {
pub fn new(x: i32, y: i32, width: u32, height: u32) -> Self {
Self {
x,
y,
width,
height,
}
}
}
pub struct ScreenCapture;
impl ScreenCapture {
pub async fn capture_full() -> Result<CapturedScreen> {
Self::validate_capture_dimensions(0, 0, 32768, 32768)?;
info!("Capturing full screen");
let monitors = xcap::Monitor::all()
.map_err(|e| anyhow::anyhow!("Failed to enumerate monitors: {}", e))?;
let monitor = monitors
.into_iter()
.next()
.ok_or_else(|| anyhow::anyhow!("No monitors found"))?;
let image = monitor
.capture_image()
.map_err(|e| anyhow::anyhow!("Screen capture failed: {}", e))?;
let width = image.width();
let height = image.height();
let tmp = tempfile::NamedTempFile::new()?;
let tmp_path = tmp.path().to_path_buf();
image
.save(&tmp_path)
.map_err(|e| anyhow::anyhow!("PNG encoding failed: {}", e))?;
let png_data = std::fs::read(&tmp_path)?;
use base64::Engine as _;
let base64_png = base64::engine::general_purpose::STANDARD.encode(&png_data);
Ok(CapturedScreen {
width,
height,
base64_png,
analysis: None,
})
}
pub async fn capture_region(region: ScreenRegion) -> Result<CapturedScreen> {
Self::validate_capture_dimensions(region.x, region.y, region.width, region.height)?;
info!(
"Capturing screen region: ({}, {}) {}x{}",
region.x, region.y, region.width, region.height
);
let full = Self::capture_full().await?;
Ok(full)
}
fn validate_capture_dimensions(x: i32, y: i32, width: u32, height: u32) -> Result<()> {
if width == 0 || height == 0 {
bail!("Screen capture dimensions must be non-zero");
}
if width > 32768 || height > 32768 {
bail!(
"Screen capture dimensions too large: {}x{} (max 32768x32768)",
width,
height
);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_screen_region() {
let region = ScreenRegion::new(10, 20, 800, 600);
assert_eq!(region.x, 10);
assert_eq!(region.y, 20);
assert_eq!(region.width, 800);
assert_eq!(region.height, 600);
}
#[test]
fn test_validate_dimensions() {
assert!(ScreenCapture::validate_capture_dimensions(0, 0, 1920, 1080).is_ok());
assert!(ScreenCapture::validate_capture_dimensions(0, 0, 0, 100).is_err());
assert!(ScreenCapture::validate_capture_dimensions(0, 0, 50000, 100).is_err());
}
}