From d329b035cee47d968a8c93b5cbd8fde879ce6f0d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 24 Jul 2020 07:05:37 +0000 Subject: [PATCH 01/61] Add a hook when the pane title changed. --- cmd-select-pane.c | 1 + input.c | 3 +++ options-table.c | 1 + 3 files changed, 5 insertions(+) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index b0c78d74..30529722 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -198,6 +198,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'T')) { title = format_single_from_target(item, args_get(args, 'T')); if (screen_set_title(&wp->base, title)) { + notify_pane("pane-title-changed", wp); server_redraw_window_borders(wp->window); server_status_window(wp->window); } diff --git a/input.c b/input.c index a3850371..b1b8bf94 100644 --- a/input.c +++ b/input.c @@ -1867,6 +1867,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx) case 2: screen_pop_title(sctx->s); if (wp != NULL) { + notify_pane("pane-title-changed", wp); server_redraw_window_borders(wp->window); server_status_window(wp->window); } @@ -2261,6 +2262,7 @@ input_exit_osc(struct input_ctx *ictx) case 0: case 2: if (screen_set_title(sctx->s, p) && wp != NULL) { + notify_pane("pane-title-changed", wp); server_redraw_window_borders(wp->window); server_status_window(wp->window); } @@ -2326,6 +2328,7 @@ input_exit_apc(struct input_ctx *ictx) log_debug("%s: \"%s\"", __func__, ictx->input_buf); if (screen_set_title(sctx->s, ictx->input_buf) && wp != NULL) { + notify_pane("pane-title-changed", wp); server_redraw_window_borders(wp->window); server_status_window(wp->window); } diff --git a/options-table.c b/options-table.c index 54bdadba..95d865ce 100644 --- a/options-table.c +++ b/options-table.c @@ -1121,6 +1121,7 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_PANE_HOOK("pane-focus-out", ""), OPTIONS_TABLE_PANE_HOOK("pane-mode-changed", ""), OPTIONS_TABLE_PANE_HOOK("pane-set-clipboard", ""), + OPTIONS_TABLE_PANE_HOOK("pane-title-changed", ""), OPTIONS_TABLE_HOOK("session-closed", ""), OPTIONS_TABLE_HOOK("session-created", ""), OPTIONS_TABLE_HOOK("session-renamed", ""), From 40e65c511502fe47932e230290537e7391ab8a83 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Jul 2020 08:03:10 +0000 Subject: [PATCH 02/61] Add a -d option to display-message to set delay, from theonekeyg at gmail dot com in GitHub issue 2322. --- alerts.c | 4 ++-- cmd-display-message.c | 16 +++++++++++++--- cmd-list-keys.c | 2 +- cmd-queue.c | 2 +- mode-tree.c | 2 +- status.c | 12 +++++++++--- tmux.1 | 10 +++++++++- tmux.h | 3 +-- window-customize.c | 4 ++-- 9 files changed, 39 insertions(+), 16 deletions(-) diff --git a/alerts.c b/alerts.c index 9675934a..38e88801 100644 --- a/alerts.c +++ b/alerts.c @@ -316,9 +316,9 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option) if (visual == VISUAL_OFF) continue; if (c->session->curw == wl) - status_message_set(c, 1, "%s in current window", type); + status_message_set(c, -1, 1, "%s in current window", type); else { - status_message_set(c, 1, "%s in window %d", type, + status_message_set(c, -1, 1, "%s in window %d", type, wl->idx); } } diff --git a/cmd-display-message.c b/cmd-display-message.c index 634f0a93..fc9c4851 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:Ipt:F:v", 0, 1 }, - .usage = "[-aIpv] [-c target-client] [-F format] " + .args = { "acd:Ipt:F:v", 0, 1 }, + .usage = "[-aIpv] [-c target-client] [-d delay] [-F format] " CMD_TARGET_PANE_USAGE " [message]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -68,6 +68,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) struct window_pane *wp = target->wp; const char *template; char *msg, *cause; + int delay = -1; struct format_tree *ft; int flags; @@ -85,6 +86,15 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } + if (args_has(args, 'd')) { + delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause); + if (cause != NULL) { + cmdq_error(item, "delay %s", cause); + free(cause); + return (CMD_RETURN_ERROR); + } + } + template = args_get(args, 'F'); if (args->argc != 0) template = args->argv[0]; @@ -117,7 +127,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'p')) cmdq_print(item, "%s", msg); else if (tc != NULL) - status_message_set(tc, 0, "%s", msg); + status_message_set(tc, delay, 0, "%s", msg); free(msg); format_free(ft); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index b3bdbd12..dd82e57e 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -114,7 +114,7 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, note = xstrdup(bd->note); tmp = utf8_padcstr(key, keywidth + 1); if (args_has(args, '1') && tc != NULL) - status_message_set(tc, 1, "%s%s%s", prefix, tmp, note); + status_message_set(tc, -1, 1, "%s%s%s", prefix, tmp, note); else cmdq_print(item, "%s%s%s", prefix, tmp, note); free(tmp); diff --git a/cmd-queue.c b/cmd-queue.c index 693f7d90..36f1c9be 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -858,7 +858,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) c->retval = 1; } else { *msg = toupper((u_char) *msg); - status_message_set(c, 1, "%s", msg); + status_message_set(c, -1, 1, "%s", msg); } free(msg); diff --git a/mode-tree.c b/mode-tree.c index c4b776f9..a47c0c06 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1176,7 +1176,7 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs, if (status == CMD_PARSE_ERROR) { if (c != NULL) { *error = toupper((u_char)*error); - status_message_set(c, 1, "%s", error); + status_message_set(c, -1, 1, "%s", error); } free(error); } diff --git a/status.c b/status.c index 7313d2a0..668c0a3b 100644 --- a/status.c +++ b/status.c @@ -423,11 +423,11 @@ status_redraw(struct client *c) /* Set a status line message. */ void -status_message_set(struct client *c, int ignore_styles, const char *fmt, ...) +status_message_set(struct client *c, int delay, int ignore_styles, + const char *fmt, ...) { struct timeval tv; va_list ap; - int delay; status_message_clear(c); status_push_screen(c); @@ -439,7 +439,12 @@ status_message_set(struct client *c, int ignore_styles, const char *fmt, ...) server_add_message("%s message: %s", c->name, c->message_string); - delay = options_get_number(c->session->options, "display-time"); + /* + * With delay -1, the display-time option is used; zero means wait for + * key press; more than zero is the actual delay time in milliseconds. + */ + if (delay == -1) + delay = options_get_number(c->session->options, "display-time"); if (delay > 0) { tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; @@ -447,6 +452,7 @@ status_message_set(struct client *c, int ignore_styles, const char *fmt, ...) if (event_initialized(&c->message_timer)) evtimer_del(&c->message_timer); evtimer_set(&c->message_timer, status_message_callback, c); + evtimer_add(&c->message_timer, &tv); } diff --git a/tmux.1 b/tmux.1 index 77cb3c56..cd20c056 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5378,6 +5378,7 @@ The following keys are also available: .It Xo Ic display-message .Op Fl aIpv .Op Fl c Ar target-client +.Op Fl d Ar delay .Op Fl t Ar target-pane .Op Ar message .Xc @@ -5387,7 +5388,14 @@ If .Fl p is given, the output is printed to stdout, otherwise it is displayed in the .Ar target-client -status line. +status line for up to +.Ar delay +milliseconds. +If +.Ar delay +is not given, the +.Ic message-time +option is used; a delay of zero waits for a key press. The format of .Ar message is described in the diff --git a/tmux.h b/tmux.h index a6749e99..daba4b25 100644 --- a/tmux.h +++ b/tmux.h @@ -2450,8 +2450,7 @@ struct style_range *status_get_range(struct client *, u_int, u_int); void status_init(struct client *); void status_free(struct client *); int status_redraw(struct client *); -void printflike(3, 4) status_message_set(struct client *, int, const char *, - ...); +void status_message_set(struct client *, int, int, const char *, ...); void status_message_clear(struct client *); int status_message_redraw(struct client *); void status_prompt_set(struct client *, struct cmd_find_state *, diff --git a/window-customize.c b/window-customize.c index ecafc776..c8606245 100644 --- a/window-customize.c +++ b/window-customize.c @@ -1003,7 +1003,7 @@ window_customize_set_option_callback(struct client *c, void *itemdata, fail: *cause = toupper((u_char)*cause); - status_message_set(c, 1, "%s", cause); + status_message_set(c, -1, 1, "%s", cause); free(cause); return (0); } @@ -1209,7 +1209,7 @@ window_customize_set_command_callback(struct client *c, void *itemdata, fail: *error = toupper((u_char)*error); - status_message_set(c, 1, "%s", error); + status_message_set(c, -1, 1, "%s", error); free(error); return (0); } From 944177eec3b0659a95b8484eb73e27201bffd112 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 30 Jul 2020 07:32:52 +0000 Subject: [PATCH 03/61] Trim newline from ctime, from Thomas Adam. --- server-fn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server-fn.c b/server-fn.c index 84ec4123..d2465569 100644 --- a/server-fn.c +++ b/server-fn.c @@ -341,6 +341,7 @@ server_destroy_pane(struct window_pane *wp, int notify) time(&t); ctime_r(&t, tim); + tim[strcspn(tim, "\n")] = '\0'; if (WIFEXITED(wp->status)) { screen_write_nputs(&ctx, -1, &gc, From 82c65e3f3705c2922e88049f9336333781a26e55 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 4 Aug 2020 08:50:01 +0000 Subject: [PATCH 04/61] Also ignore SIGQUIT so it can't be used to kill the client when locked. --- proc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/proc.c b/proc.c index ce3bc8af..c0e2f55b 100644 --- a/proc.c +++ b/proc.c @@ -226,6 +226,7 @@ proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int)) sigaction(SIGTSTP, &sa, NULL); sigaction(SIGTTIN, &sa, NULL); sigaction(SIGTTOU, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); signal_set(&tp->ev_sigint, SIGINT, proc_signal_cb, tp); signal_add(&tp->ev_sigint, NULL); From df7fbcd7a5a775586522380a70530721d1f2c151 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 5 Aug 2020 09:11:09 +0000 Subject: [PATCH 05/61] Change searching to behave more like emacs and so that regex searching doesn't overlap when searching forwards. --- server-client.c | 2 - window-copy.c | 119 ++++++++++++++++++++++++++++-------------------- 2 files changed, 69 insertions(+), 52 deletions(-) diff --git a/server-client.c b/server-client.c index 3a79a5d1..4010019d 100644 --- a/server-client.c +++ b/server-client.c @@ -1662,8 +1662,6 @@ server_client_reset_state(struct client *c) s = wp->screen; if (s != NULL) mode = s->mode; - if (c->prompt_string != NULL || c->message_string != NULL) - mode &= ~MODE_CURSOR; log_debug("%s: client %s mode %x", __func__, c->name, mode); /* Reset region and margin. */ diff --git a/window-copy.c b/window-copy.c index 7103131d..1f0f7e78 100644 --- a/window-copy.c +++ b/window-copy.c @@ -76,10 +76,10 @@ static void window_copy_move_right(struct screen *, u_int *, u_int *, int); static int window_copy_is_lowercase(const char *); static int window_copy_search_jump(struct window_mode_entry *, struct grid *, struct grid *, u_int, u_int, u_int, int, int, - int, int); -static int window_copy_search(struct window_mode_entry *, int, int); -static int window_copy_search_up(struct window_mode_entry *, int); -static int window_copy_search_down(struct window_mode_entry *, int); + int, int, u_int *); +static int window_copy_search(struct window_mode_entry *, int, int, int); +static int window_copy_search_up(struct window_mode_entry *, int, int); +static int window_copy_search_down(struct window_mode_entry *, int, int); static void window_copy_goto_line(struct window_mode_entry *, const char *); static void window_copy_update_cursor(struct window_mode_entry *, u_int, u_int); @@ -1685,10 +1685,10 @@ window_copy_cmd_search_again(struct window_copy_cmd_state *cs) if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) - window_copy_search_up(wme, data->searchregex); + window_copy_search_up(wme, data->searchregex, 1); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_down(wme, data->searchregex); + window_copy_search_down(wme, data->searchregex, 1); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1702,10 +1702,10 @@ window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) - window_copy_search_down(wme, data->searchregex); + window_copy_search_down(wme, data->searchregex, 1); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_up(wme, data->searchregex); + window_copy_search_up(wme, data->searchregex, 1); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1953,7 +1953,7 @@ window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) data->searchregex = 1; data->timeout = 0; for (; np != 0; np--) - window_copy_search_up(wme, 1); + window_copy_search_up(wme, 1, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1973,7 +1973,7 @@ window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs) data->searchregex = 0; data->timeout = 0; for (; np != 0; np--) - window_copy_search_up(wme, 0); + window_copy_search_up(wme, 0, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1993,7 +1993,7 @@ window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) data->searchregex = 1; data->timeout = 0; for (; np != 0; np--) - window_copy_search_down(wme, 1); + window_copy_search_down(wme, 1, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2013,7 +2013,7 @@ window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs) data->searchregex = 0; data->timeout = 0; for (; np != 0; np--) - window_copy_search_down(wme, 0); + window_copy_search_down(wme, 0, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2052,7 +2052,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_up(wme, 0)) { + if (!window_copy_search_up(wme, 0, 1)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2062,7 +2062,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_down(wme, 0)) { + if (!window_copy_search_down(wme, 0, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2105,7 +2105,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_down(wme, 0)) { + if (!window_copy_search_down(wme, 0, 1)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2115,7 +2115,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_up(wme, 0)) { + if (!window_copy_search_up(wme, 0, 1)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2406,8 +2406,8 @@ window_copy_search_compare(struct grid *gd, u_int px, u_int py, } static int -window_copy_search_lr(struct grid *gd, - struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) +window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py, + u_int first, u_int last, int cis) { u_int ax, bx, px, pywrap, endline; int matched; @@ -2854,7 +2854,7 @@ window_copy_is_lowercase(const char *ptr) static int window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap, - int direction, int regex) + int direction, int regex, u_int *foundlen) { u_int i, px, sx, ssize = 1; int found = 0, cflags = REG_EXTENDED; @@ -2878,15 +2878,20 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, if (regex) { found = window_copy_search_lr_regex(gd, &px, &sx, i, fx, gd->sx, ®); + if (found) + *foundlen = sx; } else { found = window_copy_search_lr(gd, sgd, &px, i, fx, gd->sx, cis); + if (found) + *foundlen = sgd->sx; } if (found) break; fx = 0; } } else { + *foundlen = 0; for (i = fy + 1; endline < i; i--) { if (regex) { found = window_copy_search_rl_regex(gd, @@ -2915,7 +2920,7 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, return (window_copy_search_jump(wme, gd, sgd, direction ? 0 : gd->sx - 1, direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0, - direction, regex)); + direction, regex, foundlen)); } return (0); } @@ -2925,7 +2930,7 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, * down. */ static int -window_copy_search(struct window_mode_entry *wme, int direction, int regex) +window_copy_search(struct window_mode_entry *wme, int direction, int regex, int again) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; @@ -2933,7 +2938,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) struct screen_write_ctx ctx; struct grid *gd = s->grid; const char *str = data->searchstr; - u_int fx, fy, endline; + u_int fx, fy, endline, i, foundlen; int wrapflag, cis, found, visible_only; if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') @@ -2961,18 +2966,23 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex) wrapflag = options_get_number(wp->window->options, "wrap-search"); cis = window_copy_is_lowercase(str); - if (direction) { - window_copy_move_right(s, &fx, &fy, wrapflag); + if (direction) endline = gd->hsize + gd->sy - 1; - } else { - window_copy_move_left(s, &fx, &fy, wrapflag); + else { + if (again) + window_copy_move_left(s, &fx, &fy, wrapflag); endline = 0; } found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, - wrapflag, direction, regex); - if (found) + wrapflag, direction, regex, &foundlen); + if (found) { window_copy_search_marks(wme, &ss, regex, visible_only); + if (foundlen != 0) { + for (i = 0; i < foundlen; i++) + window_copy_cursor_right(wme); + } + } window_copy_redraw_screen(wme); screen_free(&ss); @@ -2995,8 +3005,8 @@ window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start, } static int -window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px, u_int py, - u_int *at) +window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px, + u_int py, u_int *at) { struct screen *s = data->backing; struct grid *gd = s->grid; @@ -3096,7 +3106,7 @@ again: data->searchgen++; } - px++; + px += width; } t = get_timer(); @@ -3163,15 +3173,15 @@ window_copy_clear_marks(struct window_mode_entry *wme) } static int -window_copy_search_up(struct window_mode_entry *wme, int regex) +window_copy_search_up(struct window_mode_entry *wme, int regex, int again) { - return (window_copy_search(wme, 0, regex)); + return (window_copy_search(wme, 0, regex, again)); } static int -window_copy_search_down(struct window_mode_entry *wme, int regex) +window_copy_search_down(struct window_mode_entry *wme, int regex, int again) { - return (window_copy_search(wme, 1, regex)); + return (window_copy_search(wme, 1, regex, again)); } static void @@ -3256,7 +3266,7 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, { struct window_copy_mode_data *data = wme->data; u_int mark, start, end, cy, cursor, current; - int inv = 0; + int inv = 0, found = 0; if (data->showmark && fy == data->my) { gc->attr = mkgc->attr; @@ -3282,20 +3292,28 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, return; cy = screen_hsize(data->backing) - data->oy + data->cy; - if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0 && - data->searchmark[cursor] == mark) { - window_copy_match_start_end(data, cursor, &start, &end); - if (current >= start && current <= end) { - gc->attr = cgc->attr; - if (inv) { - gc->fg = cgc->bg; - gc->bg = cgc->fg; + if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) { + if (data->searchmark[cursor] == mark) + found = 1; + else if (cursor != 0) { + cursor--; + if (data->searchmark[cursor] == mark) + found = 1; + } + if (found) { + window_copy_match_start_end(data, cursor, &start, &end); + if (current >= start && current <= end) { + gc->attr = cgc->attr; + if (inv) { + gc->fg = cgc->bg; + gc->bg = cgc->fg; + } + else { + gc->fg = cgc->fg; + gc->bg = cgc->bg; + } + return; } - else { - gc->fg = cgc->fg; - gc->bg = cgc->bg; - } - return; } } @@ -3370,7 +3388,8 @@ window_copy_write_line(struct window_mode_entry *wme, } else if (data->searchthis == -1) { size = xsnprintf(hdr, sizeof hdr, "(%d%s results) [%u/%u]", data->searchcount, - data->searchmore ? "+" : "", data->oy, hsize); + data->searchmore ? "+" : "", data->oy, + hsize); } else { size = xsnprintf(hdr, sizeof hdr, "(%d/%d results) [%u/%u]", data->searchthis, From 212c0c1f7212b537aafc6f83d848ecf0b804696d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 7 Aug 2020 07:02:57 +0000 Subject: [PATCH 06/61] Do not force line width to grid width because it may need to be larger to accomodate a wide character. GitHub issue 2336. --- grid.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/grid.c b/grid.c index 5ea5bf62..96302fc3 100644 --- a/grid.c +++ b/grid.c @@ -463,7 +463,7 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg) sx = gd->sx / 4; else if (sx < gd->sx / 2) sx = gd->sx / 2; - else + else if (gd->sx > sx) sx = gd->sx; gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); @@ -1277,7 +1277,7 @@ grid_reflow(struct grid *gd, u_int sx) struct grid *target; struct grid_line *gl; struct grid_cell gc; - u_int yy, width, i, at, first; + u_int yy, width, i, at; /* * Create a destination grid. This is just used as a container for the @@ -1294,13 +1294,12 @@ grid_reflow(struct grid *gd, u_int sx) continue; /* - * Work out the width of this line. first is the width of the - * first character, at is the point at which the available - * width is hit, and width is the full line width. + * Work out the width of this line. at is the point at which + * the available width is hit, and width is the full line + * width. */ - first = at = width = 0; + at = width = 0; if (~gl->flags & GRID_LINE_EXTENDED) { - first = 1; width = gl->cellused; if (width > sx) at = sx; @@ -1309,8 +1308,6 @@ grid_reflow(struct grid *gd, u_int sx) } else { for (i = 0; i < gl->cellused; i++) { grid_get_cell1(gl, i, &gc); - if (i == 0) - first = gc.data.width; if (at == 0 && width + gc.data.width > sx) at = i; width += gc.data.width; @@ -1318,10 +1315,10 @@ grid_reflow(struct grid *gd, u_int sx) } /* - * If the line is exactly right or the first character is wider - * than the target width, just move it across unchanged. + * If the line is exactly right, just move it across + * unchanged. */ - if (width == sx || first > sx) { + if (width == sx) { grid_reflow_move(target, gl); continue; } From f08bfa7cd13f605396ac7e2810b8fa516f8e11bd Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 19 Aug 2020 06:37:23 +0000 Subject: [PATCH 07/61] Respond to colour requests if a colour is available, from Michal Goral. --- input.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/input.c b/input.c index b1b8bf94..42a60c92 100644 --- a/input.c +++ b/input.c @@ -65,7 +65,7 @@ struct input_param { INPUT_MISSING, INPUT_NUMBER, INPUT_STRING - } type; + } type; union { int num; char *str; @@ -81,7 +81,7 @@ struct input_ctx { struct input_cell cell; struct input_cell old_cell; - u_int old_cx; + u_int old_cx; u_int old_cy; int old_mode; @@ -121,7 +121,7 @@ struct input_ctx { * All input received since we were last in the ground state. Sent to * control clients on connection. */ - struct evbuffer *since_ground; + struct evbuffer *since_ground; }; /* Helper functions. */ @@ -2459,13 +2459,31 @@ input_osc_parse_colour(const char *p, u_int *r, u_int *g, u_int *b) return (1); } +/* Reply to a colour request. */ +static void +input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c) +{ + u_char r, g, b; + const char *end; + + if (c == 8 || (~c & COLOUR_FLAG_RGB)) + return; + colour_split_rgb(c, &r, &g, &b); + + if (ictx->input_end == INPUT_END_BEL) + end = "\007"; + else + end = "\033\\"; + input_reply(ictx, "\033]%u;rgb:%02hhx/%02hhx/%02hhx%s", n, r, g, b, end); +} + /* Handle the OSC 4 sequence for setting (multiple) palette entries. */ static void input_osc_4(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; char *copy, *s, *next = NULL; - long idx; + long idx; u_int r, g, b; if (wp == NULL) @@ -2497,17 +2515,22 @@ bad: free(copy); } -/* Handle the OSC 10 sequence for setting foreground colour. */ +/* Handle the OSC 10 sequence for setting and querying foreground colour. */ static void input_osc_10(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; + struct grid_cell defaults; u_int r, g, b; if (wp == NULL) return; - if (strcmp(p, "?") == 0) + + if (strcmp(p, "?") == 0) { + tty_default_colours(&defaults, wp); + input_osc_colour_reply(ictx, 10, defaults.fg); return; + } if (!input_osc_parse_colour(p, &r, &g, &b)) goto bad; @@ -2520,17 +2543,22 @@ bad: log_debug("bad OSC 10: %s", p); } -/* Handle the OSC 11 sequence for setting background colour. */ +/* Handle the OSC 11 sequence for setting and querying background colour. */ static void input_osc_11(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; + struct grid_cell defaults; u_int r, g, b; if (wp == NULL) return; - if (strcmp(p, "?") == 0) + + if (strcmp(p, "?") == 0) { + tty_default_colours(&defaults, wp); + input_osc_colour_reply(ictx, 11, defaults.bg); return; + } if (!input_osc_parse_colour(p, &r, &g, &b)) goto bad; From d8b6560cbfb5677223982e4b27be92b2fcd034df Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 19 Aug 2020 07:15:42 +0000 Subject: [PATCH 08/61] Set alert flag for the current window if the session is unattached. GitHub issues 1182 and 2299. From Eric Garver. --- alerts.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/alerts.c b/alerts.c index 38e88801..0f2eb179 100644 --- a/alerts.c +++ b/alerts.c @@ -200,7 +200,7 @@ alerts_check_bell(struct window *w) * not check WINLINK_BELL). */ s = wl->session; - if (s->curw != wl) { + if (s->curw != wl || s->attached == 0) { wl->flags |= WINLINK_BELL; server_status_session(s); } @@ -236,7 +236,7 @@ alerts_check_activity(struct window *w) if (wl->flags & WINLINK_ACTIVITY) continue; s = wl->session; - if (s->curw != wl) { + if (s->curw != wl || s->attached == 0) { wl->flags |= WINLINK_ACTIVITY; server_status_session(s); } @@ -272,7 +272,7 @@ alerts_check_silence(struct window *w) if (wl->flags & WINLINK_SILENCE) continue; s = wl->session; - if (s->curw != wl) { + if (s->curw != wl || s->attached == 0) { wl->flags |= WINLINK_SILENCE; server_status_session(s); } From d0957529edcdd3f616d0361972dd819b8b4c29d0 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 20 Aug 2020 16:57:40 +0000 Subject: [PATCH 09/61] Add n: modifier to get length of a format, also automatically expand variable name arguments again if they contain a #{. --- format.c | 37 ++++++++++++++++++++++++++----------- tmux.1 | 3 +++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/format.c b/format.c index 20d1fc69..c95eed7d 100644 --- a/format.c +++ b/format.c @@ -94,6 +94,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_WINDOWS 0x100 #define FORMAT_PANES 0x200 #define FORMAT_PRETTY 0x400 +#define FORMAT_LENGTH 0x800 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 10 @@ -1647,7 +1648,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) /* * Modifiers are a ; separated list of the forms: - * l,m,C,b,d,t,q,E,T,S,W,P,<,> + * l,m,C,b,d,n,t,q,E,T,S,W,P,<,> * =a * =/a * =/a/ @@ -1664,7 +1665,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) cp++; /* Check single character modifiers with no arguments. */ - if (strchr("lbdqETSWP<>", cp[0]) != NULL && + if (strchr("lbdnqETSWP<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; @@ -2122,6 +2123,9 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, case 'd': modifiers |= FORMAT_DIRNAME; break; + case 'n': + modifiers |= FORMAT_LENGTH; + break; case 't': modifiers |= FORMAT_TIMESTRING; if (fm->argc < 1) @@ -2301,13 +2305,17 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, if (value == NULL) value = xstrdup(""); } else { - /* Neither: look up directly. */ - value = format_find(ft, copy, modifiers, time_format); - if (value == NULL) { - format_log(ft, "format '%s' not found", copy); - value = xstrdup(""); - } else - format_log(ft, "format '%s' found: %s", copy, value); + if (strstr(copy, "#{") != 0) { + format_log(ft, "expanding inner format '%s'", copy); + value = format_expand(ft, copy); + } else { + value = format_find(ft, copy, modifiers, time_format); + if (value == NULL) { + format_log(ft, "format '%s' not found", copy); + value = xstrdup(""); + } else + format_log(ft, "format '%s' found: %s", copy, value); + } } done: @@ -2316,8 +2324,7 @@ done: new = format_expand(ft, value); free(value); value = new; - } - else if (modifiers & FORMAT_EXPANDTIME) { + } else if (modifiers & FORMAT_EXPANDTIME) { new = format_expand_time(ft, value); free(value); value = new; @@ -2371,6 +2378,14 @@ done: format_log(ft, "applied padding width %d: %s", width, value); } + /* Replace with the length if needed. */ + if (modifiers & FORMAT_LENGTH) { + xasprintf(&new, "%zu", strlen(value)); + free(value); + value = new; + format_log(ft, "replacing with length: %s", new); + } + /* Expand the buffer and copy in the value. */ valuelen = strlen(value); while (*len - *off < valuelen + 1) { diff --git a/tmux.1 b/tmux.1 index cd20c056..46fe1fa2 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4574,6 +4574,9 @@ pads the string to a given width, for example .Ql #{p10:pane_title} will result in a width of at least 10 characters. A positive width pads on the left, a negative on the right. +.Ql n +expands to the length of the variable, for example +.Ql #{n:window_name} . .Pp Prefixing a time variable with .Ql t:\& From 43e3e5390861cebdc9f3c87ebf7ed1414cf9b596 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 24 Aug 2020 05:22:28 +0000 Subject: [PATCH 10/61] Do not run off end of string when stripping delays, reported by Dave Vandervies. --- tty-term.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tty-term.c b/tty-term.c index 3ccff2ff..5aac1e0c 100644 --- a/tty-term.c +++ b/tty-term.c @@ -302,6 +302,8 @@ tty_term_strip(const char *s) ptr++; if (*ptr == '>') ptr++; + if (*ptr == '\0') + break; } buf[len++] = *ptr; From e4a4fcfc90b09774cb15b030689e977fefe12678 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 24 Aug 2020 05:23:30 +0000 Subject: [PATCH 11/61] Old Terminal.app versions do not respond correctly to secondary DA, instead responding with the primary DA response. Ignore it. Reported by Dave Vandervies. --- tty-keys.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index 19ad4f5b..036bd91d 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1192,7 +1192,10 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, if (tty->flags & TTY_HAVEDA) return (-1); - /* First three bytes are always \033[?. */ + /* + * First three bytes are always \033[>. Some older Terminal.app + * versions respond as for DA (\033[?) so accept and ignore that. + */ if (buf[0] != '\033') return (-1); if (len == 1) @@ -1201,7 +1204,7 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, return (-1); if (len == 2) return (1); - if (buf[2] != '>') + if (buf[2] != '>' && buf[2] != '?') return (-1); if (len == 3) return (1); @@ -1219,6 +1222,10 @@ tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, 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) { From 20fcdcfea1651d26990ae482cbc0f3594c4cea54 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 25 Aug 2020 11:35:32 +0000 Subject: [PATCH 12/61] Allow colour to be spelt as color, from Boris Verkhovsky. GitHub issue 2317. --- colour.c | 6 ++++++ options-table.c | 8 ++++++++ options.c | 42 ++++++++++++++++++++++++++++++------------ tmux.h | 9 ++++++++- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/colour.c b/colour.c index c7972878..ee4b95db 100644 --- a/colour.c +++ b/colour.c @@ -189,6 +189,12 @@ colour_fromstring(const char *s) return (-1); return (n | COLOUR_FLAG_256); } + if (strncasecmp(s, "color", (sizeof "color") - 1) == 0) { + n = strtonum(s + (sizeof "color") - 1, 0, 255, &errstr); + if (errstr != NULL) + return (-1); + return (n | COLOUR_FLAG_256); + } if (strcasecmp(s, "default") == 0) return (8); diff --git a/options-table.c b/options-table.c index 95d865ce..2ae49d7a 100644 --- a/options-table.c +++ b/options-table.c @@ -171,6 +171,14 @@ static const char *options_table_status_format_default[] = { .separator = "" \ } +/* Map of name conversions. */ +const struct options_name_map options_other_names[] = { + { "display-panes-color", "display-panes-colour" }, + { "display-panes-active-color", "display-panes-active-colour" }, + { "clock-mode-color", "clock-mode-colour" }, + { NULL, NULL } +}; + /* Top-level options. */ const struct options_table_entry options_table[] = { /* Server options. */ diff --git a/options.c b/options.c index 336eb732..09850f7e 100644 --- a/options.c +++ b/options.c @@ -95,6 +95,18 @@ options_cmp(struct options_entry *lhs, struct options_entry *rhs) return (strcmp(lhs->name, rhs->name)); } +static const char * +options_map_name(const char *name) +{ + const struct options_name_map *map; + + for (map = options_other_names; map->from != NULL; map++) { + if (strcmp(map->from, name) == 0) + return (map->to); + } + return (name); +} + static const struct options_table_entry * options_parent_table_entry(struct options *oo, const char *s) { @@ -204,10 +216,14 @@ options_next(struct options_entry *o) struct options_entry * options_get_only(struct options *oo, const char *name) { - struct options_entry o; + struct options_entry o = { .name = name }, *found; - o.name = name; - return (RB_FIND(options_tree, &oo->tree, &o)); + found = RB_FIND(options_tree, &oo->tree, &o); + if (found == NULL) { + o.name = options_map_name(name); + return (RB_FIND(options_tree, &oo->tree, &o)); + } + return (found); } struct options_entry * @@ -608,19 +624,21 @@ char * options_match(const char *s, int *idx, int *ambiguous) { const struct options_table_entry *oe, *found; - char *name; + char *parsed; + const char *name; size_t namelen; - name = options_parse(s, idx); - if (name == NULL) + parsed = options_parse(s, idx); + if (parsed == NULL) return (NULL); - namelen = strlen(name); - - if (*name == '@') { + if (*parsed == '@') { *ambiguous = 0; - return (name); + return (parsed); } + name = options_map_name(parsed); + namelen = strlen(name); + found = NULL; for (oe = options_table; oe->name != NULL; oe++) { if (strcmp(oe->name, name) == 0) { @@ -630,13 +648,13 @@ options_match(const char *s, int *idx, int *ambiguous) if (strncmp(oe->name, name, namelen) == 0) { if (found != NULL) { *ambiguous = 1; - free(name); + free(parsed); return (NULL); } found = oe; } } - free(name); + free(parsed); if (found == NULL) { *ambiguous = 0; return (NULL); diff --git a/tmux.h b/tmux.h index daba4b25..76953c3e 100644 --- a/tmux.h +++ b/tmux.h @@ -1788,6 +1788,7 @@ enum options_table_type { struct options_table_entry { const char *name; + const char *alternative_name; enum options_table_type type; int scope; int flags; @@ -1807,6 +1808,11 @@ struct options_table_entry { const char *unit; }; +struct options_name_map { + const char *from; + const char *to; +}; + /* Common command usages. */ #define CMD_TARGET_PANE_USAGE "[-t target-pane]" #define CMD_TARGET_WINDOW_USAGE "[-t target-window]" @@ -2039,7 +2045,8 @@ int options_remove_or_default(struct options_entry *, int, char **); /* options-table.c */ -extern const struct options_table_entry options_table[]; +extern const struct options_table_entry options_table[]; +extern const struct options_name_map options_other_names[]; /* job.c */ typedef void (*job_update_cb) (struct job *); From 2ab289980afd7d47a92dfb17d488c127d335b429 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 27 Aug 2020 06:55:54 +0000 Subject: [PATCH 13/61] Add pane_last format, GitHub issue 2353. --- format.c | 1 + tmux.1 | 1 + 2 files changed, 2 insertions(+) diff --git a/format.c b/format.c index c95eed7d..bf3ba93d 100644 --- a/format.c +++ b/format.c @@ -2909,6 +2909,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_dead", "%d", wp->fd == -1); else format_add(ft, "pane_dead", "0"); + format_add(ft, "pane_last", "%d", wp == w->last); if (server_check_marked() && marked_pane.wp == wp) format_add(ft, "pane_marked", "1"); diff --git a/tmux.1 b/tmux.1 index 46fe1fa2..21fcdedd 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4773,6 +4773,7 @@ The following variables are available, where appropriate: .It Li "pane_in_mode" Ta "" Ta "1 if pane is in a mode" .It Li "pane_index" Ta "#P" Ta "Index of pane" .It Li "pane_input_off" Ta "" Ta "1 if input to pane is disabled" +.It Li "pane_last" Ta "" Ta "1 if last pane" .It Li "pane_left" Ta "" Ta "Left of pane" .It Li "pane_marked" Ta "" Ta "1 if this is the marked pane" .It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set" From b2a262e35357228d01f93f81ab57df204fdbaef0 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2020 08:50:14 +0000 Subject: [PATCH 14/61] Only print below number when there is enough space. --- cmd-display-panes.c | 89 ++++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/cmd-display-panes.c b/cmd-display-panes.c index a13a06ae..352b2e4d 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -55,11 +55,11 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, struct session *s = c->session; struct options *oo = s->options; struct window *w = wp->window; - struct grid_cell gc; - u_int idx, px, py, i, j, xoff, yoff, sx, sy; + struct grid_cell fgc, bgc; + u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy; int colour, active_colour; - char buf[16], *ptr; - size_t len; + char buf[16], lbuf[16], rbuf[16], *ptr; + size_t len, llen, rlen; if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx || @@ -109,29 +109,50 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, px = sx / 2; py = sy / 2; - if (window_pane_index(wp, &idx) != 0) + if (window_pane_index(wp, &pane) != 0) fatalx("index not found"); - len = xsnprintf(buf, sizeof buf, "%u", idx); + len = xsnprintf(buf, sizeof buf, "%u", pane); if (sx < len) return; colour = options_get_number(oo, "display-panes-colour"); active_colour = options_get_number(oo, "display-panes-active-colour"); + memcpy(&fgc, &grid_default_cell, sizeof fgc); + memcpy(&bgc, &grid_default_cell, sizeof bgc); + if (w->active == wp) { + fgc.fg = active_colour; + bgc.bg = active_colour; + } else { + fgc.fg = colour; + bgc.bg = colour; + } + + rlen = xsnprintf(rbuf, sizeof rbuf, "%ux%u", wp->sx, wp->sy); + if (pane > 9 && pane < 35) + llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10)); + else + llen = 0; + if (sx < len * 6 || sy < 5) { - tty_cursor(tty, xoff + px - len / 2, yoff + py); - goto draw_text; + tty_attributes(tty, &fgc, &grid_default_cell, NULL); + if (sx >= len + llen + 1) { + len += llen + 1; + tty_cursor(tty, xoff + px - len / 2, yoff + py); + tty_putn(tty, buf, len, len); + tty_putn(tty, " ", 1, 1); + tty_putn(tty, lbuf, llen, llen); + } else { + tty_cursor(tty, xoff + px - len / 2, yoff + py); + tty_putn(tty, buf, len, len); + } + goto out; } px -= len * 3; py -= 2; - memcpy(&gc, &grid_default_cell, sizeof gc); - if (w->active == wp) - gc.bg = active_colour; - else - gc.bg = colour; - tty_attributes(tty, &gc, &grid_default_cell, NULL); + tty_attributes(tty, &bgc, &grid_default_cell, NULL); for (ptr = buf; *ptr != '\0'; ptr++) { if (*ptr < '0' || *ptr > '9') continue; @@ -147,20 +168,20 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, px += 6; } - len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy); - if (sx < len || sy < 6) - return; - tty_cursor(tty, xoff + sx - len, yoff); - -draw_text: - memcpy(&gc, &grid_default_cell, sizeof gc); - if (w->active == wp) - gc.fg = active_colour; - else - gc.fg = colour; - tty_attributes(tty, &gc, &grid_default_cell, NULL); - tty_puts(tty, buf); + if (sy <= 6) + goto out; + tty_attributes(tty, &fgc, &grid_default_cell, NULL); + if (rlen != 0 && sx >= rlen) { + tty_cursor(tty, xoff + sx - rlen, yoff); + tty_putn(tty, rbuf, rlen, rlen); + } + if (llen != 0) { + tty_cursor(tty, xoff + sx / 2 + len * 3 - llen - 1, + yoff + py + 5); + tty_putn(tty, lbuf, llen, llen); + } +out: tty_cursor(tty, 0, 0); } @@ -197,11 +218,21 @@ cmd_display_panes_key(struct client *c, struct key_event *event) struct window *w = c->session->curw->window; struct window_pane *wp; enum cmd_parse_status status; + u_int index; + key_code key; - if (event->key < '0' || event->key > '9') + if (event->key >= '0' && event->key <= '9') + index = event->key - '0'; + else if ((event->key & KEYC_MASK_MODIFIERS) == 0) { + key = (event->key & KEYC_MASK_KEY); + if (key >= 'a' && key <= 'z') + index = 10 + (key - 'a'); + else + return (-1); + } else return (-1); - wp = window_pane_at_index(w, event->key - '0'); + wp = window_pane_at_index(w, index); if (wp == NULL) return (1); window_unzoom(w); From 60860aced8d424ef6c1d527b63842a0ea0f452ad Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2020 09:19:01 +0000 Subject: [PATCH 15/61] Add -F to set-environment and source-file; GitHub issue 2359. --- cmd-set-environment.c | 25 ++++++++++++++++++------- cmd-source-file.c | 14 ++++++++++---- tmux.1 | 14 ++++++++++++-- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 3c43b635..f142df53 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -34,8 +34,8 @@ const struct cmd_entry cmd_set_environment_entry = { .name = "set-environment", .alias = "setenv", - .args = { "hgrt:u", 1, 2 }, - .usage = "[-hgru] " CMD_TARGET_SESSION_USAGE " name [value]", + .args = { "Fhgrt:u", 1, 2 }, + .usage = "[-Fhgru] " CMD_TARGET_SESSION_USAGE " name [value]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, @@ -50,6 +50,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *target = cmdq_get_target(item); struct environ *env; const char *name, *value, *tflag; + char *expand = NULL; + enum cmd_retval retval = CMD_RETURN_NORMAL; name = args->argv[0]; if (*name == '\0') { @@ -63,6 +65,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) if (args->argc < 2) value = NULL; + else if (args_has(args, 'F')) + value = expand = format_single_from_target(item, args->argv[1]); else value = args->argv[1]; @@ -75,7 +79,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) cmdq_error(item, "no such session: %s", tflag); else cmdq_error(item, "no current session"); - return (CMD_RETURN_ERROR); + retval = CMD_RETURN_ERROR; + goto out; } env = target->s->environ; } @@ -83,25 +88,31 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'u')) { if (value != NULL) { cmdq_error(item, "can't specify a value with -u"); - return (CMD_RETURN_ERROR); + retval = CMD_RETURN_ERROR; + goto out; } environ_unset(env, name); } else if (args_has(args, 'r')) { if (value != NULL) { cmdq_error(item, "can't specify a value with -r"); - return (CMD_RETURN_ERROR); + retval = CMD_RETURN_ERROR; + goto out; } environ_clear(env, name); } else { if (value == NULL) { cmdq_error(item, "no value specified"); - return (CMD_RETURN_ERROR); + retval = CMD_RETURN_ERROR; + goto out; } + if (args_has(args, 'h')) environ_set(env, name, ENVIRON_HIDDEN, "%s", value); else environ_set(env, name, 0, "%s", value); } - return (CMD_RETURN_NORMAL); +out: + free(expand); + return (retval); } diff --git a/cmd-source-file.c b/cmd-source-file.c index 62773872..b7a7abee 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -36,8 +36,8 @@ const struct cmd_entry cmd_source_file_entry = { .name = "source-file", .alias = "source", - .args = { "nqv", 1, -1 }, - .usage = "[-nqv] path ...", + .args = { "Fnqv", 1, -1 }, + .usage = "[-Fnqv] path ...", .flags = 0, .exec = cmd_source_file_exec @@ -127,7 +127,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) struct cmd_source_file_data *cdata; struct client *c = cmdq_get_client(item); enum cmd_retval retval = CMD_RETURN_NORMAL; - char *pattern, *cwd; + char *pattern, *cwd, *expand = NULL; const char *path, *error; glob_t g; int i, result; @@ -146,7 +146,12 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB); for (i = 0; i < args->argc; i++) { - path = args->argv[i]; + if (args_has(args, 'F')) { + free(expand); + expand = format_single_from_target(item, args->argv[i]); + path = expand; + } else + path = args->argv[i]; if (strcmp(path, "-") == 0) { cmd_source_file_add(cdata, "-"); continue; @@ -173,6 +178,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) free(pattern); continue; } + free(expand); free(pattern); for (j = 0; j < g.gl_pathc; j++) diff --git a/tmux.1 b/tmux.1 index 21fcdedd..c11df7ce 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1407,7 +1407,7 @@ and .Fl T show debugging information about jobs and terminals. .It Xo Ic source-file -.Op Fl nqv +.Op Fl Fnqv .Ar path .Ar ... .Xc @@ -1418,6 +1418,11 @@ Execute commands from one or more files specified by .Xr glob 7 patterns). If +.Fl F +is present, then +.Ar path +is expanded as a format. +If .Fl q is given, no error will be returned if .Ar path @@ -5102,7 +5107,7 @@ section). Commands to alter and view the environment are: .Bl -tag -width Ds .It Xo Ic set-environment -.Op Fl hgru +.Op Fl Fhgru .Op Fl t Ar target-session .Ar name Op Ar value .Xc @@ -5113,6 +5118,11 @@ If is used, the change is made in the global environment; otherwise, it is applied to the session environment for .Ar target-session . +If +.Fl F +is present, then +.Ar value +is expanded as a format. The .Fl u flag unsets a variable. From 37b1600d9cf7076498760372dcc20f021e4c181a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Sep 2020 13:46:35 +0000 Subject: [PATCH 16/61] Add a -w flag to set- and load-buffer to send to clipboard using OSC 52. GitHub issue 2363. --- cmd-load-buffer.c | 20 ++++++++++++++++---- cmd-set-buffer.c | 10 +++++++--- tmux.1 | 19 ++++++++++++++++++- tmux.h | 1 + tty.c | 22 +++++++++++++++------- 5 files changed, 57 insertions(+), 15 deletions(-) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 49e834d6..f5a080be 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -37,14 +37,15 @@ const struct cmd_entry cmd_load_buffer_entry = { .name = "load-buffer", .alias = "loadb", - .args = { "b:", 1, 1 }, - .usage = CMD_BUFFER_USAGE " path", + .args = { "b:t:w", 1, 1 }, + .usage = CMD_BUFFER_USAGE " " CMD_TARGET_CLIENT_USAGE " path", - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL, .exec = cmd_load_buffer_exec }; struct cmd_load_buffer_data { + struct client *client; struct cmdq_item *item; char *name; }; @@ -54,6 +55,7 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error, int closed, struct evbuffer *buffer, void *data) { struct cmd_load_buffer_data *cdata = data; + struct client *tc = cdata->client; struct cmdq_item *item = cdata->item; void *bdata = EVBUFFER_DATA(buffer); size_t bsize = EVBUFFER_LENGTH(buffer); @@ -72,7 +74,12 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error, cmdq_error(item, "%s", cause); free(cause); free(copy); - } + } else if (tc != NULL && + tc->session != NULL && + (~tc->flags & CLIENT_DEAD)) + tty_set_selection(&tc->tty, copy, bsize); + if (tc != NULL) + server_client_unref(tc); } cmdq_continue(item); @@ -84,6 +91,7 @@ static enum cmd_retval cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); struct cmd_load_buffer_data *cdata; const char *bufname = args_get(args, 'b'); char *path; @@ -94,6 +102,10 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) cdata->name = xstrdup(bufname); else cdata->name = NULL; + if (args_has(args, 'w') && tc != NULL) { + cdata->client = tc; + cdata->client->references++; + } path = format_single_from_target(item, args->argv[0]); file_read(cmdq_get_client(item), path, cmd_load_buffer_done, cdata); diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 0f3fffce..94d8cd52 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -33,10 +33,11 @@ const struct cmd_entry cmd_set_buffer_entry = { .name = "set-buffer", .alias = "setb", - .args = { "ab:n:", 0, 1 }, - .usage = "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", + .args = { "ab:t:n:w", 0, 1 }, + .usage = "[-aw] " CMD_BUFFER_USAGE " [-n new-buffer-name] " + CMD_TARGET_CLIENT_USAGE " data", - .flags = CMD_AFTERHOOK, + .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL, .exec = cmd_set_buffer_exec }; @@ -55,6 +56,7 @@ static enum cmd_retval cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct client *tc = cmdq_get_target_client(item); struct paste_buffer *pb; char *bufdata, *cause; const char *bufname, *olddata; @@ -118,6 +120,8 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) free(cause); return (CMD_RETURN_ERROR); } + if (args_has(args, 'w') && tc != NULL) + tty_set_selection(&tc->tty, bufdata, bufsize); return (CMD_RETURN_NORMAL); } diff --git a/tmux.1 b/tmux.1 index c11df7ce..08b329f4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5651,12 +5651,21 @@ See the .Sx FORMATS section. .It Xo Ic load-buffer +.Op Fl w .Op Fl b Ar buffer-name +.Op Fl t Ar target-client .Ar path .Xc .D1 (alias: Ic loadb ) Load the contents of the specified paste buffer from .Ar path . +If +.Fl w +is given, the buffer is also sent to the clipboard for +.Ar target-client +using the +.Xr xterm 1 +escape sequence, if possible. .It Xo Ic paste-buffer .Op Fl dpr .Op Fl b Ar buffer-name @@ -5693,14 +5702,22 @@ The .Fl a option appends to rather than overwriting the file. .It Xo Ic set-buffer -.Op Fl a +.Op Fl aw .Op Fl b Ar buffer-name +.Op Fl t Ar target-client .Op Fl n Ar new-buffer-name .Ar data .Xc .D1 (alias: Ic setb ) Set the contents of the specified buffer to .Ar data . +If +.Fl w +is given, the buffer is also sent to the clipboard for +.Ar target-client +using the +.Xr xterm 1 +escape sequence, if possible. The .Fl a option appends to rather than overwriting the buffer. diff --git a/tmux.h b/tmux.h index 76953c3e..9fb3830a 100644 --- a/tmux.h +++ b/tmux.h @@ -2126,6 +2126,7 @@ int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); void tty_update_features(struct tty *); +void tty_set_selection(struct tty *, const char *, size_t); void tty_write(void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); diff --git a/tty.c b/tty.c index 14b770e2..148b1d2d 100644 --- a/tty.c +++ b/tty.c @@ -1896,19 +1896,27 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx) { - char *buf; - size_t off; + tty_set_selection(tty, ctx->ptr, ctx->num); +} + +void +tty_set_selection(struct tty *tty, const char *buf, size_t len) +{ + char *encoded; + size_t size; if (!tty_term_has(tty->term, TTYC_MS)) return; + if (~tty->flags & TTY_STARTED) + return; - off = 4 * ((ctx->num + 2) / 3) + 1; /* storage for base64 */ - buf = xmalloc(off); + size = 4 * ((len + 2) / 3) + 1; /* storage for base64 */ + encoded = xmalloc(size); - b64_ntop(ctx->ptr, ctx->num, buf, off); - tty_putcode_ptr2(tty, TTYC_MS, "", buf); + b64_ntop(buf, len, encoded, size); + tty_putcode_ptr2(tty, TTYC_MS, "", encoded); - free(buf); + free(encoded); } void From e538bef7576456911cd8cf76d3e4ff2ef97bd671 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Sep 2020 17:19:58 +0000 Subject: [PATCH 17/61] Check started flag before looking for capability. --- tty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tty.c b/tty.c index 148b1d2d..30b94e04 100644 --- a/tty.c +++ b/tty.c @@ -1905,10 +1905,10 @@ tty_set_selection(struct tty *tty, const char *buf, size_t len) char *encoded; size_t size; - if (!tty_term_has(tty->term, TTYC_MS)) - return; if (~tty->flags & TTY_STARTED) return; + if (!tty_term_has(tty->term, TTYC_MS)) + return; size = 4 * ((len + 2) / 3) + 1; /* storage for base64 */ encoded = xmalloc(size); From eadf18b9fa3f32ffd06be5dbca627047430bc01c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Sep 2020 12:47:33 +0000 Subject: [PATCH 18/61] Do not free old session working directory until after expanding the new one because it may be needed. --- cmd-attach-session.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 38d9c024..6a7ebba7 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -59,7 +59,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, struct session *s; struct winlink *wl; struct window_pane *wp; - char *cause; + char *cwd, *cause; enum msgtype msgtype; if (RB_EMPTY(&sessions)) { @@ -99,8 +99,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, } if (cflag != NULL) { + cwd = format_single(item, cflag, c, s, wl, wp); free((void *)s->cwd); - s->cwd = format_single(item, cflag, c, s, wl, wp); + s->cwd = cwd; } if (fflag) server_client_set_flags(c, fflag); From 233d14f4da86ee383815b870d547fcd2e39b8c53 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 4 Sep 2020 08:36:34 +0100 Subject: [PATCH 19/61] Hide warnings due to Apple's stupidity with __dead, reported by Kurtis Rader. --- compat.h | 4 ++++ configure.ac | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/compat.h b/compat.h index 5b23b178..4efad6ee 100644 --- a/compat.h +++ b/compat.h @@ -35,6 +35,10 @@ #define __attribute__(a) #endif +#ifdef BROKEN___DEAD +#undef __dead +#endif + #ifndef __unused #define __unused __attribute__ ((__unused__)) #endif diff --git a/configure.ac b/configure.ac index a62029b3..f89c590b 100644 --- a/configure.ac +++ b/configure.ac @@ -550,6 +550,12 @@ case "$host_os" in AC_MSG_RESULT(darwin) PLATFORM=darwin # + # OS X uses __dead2 instead of __dead, like FreeBSD. But it + # defines __dead away so it needs to be removed before we can + # replace it. + # + AC_DEFINE(BROKEN___DEAD) + # # OS X CMSG_FIRSTHDR is broken, so redefine it with a working # one. daemon works but has some stupid side effects, so use # our internal version which has a workaround. From 9b45ba82fd38d30d9f6bbe3f659e33a32b3b5ace Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 4 Sep 2020 12:24:25 +0000 Subject: [PATCH 20/61] calloc cb data so the client is NULL. --- cmd-load-buffer.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index f5a080be..bca9a860 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -96,12 +96,10 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) const char *bufname = args_get(args, 'b'); char *path; - cdata = xmalloc(sizeof *cdata); + cdata = xcalloc(1, sizeof *cdata); cdata->item = item; if (bufname != NULL) cdata->name = xstrdup(bufname); - else - cdata->name = NULL; if (args_has(args, 'w') && tc != NULL) { cdata->client = tc; cdata->client->references++; From 1fed7e84a3d65c8fbfbb321b84236ccab7265d46 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Sep 2020 10:19:19 +0000 Subject: [PATCH 21/61] Allow -N without a command to change or add a note to an existing key. --- cmd-bind-key.c | 42 ++++++++++++++++++++++-------------------- key-bindings.c | 10 ++++++++++ 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index dcb56c06..b4e4167c 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -33,9 +33,9 @@ const struct cmd_entry cmd_bind_key_entry = { .name = "bind-key", .alias = "bind", - .args = { "nrN:T:", 2, -1 }, + .args = { "nrN:T:", 1, -1 }, .usage = "[-nr] [-T key-table] [-N note] key " - "command [arguments]", + "[command [arguments]]", .flags = CMD_AFTERHOOK, .exec = cmd_bind_key_exec @@ -46,7 +46,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); key_code key; - const char *tablename, *note; + const char *tablename, *note = args_get(args, 'N'); struct cmd_parse_result *pr; char **argv = args->argv; int argc = args->argc, repeat; @@ -65,22 +65,24 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) tablename = "prefix"; repeat = args_has(args, 'r'); - if (argc == 2) - pr = cmd_parse_from_string(argv[1], NULL); - else - pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL); - switch (pr->status) { - case CMD_PARSE_EMPTY: - cmdq_error(item, "empty command"); - return (CMD_RETURN_ERROR); - case CMD_PARSE_ERROR: - cmdq_error(item, "%s", pr->error); - free(pr->error); - return (CMD_RETURN_ERROR); - case CMD_PARSE_SUCCESS: - break; - } - note = args_get(args, 'N'); - key_bindings_add(tablename, key, note, repeat, pr->cmdlist); + if (argc != 1) { + if (argc == 2) + pr = cmd_parse_from_string(argv[1], NULL); + else + pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL); + switch (pr->status) { + case CMD_PARSE_EMPTY: + cmdq_error(item, "empty command"); + return (CMD_RETURN_ERROR); + case CMD_PARSE_ERROR: + cmdq_error(item, "%s", pr->error); + free(pr->error); + return (CMD_RETURN_ERROR); + case CMD_PARSE_SUCCESS: + break; + } + key_bindings_add(tablename, key, note, repeat, pr->cmdlist); + } else + key_bindings_add(tablename, key, note, repeat, NULL); return (CMD_RETURN_NORMAL); } diff --git a/key-bindings.c b/key-bindings.c index f11bb430..63d4bb26 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -191,6 +191,16 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat, table = key_bindings_get_table(name, 1); bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS); + if (cmdlist == NULL) { + if (bd != NULL) { + free((void *)bd->note); + if (note != NULL) + bd->note = xstrdup(note); + else + bd->note = NULL; + } + return; + } if (bd != NULL) { RB_REMOVE(key_bindings, &table->key_bindings, bd); key_bindings_free(bd); From 869c0e860fcf0851ef1751ca9187599913ca056a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Sep 2020 18:37:55 +0000 Subject: [PATCH 22/61] Fix some warnings, GitHub issue 2382. --- format.c | 5 ----- menu.c | 2 +- server.c | 8 +++++--- utf8.c | 4 ++-- window-copy.c | 21 ++------------------- 5 files changed, 10 insertions(+), 30 deletions(-) diff --git a/format.c b/format.c index bf3ba93d..66107b68 100644 --- a/format.c +++ b/format.c @@ -1373,7 +1373,6 @@ format_pretty_time(time_t t) struct tm now_tm, tm; time_t now, age; char s[6]; - int m; time(&now); if (now < t) @@ -1397,10 +1396,6 @@ format_pretty_time(time_t t) } /* Last 12 months. */ - if (now_tm.tm_mon == 0) - m = 11; - else - m = now_tm.tm_mon - 1; if ((tm.tm_year == now_tm.tm_year && tm.tm_mon < now_tm.tm_mon) || (tm.tm_year == now_tm.tm_year - 1 && tm.tm_mon > now_tm.tm_mon)) { strftime(s, sizeof s, "%d%b", &tm); diff --git a/menu.c b/menu.c index 16374115..4fcf660a 100644 --- a/menu.c +++ b/menu.c @@ -187,7 +187,7 @@ menu_key_cb(struct client *c, struct key_event *event) struct mouse_event *m = &event->m; u_int i; int count = menu->count, old = md->choice; - const char *name; + const char *name = NULL; const struct menu_item *item; struct cmdq_state *state; enum cmd_parse_status status; diff --git a/server.c b/server.c index f07bf673..b2c75e11 100644 --- a/server.c +++ b/server.c @@ -158,7 +158,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, { int pair[2]; sigset_t set, oldset; - struct client *c; + struct client *c = NULL; char *cause = NULL; sigfillset(&set); @@ -224,9 +224,11 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, } if (cause != NULL) { - cmdq_append(c, cmdq_get_error(cause)); + if (c != NULL) { + cmdq_append(c, cmdq_get_error(cause)); + c->flags |= CLIENT_EXIT; + } free(cause); - c->flags |= CLIENT_EXIT; } server_add_accept(0); diff --git a/utf8.c b/utf8.c index 36c9daad..88dbdb7a 100644 --- a/utf8.c +++ b/utf8.c @@ -101,9 +101,9 @@ utf8_put_item(const char *data, size_t size, u_int *index) ui = utf8_item_by_data(data, size); if (ui != NULL) { + *index = ui->index; log_debug("%s: found %.*s = %u", __func__, (int)size, data, *index); - *index = ui->index; return (0); } @@ -118,8 +118,8 @@ utf8_put_item(const char *data, size_t size, u_int *index) ui->size = size; RB_INSERT(utf8_data_tree, &utf8_data_tree, ui); - log_debug("%s: added %.*s = %u", __func__, (int)size, data, *index); *index = ui->index; + log_debug("%s: added %.*s = %u", __func__, (int)size, data, *index); return (0); } diff --git a/window-copy.c b/window-copy.c index 1f0f7e78..640a69ec 100644 --- a/window-copy.c +++ b/window-copy.c @@ -72,7 +72,6 @@ static int window_copy_search_marks(struct window_mode_entry *, struct screen *, int, int); static void window_copy_clear_marks(struct window_mode_entry *); static void window_copy_move_left(struct screen *, u_int *, u_int *, int); -static void window_copy_move_right(struct screen *, u_int *, u_int *, int); static int window_copy_is_lowercase(const char *); static int window_copy_search_jump(struct window_mode_entry *, struct grid *, struct grid *, u_int, u_int, u_int, int, int, @@ -2817,23 +2816,6 @@ window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag) *fx = *fx - 1; } -static void -window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag) -{ - if (*fx == screen_size_x(s) - 1) { /* right */ - if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */ - if (wrapflag) { - *fx = 0; - *fy = 0; - } - return; - } - *fx = 0; - *fy = *fy + 1; - } else - *fx = *fx + 1; -} - static int window_copy_is_lowercase(const char *ptr) { @@ -2930,7 +2912,8 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, * down. */ static int -window_copy_search(struct window_mode_entry *wme, int direction, int regex, int again) +window_copy_search(struct window_mode_entry *wme, int direction, int regex, + int again) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; From 3206869ea5cbcf0caa9e62ec11edb170aae2cf27 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Sep 2020 19:12:59 +0000 Subject: [PATCH 23/61] Add -q flag to unbind-key to hide errors, GitHub issue 2381. --- cmd-unbind-key.c | 54 ++++++++++++++++++++++++++++-------------------- tmux.1 | 5 ++++- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 4b9f39a6..a29831af 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -32,8 +32,8 @@ const struct cmd_entry cmd_unbind_key_entry = { .name = "unbind-key", .alias = "unbind", - .args = { "anT:", 0, 1 }, - .usage = "[-an] [-T key-table] key", + .args = { "anqT:", 0, 1 }, + .usage = "[-anq] [-T key-table] key", .flags = CMD_AFTERHOOK, .exec = cmd_unbind_key_exec @@ -45,44 +45,54 @@ cmd_unbind_key_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); key_code key; const char *tablename; + int quiet = args_has(args, 'q'); - if (!args_has(args, 'a')) { - if (args->argc != 1) { - cmdq_error(item, "missing key"); - return (CMD_RETURN_ERROR); - } - key = key_string_lookup_string(args->argv[0]); - if (key == KEYC_NONE || key == KEYC_UNKNOWN) { - cmdq_error(item, "unknown key: %s", args->argv[0]); - return (CMD_RETURN_ERROR); - } - } else { + if (args_has(args, 'a')) { if (args->argc != 0) { - cmdq_error(item, "key given with -a"); + if (!quiet) + cmdq_error(item, "key given with -a"); return (CMD_RETURN_ERROR); } - key = KEYC_UNKNOWN; - } - if (key == KEYC_UNKNOWN) { tablename = args_get(args, 'T'); if (tablename == NULL) { - key_bindings_remove_table("root"); - key_bindings_remove_table("prefix"); - return (CMD_RETURN_NORMAL); + if (args_has(args, 'n')) + tablename = "root"; + else + tablename = "prefix"; } if (key_bindings_get_table(tablename, 0) == NULL) { - cmdq_error(item, "table %s doesn't exist", tablename); + if (!quiet) { + cmdq_error(item, "table %s doesn't exist" , + tablename); + } return (CMD_RETURN_ERROR); } + key_bindings_remove_table(tablename); return (CMD_RETURN_NORMAL); } + if (args->argc != 1) { + if (!quiet) + cmdq_error(item, "missing key"); + return (CMD_RETURN_ERROR); + } + + key = key_string_lookup_string(args->argv[0]); + if (key == KEYC_NONE || key == KEYC_UNKNOWN) { + if (!quiet) + cmdq_error(item, "unknown key: %s", args->argv[0]); + return (CMD_RETURN_ERROR); + } + if (args_has(args, 'T')) { tablename = args_get(args, 'T'); if (key_bindings_get_table(tablename, 0) == NULL) { - cmdq_error(item, "table %s doesn't exist", tablename); + if (!quiet) { + cmdq_error(item, "table %s doesn't exist" , + tablename); + } return (CMD_RETURN_ERROR); } } else if (args_has(args, 'n')) diff --git a/tmux.1 b/tmux.1 index 08b329f4..b5cbafe8 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3039,7 +3039,7 @@ Send the prefix key, or with .Fl 2 the secondary prefix key, to a window as if it was pressed. .It Xo Ic unbind-key -.Op Fl an +.Op Fl anq .Op Fl T Ar key-table .Ar key .Xc @@ -3054,6 +3054,9 @@ are the same as for If .Fl a is present, all key bindings are removed. +The +.Fl q +option prevents errors being returned. .El .Sh OPTIONS The appearance and behaviour of From ed946dccc76f87064f1b8299b6ea332db9ab6c19 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 18 Sep 2020 11:20:59 +0000 Subject: [PATCH 24/61] Some other warnings, GitHub issue 2382. --- control.c | 4 ++-- input-keys.c | 2 ++ window-customize.c | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/control.c b/control.c index c52f2020..e86429cf 100644 --- a/control.c +++ b/control.c @@ -688,8 +688,8 @@ control_write_pending(struct client *c, struct control_pane *cp, size_t limit) else age = 0; log_debug("%s: %s: output block %zu (age %llu) for %%%u " - "(used %zu/%zu)", __func__, c->name, cb->size, age, - cp->pane, used, limit); + "(used %zu/%zu)", __func__, c->name, cb->size, + (unsigned long long)age, cp->pane, used, limit); size = cb->size; if (size > limit - used) diff --git a/input-keys.c b/input-keys.c index 938c530f..224bcfa5 100644 --- a/input-keys.c +++ b/input-keys.c @@ -556,6 +556,8 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) case KEYC_SHIFT|KEYC_META|KEYC_CTRL: modifier = '8'; break; + default: + fatalx("invalid key modifiers: %llx", key); } xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier); bufferevent_write(bev, tmp, strlen(tmp)); diff --git a/window-customize.c b/window-customize.c index c8606245..1dad07cd 100644 --- a/window-customize.c +++ b/window-customize.c @@ -380,7 +380,7 @@ window_customize_build_options(struct window_customize_modedata *data, struct format_tree *ft, const char *filter, struct cmd_find_state *fs) { struct mode_tree_item *top; - struct options_entry *o, *loop; + struct options_entry *o = NULL, *loop; const char **list = NULL, *name; u_int size = 0, i; enum window_customize_scope scope; @@ -1018,7 +1018,7 @@ window_customize_set_option(struct client *c, struct options *oo; struct window_customize_itemdata *new_item; int flag, idx = item->idx; - enum window_customize_scope scope; + enum window_customize_scope scope = WINDOW_CUSTOMIZE_NONE; u_int choice; const char *name = item->name, *space = ""; char *prompt, *value, *text; @@ -1031,7 +1031,7 @@ window_customize_set_option(struct client *c, return; oe = options_table_entry(o); - if (~oe->scope & OPTIONS_TABLE_PANE) + if (oe != NULL && ~oe->scope & OPTIONS_TABLE_PANE) pane = 0; if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { scope = item->scope; From 88b66e9e28733676b15a996d8fb5cbf66e01bc88 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 18 Sep 2020 11:23:29 +0000 Subject: [PATCH 25/61] Free buffer earlier to avoid confusing some compilers, GitHub issue 2382. --- window-copy.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/window-copy.c b/window-copy.c index 640a69ec..e46c910c 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2853,6 +2853,7 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, free(sbuf); return (0); } + free(sbuf); } if (direction) { @@ -2889,10 +2890,8 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, fx = gd->sx - 1; } } - if (regex) { - free(sbuf); + if (regex) regfree(®); - } if (found) { window_copy_scroll_to(wme, px, i, 1); @@ -3042,6 +3041,7 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp, free(sbuf); return (0); } + free(sbuf); } tstart = get_timer(); @@ -3139,10 +3139,8 @@ again: out: if (ssp == &ss) screen_free(&ss); - if (regex) { - free(sbuf); + if (regex) regfree(®); - } return (1); } From 86d6ac2f0695b02bdbef542cce3cdb0cca39160e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Sep 2020 05:23:34 +0000 Subject: [PATCH 26/61] Fix warnings on some platforms with %llx and add a new message to handle 64-bit client flags. --- client.c | 18 ++++++++++++------ server-client.c | 10 ++++++++++ tmux.c | 4 ++-- tmux.h | 3 ++- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/client.c b/client.c index 852b09ce..f08d6f29 100644 --- a/client.c +++ b/client.c @@ -59,7 +59,8 @@ static struct client_files client_files = RB_INITIALIZER(&client_files); static __dead void client_exec(const char *,const char *); static int client_get_lock(char *); -static int client_connect(struct event_base *, const char *, int); +static int client_connect(struct event_base *, const char *, + uint64_t); static void client_send_identify(const char *, const char *, int); static void client_signal(int); static void client_dispatch(struct imsg *, void *); @@ -100,7 +101,7 @@ client_get_lock(char *lockfile) /* Connect client to server. */ static int -client_connect(struct event_base *base, const char *path, int flags) +client_connect(struct event_base *base, const char *path, uint64_t flags) { struct sockaddr_un sa; size_t size; @@ -238,7 +239,8 @@ client_exit(void) /* Client main loop. */ int -client_main(struct event_base *base, int argc, char **argv, int flags, int feat) +client_main(struct event_base *base, int argc, char **argv, uint64_t flags, + int feat) { struct cmd_parse_result *pr; struct msg_command *data; @@ -284,7 +286,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags, int feat) /* Save the flags. */ client_flags = flags; - log_debug("flags are %#llx", client_flags); + log_debug("flags are %#llx", (unsigned long long)client_flags); /* Initialize the client socket and start the server. */ fd = client_connect(base, socket_path, client_flags); @@ -440,6 +442,8 @@ client_send_identify(const char *ttynam, const char *cwd, int feat) pid_t pid; proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); + proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &client_flags, + sizeof client_flags); if ((s = getenv("TERM")) == NULL) s = ""; @@ -889,7 +893,8 @@ client_dispatch_wait(struct imsg *imsg) fatalx("bad MSG_FLAGS string"); memcpy(&client_flags, data, sizeof client_flags); - log_debug("new flags are %#llx", client_flags); + log_debug("new flags are %#llx", + (unsigned long long)client_flags); break; case MSG_SHELL: if (datalen == 0 || data[datalen - 1] != '\0') @@ -942,7 +947,8 @@ client_dispatch_attached(struct imsg *imsg) fatalx("bad MSG_FLAGS string"); memcpy(&client_flags, data, sizeof client_flags); - log_debug("new flags are %#llx", client_flags); + log_debug("new flags are %#llx", + (unsigned long long)client_flags); break; case MSG_DETACH: case MSG_DETACHKILL: diff --git a/server-client.c b/server-client.c index 4010019d..efeec85b 100644 --- a/server-client.c +++ b/server-client.c @@ -1985,6 +1985,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) switch (imsg->hdr.type) { case MSG_IDENTIFY_FEATURES: case MSG_IDENTIFY_FLAGS: + case MSG_IDENTIFY_LONGFLAGS: case MSG_IDENTIFY_TERM: case MSG_IDENTIFY_TTYNAME: case MSG_IDENTIFY_CWD: @@ -2143,6 +2144,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) const char *data, *home; size_t datalen; int flags, feat; + uint64_t longflags; char *name; if (c->flags & CLIENT_IDENTIFIED) @@ -2167,6 +2169,14 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) c->flags |= flags; log_debug("client %p IDENTIFY_FLAGS %#x", c, flags); break; + case MSG_IDENTIFY_LONGFLAGS: + if (datalen != sizeof longflags) + fatalx("bad MSG_IDENTIFY_LONGFLAGS size"); + memcpy(&longflags, data, sizeof longflags); + c->flags |= longflags; + log_debug("client %p IDENTIFY_LONGFLAGS %#llx", c, + (unsigned long long)longflags); + break; case MSG_IDENTIFY_TERM: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TERM string"); diff --git a/tmux.c b/tmux.c index a9aec43d..b7fc5121 100644 --- a/tmux.c +++ b/tmux.c @@ -331,8 +331,8 @@ main(int argc, char **argv) char *path = NULL, *label = NULL; char *cause, **var; const char *s, *shell, *cwd; - int opt, flags = 0, keys; - int feat = 0; + int opt, keys, feat = 0; + uint64_t flags = 0; const struct options_table_entry *oe; if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL && diff --git a/tmux.h b/tmux.h index 9fb3830a..dd8ac2c1 100644 --- a/tmux.h +++ b/tmux.h @@ -496,6 +496,7 @@ enum msgtype { MSG_IDENTIFY_CWD, MSG_IDENTIFY_FEATURES, MSG_IDENTIFY_STDOUT, + MSG_IDENTIFY_LONGFLAGS, MSG_COMMAND = 200, MSG_DETACH, @@ -2331,7 +2332,7 @@ void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...); void cmd_wait_for_flush(void); /* client.c */ -int client_main(struct event_base *, int, char **, int, int); +int client_main(struct event_base *, int, char **, uint64_t, int); /* key-bindings.c */ struct key_table *key_bindings_get_table(const char *, int); From 51909a107f312a20e77e7c975c3fbc1a98d7bc7e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Sep 2020 06:44:52 +0000 Subject: [PATCH 27/61] Resize screen to the correct size (borders need to be taken off). --- popup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/popup.c b/popup.c index 87658a6f..97acebfd 100644 --- a/popup.c +++ b/popup.c @@ -262,7 +262,7 @@ popup_handle_drag(struct client *c, struct popup_data *pd, pd->sx = m->x - pd->px; pd->sy = m->y - pd->py; - screen_resize(&pd->s, pd->sx, pd->sy, 0); + screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); if (pd->ictx == NULL) popup_write_screen(c, pd); else if (pd->job != NULL) From b9392d5cb16d4c6e95aabb65a0472acc49c2eb6b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Sep 2020 08:41:27 +0000 Subject: [PATCH 28/61] Do not wrap at end of text when positioning at end of match because the length may include trailing spaces. --- window-copy.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/window-copy.c b/window-copy.c index e46c910c..1dc0c293 100644 --- a/window-copy.c +++ b/window-copy.c @@ -109,7 +109,7 @@ static void window_copy_cursor_back_to_indentation( static void window_copy_cursor_end_of_line(struct window_mode_entry *); static void window_copy_other_end(struct window_mode_entry *); static void window_copy_cursor_left(struct window_mode_entry *); -static void window_copy_cursor_right(struct window_mode_entry *); +static void window_copy_cursor_right(struct window_mode_entry *, int); static void window_copy_cursor_up(struct window_mode_entry *, int); static void window_copy_cursor_down(struct window_mode_entry *, int); static void window_copy_cursor_jump(struct window_mode_entry *); @@ -1093,7 +1093,7 @@ window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_right(wme); + window_copy_cursor_right(wme, 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -2962,7 +2962,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex, window_copy_search_marks(wme, &ss, regex, visible_only); if (foundlen != 0) { for (i = 0; i < foundlen; i++) - window_copy_cursor_right(wme); + window_copy_cursor_right(wme, 1); } } window_copy_redraw_screen(wme); @@ -4142,7 +4142,7 @@ window_copy_cursor_left(struct window_mode_entry *wme) } static void -window_copy_cursor_right(struct window_mode_entry *wme) +window_copy_cursor_right(struct window_mode_entry *wme, int all) { struct window_copy_mode_data *data = wme->data; u_int px, py, yy, cx, cy; @@ -4150,7 +4150,7 @@ window_copy_cursor_right(struct window_mode_entry *wme) py = screen_hsize(data->backing) + data->cy - data->oy; yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1; - if (data->screen.sel != NULL && data->rectflag) + if (all || (data->screen.sel != NULL && data->rectflag)) px = screen_size_x(&data->screen); else px = window_copy_find_length(wme, py); From d6680b94742896c3c3afc31fc9e53c741ef6677b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Sep 2020 15:45:20 +0000 Subject: [PATCH 29/61] Move a sentence to the right command. --- tmux.1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index b5cbafe8..0eeb2367 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3167,6 +3167,9 @@ flag unsets an option, so a session inherits the option from the global options (or with .Fl g , restores a global option to the default). +.Ar value +depends on the option and may be a number, a string, or a flag (on, off, or +omitted to toggle). .Pp The .Fl o @@ -3238,9 +3241,6 @@ includes hooks (omitted by default). .Fl A includes options inherited from a parent set of options, such options are marked with an asterisk. -.Ar value -depends on the option and may be a number, a string, or a flag (on, off, or -omitted to toggle). .El .Pp Available server options are: From f2dfc2759ea13c1e098a21bbedc9ac2c26f0b3b9 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 23 Sep 2020 14:57:33 +0000 Subject: [PATCH 30/61] Escape+Up and the other arrow keys should be kept as Escape+Up and not converted to M-Up. Do not give them the implied meta flag so they don't match the M-Up entry in the output key tree. Fixes problem with vi reported by jsing@. --- tty-keys.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index 036bd91d..59426772 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -95,20 +95,25 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = { { "\033OC", KEYC_RIGHT|KEYC_CURSOR }, { "\033OD", KEYC_LEFT|KEYC_CURSOR }, - { "\033\033OA", KEYC_UP|KEYC_CURSOR|KEYC_META|KEYC_IMPLIED_META }, - { "\033\033OB", KEYC_DOWN|KEYC_CURSOR|KEYC_META|KEYC_IMPLIED_META }, - { "\033\033OC", KEYC_RIGHT|KEYC_CURSOR|KEYC_META|KEYC_IMPLIED_META }, - { "\033\033OD", KEYC_LEFT|KEYC_CURSOR|KEYC_META|KEYC_IMPLIED_META }, - { "\033[A", KEYC_UP|KEYC_CURSOR }, { "\033[B", KEYC_DOWN|KEYC_CURSOR }, { "\033[C", KEYC_RIGHT|KEYC_CURSOR }, { "\033[D", KEYC_LEFT|KEYC_CURSOR }, - { "\033\033[A", KEYC_UP|KEYC_CURSOR|KEYC_META|KEYC_IMPLIED_META }, - { "\033\033[B", KEYC_DOWN|KEYC_CURSOR|KEYC_META|KEYC_IMPLIED_META }, - { "\033\033[C", KEYC_RIGHT|KEYC_CURSOR|KEYC_META|KEYC_IMPLIED_META }, - { "\033\033[D", KEYC_LEFT|KEYC_CURSOR|KEYC_META|KEYC_IMPLIED_META }, + /* + * Meta arrow keys. These do not get the IMPLIED_META flag so they + * don't match the xterm-style meta keys in the output tree - Escape+Up + * should stay as Escape+Up and not become M-Up. + */ + { "\033\033OA", KEYC_UP|KEYC_CURSOR|KEYC_META }, + { "\033\033OB", KEYC_DOWN|KEYC_CURSOR|KEYC_META }, + { "\033\033OC", KEYC_RIGHT|KEYC_CURSOR|KEYC_META }, + { "\033\033OD", KEYC_LEFT|KEYC_CURSOR|KEYC_META }, + + { "\033\033[A", KEYC_UP|KEYC_CURSOR|KEYC_META }, + { "\033\033[B", KEYC_DOWN|KEYC_CURSOR|KEYC_META }, + { "\033\033[C", KEYC_RIGHT|KEYC_CURSOR|KEYC_META }, + { "\033\033[D", KEYC_LEFT|KEYC_CURSOR|KEYC_META }, /* Other (xterm) "cursor" keys. */ { "\033OH", KEYC_HOME }, From ebf27f69006bacdc9a87ff531dfac8be9f52316e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 25 Sep 2020 09:41:41 +0100 Subject: [PATCH 31/61] Mention build dependencies, based on a change from Mateusz Urbanek. --- .github/README.md | 3 +++ README | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/README.md b/.github/README.md index 2b299cc5..f41ff7e5 100644 --- a/.github/README.md +++ b/.github/README.md @@ -14,6 +14,9 @@ page](https://github.com/libevent/libevent/releases/latest). It also depends on [ncurses](https://www.gnu.org/software/ncurses/), available from [this page](https://invisible-mirror.net/archives/ncurses/). +To build tmux, a C compiler (for example gcc or clang), make and a suitable +yacc (yacc or bison) are needed. + ## Installation ### From release tarball diff --git a/README b/README index 4f577060..e19cf90d 100644 --- a/README +++ b/README @@ -16,6 +16,9 @@ It also depends on ncurses, available from: https://invisible-mirror.net/archives/ncurses/ +To build tmux, a C compiler (for example gcc or clang), make and a suitable +yacc (yacc or bison) are needed. + * Installation To build and install tmux from a release tarball, use: From e2e5169f84723e3d8e34b7915f723ebbad3bb140 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 25 Sep 2020 09:43:35 +0100 Subject: [PATCH 32/61] Also pkg-config. --- .github/README.md | 4 ++-- README | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/README.md b/.github/README.md index f41ff7e5..c1c35534 100644 --- a/.github/README.md +++ b/.github/README.md @@ -14,8 +14,8 @@ page](https://github.com/libevent/libevent/releases/latest). It also depends on [ncurses](https://www.gnu.org/software/ncurses/), available from [this page](https://invisible-mirror.net/archives/ncurses/). -To build tmux, a C compiler (for example gcc or clang), make and a suitable -yacc (yacc or bison) are needed. +To build tmux, a C compiler (for example gcc or clang), make, pkg-config and a +suitable yacc (yacc or bison) are needed. ## Installation diff --git a/README b/README index e19cf90d..730b9a30 100644 --- a/README +++ b/README @@ -16,8 +16,8 @@ It also depends on ncurses, available from: https://invisible-mirror.net/archives/ncurses/ -To build tmux, a C compiler (for example gcc or clang), make and a suitable -yacc (yacc or bison) are needed. +To build tmux, a C compiler (for example gcc or clang), make, pkg-config and a +suitable yacc (yacc or bison) are needed. * Installation From 4f638c0e31240b2fed9aa5035c25dc08e309ae15 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 25 Sep 2020 10:04:52 +0100 Subject: [PATCH 33/61] Check if UNIX 03 is needed for CMSG_DATA, for newer Solaris. From Eric N Vander Weele. --- configure.ac | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index f89c590b..82be9254 100644 --- a/configure.ac +++ b/configure.ac @@ -328,8 +328,9 @@ AC_SEARCH_LIBS(inet_ntoa, nsl) AC_SEARCH_LIBS(socket, socket) AC_CHECK_LIB(xnet, socket) -# Check for CMSG_DATA. Some platforms require _XOPEN_SOURCE_EXTENDED (for -# example see xopen_networking(7) on HP-UX). +# Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95 +# (_XOPEN_SOURCE and _XOPEN_SOURCE_EXTENDED) (see xopen_networking(7)). On +# others, UNIX 03 (_XOPEN_SOURCE 600, see standards(7) on Solaris). XOPEN_DEFINES= AC_MSG_CHECKING(for CMSG_DATA) AC_EGREP_CPP( @@ -362,6 +363,25 @@ if test "x$found_cmsg_data" = xno; then AC_MSG_RESULT($found_cmsg_data) if test "x$found_cmsg_data" = xyes; then XOPEN_DEFINES="-D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED" + fi +fi +if test "x$found_cmsg_data" = xno; then + AC_MSG_CHECKING(if CMSG_DATA needs _XOPEN_SOURCE 600) + AC_EGREP_CPP( + yes, + [ + #define _XOPEN_SOURCE 600 + #include + #ifdef CMSG_DATA + yes + #endif + ], + found_cmsg_data=yes, + found_cmsg_data=no + ) + AC_MSG_RESULT($found_cmsg_data) + if test "x$found_cmsg_data" = xyes; then + XOPEN_DEFINES="-D_XOPEN_SOURCE=600" else AC_MSG_ERROR("CMSG_DATA not found") fi From 5f50e7d942937fc43baeac297fa7f63c910e0939 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 30 Sep 2020 13:26:31 +0100 Subject: [PATCH 34/61] Trim "s from process names, from Gregory Pakosz. --- names.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/names.c b/names.c index 07c689d1..f437b53e 100644 --- a/names.c +++ b/names.c @@ -107,7 +107,7 @@ check_window_name(struct window *w) char * default_window_name(struct window *w) { - char *cmd, *s; + char *cmd, *s; cmd = cmd_stringify_argv(w->active->argc, w->active->argv); if (cmd != NULL && *cmd != '\0') @@ -142,6 +142,10 @@ parse_window_name(const char *in) char *copy, *name, *ptr; name = copy = xstrdup(in); + if (*name == '"') + name++; + name[strcspn (name, "\"")] = '\0'; + if (strncmp(name, "exec ", (sizeof "exec ") - 1) == 0) name = name + (sizeof "exec ") - 1; From f43e3e5b4f4df398df39cf870e5c6b0ef2fec0ec Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 30 Sep 2020 13:36:58 +0100 Subject: [PATCH 35/61] Next version. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index eb1a9594..82be9254 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.2-rc2) +AC_INIT([tmux], next-3.3) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From 9ec68db74fda97cc23bcc1e6310a75e4839fcf10 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 30 Sep 2020 14:17:27 +0100 Subject: [PATCH 36/61] Correct break-pane default format, from Gregory Pakosz. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 9b8f4420..56c7db7f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1858,7 +1858,7 @@ The .Fl P option prints information about the new window after it has been created. By default, it uses the format -.Ql #{session_name}:#{window_index} +.Ql #{session_name}:#{window_index}.#{pane_index} but a different format may be specified with .Fl F . .It Xo Ic capture-pane From 92a2e7411f26257bba32317793eac3d42a330b6b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 1 Oct 2020 09:01:42 +0100 Subject: [PATCH 37/61] Link to install wiki page. --- .github/README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index c1c35534..5590d0b2 100644 --- a/.github/README.md +++ b/.github/README.md @@ -19,6 +19,12 @@ suitable yacc (yacc or bison) are needed. ## Installation +### Binary packages + +Some platforms provide binary packages for tmux, although these are sometimes +out of date. Examples are listed on +[this page](https://github.com/tmux/tmux/wiki/Installing). + ### From release tarball To build and install tmux from a release tarball, use: @@ -31,6 +37,9 @@ sudo make install tmux can use the utempter library to update utmp(5), if it is installed - run configure with `--enable-utempter` to enable this. +For more detailed instructions on building and installing tmux, see +[this page](https://github.com/tmux/tmux/wiki/Installing). + ### From version control To get and build the latest from version control - note that this requires @@ -72,7 +81,7 @@ And a bash(1) completion file at: https://github.com/imomaliev/tmux-bash-completion -For debugging, run tmux with `-v` or `-vv` to generate server and client log +For debugging, run tmux with `-v` or `-vv` to generate server and client log files in the current directory. ## Support From c8f3736b07a0ae4c73bd6517ab8c8596d74c1c67 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 5 Oct 2020 09:53:01 +0000 Subject: [PATCH 38/61] Use the setal capability as well as (tmux's) Setulc. --- tmux.1 | 4 ++-- tmux.h | 2 ++ tty-features.c | 3 ++- tty-term.c | 2 ++ tty.c | 32 +++++++++++++++++++++++--------- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/tmux.1 b/tmux.1 index 0eeb2367..c2fecd9d 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5897,8 +5897,8 @@ Set a styled underscore. The single parameter is one of: 0 for no underscore, 1 for normal underscore, 2 for double underscore, 3 for curly underscore, 4 for dotted underscore and 5 for dashed underscore. -.It Em \&Setulc -Set the underscore colour. +.It Em \&Setulc , \&ol +Set the underscore colour or reset to the default. The argument is (red * 65536) + (green * 256) + blue where each is between 0 and 255. .It Em \&Ss , Se diff --git a/tmux.h b/tmux.h index dd8ac2c1..4ba2c13b 100644 --- a/tmux.h +++ b/tmux.h @@ -448,6 +448,7 @@ enum tty_code_code { TTYC_KUP6, TTYC_KUP7, TTYC_MS, + TTYC_OL, TTYC_OP, TTYC_REV, TTYC_RGB, @@ -459,6 +460,7 @@ enum tty_code_code { TTYC_SE, TTYC_SETAB, TTYC_SETAF, + TTYC_SETAL, TTYC_SETRGBB, TTYC_SETRGBF, TTYC_SETULC, diff --git a/tty-features.c b/tty-features.c index 5891e2c3..f167a2d3 100644 --- a/tty-features.c +++ b/tty-features.c @@ -112,6 +112,7 @@ static const struct tty_feature tty_feature_overline = { static const char *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", NULL }; static const struct tty_feature tty_feature_usstyle = { @@ -336,7 +337,7 @@ tty_default_features(int *feat, const char *name, u_int version) "256,RGB,bpaste,clipboard,strikethrough,title" { .name = "mintty", .features = TTY_FEATURES_BASE_MODERN_XTERM - ",ccolour,cstyle,extkeys,margins,overline" + ",ccolour,cstyle,extkeys,margins,overline,usstyle" }, { .name = "tmux", .features = TTY_FEATURES_BASE_MODERN_XTERM diff --git a/tty-term.c b/tty-term.c index 5aac1e0c..9161ad01 100644 --- a/tty-term.c +++ b/tty-term.c @@ -245,6 +245,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_OL] = { TTYCODE_STRING, "ol" }, [TTYC_OP] = { TTYCODE_STRING, "op" }, [TTYC_REV] = { TTYCODE_STRING, "rev" }, [TTYC_RGB] = { TTYCODE_FLAG, "RGB" }, @@ -255,6 +256,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_RMKX] = { TTYCODE_STRING, "rmkx" }, [TTYC_SETAB] = { TTYCODE_STRING, "setab" }, [TTYC_SETAF] = { TTYCODE_STRING, "setaf" }, + [TTYC_SETAL] = { TTYCODE_STRING, "setal" }, [TTYC_SETRGBB] = { TTYCODE_STRING, "setrgbb" }, [TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" }, [TTYC_SETULC] = { TTYCODE_STRING, "Setulc" }, diff --git a/tty.c b/tty.c index 30b94e04..bcbccca6 100644 --- a/tty.c +++ b/tty.c @@ -2543,7 +2543,7 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) /* Is this a 24-bit or 256-colour colour? */ if (gc->fg & COLOUR_FLAG_RGB || gc->fg & COLOUR_FLAG_256) { if (tty_try_colour(tty, gc->fg, "38") == 0) - goto save_fg; + goto save; /* Should not get here, already converted in tty_check_fg. */ return; } @@ -2555,13 +2555,13 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) tty_puts(tty, s); } else tty_putcode1(tty, TTYC_SETAF, gc->fg - 90 + 8); - goto save_fg; + goto save; } /* Otherwise set the foreground colour. */ tty_putcode1(tty, TTYC_SETAF, gc->fg); -save_fg: +save: /* Save the new values in the terminal current cell. */ tc->fg = gc->fg; } @@ -2575,7 +2575,7 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) /* Is this a 24-bit or 256-colour colour? */ if (gc->bg & COLOUR_FLAG_RGB || gc->bg & COLOUR_FLAG_256) { if (tty_try_colour(tty, gc->bg, "48") == 0) - goto save_bg; + goto save; /* Should not get here, already converted in tty_check_bg. */ return; } @@ -2587,13 +2587,13 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) tty_puts(tty, s); } else tty_putcode1(tty, TTYC_SETAB, gc->bg - 90 + 8); - goto save_bg; + goto save; } /* Otherwise set the background colour. */ tty_putcode1(tty, TTYC_SETAB, gc->bg); -save_bg: +save: /* Save the new values in the terminal current cell. */ tc->bg = gc->bg; } @@ -2605,20 +2605,34 @@ tty_colours_us(struct tty *tty, const struct grid_cell *gc) u_int c; u_char r, g, b; + /* Clear underline colour. */ + if (gc->us == 0) { + tty_putcode(tty, TTYC_OL); + goto save; + } + /* Must be an RGB colour - this should never happen. */ if (~gc->us & COLOUR_FLAG_RGB) return; /* - * Setulc follows the ncurses(3) one argument "direct colour" + * Setulc and setal follows the ncurses(3) one argument "direct colour" * capability format. Calculate the colour value. */ colour_split_rgb(gc->us, &r, &g, &b); c = (65536 * r) + (256 * g) + b; - /* Write the colour. */ - tty_putcode1(tty, TTYC_SETULC, c); + /* + * Write the colour. Only use setal if the RGB flag is set because the + * non-RGB version may be wrong. + */ + if (tty_term_has(tty->term, TTYC_SETULC)) + tty_putcode1(tty, TTYC_SETULC, c); + else if (tty_term_has(tty->term, TTYC_SETAL) && + tty_term_has(tty->term, TTYC_RGB)) + tty_putcode1(tty, TTYC_SETAL, c); +save: /* Save the new values in the terminal current cell. */ tc->us = gc->us; } From 1479e32e1af82826dbc5a323e22f1a96eb0a692b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 5 Oct 2020 10:00:51 +0000 Subject: [PATCH 39/61] Tidy the resize code, merge some common bits and add some comments. From "Mike" in GitHub issue 2392. --- resize.c | 375 +++++++++++++++++++++++++------------------------------ 1 file changed, 167 insertions(+), 208 deletions(-) diff --git a/resize.c b/resize.c index d6e6dce2..172cbcb5 100644 --- a/resize.c +++ b/resize.c @@ -92,130 +92,156 @@ ignore_client_size(struct client *c) return (0); } -void -default_window_size(struct client *c, struct session *s, struct window *w, - u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type) +static u_int +clients_with_window(struct window *w) { struct client *loop; - u_int cx, cy, n; - const char *value; + u_int n = 0; - if (type == -1) - type = options_get_number(global_w_options, "window-size"); - switch (type) { - case WINDOW_SIZE_LARGEST: + TAILQ_FOREACH(loop, &clients, entry) { + if (ignore_client_size(loop) || !session_has(loop->session, w)) + continue; + if (++n > 1) + break; + } + return (n); +} + +static int +clients_calculate_size(int type, int current, struct session *s, + struct window *w, int (*skip_client)(struct client *, int, int, + struct session *, struct window *), u_int *sx, u_int *sy, u_int *xpixel, + u_int *ypixel) +{ + struct client *loop; + u_int cx, cy, n = 0; + + /* Manual windows do not have their size changed based on a client. */ + if (type == WINDOW_SIZE_MANUAL) + return (0); + + /* + * Start comparing with 0 for largest and UINT_MAX for smallest or + * latest. + */ + if (type == WINDOW_SIZE_LARGEST) *sx = *sy = 0; - *xpixel = *ypixel = 0; - TAILQ_FOREACH(loop, &clients, entry) { - if (ignore_client_size(loop)) - continue; - if (w != NULL && !session_has(loop->session, w)) - continue; - if (w == NULL && loop->session != s) - continue; + else + *sx = *sy = UINT_MAX; + *xpixel = *ypixel = 0; - cx = loop->tty.sx; - cy = loop->tty.sy - status_line_size(loop); + /* + * For latest, count the number of clients with this window. We only + * care if there is more than one. + */ + if (type == WINDOW_SIZE_LATEST) + n = clients_with_window(w); + /* Loop over the clients and work out the size. */ + TAILQ_FOREACH(loop, &clients, entry) { + if (ignore_client_size(loop)) + continue; + if (skip_client(loop, type, current, s, w)) + continue; + + /* + * If there are multiple clients attached, only accept the + * latest client; otherwise let the only client be chosen as + * for smallest. + */ + if (type == WINDOW_SIZE_LATEST && n > 1 && loop != w->latest) + continue; + + /* Work out this client's size. */ + cx = loop->tty.sx; + cy = loop->tty.sy - status_line_size(loop); + + /* + * If it is larger or smaller than the best so far, update the + * new size. + */ + if (type == WINDOW_SIZE_LARGEST) { if (cx > *sx) *sx = cx; if (cy > *sy) *sy = cy; - - if (loop->tty.xpixel > *xpixel && - loop->tty.ypixel > *ypixel) { - *xpixel = loop->tty.xpixel; - *ypixel = loop->tty.ypixel; - } - } - if (*sx == 0 || *sy == 0) - goto manual; - break; - case WINDOW_SIZE_SMALLEST: - *sx = *sy = UINT_MAX; - *xpixel = *ypixel = 0; - TAILQ_FOREACH(loop, &clients, entry) { - if (ignore_client_size(loop)) - continue; - if (w != NULL && !session_has(loop->session, w)) - continue; - if (w == NULL && loop->session != s) - continue; - - cx = loop->tty.sx; - cy = loop->tty.sy - status_line_size(loop); - + } else { if (cx < *sx) *sx = cx; if (cy < *sy) *sy = cy; - - if (loop->tty.xpixel > *xpixel && - loop->tty.ypixel > *ypixel) { - *xpixel = loop->tty.xpixel; - *ypixel = loop->tty.ypixel; - } } - if (*sx == UINT_MAX || *sy == UINT_MAX) - goto manual; - break; - case WINDOW_SIZE_LATEST: + if (loop->tty.xpixel > *xpixel && loop->tty.ypixel > *ypixel) { + *xpixel = loop->tty.xpixel; + *ypixel = loop->tty.ypixel; + } + } + + /* Return whether a suitable size was found. */ + if (type == WINDOW_SIZE_LARGEST) + return (*sx != 0 && *sy != 0); + return (*sx != UINT_MAX && *sy != UINT_MAX); +} + +static int +default_window_size_skip_client (struct client *loop, int type, + __unused int current, struct session *s, struct window *w) +{ + /* + * Latest checks separately, so do not check here. Otherwise only + * include clients where the session contains the window or where the + * session is the given session. + */ + if (type == WINDOW_SIZE_LATEST) + return (0); + if (w != NULL && !session_has(loop->session, w)) + return (1); + if (w == NULL && loop->session != s) + return (1); + return (0); +} + +void +default_window_size(struct client *c, struct session *s, struct window *w, + u_int *sx, u_int *sy, u_int *xpixel, u_int *ypixel, int type) +{ + const char *value; + + /* Get type if not provided. */ + if (type == -1) + type = options_get_number(global_w_options, "window-size"); + + /* + * Latest clients can use the given client if suitable. If there is no + * client and no window, use the default size as for manual type. + */ + if (type == WINDOW_SIZE_LATEST) { if (c != NULL && !ignore_client_size(c)) { *sx = c->tty.sx; *sy = c->tty.sy - status_line_size(c); *xpixel = c->tty.xpixel; - *ypixel = c->tty.ypixel; - } else { - if (w == NULL) - goto manual; - n = 0; - TAILQ_FOREACH(loop, &clients, entry) { - if (!ignore_client_size(loop) && - session_has(loop->session, w)) { - if (++n > 1) - break; - } - } - *sx = *sy = UINT_MAX; - *xpixel = *ypixel = 0; - TAILQ_FOREACH(loop, &clients, entry) { - if (ignore_client_size(loop)) - continue; - if (n > 1 && loop != w->latest) - continue; - s = loop->session; - - cx = loop->tty.sx; - cy = loop->tty.sy - status_line_size(loop); - - if (cx < *sx) - *sx = cx; - if (cy < *sy) - *sy = cy; - - if (loop->tty.xpixel > *xpixel && - loop->tty.ypixel > *ypixel) { - *xpixel = loop->tty.xpixel; - *ypixel = loop->tty.ypixel; - } - } - if (*sx == UINT_MAX || *sy == UINT_MAX) - goto manual; + *ypixel = c->tty.ypixel; + goto done; } - break; - case WINDOW_SIZE_MANUAL: - goto manual; + if (w == NULL) + type = WINDOW_SIZE_MANUAL; } - goto done; -manual: - value = options_get_string(s->options, "default-size"); - if (sscanf(value, "%ux%u", sx, sy) != 2) { - *sx = 80; - *sy = 24; + /* + * Look for a client to base the size on. If none exists (or the type + * is manual), use the default-size option. + */ + if (!clients_calculate_size(type, 0, s, w, + default_window_size_skip_client, sx, sy, xpixel, ypixel)) { + value = options_get_string(s->options, "default-size"); + if (sscanf(value, "%ux%u", sx, sy) != 2) { + *sx = 80; + *sy = 24; + } } done: + /* Make sure the limits are enforced. */ if (*sx < WINDOW_MINIMUM) *sx = WINDOW_MINIMUM; if (*sx > WINDOW_MAXIMUM) @@ -226,127 +252,50 @@ done: *sy = WINDOW_MAXIMUM; } +static int +recalculate_size_skip_client(struct client *loop, __unused int type, + int current, __unused struct session *s, struct window *w) +{ + /* + * If the current flag is set, then skip any client where this window + * is not the current window - this is used for aggressive-resize. + * Otherwise skip any session that doesn't contain the window. + */ + if (current) + return (loop->session->curw->window != w); + return (session_has(loop->session, w) == 0); +} + void recalculate_size(struct window *w, int now) { - struct session *s; - struct client *c; - u_int sx, sy, cx, cy, xpixel = 0, ypixel = 0, n; - int type, current, has, changed; + u_int sx, sy, xpixel = 0, ypixel = 0; + int type, current, changed; + /* + * Do not attempt to resize windows which have no pane, they must be on + * the way to destruction. + */ if (w->active == NULL) return; log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy); + /* + * Type is manual, smallest, largest, latest. Current is the + * aggressive-resize option (do not resize based on clients where the + * window is not the current window). + */ type = options_get_number(w->options, "window-size"); current = options_get_number(w->options, "aggressive-resize"); - changed = 1; - switch (type) { - case WINDOW_SIZE_LARGEST: - sx = sy = 0; - TAILQ_FOREACH(c, &clients, entry) { - if (ignore_client_size(c)) - continue; - s = c->session; + /* Look for a suitable client and get the new size. */ + changed = clients_calculate_size(type, current, NULL, w, + recalculate_size_skip_client, &sx, &sy, &xpixel, &ypixel); - if (current) - has = (s->curw->window == w); - else - has = session_has(s, w); - if (!has) - continue; - - cx = c->tty.sx; - cy = c->tty.sy - status_line_size(c); - - if (cx > sx) - sx = cx; - if (cy > sy) - sy = cy; - - if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) { - xpixel = c->tty.xpixel; - ypixel = c->tty.ypixel; - } - } - if (sx == 0 || sy == 0) - changed = 0; - break; - case WINDOW_SIZE_SMALLEST: - sx = sy = UINT_MAX; - TAILQ_FOREACH(c, &clients, entry) { - if (ignore_client_size(c)) - continue; - s = c->session; - - if (current) - has = (s->curw->window == w); - else - has = session_has(s, w); - if (!has) - continue; - - cx = c->tty.sx; - cy = c->tty.sy - status_line_size(c); - - if (cx < sx) - sx = cx; - if (cy < sy) - sy = cy; - - if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) { - xpixel = c->tty.xpixel; - ypixel = c->tty.ypixel; - } - } - if (sx == UINT_MAX || sy == UINT_MAX) - changed = 0; - break; - case WINDOW_SIZE_LATEST: - n = 0; - TAILQ_FOREACH(c, &clients, entry) { - if (!ignore_client_size(c) && - session_has(c->session, w)) { - if (++n > 1) - break; - } - } - sx = sy = UINT_MAX; - TAILQ_FOREACH(c, &clients, entry) { - if (ignore_client_size(c)) - continue; - if (n > 1 && c != w->latest) - continue; - s = c->session; - - if (current) - has = (s->curw->window == w); - else - has = session_has(s, w); - if (!has) - continue; - - cx = c->tty.sx; - cy = c->tty.sy - status_line_size(c); - - if (cx < sx) - sx = cx; - if (cy < sy) - sy = cy; - - if (c->tty.xpixel > xpixel && c->tty.ypixel > ypixel) { - xpixel = c->tty.xpixel; - ypixel = c->tty.ypixel; - } - } - if (sx == UINT_MAX || sy == UINT_MAX) - changed = 0; - break; - case WINDOW_SIZE_MANUAL: - changed = 0; - break; - } + /* + * Make sure the size has actually changed. If the window has already + * got a resize scheduled, then use the new size; otherwise the old. + */ if (w->flags & WINDOW_RESIZE) { if (!now && changed && w->new_sx == sx && w->new_sy == sy) changed = 0; @@ -355,10 +304,20 @@ recalculate_size(struct window *w, int now) changed = 0; } + /* + * If the size hasn't changed, update the window offset but not the + * size. + */ if (!changed) { tty_update_window_offset(w); return; } + + /* + * If the now flag is set or if the window is sized manually, change + * the size immediately. Otherwise set the flag and it will be done + * later. + */ log_debug("%s: @%u new size %u,%u", __func__, w->id, sx, sy); if (now || type == WINDOW_SIZE_MANUAL) resize_window(w, sx, sy, xpixel, ypixel); From 8d9ea1b97ca1f70191808df03f9fbe9f93912773 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 5 Oct 2020 11:04:40 +0000 Subject: [PATCH 40/61] Trim "s from process names; also fix a default format in man page. --- names.c | 6 +++++- tmux.1 | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/names.c b/names.c index 07c689d1..f437b53e 100644 --- a/names.c +++ b/names.c @@ -107,7 +107,7 @@ check_window_name(struct window *w) char * default_window_name(struct window *w) { - char *cmd, *s; + char *cmd, *s; cmd = cmd_stringify_argv(w->active->argc, w->active->argv); if (cmd != NULL && *cmd != '\0') @@ -142,6 +142,10 @@ parse_window_name(const char *in) char *copy, *name, *ptr; name = copy = xstrdup(in); + if (*name == '"') + name++; + name[strcspn (name, "\"")] = '\0'; + if (strncmp(name, "exec ", (sizeof "exec ") - 1) == 0) name = name + (sizeof "exec ") - 1; diff --git a/tmux.1 b/tmux.1 index c2fecd9d..a39237d5 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1858,7 +1858,7 @@ The .Fl P option prints information about the new window after it has been created. By default, it uses the format -.Ql #{session_name}:#{window_index} +.Ql #{session_name}:#{window_index}.#{pane_index} but a different format may be specified with .Fl F . .It Xo Ic capture-pane From 680e7a382f9ea03568aceaf71f3fbb8ced83f76c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 6 Oct 2020 08:18:42 +0100 Subject: [PATCH 41/61] glibc's malloc is very bad about returning memory from the kernel, add a call to its malloc_trim to prompt it to do so. Reported by Sarunas Valaskevicius. --- compat.h | 4 ++++ configure.ac | 25 +++++++++++++++++++++++++ grid.c | 3 +++ 3 files changed, 32 insertions(+) diff --git a/compat.h b/compat.h index 4efad6ee..b213336f 100644 --- a/compat.h +++ b/compat.h @@ -27,6 +27,10 @@ #include #include +#ifdef HAVE_MALLOC_TRIM +#include +#endif + #ifdef HAVE_UTF8PROC #include #endif diff --git a/configure.ac b/configure.ac index 82be9254..8f26cabd 100644 --- a/configure.ac +++ b/configure.ac @@ -328,6 +328,31 @@ AC_SEARCH_LIBS(inet_ntoa, nsl) AC_SEARCH_LIBS(socket, socket) AC_CHECK_LIB(xnet, socket) +# Check if using glibc and have malloc_trim(3). The glibc free(3) is pretty bad +# about returning memory to the kernel unless the application tells it when to +# with malloc_trim(3). +AC_MSG_CHECKING(if free doesn't work very well) +AC_LINK_IFELSE([AC_LANG_SOURCE( + [ + #include + #ifdef __GLIBC__ + #include + int main(void) { + malloc_trim (0); + exit(0); + } + #else + no + #endif + ])], + found_malloc_trim=yes, + found_malloc_trim=no +) +AC_MSG_RESULT($found_malloc_trim) +if test "x$found_malloc_trim" = xyes; then + AC_DEFINE(HAVE_MALLOC_TRIM) +fi + # Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95 # (_XOPEN_SOURCE and _XOPEN_SOURCE_EXTENDED) (see xopen_networking(7)). On # others, UNIX 03 (_XOPEN_SOURCE 600, see standards(7) on Solaris). diff --git a/grid.c b/grid.c index 96302fc3..90e1a0a5 100644 --- a/grid.c +++ b/grid.c @@ -265,6 +265,9 @@ grid_free_lines(struct grid *gd, u_int py, u_int ny) for (yy = py; yy < py + ny; yy++) grid_free_line(gd, yy); +#ifdef HAVE_MALLOC_TRIM + malloc_trim(0); +#endif } /* Create a new grid. */ From e369f646692e411961a4a7442d71736932929afb Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 6 Oct 2020 07:36:05 +0000 Subject: [PATCH 42/61] Add a state struct to store working state during format expansion instead of modiyfing the format tree. Use this to disable nested job expansion so that the result of #() is not expanded again. Reported by Chas J Owens IV, GitHub issue 2390. --- format.c | 401 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 229 insertions(+), 172 deletions(-) diff --git a/format.c b/format.c index 66107b68..e864adc4 100644 --- a/format.c +++ b/format.c @@ -38,15 +38,18 @@ * string. */ -static char *format_job_get(struct format_tree *, const char *); -static void format_job_timer(int, short, void *); +struct format_expand_state; -static int format_replace(struct format_tree *, const char *, size_t, - char **, size_t *, size_t *); +static char *format_job_get(struct format_expand_state *, const char *); +static void format_job_timer(int, short, void *); +static char *format_expand1(struct format_expand_state *, const char *); +static int format_replace(struct format_expand_state *, const char *, + size_t, char **, size_t *, size_t *); static void format_defaults_session(struct format_tree *, struct session *); static void format_defaults_client(struct format_tree *, struct client *); -static void format_defaults_winlink(struct format_tree *, struct winlink *); +static void format_defaults_winlink(struct format_tree *, + struct winlink *); /* Entry in format job tree. */ struct format_job { @@ -99,11 +102,15 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 10 +/* Format expand flags. */ +#define FORMAT_EXPAND_TIME 0x1 +#define FORMAT_EXPAND_NOJOBS 0x2 + /* Entry in format tree. */ struct format_entry { char *key; char *value; - time_t t; + time_t time; format_cb cb; RB_ENTRY(format_entry) entry; }; @@ -120,8 +127,6 @@ struct format_tree { struct client *client; int flags; u_int tag; - time_t time; - u_int loop; struct mouse_event m; @@ -130,6 +135,14 @@ struct format_tree { static int format_entry_cmp(struct format_entry *, struct format_entry *); RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp); +/* Format expand state. */ +struct format_expand_state { + struct format_tree *ft; + u_int loop; + time_t time; + int flags; +}; + /* Format modifier. */ struct format_modifier { char modifier[3]; @@ -215,8 +228,10 @@ format_logging(struct format_tree *ft) /* Log a message if verbose. */ static void printflike(3, 4) -format_log1(struct format_tree *ft, const char *from, const char *fmt, ...) +format_log1(struct format_expand_state *es, const char *from, const char *fmt, + ...) { + struct format_tree *ft = es->ft; va_list ap; char *s; static const char spaces[] = " "; @@ -230,11 +245,22 @@ format_log1(struct format_tree *ft, const char *from, const char *fmt, ...) log_debug("%s: %s", from, s); if (ft->item != NULL && (ft->flags & FORMAT_VERBOSE)) - cmdq_print(ft->item, "#%.*s%s", ft->loop, spaces, s); + cmdq_print(ft->item, "#%.*s%s", es->loop, spaces, s); free(s); } -#define format_log(ft, fmt, ...) format_log1(ft, __func__, fmt, ##__VA_ARGS__) +#define format_log(es, fmt, ...) format_log1(es, __func__, fmt, ##__VA_ARGS__) + +/* Copy expand state. */ +static void +format_copy_state(struct format_expand_state *to, + struct format_expand_state *from, int flags) +{ + to->ft = from->ft; + to->loop = from->loop; + to->time = from->time; + to->flags = from->flags|flag; +} /* Format job update callback. */ static void @@ -304,13 +330,15 @@ format_job_complete(struct job *job) /* Find a job. */ static char * -format_job_get(struct format_tree *ft, const char *cmd) +format_job_get(struct format_expand_state *es, const char *cmd) { - struct format_job_tree *jobs; - struct format_job fj0, *fj; - time_t t; - char *expanded; - int force; + struct format_tree *ft = es->ft; + struct format_job_tree *jobs; + struct format_job fj0, *fj; + time_t t; + char *expanded; + int force; + struct format_expand_state next; if (ft->client == NULL) jobs = &format_jobs; @@ -335,7 +363,7 @@ format_job_get(struct format_tree *ft, const char *cmd) RB_INSERT(format_job_tree, jobs, fj); } - expanded = format_expand(ft, cmd); + expanded = format_expand1(es, cmd); if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) { free((void *)fj->expanded); fj->expanded = xstrdup(expanded); @@ -357,12 +385,12 @@ format_job_get(struct format_tree *ft, const char *cmd) fj->last = t; fj->updated = 0; } + free(expanded); if (ft->flags & FORMAT_STATUS) fj->status = 1; - - free(expanded); - return (format_expand(ft, fj->out)); + format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS); + return (format_expand1(&next, fj->out)); } /* Remove old jobs. */ @@ -1211,7 +1239,6 @@ format_create(struct client *c, struct cmdq_item *item, int tag, int flags) ft->tag = tag; ft->flags = flags; - ft->time = time(NULL); format_add(ft, "version", "%s", getversion()); format_add_cb(ft, "host", format_cb_host); @@ -1261,8 +1288,8 @@ format_each(struct format_tree *ft, void (*cb)(const char *, const char *, char s[64]; RB_FOREACH(fe, format_entry_tree, &ft->tree) { - if (fe->t != 0) { - xsnprintf(s, sizeof s, "%lld", (long long)fe->t); + if (fe->time != 0) { + xsnprintf(s, sizeof s, "%lld", (long long)fe->time); cb(fe->key, s, arg); } else { if (fe->value == NULL && fe->cb != NULL) { @@ -1295,7 +1322,7 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) } fe->cb = NULL; - fe->t = 0; + fe->time = 0; va_start(ap, fmt); xvasprintf(&fe->value, fmt, ap); @@ -1320,7 +1347,7 @@ format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) } fe->cb = NULL; - fe->t = tv->tv_sec; + fe->time = tv->tv_sec; fe->value = NULL; } @@ -1344,7 +1371,7 @@ format_add_cb(struct format_tree *ft, const char *key, format_cb cb) } fe->cb = cb; - fe->t = 0; + fe->time = 0; fe->value = NULL; } @@ -1440,8 +1467,8 @@ format_find(struct format_tree *ft, const char *key, int modifiers, fe_find.key = (char *)key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); if (fe != NULL) { - if (fe->t != 0) { - t = fe->t; + if (fe->time != 0) { + t = fe->time; goto found; } if (fe->value == NULL && fe->cb != NULL) { @@ -1563,8 +1590,8 @@ format_skip(const char *s, const char *end) /* Return left and right alternatives separated by commas. */ static int -format_choose(struct format_tree *ft, const char *s, char **left, char **right, - int expand) +format_choose(struct format_expand_state *es, const char *s, char **left, + char **right, int expand) { const char *cp; char *left0, *right0; @@ -1576,9 +1603,9 @@ format_choose(struct format_tree *ft, const char *s, char **left, char **right, right0 = xstrdup(cp + 1); if (expand) { - *left = format_expand(ft, left0); + *left = format_expand1(es, left0); free(left0); - *right = format_expand(ft, right0); + *right = format_expand1(es, right0); free(right0); } else { *left = left0; @@ -1634,7 +1661,8 @@ format_free_modifiers(struct format_modifier *list, u_int count) /* Build modifier list. */ static struct format_modifier * -format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) +format_build_modifiers(struct format_expand_state *es, const char **s, + u_int *count) { const char *cp = *s, *end; struct format_modifier *list = NULL; @@ -1702,7 +1730,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) argv = xcalloc(1, sizeof *argv); value = xstrndup(cp + 1, end - (cp + 1)); - argv[0] = format_expand(ft, value); + argv[0] = format_expand1(es, value); free(value); argc = 1; @@ -1726,7 +1754,7 @@ format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) argv = xreallocarray (argv, argc + 1, sizeof *argv); value = xstrndup(cp, end - cp); - argv[argc++] = format_expand(ft, value); + argv[argc++] = format_expand1(es, value); free(value); cp = end; @@ -1807,25 +1835,28 @@ format_search(struct format_modifier *fm, struct window_pane *wp, const char *s) /* Loop over sessions. */ static char * -format_loop_sessions(struct format_tree *ft, const char *fmt) +format_loop_sessions(struct format_expand_state *es, const char *fmt) { - struct client *c = ft->client; - struct cmdq_item *item = ft->item; - struct format_tree *nft; - char *expanded, *value; - size_t valuelen; - struct session *s; + struct format_tree *ft = es->ft; + struct client *c = ft->client; + struct cmdq_item *item = ft->item; + struct format_tree *nft; + struct format_expand_state next; + char *expanded, *value; + size_t valuelen; + struct session *s; value = xcalloc(1, 1); valuelen = 1; RB_FOREACH(s, sessions, &sessions) { - format_log(ft, "session loop: $%u", s->id); + format_log(es, "session loop: $%u", s->id); nft = format_create(c, item, FORMAT_NONE, ft->flags); - nft->loop = ft->loop; - format_defaults(nft, ft->c, s, NULL, NULL); - expanded = format_expand(nft, fmt); - format_free(nft); + format_defaults(next.ft, ft->c, s, NULL, NULL); + format_copy_state(&next, es, 0); + next.ft = nft; + expanded = format_expand1(&next, fmt); + format_free(next.ft); valuelen += strlen(expanded); value = xrealloc(value, valuelen); @@ -1839,22 +1870,24 @@ format_loop_sessions(struct format_tree *ft, const char *fmt) /* Loop over windows. */ static char * -format_loop_windows(struct format_tree *ft, const char *fmt) +format_loop_windows(struct format_expand_state *es, const char *fmt) { - struct client *c = ft->client; - struct cmdq_item *item = ft->item; - struct format_tree *nft; - char *all, *active, *use, *expanded, *value; - size_t valuelen; - struct winlink *wl; - struct window *w; + struct format_tree *ft = es->ft; + struct client *c = ft->client; + struct cmdq_item *item = ft->item; + struct format_tree *nft; + struct format_expand_state next; + char *all, *active, *use, *expanded, *value; + size_t valuelen; + struct winlink *wl; + struct window *w; if (ft->s == NULL) { - format_log(ft, "window loop but no session"); + format_log(es, "window loop but no session"); return (NULL); } - if (format_choose(ft, fmt, &all, &active, 0) != 0) { + if (format_choose(es, fmt, &all, &active, 0) != 0) { all = xstrdup(fmt); active = NULL; } @@ -1864,15 +1897,16 @@ format_loop_windows(struct format_tree *ft, const char *fmt) RB_FOREACH(wl, winlinks, &ft->s->windows) { w = wl->window; - format_log(ft, "window loop: %u @%u", wl->idx, w->id); + format_log(es, "window loop: %u @%u", wl->idx, w->id); if (active != NULL && wl == ft->s->curw) use = active; else use = all; nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags); - nft->loop = ft->loop; format_defaults(nft, ft->c, ft->s, wl, NULL); - expanded = format_expand(nft, use); + format_copy_state(&next, es, 0); + next.ft = nft; + expanded = format_expand1(&next, use); format_free(nft); valuelen += strlen(expanded); @@ -1890,21 +1924,23 @@ format_loop_windows(struct format_tree *ft, const char *fmt) /* Loop over panes. */ static char * -format_loop_panes(struct format_tree *ft, const char *fmt) +format_loop_panes(struct format_expand_state *es, const char *fmt) { - struct client *c = ft->client; - struct cmdq_item *item = ft->item; - struct format_tree *nft; - char *all, *active, *use, *expanded, *value; - size_t valuelen; - struct window_pane *wp; + struct format_tree *ft = es->ft; + struct client *c = ft->client; + struct cmdq_item *item = ft->item; + struct format_tree *nft; + struct format_expand_state next; + char *all, *active, *use, *expanded, *value; + size_t valuelen; + struct window_pane *wp; if (ft->w == NULL) { - format_log(ft, "pane loop but no window"); + format_log(es, "pane loop but no window"); return (NULL); } - if (format_choose(ft, fmt, &all, &active, 0) != 0) { + if (format_choose(es, fmt, &all, &active, 0) != 0) { all = xstrdup(fmt); active = NULL; } @@ -1913,15 +1949,16 @@ format_loop_panes(struct format_tree *ft, const char *fmt) valuelen = 1; TAILQ_FOREACH(wp, &ft->w->panes, entry) { - format_log(ft, "pane loop: %%%u", wp->id); + format_log(es, "pane loop: %%%u", wp->id); if (active != NULL && wp == ft->w->active) use = active; else use = all; nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags); - nft->loop = ft->loop; format_defaults(nft, ft->c, ft->s, ft->wl, wp); - expanded = format_expand(nft, use); + format_copy_state(&next, es, 0); + next.ft = nft; + expanded = format_expand1(&next, use); format_free(nft); valuelen += strlen(expanded); @@ -1938,15 +1975,15 @@ format_loop_panes(struct format_tree *ft, const char *fmt) } static char * -format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, - const char *copy) +format_replace_expression(struct format_modifier *mexp, + struct format_expand_state *es, const char *copy) { - int argc = mexp->argc; - const char *errstr; - char *endch, *value, *left = NULL, *right = NULL; - int use_fp = 0; - u_int prec = 0; - double mleft, mright, result; + int argc = mexp->argc; + const char *errstr; + char *endch, *value, *left = NULL, *right = NULL; + int use_fp = 0; + u_int prec = 0; + double mleft, mright, result; enum { ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULUS } operator; if (strcmp(mexp->argv[0], "+") == 0) @@ -1961,7 +1998,7 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, strcmp(mexp->argv[0], "m") == 0) operator = MODULUS; else { - format_log(ft, "expression has no valid operator: '%s'", + format_log(es, "expression has no valid operator: '%s'", mexp->argv[0]); goto fail; } @@ -1976,26 +2013,26 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, if (argc >= 3) { prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr); if (errstr != NULL) { - format_log (ft, "expression precision %s: %s", errstr, + format_log(es, "expression precision %s: %s", errstr, mexp->argv[2]); goto fail; } } - if (format_choose(ft, copy, &left, &right, 1) != 0) { - format_log(ft, "expression syntax error"); + if (format_choose(es, copy, &left, &right, 1) != 0) { + format_log(es, "expression syntax error"); goto fail; } mleft = strtod(left, &endch); if (*endch != '\0') { - format_log(ft, "expression left side is invalid: %s", left); + format_log(es, "expression left side is invalid: %s", left); goto fail; } mright = strtod(right, &endch); if (*endch != '\0') { - format_log(ft, "expression right side is invalid: %s", right); + format_log(es, "expression right side is invalid: %s", right); goto fail; } @@ -2003,8 +2040,8 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, mleft = (long long)mleft; mright = (long long)mright; } - format_log(ft, "expression left side is: %.*f", prec, mleft); - format_log(ft, "expression right side is: %.*f", prec, mright); + format_log(es, "expression left side is: %.*f", prec, mleft); + format_log(es, "expression right side is: %.*f", prec, mright); switch (operator) { case ADD: @@ -2027,7 +2064,7 @@ format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, xasprintf(&value, "%.*f", prec, result); else xasprintf(&value, "%.*f", prec, (double)(long long)result); - format_log(ft, "expression result is %s", value); + format_log(es, "expression result is %s", value); free(right); free(left); @@ -2041,31 +2078,34 @@ fail: /* Replace a key. */ static int -format_replace(struct format_tree *ft, const char *key, size_t keylen, +format_replace(struct format_expand_state *es, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { - struct window_pane *wp = ft->wp; - const char *errptr, *copy, *cp, *marker = NULL; - const char *time_format = NULL; - char *copy0, *condition, *found, *new; - char *value, *left, *right; - size_t valuelen; - int modifiers = 0, limit = 0, width = 0, j; - struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; - struct format_modifier **sub = NULL, *mexp = NULL; - u_int i, count, nsub = 0; + struct format_tree *ft = es->ft; + struct window_pane *wp = ft->wp; + const char *errptr, *copy, *cp, *marker = NULL; + const char *time_format = NULL; + char *copy0, *condition, *found, *new; + char *value, *left, *right; + size_t valuelen; + int modifiers = 0, limit = 0, width = 0; + int j; + struct format_modifier *list, *cmp = NULL, *search = NULL; + struct format_modifier **sub = NULL, *mexp = NULL, *fm; + u_int i, count, nsub = 0; + struct format_expand_state next; /* Make a copy of the key. */ copy = copy0 = xstrndup(key, keylen); /* Process modifier list. */ - list = format_build_modifiers(ft, ©, &count); + list = format_build_modifiers(es, ©, &count); for (i = 0; i < count; i++) { fm = &list[i]; if (format_logging(ft)) { - format_log(ft, "modifier %u is %s", i, fm->modifier); + format_log(es, "modifier %u is %s", i, fm->modifier); for (j = 0; j < fm->argc; j++) { - format_log(ft, "modifier %u argument %d: %s", i, + format_log(es, "modifier %u argument %d: %s", i, j, fm->argv[j]); } } @@ -2169,37 +2209,37 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, /* Is this a loop, comparison or condition? */ if (modifiers & FORMAT_SESSIONS) { - value = format_loop_sessions(ft, copy); + value = format_loop_sessions(es, copy); if (value == NULL) goto fail; } else if (modifiers & FORMAT_WINDOWS) { - value = format_loop_windows(ft, copy); + value = format_loop_windows(es, copy); if (value == NULL) goto fail; } else if (modifiers & FORMAT_PANES) { - value = format_loop_panes(ft, copy); + value = format_loop_panes(es, copy); if (value == NULL) goto fail; } else if (search != NULL) { /* Search in pane. */ - new = format_expand(ft, copy); + new = format_expand1(es, copy); if (wp == NULL) { - format_log(ft, "search '%s' but no pane", new); + format_log(es, "search '%s' but no pane", new); value = xstrdup("0"); } else { - format_log(ft, "search '%s' pane %%%u", new, wp->id); + format_log(es, "search '%s' pane %%%u", new, wp->id); value = format_search(fm, wp, new); } free(new); } else if (cmp != NULL) { /* Comparison of left and right. */ - if (format_choose(ft, copy, &left, &right, 1) != 0) { - format_log(ft, "compare %s syntax error: %s", + if (format_choose(es, copy, &left, &right, 1) != 0) { + format_log(es, "compare %s syntax error: %s", cmp->modifier, copy); goto fail; } - format_log(ft, "compare %s left is: %s", cmp->modifier, left); - format_log(ft, "compare %s right is: %s", cmp->modifier, right); + format_log(es, "compare %s left is: %s", cmp->modifier, left); + format_log(es, "compare %s right is: %s", cmp->modifier, right); if (strcmp(cmp->modifier, "||") == 0) { if (format_true(left) || format_true(right)) @@ -2250,11 +2290,11 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, /* Conditional: check first and choose second or third. */ cp = format_skip(copy + 1, ","); if (cp == NULL) { - format_log(ft, "condition syntax error: %s", copy + 1); + format_log(es, "condition syntax error: %s", copy + 1); goto fail; } condition = xstrndup(copy + 1, cp - (copy + 1)); - format_log(ft, "condition is: %s", condition); + format_log(es, "condition is: %s", condition); found = format_find(ft, condition, modifiers, time_format); if (found == NULL) { @@ -2263,32 +2303,32 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, * the expansion doesn't have any effect, then assume * false. */ - found = format_expand(ft, condition); + found = format_expand1(es, condition); if (strcmp(found, condition) == 0) { free(found); found = xstrdup(""); - format_log(ft, "condition '%s' found: %s", + format_log(es, "condition '%s' found: %s", condition, found); } else { - format_log(ft, + format_log(es, "condition '%s' not found; assuming false", condition); } } else - format_log(ft, "condition '%s' found", condition); + format_log(es, "condition '%s' found", condition); - if (format_choose(ft, cp + 1, &left, &right, 0) != 0) { - format_log(ft, "condition '%s' syntax error: %s", + if (format_choose(es, cp + 1, &left, &right, 0) != 0) { + format_log(es, "condition '%s' syntax error: %s", condition, cp + 1); free(found); goto fail; } if (format_true(found)) { - format_log(ft, "condition '%s' is true", condition); - value = format_expand(ft, left); + format_log(es, "condition '%s' is true", condition); + value = format_expand1(es, left); } else { - format_log(ft, "condition '%s' is false", condition); - value = format_expand(ft, right); + format_log(es, "condition '%s' is false", condition); + value = format_expand1(es, right); } free(right); free(left); @@ -2296,41 +2336,44 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, free(condition); free(found); } else if (mexp != NULL) { - value = format_replace_expression(mexp, ft, copy); + value = format_replace_expression(mexp, es, copy); if (value == NULL) value = xstrdup(""); } else { if (strstr(copy, "#{") != 0) { - format_log(ft, "expanding inner format '%s'", copy); - value = format_expand(ft, copy); + format_log(es, "expanding inner format '%s'", copy); + value = format_expand1(es, copy); } else { value = format_find(ft, copy, modifiers, time_format); if (value == NULL) { - format_log(ft, "format '%s' not found", copy); + format_log(es, "format '%s' not found", copy); value = xstrdup(""); - } else - format_log(ft, "format '%s' found: %s", copy, value); + } else { + format_log(es, "format '%s' found: %s", copy, + value); + } } } done: /* Expand again if required. */ if (modifiers & FORMAT_EXPAND) { - new = format_expand(ft, value); + new = format_expand1(es, value); free(value); value = new; } else if (modifiers & FORMAT_EXPANDTIME) { - new = format_expand_time(ft, value); + format_copy_state(&next, es, FORMAT_EXPAND_TIME); + new = format_expand1(&next, value); free(value); value = new; } /* Perform substitution if any. */ for (i = 0; i < nsub; i++) { - left = format_expand(ft, sub[i]->argv[0]); - right = format_expand(ft, sub[i]->argv[1]); + left = format_expand1(es, sub[i]->argv[0]); + right = format_expand1(es, sub[i]->argv[1]); new = format_sub(sub[i], value, left, right); - format_log(ft, "substitute '%s' to '%s': %s", left, right, new); + format_log(es, "substitute '%s' to '%s': %s", left, right, new); free(value); value = new; free(right); @@ -2347,7 +2390,7 @@ done: free(value); value = new; } - format_log(ft, "applied length limit %d: %s", limit, value); + format_log(es, "applied length limit %d: %s", limit, value); } else if (limit < 0) { new = format_trim_right(value, -limit); if (marker != NULL && strcmp(new, value) != 0) { @@ -2357,7 +2400,7 @@ done: free(value); value = new; } - format_log(ft, "applied length limit %d: %s", limit, value); + format_log(es, "applied length limit %d: %s", limit, value); } /* Pad the value if needed. */ @@ -2365,12 +2408,12 @@ done: new = utf8_padcstr(value, width); free(value); value = new; - format_log(ft, "applied padding width %d: %s", width, value); + format_log(es, "applied padding width %d: %s", width, value); } else if (width < 0) { new = utf8_rpadcstr(value, -width); free(value); value = new; - format_log(ft, "applied padding width %d: %s", width, value); + format_log(es, "applied padding width %d: %s", width, value); } /* Replace with the length if needed. */ @@ -2378,7 +2421,7 @@ done: xasprintf(&new, "%zu", strlen(value)); free(value); value = new; - format_log(ft, "replacing with length: %s", new); + format_log(es, "replacing with length: %s", new); } /* Expand the buffer and copy in the value. */ @@ -2390,7 +2433,7 @@ done: memcpy(*buf + *off, value, valuelen); *off += valuelen; - format_log(ft, "replaced '%s' with '%s'", copy0, value); + format_log(es, "replaced '%s' with '%s'", copy0, value); free(value); free(sub); @@ -2399,7 +2442,7 @@ done: return (0); fail: - format_log(ft, "failed %s", copy0); + format_log(es, "failed %s", copy0); free(sub); format_free_modifiers(list, count); @@ -2409,32 +2452,35 @@ fail: /* Expand keys in a template. */ static char * -format_expand1(struct format_tree *ft, const char *fmt, int time) +format_expand1(struct format_expand_state *es, const char *fmt) { - char *buf, *out, *name; - const char *ptr, *s; - size_t off, len, n, outlen; - int ch, brackets; - struct tm *tm; - char expanded[8192]; + struct format_tree *ft = es->ft; + char *buf, *out, *name; + const char *ptr, *s; + size_t off, len, n, outlen; + int ch, brackets; + struct tm *tm; + char expanded[8192]; if (fmt == NULL || *fmt == '\0') return (xstrdup("")); - if (ft->loop == FORMAT_LOOP_LIMIT) + if (es->loop == FORMAT_LOOP_LIMIT) return (xstrdup("")); - ft->loop++; + es->loop++; - format_log(ft, "expanding format: %s", fmt); + format_log(es, "expanding format: %s", fmt); - if (time) { - tm = localtime(&ft->time); + if (es->flags & FORMAT_EXPAND_TIME) { + if (es->time == 0) + es->time = time(NULL); + tm = localtime(&es->time); if (strftime(expanded, sizeof expanded, fmt, tm) == 0) { - format_log(ft, "format is too long"); + format_log(es, "format is too long"); return (xstrdup("")); } if (format_logging(ft) && strcmp(expanded, fmt) != 0) - format_log(ft, "after time expanded: %s", expanded); + format_log(es, "after time expanded: %s", expanded); fmt = expanded; } @@ -2468,14 +2514,15 @@ format_expand1(struct format_tree *ft, const char *fmt, int time) n = ptr - fmt; name = xstrndup(fmt, n); - format_log(ft, "found #(): %s", name); + format_log(es, "found #(): %s", name); - if (ft->flags & FORMAT_NOJOBS) { + if ((ft->flags & FORMAT_NOJOBS) || + (es->flags & FORMAT_EXPAND_NOJOBS)) { out = xstrdup(""); - format_log(ft, "#() is disabled"); + format_log(es, "#() is disabled"); } else { - out = format_job_get(ft, name); - format_log(ft, "#() result: %s", out); + out = format_job_get(es, name); + format_log(es, "#() result: %s", out); } free(name); @@ -2497,15 +2544,15 @@ format_expand1(struct format_tree *ft, const char *fmt, int time) break; n = ptr - fmt; - format_log(ft, "found #{}: %.*s", (int)n, fmt); - if (format_replace(ft, fmt, n, &buf, &len, &off) != 0) + format_log(es, "found #{}: %.*s", (int)n, fmt); + if (format_replace(es, fmt, n, &buf, &len, &off) != 0) break; fmt += n + 1; continue; case '}': case '#': case ',': - format_log(ft, "found #%c", ch); + format_log(es, "found #%c", ch); while (len - off < 2) { buf = xreallocarray(buf, 2, len); len *= 2; @@ -2528,8 +2575,8 @@ format_expand1(struct format_tree *ft, const char *fmt, int time) continue; } n = strlen(s); - format_log(ft, "found #%c: %s", ch, s); - if (format_replace(ft, s, n, &buf, &len, &off) != 0) + format_log(es, "found #%c: %s", ch, s); + if (format_replace(es, s, n, &buf, &len, &off) != 0) break; continue; } @@ -2538,8 +2585,8 @@ format_expand1(struct format_tree *ft, const char *fmt, int time) } buf[off] = '\0'; - format_log(ft, "result is: %s", buf); - ft->loop--; + format_log(es, "result is: %s", buf); + es->loop--; return (buf); } @@ -2548,14 +2595,24 @@ format_expand1(struct format_tree *ft, const char *fmt, int time) char * format_expand_time(struct format_tree *ft, const char *fmt) { - return (format_expand1(ft, fmt, 1)); + struct format_expand_state es; + + memset(&es, 0, sizeof es); + es.ft = ft; + es.flags = FORMAT_EXPAND_TIME; + return (format_expand1(&es, fmt)); } /* Expand keys in a template. */ char * format_expand(struct format_tree *ft, const char *fmt) { - return (format_expand1(ft, fmt, 0)); + struct format_expand_state es; + + memset(&es, 0, sizeof es); + es.ft = ft; + es.flags = 0; + return (format_expand1(&es, fmt)); } /* Expand a single string. */ From 7e319756d2450798855c648ede9fbcf7beb187e2 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 6 Oct 2020 07:36:42 +0000 Subject: [PATCH 43/61] Fix a last minute change in previous. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index e864adc4..03ac00cf 100644 --- a/format.c +++ b/format.c @@ -259,7 +259,7 @@ format_copy_state(struct format_expand_state *to, to->ft = from->ft; to->loop = from->loop; to->time = from->time; - to->flags = from->flags|flag; + to->flags = from->flags|flags; } /* Format job update callback. */ From 3afcc6faac828be21da68576d196f5ab344f84d5 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 7 Oct 2020 08:23:55 +0000 Subject: [PATCH 44/61] Allow fnmatch(3) wildcards in update-environment, GitHub issue 2397. --- environ.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/environ.c b/environ.c index 940109b0..74d672e0 100644 --- a/environ.c +++ b/environ.c @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -191,7 +192,11 @@ 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); - if ((envent = environ_find(src, ov->string)) == NULL) + RB_FOREACH(envent, environ, src) { + if (fnmatch(ov->string, envent->name, 0) == 0) + break; + } + if (envent == NULL) environ_clear(dst, ov->string); else environ_set(dst, envent->name, 0, "%s", envent->value); From 991d5a9c74f658225cc82e0b08d168c092eefdbe Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 7 Oct 2020 09:39:43 +0100 Subject: [PATCH 45/61] Add compat for getdtablesize, GitHub issue 2406. --- compat/getdtablesize.c | 29 +++++++++++++++++++++++++++++ compat/imsg-buffer.c | 4 ++-- compat/imsg.h | 10 ++++++---- configure.ac | 1 + 4 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 compat/getdtablesize.c diff --git a/compat/getdtablesize.c b/compat/getdtablesize.c new file mode 100644 index 00000000..fa82577f --- /dev/null +++ b/compat/getdtablesize.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "compat.h" + +#ifdef HAVE_SYSCONF +int +getdtablesize(void) +{ + return (sysconf(_SC_OPEN_MAX)); +} +#endif diff --git a/compat/imsg-buffer.c b/compat/imsg-buffer.c index 814591f4..67d4c705 100644 --- a/compat/imsg-buffer.c +++ b/compat/imsg-buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: imsg-buffer.c,v 1.11 2017/12/14 09:27:44 kettenis Exp $ */ +/* $OpenBSD: imsg-buffer.c,v 1.12 2019/01/20 02:50:03 bcook Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -70,7 +70,7 @@ ibuf_dynamic(size_t len, size_t max) static int ibuf_realloc(struct ibuf *buf, size_t len) { - u_char *b; + unsigned char *b; /* on static buffers max is eq size and so the following fails */ if (buf->wpos + len > buf->max) { diff --git a/compat/imsg.h b/compat/imsg.h index 8bf94147..5b092cfc 100644 --- a/compat/imsg.h +++ b/compat/imsg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: imsg.h,v 1.4 2017/03/24 09:34:12 nicm Exp $ */ +/* $OpenBSD: imsg.h,v 1.5 2019/01/20 02:50:03 bcook Exp $ */ /* * Copyright (c) 2006, 2007 Pierre-Yves Ritschard @@ -21,13 +21,15 @@ #ifndef _IMSG_H_ #define _IMSG_H_ +#include + #define IBUF_READ_SIZE 65535 #define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) #define MAX_IMSGSIZE 16384 struct ibuf { TAILQ_ENTRY(ibuf) entry; - u_char *buf; + unsigned char *buf; size_t size; size_t max; size_t wpos; @@ -42,8 +44,8 @@ struct msgbuf { }; struct ibuf_read { - u_char buf[IBUF_READ_SIZE]; - u_char *rptr; + unsigned char buf[IBUF_READ_SIZE]; + unsigned char *rptr; size_t wpos; }; diff --git a/configure.ac b/configure.ac index 8f26cabd..93246fc8 100644 --- a/configure.ac +++ b/configure.ac @@ -113,6 +113,7 @@ AC_REPLACE_FUNCS([ \ fgetln \ freezero \ getdtablecount \ + getdtablesize \ getline \ getprogname \ memmem \ From 4dc76e084b9c9d3d4b04c7e29bbe425c77ee5ae8 Mon Sep 17 00:00:00 2001 From: tim Date: Fri, 9 Oct 2020 19:12:36 +0000 Subject: [PATCH 46/61] Escape ! in Ql OK jmc@ nicm@, agreement from schwarze@ --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index a39237d5..7900ecce 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1003,7 +1003,7 @@ wait for an empty line input before exiting in control mode .El .Pp A leading -.Ql ! +.Ql \&! turns a flag off if the client is already attached. .Fl r is an alias for From d603dbdef007c879dfb812d569289119e61b4d3f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Oct 2020 07:29:24 +0000 Subject: [PATCH 47/61] Set RGB flag if capabilities are present, GitHub issue 2418. --- tty-term.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tty-term.c b/tty-term.c index 9161ad01..b4feea60 100644 --- a/tty-term.c +++ b/tty-term.c @@ -578,6 +578,9 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) (!tty_term_has(term, TTYC_SETRGBF) || !tty_term_has(term, TTYC_SETRGBB))) tty_add_features(feat, "RGB", ","); + if (tty_term_has(term, TTYC_SETRGBF) && + tty_term_has(term, TTYC_SETRGBB)) + term->flags |= TERM_RGBCOLOURS; /* Apply the features and overrides again. */ tty_apply_features(term, *feat); From 4c8706d39977ca1f12b8980bcc168e0a24a3d0f7 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 13 Oct 2020 10:15:23 +0000 Subject: [PATCH 48/61] Fix note for "previous-window" default key binding, from Sebastian Falbesoner. --- key-bindings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-bindings.c b/key-bindings.c index 63d4bb26..b47f6ff7 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -380,7 +380,7 @@ key_bindings_init(void) "bind -N 'Select the next window' n next-window", "bind -N 'Select the next pane' o select-pane -t:.+", "bind -N 'Customize options' C customize-mode -Z", - "bind -N 'Select the previous pane' p previous-window", + "bind -N 'Select the previous window' p previous-window", "bind -N 'Display pane numbers' q display-panes", "bind -N 'Redraw the current client' r refresh-client", "bind -N 'Choose a session from a list' s choose-tree -Zs", From d8cda9286ff1157ed46126bc4a797b08bb9436ae Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 19 Oct 2020 06:39:28 +0000 Subject: [PATCH 49/61] Client could be NULL in select-window (for example in .tmux.conf), do not set latest session if so. GitHub issue 2429 from Han Boetes. --- cmd-select-window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-select-window.c b/cmd-select-window.c index c85f36be..8dd358b0 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -142,7 +142,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) } cmdq_insert_hook(s, item, current, "after-select-window"); } - if (c->session != NULL) + if (c != NULL && c->session != NULL) s->curw->window->latest = c; recalculate_sizes(); From 31ed29e5511958845fabc66a479eeec39c1836bd Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 26 Oct 2020 19:00:37 +0000 Subject: [PATCH 50/61] SIGQUIT handler needs to be cleared before fork like the others, reported by Simon Andersson. --- proc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/proc.c b/proc.c index c0e2f55b..ff011565 100644 --- a/proc.c +++ b/proc.c @@ -270,6 +270,7 @@ proc_clear_signals(struct tmuxproc *tp, int defaults) if (defaults) { sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGCHLD, &sa, NULL); sigaction(SIGCONT, &sa, NULL); From b33a302235affc19d8a1d8f7473fe589d1bcd17e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 28 Oct 2020 10:09:10 +0000 Subject: [PATCH 51/61] Do not require that there be no other clients before loading the config, being the first client is enough. GitHub issue 2438. --- server-client.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/server-client.c b/server-client.c index efeec85b..4c9706f7 100644 --- a/server-client.c +++ b/server-client.c @@ -2243,7 +2243,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) c->name = name; log_debug("client %p name is %s", c, c->name); - if (c->flags & CLIENT_CONTROL) + if (c->flags & CLIENT_CONTROL) control_start(c); else if (c->fd != -1) { if (tty_init(&c->tty, c) != 0) { @@ -2258,13 +2258,13 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) } /* - * If this is the first client that has finished identifying, load - * configuration files. + * If this is the first client, load configuration files. Any later + * clients are allowed to continue with their command even if the + * config has not been loaded - they might have been run from inside it */ if ((~c->flags & CLIENT_EXIT) && - !cfg_finished && - c == TAILQ_FIRST(&clients) && - TAILQ_NEXT(c, entry) == NULL) + !cfg_finished && + c == TAILQ_FIRST(&clients)) start_cfg(); } From a868bacb46e3c900530bed47a1c6f85b0fbe701c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 29 Oct 2020 16:33:01 +0000 Subject: [PATCH 52/61] Do not write after the end of the array and overwrite the stack when colon-separated SGR sequences contain empty arguments. Reported by Sergey Nizovtsev. --- input.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/input.c b/input.c index 42a60c92..c280c0d9 100644 --- a/input.c +++ b/input.c @@ -1976,8 +1976,13 @@ input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i) free(copy); return; } - } else + } else { n++; + if (n == nitems(p)) { + free(copy); + return; + } + } log_debug("%s: %u = %d", __func__, n - 1, p[n - 1]); } free(copy); From 649e5970e98b0073763f42a25dcab02aadea688f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 30 Oct 2020 08:55:56 +0000 Subject: [PATCH 53/61] Add a -O flag to display-menu to change the mouse behaviour and not close the menu when the mouse is released, from teo_paul1 at yahoo dot com. --- cmd-display-menu.c | 6 ++++-- menu.c | 22 ++++++++++++++++++---- server-client.c | 2 +- tmux.1 | 11 +++++++++-- tmux.h | 1 + 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/cmd-display-menu.c b/cmd-display-menu.c index ae322444..205d1243 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -36,8 +36,8 @@ const struct cmd_entry cmd_display_menu_entry = { .name = "display-menu", .alias = "menu", - .args = { "c:t:T:x:y:", 1, -1 }, - .usage = "[-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] " + .args = { "c:t:OT:x:y:", 1, -1 }, + .usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] " "[-x position] [-y position] name key command ...", .target = { 't', CMD_FIND_PANE, 0 }, @@ -229,6 +229,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) cmd_display_menu_get_position(tc, item, args, &px, &py, menu->width + 4, menu->count + 2); + if (args_has(args, 'O')) + flags |= MENU_STAYOPEN; if (!event->m.valid) flags |= MENU_NOMOUSE; if (menu_display(menu, flags, item, px, py, tc, target, NULL, diff --git a/menu.c b/menu.c index 4fcf660a..3bd56ab1 100644 --- a/menu.c +++ b/menu.c @@ -203,16 +203,28 @@ menu_key_cb(struct client *c, struct key_event *event) m->x > md->px + 4 + menu->width || m->y < md->py + 1 || m->y > md->py + 1 + count - 1) { - if (MOUSE_RELEASE(m->b)) - return (1); + if (~md->flags & MENU_STAYOPEN) { + if (MOUSE_RELEASE(m->b)) + return (1); + } else { + if (!MOUSE_RELEASE(m->b) && + MOUSE_WHEEL(m->b) == 0 && + !MOUSE_DRAG(m->b)) + return (1); + } if (md->choice != -1) { md->choice = -1; c->flags |= CLIENT_REDRAWOVERLAY; } return (0); } - if (MOUSE_RELEASE(m->b)) - goto chosen; + if (~md->flags & MENU_STAYOPEN) { + if (MOUSE_RELEASE(m->b)) + goto chosen; + } else { + if (MOUSE_WHEEL(m->b) == 0 && !MOUSE_DRAG(m->b)) + goto chosen; + } md->choice = m->y - (md->py + 1); if (md->choice != old) c->flags |= CLIENT_REDRAWOVERLAY; @@ -303,6 +315,8 @@ chosen: if (md->choice == -1) return (1); item = &menu->items[md->choice]; + if ((md->flags & MENU_STAYOPEN) && item->name == NULL) + return (0); if (item->name == NULL || *item->name == '-') return (1); if (md->cb != NULL) { diff --git a/server-client.c b/server-client.c index 4c9706f7..3c2b54d5 100644 --- a/server-client.c +++ b/server-client.c @@ -1693,8 +1693,8 @@ server_client_reset_state(struct client *c) * mode. */ if (options_get_number(oo, "mouse")) { - mode &= ~ALL_MOUSE_MODES; if (c->overlay_draw == NULL) { + mode &= ~ALL_MOUSE_MODES; TAILQ_FOREACH(loop, &w->panes, entry) { if (loop->screen->mode & MODE_MOUSE_ALL) mode |= MODE_MOUSE_ALL; diff --git a/tmux.1 b/tmux.1 index 7900ecce..955ce16e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5331,6 +5331,7 @@ option. This command works only from inside .Nm . .It Xo Ic display-menu +.Op Fl O .Op Fl c Ar target-client .Op Fl t Ar target-pane .Op Fl T Ar title @@ -5382,8 +5383,14 @@ Both may be a row or column number, or one of the following special values: Each menu consists of items followed by a key shortcut shown in brackets. If the menu is too large to fit on the terminal, it is not displayed. Pressing the key shortcut chooses the corresponding item. -If the mouse is enabled and the menu is opened from a mouse key binding, releasing -the mouse button with an item selected will choose that item. +If the mouse is enabled and the menu is opened from a mouse key binding, +releasing the mouse button with an item selected chooses that item and +releasing the mouse button without an item selected closes the menu. +.Fl O +changes this behaviour so that the menu does not close when the mouse button is +released without an item selected the menu is not closed and a mouse button +must be clicked to choose an item. +.Pp The following keys are also available: .Bl -column "Key" "Function" -offset indent .It Sy "Key" Ta Sy "Function" diff --git a/tmux.h b/tmux.h index 4ba2c13b..1845775d 100644 --- a/tmux.h +++ b/tmux.h @@ -2978,6 +2978,7 @@ __dead void printflike(1, 2) fatalx(const char *, ...); /* menu.c */ #define MENU_NOMOUSE 0x1 #define MENU_TAB 0x2 +#define MENU_STAYOPEN 0x4 struct menu *menu_create(const char *); void menu_add_items(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, From 910457f68dfc04c491f31d773788c61300f3f8c7 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 30 Oct 2020 09:00:07 +0000 Subject: [PATCH 54/61] There is no reason not to fire focus events when a pane is in a mode, GitHub issue 2372. --- server-client.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server-client.c b/server-client.c index 3c2b54d5..190897ff 100644 --- a/server-client.c +++ b/server-client.c @@ -1590,10 +1590,6 @@ server_client_check_pane_focus(struct window_pane *wp) if (wp->window->active != wp) goto not_focused; - /* If we're in a mode, we're not focused. */ - if (wp->screen != &wp->base) - goto not_focused; - /* * If our window is the current window in any focused clients with an * attached session, we're focused. From ce2b6ff40e13fd25f359cfe3faad49b094aad115 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 30 Oct 2020 09:25:41 +0000 Subject: [PATCH 55/61] Style trim test (currently failing). --- regress/style-trim.sh | 93 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 regress/style-trim.sh diff --git a/regress/style-trim.sh b/regress/style-trim.sh new file mode 100644 index 00000000..56f93656 --- /dev/null +++ b/regress/style-trim.sh @@ -0,0 +1,93 @@ +#!/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 + +$TMUX2 -f/dev/null new -d "$TMUX -f/dev/null new" +sleep 2 +$TMUX set -g status-style fg=default,bg=default + +check() { + v=$($TMUX display -p "$1") + $TMUX set -g status-format[0] "$1" + sleep 1 + r=$($TMUX2 capturep -Cep|tail -1|sed 's|\\033\[||g') + + #printf "$1 = [$v = $2] [$r = $3]" + if [ "$v" = "$2" -a "$r" = "$3" ]; then + : #printf " good\n" + else + #printf " \033[31mbad\033[0m\n" + exit 1 + fi +} + +# drawn as #0 +$TMUX setenv -g V '#0' +check '#{V} #{w:V}' '#0 2' '#0 2' +check '#{=3:V}' '#0' '#0' +check '#{=-3:V}' '#0' '#0' + +# drawn as #0 +$TMUX setenv -g V '###[bg=yellow]0' +check '#{V} #{w:V}' '###[bg=yellow]0 2' '#43m0 249m' +check '#{=3:V}' '###[bg=yellow]0' '#43m049m' +check '#{=-3:V}' '###[bg=yellow]0' '#43m049m' + +# drawn as #0123456 +$TMUX setenv -g V '#0123456' +check '#{V} #{w:V}' '#0123456 8' '#0123456 8' +check '#{=3:V}' '#01' '#01' +check '#{=-3:V}' '456' '456' + +# drawn as ##0123456 +$TMUX setenv -g V '##0123456' +check '#{V} #{w:V}' '##0123456 9' '##0123456 9' +check '#{=3:V}' '##0' '##0' +check '#{=-3:V}' '456' '456' + +# drawn as ###0123456 +$TMUX setenv -g V '###0123456' +check '#{V} #{w:V}' '###0123456 10' '###0123456 10' +check '#{=3:V}' '###' '###' +check '#{=-3:V}' '456' '456' + +# drawn as 0123456 +$TMUX setenv -g V '#[bg=yellow]0123456' +check '#{V} #{w:V}' '#[bg=yellow]0123456 7' '43m0123456 749m' +check '#{=3:V}' '#[bg=yellow]012' '43m01249m' +check '#{=-3:V}' '#[bg=yellow]456' '43m45649m' + +# drawn as #[bg=yellow]0123456 +$TMUX setenv -g V '##[bg=yellow]0123456' +check '#{V} #{w:V}' '##[bg=yellow]0123456 19' '#[bg=yellow]0123456 19' +check '#{=3:V}' '##[b' '#[b' +check '#{=-3:V}' '456' '456' + +# drawn as #0123456 +$TMUX setenv -g V '###[bg=yellow]0123456' +check '#{V} #{w:V}' '###[bg=yellow]0123456 8' '#43m0123456 849m' +check '#{=3:V}' '###[bg=yellow]01' '#43m0149m' +check '#{=-3:V}' '#[bg=yellow]456' '43m45649m' + +# drawn as ##[bg=yellow]0123456 +$TMUX setenv -g V '####[bg=yellow]0123456' +check '#{V} #{w:V}' '####[bg=yellow]0123456 20' '##[bg=yellow]0123456 20' +check '#{=3:V}' '####[' '##[' +check '#{=-3:V}' '456' '456' + +# drawn as ###0123456 +$TMUX setenv -g V '#####[bg=yellow]0123456' +check '#{V} #{w:V}' '#####[bg=yellow]0123456 9' '##43m0123456 949m' +check '#{=3:V}' '#####[bg=yellow]0' '##43m049m' +check '#{=-3:V}' '#[bg=yellow]456' '43m45649m' + +$TMUX kill-server 2>/dev/null +$TMUX2 kill-server 2>/dev/null +exit 0 From 02197f20d0809a5c248a32ef0ca3a45c7e3566bd Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 30 Oct 2020 11:33:41 +0000 Subject: [PATCH 56/61] Do not leak path when freeing screen, from Sergey Nizovtsev. --- screen.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/screen.c b/screen.c index 227934dd..d39447de 100644 --- a/screen.c +++ b/screen.c @@ -79,6 +79,7 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) s->title = xstrdup(""); s->titles = NULL; + s->path = NULL; s->cstyle = 0; s->ccolour = xstrdup(""); @@ -121,6 +122,7 @@ screen_free(struct screen *s) { free(s->sel); free(s->tabs); + free(s->path); free(s->title); free(s->ccolour); From 8e1d28453d23d6283abe1bb709a4fe06139d2750 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 30 Oct 2020 11:34:13 +0000 Subject: [PATCH 57/61] Limit range of repeat to avoid silly high numbers causing delays, from Sergey Nizovtsev. --- input.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/input.c b/input.c index c280c0d9..8b7ba08a 100644 --- a/input.c +++ b/input.c @@ -1545,6 +1545,10 @@ input_csi_dispatch(struct input_ctx *ictx) if (n == -1) break; + m = screen_size_x(s) - s->cx; + if (n > m) + n = m; + if (ictx->last == -1) break; ictx->ch = ictx->last; From 9726c4454e29cb5b9c6681abfb5c99972a9bd574 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 30 Oct 2020 12:00:01 +0000 Subject: [PATCH 58/61] Do not allow disabled items to be selected. --- menu.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/menu.c b/menu.c index 3bd56ab1..48c9ed2f 100644 --- a/menu.c +++ b/menu.c @@ -315,10 +315,11 @@ chosen: if (md->choice == -1) return (1); item = &menu->items[md->choice]; - if ((md->flags & MENU_STAYOPEN) && item->name == NULL) - return (0); - if (item->name == NULL || *item->name == '-') + if (item->name == NULL || *item->name == '-') { + if (md->flags & MENU_STAYOPEN) + return (0); return (1); + } if (md->cb != NULL) { md->cb(md->menu, md->choice, item->key, md->data); md->cb = NULL; From 0b8ae4de5c38210d9a3ac6d003726f7cab89f285 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 30 Oct 2020 12:29:40 +0000 Subject: [PATCH 59/61] Update CHANGES. --- CHANGES | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index cf00a5b0..d385ed16 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,33 @@ -CHANGES FROM 3.1b TO 3.2 +CHANGES FROM 3.2 TO 3.3 + +* Fire focus events even when the pane is in a mode. + +* Add -O flag to display-menu to not automatically close when all mouse buttons + are released. + +* Allow fnmatch(3) wildcards in update-environment. + +* Disable nested job expansion so that the result of #() is not expanded again. + +* Use the setal capability as well as (tmux's) Setulc. + +* Add -q flag to unbind-key to hide errors. + +* Allow -N without a command to change or add a note to an existing key. + +* Add a -w flag to set- and load-buffer to send to clipboard using OSC 52. + +* Add -F to set-environment and source-file. + +* Allow colour to be spelt as color in various places. + +* Add n: modifier to get length of a format. + +* Respond to OSC colour requests if a colour is available. + +* Add a -d option to display-message to set delay. + +CHANGES FROM 3.1c TO 3.2 * Add a way for control mode clients to subscribe to a format and be notified of changes rather than having to poll. @@ -265,6 +294,11 @@ CHANGES FROM 3.1b TO 3.2 * Add number operators for formats (+, -, *, / and m), +CHANGED FROM 3.1b TO 3.1c + +* Do not write after the end of the array and overwrite the stack when + colon-separated SGR sequences contain empty arguments. + CHANGES FROM 3.1a TO 3.1b * Fix build on systems without sys/queue.h. From 95841ba16acafa8c1a516712ad0f2b48e34357e6 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 30 Oct 2020 18:54:23 +0000 Subject: [PATCH 60/61] With csh, a tmux client gets SIGTERM before SIGCONT when killed with "kill %%", so when the client tells the server it got SIGCONT, don't use bits that may already have been freed when it got SIGTERM. Also don't print anything on exit if we get SIGTERM while suspended. Reported by Theo. --- client.c | 9 +++++++-- server-client.c | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/client.c b/client.c index f08d6f29..dcbc0d18 100644 --- a/client.c +++ b/client.c @@ -36,6 +36,7 @@ static struct tmuxproc *client_proc; static struct tmuxpeer *client_peer; static uint64_t client_flags; +static int client_suspended; static enum { CLIENT_EXIT_NONE, CLIENT_EXIT_DETACHED, @@ -221,7 +222,7 @@ static void client_exit(void) { struct client_file *cf; - size_t left; + size_t left; int waiting = 0; RB_FOREACH (cf, client_files, &client_files) { @@ -763,6 +764,7 @@ client_signal(int sig) struct sigaction sigact; int status; + log_debug("%s: %s", __func__, strsignal(sig)); if (sig == SIGCHLD) waitpid(WAIT_ANY, &status, WNOHANG); else if (!client_attached) { @@ -776,7 +778,8 @@ client_signal(int sig) proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; case SIGTERM: - client_exitreason = CLIENT_EXIT_TERMINATED; + if (!client_suspended) + client_exitreason = CLIENT_EXIT_TERMINATED; client_exitval = 1; proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; @@ -791,6 +794,7 @@ client_signal(int sig) if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0); + client_suspended = 0; break; } } @@ -1003,6 +1007,7 @@ client_dispatch_attached(struct imsg *imsg) sigact.sa_handler = SIG_DFL; if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); + client_suspended = 1; kill(getpid(), SIGTSTP); break; case MSG_LOCK: diff --git a/server-client.c b/server-client.c index 190897ff..3e256a92 100644 --- a/server-client.c +++ b/server-client.c @@ -2025,7 +2025,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) break; c->flags &= ~CLIENT_SUSPENDED; - if (c->fd == -1) /* exited in the meantime */ + if (c->fd == -1 || c->session == NULL) /* exited already */ break; s = c->session; From ac5045a00f1fee2ca94aef063e6a5a3d2efce3f1 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 2 Nov 2020 08:21:30 +0000 Subject: [PATCH 61/61] Add numeric comparisons for formats, from teo_paul1 at yahoo dot com in GitHub issue 2442. --- format.c | 42 +++++++++++++++++++++++++++++++++++++++++- tmux.1 | 12 ++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/format.c b/format.c index 03ac00cf..99efbc1c 100644 --- a/format.c +++ b/format.c @@ -1984,7 +1984,17 @@ format_replace_expression(struct format_modifier *mexp, int use_fp = 0; u_int prec = 0; double mleft, mright, result; - enum { ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULUS } operator; + enum { ADD, + SUBTRACT, + MULTIPLY, + DIVIDE, + MODULUS, + EQUAL, + NOT_EQUAL, + GREATER_THAN, + GREATER_THAN_EQUAL, + LESS_THAN, + LESS_THAN_EQUAL } operator; if (strcmp(mexp->argv[0], "+") == 0) operator = ADD; @@ -1997,6 +2007,18 @@ format_replace_expression(struct format_modifier *mexp, else if (strcmp(mexp->argv[0], "%") == 0 || strcmp(mexp->argv[0], "m") == 0) operator = MODULUS; + else if (strcmp(mexp->argv[0], "==") == 0) + operator = EQUAL; + else if (strcmp(mexp->argv[0], "!=") == 0) + operator = NOT_EQUAL; + else if (strcmp(mexp->argv[0], ">") == 0) + operator = GREATER_THAN; + else if (strcmp(mexp->argv[0], "<") == 0) + operator = LESS_THAN; + else if (strcmp(mexp->argv[0], ">=") == 0) + operator = GREATER_THAN_EQUAL; + else if (strcmp(mexp->argv[0], "<=") == 0) + operator = LESS_THAN_EQUAL; else { format_log(es, "expression has no valid operator: '%s'", mexp->argv[0]); @@ -2059,6 +2081,24 @@ format_replace_expression(struct format_modifier *mexp, case MODULUS: result = fmod(mleft, mright); break; + case EQUAL: + result = fabs(mleft - mright) < 1e-9; + break; + case NOT_EQUAL: + result = fabs(mleft - mright) > 1e-9; + break; + case GREATER_THAN: + result = (mleft > mright); + break; + case GREATER_THAN_EQUAL: + result = (mleft >= mright); + break; + case LESS_THAN: + result = (mleft < mright); + break; + case LESS_THAN_EQUAL: + result = (mleft > mright); + break; } if (use_fp) xasprintf(&value, "%.*f", prec, result); diff --git a/tmux.1 b/tmux.1 index 955ce16e..3d58b32f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -4544,7 +4544,7 @@ multiplication .Ql * , division .Ql / , -and modulus +modulus .Ql m or .Ql % @@ -4553,7 +4553,15 @@ or must be escaped as .Ql %% in formats which are also expanded by -.Xr strftime 3 ) . +.Xr strftime 3 ) +and numeric comparison operators +.Ql == , +.Ql != , +.Ql < , +.Ql <= , +.Ql > +and +.Ql >= . For example, .Ql #{e|*|f|4:5.5,3} multiplies 5.5 by 3 for a result with four decimal places and