loopflow/engine/
clipboard.rs1use std::io::Write;
2use std::process::{Command, Stdio};
3
4pub fn read() -> Option<String> {
8 let output = if cfg!(target_os = "macos") {
9 Command::new("pbpaste").output().ok()?
10 } else {
11 try_command("xclip", &["-selection", "clipboard", "-o"])
12 .or_else(|| try_command("xsel", &["--clipboard", "--output"]))
13 .or_else(|| try_command("wl-paste", &[]))?
14 };
15
16 if output.status.success() {
17 let text = String::from_utf8_lossy(&output.stdout).to_string();
18 if !text.trim().is_empty() {
19 return Some(text);
20 }
21 }
22 None
23}
24
25pub fn write(text: &str) -> Result<(), std::io::Error> {
27 if cfg!(target_os = "macos") {
28 write_via("pbcopy", &[], text)
29 } else {
30 write_via("xclip", &["-selection", "clipboard"], text)
31 .or_else(|_| write_via("xsel", &["--clipboard", "--input"], text))
32 .or_else(|_| write_via("wl-copy", &[], text))
33 }
34}
35
36fn try_command(cmd: &str, args: &[&str]) -> Option<std::process::Output> {
37 Command::new(cmd).args(args).output().ok()
38}
39
40fn write_via(cmd: &str, args: &[&str], text: &str) -> Result<(), std::io::Error> {
41 let mut child = Command::new(cmd).args(args).stdin(Stdio::piped()).spawn()?;
42
43 if let Some(ref mut stdin) = child.stdin {
44 stdin.write_all(text.as_bytes())?;
45 }
46
47 let status = child.wait()?;
48 if !status.success() {
49 return Err(std::io::Error::other(format!("{cmd} exited with {status}")));
50 }
51 Ok(())
52}