Haystack/frontend/src-tauri/src/screenshot.rs

94 lines
3.0 KiB
Rust

use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
use std::process::{Command, Output};
use std::{fs, path::PathBuf};
use tauri::{AppHandle, Emitter, Runtime};
#[cfg(target_os = "macos")]
fn screenshot(path: &PathBuf) -> Result<Output, String> {
Command::new("screencapture")
.arg("-i") // interactive selection
.arg("-x") // don't play sound
.arg("-o") // don't show cursor
.arg("-r") // don't add shadow
.arg(path.to_str().unwrap())
.output()
.map_err(|e| format!("Failed to execute screencapture: {}", e))
}
#[cfg(target_os = "linux")]
fn screenshot(path: &PathBuf) -> Result<Output, String> {
let slurp_output = Command::new("slurp")
.output()
.map_err(|e| format!("Failed to execute screencapture: {}", e))?;
if !slurp_output.status.success() {
let stderr = String::from_utf8_lossy(&slurp_output.stderr);
if slurp_output.status.code() == Some(1) && stderr.is_empty() {
log::warn!("slurp cancelled by user.");
return Err("Screenshot cancelled by user.".to_string());
}
return Err(format!(
"slurp failed. Status: {:?}, Stderr: {}",
slurp_output.status, stderr
));
}
let geometry = String::from_utf8(slurp_output.stdout)
.map_err(|e| format!("slurp output is not valid UTF-8: {}", e))?
.trim()
.to_string();
if geometry.is_empty() {
return Err("slurp succeeded but returned empty geometry".to_string());
}
Command::new("grim")
.arg("-g")
.arg(&geometry)
.arg(path.to_str().unwrap())
.output()
.map_err(|e| format!("Failed to execute screencapture: {}", e))
}
/// Takes a screenshot of a selected area and returns the image data as base64
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub fn take_area_screenshot<R: Runtime>(app: &AppHandle<R>) -> Result<String, String> {
// Create a temporary file path
let temp_dir = std::env::temp_dir();
let timestamp = chrono::Local::now().format("%Y%m%d_%H%M%S");
let temp_file = temp_dir.join(format!("haystack_screenshot_{}.png", timestamp));
log::info!("{}", temp_file.display());
let output = screenshot(&temp_file)?;
log::info!("0");
if !output.status.success() {
return Err(format!(
"screencapture failed: {}",
String::from_utf8_lossy(&output.stderr)
));
}
log::info!("1");
// Read the captured image
let contents =
fs::read(&temp_file).map_err(|e| format!("Failed to read screenshot file: {}", e))?;
log::info!("2");
// Convert to base64
let base64_string = BASE64.encode(&contents);
// Clean up the temporary file
if let Err(e) = fs::remove_file(&temp_file) {
println!("Warning: Failed to remove temporary screenshot file: {}", e);
}
app.emit("png-processed", base64_string.clone())
.map_err(|e| format!("Failed to emit event: {}", e))?;
Ok(base64_string)
}