Add a get-clipboard option which when enabled (the default is off) uses

the same mechanism as palette requests to request clipboard from the
terminal and forward to the requesting pane. Remove the now-redundant
forward-to-pane ability from "refresh-client -l". GitHub issue 4275.
This commit is contained in:
nicm
2025-12-02 08:20:32 +00:00
parent e4c552f5a5
commit 322adfbdde
7 changed files with 158 additions and 93 deletions

View File

@@ -34,7 +34,7 @@ const struct cmd_entry cmd_refresh_client_entry = {
.name = "refresh-client",
.alias = "refresh",
.args = { "A:B:cC:Df:r:F:l::LRSt:U", 0, 1, NULL },
.args = { "A:B:cC:Df:r:F:lLRSt:U", 0, 1, NULL },
.usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] "
"[-C XxY] [-f flags] [-r pane:report] " CMD_TARGET_CLIENT_USAGE
" [adjustment]",
@@ -163,37 +163,6 @@ out:
free(copy);
}
static enum cmd_retval
cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
const char *p;
u_int i;
struct cmd_find_state fs;
p = args_get(args, 'l');
if (p == NULL) {
if (tc->flags & CLIENT_CLIPBOARDBUFFER)
return (CMD_RETURN_NORMAL);
tc->flags |= CLIENT_CLIPBOARDBUFFER;
} else {
if (cmd_find_target(&fs, item, p, CMD_FIND_PANE, 0) != 0)
return (CMD_RETURN_ERROR);
for (i = 0; i < tc->clipboard_npanes; i++) {
if (tc->clipboard_panes[i] == fs.wp->id)
break;
}
if (i != tc->clipboard_npanes)
return (CMD_RETURN_NORMAL);
tc->clipboard_panes = xreallocarray(tc->clipboard_panes,
tc->clipboard_npanes + 1, sizeof *tc->clipboard_panes);
tc->clipboard_panes[tc->clipboard_npanes++] = fs.wp->id;
}
tty_clipboard_query(&tc->tty);
return (CMD_RETURN_NORMAL);
}
static void
cmd_refresh_report(struct tty *tty, const char *value)
{
@@ -284,8 +253,10 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'l'))
return (cmd_refresh_client_clipboard(self, item));
if (args_has(args, 'l')) {
tty_clipboard_query(&tc->tty);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'F')) /* -F is an alias for -f */
server_client_set_flags(tc, args_get(args, 'F'));

91
input.c
View File

