Add argument to refresh-client -l to forward clipboard to a pane. GitHub

issue 3068.
This commit is contained in:
nicm 2022-03-08 12:01:19 +00:00 committed by Nicholas Marriott
parent 8aed444201
commit 57f331438a
7 changed files with 191 additions and 114 deletions

View File

@ -34,7 +34,7 @@ const struct cmd_entry cmd_refresh_client_entry = {
.name = "refresh-client", .name = "refresh-client",
.alias = "refresh", .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] " .usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] "
"[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]", "[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]",
@ -162,6 +162,37 @@ out:
free(copy); 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 static enum cmd_retval
cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) 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); return (CMD_RETURN_NORMAL);
} }
if (args_has(args, 'l')) { if (args_has(args, 'l'))
tty_send_osc52_query(&tc->tty); return (cmd_refresh_client_clipboard(self, item));
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'F')) /* -F is an alias for -f */ if (args_has(args, 'F')) /* -F is an alias for -f */
server_client_set_flags(tc, args_get(args, 'F')); server_client_set_flags(tc, args_get(args, 'F'));

47
input.c
View File

@ -2682,8 +2682,8 @@ input_osc_52(struct input_ctx *ictx, const char *p)
{ {
struct window_pane *wp = ictx->wp; struct window_pane *wp = ictx->wp;
char *end; char *end;
const char *buf; const char *buf = NULL;
size_t len; size_t len = 0;
u_char *out; u_char *out;
int outlen, state; int outlen, state;
struct screen_write_ctx ctx; 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); log_debug("%s: %s", __func__, end);
if (strcmp(end, "?") == 0) { if (strcmp(end, "?") == 0) {
if ((pb = paste_get_top(NULL)) != NULL) { if ((pb = paste_get_top(NULL)) != NULL)
buf = paste_buffer_data(pb, &len); 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) if (ictx->input_end == INPUT_END_BEL)
bufferevent_write(ictx->event, "\007", 1); input_reply_clipboard(ictx->event, buf, len, "\007");
else else
bufferevent_write(ictx->event, "\033\\", 2); input_reply_clipboard(ictx->event, buf, len, "\033\\");
free(out);
return; return;
} }
@ -2780,3 +2766,26 @@ input_osc_104(struct input_ctx *ictx, const char *p)
screen_write_fullredraw(&ictx->ctx); screen_write_fullredraw(&ictx->ctx);
free(copy); 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);
}

View File

@ -422,6 +422,7 @@ server_client_lost(struct client *c)
if (c->flags & CLIENT_TERMINAL) if (c->flags & CLIENT_TERMINAL)
tty_free(&c->tty); tty_free(&c->tty);
free(c->ttyname); free(c->ttyname);
free(c->clipboard_panes);
free(c->term_name); free(c->term_name);
free(c->term_type); free(c->term_type);

15
tmux.1
View File

