terminal_clipboard/
lib.rs

1/*!
2
3**terminal-clipboard** is a cross-platform clipboard library focused on strings copying and pasting for terminal applications:
4
5* it's tested on linux, windows and Android (Termux)
6* it doesn't handle other types of objects than strings
7* it doesn't handle non UTF8 strings
8
9# Usage
10
11```
12terminal_clipboard::set_string("test").unwrap();
13assert_eq!("test", terminal_clipboard::get_string().unwrap());
14```
15
16# Supported platforms
17
18The implementation is currently chosen from the "target_os" part of the compilation target.
19
20## Android (Termux)
21
22The current implementation will defer to Termux API facilities to access the Android clipboard, and won't work if the Termux API isn't available at runtime.
23
24If you know of solutions to access the Android clipboard without Termux, please open an issue.
25
26## Linux
27
28If a unix-like target is detected and the "termux" feature isn't enabled, terminal-clipboard uses the [x11-clipboard](https://crates.io/crates/x11-clipboard) crate.
29
30You'll need to have `xorg-dev` and `libxcb-composite0-dev` to compile.
31
32On Debian and Ubuntu you can install them with
33
34```bash
35sudo apt install xorg-dev libxcb-composite0-dev
36```
37
38## Windows
39
40If the compilation target is "windows", terminal-clipboard uses the [clipboard-win](https://crates.io/crates/clipboard-win) crate. If you're only interested in this platform, you should use this crate directly.
41
42
43```
44use terminal_clipboard;
45terminal_clipboard::set_string("test").unwrap();
46assert_eq!("test", terminal_clipboard::get_string().unwrap());
47```
48
49*/
50
51mod clipboard;
52mod errors;
53mod local;
54
55pub use {
56    clipboard::Clipboard,
57    errors::ClipboardError,
58    local::LocalClipboard,
59};
60
61#[cfg(target_os = "macos")]
62mod macos;
63#[cfg(target_os = "macos")]
64pub use macos::MacClipboard;
65
66#[cfg(target_os = "windows")]
67mod win;
68#[cfg(target_os = "windows")]
69pub use win::WinClipboard;
70
71#[cfg(target_os = "android")]
72mod termux;
73#[cfg(target_os = "android")]
74pub use termux::TermuxClipboard;
75
76#[cfg(not(any(target_os="windows",target_os="android",target_os="macos")))]
77mod x11;
78#[cfg(not(any(target_os="windows",target_os="android",target_os="macos")))]
79pub use x11::X11Clipboard;
80
81use {
82    once_cell::sync::Lazy,
83    std::sync::Mutex,
84};
85
86static CLIPBOARD: Lazy<Mutex<Box<dyn Clipboard + Send>>> = Lazy::new(|| Mutex::new(new_clipboard()));
87
88/// Build a new clipboard.
89///
90/// It's recommended to not use this directly but the get_string
91/// and set_string global functions instead, as those functions
92/// ensure the lazy static initialization of the clipboard.
93pub fn new_clipboard() -> Box<dyn Clipboard + Send> {
94    #[cfg(target_os = "windows")]
95    {
96        // To my knowledge
97        return Box::new(WinClipboard::new());
98    }
99
100    #[cfg(target_os = "macos")]
101    {
102        // only use MacClipboard after it is verified.
103        if let Ok(clipboard) = MacClipboard::verified() {
104            return Box::new(clipboard);
105        }
106    }
107
108    #[cfg(target_os = "android")]
109    {
110        // we'll use the Termux clipboard, but only after having
111        // checked it works. It fails for example when the
112        // Termux API isn't available on the device
113        if let Ok(clipboard) =TermuxClipboard::verified() {
114            return Box::new(clipboard);
115        }
116    }
117
118    #[cfg(not(any(target_os="windows",target_os="android",target_os="macos")))]
119    {
120        // we'll use the X11 clipboard, but only after having
121        // checked it works. As nobody understants X11 anyway,
122        // I won't try to pretend I know when it works and when
123        // it doesn't
124        if let Ok(clipboard) = X11Clipboard::verified() {
125            return Box::new(clipboard);
126        }
127    }
128
129    // when everything else failed, use a local clipboard
130    #[allow(unreachable_code)]
131    Box::new(LocalClipboard::new())
132}
133
134/// Return the type of the Clipboard, for example
135/// "X11", "Windows", "Local", or "Termux"
136pub fn get_type() -> &'static str {
137    CLIPBOARD.lock().unwrap().get_type()
138}
139
140/// Return the content of the clipboard
141pub fn get_string() -> Result<String, ClipboardError> {
142    CLIPBOARD.lock().unwrap().get_string()
143}
144
145/// Fill the clipboard with the given string
146pub fn set_string<S: AsRef<str>>(s: S) -> Result<(), ClipboardError> {
147    CLIPBOARD.lock().unwrap().set_string(s.as_ref())
148}
149
150// Those tests are the same than doc tests but they must be
151// kept separate because cargo-cross doesn't run doc tests
152// (see https://github.com/rust-embedded/cross/issues/225)
153#[cfg(test)]
154mod clipboard_tests {
155
156    use super::*;
157
158    #[test]
159    fn write_read() {
160        let test = "TEST";
161        set_string(test).unwrap();
162        assert_eq!(test, get_string().unwrap());
163    }
164}