mirror of
https://github.com/tmux/tmux.git
synced 2025-01-05 23:38:48 +00:00
Support OSC 52 ? to read the top buffer inside tmux (when set-clipboard
is changed to on), also add refresh-client -l to ask tmux to use the same mechanism to get the clipboard from the terminal outside tmux. GitHub issue 1477.
This commit is contained in:
parent
bc0e527f32
commit
a51668ca06
@ -31,8 +31,8 @@ const struct cmd_entry cmd_refresh_client_entry = {
|
||||
.name = "refresh-client",
|
||||
.alias = "refresh",
|
||||
|
||||
.args = { "C:St:", 0, 0 },
|
||||
.usage = "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE,
|
||||
.args = { "C:lSt:", 0, 0 },
|
||||
.usage = "[-lS] [-C size] " CMD_TARGET_CLIENT_USAGE,
|
||||
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.exec = cmd_refresh_client_exec
|
||||
@ -49,7 +49,10 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
|
||||
return (CMD_RETURN_ERROR);
|
||||
|
||||
if (args_has(args, 'C')) {
|
||||
if (args_has(args, 'l')) {
|
||||
if (c->session != NULL)
|
||||
tty_putcode_ptr2(&c->tty, TTYC_MS, "", "?");
|
||||
} else if (args_has(args, 'C')) {
|
||||
if ((size = args_get(args, 'C')) == NULL) {
|
||||
cmdq_error(item, "missing size");
|
||||
return (CMD_RETURN_ERROR);
|
||||
|
118
input.c
118
input.c
@ -93,6 +93,10 @@ struct input_ctx {
|
||||
u_char *input_buf;
|
||||
size_t input_len;
|
||||
size_t input_space;
|
||||
enum {
|
||||
INPUT_END_ST,
|
||||
INPUT_END_BEL
|
||||
} input_end;
|
||||
|
||||
struct input_param param_list[24];
|
||||
u_int param_list_len;
|
||||
@ -126,11 +130,11 @@ static void input_set_state(struct window_pane *,
|
||||
const struct input_transition *);
|
||||
static void input_reset_cell(struct input_ctx *);
|
||||
|
||||
static void input_osc_4(struct window_pane *, const char *);
|
||||
static void input_osc_10(struct window_pane *, const char *);
|
||||
static void input_osc_11(struct window_pane *, const char *);
|
||||
static void input_osc_52(struct window_pane *, const char *);
|
||||
static void input_osc_104(struct window_pane *, const char *);
|
||||
static void input_osc_4(struct input_ctx *, const char *);
|
||||
static void input_osc_10(struct input_ctx *, const char *);
|
||||
static void input_osc_11(struct input_ctx *, const char *);
|
||||
static void input_osc_52(struct input_ctx *, const char *);
|
||||
static void input_osc_104(struct input_ctx *, const char *);
|
||||
|
||||
/* Transition entry/exit handlers. */
|
||||
static void input_clear(struct input_ctx *);
|
||||
@ -161,6 +165,7 @@ static void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *);
|
||||
static void input_csi_dispatch_sgr(struct input_ctx *);
|
||||
static int input_dcs_dispatch(struct input_ctx *);
|
||||
static int input_top_bit_set(struct input_ctx *);
|
||||
static int input_end_bel(struct input_ctx *);
|
||||
|
||||
/* Command table comparison function. */
|
||||
static int input_table_compare(const void *, const void *);
|
||||
@ -487,7 +492,7 @@ static const struct input_transition input_state_esc_enter_table[] = {
|
||||
{ -1, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
/* esc_interm state table. */
|
||||
/* esc_intermediate state table. */
|
||||
static const struct input_transition input_state_esc_intermediate_table[] = {
|
||||
INPUT_STATE_ANYWHERE,
|
||||
|
||||
@ -602,7 +607,7 @@ static const struct input_transition input_state_dcs_parameter_table[] = {
|
||||
{ -1, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
/* dcs_interm state table. */
|
||||
/* dcs_intermediate state table. */
|
||||
static const struct input_transition input_state_dcs_intermediate_table[] = {
|
||||
INPUT_STATE_ANYWHERE,
|
||||
|
||||
@ -655,12 +660,12 @@ static const struct input_transition input_state_dcs_ignore_table[] = {
|
||||
static const struct input_transition input_state_osc_string_table[] = {
|
||||
INPUT_STATE_ANYWHERE,
|
||||
|
||||
{ 0x00, 0x06, NULL, NULL },
|
||||
{ 0x07, 0x07, NULL, &input_state_ground },
|
||||
{ 0x08, 0x17, NULL, NULL },
|
||||
{ 0x19, 0x19, NULL, NULL },
|
||||
{ 0x1c, 0x1f, NULL, NULL },
|
||||
{ 0x20, 0xff, input_input, NULL },
|
||||
{ 0x00, 0x06, NULL, NULL },
|
||||
{ 0x07, 0x07, input_end_bel, &input_state_ground },
|
||||
{ 0x08, 0x17, NULL, NULL },
|
||||
{ 0x19, 0x19, NULL, NULL },
|
||||
{ 0x1c, 0x1f, NULL, NULL },
|
||||
{ 0x20, 0xff, input_input, NULL },
|
||||
|
||||
{ -1, -1, NULL, NULL }
|
||||
};
|
||||
@ -993,8 +998,8 @@ input_get(struct input_ctx *ictx, u_int validx, int minval, int defval)
|
||||
static void
|
||||
input_reply(struct input_ctx *ictx, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *reply;
|
||||
va_list ap;
|
||||
char *reply;
|
||||
|
||||
va_start(ap, fmt);
|
||||
xvasprintf(&reply, fmt, ap);
|
||||
@ -1019,6 +1024,8 @@ input_clear(struct input_ctx *ictx)
|
||||
*ictx->input_buf = '\0';
|
||||
ictx->input_len = 0;
|
||||
|
||||
ictx->input_end = INPUT_END_ST;
|
||||
|
||||
ictx->flags &= ~INPUT_DISCARD;
|
||||
}
|
||||
|
||||
@ -2046,6 +2053,17 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
|
||||
}
|
||||
}
|
||||
|
||||
/* End of input with BEL. */
|
||||
static int
|
||||
input_end_bel(struct input_ctx *ictx)
|
||||
{
|
||||
log_debug("%s", __func__);
|
||||
|
||||
ictx->input_end = INPUT_END_BEL;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* DCS string started. */
|
||||
static void
|
||||
input_enter_dcs(struct input_ctx *ictx)
|
||||
@ -2102,7 +2120,8 @@ input_exit_osc(struct input_ctx *ictx)
|
||||
if (ictx->input_len < 1 || *p < '0' || *p > '9')
|
||||
return;
|
||||
|
||||
log_debug("%s: \"%s\"", __func__, p);
|
||||
log_debug("%s: \"%s\" (end %s)", __func__, p,
|
||||
ictx->input_end == INPUT_END_ST ? "ST" : "BEL");
|
||||
|
||||
option = 0;
|
||||
while (*p >= '0' && *p <= '9')
|
||||
@ -2119,23 +2138,23 @@ input_exit_osc(struct input_ctx *ictx)
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
input_osc_4(ictx->wp, p);
|
||||
input_osc_4(ictx, p);
|
||||
break;
|
||||
case 10:
|
||||
input_osc_10(ictx->wp, p);
|
||||
input_osc_10(ictx, p);
|
||||
break;
|
||||
case 11:
|
||||
input_osc_11(ictx->wp, p);
|
||||
input_osc_11(ictx, p);
|
||||
break;
|
||||
case 12:
|
||||
if (utf8_isvalid(p) && *p != '?') /* ? is colour request */
|
||||
screen_set_cursor_colour(ictx->ctx.s, p);
|
||||
break;
|
||||
case 52:
|
||||
input_osc_52(ictx->wp, p);
|
||||
input_osc_52(ictx, p);
|
||||
break;
|
||||
case 104:
|
||||
input_osc_104(ictx->wp, p);
|
||||
input_osc_104(ictx, p);
|
||||
break;
|
||||
case 112:
|
||||
if (*p == '\0') /* no arguments allowed */
|
||||
@ -2237,11 +2256,12 @@ input_top_bit_set(struct input_ctx *ictx)
|
||||
|
||||
/* Handle the OSC 4 sequence for setting (multiple) palette entries. */
|
||||
static void
|
||||
input_osc_4(struct window_pane *wp, const char *p)
|
||||
input_osc_4(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
char *copy, *s, *next = NULL;
|
||||
long idx;
|
||||
u_int r, g, b;
|
||||
struct window_pane *wp = ictx->wp;
|
||||
char *copy, *s, *next = NULL;
|
||||
long idx;
|
||||
u_int r, g, b;
|
||||
|
||||
copy = s = xstrdup(p);
|
||||
while (s != NULL && *s != '\0') {
|
||||
@ -2271,9 +2291,10 @@ bad:
|
||||
|
||||
/* Handle the OSC 10 sequence for setting foreground colour. */
|
||||
static void
|
||||
input_osc_10(struct window_pane *wp, const char *p)
|
||||
input_osc_10(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
u_int r, g, b;
|
||||
struct window_pane *wp = ictx->wp;
|
||||
u_int r, g, b;
|
||||
|
||||
if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3)
|
||||
goto bad;
|
||||
@ -2289,9 +2310,10 @@ bad:
|
||||
|
||||
/* Handle the OSC 11 sequence for setting background colour. */
|
||||
static void
|
||||
input_osc_11(struct window_pane *wp, const char *p)
|
||||
input_osc_11(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
u_int r, g, b;
|
||||
struct window_pane *wp = ictx->wp;
|
||||
u_int r, g, b;
|
||||
|
||||
if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3)
|
||||
goto bad;
|
||||
@ -2307,13 +2329,16 @@ bad:
|
||||
|
||||
/* Handle the OSC 52 sequence for setting the clipboard. */
|
||||
static void
|
||||
input_osc_52(struct window_pane *wp, const char *p)
|
||||
input_osc_52(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
struct window_pane *wp = ictx->wp;
|
||||
char *end;
|
||||
const char *buf;
|
||||
size_t len;
|
||||
u_char *out;
|
||||
int outlen, state;
|
||||
struct screen_write_ctx ctx;
|
||||
struct paste_buffer *pb;
|
||||
|
||||
state = options_get_number(global_options, "set-clipboard");
|
||||
if (state != 2)
|
||||
@ -2324,6 +2349,32 @@ input_osc_52(struct window_pane *wp, const char *p)
|
||||
end++;
|
||||
if (*end == '\0')
|
||||
return;
|
||||
log_debug("%s: %s", __func__, end);
|
||||
|
||||
if (strcmp(end, "?") == 0) {
|
||||
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) {
|
||||
abort();
|
||||
free(out);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
outlen = 0;
|
||||
out = NULL;
|
||||
}
|
||||
bufferevent_write(wp->event, "\033]52;;", 6);
|
||||
if (outlen != 0)
|
||||
bufferevent_write(wp->event, out, outlen);
|
||||
if (ictx->input_end == INPUT_END_BEL)
|
||||
bufferevent_write(wp->event, "\007", 1);
|
||||
else
|
||||
bufferevent_write(wp->event, "\033\\", 2);
|
||||
free(out);
|
||||
return;
|
||||
}
|
||||
|
||||
len = (strlen(end) / 4) * 3;
|
||||
if (len == 0)
|
||||
@ -2345,10 +2396,11 @@ input_osc_52(struct window_pane *wp, const char *p)
|
||||
|
||||
/* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */
|
||||
static void
|
||||
input_osc_104(struct window_pane *wp, const char *p)
|
||||
input_osc_104(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
char *copy, *s;
|
||||
long idx;
|
||||
struct window_pane *wp = ictx->wp;
|
||||
char *copy, *s;
|
||||
long idx;
|
||||
|
||||
if (*p == '\0') {
|
||||
window_pane_reset_palette(wp);
|
||||
|
10
tmux.1
10
tmux.1
@ -917,8 +917,8 @@ is used, the
|
||||
.Ic update-environment
|
||||
option will not be applied.
|
||||
.It Xo Ic refresh-client
|
||||
.Op Fl lS
|
||||
.Op Fl C Ar width,height
|
||||
.Op Fl S
|
||||
.Op Fl t Ar target-client
|
||||
.Xc
|
||||
.D1 (alias: Ic refresh )
|
||||
@ -931,6 +931,10 @@ is specified, only update the client's status line.
|
||||
.Pp
|
||||
.Fl C
|
||||
sets the width and height of a control client.
|
||||
.Fl l
|
||||
requests the clipboard from the client using the
|
||||
.Xr xterm 1
|
||||
escape sequence and stores it in a new paste buffer.
|
||||
.It Xo Ic rename-session
|
||||
.Op Fl t Ar target-session
|
||||
.Ar new-name
|
||||
@ -3890,7 +3894,7 @@ option.
|
||||
.El
|
||||
.Pp
|
||||
When a pane is first created, its title is the hostname.
|
||||
A pane's title can be set via the OSC title setting sequence, for example:
|
||||
A pane's title can be set via the title setting escape sequence, for example:
|
||||
.Bd -literal -offset indent
|
||||
$ printf '\e033]2;My Title\e033\e\e'
|
||||
.Ed
|
||||
@ -4444,7 +4448,7 @@ Indicate that the terminal supports the
|
||||
.Ql direct colour
|
||||
RGB escape sequence (for example, \ee[38;2;255;255;255m).
|
||||
.Pp
|
||||
If supported, this is used for the OSC initialize colour escape sequence (which
|
||||
If supported, this is used for the initialize colour escape sequence (which
|
||||
may be enabled by adding the
|
||||
.Ql initc
|
||||
and
|
||||
|
103
tty-keys.c
103
tty-keys.c
@ -19,7 +19,10 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <resolv.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
@ -44,6 +47,8 @@ static int tty_keys_next1(struct tty *, const char *, size_t, key_code *,
|
||||
size_t *, int);
|
||||
static void tty_keys_callback(int, short, void *);
|
||||
static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *);
|
||||
static int tty_keys_clipboard(struct tty *, const char *, size_t,
|
||||
size_t *);
|
||||
static int tty_keys_device_attributes(struct tty *, const char *, size_t,
|
||||
size_t *);
|
||||
|
||||
@ -571,6 +576,17 @@ tty_keys_next(struct tty *tty)
|
||||
return (0);
|
||||
log_debug("%s: keys are %zu (%.*s)", c->name, len, (int)len, buf);
|
||||
|
||||
/* Is this a clipboard response? */
|
||||
switch (tty_keys_clipboard(tty, buf, len, &size)) {
|
||||
case 0: /* yes */
|
||||
key = KEYC_UNKNOWN;
|
||||
goto complete_key;
|
||||
case -1: /* no, or not valid */
|
||||
break;
|
||||
case 1: /* partial */
|
||||
goto partial_key;
|
||||
}
|
||||
|
||||
/* Is this a device attributes response? */
|
||||
switch (tty_keys_device_attributes(tty, buf, len, &size)) {
|
||||
case 0: /* yes */
|
||||
@ -871,6 +887,93 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle OSC 52 clipboard input. Returns 0 for success, -1 for failure, 1 for
|
||||
* partial.
|
||||
*/
|
||||
static int
|
||||
tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len,
|
||||
size_t *size)
|
||||
{
|
||||
size_t end, terminator, needed;
|
||||
char *copy, *out;
|
||||
int outlen;
|
||||
|
||||
*size = 0;
|
||||
|
||||
/* First three bytes are always \033]52;. */
|
||||
if (buf[0] != '\033')
|
||||
return (-1);
|
||||
if (len == 1)
|
||||
return (1);
|
||||
if (buf[1] != ']')
|
||||
return (-1);
|
||||
if (len == 2)
|
||||
return (1);
|
||||
if (buf[2] != '5')
|
||||
return (-1);
|
||||
if (len == 3)
|
||||
return (1);
|
||||
if (buf[3] != '2')
|
||||
return (-1);
|
||||
if (len == 4)
|
||||
return (1);
|
||||
if (buf[4] != ';')
|
||||
return (-1);
|
||||
if (len == 5)
|
||||
return (1);
|
||||
|
||||
/* Find the terminator if any. */
|
||||
for (end = 5; end < len; end++) {
|
||||
if (buf[end] == '\007') {
|
||||
terminator = 1;
|
||||
break;
|
||||
}
|
||||
if (end > 5 && buf[end - 1] == '\033' && buf[end] == '\\') {
|
||||
terminator = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (end == len)
|
||||
return (1);
|
||||
*size = end + terminator;
|
||||
|
||||
/* Skip the initial part. */
|
||||
buf += 5;
|
||||
end -= 5;
|
||||
|
||||
/* Get the second argument. */
|
||||
while (end != 0 && *buf != ';') {
|
||||
buf++;
|
||||
end--;
|
||||
}
|
||||
if (end == 0 || end == 1)
|
||||
return (0);
|
||||
buf++;
|
||||
end--;
|
||||
|
||||
/* It has to be a string so copy it. */
|
||||
copy = xmalloc(end + 1);
|
||||
memcpy(copy, buf, end);
|
||||
copy[end] = '\0';
|
||||
|
||||
/* Convert from base64. */
|
||||
needed = (end / 4) * 3;
|
||||
out = xmalloc(needed);
|
||||
if ((outlen = b64_pton(copy, out, len)) == -1) {
|
||||
free(out);
|
||||
free(copy);
|
||||
return (0);
|
||||
}
|
||||
free(copy);
|
||||
|
||||
/* Create a new paste buffer. */
|
||||
log_debug("%s: %.*s", __func__, outlen, out);
|
||||
paste_add(out, outlen);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle device attributes input. Returns 0 for success, -1 for failure, 1 for
|
||||
* partial.
|
||||
|
Loading…
Reference in New Issue
Block a user