fotos_lib/capture/
xcap_backend.rs1use anyhow::Result;
6use image::{DynamicImage, ImageBuffer, Rgba};
7use xcap::{Monitor, Window};
8
9pub async fn capture_fullscreen() -> Result<DynamicImage> {
10 tokio::task::spawn_blocking(|| {
13 let monitors = Monitor::all()?;
15
16 if monitors.is_empty() {
17 anyhow::bail!("No monitors detected");
18 }
19
20 struct MonitorGeom {
24 x: i32,
25 y: i32,
26 width: u32,
27 height: u32,
28 image: ImageBuffer<Rgba<u8>, Vec<u8>>,
29 }
30
31 let mut geoms: Vec<MonitorGeom> = Vec::with_capacity(monitors.len());
32 for monitor in monitors {
33 geoms.push(MonitorGeom {
34 x: monitor.x()?,
35 y: monitor.y()?,
36 width: monitor.width()?,
37 height: monitor.height()?,
38 image: monitor.capture_image()?,
39 });
40 }
41
42 let min_x = geoms.iter().map(|g| g.x).min().unwrap();
44 let min_y = geoms.iter().map(|g| g.y).min().unwrap();
45 let max_x = geoms.iter().map(|g| g.x + g.width as i32).max().unwrap();
46 let max_y = geoms.iter().map(|g| g.y + g.height as i32).max().unwrap();
47
48 let canvas_w = (max_x - min_x) as u32;
49 let canvas_h = (max_y - min_y) as u32;
50
51 let mut composite = ImageBuffer::from_pixel(canvas_w, canvas_h, Rgba([0, 0, 0, 255]));
52
53 for geom in geoms {
54 let offset_x = (geom.x - min_x) as i64;
55 let offset_y = (geom.y - min_y) as i64;
56 image::imageops::overlay(&mut composite, &geom.image, offset_x, offset_y);
57 }
58
59 Ok(DynamicImage::ImageRgba8(composite))
60 })
61 .await
62 .map_err(|e| anyhow::anyhow!("Task join error: {}", e))?
63}
64
65pub async fn capture_monitor(index: u32) -> Result<DynamicImage> {
66 tokio::task::spawn_blocking(move || {
67 let monitors = Monitor::all()?;
68 let monitor = monitors
69 .into_iter()
70 .nth(index as usize)
71 .ok_or_else(|| anyhow::anyhow!("Monitor index {} out of range", index))?;
72 let image = monitor.capture_image()?;
73 Ok(DynamicImage::ImageRgba8(image))
74 })
75 .await
76 .map_err(|e| anyhow::anyhow!("Task join error: {}", e))?
77}
78
79pub async fn capture_window(window_id: u32) -> Result<DynamicImage> {
80 tokio::task::spawn_blocking(move || {
81 let windows = Window::all()?;
82 let window = windows
83 .into_iter()
84 .find(|w| w.id().ok() == Some(window_id))
85 .ok_or_else(|| anyhow::anyhow!("No window found with id {}", window_id))?;
86 if window.is_minimized()? {
87 anyhow::bail!("Window {} is minimized and cannot be captured", window_id);
88 }
89 let image = window.capture_image()?;
90 Ok(DynamicImage::ImageRgba8(image))
91 })
92 .await
93 .map_err(|e| anyhow::anyhow!("Task join error: {}", e))?
94}