From 968382aa6a4b9c71fbc221aa4f0e899f6a83a260 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 28 Nov 2019 12:35:18 +0000 Subject: [PATCH 01/29] Pass through SIXEL DCS sequences (treat similarly to the passthrough escape sequence) if it appears the terminal outside supports them. --- input.c | 18 +++++++++++++----- screen-write.c | 15 +++++++++++++++ tmux.1 | 2 ++ tmux.h | 7 ++++++- tty-keys.c | 9 ++++++--- tty-term.c | 1 + tty.c | 15 ++++++++++++++- 7 files changed, 57 insertions(+), 10 deletions(-) diff --git a/input.c b/input.c index 452eac7f..b8d2f102 100644 --- a/input.c +++ b/input.c @@ -1305,8 +1305,8 @@ input_csi_dispatch(struct input_ctx *ictx) if (ictx->flags & INPUT_DISCARD) return (0); - log_debug("%s: '%c' \"%s\" \"%s\"", - __func__, ictx->ch, ictx->interm_buf, ictx->param_buf); + log_debug("%s: '%c' \"%s\" \"%s\"", __func__, ictx->ch, + ictx->interm_buf, ictx->param_buf); if (input_split(ictx) != 0) return (0); @@ -2151,19 +2151,27 @@ static int input_dcs_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; - u_char *buf = ictx->input_buf; - size_t len = ictx->input_len; + u_char *buf = ictx->input_buf, *pbuf = ictx->param_buf; + size_t len = ictx->input_len, plen = ictx->param_len; const char prefix[] = "tmux;"; const u_int prefixlen = (sizeof prefix) - 1; if (ictx->flags & INPUT_DISCARD) return (0); - log_debug("%s: \"%s\"", __func__, buf); + log_debug("%s: \"%s\" \"%s\" \"%s\"", __func__, buf, ictx->interm_buf, + ictx->param_buf); if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0) screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen); + if (buf[0] == 'q') { + screen_write_rawsixel(sctx, (char *)"\033P", 2, 1); + screen_write_rawsixel(sctx, pbuf, plen, 1); + screen_write_rawsixel(sctx, buf, len, 1); + screen_write_rawsixel(sctx, (char *)"\033\\", 2, 0); + } + return (0); } diff --git a/screen-write.c b/screen-write.c index 43cb42b4..9bd28836 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1671,3 +1671,18 @@ screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) tty_write(tty_cmd_rawstring, &ttyctx); } + +/* Write unmodified SIXEL data. */ +void +screen_write_rawsixel(struct screen_write_ctx *ctx, u_char *str, u_int len, + int more) +{ + struct tty_ctx ttyctx; + + screen_write_initctx(ctx, &ttyctx); + ttyctx.ptr = str; + ttyctx.num = len; + ttyctx.more = more; + + tty_write(tty_cmd_rawsixel, &ttyctx); +} diff --git a/tmux.1 b/tmux.1 index 9513bd79..aac4625c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5200,6 +5200,8 @@ $ printf '\e033[4 q' If .Em Se is not set, \&Ss with argument 0 will be used to reset the cursor style instead. +.It Em \&Sxl +Indicates that the terminal supports SIXEL. .It Em \&Tc Indicate that the terminal supports the .Ql direct colour diff --git a/tmux.h b/tmux.h index b7220b88..7c901b9d 100644 --- a/tmux.h +++ b/tmux.h @@ -446,6 +446,7 @@ enum tty_code_code { TTYC_SMULX, TTYC_SMUL, TTYC_SMXX, + TTYC_SXL, TTYC_SS, TTYC_TC, TTYC_TSL, @@ -1145,6 +1146,7 @@ struct tty_term { #define TERM_256COLOURS 0x1 #define TERM_EARLYWRAP 0x2 +#define TERM_SIXEL 0x4 int flags; LIST_ENTRY(tty_term) entry; @@ -1248,6 +1250,7 @@ struct tty_ctx { u_int num; void *ptr; + int more; /* * Cursor and region position before the screen was updated - this is @@ -1947,7 +1950,7 @@ void tty_draw_line(struct tty *, struct window_pane *, struct screen *, int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); -void tty_set_type(struct tty *, int); +void tty_set_type(struct tty *, int, int); void tty_write(void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); @@ -1971,6 +1974,7 @@ void tty_cmd_scrolldown(struct tty *, const struct tty_ctx *); void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); +void tty_cmd_rawsixel(struct tty *, const struct tty_ctx *); /* tty-term.c */ extern struct tty_terms tty_terms; @@ -2357,6 +2361,7 @@ void screen_write_collect_add(struct screen_write_ctx *, void screen_write_cell(struct screen_write_ctx *, const struct grid_cell *); void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int); void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int); +void screen_write_rawsixel(struct screen_write_ctx *, u_char *, u_int, int); /* screen-redraw.c */ void screen_redraw_screen(struct client *); diff --git a/tty-keys.c b/tty-keys.c index 6be40d0e..e644addb 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1005,7 +1005,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, u_int i, n = 0; char tmp[64], *endptr, p[32] = { 0 }, *cp, *next; static const char *types[] = TTY_TYPES; - int type; + int type, flags = 0; *size = 0; @@ -1068,9 +1068,12 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, type = TTY_VT520; break; } - for (i = 2; i < n; i++) + for (i = 2; i < n; i++) { log_debug("%s: DA feature: %d", c->name, p[i]); - tty_set_type(tty, type); + if (p[i] == 4) + flags |= TERM_SIXEL; + } + tty_set_type(tty, type, flags); log_debug("%s: received DA %.*s (%s)", c->name, (int)*size, buf, types[type]); diff --git a/tty-term.c b/tty-term.c index 3ac9bc6c..ce3d690a 100644 --- a/tty-term.c +++ b/tty-term.c @@ -252,6 +252,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" }, [TTYC_SETULC] = { TTYCODE_STRING, "Setulc" }, [TTYC_SE] = { TTYCODE_STRING, "Se" }, + [TTYC_SXL] = { TTYCODE_FLAG, "Sxl" }, [TTYC_SGR0] = { TTYCODE_STRING, "sgr0" }, [TTYC_SITM] = { TTYCODE_STRING, "sitm" }, [TTYC_SMACS] = { TTYCODE_STRING, "smacs" }, diff --git a/tty.c b/tty.c index 594f02fa..cae2fd24 100644 --- a/tty.c +++ b/tty.c @@ -436,9 +436,10 @@ tty_free(struct tty *tty) } void -tty_set_type(struct tty *tty, int type) +tty_set_type(struct tty *tty, int type, int flags) { tty->term_type = type; + tty->term_flags |= flags; if (tty_use_margin(tty)) tty_puts(tty, "\033[?69h"); /* DECLRMM */ @@ -1865,6 +1866,18 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) tty_invalidate(tty); } +void +tty_cmd_rawsixel(struct tty *tty, const struct tty_ctx *ctx) +{ + int flags = (tty->term->flags|tty->term_flags); + + if ((flags & TERM_SIXEL) || tty_term_has(tty->term, TTYC_SXL)) { + tty_add(tty, ctx->ptr, ctx->num); + if (!ctx->more) + tty_invalidate(tty); + } +} + static void tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp) { From a5b1e209417b7d3f5b0099642dd317c312f79377 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 28 Nov 2019 14:20:22 +0000 Subject: [PATCH 02/29] Add a flag to disable blocking while sending a SIXEL image (turned off when the buffer hits 0 size). --- tmux.h | 1 + tty.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/tmux.h b/tmux.h index 7c901b9d..b1e7f44e 100644 --- a/tmux.h +++ b/tmux.h @@ -1203,6 +1203,7 @@ struct tty { #define TTY_OPENED 0x20 #define TTY_FOCUS 0x40 #define TTY_BLOCK 0x80 +#define TTY_NOBLOCK 0x100 int flags; struct tty_term *term; diff --git a/tty.c b/tty.c index cae2fd24..da21645f 100644 --- a/tty.c +++ b/tty.c @@ -211,6 +211,11 @@ tty_block_maybe(struct tty *tty) size_t size = EVBUFFER_LENGTH(tty->out); struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL }; + if (size == 0) + tty->flags &= ~TTY_NOBLOCK; + else if (tty->flags & TTY_NOBLOCK) + return (0); + if (size < TTY_BLOCK_START(tty)) return (0); @@ -1872,6 +1877,7 @@ tty_cmd_rawsixel(struct tty *tty, const struct tty_ctx *ctx) int flags = (tty->term->flags|tty->term_flags); if ((flags & TERM_SIXEL) || tty_term_has(tty->term, TTYC_SXL)) { + tty->flags |= TTY_NOBLOCK; tty_add(tty, ctx->ptr, ctx->num); if (!ctx->more) tty_invalidate(tty); From 0a15bbf3f1972dc84c5c84d5128024c1bc4c0074 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 30 Nov 2019 09:15:35 +0000 Subject: [PATCH 03/29] Do not defer redraw if it is just the status line (will need to do more here I think). --- server-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index 2cfd8838..075b0ba1 100644 --- a/server-client.c +++ b/server-client.c @@ -1612,7 +1612,7 @@ server_client_check_redraw(struct client *c) * end up back here. */ needed = 0; - if (c->flags & CLIENT_ALLREDRAWFLAGS) + if (c->flags & (CLIENT_ALLREDRAWFLAGS & ~CLIENT_REDRAWSTATUS)) needed = 1; else { TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { From deb734c7f61c36e18ccc2e4fdab4f06a90575fc9 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 12 Jan 2020 20:20:20 +0000 Subject: [PATCH 04/29] Loop over all DA features, don't skip the first. --- tty-keys.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index 6be40d0e..1d97b7d2 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1038,7 +1038,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, cp = tmp; while ((next = strsep(&cp, ";")) != NULL) { p[n] = strtoul(next, &endptr, 10); - if (*endptr != '\0' && *endptr != ';') + if (*endptr != '\0') p[n] = 0; n++; } @@ -1068,7 +1068,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, type = TTY_VT520; break; } - for (i = 2; i < n; i++) + for (i = 1; i < n; i++) log_debug("%s: DA feature: %d", c->name, p[i]); tty_set_type(tty, type); From 193e637de050e3757698812e9a87b869c87def6c Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 12 Jan 2020 21:07:07 +0000 Subject: [PATCH 05/29] The terminal type was never as much use as I expected so remove it in favour of a couple of flags for the features used (DECSLRM and DECFRA). Also rename the flag for no xenl to be more obvious while here. --- format.c | 3 --- tmux.1 | 1 - tmux.h | 25 ++++--------------------- tty-keys.c | 40 +++++++++------------------------------- tty-term.c | 2 +- tty.c | 23 +++++++++++------------ 6 files changed, 25 insertions(+), 69 deletions(-) diff --git a/format.c b/format.c index eaff6115..f5e7b2ce 100644 --- a/format.c +++ b/format.c @@ -2346,7 +2346,6 @@ format_defaults_client(struct format_tree *ft, struct client *c) struct session *s; const char *name; struct tty *tty = &c->tty; - const char *types[] = TTY_TYPES; if (ft->s == NULL) ft->s = c->session; @@ -2364,8 +2363,6 @@ format_defaults_client(struct format_tree *ft, struct client *c) if (tty->term_name != NULL) format_add(ft, "client_termname", "%s", tty->term_name); - if (tty->term_name != NULL) - format_add(ft, "client_termtype", "%s", types[tty->term_type]); format_add_tv(ft, "client_created", &c->creation_time); format_add_tv(ft, "client_activity", &c->activity_time); diff --git a/tmux.1 b/tmux.1 index 1e8c13af..9c9cfb39 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4227,7 +4227,6 @@ The following variables are available, where appropriate: .It Li "client_readonly" Ta "" Ta "1 if client is readonly" .It Li "client_session" Ta "" Ta "Name of the client's session" .It Li "client_termname" Ta "" Ta "Terminal name of client" -.It Li "client_termtype" Ta "" Ta "Terminal type of client" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" .It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" .It Li "client_width" Ta "" Ta "Width of client" diff --git a/tmux.h b/tmux.h index 0a0fc1b5..7c7dcc99 100644 --- a/tmux.h +++ b/tmux.h @@ -1168,7 +1168,9 @@ struct tty_term { struct tty_code *codes; #define TERM_256COLOURS 0x1 -#define TERM_EARLYWRAP 0x2 +#define TERM_NOXENL 0x2 +#define TERM_DECSLRM 0x4 +#define TERM_DECFRA 0x8 int flags; LIST_ENTRY(tty_term) entry; @@ -1230,16 +1232,6 @@ struct tty { struct tty_term *term; char *term_name; int term_flags; - enum { - TTY_VT100, - TTY_VT101, - TTY_VT102, - TTY_VT220, - TTY_VT320, - TTY_VT420, - TTY_VT520, - TTY_UNKNOWN - } term_type; u_int mouse_last_x; u_int mouse_last_y; @@ -1253,15 +1245,6 @@ struct tty { struct event key_timer; struct tty_key *key_tree; }; -#define TTY_TYPES \ - { "VT100", \ - "VT101", \ - "VT102", \ - "VT220", \ - "VT320", \ - "VT420", \ - "VT520", \ - "Unknown" } /* TTY command context. */ struct tty_ctx { @@ -1992,7 +1975,7 @@ void tty_draw_line(struct tty *, struct window_pane *, struct screen *, int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); -void tty_set_type(struct tty *, int); +void tty_set_flags(struct tty *, int); void tty_write(void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); diff --git a/tty-keys.c b/tty-keys.c index 1d97b7d2..4644340c 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1001,11 +1001,10 @@ static int tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, size_t *size) { - struct client *c = tty->client; - u_int i, n = 0; - char tmp[64], *endptr, p[32] = { 0 }, *cp, *next; - static const char *types[] = TTY_TYPES; - int type; + struct client *c = tty->client; + u_int i, n = 0; + char tmp[64], *endptr, p[32] = { 0 }, *cp, *next; + int flags = 0; *size = 0; @@ -1043,36 +1042,15 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, n++; } - /* Store terminal type. */ - type = TTY_UNKNOWN; + /* Set terminal flags. */ switch (p[0]) { - case 1: - if (p[1] == 2) - type = TTY_VT100; - else if (p[1] == 0) - type = TTY_VT101; - break; - case 6: - type = TTY_VT102; - break; - case 62: - type = TTY_VT220; - break; - case 63: - type = TTY_VT320; - break; - case 64: - type = TTY_VT420; - break; - case 65: - type = TTY_VT520; + case 64: /* VT420 */ + flags |= (TERM_DECFRA|TERM_DECSLRM); break; } for (i = 1; i < n; i++) log_debug("%s: DA feature: %d", c->name, p[i]); - tty_set_type(tty, type); - - log_debug("%s: received DA %.*s (%s)", c->name, (int)*size, buf, - types[type]); + log_debug("%s: received DA %.*s", c->name, (int)*size, buf); + tty_set_flags(tty, flags); return (0); } diff --git a/tty-term.c b/tty-term.c index 52468faf..164ab2f1 100644 --- a/tty-term.c +++ b/tty-term.c @@ -538,7 +538,7 @@ tty_term_find(char *name, int fd, char **cause) * do the best possible. */ if (!tty_term_flag(term, TTYC_XENL)) - term->flags |= TERM_EARLYWRAP; + term->flags |= TERM_NOXENL; /* Generate ACS table. If none is present, use nearest ASCII. */ memset(term->acs, 0, sizeof term->acs); diff --git a/tty.c b/tty.c index 2d7acb17..c9843e90 100644 --- a/tty.c +++ b/tty.c @@ -74,7 +74,7 @@ static void tty_default_attributes(struct tty *, struct window_pane *, u_int); #define tty_use_margin(tty) \ - ((tty)->term_type == TTY_VT420) + ((tty->term->flags|tty->term_flags) & TERM_DECSLRM) #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) @@ -115,9 +115,7 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term) tty->ccolour = xstrdup(""); tty->flags = 0; - tty->term_flags = 0; - tty->term_type = TTY_UNKNOWN; return (0); } @@ -438,9 +436,9 @@ tty_free(struct tty *tty) } void -tty_set_type(struct tty *tty, int type) +tty_set_flags(struct tty *tty, int flags) { - tty->term_type = type; + tty->term_flags |= flags; if (tty_use_margin(tty)) tty_puts(tty, "\033[?69h"); /* DECLRMM */ @@ -543,7 +541,7 @@ tty_putc(struct tty *tty, u_char ch) { const char *acs; - if ((tty->term->flags & TERM_EARLYWRAP) && + if ((tty->term->flags & TERM_NOXENL) && ch >= 0x20 && ch != 0x7f && tty->cy == tty->sy - 1 && tty->cx + 1 >= tty->sx) @@ -569,7 +567,7 @@ tty_putc(struct tty *tty, u_char ch) * where we think it should be after a line wrap - this * means it works on sensible terminals as well. */ - if (tty->term->flags & TERM_EARLYWRAP) + if (tty->term->flags & TERM_NOXENL) tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx); } else tty->cx++; @@ -579,7 +577,7 @@ tty_putc(struct tty *tty, u_char ch) void tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) { - if ((tty->term->flags & TERM_EARLYWRAP) && + if ((tty->term->flags & TERM_NOXENL) && tty->cy == tty->sy - 1 && tty->cx + len >= tty->sx) len = tty->sx - tty->cx - 1; @@ -1129,7 +1127,8 @@ tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny, * background colour isn't default (because it doesn't work * after SGR 0). */ - if (tty->term_type == TTY_VT420 && !COLOUR_DEFAULT(bg)) { + if (((tty->term->flags|tty->term_flags) & TERM_DECFRA) && + !COLOUR_DEFAULT(bg)) { xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x", py + 1, px + 1, py + ny, px + nx); tty_puts(tty, tmp); @@ -1824,7 +1823,7 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) ctx->xoff + ctx->ocx + ctx->num > ctx->ox + ctx->sx)) { if (!ctx->wrapped || !tty_pane_full_width(tty, ctx) || - (tty->term->flags & TERM_EARLYWRAP) || + (tty->term->flags & TERM_NOXENL) || ctx->xoff + ctx->ocx != 0 || ctx->yoff + ctx->ocy != tty->cy + 1 || tty->cx < tty->sx || @@ -1873,7 +1872,7 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp) const struct grid_cell *gcp; /* Skip last character if terminal is stupid. */ - if ((tty->term->flags & TERM_EARLYWRAP) && + if ((tty->term->flags & TERM_NOXENL) && tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1) return; @@ -2032,7 +2031,7 @@ tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, { if (!ctx->wrapped || !tty_pane_full_width(tty, ctx) || - (tty->term->flags & TERM_EARLYWRAP) || + (tty->term->flags & TERM_NOXENL) || ctx->xoff + cx != 0 || ctx->yoff + cy != tty->cy + 1 || tty->cx < tty->sx || From 381333c4a9fd521bee8a0287e648f0c6aeb96a0b Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 12 Jan 2020 22:00:20 +0000 Subject: [PATCH 06/29] Detect iTerm2 and enable DECSLRM. --- tty-keys.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tty.c | 2 +- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/tty-keys.c b/tty-keys.c index 4644340c..5054c424 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -52,6 +52,8 @@ 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 *); +static int tty_keys_device_status_report(struct tty *, const char *, + size_t, size_t *); /* Default raw keys. */ struct tty_default_key_raw { @@ -607,6 +609,17 @@ tty_keys_next(struct tty *tty) goto partial_key; } + /* Is this a device status report response? */ + switch (tty_keys_device_status_report(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 mouse key press? */ switch (tty_keys_mouse(tty, buf, len, &size, &m)) { case 0: /* yes */ @@ -1054,3 +1067,47 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, tty_set_flags(tty, flags); return (0); } + +/* + * Handle device status report input. Returns 0 for success, -1 for failure, 1 + * for partial. + */ +static int +tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, + size_t *size) +{ + struct client *c = tty->client; + u_int i; + char tmp[64]; + int flags = 0; + + *size = 0; + + /* First three bytes are always \033[. */ + if (buf[0] != '\033') + return (-1); + if (len == 1) + return (1); + if (buf[1] != '[') + return (-1); + if (len == 2) + return (1); + + /* Copy the rest up to a 'n'. */ + for (i = 0; i < (sizeof tmp) - 1 && buf[2 + i] != 'n'; i++) { + if (2 + i == len) + return (1); + tmp[i] = buf[2 + i]; + } + if (i == (sizeof tmp) - 1) + return (-1); + tmp[i] = '\0'; + *size = 3 + i; + + /* Set terminal flags. */ + if (strncmp(tmp, "ITERM2 ", 7) == 0) + flags |= TERM_DECSLRM; + log_debug("%s: received DSR %.*s", c->name, (int)*size, buf); + tty_set_flags(tty, flags); + return (0); +} diff --git a/tty.c b/tty.c index c9843e90..2d4b4cc0 100644 --- a/tty.c +++ b/tty.c @@ -327,7 +327,7 @@ tty_start_tty(struct tty *tty) tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } - tty_puts(tty, "\033[c"); + tty_puts(tty, "\033[c\033[1337n"); } tty->flags |= TTY_STARTED; From 04eee2410df4a85005c9562db13a1fec93d7c2e4 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jan 2020 07:51:54 +0000 Subject: [PATCH 07/29] Treat plausible but invalid keys (like C-BSpace) as literal like any other unrecognised string passed to send-keys. Reported by Anthony Sottile in GitHub issue 2049. --- cmd-send-keys.c | 14 +++++++++++--- input-keys.c | 17 +++++++++-------- tmux.h | 4 ++-- window.c | 14 ++++++++------ 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index ddbab6f7..cc04a73f 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -60,6 +60,9 @@ static struct cmdq_item * cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, struct cmdq_item *item, key_code key) { + struct session *s = fs->s; + struct winlink *wl = fs->wl; + struct window_pane *wp = fs->wp; struct window_mode_entry *wme; struct key_table *table; struct key_binding *bd; @@ -68,7 +71,8 @@ cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, if (wme == NULL || wme->mode->key_table == NULL) { if (options_get_number(fs->wp->window->options, "xterm-keys")) key |= KEYC_XTERM; - window_pane_key(fs->wp, item->client, fs->s, fs->wl, key, NULL); + if (window_pane_key(wp, item->client, s, wl, key, NULL) != 0) + return (NULL); return (item); } table = key_bindings_get_table(wme->mode->key_table(wme), 1); @@ -87,6 +91,7 @@ cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs, struct cmdq_item *item, struct args *args, int i) { const char *s = args->argv[i]; + struct cmdq_item *new_item; struct utf8_data *ud, *uc; wchar_t wc; key_code key; @@ -104,8 +109,11 @@ cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs, literal = args_has(args, 'l'); if (!literal) { key = key_string_lookup_string(s); - if (key != KEYC_NONE && key != KEYC_UNKNOWN) - return (cmd_send_keys_inject_key(c, fs, item, key)); + if (key != KEYC_NONE && key != KEYC_UNKNOWN) { + new_item = cmd_send_keys_inject_key(c, fs, item, key); + if (new_item != NULL) + return (new_item); + } literal = 1; } if (literal) { diff --git a/input-keys.c b/input-keys.c index 01f9ad9d..5c7e8b1f 100644 --- a/input-keys.c +++ b/input-keys.c @@ -150,7 +150,7 @@ input_split2(u_int c, u_char *dst) } /* Translate a key code into an output key sequence. */ -void +int input_key(struct window_pane *wp, key_code key, struct mouse_event *m) { const struct input_key_ent *ike; @@ -167,14 +167,14 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) if (KEYC_IS_MOUSE(key)) { if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) input_key_mouse(wp, m); - return; + return (0); } /* Literal keys go as themselves (can't be more than eight bits). */ if (key & KEYC_LITERAL) { ud.data[0] = (u_char)key; bufferevent_write(wp->event, &ud.data[0], 1); - return; + return (0); } /* Is this backspace? */ @@ -195,15 +195,15 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) bufferevent_write(wp->event, "\033", 1); ud.data[0] = justkey; bufferevent_write(wp->event, &ud.data[0], 1); - return; + return (0); } if (justkey > 0x7f && justkey < KEYC_BASE) { if (utf8_split(justkey, &ud) != UTF8_DONE) - return; + return (-1); if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); bufferevent_write(wp->event, ud.data, ud.size); - return; + return (0); } /* @@ -214,7 +214,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) if ((out = xterm_keys_lookup(key)) != NULL) { bufferevent_write(wp->event, out, strlen(out)); free(out); - return; + return (0); } } key &= ~KEYC_XTERM; @@ -237,7 +237,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) } if (i == nitems(input_keys)) { log_debug("key 0x%llx missing", key); - return; + return (-1); } dlen = strlen(ike->data); log_debug("found key 0x%llx: \"%s\"", key, ike->data); @@ -246,6 +246,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); bufferevent_write(wp->event, ike->data, dlen); + return (0); } /* Translate mouse and output. */ diff --git a/tmux.h b/tmux.h index 7c7dcc99..75e483c5 100644 --- a/tmux.h +++ b/tmux.h @@ -2275,7 +2275,7 @@ void input_parse(struct window_pane *); void input_parse_buffer(struct window_pane *, u_char *, size_t); /* input-key.c */ -void input_key(struct window_pane *, key_code, struct mouse_event *); +int input_key(struct window_pane *, key_code, struct mouse_event *); /* xterm-keys.c */ char *xterm_keys_lookup(key_code); @@ -2498,7 +2498,7 @@ int window_pane_set_mode(struct window_pane *, struct args *); void window_pane_reset_mode(struct window_pane *); void window_pane_reset_mode_all(struct window_pane *); -void window_pane_key(struct window_pane *, struct client *, +int window_pane_key(struct window_pane *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); int window_pane_visible(struct window_pane *); diff --git a/window.c b/window.c index 64c95855..bd25b5d1 100644 --- a/window.c +++ b/window.c @@ -1229,7 +1229,7 @@ window_pane_reset_mode_all(struct window_pane *wp) window_pane_reset_mode(wp); } -void +int window_pane_key(struct window_pane *wp, struct client *c, struct session *s, struct winlink *wl, key_code key, struct mouse_event *m) { @@ -1237,23 +1237,24 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, struct window_pane *wp2; if (KEYC_IS_MOUSE(key) && m == NULL) - return; + return (-1); wme = TAILQ_FIRST(&wp->modes); if (wme != NULL) { wp->modelast = time(NULL); if (wme->mode->key != NULL) wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m); - return; + return (0); } if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) - return; + return (0); - input_key(wp, key, m); + if (input_key(wp, key, m) != 0) + return (-1); if (KEYC_IS_MOUSE(key)) - return; + return (0); if (options_get_number(wp->window->options, "synchronize-panes")) { TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (wp2 != wp && @@ -1264,6 +1265,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, input_key(wp2, key, NULL); } } + return (0); } int From 835a6c0cf088437e318783c96b142c7dc26c290f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jan 2020 08:12:53 +0000 Subject: [PATCH 08/29] Be more specific in the DSR we are looking for so it doesn't get confused with mouse sequences. Also set a flag and don't bother checking for it if we have already seen it (same for DA), and don't check if we never asked for it. --- tmux.h | 2 ++ tty-keys.c | 14 ++++++++++++++ tty.c | 5 +++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/tmux.h b/tmux.h index 75e483c5..adc1a240 100644 --- a/tmux.h +++ b/tmux.h @@ -1227,6 +1227,8 @@ struct tty { #define TTY_OPENED 0x20 #define TTY_FOCUS 0x40 #define TTY_BLOCK 0x80 +#define TTY_HAVEDA 0x100 +#define TTY_HAVEDSR 0x200 int flags; struct tty_term *term; diff --git a/tty-keys.c b/tty-keys.c index 5054c424..968f67ca 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1020,6 +1020,8 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, int flags = 0; *size = 0; + if (tty->flags & TTY_HAVEDA) + return (-1); /* First three bytes are always \033[?. */ if (buf[0] != '\033') @@ -1064,7 +1066,10 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, for (i = 1; i < n; i++) log_debug("%s: DA feature: %d", c->name, p[i]); log_debug("%s: received DA %.*s", c->name, (int)*size, buf); + tty_set_flags(tty, flags); + tty->flags |= TTY_HAVEDA; + return (0); } @@ -1082,6 +1087,8 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, int flags = 0; *size = 0; + if (tty->flags & TTY_HAVEDSR) + return (-1); /* First three bytes are always \033[. */ if (buf[0] != '\033') @@ -1092,6 +1099,10 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, return (-1); if (len == 2) return (1); + if (buf[2] != 'I') + return (-1); + if (len == 3) + return (1); /* Copy the rest up to a 'n'. */ for (i = 0; i < (sizeof tmp) - 1 && buf[2 + i] != 'n'; i++) { @@ -1108,6 +1119,9 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, if (strncmp(tmp, "ITERM2 ", 7) == 0) flags |= TERM_DECSLRM; log_debug("%s: received DSR %.*s", c->name, (int)*size, buf); + tty_set_flags(tty, flags); + tty->flags |= TTY_HAVEDSR; + return (0); } diff --git a/tty.c b/tty.c index 2d4b4cc0..a1324528 100644 --- a/tty.c +++ b/tty.c @@ -327,8 +327,9 @@ tty_start_tty(struct tty *tty) tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } - tty_puts(tty, "\033[c\033[1337n"); - } + tty_puts(tty, "\033[c\033[1337n"); /* DA and DSR */ + } else + tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); tty->flags |= TTY_STARTED; tty_invalidate(tty); From da515570dc13daefc92e26a5c5417e1f190f80e8 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jan 2020 11:59:21 +0000 Subject: [PATCH 09/29] Stop handling DA and DSR after a second (they should be the first thing sent) so this should be plenty. --- tmux.h | 1 + tty.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/tmux.h b/tmux.h index adc1a240..ac555be5 100644 --- a/tmux.h +++ b/tmux.h @@ -1179,6 +1179,7 @@ LIST_HEAD(tty_terms, tty_term); struct tty { struct client *client; + struct event start_timer; u_int sx; u_int sy; diff --git a/tty.c b/tty.c index a1324528..3d58f05b 100644 --- a/tty.c +++ b/tty.c @@ -285,11 +285,22 @@ tty_open(struct tty *tty, char **cause) return (0); } +static void +tty_start_timer_callback(__unused int fd, __unused short events, void *data) +{ + struct tty *tty = data; + struct client *c = tty->client; + + log_debug("%s: start timer fired", c->name); + tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); +} + void tty_start_tty(struct tty *tty) { struct client *c = tty->client; struct termios tio; + struct timeval tv = { .tv_sec = 1 }; if (tty->fd != -1 && tcgetattr(tty->fd, &tty->tio) == 0) { setblocking(tty->fd, 0); @@ -328,9 +339,13 @@ tty_start_tty(struct tty *tty) tty_puts(tty, "\033[?1004h"); } tty_puts(tty, "\033[c\033[1337n"); /* DA and DSR */ + } else tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); + evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); + evtimer_add(&tty->start_timer, &tv); + tty->flags |= TTY_STARTED; tty_invalidate(tty); @@ -351,6 +366,8 @@ tty_stop_tty(struct tty *tty) return; tty->flags &= ~TTY_STARTED; + evtimer_del(&tty->start_timer); + event_del(&tty->timer); tty->flags &= ~TTY_BLOCK; From cdf138372c4b7a0cc48e15c1ad6412a88db08fb8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 14 Jan 2020 16:02:22 +0000 Subject: [PATCH 10/29] Add to CHANGES. --- CHANGES | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGES b/CHANGES index cd3a1b8a..ae980bb0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,22 @@ CHANGES FROM 3.0 to X.X +* Treat plausible but invalid keys (like C-BSpace) as literal like any other + unrecognised string passed to send-keys + +* Detect iTerm2 and enable use of DECSLRM (much faster with horizontally split + windows). + +* Add -Z to default switch-client command in tree mode. + +* Add ~ to quoted characters for %%%. + +* Document client exit messages in the manual page. + +* Do not let read-only clients limit the size. + +* Add a number of new formats to inspect what sessions and clients a window is + present or active in. + * Change file reading and writing to go through the client if necessary. This fixes commands like "tmux loadb /dev/fd/X". Also modify source-file to support "-" for standard input, like load-buffer and save-buffer. From 9169ee0e874a9e4c46eda77abca075b9ec38dbb6 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 25 Jan 2020 16:40:32 +0000 Subject: [PATCH 11/29] Mention swap-window -d, GitHub issue 2068. --- tmux.1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index 9c9cfb39..eb5087be 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2533,10 +2533,11 @@ This is similar to except the source and destination windows are swapped. It is an error if no window exists at .Ar src-window . +If +.Fl d +is given, the new window does not become the current window. .Pp -Like -.Ic swap-pane , -if +If .Fl s is omitted and a marked pane is present (see .Ic select-pane From 74b424075c6d0ee668343936624b12cea10f3f19 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 25 Jan 2020 16:41:49 +0000 Subject: [PATCH 12/29] Use FNM_IGNORECASE if present, from Eric N Vander Weele in GitHub issue 2067. --- compat.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compat.h b/compat.h index 794ac67d..70801d0d 100644 --- a/compat.h +++ b/compat.h @@ -171,8 +171,12 @@ void warnx(const char *, ...); #endif #ifndef FNM_CASEFOLD +#ifdef FNM_IGNORECASE +#define FNM_CASEFOLD FNM_IGNORECASE +#else #define FNM_CASEFOLD 0 #endif +#endif #ifndef INFTIM #define INFTIM -1 From 2e39b621c9b29b58b4bfe761989b14f0f13d07b6 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Jan 2020 08:23:42 +0000 Subject: [PATCH 13/29] Change so that assignments may be specified alone - a command isn't required. GitHub issue 2062. --- cmd-parse.y | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index 97b50f57..41bcdaa8 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -133,7 +133,12 @@ statements : statement '\n' free($2); } -statement : condition +statement : /* empty */ + { + $$ = xmalloc (sizeof *$$); + TAILQ_INIT($$); + } + | condition { struct cmd_parse_state *ps = &parse_state; @@ -144,11 +149,6 @@ statement : condition cmd_parse_free_commands($1); } } - | assignment - { - $$ = xmalloc (sizeof *$$); - TAILQ_INIT($$); - } | commands { struct cmd_parse_state *ps = &parse_state; @@ -194,8 +194,10 @@ expanded : format free($1); } -assignment : /* empty */ - | EQUALS +optional_assignment : /* empty */ + | assignment + +assignment : EQUALS { struct cmd_parse_state *ps = &parse_state; int flags = ps->input->flags; @@ -372,7 +374,15 @@ commands : command $$ = $1; } -command : assignment TOKEN +command : assignment + { + struct cmd_parse_state *ps = &parse_state; + + $$ = xcalloc(1, sizeof *$$); + $$->name = NULL; + $$->line = ps->input->line; + } + | optional_assignment TOKEN { struct cmd_parse_state *ps = &parse_state; @@ -381,7 +391,7 @@ command : assignment TOKEN $$->line = ps->input->line; } - | assignment TOKEN arguments + | optional_assignment TOKEN arguments { struct cmd_parse_state *ps = &parse_state; @@ -631,6 +641,8 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, * command list. */ TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) { + if (cmd->name == NULL) + continue; alias = cmd_get_alias(cmd->name); if (alias == NULL) continue; @@ -676,6 +688,8 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, */ result = cmd_list_new(); TAILQ_FOREACH(cmd, cmds, entry) { + if (cmd->name == NULL) + continue; log_debug("%s: %u %s", __func__, cmd->line, cmd->name); cmd_log_argv(cmd->argc, cmd->argv, __func__); From d0b8d036be97efc885f879aa63ce9bbb0efd8222 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Jan 2020 08:53:13 +0000 Subject: [PATCH 14/29] Add support for adding a note to a key binding (with bind-key -N) and use this to add descriptions to the default key bindings. A new -N flag to list-keys shows key bindings with notes rather than the default bind-key command used to create them. Change the default ? binding to use this to show a readable summary of keys. Also extend command-prompt to return the name of the key pressed and add a default binding (/) to show the note for the next key pressed Suggested by Alex Tremblay in GitHub issue 2000. --- cmd-bind-key.c | 12 ++-- cmd-command-prompt.c | 6 +- cmd-list-keys.c | 150 ++++++++++++++++++++++++++++++++++++-- key-bindings.c | 168 ++++++++++++++++++++++--------------------- status.c | 8 ++- tmux.1 | 48 ++++++++++--- tmux.h | 5 +- 7 files changed, 292 insertions(+), 105 deletions(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 2af15d29..27f75dd0 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -33,8 +33,8 @@ const struct cmd_entry cmd_bind_key_entry = { .name = "bind-key", .alias = "bind", - .args = { "cnrT:", 2, -1 }, - .usage = "[-cnr] [-T key-table] key " + .args = { "cnrN:T:", 2, -1 }, + .usage = "[-cnr] [-T key-table] [-N note] key " "command [arguments]", .flags = CMD_AFTERHOOK, @@ -46,10 +46,10 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; key_code key; - const char *tablename; + const char *tablename, *note; struct cmd_parse_result *pr; char **argv = args->argv; - int argc = args->argc; + int argc = args->argc, repeat; key = key_string_lookup_string(argv[0]); if (key == KEYC_NONE || key == KEYC_UNKNOWN) { @@ -63,6 +63,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) tablename = "root"; else tablename = "prefix"; + repeat = args_has(args, 'r'); if (argc == 2) pr = cmd_parse_from_string(argv[1], NULL); @@ -79,6 +80,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) case CMD_PARSE_SUCCESS: break; } - key_bindings_add(tablename, key, args_has(args, 'r'), pr->cmdlist); + note = args_get(args, 'N'); + key_bindings_add(tablename, key, note, repeat, pr->cmdlist); return (CMD_RETURN_NORMAL); } diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 603ddb0a..9f0ea19f 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -40,8 +40,8 @@ const struct cmd_entry cmd_command_prompt_entry = { .name = "command-prompt", .alias = NULL, - .args = { "1iI:Np:t:", 0, 1 }, - .usage = "[-1Ni] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " + .args = { "1kiI:Np:t:", 0, 1 }, + .usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " "[template]", .flags = 0, @@ -122,6 +122,8 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cdata->flags |= PROMPT_NUMERIC; else if (args_has(args, 'i')) cdata->flags |= PROMPT_INCREMENTAL; + else if (args_has(args, 'k')) + cdata->flags |= PROMPT_KEY; status_prompt_set(c, prompt, input, cmd_command_prompt_callback, cmd_command_prompt_free, cdata, cdata->flags); free(prompt); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 8636b70a..e9e75a72 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -36,8 +36,8 @@ const struct cmd_entry cmd_list_keys_entry = { .name = "list-keys", .alias = "lsk", - .args = { "T:", 0, 0 }, - .usage = "[-T key-table]", + .args = { "1NP:T:", 0, 1 }, + .usage = "[-1N] [-P prefix-string] [-T key-table] [key]", .flags = CMD_STARTSERVER|CMD_AFTERHOOK, .exec = cmd_list_keys_exec @@ -54,6 +54,88 @@ const struct cmd_entry cmd_list_commands_entry = { .exec = cmd_list_keys_exec }; +static u_int +cmd_list_keys_get_width(const char *tablename, key_code only) +{ + struct key_table *table; + struct key_binding *bd; + u_int width, keywidth = 0; + + table = key_bindings_get_table(tablename, 0); + if (table == NULL) + return (0); + bd = key_bindings_first(table); + while (bd != NULL) { + if ((only != KEYC_UNKNOWN && bd->key != only) || + KEYC_IS_MOUSE(bd->key) || + bd->note == NULL) { + bd = key_bindings_next(table, bd); + continue; + } + width = utf8_cstrwidth(key_string_lookup_key(bd->key)); + if (width > keywidth) + keywidth = width; + + bd = key_bindings_next(table, bd); + } + return (keywidth); +} + +static int +cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, + const char *tablename, u_int keywidth, key_code only, const char *prefix) +{ + struct client *c = cmd_find_client(item, NULL, 1); + struct key_table *table; + struct key_binding *bd; + const char *key; + char *tmp; + int found = 0; + + table = key_bindings_get_table(tablename, 0); + if (table == NULL) + return (0); + bd = key_bindings_first(table); + while (bd != NULL) { + if ((only != KEYC_UNKNOWN && bd->key != only) || + KEYC_IS_MOUSE(bd->key) || + bd->note == NULL) { + bd = key_bindings_next(table, bd); + continue; + } + found = 1; + key = key_string_lookup_key(bd->key); + + tmp = utf8_padcstr(key, keywidth + 1); + if (args_has(args, '1') && c != NULL) + status_message_set(c, "%s%s%s", prefix, tmp, bd->note); + else + cmdq_print(item, "%s%s%s", prefix, tmp, bd->note); + free(tmp); + + if (args_has(args, '1')) + break; + bd = key_bindings_next(table, bd); + } + return (found); +} + +static char * +cmd_list_keys_get_prefix(struct args *args, key_code *prefix) +{ + char *s; + + *prefix = options_get_number(global_s_options, "prefix"); + if (!args_has(args, 'P')) { + if (*prefix != KEYC_NONE) + xasprintf(&s, "%s ", key_string_lookup_key(*prefix)); + else + s = xstrdup(""); + } else + s = xstrdup(args_get(args, 'P')); + return (s); +} + static enum cmd_retval cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) { @@ -61,19 +143,63 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) struct key_table *table; struct key_binding *bd; const char *tablename, *r; - char *key, *cp, *tmp; - int repeat, width, tablewidth, keywidth; + char *key, *cp, *tmp, *start, *empty; + key_code prefix, only = KEYC_UNKNOWN; + int repeat, width, tablewidth, keywidth, found = 0; size_t tmpsize, tmpused, cplen; if (self->entry == &cmd_list_commands_entry) return (cmd_list_keys_commands(self, item)); + if (args->argc != 0) { + only = key_string_lookup_string(args->argv[0]); + if (only == KEYC_UNKNOWN) { + cmdq_error(item, "invalid key: %s", args->argv[0]); + return (CMD_RETURN_ERROR); + } + } + tablename = args_get(args, 'T'); if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) { cmdq_error(item, "table %s doesn't exist", tablename); return (CMD_RETURN_ERROR); } + if (args_has(args, 'N')) { + if (tablename == NULL) { + start = cmd_list_keys_get_prefix(args, &prefix); + keywidth = cmd_list_keys_get_width("root", only); + if (prefix != KEYC_NONE) { + width = cmd_list_keys_get_width("prefix", only); + if (width == 0) + prefix = KEYC_NONE; + else if (width > keywidth) + keywidth = width; + } + empty = utf8_padcstr("", utf8_cstrwidth(start)); + + found = cmd_list_keys_print_notes(item, args, "root", + keywidth, only, empty); + if (prefix != KEYC_NONE) { + if (cmd_list_keys_print_notes(item, args, + "prefix", keywidth, only, start)) + found = 1; + } + free(empty); + } else { + if (args_has(args, 'P')) + start = xstrdup(args_get(args, 'P')); + else + start = xstrdup(""); + keywidth = cmd_list_keys_get_width(tablename, only); + found = cmd_list_keys_print_notes(item, args, tablename, + keywidth, only, start); + + } + free(start); + goto out; + } + repeat = 0; tablewidth = keywidth = 0; table = key_bindings_first_table (); @@ -84,6 +210,10 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) } bd = key_bindings_first(table); while (bd != NULL) { + if (only != KEYC_UNKNOWN && bd->key != only) { + bd = key_bindings_next(table, bd); + continue; + } key = args_escape(key_string_lookup_key(bd->key)); if (bd->flags & KEY_BINDING_REPEAT) @@ -113,6 +243,11 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) } bd = key_bindings_first(table); while (bd != NULL) { + if (only != KEYC_UNKNOWN && bd->key != only) { + bd = key_bindings_next(table, bd); + continue; + } + found = 1; key = args_escape(key_string_lookup_key(bd->key)); if (!repeat) @@ -162,13 +297,18 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) free(tmp); +out: + if (only != KEYC_UNKNOWN && !found) { + cmdq_error(item, "unknown key: %s", args->argv[0]); + return (CMD_RETURN_ERROR); + } return (CMD_RETURN_NORMAL); } static enum cmd_retval cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; + struct args *args = self->args; const struct cmd_entry **entryp; const struct cmd_entry *entry; struct format_tree *ft; diff --git a/key-bindings.c b/key-bindings.c index 94110a49..4387c011 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -90,6 +90,7 @@ key_bindings_free(struct key_table *table, struct key_binding *bd) { RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); + free((void *)bd->note); free(bd); } @@ -163,7 +164,7 @@ key_bindings_next(__unused struct key_table *table, struct key_binding *bd) } void -key_bindings_add(const char *name, key_code key, int repeat, +key_bindings_add(const char *name, key_code key, const char *note, int repeat, struct cmd_list *cmdlist) { struct key_table *table; @@ -177,6 +178,8 @@ key_bindings_add(const char *name, key_code key, int repeat, bd = xcalloc(1, sizeof *bd); bd->key = key; + if (note != NULL) + bd->note = xstrdup(note); RB_INSERT(key_bindings, &table->key_bindings, bd); if (repeat) @@ -226,87 +229,88 @@ void key_bindings_init(void) { static const char *defaults[] = { - "bind C-b send-prefix", - "bind C-o rotate-window", - "bind C-z suspend-client", - "bind Space next-layout", - "bind ! break-pane", - "bind '\"' split-window", - "bind '#' list-buffers", - "bind '$' command-prompt -I'#S' \"rename-session -- '%%'\"", - "bind % split-window -h", - "bind & confirm-before -p\"kill-window #W? (y/n)\" kill-window", - "bind \"'\" command-prompt -pindex \"select-window -t ':%%'\"", - "bind ( switch-client -p", - "bind ) switch-client -n", - "bind , command-prompt -I'#W' \"rename-window -- '%%'\"", - "bind - delete-buffer", - "bind . command-prompt \"move-window -t '%%'\"", - "bind 0 select-window -t:=0", - "bind 1 select-window -t:=1", - "bind 2 select-window -t:=2", - "bind 3 select-window -t:=3", - "bind 4 select-window -t:=4", - "bind 5 select-window -t:=5", - "bind 6 select-window -t:=6", - "bind 7 select-window -t:=7", - "bind 8 select-window -t:=8", - "bind 9 select-window -t:=9", - "bind : command-prompt", - "bind \\; last-pane", - "bind = choose-buffer -Z", - "bind ? list-keys", - "bind D choose-client -Z", - "bind E select-layout -E", - "bind L switch-client -l", - "bind M select-pane -M", - "bind [ copy-mode", - "bind ] paste-buffer", - "bind c new-window", - "bind d detach-client", - "bind f command-prompt \"find-window -Z -- '%%'\"", - "bind i display-message", - "bind l last-window", - "bind m select-pane -m", - "bind n next-window", - "bind o select-pane -t:.+", - "bind p previous-window", - "bind q display-panes", - "bind r refresh-client", - "bind s choose-tree -Zs", - "bind t clock-mode", - "bind w choose-tree -Zw", - "bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane", - "bind z resize-pane -Z", - "bind '{' swap-pane -U", - "bind '}' swap-pane -D", - "bind '~' show-messages", - "bind PPage copy-mode -u", - "bind -r Up select-pane -U", - "bind -r Down select-pane -D", - "bind -r Left select-pane -L", - "bind -r Right select-pane -R", - "bind M-1 select-layout even-horizontal", - "bind M-2 select-layout even-vertical", - "bind M-3 select-layout main-horizontal", - "bind M-4 select-layout main-vertical", - "bind M-5 select-layout tiled", - "bind M-n next-window -a", - "bind M-o rotate-window -D", - "bind M-p previous-window -a", - "bind -r S-Up refresh-client -U 10", - "bind -r S-Down refresh-client -D 10", - "bind -r S-Left refresh-client -L 10", - "bind -r S-Right refresh-client -R 10", - "bind -r DC refresh-client -c", - "bind -r M-Up resize-pane -U 5", - "bind -r M-Down resize-pane -D 5", - "bind -r M-Left resize-pane -L 5", - "bind -r M-Right resize-pane -R 5", - "bind -r C-Up resize-pane -U", - "bind -r C-Down resize-pane -D", - "bind -r C-Left resize-pane -L", - "bind -r C-Right resize-pane -R", + "bind -N 'Send the prefix key' C-b send-prefix", + "bind -N 'Rotate through the panes' C-o rotate-window", + "bind -N 'Suspend the current client' C-z suspend-client", + "bind -N 'Select next layout' Space next-layout", + "bind -N 'Break pane to a new window' ! break-pane", + "bind -N 'Split window vertically' '\"' split-window", + "bind -N 'List all paste buffers' '#' list-buffers", + "bind -N 'Rename current session' '$' command-prompt -I'#S' \"rename-session -- '%%'\"", + "bind -N 'Split window horizontally' % split-window -h", + "bind -N 'Kill current window' & confirm-before -p\"kill-window #W? (y/n)\" kill-window", + "bind -N 'Prompt for window index to select' \"'\" command-prompt -pindex \"select-window -t ':%%'\"", + "bind -N 'Switch to previous client' ( switch-client -p", + "bind -N 'Switch to next client' ) switch-client -n", + "bind -N 'Rename current window' , command-prompt -I'#W' \"rename-window -- '%%'\"", + "bind -N 'Delete the most recent paste buffer' - delete-buffer", + "bind -N 'Move the current window' . command-prompt \"move-window -t '%%'\"", + "bind -N 'Describe key binding' '/' command-prompt -kpkey 'list-keys -1N \"%%%\"'", + "bind -N 'Select window 0' 0 select-window -t:=0", + "bind -N 'Select window 1' 1 select-window -t:=1", + "bind -N 'Select window 2' 2 select-window -t:=2", + "bind -N 'Select window 3' 3 select-window -t:=3", + "bind -N 'Select window 4' 4 select-window -t:=4", + "bind -N 'Select window 5' 5 select-window -t:=5", + "bind -N 'Select window 6' 6 select-window -t:=6", + "bind -N 'Select window 7' 7 select-window -t:=7", + "bind -N 'Select window 8' 8 select-window -t:=8", + "bind -N 'Select window 9' 9 select-window -t:=9", + "bind -N 'Prompt for a command' : command-prompt", + "bind -N 'Move to the previously active pane' \\; last-pane", + "bind -N 'Choose a paste buffer from a list' = choose-buffer -Z", + "bind -N 'List key bindings' ? list-keys -N", + "bind -N 'Choose a client from a list' D choose-client -Z", + "bind -N 'Spread panes out evenly' E select-layout -E", + "bind -N 'Switch to the last client' L switch-client -l", + "bind -N 'Clear the marked pane' M select-pane -M", + "bind -N 'Enter copy mode' [ copy-mode", + "bind -N 'Paste the most recent paste buffer' ] paste-buffer", + "bind -N 'Create a new window' c new-window", + "bind -N 'Detach the current client' d detach-client", + "bind -N 'Search for a pane' f command-prompt \"find-window -Z -- '%%'\"", + "bind -N 'Display window information' i display-message", + "bind -N 'Select the previously current window' l last-window", + "bind -N 'Toggle the marked pane' m select-pane -m", + "bind -N 'Select the next window' n next-window", + "bind -N 'Select the next pane' o select-pane -t:.+", + "bind -N 'Select the previous pane' p previous-window", + "bind -N 'Display pane numbers' q display-panes", + "bind -N 'Redraw the current client' r refresh-client", + "bind -N 'Choose a session from a list' s choose-tree -Zs", + "bind -N 'Show a clock' t clock-mode", + "bind -N 'Choose a window from a list' w choose-tree -Zw", + "bind -N 'Kill the active pane' x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane", + "bind -N 'Zoom the active pane' z resize-pane -Z", + "bind -N 'Swap the active pane with the pane above' '{' swap-pane -U", + "bind -N 'Swap the active pane with the pane below' '}' swap-pane -D", + "bind -N 'Show messages' '~' show-messages", + "bind -N 'Enter copy mode and scroll up' PPage copy-mode -u", + "bind -N 'Select the pane above the active pane' -r Up select-pane -U", + "bind -N 'Select the pane below the active pane' -r Down select-pane -D", + "bind -N 'Select the pane to the left of the active pane' -r Left select-pane -L", + "bind -N 'Select the pane to the right of the active pane' -r Right select-pane -R", + "bind -N 'Set the even-horizontal layout' M-1 select-layout even-horizontal", + "bind -N 'Set the even-vertical layout' M-2 select-layout even-vertical", + "bind -N 'Set the main-horizontal layout' M-3 select-layout main-horizontal", + "bind -N 'Set the main-vertical layout' M-4 select-layout main-vertical", + "bind -N 'Select the tiled layout' M-5 select-layout tiled", + "bind -N 'Select the next window with an alert' M-n next-window -a", + "bind -N 'Rotate through the panes in reverse' M-o rotate-window -D", + "bind -N 'Select the previous window with an alert' M-p previous-window -a", + "bind -N 'Move the visible part of the window up' -r S-Up refresh-client -U 10", + "bind -N 'Move the visible part of the window down' -r S-Down refresh-client -D 10", + "bind -N 'Move the visible part of the window left' -r S-Left refresh-client -L 10", + "bind -N 'Move the visible part of the window right' -r S-Right refresh-client -R 10", + "bind -N 'Reset so the visible part of the window follows the cursor' -r DC refresh-client -c", + "bind -N 'Resize the pane up by 5' -r M-Up resize-pane -U 5", + "bind -N 'Resize the pane down by 5' -r M-Down resize-pane -D 5", + "bind -N 'Resize the pane left by 5' -r M-Left resize-pane -L 5", + "bind -N 'Resize the pane right by 5' -r M-Right resize-pane -R 5", + "bind -N 'Resize the pane up' -r C-Up resize-pane -U", + "bind -N 'Resize the pane down' -r C-Down resize-pane -D", + "bind -N 'Resize the pane left' -r C-Left resize-pane -L", + "bind -N 'Resize the pane right' -r C-Right resize-pane -R", "bind -n MouseDown1Pane select-pane -t=\\; send-keys -M", "bind -n MouseDrag1Border resize-pane -M", diff --git a/status.c b/status.c index 0f96f0d3..33f6c47a 100644 --- a/status.c +++ b/status.c @@ -915,11 +915,17 @@ status_prompt_key(struct client *c, key_code key) { struct options *oo = c->session->options; char *s, *cp, word[64], prefix = '='; - const char *histstr, *ws = NULL; + const char *histstr, *ws = NULL, *keystring; size_t size, n, off, idx, used; struct utf8_data tmp, *first, *last, *ud; int keys; + if (c->prompt_flags & PROMPT_KEY) { + keystring = key_string_lookup_key(key); + c->prompt_inputcb(c, c->prompt_data, keystring, 1); + status_prompt_clear(c); + return (0); + } size = utf8_strlen(c->prompt_buffer); if (c->prompt_flags & PROMPT_NUMERIC) { diff --git a/tmux.1 b/tmux.1 index eb5087be..4a618186 100644 --- a/tmux.1 +++ b/tmux.1 @@ -551,7 +551,7 @@ Braces may be enclosed inside braces, for example: .Bd -literal -offset indent bind x if-shell "true" { if-shell "true" { - display "true!" + display "true!" } } .Ed @@ -1335,7 +1335,8 @@ is used, option will not be applied. .Pp .Fl T -sets the client's key table; the next key from the client will be interpreted from +sets the client's key table; the next key from the client will be interpreted +from .Ar key-table . This may be used to configure multiple prefix keys, or to bind commands to sequences of keys. @@ -2613,6 +2614,7 @@ Commands related to key bindings are as follows: .Bl -tag -width Ds .It Xo Ic bind-key .Op Fl nr +.Op Fl N Ar note .Op Fl T Ar key-table .Ar key Ar command Op Ar arguments .Xc @@ -2660,22 +2662,46 @@ The flag indicates this key may repeat, see the .Ic repeat-time option. +.Fl N +attaches a note to the key (shown with +.Ic list-keys +.Fl N ) . .Pp To view the default bindings and possible commands, see the .Ic list-keys command. .It Xo Ic list-keys -.Op Fl T Ar key-table +.Op Fl 1N +.Op Fl P Ar prefix-string Fl T Ar key-table +.Op key .Xc .D1 (alias: Ic lsk ) List all key bindings. -Without -.Fl T -all key tables are printed. +By default this shows all keys or any bindings for +.Ar key +in the syntax of the +.Ic bind-key +command. +.Fl N +instead show keys and attached notes, i +.Ar key-table +if given or in the +.Em root +and +.Em prefix +key tables by default. +.Fl P +specifies a prefix to print before each key. With +.Fl 1 +only the first matching key and note is shown. +.Pp +Without +.Fl N , .Fl T -only -.Ar key-table . +prints only keys in +.Ar key-table , +otherwise all key tables are printed. .It Xo Ic send-keys .Op Fl FHlMRX .Op Fl N Ar repeat-count @@ -4693,7 +4719,7 @@ session option. Commands related to the status line are as follows: .Bl -tag -width Ds .It Xo Ic command-prompt -.Op Fl 1Ni +.Op Fl 1ikN .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client @@ -4743,6 +4769,10 @@ but any quotation marks are escaped. .Fl 1 makes the prompt only accept one key press, in this case the resulting input is a single character. +.Fl k +is like +.Fl 1 +but the key press is translated to a key name. .Fl N makes the prompt only accept numeric key presses. .Fl i diff --git a/tmux.h b/tmux.h index ac555be5..093f17a4 100644 --- a/tmux.h +++ b/tmux.h @@ -1609,6 +1609,7 @@ struct client { #define PROMPT_NUMERIC 0x2 #define PROMPT_INCREMENTAL 0x4 #define PROMPT_NOFORMAT 0x8 +#define PROMPT_KEY 0x10 int prompt_flags; struct session *session; @@ -1636,6 +1637,7 @@ TAILQ_HEAD(clients, client); struct key_binding { key_code key; struct cmd_list *cmdlist; + const char *note; int flags; #define KEY_BINDING_REPEAT 0x1 @@ -2147,7 +2149,8 @@ void key_bindings_unref_table(struct key_table *); struct key_binding *key_bindings_get(struct key_table *, key_code); struct key_binding *key_bindings_first(struct key_table *); struct key_binding *key_bindings_next(struct key_table *, struct key_binding *); -void key_bindings_add(const char *, key_code, int, struct cmd_list *); +void key_bindings_add(const char *, key_code, const char *, int, + struct cmd_list *); void key_bindings_remove(const char *, key_code); void key_bindings_remove_table(const char *); void key_bindings_init(void); From 2c38e01b548553aa162f9f126147b5ed64fd1700 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Jan 2020 09:04:47 +0000 Subject: [PATCH 15/29] Expand description of start-server. --- tmux.1 | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tmux.1 b/tmux.1 index 4a618186..11bdf4bc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1287,6 +1287,17 @@ shows the parsed commands and line numbers if possible. Start the .Nm server, if not already running, without creating any sessions. +.Pp +Note that as by default the +.Nm +server will exit with no sessions, this is only useful if a session is created in +.Pa ~/.tmux.conf , +.Ic exit-empty +is turned off, or another command is run as part of the same command sequence. +For example: +.Bd -literal -offset indent +$ tmux start \\; show -g +.Ed .It Xo Ic suspend-client .Op Fl t Ar target-client .Xc From 24350879cdfb9ef23dee0da409b621e9830d8baf Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Jan 2020 08:06:11 +0000 Subject: [PATCH 16/29] Add a define for flags meaning a client is not attached, and fix unattached counter, reported by Thomas Sattler. --- resize.c | 5 +++-- server-client.c | 4 ++-- tmux.h | 4 ++++ window-client.c | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/resize.c b/resize.c index 054b025f..b4142a70 100644 --- a/resize.c +++ b/resize.c @@ -363,14 +363,15 @@ recalculate_sizes(void) * client. */ TAILQ_FOREACH(c, &clients, entry) { + s = c->session; + if (s != NULL && !(c->flags & CLIENT_UNATTACHEDFLAGS)) + s->attached++; if (ignore_client_size(c)) continue; - s = c->session; if (c->tty.sy <= s->statuslines || (c->flags & CLIENT_CONTROL)) c->flags |= CLIENT_STATUSOFF; else c->flags &= ~CLIENT_STATUSOFF; - s->attached++; } /* Walk each window and adjust the size. */ diff --git a/server-client.c b/server-client.c index ee7b4c70..12e07327 100644 --- a/server-client.c +++ b/server-client.c @@ -1034,7 +1034,7 @@ server_client_key_callback(struct cmdq_item *item, void *data) key_code key0; /* Check the client is good to accept input. */ - if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) + if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) goto out; wl = s->curw; @@ -1221,7 +1221,7 @@ server_client_handle_key(struct client *c, struct key_event *event) struct cmdq_item *item; /* Check the client is good to accept input. */ - if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) + if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) return (0); /* diff --git a/tmux.h b/tmux.h index 093f17a4..d56c5fea 100644 --- a/tmux.h +++ b/tmux.h @@ -1582,6 +1582,10 @@ struct client { CLIENT_REDRAWSTATUSALWAYS| \ CLIENT_REDRAWBORDERS| \ CLIENT_REDRAWOVERLAY) +#define CLIENT_UNATTACHEDFLAGS \ + (CLIENT_DEAD| \ + CLIENT_SUSPENDED| \ + CLIENT_DETACHING) #define CLIENT_NOSIZEFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ diff --git a/window-client.c b/window-client.c index 22a0f2e2..4688cbf3 100644 --- a/window-client.c +++ b/window-client.c @@ -210,7 +210,7 @@ window_client_draw(__unused void *modedata, void *itemdata, struct window_pane *wp; u_int cx = s->cx, cy = s->cy, lines, at; - if (c->session == NULL || (c->flags & (CLIENT_DEAD|CLIENT_DETACHING))) + if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) return; wp = c->session->curw->window->active; From 32816eaebd4ccd712cc7f7291a587146d122a9ff Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Jan 2020 10:21:21 +0000 Subject: [PATCH 17/29] Set up working directory before killing the existing pane on respawn. --- spawn.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/spawn.c b/spawn.c index 75995221..933f0c6a 100644 --- a/spawn.c +++ b/spawn.c @@ -225,6 +225,17 @@ spawn_pane(struct spawn_context *sc, char **cause) spawn_log(__func__, sc); + /* + * Work out the current working directory. If respawning, use + * the pane's stored one unless specified. + */ + if (sc->cwd != NULL) + cwd = format_single(item, sc->cwd, c, s, NULL, NULL); + else if (~sc->flags & SPAWN_RESPAWN) + cwd = xstrdup(server_client_get_cwd(c, s)); + else + cwd = NULL; + /* * If we are respawning then get rid of the old process. Otherwise * either create a new cell or assign to the one we are given. @@ -235,6 +246,7 @@ spawn_pane(struct spawn_context *sc, char **cause) window_pane_index(sc->wp0, &idx); xasprintf(cause, "pane %s:%d.%u still active", s->name, sc->wl->idx, idx); + free(cwd); return (NULL); } if (sc->wp0->fd != -1) { @@ -255,8 +267,8 @@ spawn_pane(struct spawn_context *sc, char **cause) } /* - * Now we have a pane with nothing running in it ready for the new - * process. Work out the command and arguments. + * Now we have a pane with nothing running in it ready for the new process. + * Work out the command and arguments and store the working directory. */ if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN)) { cmd = options_get_string(s->options, "default-command"); @@ -271,6 +283,10 @@ spawn_pane(struct spawn_context *sc, char **cause) argc = sc->argc; argv = sc->argv; } + if (cwd != NULL) { + free(new_wp->cwd); + new_wp->cwd = cwd; + } /* * Replace the stored arguments if there are new ones. If not, the @@ -282,21 +298,6 @@ spawn_pane(struct spawn_context *sc, char **cause) new_wp->argv = cmd_copy_argv(argc, argv); } - /* - * Work out the current working directory. If respawning, use - * the pane's stored one unless specified. - */ - if (sc->cwd != NULL) - cwd = format_single(item, sc->cwd, c, s, NULL, NULL); - else if (~sc->flags & SPAWN_RESPAWN) - cwd = xstrdup(server_client_get_cwd(c, s)); - else - cwd = NULL; - if (cwd != NULL) { - free(new_wp->cwd); - new_wp->cwd = cwd; - } - /* Create an environment for this pane. */ child = environ_for_session(s, 0); if (sc->environ != NULL) From f165221dc4641837ee9f589bb666d310a495904c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Jan 2020 10:44:30 +0000 Subject: [PATCH 18/29] Reduce a difference with portable tmux by adding the -V flag and #{version} format; on OpenBSD these just report the OpenBSD version. --- format.c | 1 + proc.c | 4 ++-- tmux.1 | 8 +++++++- tmux.c | 20 +++++++++++++++++++- tmux.h | 1 + 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index f5e7b2ce..ff8e3c62 100644 --- a/format.c +++ b/format.c @@ -1106,6 +1106,7 @@ format_create(struct client *c, struct cmdq_item *item, int tag, int flags) ft->flags = flags; ft->time = time(NULL); + format_add(ft, "version", "%s", getversion()); format_add_cb(ft, "host", format_cb_host); format_add_cb(ft, "host_short", format_cb_host_short); format_add_cb(ft, "pid", format_cb_pid); diff --git a/proc.c b/proc.c index 48e0d90c..3fc190a2 100644 --- a/proc.c +++ b/proc.c @@ -182,8 +182,8 @@ proc_start(const char *name) if (uname(&u) < 0) memset(&u, 0, sizeof u); - log_debug("%s started (%ld): socket %s, protocol %d", name, - (long)getpid(), socket_path, PROTOCOL_VERSION); + log_debug("%s started (%ld): version %s, socket %s, protocol %d", name, + (long)getpid(), getversion(), socket_path, PROTOCOL_VERSION); log_debug("on %s %s %s; libevent %s (%s)", u.sysname, u.release, u.version, event_get_version(), event_get_method()); diff --git a/tmux.1 b/tmux.1 index 11bdf4bc..bc24c193 100644 --- a/tmux.1 +++ b/tmux.1 @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm tmux .Bk -words -.Op Fl 2Cluv +.Op Fl 2CluvV .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name @@ -210,6 +210,11 @@ signal may be sent to the server process to toggle logging between on (as if .Fl v was given) and off. +.It Fl V +Report the +.Nm +version. +.Pp .It Ar command Op Ar flags This specifies one of a set of commands used to control .Nm , @@ -4369,6 +4374,7 @@ The following variables are available, where appropriate: .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "socket_path" Ta "" Ta "Server socket path" .It Li "start_time" Ta "" Ta "Server start time" +.It Li "version" Ta "" Ta "Server version" .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_list" Ta "" Ta "List of clients viewing this window" diff --git a/tmux.c b/tmux.c index fe2647f5..f98037b5 100644 --- a/tmux.c +++ b/tmux.c @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -212,6 +213,20 @@ find_home(void) return (home); } +const char * +getversion(void) +{ + static char *version; + struct utsname u; + + if (version == NULL) { + if (uname(&u) < 0) + fatalx("uname failed"); + xasprintf(&version, "openbsd-%s", u.release); + } + return version; +} + int main(int argc, char **argv) { @@ -238,7 +253,7 @@ main(int argc, char **argv) flags = 0; label = path = NULL; - while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { + while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUvV")) != -1) { switch (opt) { case '2': flags |= CLIENT_256COLOURS; @@ -255,6 +270,9 @@ main(int argc, char **argv) case 'f': set_cfg_file(optarg); break; + case 'V': + printf("%s %s\n", getprogname(), getversion()); + exit(0); case 'l': flags |= CLIENT_LOGIN; break; diff --git a/tmux.h b/tmux.h index d56c5fea..24e4277f 100644 --- a/tmux.h +++ b/tmux.h @@ -1769,6 +1769,7 @@ int areshell(const char *); void setblocking(int, int); const char *find_cwd(void); const char *find_home(void); +const char *getversion(void); /* proc.c */ struct imsg; From 90e962fff8f4d251cdf7fcc653caa34973c82d91 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Jan 2020 10:59:29 +0000 Subject: [PATCH 19/29] Add support for the iTerm2 DSR 1337 sequence to get the terminal version. --- input.c | 9 +++++++++ tty-keys.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/input.c b/input.c index 452eac7f..d7071bfe 100644 --- a/input.c +++ b/input.c @@ -20,6 +20,7 @@ #include +#include #include #include #include @@ -1301,6 +1302,7 @@ input_csi_dispatch(struct input_ctx *ictx) struct input_table_entry *entry; int i, n, m; u_int cx, bg = ictx->cell.cell.bg; + char *copy, *cp; if (ictx->flags & INPUT_DISCARD) return (0); @@ -1432,6 +1434,13 @@ input_csi_dispatch(struct input_ctx *ictx) case 6: input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1); break; + case 1337: /* Terminal version, from iTerm2. */ + copy = xstrdup(getversion()); + for (cp = copy; *cp != '\0'; cp++) + *cp = toupper((u_char)*cp); + input_reply(ictx, "\033[TMUX %sn", copy); + free(copy); + break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; diff --git a/tty-keys.c b/tty-keys.c index 968f67ca..987fd1b0 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1099,7 +1099,7 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, return (-1); if (len == 2) return (1); - if (buf[2] != 'I') + if (buf[2] != 'I' && buf[2] != 'T') return (-1); if (len == 3) return (1); From 685eb381dec7fc741a15ce11a84d8c96ed66ce42 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 28 Jan 2020 11:28:30 +0000 Subject: [PATCH 20/29] Fix for version changes. --- Makefile.am | 5 +++-- input.c | 1 + tmux.c | 13 +------------ 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/Makefile.am b/Makefile.am index 6902477e..082a467c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,8 +11,9 @@ EXTRA_DIST = \ dist_EXTRA_tmux_SOURCES = compat/*.[ch] # Preprocessor flags. -AM_CPPFLAGS += @XOPEN_DEFINES@ -AM_CPPFLAGS += -DTMUX_CONF="\"$(sysconfdir)/tmux.conf:~/.tmux.conf:~/.config/tmux/tmux.conf\"" +AM_CPPFLAGS += @XOPEN_DEFINES@ \ + -DTMUX_VERSION="\"@VERSION@\"" \ + -DTMUX_CONF="\"$(sysconfdir)/tmux.conf:~/.tmux.conf:~/.config/tmux/tmux.conf\"" # Additional object files. LDADD = $(LIBOBJS) diff --git a/input.c b/input.c index 452eac7f..a2c63e14 100644 --- a/input.c +++ b/input.c @@ -20,6 +20,7 @@ #include +#include #include #include #include diff --git a/tmux.c b/tmux.c index b5cdfbf9..3c1feccc 100644 --- a/tmux.c +++ b/tmux.c @@ -213,15 +213,7 @@ find_home(void) const char * getversion(void) { - static char *version; - struct utsname u; - - if (version == NULL) { - if (uname(&u) < 0) - fatalx("uname failed"); - xasprintf(&version, "openbsd-%s", u.release); - } - return version; + return TMUX_VERSION; } int @@ -264,9 +256,6 @@ main(int argc, char **argv) else flags |= CLIENT_CONTROL; break; - case 'V': - printf("%s %s\n", getprogname(), VERSION); - exit(0); case 'f': set_cfg_file(optarg); break; From 84995ae1726589b5fb04e002e43496775e0ebfcd Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Jan 2020 11:31:31 +0000 Subject: [PATCH 21/29] -V also needs to go in usage. --- tmux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.c b/tmux.c index f98037b5..6572ad4e 100644 --- a/tmux.c +++ b/tmux.c @@ -56,7 +56,7 @@ static __dead void usage(void) { fprintf(stderr, - "usage: %s [-2Cluv] [-c shell-command] [-f file] [-L socket-name]\n" + "usage: %s [-2CluvV] [-c shell-command] [-f file] [-L socket-name]\n" " [-S socket-path] [command [flags]]\n", getprogname()); exit(1); From a6129e99749d2bbc8b4a991c7b5d09300aa55f39 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Jan 2020 11:39:51 +0000 Subject: [PATCH 22/29] If we can identify the terminal as iTerm2 or as tmux, we can be sure they support 256 and RGB colours, so set those flags too. --- tmux.h | 1 + tty-keys.c | 4 +++- tty-term.c | 28 +++++++++------------------- tty.c | 27 +++++++++++++++------------ 4 files changed, 28 insertions(+), 32 deletions(-) diff --git a/tmux.h b/tmux.h index 24e4277f..3ec89019 100644 --- a/tmux.h +++ b/tmux.h @@ -1171,6 +1171,7 @@ struct tty_term { #define TERM_NOXENL 0x2 #define TERM_DECSLRM 0x4 #define TERM_DECFRA 0x8 +#define TERM_RGBCOLOURS 0x10 int flags; LIST_ENTRY(tty_term) entry; diff --git a/tty-keys.c b/tty-keys.c index 987fd1b0..064f2172 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1117,7 +1117,9 @@ tty_keys_device_status_report(struct tty *tty, const char *buf, size_t len, /* Set terminal flags. */ if (strncmp(tmp, "ITERM2 ", 7) == 0) - flags |= TERM_DECSLRM; + flags |= (TERM_DECSLRM|TERM_256COLOURS|TERM_RGBCOLOURS); + if (strncmp(tmp, "TMUX ", 5) == 0) + flags |= (TERM_256COLOURS|TERM_RGBCOLOURS); log_debug("%s: received DSR %.*s", c->name, (int)*size, buf); tty_set_flags(tty, flags); diff --git a/tty-term.c b/tty-term.c index 164ab2f1..ef7d7905 100644 --- a/tty-term.c +++ b/tty-term.c @@ -522,11 +522,16 @@ tty_term_find(char *name, int fd, char **cause) goto error; } - /* Figure out if we have 256 colours (or more). */ - if (tty_term_number(term, TTYC_COLORS) >= 256 || - tty_term_has(term, TTYC_RGB)) + /* Set flag if terminal has 256 colours. */ + if (tty_term_number(term, TTYC_COLORS) >= 256) term->flags |= TERM_256COLOURS; + /* Set flag if terminal has RGB colours. */ + if ((tty_term_flag(term, TTYC_TC) || tty_term_has(term, TTYC_RGB)) || + (tty_term_has(term, TTYC_SETRGBF) && + tty_term_has(term, TTYC_SETRGBB))) + term->flags |= TERM_RGBCOLOURS; + /* * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). @@ -561,22 +566,7 @@ tty_term_find(char *name, int fd, char **cause) code->type = TTYCODE_STRING; } - /* - * On terminals with RGB colour (Tc or RGB), fill in setrgbf and - * setrgbb if they are missing. - */ - if ((tty_term_flag(term, TTYC_TC) || tty_term_flag(term, TTYC_RGB)) && - !tty_term_has(term, TTYC_SETRGBF) && - !tty_term_has(term, TTYC_SETRGBB)) { - code = &term->codes[TTYC_SETRGBF]; - code->value.string = xstrdup("\033[38;2;%p1%d;%p2%d;%p3%dm"); - code->type = TTYCODE_STRING; - code = &term->codes[TTYC_SETRGBB]; - code->value.string = xstrdup("\033[48;2;%p1%d;%p2%d;%p3%dm"); - code->type = TTYCODE_STRING; - } - - /* Log it. */ + /* Log the capabilities. */ for (i = 0; i < tty_term_ncodes(); i++) log_debug("%s%s", name, tty_term_describe(term, i)); diff --git a/tty.c b/tty.c index 3d58f05b..54c3be30 100644 --- a/tty.c +++ b/tty.c @@ -2386,11 +2386,10 @@ tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) /* Is this a 24-bit colour? */ if (gc->fg & COLOUR_FLAG_RGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ - if (!tty_term_has(tty->term, TTYC_SETRGBF)) { - colour_split_rgb(gc->fg, &r, &g, &b); - gc->fg = colour_find_rgb(r, g, b); - } else + if ((tty->term->flags|tty->term_flags) & TERM_RGBCOLOURS) return; + colour_split_rgb(gc->fg, &r, &g, &b); + gc->fg = colour_find_rgb(r, g, b); } /* How many colours does this terminal have? */ @@ -2436,11 +2435,10 @@ tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) /* Is this a 24-bit colour? */ if (gc->bg & COLOUR_FLAG_RGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ - if (!tty_term_has(tty->term, TTYC_SETRGBB)) { - colour_split_rgb(gc->bg, &r, &g, &b); - gc->bg = colour_find_rgb(r, g, b); - } else + if ((tty->term->flags|tty->term_flags) & TERM_RGBCOLOURS) return; + colour_split_rgb(gc->bg, &r, &g, &b); + gc->bg = colour_find_rgb(r, g, b); } /* How many colours does this terminal have? */ @@ -2617,15 +2615,14 @@ tty_try_colour(struct tty *tty, int colour, const char *type) } if (colour & COLOUR_FLAG_RGB) { + colour_split_rgb(colour & 0xffffff, &r, &g, &b); if (*type == '3') { if (!tty_term_has(tty->term, TTYC_SETRGBF)) - return (-1); - colour_split_rgb(colour & 0xffffff, &r, &g, &b); + goto fallback_rgb; tty_putcode3(tty, TTYC_SETRGBF, r, g, b); } else { if (!tty_term_has(tty->term, TTYC_SETRGBB)) - return (-1); - colour_split_rgb(colour & 0xffffff, &r, &g, &b); + goto fallback_rgb; tty_putcode3(tty, TTYC_SETRGBB, r, g, b); } return (0); @@ -2638,6 +2635,12 @@ fallback_256: log_debug("%s: 256 colour fallback: %s", tty->client->name, s); tty_puts(tty, s); return (0); + +fallback_rgb: + xsnprintf(s, sizeof s, "\033[%s;2;%d;%d;%dm", type, r, g, b); + log_debug("%s: RGB colour fallback: %s", tty->client->name, s); + tty_puts(tty, s); + return (0); } static void From e3887022604d6ee6f922cdaa274b54cdf06020ba Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Jan 2020 13:10:14 +0000 Subject: [PATCH 23/29] Ignore empty commands rather than adding them to the command list rather than trying to skip them later, fixes problem reported by M Kelly. --- cmd-parse.y | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cmd-parse.y b/cmd-parse.y index 41bcdaa8..2375370b 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -341,7 +341,8 @@ commands : command struct cmd_parse_state *ps = &parse_state; $$ = cmd_parse_new_commands(); - if (ps->scope == NULL || ps->scope->flag) + if ($1->name != NULL && + (ps->scope == NULL || ps->scope->flag)) TAILQ_INSERT_TAIL($$, $1, entry); else cmd_parse_free_command($1); @@ -360,7 +361,8 @@ commands : command { struct cmd_parse_state *ps = &parse_state; - if (ps->scope == NULL || ps->scope->flag) { + if ($3->name != NULL && + (ps->scope == NULL || ps->scope->flag)) { $$ = $1; TAILQ_INSERT_TAIL($$, $3, entry); } else { @@ -641,8 +643,6 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, * command list. */ TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) { - if (cmd->name == NULL) - continue; alias = cmd_get_alias(cmd->name); if (alias == NULL) continue; @@ -688,8 +688,6 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds, */ result = cmd_list_new(); TAILQ_FOREACH(cmd, cmds, entry) { - if (cmd->name == NULL) - continue; log_debug("%s: %u %s", __func__, cmd->line, cmd->name); cmd_log_argv(cmd->argc, cmd->argv, __func__); From b905c5d455b354f7210dc118f748a658f0358cd7 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Jan 2020 13:23:24 +0000 Subject: [PATCH 24/29] If ALL clients are readonly, allow them to affect the size, suggested by Thomas Sattler. --- resize.c | 16 ++++++++++++++++ tmux.h | 3 +-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/resize.c b/resize.c index b4142a70..96d733f0 100644 --- a/resize.c +++ b/resize.c @@ -66,10 +66,26 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) static int ignore_client_size(struct client *c) { + struct client *loop; + if (c->session == NULL) return (1); if (c->flags & CLIENT_NOSIZEFLAGS) return (1); + if (c->flags & CLIENT_READONLY) { + /* + * Ignore readonly clients if there are any attached clients + * that aren't readonly. + */ + TAILQ_FOREACH (loop, &clients, entry) { + if (loop->session == NULL) + continue; + if (loop->flags & CLIENT_NOSIZEFLAGS) + continue; + if (~loop->flags & CLIENT_READONLY) + return (1); + } + } if ((c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_SIZECHANGED)) return (1); return (0); diff --git a/tmux.h b/tmux.h index 3ec89019..418f0a44 100644 --- a/tmux.h +++ b/tmux.h @@ -1590,8 +1590,7 @@ struct client { #define CLIENT_NOSIZEFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ - CLIENT_DETACHING| \ - CLIENT_READONLY) + CLIENT_DETACHING) int flags; struct key_table *keytable; From 7f3feb1896ffde7bca70ffa8db933196af7b2f91 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 28 Jan 2020 15:52:04 +0000 Subject: [PATCH 25/29] Add to CHANGES. --- CHANGES | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index ae980bb0..d6806e49 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,14 @@ CHANGES FROM 3.0 to X.X +* Add support for adding a note to a key binding (with bind-key -N) and use + this to add descriptions to the default key bindings. A new -N flag to + list-keys shows key bindings with notes. Change the default ? binding to use + this to show a readable summary of keys. Also extend command-prompt to return + the name of the key pressed and add a default binding (/) to show the note + for the next key pressed. + +* Add support for the iTerm2 DSR 1337 sequence to get the terminal version. + * Treat plausible but invalid keys (like C-BSpace) as literal like any other unrecognised string passed to send-keys @@ -12,7 +21,8 @@ CHANGES FROM 3.0 to X.X * Document client exit messages in the manual page. -* Do not let read-only clients limit the size. +* Do not let read-only clients limit the size, unless all clients are + read-only. * Add a number of new formats to inspect what sessions and clients a window is present or active in. From 7a15d10bf4abb28e0a85e0090de9c13f80f83aaa Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 29 Jan 2020 08:28:17 +0000 Subject: [PATCH 26/29] Remove extra Pp (from jmc) and add a missing word. --- tmux.1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index bc24c193..21510722 100644 --- a/tmux.1 +++ b/tmux.1 @@ -214,7 +214,6 @@ was given) and off. Report the .Nm version. -.Pp .It Ar command Op Ar flags This specifies one of a set of commands used to control .Nm , @@ -4953,7 +4952,7 @@ When the option is reached, the oldest automatically named buffer is deleted. Explicitly named buffers are not subject to .Ic buffer-limit -and may be deleted with +and may be deleted with the .Ic delete-buffer command. .Pp From 531daba584f55fed87ec87986a7f0b497c14626f Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 29 Jan 2020 15:07:49 +0000 Subject: [PATCH 27/29] Do not send DA and DSR again if already have a response. --- input.c | 1 + tty.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/input.c b/input.c index d7071bfe..ea9320d1 100644 --- a/input.c +++ b/input.c @@ -773,6 +773,7 @@ input_save_state(struct input_ctx *ictx) ictx->old_mode = s->mode; } +/* Restore screen state. */ static void input_restore_state(struct input_ctx *ictx) { diff --git a/tty.c b/tty.c index 54c3be30..3bab556d 100644 --- a/tty.c +++ b/tty.c @@ -338,8 +338,10 @@ tty_start_tty(struct tty *tty) tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } - tty_puts(tty, "\033[c\033[1337n"); /* DA and DSR */ - + if (~tty->flags & TTY_HAVEDA) + tty_puts(tty, "\033[c"); + if (~tty->flags & TTY_HAVEDSR) + tty_puts(tty, "\033[1337n"); } else tty->flags |= (TTY_HAVEDA|TTY_HAVEDSR); From 44dad918f82124272486dfd4439dd72cb969fa12 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 29 Jan 2020 16:22:32 +0000 Subject: [PATCH 28/29] Warn if a message type that is no longer used is received. --- client.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client.c b/client.c index a36c6471..0c2a6ee2 100644 --- a/client.c +++ b/client.c @@ -863,6 +863,12 @@ client_dispatch_wait(struct imsg *imsg) case MSG_WRITE_CLOSE: client_write_close(data, datalen); break; + case MSG_OLDSTDERR: + case MSG_OLDSTDIN: + case MSG_OLDSTDOUT: + fprintf(stderr, "server version is too old for client\n"); + proc_exit(client_proc); + break; } } From 339832b92c298538f398754f6d3fc21d15d13326 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 30 Jan 2020 09:04:51 +0000 Subject: [PATCH 29/29] Bad merge. --- tty-keys.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tty-keys.c b/tty-keys.c index e6f00b2e..8cc90a08 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1068,7 +1068,6 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, if (p[i] == 4) flags |= TERM_SIXEL; } - tty_set_type(tty, type, flags); tty_set_flags(tty, flags); tty->flags |= TTY_HAVEDA;