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"

158
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;
@ -1685,50 +1685,50 @@ typedef int (*overlay_key_cb)(struct client *, void *, struct key_event *);
typedef void (*overlay_free_cb)(struct client *, void *); typedef void (*overlay_free_cb)(struct client *, void *);
typedef void (*overlay_resize_cb)(struct client *, void *); typedef void (*overlay_resize_cb)(struct client *, void *);
struct client { struct client {
const char *name; const char *name;
struct tmuxpeer *peer; struct tmuxpeer *peer;
struct cmdq_list *queue; struct cmdq_list *queue;
struct client_windows windows; struct client_windows windows;
struct control_state *control_state; struct control_state *control_state;
u_int pause_age; u_int pause_age;
pid_t pid; pid_t pid;
int fd; int fd;
int out_fd; int out_fd;
struct event event; struct event event;
int retval; int retval;
struct timeval creation_time; struct timeval creation_time;
struct timeval activity_time; struct timeval activity_time;
struct environ *environ; struct environ *environ;
struct format_job_tree *jobs; struct format_job_tree *jobs;
char *title; char *title;
const char *cwd; const char *cwd;
char *term_name; char *term_name;
int term_features; int term_features;
char *term_type; char *term_type;
char **term_caps; char **term_caps;
u_int term_ncaps; u_int term_ncaps;
char *ttyname; char *ttyname;
struct tty tty; struct tty tty;
size_t written; size_t written;
size_t discarded; size_t discarded;
size_t redraw; size_t redraw;
struct event repeat_timer; struct event repeat_timer;
struct event click_timer; struct event click_timer;
u_int click_button; u_int click_button;
struct mouse_event click_event; struct mouse_event click_event;
struct status_line status; struct status_line status;
#define CLIENT_TERMINAL 0x1 #define CLIENT_TERMINAL 0x1
#define CLIENT_LOGIN 0x2 #define CLIENT_LOGIN 0x2
@ -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| \
@ -1776,73 +1777,79 @@ struct client {
(CLIENT_DEAD| \ (CLIENT_DEAD| \
CLIENT_SUSPENDED| \ CLIENT_SUSPENDED| \
CLIENT_EXIT) CLIENT_EXIT)
#define CLIENT_NODETACHFLAGS \ #define CLIENT_NODETACHFLAGS \
(CLIENT_DEAD| \ (CLIENT_DEAD| \
CLIENT_EXIT) CLIENT_EXIT)
#define CLIENT_NOSIZEFLAGS \ #define CLIENT_NOSIZEFLAGS \
(CLIENT_DEAD| \ (CLIENT_DEAD| \
CLIENT_SUSPENDED| \ CLIENT_SUSPENDED| \
CLIENT_EXIT) CLIENT_EXIT)
uint64_t flags; uint64_t flags;
enum { enum {
CLIENT_EXIT_RETURN, CLIENT_EXIT_RETURN,
CLIENT_EXIT_SHUTDOWN, CLIENT_EXIT_SHUTDOWN,
CLIENT_EXIT_DETACH CLIENT_EXIT_DETACH
} exit_type; } exit_type;
enum msgtype exit_msgtype; enum msgtype exit_msgtype;
char *exit_session; char *exit_session;
char *exit_message; char *exit_message;
struct key_table *keytable; struct key_table *keytable;
uint64_t redraw_panes; uint64_t redraw_panes;
int message_ignore_keys; int message_ignore_keys;
int message_ignore_styles; int message_ignore_styles;
char *message_string; char *message_string;
struct event message_timer; struct event message_timer;
char *prompt_string; char *prompt_string;
struct utf8_data *prompt_buffer; struct utf8_data *prompt_buffer;
char *prompt_last; char *prompt_last;
size_t prompt_index; size_t prompt_index;
prompt_input_cb prompt_inputcb; prompt_input_cb prompt_inputcb;
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 {
struct utf8_data *prompt_saved; PROMPT_ENTRY,
PROMPT_COMMAND
} prompt_mode;
struct utf8_data *prompt_saved;
#define PROMPT_SINGLE 0x1 #define PROMPT_SINGLE 0x1
#define PROMPT_NUMERIC 0x2 #define PROMPT_NUMERIC 0x2
#define PROMPT_INCREMENTAL 0x4 #define PROMPT_INCREMENTAL 0x4
#define PROMPT_NOFORMAT 0x8 #define PROMPT_NOFORMAT 0x8
#define PROMPT_KEY 0x10 #define PROMPT_KEY 0x10
int prompt_flags; int prompt_flags;
enum prompt_type prompt_type; enum prompt_type prompt_type;
int prompt_cursor; int prompt_cursor;
struct session *session; struct session *session;
struct session *last_session; struct session *last_session;
int references; int references;
void *pan_window; void *pan_window;
u_int pan_ox; u_int pan_ox;
u_int pan_oy; u_int pan_oy;
overlay_check_cb overlay_check; overlay_check_cb overlay_check;
overlay_mode_cb overlay_mode; overlay_mode_cb overlay_mode;
overlay_draw_cb overlay_draw; overlay_draw_cb overlay_draw;
overlay_key_cb overlay_key; overlay_key_cb overlay_key;
overlay_free_cb overlay_free; overlay_free_cb overlay_free;
overlay_resize_cb overlay_resize; overlay_resize_cb overlay_resize;
void *overlay_data; void *overlay_data;
struct event overlay_timer; struct event overlay_timer;
struct client_files files; struct client_files files;
TAILQ_ENTRY(client) entry; u_int *clipboard_panes;
u_int clipboard_npanes;
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)
{ {
size_t end, terminator, needed; struct client *c = tty->client;
char *copy, *out; struct window_pane *wp;
int outlen; size_t end, terminator, needed;
char *copy, *out;
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);
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); 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);
} }