@ -1341,11 +1341,12 @@ and sets an environment variable for the newly created session; it may be
specified multiple times. specified multiple times.
.Tg refresh .Tg refresh
.It Xo Ic refresh-client .It Xo Ic refresh-client
.Op Fl cDlLRSU .Op Fl cDLRSU
.Op Fl A Ar pane:state .Op Fl A Ar pane:state
.Op Fl B Ar name:what:format .Op Fl B Ar name:what:format
.Op Fl C Ar size .Op Fl C Ar size
.Op Fl f Ar flags .Op Fl f Ar flags
.Op Fl l Op Ar target-pane
.Op Fl t Ar target-client .Op Fl t Ar target-client
.Op Ar adjustment .Op Ar adjustment
.Xc .Xc
@ -1459,7 +1460,11 @@ sets a comma-separated list of client flags, see
.Fl l .Fl l
requests the clipboard from the client using the requests the clipboard from the client using the
.Xr xterm 1 .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 .Pp
.Fl L , .Fl L ,
.Fl R , .Fl R ,
@ -5057,6 +5062,8 @@ The following variables are available, where appropriate:
.It Li "client_termname" Ta "" Ta "Terminal name of client" .It Li "client_termname" Ta "" Ta "Terminal name of client"
.It Li "client_termtype" Ta "" Ta "Terminal type of client, if available" .It Li "client_termtype" Ta "" Ta "Terminal type of client, if available"
.It Li "client_tty" Ta "" Ta "Pseudo terminal of client" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client"
.It Li "client_uid" Ta "" Ta "UID of client process"
.It Li "client_user" Ta "" Ta "User of client process"
.It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" .It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8"
.It Li "client_width" Ta "" Ta "Width of client" .It Li "client_width" Ta "" Ta "Width of client"
.It Li "client_written" Ta "" Ta "Bytes written to client" .It Li "client_written" Ta "" Ta "Bytes written to client"
@ -5174,6 +5181,8 @@ The following variables are available, where appropriate:
.It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "session_windows" Ta "" Ta "Number of windows in session"
.It Li "socket_path" Ta "" Ta "Server socket path" .It Li "socket_path" Ta "" Ta "Server socket path"
.It Li "start_time" Ta "" Ta "Server start time" .It Li "start_time" Ta "" Ta "Server start time"
.It Li "uid" Ta "" Ta "Server UID"
.It Li "user" Ta "" Ta "Server user"
.It Li "version" Ta "" Ta "Server version" .It Li "version" Ta "" Ta "Server version"
.It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_active" Ta "" Ta "1 if window active"
.It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window" .It Li "window_active_clients" Ta "" Ta "Number of clients viewing this window"
@ -5188,7 +5197,6 @@ The following variables are available, where appropriate:
.It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels" .It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels"
.It Li "window_end_flag" Ta "" Ta "1 if window has the highest index" .It Li "window_end_flag" Ta "" Ta "1 if window has the highest index"
.It Li "window_flags" Ta "#F" Ta "Window flags with # escaped as ##" .It Li "window_flags" Ta "#F" Ta "Window flags with # escaped as ##"
.It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped"
.It Li "window_format" Ta "" Ta "1 if format is for a window" .It Li "window_format" Ta "" Ta "1 if format is for a window"
.It Li "window_height" Ta "" Ta "Height of window" .It Li "window_height" Ta "" Ta "Height of window"
.It Li "window_id" Ta "" Ta "Unique window ID" .It Li "window_id" Ta "" Ta "Unique window ID"
@ -5203,6 +5211,7 @@ The following variables are available, where appropriate:
.It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client" .It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client"
.It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client" .It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client"
.It Li "window_panes" Ta "" Ta "Number of panes in window" .It Li "window_panes" Ta "" Ta "Number of panes in window"
.It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped"
.It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" .It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert"
.It Li "window_stack_index" Ta "" Ta "Index in session most recent stack" .It Li "window_stack_index" Ta "" Ta "Index in session most recent stack"
.It Li "window_start_flag" Ta "" Ta "1 if window has the lowest index" .It Li "window_start_flag" Ta "" Ta "1 if window has the lowest index"

16
tmux.h
View File

