use anyhow::{Context, Result};
use std::process::Command;
#[derive(Debug, Clone, Copy)]
enum DisplayServer {
Wayland,
X11,
}
fn detect_display_server() -> Option<DisplayServer> {
if std::env::var("WAYLAND_DISPLAY").is_ok() {
if Command::new("which")
.arg("wl-paste")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
{
return Some(DisplayServer::Wayland);
}
}
if std::env::var("DISPLAY").is_ok() {
if Command::new("which")
.arg("xclip")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
{
return Some(DisplayServer::X11);
}
}
None
}
pub fn has_image() -> bool {
match detect_display_server() {
Some(DisplayServer::Wayland) => Command::new("wl-paste")
.arg("--list-types")
.output()
.map(|o| {
let types = String::from_utf8_lossy(&o.stdout);
types.contains("image/png") || types.contains("image/jpeg")
})
.unwrap_or(false),
Some(DisplayServer::X11) => Command::new("xclip")
.args(["-selection", "clipboard", "-t", "TARGETS", "-o"])
.output()
.map(|o| {
let types = String::from_utf8_lossy(&o.stdout);
types.contains("image/png") || types.contains("image/jpeg")
})
.unwrap_or(false),
None => false,
}
}
pub fn read_image_bytes() -> Result<(Vec<u8>, String)> {
let server =
detect_display_server().context("No display server detected (need X11 with xclip or Wayland with wl-paste)")?;
for (mime, format) in [("image/png", "png"), ("image/jpeg", "jpeg")] {
let output = match server {
DisplayServer::Wayland => Command::new("wl-paste").args(["--type", mime]).output(),
DisplayServer::X11 => Command::new("xclip")
.args(["-selection", "clipboard", "-t", mime, "-o"])
.output(),
};
if let Ok(output) = output {
if output.status.success() && !output.stdout.is_empty() {
return Ok((output.stdout, format.to_string()));
}
}
}
anyhow::bail!("No image data found in clipboard")
}
pub fn read_text() -> Result<String> {
let server =
detect_display_server().context("No display server detected (need X11 with xclip or Wayland with wl-paste)")?;
let output = match server {
DisplayServer::Wayland => Command::new("wl-paste")
.args(["--type", "text/plain"])
.output(),
DisplayServer::X11 => Command::new("xclip")
.args(["-selection", "clipboard", "-o"])
.output(),
};
let output = output.context("Failed to execute clipboard command")?;
if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).to_string())
} else {
anyhow::bail!("Clipboard does not contain text")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_display_server() {
let _ = detect_display_server();
}
#[test]
fn test_has_image_no_crash() {
let _ = has_image();
}
}