@@ -3032,18 +3032,41 @@ input_osc_133(struct input_ctx *ictx, const char *p)
}
}
/* Handle OSC 52 reply. */
static void
input_osc_52_reply(struct input_ctx *ictx)
{
struct paste_buffer *pb;
int state;
const char *buf;
size_t len;
state = options_get_number(global_options, "get-clipboard");
if (state == 0)
return;
if (state == 1) {
if ((pb = paste_get_top(NULL)) == NULL)
return;
buf = paste_buffer_data(pb, &len);
if (ictx->input_end == INPUT_END_BEL)
input_reply_clipboard(ictx->event, buf, len, "\007");
else
input_reply_clipboard(ictx->event, buf, len, "\033\\");
return;
}
input_add_request(ictx, INPUT_REQUEST_CLIPBOARD, ictx->input_end);
}
/* Handle the OSC 52 sequence for setting the clipboard. */
static void
input_osc_52(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
size_t len;
char *end;
const char *buf = NULL;
size_t len = 0;
u_char *out;
int outlen, state;
struct screen_write_ctx ctx;
struct paste_buffer *pb;
const char* allow = "cpqs01234567";
char flags[sizeof "cpqs01234567"] = "";
u_int i, j = 0;
@@ -3068,12 +3091,7 @@ input_osc_52(struct input_ctx *ictx, const char *p)
log_debug("%s: %.*s %s", __func__, (int)(end - p - 1), p, flags);
if (strcmp(end, "?") == 0) {
if ((pb = paste_get_top(NULL)) != NULL)
buf = paste_buffer_data(pb, &len);
if (ictx->input_end == INPUT_END_BEL)
input_reply_clipboard(ictx->event, buf, len, "\007");
else
input_reply_clipboard(ictx->event, buf, len, "\033\\");
input_osc_52_reply(ictx);
return;
}
@@ -3132,6 +3150,7 @@ input_osc_104(struct input_ctx *ictx, const char *p)
free(copy);
}
/* Send a clipboard reply. */
void
input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len,
const char *end)
@@ -3268,6 +3287,9 @@ input_add_request(struct input_ctx *ictx, enum input_request_type type, int idx)
xsnprintf(s, sizeof s, "\033]4;%d;?\033\\", idx);
tty_puts(&c->tty, s);
break;
case INPUT_REQUEST_CLIPBOARD:
tty_putcode_ss(&c->tty, TTYC_MS, "", "?");
break;
case INPUT_REQUEST_QUEUE:
break;
}
@@ -3275,6 +3297,39 @@ input_add_request(struct input_ctx *ictx, enum input_request_type type, int idx)
return (0);
}
/* Handle a palette reply. */
static void
input_request_palette_reply(struct input_request *ir, void *data)
{
struct input_request_palette_data *pd = data;
input_osc_colour_reply(ir->ictx, 0, 4, pd->idx, pd->c, ir->end);
}
/* Handle a clipboard reply. */
static void
input_request_clipboard_reply(struct input_request *ir, void *data)
{
struct input_ctx *ictx = ir->ictx;
struct input_request_clipboard_data *cd = data;
int state;
char *copy;
state = options_get_number(global_options, "get-clipboard");
if (state == 0 || state == 1)
return;
if (state == 3) {
copy = xmalloc(cd->len);
memcpy(copy, cd->buf, cd->len);
paste_add(NULL, copy, cd->len);
}
if (ir->idx == INPUT_END_BEL)
input_reply_clipboard(ictx->event, cd->buf, cd->len, "\007");
else
input_reply_clipboard(ictx->event, cd->buf, cd->len, "\033\\");
}
/* Handle a reply to a request. */
void
input_request_reply(struct client *c, enum input_request_type type, void *data)
@@ -3284,11 +3339,18 @@ input_request_reply(struct client *c, enum input_request_type type, void *data)
int complete = 0;
TAILQ_FOREACH_SAFE(ir, &c->input_requests, centry, ir1) {
if (ir->type == type && pd->idx == ir->idx) {
if (ir->type != type) {
input_free_request(ir);
continue;
}
if (type == INPUT_REQUEST_PALETTE && pd->idx == ir->idx) {
found = ir;
break;
}
if (type == INPUT_REQUEST_CLIPBOARD) {
found = ir;
break;
}
input_free_request(ir);
}
if (found == NULL)
return;
@@ -3298,8 +3360,11 @@ input_request_reply(struct client *c, enum input_request_type type, void *data)
break;
if (ir->type == INPUT_REQUEST_QUEUE)
input_send_reply(ir->ictx, ir->data);
else if (ir == found && ir->type == INPUT_REQUEST_PALETTE) {
input_osc_colour_reply(ir->ictx, 0, 4, pd->idx, pd->c, ir->end);
else if (ir == found) {
if (ir->type == INPUT_REQUEST_PALETTE)
input_request_palette_reply(ir, data);
else if (ir->type == INPUT_REQUEST_CLIPBOARD)
input_request_clipboard_reply(ir, data);
complete = 1;
}
input_free_request(ir);

View File

@@ -85,6 +85,9 @@ static const char *options_table_popup_border_lines_list[] = {
static const char *options_table_set_clipboard_list[] = {
"off", "external", "on", NULL
};
static const char *options_table_get_clipboard_list[] = {
"off", "buffer", "request", "both", NULL
};
static const char *options_table_window_size_list[] = {
"largest", "smallest", "manual", "latest", NULL
};
@@ -406,6 +409,18 @@ const struct options_table_entry options_table[] = {
.text = "Whether to send focus events to applications."
},
{ .name = "get-clipboard",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SERVER,
.choices = options_table_get_clipboard_list,
.default_num = 1,
.text = "When an application requests the clipboard, whether to "
"ignore the request ('off'); respond with the newest buffer "
"('buffer'); request the clipboard from the most recently "
"used terminal ('request'); or to request the clipboard, "
"create a buffer, and send it to the application ('both')."
},
{ .name = "history-file",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,

35
tmux.1
View File

@@ -1361,12 +1361,11 @@ and sets an environment variable for the newly created session; it may be
specified multiple times.
.Tg refresh
.It Xo Ic refresh-client
.Op Fl cDLRSU
.Op Fl cDlLRSU
.Op Fl A Ar pane:state
.Op Fl B Ar name:what:format
.Op Fl C Ar size
.Op Fl f Ar flags
.Op Fl l Op Ar target-pane
.Op Fl r Ar pane:report
.Op Fl t Ar target-client
.Op Ar adjustment
@@ -1487,11 +1486,7 @@ a colon, then a report escape sequence.
.Fl l
requests the clipboard from the client using the
.Xr xterm 1
escape sequence.
If
.Ar target-pane
is given, the clipboard is sent (in encoded form), otherwise it is stored in a
new paste buffer.
escape sequence and stores it in a new paste buffer.
.Pp
.Fl L ,
.Fl R ,
@@ -4239,6 +4234,32 @@ passed through to applications running in
.Nm .
Attached clients should be detached and attached again after changing this
option.
.It Xo Ic get-clipboard
.Op Ic both | request | buffer | off
.Xc
Controls the behaviour when an application requests the clipboard from
.Nm .
.Pp
If
.Ic off ,
the request is ignored;
if
.Ic buffer ,
.Nm
responds with the newest paste buffer;
.Ic request
causes
.Nm
to request the clipboard from the most recently used client (if possible) and
send the reply (if any) back to the application;
.Ic buffer
is the same as
.Ic request
but also creates a paste buffer.
.Pp
See also the
.Ic set-clipboard
option.
.It Ic history-file Ar path
If not empty, a file to which
.Nm

9
tmux.h
View File

@@ -1098,6 +1098,7 @@ struct window_mode_entry {
/* Type of request to client. */
enum input_request_type {
INPUT_REQUEST_PALETTE,
INPUT_REQUEST_CLIPBOARD,
INPUT_REQUEST_QUEUE
};
@@ -1107,6 +1108,12 @@ struct input_request_palette_data {
int c;
};
/* Clipboard request reply data. */
struct input_request_clipboard_data {
char *buf;
size_t len;
};
/* Request sent to client on behalf of pane. */
TAILQ_HEAD(input_requests, input_request);
@@ -1975,7 +1982,7 @@ struct client {
#define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL
#define CLIENT_CONTROL_WAITEXIT 0x200000000ULL
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL
/* 0x800000000ULL unused */
#define CLIENT_BRACKETPASTING 0x1000000000ULL
#define CLIENT_ASSUMEPASTING 0x2000000000ULL
#define CLIENT_REDRAWSCROLLBARS 0x4000000000ULL

View File

@@ -1301,12 +1301,11 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
static int
tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
{
struct client *c = tty->client;
struct window_pane *wp;
size_t end, terminator = 0, needed;
char *copy, *out;
int outlen;
u_int i;
struct client *c = tty->client;
size_t end, terminator = 0, needed;
char *copy, *out;
int outlen;
struct input_request_clipboard_data cd;
*size = 0;
@@ -1364,12 +1363,6 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
buf++;
end--;
/* If we did not request this, ignore it. */
if (~tty->flags & TTY_OSC52QUERY)
return (0);
tty->flags &= ~TTY_OSC52QUERY;
evtimer_del(&tty->clipboard_timer);
/* It has to be a string so copy it. */
copy = xmalloc(end + 1);
memcpy(copy, buf, end);
@@ -1384,22 +1377,22 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
return (0);
}
free(copy);
/* Create a new paste buffer and forward to panes. */
log_debug("%s: %.*s", __func__, outlen, out);
if (c->flags & CLIENT_CLIPBOARDBUFFER) {
paste_add(NULL, out, outlen);
c->flags &= ~CLIENT_CLIPBOARDBUFFER;
}
for (i = 0; i < c->clipboard_npanes; i++) {
wp = window_pane_find_by_id(c->clipboard_panes[i]);
if (wp != NULL)
input_reply_clipboard(wp->event, out, outlen, "\033\\");
}
free(c->clipboard_panes);
c->clipboard_panes = NULL;
c->clipboard_npanes = 0;
/* Set reply if any. */
cd.buf = out;
cd.len = outlen;
input_request_reply(c, INPUT_REQUEST_CLIPBOARD, &cd);
/* Create a buffer if requested. */
if (tty->flags & TTY_OSC52QUERY) {
paste_add(NULL, out, outlen);
out = NULL;
evtimer_del(&tty->clipboard_timer);
tty->flags &= ~TTY_OSC52QUERY;
}
free(out);
return (0);
}

17
tty.c
View File

@@ -3090,12 +3090,6 @@ static void
tty_clipboard_query_callback(__unused int fd, __unused short events, void *data)
{
struct tty *tty = data;
struct client *c = tty->client;
c->flags &= ~CLIENT_CLIPBOARDBUFFER;
free(c->clipboard_panes);
c->clipboard_panes = NULL;
c->clipboard_npanes = 0;
tty->flags &= ~TTY_OSC52QUERY;
}
@@ -3105,10 +3099,9 @@ tty_clipboard_query(struct tty *tty)
{
struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT };
if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY))
return;
tty_putcode_ss(tty, TTYC_MS, "", "?");
tty->flags |= TTY_OSC52QUERY;
evtimer_add(&tty->clipboard_timer, &tv);
if ((tty->flags & TTY_STARTED) && (~tty->flags & TTY_OSC52QUERY)) {
tty_putcode_ss(tty, TTYC_MS, "", "?");
tty->flags |= TTY_OSC52QUERY;
evtimer_add(&tty->clipboard_timer, &tv);
}
}