@ -1332,7 +1332,7 @@ LIST_HEAD(tty_terms, tty_term);
struct tty { struct tty {
struct client *client; struct client *client;
struct event start_timer; struct event start_timer;
struct event query_timer; struct event clipboard_timer;
u_int sx; u_int sx;
u_int sy; u_int sy;
@ -1765,6 +1765,7 @@ struct client {
#define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL #define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL
#define CLIENT_CONTROL_WAITEXIT 0x200000000ULL #define CLIENT_CONTROL_WAITEXIT 0x200000000ULL
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL #define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL
#define CLIENT_ALLREDRAWFLAGS \ #define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \ (CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \ CLIENT_REDRAWSTATUS| \
@ -1811,7 +1812,10 @@ struct client {
prompt_free_cb prompt_freecb; prompt_free_cb prompt_freecb;
void *prompt_data; void *prompt_data;
u_int prompt_hindex[PROMPT_NTYPES]; u_int prompt_hindex[PROMPT_NTYPES];
enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode; enum {
PROMPT_ENTRY,
PROMPT_COMMAND
} prompt_mode;
struct utf8_data *prompt_saved; struct utf8_data *prompt_saved;
#define PROMPT_SINGLE 0x1 #define PROMPT_SINGLE 0x1
#define PROMPT_NUMERIC 0x2 #define PROMPT_NUMERIC 0x2
@ -1842,6 +1846,9 @@ struct client {
struct client_files files; struct client_files files;
u_int *clipboard_panes;
u_int clipboard_npanes;
TAILQ_ENTRY(client) entry; TAILQ_ENTRY(client) entry;
}; };
TAILQ_HEAD(clients, client); TAILQ_HEAD(clients, client);
@ -2016,6 +2023,7 @@ void proc_remove_peer(struct tmuxpeer *);
void proc_kill_peer(struct tmuxpeer *); void proc_kill_peer(struct tmuxpeer *);
void proc_toggle_log(struct tmuxproc *); void proc_toggle_log(struct tmuxproc *);
pid_t proc_fork_and_daemon(int *); pid_t proc_fork_and_daemon(int *);
uid_t proc_get_peer_uid(struct tmuxpeer *);
/* cfg.c */ /* cfg.c */
extern int cfg_finished; extern int cfg_finished;
@ -2229,7 +2237,7 @@ void tty_reset(struct tty *);
void tty_region_off(struct tty *); void tty_region_off(struct tty *);
void tty_margin_off(struct tty *); void tty_margin_off(struct tty *);
void tty_cursor(struct tty *, u_int, u_int); 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_putcode(struct tty *, enum tty_code_code);
void tty_putcode1(struct tty *, enum tty_code_code, int); void tty_putcode1(struct tty *, enum tty_code_code, int);
void tty_putcode2(struct tty *, enum tty_code_code, int, int); void tty_putcode2(struct tty *, enum tty_code_code, int, int);
@ -2675,6 +2683,8 @@ void input_parse_pane(struct window_pane *);
void input_parse_buffer(struct window_pane *, u_char *, size_t); void input_parse_buffer(struct window_pane *, u_char *, size_t);
void input_parse_screen(struct input_ctx *, struct screen *, void input_parse_screen(struct input_ctx *, struct screen *,
screen_write_init_ctx_cb, void *, u_char *, size_t); 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 */ /* input-key.c */
void input_key_build(void); void input_key_build(void);

View File

@ -1154,12 +1154,14 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size,
* partial. * partial.
*/ */
static int static int
tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size)
size_t *size)
{ {
struct client *c = tty->client;
struct window_pane *wp;
size_t end, terminator, needed; size_t end, terminator, needed;
char *copy, *out; char *copy, *out;
int outlen; int outlen;
u_int i;
*size = 0; *size = 0;
@ -1221,6 +1223,7 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len,
if (~tty->flags & TTY_OSC52QUERY) if (~tty->flags & TTY_OSC52QUERY)
return (0); return (0);
tty->flags &= ~TTY_OSC52QUERY; tty->flags &= ~TTY_OSC52QUERY;
evtimer_del(&tty->clipboard_timer);
/* It has to be a string so copy it. */ /* It has to be a string so copy it. */
copy = xmalloc(end + 1); copy = xmalloc(end + 1);
@ -1237,9 +1240,20 @@ tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len,
} }
free(copy); free(copy);
/* Create a new paste buffer. */ /* Create a new paste buffer and forward to panes. */
log_debug("%s: %.*s", __func__, outlen, out); log_debug("%s: %.*s", __func__, outlen, out);
if (c->flags & CLIENT_CLIPBOARDBUFFER) {
paste_add(NULL, out, outlen); 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); return (0);
} }

17
tty.c
View File

@ -2921,24 +2921,29 @@ tty_default_attributes(struct tty *tty, const struct grid_cell *defaults,
} }
static void 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 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; tty->flags &= ~TTY_OSC52QUERY;
} }
void void
tty_send_osc52_query(struct tty *tty) tty_clipboard_query(struct tty *tty)
{ {
struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT }; struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT };
if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY)) if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY))
return; return;
tty_putcode_ptr2(tty, TTYC_MS, "", "?"); tty_putcode_ptr2(tty, TTYC_MS, "", "?");
tty->flags |= TTY_OSC52QUERY; tty->flags |= TTY_OSC52QUERY;
evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty);
evtimer_set(&tty->query_timer, tty_query_timer_callback, tty); evtimer_add(&tty->clipboard_timer, &tv);
evtimer_add(&tty->query_timer, &tv);
} }