From f9bfc3f860b0cb5f0875121aee95a03eaac656c0 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Mar 2022 12:01:19 +0000 Subject: [PATCH] Add argument to refresh-client -l to forward clipboard to a pane. GitHub issue 3068. --- usr.bin/tmux/cmd-refresh-client.c | 41 ++++++++++++++++++++++---- usr.bin/tmux/input.c | 49 ++++++++++++++++++------------- usr.bin/tmux/server-client.c | 3 +- usr.bin/tmux/tmux.1 | 11 +++++-- usr.bin/tmux/tmux.h | 12 ++++++-- usr.bin/tmux/tty-keys.c | 30 ++++++++++++++----- usr.bin/tmux/tty.c | 19 +++++++----- 7 files changed, 117 insertions(+), 48 deletions(-) diff --git a/usr.bin/tmux/cmd-refresh-client.c b/usr.bin/tmux/cmd-refresh-client.c index 696d5717e26..eb7db1e489a 100644 --- a/usr.bin/tmux/cmd-refresh-client.c +++ b/usr.bin/tmux/cmd-refresh-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-refresh-client.c,v 1.46 2022/02/15 13:03:02 nicm Exp $ */ +/* $OpenBSD: cmd-refresh-client.c,v 1.47 2022/03/08 12:01:19 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -34,7 +34,7 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "A:B:cC:Df:F:lLRSt:U", 0, 1, NULL }, + .args = { "A:B:cC:Df:F:l::LRSt:U", 0, 1, NULL }, .usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] " "[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]", @@ -162,6 +162,37 @@ 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 enum cmd_retval cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) { @@ -224,10 +255,8 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'l')) { - tty_send_osc52_query(&tc->tty); - return (CMD_RETURN_NORMAL); - } + if (args_has(args, 'l')) + return (cmd_refresh_client_clipboard(self, item)); if (args_has(args, 'F')) /* -F is an alias for -f */ server_client_set_flags(tc, args_get(args, 'F')); diff --git a/usr.bin/tmux/input.c b/usr.bin/tmux/input.c index 14d8d575594..86f6b60c51e 100644 --- a/usr.bin/tmux/input.c +++ b/usr.bin/tmux/input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: input.c,v 1.199 2022/02/15 13:11:29 nicm Exp $ */ +/* $OpenBSD: input.c,v 1.200 2022/03/08 12:01:19 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -2682,8 +2682,8 @@ input_osc_52(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; char *end; - const char *buf; - size_t len; + const char *buf = NULL; + size_t len = 0; u_char *out; int outlen, state; struct screen_write_ctx ctx; @@ -2703,26 +2703,12 @@ input_osc_52(struct input_ctx *ictx, const char *p) log_debug("%s: %s", __func__, end); if (strcmp(end, "?") == 0) { - if ((pb = paste_get_top(NULL)) != NULL) { + if ((pb = paste_get_top(NULL)) != NULL) buf = paste_buffer_data(pb, &len); - outlen = 4 * ((len + 2) / 3) + 1; - out = xmalloc(outlen); - if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { - free(out); - return; - } - } else { - outlen = 0; - out = NULL; - } - bufferevent_write(ictx->event, "\033]52;;", 6); - if (outlen != 0) - bufferevent_write(ictx->event, out, outlen); if (ictx->input_end == INPUT_END_BEL) - bufferevent_write(ictx->event, "\007", 1); + input_reply_clipboard(ictx->event, buf, len, "\007"); else - bufferevent_write(ictx->event, "\033\\", 2); - free(out); + input_reply_clipboard(ictx->event, buf, len, "\033\\"); return; } @@ -2780,3 +2766,26 @@ input_osc_104(struct input_ctx *ictx, const char *p) screen_write_fullredraw(&ictx->ctx); free(copy); } + +void +input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len, + const char *end) +{ + char *out = NULL; + size_t outlen = 0; + + if (buf != NULL && len != 0) { + outlen = 4 * ((len + 2) / 3) + 1; + out = xmalloc(outlen); + if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { + free(out); + return; + } + } + + bufferevent_write(bev, "\033]52;;", 6); + if (outlen != 0) + bufferevent_write(bev, out, outlen); + bufferevent_write(bev, end, strlen(end)); + free(out); +} diff --git a/usr.bin/tmux/server-client.c b/usr.bin/tmux/server-client.c index c6e9826d951..a3279a4aef2 100644 --- a/usr.bin/tmux/server-client.c +++ b/usr.bin/tmux/server-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server-client.c,v 1.391 2022/02/16 18:55:05 nicm Exp $ */ +/* $OpenBSD: server-client.c,v 1.392 2022/03/08 12:01:19 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -425,6 +425,7 @@ server_client_lost(struct client *c) if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); free(c->ttyname); + free(c->clipboard_panes); free(c->term_name); free(c->term_type); diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1 index 04f8dfe46a0..743bd645efe 100644 --- a/usr.bin/tmux/tmux.1 +++ b/usr.bin/tmux/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.880 2022/03/08 11:28:40 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.881 2022/03/08 12:01:19 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -1338,11 +1338,12 @@ 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 cDlLRSU +.Op Fl cDLRSU .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 t Ar target-client .Op Ar adjustment .Xc @@ -1456,7 +1457,11 @@ sets a comma-separated list of client flags, see .Fl l requests the clipboard from the client using the .Xr xterm 1 -escape sequence and stores it in a new paste buffer. +escape sequence. +If +Ar target-pane +is given, the clipboard is sent (in encoded form), otherwise it is stored in a +new paste buffer. .Pp .Fl L , .Fl R , diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h index 22369b5b3af..d88f9cb1a02 100644 --- a/usr.bin/tmux/tmux.h +++ b/usr.bin/tmux/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1162 2022/03/08 11:28:40 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1163 2022/03/08 12:01:19 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1331,7 +1331,7 @@ LIST_HEAD(tty_terms, tty_term); struct tty { struct client *client; struct event start_timer; - struct event query_timer; + struct event clipboard_timer; u_int sx; u_int sy; @@ -1764,6 +1764,7 @@ struct client { #define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL #define CLIENT_CONTROL_WAITEXIT 0x200000000ULL #define CLIENT_WINDOWSIZECHANGED 0x400000000ULL +#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -1844,6 +1845,9 @@ struct client { struct client_files files; + u_int *clipboard_panes; + u_int clipboard_npanes; + TAILQ_ENTRY(client) entry; }; TAILQ_HEAD(clients, client); @@ -2232,7 +2236,7 @@ void tty_reset(struct tty *); void tty_region_off(struct tty *); void tty_margin_off(struct tty *); void tty_cursor(struct tty *, u_int, u_int); -void tty_send_osc52_query(struct tty *); +void tty_clipboard_query(struct tty *); void tty_putcode(struct tty *, enum tty_code_code); void tty_putcode1(struct tty *, enum tty_code_code, int); void tty_putcode2(struct tty *, enum tty_code_code, int, int); @@ -2678,6 +2682,8 @@ void input_parse_pane(struct window_pane *); void input_parse_buffer(struct window_pane *, u_char *, size_t); void input_parse_screen(struct input_ctx *, struct screen *, screen_write_init_ctx_cb, void *, u_char *, size_t); +void input_reply_clipboard(struct bufferevent *, const char *, size_t, + const char *); /* input-key.c */ void input_key_build(void); diff --git a/usr.bin/tmux/tty-keys.c b/usr.bin/tmux/tty-keys.c index 3fcad31f761..70ac1b4a2be 100644 --- a/usr.bin/tmux/tty-keys.c +++ b/usr.bin/tmux/tty-keys.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty-keys.c,v 1.153 2022/02/16 18:55:05 nicm Exp $ */ +/* $OpenBSD: tty-keys.c,v 1.154 2022/03/08 12:01:19 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1154,12 +1154,14 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, * partial. */ static int -tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, - size_t *size) +tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size) { - size_t end, terminator, needed; - char *copy, *out; - int outlen; + struct client *c = tty->client; + struct window_pane *wp; + size_t end, terminator, needed; + char *copy, *out; + int outlen; + u_int i; *size = 0; @@ -1221,6 +1223,7 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, 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); @@ -1237,9 +1240,20 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, } free(copy); - /* Create a new paste buffer. */ + /* Create a new paste buffer and forward to panes. */ log_debug("%s: %.*s", __func__, outlen, out); - paste_add(NULL, out, outlen); + 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; return (0); } diff --git a/usr.bin/tmux/tty.c b/usr.bin/tmux/tty.c index 253fc10e562..da7bd1921b7 100644 --- a/usr.bin/tmux/tty.c +++ b/usr.bin/tmux/tty.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tty.c,v 1.416 2022/02/15 13:03:02 nicm Exp $ */ +/* $OpenBSD: tty.c,v 1.417 2022/03/08 12:01:19 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -2921,24 +2921,29 @@ tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, } static void -tty_query_timer_callback(__unused int fd, __unused short events, void *data) +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; } void -tty_send_osc52_query(struct tty *tty) +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_ptr2(tty, TTYC_MS, "", "?"); - tty->flags |= TTY_OSC52QUERY; - evtimer_set(&tty->query_timer, tty_query_timer_callback, tty); - evtimer_add(&tty->query_timer, &tv); + tty->flags |= TTY_OSC52QUERY; + evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty); + evtimer_add(&tty->clipboard_timer, &tv); } - -- 2.20.1