jabba_lib/
jclipboard.rs

1//! clipboard (supported platforms: Linux (with X server), Windows)
2
3use crate::jos;
4use which::which;
5
6// see https://rust-lang-nursery.github.io/rust-cookbook/os/external.html
7
8/// Checks if the current platform is supported.
9/// If not supported, then it panics.
10///
11/// Supported platforms: Linux (with X server), Windows.
12///
13/// Under Linux you must have the program `xsel` installed.
14/// You can install it with your package manager.
15///
16/// Under Linux, the text is pasted on both clipboards (to "primary" and "clipboard").
17///
18/// # Examples
19///
20/// ```
21/// jabba_lib::jclipboard::check();  // verify if your platform is supported
22/// ```
23pub fn check() {
24    if !jos::is_linux() && !jos::is_windows() {
25        let platform = jos::get_operating_system_name();
26        panic!("Error: unknown platform {}", platform);
27    }
28    // else, if linux or windows:
29
30    if jos::is_windows() {
31        return;
32    }
33    // else, if linux
34    let command = "xsel";
35    let result = which(command);
36    if result.is_err() {
37        panic!("Error: the command {} was not found", command);
38    }
39}
40
41/// Puts the given string on the clipboard.
42///
43/// Supported platforms: Linux (with X server), Windows.
44///
45/// Under Linux you must have the program `xsel` installed.
46/// You can install it with your package manager.
47///
48/// Under Linux, the text is pasted on both clipboards (to "primary" and "clipboard").
49///
50/// # Examples
51///
52/// ```
53/// let backup = jabba_lib::jclipboard::get_text().unwrap();
54/// let text = "hello";
55///
56/// jabba_lib::jclipboard::check();  // verify if your platform is supported
57///
58/// jabba_lib::jclipboard::set_text(text).unwrap();
59/// println!("The text {:?} was pasted on the clipboard", text);
60///
61/// let read = jabba_lib::jclipboard::get_text().unwrap();
62/// println!("Contents of the clipboard: {:?}", read);
63///
64/// assert_eq!(read, text);
65/// jabba_lib::jclipboard::set_text(&backup).unwrap();
66/// ```
67pub fn set_text(text: &str) -> Result<(), &'static str> {
68    cfg_if::cfg_if! {
69        if #[cfg(windows)] {
70            set_text_windows(text)?;
71            return Ok(());
72        } else if #[cfg(unix)] {
73            let result = set_text_linux(text);
74            return match result {
75                Ok(_) => Ok(()),
76                Err(_) => Err("Error: cannot write to clipboard"),
77            };
78        } else {
79            let platform = jos::get_operating_system_name();
80            panic!("Error: unknown platform {}", platform);
81        }
82    }
83}
84
85/// Reads the contents of the clipboard.
86///
87/// Supported platforms: Linux (with X server), Windows.
88///
89/// Under Linux you must have the program `xsel` installed.
90/// You can install it with your package manager.
91///
92/// # Examples
93///
94/// ```
95/// let backup = jabba_lib::jclipboard::get_text().unwrap();
96/// let text = "hello";
97///
98/// jabba_lib::jclipboard::check();  // verify if your platform is supported
99///
100/// jabba_lib::jclipboard::set_text(text).unwrap();
101/// println!("The text {:?} was pasted on the clipboard", text);
102///
103/// let read = jabba_lib::jclipboard::get_text().unwrap();
104/// println!("Contents of the clipboard: {:?}", read);
105///
106/// assert_eq!(read, text);
107/// jabba_lib::jclipboard::set_text(&backup).unwrap();
108/// ```
109pub fn get_text() -> Result<String, &'static str> {
110    cfg_if::cfg_if! {
111        if #[cfg(windows)] {
112            let text = get_text_windows()?;
113            Ok(text)
114        } else if #[cfg(unix)] {
115            let result = get_text_linux();
116            match result {
117                Ok(text) => Ok(text),
118                Err(_) => Err("Error: cannot read from clipboard"),
119            }
120        } else {
121            let platform = jos::get_operating_system_name();
122            panic!("Error: unknown platform {}", platform);
123        }
124    }
125}
126
127////////////////////////////////
128// Windows-specific functions //
129////////////////////////////////
130
131#[cfg(windows)]
132use clipboard_win::formats::Unicode;
133#[cfg(windows)]
134use clipboard_win::{Clipboard, Getter, Setter};
135
136#[cfg(windows)]
137fn set_text_windows(text: &str) -> Result<(), &'static str> {
138    let _clip = Clipboard::new_attempts(10).expect("Open clipboard");
139    let result = Unicode.write_clipboard(&text);
140    match result {
141        Ok(_) => Ok(()),
142        _ => Err("cannot write to clipboard"),
143    }
144}
145
146#[cfg(windows)]
147fn get_text_windows() -> Result<String, &'static str> {
148    let _clip = Clipboard::new_attempts(10).expect("Open clipboard");
149    let mut output = String::new();
150    let result = Unicode.read_clipboard(&mut output);
151    match result {
152        Ok(_) => Ok(output),
153        _ => Err("cannot read clipboard"),
154    }
155}
156
157//////////////////////////////
158// Linux-specific functions //
159//////////////////////////////
160
161#[cfg(unix)]
162use std::error::Error;
163#[cfg(unix)]
164use std::process::{Command, Stdio};
165
166#[cfg(unix)]
167fn set_text_linux(text: &str) -> Result<(), Box<dyn Error>> {
168    set_text_with_xsel(text, "-pi")?; // primary
169    set_text_with_xsel(text, "-bi")?; // clipboard
170    Ok(())
171}
172
173#[cfg(unix)]
174fn set_text_with_xsel(text: &str, which_clipboard: &str) -> Result<(), Box<dyn Error>> {
175    let mut echo_output_child = Command::new("echo")
176        .arg("-n")
177        .arg(text)
178        .stdout(Stdio::piped())
179        .spawn()?;
180
181    echo_output_child.wait()?;
182
183    if let Some(echo_output) = echo_output_child.stdout.take() {
184        let mut xsel_output_child = Command::new("xsel")
185            .arg(which_clipboard) // difference here
186            .stdin(echo_output)
187            .spawn()?;
188
189        xsel_output_child.wait()?;
190    }
191
192    Ok(())
193}
194
195#[cfg(unix)]
196fn get_text_linux() -> Result<String, Box<dyn Error>> {
197    let output_child = Command::new("xsel")
198        .arg("-bo")
199        .stdout(Stdio::piped())
200        .spawn()?;
201
202    let prg_stdout = output_child.wait_with_output()?;
203    Ok(String::from_utf8(prg_stdout.stdout)?)
204}
205
206// ==========================================================================
207
208#[cfg(test)]
209mod tests {
210    use super::*;
211
212    #[test]
213    fn check_test() {
214        assert_eq!(check(), ());
215    }
216
217    #[test]
218    fn set_text_and_get_text_test() {
219        check();
220        let backup = get_text().unwrap();
221        //
222        for &s in &["", "a", "aa", "hello", "rust is cool", "Éva"] {
223            set_text(s).unwrap();
224            assert_eq!(get_text().unwrap(), s);
225        }
226        //
227        set_text(&backup).unwrap();
228        assert_eq!(get_text().unwrap(), backup);
229    }
230}