diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index df1d2135..0f45c2d5 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -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); diff --git a/input.c b/input.c index 7954099d..41cdfb70 100644 --- a/input.c +++ b/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); diff --git a/tmux.1 b/tmux.1 index fac01887..1111a0e4 100644 --- a/tmux.1 +++ b/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 diff --git a/tty-keys.c b/tty-keys.c index 69d01faf..a0f608c0 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -19,7 +19,10 @@ #include #include +#include + #include +#include #include #include #include @@ -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.