From 36d904011a0620284ad3a51639a090024f2575fb Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Aug 2022 08:57:01 +0000 Subject: -u is no longer equivalent to -TUTF-8 so don't say it is. --- tmux.1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index 1fc2d061..a1298fe0 100644 --- a/tmux.1 +++ b/tmux.1 @@ -214,8 +214,6 @@ that is set does not contain .Qq UTF-8 or .Qq UTF8 . -This is equivalent to -.Fl T Ar UTF-8 . .It Fl T Ar features Set terminal features for the client. This is a comma-separated list of features. -- cgit From 33c59100aeb49894550b97cce268f46032f4c8d6 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Aug 2022 09:23:34 +0000 Subject: Fix validation of missing percentage arguments. --- arguments.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arguments.c b/arguments.c index 485af2be..b08582ee 100644 --- a/arguments.c +++ b/arguments.c @@ -895,6 +895,10 @@ args_percentage(struct args *args, u_char flag, long long minval, *cause = xstrdup("missing"); return (0); } + if (TAILQ_EMPTY(&entry->values)) { + *cause = xstrdup("empty"); + return (0); + } value = TAILQ_LAST(&entry->values, args_values)->string; return (args_string_percentage(value, minval, maxval, curval, cause)); } @@ -909,6 +913,10 @@ args_string_percentage(const char *value, long long minval, long long maxval, size_t valuelen = strlen(value); char *copy; + if (valuelen == 0) { + *cause = xstrdup("empty"); + return (0); + } if (value[valuelen - 1] == '%') { copy = xstrdup(value); copy[valuelen - 1] = '\0'; @@ -955,6 +963,10 @@ args_percentage_and_expand(struct args *args, u_char flag, long long minval, *cause = xstrdup("missing"); return (0); } + if (TAILQ_EMPTY(&entry->values)) { + *cause = xstrdup("empty"); + return (0); + } value = TAILQ_LAST(&entry->values, args_values)->string; return (args_string_percentage_and_expand(value, minval, maxval, curval, item, cause)); -- cgit From 00812c9053a0b1067f833925b726086a67d8528d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 2 Aug 2022 11:52:09 +0100 Subject: Check for $YACC, from Wei Shih in GitHub issue 3267. --- configure.ac | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure.ac b/configure.ac index 276f71c8..8e846042 100644 --- a/configure.ac +++ b/configure.ac @@ -267,6 +267,12 @@ if test "x$found_libevent" = xno; then AC_MSG_ERROR("libevent not found") fi +# Look for yacc. +AC_CHECK_PROG(found_yacc, $YACC, yes, no) +if test "x$found_yacc" = xno; then + AC_MSG_ERROR("yacc not found") +fi + # Look for ncurses or curses. Try pkg-config first then directly for the # library. PKG_CHECK_MODULES( -- cgit From 42ba6c1b229c92256274e848e9c5ff1d59d9081b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Aug 2022 11:09:26 +0000 Subject: Add a third state "all" to allow-passthrough to work even in invisible panes, from Sergei Grechanik in GitHub issue 3274. --- input.c | 11 ++++++++--- options-table.c | 10 ++++++++-- screen-write.c | 4 +++- tmux.1 | 8 +++++++- tmux.h | 46 +++++++++++++++++++++++++++------------------- tty.c | 21 ++++++++++++++------- 6 files changed, 67 insertions(+), 33 deletions(-) diff --git a/input.c b/input.c index 4252428b..d8b334de 100644 --- a/input.c +++ b/input.c @@ -2242,22 +2242,27 @@ static int input_dcs_dispatch(struct input_ctx *ictx) { struct window_pane *wp = ictx->wp; + struct options *oo = wp->options; struct screen_write_ctx *sctx = &ictx->ctx; u_char *buf = ictx->input_buf; size_t len = ictx->input_len; const char prefix[] = "tmux;"; const u_int prefixlen = (sizeof prefix) - 1; + long long allow_passthrough = 0; if (wp == NULL) return (0); if (ictx->flags & INPUT_DISCARD) return (0); - if (!options_get_number(ictx->wp->options, "allow-passthrough")) + allow_passthrough = options_get_number(oo, "allow-passthrough"); + if (!allow_passthrough) return (0); log_debug("%s: \"%s\"", __func__, buf); - if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0) - screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen); + if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0) { + screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen, + allow_passthrough == 2); + } return (0); } diff --git a/options-table.c b/options-table.c index d1e65f15..377835bb 100644 --- a/options-table.c +++ b/options-table.c @@ -88,6 +88,9 @@ static const char *options_table_detach_on_destroy_list[] = { static const char *options_table_extended_keys_list[] = { "off", "on", "always", NULL }; +static const char *options_table_allow_passthrough_list[] = { + "off", "on", "all", NULL +}; /* Status line format. */ #define OPTIONS_TABLE_STATUS_FORMAT1 \ @@ -804,11 +807,14 @@ const struct options_table_entry options_table[] = { }, { .name = "allow-passthrough", - .type = OPTIONS_TABLE_FLAG, + .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .choices = options_table_allow_passthrough_list, .default_num = 0, .text = "Whether applications are allowed to use the escape sequence " - "to bypass tmux." + "to bypass tmux. Can be 'off' (disallowed), 'on' (allowed " + "if the pane is visible), or 'all' (allowed even if the pane " + "is invisible)." }, { .name = "allow-rename", diff --git a/screen-write.c b/screen-write.c index 213b533c..476fe4dd 100644 --- a/screen-write.c +++ b/screen-write.c @@ -2100,13 +2100,15 @@ screen_write_setselection(struct screen_write_ctx *ctx, const char *flags, /* Write unmodified string. */ void -screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) +screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len, + int allow_invisible_panes) { struct tty_ctx ttyctx; screen_write_initctx(ctx, &ttyctx, 0); ttyctx.ptr = str; ttyctx.num = len; + ttyctx.allow_invisible_panes = allow_invisible_panes; tty_write(tty_cmd_rawstring, &ttyctx); } diff --git a/tmux.1 b/tmux.1 index a1298fe0..fa7fcbfb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4461,11 +4461,17 @@ Available pane options are: .Pp .Bl -tag -width Ds -compact .It Xo Ic allow-passthrough -.Op Ic on | off +.Op Ic on | off | all .Xc Allow programs in the pane to bypass .Nm using a terminal escape sequence (\eePtmux;...\ee\e\e). +If set to +.Ic on , +passthrough sequences will be allowed only if the pane is visible. +If set to +.Ic all , +they will be allowed even if the pane is invisible. .Pp .It Xo Ic allow-rename .Op Ic on | off diff --git a/tmux.h b/tmux.h index 3f720e50..4196a31c 100644 --- a/tmux.h +++ b/tmux.h @@ -1431,38 +1431,45 @@ struct tty_ctx { void *ptr; void *ptr2; + /* + * Whether this command should be sent even when the pane is not + * visible (used for a passthrough sequence when allow-passthrough is + * "all"). + */ + int allow_invisible_panes; + /* * Cursor and region position before the screen was updated - this is * where the command should be applied; the values in the screen have * already been updated. */ - u_int ocx; - u_int ocy; + u_int ocx; + u_int ocy; - u_int orupper; - u_int orlower; + u_int orupper; + u_int orlower; /* Target region (usually pane) offset and size. */ - u_int xoff; - u_int yoff; - u_int rxoff; - u_int ryoff; - u_int sx; - u_int sy; + u_int xoff; + u_int yoff; + u_int rxoff; + u_int ryoff; + u_int sx; + u_int sy; /* The background colour used for clearing (erasing). */ - u_int bg; + u_int bg; /* The default colours and palette. */ - struct grid_cell defaults; - struct colour_palette *palette; + struct grid_cell defaults; + struct colour_palette *palette; /* Containing region (usually window) offset and size. */ - int bigger; - u_int wox; - u_int woy; - u_int wsx; - u_int wsy; + int bigger; + u_int wox; + u_int woy; + u_int wsx; + u_int wsy; }; /* Saved message entry. */ @@ -2889,7 +2896,8 @@ 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 *, const char *, u_char *, u_int); -void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int); +void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int, + int); void screen_write_alternateon(struct screen_write_ctx *, struct grid_cell *, int); void screen_write_alternateoff(struct screen_write_ctx *, diff --git a/tty.c b/tty.c index 1f373821..a7ad536f 100644 --- a/tty.c +++ b/tty.c @@ -1626,13 +1626,20 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), if (ctx->set_client_cb == NULL) return; TAILQ_FOREACH(c, &clients, entry) { - if (!tty_client_ready(c)) - continue; - state = ctx->set_client_cb(ctx, c); - if (state == -1) - break; - if (state == 0) - continue; + if (ctx->allow_invisible_panes) { + if (c->session == NULL || + c->tty.term == NULL || + c->flags & CLIENT_SUSPENDED) + continue; + } else { + if (!tty_client_ready(c)) + continue; + state = ctx->set_client_cb(ctx, c); + if (state == -1) + break; + if (state == 0) + continue; + } cmdfn(&c->tty, ctx); } } -- cgit From c6e7568471f35f7a22e199b444adf17c685e86f3 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 3 Aug 2022 13:27:48 +0000 Subject: Do not crash when searching for .* with extremely long lines. Reported by Torbjorn Lonnemark, GitHub issue 3272. --- window-copy.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/window-copy.c b/window-copy.c index ab7ed5b1..834a2d99 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3673,6 +3673,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) if (direction && window_copy_search_mark_at(data, fx, fy, &at) == 0 && at > 0 && + data->searchmark != NULL && data->searchmark[at] == data->searchmark[at - 1]) { window_copy_move_after_search_mark(data, &fx, &fy, wrapflag); @@ -3705,6 +3706,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) &start) == 0) { while (window_copy_search_mark_at(data, fx, fy, &at) == 0 && + data->searchmark != NULL && data->searchmark[at] == data->searchmark[start]) { data->cx = fx; -- cgit From de5cd54124e37672adfdfe0229712e7e74acc9b0 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Aug 2022 12:06:09 +0000 Subject: Change g and G to go to top and bottom of menu, GitHub issue 3286. --- menu.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/menu.c b/menu.c index dc3b289f..4aad1d8c 100644 --- a/menu.c +++ b/menu.c @@ -323,27 +323,64 @@ menu_key_cb(struct client *c, void *data, struct key_event *event) } while ((name == NULL || *name == '-') && md->choice != old); c->flags |= CLIENT_REDRAWOVERLAY; return (0); - case 'g': case KEYC_PPAGE: case '\002': /* C-b */ - if (md->choice > 5) - md->choice -= 5; - else + if (md->choice < 6) md->choice = 0; - while (md->choice != count && (name == NULL || *name == '-')) - md->choice++; - if (md->choice == count) - md->choice = -1; + else { + i = 5; + while (i > 0) { + md->choice--; + name = menu->items[md->choice].name; + if (md->choice != 0 && + (name != NULL && *name != '-')) + i--; + else if (md->choice == 0) + break; + } + } c->flags |= CLIENT_REDRAWOVERLAY; break; - case 'G': case KEYC_NPAGE: - if (md->choice > count - 6) + if (md->choice > count - 6) { md->choice = count - 1; - else - md->choice += 5; - while (md->choice != -1 && (name == NULL || *name == '-')) + name = menu->items[md->choice].name; + } else { + i = 5; + while (i > 0) { + md->choice++; + name = menu->items[md->choice].name; + if (md->choice != count - 1 && + (name != NULL && *name != '-')) + i++; + else if (md->choice == count - 1) + break; + } + } + while (name == NULL || *name == '-') { md->choice--; + name = menu->items[md->choice].name; + } + c->flags |= CLIENT_REDRAWOVERLAY; + break; + case 'g': + case KEYC_HOME: + md->choice = 0; + name = menu->items[md->choice].name; + while (name == NULL || *name == '-') { + md->choice++; + name = menu->items[md->choice].name; + } + c->flags |= CLIENT_REDRAWOVERLAY; + break; + case 'G': + case KEYC_END: + md->choice = count - 1; + name = menu->items[md->choice].name; + while (name == NULL || *name == '-') { + md->choice--; + name = menu->items[md->choice].name; + } c->flags |= CLIENT_REDRAWOVERLAY; break; case '\006': /* C-f */ -- cgit From 273577ba0a88c8a373bc8a2ded7e6a08b82114d0 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 10 Aug 2022 14:03:59 +0000 Subject: Fix check of home directory (&& not ||), from Markus F X J Oberhumer, GitHub issue 3297. --- spawn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spawn.c b/spawn.c index 2349f1b3..98c9ba52 100644 --- a/spawn.c +++ b/spawn.c @@ -391,7 +391,7 @@ spawn_pane(struct spawn_context *sc, char **cause) */ if (chdir(new_wp->cwd) == 0) environ_set(child, "PWD", 0, "%s", new_wp->cwd); - else if ((tmp = find_home()) != NULL || chdir(tmp) == 0) + else if ((tmp = find_home()) != NULL && chdir(tmp) == 0) environ_set(child, "PWD", 0, "%s", tmp); else if (chdir("/") == 0) environ_set(child, "PWD", 0, "/"); -- cgit From e139f977b150d526b3403d7996fdc6690dea0932 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Aug 2022 09:11:26 +0000 Subject: vi(1) Home/End bindings, from Markus F X J Oberhumer. --- key-bindings.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/key-bindings.c b/key-bindings.c index bcc6004d..6ce9c14c 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -616,6 +616,8 @@ key_bindings_init(void) "bind -Tcopy-mode-vi '{' { send -X previous-paragraph }", "bind -Tcopy-mode-vi '}' { send -X next-paragraph }", "bind -Tcopy-mode-vi % { send -X next-matching-bracket }", + "bind -Tcopy-mode-vi Home { send -X start-of-line }", + "bind -Tcopy-mode-vi End { send -X end-of-line }", "bind -Tcopy-mode-vi MouseDown1Pane { select-pane }", "bind -Tcopy-mode-vi MouseDrag1Pane { select-pane; send -X begin-selection }", "bind -Tcopy-mode-vi MouseDragEnd1Pane { send -X copy-pipe-and-cancel }", -- cgit From cfdc5b62ad64a4a41a604cd3b8ae00d9bb928bdf Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 15 Aug 2022 08:37:03 +0000 Subject: Don't stop at first match when updating environment. --- environ.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/environ.c b/environ.c index 74d672e0..5abf383c 100644 --- a/environ.c +++ b/environ.c @@ -182,9 +182,11 @@ void environ_update(struct options *oo, struct environ *src, struct environ *dst) { struct environ_entry *envent; + struct environ_entry *envent1; struct options_entry *o; struct options_array_item *a; union options_value *ov; + int found; o = options_get(oo, "update-environment"); if (o == NULL) @@ -192,14 +194,15 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst) a = options_array_first(o); while (a != NULL) { ov = options_array_item_value(a); - RB_FOREACH(envent, environ, src) { - if (fnmatch(ov->string, envent->name, 0) == 0) - break; + found = 0; + RB_FOREACH_SAFE(envent, environ, src, envent1) { + if (fnmatch(ov->string, envent->name, 0) == 0) { + environ_set(dst, envent->name, 0, "%s", envent->value); + found = 1; + } } - if (envent == NULL) + if (!found) environ_clear(dst, ov->string); - else - environ_set(dst, envent->name, 0, "%s", envent->value); a = options_array_next(a); } } -- cgit From 497021d0db40790cc9a98bb1d70ae091d170831e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 15 Aug 2022 08:41:13 +0000 Subject: Add some const, from Markus F X J Oberhumer. --- key-bindings.c | 2 +- tty-features.c | 54 +++++++++++++++++++++++++++--------------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/key-bindings.c b/key-bindings.c index 6ce9c14c..4b790dfc 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -344,7 +344,7 @@ key_bindings_init_done(__unused struct cmdq_item *item, __unused void *data) void key_bindings_init(void) { - static const char *defaults[] = { + static const char *const defaults[] = { /* Prefix keys. */ "bind -N 'Send the prefix key' C-b { send-prefix }", "bind -N 'Rotate through the panes' C-o { rotate-window }", diff --git a/tty-features.c b/tty-features.c index 396a351e..261cb2b8 100644 --- a/tty-features.c +++ b/tty-features.c @@ -36,13 +36,13 @@ /* A named terminal feature. */ struct tty_feature { - const char *name; - const char **capabilities; - int flags; + const char *name; + const char *const *capabilities; + int flags; }; /* Terminal has xterm(1) title setting. */ -static const char *tty_feature_title_capabilities[] = { +static const char *const tty_feature_title_capabilities[] = { "tsl=\\E]0;", /* should be using TS really */ "fsl=\\a", NULL @@ -54,7 +54,7 @@ static const struct tty_feature tty_feature_title = { }; /* Terminal has OSC 7 working directory. */ -static const char *tty_feature_osc7_capabilities[] = { +static const char *const tty_feature_osc7_capabilities[] = { "Swd=\\E]7;", "fsl=\\a", NULL @@ -66,7 +66,7 @@ static const struct tty_feature tty_feature_osc7 = { }; /* Terminal has mouse support. */ -static const char *tty_feature_mouse_capabilities[] = { +static const char *const tty_feature_mouse_capabilities[] = { "kmous=\\E[M", NULL }; @@ -77,7 +77,7 @@ static const struct tty_feature tty_feature_mouse = { }; /* Terminal can set the clipboard with OSC 52. */ -static const char *tty_feature_clipboard_capabilities[] = { +static const char *const tty_feature_clipboard_capabilities[] = { "Ms=\\E]52;%p1%s;%p2%s\\a", NULL }; @@ -88,7 +88,7 @@ static const struct tty_feature tty_feature_clipboard = { }; /* Terminal supports OSC 8 hyperlinks. */ -static const char *tty_feature_hyperlinks_capabilities[] = { +static const char *const tty_feature_hyperlinks_capabilities[] = { "*:Hls=\\E]8;%?%p1%l%tid=%p1%s%;;%p2%s\\E\\\\", NULL }; @@ -103,7 +103,7 @@ static const struct tty_feature tty_feature_hyperlinks = { * terminals with RGB have versions that do not allow setting colours from the * 256 palette. */ -static const char *tty_feature_rgb_capabilities[] = { +static const char *const tty_feature_rgb_capabilities[] = { "AX", "setrgbf=\\E[38;2;%p1%d;%p2%d;%p3%dm", "setrgbb=\\E[48;2;%p1%d;%p2%d;%p3%dm", @@ -118,7 +118,7 @@ static const struct tty_feature tty_feature_rgb = { }; /* Terminal supports 256 colours. */ -static const char *tty_feature_256_capabilities[] = { +static const char *const tty_feature_256_capabilities[] = { "AX", "setab=\\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m", "setaf=\\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m", @@ -131,7 +131,7 @@ static const struct tty_feature tty_feature_256 = { }; /* Terminal supports overline. */ -static const char *tty_feature_overline_capabilities[] = { +static const char *const tty_feature_overline_capabilities[] = { "Smol=\\E[53m", NULL }; @@ -142,7 +142,7 @@ static const struct tty_feature tty_feature_overline = { }; /* Terminal supports underscore styles. */ -static const char *tty_feature_usstyle_capabilities[] = { +static const char *const tty_feature_usstyle_capabilities[] = { "Smulx=\\E[4::%p1%dm", "Setulc=\\E[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m", "ol=\\E[59m", @@ -155,7 +155,7 @@ static const struct tty_feature tty_feature_usstyle = { }; /* Terminal supports bracketed paste. */ -static const char *tty_feature_bpaste_capabilities[] = { +static const char *const tty_feature_bpaste_capabilities[] = { "Enbp=\\E[?2004h", "Dsbp=\\E[?2004l", NULL @@ -167,7 +167,7 @@ static const struct tty_feature tty_feature_bpaste = { }; /* Terminal supports focus reporting. */ -static const char *tty_feature_focus_capabilities[] = { +static const char *const tty_feature_focus_capabilities[] = { "Enfcs=\\E[?1004h", "Dsfcs=\\E[?1004l", NULL @@ -179,7 +179,7 @@ static const struct tty_feature tty_feature_focus = { }; /* Terminal supports cursor styles. */ -static const char *tty_feature_cstyle_capabilities[] = { +static const char *const tty_feature_cstyle_capabilities[] = { "Ss=\\E[%p1%d q", "Se=\\E[2 q", NULL @@ -191,7 +191,7 @@ static const struct tty_feature tty_feature_cstyle = { }; /* Terminal supports cursor colours. */ -static const char *tty_feature_ccolour_capabilities[] = { +static const char *const tty_feature_ccolour_capabilities[] = { "Cs=\\E]12;%p1%s\\a", "Cr=\\E]112\\a", NULL @@ -203,7 +203,7 @@ static const struct tty_feature tty_feature_ccolour = { }; /* Terminal supports strikethrough. */ -static const char *tty_feature_strikethrough_capabilities[] = { +static const char *const tty_feature_strikethrough_capabilities[] = { "smxx=\\E[9m", NULL }; @@ -214,7 +214,7 @@ static const struct tty_feature tty_feature_strikethrough = { }; /* Terminal supports synchronized updates. */ -static const char *tty_feature_sync_capabilities[] = { +static const char *const tty_feature_sync_capabilities[] = { "Sync=\\EP=%p1%ds\\E\\\\", NULL }; @@ -225,7 +225,7 @@ static const struct tty_feature tty_feature_sync = { }; /* Terminal supports extended keys. */ -static const char *tty_feature_extkeys_capabilities[] = { +static const char *const tty_feature_extkeys_capabilities[] = { "Eneks=\\E[>4;1m", "Dseks=\\E[>4m", NULL @@ -237,7 +237,7 @@ static const struct tty_feature tty_feature_extkeys = { }; /* Terminal supports DECSLRM margins. */ -static const char *tty_feature_margins_capabilities[] = { +static const char *const tty_feature_margins_capabilities[] = { "Enmg=\\E[?69h", "Dsmg=\\E[?69l", "Clmg=\\E[s", @@ -251,7 +251,7 @@ static const struct tty_feature tty_feature_margins = { }; /* Terminal supports DECFRA rectangle fill. */ -static const char *tty_feature_rectfill_capabilities[] = { +static const char *const tty_feature_rectfill_capabilities[] = { "Rect", NULL }; @@ -262,7 +262,7 @@ static const struct tty_feature tty_feature_rectfill = { }; /* Use builtin function keys only. */ -static const char *tty_feature_ignorefkeys_capabilities[] = { +static const char *const tty_feature_ignorefkeys_capabilities[] = { "kf0@", "kf1@", "kf2@", @@ -336,7 +336,7 @@ static const struct tty_feature tty_feature_ignorefkeys = { }; /* Available terminal features. */ -static const struct tty_feature *tty_features[] = { +static const struct tty_feature *const tty_features[] = { &tty_feature_256, &tty_feature_bpaste, &tty_feature_ccolour, @@ -410,9 +410,9 @@ tty_get_features(int feat) int tty_apply_features(struct tty_term *term, int feat) { - const struct tty_feature *tf; - const char **capability; - u_int i; + const struct tty_feature *tf; + const char *const *capability; + u_int i; if (feat == 0) return (0); @@ -443,7 +443,7 @@ tty_apply_features(struct tty_term *term, int feat) void tty_default_features(int *feat, const char *name, u_int version) { - static struct { + static const struct { const char *name; u_int version; const char *features; -- cgit From 03149bf7f62e2c92d0e60087c52604d2dd51794f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 15 Aug 2022 08:54:03 +0000 Subject: Add a Nobr terminfo capability to tell tmux the terminal does not use bright colours for bold (makes a difference to how tmux applies palette differences). From Damien Tardy-Panis in GitHub issue 3301. --- tmux.1 | 4 ++++ tmux.h | 1 + tty-term.c | 1 + tty.c | 8 +++++--- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index fa7fcbfb..b6283373 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6430,6 +6430,10 @@ These are set automatically if the capability is present. .It Em \&Hls Set or clear a hyperlink annotation. +.It Em \&Nobr +Tell +.Nm +that the terminal does not use bright colors for bold display. .It Em \&Rect Tell .Nm diff --git a/tmux.h b/tmux.h index 4196a31c..413d8c38 100644 --- a/tmux.h +++ b/tmux.h @@ -514,6 +514,7 @@ enum tty_code_code { TTYC_KUP6, TTYC_KUP7, TTYC_MS, + TTYC_NOBR, TTYC_OL, TTYC_OP, TTYC_RECT, diff --git a/tty-term.c b/tty-term.c index a877ef6b..e3167f19 100644 --- a/tty-term.c +++ b/tty-term.c @@ -247,6 +247,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_KUP6] = { TTYCODE_STRING, "kUP6" }, [TTYC_KUP7] = { TTYCODE_STRING, "kUP7" }, [TTYC_MS] = { TTYCODE_STRING, "Ms" }, + [TTYC_NOBR] = { TTYCODE_STRING, "Nobr" }, [TTYC_OL] = { TTYCODE_STRING, "ol" }, [TTYC_OP] = { TTYCODE_STRING, "op" }, [TTYC_RECT] = { TTYCODE_STRING, "Rect" }, diff --git a/tty.c b/tty.c index a7ad536f..1394075d 100644 --- a/tty.c +++ b/tty.c @@ -2690,12 +2690,14 @@ tty_check_fg(struct tty *tty, struct colour_palette *palette, /* * Perform substitution if this pane has a palette. If the bright - * attribute is set, use the bright entry in the palette by changing to - * the aixterm colour. + * attribute is set and Nobr is not present, use the bright entry in + * the palette by changing to the aixterm colour */ if (~gc->flags & GRID_FLAG_NOPALETTE) { c = gc->fg; - if (c < 8 && gc->attr & GRID_ATTR_BRIGHT) + if (c < 8 && + gc->attr & GRID_ATTR_BRIGHT && + !tty_term_has(tty->term, TTYC_NOBR)) c += 90; if ((c = colour_palette_get(palette, c)) != -1) gc->fg = c; -- cgit From 7c2dcd72380dc2d9e119e99cb423a67ae17b6bd2 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 15 Aug 2022 09:10:34 +0000 Subject: Notify when a paste buffer is deleted, GitHub issue 3302 from George Nachman. --- control-notify.c | 13 +++++++++++++ notify.c | 29 ++++++++++++++++++++++------- paste.c | 11 +++++++++++ tmux.1 | 4 ++++ tmux.h | 2 ++ 5 files changed, 52 insertions(+), 7 deletions(-) diff --git a/control-notify.c b/control-notify.c index 6ff0e436..a252dd05 100644 --- a/control-notify.c +++ b/control-notify.c @@ -234,3 +234,16 @@ control_notify_session_window_changed(struct session *s) s->curw->window->id); } } + +void +control_notify_paste_buffer_changed(const char *name) +{ + struct client *c; + + TAILQ_FOREACH(c, &clients, entry) { + if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) + continue; + + control_write(c, "%%paste-changed %s", name); + } +} diff --git a/notify.c b/notify.c index 619bd933..03730b98 100644 --- a/notify.c +++ b/notify.c @@ -33,6 +33,7 @@ struct notify_entry { struct session *session; struct window *window; int pane; + const char *pbname; }; static struct cmdq_item * @@ -150,6 +151,8 @@ notify_callback(struct cmdq_item *item, void *data) control_notify_session_closed(ne->session); if (strcmp(ne->name, "session-window-changed") == 0) control_notify_session_window_changed(ne->session); + if (strcmp(ne->name, "paste-buffer-changed") == 0) + control_notify_paste_buffer_changed(ne->pbname); notify_insert_hook(item, ne); @@ -165,6 +168,7 @@ notify_callback(struct cmdq_item *item, void *data) format_free(ne->formats); free((void *)ne->name); + free((void *)ne->pbname); free(ne); return (CMD_RETURN_NORMAL); @@ -172,7 +176,8 @@ notify_callback(struct cmdq_item *item, void *data) static void notify_add(const char *name, struct cmd_find_state *fs, struct client *c, - struct session *s, struct window *w, struct window_pane *wp) + struct session *s, struct window *w, struct window_pane *wp, + const char *pbname) { struct notify_entry *ne; struct cmdq_item *item; @@ -188,6 +193,7 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c, ne->session = s; ne->window = w; ne->pane = (wp != NULL ? wp->id : -1); + ne->pbname = (pbname != NULL ? xstrdup(pbname) : NULL); ne->formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS); format_add(ne->formats, "hook", "%s", name); @@ -249,7 +255,7 @@ notify_client(const char *name, struct client *c) struct cmd_find_state fs; cmd_find_from_client(&fs, c, 0); - notify_add(name, &fs, c, NULL, NULL, NULL); + notify_add(name, &fs, c, NULL, NULL, NULL, NULL); } void @@ -261,7 +267,7 @@ notify_session(const char *name, struct session *s) cmd_find_from_session(&fs, s, 0); else cmd_find_from_nothing(&fs, 0); - notify_add(name, &fs, NULL, s, NULL, NULL); + notify_add(name, &fs, NULL, s, NULL, NULL, NULL); } void @@ -270,7 +276,7 @@ notify_winlink(const char *name, struct winlink *wl) struct cmd_find_state fs; cmd_find_from_winlink(&fs, wl, 0); - notify_add(name, &fs, NULL, wl->session, wl->window, NULL); + notify_add(name, &fs, NULL, wl->session, wl->window, NULL, NULL); } void @@ -279,7 +285,7 @@ notify_session_window(const char *name, struct session *s, struct window *w) struct cmd_find_state fs; cmd_find_from_session_window(&fs, s, w, 0); - notify_add(name, &fs, NULL, s, w, NULL); + notify_add(name, &fs, NULL, s, w, NULL, NULL); } void @@ -288,7 +294,7 @@ notify_window(const char *name, struct window *w) struct cmd_find_state fs; cmd_find_from_window(&fs, w, 0); - notify_add(name, &fs, NULL, NULL, w, NULL); + notify_add(name, &fs, NULL, NULL, w, NULL, NULL); } void @@ -297,5 +303,14 @@ notify_pane(const char *name, struct window_pane *wp) struct cmd_find_state fs; cmd_find_from_pane(&fs, wp, 0); - notify_add(name, &fs, NULL, NULL, NULL, wp); + notify_add(name, &fs, NULL, NULL, NULL, wp, NULL); +} + +void +notify_paste_buffer(const char *pbname) +{ + struct cmd_find_state fs; + + cmd_find_clear_state(&fs, 0); + notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL, NULL, pbname); } diff --git a/paste.c b/paste.c index 5ff36854..53477387 100644 --- a/paste.c +++ b/paste.c @@ -151,6 +151,8 @@ paste_get_name(const char *name) void paste_free(struct paste_buffer *pb) { + notify_paste_buffer(pb->name); + RB_REMOVE(paste_name_tree, &paste_by_name, pb); RB_REMOVE(paste_time_tree, &paste_by_time, pb); if (pb->automatic) @@ -207,6 +209,8 @@ paste_add(const char *prefix, char *data, size_t size) pb->order = paste_next_order++; RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); + + notify_paste_buffer(pb->name); } /* Rename a paste buffer. */ @@ -254,6 +258,9 @@ paste_rename(const char *oldname, const char *newname, char **cause) RB_INSERT(paste_name_tree, &paste_by_name, pb); + notify_paste_buffer(oldname); + notify_paste_buffer(newname); + return (0); } @@ -302,6 +309,8 @@ paste_set(char *data, size_t size, const char *name, char **cause) RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); + notify_paste_buffer(name); + return (0); } @@ -312,6 +321,8 @@ paste_replace(struct paste_buffer *pb, char *data, size_t size) free(pb->data); pb->data = data; pb->size = size; + + notify_paste_buffer(pb->name); } /* Convert start of buffer into a nice string. */ diff --git a/tmux.1 b/tmux.1 index b6283373..3a378c49 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6596,6 +6596,10 @@ escapes non-printable characters and backslash as octal \\xxx. The pane with ID .Ar pane-id has changed mode. +.It Ic %paste-buffer-changed Ar name +Paste buffer +.Ar name +has been changed. .It Ic %pause Ar pane-id The pane has been paused (if the .Ar pause-after diff --git a/tmux.h b/tmux.h index 413d8c38..9b43e0fd 100644 --- a/tmux.h +++ b/tmux.h @@ -2154,6 +2154,7 @@ void notify_winlink(const char *, struct winlink *); void notify_session_window(const char *, struct session *, struct window *); void notify_window(const char *, struct window *); void notify_pane(const char *, struct window_pane *); +void notify_paste_buffer(const char *); /* options.c */ struct options *options_create(struct options *); @@ -3174,6 +3175,7 @@ void control_notify_session_renamed(struct session *); void control_notify_session_created(struct session *); void control_notify_session_closed(struct session *); void control_notify_session_window_changed(struct session *); +void control_notify_paste_buffer_changed(const char *); /* session.c */ extern struct sessions sessions; -- cgit From 19344efa78be23a02008be9da0991f54455c9f9e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 22 Aug 2022 08:20:49 +0100 Subject: Fix fallback implementaion of getpeereid, from Pino Toscano. --- compat/getpeereid.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compat/getpeereid.c b/compat/getpeereid.c index c194e886..b79f420a 100644 --- a/compat/getpeereid.c +++ b/compat/getpeereid.c @@ -18,6 +18,7 @@ #include #include +#include #ifdef HAVE_UCRED_H #include @@ -49,6 +50,8 @@ getpeereid(int s, uid_t *uid, gid_t *gid) ucred_free(ucred); return (0); #else - return (getuid()); + *uid = geteuid(); + *gid = getegid(); + return (0); #endif } -- cgit From 416c27c9958e928b9ff845c1701acf5b10d0db8d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 23 Aug 2022 08:14:19 +0000 Subject: Add scroll-middle copy mode command to make cursor line in the middle, from Varun Kumar E in GitHub issue 3307. --- key-bindings.c | 1 + tmux.1 | 1 + window-copy.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/key-bindings.c b/key-bindings.c index 4b790dfc..528e0b73 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -605,6 +605,7 @@ key_bindings_init(void) "bind -Tcopy-mode-vi h { send -X cursor-left }", "bind -Tcopy-mode-vi j { send -X cursor-down }", "bind -Tcopy-mode-vi k { send -X cursor-up }", + "bind -Tcopy-mode-vi z { send -X scroll-middle }", "bind -Tcopy-mode-vi l { send -X cursor-right }", "bind -Tcopy-mode-vi n { send -X search-again }", "bind -Tcopy-mode-vi o { send -X other-end }", diff --git a/tmux.1 b/tmux.1 index 3a378c49..bf72cc0c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1808,6 +1808,7 @@ The following commands are supported in copy mode: .It Li "search-forward " Ta "/" Ta "" .It Li "search-forward-incremental " Ta "" Ta "C-s" .It Li "search-forward-text " Ta "" Ta "" +.It Li "scroll-middle" Ta "z" Ta "" .It Li "search-reverse" Ta "N" Ta "N" .It Li "select-line" Ta "V" Ta "" .It Li "select-word" Ta "" Ta "" diff --git a/window-copy.c b/window-copy.c index 834a2d99..743364d6 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1250,6 +1250,32 @@ window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_NOTHING); } +static enum window_copy_cmd_action +window_copy_cmd_scroll_middle(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int mid_value, oy, delta; + int scroll_up; /* >0 up, <0 down */ + + mid_value = (screen_size_y(&data->screen) - 1) / 2; + scroll_up = data->cy - mid_value; + delta = abs(scroll_up); + oy = screen_hsize(data->backing) + data->cy - data->oy; + + log_debug ("XXX %u %u %u %d %u", mid_value, oy, delta, scroll_up, data->oy); + if (scroll_up > 0 && data->oy >= delta) { + window_copy_scroll_up(wme, delta); + data->cy -= delta; + } else if (scroll_up < 0 && oy >= delta) { + window_copy_scroll_down(wme, delta); + data->cy += delta; + } + + window_copy_update_selection(wme, 0, 0); + return (WINDOW_COPY_CMD_REDRAW); +} + static enum window_copy_cmd_action window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs) { @@ -2780,6 +2806,12 @@ static const struct { .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, .f = window_copy_cmd_scroll_down_and_cancel }, + { .command = "scroll-middle", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_scroll_middle + }, { .command = "scroll-up", .minargs = 0, .maxargs = 0, -- cgit From e86752820993a00e3d28350cbe46878ba95d9012 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 24 Aug 2022 07:22:30 +0000 Subject: Check for NULL returns from bufferevent_new. --- control.c | 4 ++++ file.c | 4 ++++ window.c | 2 ++ 3 files changed, 10 insertions(+) diff --git a/control.c b/control.c index f75035ef..8b44a274 100644 --- a/control.c +++ b/control.c @@ -776,12 +776,16 @@ control_start(struct client *c) cs->read_event = bufferevent_new(c->fd, control_read_callback, control_write_callback, control_error_callback, c); + if (cs->read_event == NULL) + fatalx("out of memory"); if (c->flags & CLIENT_CONTROLCONTROL) cs->write_event = cs->read_event; else { cs->write_event = bufferevent_new(c->out_fd, NULL, control_write_callback, control_error_callback, c); + if (cs->write_event == NULL) + fatalx("out of memory"); } bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW, 0); diff --git a/file.c b/file.c index 7b956120..280f3547 100644 --- a/file.c +++ b/file.c @@ -588,6 +588,8 @@ file_write_open(struct client_files *files, struct tmuxpeer *peer, cf->event = bufferevent_new(cf->fd, NULL, file_write_callback, file_write_error_callback, cf); + if (cf->event == NULL) + fatalx("out of memory"); bufferevent_enable(cf->event, EV_WRITE); goto reply; @@ -747,6 +749,8 @@ file_read_open(struct client_files *files, struct tmuxpeer *peer, cf->event = bufferevent_new(cf->fd, file_read_callback, NULL, file_read_error_callback, cf); + if (cf->event == NULL) + fatalx("out of memory"); bufferevent_enable(cf->event, EV_READ); return; diff --git a/window.c b/window.c index 2be5d5e6..1b0066c2 100644 --- a/window.c +++ b/window.c @@ -1033,6 +1033,8 @@ window_pane_set_event(struct window_pane *wp) wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, window_pane_error_callback, wp); + if (wp->event == NULL) + fatalx("out of memory"); wp->ictx = input_init(wp, wp->event, &wp->palette); bufferevent_enable(wp->event, EV_READ|EV_WRITE); -- cgit From 68dc9af9ac1a8ea332c82fd510b01c46602fa03a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 31 Aug 2022 08:07:05 +0000 Subject: Fix window size report, from Vincent Bernat. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index d8b334de..37d64218 100644 --- a/input.c +++ b/input.c @@ -1899,7 +1899,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx) } break; case 18: - input_reply(ictx, "\033[8;%u;%ut", x, y); + input_reply(ictx, "\033[8;%u;%ut", y, x); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); -- cgit From 0a0ded32685edafe934b767e6df9d218e227eb95 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 7 Sep 2022 07:28:26 +0100 Subject: Regress typos. --- regress/conf/2eae5d47049c1f6d0bef3db4e171aed7.conf | 2 +- regress/conf/a46e6e84cd1071105aa807256dbc158d.conf | 2 +- regress/conf/ad21dbb0893240563ddfdd954b9903a1.conf | 2 +- regress/conf/b9f0ce1976ec62ec60dc5da7dd92c160.conf | 6 +++--- regress/conf/e2661d67d0d45a8647fb95de76ec8174.conf | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/regress/conf/2eae5d47049c1f6d0bef3db4e171aed7.conf b/regress/conf/2eae5d47049c1f6d0bef3db4e171aed7.conf index c09adc24..c3a4da4b 100644 --- a/regress/conf/2eae5d47049c1f6d0bef3db4e171aed7.conf +++ b/regress/conf/2eae5d47049c1f6d0bef3db4e171aed7.conf @@ -12,7 +12,7 @@ set-window-option -g pane-base-index 1 unbind ^B bind ^B select-pane -t :.+ -# Reload config wtih a key +# Reload config with a key bind-key r source-file ~/.tmux.conf \; display "Config reloaded!" # Mouse works as expected diff --git a/regress/conf/a46e6e84cd1071105aa807256dbc158d.conf b/regress/conf/a46e6e84cd1071105aa807256dbc158d.conf index bfbb2d3e..d8fd9d20 100644 --- a/regress/conf/a46e6e84cd1071105aa807256dbc158d.conf +++ b/regress/conf/a46e6e84cd1071105aa807256dbc158d.conf @@ -24,7 +24,7 @@ set-option -g default-terminal 'screen-256color' # allow Vim to receive focus events from terminal window set-option -g focus-events on -# allow Vim to recieve modifier keys: Shift, Control, Alt +# allow Vim to receive modifier keys: Shift, Control, Alt set-window-option -g xterm-keys on # prevent tmux from catching modifier keys meant for Vim diff --git a/regress/conf/ad21dbb0893240563ddfdd954b9903a1.conf b/regress/conf/ad21dbb0893240563ddfdd954b9903a1.conf index 27d8f310..0c41caa4 100644 --- a/regress/conf/ad21dbb0893240563ddfdd954b9903a1.conf +++ b/regress/conf/ad21dbb0893240563ddfdd954b9903a1.conf @@ -552,7 +552,7 @@ setw -g status-keys emacs # Changelog: https://github.com/tmux/tmux/blob/master/CHANGES # style colors: default, black, red, green, yellow, blue, magenta, cyan, white, -# colour0-colour255, hexdecimal RGB string '#ffffff' +# colour0-colour255, hexadecimal RGB string '#ffffff' # Use $SCRIPTS/bash/256-colors.sh to figure out the color number you want # style attributes: none, bold/bright, dim, underscore, blink, reverse, hidden, # or italics diff --git a/regress/conf/b9f0ce1976ec62ec60dc5da7dd92c160.conf b/regress/conf/b9f0ce1976ec62ec60dc5da7dd92c160.conf index 0a878369..845002c2 100644 --- a/regress/conf/b9f0ce1976ec62ec60dc5da7dd92c160.conf +++ b/regress/conf/b9f0ce1976ec62ec60dc5da7dd92c160.conf @@ -1,6 +1,6 @@ -# none of these attempts worked, to bind keys, except sometimes during the sesssion. Oh well. +# none of these attempts worked, to bind keys, except sometimes during the session. Oh well. # I thought maybe that was because F1 is handled differently in a console than in X, but -# even just C-1 didnt work. Using just "a" or "x" as the key did, but not yet sure why not "C-". +# even just C-1 didn't work. Using just "a" or "x" as the key did, but not yet sure why not "C-". #bind-key -T root C-1 attach-session -t$0 #But this one works now, only picks the wrong one? Mbe need2understand what "$1" or $0 mean, better, #but with the stub maybe this doesn't matter: @@ -47,7 +47,7 @@ select-window -t :=3 #$3 for email (mutt) new-session sula new-window sula ; send-keys mutt Enter -#nah, probly betr not?: +#nah, probably better not?: #send-keys -l z #send-keys -l "thepassifdecide" #send-keys Enter diff --git a/regress/conf/e2661d67d0d45a8647fb95de76ec8174.conf b/regress/conf/e2661d67d0d45a8647fb95de76ec8174.conf index 79f46df1..3cf1ae9d 100644 --- a/regress/conf/e2661d67d0d45a8647fb95de76ec8174.conf +++ b/regress/conf/e2661d67d0d45a8647fb95de76ec8174.conf @@ -63,7 +63,7 @@ bind N command-prompt -p hosts: 'run-shell -b "bash -c \"~/lbin/nw %% >/dev/null #05:59 < Celti> annihilannic: I believe the #{pane_in_mode} format does what you want #05:59 < Celti> put it in your statusline #05:59 < Celti> annihilannic: No, my mistake, I should have read farther down, you want #{pane_synchronized} -# only works in tmux 2.0?, higher than 1.6.3 anyawy +# only works in tmux 2.0?, higher than 1.6.3 anyway set-option -g window-status-format ' #I:#W#F#{?pane_synchronized,S,}' #set-option -g window-status-current-format ' #I:#W#{?pane_synchronized,[sync],}#F' # to highlight in red when sync is on... not sure why I did this with set-window-option instead of set-option, perhaps -- cgit From f03c3ca6c36cd52ae5ec4ae8cd4fa137cf79aaf3 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 9 Sep 2022 11:02:23 +0000 Subject: Add message-line option to control where message and prompt go, from Varun Kumar E in GitHub issue 3324. --- options-table.c | 13 ++++++++++++- status.c | 37 ++++++++++++++++++++++++++++--------- tmux.1 | 4 ++++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/options-table.c b/options-table.c index 377835bb..6fc5a96f 100644 --- a/options-table.c +++ b/options-table.c @@ -42,6 +42,9 @@ static const char *options_table_clock_mode_style_list[] = { static const char *options_table_status_list[] = { "off", "on", "2", "3", "4", "5", NULL }; +static const char *options_table_message_line_list[] = { + "0", "1", "2", "3", "4", NULL +}; static const char *options_table_status_keys_list[] = { "emacs", "vi", NULL }; @@ -542,13 +545,21 @@ const struct options_table_entry options_table[] = { "'mode-keys' is set to 'vi'." }, + { .name = "message-line", + .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SESSION, + .choices = options_table_message_line_list, + .default_num = 0, + .text = "Position (line) of messages and the command prompt." + }, + { .name = "message-style", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=yellow,fg=black", .flags = OPTIONS_TABLE_IS_STYLE, .separator = ",", - .text = "Style of the command prompt." + .text = "Style of messages and the command prompt." }, { .name = "mouse", diff --git a/status.c b/status.c index 929276d2..baf6ed2b 100644 --- a/status.c +++ b/status.c @@ -263,6 +263,17 @@ status_line_size(struct client *c) return (s->statuslines); } +/* Get the prompt line number for client's session. 1 means at the bottom. */ +static u_int +status_prompt_line_at(struct client *c) +{ + struct session *s = c->session; + + if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL)) + return (1); + return (options_get_number(s->options, "status-prompt-line")); +} + /* Get window at window list position. */ struct style_range * status_get_range(struct client *c, u_int x, u_int y) @@ -533,7 +544,7 @@ status_message_redraw(struct client *c) struct session *s = c->session; struct screen old_screen; size_t len; - u_int lines, offset; + u_int lines, offset, messageline; struct grid_cell gc; struct format_tree *ft; @@ -546,6 +557,10 @@ status_message_redraw(struct client *c) lines = 1; screen_init(sl->active, c->tty.sx, lines, 0); + messageline = status_prompt_line_at(c); + if (messageline > lines - 1) + messageline = lines - 1; + len = screen_write_strlen("%s", c->message_string); if (len > c->tty.sx) len = c->tty.sx; @@ -555,11 +570,11 @@ status_message_redraw(struct client *c) format_free(ft); screen_write_start(&ctx, sl->active); - screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); - screen_write_cursormove(&ctx, 0, lines - 1, 0); + screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines); + screen_write_cursormove(&ctx, 0, messageline, 0); for (offset = 0; offset < c->tty.sx; offset++) screen_write_putc(&ctx, &gc, ' '); - screen_write_cursormove(&ctx, 0, lines - 1, 0); + screen_write_cursormove(&ctx, 0, messageline, 0); if (c->message_ignore_styles) screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); else @@ -695,7 +710,7 @@ status_prompt_redraw(struct client *c) struct session *s = c->session; struct screen old_screen; u_int i, lines, offset, left, start, width; - u_int pcursor, pwidth; + u_int pcursor, pwidth, promptline; struct grid_cell gc, cursorgc; struct format_tree *ft; @@ -708,6 +723,10 @@ status_prompt_redraw(struct client *c) lines = 1; screen_init(sl->active, c->tty.sx, lines, 0); + promptline = status_prompt_line_at(c); + if (promptline > lines - 1) + promptline = lines - 1; + ft = format_create_defaults(NULL, c, NULL, NULL, NULL); if (c->prompt_mode == PROMPT_COMMAND) style_apply(&gc, s->options, "message-command-style", ft); @@ -723,13 +742,13 @@ status_prompt_redraw(struct client *c) start = c->tty.sx; screen_write_start(&ctx, sl->active); - screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); - screen_write_cursormove(&ctx, 0, lines - 1, 0); + screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines); + screen_write_cursormove(&ctx, 0, promptline, 0); for (offset = 0; offset < c->tty.sx; offset++) screen_write_putc(&ctx, &gc, ' '); - screen_write_cursormove(&ctx, 0, lines - 1, 0); + screen_write_cursormove(&ctx, 0, promptline, 0); format_draw(&ctx, &gc, start, c->prompt_string, NULL, 0); - screen_write_cursormove(&ctx, start, lines - 1, 0); + screen_write_cursormove(&ctx, start, promptline, 0); left = c->tty.sx - start; if (left == 0) diff --git a/tmux.1 b/tmux.1 index bf72cc0c..485b5d3a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3873,6 +3873,10 @@ For how to specify see the .Sx STYLES section. +.It Xo Ic message-line +.Op Ic 0 | 1 | 2 | 3 | 4 +.Xc +Set line on which status line messages and the command prompt are shown. .It Ic message-style Ar style Set status line message style. This is used for messages and for the command prompt. -- cgit From 9ab1ba36cd2de4ca83694c3d475061323ffcc4d4 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 10 Sep 2022 17:01:33 +0000 Subject: Use correct option name. --- status.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/status.c b/status.c index baf6ed2b..b504bbbe 100644 --- a/status.c +++ b/status.c @@ -271,7 +271,7 @@ status_prompt_line_at(struct client *c) if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL)) return (1); - return (options_get_number(s->options, "status-prompt-line")); + return (options_get_number(s->options, "message-line")); } /* Get window at window list position. */ -- cgit From a2cc601c3d1a569037ccd238b2638b5c416baca8 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 12 Sep 2022 12:02:17 +0000 Subject: Don't use options from pane if pane is NULL. --- input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/input.c b/input.c index 37d64218..8b174769 100644 --- a/input.c +++ b/input.c @@ -2242,7 +2242,6 @@ static int input_dcs_dispatch(struct input_ctx *ictx) { struct window_pane *wp = ictx->wp; - struct options *oo = wp->options; struct screen_write_ctx *sctx = &ictx->ctx; u_char *buf = ictx->input_buf; size_t len = ictx->input_len; @@ -2254,7 +2253,8 @@ input_dcs_dispatch(struct input_ctx *ictx) return (0); if (ictx->flags & INPUT_DISCARD) return (0); - allow_passthrough = options_get_number(oo, "allow-passthrough"); + allow_passthrough = options_get_number(wp->options, + "allow-passthrough"); if (!allow_passthrough) return (0); log_debug("%s: \"%s\"", __func__, buf); -- cgit From 19344ec890e0c4343e3c20695806d482c631d67c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 19 Sep 2022 07:03:17 +0100 Subject: Add headers and fix type, from Marvin Schmidt. GitHub issue 3332. --- compat/systemd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compat/systemd.c b/compat/systemd.c index 7317e43a..9a3adbb3 100644 --- a/compat/systemd.c +++ b/compat/systemd.c @@ -21,6 +21,8 @@ #include +#include + #include "tmux.h" int @@ -29,7 +31,7 @@ systemd_create_socket(int flags, char **cause) int fds; int fd; struct sockaddr_un sa; - int addrlen = sizeof sa; + socklen_t addrlen = sizeof sa; fds = sd_listen_fds(0); if (fds > 1) { /* too many file descriptors */ -- cgit From 9cc8e40aa08ff91bc1c5d0211c1d2cef02f2c7a2 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 28 Sep 2022 07:55:29 +0000 Subject: Add a -T flag to capture-pane to stop at the last used cell instead of the full width. Restore the previous behaviour by making it default to off unless -J is used (the only time it matters). Fixes mosh unit tests; GitHub issue 3339. --- cmd-capture-pane.c | 18 ++++++----- grid-view.c | 2 +- grid.c | 88 ++++++++++++++++++++++++++++++------------------------ tmux.h | 11 ++++++- 4 files changed, 71 insertions(+), 48 deletions(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 422f87d6..57e9716d 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -40,7 +40,7 @@ const struct cmd_entry cmd_capture_pane_entry = { .alias = "capturep", .args = { "ab:CeE:JNpPqS:t:", 0, 0, NULL }, - .usage = "[-aCeJNpPq] " CMD_BUFFER_USAGE " [-E end-line] " + .usage = "[-aCeJNpPqT] " CMD_BUFFER_USAGE " [-E end-line] " "[-S start-line] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -110,7 +110,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, struct grid *gd; const struct grid_line *gl; struct grid_cell *gc = NULL; - int n, with_codes, escape_c0, join_lines, no_trim; + int n, join_lines, flags = 0; u_int i, sx, top, bottom, tmp; char *cause, *buf, *line; const char *Sflag, *Eflag; @@ -169,15 +169,19 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item, top = tmp; } - with_codes = args_has(args, 'e'); - escape_c0 = args_has(args, 'C'); join_lines = args_has(args, 'J'); - no_trim = args_has(args, 'N'); + if (args_has(args, 'e')) + flags |= GRID_STRING_WITH_SEQUENCES; + if (args_has(args, 'C')) + flags |= GRID_STRING_ESCAPE_SEQUENCES; + if (!join_lines && !args_has(args, 'T')) + flags |= GRID_STRING_EMPTY_CELLS; + if (!join_lines && !args_has(args, 'N')) + flags |= GRID_STRING_TRIM_SPACES; buf = NULL; for (i = top; i <= bottom; i++) { - line = grid_string_cells(gd, 0, i, sx, &gc, with_codes, - escape_c0, !join_lines && !no_trim, wp->screen); + line = grid_string_cells(gd, 0, i, sx, &gc, flags, wp->screen); linelen = strlen(line); buf = cmd_capture_pane_append(buf, len, line, linelen); diff --git a/grid-view.c b/grid-view.c index 689ac4e4..4d687339 100644 --- a/grid-view.c +++ b/grid-view.c @@ -231,5 +231,5 @@ grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) px = grid_view_x(gd, px); py = grid_view_y(gd, py); - return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0, NULL)); + return (grid_string_cells(gd, px, py, nx, NULL, 0, NULL)); } diff --git a/grid.c b/grid.c index b1afd398..58de03a3 100644 --- a/grid.c +++ b/grid.c @@ -861,40 +861,45 @@ grid_string_cells_us(const struct grid_cell *gc, int *values) /* Add on SGR code. */ static void grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc, - int *oldc, size_t nnewc, size_t noldc, int escape_c0) + int *oldc, size_t nnewc, size_t noldc, int flags) { u_int i; char tmp[64]; - - if (nnewc != 0 && - (nnewc != noldc || - memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 || - (n != 0 && s[0] == 0))) { - if (escape_c0) - strlcat(buf, "\\033[", len); + int reset = (n != 0 && s[0] == 0); + + if (nnewc == 0) + return; /* no code to add */ + if (!reset && + nnewc == noldc && + memcmp(newc, oldc, nnewc * sizeof newc[0]) == 0) + return; /* no reset and colour unchanged */ + if (reset && (newc[0] == 49 || newc[0] == 39)) + return; /* reset and colour default */ + + if (flags & GRID_STRING_ESCAPE_SEQUENCES) + strlcat(buf, "\\033[", len); + else + strlcat(buf, "\033[", len); + for (i = 0; i < nnewc; i++) { + if (i + 1 < nnewc) + xsnprintf(tmp, sizeof tmp, "%d;", newc[i]); else - strlcat(buf, "\033[", len); - for (i = 0; i < nnewc; i++) { - if (i + 1 < nnewc) - xsnprintf(tmp, sizeof tmp, "%d;", newc[i]); - else - xsnprintf(tmp, sizeof tmp, "%d", newc[i]); - strlcat(buf, tmp, len); - } - strlcat(buf, "m", len); + xsnprintf(tmp, sizeof tmp, "%d", newc[i]); + strlcat(buf, tmp, len); } + strlcat(buf, "m", len); } static int grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id, - const char *uri, int escape_c0) + const char *uri, int flags) { char *tmp; if (strlen(uri) + strlen(id) + 17 >= len) return (0); - if (escape_c0) + if (flags & GRID_STRING_ESCAPE_SEQUENCES) strlcat(buf, "\\033]8;", len); else strlcat(buf, "\033]8;", len); @@ -905,7 +910,7 @@ grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id, } else strlcat(buf, ";", len); strlcat(buf, uri, len); - if (escape_c0) + if (flags & GRID_STRING_ESCAPE_SEQUENCES) strlcat(buf, "\\033\\\\", len); else strlcat(buf, "\033\\", len); @@ -918,7 +923,7 @@ grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id, */ static void grid_string_cells_code(const struct grid_cell *lastgc, - const struct grid_cell *gc, char *buf, size_t len, int escape_c0, + const struct grid_cell *gc, char *buf, size_t len, int flags, struct screen *sc, int *has_link) { int oldc[64], newc[64], s[128]; @@ -927,7 +932,7 @@ grid_string_cells_code(const struct grid_cell *lastgc, char tmp[64]; const char *uri, *id; - struct { + static const struct { u_int mask; u_int code; } attrs[] = { @@ -966,7 +971,7 @@ grid_string_cells_code(const struct grid_cell *lastgc, /* Write the attributes. */ *buf = '\0'; if (n > 0) { - if (escape_c0) + if (flags & GRID_STRING_ESCAPE_SEQUENCES) strlcat(buf, "\\033[", len); else strlcat(buf, "\033[", len); @@ -988,29 +993,29 @@ grid_string_cells_code(const struct grid_cell *lastgc, nnewc = grid_string_cells_fg(gc, newc); noldc = grid_string_cells_fg(lastgc, oldc); grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, - escape_c0); + flags); /* If the background colour changed, append its parameters. */ nnewc = grid_string_cells_bg(gc, newc); noldc = grid_string_cells_bg(lastgc, oldc); grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, - escape_c0); + flags); /* If the underscore colour changed, append its parameters. */ nnewc = grid_string_cells_us(gc, newc); noldc = grid_string_cells_us(lastgc, oldc); grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, - escape_c0); + flags); /* Append shift in/shift out if needed. */ if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) { - if (escape_c0) + if (flags & GRID_STRING_ESCAPE_SEQUENCES) strlcat(buf, "\\016", len); /* SO */ else strlcat(buf, "\016", len); /* SO */ } if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) { - if (escape_c0) + if (flags & GRID_STRING_ESCAPE_SEQUENCES) strlcat(buf, "\\017", len); /* SI */ else strlcat(buf, "\017", len); /* SI */ @@ -1020,10 +1025,10 @@ grid_string_cells_code(const struct grid_cell *lastgc, if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) { if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) { *has_link = grid_string_cells_add_hyperlink(buf, len, - id, uri, escape_c0); + id, uri, flags); } else if (*has_link) { grid_string_cells_add_hyperlink(buf, len, "", "", - escape_c0); + flags); *has_link = 0; } } @@ -1032,15 +1037,14 @@ grid_string_cells_code(const struct grid_cell *lastgc, /* Convert cells into a string. */ char * grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, - struct grid_cell **lastgc, int with_codes, int escape_c0, int trim, - struct screen *s) + struct grid_cell **lastgc, int flags, struct screen *s) { struct grid_cell gc; static struct grid_cell lastgc1; const char *data; char *buf, code[8192]; size_t len, off, size, codelen; - u_int xx, has_link = 0; + u_int xx, has_link = 0, end; const struct grid_line *gl; if (lastgc != NULL && *lastgc == NULL) { @@ -1053,16 +1057,20 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, off = 0; gl = grid_peek_line(gd, py); + if (flags & GRID_STRING_EMPTY_CELLS) + end = gl->cellsize; + else + end = gl->cellused; for (xx = px; xx < px + nx; xx++) { - if (gl == NULL || xx >= gl->cellused) + if (gl == NULL || xx >= end) break; grid_get_cell(gd, xx, py, &gc); if (gc.flags & GRID_FLAG_PADDING) continue; - if (with_codes) { + if (flags & GRID_STRING_WITH_SEQUENCES) { grid_string_cells_code(*lastgc, &gc, code, sizeof code, - escape_c0, s, &has_link); + flags, s, &has_link); codelen = strlen(code); memcpy(*lastgc, &gc, sizeof **lastgc); } else @@ -1070,7 +1078,9 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, data = gc.data.data; size = gc.data.size; - if (escape_c0 && size == 1 && *data == '\\') { + if ((flags & GRID_STRING_ESCAPE_SEQUENCES) && + size == 1 && + *data == '\\') { data = "\\\\"; size = 2; } @@ -1090,7 +1100,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, if (has_link) { grid_string_cells_add_hyperlink(code, sizeof code, "", "", - escape_c0); + flags); codelen = strlen(code); while (len < off + size + codelen + 1) { buf = xreallocarray(buf, 2, len); @@ -1100,7 +1110,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, off += codelen; } - if (trim) { + if (flags & GRID_STRING_TRIM_SPACES) { while (off > 0 && buf[off - 1] == ' ') off--; } diff --git a/tmux.h b/tmux.h index 9b43e0fd..77019edc 100644 --- a/tmux.h +++ b/tmux.h @@ -667,6 +667,14 @@ struct colour_palette { #define GRID_LINE_EXTENDED 0x2 #define GRID_LINE_DEAD 0x4 +/* Grid string flags. */ +#define GRID_STRING_WITH_SEQUENCES 0x1 +#define GRID_STRING_ESCAPE_SEQUENCES 0x2 +#define GRID_STRING_TRIM_SPACES 0x4 +#define GRID_STRING_USED_ONLY 0x8 +#define GRID_STRING_EMPTY_CELLS 0x10 + +/* Cell positions. */ #define CELL_INSIDE 0 #define CELL_TOPBOTTOM 1 #define CELL_LEFTRIGHT 2 @@ -681,6 +689,7 @@ struct colour_palette { #define CELL_JOIN 11 #define CELL_OUTSIDE 12 +/* Cell borders. */ #define CELL_BORDERS " xqlkmjwvtun~" #define SIMPLE_BORDERS " |-+++++++++." #define PADDED_BORDERS " " @@ -2783,7 +2792,7 @@ void grid_clear_lines(struct grid *, u_int, u_int, u_int); void grid_move_lines(struct grid *, u_int, u_int, u_int, u_int); void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int, u_int); char *grid_string_cells(struct grid *, u_int, u_int, u_int, - struct grid_cell **, int, int, int, struct screen *); + struct grid_cell **, int, struct screen *); void grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int, u_int); void grid_reflow(struct grid *, u_int); -- cgit From a10452be2d84d4ff1b9d4e636627b7a9c5a2c42a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 28 Sep 2022 07:59:50 +0000 Subject: Add scroll-top and scroll-bottom commands to scroll so cursor is at top or bottom. From Anindya Mukherjee, GitHub issue 3334. --- tmux.1 | 11 +++++++++-- window-copy.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/tmux.1 b/tmux.1 index 485b5d3a..5e896902 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1808,7 +1808,9 @@ The following commands are supported in copy mode: .It Li "search-forward " Ta "/" Ta "" .It Li "search-forward-incremental " Ta "" Ta "C-s" .It Li "search-forward-text " Ta "" Ta "" +.It Li "scroll-bottom" Ta "" Ta "" .It Li "scroll-middle" Ta "z" Ta "" +.It Li "scroll-top" Ta "" Ta "" .It Li "search-reverse" Ta "N" Ta "N" .It Li "select-line" Ta "V" Ta "" .It Li "select-word" Ta "" Ta "" @@ -2011,7 +2013,7 @@ but a different format may be specified with .Fl F . .Tg capturep .It Xo Ic capture-pane -.Op Fl aepPqCJN +.Op Fl aAepPqCJN .Op Fl b Ar buffer-name .Op Fl E Ar end-line .Op Fl S Ar start-line @@ -2036,10 +2038,15 @@ is given, the output includes escape sequences for text and background attributes. .Fl C also escapes non-printable characters as octal \exxx. +.Fl T +ignores trailing positions that do not contain a character. .Fl N preserves trailing spaces at each line's end and .Fl J -preserves trailing spaces and joins any wrapped lines. +preserves trailing spaces and joins any wrapped lines; +.Fl J +implies +.Fl T . .Fl P captures only any output that the pane has received that is the beginning of an as-yet incomplete escape sequence. diff --git a/window-copy.c b/window-copy.c index 743364d6..ed481d70 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1250,20 +1250,23 @@ window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_NOTHING); } +/* Scroll line containing the cursor to the given position. */ static enum window_copy_cmd_action -window_copy_cmd_scroll_middle(struct window_copy_cmd_state *cs) +window_copy_cmd_scroll_to(struct window_copy_cmd_state *cs, u_int to) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; - u_int mid_value, oy, delta; + u_int oy, delta; int scroll_up; /* >0 up, <0 down */ - mid_value = (screen_size_y(&data->screen) - 1) / 2; - scroll_up = data->cy - mid_value; + scroll_up = data->cy - to; delta = abs(scroll_up); - oy = screen_hsize(data->backing) + data->cy - data->oy; + oy = screen_hsize(data->backing) - data->oy; - log_debug ("XXX %u %u %u %d %u", mid_value, oy, delta, scroll_up, data->oy); + /* + * oy is the maximum scroll down amount, while data->oy is the maximum + * scroll up amount. + */ if (scroll_up > 0 && data->oy >= delta) { window_copy_scroll_up(wme, delta); data->cy -= delta; @@ -1276,6 +1279,35 @@ window_copy_cmd_scroll_middle(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_REDRAW); } +/* Scroll line containing the cursor to the bottom. */ +static enum window_copy_cmd_action +window_copy_cmd_scroll_bottom(struct window_copy_cmd_state *cs) +{ + struct window_copy_mode_data *data = cs->wme->data; + u_int bottom; + + bottom = screen_size_y(&data->screen) - 1; + return (window_copy_cmd_scroll_to(cs, bottom)); +} + +/* Scroll line containing the cursor to the middle. */ +static enum window_copy_cmd_action +window_copy_cmd_scroll_middle(struct window_copy_cmd_state *cs) +{ + struct window_copy_mode_data *data = cs->wme->data; + u_int mid_value; + + mid_value = (screen_size_y(&data->screen) - 1) / 2; + return (window_copy_cmd_scroll_to(cs, mid_value)); +} + +/* Scroll line containing the cursor to the top. */ +static enum window_copy_cmd_action +window_copy_cmd_scroll_top(struct window_copy_cmd_state *cs) +{ + return (window_copy_cmd_scroll_to(cs, 0)); +} + static enum window_copy_cmd_action window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs) { @@ -2794,6 +2826,12 @@ static const struct { .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, .f = window_copy_cmd_refresh_from_pane }, + { .command = "scroll-bottom", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_scroll_bottom + }, { .command = "scroll-down", .minargs = 0, .maxargs = 0, @@ -2812,6 +2850,12 @@ static const struct { .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, .f = window_copy_cmd_scroll_middle }, + { .command = "scroll-top", + .minargs = 0, + .maxargs = 0, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_scroll_top + }, { .command = "scroll-up", .minargs = 0, .maxargs = 0, -- cgit From ff2766b024e6657b5f5f70dfd5acdb32085d7b70 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 17 Oct 2022 10:59:42 +0000 Subject: Preserve marked pane when renumbering windows. --- session.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/session.c b/session.c index 5bc5330e..c9a98d23 100644 --- a/session.c +++ b/session.c @@ -710,7 +710,7 @@ session_renumber_windows(struct session *s) struct winlink *wl, *wl1, *wl_new; struct winlinks old_wins; struct winlink_stack old_lastw; - int new_idx, new_curw_idx; + int new_idx, new_curw_idx, marked_idx = -1; /* Save and replace old window list. */ memcpy(&old_wins, &s->windows, sizeof old_wins); @@ -727,6 +727,8 @@ session_renumber_windows(struct session *s) winlink_set_window(wl_new, wl->window); wl_new->flags |= wl->flags & WINLINK_ALERTFLAGS; + if (wl == marked_pane.wl) + marked_idx = wl_new->idx; if (wl == s->curw) new_curw_idx = wl_new->idx; @@ -743,6 +745,11 @@ session_renumber_windows(struct session *s) } /* Set the current window. */ + if (marked_idx != -1) { + marked_pane.wl = winlink_find_by_index(&s->windows, marked_idx); + if (marked_pane.wl == NULL) + server_clear_marked(); + } s->curw = winlink_find_by_index(&s->windows, new_curw_idx); /* Free the old winlinks (reducing window references too). */ -- cgit From 5ce34add77fa3517a01e63b915c5f4e3241af470 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 18 Oct 2022 15:58:06 +0100 Subject: Do not attempt to connect to the socket as a client if systemd is active, from Julien Moutinho in GitHub issue 3345. --- client.c | 6 ++++++ compat.h | 1 + compat/systemd.c | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/client.c b/client.c index df6cee94..7f712ffb 100644 --- a/client.c +++ b/client.c @@ -284,6 +284,12 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, log_debug("flags are %#llx", (unsigned long long)client_flags); /* Initialize the client socket and start the server. */ +#ifdef HAVE_SYSTEMD + if (systemd_activated()) { + /* socket-based activation, do not even try to be a client. */ + fd = server_start(client_proc, flags, base, 0, NULL); + } else +#endif fd = client_connect(base, socket_path, client_flags); if (fd == -1) { if (errno == ECONNREFUSED) { diff --git a/compat.h b/compat.h index 6eb97619..aefc0d38 100644 --- a/compat.h +++ b/compat.h @@ -423,6 +423,7 @@ void *recallocarray(void *, size_t, size_t, size_t); #ifdef HAVE_SYSTEMD /* systemd.c */ +int systemd_activated(void); int systemd_create_socket(int, char **); #endif diff --git a/compat/systemd.c b/compat/systemd.c index 9a3adbb3..cce42ed4 100644 --- a/compat/systemd.c +++ b/compat/systemd.c @@ -25,6 +25,12 @@ #include "tmux.h" +int +systemd_activated(void) +{ + return (sd_listen_fds(0) >= 1); +} + int systemd_create_socket(int flags, char **cause) { -- cgit From 0fc961b22ee6c7298e8dd40d3ce8e2b484bc6b1d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 25 Oct 2022 09:04:49 +0000 Subject: Do not fire redraw callback if NULL. --- screen-write.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/screen-write.c b/screen-write.c index 476fe4dd..a2107f2a 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1505,8 +1505,10 @@ screen_write_fullredraw(struct screen_write_ctx *ctx) screen_write_collect_flush(ctx, 0, __func__); - screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.redraw_cb(&ttyctx); + if (ttyctx.redraw_cb != NULL) { + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.redraw_cb(&ttyctx); + } } /* Trim collected items. */ @@ -2127,8 +2129,10 @@ screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc, screen_write_collect_flush(ctx, 0, __func__); screen_alternate_on(ctx->s, gc, cursor); - screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.redraw_cb(&ttyctx); + if (ttyctx.redraw_cb != NULL) { + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.redraw_cb(&ttyctx); + } } /* Turn alternate screen off. */ @@ -2145,6 +2149,8 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc, screen_write_collect_flush(ctx, 0, __func__); screen_alternate_off(ctx->s, gc, cursor); - screen_write_initctx(ctx, &ttyctx, 1); - ttyctx.redraw_cb(&ttyctx); + if (ttyctx.redraw_cb != NULL) { + screen_write_initctx(ctx, &ttyctx, 1); + ttyctx.redraw_cb(&ttyctx); + } } -- cgit From 2111142cf1715eeac174cd5c71ed90f00595b17e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 25 Oct 2022 09:12:05 +0000 Subject: Fix a memory leak, from Japin Li in GitHub issue 3358. --- cmd-parse.y | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd-parse.y b/cmd-parse.y index 1d692770..cdf026f3 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -1086,7 +1086,8 @@ cmd_parse_from_arguments(struct args_value *values, u_int count, arg->type = CMD_PARSE_STRING; arg->string = copy; TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry); - } + } else + free(copy); } else if (values[i].type == ARGS_COMMANDS) { arg = xcalloc(1, sizeof *arg); arg->type = CMD_PARSE_PARSED_COMMANDS; -- cgit From c2580cfe2466589c8bd9348225820888a3fc4c0a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 25 Oct 2022 17:53:31 +0000 Subject: Initialize context before testing it. --- screen-write.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/screen-write.c b/screen-write.c index a2107f2a..24195708 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1505,10 +1505,9 @@ screen_write_fullredraw(struct screen_write_ctx *ctx) screen_write_collect_flush(ctx, 0, __func__); - if (ttyctx.redraw_cb != NULL) { - screen_write_initctx(ctx, &ttyctx, 1); + screen_write_initctx(ctx, &ttyctx, 1); + if (ttyctx.redraw_cb != NULL) ttyctx.redraw_cb(&ttyctx); - } } /* Trim collected items. */ @@ -2129,10 +2128,9 @@ screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc, screen_write_collect_flush(ctx, 0, __func__); screen_alternate_on(ctx->s, gc, cursor); - if (ttyctx.redraw_cb != NULL) { - screen_write_initctx(ctx, &ttyctx, 1); + screen_write_initctx(ctx, &ttyctx, 1); + if (ttyctx.redraw_cb != NULL) ttyctx.redraw_cb(&ttyctx); - } } /* Turn alternate screen off. */ @@ -2149,8 +2147,7 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc, screen_write_collect_flush(ctx, 0, __func__); screen_alternate_off(ctx->s, gc, cursor); - if (ttyctx.redraw_cb != NULL) { - screen_write_initctx(ctx, &ttyctx, 1); + screen_write_initctx(ctx, &ttyctx, 1); + if (ttyctx.redraw_cb != NULL) ttyctx.redraw_cb(&ttyctx); - } } -- cgit From 8edece2cdb7b4425526bae904506a246edbb6409 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Oct 2022 13:00:02 +0000 Subject: Add paste-buffer-deleted notification and fix name of paste-buffer-changed. --- control-notify.c | 15 ++++++++++++++- input.c | 11 ----------- notify.c | 12 ++++++++++-- paste.c | 12 ++++++------ tmux.1 | 4 ++++ tmux.h | 3 ++- 6 files changed, 36 insertions(+), 21 deletions(-) diff --git a/control-notify.c b/control-notify.c index a252dd05..30f94194 100644 --- a/control-notify.c +++ b/control-notify.c @@ -244,6 +244,19 @@ control_notify_paste_buffer_changed(const char *name) if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; - control_write(c, "%%paste-changed %s", name); + control_write(c, "%%paste-buffer-changed %s", name); + } +} + +void +control_notify_paste_buffer_deleted(const char *name) +{ + struct client *c; + + TAILQ_FOREACH(c, &clients, entry) { + if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) + continue; + + control_write(c, "%%paste-buffer-deleted %s", name); } } diff --git a/input.c b/input.c index 8b174769..779b9013 100644 --- a/input.c +++ b/input.c @@ -1755,7 +1755,6 @@ static void input_csi_dispatch_sm_private(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; - struct window_pane *wp = ictx->wp; struct grid_cell *gc = &ictx->cell.cell; u_int i; @@ -1797,17 +1796,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) screen_write_mode_set(sctx, MODE_MOUSE_ALL); break; case 1004: - if (sctx->s->mode & MODE_FOCUSON) - break; screen_write_mode_set(sctx, MODE_FOCUSON); - if (wp == NULL) - break; - if (!options_get_number(global_options, "focus-events")) - break; - if (wp->flags & PANE_FOCUSED) - bufferevent_write(wp->event, "\033[I", 3); - else - bufferevent_write(wp->event, "\033[O", 3); break; case 1005: screen_write_mode_set(sctx, MODE_MOUSE_UTF8); diff --git a/notify.c b/notify.c index 03730b98..138fbeb1 100644 --- a/notify.c +++ b/notify.c @@ -153,6 +153,8 @@ notify_callback(struct cmdq_item *item, void *data) control_notify_session_window_changed(ne->session); if (strcmp(ne->name, "paste-buffer-changed") == 0) control_notify_paste_buffer_changed(ne->pbname); + if (strcmp(ne->name, "paste-buffer-deleted") == 0) + control_notify_paste_buffer_deleted(ne->pbname); notify_insert_hook(item, ne); @@ -307,10 +309,16 @@ notify_pane(const char *name, struct window_pane *wp) } void -notify_paste_buffer(const char *pbname) +notify_paste_buffer(const char *pbname, int deleted) { struct cmd_find_state fs; cmd_find_clear_state(&fs, 0); - notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL, NULL, pbname); + if (deleted) { + notify_add("paste-buffer-deleted", &fs, NULL, NULL, NULL, NULL, + pbname); + } else { + notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL, NULL, + pbname); + } } diff --git a/paste.c b/paste.c index 53477387..7565c207 100644 --- a/paste.c +++ b/paste.c @@ -151,7 +151,7 @@ paste_get_name(const char *name) void paste_free(struct paste_buffer *pb) { - notify_paste_buffer(pb->name); + notify_paste_buffer(pb->name, 1); RB_REMOVE(paste_name_tree, &paste_by_name, pb); RB_REMOVE(paste_time_tree, &paste_by_time, pb); @@ -210,7 +210,7 @@ paste_add(const char *prefix, char *data, size_t size) RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); - notify_paste_buffer(pb->name); + notify_paste_buffer(pb->name, 0); } /* Rename a paste buffer. */ @@ -258,8 +258,8 @@ paste_rename(const char *oldname, const char *newname, char **cause) RB_INSERT(paste_name_tree, &paste_by_name, pb); - notify_paste_buffer(oldname); - notify_paste_buffer(newname); + notify_paste_buffer(oldname, 1); + notify_paste_buffer(newname, 0); return (0); } @@ -309,7 +309,7 @@ paste_set(char *data, size_t size, const char *name, char **cause) RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); - notify_paste_buffer(name); + notify_paste_buffer(name, 0); return (0); } @@ -322,7 +322,7 @@ paste_replace(struct paste_buffer *pb, char *data, size_t size) pb->data = data; pb->size = size; - notify_paste_buffer(pb->name); + notify_paste_buffer(pb->name, 0); } /* Convert start of buffer into a nice string. */ diff --git a/tmux.1 b/tmux.1 index 5e896902..dbfc85b1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6612,6 +6612,10 @@ has changed mode. Paste buffer .Ar name has been changed. +.It Ic %paste-buffer-deleted Ar name +Paste buffer +.Ar name +has been deleted. .It Ic %pause Ar pane-id The pane has been paused (if the .Ar pause-after diff --git a/tmux.h b/tmux.h index 77019edc..a0f9f461 100644 --- a/tmux.h +++ b/tmux.h @@ -2163,7 +2163,7 @@ void notify_winlink(const char *, struct winlink *); void notify_session_window(const char *, struct session *, struct window *); void notify_window(const char *, struct window *); void notify_pane(const char *, struct window_pane *); -void notify_paste_buffer(const char *); +void notify_paste_buffer(const char *, int); /* options.c */ struct options *options_create(struct options *); @@ -3185,6 +3185,7 @@ void control_notify_session_created(struct session *); void control_notify_session_closed(struct session *); void control_notify_session_window_changed(struct session *); void control_notify_paste_buffer_changed(const char *); +void control_notify_paste_buffer_deleted(const char *); /* session.c */ extern struct sessions sessions; -- cgit From 22910451162c5c1ffe5505ca7c9b1b8cc99ba187 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Nov 2022 09:46:14 +0000 Subject: Use active pane in target window not current window for +/-. GitHub issue 3370. --- cmd-find.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 9f04c4a8..2929bbc9 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -588,22 +588,22 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) return (-1); return (0); } else if (strcmp(pane, "{up-of}") == 0) { - fs->wp = window_pane_find_up(fs->current->wp); + fs->wp = window_pane_find_up(fs->w->active); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{down-of}") == 0) { - fs->wp = window_pane_find_down(fs->current->wp); + fs->wp = window_pane_find_down(fs->w->active); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{left-of}") == 0) { - fs->wp = window_pane_find_left(fs->current->wp); + fs->wp = window_pane_find_left(fs->w->active); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{right-of}") == 0) { - fs->wp = window_pane_find_right(fs->current->wp); + fs->wp = window_pane_find_right(fs->w->active); if (fs->wp == NULL) return (-1); return (0); @@ -615,7 +615,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) n = strtonum(pane + 1, 1, INT_MAX, NULL); else n = 1; - wp = fs->current->wp; + wp = fs->w->active; if (pane[0] == '+') fs->wp = window_pane_next_by_number(fs->w, wp, n); else -- cgit From 2d08235987efa1e2f9c932c4a5c8a0200b4804c1 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Nov 2022 09:54:13 +0000 Subject: Add modified Tab key sequences, from Aaron Jensen, GitHub issue 3368. --- input-keys.c | 18 ++++++++++++++++++ key-string.c | 2 ++ tmux.h | 1 + tty-keys.c | 5 ++++- 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/input-keys.c b/input-keys.c index 038003df..5c4d3e1b 100644 --- a/input-keys.c +++ b/input-keys.c @@ -307,6 +307,20 @@ static struct input_key_entry input_key_defaults[] = { }, { .key = KEYC_DC|KEYC_BUILD_MODIFIERS, .data = "\033[3;_~" + }, + + /* Tab and modifiers. */ + { .key = '\011'|KEYC_CTRL, + .data = "\011" + }, + { .key = '\011'|KEYC_CTRL|KEYC_EXTENDED, + .data = "\033[9;5u" + }, + { .key = '\011'|KEYC_CTRL|KEYC_SHIFT, + .data = "\011" + }, + { .key = '\011'|KEYC_CTRL|KEYC_SHIFT|KEYC_EXTENDED, + .data = "\033[1;5Z" } }; static const key_code input_key_modifiers[] = { @@ -469,6 +483,8 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) key &= ~KEYC_KEYPAD; if (~s->mode & MODE_KCURSOR) key &= ~KEYC_CURSOR; + if (~s->mode & MODE_KEXTENDED) + key &= ~KEYC_EXTENDED; ike = input_key_get(key); if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META)) ike = input_key_get(key & ~KEYC_META); @@ -476,6 +492,8 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) ike = input_key_get(key & ~KEYC_CURSOR); if (ike == NULL && (key & KEYC_KEYPAD)) ike = input_key_get(key & ~KEYC_KEYPAD); + if (ike == NULL && (key & KEYC_EXTENDED)) + ike = input_key_get(key & ~KEYC_EXTENDED); if (ike != NULL) { log_debug("found key 0x%llx: \"%s\"", key, ike->data); if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META)) diff --git a/key-string.c b/key-string.c index 0ca91306..086c3ac4 100644 --- a/key-string.c +++ b/key-string.c @@ -460,6 +460,8 @@ out: strlcat(out, "I", sizeof out); if (saved & KEYC_BUILD_MODIFIERS) strlcat(out, "B", sizeof out); + if (saved & KEYC_EXTENDED) + strlcat(out, "E", sizeof out); strlcat(out, "]", sizeof out); } return (out); diff --git a/tmux.h b/tmux.h index a0f9f461..6cb6cc96 100644 --- a/tmux.h +++ b/tmux.h @@ -137,6 +137,7 @@ struct winlink; #define KEYC_IMPLIED_META 0x08000000000000ULL #define KEYC_BUILD_MODIFIERS 0x10000000000000ULL #define KEYC_VI 0x20000000000000ULL +#define KEYC_EXTENDED 0x40000000000000ULL /* Masks for key bits. */ #define KEYC_MASK_MODIFIERS 0x00f00000000000ULL diff --git a/tty-keys.c b/tty-keys.c index bb9ec231..db82754f 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -208,6 +208,9 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { /* Paste keys. */ { "\033[200~", KEYC_PASTE_START }, { "\033[201~", KEYC_PASTE_END }, + + /* Extended keys. */ + { "\033[1;5Z", '\011'|KEYC_CTRL|KEYC_SHIFT|KEYC_EXTENDED }, }; /* Default xterm keys. */ @@ -974,7 +977,7 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, log_debug("%s: extended key %.*s is %llx (%s)", c->name, (int)*size, buf, nkey, key_string_lookup_key(nkey, 1)); } - *key = nkey; + *key = nkey|KEYC_EXTENDED; return (0); } -- cgit From 9614f5156079773b338a13e95ec932cc7c95b031 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Nov 2022 07:36:07 +0000 Subject: Instead of always setting the extended flag, set it only when searching. Allows send-keys to work. From Aaron Jensen. --- input-keys.c | 9 +++++---- tty-keys.c | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/input-keys.c b/input-keys.c index 5c4d3e1b..d48d7d09 100644 --- a/input-keys.c +++ b/input-keys.c @@ -431,7 +431,7 @@ input_key_write(const char *from, struct bufferevent *bev, const char *data, int input_key(struct screen *s, struct bufferevent *bev, key_code key) { - struct input_key_entry *ike; + struct input_key_entry *ike = NULL; key_code justkey, newkey, outkey, modifiers; struct utf8_data ud; char tmp[64], modifier; @@ -483,9 +483,10 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) key &= ~KEYC_KEYPAD; if (~s->mode & MODE_KCURSOR) key &= ~KEYC_CURSOR; - if (~s->mode & MODE_KEXTENDED) - key &= ~KEYC_EXTENDED; - ike = input_key_get(key); + if (s->mode & MODE_KEXTENDED) + ike = input_key_get(key|KEYC_EXTENDED); + if (ike == NULL) + ike = input_key_get(key); if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META)) ike = input_key_get(key & ~KEYC_META); if (ike == NULL && (key & KEYC_CURSOR)) diff --git a/tty-keys.c b/tty-keys.c index db82754f..cb8efd49 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -210,7 +210,7 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033[201~", KEYC_PASTE_END }, /* Extended keys. */ - { "\033[1;5Z", '\011'|KEYC_CTRL|KEYC_SHIFT|KEYC_EXTENDED }, + { "\033[1;5Z", '\011'|KEYC_CTRL|KEYC_SHIFT }, }; /* Default xterm keys. */ @@ -977,7 +977,7 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, log_debug("%s: extended key %.*s is %llx (%s)", c->name, (int)*size, buf, nkey, key_string_lookup_key(nkey, 1)); } - *key = nkey|KEYC_EXTENDED; + *key = nkey; return (0); } -- cgit From 3be369522b6de38ba08ee50a86a49a9a86af490b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Nov 2022 08:33:57 +0000 Subject: Add a -l flag to display-message to disable format expansion, from Aaron Jensen. GitHub issue 3372. --- cmd-display-message.c | 10 +++++++--- tmux.1 | 9 +++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/cmd-display-message.c b/cmd-display-message.c index 7828f694..f5e91020 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = { .name = "display-message", .alias = "display", - .args = { "ac:d:INpt:F:v", 0, 1, NULL }, - .usage = "[-aINpv] [-c target-client] [-d delay] [-F format] " + .args = { "ac:d:lINpt:F:v", 0, 1, NULL }, + .usage = "[-aIlNpv] [-c target-client] [-d delay] [-F format] " CMD_TARGET_PANE_USAGE " [message]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -132,7 +132,11 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - msg = format_expand_time(ft, template); + if (args_has(args, 'l')) + msg = xstrdup(template); + else + msg = format_expand_time(ft, template); + if (cmdq_get_client(item) == NULL) cmdq_error(item, "%s", msg); else if (args_has(args, 'p')) diff --git a/tmux.1 b/tmux.1 index dbfc85b1..36faf683 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5887,7 +5887,7 @@ The following keys are also available: .El .Tg display .It Xo Ic display-message -.Op Fl aINpv +.Op Fl aIlNpv .Op Fl c Ar target-client .Op Fl d Ar delay .Op Fl t Ar target-pane @@ -5909,7 +5909,12 @@ is not given, the option is used; a delay of zero waits for a key press. .Ql N ignores key presses and closes only after the delay expires. -The format of +If +.Fl l +is given, +.Ar message +is printed unchanged. +Otherwise, the format of .Ar message is described in the .Sx FORMATS -- cgit From 17290b912116c4397620526d43dcf6ddcf0044b7 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Nov 2022 08:41:53 +0000 Subject: If there are no buffers, reset mode as soon as any key pressed. Fixes crash reported by Gaoyang Zhang in GitHub issue 3373. --- window-buffer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/window-buffer.c b/window-buffer.c index 544a1155..aac0bc40 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -508,6 +508,11 @@ window_buffer_key(struct window_mode_entry *wme, struct client *c, struct window_buffer_itemdata *item; int finished; + if (paste_get_top(NULL) == NULL) { + finished = 1; + goto out; + } + finished = mode_tree_key(mtd, c, &key, m, NULL, NULL); switch (key) { case 'e': @@ -534,6 +539,8 @@ window_buffer_key(struct window_mode_entry *wme, struct client *c, finished = 1; break; } + +out: if (finished || paste_get_top(NULL) == NULL) window_pane_reset_mode(wp); else { -- cgit From 77c135349aaaa026c3fbb24d291877ce926d682e Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 4 Nov 2022 08:03:23 +0000 Subject: Unescape the string for the literal operator (l:) so special characters work. --- format.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index 535af061..5b08a7a4 100644 --- a/format.c +++ b/format.c @@ -3575,7 +3575,32 @@ found: return (found); } -/* Remove escaped characters from string. */ +/* Unescape escaped characters. */ +static char * +format_unescape(const char *s) +{ + char *out, *cp; + int brackets = 0; + + cp = out = xmalloc(strlen(s) + 1); + for (; *s != '\0'; s++) { + if (*s == '#' && s[1] == '{') + brackets++; + if (brackets == 0 && + *s == '#' && + strchr(",#{}:", s[1]) != NULL) { + *cp++ = *++s; + continue; + } + if (*s == '}') + brackets--; + *cp++ = *s; + } + *cp = '\0'; + return (out); +} + +/* Remove escaped characters. */ static char * format_strip(const char *s) { @@ -4338,7 +4363,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, /* Is this a literal string? */ if (modifiers & FORMAT_LITERAL) { - value = xstrdup(copy); + format_log(es, "literal string is '%s'", copy); + value = format_unescape(copy); goto done; } -- cgit From f86eba21294b61f410300b0c9a8920cb155ab738 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Nov 2022 10:04:31 +0000 Subject: Fix C-S-Tab without extended keys, from Aaron Jensen. --- input-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input-keys.c b/input-keys.c index d48d7d09..93123b33 100644 --- a/input-keys.c +++ b/input-keys.c @@ -317,7 +317,7 @@ static struct input_key_entry input_key_defaults[] = { .data = "\033[9;5u" }, { .key = '\011'|KEYC_CTRL|KEYC_SHIFT, - .data = "\011" + .data = "\033[Z" }, { .key = '\011'|KEYC_CTRL|KEYC_SHIFT|KEYC_EXTENDED, .data = "\033[1;5Z" -- cgit From 48f41e4a411dc8dc77514e211293365ee8394792 Mon Sep 17 00:00:00 2001 From: jmc Date: Thu, 10 Nov 2022 22:58:39 +0000 Subject: - sort options; from josiah frentsos ok nicm - add -N to SYNOPSIS - sort usage() --- tmux.1 | 22 +++++++++++----------- tmux.c | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tmux.1 b/tmux.1 index 36faf683..887608cb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm tmux .Bk -words -.Op Fl 2CDluvV +.Op Fl 2CDlNuVv .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name @@ -203,6 +203,12 @@ If is specified, the default socket directory is not used and any .Fl L flag is ignored. +.It Fl T Ar features +Set terminal features for the client. +This is a comma-separated list of features. +See the +.Ic terminal-features +option. .It Fl u Write UTF-8 output to the terminal even if the first environment variable of @@ -214,12 +220,10 @@ that is set does not contain .Qq UTF-8 or .Qq UTF8 . -.It Fl T Ar features -Set terminal features for the client. -This is a comma-separated list of features. -See the -.Ic terminal-features -option. +.It Fl V +Report the +.Nm +version. .It Fl v Request verbose logging. Log messages will be saved into @@ -244,10 +248,6 @@ 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. .It Ar command Op Ar flags This specifies one of a set of commands used to control .Nm , diff --git a/tmux.c b/tmux.c index ceaa99da..0d752221 100644 --- a/tmux.c +++ b/tmux.c @@ -57,7 +57,7 @@ static __dead void usage(void) { fprintf(stderr, - "usage: %s [-2CDlNuvV] [-c shell-command] [-f file] [-L socket-name]\n" + "usage: %s [-2CDlNuVv] [-c shell-command] [-f file] [-L socket-name]\n" " [-S socket-path] [-T features] [command [flags]]\n", getprogname()); exit(1); -- cgit From 079f48e8a6131b2ef46b370c0905252d29d9d815 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Nov 2022 08:27:17 +0000 Subject: Document alternative delimiters for substitution, from Jim Wisniewski. --- tmux.1 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tmux.1 b/tmux.1 index 887608cb..52a20266 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5080,6 +5080,15 @@ would change .Ql abABab into .Ql bxBxbx . +A different delimiter character may also be used, to avoid collisions with +literal slashes in the pattern. +For example, +.Ql s|foo/|bar/|: +will substitute +.Ql foo/ +with +.Ql bar/ +throughout. .Pp In addition, the last line of a shell command's output may be inserted using .Ql #() . -- cgit From fe475bd856ff1f98bf8a4cd9b6aedd5da81a7e3c Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Nov 2022 08:37:55 +0000 Subject: Parse primary device attributes as well as secondary and add a SIXEL flag (not used yet), from Anindya Mukherjee. --- input.c | 4 +-- tmux.1 | 4 +++ tmux.h | 5 ++- tty-features.c | 12 +++++++ tty-keys.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++-------- tty-term.c | 1 + tty.c | 10 +++--- 7 files changed, 124 insertions(+), 22 deletions(-) diff --git a/input.c b/input.c index 779b9013..f162b92f 100644 --- a/input.c +++ b/input.c @@ -1345,8 +1345,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); diff --git a/tmux.1 b/tmux.1 index 52a20266..4f314110 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3687,6 +3687,8 @@ Supports the overline SGR attribute. Supports the DECFRA rectangle fill escape sequence. .It RGB Supports RGB colour with the SGR escape sequences. +.It sixel +Supports SIXEL graphics. .It strikethrough Supports the strikethrough SGR escape sequence. .It sync @@ -6491,6 +6493,8 @@ Set the opening sequence for the working directory notification. The sequence is terminated using the standard .Em fsl capability. +.It Em \&Sxl +Indicates that the terminal supports SIXEL. .It Em \&Sync Start (parameter is 1) or end (parameter is 2) a synchronized update. .It Em \&Tc diff --git a/tmux.h b/tmux.h index 6cb6cc96..8c5e071f 100644 --- a/tmux.h +++ b/tmux.h @@ -543,6 +543,7 @@ enum tty_code_code { TTYC_SMUL, TTYC_SMULX, TTYC_SMXX, + TTYC_SXL, TTYC_SS, TTYC_SWD, TTYC_SYNC, @@ -1349,6 +1350,7 @@ struct tty_term { #define TERM_DECFRA 0x8 #define TERM_RGBCOLOURS 0x10 #define TERM_VT100LIKE 0x20 +#define TERM_SIXEL 0x40 int flags; LIST_ENTRY(tty_term) entry; @@ -1405,9 +1407,10 @@ struct tty { #define TTY_OPENED 0x20 #define TTY_OSC52QUERY 0x40 #define TTY_BLOCK 0x80 -#define TTY_HAVEDA 0x100 +#define TTY_HAVEDA 0x100 /* Primary DA. */ #define TTY_HAVEXDA 0x200 #define TTY_SYNCING 0x400 +#define TTY_HAVEDA2 0x800 /* Seconday DA. */ int flags; struct tty_term *term; diff --git a/tty-features.c b/tty-features.c index 261cb2b8..b1268827 100644 --- a/tty-features.c +++ b/tty-features.c @@ -335,6 +335,17 @@ static const struct tty_feature tty_feature_ignorefkeys = { 0 }; +/* Terminal has sixel capability. */ +static const char *const tty_feature_sixel_capabilities[] = { + "Sxl", + NULL +}; +static const struct tty_feature tty_feature_sixel = { + "sixel", + tty_feature_sixel_capabilities, + 0 +}; + /* Available terminal features. */ static const struct tty_feature *const tty_features[] = { &tty_feature_256, @@ -352,6 +363,7 @@ static const struct tty_feature *const tty_features[] = { &tty_feature_overline, &tty_feature_rectfill, &tty_feature_rgb, + &tty_feature_sixel, &tty_feature_strikethrough, &tty_feature_sync, &tty_feature_title, diff --git a/tty-keys.c b/tty-keys.c index cb8efd49..65294ba8 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -55,6 +55,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_attributes2(struct tty *, const char *, size_t, + size_t *); static int tty_keys_extended_device_attributes(struct tty *, const char *, size_t, size_t *); @@ -684,7 +686,7 @@ tty_keys_next(struct tty *tty) goto partial_key; } - /* Is this a device attributes response? */ + /* Is this a primary device attributes response? */ switch (tty_keys_device_attributes(tty, buf, len, &size)) { case 0: /* yes */ key = KEYC_UNKNOWN; @@ -695,6 +697,17 @@ tty_keys_next(struct tty *tty) goto partial_key; } + /* Is this a secondary device attributes response? */ + switch (tty_keys_device_attributes2(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 an extended device attributes response? */ switch (tty_keys_extended_device_attributes(tty, buf, len, &size)) { case 0: /* yes */ @@ -1235,7 +1248,7 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size) } /* - * Handle secondary device attributes input. Returns 0 for success, -1 for + * Handle primary device attributes input. Returns 0 for success, -1 for * failure, 1 for partial. */ static int @@ -1244,16 +1257,13 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, { struct client *c = tty->client; u_int i, n = 0; - char tmp[64], *endptr, p[32] = { 0 }, *cp, *next; + char tmp[128], *endptr, p[32] = { 0 }, *cp, *next; *size = 0; if (tty->flags & TTY_HAVEDA) return (-1); - /* - * First three bytes are always \033[>. Some older Terminal.app - * versions respond as for DA (\033[?) so accept and ignore that. - */ + /* First three bytes are always \033[?. */ if (buf[0] != '\033') return (-1); if (len == 1) @@ -1262,27 +1272,96 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, return (-1); if (len == 2) return (1); - if (buf[2] != '>' && buf[2] != '?') + if (buf[2] != '?') return (-1); if (len == 3) return (1); /* Copy the rest up to a 'c'. */ - for (i = 0; i < (sizeof tmp) - 1; i++) { + for (i = 0; i < (sizeof tmp); i++) { if (3 + i == len) return (1); if (buf[3 + i] == 'c') break; tmp[i] = buf[3 + i]; } - if (i == (sizeof tmp) - 1) + if (i == (sizeof tmp)) return (-1); tmp[i] = '\0'; *size = 4 + i; - /* Ignore DA response. */ - if (buf[2] == '?') - return (0); + /* Convert all arguments to numbers. */ + cp = tmp; + while ((next = strsep(&cp, ";")) != NULL) { + p[n] = strtoul(next, &endptr, 10); + if (*endptr != '\0') + p[n] = 0; + if (++n == nitems(p)) + break; + } + + /* Add terminal features. */ + switch (p[0]) { + case 62: /* VT220 */ + case 63: /* VT320 */ + case 64: /* VT420 */ + for (i = 1; i < n; i++) { + log_debug("%s: DA feature: %d", c->name, p[i]); + if (p[i] == 4) + tty->term->flags |= TERM_SIXEL; + } + break; + } + log_debug("%s: received primary DA %.*s", c->name, (int)*size, buf); + + tty_update_features(tty); + tty->flags |= TTY_HAVEDA; + + return (0); +} + +/* + * Handle secondary device attributes input. Returns 0 for success, -1 for + * failure, 1 for partial. + */ +static int +tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len, + size_t *size) +{ + struct client *c = tty->client; + u_int i, n = 0; + char tmp[128], *endptr, p[32] = { 0 }, *cp, *next; + + *size = 0; + if (tty->flags & TTY_HAVEDA2) + return (-1); + + /* 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); + if (buf[2] != '>') + return (-1); + if (len == 3) + return (1); + + /* Copy the rest up to a 'c'. */ + for (i = 0; i < (sizeof tmp); i++) { + if (3 + i == len) + return (1); + if (buf[3 + i] == 'c') + break; + tmp[i] = buf[3 + i]; + } + if (i == (sizeof tmp)) + return (-1); + tmp[i] = '\0'; + *size = 4 + i; /* Convert all arguments to numbers. */ cp = tmp; @@ -1290,7 +1369,8 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, p[n] = strtoul(next, &endptr, 10); if (*endptr != '\0') p[n] = 0; - n++; + if (++n == nitems(p)) + break; } /* Add terminal features. */ @@ -1311,7 +1391,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf); tty_update_features(tty); - tty->flags |= TTY_HAVEDA; + tty->flags |= TTY_HAVEDA2; return (0); } diff --git a/tty-term.c b/tty-term.c index e3167f19..23abccf5 100644 --- a/tty-term.c +++ b/tty-term.c @@ -265,6 +265,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 1394075d..43a2961f 100644 --- a/tty.c +++ b/tty.c @@ -299,9 +299,9 @@ tty_start_timer_callback(__unused int fd, __unused short events, void *data) struct client *c = tty->client; log_debug("%s: start timer fired", c->name); - if ((tty->flags & (TTY_HAVEDA|TTY_HAVEXDA)) == 0) + if ((tty->flags & (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)) == 0) tty_update_features(tty); - tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA); + tty->flags |= (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA); } void @@ -363,12 +363,14 @@ tty_send_requests(struct tty *tty) return; if (tty->term->flags & TERM_VT100LIKE) { - if (~tty->flags & TTY_HAVEDA) + if (~tty->term->flags & TTY_HAVEDA) + tty_puts(tty, "\033[c"); + if (~tty->flags & TTY_HAVEDA2) tty_puts(tty, "\033[>c"); if (~tty->flags & TTY_HAVEXDA) tty_puts(tty, "\033[>q"); } else - tty->flags |= (TTY_HAVEDA|TTY_HAVEXDA); + tty->flags |= (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA); } void -- cgit From 20da16737715a183a019f1072735614615b5fd1c Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Nov 2022 08:44:11 +0000 Subject: Tweak previous to set and log the feature instead of just setting the flag. --- tty-features.c | 2 +- tty-keys.c | 29 ++++++++++++++++------------- tty-term.c | 3 +++ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/tty-features.c b/tty-features.c index b1268827..3f4fdbfd 100644 --- a/tty-features.c +++ b/tty-features.c @@ -343,7 +343,7 @@ static const char *const tty_feature_sixel_capabilities[] = { static const struct tty_feature tty_feature_sixel = { "sixel", tty_feature_sixel_capabilities, - 0 + TERM_SIXEL }; /* Available terminal features. */ diff --git a/tty-keys.c b/tty-keys.c index 65294ba8..6fe121f0 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1256,6 +1256,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, size_t *size) { struct client *c = tty->client; + int *features = &c->term_features; u_int i, n = 0; char tmp[128], *endptr, p[32] = { 0 }, *cp, *next; @@ -1305,11 +1306,11 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, case 62: /* VT220 */ case 63: /* VT320 */ case 64: /* VT420 */ - for (i = 1; i < n; i++) { - log_debug("%s: DA feature: %d", c->name, p[i]); - if (p[i] == 4) - tty->term->flags |= TERM_SIXEL; - } + for (i = 1; i < n; i++) { + log_debug("%s: DA feature: %d", c->name, p[i]); + if (p[i] == 4) + tty_add_features(features, "sixel", ","); + } break; } log_debug("%s: received primary DA %.*s", c->name, (int)*size, buf); @@ -1329,6 +1330,7 @@ tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len, size_t *size) { struct client *c = tty->client; + int *features = &c->term_features; u_int i, n = 0; char tmp[128], *endptr, p[32] = { 0 }, *cp, *next; @@ -1376,16 +1378,16 @@ tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len, /* Add terminal features. */ switch (p[0]) { case 41: /* VT420 */ - tty_add_features(&c->term_features, "margins,rectfill", ","); + tty_add_features(features, "margins,rectfill", ","); break; case 'M': /* mintty */ - tty_default_features(&c->term_features, "mintty", 0); + tty_default_features(features, "mintty", 0); break; case 'T': /* tmux */ - tty_default_features(&c->term_features, "tmux", 0); + tty_default_features(features, "tmux", 0); break; case 'U': /* rxvt-unicode */ - tty_default_features(&c->term_features, "rxvt-unicode", 0); + tty_default_features(features, "rxvt-unicode", 0); break; } log_debug("%s: received secondary DA %.*s", c->name, (int)*size, buf); @@ -1405,6 +1407,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, size_t len, size_t *size) { struct client *c = tty->client; + int *features = &c->term_features; u_int i; char tmp[128]; @@ -1445,13 +1448,13 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, /* Add terminal features. */ if (strncmp(tmp, "iTerm2 ", 7) == 0) - tty_default_features(&c->term_features, "iTerm2", 0); + tty_default_features(features, "iTerm2", 0); else if (strncmp(tmp, "tmux ", 5) == 0) - tty_default_features(&c->term_features, "tmux", 0); + tty_default_features(features, "tmux", 0); else if (strncmp(tmp, "XTerm(", 6) == 0) - tty_default_features(&c->term_features, "XTerm", 0); + tty_default_features(features, "XTerm", 0); else if (strncmp(tmp, "mintty ", 7) == 0) - tty_default_features(&c->term_features, "mintty", 0); + tty_default_features(features, "mintty", 0); log_debug("%s: received extended DA %.*s", c->name, (int)*size, buf); free(c->term_type); diff --git a/tty-term.c b/tty-term.c index 23abccf5..7998f657 100644 --- a/tty-term.c +++ b/tty-term.c @@ -454,6 +454,9 @@ tty_term_apply_overrides(struct tty_term *term) a = options_array_next(a); } + /* Log the SIXEL flag. */ + log_debug("SIXEL flag is %d", !!(term->flags & TERM_SIXEL)); + /* Update the RGB flag if the terminal has RGB colours. */ if (tty_term_has(term, TTYC_SETRGBF) && tty_term_has(term, TTYC_SETRGBB)) -- cgit From e46d0632a5ed8e9fbc90ae49039e84450fba4925 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 11 Nov 2022 08:47:55 +0000 Subject: Add key regression tests from Aaron Jensen. --- regress/input-keys.sh | 299 +++++++++++++++++++++++++++++++++++++++++ regress/tty-keys.sh | 361 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 660 insertions(+) create mode 100644 regress/input-keys.sh create mode 100644 regress/tty-keys.sh diff --git a/regress/input-keys.sh b/regress/input-keys.sh new file mode 100644 index 00000000..262d12a6 --- /dev/null +++ b/regress/input-keys.sh @@ -0,0 +1,299 @@ +#!/bin/sh + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -Ltest" +$TMUX kill-server 2>/dev/null + +$TMUX -f/dev/null new -x20 -y2 -d || exit 1 + +sleep 0.1 + +exit_status=0 + +assert_key () { + key=$1 + expected_code=$2 + + $TMUX new-window -- sh -c 'stty raw -echo && cat -tv' + $TMUX send-keys "$key" $ + + actual_code=$($TMUX capturep -p | head -1 | sed -e 's/\$$//') + $TMUX kill-window + + if [ "$actual_code" = "$expected_code" ]; then + if [ -n "$VERBOSE" ]; then + echo "[PASS] $key -> $actual_code" + fi + else + echo "[FAIL] $key -> $expected_code (Got: $actual_code)" + exit_status=1 + fi + + shift + shift + + if [ "$1" = "--" ]; then + shift + assert_key "$@" + fi +} + +assert_key 'C-Space' '^@' +assert_key 'C-a' '^A' -- 'M-C-a' '^[^A' +assert_key 'C-b' '^B' -- 'M-C-b' '^[^B' +assert_key 'C-c' '^C' -- 'M-C-c' '^[^C' +assert_key 'C-d' '^D' -- 'M-C-d' '^[^D' +assert_key 'C-e' '^E' -- 'M-C-e' '^[^E' +assert_key 'C-f' '^F' -- 'M-C-f' '^[^F' +assert_key 'C-g' '^G' -- 'M-C-g' '^[^G' +assert_key 'C-h' '^H' -- 'M-C-h' '^[^H' +assert_key 'C-i' '^I' -- 'M-C-i' '^[^I' +assert_key 'C-j' '' -- 'M-C-j' '^[' # NL +assert_key 'C-k' '^K' -- 'M-C-k' '^[^K' +assert_key 'C-l' '^L' -- 'M-C-l' '^[^L' +assert_key 'C-m' '^M' -- 'M-C-m' '^[^M' +assert_key 'C-n' '^N' -- 'M-C-n' '^[^N' +assert_key 'C-o' '^O' -- 'M-C-o' '^[^O' +assert_key 'C-p' '^P' -- 'M-C-p' '^[^P' +assert_key 'C-q' '^Q' -- 'M-C-q' '^[^Q' +assert_key 'C-r' '^R' -- 'M-C-r' '^[^R' +assert_key 'C-s' '^S' -- 'M-C-s' '^[^S' +assert_key 'C-t' '^T' -- 'M-C-t' '^[^T' +assert_key 'C-u' '^U' -- 'M-C-u' '^[^U' +assert_key 'C-v' '^V' -- 'M-C-v' '^[^V' +assert_key 'C-w' '^W' -- 'M-C-w' '^[^W' +assert_key 'C-x' '^X' -- 'M-C-x' '^[^X' +assert_key 'C-y' '^Y' -- 'M-C-y' '^[^Y' +assert_key 'C-z' '^Z' -- 'M-C-z' '^[^Z' +assert_key 'Escape' '^[' -- 'M-Escape' '^[^[' +assert_key "C-\\" "^\\" -- "M-C-\\" "^[^\\" +assert_key 'C-]' '^]' -- 'M-C-]' '^[^]' +assert_key 'C-^' '^^' -- 'M-C-^' '^[^^' +assert_key 'C-_' '^_' -- 'M-C-_' '^[^_' +assert_key 'Space' ' ' -- 'M-Space' '^[ ' +assert_key '!' '!' -- 'M-!' '^[!' +assert_key '"' '"' -- 'M-"' '^["' +assert_key '#' '#' -- 'M-#' '^[#' +assert_key '$' '$' -- 'M-$' '^[$' +assert_key '%' '%' -- 'M-%' '^[%' +assert_key '&' '&' -- 'M-&' '^[&' +assert_key "'" "'" -- "M-'" "^['" +assert_key '(' '(' -- 'M-(' '^[(' +assert_key ')' ')' -- 'M-)' '^[)' +assert_key '*' '*' -- 'M-*' '^[*' +assert_key '+' '+' -- 'M-+' '^[+' +assert_key ',' ',' -- 'M-,' '^[,' +assert_key '-' '-' -- 'M--' '^[-' +assert_key '.' '.' -- 'M-.' '^[.' +assert_key '/' '/' -- 'M-/' '^[/' +assert_key '0' '0' -- 'M-0' '^[0' +assert_key '1' '1' -- 'M-1' '^[1' +assert_key '2' '2' -- 'M-2' '^[2' +assert_key '3' '3' -- 'M-3' '^[3' +assert_key '4' '4' -- 'M-4' '^[4' +assert_key '5' '5' -- 'M-5' '^[5' +assert_key '6' '6' -- 'M-6' '^[6' +assert_key '7' '7' -- 'M-7' '^[7' +assert_key '8' '8' -- 'M-8' '^[8' +assert_key '9' '9' -- 'M-9' '^[9' +assert_key ':' ':' -- 'M-:' '^[:' +assert_key '\;' ';' -- 'M-\;' '^[;' +assert_key '<' '<' -- 'M-<' '^[<' +assert_key '=' '=' -- 'M-=' '^[=' +assert_key '>' '>' -- 'M->' '^[>' +assert_key '?' '?' -- 'M-?' '^[?' +assert_key '@' '@' -- 'M-@' '^[@' +assert_key 'A' 'A' -- 'M-A' '^[A' +assert_key 'B' 'B' -- 'M-B' '^[B' +assert_key 'C' 'C' -- 'M-C' '^[C' +assert_key 'D' 'D' -- 'M-D' '^[D' +assert_key 'E' 'E' -- 'M-E' '^[E' +assert_key 'F' 'F' -- 'M-F' '^[F' +assert_key 'G' 'G' -- 'M-G' '^[G' +assert_key 'H' 'H' -- 'M-H' '^[H' +assert_key 'I' 'I' -- 'M-I' '^[I' +assert_key 'J' 'J' -- 'M-J' '^[J' +assert_key 'K' 'K' -- 'M-K' '^[K' +assert_key 'L' 'L' -- 'M-L' '^[L' +assert_key 'M' 'M' -- 'M-M' '^[M' +assert_key 'N' 'N' -- 'M-N' '^[N' +assert_key 'O' 'O' -- 'M-O' '^[O' +assert_key 'P' 'P' -- 'M-P' '^[P' +assert_key 'Q' 'Q' -- 'M-Q' '^[Q' +assert_key 'R' 'R' -- 'M-R' '^[R' +assert_key 'S' 'S' -- 'M-S' '^[S' +assert_key 'T' 'T' -- 'M-T' '^[T' +assert_key 'U' 'U' -- 'M-U' '^[U' +assert_key 'V' 'V' -- 'M-V' '^[V' +assert_key 'W' 'W' -- 'M-W' '^[W' +assert_key 'X' 'X' -- 'M-X' '^[X' +assert_key 'Y' 'Y' -- 'M-Y' '^[Y' +assert_key 'Z' 'Z' -- 'M-Z' '^[Z' +assert_key '[' '[' -- 'M-[' '^[[' +assert_key "\\" "\\" -- "M-\\" "^[\\" +assert_key ']' ']' -- 'M-]' '^[]' +assert_key '^' '^' -- 'M-^' '^[^' +assert_key '_' '_' -- 'M-_' '^[_' +assert_key '`' '`' -- 'M-`' '^[`' +assert_key 'a' 'a' -- 'M-a' '^[a' +assert_key 'b' 'b' -- 'M-b' '^[b' +assert_key 'c' 'c' -- 'M-c' '^[c' +assert_key 'd' 'd' -- 'M-d' '^[d' +assert_key 'e' 'e' -- 'M-e' '^[e' +assert_key 'f' 'f' -- 'M-f' '^[f' +assert_key 'g' 'g' -- 'M-g' '^[g' +assert_key 'h' 'h' -- 'M-h' '^[h' +assert_key 'i' 'i' -- 'M-i' '^[i' +assert_key 'j' 'j' -- 'M-j' '^[j' +assert_key 'k' 'k' -- 'M-k' '^[k' +assert_key 'l' 'l' -- 'M-l' '^[l' +assert_key 'm' 'm' -- 'M-m' '^[m' +assert_key 'n' 'n' -- 'M-n' '^[n' +assert_key 'o' 'o' -- 'M-o' '^[o' +assert_key 'p' 'p' -- 'M-p' '^[p' +assert_key 'q' 'q' -- 'M-q' '^[q' +assert_key 'r' 'r' -- 'M-r' '^[r' +assert_key 's' 's' -- 'M-s' '^[s' +assert_key 't' 't' -- 'M-t' '^[t' +assert_key 'u' 'u' -- 'M-u' '^[u' +assert_key 'v' 'v' -- 'M-v' '^[v' +assert_key 'w' 'w' -- 'M-w' '^[w' +assert_key 'x' 'x' -- 'M-x' '^[x' +assert_key 'y' 'y' -- 'M-y' '^[y' +assert_key 'z' 'z' -- 'M-z' '^[z' +assert_key '{' '{' -- 'M-{' '^[{' +assert_key '|' '|' -- 'M-|' '^[|' +assert_key '}' '}' -- 'M-}' '^[}' +assert_key '~' '~' -- 'M-~' '^[~' + +assert_key 'Tab' '^I' -- 'M-Tab' '^[^I' +assert_key 'BSpace' '^?' -- 'M-BSpace' '^[^?' + +## These cannot be sent, is that intentional? +## assert_key 'PasteStart' "^[[200~" +## assert_key 'PasteEnd' "^[[201~" + +assert_key 'F1' "^[OP" +assert_key 'F2' "^[OQ" +assert_key 'F3' "^[OR" +assert_key 'F4' "^[OS" +assert_key 'F5' "^[[15~" +assert_key 'F6' "^[[17~" +assert_key 'F8' "^[[19~" +assert_key 'F9' "^[[20~" +assert_key 'F10' "^[[21~" +assert_key 'F11' "^[[23~" +assert_key 'F12' "^[[24~" + +assert_key 'IC' '^[[2~' +assert_key 'Insert' '^[[2~' +assert_key 'DC' '^[[3~' +assert_key 'Delete' '^[[3~' + +## Why do these differ from tty-keys? +assert_key 'Home' '^[[1~' +assert_key 'End' '^[[4~' + +assert_key 'NPage' '^[[6~' +assert_key 'PageDown' '^[[6~' +assert_key 'PgDn' '^[[6~' +assert_key 'PPage' '^[[5~' +assert_key 'PageUp' '^[[5~' +assert_key 'PgUp' '^[[5~' + +assert_key 'BTab' '^[[Z' +assert_key 'C-S-Tab' '^[[Z' + +assert_key 'Up' '^[[A' +assert_key 'Down' '^[[B' +assert_key 'Right' '^[[C' +assert_key 'Left' '^[[D' + +# assert_key 'KPEnter' +assert_key 'KP*' '*' -- 'M-KP*' '^[*' +assert_key 'KP+' '+' -- 'M-KP+' '^[+' +assert_key 'KP-' '-' -- 'M-KP-' '^[-' +assert_key 'KP.' '.' -- 'M-KP.' '^[.' +assert_key 'KP/' '/' -- 'M-KP/' '^[/' +assert_key 'KP0' '0' -- 'M-KP0' '^[0' +assert_key 'KP1' '1' -- 'M-KP1' '^[1' +assert_key 'KP2' '2' -- 'M-KP2' '^[2' +assert_key 'KP3' '3' -- 'M-KP3' '^[3' +assert_key 'KP4' '4' -- 'M-KP4' '^[4' +assert_key 'KP5' '5' -- 'M-KP5' '^[5' +assert_key 'KP6' '6' -- 'M-KP6' '^[6' +assert_key 'KP7' '7' -- 'M-KP7' '^[7' +assert_key 'KP8' '8' -- 'M-KP8' '^[8' +assert_key 'KP9' '9' -- 'M-KP9' '^[9' + +# Extended keys +$TMUX set -g extended-keys always + +assert_extended_key () { + extended_key=$1 + expected_code_pattern=$2 + + expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;2/') + assert_key "S-$extended_key" "$expected_code" + + expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;3/') + assert_key "M-$extended_key" "$expected_code" + + expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;4/') + assert_key "S-M-$extended_key" "$expected_code" + + expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;5/') + assert_key "C-$extended_key" "$expected_code" + + expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;6/') + assert_key "S-C-$extended_key" "$expected_code" + + expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;7/') + assert_key "C-M-$extended_key" "$expected_code" + + expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;8/') + assert_key "S-C-M-$extended_key" "$expected_code" +} + +## Many of these pass without extended keys enabled -- are they extended keys? +assert_extended_key 'F1' '^[[1;_P' +assert_extended_key 'F2' "^[[1;_Q" +assert_extended_key 'F3' "^[[1;_R" +assert_extended_key 'F4' "^[[1;_S" +assert_extended_key 'F5' "^[[15;_~" +assert_extended_key 'F6' "^[[17;_~" +assert_extended_key 'F8' "^[[19;_~" +assert_extended_key 'F9' "^[[20;_~" +assert_extended_key 'F10' "^[[21;_~" +assert_extended_key 'F11' "^[[23;_~" +assert_extended_key 'F12' "^[[24;_~" + +assert_extended_key 'Up' '^[[1;_A' +assert_extended_key 'Down' '^[[1;_B' +assert_extended_key 'Right' '^[[1;_C' +assert_extended_key 'Left' '^[[1;_D' + +assert_extended_key 'Home' '^[[1;_H' +assert_extended_key 'End' '^[[1;_F' + +assert_extended_key 'PPage' '^[[5;_~' +assert_extended_key 'PageUp' '^[[5;_~' +assert_extended_key 'PgUp' '^[[5;_~' +assert_extended_key 'NPage' '^[[6;_~' +assert_extended_key 'PageDown' '^[[6;_~' +assert_extended_key 'PgDn' '^[[6;_~' + +assert_extended_key 'IC' '^[[2;_~' +assert_extended_key 'Insert' '^[[2;_~' +assert_extended_key 'DC' '^[[3;_~' +assert_extended_key 'Delete' '^[[3;_~' + +assert_key 'C-Tab' "^[[9;5u" +assert_key 'C-S-Tab' "^[[1;5Z" + +$TMUX kill-server 2>/dev/null + +exit $exit_status diff --git a/regress/tty-keys.sh b/regress/tty-keys.sh new file mode 100644 index 00000000..1fcc3657 --- /dev/null +++ b/regress/tty-keys.sh @@ -0,0 +1,361 @@ +#!/bin/sh + +PATH=/bin:/usr/bin +TERM=screen + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -Ltest" +$TMUX kill-server 2>/dev/null +TMUX2="$TEST_TMUX -Ltest2" +$TMUX2 kill-server 2>/dev/null + +TMP=$(mktemp) +trap "rm -f $TMP" 0 1 15 + +$TMUX2 -f/dev/null new -d || exit 1 +$TMUX -f/dev/null new -d "$TMUX2 attach" || exit 1 +sleep 0.1 + +exit_status=0 + +format_string () { + case $1 in + *\') + printf '"%%%%"' + ;; + *) + printf "'%%%%'" + ;; + esac +} + +assert_key () { + keys=$1 + expected_name=$2 + format_string=$(format_string "$expected_name") + + $TMUX2 command-prompt -k 'display-message -pl '"$format_string" > "$TMP" & + sleep 0.05 + + $TMUX send-keys $keys + + wait + + keys=$(printf '%s' "$keys" | sed -e 's/Escape/\\\\033/g' | tr -d '[:space:]') + actual_name=$(tr -d '[:space:]' < "$TMP") + + if [ "$actual_name" = "$expected_name" ]; then + if [ -n "$VERBOSE" ]; then + echo "[PASS] $keys -> $actual_name" + fi + else + echo "[FAIL] $keys -> $expected_name (Got: '$actual_name')" + exit_status=1 + fi + + if [ "$3" = "--" ]; then + shift; shift; shift + assert_key "$@" + fi + +} + +assert_key 0x00 'C-Space' # -- 'Escape 0x00' 'M-C-Space' +assert_key 0x01 'C-a' -- 'Escape 0x01' 'M-C-a' +assert_key 0x02 'C-b' -- 'Escape 0x02' 'M-C-b' +assert_key 0x03 'C-c' -- 'Escape 0x03' 'M-C-c' +assert_key 0x04 'C-d' -- 'Escape 0x04' 'M-C-d' +assert_key 0x05 'C-e' -- 'Escape 0x05' 'M-C-e' +assert_key 0x06 'C-f' -- 'Escape 0x06' 'M-C-f' +assert_key 0x07 'C-g' -- 'Escape 0x07' 'M-C-g' +assert_key 0x08 'C-h' -- 'Escape 0x08' 'M-C-h' +assert_key 0x09 'Tab' -- 'Escape 0x09' 'M-Tab' +assert_key 0x0A 'C-j' -- 'Escape 0x0A' 'M-C-j' +assert_key 0x0B 'C-k' -- 'Escape 0x0B' 'M-C-k' +assert_key 0x0C 'C-l' -- 'Escape 0x0C' 'M-C-l' +assert_key 0x0D 'Enter' -- 'Escape 0x0D' 'M-Enter' +assert_key 0x0E 'C-n' -- 'Escape 0x0E' 'M-C-n' +assert_key 0x0F 'C-o' -- 'Escape 0x0F' 'M-C-o' +assert_key 0x10 'C-p' -- 'Escape 0x10' 'M-C-p' +assert_key 0x11 'C-q' -- 'Escape 0x11' 'M-C-q' +assert_key 0x12 'C-r' -- 'Escape 0x12' 'M-C-r' +assert_key 0x13 'C-s' -- 'Escape 0x13' 'M-C-s' +assert_key 0x14 'C-t' -- 'Escape 0x14' 'M-C-t' +assert_key 0x15 'C-u' -- 'Escape 0x15' 'M-C-u' +assert_key 0x16 'C-v' -- 'Escape 0x16' 'M-C-v' +assert_key 0x17 'C-w' -- 'Escape 0x17' 'M-C-w' +assert_key 0x18 'C-x' -- 'Escape 0x18' 'M-C-x' +assert_key 0x19 'C-y' -- 'Escape 0x19' 'M-C-y' +assert_key 0x1A 'C-z' -- 'Escape 0x1A' 'M-C-z' +assert_key 0x1B 'Escape' -- 'Escape 0x1B' 'M-Escape' +assert_key 0x1C "C-\\" -- 'Escape 0x1C' "M-C-\\" +assert_key 0x1D 'C-]' -- 'Escape 0x1D' 'M-C-]' +assert_key 0x1E 'C-^' -- 'Escape 0x1E' 'M-C-^' +assert_key 0x1F 'C-_' -- 'Escape 0x1F' 'M-C-_' +assert_key 0x20 'Space' -- 'Escape 0x20' 'M-Space' +assert_key 0x21 '!' -- 'Escape 0x21' 'M-!' +assert_key 0x22 '"' -- 'Escape 0x22' 'M-"' +assert_key 0x23 '#' -- 'Escape 0x23'= 'M-#' +assert_key 0x24 '$' -- 'Escape 0x24'= 'M-$' +assert_key 0x25 '%' -- 'Escape 0x25'= 'M-%' +assert_key 0x26 '&' -- 'Escape 0x26'= 'M-&' +assert_key 0x27 "'" -- 'Escape 0x27' "M-'" +assert_key 0x28 '(' -- 'Escape 0x28' 'M-(' +assert_key 0x29 ')' -- 'Escape 0x29' 'M-)' +assert_key 0x2A '*' -- 'Escape 0x2A' 'M-*' +assert_key 0x2B '+' -- 'Escape 0x2B' 'M-+' +assert_key 0x2C ',' -- 'Escape 0x2C' 'M-,' +assert_key 0x2D '-' -- 'Escape 0x2D' 'M--' +assert_key 0x2E '.' -- 'Escape 0x2E' 'M-.' +assert_key 0x2F '/' -- 'Escape 0x2F' 'M-/' +assert_key 0x30 '0' -- 'Escape 0x30' 'M-0' +assert_key 0x31 '1' -- 'Escape 0x31' 'M-1' +assert_key 0x32 '2' -- 'Escape 0x32' 'M-2' +assert_key 0x33 '3' -- 'Escape 0x33' 'M-3' +assert_key 0x34 '4' -- 'Escape 0x34' 'M-4' +assert_key 0x35 '5' -- 'Escape 0x35' 'M-5' +assert_key 0x36 '6' -- 'Escape 0x36' 'M-6' +assert_key 0x37 '7' -- 'Escape 0x37' 'M-7' +assert_key 0x38 '8' -- 'Escape 0x38' 'M-8' +assert_key 0x39 '9' -- 'Escape 0x39' 'M-9' +assert_key 0x3A ':' -- 'Escape 0x3A' 'M-:' +assert_key 0x3B ';' -- 'Escape 0x3B' 'M-;' +assert_key 0x3C '<' -- 'Escape 0x3C' 'M-<' +assert_key 0x3D '=' -- 'Escape 0x3D' 'M-=' +assert_key 0x3E '>' -- 'Escape 0x3E' 'M->' +assert_key 0x3F '?' -- 'Escape 0x3F' 'M-?' +assert_key 0x40 '@' -- 'Escape 0x40' 'M-@' +assert_key 0x41 'A' -- 'Escape 0x41' 'M-A' +assert_key 0x42 'B' -- 'Escape 0x42' 'M-B' +assert_key 0x43 'C' -- 'Escape 0x43' 'M-C' +assert_key 0x44 'D' -- 'Escape 0x44' 'M-D' +assert_key 0x45 'E' -- 'Escape 0x45' 'M-E' +assert_key 0x46 'F' -- 'Escape 0x46' 'M-F' +assert_key 0x47 'G' -- 'Escape 0x47' 'M-G' +assert_key 0x48 'H' -- 'Escape 0x48' 'M-H' +assert_key 0x49 'I' -- 'Escape 0x49' 'M-I' +assert_key 0x4A 'J' -- 'Escape 0x4A' 'M-J' +assert_key 0x4B 'K' -- 'Escape 0x4B' 'M-K' +assert_key 0x4C 'L' -- 'Escape 0x4C' 'M-L' +assert_key 0x4D 'M' -- 'Escape 0x4D' 'M-M' +assert_key 0x4E 'N' -- 'Escape 0x4E' 'M-N' +assert_key 0x4F 'O' -- 'Escape 0x4F' 'M-O' +assert_key 0x50 'P' -- 'Escape 0x50' 'M-P' +assert_key 0x51 'Q' -- 'Escape 0x51' 'M-Q' +assert_key 0x52 'R' -- 'Escape 0x52' 'M-R' +assert_key 0x53 'S' -- 'Escape 0x53' 'M-S' +assert_key 0x54 'T' -- 'Escape 0x54' 'M-T' +assert_key 0x55 'U' -- 'Escape 0x55' 'M-U' +assert_key 0x56 'V' -- 'Escape 0x56' 'M-V' +assert_key 0x57 'W' -- 'Escape 0x57' 'M-W' +assert_key 0x58 'X' -- 'Escape 0x58' 'M-X' +assert_key 0x59 'Y' -- 'Escape 0x59' 'M-Y' +assert_key 0x5A 'Z' -- 'Escape 0x5A' 'M-Z' +assert_key 0x5B '[' -- 'Escape 0x5B' 'M-[' +assert_key 0x5C "\\" -- 'Escape 0x5C' "M-\\" +assert_key 0x5D ']' -- 'Escape 0x5D' 'M-]' +assert_key 0x5E '^' -- 'Escape 0x5E' 'M-^' +assert_key 0x5F '_' -- 'Escape 0x5F' 'M-_' +assert_key 0x60 '`' -- 'Escape 0x60' 'M-`' +assert_key 0x61 'a' -- 'Escape 0x61' 'M-a' +assert_key 0x62 'b' -- 'Escape 0x62' 'M-b' +assert_key 0x63 'c' -- 'Escape 0x63' 'M-c' +assert_key 0x64 'd' -- 'Escape 0x64' 'M-d' +assert_key 0x65 'e' -- 'Escape 0x65' 'M-e' +assert_key 0x66 'f' -- 'Escape 0x66' 'M-f' +assert_key 0x67 'g' -- 'Escape 0x67' 'M-g' +assert_key 0x68 'h' -- 'Escape 0x68' 'M-h' +assert_key 0x69 'i' -- 'Escape 0x69' 'M-i' +assert_key 0x6A 'j' -- 'Escape 0x6A' 'M-j' +assert_key 0x6B 'k' -- 'Escape 0x6B' 'M-k' +assert_key 0x6C 'l' -- 'Escape 0x6C' 'M-l' +assert_key 0x6D 'm' -- 'Escape 0x6D' 'M-m' +assert_key 0x6E 'n' -- 'Escape 0x6E' 'M-n' +assert_key 0x6F 'o' -- 'Escape 0x6F' 'M-o' +assert_key 0x70 'p' -- 'Escape 0x70' 'M-p' +assert_key 0x71 'q' -- 'Escape 0x71' 'M-q' +assert_key 0x72 'r' -- 'Escape 0x72' 'M-r' +assert_key 0x73 's' -- 'Escape 0x73' 'M-s' +assert_key 0x74 't' -- 'Escape 0x74' 'M-t' +assert_key 0x75 'u' -- 'Escape 0x75' 'M-u' +assert_key 0x76 'v' -- 'Escape 0x76' 'M-v' +assert_key 0x77 'w' -- 'Escape 0x77' 'M-w' +assert_key 0x78 'x' -- 'Escape 0x78' 'M-x' +assert_key 0x79 'y' -- 'Escape 0x79' 'M-y' +assert_key 0x7A 'z' -- 'Escape 0x7A' 'M-z' +assert_key 0x7B '{' -- 'Escape 0x7B' 'M-{' +assert_key 0x7C '|' -- 'Escape 0x7C' 'M-|' +assert_key 0x7D '}' -- 'Escape 0x7D' 'M-}' +assert_key 0x7E '~' -- 'Escape 0x7E' 'M-~' +assert_key 0x7F 'BSpace' -- 'Escape 0x7F' 'M-BSpace' + +# Numeric keypad +assert_key 'Escape OM' 'KPEnter' -- 'Escape Escape OM' 'M-KPEnter' +assert_key 'Escape Oj' 'KP*' -- 'Escape Escape Oj' 'M-KP*' +assert_key 'Escape Ok' 'KP+' -- 'Escape Escape Ok' 'M-KP+' +assert_key 'Escape Om' 'KP-' -- 'Escape Escape Om' 'M-KP-' +assert_key 'Escape On' 'KP.' -- 'Escape Escape On' 'M-KP.' +assert_key 'Escape Oo' 'KP/' -- 'Escape Escape Oo' 'M-KP/' +assert_key 'Escape Op' 'KP0' -- 'Escape Escape Op' 'M-KP0' +assert_key 'Escape Oq' 'KP1' -- 'Escape Escape Oq' 'M-KP1' +assert_key 'Escape Or' 'KP2' -- 'Escape Escape Or' 'M-KP2' +assert_key 'Escape Os' 'KP3' -- 'Escape Escape Os' 'M-KP3' +assert_key 'Escape Ot' 'KP4' -- 'Escape Escape Ot' 'M-KP4' +assert_key 'Escape Ou' 'KP5' -- 'Escape Escape Ou' 'M-KP5' +assert_key 'Escape Ov' 'KP6' -- 'Escape Escape Ov' 'M-KP6' +assert_key 'Escape Ow' 'KP7' -- 'Escape Escape Ow' 'M-KP7' +assert_key 'Escape Ox' 'KP8' -- 'Escape Escape Ox' 'M-KP8' +assert_key 'Escape Oy' 'KP9' -- 'Escape Escape Oy' 'M-KP9' + +# Arrow keys +assert_key 'Escape OA' 'Up' -- 'Escape Escape OA' 'M-Up' +assert_key 'Escape OB' 'Down' -- 'Escape Escape OB' 'M-Down' +assert_key 'Escape OC' 'Right' -- 'Escape Escape OC' 'M-Right' +assert_key 'Escape OD' 'Left' -- 'Escape Escape OD' 'M-Left' + +assert_key 'Escape [A' 'Up' -- 'Escape Escape [A' 'M-Up' +assert_key 'Escape [B' 'Down' -- 'Escape Escape [B' 'M-Down' +assert_key 'Escape [C' 'Right' -- 'Escape Escape [C' 'M-Right' +assert_key 'Escape [D' 'Left' -- 'Escape Escape [D' 'M-Left' + +# Other xterm keys +assert_key 'Escape OH' 'Home' -- 'Escape Escape OH' 'M-Home' +assert_key 'Escape OF' 'End' -- 'Escape Escape OF' 'M-End' + +assert_key 'Escape [H' 'Home' -- 'Escape Escape [H' 'M-Home' +assert_key 'Escape [F' 'End' -- 'Escape Escape [F' 'M-End' + +# rxvt arrow keys +assert_key 'Escape Oa' 'C-Up' +assert_key 'Escape Ob' 'C-Down' +assert_key 'Escape Oc' 'C-Right' +assert_key 'Escape Od' 'C-Left' +assert_key 'Escape [a' 'S-Up' +assert_key 'Escape [b' 'S-Down' +assert_key 'Escape [c' 'S-Right' +assert_key 'Escape [d' 'S-Left' + +# rxvt function keys +assert_key 'Escape [11~' 'F1' +assert_key 'Escape [12~' 'F2' +assert_key 'Escape [13~' 'F3' +assert_key 'Escape [14~' 'F4' +assert_key 'Escape [15~' 'F5' +assert_key 'Escape [17~' 'F6' +assert_key 'Escape [18~' 'F7' +assert_key 'Escape [19~' 'F8' +assert_key 'Escape [20~' 'F9' +assert_key 'Escape [21~' 'F10' +assert_key 'Escape [23~' 'F11' +assert_key 'Escape [24~' 'F12' + +# With TERM=screen, these will be seen as F11 and F12 +# assert_key 'Escape [23~' 'S-F1' +# assert_key 'Escape [24~' 'S-F2' +assert_key 'Escape [25~' 'S-F3' +assert_key 'Escape [26~' 'S-F4' +assert_key 'Escape [28~' 'S-F5' +assert_key 'Escape [29~' 'S-F6' +assert_key 'Escape [31~' 'S-F7' +assert_key 'Escape [32~' 'S-F8' +assert_key 'Escape [33~' 'S-F9' +assert_key 'Escape [34~' 'S-F10' +assert_key 'Escape [23$' 'S-F11' +assert_key 'Escape [24$' 'S-F12' + +assert_key 'Escape [11^' 'C-F1' +assert_key 'Escape [12^' 'C-F2' +assert_key 'Escape [13^' 'C-F3' +assert_key 'Escape [14^' 'C-F4' +assert_key 'Escape [15^' 'C-F5' +assert_key 'Escape [17^' 'C-F6' +assert_key 'Escape [18^' 'C-F7' +assert_key 'Escape [19^' 'C-F8' +assert_key 'Escape [20^' 'C-F9' +assert_key 'Escape [21^' 'C-F10' +assert_key 'Escape [23^' 'C-F11' +assert_key 'Escape [24^' 'C-F12' + +assert_key 'Escape [11@' 'C-S-F1' +assert_key 'Escape [12@' 'C-S-F2' +assert_key 'Escape [13@' 'C-S-F3' +assert_key 'Escape [14@' 'C-S-F4' +assert_key 'Escape [15@' 'C-S-F5' +assert_key 'Escape [17@' 'C-S-F6' +assert_key 'Escape [18@' 'C-S-F7' +assert_key 'Escape [19@' 'C-S-F8' +assert_key 'Escape [20@' 'C-S-F9' +assert_key 'Escape [21@' 'C-S-F10' +assert_key 'Escape [23@' 'C-S-F11' +assert_key 'Escape [24@' 'C-S-F12' + +# Focus tracking +assert_key 'Escape [I' 'FocusIn' +assert_key 'Escape [O' 'FocusOut' + +# Paste keys +assert_key 'Escape [200~' 'PasteStart' +assert_key 'Escape [201~' 'PasteEnd' + +assert_key 'Escape [Z' 'BTab' + +assert_extended_key () { + code=$1 + key_name=$2 + + assert_key "Escape [${code};5u" "C-$key_name" + assert_key "Escape [${code};7u" "M-C-$key_name" +} + +# Extended keys +# assert_extended_key 65 'A' +# assert_extended_key 66 'B' +# assert_extended_key 67 'C' +# assert_extended_key 68 'D' +# assert_extended_key 69 'E' +# assert_extended_key 70 'F' +# assert_extended_key 71 'G' +# assert_extended_key 72 'H' +# assert_extended_key 73 'I' +# assert_extended_key 74 'J' +# assert_extended_key 75 'K' +# assert_extended_key 76 'L' +# assert_extended_key 77 'M' +# assert_extended_key 78 'N' +# assert_extended_key 79 'O' +# assert_extended_key 80 'P' +# assert_extended_key 81 'Q' +# assert_extended_key 82 'R' +# assert_extended_key 83 'S' +# assert_extended_key 84 'T' +# assert_extended_key 85 'U' +# assert_extended_key 86 'V' +# assert_extended_key 87 'W' +# assert_extended_key 88 'X' +# assert_extended_key 89 'Y' +# assert_extended_key 90 'Z' +# assert_extended_key 123 '{' +# assert_extended_key 124 '|' +# assert_extended_key 125 '}' + +# assert_key 'Escape [105;5u' 'C-i' +# assert_key 'Escape [73;5u' 'C-I' + +# assert_key 'Escape [109;5u' 'C-m' +# assert_key 'Escape [77;5u' 'C-M' + +# assert_key 'Escape [91;5u' 'C-[' +assert_key 'Escape [123;5u' 'C-{' + +# assert_key 'Escape [64;5u' 'C-@' + +assert_key 'Escape [32;2u' 'S-Space' +# assert_key 'Escape [32;6u' 'C-S-Space' + +assert_key 'Escape [9;5u' 'C-Tab' +assert_key 'Escape [1;5Z' 'C-S-Tab' + +$TMUX kill-server 2>/dev/null +$TMUX2 kill-server 2>/dev/null + +exit $exit_status -- cgit From 7e497c7f2303b29b0f44fe360a78c44ca86b87f9 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 7 Dec 2022 09:44:44 +0000 Subject: Process escape sequences in show-buffer, GitHub issue 3401. --- cmd-queue.c | 96 ++++++++++++++++++++++++++++++++++++++++--------------- cmd-save-buffer.c | 13 +++++--- tmux.h | 1 + 3 files changed, 80 insertions(+), 30 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 8325e2e8..8ed3673b 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "tmux.h" @@ -823,43 +824,88 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags) /* Show message from command. */ void -cmdq_print(struct cmdq_item *item, const char *fmt, ...) +cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb) { struct client *c = item->client; + void *data = EVBUFFER_DATA(evb); + size_t size = EVBUFFER_LENGTH(evb); struct window_pane *wp; struct window_mode_entry *wme; - va_list ap; - char *tmp, *msg; - - va_start(ap, fmt); - xvasprintf(&msg, fmt, ap); - va_end(ap); + char *sanitized, *msg, *line; - log_debug("%s: %s", __func__, msg); + if (!parse) { + utf8_stravisx(&msg, data, size, VIS_OCTAL|VIS_CSTYLE|VIS_TAB); + log_debug("%s: %s", __func__, msg); + } else { + msg = EVBUFFER_DATA(evb); + if (msg[size - 1] != '\0') + evbuffer_add(evb, "", 1); + } if (c == NULL) - /* nothing */; - else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { + goto out; + + if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { if (~c->flags & CLIENT_UTF8) { - tmp = msg; - msg = utf8_sanitize(tmp); - free(tmp); + sanitized = utf8_sanitize(msg); + if (c->flags & CLIENT_CONTROL) + control_write(c, "%s", sanitized); + else + file_print(c, "%s\n", sanitized); + free(sanitized); + } else { + if (c->flags & CLIENT_CONTROL) + control_write(c, "%s", msg); + else + file_print(c, "%s\n", msg); } - if (c->flags & CLIENT_CONTROL) - control_write(c, "%s", msg); - else - file_print(c, "%s\n", msg); - } else { - wp = server_client_get_pane(c); - wme = TAILQ_FIRST(&wp->modes); - if (wme == NULL || wme->mode != &window_view_mode) { - window_pane_set_mode(wp, NULL, &window_view_mode, NULL, - NULL); + goto out; + } + + wp = server_client_get_pane(c); + wme = TAILQ_FIRST(&wp->modes); + if (wme == NULL || wme->mode != &window_view_mode) + window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL); + if (parse) { + do { + line = evbuffer_readln(evb, NULL, EVBUFFER_EOL_LF); + if (line != NULL) { + window_copy_add(wp, 1, "%s", line); + free(line); + } + } while (line != NULL); + + size = EVBUFFER_LENGTH(evb); + if (size != 0) { + line = EVBUFFER_DATA(evb); + window_copy_add(wp, 1, "%.*s", (int)size, line); } + } else window_copy_add(wp, 0, "%s", msg); - } - free(msg); +out: + if (!parse) + free(msg); + +} + +/* Show message from command. */ +void +cmdq_print(struct cmdq_item *item, const char *fmt, ...) +{ + va_list ap; + struct evbuffer *evb; + + evb = evbuffer_new(); + if (evb == NULL) + fatalx("out of memory"); + + va_start(ap, fmt); + evbuffer_add_vprintf(evb, fmt, ap); + va_end(ap); + + cmdq_print_data(item, 0, evb); + evbuffer_free(evb); } /* Show error from command. */ diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 513181e1..2983282d 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -79,7 +79,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) int flags; const char *bufname = args_get(args, 'b'), *bufdata; size_t bufsize; - char *path, *tmp; + char *path; + struct evbuffer *evb; if (bufname == NULL) { if ((pb = paste_get_top(NULL)) == NULL) { @@ -97,10 +98,12 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) if (cmd_get_entry(self) == &cmd_show_buffer_entry) { if (c->session != NULL || (c->flags & CLIENT_CONTROL)) { - utf8_stravisx(&tmp, bufdata, bufsize, - VIS_OCTAL|VIS_CSTYLE|VIS_TAB); - cmdq_print(item, "%s", tmp); - free(tmp); + evb = evbuffer_new(); + if (evb == NULL) + fatalx("out of memory"); + evbuffer_add(evb, bufdata, bufsize); + cmdq_print_data(item, 1, evb); + evbuffer_free(evb); return (CMD_RETURN_NORMAL); } path = xstrdup("-"); diff --git a/tmux.h b/tmux.h index 8c5e071f..9a13162d 100644 --- a/tmux.h +++ b/tmux.h @@ -2550,6 +2550,7 @@ u_int cmdq_next(struct client *); struct cmdq_item *cmdq_running(struct client *); void cmdq_guard(struct cmdq_item *, const char *, int); void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...); +void cmdq_print_data(struct cmdq_item *, int, struct evbuffer *); void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...); /* cmd-wait-for.c */ -- cgit From 70ff8cfe1e06987501a55a32df31d1f69acd2f99 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 7 Dec 2022 12:30:36 +0000 Subject: No vis.h in portable. --- cmd-queue.c | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd-queue.c b/cmd-queue.c index 8ed3673b..9f6b4650 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -24,7 +24,6 @@ #include #include #include -#include #include "tmux.h" -- cgit From 3b3f42053a5f11af5285392a5a072facbc16f4a9 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 16 Dec 2022 08:13:40 +0000 Subject: Add send-keys -K to handle keys directly as if typed (so look up in key table). GitHub issue 3361. --- arguments.c | 203 ++++++++++++++++++++++++++++++++++-------------------- cmd-find-window.c | 4 +- cmd-send-keys.c | 35 +++++++--- tmux.1 | 11 ++- tmux.h | 2 +- 5 files changed, 163 insertions(+), 92 deletions(-) diff --git a/arguments.c b/arguments.c index b08582ee..47ca17ce 100644 --- a/arguments.c +++ b/arguments.c @@ -37,6 +37,10 @@ struct args_entry { u_char flag; struct args_values values; u_int count; + + int flags; +#define ARGS_ENTRY_OPTIONAL_VALUE 0x1 + RB_ENTRY(args_entry) entry; }; @@ -122,6 +126,101 @@ args_create(void) return (args); } +/* Parse a single flag. */ +static int +args_parse_flag_argument(struct args_value *values, u_int count, char **cause, + struct args *args, u_int *i, const char *string, int flag, + int optional_argument) +{ + struct args_value *argument, *new; + const char *s; + + new = xcalloc(1, sizeof *new); + if (*string != '\0') { + new->type = ARGS_STRING; + new->string = xstrdup(string); + goto out; + } + + if (*i == count) + argument = NULL; + else { + argument = &values[*i]; + if (argument->type != ARGS_STRING) { + xasprintf(cause, "-%c argument must be a string", flag); + return (-1); + } + if (argument->string[0] == '-') + argument = NULL; + } + if (argument == NULL) { + if (optional_argument) { + log_debug("%s: -%c (optional)", __func__, flag); + args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE); + return (0); /* either - or end */ + } + xasprintf(cause, "-%c expects an argument", flag); + return (-1); + } + args_copy_value(new, argument); + (*i)++; + +out: + s = args_value_as_string(new); + log_debug("%s: -%c = %s", __func__, flag, s); + args_set(args, flag, new, 0); + return (0); +} + +/* Parse flags argument. */ +static int +args_parse_flags(const struct args_parse *parse, struct args_value *values, + u_int count, char **cause, struct args *args, int *i) +{ + struct args_value *value; + u_char flag; + const char *found, *string; + int optional_argument; + + value = &values[*i]; + if (value->type != ARGS_STRING) + return (1); + + string = value->string; + log_debug("%s: next %s", __func__, string); + if (*string++ != '-' || *string == '\0') + return (1); + (*i)++; + if (string[0] == '-' && string[1] == '\0') + return (1); + + for (;;) { + flag = *string++; + if (flag == '\0') + return (0); + if (flag == '?') + return (-1); + if (!isalnum(flag)) { + xasprintf(cause, "invalid flag -%c", flag); + return (-1); + } + + found = strchr(parse->template, flag); + if (found == NULL) { + xasprintf(cause, "unknown flag -%c", flag); + return (-1); + } + if (*++found != ':') { + log_debug("%s: -%c", __func__, flag); + args_set(args, flag, NULL, 0); + continue; + } + optional_argument = (*found == ':'); + return (args_parse_flag_argument(values, count, cause, args, i, + string, flag, optional_argument)); + } +} + /* Parse arguments into a new argument set. */ struct args * args_parse(const struct args_parse *parse, struct args_value *values, @@ -131,86 +230,21 @@ args_parse(const struct args_parse *parse, struct args_value *values, u_int i; enum args_parse_type type; struct args_value *value, *new; - u_char flag; - const char *found, *string, *s; - int optional_argument; + const char *s; + int stop; if (count == 0) return (args_create()); args = args_create(); for (i = 1; i < count; /* nothing */) { - value = &values[i]; - if (value->type != ARGS_STRING) - break; - - string = value->string; - if (*string++ != '-' || *string == '\0') - break; - i++; - if (string[0] == '-' && string[1] == '\0') - break; - - for (;;) { - flag = *string++; - if (flag == '\0') - break; - if (flag == '?') { - args_free(args); - return (NULL); - } - if (!isalnum(flag)) { - xasprintf(cause, "invalid flag -%c", flag); - args_free(args); - return (NULL); - } - found = strchr(parse->template, flag); - if (found == NULL) { - xasprintf(cause, "unknown flag -%c", flag); - args_free(args); - return (NULL); - } - if (*++found != ':') { - log_debug("%s: -%c", __func__, flag); - args_set(args, flag, NULL); - continue; - } - if (*found == ':') { - optional_argument = 1; - found++; - } - new = xcalloc(1, sizeof *new); - if (*string != '\0') { - new->type = ARGS_STRING; - new->string = xstrdup(string); - } else { - if (i == count) { - if (optional_argument) { - log_debug("%s: -%c", __func__, - flag); - args_set(args, flag, NULL); - continue; - } - xasprintf(cause, - "-%c expects an argument", - flag); - args_free(args); - return (NULL); - } - if (values[i].type != ARGS_STRING) { - xasprintf(cause, - "-%c argument must be a string", - flag); - args_free(args); - return (NULL); - } - args_copy_value(new, &values[i++]); - } - s = args_value_as_string(new); - log_debug("%s: -%c = %s", __func__, flag, s); - args_set(args, flag, new); - break; + stop = args_parse_flags(parse, values, count, cause, args, &i); + if (stop == -1) { + args_free(args); + return (NULL); } + if (stop == 1) + break; } log_debug("%s: flags end at %u of %u", __func__, i, count); if (i != count) { @@ -323,13 +357,13 @@ args_copy(struct args *args, int argc, char **argv) RB_FOREACH(entry, args_tree, &args->tree) { if (TAILQ_EMPTY(&entry->values)) { for (i = 0; i < entry->count; i++) - args_set(new_args, entry->flag, NULL); + args_set(new_args, entry->flag, NULL, 0); continue; } TAILQ_FOREACH(value, &entry->values, entry) { new_value = xcalloc(1, sizeof *new_value); args_copy_copy_value(new_value, value, argc, argv); - args_set(new_args, entry->flag, new_value); + args_set(new_args, entry->flag, new_value, 0); } } if (args->count == 0) @@ -487,6 +521,7 @@ args_print(struct args *args) char *buf; u_int i, j; struct args_entry *entry; + struct args_entry *last = NULL; struct args_value *value; len = 1; @@ -494,6 +529,8 @@ args_print(struct args *args) /* Process the flags first. */ RB_FOREACH(entry, args_tree, &args->tree) { + if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) + continue; if (!TAILQ_EMPTY(&entry->values)) continue; @@ -505,6 +542,16 @@ args_print(struct args *args) /* Then the flags with arguments. */ RB_FOREACH(entry, args_tree, &args->tree) { + if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) { + if (*buf != '\0') + args_print_add(&buf, &len, " -%c", entry->flag); + else + args_print_add(&buf, &len, "-%c", entry->flag); + last = entry; + continue; + } + if (TAILQ_EMPTY(&entry->values)) + continue; TAILQ_FOREACH(value, &entry->values, entry) { if (*buf != '\0') args_print_add(&buf, &len, " -%c", entry->flag); @@ -512,7 +559,10 @@ args_print(struct args *args) args_print_add(&buf, &len, "-%c", entry->flag); args_print_add_value(&buf, &len, value); } + last = entry; } + if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE)) + args_print_add(&buf, &len, " --"); /* And finally the argument vector. */ for (i = 0; i < args->count; i++) @@ -582,7 +632,7 @@ args_has(struct args *args, u_char flag) /* Set argument value in the arguments tree. */ void -args_set(struct args *args, u_char flag, struct args_value *value) +args_set(struct args *args, u_char flag, struct args_value *value, int flags) { struct args_entry *entry; @@ -591,6 +641,7 @@ args_set(struct args *args, u_char flag, struct args_value *value) entry = xcalloc(1, sizeof *entry); entry->flag = flag; entry->count = 1; + entry->flags = flags; TAILQ_INIT(&entry->values); RB_INSERT(args_tree, &args->tree, entry); } else diff --git a/cmd-find-window.c b/cmd-find-window.c index 6e07537c..cb9afacb 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -103,8 +103,8 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) new_args = args_create(); if (args_has(args, 'Z')) - args_set(new_args, 'Z', NULL); - args_set(new_args, 'f', filter); + args_set(new_args, 'Z', NULL, 0); + args_set(new_args, 'f', filter, 0); window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args); args_free(new_args); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index e22d94a6..2eed4ccd 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -33,13 +33,13 @@ const struct cmd_entry cmd_send_keys_entry = { .name = "send-keys", .alias = "send", - .args = { "FHlMN:Rt:X", 0, -1, NULL }, - .usage = "[-FHlMRX] [-N repeat-count] " CMD_TARGET_PANE_USAGE - " key ...", + .args = { "c:FHKlMN:Rt:X", 0, -1, NULL }, + .usage = "[-FHKlMRX] [-c target-client] [-N repeat-count] " + CMD_TARGET_PANE_USAGE " key ...", .target = { 't', CMD_FIND_PANE, 0 }, - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG, .exec = cmd_send_keys_exec }; @@ -58,7 +58,7 @@ const struct cmd_entry cmd_send_prefix_entry = { static struct cmdq_item * cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, - key_code key) + struct args *args, key_code key) { struct cmd_find_state *target = cmdq_get_target(item); struct client *tc = cmdq_get_target_client(item); @@ -66,8 +66,18 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, struct winlink *wl = target->wl; struct window_pane *wp = target->wp; struct window_mode_entry *wme; - struct key_table *table; + struct key_table *table = NULL; struct key_binding *bd; + struct key_event *event; + + if (args_has(args, 'K')) { + event = xmalloc(sizeof *event); + event->key = key; + memset(&event->m, 0, sizeof event->m); + if (server_client_handle_key(tc, event) == 0) + free(event); + return (item); + } wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode->key_table == NULL) { @@ -102,14 +112,16 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after, n = strtol(s, &endptr, 16); if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0') return (item); - return (cmd_send_keys_inject_key(item, after, KEYC_LITERAL|n)); + return (cmd_send_keys_inject_key(item, after, args, + KEYC_LITERAL|n)); } literal = args_has(args, 'l'); if (!literal) { key = key_string_lookup_string(s); if (key != KEYC_NONE && key != KEYC_UNKNOWN) { - after = cmd_send_keys_inject_key(item, after, key); + after = cmd_send_keys_inject_key(item, after, args, + key); if (after != NULL) return (after); } @@ -125,7 +137,8 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after, continue; key = uc; } - after = cmd_send_keys_inject_key(item, after, key); + after = cmd_send_keys_inject_key(item, after, args, + key); } free(ud); } @@ -193,7 +206,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) key = options_get_number(s->options, "prefix2"); else key = options_get_number(s->options, "prefix"); - cmd_send_keys_inject_key(item, item, key); + cmd_send_keys_inject_key(item, item, args, key); return (CMD_RETURN_NORMAL); } @@ -207,7 +220,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'N') || args_has(args, 'R')) return (CMD_RETURN_NORMAL); for (; np != 0; np--) - cmd_send_keys_inject_key(item, NULL, event->key); + cmd_send_keys_inject_key(item, NULL, args, event->key); return (CMD_RETURN_NORMAL); } diff --git a/tmux.1 b/tmux.1 index 4f314110..2a2e54bf 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3212,13 +3212,14 @@ lists only the first matching key. lists the command for keys that do not have a note rather than skipping them. .Tg send .It Xo Ic send-keys -.Op Fl FHlMRX +.Op Fl FHKlMRX +.Op Fl c Ar target-client .Op Fl N Ar repeat-count .Op Fl t Ar target-pane .Ar key Ar ... .Xc .D1 Pq alias: Ic send -Send a key or keys to a window. +Send a key or keys to a window or client. Each argument .Ar key is the name of the key (such as @@ -3227,6 +3228,12 @@ or .Ql NPage ) to send; if the string is not recognised as a key, it is sent as a series of characters. +If +.Fl K +is given, keys are sent to +.Ar target-client , +so they are looked up in the client's key table, rather than to +.Ar target-pane . All arguments are sent sequentially from first to last. If no keys are given and the command is bound to a key, then that key is used. .Pp diff --git a/tmux.h b/tmux.h index 9a13162d..5b15c1e1 100644 --- a/tmux.h +++ b/tmux.h @@ -2387,7 +2387,7 @@ void tty_keys_free(struct tty *); int tty_keys_next(struct tty *); /* arguments.c */ -void args_set(struct args *, u_char, struct args_value *); +void args_set(struct args *, u_char, struct args_value *, int); struct args *args_create(void); struct args *args_parse(const struct args_parse *, struct args_value *, u_int, char **); -- cgit From 8bd17bff49888d11f9cb19955e778bdcbc4eeea6 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 16 Dec 2022 08:19:58 +0000 Subject: Make U+FE0F VARIATION SELECTOR-16 change the width from 1 to 2. GitHub issue 3409. --- screen-write.c | 14 +++++++++++--- utf8.c | 11 +++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/screen-write.c b/screen-write.c index 24195708..59d289ec 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1820,7 +1820,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) struct grid_cell tmp_gc, now_gc; struct tty_ctx ttyctx; u_int sx = screen_size_x(s), sy = screen_size_y(s); - u_int width = gc->data.width, xx, last, cx, cy; + u_int width = gc->data.width, xx, last, cy; int selected, skip = 1; /* Ignore padding cells. */ @@ -1853,12 +1853,12 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) ctx->flags &= ~SCREEN_WRITE_ZWJ; screen_write_collect_flush(ctx, 0, __func__); if ((gc = screen_write_combine(ctx, ud, &xx)) != NULL) { - cx = s->cx; cy = s->cy; + cy = s->cy; screen_write_set_cursor(ctx, xx, s->cy); screen_write_initctx(ctx, &ttyctx, 0); ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); - s->cx = cx; s->cy = cy; + s->cx = xx + 1 + gc->data.width; s->cy = cy; } return; } @@ -2016,6 +2016,14 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud, memcpy(gc.data.data + gc.data.size, ud->data, ud->size); gc.data.size += ud->size; + /* If this is U+FE0F VARIATION SELECTOR-16, force the width to 2. */ + if (gc.data.width == 1 && + ud->size == 3 && + memcmp(ud->data, "\357\270\217", 3) == 0) { + grid_view_set_padding(gd, (*xx) + 1, s->cy); + gc.data.width = 2; + } + /* Set the new cell. */ grid_view_set_cell(gd, *xx, s->cy, &gc); diff --git a/utf8.c b/utf8.c index 55a68110..03918cd2 100644 --- a/utf8.c +++ b/utf8.c @@ -227,12 +227,11 @@ utf8_width(struct utf8_data *ud, int *width) return (UTF8_ERROR); } *width = wcwidth(wc); - if (*width < 0 || *width > 0xff) { - log_debug("UTF-8 %.*s, wcwidth() %d", (int)ud->size, ud->data, - *width); - return (UTF8_ERROR); - } - return (UTF8_DONE); + log_debug("UTF-8 %.*s %#x, wcwidth() %d", (int)ud->size, ud->data, + (u_int)wc, *width); + if (*width >= 0 && *width <= 0xff) + return (UTF8_DONE); + return (UTF8_ERROR); } /* -- cgit From 7cb48fc40b178d0b7bf281dee6799fa0e3745c70 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 16 Dec 2022 08:22:05 +0000 Subject: Do not escape tabs in output (iTerm2 needs them). GitHub issue 3414. --- cmd-queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-queue.c b/cmd-queue.c index 8ed3673b..827630d7 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -834,7 +834,7 @@ cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb) char *sanitized, *msg, *line; if (!parse) { - utf8_stravisx(&msg, data, size, VIS_OCTAL|VIS_CSTYLE|VIS_TAB); + utf8_stravisx(&msg, data, size, VIS_OCTAL|VIS_CSTYLE); log_debug("%s: %s", __func__, msg); } else { msg = EVBUFFER_DATA(evb); -- cgit From 4d79d463ef73fece1e3ab2d03b0076df5af9dc2f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 19 Dec 2022 07:30:10 +0000 Subject: Allow send-keys without a client again, reported by Stefan Hagen. --- cmd-send-keys.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 2eed4ccd..a7c5ee10 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_send_keys_entry = { .target = { 't', CMD_FIND_PANE, 0 }, - .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG, + .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL, .exec = cmd_send_keys_exec }; @@ -71,6 +71,8 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, struct key_event *event; if (args_has(args, 'K')) { + if (tc == NULL) + return (item); event = xmalloc(sizeof *event); event->key = key; memset(&event->m, 0, sizeof event->m); -- cgit From b5ab4d2c13277e749c96920d4b1b09f2acc73390 Mon Sep 17 00:00:00 2001 From: kn Date: Thu, 22 Dec 2022 19:53:23 +0000 Subject: Denote multiple arguments with 'arg ...' not 'args' A few programs used the plural in their synopsis which doesn't read as clear as the obvious triple-dot notation. mdoc(7) .Ar defaults to "file ..." if no arguments are given and consistent use of 'arg ...' matches that behaviour. Cleanup a few markups of the same argument so the text keeps reading naturally; omit unhelpful parts like 'if optional arguments are given, they are passed along' for tools like time(1) and timeout(1) that obviously execute commands with whatever arguments where given -- just like doas(1) which doesn't mention arguments in its DESCRIPTION in the first place. For expr(1) the difference between 'expressions' and 'expression ...' is crucial, as arguments must be passed as individual words. Feedback millert jmc schwarze deraadt OK jmc --- tmux.1 | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tmux.1 b/tmux.1 index 2a2e54bf..c84d9e87 100644 --- a/tmux.1 +++ b/tmux.1 @@ -961,7 +961,7 @@ Will run directly without invoking the shell. .Pp .Ar command -.Op Ar arguments +.Op Ar argument ... refers to a .Nm command, either passed with the command and arguments separately, for example: @@ -1538,8 +1538,7 @@ show debugging information about jobs and terminals. .Tg source .It Xo Ic source-file .Op Fl Fnqv -.Ar path -.Ar ... +.Ar path ... .Xc .D1 Pq alias: Ic source Execute commands from one or more files specified by @@ -3120,7 +3119,7 @@ Commands related to key bindings are as follows: .Op Fl nr .Op Fl N Ar note .Op Fl T Ar key-table -.Ar key command Op Ar arguments +.Ar key command Op Ar argument ... .Xc .D1 Pq alias: Ic bind Bind key @@ -3216,7 +3215,7 @@ lists the command for keys that do not have a note rather than skipping them. .Op Fl c Ar target-client .Op Fl N Ar repeat-count .Op Fl t Ar target-pane -.Ar key Ar ... +.Ar key ... .Xc .D1 Pq alias: Ic send Send a key or keys to a window or client. @@ -5821,8 +5820,7 @@ until it is dismissed. .Op Fl y Ar position .Ar name .Ar key -.Ar command -.Ar ... +.Ar command Op Ar argument ... .Xc .D1 Pq alias: Ic menu Display a menu on -- cgit From 3fe01ff09c2fe8629ebd5b0f2c2ce3aa5fa33c14 Mon Sep 17 00:00:00 2001 From: jmc Date: Mon, 26 Dec 2022 19:16:03 +0000 Subject: spelling fixes; from paul tagliamonte amendments to his diff are noted on tech --- tmux.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.h b/tmux.h index 5b15c1e1..dac23ebb 100644 --- a/tmux.h +++ b/tmux.h @@ -1410,7 +1410,7 @@ struct tty { #define TTY_HAVEDA 0x100 /* Primary DA. */ #define TTY_HAVEXDA 0x200 #define TTY_SYNCING 0x400 -#define TTY_HAVEDA2 0x800 /* Seconday DA. */ +#define TTY_HAVEDA2 0x800 /* Secondary DA. */ int flags; struct tty_term *term; -- cgit From a41a92744188ec5c8a8d4ddc100ec15b52d04603 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 3 Jan 2023 11:43:24 +0000 Subject: Query the client terminal for foreground and background colours and if OSC 10 or 11 is received but no colour has been set inside tmux, return the colour from the first attached client (probably most people will have all light or or all dark terminals). --- cmd-queue.c | 3 +- colour.c | 42 +++++++++++++++++++++- input.c | 115 ++++++++++++++++++++++++++++++++---------------------------- tmux.h | 7 ++++ tty-keys.c | 83 +++++++++++++++++++++++++++++++++++++++++-- tty.c | 10 ++++-- 6 files changed, 199 insertions(+), 61 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 827630d7..bf1dbdaf 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -834,7 +834,8 @@ cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb) char *sanitized, *msg, *line; if (!parse) { - utf8_stravisx(&msg, data, size, VIS_OCTAL|VIS_CSTYLE); + utf8_stravisx(&msg, data, size, + VIS_OCTAL|VIS_CSTYLE|VIS_NOSLASH); log_debug("%s: %s", __func__, msg); } else { msg = EVBUFFER_DATA(evb); diff --git a/colour.c b/colour.c index a282d182..9bde646f 100644 --- a/colour.c +++ b/colour.c @@ -960,6 +960,47 @@ colour_byname(const char *name) return (-1); } +/* Parse colour from an X11 string. */ +int +colour_parseX11(const char *p) +{ + double c, m, y, k = 0; + u_int r, g, b; + size_t len = strlen(p); + int colour = -1; + char *copy; + + if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) || + (len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) || + sscanf(p, "%d,%d,%d", &r, &g, &b) == 3) + colour = colour_join_rgb(r, g, b); + else if ((len == 18 && + sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) || + (len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3)) + colour = colour_join_rgb(r >> 8, g >> 8, b >> 8); + else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 || + sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) && + c >= 0 && c <= 1 && m >= 0 && m <= 1 && + y >= 0 && y <= 1 && k >= 0 && k <= 1) { + colour = colour_join_rgb( + (1 - c) * (1 - k) * 255, + (1 - m) * (1 - k) * 255, + (1 - y) * (1 - k) * 255); + } else { + while (len != 0 && *p == ' ') { + p++; + len--; + } + while (len != 0 && p[len - 1] == ' ') + len--; + copy = xstrndup(p, len); + colour = colour_byname(copy); + free(copy); + } + log_debug("%s: %s = %s", __func__, p, colour_tostring(colour)); + return (colour); +} + /* Initialize palette. */ void colour_palette_init(struct colour_palette *p) @@ -1069,5 +1110,4 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo) } a = options_array_next(a); } - } diff --git a/input.c b/input.c index f162b92f..adbea179 100644 --- a/input.c +++ b/input.c @@ -1086,6 +1086,7 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...) xvasprintf(&reply, fmt, ap); va_end(ap); + log_debug("%s: %s", __func__, reply); bufferevent_write(bev, reply, strlen(reply)); free(reply); } @@ -2456,47 +2457,6 @@ input_top_bit_set(struct input_ctx *ictx) return (0); } -/* Parse colour from OSC. */ -static int -input_osc_parse_colour(const char *p) -{ - double c, m, y, k = 0; - u_int r, g, b; - size_t len = strlen(p); - int colour = -1; - char *copy; - - if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) || - (len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) || - sscanf(p, "%d,%d,%d", &r, &g, &b) == 3) - colour = colour_join_rgb(r, g, b); - else if ((len == 18 && - sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) || - (len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3)) - colour = colour_join_rgb(r >> 8, g >> 8, b >> 8); - else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 || - sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) && - c >= 0 && c <= 1 && m >= 0 && m <= 1 && - y >= 0 && y <= 1 && k >= 0 && k <= 1) { - colour = colour_join_rgb( - (1 - c) * (1 - k) * 255, - (1 - m) * (1 - k) * 255, - (1 - y) * (1 - k) * 255); - } else { - while (len != 0 && *p == ' ') { - p++; - len--; - } - while (len != 0 && p[len - 1] == ' ') - len--; - copy = xstrndup(p, len); - colour = colour_byname(copy); - free(copy); - } - log_debug("%s: %s = %s", __func__, p, colour_tostring(colour)); - return (colour); -} - /* Reply to a colour request. */ static void input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c) @@ -2545,7 +2505,7 @@ input_osc_4(struct input_ctx *ictx, const char *p) input_osc_colour_reply(ictx, 4, c); continue; } - if ((c = input_osc_parse_colour(s)) == -1) { + if ((c = colour_parseX11(s)) == -1) { s = next; continue; } @@ -2601,6 +2561,47 @@ bad: free(id); } +/* + * Get a client with a foreground for the pane. There isn't much to choose + * between them so just use the first. + */ +static int +input_get_fg_client(struct window_pane *wp) +{ + struct window *w = wp->window; + struct client *loop; + + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->flags & CLIENT_UNATTACHEDFLAGS) + continue; + if (loop->session == NULL || !session_has(loop->session, w)) + continue; + if (loop->tty.fg == -1) + continue; + return (loop->tty.fg); + } + return (-1); +} + +/* Get a client with a background for the pane. */ +static int +input_get_bg_client(struct window_pane *wp) +{ + struct window *w = wp->window; + struct client *loop; + + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->flags & CLIENT_UNATTACHEDFLAGS) + continue; + if (loop->session == NULL || !session_has(loop->session, w)) + continue; + if (loop->tty.bg == -1) + continue; + return (loop->tty.bg); + } + return (-1); +} + /* Handle the OSC 10 sequence for setting and querying foreground colour. */ static void input_osc_10(struct input_ctx *ictx, const char *p) @@ -2610,14 +2611,18 @@ input_osc_10(struct input_ctx *ictx, const char *p) int c; if (strcmp(p, "?") == 0) { - if (wp != NULL) { - tty_default_colours(&defaults, wp); - input_osc_colour_reply(ictx, 10, defaults.fg); - } + if (wp == NULL) + return; + tty_default_colours(&defaults, wp); + if (COLOUR_DEFAULT(defaults.fg)) + c = input_get_fg_client(wp); + else + c = defaults.fg; + input_osc_colour_reply(ictx, 10, c); return; } - if ((c = input_osc_parse_colour(p)) == -1) { + if ((c = colour_parseX11(p)) == -1) { log_debug("bad OSC 10: %s", p); return; } @@ -2654,14 +2659,18 @@ input_osc_11(struct input_ctx *ictx, const char *p) int c; if (strcmp(p, "?") == 0) { - if (wp != NULL) { - tty_default_colours(&defaults, wp); - input_osc_colour_reply(ictx, 11, defaults.bg); - } + if (wp == NULL) + return; + tty_default_colours(&defaults, wp); + if (COLOUR_DEFAULT(defaults.bg)) + c = input_get_bg_client(wp); + else + c = defaults.bg; + input_osc_colour_reply(ictx, 11, c); return; } - if ((c = input_osc_parse_colour(p)) == -1) { + if ((c = colour_parseX11(p)) == -1) { log_debug("bad OSC 11: %s", p); return; } @@ -2706,7 +2715,7 @@ input_osc_12(struct input_ctx *ictx, const char *p) return; } - if ((c = input_osc_parse_colour(p)) == -1) { + if ((c = colour_parseX11(p)) == -1) { log_debug("bad OSC 12: %s", p); return; } diff --git a/tmux.h b/tmux.h index dac23ebb..7d7a7609 100644 --- a/tmux.h +++ b/tmux.h @@ -1380,6 +1380,8 @@ struct tty { u_int osy; int mode; + int fg; + int bg; u_int rlower; u_int rupper; @@ -1411,6 +1413,10 @@ struct tty { #define TTY_HAVEXDA 0x200 #define TTY_SYNCING 0x400 #define TTY_HAVEDA2 0x800 /* Secondary DA. */ +#define TTY_HAVEFG 0x1000 +#define TTY_HAVEBG 0x2000 +#define TTY_ALL_REQUEST_FLAGS \ + (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA|TTY_HAVEFG|TTY_HAVEBG) int flags; struct tty_term *term; @@ -2759,6 +2765,7 @@ int colour_fromstring(const char *s); int colour_256toRGB(int); int colour_256to16(int); int colour_byname(const char *); +int colour_parseX11(const char *); void colour_palette_init(struct colour_palette *); void colour_palette_clear(struct colour_palette *); void colour_palette_free(struct colour_palette *); diff --git a/tty-keys.c b/tty-keys.c index 6fe121f0..87c7afd8 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -59,6 +59,7 @@ static int tty_keys_device_attributes2(struct tty *, const char *, size_t, size_t *); static int tty_keys_extended_device_attributes(struct tty *, const char *, size_t, size_t *); +static int tty_keys_colours(struct tty *, const char *, size_t, size_t *); /* A key tree entry. */ struct tty_key { @@ -719,6 +720,17 @@ tty_keys_next(struct tty *tty) goto partial_key; } + /* Is this a colours response? */ + switch (tty_keys_colours(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 */ @@ -1278,7 +1290,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, if (len == 3) return (1); - /* Copy the rest up to a 'c'. */ + /* Copy the rest up to a c. */ for (i = 0; i < (sizeof tmp); i++) { if (3 + i == len) return (1); @@ -1352,7 +1364,7 @@ tty_keys_device_attributes2(struct tty *tty, const char *buf, size_t len, if (len == 3) return (1); - /* Copy the rest up to a 'c'. */ + /* Copy the rest up to a c. */ for (i = 0; i < (sizeof tmp); i++) { if (3 + i == len) return (1); @@ -1433,7 +1445,7 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, if (len == 4) return (1); - /* Copy the rest up to a '\033\\'. */ + /* Copy the rest up to \033\. */ for (i = 0; i < (sizeof tmp) - 1; i++) { if (4 + i == len) return (1); @@ -1465,3 +1477,68 @@ tty_keys_extended_device_attributes(struct tty *tty, const char *buf, return (0); } + +/* + * Handle foreground or background input. Returns 0 for success, -1 for + * failure, 1 for partial. + */ +static int +tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size) +{ + struct client *c = tty->client; + u_int i; + char tmp[128]; + int n; + + *size = 0; + if ((tty->flags & TTY_HAVEFG) && (tty->flags & TTY_HAVEBG)) + return (-1); + + /* First four bytes are always \033]1 and 0 or 1 and ;. */ + if (buf[0] != '\033') + return (-1); + if (len == 1) + return (1); + if (buf[1] != ']') + return (-1); + if (len == 2) + return (1); + if (buf[2] != '1') + return (-1); + if (len == 3) + return (1); + if (buf[3] != '0' && buf[3] != '1') + return (-1); + if (len == 4) + return (1); + if (buf[4] != ';') + return (-1); + if (len == 5) + return (1); + + /* Copy the rest up to \033\. */ + for (i = 0; i < (sizeof tmp) - 1; i++) { + if (5 + i == len) + return (1); + if (buf[5 + i - 1] == '\033' && buf[5 + i] == '\\') + break; + tmp[i] = buf[5 + i]; + } + if (i == (sizeof tmp) - 1) + return (-1); + tmp[i - 1] = '\0'; + *size = 6 + i; + + n = colour_parseX11(tmp); + if (n != -1 && buf[3] == '0') { + log_debug("%s: foreground is %s", c->name, colour_tostring(n)); + tty->fg = n; + tty->flags |= TTY_HAVEFG; + } else if (n != -1) { + log_debug("%s: background is %s", c->name, colour_tostring(n)); + tty->bg = n; + tty->flags |= TTY_HAVEBG; + } + + return (0); +} diff --git a/tty.c b/tty.c index 43a2961f..d31a2cab 100644 --- a/tty.c +++ b/tty.c @@ -108,6 +108,7 @@ tty_init(struct tty *tty, struct client *c) tty->cstyle = SCREEN_CURSOR_DEFAULT; tty->ccolour = -1; + tty->fg = tty->bg = -1; if (tcgetattr(c->fd, &tty->tio) != 0) return (-1); @@ -286,7 +287,6 @@ tty_open(struct tty *tty, char **cause) evtimer_set(&tty->timer, tty_timer_callback, tty); tty_start_tty(tty); - tty_keys_build(tty); return (0); @@ -301,7 +301,7 @@ tty_start_timer_callback(__unused int fd, __unused short events, void *data) log_debug("%s: start timer fired", c->name); if ((tty->flags & (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)) == 0) tty_update_features(tty); - tty->flags |= (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA); + tty->flags |= TTY_ALL_REQUEST_FLAGS; } void @@ -369,8 +369,12 @@ tty_send_requests(struct tty *tty) tty_puts(tty, "\033[>c"); if (~tty->flags & TTY_HAVEXDA) tty_puts(tty, "\033[>q"); + if (~tty->flags & TTY_HAVEFG) + tty_puts(tty, "\033]10;?\033\\"); + if (~tty->flags & TTY_HAVEBG) + tty_puts(tty, "\033]11;?\033\\"); } else - tty->flags |= (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA); + tty->flags |= TTY_ALL_REQUEST_FLAGS; } void -- cgit From 09afc6c8ee971918d925c441c41a9de7f598efb7 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 6 Jan 2023 07:09:27 +0000 Subject: If a pane is killed, cancel reading from the file. GitHub issue 3422. --- client.c | 3 +++ file.c | 44 ++++++++++++++++++++++++++++++++++++++++---- tmux-protocol.h | 7 ++++++- tmux.h | 5 ++++- window.c | 16 +++++++--------- 5 files changed, 60 insertions(+), 15 deletions(-) diff --git a/client.c b/client.c index ef7dea69..4f91d30e 100644 --- a/client.c +++ b/client.c @@ -693,6 +693,9 @@ client_dispatch_wait(struct imsg *imsg) !(client_flags & CLIENT_CONTROL), client_file_check_cb, NULL); break; + case MSG_READ_CANCEL: + file_read_cancel(&client_files, imsg); + break; case MSG_WRITE_OPEN: file_write_open(&client_files, client_peer, imsg, 1, !(client_flags & CLIENT_CONTROL), client_file_check_cb, diff --git a/file.c b/file.c index 280f3547..6c83caac 100644 --- a/file.c +++ b/file.c @@ -152,7 +152,8 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg) struct client_file *cf = arg; struct client *c = cf->c; - if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD))) + if (cf->cb != NULL && + (cf->closed || c == NULL || (~c->flags & CLIENT_DEAD))) cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data); file_free(cf); } @@ -355,7 +356,7 @@ done: } /* Read a file. */ -void +struct client_file * file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata) { struct client_file *cf; @@ -423,10 +424,27 @@ skip: goto done; } free(msg); - return; + return cf; done: file_fire_done(cf); + return NULL; +} + +/* Cancel a file read. */ +void +file_cancel(struct client_file *cf) +{ + struct msg_read_cancel msg; + + log_debug("read cancel file %d", cf->stream); + + if (cf->closed) + return; + cf->closed = 1; + + msg.stream = cf->stream; + proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg); } /* Push event, fired if there is more writing to be done. */ @@ -760,6 +778,24 @@ reply: proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply); } +/* Handle a read cancel message (client). */ +void +file_read_cancel(struct client_files *files, struct imsg *imsg) +{ + struct msg_read_cancel *msg = imsg->data; + size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; + struct client_file find, *cf; + + if (msglen != sizeof *msg) + fatalx("bad MSG_READ_CANCEL size"); + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, files, &find)) == NULL) + fatalx("unknown stream number"); + log_debug("cancel file %d", cf->stream); + + file_read_error_callback(NULL, 0, cf); +} + /* Handle a write ready message (server). */ void file_write_ready(struct client_files *files, struct imsg *imsg) @@ -797,7 +833,7 @@ file_read_data(struct client_files *files, struct imsg *imsg) return; log_debug("file %d read %zu bytes", cf->stream, bsize); - if (cf->error == 0) { + if (cf->error == 0 && !cf->closed) { if (evbuffer_add(cf->buffer, bdata, bsize) != 0) { cf->error = ENOMEM; file_fire_done(cf); diff --git a/tmux-protocol.h b/tmux-protocol.h index 08422291..3cf00c09 100644 --- a/tmux-protocol.h +++ b/tmux-protocol.h @@ -66,7 +66,8 @@ enum msgtype { MSG_WRITE_OPEN, MSG_WRITE, MSG_WRITE_READY, - MSG_WRITE_CLOSE + MSG_WRITE_CLOSE, + MSG_READ_CANCEL }; /* @@ -92,6 +93,10 @@ struct msg_read_done { int error; }; +struct msg_read_cancel { + int stream; +}; + struct msg_write_open { int stream; int fd; diff --git a/tmux.h b/tmux.h index 7d7a7609..bb33b312 100644 --- a/tmux.h +++ b/tmux.h @@ -2611,7 +2611,9 @@ void file_print_buffer(struct client *, void *, size_t); void printflike(2, 3) file_error(struct client *, const char *, ...); void file_write(struct client *, const char *, int, const void *, size_t, client_file_cb, void *); -void file_read(struct client *, const char *, client_file_cb, void *); +struct client_file *file_read(struct client *, const char *, client_file_cb, + void *); +void file_cancel(struct client_file *); void file_push(struct client_file *); int file_write_left(struct client_files *); void file_write_open(struct client_files *, struct tmuxpeer *, @@ -2623,6 +2625,7 @@ void file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *, void file_write_ready(struct client_files *, struct imsg *); void file_read_data(struct client_files *, struct imsg *); void file_read_done(struct client_files *, struct imsg *); +void file_read_cancel(struct client_files *, struct imsg *); /* server.c */ extern struct tmuxproc *server_proc; diff --git a/window.c b/window.c index 1b0066c2..4929383e 100644 --- a/window.c +++ b/window.c @@ -66,6 +66,7 @@ static u_int next_active_point; struct window_pane_input_data { struct cmdq_item *item; u_int wp; + struct client_file *file; }; static struct window_pane *window_pane_create(struct window *, u_int, u_int, @@ -1533,18 +1534,16 @@ window_pane_input_callback(struct client *c, __unused const char *path, size_t len = EVBUFFER_LENGTH(buffer); wp = window_pane_find_by_id(cdata->wp); - if (wp == NULL || closed || error != 0 || (c->flags & CLIENT_DEAD)) { + if (cdata->file != NULL && (wp == NULL || c->flags & CLIENT_DEAD)) { if (wp == NULL) c->flags |= CLIENT_EXIT; - - evbuffer_drain(buffer, len); + file_cancel(cdata->file); + } else if (cdata->file == NULL || closed || error != 0) { cmdq_continue(cdata->item); - server_client_unref(c); free(cdata); - return; - } - input_parse_buffer(wp, buf, len); + } else + input_parse_buffer(wp, buf, len); evbuffer_drain(buffer, len); } @@ -1567,9 +1566,8 @@ window_pane_start_input(struct window_pane *wp, struct cmdq_item *item, cdata = xmalloc(sizeof *cdata); cdata->item = item; cdata->wp = wp->id; - + cdata->file = file_read(c, "-", window_pane_input_callback, cdata); c->references++; - file_read(c, "-", window_pane_input_callback, cdata); return (0); } -- cgit From 093fb53773ee9b8bd1130e667464cd1f99a16807 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 6 Jan 2023 11:38:41 +0000 Subject: Missing #endif. --- utf8.c | 1 + 1 file changed, 1 insertion(+) diff --git a/utf8.c b/utf8.c index 604be36f..05ab9cfe 100644 --- a/utf8.c +++ b/utf8.c @@ -233,6 +233,7 @@ utf8_width(struct utf8_data *ud, int *width) *width = utf8proc_wcwidth(wc); #else *width = wcwidth(wc); +#endif log_debug("UTF-8 %.*s %#x, wcwidth() %d", (int)ud->size, ud->data, (u_int)wc, *width); if (*width >= 0 && *width <= 0xff) -- cgit From cb51942669cef089b46cd2b6cdbd62405000c0e7 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 8 Jan 2023 21:00:01 +0000 Subject: Quotes are now required in select-layout example. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index c84d9e87..6a0bfe52 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1971,7 +1971,7 @@ For example: $ tmux list-windows 0: ksh [159x48] layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} -$ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} +$ tmux select-layout 'bb62,159x48,0,0{79x48,0,0,79x48,80,0}' .Ed .Pp .Nm -- cgit From 7ced0a03d2ff51274d5fa5fb6eeaa6f4aac9f2f4 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 8 Jan 2023 22:15:30 +0000 Subject: Restore code to handle wcwidth failure so that unknown codepoints still do the most likely right thing. GitHub issue 3427, patch based on an diff from Jesse Luehrs in GitHub issue 3003. --- utf8.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/utf8.c b/utf8.c index 03918cd2..3c6f88ff 100644 --- a/utf8.c +++ b/utf8.c @@ -226,9 +226,16 @@ utf8_width(struct utf8_data *ud, int *width) case 0: return (UTF8_ERROR); } + log_debug("UTF-8 %.*s is %08X", (int)ud->size, ud->data, (u_int)wc); *width = wcwidth(wc); - log_debug("UTF-8 %.*s %#x, wcwidth() %d", (int)ud->size, ud->data, - (u_int)wc, *width); + log_debug("wcwidth(%08X) returned %d", (u_int)wc, *width); + if (*width < 0) { + /* + * C1 control characters are nonprintable, so they are always + * zero width. + */ + *width = (wc >= 0x80 && wc <= 0x9f) ? 0 : 1; + } if (*width >= 0 && *width <= 0xff) return (UTF8_DONE); return (UTF8_ERROR); -- cgit From 2a32565e0c882c9e78ef9c7d52476c3574331f62 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 8 Jan 2023 22:15:38 +0000 Subject: Restore code to handle wcwidth failure so that unknown codepoints still do the most likely right thing. GitHub issue 3427, patch based on an diff from Jesse Luehrs in GitHub issue 3003. --- utf8.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/utf8.c b/utf8.c index 05ab9cfe..042ddf89 100644 --- a/utf8.c +++ b/utf8.c @@ -229,13 +229,21 @@ utf8_width(struct utf8_data *ud, int *width) case 0: return (UTF8_ERROR); } + log_debug("UTF-8 %.*s is %08X", (int)ud->size, ud->data, (u_int)wc); #ifdef HAVE_UTF8PROC *width = utf8proc_wcwidth(wc); + log_debug("utf8proc_wcwidth(%08X) returned %d", (u_int)wc, *width); #else *width = wcwidth(wc); + log_debug("wcwidth(%08X) returned %d", (u_int)wc, *width); + if (*width < 0) { + /* + * C1 control characters are nonprintable, so they are always + * zero width. + */ + *width = (wc >= 0x80 && wc <= 0x9f) ? 0 : 1; + } #endif - log_debug("UTF-8 %.*s %#x, wcwidth() %d", (int)ud->size, ud->data, - (u_int)wc, *width); if (*width >= 0 && *width <= 0xff) return (UTF8_DONE); return (UTF8_ERROR); -- cgit From 7c0789d2d2721b70e04fe6a589f644797d2b5e1f Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 8 Jan 2023 22:17:04 +0000 Subject: Have client return 1 if process is interrupted to an input pane. --- window.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/window.c b/window.c index 4929383e..0fd71c74 100644 --- a/window.c +++ b/window.c @@ -1535,8 +1535,10 @@ window_pane_input_callback(struct client *c, __unused const char *path, wp = window_pane_find_by_id(cdata->wp); if (cdata->file != NULL && (wp == NULL || c->flags & CLIENT_DEAD)) { - if (wp == NULL) + if (wp == NULL) { + c->retval = 1; c->flags |= CLIENT_EXIT; + } file_cancel(cdata->file); } else if (cdata->file == NULL || closed || error != 0) { cmdq_continue(cdata->item); -- cgit From 153ae758c92207d34d4fe6f41779cf1ce3213d59 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 8 Jan 2023 23:27:54 +0000 Subject: portable: fixup merge with utf8.c --- utf8.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/utf8.c b/utf8.c index 76645363..042ddf89 100644 --- a/utf8.c +++ b/utf8.c @@ -230,13 +230,10 @@ utf8_width(struct utf8_data *ud, int *width) return (UTF8_ERROR); } log_debug("UTF-8 %.*s is %08X", (int)ud->size, ud->data, (u_int)wc); -<<<<<<< HEAD #ifdef HAVE_UTF8PROC *width = utf8proc_wcwidth(wc); log_debug("utf8proc_wcwidth(%08X) returned %d", (u_int)wc, *width); #else -======= ->>>>>>> obsd-master *width = wcwidth(wc); log_debug("wcwidth(%08X) returned %d", (u_int)wc, *width); if (*width < 0) { @@ -246,10 +243,7 @@ utf8_width(struct utf8_data *ud, int *width) */ *width = (wc >= 0x80 && wc <= 0x9f) ? 0 : 1; } -<<<<<<< HEAD #endif -======= ->>>>>>> obsd-master if (*width >= 0 && *width <= 0xff) return (UTF8_DONE); return (UTF8_ERROR); -- cgit From 565de3f54b37a955c15ae26db40aaaa76f71d02e Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 8 Jan 2023 23:34:46 +0000 Subject: Fix parsing of optional arguments so that and accept a - starting an argument. --- arguments.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arguments.c b/arguments.c index 47ca17ce..326fad17 100644 --- a/arguments.c +++ b/arguments.c @@ -150,8 +150,6 @@ args_parse_flag_argument(struct args_value *values, u_int count, char **cause, xasprintf(cause, "-%c argument must be a string", flag); return (-1); } - if (argument->string[0] == '-') - argument = NULL; } if (argument == NULL) { if (optional_argument) { @@ -210,12 +208,12 @@ args_parse_flags(const struct args_parse *parse, struct args_value *values, xasprintf(cause, "unknown flag -%c", flag); return (-1); } - if (*++found != ':') { + if (found[1] != ':') { log_debug("%s: -%c", __func__, flag); args_set(args, flag, NULL, 0); continue; } - optional_argument = (*found == ':'); + optional_argument = (found[2] == ':'); return (args_parse_flag_argument(values, count, cause, args, i, string, flag, optional_argument)); } -- cgit From c0031f8b8581f7fc2d75cabade596be68f85aa81 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 9 Jan 2023 07:57:14 +0000 Subject: Accept \007 as terminator to OSC 10 or 11. --- tty-keys.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index 87c7afd8..d0199bb6 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1516,18 +1516,25 @@ tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size) if (len == 5) return (1); - /* Copy the rest up to \033\. */ + /* Copy the rest up to \033\ or \007. */ for (i = 0; i < (sizeof tmp) - 1; i++) { if (5 + i == len) return (1); if (buf[5 + i - 1] == '\033' && buf[5 + i] == '\\') break; + if (buf[5 + i] == '\007') + break; tmp[i] = buf[5 + i]; } if (i == (sizeof tmp) - 1) return (-1); - tmp[i - 1] = '\0'; - *size = 6 + i; + if (tmp[i] == '\007') { + *size = 5 + i; + tmp[i] = '\0'; + } else { + *size = 6 + i; + tmp[i - 1] = '\0'; + } n = colour_parseX11(tmp); if (n != -1 && buf[3] == '0') { -- cgit From b41892622de3a2383ff5b419364530f08223558e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 9 Jan 2023 14:12:41 +0000 Subject: Fix behaviour with \007 (used the wrong tree for last change). --- tty-keys.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index d0199bb6..5677f133 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1528,13 +1528,11 @@ tty_keys_colours(struct tty *tty, const char *buf, size_t len, size_t *size) } if (i == (sizeof tmp) - 1) return (-1); - if (tmp[i] == '\007') { - *size = 5 + i; - tmp[i] = '\0'; - } else { - *size = 6 + i; + if (tmp[i - 1] == '\033') tmp[i - 1] = '\0'; - } + else + tmp[i] = '\0'; + *size = 6 + i; n = colour_parseX11(tmp); if (n != -1 && buf[3] == '0') { -- cgit From 483cc77c1cbc6898fef143c8100945139c14a92c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Jan 2023 18:49:11 +0000 Subject: Have tmux recognise pasted texts wrapped in bracket paste sequences, rather than only forwarding them to the program inside. From Andrew Onyshchuk in GitHub issue 3431. --- input-keys.c | 3 +++ server-client.c | 24 ++++++++++++++++++++++++ tmux.h | 1 + tty.c | 12 ++++-------- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/input-keys.c b/input-keys.c index 93123b33..f3281b0e 100644 --- a/input-keys.c +++ b/input-keys.c @@ -497,6 +497,9 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) ike = input_key_get(key & ~KEYC_EXTENDED); if (ike != NULL) { log_debug("found key 0x%llx: \"%s\"", key, ike->data); + if ((key == KEYC_PASTE_START || key == KEYC_PASTE_END) && + (~s->mode & MODE_BRACKETPASTE)) + return (0); if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META)) input_key_write(__func__, bev, "\033", 1); input_key_write(__func__, bev, ike->data, strlen(ike->data)); diff --git a/server-client.c b/server-client.c index 4109c1df..cb48d001 100644 --- a/server-client.c +++ b/server-client.c @@ -45,6 +45,7 @@ static void server_client_check_modes(struct client *); static void server_client_set_title(struct client *); static void server_client_set_path(struct client *); static void server_client_reset_state(struct client *); +static int server_client_is_bracket_pasting(struct client *, key_code); static int server_client_assume_paste(struct session *); static void server_client_update_latest(struct client *); @@ -1757,6 +1758,25 @@ out: return (key); } +/* Is this a bracket paste key? */ +static int +server_client_is_bracket_pasting(struct client *c, key_code key) +{ + if (key == KEYC_PASTE_START) { + c->flags |= CLIENT_BRACKETPASTING; + log_debug("%s: bracket paste on", c->name); + return (1); + } + + if (key == KEYC_PASTE_END) { + c->flags &= ~CLIENT_BRACKETPASTING; + log_debug("%s: bracket paste off", c->name); + return (1); + } + + return !!(c->flags & CLIENT_BRACKETPASTING); +} + /* Is this fast enough to probably be a paste? */ static int server_client_assume_paste(struct session *s) @@ -1865,6 +1885,10 @@ server_client_key_callback(struct cmdq_item *item, void *data) if (KEYC_IS_MOUSE(key) && !options_get_number(s->options, "mouse")) goto forward_key; + /* Forward if bracket pasting. */ + if (server_client_is_bracket_pasting(c, key)) + goto forward_key; + /* Treat everything as a regular key when pasting is detected. */ if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s)) goto forward_key; diff --git a/tmux.h b/tmux.h index bb33b312..4df99d01 100644 --- a/tmux.h +++ b/tmux.h @@ -1811,6 +1811,7 @@ struct client { #define CLIENT_CONTROL_WAITEXIT 0x200000000ULL #define CLIENT_WINDOWSIZECHANGED 0x400000000ULL #define CLIENT_CLIPBOARDBUFFER 0x800000000ULL +#define CLIENT_BRACKETPASTING 0x1000000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ diff --git a/tty.c b/tty.c index d31a2cab..c8a2a6ee 100644 --- a/tty.c +++ b/tty.c @@ -341,6 +341,8 @@ tty_start_tty(struct tty *tty) tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l"); tty_puts(tty, "\033[?1006l\033[?1005l"); } + if (tty_term_has(tty->term, TTYC_ENBP)) + tty_putcode(tty, TTYC_ENBP); evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); evtimer_add(&tty->start_timer, &tv); @@ -417,8 +419,6 @@ tty_stop_tty(struct tty *tty) else if (tty_term_has(tty->term, TTYC_SS)) tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0)); } - if (tty->mode & MODE_BRACKETPASTE) - tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP)); if (tty->ccolour != -1) tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); @@ -427,6 +427,8 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l"); tty_raw(tty, "\033[?1006l\033[?1005l"); } + if (tty_term_has(tty->term, TTYC_DSBP)) + tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP)); if (tty->term->flags & TERM_VT100LIKE) tty_raw(tty, "\033[?7727l"); @@ -825,12 +827,6 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) else if (mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000h"); } - if (changed & MODE_BRACKETPASTE) { - if (mode & MODE_BRACKETPASTE) - tty_putcode(tty, TTYC_ENBP); - else - tty_putcode(tty, TTYC_DSBP); - } tty->mode = mode; } -- cgit From eb1f8d70a7d1fe4b0fe604d5a36fcbc2babef249 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 16 Jan 2023 11:26:14 +0000 Subject: Mark keys sent by command and skip paste handling for them. --- cmd-send-keys.c | 2 +- key-string.c | 2 ++ server-client.c | 4 +++- tmux.h | 11 ++++++----- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index a7c5ee10..ac99a6fd 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -74,7 +74,7 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, if (tc == NULL) return (item); event = xmalloc(sizeof *event); - event->key = key; + event->key = key|KEYC_SENT; memset(&event->m, 0, sizeof event->m); if (server_client_handle_key(tc, event) == 0) free(event); diff --git a/key-string.c b/key-string.c index 086c3ac4..699d460f 100644 --- a/key-string.c +++ b/key-string.c @@ -462,6 +462,8 @@ out: strlcat(out, "B", sizeof out); if (saved & KEYC_EXTENDED) strlcat(out, "E", sizeof out); + if (saved & KEYC_SENT) + strlcat(out, "S", sizeof out); strlcat(out, "]", sizeof out); } return (out); diff --git a/server-client.c b/server-client.c index cb48d001..f0d1d538 100644 --- a/server-client.c +++ b/server-client.c @@ -1890,7 +1890,9 @@ server_client_key_callback(struct cmdq_item *item, void *data) goto forward_key; /* Treat everything as a regular key when pasting is detected. */ - if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s)) + if (!KEYC_IS_MOUSE(key) && + (~key & KEYC_SENT) && + server_client_assume_paste(s)) goto forward_key; /* diff --git a/tmux.h b/tmux.h index 4df99d01..718a067b 100644 --- a/tmux.h +++ b/tmux.h @@ -131,13 +131,14 @@ struct winlink; #define KEYC_SHIFT 0x00400000000000ULL /* Key flag bits. */ -#define KEYC_LITERAL 0x01000000000000ULL -#define KEYC_KEYPAD 0x02000000000000ULL -#define KEYC_CURSOR 0x04000000000000ULL +#define KEYC_LITERAL 0x01000000000000ULL +#define KEYC_KEYPAD 0x02000000000000ULL +#define KEYC_CURSOR 0x04000000000000ULL #define KEYC_IMPLIED_META 0x08000000000000ULL #define KEYC_BUILD_MODIFIERS 0x10000000000000ULL -#define KEYC_VI 0x20000000000000ULL -#define KEYC_EXTENDED 0x40000000000000ULL +#define KEYC_VI 0x20000000000000ULL +#define KEYC_EXTENDED 0x40000000000000ULL +#define KEYC_SENT 0x80000000000000ULL /* Masks for key bits. */ #define KEYC_MASK_MODIFIERS 0x00f00000000000ULL -- cgit From d578cf8d3fa1e42ab14b9ec9499f31c6b9c6f384 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Jan 2023 06:50:55 +0000 Subject: Update palette when moving a pane, GitHub issue 3437. --- cmd-break-pane.c | 1 + cmd-join-pane.c | 1 + cmd-swap-pane.c | 2 ++ 3 files changed, 4 insertions(+) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 4f38d4bd..9c4b1508 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -115,6 +115,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) layout_init(w, wp); wp->flags |= PANE_CHANGED; + colour_palette_from_option(&wp->palette, wp->options); if (idx == -1) idx = -1 - options_get_number(dst_s->options, "base-index"); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index fbe9eff2..627424ec 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -156,6 +156,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) else TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); layout_assign_pane(lc, src_wp, 0); + colour_palette_from_option(&src_wp->palette, src_wp->options); recalculate_sizes(); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 4191b894..80c20c80 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -132,6 +132,8 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) src_w->last = NULL; if (dst_w->last == dst_wp) dst_w->last = NULL; + colour_palette_from_option(&src_wp->palette, src_wp->options); + colour_palette_from_option(&dst_wp->palette, dst_wp->options); } server_redraw_window(src_w); server_redraw_window(dst_w); -- cgit From 9789ea3fb4b3215e48b3f0024e2c21c50f95edec Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 17 Jan 2023 10:40:51 +0000 Subject: Support -1 without -N for list-keys. --- cmd-list-keys.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index ae9f995c..395b147c 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -148,6 +148,7 @@ static enum cmd_retval cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); struct key_table *table; struct key_binding *bd; const char *tablename, *r, *keystr; @@ -296,9 +297,15 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) strlcat(tmp, cp, tmpsize); free(cp); - cmdq_print(item, "bind-key %s", tmp); - + if (args_has(args, '1') && tc != NULL) { + status_message_set(tc, -1, 1, 0, "bind-key %s", + tmp); + } else + cmdq_print(item, "bind-key %s", tmp); free(key); + + if (args_has(args, '1')) + break; bd = key_bindings_next(table, bd); } table = key_bindings_next_table(table); -- cgit From 3aa458ea6398b0de37c3e146304bd2a4e17ea3c0 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Jan 2023 21:36:00 +0000 Subject: Add a flag to display-menu to select the manu item chosen first, GitHub issue 3442. --- cmd-display-menu.c | 29 ++++++++++++++++++++------- menu.c | 57 +++++++++++++++++++++++++++++++++++++----------------- mode-tree.c | 4 ++-- popup.c | 2 +- status.c | 4 ++-- tmux.1 | 4 ++++ tmux.h | 8 ++++---- 7 files changed, 74 insertions(+), 34 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 6ecfad29..deff4907 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -39,9 +39,10 @@ const struct cmd_entry cmd_display_menu_entry = { .name = "display-menu", .alias = "menu", - .args = { "c:t:OT:x:y:", 1, -1, cmd_display_menu_args_parse }, - .usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] " - "[-x position] [-y position] name key command ...", + .args = { "c:t:S:OT:x:y:", 1, -1, cmd_display_menu_args_parse }, + .usage = "[-O] [-c target-client] [-S starting-choice] " + CMD_TARGET_PANE_USAGE " [-T title] [-x position] " + "[-y position] name key command ...", .target = { 't', CMD_FIND_PANE, 0 }, @@ -288,13 +289,27 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) struct menu *menu = NULL; struct menu_item menu_item; const char *key, *name; - char *title; - int flags = 0; + char *title, *cause; + int flags = 0, starting_choice = 0; u_int px, py, i, count = args_count(args); if (tc->overlay_draw != NULL) return (CMD_RETURN_NORMAL); + if (args_has(args, 'S')) { + if (strcmp(args_get(args, 'S'), "-") == 0) + starting_choice = -1; + else { + starting_choice = args_strtonum(args, 'S', 0, UINT_MAX, + &cause); + if (cause != NULL) { + cmdq_error(item, "starting choice %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + } + } + if (args_has(args, 'T')) title = format_single_from_target(item, args_get(args, 'T')); else @@ -341,8 +356,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) flags |= MENU_STAYOPEN; if (!event->m.valid) flags |= MENU_NOMOUSE; - if (menu_display(menu, flags, item, px, py, tc, target, NULL, - NULL) != 0) + if (menu_display(menu, flags, starting_choice, item, px, py, tc, target, + NULL, NULL) != 0) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } diff --git a/menu.c b/menu.c index 4aad1d8c..0ff180aa 100644 --- a/menu.c +++ b/menu.c @@ -427,12 +427,12 @@ chosen: } struct menu_data * -menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px, - u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, - void *data) +menu_prepare(struct menu *menu, int flags, int starting_choice, + struct cmdq_item *item, u_int px, u_int py, struct client *c, + struct cmd_find_state *fs, menu_choice_cb cb, void *data) { struct menu_data *md; - u_int i; + int choice; const char *name; if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) @@ -457,18 +457,38 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px, md->py = py; md->menu = menu; + md->choice = -1; + if (md->flags & MENU_NOMOUSE) { - for (i = 0; i < menu->count; i++) { - name = menu->items[i].name; - if (name != NULL && *name != '-') - break; + if (starting_choice >= (int)menu->count) { + starting_choice = menu->count - 1; + choice = starting_choice + 1; + for (;;) { + name = menu->items[choice - 1].name; + if (name != NULL && *name != '-') { + md->choice = choice - 1; + break; + } + if (--choice == 0) + choice = menu->count; + if (choice == starting_choice + 1) + break; + } + } else if (starting_choice >= 0) { + choice = starting_choice; + for (;;) { + name = menu->items[choice].name; + if (name != NULL && *name != '-') { + md->choice = choice; + break; + } + if (++choice == (int)menu->count) + choice = 0; + if (choice == starting_choice) + break; + } } - if (i != menu->count) - md->choice = i; - else - md->choice = -1; - } else - md->choice = -1; + } md->cb = cb; md->data = data; @@ -476,13 +496,14 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px, } int -menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, - u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, - void *data) +menu_display(struct menu *menu, int flags, int starting_choice, + struct cmdq_item *item, u_int px, u_int py, struct client *c, + struct cmd_find_state *fs, menu_choice_cb cb, void *data) { struct menu_data *md; - md = menu_prepare(menu, flags, item, px, py, c, fs, cb, data); + md = menu_prepare(menu, flags, starting_choice, item, px, py, c, fs, cb, + data); if (md == NULL) return (-1); server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, diff --git a/mode-tree.c b/mode-tree.c index c007e27f..9d465e7b 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -962,8 +962,8 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x, x -= (menu->width + 4) / 2; else x = 0; - if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback, - mtm) != 0) + if (menu_display(menu, 0, 0, NULL, x, y, c, NULL, + mode_tree_menu_callback, mtm) != 0) menu_free(menu); } diff --git a/popup.c b/popup.c index 12f31c40..250888a8 100644 --- a/popup.c +++ b/popup.c @@ -574,7 +574,7 @@ menu: x = m->x - (pd->menu->width + 4) / 2; else x = 0; - pd->md = menu_prepare(pd->menu, 0, NULL, x, m->y, c, NULL, + pd->md = menu_prepare(pd->menu, 0, 0, NULL, x, m->y, c, NULL, popup_menu_done, pd); c->flags |= CLIENT_REDRAWOVERLAY; diff --git a/status.c b/status.c index b504bbbe..08952f58 100644 --- a/status.c +++ b/status.c @@ -1766,7 +1766,7 @@ status_prompt_complete_list_menu(struct client *c, char **list, u_int size, else offset = 0; - if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset, + if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, 0, NULL, offset, py, c, NULL, status_prompt_menu_callback, spm) != 0) { menu_free(menu); free(spm); @@ -1859,7 +1859,7 @@ status_prompt_complete_window_menu(struct client *c, struct session *s, else offset = 0; - if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset, + if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, 0, NULL, offset, py, c, NULL, status_prompt_menu_callback, spm) != 0) { menu_free(menu); free(spm); diff --git a/tmux.1 b/tmux.1 index 6a0bfe52..c75afd24 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5815,6 +5815,7 @@ until it is dismissed. .Op Fl O .Op Fl c Ar target-client .Op Fl t Ar target-pane +.Op Fl S Ar starting-choice .Op Fl T Ar title .Op Fl x Ar position .Op Fl y Ar position @@ -5844,6 +5845,9 @@ command should be omitted. .Fl T is a format for the menu title (see .Sx FORMATS ) . +.Fl S +sets the menu item selected by default, if the menu is not bound to a mouse key +binding. .Pp .Fl x and diff --git a/tmux.h b/tmux.h index 718a067b..3b1a4fc9 100644 --- a/tmux.h +++ b/tmux.h @@ -3292,11 +3292,11 @@ void menu_add_item(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, struct cmd_find_state *); void menu_free(struct menu *); -struct menu_data *menu_prepare(struct menu *, int, struct cmdq_item *, u_int, - u_int, struct client *, struct cmd_find_state *, +struct menu_data *menu_prepare(struct menu *, int, int, struct cmdq_item *, + u_int, u_int, struct client *, struct cmd_find_state *, menu_choice_cb, void *); -int menu_display(struct menu *, int, struct cmdq_item *, u_int, - u_int, struct client *, struct cmd_find_state *, +int menu_display(struct menu *, int, int, struct cmdq_item *, + u_int, u_int, struct client *, struct cmd_find_state *, menu_choice_cb, void *); struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *); void menu_check_cb(struct client *, void *, u_int, u_int, u_int, -- cgit From e7e112fbd0263bb8661d0cf931d66c1b57e7fd3b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Jan 2023 09:33:51 +0000 Subject: Too many \s in example, GitHub issue 3445. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index c75afd24..fdbc97cc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -542,7 +542,7 @@ for example in these .Xr sh 1 commands: .Bd -literal -offset indent -$ tmux neww\e\e; splitw +$ tmux neww\e; splitw .Ed .Pp Or: -- cgit