feat: linux screenshots

This commit is contained in:
2025-04-26 16:12:48 +01:00
parent fa187b3a79
commit 151142fa9b
9 changed files with 101 additions and 36 deletions

View File

@@ -9,6 +9,7 @@ dependencies = [
"base64 0.21.7",
"chrono",
"cocoa",
"log",
"notify",
"serde",
"serde_json",

View File

@@ -27,6 +27,7 @@ tokio = { version = "1.36.0", features = ["full"] }
tauri-plugin-store = "2.0.0-beta.12"
tauri-plugin-http = "2.0.0-beta.12"
chrono = "0.4"
log = "0.4"
tauri-plugin-log = "2"
tauri-plugin-sharetarget = "0.1.6"
tauri-plugin-fs = "2"

View File

@@ -1,3 +1,4 @@
use crate::screenshot::take_area_screenshot;
use crate::state::SharedWatcherState;
use crate::utils::process_png_file;
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
@@ -69,3 +70,8 @@ pub async fn handle_selected_folder(
Ok(format!("Now watching directory: {}", path))
}
#[tauri::command]
pub fn take_screenshot(app: AppHandle) -> Result<String, String> {
take_area_screenshot(&app)
}

View File

@@ -15,6 +15,7 @@ pub fn desktop() {
let watcher_state = new_shared_watcher_state();
tauri::Builder::default()
.plugin(tauri_plugin_log::Builder::new().build())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_store::Builder::new().build())
.plugin(tauri_plugin_http::init())
@@ -24,6 +25,7 @@ pub fn desktop() {
.manage(watcher_state)
.invoke_handler(tauri::generate_handler![
commands::handle_selected_folder,
commands::take_screenshot,
shortcut::change_shortcut,
shortcut::unregister_shortcut,
shortcut::get_current_shortcut,

View File

@@ -1,8 +1,55 @@
use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
use std::fs;
use std::process::Command;
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
pub fn take_area_screenshot<R: Runtime>(app: &AppHandle<R>) -> Result<String, String> {
// Create a temporary file path
@@ -10,15 +57,10 @@ pub fn take_area_screenshot<R: Runtime>(app: &AppHandle<R>) -> Result<String, St
let timestamp = chrono::Local::now().format("%Y%m%d_%H%M%S");
let temp_file = temp_dir.join(format!("haystack_screenshot_{}.png", timestamp));
// Use screencapture command with -i flag for interactive selection
let output = 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(temp_file.to_str().unwrap())
.output()
.map_err(|e| format!("Failed to execute screencapture: {}", e))?;
log::info!("{}", temp_file.display());
let output = screenshot(&temp_file)?;
log::info!("0");
if !output.status.success() {
return Err(format!(
@@ -27,10 +69,14 @@ pub fn take_area_screenshot<R: Runtime>(app: &AppHandle<R>) -> Result<String, St
));
}
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);

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 B