Skip to content

Commit 2541bf4

Browse files
author
Naseschwarz
committed
Copy text using OSC52 if X/Wayland methods fail
1 parent 0b0d057 commit 2541bf4

File tree

4 files changed

+41
-7
lines changed

4 files changed

+41
-7
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Changed
1111
* improve syntax highlighting file detection [[@acuteenvy](https://github.com/acuteenvy)] ([#2524](https://github.com/extrawurst/gitui/pull/2524))
1212
* After commit: jump back to unstaged area [[@tommady](https://github.com/tommady)] ([#2476](https://github.com/extrawurst/gitui/issues/2476))
13+
* use OSC52 copying in case other methods fail [[@naseschwarz](https://github.com/naseschwarz)] ([#2366](https://github.com/gitui-org/gitui/issues/2366))
1314

1415
## [0.27.0] - 2024-01-14
1516

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ build = "build.rs"
1818
anyhow = "1.0"
1919
asyncgit = { path = "./asyncgit", version = "0.27.0", default-features = false }
2020
backtrace = "0.3"
21+
base64 = "0.21"
2122
bitflags = "2.8"
2223
bugreport = "0.5.1"
2324
bwrap = { version = "1.3", features = ["use_std"] }

src/clipboard.rs

+38-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use anyhow::{anyhow, Result};
2+
use base64::prelude::{Engine, BASE64_STANDARD};
23
use std::io::Write;
34
use std::path::PathBuf;
45
use std::process::{Command, Stdio};
@@ -63,10 +64,27 @@ fn is_wsl() -> bool {
6364
false
6465
}
6566

67+
// Copy text using escape sequence Ps = 5 2.
68+
// This enables copying even if there is no Wayland or X socket available,
69+
// e.g. via SSH, as long as it supported by the terminal.
70+
// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
71+
fn copy_string_osc52(text: &str, out: &mut impl Write) -> Result<()> {
72+
const OSC52_DESTINATION_CLIPBOARD: char = 'c';
73+
write!(
74+
out,
75+
"\x1b]52;{destination};{encoded_text}\x07",
76+
destination = OSC52_DESTINATION_CLIPBOARD,
77+
encoded_text = BASE64_STANDARD.encode(text)
78+
)?;
79+
Ok(())
80+
}
81+
6682
#[cfg(all(target_family = "unix", not(target_os = "macos")))]
6783
pub fn copy_string(text: &str) -> Result<()> {
6884
if std::env::var("WAYLAND_DISPLAY").is_ok() {
69-
return exec_copy_with_args("wl-copy", &[], text, false);
85+
if exec_copy_with_args("wl-copy", &[], text, false).is_err() {
86+
copy_string_osc52(text, &mut std::io::stdout())?;
87+
}
7088
}
7189

7290
if is_wsl() {
@@ -81,12 +99,11 @@ pub fn copy_string(text: &str) -> Result<()> {
8199
)
82100
.is_err()
83101
{
84-
return exec_copy_with_args(
85-
"xsel",
86-
&["--clipboard"],
87-
text,
88-
true,
89-
);
102+
if exec_copy_with_args("xsel", &["--clipboard"], text, true)
103+
.is_err()
104+
{
105+
copy_string_osc52(text, &mut std::io::stdout())?;
106+
}
90107
}
91108

92109
Ok(())
@@ -106,3 +123,17 @@ pub fn copy_string(text: &str) -> Result<()> {
106123
pub fn copy_string(text: &str) -> Result<()> {
107124
exec_copy("clip", text)
108125
}
126+
127+
#[cfg(test)]
128+
mod tests {
129+
#[test]
130+
fn test_copy_string_osc52() {
131+
let mut buffer = Vec::<u8>::new();
132+
{
133+
let mut cursor = std::io::Cursor::new(&mut buffer);
134+
super::copy_string_osc52("foo", &mut cursor).unwrap();
135+
}
136+
let output = String::from_utf8(buffer).unwrap();
137+
assert_eq!(output, "\x1b]52;c;Zm9v\x07");
138+
}
139+
}

0 commit comments

Comments
 (0)