1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*!

**terminal-clipboard** is a cross-platform clipboard library focused on strings copying and pasting for terminal applications:

* it's tested on linux, windows and Android (Termux)
* it doesn't handle other types of objects than strings
* it doesn't handle non UTF8 strings

# Usage

```
terminal_clipboard::set_string("test").unwrap();
assert_eq!("test", terminal_clipboard::get_string().unwrap());
```

# Supported platforms

The implementation is currently chosen from the "target_os" part of the compilation target.

## Android (Termux)

The 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.

If you know of solutions to access the Android clipboard without Termux, please open an issue.

## Linux

If 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.

You'll need to have `xorg-dev` and `libxcb-composite0-dev` to compile.

On Debian and Ubuntu you can install them with

```bash
sudo apt install xorg-dev libxcb-composite0-dev
```

## Windows

If 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.


```
use terminal_clipboard;
terminal_clipboard::set_string("test").unwrap();
assert_eq!("test", terminal_clipboard::get_string().unwrap());
```

*/

mod clipboard;
mod errors;
mod local;

pub use {
    clipboard::Clipboard,
    errors::ClipboardError,
    local::LocalClipboard,
};

#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "macos")]
pub use macos::MacClipboard;

#[cfg(target_os = "windows")]
mod win;
#[cfg(target_os = "windows")]
pub use win::WinClipboard;

#[cfg(target_os = "android")]
mod termux;
#[cfg(target_os = "android")]
pub use termux::TermuxClipboard;

#[cfg(not(any(target_os="windows",target_os="android",target_os="macos")))]
mod x11;
#[cfg(not(any(target_os="windows",target_os="android",target_os="macos")))]
pub use x11::X11Clipboard;

use {
    once_cell::sync::Lazy,
    std::sync::Mutex,
};

static CLIPBOARD: Lazy<Mutex<Box<dyn Clipboard + Send>>> = Lazy::new(|| Mutex::new(new_clipboard()));

/// Build a new clipboard.
///
/// It's recommended to not use this directly but the get_string
/// and set_string global functions instead, as those functions
/// ensure the lazy static initialization of the clipboard.
pub fn new_clipboard() -> Box<dyn Clipboard + Send> {
    #[cfg(target_os = "windows")]
    {
        // To my knowledge
        return Box::new(WinClipboard::new());
    }

    #[cfg(target_os = "macos")]
    {
        // only use MacClipboard after it is verified.
        if let Ok(clipboard) = MacClipboard::verified() {
            return Box::new(clipboard);
        }
    }

    #[cfg(target_os = "android")]
    {
        // we'll use the Termux clipboard, but only after having
        // checked it works. It fails for example when the
        // Termux API isn't available on the device
        if let Ok(clipboard) =TermuxClipboard::verified() {
            return Box::new(clipboard);
        }
    }

    #[cfg(not(any(target_os="windows",target_os="android",target_os="macos")))]
    {
        // we'll use the X11 clipboard, but only after having
        // checked it works. As nobody understants X11 anyway,
        // I won't try to pretend I know when it works and when
        // it doesn't
        if let Ok(clipboard) = X11Clipboard::verified() {
            return Box::new(clipboard);
        }
    }

    // when everything else failed, use a local clipboard
    #[allow(unreachable_code)]
    Box::new(LocalClipboard::new())
}

/// Return the type of the Clipboard, for example
/// "X11", "Windows", "Local", or "Termux"
pub fn get_type() -> &'static str {
    CLIPBOARD.lock().unwrap().get_type()
}

/// Return the content of the clipboard
pub fn get_string() -> Result<String, ClipboardError> {
    CLIPBOARD.lock().unwrap().get_string()
}

/// Fill the clipboard with the given string
pub fn set_string<S: AsRef<str>>(s: S) -> Result<(), ClipboardError> {
    CLIPBOARD.lock().unwrap().set_string(s.as_ref())
}

// Those tests are the same than doc tests but they must be
// kept separate because cargo-cross doesn't run doc tests
// (see https://github.com/rust-embedded/cross/issues/225)
#[cfg(test)]
mod clipboard_tests {

    use super::*;

    #[test]
    fn write_read() {
        let test = "TEST";
        set_string(test).unwrap();
        assert_eq!(test, get_string().unwrap());
    }
}