use crate::core::log;
use crate::wxwork::send_to_wecom;
use xcap::Monitor;
use std::io::Cursor;
use std::sync::atomic::{AtomicBool, Ordering};
fn encode_png(width: u32, height: u32, rgba: &[u8]) -> Result<Vec<u8>, String> {
let mut buf = Cursor::new(Vec::new());
{
let mut encoder = png::Encoder::new(&mut buf, width, height);
encoder.set_color(png::ColorType::Rgba);
encoder.set_depth(png::BitDepth::Eight);
let mut writer = encoder
.write_header()
.map_err(|e| format!("PNG 写入头失败: {}", e))?;
writer
.write_image_data(rgba)
.map_err(|e| format!("PNG 写入数据失败: {}", e))?;
}
Ok(buf.into_inner())
}
pub static CAPTURING: AtomicBool = AtomicBool::new(false);
fn capture_all_monitors() -> Result<Vec<(String, Vec<u8>)>, String> {
let monitors = Monitor::all().map_err(|e| format!("获取显示器列表失败: {}", e))?;
log::info!("检测到 {} 个显示器:", monitors.len());
for m in &monitors {
let id = m.id().unwrap_or(0);
let x = m.x().unwrap_or(0);
let y = m.y().unwrap_or(0);
let w = m.width().unwrap_or(0);
let h = m.height().unwrap_or(0);
log::info!(
" - id={}: ({}, {}) -> ({}, {}), {}x{}",
id, x, y, x + w as i32, y + h as i32, w, h,
);
}
if monitors.is_empty() {
return Err("未检测到任何显示器".into());
}
let mut results: Vec<(String, Vec<u8>)> = Vec::new();
for monitor in &monitors {
let id = monitor.id().unwrap_or(0);
let label = format!("id={}", id);
let image = monitor
.capture_image()
.map_err(|e| format!("截图失败 ({}): {}", label, e))?;
let (w, h) = (image.width(), image.height());
let rgba = image.into_raw();
let bytes =
encode_png(w, h, &rgba).map_err(|e| format!("编码 PNG 失败 ({}): {}", label, e))?;
let size_mb = bytes.len() as f64 / (1024.0 * 1024.0);
log::info!(
"截图 {}: ({}, {}) {}x{} 大小 {:.2} MB",
label,
monitor.x().unwrap_or(0),
monitor.y().unwrap_or(0),
monitor.width().unwrap_or(0),
monitor.height().unwrap_or(0),
size_mb,
);
if bytes.len() > 2 * 1024 * 1024 {
log::warn!(
"警告: {} 截图超过 2MB ({:.2} MB),企业微信可能拒绝",
label,
size_mb
);
}
results.push((label, bytes));
}
Ok(results)
}
pub fn do_capture_and_send() {
let images = match capture_all_monitors() {
Ok(p) => {
log::info!("共截取 {} 个显示器", p.len());
p
}
Err(e) => {
log::warn!("截图失败: {}", e);
CAPTURING.store(false, Ordering::SeqCst);
return;
}
};
std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("创建 tokio runtime 失败");
let result = rt.block_on(send_to_wecom(&images));
match result {
Ok(()) => log::info!("=== 截图发送完成 ==="),
Err(e) => log::warn!("发送失败: {}", e),
}
CAPTURING.store(false, Ordering::SeqCst);
});
}
pub fn capture_and_upload() {
if CAPTURING.swap(true, Ordering::SeqCst) {
log::info!("截图发送正在进行中,跳过本次触发");
return;
}
log::info!("=== 开始截图 (F3) ===");
do_capture_and_send();
}