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 { 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 { 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(app: &AppHandle) -> Result { // 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) }