use crate::error::{AumateError, Result};
use image::{ImageBuffer, ImageEncoder, RgbaImage};
use xcap::Monitor;
#[derive(Debug, Clone)]
pub struct ScreenCapture {
pub width: u32,
pub height: u32,
pub image: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ScreenSize {
pub width: u32,
pub height: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelColor {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl PixelColor {
pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
Self { r, g, b, a }
}
pub fn to_hex(&self) -> String {
format!("#{:02X}{:02X}{:02X}", self.r, self.g, self.b)
}
pub fn to_hex_with_alpha(&self) -> String {
format!("#{:02X}{:02X}{:02X}{:02X}", self.r, self.g, self.b, self.a)
}
}
pub fn capture_screen() -> Result<ScreenCapture> {
capture_screen_region(None, None, None, None)
}
pub fn capture_screen_region(
x: Option<u32>,
y: Option<u32>,
width: Option<u32>,
height: Option<u32>,
) -> Result<ScreenCapture> {
let monitors = Monitor::all()
.map_err(|e| AumateError::Screen(format!("Failed to get monitors: {}", e)))?;
if monitors.is_empty() {
return Err(AumateError::Screen("No monitors found".to_string()));
}
let monitor = &monitors[0];
let image = monitor
.capture_image()
.map_err(|e| AumateError::Screen(format!("Failed to capture screen: {}", e)))?;
let img_width = image.width();
let img_height = image.height();
let (x, y, width, height) = match (x, y, width, height) {
(Some(x), Some(y), Some(w), Some(h)) => {
let x = x.min(img_width);
let y = y.min(img_height);
let w = w.min(img_width - x);
let h = h.min(img_height - y);
(x, y, w, h)
}
_ => (0, 0, img_width, img_height),
};
let raw_buffer = image.as_raw();
let mut region_buffer = Vec::new();
if x == 0 && y == 0 && width == img_width && height == img_height {
region_buffer.extend_from_slice(raw_buffer);
} else {
region_buffer.reserve((width * height * 4) as usize);
for row in y..(y + height) {
for col in x..(x + width) {
let idx = ((row * img_width + col) * 4) as usize;
if idx + 3 < raw_buffer.len() {
region_buffer.extend_from_slice(&raw_buffer[idx..idx + 4]);
}
}
}
}
let rgba_image: RgbaImage = ImageBuffer::from_raw(width, height, region_buffer)
.ok_or_else(|| AumateError::Screen("Failed to create image buffer".to_string()))?;
let mut png_bytes = Vec::new();
{
let encoder = image::codecs::png::PngEncoder::new(&mut png_bytes);
encoder
.write_image(rgba_image.as_raw(), width, height, image::ExtendedColorType::Rgba8)
.map_err(|e| AumateError::Screen(format!("Failed to encode PNG: {}", e)))?;
}
Ok(ScreenCapture { width, height, image: png_bytes })
}
pub fn get_screen_size() -> Result<ScreenSize> {
let monitors = Monitor::all()
.map_err(|e| AumateError::Screen(format!("Failed to get monitors: {}", e)))?;
if monitors.is_empty() {
return Err(AumateError::Screen("No monitors found".to_string()));
}
let monitor = &monitors[0];
Ok(ScreenSize {
width: monitor
.width()
.map_err(|e| AumateError::Screen(format!("Failed to get monitor width: {}", e)))?,
height: monitor
.height()
.map_err(|e| AumateError::Screen(format!("Failed to get monitor height: {}", e)))?,
})
}
pub fn get_pixel_color(x: u32, y: u32) -> Result<PixelColor> {
let monitors = Monitor::all()
.map_err(|e| AumateError::Screen(format!("Failed to get monitors: {}", e)))?;
if monitors.is_empty() {
return Err(AumateError::Screen("No monitors found".to_string()));
}
let monitor = &monitors[0];
let image = monitor
.capture_image()
.map_err(|e| AumateError::Screen(format!("Failed to capture screen: {}", e)))?;
let img_width = image.width();
let img_height = image.height();
if x >= img_width || y >= img_height {
return Err(AumateError::Screen(format!(
"Coordinates out of bounds: ({}, {}) for screen size {}x{}",
x, y, img_width, img_height
)));
}
let buffer = image.as_raw();
let index = ((y * img_width + x) * 4) as usize;
if index + 3 >= buffer.len() {
return Err(AumateError::Screen("Invalid buffer index".to_string()));
}
Ok(PixelColor {
r: buffer[index],
g: buffer[index + 1],
b: buffer[index + 2],
a: buffer[index + 3],
})
}
pub fn get_monitors() -> Result<Vec<MonitorInfo>> {
let monitors = Monitor::all()
.map_err(|e| AumateError::Screen(format!("Failed to get monitors: {}", e)))?;
monitors
.iter()
.enumerate()
.map(|(i, m)| {
Ok(MonitorInfo {
id: i as u32,
name: m.name().unwrap_or_else(|_| format!("Monitor {}", i)),
width: m.width().map_err(|e| {
AumateError::Screen(format!("Failed to get monitor width: {}", e))
})?,
height: m.height().map_err(|e| {
AumateError::Screen(format!("Failed to get monitor height: {}", e))
})?,
x: m.x()
.map_err(|e| AumateError::Screen(format!("Failed to get monitor x: {}", e)))?,
y: m.y()
.map_err(|e| AumateError::Screen(format!("Failed to get monitor y: {}", e)))?,
is_primary: i == 0,
})
})
.collect()
}
#[derive(Debug, Clone)]
pub struct MonitorInfo {
pub id: u32,
pub name: String,
pub width: u32,
pub height: u32,
pub x: i32,
pub y: i32,
pub is_primary: bool,
}