use std::{
borrow::Cow,
env,
io::{Read, Write},
path::PathBuf,
process::{Command, Stdio},
};
use arboard::{Clipboard, ImageData, SetExtLinux};
use serde::{Deserialize, Serialize};
use super::{board::Content, linux_files, sync};
use crate::Result;
#[derive(Serialize, Deserialize, Default)]
struct Kept {
#[serde(default)]
text: Option<String>,
#[serde(default)]
files: Vec<PathBuf>,
#[serde(default)]
png: Vec<u8>,
}
pub fn hand_over(content: Content) {
let kept = match content {
Content::Text(text) => Kept {
text: Some(text),
..Kept::default()
},
Content::Files(files) => Kept {
files,
..Kept::default()
},
Content::Image { width, height, rgba } => match sync::encode_png(width, height, &rgba) {
Ok(png) => Kept { png, ..Kept::default() },
Err(_) => return,
},
};
let Ok(exe) = env::current_exe() else { return };
let mut cmd = Command::new(exe);
cmd.args(["clip", "keep"])
.stdin(Stdio::piped())
.stdout(Stdio::null())
.stderr(Stdio::null());
std::os::unix::process::CommandExt::process_group(&mut cmd, 0);
if let Ok(mut child) = cmd.spawn()
&& let Some(mut stdin) = child.stdin.take()
{
let _ = stdin.write_all(&serde_json::to_vec(&kept).unwrap_or_default());
}
}
pub fn hold() -> Result<()> {
let mut input = Vec::new();
std::io::stdin().read_to_end(&mut input).map_err(|e| e.to_string())?;
let kept: Kept = serde_json::from_slice(&input).map_err(|e| e.to_string())?;
if !kept.files.is_empty() {
return linux_files::set(&kept.files, true);
}
let mut clipboard = Clipboard::new().map_err(|e| e.to_string())?;
let set = clipboard.set().wait();
let result = match (kept.text, kept.png) {
(Some(text), _) => set.text(text),
(None, png) if !png.is_empty() => match sync::decode_png(&png)? {
Content::Image { width, height, rgba } => set.image(ImageData {
width,
height,
bytes: Cow::Owned(rgba),
}),
_ => return Ok(()),
},
_ => return Ok(()),
};
result.map_err(|e| e.to_string())
}