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
//! Clipboard routing (issue #367): decide whether yanked text goes to the
//! host clipboard binaries or to the terminal via an OSC52 escape sequence,
//! and build the exact bytes for the OSC52 path.
//!
//! # Why this exists
//!
//! [`crate::tui::clipboard_candidates`] shells out to `pbcopy` / `wl-copy` /
//! `xclip` / `clip.exe`. Those write to the clipboard of the machine gwm runs
//! on. Over SSH that is the wrong machine — and the failure is silent rather
//! than loud: on a remote macOS host `pbcopy` exists, runs, exits 0, so gwm
//! reports `yanked branch name (pbcopy)` while the user's actual clipboard is
//! untouched. OSC52 hands the text to the terminal emulator instead, which is
//! the process that owns the clipboard the user pastes from.
//!
//! # Purity
//!
//! [`plan_clipboard_write`] takes the environment (SSH, tmux, screen) as
//! parameters rather than reading `std::env` itself, mirroring
//! [`crate::multiplexer::detect_tmux`]. The whole decision matrix is then
//! unit-testable without an SSH session or a live multiplexer — which matters
//! more than usual here, because OSC52 has no acknowledgement: a wrong
//! sequence cannot be caught at runtime, only printed as garbage.
//!
//! # Known limits (not bugs — inherent to OSC52)
//!
//! - **No confirmation.** Nothing reports back whether the terminal accepted
//! the sequence. A success message means "emitted", not "copied".
//! - **tmux needs `allow-passthrough`.** Off by default since tmux 3.3. The
//! DCS wrapper below is necessary but not sufficient; the user must enable
//! the option. gwm cannot detect or force this.
//! - **Terminal support varies.** kitty, WezTerm, Alacritty and iTerm2 (with
//! the setting on) honour OSC52; Terminal.app does not. Hence
//! [`ClipboardMode::Tools`] as an escape hatch.
use crateClipboardMode;
use CopyToClipboard;
use Command;
/// Maximum input length, in bytes, gwm will put in an OSC52 sequence.
///
/// Terminals cap the length they will accept and the ceiling is not
/// standardised — 100k is a common one, several are far lower. This is a
/// deliberately conservative bound on the *input*: base64 inflates by 4/3, so
/// 64 KiB of text becomes ~87k of sequence, which stays under the usual caps.
///
/// The alternative to a ceiling is emitting a sequence the terminal truncates,
/// which pastes as corrupt text or swallows subsequent output. Refusing is the
/// honest failure — the Command Logs copy can realistically reach this size.
pub const MAX_OSC52_BYTES: usize = 64 * 1024;
/// What the caller should do to put the text on the clipboard.
/// Decide how to copy `text`, and build the sequence when the answer is OSC52.
///
/// `is_ssh` / `in_tmux` / `in_screen` are injected (see the module note). The
/// caller derives them from `$SSH_TTY`/`$SSH_CONNECTION`, `$TMUX` and `$STY`.
/// Wrap `seq` in tmux's DCS passthrough so tmux forwards it to the outer
/// terminal instead of interpreting it: `\x1bPtmux;<seq with ESC doubled>\x1b\\`.
///
/// Every ESC inside the payload must be doubled — the OSC52 sequence has two
/// (the `\x1b]` introducer and the `\x1b\\` terminator).
///
/// Requires `set -g allow-passthrough on` in the user's tmux config; it is off
/// by default since tmux 3.3 and nothing here can change that.