Skip to content

Commit 7da7e31

Browse files
naseschwarzNaseschwarz
and
Naseschwarz
authored
Add copying to clipboard using OSC52 (#974)
Many terminal emulators support copying text to clipboard using ANSI OSC Ps; PT ST with Ps = 5 2, see https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands This enables copying even through SSH and terminal multiplexers. Co-authored-by: Naseschwarz <[email protected]>
1 parent eb3be0f commit 7da7e31

File tree

7 files changed

+384
-1
lines changed

7 files changed

+384
-1
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Unreleased
22

3+
## Added ⭐
4+
5+
- Copy to clipboard using OSC52 (#974)
6+
37
# Version 0.28.1
48

59
## Fixed 🐛

Cargo.toml

+8
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ use-dev-tty = ["filedescriptor", "rustix/process"]
4747
## Enables `is_*` helper functions for event enums.
4848
derive-more = ["dep:derive_more"]
4949

50+
## Enables interacting with a host clipboard via [`clipboard`](clipboard/index.html)
51+
osc52 = ["dep:base64"]
52+
5053
[dependencies]
54+
base64 = { version = "0.22", optional = true }
5155
bitflags = { version = "2.3" }
5256
derive_more = { version = "1.0.0", features = ["is_variant"], optional = true }
5357
document-features = "0.2.10"
@@ -112,3 +116,7 @@ required-features = ["events"]
112116
[[example]]
113117
name = "key-display"
114118
required-features = ["events"]
119+
120+
[[example]]
121+
name = "copy-to-clipboard"
122+
required-features = ["osc52"]

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ features = ["event-stream"]
154154
| `events` | Reading input/system events (enabled by default) |
155155
| `filedescriptor` | Use raw filedescriptor for all events rather then mio dependency |
156156
| `derive-more` | Adds `is_*` helper functions for event types |
157+
| `osc52` | Enables crossterm::clipboard |
157158

158159

159160
To use crossterm as a very thin layer you can disable the `events` feature or use `filedescriptor` feature.
@@ -172,6 +173,7 @@ This can disable `mio` / `signal-hook` / `signal-hook-mio` dependencies.
172173
| `futures-core` | For async stream of events | only with `event-stream` feature flag |
173174
| `serde` | ***ser***ializing and ***de***serializing of events | only with `serde` feature flag |
174175
| `derive_more` | Adds `is_*` helper functions for event types | optional (`derive-more` feature), included by default |
176+
| `base64` | Encoding clipboard data for OSC52 sequences in crossterm::clipboard | only with `osc52` feature flag |
175177

176178
### Other Resources
177179

examples/copy-to-clipboard.rs

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//! Demonstrates copying a string to clipboard
2+
//!
3+
//! This example uses OSC control sequence `Pr = 5 2` (See
4+
//! <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands)>
5+
//! to copy data to the terminal host clipboard.
6+
//!
7+
//! This only works if it is enabled on the respective terminal emulator. If a terminal multiplexer
8+
//! is used, the multiplexer will likely need to support it, too.
9+
//!
10+
//! ```no_run
11+
//! cargo run --example copy-to-clipboard -- --clipboard "Some String"
12+
//! cargo run --example copy-to-clipboard -- --primary "Some String"
13+
//! cargo run --example copy-to-clipboard -- "Some String"
14+
//! ```
15+
16+
use std::io;
17+
18+
use crossterm::clipboard;
19+
use crossterm::execute;
20+
21+
fn main() -> io::Result<()> {
22+
let mut stdout = io::stdout();
23+
let mut args = std::env::args();
24+
args.next(); // Skip to first argument
25+
26+
let default_text = String::from("Example text");
27+
let (text, dest) = match args.next().as_deref() {
28+
Some("--clipboard") => (
29+
args.next().unwrap_or(default_text),
30+
clipboard::ClipboardType::Clipboard,
31+
),
32+
Some("--primary") => (
33+
args.next().unwrap_or(default_text),
34+
clipboard::ClipboardType::Primary,
35+
),
36+
Some(text) => (text.to_owned(), clipboard::ClipboardType::Clipboard),
37+
None => (default_text, clipboard::ClipboardType::Clipboard),
38+
};
39+
execute!(
40+
stdout,
41+
clipboard::CopyToClipboard {
42+
content: text,
43+
destination: clipboard::ClipboardSelection(vec![dest])
44+
}
45+
)
46+
}

0 commit comments

Comments
 (0)